~swag/armagetronad/0.2.9-sty+ct+ap-fork

« back to all changes in this revision

Viewing changes to src/doc/net/upper.html.m4

  • Committer: luke-jr
  • Date: 2006-05-29 01:55:42 UTC
  • Revision ID: svn-v3-list-QlpoOTFBWSZTWZvbKhsAAAdRgAAQABK6798QIABURMgAAaeoNT1TxT1DQbKaeobXKiyAmlWT7Y5MkdJOtXDtB7w7DOGFBHiOBxaUIu7HQyyQSvxdyRThQkJvbKhs:7d95bf1e-0414-0410-9756-b78462a59f44:armagetronad%2Fbranches%2F0.2.8%2Farmagetronad:4612
Unify tags/branches of modules released together

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
include(head.html.m4)
 
2
define(SECTION,upper)
 
3
include(navbar.html.m4)
 
4
 
 
5
TITLE(Layer three: network aware objects)
 
6
<p align=justify>
 
7
Layer three (files:<CODE>netobject.h</CODE> and <CODE>netobject.C</CODE>)
 
8
uses layer two to define a base class for classes of network
 
9
aware objects (NAOs). When a NAO is created on one of the computers in 
 
10
the network game, copies of it will be created on the other
 
11
computers. If the state of the original changes, it can send sync messages
 
12
to it's copies, making the same changes on them. Every NAO has an unique ID
 
13
number (shared with it's copies) and an owner (usually the computer which
 
14
created the original NAO, or the computer the player controlling the NAO
 
15
is using). If desired, a security system (against cheating) 
 
16
allows the original NAO only 
 
17
to reside on the server, and sync messages only to flow from the 
 
18
server to the clients. Only the NAO's owner is then allowed to send
 
19
control messages to the original NAO on the server, which will send back
 
20
sync messages reporting the changes made. Confused? Let's look at it
 
21
again:
 
22
 
 
23
SUBSECTION(NAO class with security disabled)
 
24
<p align=justify>
 
25
That's the easy part. Any computer can create/delete such an NAO and 
 
26
automatically owns it. Copies of the NAO will spawn/be deleted on all the other
 
27
computers. The owner is allowed to change the NAO (i.e. move it one meter
 
28
to the left) and issue sync commands;
 
29
then, sync messages will be sent to all other computers in the game
 
30
(if the NAO was created on a client, they will be directed through the server)
 
31
transferring the NAO's new state. Control messages 
 
32
("move one meter to the left") may be sent from the owner
 
33
to the NAO's copy at the server which may interpret them, change it's
 
34
(or some other NAO's) state and send syncs about the change to all clients.
 
35
</p>
 
36
 
 
37
SUBSECTION(NAO class with security enabled)
 
38
<p align=justify>
 
39
Here, <strong>only the server</strong> 
 
40
is allowed to create an NAO. The owner may still be
 
41
one of the clients. As above, copies of the NAO will spawn on all clients. 
 
42
<strong>Only the server</strong> is allowed to change the NAO and 
 
43
issue sync commands, sending sync messages to the clients.
 
44
The only way the NAO's owner has influence on it are the control messages
 
45
it may send to the server. 
 
46
</p><p align=justify>
 
47
The use of the security mode simply is: the server has full control over
 
48
the objects. Otherwise, people in an ego shooter could just teleport
 
49
themselves at will through the arena or make themselves invincible 
 
50
by simple modifications to the game (that's even an issue in closed
 
51
source games; look at Diabolo!).
 
52
</p>
 
53
SECTION(Usage)
 
54
<p align=justify>
 
55
A sample program defining a class of NAOs is 
 
56
<a href=../../src/network/l3_demo.cpp><CODE>l3_demo.cpp</CODE></a>
 
57
from the source directory. Compile it with <CODE>make
 
58
l3_demo</CODE>; the syntax is <CODE>l3_demo</CODE> to start it in 
 
59
server mode, just listening to clients, or <CODE>l3_demo
 
60
servername</CODE> to start it in client mode connecting to the server
 
61
given by <CODE>servername</CODE>; it will first show you how to
 
62
synchronize the NAO with sync messages, then how to control it with
 
63
control messages. Try connecting multiple clients to the server!
 
64
You'll see how easy it is; it's best to read this document and the 
 
65
sample program in parallel.
 
66
</p>
 
67
 
 
68
SUBSECTION(Overview)
 
69
To define a class of NAO's (let's call it <CODE>your_NAO</CODE>),
 
