RolePlayingDesign: Difference between revisions

Jump to navigation Jump to search
Content deleted Content added
imported>Hendrik Brummermann
No edit summary
 
(97 intermediate revisions by 5 users not shown)
Line 1: Line 1:
{{Navigation for Marauroa Top|Internals}}
It is perhaps the most complex part of all the middleware that compromises Arianne.<br>
{{Navigation for Marauroa Developers}}
Role Playing Design determines how ''easy'' is to create a new game for Arianne. We had to choose easing the creation of turn time limited based games, so Arianne will work better with that kind of games, also known as realtime games.



Role Playing Desing anyway tries to keep generic and game agnostic.
This is possibly the most complex part of all the middleware that makes up Arianne.<br>
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:
The very basic idea behind RPManager is:
<pre>
<pre>
Line 14: Line 18:


To achieve this we use several classes:
To achieve this we use several classes:
* RPManager that is coded in the Marauroa and don't need to be modified.
* RPManager is coded in Marauroa and doesn't need to be modified.
* IRPRuleProcessor is the interface that you need to implement in order to personalize the actions to execute.
* IRPRuleProcessor is the interface that you need to modify in order to personalize the actions that your game will execute.
* RPWorld that is the class that you need to extend to implement the onInit and onFinish in order to personalize what happens when you init the server and what happens when you close the server.
* 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 a interface that you ''could'' implement to achive the highest personalization possible of the engine, I would instead use MarauroaRPZone that implements our great feature Delta<sup>2</sup>.
* 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.




=RPManager=
=RPManager=
The goal of RP Manager is to handle the whole RP game. This means mainly:
The goal of the RP Manager is to handle the RP of the game. This means:
* run RPActions from clients
* run RPActions from clients
* manage RPWorld
* manage RPWorld
Line 27: Line 31:
* control AI
* control AI


As you see this is a HUGE taks that is complex. So the idea is to split this behavior into smaller subclasses.
This is a HUGE task that is very complex. Hence we split this behavior into smaller subclasses.


RPManager provides a simple interface to the GameManager for using it:
RPManager exposes a simple interface to the GameManager:
* addRPAction <br> It simply queues an action for that player to be executed on the next turn.
* <i>addRPAction</i> <br> This function queues an action for a particular player to be executed on the next turn.
* getRPObject <br> It is an interface to manage RPWorld to ease the adquisition of the RPObject when exiting the game.
* <i>getRPObject</i> <br> This is an interface to manage RPWorld to ease the aquisition of the RPObject when exiting the game.
* onInit Player
* <i>onInit Player</i>
* onExit Player<br> They are callback functions that are used by GameManager to notify that a player has entered the game or that a player has exit the game.
* <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.
* transferContent<br> It is a callback function too that is called by RPRuleProcessor to stream content to players.
* <i>transferContent</i><br> is a callback function that is called by RPRuleProcessor to stream content to players.


The main outline of RPManager could be:
The main flow of RPManager is:
<pre>
<pre>
forever
forever
Line 57: Line 61:
RPRuleProcessor is a wrapper class for actions code and initialization and exit code. <br>
RPRuleProcessor is a wrapper class for actions code and initialization and exit code. <br>
All the actions code MUST be here.<br>
All the actions code MUST be here.<br>
By implementing RPRuleProcessor you personalize Marauroa to be the kind of game you want to code. But keep in mind that you are ''limited'' to a realtime like game.
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=
=Objects and Actions=
The whole Marauroa system is managed by two main entities, RPAction and RPObject and several helper classes like Attributes, RPSlot and RPClass
The whole Marauroa system is managed by two main entities, RPAction and RPObject. There are also several helper classes like Attributes, RPSlot and RPClass


==Atributtes==
==Attributes==
Attributes is a collection of pairs name-value.
Attributes is a collection of pairs of values in the form name-value.
We can store almost any basic type in a Attribute object:
We can store almost any basic type in a Attribute object:
* strings
* strings
Line 70: Line 74:
* boolean
* boolean


We can't store collections in the attribute, but you can convert it to a string and store it as a string. Marauroa provides helper methods for it.
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==
==Objects==
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.
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
Mandatory Object Attributes: id, type and zoneid


id is an unique identification for the Object and 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.
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.
An id is only unique inside the zone which contains that object.


Also engine give special treatment to two types of attributes:
NOTE: The engine provided two special 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 from all other users except the owner of the object.
- Attributes that begin with # are completely hidden for all the users.
- Attributes that begin with # are completely hidden for all users.


