Almost every aspect of SpeckSim can be changed and extended, this section will detail what can be changed, how to do it, and highlight the points to watch out for.
Extension classes can be loaded into SpeckSim with the "-l" cammand line option, like so:
java -jar -Djava.library.path=<natives> SpeckSim.jar -l foo.bar.MySpeckClass,foo.bar.MyMovementModel
The list of classes to be loaded must be comma-separated.
Before the specifics of extending are explained, you'll need a brief
overview of how SpeckSim works. This is a very rough diagram of how the
different parts of the simulator fit together
The SpeckSim main class maintains a queue of events that are executed
in order. These events are usually placed in the queue by the specks
resident in the simulator, but there is no firm restriction to this
effect. At some point, an event is executed that causes the current
state of the simulator to be copied into the a SimulatorState
object. Statistics are calculated from the updated state object and the
GUI is updated.
The important thing to take from all this is how important the state
object is. StatisticGatherers
and StateRenderers
operate only on the state, and so all needed data must be present.
At any rate, javadoc for the entire system can be found here.
The basic interface for a Speck is very
bare, in order to permit a wide array of implementations. Things are
made a bit easier by extending the AbstractSpeck
class, but by far the easiest way to add a custom speck behaviour is to
take an existing one and make whatever changes are necessary.
The most suitable candidate for this treatment is the NeighbourlySpeck. This speck
implementation has very simple behaviour - it periodically broadcasts
it's own ID number while maintaining a list of other IDs that it has
received recently - but still demonstrates all the important points of
speck behaviour:
The NeighbourlySpeck
implementation is extensively
commented, so any explanation here will probably be duplication. For an explanation of the annotations applied to some of the fields, head on over to the Configuration section.
Once you have your Speck implementation ready, it can be added into
SpeckSim simply by specifying the full class name on the command-line as described above.
Alternatively, it can be loaded programatically with a static call to SpeckFactory.register( class name );
Specks communicate by means of MessageShells
that define the limits of their broadcasts. Again, the minimum
interface is very sparse, so there's not much to discuss.
You can see a simple example of a MessageShell in the PerfectRadioShell.
You can test your custom MessageShells using the ShellTest application
that's also included in the SpeckSim jar. Run it with:
java
-Djava.library.path=<natives> SpeckSim.jar -st -l foo.bar.MyMessageShell
where foo.bar.MyMessageShell is the name of your new implementation (Which is also on the classpath).
Custom MessageShells will be referenced by a Speck implementation
(Remember, unless you're sure about what you're doing, it should be a
static reference) and so no special call is needed to include them in
SpeckSim.
The position and motion of all specks are dictated by the MovementModel.
As ever, examples are roughly three thousand times more effective at
explaining things than a load of text, so without further ado:
Movement models also have the opportunity to produce some data to be
placed in the SimulatorState object. In this way you can provide the
data needed to visualise your fluid flow field or whatever you've
cooked up. On the other hand, you can always just pass null if you
don't want to bother.
As with Speck implementations, simply specify the class name on the
command line or call MovementModelManager.register(
class name ) to include your model in SpeckSim.
Calculating new statistics is simply a matter of implementing the StatisticsModule
interface. The only thing to watch out for here is to make sure that
the indices of the statistics returned match up with the indices of the
name array. See NeighbourhoodModule
for a simple example.
Once again, add your StatisticsModules into specksim either by adding
the class name to the command line, or calling StatisticsGatherer.register( class name ).
A well behaved StatisticsModule will take account of the will of any StateFilters (see below) by calling isIncluded( int index ). Note that this method belongs to the StatisticsModule abstract superclass, and is distinct from SimulatorState.isIncluded( int index ). This allows SpeckSim to automatically generate both filtered and unfiltered statistics automatically.
Statistics are all well and good, but visualising data is far more compelling. The basic interface for visualising is the StateRenderer, but I suggest you instead extend AbstractStateRenderer. This will handle some of the fine details of maintaining a display list and so on.
Remember to check if a particular speck's details should be rendered with a call to state.isIncluded( int index ).
As ever, a simple example is the NeighbourhoodRenderer.
StateRenderers are added to SpeckSim by, you've guessed it, adding the
class name to the command line or by calling SpeckVisualiser.loadRenderer( classname ).
Now that you've implemented your own statistics and visualisations, you
might have noticed that you're duplicating some processing work. This,
clearly, is a waste of resources, and you wish there was some way to do
the processing once and then access the results in both
StatisticModules and StateVisualisers.
Rejoice! Implement the processing in a StateProcessor,
and the results will be made available for statistics and visualisation.
Continuing on with the Neighbourly examples, see NeighbourhoodProcessor.
The only thing to watch out for when implementing a StateProcessor is
to make sure that you call processState() whenever the results are
requested. You should not worry about calling processState() too many
times, the processing will only occur once for every time the
SimulatorState object is changed.
Once again, implementors should be sure to check is a particular speck should be processed with a call to state.isIncluded( int index ).
Add a StateProcessor to SpeckSim by specifying the class name on the
command line or by calling SimulatorState.register(
classname ).
Whilst the configuration system is handy for making sweeping changes to
all specks, it is sometime useful to be able to make changes to
individual specks. This can be achieved by using a GLSelectionListener. These allow you to attach some code that is alerted whenever the selection of the visualisation changes
See SpeckLoggingController
for an example.
Like the StateRenderers, GLSelectionListeners are added to SpeckSim by
specifying their classname on the command line or by calling SpeckVisualiser.loadSelectionListener(
classname ).
By default, all modules that consume the state (Statistic gathereres, visualisers, state processors) will operate on all available data. It is sometimes desirable to process some subset of the state objects. For instance, you might want to gather statistics on only those specks within a certain quadrant of the field, or only the state from a particular flavour of speck.
This can be achieved by using StateFilters. These customisable modules make a decision on some aspect of the state object, and well-behaved state consuming modules will honour this decision. All that is required for the state consuming modules to take account of the wishes of the StateFilters is to check whether or not to process a given state element with a call to SimulatorState.isIncluded( int index ).
Examples may be found in the FlavourFilter, which filters based on the type of speck, and the LeaderFilter, which filters based on the leader id of LeaderAware specks.
StateFilters are added to the simulator with a call to StateFilterManager.register( classname ), or by specifying the classname on the command line.