Sophie

Sophie

distrib > Mageia > 1 > i586 > by-pkgid > 2a65493842c5486a04c1dad1d9577e1c > files > 47

armagetron-0.2.8.3.1-1.mga1.i586.rpm

 







































 
 




















<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
   <meta name="date" content="2011-03-13T11:37:55+01:00">

   <meta name="author" content="Manuel Moos">
   <title>Armagetron: network code documentation</title>
   <meta name="description" content="Armagetron: network code documentation">
DEFINE(Armagetron Advanced, Armagetron)

</head><body>



<table width="90%" align=center>
<tr>



<td align=center width="25%">
<a href="index.html" target="_top">Introduction</a>
</td>

<td align=center width="25%">
<a href="lower.html" target="_top">Layer One</a>
</td>

<td align=center width="25%">
<a href="middle.html" target="_top">Layer Two</a>
</td>

<td align=center width="25%">
<a href="upper.html" target="_top">Layer Three</a>
</td>


</tr>
</table>



<a name=><h1 align=center>Layer two: network messages</h1></a>
<p align=justify>
This layer is responsible for the basic communication in a client/server
framework (Files: <code>network.h</code> and <code>network.C</code>).
It allows the clients to connect to/disconnect from a server and 
provides a way to send messages in both directions; each server/client 
gets a unique user ID (zero for the server, 1...n for the clients).
The average round trip time of network packets (the ping) is estimated
and stored in <code>REAL avg_ping[user ID]</code> (unit: seconds).
A bandwidth limitation and message queue based on priorities is provided. 
For every message the
server/client receives, an acknowledgement is returned. If no acknowledgement
for a sent message is received within a reasonable time (about ping*1.1),
a message is considered lost and is sent again.
</p>

<a name=><h2 align=left>Usage</h2></a>
<p align=justify>
A sample program for layer two is 
<a href=../../src/network/l2_demo.cpp><code>l2_demo.cpp</code></a>
from the source directory. Compile it with <code>make
l2_demo</code>; the syntax is <code>l2_demo</code> to start it in 
server mode, just listening to clients, or 
<code>l2_demo servername</code> to start it in client mode 
connecting to the server
given by <code>servername</code>; it will send some simple messages from
the client to the server, who will display the results.
</p>

<a name=><h3 align=left>Bandwidth control</h3></a>
<p align=justify>
Before starting up the network, set <code>max_in_rate</code>
and <code>max_out_rate</code> to the input/output bandwidth (in kB/s) 
of the used network interface. It's best to let the user decide that.
The network subsystem will take care that the actual rates stay below
these given numbers, avoiding massive problem with lost packets.
</p>

<a name=><h3 align=left>Communication setup</h3></a>
<p align=justify>
Armagetron's network subsystem can be in three different states: 
standalone (no networking), server, or client (connected to a server). 
The current state can be read via <code>get_netstate()</code> (and is 
an enum type <code>netstate</code>, which can be one of 
<code>NET_STANDALONE, NET_SERVER</code> and <code>NET_CLIENT</code>);
<code>set_netstate(netstate state)</code> is used to set the current state.
The client state is most conveniently set by 
<code>void connect(const string &server)</code>, which will set the state and
establish a connection to the given server; check with 
<code>get_netstate()</code> whether the login was sucessfull. Logging out 
is simply done with <code>set_netstate(NET_STANDALONE)</code>. 
When switching between server and client state, one should visit 
the standalone state in between.
</p>

<a name=><h3 align=left>Network loop</h3></a>
<p align=justify>
The network subsystem does not use threads; so, to receive network messages,
you have to call the function <code>receive()</code> every once in a while
to get the messages the other computers send. Do it at least once in the game
loop. <code>receive()</code> is responsible for sending away queued messages, 
too.
</p>