70
you derive a class from the base class
 
71
<CODE>netobject</CODE>  
 
72
and declare some member functions:
 
73
 
 
74
<ul>
 
75
<li>A normal <a href=#constr>constructor</a> and a virtual destructor.
 
76
</li>
 
77
<br><br>
 
78
<li>
 
79
For the <a href=#sync>synchronisation messages</a>,
 
80
 the send and receive functions
 
81
<pre>
 
82
virtual void write_sync(netmessage &m);
 
83
virtual void read_sync(netmessage &m);
 
84
</pre>
 
85
where <CODE>read_sync()</CODE> should read exactly the information
 
86
from <CODE>m</CODE> that <CODE>write_sync()</CODE> writes to it, and
 
87
if necessary a function deciding whether a sync message should be
 
88
accepted:
 
89
<pre>
 
90
virtual bool sync_is_new(netmessage &m);
 
91
</pre>
 
92
</li>
 
93
<li>
 
94
For <a href=#create>remote creation</a>, the send function
 
95
<pre>
 
96
virtual void write_create(netmessage &m); 
 
97
</pre>
 
98
and the remote constructor
 
99
<pre>
 
100
your_NAO(netmessage &m); 
 
101
</pre>
 
102
again reading exactly the information from <CODE>m</CODE> that 
 
103
<CODE>write_create()</CODE> wrote to it, and eventually a post-creation
 
104
function
 
105
<pre>
 
106
virtual void init_after_creation();
 
107
</pre>
 
108
being called after an object has been remotely created.
 
109
</li>
 
110
<br><br>
 
111
<li>
 
112
For the security system,
 
113
<pre>
 
114
virtual bool accept_client_sync() const;
 
115
</pre>
 
116
returning true if security is to be disabled. (Default: security is on.)
 
117
</li>
 
118
<br><br>
 
119
<li>
 
120
You need to create one object of the template class
 
121
<pre>
 
122
<a href=#identification>class net_initialisator&lt;your_NAO&gt;</a>;
 
123
</pre>
 
124
to give your class a unique identification across the network
 
125
(the constructor takes the name of your class as a string argument) and
 
126
the member function
 
127
<pre>
 
128
virtual netdescriptor &creator_descriptor() const;
 
129
</pre>
 
130
returning a reference to that object 
 
131
(<CODE>net_initialisator&lt;your_NAO&gt;</CODE>
 
132
is derived from the class <CODE>netdescriptor</CODE>).
 
133
</li>
 
134
<br><br>
 
135
<li>
 
136
And finally, if you want to use <a href=#control>control messages</a>, 
 
137
the receive function
 
138
<pre>
 
139
virtual void receive_control(netmessage &m);
 
140
</pre>
 
141
and of course some sending function.
 
142
</li>
 
143
</ul>
 
144
 
 
145
SUBSECTION(Details)
 
146
 
 
147
SUBSUBSECTION(<a name=constr>Constructor</a>)
 
148
<p align=justify>
 
149
The constructor
 
150
has to call call <CODE>netobject</CODE>'s constructor
 
151
</p><pre>
 
152
netobject::netobject(int owner=-1);
 
153
</pre><p align=justify>
 
154
where the argument is the user ID of the object's owner; leave it
 
155
blank or at <CODE>-1</CODE> if the creating computer itself should be 
 
156
the owner.
 
157
All in all, your constructor should look something like this:
 
158
</p><pre>
 
159
your_NAO::your_NAO(...) :netobject(owner) {
 
160
  // normal initialisation
 
161
  ...
 
162
}
 
163
</pre>
 
164
 
 
165
SUBSUBSECTION(<a name=sync>Synchronisation</a>)
 
166
<p align=justify>
 
167
Whenever you feel like your NAO's copies need to be synchronised with the
 
168
original (i.e. every time you change the original, or every .1
 
169
seconds), call the original's member function
 
170
</p><pre>
 
171
void request_sync(bool ack=true);
 
