From: dazzt
Date: 2002-03-25 01:02:53
Here are some technical details about the implementation that I have been thinking about.
Since the server architecture is distributed by nature, different entities (entity = player/item/object/weather daemon/whatever) may reside on physically different servers. This means that there cannot be any direct function calls between objects; they must communicate using messages.
Usually, code that works using message passing is asynchronous. To perform a set of actions sequentially, the programmer must build a state machine which really sucks big time :) (see below). Thus it would be nice if the scripting
language could have multiple different scripts running at a time. In practice this means that if we need to wait for a specific message to arrive in a script, the script execution is suspended until the message has really arrived. Meanwhile,
other scripts are free to run. This would hide the asynchronous nature of the underlying communication (messages). Instead of programming huge constructs and
twiddling with session states like this:
loop \t\twait for message \t\tif message == login \t\t create new session for user \t\t store login name to session \t\t send session id back to client \t\t send password query to client \t\telse if message == password \t\t get session id from message \t\t get login from session \t\t check if password is correct for login \t\t send loggedin to client \t end loopthe programmer could program it synchronously, like this:
\t wait for login message \t name = message.login \t send password query to client \t wait for password message from the same client \t password = message.password \t check if password is correct \t send loggedin to client
Possible solutions for the scripting language would be Lua and Stackless Python. Personally, I'd prefer Python since it is more fully-featured language than Lua, but unfortunately the stackless version is still a bit under development.
Server objects
All server objects must be scriptable. A script must be able to:
- Change object's state \t - State can include e.g. tree positions for a forest, 3d model and location for an item, ... - Send messages to other objects - Receive messages from other objects
Initially, no scripts are running. Scripts will be called when various things happen, including
- A specific amount of time has passed (a timer) - A message destined to the object is received - When another object comes to the vicinity of the object - When another object interacts with the object (a player picks it up, ...)
Each object may have one script running at a time. We must create a small scheduler for objects that stores incoming messages and delays their delivery until the script has finished its current operation.
A script: (nuuh) (do stuff) message y arrives, script is running so it's queued (do more stuff) (twiddle with kiukku) wait for message x \t(waits) message x arrives, since script is waiting for it it's delivered immediately (do things with message x) message z arrives, script is running so it's queued wait for message y message y is already in the queue so it's delivered immediately (do things with message y) (end)
After the script has ended, z is in the queue so it will be delivered
Storing server objects to disk ('serialization')
Each object must define what data will be saved to disk. The format of this data must be defined so that we can guarantee the validity of objects at all times. If the format of the data must be modified in order to handle some new functionality, a new version of the data definition must be created. To handle data with differing versions, it's also required to create a function that will upgrade old data to a more recent version.
If we decide to use python, it would be possible to (atleast initially) just dump the contents of each python object to disk since python has serialization features ('pickling') builtä-in. The only requirement from the programmer would be to supply a function that defines the version of the object, and a function to upgrade an object to a newer version.
To illustrate in python, let's assume we have a Player object that has three attributes: name, strength and intelligence. Later on, we decide that we don't need the intelligence-attribute at all, so we create a new version.
class Player(Object): def __init__(): self.setVersion(2) self.name = '' self.strength = 0 self.intelligence = 0 def upgradeVersion(oldversion): if oldversion == 1: del self.intelligence # Get rid of the attribute self.setVersion(oldversion + 1)
Messaging
Messages are essentially similar versionable containers. The format of messages should be strictly defined so that client and server are guaranteed to speak the same language.
class WeatherQueryReplyMessage(Message): def __init__(): self.setVersion(1) self.addField('rain', S_FLOAT) self.addField('temperature', S_FLOAT) # Later somewhere else .. def onMessage(msg): if type(msg) == type(WeatherQueryReplyMessage): setTemperature(msg.temperature)