~ivantis/armagetronad/sty+ct+ivantis

« back to all changes in this revision

Viewing changes to share/doc/games/armagetronad-dedicated/html/net/upper.html

  • Committer: ivantis
  • Date: 2008-09-09 21:33:18 UTC
  • Revision ID: ivantis@ivantis.net-20080909213318-k43y6yuq0zd6wbsa
first commit

Show diffs side-by-side

added added

removed removed

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