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