RolePlayingDesign: Difference between revisions
No edit summary |
imported>Hendrik Brummermann No edit summary |
||
| (186 intermediate revisions by 5 users not shown) | |||
| Line 1: | Line 1: | ||
{{Navigation for Marauroa Top|Internals}} |
|||
<?plugin CreateToc with_toclink||=1 ?> |
|||
{{Navigation for Marauroa Developers}} |
|||
!!Actions and Objects |
|||
Last updated: 2003/10/07 |
|||
This is possibly the most complex part of all the middleware that makes up Arianne.<br> |
|||
The whole Marauroa system is managed by two main entities, RPAction and RPObject |
|||
Role Playing Design is the determining factor on how ''easy'' is to create a new game for Arianne. We had to choose easing the creation of turn time limited based games. Arianne will work better with this kind of games (also known as realtime games). |
|||
!Actions |
|||
To express the willing of a client to do something it must send the server a MessageC2SAction message. |
|||
Role Playing Design tries to be generic and game agnostic (independant of the game being made). |
|||
An action is composed of several attributes, an attributed is similar to a variable that has a name and contains a value. |
|||
The very basic idea behind RPManager is: |
|||
<pre> |
|||
forever |
|||
{ |
|||
Execute Actions |
|||
Send Perceptions |
|||
Wait for next turn |
|||
} |
|||
</pre> |
|||
To achieve this we use several classes: |
|||
There are optional and mandatory attributes. If a mandatory attribute is not found, the message is skipped by the RPServerManager. |
|||
* RPManager is coded in Marauroa and doesn't need to be modified. |
|||
* IRPRuleProcessor is the interface that you need to modify in order to personalize the actions that your game will execute. |
|||
* RPWorld is the class that you need to extend inorder to implement the onInit and onFinish methods which personalize what happens when you initialise the server and what happens when you close the server. |
|||
* IRPZone is an interface that you ''could'' implement if you wanted to achive the highest personalization possible of the engine, however, I would use MarauroaRPZone instead as that uses our great Delta<sup>2</sup> feature. |
|||
Mandatory Actions Attributes are action_id and type. |
|||
=RPManager= |
|||
The action_id is used to identify the action when a resulting response comes in a perception |
|||
The goal of the RP Manager is to handle the RP of the game. This means: |
|||
* run RPActions from clients |
|||
* manage RPWorld |
|||
* control triggers for events |
|||
* control AI |
|||
This is a HUGE task that is very complex. Hence we split this behavior into smaller subclasses. |
|||
Optional Actions Attributes: (Read "Actions Explained" for more details.) |
|||
RPManager exposes a simple interface to the GameManager: |
|||
!Objects |
|||
* <i>addRPAction</i> <br> This function queues an action for a particular player to be executed on the next turn. |
|||
The containers of information of the whole Marauroa server are RPObjects. An object is composed of several attributes, an attribute is similar to a variable that has a name and contains a value and also it is composed of Slots. A Slot is a container or array of containers that the object has to host other objects inside it. |
|||
* <i>getRPObject</i> <br> This is an interface to manage RPWorld to ease the aquisition of the RPObject when exiting the game. |
|||
* <i>onInit Player</i> |
|||
* <i>onExit Player</i><br> These are callback functions that are used by GameManager to notify that a player has entered or exited the game. |
|||
* <i>transferContent</i><br> is a callback function that is called by RPRuleProcessor to stream content to players. |
|||
The main flow of RPManager is: |
|||
Mandatory Object Attributes: id |
|||
<pre> |
|||
forever |
|||
{ |
|||
Procced through every action in this turn |
|||
{ |
|||
rpRuleProcessor executes action |
|||
} |
|||
Build Perception |
|||
Optional Object Attributes: (Read "Objects Explained" for more details.) |
|||
Remove timed out players |
|||
Wait for Turn completion. |
|||
Go to Next Turn |
|||
} |
|||
</pre> |
|||
RPScheduler is the class that handles actions to be queued for each player. All the complexity of Action management should be handled here. |
|||
!! Object Explained |
|||
Last updated: 2004/04/27 |
|||
RPRuleProcessor is a wrapper class for actions code and initialization and exit code. <br> |
|||
Objects are the basic Marauroa data structure, and you should already be familiarized with it because you read the Actions and Object document. |
|||
All the actions code MUST be here.<br> |
|||
By implementing RPRuleProcessor you personalize Marauroa to the game you want to make. However, keep in mind that you are ''limited'' to a realtime style game. |
|||
=Objects and Actions= |
|||
Marauroa engine only enforce that objects has two attributes: |
|||
The whole Marauroa system is managed by two main entities, RPAction and RPObject. There are also several helper classes like Attributes, RPSlot and RPClass |
|||
- id |
|||
- type |
|||
==Attributes== |
|||
id is an unique identification for the Object and type is the type of the object aka class, so that you can share attributes for all the instances of the class. |
|||
Attributes is a collection of pairs of values in the form name-value. |
|||
We can store almost any basic type in a Attribute object: |
|||
* strings |
|||
* integer |
|||
* floats |
|||
* boolean |
|||
We can't store structures in the attribute, but you can convert groups of data to a string and store it as a string. Marauroa provides helper methods for this. |
|||
Also engine give special treatment to two types of attributes: |
|||
- Attributes that begin with ! are completely hidden for all the users but the owner of the object. |
|||
- Attributes that begin with # are completely hidden for all the users. |
|||
==Objects== |
|||
NOTE: This document relates to an early specification of Gladiators and is no longer relevant as it refers to specification, but can help you to know how to make the basic steps to define a game. |
|||
All information in the Marauroa server is contained in RPObjects. An object is composed of several attributes (an attribute is similar to a variable in that it has a name and contains a value) and Slots. A Slot is a container or array of containers belonging to an object, and are used to host (store) other objects inside them. |
|||
Mandatory Object Attributes: id, type and zoneid |
|||
So let's point the multiple objects that we are going to have in Marauroa and their attributes and slots. Most object share a few attributes. They are: |
|||
- object_id |
|||
- object_type |
|||
- object_look |
|||
- object_position |
|||
id is an unique identification for the Object, zoneid is the identification for the zone where the object resides and type is the type of the object aka class, so that you can share attributes for all the instances of the class. |
|||
The object_id attribute define the unique identifier of the object in the world. The object_type define the type of object it is, for example: human. The object_look would point to the model used to represent this object. The object_position determine where the object is in the world using a X, Y basis Again, we could add the Z value, but it is totally fake, we calculate Z for adjusting character to terrain. |
|||
An id is only unique inside the zone which contains that object. |
|||
Object Avatar The avatar is the entity in Marauroa that represent our character and it is the way of handling everything in that world. This object must have also the next set of attributes: |
|||
- object_name |
|||
NOTE: The engine provided two special types of attributes: |
|||
Also add too the RP attributes that are still to be defined. The object_name is just the name of the character this object is representing. The object has the next slots: |
|||
- Attributes that begin with ! are completely hidden from all other users except the owner of the object. |
|||
- left_hand |
|||
- Attributes that begin with # are completely hidden for all users. |
|||
- right_hand |
|||
- backpack |
|||
===Classes of Objects Explained=== |
|||
Object Stone A stone is just a little rock used for testing get, release and exchange actions. |
|||
Classes of Objects are the basic way of structuring Marauroa data structures. |
|||
The type of an attribute of a given object must be equal to a RPClass name of the type class you wish to use. |
|||
The class defines the type of the attribute, its visibility and assigns it an internal code that is used to speed up searchs and save bandwidth. You can base a class on another, this feature is known as inheritance (a new class is create from a class that already exists and takes all the original classes methods and data and extends it). |
|||
Object Torch A torch is a small piece of wood in form of stick that is used to light on the near area. This object must have the next set of attributes: |
|||
- object_status |
|||
The data types available are: |
|||
The object_status define if the torch is ON or OFF. |
|||
* Strings |
|||
* Short strings ( up to 255 bytes ) |
|||
* Integers ( 4 bytes ) |
|||
* Shorts ( 2 bytes ) |
|||
* Byte ( 1 byte ) |
|||
* Flag ( it is a binary attribute ) |
|||
Attributes can be visible which means clients see them when they change, or invisible if clients can't see them. |
|||
Object Chest A wooden box with a lock that can be open or closed and can contain items inside. This object must have the next set of attributes: |
|||
- object_status |
|||
- object_forkey |
|||
===Slots=== |
|||
The object_status define if the chest is OPEN or CLOSED. The object_forkey define the identifier of the key that opens this lock. The object has the next slots: |
|||
Objects can reside inside other objects much like you have the keys inside your pocket. The goal of Slots is to provide a richer game play while reducing the number of object in the zone. |
|||
- container |
|||
To have objects inside, we need our hoster object to have slots to place them in. One slot can only handle one single object. |
|||
Object Key A small piece of metal that is placed inside the lock to unlock it. This object must have the next set of attributes: |
|||
- object_key |
|||
For example an avatar can have: |
|||
The object_key define the identifier of the key. |
|||
* left hand |
|||
* right hand |
|||
* backpack |
|||
* left pocket |
|||
* right pocket |
|||
and we can store objects in each of these slots. |
|||
!!Object and Slots |
|||
Last updated: 2003/11/28 |
|||
Once an object has been stored inside an objects slot, the only way of accessing the stored object is through the object that contains our stored object. |
|||
As you know Objects can contain inside another object much like you have the keys inside your pocket. The goal of Slots is to provide a richer game play while reducing the number of object in the zone. |
|||
As attributes, slots have two special types: |
|||
To have these objects inside, we need our hoster object to have slots to place them. One slot can only handle one single object. |
|||
* Slots names that start with ! are only sent to owner player. (Hence only seen by the owner) |
|||
* Slots names that start with # are not sent to players. (Invisible to all) |
|||
==Actions== |
|||
For example a avatar can have: |
|||
To express the willingness of a client to do something it must send the server a MessageC2SAction message. |
|||
- left hand |
|||
- right hand |
|||
- backpack |
|||
- left pocket |
|||
- right pocket |
|||
An action is composed of several attributes. (an attribute is similar to a variable in that it has a name and contains a value). |
|||
and we can store objects on these slots. |
|||
There are optional and mandatory attributes. If a mandatory attribute is not found, the message is skipped by the RPServerManager. |
|||
!! Actions Explained |
|||
Last updated: 2003/10/23 |
|||
Mandatory Action Attributes are action_id and type. |
|||
NOTE: This section relates to an early specification of Gladiators and is no longer relevant as it refers to specification, but can help you to know how to make the basic steps to define a game. |
|||
The action_id is used to identify the action when a resulting response comes in a perception |
|||
Arianne Game is based on several actions that are a MUST for the correct development of the game. Mainly these actions are: |
|||
=Perceptions= |
|||
- Talk |
|||
The basic structure for sending world updates to clients is called perceptions. |
|||
- Move |
|||
- Object handling |
|||
- Get |
|||
- Release |
|||
- Use |
|||
- Use with |
|||
- Exchange |
|||
These actions have their own attributes so that they can be useful for the game itself. Let's see these attributes and what each action is intended to be for. As common attributes, every action has the type attribute that express the type of Action that it is, and the sourceid express the object that is casting the action, but this attribute is filled on server rather than in client. |
|||
There are two types of perception: |
|||
Action Talk This action is intended to express what the character wants to say. As that it is sent from client to server. Action has this next set of attributes: |
|||
* Sync perceptions: these are used to synchronize clients with the server world representation. This is the only valid way of knowing world's status. |
|||
- type |
|||
* Delta perception: this is used to send only the changes to the world since the last perception. |
|||
- sourceid |
|||
- talk_text |
|||
Our actual Perception system is called Delta<sup>2</sup>. It is heavily attached to the Marauroa core, so I recommend you to use it :) |
|||
The talk_text attribute is for containing the text that send the client. |
|||
== How Perceptions and Actions work == |
|||
Action Move This action is intended to indicate the willing to move from one point to another point. Action has this set of attributes: |
|||
Actions are sent from the client to the server in order to make the character perform an action. In order for the client to know the result of the action the Server needs to send a reply to the client. How will this be done? |
|||
- type |
|||
- sourceid |
|||
- move_to |
|||
- move_speed |
|||
In a first attempt, we send clients back an action that was the result of their action. However, this made the code really hard because we had to update two different things, perceptions and actions. Instead the solution appears intuitively: Why not join action reply and perceptions. |
|||
The move_to is a Position , in the system the Z is a fake, as there is no technology ready for using a huge world with multiple levels. The move_speed is also a meaning the increase of positions per turns. This action should take care of: |
|||
- Collision detection |
|||
- Path finding |
|||
So the action reply is stored inside each object (that executed the action ) with a set of attributes that determine the action return status and the attributes. This way of doing replies makes it a bit harder on RPManager but it simplifies the creation of new clients a lot. |
|||
Action Get This action is intended to indicate the action of taking one object from floor. Action has this set of attributes: |
|||
- type |
|||
- sourceid |
|||
- get_objectid |
|||
- get_toSlot |
|||
See Actions reply in the Objects documentation to know exactly what is returned. However, keep in mind that the return result depends of each particular game. |
|||
The get_objectid is the RPObject.ID of the object we want to get and the get_toSlot attribute is the Slot of the character that send the action where the object is placed. This action has several limitations: |
|||
- You can't get players |
|||
- The destination slot must be free |
|||
==Delta<sup>2</sup> perception Algorithm== |
|||
Action Release This action is the contrary of Get, and it is used to drop an object from a Slot. Action has this set of attributes: |
|||
The idea behind the DPA is to avoid sending ALL the objects to a client each time, but only those that have been modified. |
|||
- type |
|||
- sourceid |
|||
- release_fromSlot |
|||
- release_objectid |
|||
Imagine that we have 1000 objects, and only O1 and O505 are active objects that are modified each turn. |
|||
The release_fromSlot is the slot that contains the object pointed by release_objectid. The only limitation of this action is that of course the object MUST be there. |
|||
Action Use This action makes the object to perform the expected action from them. This action is a real good candidate for scripting. Action has this set of attributes: |
|||
- type |
|||
- sourceid |
|||
- use_objectid |
|||
The use_objectid attribute point the object to use. By now we will only have one default Use, later we can add extra attributes to the action. |
|||
Action ~UseWith This action is closely related with Use action, the difference is that we use ObjectA with ObjectB so that the state of ObjectA is modified, ObjectB may be modified as well. Action has this set of attributes: |
|||
- type |
|||
- sourceid |
|||
- usewith_objectidA |
|||
- usewith_objectidB |
|||
The usewith_objectidA refers to the Object that is the center of the action and usewith_objectidB is the catalyzer of the action. For example a door and a key respectively. |
|||
Action Exchange This action is use to exchange an object between two characters. Action has this set of attributes: |
|||
- type |
|||
- sourceid |
|||
- exchange_sourceSlot |
|||
- exchange_destObjectid |
|||
- exchange_destSlot |
|||
The exchange_sourceSlot is the slot of the source character that owns one of the objects and the exchange_destObjecid is the other object and exchange_destSlot is the slot where the other object is. The action MUST be executed by both peers on the same terms so they agree and the action is executed. |
|||
Action replies are placed inside Objects and sent to clients as perceptions, so please refer to the Action reply in "Objects Explained" document. |
|||
!! How Perceptions and Actions work |
|||
Last updated: 2004/04/27 |
|||
Actions are sent from client to server in order to make their character to do an action. In order for the client to know the result of the action Server need to send it to client. How? |
|||
On a first try, we used to send client back an action that was the result, but make code really hard because we had to update to different things, perceptions and actions, so the idea appeared intuitively: Why not join action reply and perceptions. |
|||
So the action reply is stored inside each object (that executed the action ) with a set of attributes that determine the action return status and the attributes. This way of working make a bit harder to RPManager but it simplify a lot the creation of new clients. |
|||
See Actions reply in Objects document to know exactly what is returned, but keep in mind that it depends of each particular game. |
|||
!! RPManager |
|||
Last updated: 2003/11/23 |
|||
The goal of RP Manager is to handle the whole RP game. This means mainly: |
|||
- run RPActions from clients |
|||
- manage RPWorld |
|||
- control triggers for events |
|||
- control AI |
|||
As you see this is a HUGE class that is complex. So the idea is to split this behavior into smaller subclasses. |
|||
RPManager provides a simple interface to the ~GameManager for using it: |
|||
- addRPAction |
|||
- addRPObject |
|||
- removeRPObject |
|||
- hasRPObject |
|||
addRPAction simply queues an action for that player to be executed on the next turn. |
|||
addRPObject, removeRPObject and hasRPObject is a interface to manage RPWorld. |
|||
The main outline of RPManager could be: |
|||
<verbatim> |
|||
forever |
|||
{ |
|||
Procced through every action in this turn |
|||
Build Perception |
|||
Remove timed out players |
|||
Wait for Turn completion. |
|||
Go to Next Turn |
|||
} |
|||
</verbatim> |
|||
As this part of Marauroa is subject still to development, there are still not many details. |
|||
RPScheduler is the class that handles actions to be queued for each player. All the complexity of Action management should be handled here. |
|||
~RuleProcessor is a wrapper class for hide actions code. All the actions code MUST be here, this class also acts as a Action code loader, as some actions are not part of Marauroa, but scripts. |
|||
!! Delta perception Algorithm |
|||
Last updated: 2004/04/27 |
|||
The main idea behind the DPA is not to send ALL the objects to client, but only those that has been modified. |
|||
Imagine that we have 1000 objects, and only O1 and O505 are active objects that are modified each turn. Ok? |
|||
Traditional method: |
|||
The Traditional method: |
|||
<pre> |
|||
- Get objects that our player should see ( 1000 objects ) |
- Get objects that our player should see ( 1000 objects ) |
||
- Send them to player ( 1000 objects ) |
- Send them to player ( 1000 objects ) |
||
| Line 240: | Line 168: | ||
- Next turn |
- Next turn |
||
... |
... |
||
</pre> |
|||
I hope you see the problem..., we are sending again objects that never changed. |
|||
I hope you see the problem... we are sending objects that haven't changed each turn. |
|||
The delta perception algorithm: |
|||
The delta perception algorithm: |
|||
<pre> |
|||
- Get objects that our player should see ( 1000 objects ) |
- Get objects that our player should see ( 1000 objects ) |
||
- Reduce the list to the modified ones ( 1000 objects ) |
- Reduce the list to the modified ones ( 1000 objects ) |
||
| Line 255: | Line 185: | ||
- Next turn |
- Next turn |
||
... |
... |
||
</pre> |
|||
The next step on delta perception algorithm is pretty clear: delta^2 The idea is to send only what changes of the objects that changed. That why you save even more bandwidth, making perceptions around 20% of the delta perception size. |
|||
The next step of the delta perception algorithm is pretty clear: delta<sup>2</sup> The idea is to send only what changes of the objects that changed. This way we save even more bandwidth, making perceptions around 20% of the original delta perception size. |
|||
The delta^2 algorithm is based on four containers: |
|||
The delta<sup>2</sup> algorithm is based on four containers: |
|||
- List of added objects |
|||
- List of modified added attributes of objects |
|||
- List of modified deleted attributes of objects |
|||
- List of deleted objects |
|||
An area really related to DPA is RPZone |
|||
* List of added objects |
|||
In order to get the Delta perception algorithm to work correctly you have to play with several parameters: |
|||
* List of modified added attributes of objects |
|||
* List of modified deleted attributes of objects |
|||
* List of deleted objects |
|||
An area very related to DPA is RPZone (see lower in this doc) |
|||
- RP Turn Duration |
|||
- Relation between TOTAL perception and DELTA perception |
|||
- Know your game :) |
|||
Well, as you should know, MPEG adds a full frame each X number of frames, so it can be used as synchronization in case the file get corrupted. The idea is that if you fail to continue decompressing data, you can always omit things until the next full frame and then when you synced. The idea here is the same, we send a perception on each turn and every X turns we send a full perception so that clients can synchronize, as UDP is not a secure transport and you can also have new clients joining. |
|||
As you should know, an MPEG video adds a full frame each X number of frames, so it can be used as synchronization in case the file gets corrupted. The idea is that if you fail to continue decompressing data, you can always omit things until the next full frame and then you get synced. The idea here is similar, if we fail to synchronize with server we send it an Out of Sync Message so that server will send a sync perception so that the clients can synchronize. Remember, UDP is not a secure transport. |
|||
So the point is to adjust turn duration and relation so that the maximum time a client is out of sync is around 20-30 seconds. Of course, depending of the type of game you may lower or increase this value. |
|||
To make |
To make perceptions work, it is important to call the modify method in RPZone, so this way objects modified are stored in the modified list. |
||
=Zones and Worlds= |
|||
Worlds in Marauroa can be so big, so huge, that we need to split them in to several pieces. |
|||
Last updated: 2003/11/28 |
|||
Each of these pieces are what we call an RPZone. |
|||
So our world is made of several RPZones that are '''independent''' of each other. |
|||
Objects must be stored somewhere, and we use Zones now to store them. A zone is just a container of Objects. |
|||
To move from one RPZone to another RPZone you have to code the correct behaviour in RPRuleProcessor. Just look at any of our coded examples. |
|||
==RPWorld== |
|||
In order to improve the modifiability of the Marauroa platform we have made RPZone to be an interface so that if you want you can implement it as you think it is more efficient. |
|||
As we have already said, RPWorld stores several RPZones that are independent of each other.<br> |
|||
RPWorld provides onInit and onFinish methods that are called on server initialisation and server finalization to define what to do with the world on these events. There is no default behaviour and you need to extend this class to redefine the behaviour. |
|||
Also it provides methods for adding and getting new RPZones: |
|||
The actual Marauroa RP Zone consists of several data structures: |
|||
* addRPZone |
|||
- a ~HashMap of |
|||
* getRPZone, which can be used with either RPZone.ID or RPObject.ID |
|||
- a List of RPObject |
|||
- a Perception |
|||
Finally it also contains methods for managing RPObjects as: |
|||
The idea is to have already computed in the Zone the perception so saving LOTS of time that would be needed to generate it. All the data structures contain the same objects, but the hashmap is used to fast search of objects using its RPObject.ID, this is the most usual way for locating the object. List is used to improve the time required to build a total perception. And well, we used perception to pre-calculate the delta perception. |
|||
* addRPObject, that need that our RPObject contains a valid RPZone.ID inside its RPObject.ID |
|||
* getRPObject |
|||
* modifyRPObject |
|||
* changeZone that moves one object from the old zone to the new zone and adds a proper valid id. |
|||
At last, RPWorld also contains a method called nextTurn that is called by RPManager to move from one turn to the next turn. It resets the delta<sup>2</sup> data. |
|||
Actually the perception is the same for all the players on the Zone, on the future we could split the zone into several smaller areas each of them would have its own perception |
|||
==RPZone== |
|||
In order to make perceptions work, you have to manually call modify method so that you notify the zone about changes in a character. |
|||
Objects must be stored somewhere, and we use Zones now to store them. A zone is just a container of Objects which has a name. |
|||
Each RPZone '''must''' have a unique name. |
|||
!! Action Reply in Objects |
|||
Last updated: 2003/11/28 |
|||
In order to improve the modifiability of the Marauroa platform we have made RPZone to be an interface so that if you want you can implement it as you require. |
|||
NOTE: This section relates to an early specification of Gladiators and is no longer relevant as it refers to specification, but can help you to know how to make the basic steps to define a game. |
|||
But in most cases, if you think the Delta<sup>2</sup> system is fine and matches your games style, you can use MarauroaRPZone that is our reference implementation of Delta<sup>2</sup> algorithm. |
|||
As you have read in How Perceptions and Actions works, each Object includes the attributes that are result of the action execution. So for each action we will have: |
|||
The actual Marauroa RPZone consists of several data structures: |
|||
Action Talk The attributes added to the object are: |
|||
* a HashMap of RPObject.ID to RPObject |
|||
- action_talk_text |
|||
* a List of RPObject |
|||
* a Perception |
|||
The idea is to have already computed, in the Zone, the perception hence saving LOTS of time that would normally be needed to generate it. All the data structures contain the same objects, but the hashmap is used to do a fast search of objects using the RPObject.ID. This is the most common way for locating an object with a known ID. List is used to improve the time required to build a total perception. Perception is used to pre-calculate the delta perception (i.e. to find the changes between the current state of the zone and the previous state send to the client last turn) |
|||
The action_talk_text contains the text the character has been asked to say. This action is always one turn only, and can never fail. |
|||
The perception is the same for all the players in the Zone. |
|||
Action Move The attributes added to the object are: |
|||
- action_move_destination |
|||
- action_move_speed |
|||
- action_move_status |
|||
In order to make perceptions work, you have to manually call the modify method so that you notify the zone about changes in a character or object. |
|||
The action_move_destination is the place where the object wants to go when the action ends, and it is as before a Position denoted by , action_move_speed is the assigned speed for the character by the RP. The action_move_status denotes if the action is complete, incomplete or failed. This action is completed when position=destination and fail if destination is unreachable. |
|||
Action Get The attributes added to the object are: |
|||
- action_get_status |
|||
The action_get_status denotes if the action is complete or failed This action is always one turn long. |
|||
Action Release The attributes added to the object are: |
|||
- action_release_status |
|||
The action_release_status denotes if the action is complete or failed This action is always one turn long. |
|||
Action Use The attributes added to the object are: |
|||
- action_use_status |
|||
- (Specific attributes depending on the object) |
|||
The action_use_status denotes if the action is complete, incomplete or failed. Usually this action modify attributes of the RPObject instead of adding new attributes. |
|||
Action ~UseWith The attributes added to the object are: |
|||
- action_usewith_status |
|||
- (Specific attributes depending on the object) |
|||
The action_usewith_status denotes if the action is complete, incomplete or failed. Usually this action modify attributes of the RPObject instead of adding new attributes. |
|||
Action Exchange The attributes added to the object are: |
|||
- action_exchange_status |
|||
- action_exchange_timeout |
|||
The action_exchange_status denotes if the action is complete, incomplete or failed. This action last several turns until both peers accept or the timeout elapses. |
|||
On every object that executed an action you will have a status_= that explains what happened with the action. |
|||
!! Attributes |
|||
Last updated: 2003/12/22 |
|||
Attributes are the containers of information in Marauroa. They are designed to contains strings, because they are the most flexible. |
|||
We have also added support for List of Strings. They are encoded as: |
|||
<verbatim> |
|||
name=[value|...|value] |
|||
</verbatim> |
|||
Try to keep the names as short as possible. |
|||
!! Classes of Objects Explained |
|||
Last updated: 2004/07/13 |
|||
Classes of Objects are the basic way of structuring Marauroa data structures. |
|||
A class defines types of the attributes and its visibility and gives it an internal code that is used to speed up searchs and save bandwidth. You can base a class on another, this feature is known as inheritance. |
|||
The data types available are: |
|||
- Strings |
|||
- Short strings ( up to 255 bytes ) |
|||
- Integers ( 4 bytes ) |
|||
- Shorts ( 2 bytes ) |
|||
- Byte ( 1 byte ) |
|||
- Flag ( it is a binary attribute ) |
|||
[[Category:Marauroa]] |
|||
Attributes can also be visible if clients see them when they change, or invisible if clients can't see them. |
|||
{{#breadcrumbs: [[Marauroa]] | [[Navigation for Marauroa Developers|Internals]] | [[RolePlayingDesign|Role Playing Design]] }} |
|||
Latest revision as of 23:28, 18 September 2010
This is possibly the most complex part of all the middleware that makes up Arianne.
Role Playing Design is the determining factor on how easy is to create a new game for Arianne. We had to choose easing the creation of turn time limited based games. Arianne will work better with this kind of games (also known as realtime games).
Role Playing Design tries to be generic and game agnostic (independant of the game being made). The very basic idea behind RPManager is:
forever
{
Execute Actions
Send Perceptions
Wait for next turn
}
To achieve this we use several classes:
- RPManager is coded in Marauroa and doesn't need to be modified.
- IRPRuleProcessor is the interface that you need to modify in order to personalize the actions that your game will execute.
- RPWorld is the class that you need to extend inorder to implement the onInit and onFinish methods which personalize what happens when you initialise the server and what happens when you close the server.
- IRPZone is an interface that you could implement if you wanted to achive the highest personalization possible of the engine, however, I would use MarauroaRPZone instead as that uses our great Delta2 feature.
RPManager
The goal of the RP Manager is to handle the RP of the game. This means:
- run RPActions from clients
- manage RPWorld
- control triggers for events
- control AI
This is a HUGE task that is very complex. Hence we split this behavior into smaller subclasses.
RPManager exposes a simple interface to the GameManager:
- addRPAction
This function queues an action for a particular player to be executed on the next turn. - getRPObject
This is an interface to manage RPWorld to ease the aquisition of the RPObject when exiting the game. - onInit Player
- onExit Player
These are callback functions that are used by GameManager to notify that a player has entered or exited the game. - transferContent
is a callback function that is called by RPRuleProcessor to stream content to players.
The main flow of RPManager is:
forever
{
Procced through every action in this turn
{
rpRuleProcessor executes action
}
Build Perception
Remove timed out players
Wait for Turn completion.
Go to Next Turn
}
RPScheduler is the class that handles actions to be queued for each player. All the complexity of Action management should be handled here.
RPRuleProcessor is a wrapper class for actions code and initialization and exit code.
All the actions code MUST be here.
By implementing RPRuleProcessor you personalize Marauroa to the game you want to make. However, keep in mind that you are limited to a realtime style game.
Objects and Actions
The whole Marauroa system is managed by two main entities, RPAction and RPObject. There are also several helper classes like Attributes, RPSlot and RPClass
Attributes
Attributes is a collection of pairs of values in the form name-value. We can store almost any basic type in a Attribute object:
- strings
- integer
- floats
- boolean
We can't store structures in the attribute, but you can convert groups of data to a string and store it as a string. Marauroa provides helper methods for this.
Objects
All information in the Marauroa server is contained in RPObjects. An object is composed of several attributes (an attribute is similar to a variable in that it has a name and contains a value) and Slots. A Slot is a container or array of containers belonging to an object, and are used to host (store) other objects inside them.
Mandatory Object Attributes: id, type and zoneid
id is an unique identification for the Object, zoneid is the identification for the zone where the object resides and type is the type of the object aka class, so that you can share attributes for all the instances of the class.
An id is only unique inside the zone which contains that object.
NOTE: The engine provided two special types of attributes: - Attributes that begin with ! are completely hidden from all other users except the owner of the object. - Attributes that begin with # are completely hidden for all users.
Classes of Objects Explained
Classes of Objects are the basic way of structuring Marauroa data structures. The type of an attribute of a given object must be equal to a RPClass name of the type class you wish to use.
The class defines the type of the attribute, its visibility and assigns it an internal code that is used to speed up searchs and save bandwidth. You can base a class on another, this feature is known as inheritance (a new class is create from a class that already exists and takes all the original classes methods and data and extends it).
The data types available are:
- Strings
- Short strings ( up to 255 bytes )
- Integers ( 4 bytes )
- Shorts ( 2 bytes )
- Byte ( 1 byte )
- Flag ( it is a binary attribute )
Attributes can be visible which means clients see them when they change, or invisible if clients can't see them.
Slots
Objects can reside inside other objects much like you have the keys inside your pocket. The goal of Slots is to provide a richer game play while reducing the number of object in the zone.
To have objects inside, we need our hoster object to have slots to place them in. One slot can only handle one single object.
For example an avatar can have:
- left hand
- right hand
- backpack
- left pocket
- right pocket
and we can store objects in each of these slots.
Once an object has been stored inside an objects slot, the only way of accessing the stored object is through the object that contains our stored object.
As attributes, slots have two special types:
- Slots names that start with ! are only sent to owner player. (Hence only seen by the owner)
- Slots names that start with # are not sent to players. (Invisible to all)
Actions
To express the willingness of a client to do something it must send the server a MessageC2SAction message.
An action is composed of several attributes. (an attribute is similar to a variable in that it has a name and contains a value).
There are optional and mandatory attributes. If a mandatory attribute is not found, the message is skipped by the RPServerManager.
Mandatory Action Attributes are action_id and type.
The action_id is used to identify the action when a resulting response comes in a perception
Perceptions
The basic structure for sending world updates to clients is called perceptions.
There are two types of perception:
- Sync perceptions: these are used to synchronize clients with the server world representation. This is the only valid way of knowing world's status.
- Delta perception: this is used to send only the changes to the world since the last perception.
Our actual Perception system is called Delta2. It is heavily attached to the Marauroa core, so I recommend you to use it :)
How Perceptions and Actions work
Actions are sent from the client to the server in order to make the character perform an action. In order for the client to know the result of the action the Server needs to send a reply to the client. How will this be done?
In a first attempt, we send clients back an action that was the result of their action. However, this made the code really hard because we had to update two different things, perceptions and actions. Instead the solution appears intuitively: Why not join action reply and perceptions.
So the action reply is stored inside each object (that executed the action ) with a set of attributes that determine the action return status and the attributes. This way of doing replies makes it a bit harder on RPManager but it simplifies the creation of new clients a lot.
See Actions reply in the Objects documentation to know exactly what is returned. However, keep in mind that the return result depends of each particular game.
Delta2 perception Algorithm
The idea behind the DPA is to avoid sending ALL the objects to a client each time, but only those that have been modified.
Imagine that we have 1000 objects, and only O1 and O505 are active objects that are modified each turn.
The Traditional method:
- Get objects that our player should see ( 1000 objects ) - Send them to player ( 1000 objects ) - Next turn - Get objects that our player should see ( 1000 objects ) - Send them to player - Next turn ...
I hope you see the problem... we are sending objects that haven't changed each turn.
The delta perception algorithm:
- Get objects that our player should see ( 1000 objects ) - Reduce the list to the modified ones ( 1000 objects ) - Store also the objects that are not longer visible ( 0 objects ) - Send them to player ( 1000 objects ) - Next turn - Get objects that our player should see ( 1000 objects ) - Reduce the list to the modified ones ( 2 objects ) - Store also the objects that are not longer visible ( 0 objects ) - Send them to player ( 2 objects ) - Next turn ...
The next step of the delta perception algorithm is pretty clear: delta2 The idea is to send only what changes of the objects that changed. This way we save even more bandwidth, making perceptions around 20% of the original delta perception size.
The delta2 algorithm is based on four containers:
- List of added objects
- List of modified added attributes of objects
- List of modified deleted attributes of objects
- List of deleted objects
An area very related to DPA is RPZone (see lower in this doc)
As you should know, an MPEG video adds a full frame each X number of frames, so it can be used as synchronization in case the file gets corrupted. The idea is that if you fail to continue decompressing data, you can always omit things until the next full frame and then you get synced. The idea here is similar, if we fail to synchronize with server we send it an Out of Sync Message so that server will send a sync perception so that the clients can synchronize. Remember, UDP is not a secure transport.
To make perceptions work, it is important to call the modify method in RPZone, so this way objects modified are stored in the modified list.
Zones and Worlds
Worlds in Marauroa can be so big, so huge, that we need to split them in to several pieces. Each of these pieces are what we call an RPZone.
So our world is made of several RPZones that are independent of each other. To move from one RPZone to another RPZone you have to code the correct behaviour in RPRuleProcessor. Just look at any of our coded examples.
RPWorld
As we have already said, RPWorld stores several RPZones that are independent of each other.
RPWorld provides onInit and onFinish methods that are called on server initialisation and server finalization to define what to do with the world on these events. There is no default behaviour and you need to extend this class to redefine the behaviour.
Also it provides methods for adding and getting new RPZones:
- addRPZone
- getRPZone, which can be used with either RPZone.ID or RPObject.ID
Finally it also contains methods for managing RPObjects as:
- addRPObject, that need that our RPObject contains a valid RPZone.ID inside its RPObject.ID
- getRPObject
- modifyRPObject
- changeZone that moves one object from the old zone to the new zone and adds a proper valid id.
At last, RPWorld also contains a method called nextTurn that is called by RPManager to move from one turn to the next turn. It resets the delta2 data.
RPZone
Objects must be stored somewhere, and we use Zones now to store them. A zone is just a container of Objects which has a name.
Each RPZone must have a unique name.
In order to improve the modifiability of the Marauroa platform we have made RPZone to be an interface so that if you want you can implement it as you require.
But in most cases, if you think the Delta2 system is fine and matches your games style, you can use MarauroaRPZone that is our reference implementation of Delta2 algorithm.
The actual Marauroa RPZone consists of several data structures:
- a HashMap of RPObject.ID to RPObject
- a List of RPObject
- a Perception
The idea is to have already computed, in the Zone, the perception hence saving LOTS of time that would normally be needed to generate it. All the data structures contain the same objects, but the hashmap is used to do a fast search of objects using the RPObject.ID. This is the most common way for locating an object with a known ID. List is used to improve the time required to build a total perception. Perception is used to pre-calculate the delta perception (i.e. to find the changes between the current state of the zone and the previous state send to the client last turn)
The perception is the same for all the players in the Zone.
In order to make perceptions work, you have to manually call the modify method so that you notify the zone about changes in a character or object. {{#breadcrumbs: Marauroa | Internals | Role Playing Design }}