package greenfoot.core;

import greenfoot.Actor;
import greenfoot.ActorVisitor;
import greenfoot.World;
import greenfoot.WorldVisitor;
import greenfoot.event.SimulationListener;
import greenfoot.event.SimulationListener.AsyncEvent;
import greenfoot.event.SimulationListener.SyncEvent;
import greenfoot.event.WorldEvent;
import greenfoot.event.WorldListener;
import greenfoot.util.HDTimer;
import threadchecker.OnThread;
import threadchecker.Tag;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

import javax.swing.event.EventListenerList;


| The main class of the simulation. It drives the simulation and calls act() | on the objects in the world and then paints them. | | @author Poul Henriksen | @OnThread(Tag.Simulation) public class Simulation extends Thread implements WorldListener{ @OnThread(Tag.Any) private WorldHandler worldHandler;
| Whether the simulation is (to be) paused | @OnThread(value = Tag.Any, requireSynchronized = true) private boolean paused;
| Whether the simulation is enabled (world installed) | private volatile boolean enabled;
| Whether to run one loop when paused | @OnThread(value = Tag.Any, requireSynchronized = true) private boolean runOnce;
| Tasks that are queued to run on the simulation thread | @OnThread(value = Tag.Any, requireSynchronized = true) private Queue<SimulationRunnable> queuedTasks = new LinkedList<>(); @OnThread(Tag.Any) private final List<SimulationListener> listenerList = new ArrayList<>(); @OnThread(value = Tag.Any, requireSynchronized = true) private static Simulation instance;
| for timing the animation | public static final int MAX_SIMULATION_SPEED = 100; @OnThread(value = Tag.Any, requireSynchronized = true) private int speed; private long lastDelayTime; private long delay; | Lock to synchronize access to the two fields: delaying and interruptDelay | @OnThread(Tag.Any) private Object interruptLock = new Object();
| Whether we are currently delaying between act-loops. | @OnThread(Tag.Any) private boolean delaying;
| Whether a delay between act-loops should be interrupted. | @OnThread(Tag.Any) private boolean interruptDelay;
| Used to figure out when we are transitioning from running to paused state and vice versa. | Only modify this from the simulation thread. | private boolean isRunning = false;
| flag to indicate that we want to abort the simulation and never start it again. | private volatile boolean abort;
| Create new simulation. Leaves the simulation in paused state | @OnThread(Tag.Any) private Simulation() { this.setName("SimulationThread"); setPriority(Thread.MIN_PRIORITY); paused = true; speed = 50; delay = calculateDelay(speed); HDTimer.init(); }
| Initialize the (singleton) simulation instance. | The simulation thread will not actually be started until the WorldHandler | is attached. | @OnThread(Tag.Any) public static synchronized void initialize() { instance = new Simulation(); }
| Returns the simulation if it is initialised. If not, it will return null. | @OnThread(Tag.Any) public static synchronized Simulation getInstance() { return instance; }
| Attach this simulation to the world handler (and vice versa). | @OnThread(Tag.Any) public void attachWorldHandler(WorldHandler worldHandler) { this.worldHandler = worldHandler; worldHandler.addWorldListener(this); addSimulationListener(worldHandler); start(); } | Runs the simulation from the current state. | @Override @OnThread(value = Tag.Simulation, ignoreParent = true) public void run() {
| It is important this redirects to another method. | The debugger sets a breakpoint on the first line of this method, and if | that is a loop (as is the case for the first line of runContent at the time of writing) | then it hits the breakpoint every time. By putting it all in a separate | method, we avoid that happening: | runContent(); } private void runContent() { while (!abort){ try { maybePause(); if (worldHandler.hasWorld()) { runOneLoop(worldHandler.getWorld()); } boolean currentlyPaused; synchronized (this) { currentlyPaused = paused; } if (!currentlyPaused) { delay(); } } catch (ActInterruptedException e) { } catch (InterruptedException e) { } catch (Throwable t) { synchronized (this) { paused = true; } t.printStackTrace(); WorldHandler.getInstance().notifyStoppedWithError(); paintRemote(true); } } synchronized (this) { if (isRunning) { World world = worldHandler.getWorld(); if (world != null) { worldStopped(world); } isRunning = false; } } } public static interface SimulationRunnable { @OnThread(Tag.Simulation) public void run(); }
| Schedule some task to run on the simulation thread. The task will be run with the | world write lock held. | @OnThread(Tag.Any) public synchronized void runLater(SimulationRunnable r) { queuedTasks.add(r); if (paused || ! enabled) { notify(); } } public final static String PAUSED = "simulationWait";
| A special method recognised by the debugger as indicating that the simulation | is pausing. | private void simulationWait() throws InterruptedException { paintRemote(true); this.wait(); } public final static String WORLD_STARTED = "worldStarted"; private static void worldStarted(World world) { world.started(); } public final static String WORLD_STOPPED = "worldStopped"; private static void worldStopped(World world) { world.stopped(); }
| Block if the simulation is paused. This will block until the simulation | is resumed (is both enabled and unpaused). It should only be called on the | simulation thread. | | @throws InterruptedException If it couldn't acquire the world lock when | signalling started()/stopped() to the world. | private void maybePause() throws InterruptedException { while (!abort){ runQueuedTasks(); World world; boolean checkStop; synchronized (this) { checkStop = (paused || !enabled) && isRunning; world = worldHandler.getWorld(); if (checkStop) { isRunning = false; synchronized (interruptLock) { interruptDelay = false; } } else if (isRunning) { return; } } if (checkStop) { try { signalStopping(world); } catch (InterruptedException ie) { continue; } synchronized (this) { runOnce = false; if (! paused) { isRunning = enabled; } } } boolean doResumeRunning; synchronized (this) { doResumeRunning = !paused && enabled && !abort && !isRunning; if (! isRunning && ! doResumeRunning && ! runOnce) { if (enabled) { fireSimulationEventAsync(AsyncEvent.STOPPED); } if (worldHandler != null) { worldHandler.repaint(); } if (! queuedTasks.isEmpty()) { continue; } System.gc(); try { simulationWait(); lastDelayTime = System.nanoTime(); } catch (InterruptedException e1) { } continue; } } if (doResumeRunning) { resumeRunning(); } synchronized (this) { if (runOnce || isRunning) { runOnce = false; return; } } } runQueuedTasks(); }
| Send a started event and notify the world that it is now running. | | @throws InterruptedException | private void resumeRunning() throws InterruptedException { isRunning = true; lastDelayTime = System.nanoTime(); fireSimulationEventSync(SyncEvent.STARTED); World world = worldHandler.getWorld(); if (world != null) { try { worldStarted(world); } catch (Throwable t) { isRunning = false; synchronized (interruptLock) { Thread.interrupted(); interruptDelay = false; } setPaused(true); t.printStackTrace(); return; } } }
| Tell the world that the simulation is stopping. The world might resume | the simulation when this happens. | private void signalStopping(World world) throws InterruptedException { if (world != null) { try { worldStopped(world); } catch (ActInterruptedException aie) { synchronized (this) { paused = true; } throw aie; } catch (Throwable t) { synchronized (this) { paused = true; } t.printStackTrace(); } } }
| This must match the method name below! | public static String RUN_QUEUED_TASKS = "runQueuedTasks";
| Run all tasks that have been schedule to run on the simulation thread. | Of course, this should only be called from the simulation thread... | (and from an unsynchronized context). | private void runQueuedTasks() { SimulationRunnable r; synchronized (this) { r = queuedTasks.poll(); } while (r != null){ try { fireSimulationEventSync(SyncEvent.QUEUED_TASK_BEGIN); try { r.run(); } catch (Throwable t) { t.printStackTrace(); } fireSimulationEventSync(SyncEvent.QUEUED_TASK_END); } finally { } synchronized (this) { r = queuedTasks.poll(); } } }
| Performs one step in the simulation. Calls act() on all actors. | May propagate a runtime exception or error from user code. | | @throws ActInterruptedException if an act() call was interrupted. | private void runOneLoop(World world) { fireSimulationEventSync(SyncEvent.NEW_ACT_ROUND); ActInterruptedException interruptedException = null; List<? extends Actor> objects = null; try { actWorld(world); if (world != worldHandler.getWorld()) { paintRemote(false); return; } } catch (ActInterruptedException e) { interruptedException = e; } objects = new ArrayList<Actor>(WorldVisitor.getObjectsListInActOrder(world)); for (Actor actor : objects) { if (!enabled) { return; } if (ActorVisitor.getWorld(actor) != null) { try { actActor(actor); if (world != worldHandler.getWorld()) { return; } } catch (ActInterruptedException e) { if (interruptedException == null) { interruptedException = e; } } } } worldHandler.getKeyboardManager().clearLatchedKeys(); if (interruptedException != null) { throw interruptedException; } repaintIfNeeded(); fireSimulationEventSync(SyncEvent.END_ACT_ROUND); } public static final String ACT_ACTOR = "actActor"; private static void actActor(Actor actor) { actor.act(); } public static final String ACT_WORLD = "actWorld"; private static void actWorld(World world) { world.act(); } public static final String NEW_INSTANCE = "newInstance"; public static Object newInstance(Constructor<?> constructor) throws InvocationTargetException, IllegalArgumentException, InstantiationException, IllegalAccessException { return constructor.newInstance((Object[])null); }
| Repaints the world if needed to obtain the desired frame rate. | private void repaintIfNeeded() { paintRemote(false); } protected void paintRemote(boolean forcePaint) { WorldHandler.getInstance().paint(forcePaint); }
| Debug output to print the rate at which updates are performed | (acts/second). | | | |private void printUpdateRate(long currentTime) | |{} //updates++; | |long timeSinceUpdate = currentTime - lastUpdate; | |if (timeSinceUpdate > 3000000000L) {} lastUpdate = currentTime; | |//updates = 0; | |} | |} | Run one step of the simulation. Each actor in the world acts once. | @OnThread(Tag.Any) public synchronized void runOnce() { if (enabled) { synchronized (interruptLock) { interruptDelay = false; } } runOnce = true; notifyAll(); }
| Toggles the running/paused state of the simulation. | @OnThread(Tag.Any) public synchronized void togglePaused() { setPaused(!paused); }
| Pauses and unpauses the simulation. | @OnThread(Tag.Any) public synchronized void setPaused(boolean b) { if (paused == b) { return; } paused = b; if (enabled) { if (!paused) { synchronized (interruptLock) { interruptDelay = false; } } notifyAll(); if (paused) { interruptDelay(); } } }
| Interrupt if we are currently delaying between act-loops or the user is | using the Greenfoot.delay() method. This will basically jump to the next | act-loop as fast as possible while still executing the rest of actors in | the current loop. Used by setPaused() and setSpeed() to interrupt current | delays. | @OnThread(Tag.Any) private void interruptDelay() { synchronized (interruptLock) { if (delaying) { interrupt(); } else { interruptDelay = true; } } }
| Enable or disable the simulation. | @OnThread(Tag.Any) public synchronized void setEnabled(boolean b) { if (b == enabled) { return; } enabled = b; if (b) { notifyAll(); if (paused) { fireSimulationEventAsync(AsyncEvent.STOPPED); } } else { interruptDelay(); if (! paused) { paused = true; } else { synchronized (interruptLock) { interruptDelay = false; } } fireSimulationEventAsync(AsyncEvent.DISABLED); } } @OnThread(Tag.Simulation) private void fireSimulationEventSync(SyncEvent event) { synchronized (listenerList) { for (SimulationListener listener : listenerList) { listener.simulationChangedSync(event); } } } @OnThread(Tag.Any) private void fireSimulationEventAsync(AsyncEvent event) { synchronized (listenerList) { for (SimulationListener listener : listenerList) { listener.simulationChangedAsync(event); } } }
| Add a simulationListener to listen for changes. | | @param l | Listener to add | @OnThread(Tag.Any) public void addSimulationListener(SimulationListener l) { synchronized (listenerList) { listenerList.add(0, l); } }
| Set the speed of the simulation. | | @param newSpeed | The speed in the range (0..100) | @OnThread(Tag.Any) public void setSpeed(int newSpeed) { if (newSpeed < 0) { newSpeed = 0; } else if (newSpeed > MAX_SIMULATION_SPEED) { newSpeed = MAX_SIMULATION_SPEED; } boolean speedChanged; synchronized (this) { speedChanged = this.speed != newSpeed; if (speedChanged) { this.speed = newSpeed; this.delay = calculateDelay(newSpeed); if (!paused) { synchronized (interruptLock) { if (delaying) { interrupt(); } } } } } if (speedChanged) { fireSimulationEventAsync(AsyncEvent.CHANGED_SPEED); } }
| Returns the delay as a function of the speed. | | @return The delay in nanoseconds. | @OnThread(Tag.Any) private static long calculateDelay(int curSpeed) { long rawDelay = MAX_SIMULATION_SPEED - curSpeed; long min = 30 * 1000L; long max = 10000 * 1000L * 1000L; double a = Math.pow(max / (double) min, 1D / (MAX_SIMULATION_SPEED - 1)); long calcDelay = 0; if (rawDelay > 0) { calcDelay = (long) (Math.pow(a, rawDelay - 1) * min); } return calcDelay; }
| Get the current simulation speed. | | @return The speed in the range (1..100) | @OnThread(Tag.Any) public synchronized int getSpeed() { return speed; }
| Sleep an amount of time according to the current speed setting for this | simulation. This will wait without considering previous waits, as opposed | to delay(). It should be called only from the simulation thread, in an | unsynchronized context. | @OnThread(Tag.Simulation) public void sleep(int numCycles) { synchronized (this) { if (paused && isRunning && !runOnce) { return; } if (! enabled) { return; } synchronized (interruptLock) { if (interruptDelay) { return; } delaying = true; } } fireSimulationEventSync(SyncEvent.DELAY_LOOP_ENTERED); try { worldHandler.repaint(); for (int i = 0; i < numCycles; i++) { HDTimer.sleep(delay); } } catch (InterruptedException e) { } finally { synchronized (interruptLock) { Thread.interrupted(); interruptDelay = false; delaying = false; } fireSimulationEventSync(SyncEvent.DELAY_LOOP_COMPLETED); } }
| Cause a delay (wait) according to the current speed setting for this | simulation. It will take the time spend in this simulation loop into | consideration and only pause the remaining time. | | <p>This method is used for controlling the speed of the animation. | | <p>The world lock should not be held when this method is called, so | that repaints can occur. | private void delay() { long currentTime = System.nanoTime(); long timeElapsed = currentTime - lastDelayTime; long actualDelay = Math.max(delay - timeElapsed, 0L); synchronized (this) { synchronized (interruptLock) { if (interruptDelay) { interruptDelay = false; if (paused || abort) { lastDelayTime = currentTime; return; } } delaying = true; } } fireSimulationEventSync(SyncEvent.DELAY_LOOP_ENTERED); while (actualDelay > 0) { try { HDTimer.sleep(actualDelay); } catch (InterruptedException ie) { synchronized (this) { if (!enabled || paused || abort) { break; } } } currentTime = System.nanoTime(); timeElapsed = currentTime - lastDelayTime; actualDelay = delay - timeElapsed; } lastDelayTime = currentTime; synchronized (interruptLock) { Thread.interrupted(); interruptDelay = false; delaying = false; } fireSimulationEventSync(SyncEvent.DELAY_LOOP_COMPLETED); }
| Abort the simulation. It abruptly stops what is running and ends the | simulation thread, and it is not possible to start it again. | @OnThread(Tag.Any) public void abort() { abort = true; setEnabled(false); } | A new world was created - we're ready to go. Enable the simulation | functions. | @Override public void worldCreated(WorldEvent e) { setEnabled(true); }
| The world was removed - disable the simulation functions. | @Override public void worldRemoved(WorldEvent e) { setEnabled(false); } }
top, use, map, class Simulation

.   Simulation
.   initialize
.   getInstance
.   attachWorldHandler
.   run
.   runContent

top, use, map, interface SimulationRunnable

.   run
.   runLater
.   simulationWait
.   worldStarted
.   worldStopped
.   maybePause
.   resumeRunning
.   signalStopping
.   runQueuedTasks
.   runOneLoop
.   actActor
.   actWorld
.   newInstance
.   repaintIfNeeded
.   paintRemote
.   runOnce
.   togglePaused
.   setPaused
.   interruptDelay
.   setEnabled
.   fireSimulationEventSync
.   fireSimulationEventAsync
.   addSimulationListener
.   setSpeed
.   calculateDelay
.   getSpeed
.   sleep
.   delay
.   abort
.   worldCreated
.   worldRemoved




1159 neLoCode + 91 LoComm