Changes in Q2Java 0.9.6

Two (only two?) big changes this time...the Gamelet and GameletManager classes have been reworked/simplified/DOM-esticated, and a new "status" facility has been added to the q2java.core.Game class.

The updated Gamelet scheme is basically: gamelet constructors now take a DOM document as an argument (instead of just a string), and the DOM document may contain data passed to the GameletManager.addGamelet() method and/or data stored in a file with the same name as the gamelet classfile except with a .gamelet extension instead of .class.

The GameletManager no longer supports the notion of "dependencies", the gamelets should work that sort of thing out amongst themselves. The existing BIG gamelets used to be dependant on the q2java.baseq2.BaseQ2 gamelet, but PITA situation was resolved by splitting the "gamelet" part of BaseQ2 off into a new gamelet called "q2java.baseq2.Deathmatch", and what was left over was no longer a gamelet, but instead is now a support class that -real- gamelets like CTF, Paranoia, BountyHunters, and the new Deathmatch could call upon.

Gamelets no longer have an isLevelChangeRequired() method, or an init() method that they can have called when a level starts - instead they should just register as GameStatusListeners and find out about level starts the normal way. As far as waiting for a level change to unload goes, they can be marked that way with a <option name="wait for level change to unload"> tag in the corresponding .gamelet file. A <option name="avoid multiple instances"> tag marks gamelets that should not be loaded multiple times.

Because of the Gamelet and Gamelet manager changes, all the gamelets included in q2java had to be updated to fit in the new scheme...I won't bother listing each one individually.

The new "status" scheme is basically: the Game class creates a DOM Document containing just (in XML notation) <status game="Quake2" ip="a.b.c.d" port="xxx" map="yyy"/> and updates the mapname as necessary. This document can be fetched by calling Game.getDocument("q2java.status"), and updated by any interested code. If the status document is updated, Game.notifyDocumentChanged("q2java.status") should be called, so a GameStatusEvent.GAME_DOCUMENT_CHANGED event will be fired off and interested objects will learn about the change.

That may not sound very interesting, but that's what makes it so powerful. It's completely up to gamelet code to decide what to put in the status document, and what to do when it's updated. To see the status document feature in action, load the barryp.status.PlayerStatus gamelet to have the status document updated to show connected players, barryp.status.GameletStatus to have the doc updated to show loaded gamelets, and barryp.status.WriteXML to write the document out to a file whenever it changes.

Other gamelets may be written to maintain additional or different info, and do other things with the document when it changes, such as fire a copy off to some sort of master server through UDP or TCP (as a hypothetical example).

