forked from smcameron/space-nerds-in-space
-
Notifications
You must be signed in to change notification settings - Fork 0
/
snis-protocol.html
245 lines (212 loc) · 9.74 KB
/
snis-protocol.html
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
<html>
<title>Space Nerds In Space Protocol</title>
<body>
<h1>Space Nerds In Space Protocol</h1>
<p>This file describes the protocol used by Space Nerds In Space.
<h2>1. Lobby protocol</h2>
<p>To establish connections between SNIS clients and SNIS game servers, the
Super Simple Game Lobby protocol is used. TBD: describe that.
<h2>2. Game protocol</h2>
<h3>2.1 General Overview</h3>
<p>Space Nerds In Space is a multi-player networked game which attempts to
simulate a starship bridge. The idea is several players in the same room
take on various roles such as Captain, Navigator, Weapons Officer, Science
Officer, Communications officer, etc. The design of the game is a client
server architecture. Each player runs a client which connects to the server,
making requests of the server, and receiving data about all aspects of the
game universe from the server. The server runs the authoritative simulation
of the game universe. Once an initial connection is established between
client and server, communication is done via a packetized protocol. Each
packet begins with an opcode to identify the type of packet, and the data
which follows the opcode depends on the type of packet.
<p>The server process has a main thread which runs the simulation of the
universe, and it also has two threads per client, one to read requests
from the client, and one to send updates to the client. The client
reader thread recieves packets from the client and interprets them and
updates the state of the universe as appropriate. The client writer
thread periodically examines the state of the universe to see what news
the client needs to be appraised of, and sends the appropriate packets to
update the client.
<p>The client process has a main thread running the user interface as well
as a reader and writer thread to communicate with the server. User actions
typically result in request packets being sent to the server. For example
the user may request to increase the throttle or to turn the ship right or
left. These inputs are packetized and sent to the server. The server
considers these inputs and simulates the universe. As time passes in the
server-side simulation, presumably the player's ship will speed up, or turn,
or whatever the case may be, and these changes will be communicated back
to the client by the per-client writer thread running on the server.
<h3>2.2 Conventions and primitive data types</h3>
<h4>2.2.1 Integer types</h4>
<p>All integer data types are transmitted in network byte order (big endian).
Only unsigned 8, 16 and 32 bit integer types are supported.
<h4>2.2.2 Floating point types</h4>
Floating point numbers (doubles) are converted to unsigned 32 bit integers prior to
being transmitted by a scaling algorithm that varies depending on the
expected range of the value. See esp. packed_buffer_append() and
packed_buffer_extract() in snis_marshal.c for authoritative information on
how the encodings are done.
<p>Only doubles are supported by the protocol. The protocol distinguishes
between signed and unsigned doubles.
<p>Positive (unsigned) doubles are converted to uint32_t's for transmission by this formula:
<pre>
return (uint32_t) ((d / scale) * (double) UINT32_MAX);
</pre>
and back to doubles upon receipt by this formula:
<pre>
return ((double) u * (double) scale) / (double) UINT32_MAX;
</pre>
where d (or u) is the double to be converted and scale is a uint32_t
with a value that is specific to the use case (e.g. typically 360 for
values that are angles in degrees, and 2000000 (UNIVERSE_DIM) for
spatial coordinates.
<p>If the value is signed, scale is an int32_t rather than a uint32_t
and INT32_MAX is used rather than UINT32_MAX.
<h4>2.2.3 Strings</h4>
<p>Strings are transmitted by encoding the length as a big endian
uint16_t preceding the bytes of the string.
<h4>2.2.4 "Raw" data</h4>
<p>There is a "raw" type which is encoded much as strings are.
The raw data is preceded by a big endian uint16_t indicating
the length of the raw data. After the 2-byte length field, the
raw data is transmitted verbatim. The raw data format is currently
only used for COMMS transmissions. (Note: revisit this and see if
the "raw" format should be eliminated.)
<h4>2.2.5 Generic packet format</h4>
SNIS packets typically have the following form:
<pre>
<p align=center><table border=1, width=75%>
<tr>
<td>
16-bit, big-endian opcode
</td>
</tr>
<tr>
<td>
Other data (depends on opcode).
</td>
</tr>
</table>
</pre>
<h3>2.3 Initial connection</h3>
<p>Once an SNIS client discovers the IP address and port of an SNIS server,
it can begin establishing a connection and the following sequence of events
occurs:
<ol>
<li>The client initiates a connection to the gameserver on a TCP socket
with TCP_NODELAY option set.
<li>Client transmits the 7 bytes indicating the SNIS protocol version
(currently, this is denoted by the string "SNIS001").
<li>Client sends an UPDATE PLAYER packet:
<pre>
<table align=center, width=75%, border=1>
<tr>
<td>u16</td>
<td>OPCODE = 104 (UPDATE PLAYER)</td>
</tr>
<tr>
<td>u32</td><td>ROLE (bitmap of roles)</td>
</tr>
<tr>
<td>u8 (20)</td>
<td>Ship name (20 chars, null terminated)</td>
</tr>
<tr>
<td>u8 (20)</td>
<td>Plaintext "password" (20 chars, null terminated)</td>
</tr>
</table>
</pre>
<li>This completes the client side connection initiation protocol.
The client then starts two threads, one to read from the gameserver
socket, and one to write to the gameserver socket.
</ol>
<p>On the server side, the connection initiation process is as follows:
<ol>
<li>Connection is accepted, TCP_NODELAY socket option is set.
<li>7 bytes are read and matched against "SNIS001" to verify the
protocol version. If they do not match, the connection is closed.
<li>UPDATE PLAYER packet is read, opcode is checked to be 104 (if
not connection is closed).
<li>The ship name and password fields are
forcibly null terminated in each fields last byte (19).
<li>The ship
name and password are verified to contain only alphanumeric
characters (via isalnum()) -- if not, connection is closed.
<li>The ship name and password are looked up to see if already present
in the game universe. If so, client is "joined" to the existing crew,
otherwise a new ship is created in the game universe and the client is
"joined" to that crew.
<li>A CLIENT_ID packet is sent to the client. NOTE: All objects in the
SNIS game universe have a 32-bit unsigned integer unique ID. The purpose
of the CLIENT_ID packet is for the server to let the client know the unique
ID of his own ship.
<pre>
<table align=center, width=75%, border=1>
<tr><td>u16</td><td>ID CLIENT (106)</td></tr>
<tr><td>u32</td><td>CLIENT SHIP ID</td></tr>
</table>
</pre>
</ol>
<h3>2.4 Operation Codes</h3>
<p>Operation codes ("op codes") are transmitted as unsigned 16 bit
big-endian integers. An authoritative list of opcodes may be found in snis_packet.h
<p align=center><table align=center, width=75%, border=1>
<tr><td>OPCODE_UPDATE_SHIP</td><td>100</td></tr>
<tr><td>OPCODE_UPDATE_STARBASE</td><td> 101</td></tr>
<tr><td>OPCODE_UPDATE_LASER</td><td> 102</td></tr>
<tr><td>OPCODE_UPDATE_TORPEDO</td><td> 103</td></tr>
<tr><td>OPCODE_UPDATE_PLAYER</td><td> 104</td></tr>
<tr><td>OPCODE_ACK_PLAYER</td><td> 105 </td></tr>
<tr><td>OPCODE_ID_CLIENT_SHIP</td><td> 106</td></tr>
<tr><td>OPCODE_UPDATE_PLANET</td><td> 107</td></tr>
<tr><td>OPCODE_REQUEST_YAW</td><td> 108</td></tr>
<tr><td>OPCODE_REQUEST_THRUST</td><td> 109</td></tr>
<tr><td>OPCODE_REQUEST_GUNYAW</td><td> 110</td></tr>
<tr><td>OPCODE_REQUEST_TORPEDO</td><td> 111</td></tr>
<tr><td>OPCODE_DELETE_OBJECT</td><td> 112</td></tr>
<tr><td>OPCODE_UPDATE_EXPLOSION</td><td> 113</td></tr>
<tr><td>OPCODE_PLAY_SOUND</td><td> 114</td></tr>
<tr><td>OPCODE_REQUEST_SCIYAW</td><td> 115</td></tr>
<tr><td>OPCODE_REQUEST_SCIBEAMWIDTH</td><td> 116</td></tr>
<tr><td>OPCODE_UPDATE_SHIP2</td><td> 117</td></tr>
<tr><td>OPCODE_ECON_UPDATE_SHIP</td><td> 118</td></tr>
<tr><td>OPCODE_REQUEST_SHIP_SDATA</td><td> 119</td></tr>
<tr><td>OPCODE_SHIP_SDATA</td><td> 120</td></tr>
<tr><td>OPCODE_LOAD_TORPEDO</td><td> 121</td></tr>
<tr><td>OPCODE_REQUEST_PHASER</td><td> 122</td></tr>
<tr><td>OPCODE_REQUEST_THROTTLE</td><td> 123</td></tr>
<tr><td>OPCODE_REQUEST_MANEUVERING_PWR</td><td> 124</td></tr>
<tr><td>OPCODE_REQUEST_WARP_PWR</td><td> 125</td></tr>
<tr><td>OPCODE_REQUEST_IMPULSE_PWR</td><td> 126</td></tr>
<tr><td>OPCODE_REQUEST_SHIELDS_PWR</td><td> 127</td></tr>
<tr><td>OPCODE_REQUEST_COMMS_PWR</td><td> 128</td></tr>
<tr><td>OPCODE_REQUEST_SENSORS_PWR</td><td> 129</td></tr>
<tr><td>OPCODE_REQUEST_PHASERBANKS_PWR</td><td> 130</td></tr>
<tr><td>OPCODE_REQUEST_SCIZOOM</td><td> 131</td></tr>
<tr><td>OPCODE_REQUEST_WARPDRIVE</td><td> 132</td></tr>
<tr><td>OPCODE_ENGAGE_WARP</td><td> 133</td></tr>
<tr><td>OPCODE_ROLE_ONSCREEN</td><td> 134</td></tr>
<tr><td>OPCODE_SCI_SELECT_TARGET</td><td> 135</td></tr>
<tr><td>OPCODE_UPDATE_DAMAGE</td><td> 136</td></tr>
<tr><td>OPCODE_REQUEST_LASER</td><td> 137</td></tr>
<tr><td>OPCODE_REQUEST_LASER_WAVELENGTH</td><td> 138</td></tr>
<tr><td>OPCODE_SCI_SELECT_COORDS</td><td> 139</td></tr>
<tr><td>OPCODE_REQUEST_SHIELD</td><td> 140</td></tr>
<tr><td>OPCODE_UPDATE_RESPAWN_TIME</td><td> 141</td></tr>
<tr><td>OPCODE_UPDATE_NETSTATS</td><td> 142</td></tr>
<tr><td>OPCODE_COMMS_TRANSMISSION</td><td> 143</td></tr>
<tr><td>OPCODE_WARP_LIMBO</td><td> 144 </td></tr>
<tr><td>OPCODE_DEMON_COMMAND</td><td> 145</td></tr>
<tr><td>OPCODE_UPDATE_NEBULA</td><td> 146</td></tr>
<tr><td>OPCODE_DAMCON_OBJ_UPDATE</td><td> 147</td></tr>
<tr><td>OPCODE_REQUEST_ROBOT_YAW</td><td> 148</td></tr>
<tr><td>OPCODE_REQUEST_ROBOT_THRUST</td><td> 149</td></tr>
<tr><td>OPCODE_DAMCON_SOCKET_UPDATE</td><td> 150</td></tr>
<tr><td>OPCODE_POS_SHIP</td><td> 200</td></tr>
<tr><td>OPCODE_POS_STARBASE</td><td> 201</td></tr>
<tr><td>OPCODE_POS_LASER</td><td> 202</td></tr>
<tr><td>OPCODE_NOOP</td><td> 0xffff</td></tr>
</table>
</body>
</html>