172
</pre><p align=justify>
 
173
(inherited form <CODE>netobject</CODE>). 
 
174
<CODE>ack</CODE> determines whether the sync message is guaranteed to
 
175
arrive; If the synchronisation is not vital 
 
176
(like updates of a constantly changing position where it is not fatal 
 
177
if one update is missed), you can set
 
178
<CODE>ack</CODE> to false. 
 
179
To really send the sync messages, you need to call the static function
 
180
</p><pre>
 
181
netobject::sync_all();
 
182
</pre><p align=justify>
 
183
every once in a while (best immediately before <CODE>receive()</CODE> from 
 
184
layer two). Shortly after your call of <CODE>request_sync()</CODE>, during
 
185
one of your calls to <CODE>netobject::sync_all();</CODE>
 
186
the network subsystem will call your original's member 
 
187
function <CODE>your_NAO::write_sync(netmessage&nbsp;&m)</CODE>
 
188
and broadcast the message <CODE>m</CODE>. 
 
189
When the other computers receive <CODE>m</CODE>, they
 
190
will simply call your NAO's copy's member function 
 
191
<CODE>your_NAO::read_sync(netmessage&nbsp;&m)</CODE>.
 
192
So your sync functions should read something like 
 
193
</p><pre>
 
194
virtual void write_sync(netmessage &m){
 
195
   // proper heritage:
 
196
   netobject::write_sync(m);
 
197
 
 
198
   // write all the possibly changing  
 
199
   // information form your object to m:
 
200
   m &lt;&lt; ...
 
201
}
 
202
 
 
203
virtual void read_sync(netmessage &m){
 
204
   // heritage:
 
205
   netobject::read_sync(m);
 
206
   
 
207
   // read the information exactly in the same 
 
208
   // order as it was written in write_sync:
 
209
   m &gt;&gt; ....
 
210
}
 
211
</pre><p align=justify>
 
212
If you detect an error during your <CODE>read_sync()</CODE>, caused by
 
213
the message being in a wrong [format], feel free to  kick the message's 
 
214
sender by throwing a <CODE>killhim</CODE>-exception.
 
215
</p><p align=justify>
 
216
Since sync messages may get lost or arrive in the wrong order, it is not
 
217
a good idea to write just the information that really changed in 
 
218
<CODE>write_sync()</CODE>; if you decide to do that kind of
 
219
bandwidth optimisation anyway, think exactly about what you are doing!
 
220
</p><p align=justify>
 
221
What happens now if a sync message is lost, sent again, lost again....
 
222
and receives the other computers way too late? Without protection measurements,
 
223
this will i.e. cause your racing car to be set back on the track until the
 
224
next sync packet arrives, correcting the mistake. This is not fatal, but
 
225
disturbing, and something needs to be done. Therefore, before calling
 
226
your NAO's <CODE>read_sync()</CODE>, the network subsystem calls
 
227
your NAO's member function
 
228
</p><pre>
 
229
virtual bool sync_is_new(netmessage &m);
 
230
</pre><p align=justify>
 
231
where you can read out <CODE>m</CODE> just like in <CODE>read_sync()</CODE>
 
232
and do some checks whether you really wish to accept the sync; i.e. with
 
233
every <CODE>write_sync</CODE>, you could include a time stamp (you should do
 
234
that anyway in a real time game) <CODE>sync_is_new()</CODE> may check;
 
235
if the timestamp is too old, you should reject the message. Only if
 
236
<CODE>sync_is_new()</CODE> returns <CODE>true</CODE>, <CODE>read_sync()</CODE>
 
237
is called. As the class <CODE>netobject</CODE> already implements a rudimentary
 
238
check in <CODE>netobject::sync_is_new(netmessage &m)</CODE>, guaranteeing
 
239
that each accepted sync message is newer than the one before,
 
240
you do not need to write your own check in most cases. If you do, it should
 
241
look like
 
242
</p><pre>
 
243
virtual bool sync_is_new(netmessage &m){
 
244
   // heritage:
 
245
   if (!netobject::sync_is_new(m))
 
246
     return false;      
 
247
 
 
248
   // your own checks, reading EXACTLY
 
249
   // the same information as read_sync()
 
250
   // (important for derived classes)
 
251
   m &gt;&gt; ....
 
252
   return result;
 
253
}
 