<a name=><h3 align=left>Messages</h3></a>
<p align=justify>
Before writing a piece of code that sends a message to another computer,
you have to decide what the receiver should do with it. Layer two already
takes the responsibility to sort the incoming messages by type,
so there's no need to write one big <code>receive</code> function that analyses
and processes the messages. Instead, for every type of message (player moves,
player dies, player shoots,...) you want to have, you write an own small
receive function, accepting a reference to an object of the class 
<code>netmessage</code>, for example, if you want to handle a
"player dies"-message consisting of only one <code>short</code> containing
the ID of the player dying, you write
</p>
<pre>
void kill_handler(netmessage &m){
  short player_id;
  m &gt;&gt; player_id; // read the ID from the message

  // do some security checks; is the computer we got the message
  // from really allowed to kill the player? The user ID of the
  // message's sender can be read by m.net_id(). If he's cheating,
  // kick him by throwing a killhim-exception.

  .......

  // kill player player_id.

  .......
}
</pre>
<p align=justify>
Then, you need to define a class of messages responsible for killing players;
to do that, you create an object of the class <code>netdescriptor</code> with
your message handler's address and a nice name as arguments, i.e. by writing
</p>
<pre>
static netdescriptor kill_descriptor(&kill_handler,"Kill");
</pre>
<p align=justify>
directly after <code>kill_handler</code>. To send a message, you have to
create an object of class <code>netmessage</code>
with <code>new</code> and your descriptor as argument:
</p>
<pre>
netmessage *m=new netmessage(kill_descriptor);
</pre>
<p align=justify>
Then, you write the data into the message, in our example only
</p>
<pre>
short this_player_has_to_die=3;
(*m) &lt;&lt; this_player_has_to_die;
</pre>
<p align=justify>
And send the message to the computer with user ID <code>peer</code>
with the message's member function 
<code>send(int&nbsp;peer, REAL&nbsp;priority=0, bool&nbsp;ack=true)</code> 
or to all connected
computers (the server if the network subsystem is in client state, all
clients if it's the server state) via the member function 
<code>broadcast(bool&nbsp;ack=true)</code>, in our case most conveniently
</p>
<pre>
m->broadcast();
</pre>
<p align=justify>
Normally, the message is guaranteed
to arrive sooner or later (and is sent again if it gets lost). Not so important
messages (like position updates in a racing game) 
may be market by giving <code>ack=false</code> as an argument.
As usual, giving a lower  <code>priority</code> than 0 will make the
message more urgent, giving a higher <code>priority</code> will make it sent
later (<code>priority</code> is given in seconds; that is, a message with
priority one, already waiting for one second, is considered 
as important as a message with priority zero that just entered the queue).
DO NOT use <code>delete</code> to get rid of the message; it will delete
itself as soon as it no longer needed. Of course, feel free to encapsulate
all the steps above in a derived class of <code>netmessage</code>.
</p>
<p align=justify>
And that's about all there is. With the operators <code>&lt;&lt;</code> and
<code>&gt;&gt;</code>, 
you can write/read shorts, ints, REALs and strings. All data
is sent in a portable manner; you do not have to worry about endianness or
different float formats. If you want to send/receive messages with variable
length, you can use the member function <code>netmessage::end()</code>. It
tells you whether the message you are just reading out with <code>&gt;&gt;</code>
has been fully read. (Any more reads when <code>end()</code> returns 
<code>true</code> result in a network error, causing the connection to
be brought down.)
</p>

<a name=><h3 align=left>Some special functions</h3></a>

<h4 align=left><a name="client_con"><code>void client_con(const string &message,int client=-1);</code></a></h4>
<p align=left>Lets client No. <code>client</code> display <code>message</code>
on his console. If <code>client</code> is left to <code>-1</code>,
all clients display the message.</p><hr>

<h4 align=left><a name="client_center_message"><code>void client_center_message(const string &message,int client=-1);</code></a></h4>
<p align=left>The same as above, only the message is displayed in large letters at
the center of the screen.</p><hr>

<h4 align=left><a name="sync"><code>void sync(REAL timeout,bool sync_netobjects=false);</code></a></h4>
<p align=left>Synchronises the clients with the server; waits until all
queued network packets are sent or <code>timeout</code> seconds pass.
Umm, already a part or layer three: if <code>sync_netobjects</code>
is set to <code>true</code>, the network objects are synchronised, too.</p><hr>


<a name=><h2 align=left>Protocol details</h2></a>
<p align=justify>
A network message is sent in the following structure (all data types are 
<code>unsigned short</code>s):
</p>

<table border>
<tr>
<td valign=top>Descriptor ID</td>
<td>The type of message (login, logout, acknowledgement, user input...).
Determines what message handler is called at the receiver.</td>
</tr>
<tr>
<td valign=top>Message ID</td>
<td>A locally unique identification number. Used for the acknowledgement and 
for discarding double messages.</td>
</tr>
<tr>
<td valign=top>Data length</td>
<td>The length of the following data in <code>short</code>s;
 used mainly to catch errors.</td>
</tr>
<tr>
<td valign=top>Data</td>
<td>The real message; everything that was written by <code>&lt;&lt;</code></td>
</tr>
</table>

<p align=justify>
Since every network protocol has a considerable amount of overhead per call 
of <a href=lower.html#write><CODE>ANET_Write</CODE></a> (56 bytes in the case
of UDP, I think...), multiple messages are transmitted with each low level 
write operation. The data send with each 
<a href=lower.html#write><CODE>ANET_Write</CODE></a> has the
following form:
</p>

<table border>
<tr><td>Message 1</td></tr>
<tr><td>.<br>.<br>.</td></tr>
<tr><td>Message n</td></tr>
<tr><td>Sender's user ID (as always, as a <code>unsigned short</code>)</td></tr>
</table>

<p align=justify>
The user ID is sent to simplify the server distinguishing the clients; with the
possibility of clients hiding behind masquerading firewalls and thus appearing
to send from changing ports, simply checking the sender's addresses may not 
be enough.
</p>

<br>
<p align=center>This Page was created by
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> ).

<p align=center>
Last modification: Sun Mar 13 11:37:55 UTC 2011

</p>




<table width="90%" align=center>
<tr>



<td align=center width="25%">
<a href="index.html" target="_top">Introduction</a>
</td>

<td align=center width="25%">
<a href="lower.html" target="_top">Layer One</a>
</td>

<td align=center width="25%">
<a href="middle.html" target="_top">Layer Two</a>
</td>

<td align=center width="25%">
<a href="upper.html" target="_top">Layer Three</a>
</td>


</tr>
</table>



</body>
</html>