[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Troll] Java Port -- Libraries
[Aside]
This is the first in what is probably a long series of messages about the
Java Port and the decisions I made/am making about it. I'm just generally
looking for feedback here; any possible ideas anyone has are welcome. This
stuff is not game design stuff, BTW, but rather gory details of the Java
implementation and the potential ramifications for the C++
version. Without further ado...
Libraries
=========
Dennis and I had a long discussion about the desired goals of the libraries
under the C++ version of TrollBridge. The difficulty that arose was seeing
how to implement the same general idea under Java, where I do not have
function pointers. Let me explain:
Typically under C++, the loading of libraries works as follows:
* TrollGame constructed
* TrollGame::loadLibrary("./troll.dll");
* loadLibrary calls TrollDllInit
* TrollDllInit makes callbacks to the TrollGame, adding the needed items as
function pointers in the array of monsters, items, etc.
Java, of course, does not have function pointers. Instead, I've decided to
go with the use of Java "Method" objects from the Reflection API. (Refer
to the Java Tutorial on Reflection, if you care.) I also need to add some
interfaces to make the calls easier. In Java the loading will work like this:
* TrollGame constructed
* TrollGame.loadLibrary("DefaultTrollLibrary"); // Or other library
* loadLibrary instantiates bootstrapper class, DefaultTrollLibrary
* DefaultTrollLibrary::init makes callbacks to the TrollGame, adding
TrollMonsterConstructors, TrollItemConstructors, etc., which are wrappers
for "Methods."
----
package com.identicalsoftware.trollbridge;
public interface TrollLibrary {
static void init () throws TrollLibraryError;
}
----
Each library needs a "bootstrap class" to load the actual library. This
class basically holds the "TrollDllInit" method, now called "Init".
----
package com.identicalsoftware.trollbridge.gamedll;
import com.identicalsoftware.trollbridge.*;
public class DefaultTrollLibrary implements TrollLibrary
{
public static void init () throws TrollLibraryError
{
try
{
Class temp;
temp = Class.forName(
"com.identicalsoftware.trollbridge.gamedll.TrollPerson");
game.addMonsterType(0,
new TrollMonsterConstructor(temp.getMethod("createGrayTroll", null)));
temp = Class.forName(
"com.identicalsoftware.trollbridge.gamedll.TrollDoor");
game.addMonsterType(1,
new TrollMonsterConstructor(temp.getMethod("createNorthDoor", null)));
game.addMonsterType(2,
new TrollMonsterConstructor(temp.getMethod("createSouthDoor", null)));
game.addMonsterType(3,
new TrollMonsterConstructor(temp.getMethod("createWestDoor", null)));
game.addMonsterType(4,
new TrollMonsterConstructor(temp.getMethod("createEastDoor", null)));
/* ... */
return;
}
catch (Exception e)
{
throw new TrollLibraryException(e.getMessage());
}
}
}
----
Here the bootstrapper simply uses Relection to create the "Method" objects
which point to the static factory methods that create the actual objects in
question. ("Factory Methods" just mean methods that create a new object
instance, and return that instance.) It is important that these methods be
static, BTW.
TrollMonsterConstructor is simply a wrapper for the Method which calls the
method and casts the result to the appropriate type. It is a array of
these that is stored in the TrollGame class. I won't bore you with the
implementation.
The only thing that remains to be examined is the TrollGame::loadLibrary
function, which looks like this:
----
/** Loads a library and runs init. <P>
* @param bootstrap_name full name of the library bootstraping class
* @throws TrollLibraryError if there is a problem loading the library
*/
protected void loadLibrary (String bootstrap_name) throws TrollLibraryError
{
try
{
TrollLibrary tl = (TrollLibrary) Class.forName(bootstrap_name)
.newInstance();
if (tl == null)
throw new TrollLibraryError(bootstrap_name + " is not a library!");
tl.init(this);
return;
}
catch (InstantiationException ie)
{
throw new TrollLibraryError(ie.getMessage());
}
catch (IllegalAccessException iae)
{
throw new TrollLibraryError(iae.getMessage());
}
}
----
We use Reflection, again, to load the class file for the library, and then
call init if everything works out.
The Bottom Line
===============
The basic frameworks for libraries under the Java port is presented
above. Currently, the code examines the arguments to the program, and
interprets them as the names of bootstrap classes for various libraries,
and attempts to load them; if no arguments are specified, the default
library is loaded. The payoff of using reflection is that the libraries
can be passed as command line arguments to the program, which ideally, is
what we want.
To implement a new library, follow these simple steps:
1) Write your monster classes, etc., as normal.
2) Decide on where they will be in the main arrays, and whether your
library will require (or be compatible with) other libraries.
3) Implement the bootstrapper class ("implements TrollLibrary") as
suggested above.
4) Pass the name of the bootstrapper class to the program when you run it.
The impact here is that for the implementor of the library, there is
minimal extra work to do this in Java, even sans function pointers. The
Java code is in the same style as the C++ code, with the addition of the
bootstrapper class.
Comments, questions, explosions?
:)
Thanks all,
Kenn
--------------------------------------------------------------
IMPORTANT: The below information has changed! Please use the
email addresses below to contact me.
------------------------------+-------------------------------
Kenneth W. Flynn | flynnk@astro.umd.edu
Graduate Fellow, Astronomy | flynn@nrl.itd.navy.mil
UMD (College Park) | And 10 or so others...
------------------------------+-------------------------------