|
1 #include "stdafx.h" |
|
2 #include "network_data.h" |
|
3 |
|
4 // Is the network enabled? |
|
5 #ifdef ENABLE_NETWORK |
|
6 |
|
7 #include "table/strings.h" |
|
8 #include "network_client.h" |
|
9 #include "command.h" |
|
10 #include "callback_table.h" |
|
11 |
|
12 // This files handles the send/receive of all packets |
|
13 |
|
14 // Create a packet for sending |
|
15 Packet *NetworkSend_Init(PacketType type) |
|
16 { |
|
17 Packet *packet = malloc(sizeof(Packet)); |
|
18 // An error is inplace here, because it simply means we ran out of memory. |
|
19 if (packet == NULL) error("Failed to allocate Packet"); |
|
20 |
|
21 // Skip the size so we can write that in before sending the packet |
|
22 packet->size = sizeof(packet->size); |
|
23 packet->buffer[packet->size++] = type; |
|
24 packet->pos = 0; |
|
25 |
|
26 return packet; |
|
27 } |
|
28 |
|
29 // The next couple of functions make sure we can send |
|
30 // uint8, uint16, uint32 and uint64 endian-safe |
|
31 // over the network. The order it uses is: |
|
32 // |
|
33 // 1 2 3 4 |
|
34 // |
|
35 |
|
36 void NetworkSend_uint8(Packet *packet, uint8 data) |
|
37 { |
|
38 assert(packet->size < sizeof(packet->buffer) - sizeof(data)); |
|
39 packet->buffer[packet->size++] = data & 0xFF; |
|
40 } |
|
41 |
|
42 void NetworkSend_uint16(Packet *packet, uint16 data) |
|
43 { |
|
44 assert(packet->size < sizeof(packet->buffer) - sizeof(data)); |
|
45 packet->buffer[packet->size++] = data & 0xFF; |
|
46 packet->buffer[packet->size++] = (data >> 8) & 0xFF; |
|
47 } |
|
48 |
|
49 void NetworkSend_uint32(Packet *packet, uint32 data) |
|
50 { |
|
51 assert(packet->size < sizeof(packet->buffer) - sizeof(data)); |
|
52 packet->buffer[packet->size++] = data & 0xFF; |
|
53 packet->buffer[packet->size++] = (data >>= 8) & 0xFF; |
|
54 packet->buffer[packet->size++] = (data >>= 8) & 0xFF; |
|
55 packet->buffer[packet->size++] = (data >> 8) & 0xFF; |
|
56 } |
|
57 |
|
58 void NetworkSend_uint64(Packet *packet, uint64 data) |
|
59 { |
|
60 assert(packet->size < sizeof(packet->buffer) - sizeof(data)); |
|
61 packet->buffer[packet->size++] = data & 0xFF; |
|
62 packet->buffer[packet->size++] = (data >>= 8) & 0xFF; |
|
63 packet->buffer[packet->size++] = (data >>= 8) & 0xFF; |
|
64 packet->buffer[packet->size++] = (data >>= 8) & 0xFF; |
|
65 packet->buffer[packet->size++] = (data >>= 8) & 0xFF; |
|
66 packet->buffer[packet->size++] = (data >>= 8) & 0xFF; |
|
67 packet->buffer[packet->size++] = (data >>= 8) & 0xFF; |
|
68 packet->buffer[packet->size++] = (data >> 8) & 0xFF; |
|
69 } |
|
70 |
|
71 // Sends a string over the network. It sends out |
|
72 // the string + '\0'. No size-byte or something. |
|
73 void NetworkSend_string(Packet *packet, const char* data) |
|
74 { |
|
75 assert(data != NULL); |
|
76 assert(packet->size < sizeof(packet->buffer) - strlen(data) - 1); |
|
77 while ((packet->buffer[packet->size++] = *data++) != '\0') {} |
|
78 } |
|
79 |
|
80 // If PacketSize changes of size, you have to change the 2 packet->size |
|
81 // lines below matching the size of packet->size/PacketSize! |
|
82 // (line 'packet->buffer[0] = packet->size & 0xFF;' and below) |
|
83 assert_compile(sizeof(PacketSize) == 2); |
|
84 |
|
85 // This function puts the packet in the send-queue and it is send |
|
86 // as soon as possible |
|
87 // (that is: the next tick, or maybe one tick later if the |
|
88 // OS-network-buffer is full) |
|
89 void NetworkSend_Packet(Packet *packet, ClientState *cs) |
|
90 { |
|
91 Packet *p; |
|
92 assert(packet != NULL); |
|
93 |
|
94 packet->pos = 0; |
|
95 packet->next = NULL; |
|
96 |
|
97 packet->buffer[0] = packet->size & 0xFF; |
|
98 packet->buffer[1] = packet->size >> 8; |
|
99 |
|
100 // Locate last packet buffered for the client |
|
101 p = cs->packet_queue; |
|
102 if (p == NULL) { |
|
103 // No packets yet |
|
104 cs->packet_queue = packet; |
|
105 } else { |
|
106 // Skip to the last packet |
|
107 while (p->next != NULL) p = p->next; |
|
108 p->next = packet; |
|
109 } |
|
110 } |
|
111 |
|
112 // Functions to help NetworkRecv_Packet/NetworkSend_Packet a bit |
|
113 // A socket can make errors. When that happens |
|
114 // this handles what to do. |
|
115 // For clients: close connection and drop back to main-menu |
|
116 // For servers: close connection and that is it |
|
117 NetworkRecvStatus CloseConnection(ClientState *cs) |
|
118 { |
|
119 CloseClient(cs); |
|
120 |
|
121 // Clients drop back to the main menu |
|
122 if (!_network_server) { |
|
123 _switch_mode = SM_MENU; |
|
124 _networking = false; |
|
125 _switch_mode_errorstr = STR_NETWORK_ERR_LOSTCONNECTION; |
|
126 |
|
127 return NETWORK_RECV_STATUS_CONN_LOST; |
|
128 } |
|
129 |
|
130 return NETWORK_RECV_STATUS_OKAY; |
|
131 } |
|
132 |
|
133 // Sends all the buffered packets out for this client |
|
134 // it stops when: |
|
135 // 1) all packets are send (queue is empty) |
|
136 // 2) the OS reports back that it can not send any more |
|
137 // data right now (full network-buffer, it happens ;)) |
|
138 // 3) sending took too long |
|
139 bool NetworkSend_Packets(ClientState *cs) |
|
140 { |
|
141 ssize_t res; |
|
142 Packet *p; |
|
143 |
|
144 // We can not write to this socket!! |
|
145 if (!cs->writable) return false; |
|
146 if (cs->socket == INVALID_SOCKET) return false; |
|
147 |
|
148 p = cs->packet_queue; |
|
149 while (p != NULL) { |
|
150 res = send(cs->socket, p->buffer + p->pos, p->size - p->pos, 0); |
|
151 if (res == -1) { |
|
152 int err = GET_LAST_ERROR(); |
|
153 if (err != EWOULDBLOCK) { |
|
154 // Something went wrong.. close client! |
|
155 DEBUG(net, 0) ("[NET] send() failed with error %d", err); |
|
156 CloseConnection(cs); |
|
157 return false; |
|
158 } |
|
159 return true; |
|
160 } |
|
161 if (res == 0) { |
|
162 // Client/server has left us :( |
|
163 CloseConnection(cs); |
|
164 return false; |
|
165 } |
|
166 |
|
167 p->pos += res; |
|
168 |
|
169 // Is this packet sent? |
|
170 if (p->pos == p->size) { |
|
171 // Go to the next packet |
|
172 cs->packet_queue = p->next; |
|
173 free(p); |
|
174 p = cs->packet_queue; |
|
175 } else |
|
176 return true; |
|
177 } |
|
178 |
|
179 return true; |
|
180 } |
|
181 |
|
182 |
|
183 // Receiving commands |
|
184 // Again, the next couple of functions are endian-safe |
|
185 // see the comment around NetworkSend_uint8 for more info. |
|
186 uint8 NetworkRecv_uint8(Packet *packet) |
|
187 { |
|
188 return packet->buffer[packet->pos++]; |
|
189 } |
|
190 |
|
191 uint16 NetworkRecv_uint16(Packet *packet) |
|
192 { |
|
193 uint16 n; |
|
194 n = (uint16)packet->buffer[packet->pos++]; |
|
195 n += (uint16)packet->buffer[packet->pos++] << 8; |
|
196 return n; |
|
197 } |
|
198 |
|
199 uint32 NetworkRecv_uint32(Packet *packet) |
|
200 { |
|
201 uint32 n; |
|
202 n = (uint32)packet->buffer[packet->pos++]; |
|
203 n += (uint32)packet->buffer[packet->pos++] << 8; |
|
204 n += (uint32)packet->buffer[packet->pos++] << 16; |
|
205 n += (uint32)packet->buffer[packet->pos++] << 24; |
|
206 return n; |
|
207 } |
|
208 |
|
209 uint64 NetworkRecv_uint64(Packet *packet) |
|
210 { |
|
211 uint64 n; |
|
212 n = (uint64)packet->buffer[packet->pos++]; |
|
213 n += (uint64)packet->buffer[packet->pos++] << 8; |
|
214 n += (uint64)packet->buffer[packet->pos++] << 16; |
|
215 n += (uint64)packet->buffer[packet->pos++] << 24; |
|
216 n += (uint64)packet->buffer[packet->pos++] << 32; |
|
217 n += (uint64)packet->buffer[packet->pos++] << 40; |
|
218 n += (uint64)packet->buffer[packet->pos++] << 48; |
|
219 n += (uint64)packet->buffer[packet->pos++] << 56; |
|
220 return n; |
|
221 } |
|
222 |
|
223 // Reads a string till it finds a '\0' in the stream |
|
224 void NetworkRecv_string(Packet *p, char* buffer, size_t size) |
|
225 { |
|
226 int pos; |
|
227 pos = p->pos; |
|
228 while (--size > 0 && pos < p->size && (*buffer++ = p->buffer[pos++]) != '\0') {} |
|
229 if (size == 0 || pos == p->size) |
|
230 { |
|
231 *buffer = '\0'; |
|
232 // If size was sooner to zero then the string in the stream |
|
233 // skip till the \0, so the packet can be read out correctly for the rest |
|
234 while (pos < p->size && p->buffer[pos] != '\0') ++pos; |
|
235 ++pos; |
|
236 } |
|
237 p->pos = pos; |
|
238 } |
|
239 |
|
240 // If PacketSize changes of size, you have to change the 2 packet->size |
|
241 // lines below matching the size of packet->size/PacketSize! |
|
242 // (the line: 'p->size = (uint16)p->buffer[0];' and below) |
|
243 assert_compile(sizeof(PacketSize) == 2); |
|
244 |
|
245 Packet *NetworkRecv_Packet(ClientState *cs, NetworkRecvStatus *status) |
|
246 { |
|
247 ssize_t res; |
|
248 Packet *p; |
|
249 |
|
250 *status = NETWORK_RECV_STATUS_OKAY; |
|
251 |
|
252 if (cs->socket == INVALID_SOCKET) return NULL; |
|
253 |
|
254 if (cs->packet_recv == NULL) { |
|
255 cs->packet_recv = malloc(sizeof(Packet)); |
|
256 if (cs->packet_recv == NULL) error("Failed to allocate packet"); |
|
257 // Set pos to zero! |
|
258 cs->packet_recv->pos = 0; |
|
259 cs->packet_recv->size = 0; // Can be ommited, just for safety reasons |
|
260 } |
|
261 |
|
262 p = cs->packet_recv; |
|
263 |
|
264 // Read packet size |
|
265 if (p->pos < sizeof(PacketSize)) { |
|
266 while (p->pos < sizeof(PacketSize)) { |
|
267 // Read the size of the packet |
|
268 res = recv(cs->socket, p->buffer + p->pos, sizeof(PacketSize) - p->pos, 0); |
|
269 if (res == -1) { |
|
270 int err = GET_LAST_ERROR(); |
|
271 if (err != EWOULDBLOCK) { |
|
272 // Something went wrong.. |
|
273 if (err != 104) // 104 is Connection Reset by Peer |
|
274 DEBUG(net, 0) ("[NET] recv() failed with error %d", err); |
|
275 *status = CloseConnection(cs); |
|
276 return NULL; |
|
277 } |
|
278 // Connection would block, so stop for now |
|
279 return NULL; |
|
280 } |
|
281 if (res == 0) { |
|
282 // Client/server has left us :( |
|
283 *status = CloseConnection(cs); |
|
284 return NULL; |
|
285 } |
|
286 p->pos += res; |
|
287 } |
|
288 |
|
289 p->size = (uint16)p->buffer[0]; |
|
290 p->size += (uint16)p->buffer[1] << 8; |
|
291 |
|
292 if (p->size > SEND_MTU) { |
|
293 *status = CloseConnection(cs); |
|
294 return NULL; |
|
295 } |
|
296 } |
|
297 |
|
298 // Read rest of packet |
|
299 while (p->pos < p->size) { |
|
300 res = recv(cs->socket, p->buffer + p->pos, p->size - p->pos, 0); |
|
301 if (res == -1) { |
|
302 int err = GET_LAST_ERROR(); |
|
303 if (err != EWOULDBLOCK) { |
|
304 // Something went wrong.. |
|
305 if (err != 104) // 104 is Connection Reset by Peer |
|
306 DEBUG(net, 0) ("[NET] recv() failed with error %d", err); |
|
307 *status = CloseConnection(cs); |
|
308 return NULL; |
|
309 } |
|
310 // Connection would block |
|
311 return NULL; |
|
312 } |
|
313 if (res == 0) { |
|
314 // Client/server has left us :( |
|
315 *status = CloseConnection(cs); |
|
316 return NULL; |
|
317 } |
|
318 |
|
319 p->pos += res; |
|
320 } |
|
321 |
|
322 // We have a complete packet, return it! |
|
323 p->pos = 2; |
|
324 p->next = NULL; // Should not be needed, but who knows... |
|
325 |
|
326 // Prepare for receiving a new packet |
|
327 cs->packet_recv = NULL; |
|
328 |
|
329 return p; |
|
330 } |
|
331 |
|
332 // Add a command to the local command queue |
|
333 void NetworkAddCommandQueue(ClientState *cs, CommandPacket *cp) |
|
334 { |
|
335 CommandPacket *new_cp = malloc(sizeof(CommandPacket)); |
|
336 |
|
337 *new_cp = *cp; |
|
338 |
|
339 if (cs->command_queue == NULL) |
|
340 cs->command_queue = new_cp; |
|
341 else { |
|
342 CommandPacket *c = cs->command_queue; |
|
343 while (c->next != NULL) c = c->next; |
|
344 c->next = new_cp; |
|
345 } |
|
346 } |
|
347 |
|
348 // If this fails, make sure you change the following line below: |
|
349 // 'memcpy(qp->dp, _decode_parameters, 10 * sizeof(uint32));' |
|
350 // Also, in network_data.h, change the size of CommandPacket->dp! |
|
351 // (this protection is there to make sure in network.h dp is of the right size!) |
|
352 assert_compile(sizeof(_decode_parameters) == 20 * sizeof(uint32)); |
|
353 |
|
354 // Prepare a DoCommand to be send over the network |
|
355 void NetworkSend_Command(uint32 tile, uint32 p1, uint32 p2, uint32 cmd, CommandCallback *callback) |
|
356 { |
|
357 CommandPacket *c = malloc(sizeof(CommandPacket)); |
|
358 byte temp_callback; |
|
359 |
|
360 c->player = _local_player; |
|
361 c->next = NULL; |
|
362 c->tile = tile; |
|
363 c->p1 = p1; |
|
364 c->p2 = p2; |
|
365 c->cmd = cmd; |
|
366 c->callback = 0; |
|
367 |
|
368 temp_callback = 0; |
|
369 |
|
370 while (temp_callback < _callback_table_count && _callback_table[temp_callback] != callback) |
|
371 temp_callback++; |
|
372 if (temp_callback == _callback_table_count) { |
|
373 DEBUG(net, 0) ("[NET] Unknown callback. (Pointer: %p) No callback sent.", callback); |
|
374 temp_callback = 0; /* _callback_table[0] == NULL */ |
|
375 } |
|
376 |
|
377 if (_network_server) { |
|
378 // We are the server, so set the command to be executed next possible frame |
|
379 c->frame = _frame_counter_max + 1; |
|
380 } else { |
|
381 c->frame = 0; // The client can't tell which frame, so just make it 0 |
|
382 } |
|
383 |
|
384 // Copy the _decode_parameters to dp |
|
385 memcpy(c->dp, _decode_parameters, 20 * sizeof(uint32)); |
|
386 |
|
387 if (_network_server) { |
|
388 // If we are the server, we queue the command in our 'special' queue. |
|
389 // In theory, we could execute the command right away, but then the |
|
390 // client on the server can do everything 1 tick faster then others. |
|
391 // So to keep the game fair, we delay the command with 1 tick |
|
392 // which gives about the same speed as most clients. |
|
393 ClientState *cs; |
|
394 |
|
395 // And we queue it for delivery to the clients |
|
396 FOR_ALL_CLIENTS(cs) { |
|
397 if (cs->status > STATUS_AUTH) { |
|
398 NetworkAddCommandQueue(cs, c); |
|
399 } |
|
400 } |
|
401 |
|
402 // Only the server gets the callback, because clients should not get them |
|
403 c->callback = temp_callback; |
|
404 if (_local_command_queue == NULL) { |
|
405 _local_command_queue = c; |
|
406 } else { |
|
407 // Find last packet |
|
408 CommandPacket *cp = _local_command_queue; |
|
409 while (cp->next != NULL) cp = cp->next; |
|
410 cp->next = c; |
|
411 } |
|
412 |
|
413 return; |
|
414 } |
|
415 |
|
416 // Clients send their command to the server and forget all about the packet |
|
417 c->callback = temp_callback; |
|
418 SEND_COMMAND(PACKET_CLIENT_COMMAND)(c); |
|
419 } |
|
420 |
|
421 // Execute a DoCommand we received from the network |
|
422 void NetworkExecuteCommand(CommandPacket *cp) |
|
423 { |
|
424 _current_player = cp->player; |
|
425 memcpy(_decode_parameters, cp->dp, sizeof(cp->dp)); |
|
426 /* cp->callback is unsigned. so we don't need to do lower bounds checking. */ |
|
427 if (cp->callback > _callback_table_count) { |
|
428 DEBUG(net,0) ("[NET] Received out-of-bounds callback! (%d)", cp->callback); |
|
429 cp->callback = 0; |
|
430 } |
|
431 DoCommandP(cp->tile, cp->p1, cp->p2, _callback_table[cp->callback], cp->cmd | CMD_NETWORK_COMMAND); |
|
432 } |
|
433 |
|
434 #endif /* ENABLE_NETWORK */ |