Whole document tree
Layer three: network aware objects
Layer three (files: NAO class with security disabledThat's the easy part. Any computer can create/delete such an NAO and automatically owns it. Copies of the NAO will spawn/be deleted on all the other computers. The owner is allowed to change the NAO (i.e. move it one meter to the left) and issue sync commands; then, sync messages will be sent to all other computers in the game (if the NAO was created on a client, they will be directed through the server) transferring the NAO's new state. Control messages ("move one meter to the left") may be sent from the owner to the NAO's copy at the server which may interpret them, change it's (or some other NAO's) state and send syncs about the change to all clients. NAO class with security enabledHere, only the server is allowed to create an NAO. The owner may still be one of the clients. As above, copies of the NAO will spawn on all clients. Only the server is allowed to change the NAO and issue sync commands, sending sync messages to the clients. The only way the NAO's owner has influence on it are the control messages it may send to the server. The use of the security mode simply is: the server has full control over the objects. Otherwise, people in an ego shooter could just teleport themselves at will through the arena or make themselves invincible by simple modifications to the game (that's even an issue in closed source games; look at Diabolo!). Usage
A sample program defining a class of NAOs is
OverviewTo define a class of NAO's (let's call ityour_NAO ),
you derive a class from the base class
netobject
and declare some member functions:
DetailsConstructor
The constructor
has to call call netobject::netobject(int owner=-1);
where the argument is the user ID of the object's owner; leave it
blank or at your_NAO::your_NAO(...) :netobject(owner) { // normal initialisation ... } SynchronisationWhenever you feel like your NAO's copies need to be synchronised with the original (i.e. every time you change the original, or every .1 seconds), call the original's member function void request_sync(bool ack=true);
(inherited form netobject::sync_all();
every once in a while (best immediately before virtual void write_sync(netmessage &m){ // proper heritage: netobject::write_sync(m); // write all the possibly changing // information form your object to m: m << ... } virtual void read_sync(netmessage &m){ // heritage: netobject::read_sync(m); // read the information exactly in the same // order as it was written in write_sync: m >> .... }
If you detect an error during your
Since sync messages may get lost or arrive in the wrong order, it is not
a good idea to write just the information that really changed in
What happens now if a sync message is lost, sent again, lost again....
and receives the other computers way too late? Without protection measurements,
this will i.e. cause your racing car to be set back on the track until the
next sync packet arrives, correcting the mistake. This is not fatal, but
disturbing, and something needs to be done. Therefore, before calling
your NAO's virtual bool sync_is_new(netmessage &m);
where you can read out virtual bool sync_is_new(netmessage &m){ // heritage: if (!netobject::sync_is_new(m)) return false; // your own checks, reading EXACTLY // the same information as read_sync() // (important for derived classes) m >> .... return result; } Remote creation
What happens now if you create a NAO with the
normal constructor?
During one of the next calls of virtual void write_create(netmessage &m){ // again: heritage netobject::write_create(m); // your fixed information m << .... }
So, when preparing a remote creation message,
your_NAO(netmessage &m) :netobject(m) // heritage { // read the fixed information m >> .... }
and after that your NAO's virtual void init_after_creation();
is called after that where you can finish the construction. Again, if
an error occurs, kick the message's sender by throwing a
IdentificationNo big deal here: just write static net_initialisator<your_NAO> your_NAO_init("your_NAO"); somewhere in your code file, declare the member function virtual netdescriptor &creator_descriptor() const; and define it as netdescriptor &your_NAO::creator_descriptor() const{ return your_NAO_init; } That's it. You have to repeat that for every NAO class you define. Control messages
Control messages can go from the owner of the NAO (a client) to the
server only. Do with them what you want, but they are mainly intended
to transport the user input to the server. Before sending a control
message, you'll have to create it using your NAO's member function
(inherited from netmessage *new_control_message(); Then, write to it whatever you want and send it to the server. The server will call your NAO's member function virtual void receive_control(netmessage &m);
which can then read and interpret the message. Of course, if you do
any changes to your NAO, you should call Pointers to netobjectsNOTE: This section is still subject to change. Many of the things you have to do manually now will be automated in future versions of Armagetron. You will come to a point where you define a class of NAOs containing pointers to other NAOs that need to be transmitted. The first problem: Transferring pointersObviously, you can't just transfer them like integers. Instead of writing a pointer to a NAO to a netmessage, simply write it's ID with m << object->my_id(); when receiving the id, it can be retransformed to a pointer with unsigned short id; m >> id; netobject *obj=netobject::object(id);
In case the netobject with ID static netobject *object_dangerous(int id);
If the NAO labelled Waiting for NAOs to spawn remotelySometimes, before you send a network message transferring a pointer to a NAO, you want to make sure the NAO has been created remotely at the message's receiver before sending the message; that avoids the problems mentioned above. The NAO's member function bool has_been_transmitted(int user) const;
(inherited form virtual bool clear_to_transmit(int user) const;
It should return false if the NAO is not yet ready to be remotely
created at the computer with user ID bool rider::clear_to_transmit(int user) const{ return netobject::clear_to_transmit() && // heritage, as always... horse->has_been_transmitted(user); } Reference countersAnother problem arises with remote destruction: It is to be avoided that a NAO is destroyed while there are still pointers to it. Therefore, each NAO has a reference counter you need to set manually: call the member function void reg(); every time you set a pointer to a NAO, and void unreg(); every time you delete the pointer or let it point elsewhere. This Page was created by Manuel Moos. Last modification: Mit Nov 15 21:39:41 CET 2000
|