254
</pre>
 
255
 
 
256
SUBSUBSECTION(<a name=create>Remote creation</a>)
 
257
<p align=justify>
 
258
What happens now if you create a NAO with the
 
259
<a href=#constr>normal constructor</a>?
 
260
During one of the next calls of <CODE>netobject::sync_all()</CODE>,
 
261
remote creation messages will be sent to the other computers. They contain
 
262
all the information of a sync message, plus a bit more:
 
263
The sync messages are only intended to transport the part of the
 
264
NAO's information that is changing during the game; other parts of
 
265
the information may be fixed, i.e. the object's name, or a character's
 
266
race in a RPG. This information has to be written only once, and it would
 
267
be a waste of bandwidth to transmit it with every sync message. Therefore,
 
268
you should write all this fixed information in your NAO's member function
 
269
</p><pre>
 
270
virtual void write_create(netmessage &m){
 
271
   // again: heritage
 
272
   netobject::write_create(m);
 
273
 
 
274
   // your fixed information
 
275
   m &lt;&lt; ....
 
276
}
 
277
</pre><p align=justify>
 
278
So, when preparing a remote creation message,
 
279
<CODE>netobject::sync_all()</CODE> first lets your NAO write it's fixed
 
280
information with <CODE>write_create()</CODE> to it, then the changing
 
281
information with <CODE>write_sync()</CODE>. After that the message is
 
282
broadcasted and guaranteed to be received by the other computers.
 
283
They will then call your remote constructor which should look like
 
284
</p><pre>
 
285
your_NAO(netmessage &m)
 
286
:netobject(m) // heritage
 
287
{
 
288
  // read the fixed information
 
289
  m &gt;&gt; ....
 
290
}
 
291
</pre><p align=justify>
 
292
and after that your NAO's <CODE>read_sync()</CODE>. Since you may
 
293
need some of the variable information read by that function to completely
 
294
initiate your NAO, it's member function
 
295
</p><pre>
 
296
virtual void init_after_creation();
 
297
</pre><p align=justify>
 
298
is called after that where you can finish the construction. Again, if
 
299
an error occurs, kick the message's sender by throwing a 
 
300
<CODE>killhim</CODE>-exception.
 
301
</p>
 
302
SUBSUBSECTION(<a name=identification>Identification</a>)
 
303
<p>No big deal here: just write
 
304
</p><pre>
 
305
static net_initialisator&lt;your_NAO&gt; your_NAO_init("your_NAO");
 
306
</pre><p align=justify>
 
307
somewhere in your code file, declare the member function
 
308
</p><pre>
 
309
virtual netdescriptor &creator_descriptor() const;
 
310
</pre><p align=justify>
 
311
and define it as
 
312
</p><pre>
 
313
netdescriptor &your_NAO::creator_descriptor() const{
 
314
  return your_NAO_init;
 
315
}
 
316
</pre><p align=justify>
 
317
That's it. You have to repeat that for every NAO class you define.
 
318
</p>
 
319
SUBSUBSECTION(<a name=control>Control messages</a>)
 
320
<p align=justify>
 
321
Control messages can go from the owner of the NAO (a client) to the
 
322
server only. Do with them what you want, but they are mainly intended
 
323
to transport the user input to the server. Before sending a control
 
324
message, you'll have to create it using your NAO's member function
 
325
(inherited from <CODE>netobject</CODE>)
 
326
</p><pre>
 
327
 netmessage *new_control_message();
 
328
</pre><p align=justify>
 
329
Then, write to it whatever you want and send it to the server. The
 
330
server will call your NAO's member function
 
331
</p><pre>
 
332
virtual void receive_control(netmessage &m);
 
333
</pre><p align=justify>
 
334
which can then read and interpret the message. Of course, if you do
 
335
any changes to your NAO, you should call <CODE>request_sync()</CODE>
 
336
at the end. It is advisable to encapsulate the sending
 
337
process in an own member function (as
 
338
<CODE>new_control_message</CODE> has protected heritage, you are
 
339
forced to do that :-) ). As you can see, you have much freedom here
 
340
(and are basically on your own).
 
