eRaz0r wrote:
I think you're talking two different (overlapping) APIs though : the "authenticating client" and the "trusted client". The first communicates via a request/response kind of protocol, and the second can operate like a server-extension : right on the data in the same JVM. Not sure if this is a meaningful distinction, but I do feel one emerging.
I have no predetermined focus for the discussion. I'm simply responding to the queries and/or ideas you've put out. But I agree that there are probably two different targets placed in our sights.
How about we pick one and we'll develop the API needed for that extension to be written? In order to avoid deep copies for now, we'll assume a built-in extension -- not something written as a service.
How about a game-specific character sheet? An extension that has all the functionality of what is currently provided by one of the FWs, but which is implemented inside MT to make it faster and provide some options that are not accessible to MTscript...?
Base assumption: All data retrieved from the server has a sequence number (aka generation number, aka version number) attached to it. When an extension wants to make a change, it sends the version number along with the new data. If the server sees an old version number (i.e. the object has been updated in the mean time), the request by the extension is rejected. Somehow the extension author will need to program for this. No idea on how that should be done, but it should be decided earlier instead of later as exceptions need to be considered while the interfaces are developed.
1/ Maps
1A/ Get a map.For this target we'll retrieve the map object and its attendant data structure. We will assume that all of the information we need is available (which won't be much since we're just building a charsheet).
1B/ Get a specific view of the map.You propose getMapViewAsToken() and I'm fine with that. Out to what distance should the vision be taken? Should the vision be with or without VBL? (Such as a Player view vs. a GM view.) What about fog -- layer it on top or leave it out? Should a grid be overlaid on top of the resulting image? Perhaps each of those options is passed as a parameter; an array of VisionOption objects or some such?
(Implementation note: if the return value is an instance of JLayeredPane, then each layer could contain the various elements. For example, the fog layer could be the 60% opaque image and the caller could decide what to do with it. For the VBL layer, a GM sees the equivalent of soft fog while a Player seeks a blacked out area. The caller chooses the layers and then has the image it wants.)
I think dimensions should be in "grid space". For gridless maps that means 1 grid cell == 1 pixel.
2/ Tokens
2A/ Information to expose.All properties of the token are exposed: size, shape, top_down vs. portrait, images, properties, macros, speech text, and so on. Changing the location of a token means changing the Location property. However, retrieving a token only retrieves a token reference. Specific properties have to be individually requested. This limits the amount of network traffic in situations where the extension is remote, but also reduces the likelihood of conflicting changes -- something MT suffers from right now. (I change a token at the same time someone else changes the token. Whose changes stick? See
base assumption, above.)
2B/ Adding new tokens.Tokens are never added to the map by the extension. Instead, it asks the server to create an empty token. That token is returned to the extension to be filled in by them. A newly created token is "locked" and is not accessible elsewhere in the program until the extension defines a Location for the token.
2C/ Activating token macros.Macros attached to tokens can be activated by an extension. The extension provides a "variable context," including the "impersonated token" and/or "selected token(s)," and passes this context as a parameter. The context includes predefined attributes for the default output destination ("GM only", "self only", "GM+self", and so on), default font characteristics, and ...?
3/ Events
3A/ Event categories.Events might be physical actions such as simple real-time alarms, user interface actions, or network actions. They may also be abstract such as in-game alarms, tokens being moved into illegal locations, or a chat message either inbound or outbound.
The Java model of using Listeners is well-established and will be familiar to Java programmers. Based on the well-defined Observer pattern, they also scale reasonably well since the implementation can execute all listeners in sequence or in parallel. All listeners have a name and registering is done by specifying the name and an optional group of components that the listener applies to (pass
null for all objects). Listeners can be ADDED, REMOVED, IGNORED, BLOCKED, and UNBLOCKED. Ignoring a listener means that events destined for that listener are dropped as they are received. (This is like removing the listener, but is likely to have performance advantages.) Blocking a listener causes the event to be queued for later delivery, and unblocking it allows them to actually be delivered.
Question: do extensions run on their own thread? If yes, should events being delivered run within the thread of the extension or on the Swing event queue? There are pros and cons both ways.
So.............................................................................................
What methods does our hypothetical charsheet extension need? Here's a rough draft of how I see the code being laid out. Comments welcome!
Code:
class CharsheetExtension extends Extension {
public boolean install() {
addListener("selectionChange", null, new Extension.callbackObject());
return true;
}
public void uninstall() {
removeListener("selectionChange", null);
}
class callbackObject extends Callback {
public callback() {
Token t = getSelectedTokens();
if (t == null || t.length != 1)
return; Token.Attribute[] props = new Token.Attribute[]
{ "name", "location", "skills.*", "size" };
Map<Token.Attribute, Token.Value> values = t.getAttributes(props);
}
}
Depending on how the thread model is going to work, the callback object's method may be inside of some kind of thread synchronization block; a read-mostly lock seems most likely, but how to decide in an automated manner which objects are being locked? Is each separate event going to lock different kinds of objects? (Just making this up, but...) So a selectionChange event locks the newly selected token, unless it's on the Object layer when it locks all tokens within 5 grid cells? (I can't envision that being necessary, but I do know enough to know that I don't know enough to know what isn't possible. I think.

)