===Classes of Objects Explained===
===Classes of Objects Explained===
Classes of Objects are the basic way of structuring Marauroa data structures.
Classes of Objects are the basic way of structuring Marauroa data structures.
You must use type attribute to make use of classes so value of type must be equal to a RPClass name.
The type of an attribute of a given object must be equal to a RPClass name of the type class you wish to use.


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 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:
The data types available are:
Line 99: Line 103:
* Flag ( it is a binary attribute )
* Flag ( it is a binary attribute )


Attributes can also be visible if clients see them when they change, or invisible if clients can't see them.
Attributes can be visible which means clients see them when they change, or invisible if clients can't see them.


===Slots===
===Slots===
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.
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 these objects inside, we need our hoster object to have slots to place them. One slot can only handle one single object.
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 a avatar can have:
For example an avatar can have:
- left hand
* left hand
- right hand
* right hand
- backpack
* backpack
- left pocket
* left pocket
- right pocket
* right pocket


and we can store objects on these slots.
and we can store objects in each of these slots.


Once the object is stored inside the avatar or another object, the only way of accessing it is through the object that contains our stored object.
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 has also two special types:
As attributes, slots have two special types:
* Slots names that start with ! are only sent to owner player.
* 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.
* Slots names that start with # are not sent to players. (Invisible to all)


==Actions==
==Actions==
To express the willing of a client to do something it must send the server a MessageC2SAction message.
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 attributed is similar to a variable that has a name and contains a value.
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.
There are optional and mandatory attributes. If a mandatory attribute is not found, the message is skipped by the RPServerManager.


Mandatory Actions Attributes are action_id and type.
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
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:
= How Perceptions and Actions work =
* Sync perceptions: these are used to synchronize clients with the server world representation. This is the only valid way of knowing world's status.
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?
* Delta perception: this is used to send only the changes to the world since the last perception.


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 :)
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.


== How Perceptions and Actions work ==
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.
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.
See Actions reply in Objects document to know exactly what is returned, but keep in mind that it depends of each particular game.


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.
=Delta<sup>2</sup> perception Algorithm=
The main idea behind the DPA is not to send ALL the objects to client, but only those that has been modified.


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.
Imagine that we have 1000 objects, and only O1 and O505 are active objects that are modified each turn. Ok?


==Delta<sup>2</sup> perception Algorithm==
Traditional method:
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:
<pre>
<pre>
- Get objects that our player should see ( 1000 objects )
- Get objects that our player should see ( 1000 objects )
Line 158: Line 170:
</pre>
</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:
Line 175: Line 187:
</pre>
</pre>


The next step on delta perception algorithm is pretty clear: delta<sup>2</sup> 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 original 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<sup>2</sup> algorithm is based on four containers:
The delta<sup>2</sup> algorithm is based on four containers:
Line 184: Line 196:
* List of deleted objects
* List of deleted objects


An area really related to DPA is RPZone
An area very related to DPA is RPZone (see lower in this doc)


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 similar, if we fail to synchronize with server we send him a Out of Sync Message so that server will send a sync perception so that clients can synchronize, as UDP is not a secure transport.
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 perception works it is important to call the modify method on RPZone so this way objects modified are stored in the modified list.


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=
=Zones and Worlds=
Worlds in Marauroa can be so big, so huge, that we need to split it in several pieces.
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 RPZone.
Each of these pieces are what we call an RPZone.


So our world is made of several RPZones that are '''independent''' of each other.
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 that behaviour on RPRuleProcessor. Just look at any of the coded examples.
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==
==RPWorld==
As we have said, it stores several RPZones that are independent of each other.
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:
* 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 delta<sup>2</sup> data.


==RPZone==
==RPZone==
Objects must be stored somewhere, and we use Zones now to store them. A zone is just a container of Objects.
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 think it is more efficient.


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.
The actual Marauroa RP Zone consists of several data structures:

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.

The actual Marauroa RPZone consists of several data structures:
* a HashMap of RPObject.ID to RPObject
* a HashMap of RPObject.ID to RPObject
* a List of RPObject
* a List of RPObject
* a Perception
* a Perception


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.
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.
Actually the perception is the same for all the players on the Zone.


[[Category:Marauroa]]
In order to make perceptions work, you have to manually call modify method so that you notify the zone about changes in a character.
{{#breadcrumbs: [[Marauroa]] | [[Navigation for Marauroa Developers|Internals]] | [[RolePlayingDesign|Role Playing Design]] }}