341
</p>
 
342
 
 
343
SUBSECTION(Pointers to netobjects)
 
344
<p align=justify>
 
345
<strong>NOTE: This section is still subject to change. Many of the
 
346
things you have to do manually now will be automated in future versions
 
347
of PROGNAME.</strong>
 
348
</p><p align=justify>
 
349
You will come to a point where you define a class of NAOs containing
 
350
pointers to other NAOs that need to be transmitted. The first problem:
 
351
</p>
 
352
SUBSUBSECTION(Transferring pointers)
 
353
<p align=justify>
 
354
Obviously, you can't just transfer them like integers. Instead of
 
355
writing a pointer to a NAO to a netmessage, simply write it's ID with
 
356
</p><pre>
 
357
m &lt;&lt; object->my_id();
 
358
</pre><p align=justify>
 
359
when receiving the id, it can be retransformed to a pointer with
 
360
</p><pre>
 
361
unsigned short id;
 
362
m &gt;&gt; id;
 
363
netobject *obj=netobject::object(id);
 
364
</pre><p align=justify>
 
365
In case the netobject with ID <CODE>id</CODE> has not yet been created,
 
366
<CODE>netobject::object(id)</CODE> will wait for it to spawn; with a
 
367
bit of luck, it will be created remotely shortly. That brings us to
 
368
the next problem: what if we're not lucky? There may easily be lockups
 
369
if i.e. the server waits for one of the client's NAOs to spawn, while
 
370
the client is waiting for some other message from the server (of
 
371
course, there is a timeout in <CODE>netobject::object()</CODE>.
 
372
But after that timeout, the connection is closed). An alternative
 
373
routine doing about the same job is <CODE>netobject</CODE>'s 
 
374
static member function
 
375
</p><pre>
 
376
static netobject *object_dangerous(int id);
 
377
</pre><p align=justify>
 
378
If the NAO labelled <CODE>id</CODE> does not exist, it will simply
 
379
return <CODE>NULL</CODE>.
 
380
</p>
 
381
SUBSUBSECTION(Waiting for NAOs to spawn remotely)
 
382
<p align=justify>
 
383
Sometimes, before you send a network message transferring a pointer to
 
384
a NAO, you want to make sure the NAO has been created remotely at the
 
385
message's receiver before sending the message; that avoids the
 
386
problems mentioned above. The NAO's member function
 
387
</p><pre>
 
388
bool has_been_transmitted(int user) const;
 
389
</pre><p align=justify>
 
390
(inherited form <CODE>netobject</CODE>) does exactly this check. 
 
391
For example, such checks should be done before a NAO depending on
 
392
another NAO (like, the rider of a horse...) is created remotely: you
 
393
want to be absolutely sure the horse is there before the rider
 
394
arrives. For exactly this situation, you can use your NAO's
 
395
member function 
 
396
</p><pre>
 
397
virtual bool clear_to_transmit(int user) const;
 
398
</pre><p align=justify>
 
399
It should return false if the NAO is not yet ready to be remotely
 
400
created at the computer with user ID <CODE>user</CODE>.
 
401
In our example, the rider's function should be defined as
 
402
</p><pre>
 
403
bool rider::clear_to_transmit(int user) const{
 
404
  return netobject::clear_to_transmit() && // heritage, as always...
 
405
         horse->has_been_transmitted(user);
 
406
}
 
407
</pre>
 
408
SUBSUBSECTION(Reference counters)
 
409
<p align=justify>
 
410
Another problem arises with remote destruction: It is to be avoided
 
411
that a NAO is destroyed while there are still pointers to
 
412
it. Therefore, each NAO has a reference counter you need to set
 
413
manually: call the member function
 
414
</p><pre>
 
415
void reg();
 
416
</pre><p align=justify>
 
417
every time you set a pointer to a NAO, and
 
418
</p><pre>
 
419
void unreg();
 
420
</pre><p align=justify>
 
421
every time you delete the pointer or let it point elsewhere.
 
422
</p>
 
423
 
 
424
include(sig.m4)
 
425
include(navbar.html.m4)
 
426
 
 
427
</body>
 
428
</html>
 
429
 
 
430
 
 
431
 
 
432
 
 
433