Changes in Java Classes
q2java.baseq2.BaseQ2
Change Barry | BaseQ2 is no longer a gamelet, but instead is now a support class used by gamelets such as CTF, Paranoia, BountyHunters and the new Deathmatch to help maintain the environment used by the various BaseQ2 spawn items.
Removed Barry | Code related to the CorpseQueue and player motion CVars such as run_roll and bob_pitch moved to the Player class.
q2java.baseq2.Deathmatch
Added Barry | New gamelet that provides a generic deathmatch - basically the gamelet part of the old q2java.baseq2.BaseQ2
q2java.baseq2.Player
Added Pete | Static addAll(XXX)Listener() methods allow code to easily register with -all- players (both current and future) rather than having to register with each one separately and also watch for new players to register with.
Added Barry | Code related to the CorpseQueue and player motion CVars such as run_roll and bob_pitch moved here from the BaseQ2 class.
Change Barry | [add|remove]InventoryListener() renamed to [add|remove]PlayerInventoryListener() for consistency with the new [add|remove]AllPlayerInventoryListener() methods.
Change Barry | cmd_giveall() disabled since it relied on a "cheating" property of the old BaseQ2 gamelet that I didn't bother recreating.
q2java.baseq2.gui.PlayerMenu
Change CheezHankrn | The menu is now closed -before- executing the selected command.
q2java.core.BasicServerCommands
Added Barry | new commands "set xxx.yyy zzz" and "get xxx.yyy" to access the "yyy" property of a gamelet named "xxx" and either set it to "zzz" or print the current value - using reflection to call "setYyy(String s)" and "String getYyy()" methods in that particular gamelet's class.
q2java.core.DefaultClassFactory
Removed Barry | loadGamelet() method was not needed anymore.
q2java.core.Game
Removed Barry | Everything related to GameletEventSupport, including adding and removing Gamelet listeners moved to the GameletManager class.
Added Barry | getDocument(String docName) allows code to get a hold of a named document the Game class is holding onto. Currently there are two documents: "q2java.level" and "q2java.status" - perhaps there will be more someday, or there will be support for gamelets making up their own documents and having the Game class hold on to them.
Change Barry | getLevelDocument() deprecated in favor of the more general getDocument() method, specifically: getDocument("q2java.level");
Added Barry | A new document, meant to contain the status of the game is created by this class, and accessible to other code by calling getDocument("q2java.status"); Currently, the Game class just updates the document to show the name of the current map, ip address and port of the server. It's up to gamelets to supply any other information to the document.
Added Barry | a notifyDocumentChanged(String docName) method can be called to fire off a GameEvent.GAME_DOCUMENT_CHANGED event so other objects can be notified that a document such as the status document has changed.
Change Barry | addPackagePath() removePackagePath() now keep track of how many times a particular package is added and removed, and only -really- removes it when it has been removed as many times as it's been added.
Change Barry | rethinkPlayerClass() now deals directly with switching player classes rather than call the old Gamelet.grabPlayers() and Gamelet.releasePlayers()
Change Leigh | startLevel() now only cleans up after the old level if there really -was- an old level (that is...it doesn't purge ServerFrame listeners and such when the first level is starting)
q2java.core.GameClassFactory
Removed Barry | loadGamelet() method was not needed anymore.
q2java.core.Gamelet
Change Barry | Gamelet constructors now take a DOM Document instead of a String as a parameter. The info in the document is hopefully something useful to the subclasses of Gamelet, but no particular structure is expected by this class.
Change Barry | getGameletDependencies() is now a deprecated final class - so any existing code that overrides this method will be flagged in error. The whole notion of "dependencies" handled by the GameletManager has been tossed out, so no gamelets should be doing this anymore. This method will eventually be removed alltogether - but was just left in for now to help flag old code that needs updating.
Added Barry | getGameletDocument() allows access to the document passed to the gamelet's constructor.
Change Barry | getGameletName() is now deprecated and final - since the notion of what name a gamelet has is really a property of the gamelet manager, and the equivalent replacement code is now: Game.getGameletManager().getGameletName(xxx);
Removed Barry | getPackageName() was not being used, and was pretty trivial anyhow.
Removed Barry | grabPlayers() releasePlayers() now handled by the Game class.
Change Barry | init() is now a deprecated final class - so any existing code that overrides this method will be flagged in error. Gamelets should go ahead an initialize in their constructor, and if they need to wait for a level to start before doing something, they can register as a GameStatusLister and find out the same way as everybody else. This method will eventually be removed alltogether - but was just left in for now to help flag old code that needs updating.
Removed Barry | isInitialized(), markInitialized() no longer needed, since Gamelets don't have an init() method anymore.
Change Barry | isLevelChangeRequired() is now deprecated and final - since it's up to gamelets themselves to deal with situations where they need to wait for a level change before becoming active. If gamelets wish to wait for a level change before unloading, that can be specified in the gamelet info Document with the element <option name="wait for level change to unload">
Removed Barry | isUnloading(), markUnloading() is not really a property of a gamelet, but rather part of the inner workings of a GameletManager.
q2java.core.GameletManager
Added Barry | addGamelet(Element e, boolean unloadAtEndLevel) can load a gamelet based on XML/DOM info, and optionally mark it as needing to be unloaded when the level is over (great for gamelets associated with a particular map)
Change Barry | addGamelet(String classname, String name) replaced with addGamelet(String, String, Element, boolean) to allow an optional DOM element (may be null) and a flag specifying whether the gamelet should automatically unload at the next level end. Also, the code now checks if specified gamelet name is already taken, and if so, adds some number to it to ensure that all gamelets have unique names (so if you tried to add a gamelet with the name "foo", and there was already one with that name, it would try "foo2") Lastly, it looks for a <option name="avoid multiple instances"> tag and refuses to load a gamelet if an instance of that class already is loaded.
Added Barry | [add|remove]GameletListener() moved here from the Game class.
Change Barry | getGamelet(String) extended to also accept classnames (which in turn makes it possible for svcmds like "sv removegamelet q2java.ctf.CTF" to function). Basically if the string contains a period, it's treated as a classname, otherwise it's assumed to be a gamelet name.
Added Barry | getGameletName(Gamelet g) for looking up what name the GameletManager has associated with a given gamelet.
Change Barry | getGamelets() now returns an array instead of a Enumeration (because of the changes to the way GameletManager kept track of gamelets internally)
Added Barry | isUnloadingAtLevelChange(Gamelet g) to find out if the GameletManager is planning on tossing a given gamelet on the next level end.
Change Barry | loadCommandLineGamelets() renamed to loadStartupGamelets() (better describes what it does, since it handles more than just the command-line).
Added Barry | loadStartupGamelets() looks for a file specified in the CVar "q2java_startup" (default value "q2java.startup") and if found, parses that file as an XML file and looks for <gamelet class="xxx"/< tags specifying gamelets to load and <cvar name="xxx" value="yyy"/> tags specifying cvars to set.
Change Barry | If no gamelets are specified on the command line, or in the q2java_startup file, or in the System properties under q2java.gamelet.xxx, then the default gamelet that's loaded is q2java.baseq2.Deathmatch (used to be q2java.baseq2.BaseQ2).
q2java.core.XMLTools
Added Barry | parseParams() methods for convenience, will examine a DOM Element looking for <param name="xxx" value="yyy"> tags, and if found, will try calling setXxx(yyy) on the specified object, optionally only within a given Class context (so a superclass doesn't inadvertantly initialize fields in a subclass when the subclass's constructor may not have been called yet)
q2java.core.event.GameletEvent
Change Barry | The constant GAMELET_REMOVED was changed to GAMELET_UNLOADING, and is now called -before- a gamelet is removed, rather than after..that way listeners have a chance to talk to the gamelet before it's gone.
Added Barry | GAMELET_LOADING event fired -before- a gamelet is created. The DOM document that will be passed to the gamelet is available through the event, so listeners can find out about what the GameletManager is proposing to load (and possibly throw an exception)
Removed Barry | GameletEvent(int, String) was not being used.
q2java.core.event.GameStatusEvent
Change Barry | GAME_BUILD_DOCUMENT event renamed to GAME_BUILD_LEVEL_DOCUMENT for clarity.
Added Barry | GAME_DOCUMENT_CHANGED event added to notify listeners that one of the game documents such as "q2java.status" has changed.
Added Barry | getDocumentName() to find out what document in a GAME_DOCUMENT_CHANGED event has changed.
q2java.core.event.GameStatusSupport
Change Barry | fireEvent() is now re-entrant, so a listener in the middle of responding to a GameStatusEvent such as GAME_INIT may fire off another GameStatusEvent such as GAME_DOCUMENT_CHANGED.
q2java.ctf.CTF
Change Barry | updated to work in new Gamelet scheme, and to handle linking to the q2java.baseq2.BaseQ2 object (which is no longer a gamelet) itself.
Change Barry | playerThink() no longer is aware of the grappling hook - the hook itself is now smart enough to register as a PlayerMoveListener and do what it needs when a PlayerMoveEvent is fired from inside q2java.baseq2.Player.playerThink().
Removed Barry | teleport() method that overrode q2java.baseq2.Player.teleport() no longer needed since the grappling hook is smart enough to listen for PlayerStateEvent.PLAYER_TELEPORTED and reset itself as necessary.

Q2Java homepage