Back to Majik 3D MMORPG information archive main page.
forum index

Scripting - technical thoughts

Message 1342

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 loop
		
the 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)
		

Message 1362

From: Archantes
Date: 2002-03-25 20:28:15
In-Reply-To: 1342


So, let me think.. as I see (?) this system enables us also to actually add new parts of world by scripting, right?

But yes, in general this seems to be quite working. Programming synchronously is somehow better IMO.

Message 1386

From: raeky
Date: 2002-03-29 00:37:18
In-Reply-To: 1342


so where talking about kinda like a simplified version of c for majik specific codeing. kinda like quake c.

Message 1624

From: yorkaturr
Date: 2002-06-11 16:27:01
In-Reply-To: 1342


As you may know, briefly after I started to experiment with Lua, I've made some progress in this sector.

In short, I made a C++ wrapper for Lua which enables us to run scripts from the C++ level and implemented a system for "script events", which means that C++ objects register themselves as handlers of certain events by associating themselves with an event identifer (shared with Lua and C++) into a "master script handler", and Lua script callbacks to our C++ level are redirected by that "master script handler" to the aforementioned registered C++ objects based on the event identifier provided by the script as a callback parameter with any arbitrary data that the script wants to pass to us, also as parameters.

For those who didn't understand what that meant, we now have an ability to do Lua scripts in our Majik server, which means that we can start building some components into the "mudlib" by writing scripts.

I am well aware that the system I coded now will eventually become obsolete, but it is a good start nonetheless.

I would like to decide on specifics such as how a character in the world is defined in the scripts? How many script threads should be running at a time, and what would determine that? How to integrate this into our systems so that the world coordinates are known?

...

Message 1363

From: dazzt
Date: 2002-03-25 22:21:22
In-Reply-To: 1362


Well, you're underestimating a bit here. The purpose is to use scripting to build the whole world. Scripted objects do have the advantage of being able to be updated on-the-fly, so avoiding C++ -code will make the game more maintainable.

Unfortunately there are issues such as performance/memory-usage that may (will?) prevent us from doing everything in a script language. However, since there is only one method of interaction (messages) between scripted objects, the pieces implemented in C++ can easily communicate with others. Also, at least in the case of python, it's fairly easy to create new python modules using C/C++ if we need e.g. some algorithm implemented as efficiently as possible, but don't want to make the entire object in C++.


Message 1364

From: Nahl_Shadore
Date: 2002-03-26 04:02:38
In-Reply-To: 1363


heh, I knew I took computer science for a reason

as far as I can tell, the main structure is the same as general C++ object-oriented programming (classes, etc)
the whole format, however, seems to be enhanced
ah well, one more language to learn ;)

Message 1625

From: yorkaturr
Date: 2002-06-11 16:41:14
In-Reply-To: 1624


Well, how a character in the world is defined in the scripts actually would be quite trivial, because you don't keep the character data in store all the time. Whenever a character, or any object, is needed in Lua scripts, you just pass Lua the relevant data with some identifier (I'm thinking of an identifier system similar to MudOS ... every object has an identifier).

Maybe this turned out more useful than I realized...