|
1 /* $Id$ */ |
|
2 |
|
3 #include "../stdafx.h" |
|
4 #include "network_data.h" |
|
5 |
|
6 #if defined(WITH_REV) |
|
7 extern const char _openttd_revision[]; |
|
8 #elif defined(WITH_REV_HACK) |
|
9 #define WITH_REV |
|
10 const char _openttd_revision[] = WITH_REV_HACK; |
|
11 #else |
|
12 const char _openttd_revision[] = NOREV_STRING; |
|
13 #endif |
|
14 |
|
15 |
|
16 #ifdef ENABLE_NETWORK |
|
17 |
|
18 #include "../openttd.h" |
|
19 #include "../debug.h" |
|
20 #include "../functions.h" |
|
21 #include "../string.h" |
|
22 #include "../strings.h" |
|
23 #include "../map.h" |
|
24 #include "../command.h" |
|
25 #include "../variables.h" |
|
26 #include "../date.h" |
|
27 #include "../newgrf_config.h" |
|
28 #include "../table/strings.h" |
|
29 #include "network_client.h" |
|
30 #include "network_server.h" |
|
31 #include "network_udp.h" |
|
32 #include "network_gamelist.h" |
|
33 #include "core/udp.h" |
|
34 #include "core/tcp.h" |
|
35 #include "network_gui.h" |
|
36 #include "../console.h" /* IConsoleCmdExec */ |
|
37 #include <stdarg.h> /* va_list */ |
|
38 #include "../md5.h" |
|
39 |
|
40 #ifdef __MORPHOS__ |
|
41 // the library base is required here |
|
42 struct Library *SocketBase = NULL; |
|
43 #endif |
|
44 |
|
45 // The listen socket for the server |
|
46 static SOCKET _listensocket; |
|
47 |
|
48 // The amount of clients connected |
|
49 static byte _network_clients_connected = 0; |
|
50 // The index counter for new clients (is never decreased) |
|
51 static uint16 _network_client_index = NETWORK_SERVER_INDEX + 1; |
|
52 |
|
53 /* Some externs / forwards */ |
|
54 extern void StateGameLoop(void); |
|
55 |
|
56 // Function that looks up the CI for a given client-index |
|
57 NetworkClientInfo *NetworkFindClientInfoFromIndex(uint16 client_index) |
|
58 { |
|
59 NetworkClientInfo *ci; |
|
60 |
|
61 for (ci = _network_client_info; ci != endof(_network_client_info); ci++) { |
|
62 if (ci->client_index == client_index) return ci; |
|
63 } |
|
64 |
|
65 return NULL; |
|
66 } |
|
67 |
|
68 /** Return the CI for a given IP |
|
69 * @param ip IP of the client we are looking for. This must be in string-format |
|
70 * @return return a pointer to the corresponding NetworkClientInfo struct or NULL on failure */ |
|
71 NetworkClientInfo *NetworkFindClientInfoFromIP(const char *ip) |
|
72 { |
|
73 NetworkClientInfo *ci; |
|
74 uint32 ip_number = inet_addr(ip); |
|
75 |
|
76 for (ci = _network_client_info; ci != endof(_network_client_info); ci++) { |
|
77 if (ci->client_ip == ip_number) return ci; |
|
78 } |
|
79 |
|
80 return NULL; |
|
81 } |
|
82 |
|
83 // Function that looks up the CS for a given client-index |
|
84 NetworkClientState *NetworkFindClientStateFromIndex(uint16 client_index) |
|
85 { |
|
86 NetworkClientState *cs; |
|
87 |
|
88 for (cs = _clients; cs != &_clients[MAX_CLIENT_INFO]; cs++) { |
|
89 if (cs->index == client_index) return cs; |
|
90 } |
|
91 |
|
92 return NULL; |
|
93 } |
|
94 |
|
95 // NetworkGetClientName is a server-safe function to get the name of the client |
|
96 // if the user did not send it yet, Client #<no> is used. |
|
97 void NetworkGetClientName(char *client_name, size_t size, const NetworkClientState *cs) |
|
98 { |
|
99 const NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs); |
|
100 |
|
101 if (ci->client_name[0] == '\0') { |
|
102 snprintf(client_name, size, "Client #%4d", cs->index); |
|
103 } else { |
|
104 ttd_strlcpy(client_name, ci->client_name, size); |
|
105 } |
|
106 } |
|
107 |
|
108 byte NetworkSpectatorCount(void) |
|
109 { |
|
110 const NetworkClientState *cs; |
|
111 byte count = 0; |
|
112 |
|
113 FOR_ALL_CLIENTS(cs) { |
|
114 if (DEREF_CLIENT_INFO(cs)->client_playas == PLAYER_SPECTATOR) count++; |
|
115 } |
|
116 |
|
117 return count; |
|
118 } |
|
119 |
|
120 // This puts a text-message to the console, or in the future, the chat-box, |
|
121 // (to keep it all a bit more general) |
|
122 // If 'self_send' is true, this is the client who is sending the message |
|
123 void CDECL NetworkTextMessage(NetworkAction action, uint16 color, bool self_send, const char *name, const char *str, ...) |
|
124 { |
|
125 char buf[1024]; |
|
126 va_list va; |
|
127 const int duration = 10; // Game days the messages stay visible |
|
128 char message[1024]; |
|
129 char temp[1024]; |
|
130 |
|
131 va_start(va, str); |
|
132 vsnprintf(buf, lengthof(buf), str, va); |
|
133 va_end(va); |
|
134 |
|
135 switch (action) { |
|
136 case NETWORK_ACTION_SERVER_MESSAGE: |
|
137 color = 1; |
|
138 snprintf(message, sizeof(message), "*** %s", buf); |
|
139 break; |
|
140 case NETWORK_ACTION_JOIN: |
|
141 color = 1; |
|
142 GetString(temp, STR_NETWORK_CLIENT_JOINED, lastof(temp)); |
|
143 snprintf(message, sizeof(message), "*** %s %s", name, temp); |
|
144 break; |
|
145 case NETWORK_ACTION_LEAVE: |
|
146 color = 1; |
|
147 GetString(temp, STR_NETWORK_ERR_LEFT, lastof(temp)); |
|
148 snprintf(message, sizeof(message), "*** %s %s (%s)", name, temp, buf); |
|
149 break; |
|
150 case NETWORK_ACTION_GIVE_MONEY: |
|
151 if (self_send) { |
|
152 SetDParamStr(0, name); |
|
153 SetDParam(1, atoi(buf)); |
|
154 GetString(temp, STR_NETWORK_GAVE_MONEY_AWAY, lastof(temp)); |
|
155 snprintf(message, sizeof(message), "*** %s", temp); |
|
156 } else { |
|
157 SetDParam(0, atoi(buf)); |
|
158 GetString(temp, STR_NETWORK_GIVE_MONEY, lastof(temp)); |
|
159 snprintf(message, sizeof(message), "*** %s %s", name, temp); |
|
160 } |
|
161 break; |
|
162 case NETWORK_ACTION_NAME_CHANGE: |
|
163 GetString(temp, STR_NETWORK_NAME_CHANGE, lastof(temp)); |
|
164 snprintf(message, sizeof(message), "*** %s %s %s", name, temp, buf); |
|
165 break; |
|
166 case NETWORK_ACTION_CHAT_COMPANY: |
|
167 SetDParamStr(0, name); |
|
168 SetDParamStr(1, buf); |
|
169 GetString(temp, self_send ? STR_NETWORK_CHAT_TO_COMPANY : STR_NETWORK_CHAT_COMPANY, lastof(temp)); |
|
170 ttd_strlcpy(message, temp, sizeof(message)); |
|
171 break; |
|
172 case NETWORK_ACTION_CHAT_CLIENT: |
|
173 SetDParamStr(0, name); |
|
174 SetDParamStr(1, buf); |
|
175 GetString(temp, self_send ? STR_NETWORK_CHAT_TO_CLIENT : STR_NETWORK_CHAT_CLIENT, lastof(temp)); |
|
176 ttd_strlcpy(message, temp, sizeof(message)); |
|
177 break; |
|
178 default: |
|
179 SetDParamStr(0, name); |
|
180 SetDParamStr(1, buf); |
|
181 GetString(temp, STR_NETWORK_CHAT_ALL, lastof(temp)); |
|
182 ttd_strlcpy(message, temp, sizeof(message)); |
|
183 break; |
|
184 } |
|
185 |
|
186 IConsolePrintF(color, "%s", message); |
|
187 AddTextMessage(color, duration, "%s", message); |
|
188 } |
|
189 |
|
190 // Calculate the frame-lag of a client |
|
191 uint NetworkCalculateLag(const NetworkClientState *cs) |
|
192 { |
|
193 int lag = cs->last_frame_server - cs->last_frame; |
|
194 // This client has missed his ACK packet after 1 DAY_TICKS.. |
|
195 // so we increase his lag for every frame that passes! |
|
196 // The packet can be out by a max of _net_frame_freq |
|
197 if (cs->last_frame_server + DAY_TICKS + _network_frame_freq < _frame_counter) |
|
198 lag += _frame_counter - (cs->last_frame_server + DAY_TICKS + _network_frame_freq); |
|
199 |
|
200 return lag; |
|
201 } |
|
202 |
|
203 |
|
204 // There was a non-recoverable error, drop back to the main menu with a nice |
|
205 // error |
|
206 static void NetworkError(StringID error_string) |
|
207 { |
|
208 _switch_mode = SM_MENU; |
|
209 _switch_mode_errorstr = error_string; |
|
210 } |
|
211 |
|
212 static void ClientStartError(const char *error) |
|
213 { |
|
214 DEBUG(net, 0, "[client] could not start network: %s",error); |
|
215 NetworkError(STR_NETWORK_ERR_CLIENT_START); |
|
216 } |
|
217 |
|
218 static void ServerStartError(const char *error) |
|
219 { |
|
220 DEBUG(net, 0, "[server] could not start network: %s",error); |
|
221 NetworkError(STR_NETWORK_ERR_SERVER_START); |
|
222 } |
|
223 |
|
224 static void NetworkClientError(NetworkRecvStatus res, NetworkClientState* cs) |
|
225 { |
|
226 // First, send a CLIENT_ERROR to the server, so he knows we are |
|
227 // disconnection (and why!) |
|
228 NetworkErrorCode errorno; |
|
229 |
|
230 // We just want to close the connection.. |
|
231 if (res == NETWORK_RECV_STATUS_CLOSE_QUERY) { |
|
232 cs->has_quit = true; |
|
233 NetworkCloseClient(cs); |
|
234 _networking = false; |
|
235 |
|
236 DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0); |
|
237 return; |
|
238 } |
|
239 |
|
240 switch (res) { |
|
241 case NETWORK_RECV_STATUS_DESYNC: errorno = NETWORK_ERROR_DESYNC; break; |
|
242 case NETWORK_RECV_STATUS_SAVEGAME: errorno = NETWORK_ERROR_SAVEGAME_FAILED; break; |
|
243 default: errorno = NETWORK_ERROR_GENERAL; break; |
|
244 } |
|
245 // This means we fucked up and the server closed the connection |
|
246 if (res != NETWORK_RECV_STATUS_SERVER_ERROR && res != NETWORK_RECV_STATUS_SERVER_FULL && |
|
247 res != NETWORK_RECV_STATUS_SERVER_BANNED) { |
|
248 SEND_COMMAND(PACKET_CLIENT_ERROR)(errorno); |
|
249 |
|
250 // Dequeue all commands before closing the socket |
|
251 NetworkSend_Packets(DEREF_CLIENT(0)); |
|
252 } |
|
253 |
|
254 _switch_mode = SM_MENU; |
|
255 NetworkCloseClient(cs); |
|
256 _networking = false; |
|
257 } |
|
258 |
|
259 /** Retrieve a string representation of an internal error number |
|
260 * @param buf buffer where the error message will be stored |
|
261 * @param err NetworkErrorCode |
|
262 * @return returns a pointer to the error message (buf) */ |
|
263 char* GetNetworkErrorMsg(char* buf, NetworkErrorCode err, const char* last) |
|
264 { |
|
265 /* List of possible network errors, used by |
|
266 * PACKET_SERVER_ERROR and PACKET_CLIENT_ERROR */ |
|
267 static const StringID network_error_strings[] = { |
|
268 STR_NETWORK_ERR_CLIENT_GENERAL, |
|
269 STR_NETWORK_ERR_CLIENT_DESYNC, |
|
270 STR_NETWORK_ERR_CLIENT_SAVEGAME, |
|
271 STR_NETWORK_ERR_CLIENT_CONNECTION_LOST, |
|
272 STR_NETWORK_ERR_CLIENT_PROTOCOL_ERROR, |
|
273 STR_NETWORK_ERR_CLIENT_NOT_AUTHORIZED, |
|
274 STR_NETWORK_ERR_CLIENT_NOT_EXPECTED, |
|
275 STR_NETWORK_ERR_CLIENT_WRONG_REVISION, |
|
276 STR_NETWORK_ERR_CLIENT_NAME_IN_USE, |
|
277 STR_NETWORK_ERR_CLIENT_WRONG_PASSWORD, |
|
278 STR_NETWORK_ERR_CLIENT_PLAYER_MISMATCH, |
|
279 STR_NETWORK_ERR_CLIENT_KICKED, |
|
280 STR_NETWORK_ERR_CLIENT_CHEATER, |
|
281 STR_NETWORK_ERR_CLIENT_SERVER_FULL |
|
282 }; |
|
283 |
|
284 if (err >= lengthof(network_error_strings)) err = 0; |
|
285 |
|
286 return GetString(buf, network_error_strings[err], last); |
|
287 } |
|
288 |
|
289 /* Count the number of active clients connected */ |
|
290 static uint NetworkCountPlayers(void) |
|
291 { |
|
292 const NetworkClientState *cs; |
|
293 uint count = 0; |
|
294 |
|
295 FOR_ALL_CLIENTS(cs) { |
|
296 const NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs); |
|
297 if (IsValidPlayer(ci->client_playas)) count++; |
|
298 } |
|
299 |
|
300 return count; |
|
301 } |
|
302 |
|
303 static bool _min_players_paused = false; |
|
304 |
|
305 /* Check if the minimum number of players has been reached and pause or unpause the game as appropriate */ |
|
306 void CheckMinPlayers(void) |
|
307 { |
|
308 if (!_network_dedicated) return; |
|
309 |
|
310 if (NetworkCountPlayers() < _network_min_players) { |
|
311 if (_min_players_paused) return; |
|
312 |
|
313 _min_players_paused = true; |
|
314 DoCommandP(0, 1, 0, NULL, CMD_PAUSE); |
|
315 NetworkServer_HandleChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "Game paused (not enough players)", NETWORK_SERVER_INDEX); |
|
316 } else { |
|
317 if (!_min_players_paused) return; |
|
318 |
|
319 _min_players_paused = false; |
|
320 DoCommandP(0, 0, 0, NULL, CMD_PAUSE); |
|
321 NetworkServer_HandleChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "Game unpaused (enough players)", NETWORK_SERVER_INDEX); |
|
322 } |
|
323 } |
|
324 |
|
325 // Find all IP-aliases for this host |
|
326 static void NetworkFindIPs(void) |
|
327 { |
|
328 int i; |
|
329 |
|
330 #if defined(BEOS_NET_SERVER) /* doesn't have neither getifaddrs or net/if.h */ |
|
331 /* Based on Andrew Bachmann's netstat+.c. Big thanks to him! */ |
|
332 int _netstat(int fd, char **output, int verbose); |
|
333 |
|
334 int seek_past_header(char **pos, const char *header) { |
|
335 char *new_pos = strstr(*pos, header); |
|
336 if (new_pos == 0) { |
|
337 return B_ERROR; |
|
338 } |
|
339 *pos += strlen(header) + new_pos - *pos + 1; |
|
340 return B_OK; |
|
341 } |
|
342 |
|
343 int output_length; |
|
344 char *output_pointer = NULL; |
|
345 char **output; |
|
346 int sock = socket(AF_INET, SOCK_DGRAM, 0); |
|
347 i = 0; |
|
348 |
|
349 // If something fails, make sure the list is empty |
|
350 _broadcast_list[0] = 0; |
|
351 |
|
352 if (sock < 0) { |
|
353 DEBUG(net, 0, "[core] error creating socket"); |
|
354 return; |
|
355 } |
|
356 |
|
357 output_length = _netstat(sock, &output_pointer, 1); |
|
358 if (output_length < 0) { |
|
359 DEBUG(net, 0, "[core] error running _netstat"); |
|
360 return; |
|
361 } |
|
362 |
|
363 output = &output_pointer; |
|
364 if (seek_past_header(output, "IP Interfaces:") == B_OK) { |
|
365 for (;;) { |
|
366 uint32 n, fields, read; |
|
367 uint8 i1, i2, i3, i4, j1, j2, j3, j4; |
|
368 struct in_addr inaddr; |
|
369 uint32 ip; |
|
370 uint32 netmask; |
|
371 |
|
372 fields = sscanf(*output, "%u: %hhu.%hhu.%hhu.%hhu, netmask %hhu.%hhu.%hhu.%hhu%n", |
|
373 &n, &i1,&i2,&i3,&i4, &j1,&j2,&j3,&j4, &read); |
|
374 read += 1; |
|
375 if (fields != 9) { |
|
376 break; |
|
377 } |
|
378 |
|
379 ip = (uint32)i1 << 24 | (uint32)i2 << 16 | (uint32)i3 << 8 | (uint32)i4; |
|
380 netmask = (uint32)j1 << 24 | (uint32)j2 << 16 | (uint32)j3 << 8 | (uint32)j4; |
|
381 |
|
382 if (ip != INADDR_LOOPBACK && ip != INADDR_ANY) { |
|
383 inaddr.s_addr = htonl(ip | ~netmask); |
|
384 _broadcast_list[i] = inaddr.s_addr; |
|
385 i++; |
|
386 } |
|
387 if (read < 0) { |
|
388 break; |
|
389 } |
|
390 *output += read; |
|
391 } |
|
392 /* XXX - Using either one of these crashes openttd heavily? - wber */ |
|
393 /*free(output_pointer);*/ |
|
394 /*free(output);*/ |
|
395 closesocket(sock); |
|
396 } |
|
397 #elif defined(HAVE_GETIFADDRS) |
|
398 struct ifaddrs *ifap, *ifa; |
|
399 |
|
400 // If something fails, make sure the list is empty |
|
401 _broadcast_list[0] = 0; |
|
402 |
|
403 if (getifaddrs(&ifap) != 0) |
|
404 return; |
|
405 |
|
406 i = 0; |
|
407 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { |
|
408 if (!(ifa->ifa_flags & IFF_BROADCAST)) continue; |
|
409 if (ifa->ifa_broadaddr == NULL) continue; |
|
410 if (ifa->ifa_broadaddr->sa_family != AF_INET) continue; |
|
411 _broadcast_list[i] = ((struct sockaddr_in*)ifa->ifa_broadaddr)->sin_addr.s_addr; |
|
412 i++; |
|
413 } |
|
414 freeifaddrs(ifap); |
|
415 |
|
416 #else /* not HAVE_GETIFADDRS */ |
|
417 SOCKET sock; |
|
418 #ifdef WIN32 |
|
419 DWORD len = 0; |
|
420 INTERFACE_INFO ifo[MAX_INTERFACES]; |
|
421 uint j; |
|
422 #else |
|
423 char buf[4 * 1024]; // Arbitrary buffer size |
|
424 struct ifconf ifconf; |
|
425 const char* buf_end; |
|
426 const char* p; |
|
427 #endif |
|
428 |
|
429 // If something fails, make sure the list is empty |
|
430 _broadcast_list[0] = 0; |
|
431 |
|
432 sock = socket(AF_INET, SOCK_DGRAM, 0); |
|
433 if (sock == INVALID_SOCKET) return; |
|
434 |
|
435 #ifdef WIN32 |
|
436 memset(&ifo[0], 0, sizeof(ifo)); |
|
437 if ((WSAIoctl(sock, SIO_GET_INTERFACE_LIST, NULL, 0, &ifo[0], sizeof(ifo), &len, NULL, NULL)) != 0) { |
|
438 closesocket(sock); |
|
439 return; |
|
440 } |
|
441 |
|
442 i = 0; |
|
443 for (j = 0; j < len / sizeof(*ifo); j++) { |
|
444 if (ifo[j].iiFlags & IFF_LOOPBACK) continue; |
|
445 if (!(ifo[j].iiFlags & IFF_BROADCAST)) continue; |
|
446 /* iiBroadcast is unusable, because it always seems to be set to |
|
447 * 255.255.255.255. |
|
448 */ |
|
449 _broadcast_list[i++] = |
|
450 ifo[j].iiAddress.AddressIn.sin_addr.s_addr | |
|
451 ~ifo[j].iiNetmask.AddressIn.sin_addr.s_addr; |
|
452 } |
|
453 #else |
|
454 ifconf.ifc_len = sizeof(buf); |
|
455 ifconf.ifc_buf = buf; |
|
456 if (ioctl(sock, SIOCGIFCONF, &ifconf) == -1) { |
|
457 closesocket(sock); |
|
458 return; |
|
459 } |
|
460 |
|
461 i = 0; |
|
462 buf_end = buf + ifconf.ifc_len; |
|
463 for (p = buf; p < buf_end;) { |
|
464 const struct ifreq* req = (const struct ifreq*)p; |
|
465 |
|
466 if (req->ifr_addr.sa_family == AF_INET) { |
|
467 struct ifreq r; |
|
468 |
|
469 strncpy(r.ifr_name, req->ifr_name, lengthof(r.ifr_name)); |
|
470 if (ioctl(sock, SIOCGIFFLAGS, &r) != -1 && |
|
471 r.ifr_flags & IFF_BROADCAST && |
|
472 ioctl(sock, SIOCGIFBRDADDR, &r) != -1) { |
|
473 _broadcast_list[i++] = |
|
474 ((struct sockaddr_in*)&r.ifr_broadaddr)->sin_addr.s_addr; |
|
475 } |
|
476 } |
|
477 |
|
478 p += sizeof(struct ifreq); |
|
479 #ifdef AF_LINK |
|
480 p += req->ifr_addr.sa_len - sizeof(struct sockaddr); |
|
481 #endif |
|
482 } |
|
483 #endif |
|
484 |
|
485 closesocket(sock); |
|
486 #endif /* not HAVE_GETIFADDRS */ |
|
487 |
|
488 _broadcast_list[i] = 0; |
|
489 |
|
490 DEBUG(net, 3, "Detected broadcast addresses:"); |
|
491 // Now display to the debug all the detected ips |
|
492 for (i = 0; _broadcast_list[i] != 0; i++) { |
|
493 DEBUG(net, 3, "%d) %s", i, inet_ntoa(*(struct in_addr *)&_broadcast_list[i]));//inet_ntoa(inaddr)); |
|
494 } |
|
495 } |
|
496 |
|
497 // Resolve a hostname to a inet_addr |
|
498 unsigned long NetworkResolveHost(const char *hostname) |
|
499 { |
|
500 in_addr_t ip; |
|
501 |
|
502 // First try: is it an ip address? |
|
503 ip = inet_addr(hostname); |
|
504 |
|
505 // If not try to resolve the name |
|
506 if (ip == INADDR_NONE) { |
|
507 struct hostent *he = gethostbyname(hostname); |
|
508 if (he == NULL) { |
|
509 DEBUG(net, 0, "Cannot resolve '%s'", hostname); |
|
510 } else { |
|
511 struct in_addr addr = *(struct in_addr *)he->h_addr_list[0]; |
|
512 DEBUG(net, 1, "Resolved '%s' to %s", hostname, inet_ntoa(addr)); |
|
513 ip = addr.s_addr; |
|
514 } |
|
515 } |
|
516 return ip; |
|
517 } |
|
518 |
|
519 // Converts a string to ip/port/player |
|
520 // Format: IP#player:port |
|
521 // |
|
522 // connection_string will be re-terminated to seperate out the hostname, and player and port will |
|
523 // be set to the player and port strings given by the user, inside the memory area originally |
|
524 // occupied by connection_string. |
|
525 void ParseConnectionString(const char **player, const char **port, char *connection_string) |
|
526 { |
|
527 char *p; |
|
528 for (p = connection_string; *p != '\0'; p++) { |
|
529 if (*p == '#') { |
|
530 *p = '\0'; |
|
531 *player = ++p; |
|
532 while (IsValidChar(*p, CS_NUMERAL)) p++; |
|
533 if (*p == '\0') break; |
|
534 } else if (*p == ':') { |
|
535 *port = p + 1; |
|
536 *p = '\0'; |
|
537 } |
|
538 } |
|
539 } |
|
540 |
|
541 // Creates a new client from a socket |
|
542 // Used both by the server and the client |
|
543 static NetworkClientState *NetworkAllocClient(SOCKET s) |
|
544 { |
|
545 NetworkClientState *cs; |
|
546 byte client_no = 0; |
|
547 |
|
548 if (_network_server) { |
|
549 // Can we handle a new client? |
|
550 if (_network_clients_connected >= MAX_CLIENTS) return NULL; |
|
551 if (_network_game_info.clients_on >= _network_game_info.clients_max) return NULL; |
|
552 |
|
553 // Register the login |
|
554 client_no = _network_clients_connected++; |
|
555 } |
|
556 |
|
557 cs = DEREF_CLIENT(client_no); |
|
558 memset(cs, 0, sizeof(*cs)); |
|
559 cs->socket = s; |
|
560 cs->last_frame = 0; |
|
561 cs->has_quit = false; |
|
562 |
|
563 cs->last_frame = _frame_counter; |
|
564 cs->last_frame_server = _frame_counter; |
|
565 |
|
566 if (_network_server) { |
|
567 NetworkClientInfo *ci = DEREF_CLIENT_INFO(cs); |
|
568 memset(ci, 0, sizeof(*ci)); |
|
569 |
|
570 cs->index = _network_client_index++; |
|
571 ci->client_index = cs->index; |
|
572 ci->client_playas = PLAYER_INACTIVE_CLIENT; |
|
573 ci->join_date = _date; |
|
574 |
|
575 InvalidateWindow(WC_CLIENT_LIST, 0); |
|
576 } |
|
577 |
|
578 return cs; |
|
579 } |
|
580 |
|
581 // Close a connection |
|
582 void NetworkCloseClient(NetworkClientState *cs) |
|
583 { |
|
584 NetworkClientInfo *ci; |
|
585 // Socket is already dead |
|
586 if (cs->socket == INVALID_SOCKET) { |
|
587 cs->has_quit = true; |
|
588 return; |
|
589 } |
|
590 |
|
591 DEBUG(net, 1, "Closed client connection %d", cs->index); |
|
592 |
|
593 if (!cs->has_quit && _network_server && cs->status > STATUS_INACTIVE) { |
|
594 // We did not receive a leave message from this client... |
|
595 NetworkErrorCode errorno = NETWORK_ERROR_CONNECTION_LOST; |
|
596 char str[100]; |
|
597 char client_name[NETWORK_CLIENT_NAME_LENGTH]; |
|
598 NetworkClientState *new_cs; |
|
599 |
|
600 NetworkGetClientName(client_name, sizeof(client_name), cs); |
|
601 |
|
602 GetNetworkErrorMsg(str, errorno, lastof(str)); |
|
603 |
|
604 NetworkTextMessage(NETWORK_ACTION_LEAVE, 1, false, client_name, "%s", str); |
|
605 |
|
606 // Inform other clients of this... strange leaving ;) |
|
607 FOR_ALL_CLIENTS(new_cs) { |
|
608 if (new_cs->status > STATUS_AUTH && cs != new_cs) { |
|
609 SEND_COMMAND(PACKET_SERVER_ERROR_QUIT)(new_cs, cs->index, errorno); |
|
610 } |
|
611 } |
|
612 } |
|
613 |
|
614 /* When the client was PRE_ACTIVE, the server was in pause mode, so unpause */ |
|
615 if (cs->status == STATUS_PRE_ACTIVE && _network_pause_on_join) { |
|
616 DoCommandP(0, 0, 0, NULL, CMD_PAUSE); |
|
617 NetworkServer_HandleChat(NETWORK_ACTION_SERVER_MESSAGE, DESTTYPE_BROADCAST, 0, "Game unpaused", NETWORK_SERVER_INDEX); |
|
618 } |
|
619 |
|
620 closesocket(cs->socket); |
|
621 cs->writable = false; |
|
622 cs->has_quit = true; |
|
623 |
|
624 // Free all pending and partially received packets |
|
625 while (cs->packet_queue != NULL) { |
|
626 Packet *p = cs->packet_queue->next; |
|
627 free(cs->packet_queue); |
|
628 cs->packet_queue = p; |
|
629 } |
|
630 free(cs->packet_recv); |
|
631 cs->packet_recv = NULL; |
|
632 |
|
633 while (cs->command_queue != NULL) { |
|
634 CommandPacket *p = cs->command_queue->next; |
|
635 free(cs->command_queue); |
|
636 cs->command_queue = p; |
|
637 } |
|
638 |
|
639 // Close the gap in the client-list |
|
640 ci = DEREF_CLIENT_INFO(cs); |
|
641 |
|
642 if (_network_server) { |
|
643 // We just lost one client :( |
|
644 if (cs->status > STATUS_INACTIVE) _network_game_info.clients_on--; |
|
645 _network_clients_connected--; |
|
646 |
|
647 while ((cs + 1) != DEREF_CLIENT(MAX_CLIENTS) && (cs + 1)->socket != INVALID_SOCKET) { |
|
648 *cs = *(cs + 1); |
|
649 *ci = *(ci + 1); |
|
650 cs++; |
|
651 ci++; |
|
652 } |
|
653 |
|
654 InvalidateWindow(WC_CLIENT_LIST, 0); |
|
655 } |
|
656 |
|
657 // Reset the status of the last socket |
|
658 cs->socket = INVALID_SOCKET; |
|
659 cs->status = STATUS_INACTIVE; |
|
660 cs->index = NETWORK_EMPTY_INDEX; |
|
661 ci->client_index = NETWORK_EMPTY_INDEX; |
|
662 |
|
663 CheckMinPlayers(); |
|
664 } |
|
665 |
|
666 // A client wants to connect to a server |
|
667 static bool NetworkConnect(const char *hostname, int port) |
|
668 { |
|
669 SOCKET s; |
|
670 struct sockaddr_in sin; |
|
671 |
|
672 DEBUG(net, 1, "Connecting to %s %d", hostname, port); |
|
673 |
|
674 s = socket(AF_INET, SOCK_STREAM, 0); |
|
675 if (s == INVALID_SOCKET) { |
|
676 ClientStartError("socket() failed"); |
|
677 return false; |
|
678 } |
|
679 |
|
680 if (!SetNoDelay(s)) DEBUG(net, 1, "Setting TCP_NODELAY failed"); |
|
681 |
|
682 sin.sin_family = AF_INET; |
|
683 sin.sin_addr.s_addr = NetworkResolveHost(hostname); |
|
684 sin.sin_port = htons(port); |
|
685 _network_last_host_ip = sin.sin_addr.s_addr; |
|
686 |
|
687 /* We failed to connect for which reason what so ever */ |
|
688 if (connect(s, (struct sockaddr*) &sin, sizeof(sin)) != 0) return false; |
|
689 |
|
690 if (!SetNonBlocking(s)) DEBUG(net, 0, "Setting non-blocking mode failed"); // XXX should this be an error? |
|
691 |
|
692 // in client mode, only the first client field is used. it's pointing to the server. |
|
693 NetworkAllocClient(s); |
|
694 |
|
695 _network_join_status = NETWORK_JOIN_STATUS_CONNECTING; |
|
696 ShowJoinStatusWindow(); |
|
697 |
|
698 return true; |
|
699 } |
|
700 |
|
701 // For the server, to accept new clients |
|
702 static void NetworkAcceptClients(void) |
|
703 { |
|
704 struct sockaddr_in sin; |
|
705 NetworkClientState *cs; |
|
706 uint i; |
|
707 bool banned; |
|
708 |
|
709 // Should never ever happen.. is it possible?? |
|
710 assert(_listensocket != INVALID_SOCKET); |
|
711 |
|
712 for (;;) { |
|
713 socklen_t sin_len = sizeof(sin); |
|
714 SOCKET s = accept(_listensocket, (struct sockaddr*)&sin, &sin_len); |
|
715 if (s == INVALID_SOCKET) return; |
|
716 |
|
717 SetNonBlocking(s); // XXX error handling? |
|
718 |
|
719 DEBUG(net, 1, "Client connected from %s on frame %d", inet_ntoa(sin.sin_addr), _frame_counter); |
|
720 |
|
721 SetNoDelay(s); // XXX error handling? |
|
722 |
|
723 /* Check if the client is banned */ |
|
724 banned = false; |
|
725 for (i = 0; i < lengthof(_network_ban_list); i++) { |
|
726 if (_network_ban_list[i] == NULL) continue; |
|
727 |
|
728 if (sin.sin_addr.s_addr == inet_addr(_network_ban_list[i])) { |
|
729 Packet *p = NetworkSend_Init(PACKET_SERVER_BANNED); |
|
730 |
|
731 DEBUG(net, 1, "Banned ip tried to join (%s), refused", _network_ban_list[i]); |
|
732 |
|
733 p->buffer[0] = p->size & 0xFF; |
|
734 p->buffer[1] = p->size >> 8; |
|
735 |
|
736 send(s, p->buffer, p->size, 0); |
|
737 closesocket(s); |
|
738 |
|
739 free(p); |
|
740 |
|
741 banned = true; |
|
742 break; |
|
743 } |
|
744 } |
|
745 /* If this client is banned, continue with next client */ |
|
746 if (banned) continue; |
|
747 |
|
748 cs = NetworkAllocClient(s); |
|
749 if (cs == NULL) { |
|
750 // no more clients allowed? |
|
751 // Send to the client that we are full! |
|
752 Packet *p = NetworkSend_Init(PACKET_SERVER_FULL); |
|
753 |
|
754 p->buffer[0] = p->size & 0xFF; |
|
755 p->buffer[1] = p->size >> 8; |
|
756 |
|
757 send(s, p->buffer, p->size, 0); |
|
758 closesocket(s); |
|
759 |
|
760 free(p); |
|
761 |
|
762 continue; |
|
763 } |
|
764 |
|
765 // a new client has connected. We set him at inactive for now |
|
766 // maybe he is only requesting server-info. Till he has sent a PACKET_CLIENT_MAP_OK |
|
767 // the client stays inactive |
|
768 cs->status = STATUS_INACTIVE; |
|
769 |
|
770 DEREF_CLIENT_INFO(cs)->client_ip = sin.sin_addr.s_addr; // Save the IP of the client |
|
771 } |
|
772 } |
|
773 |
|
774 // Set up the listen socket for the server |
|
775 static bool NetworkListen(void) |
|
776 { |
|
777 SOCKET ls; |
|
778 struct sockaddr_in sin; |
|
779 |
|
780 DEBUG(net, 1, "Listening on %s:%d", _network_server_bind_ip_host, _network_server_port); |
|
781 |
|
782 ls = socket(AF_INET, SOCK_STREAM, 0); |
|
783 if (ls == INVALID_SOCKET) { |
|
784 ServerStartError("socket() on listen socket failed"); |
|
785 return false; |
|
786 } |
|
787 |
|
788 { // reuse the socket |
|
789 int reuse = 1; |
|
790 // The (const char*) cast is needed for windows!! |
|
791 if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse, sizeof(reuse)) == -1) { |
|
792 ServerStartError("setsockopt() on listen socket failed"); |
|
793 return false; |
|
794 } |
|
795 } |
|
796 |
|
797 if (!SetNonBlocking(ls)) DEBUG(net, 0, "Setting non-blocking mode failed"); // XXX should this be an error? |
|
798 |
|
799 sin.sin_family = AF_INET; |
|
800 sin.sin_addr.s_addr = _network_server_bind_ip; |
|
801 sin.sin_port = htons(_network_server_port); |
|
802 |
|
803 if (bind(ls, (struct sockaddr*)&sin, sizeof(sin)) != 0) { |
|
804 ServerStartError("bind() failed"); |
|
805 return false; |
|
806 } |
|
807 |
|
808 if (listen(ls, 1) != 0) { |
|
809 ServerStartError("listen() failed"); |
|
810 return false; |
|
811 } |
|
812 |
|
813 _listensocket = ls; |
|
814 |
|
815 return true; |
|
816 } |
|
817 |
|
818 // Close all current connections |
|
819 static void NetworkClose(void) |
|
820 { |
|
821 NetworkClientState *cs; |
|
822 |
|
823 FOR_ALL_CLIENTS(cs) { |
|
824 if (!_network_server) { |
|
825 SEND_COMMAND(PACKET_CLIENT_QUIT)("leaving"); |
|
826 NetworkSend_Packets(cs); |
|
827 } |
|
828 NetworkCloseClient(cs); |
|
829 } |
|
830 |
|
831 if (_network_server) { |
|
832 // We are a server, also close the listensocket |
|
833 closesocket(_listensocket); |
|
834 _listensocket = INVALID_SOCKET; |
|
835 DEBUG(net, 1, "Closed listener"); |
|
836 NetworkUDPClose(); |
|
837 } |
|
838 } |
|
839 |
|
840 // Inits the network (cleans sockets and stuff) |
|
841 static void NetworkInitialize(void) |
|
842 { |
|
843 NetworkClientState *cs; |
|
844 |
|
845 _local_command_queue = NULL; |
|
846 |
|
847 // Clean all client-sockets |
|
848 memset(_clients, 0, sizeof(_clients)); |
|
849 for (cs = _clients; cs != &_clients[MAX_CLIENTS]; cs++) { |
|
850 cs->socket = INVALID_SOCKET; |
|
851 cs->status = STATUS_INACTIVE; |
|
852 cs->command_queue = NULL; |
|
853 } |
|
854 |
|
855 // Clean the client_info memory |
|
856 memset(&_network_client_info, 0, sizeof(_network_client_info)); |
|
857 memset(&_network_player_info, 0, sizeof(_network_player_info)); |
|
858 |
|
859 _sync_frame = 0; |
|
860 _network_first_time = true; |
|
861 |
|
862 _network_reconnect = 0; |
|
863 |
|
864 NetworkUDPInitialize(); |
|
865 } |
|
866 |
|
867 // Query a server to fetch his game-info |
|
868 // If game_info is true, only the gameinfo is fetched, |
|
869 // else only the client_info is fetched |
|
870 NetworkGameList *NetworkQueryServer(const char* host, unsigned short port, bool game_info) |
|
871 { |
|
872 if (!_network_available) return NULL; |
|
873 |
|
874 NetworkDisconnect(); |
|
875 |
|
876 if (game_info) return NetworkUDPQueryServer(host, port); |
|
877 |
|
878 NetworkInitialize(); |
|
879 |
|
880 _network_server = false; |
|
881 |
|
882 // Try to connect |
|
883 _networking = NetworkConnect(host, port); |
|
884 |
|
885 // We are connected |
|
886 if (_networking) { |
|
887 SEND_COMMAND(PACKET_CLIENT_COMPANY_INFO)(); |
|
888 } else { // No networking, close everything down again |
|
889 NetworkDisconnect(); |
|
890 } |
|
891 |
|
892 return NULL; |
|
893 } |
|
894 |
|
895 /* Validates an address entered as a string and adds the server to |
|
896 * the list. If you use this function, the games will be marked |
|
897 * as manually added. */ |
|
898 void NetworkAddServer(const char *b) |
|
899 { |
|
900 if (*b != '\0') { |
|
901 NetworkGameList *item; |
|
902 const char *port = NULL; |
|
903 const char *player = NULL; |
|
904 char host[NETWORK_HOSTNAME_LENGTH]; |
|
905 uint16 rport; |
|
906 |
|
907 ttd_strlcpy(host, b, lengthof(host)); |
|
908 |
|
909 ttd_strlcpy(_network_default_ip, b, lengthof(_network_default_ip)); |
|
910 rport = NETWORK_DEFAULT_PORT; |
|
911 |
|
912 ParseConnectionString(&player, &port, host); |
|
913 if (port != NULL) rport = atoi(port); |
|
914 |
|
915 item = NetworkQueryServer(host, rport, true); |
|
916 item->manually = true; |
|
917 } |
|
918 } |
|
919 |
|
920 /* Generates the list of manually added hosts from NetworkGameList and |
|
921 * dumps them into the array _network_host_list. This array is needed |
|
922 * by the function that generates the config file. */ |
|
923 void NetworkRebuildHostList(void) |
|
924 { |
|
925 uint i = 0; |
|
926 const NetworkGameList *item = _network_game_list; |
|
927 while (item != NULL && i != lengthof(_network_host_list)) { |
|
928 if (item->manually) { |
|
929 free(_network_host_list[i]); |
|
930 _network_host_list[i++] = str_fmt("%s:%i", item->info.hostname, item->port); |
|
931 } |
|
932 item = item->next; |
|
933 } |
|
934 |
|
935 for (; i < lengthof(_network_host_list); i++) { |
|
936 free(_network_host_list[i]); |
|
937 _network_host_list[i] = NULL; |
|
938 } |
|
939 } |
|
940 |
|
941 // Used by clients, to connect to a server |
|
942 bool NetworkClientConnectGame(const char *host, uint16 port) |
|
943 { |
|
944 if (!_network_available) return false; |
|
945 |
|
946 if (port == 0) return false; |
|
947 |
|
948 ttd_strlcpy(_network_last_host, host, sizeof(_network_last_host)); |
|
949 _network_last_port = port; |
|
950 |
|
951 NetworkDisconnect(); |
|
952 NetworkUDPClose(); |
|
953 NetworkInitialize(); |
|
954 |
|
955 // Try to connect |
|
956 _networking = NetworkConnect(host, port); |
|
957 |
|
958 // We are connected |
|
959 if (_networking) { |
|
960 IConsoleCmdExec("exec scripts/on_client.scr 0"); |
|
961 NetworkClient_Connected(); |
|
962 } else { |
|
963 // Connecting failed |
|
964 NetworkError(STR_NETWORK_ERR_NOCONNECTION); |
|
965 } |
|
966 |
|
967 return _networking; |
|
968 } |
|
969 |
|
970 static void NetworkInitGameInfo(void) |
|
971 { |
|
972 NetworkClientInfo *ci; |
|
973 |
|
974 ttd_strlcpy(_network_game_info.server_name, _network_server_name, sizeof(_network_game_info.server_name)); |
|
975 ttd_strlcpy(_network_game_info.server_password, _network_server_password, sizeof(_network_server_password)); |
|
976 ttd_strlcpy(_network_game_info.rcon_password, _network_rcon_password, sizeof(_network_rcon_password)); |
|
977 if (_network_game_info.server_name[0] == '\0') |
|
978 snprintf(_network_game_info.server_name, sizeof(_network_game_info.server_name), "Unnamed Server"); |
|
979 |
|
980 ttd_strlcpy(_network_game_info.server_revision, _openttd_revision, sizeof(_network_game_info.server_revision)); |
|
981 |
|
982 // The server is a client too ;) |
|
983 if (_network_dedicated) { |
|
984 _network_game_info.clients_on = 0; |
|
985 _network_game_info.companies_on = 0; |
|
986 _network_game_info.dedicated = true; |
|
987 } else { |
|
988 _network_game_info.clients_on = 1; |
|
989 _network_game_info.companies_on = 1; |
|
990 _network_game_info.dedicated = false; |
|
991 } |
|
992 |
|
993 _network_game_info.spectators_on = 0; |
|
994 |
|
995 _network_game_info.game_date = _date; |
|
996 _network_game_info.start_date = ConvertYMDToDate(_patches.starting_year, 0, 1); |
|
997 _network_game_info.map_width = MapSizeX(); |
|
998 _network_game_info.map_height = MapSizeY(); |
|
999 _network_game_info.map_set = _opt.landscape; |
|
1000 |
|
1001 _network_game_info.use_password = (_network_server_password[0] != '\0'); |
|
1002 |
|
1003 // We use _network_client_info[MAX_CLIENT_INFO - 1] to store the server-data in it |
|
1004 // The index is NETWORK_SERVER_INDEX ( = 1) |
|
1005 ci = &_network_client_info[MAX_CLIENT_INFO - 1]; |
|
1006 memset(ci, 0, sizeof(*ci)); |
|
1007 |
|
1008 ci->client_index = NETWORK_SERVER_INDEX; |
|
1009 ci->client_playas = _network_dedicated ? PLAYER_SPECTATOR : _local_player; |
|
1010 |
|
1011 ttd_strlcpy(ci->client_name, _network_player_name, sizeof(ci->client_name)); |
|
1012 ttd_strlcpy(ci->unique_id, _network_unique_id, sizeof(ci->unique_id)); |
|
1013 } |
|
1014 |
|
1015 bool NetworkServerStart(void) |
|
1016 { |
|
1017 if (!_network_available) return false; |
|
1018 |
|
1019 /* Call the pre-scripts */ |
|
1020 IConsoleCmdExec("exec scripts/pre_server.scr 0"); |
|
1021 if (_network_dedicated) IConsoleCmdExec("exec scripts/pre_dedicated.scr 0"); |
|
1022 |
|
1023 NetworkInitialize(); |
|
1024 if (!NetworkListen()) return false; |
|
1025 |
|
1026 // Try to start UDP-server |
|
1027 _network_udp_server = true; |
|
1028 _network_udp_server = NetworkUDPListen(&_udp_server_socket, _network_server_bind_ip, _network_server_port, false); |
|
1029 |
|
1030 _network_server = true; |
|
1031 _networking = true; |
|
1032 _frame_counter = 0; |
|
1033 _frame_counter_server = 0; |
|
1034 _frame_counter_max = 0; |
|
1035 _last_sync_frame = 0; |
|
1036 _network_own_client_index = NETWORK_SERVER_INDEX; |
|
1037 |
|
1038 /* Non-dedicated server will always be player #1 */ |
|
1039 if (!_network_dedicated) _network_playas = 0; |
|
1040 |
|
1041 _network_clients_connected = 0; |
|
1042 |
|
1043 NetworkInitGameInfo(); |
|
1044 |
|
1045 // execute server initialization script |
|
1046 IConsoleCmdExec("exec scripts/on_server.scr 0"); |
|
1047 // if the server is dedicated ... add some other script |
|
1048 if (_network_dedicated) IConsoleCmdExec("exec scripts/on_dedicated.scr 0"); |
|
1049 |
|
1050 _min_players_paused = false; |
|
1051 CheckMinPlayers(); |
|
1052 |
|
1053 /* Try to register us to the master server */ |
|
1054 _network_last_advertise_frame = 0; |
|
1055 _network_need_advertise = true; |
|
1056 NetworkUDPAdvertise(); |
|
1057 return true; |
|
1058 } |
|
1059 |
|
1060 // The server is rebooting... |
|
1061 // The only difference with NetworkDisconnect, is the packets that is sent |
|
1062 void NetworkReboot(void) |
|
1063 { |
|
1064 if (_network_server) { |
|
1065 NetworkClientState *cs; |
|
1066 FOR_ALL_CLIENTS(cs) { |
|
1067 SEND_COMMAND(PACKET_SERVER_NEWGAME)(cs); |
|
1068 NetworkSend_Packets(cs); |
|
1069 } |
|
1070 } |
|
1071 |
|
1072 NetworkClose(); |
|
1073 |
|
1074 // Free all queued commands |
|
1075 while (_local_command_queue != NULL) { |
|
1076 CommandPacket *p = _local_command_queue; |
|
1077 _local_command_queue = _local_command_queue->next; |
|
1078 free(p); |
|
1079 } |
|
1080 |
|
1081 _networking = false; |
|
1082 _network_server = false; |
|
1083 } |
|
1084 |
|
1085 // We want to disconnect from the host/clients |
|
1086 void NetworkDisconnect(void) |
|
1087 { |
|
1088 if (_network_server) { |
|
1089 NetworkClientState *cs; |
|
1090 FOR_ALL_CLIENTS(cs) { |
|
1091 SEND_COMMAND(PACKET_SERVER_SHUTDOWN)(cs); |
|
1092 NetworkSend_Packets(cs); |
|
1093 } |
|
1094 } |
|
1095 |
|
1096 if (_network_advertise) NetworkUDPRemoveAdvertise(); |
|
1097 |
|
1098 DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0); |
|
1099 |
|
1100 NetworkClose(); |
|
1101 |
|
1102 // Free all queued commands |
|
1103 while (_local_command_queue != NULL) { |
|
1104 CommandPacket *p = _local_command_queue; |
|
1105 _local_command_queue = _local_command_queue->next; |
|
1106 free(p); |
|
1107 } |
|
1108 |
|
1109 _networking = false; |
|
1110 _network_server = false; |
|
1111 } |
|
1112 |
|
1113 // Receives something from the network |
|
1114 static bool NetworkReceive(void) |
|
1115 { |
|
1116 NetworkClientState *cs; |
|
1117 int n; |
|
1118 fd_set read_fd, write_fd; |
|
1119 struct timeval tv; |
|
1120 |
|
1121 FD_ZERO(&read_fd); |
|
1122 FD_ZERO(&write_fd); |
|
1123 |
|
1124 FOR_ALL_CLIENTS(cs) { |
|
1125 FD_SET(cs->socket, &read_fd); |
|
1126 FD_SET(cs->socket, &write_fd); |
|
1127 } |
|
1128 |
|
1129 // take care of listener port |
|
1130 if (_network_server) FD_SET(_listensocket, &read_fd); |
|
1131 |
|
1132 tv.tv_sec = tv.tv_usec = 0; // don't block at all. |
|
1133 #if !defined(__MORPHOS__) && !defined(__AMIGA__) |
|
1134 n = select(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv); |
|
1135 #else |
|
1136 n = WaitSelect(FD_SETSIZE, &read_fd, &write_fd, NULL, &tv, NULL); |
|
1137 #endif |
|
1138 if (n == -1 && !_network_server) NetworkError(STR_NETWORK_ERR_LOSTCONNECTION); |
|
1139 |
|
1140 // accept clients.. |
|
1141 if (_network_server && FD_ISSET(_listensocket, &read_fd)) NetworkAcceptClients(); |
|
1142 |
|
1143 // read stuff from clients |
|
1144 FOR_ALL_CLIENTS(cs) { |
|
1145 cs->writable = !!FD_ISSET(cs->socket, &write_fd); |
|
1146 if (FD_ISSET(cs->socket, &read_fd)) { |
|
1147 if (_network_server) { |
|
1148 NetworkServer_ReadPackets(cs); |
|
1149 } else { |
|
1150 NetworkRecvStatus res; |
|
1151 |
|
1152 // The client already was quiting! |
|
1153 if (cs->has_quit) return false; |
|
1154 |
|
1155 res = NetworkClient_ReadPackets(cs); |
|
1156 if (res != NETWORK_RECV_STATUS_OKAY) { |
|
1157 // The client made an error of which we can not recover |
|
1158 // close the client and drop back to main menu |
|
1159 NetworkClientError(res, cs); |
|
1160 return false; |
|
1161 } |
|
1162 } |
|
1163 } |
|
1164 } |
|
1165 return true; |
|
1166 } |
|
1167 |
|
1168 // This sends all buffered commands (if possible) |
|
1169 static void NetworkSend(void) |
|
1170 { |
|
1171 NetworkClientState *cs; |
|
1172 FOR_ALL_CLIENTS(cs) { |
|
1173 if (cs->writable) { |
|
1174 NetworkSend_Packets(cs); |
|
1175 |
|
1176 if (cs->status == STATUS_MAP) { |
|
1177 // This client is in the middle of a map-send, call the function for that |
|
1178 SEND_COMMAND(PACKET_SERVER_MAP)(cs); |
|
1179 } |
|
1180 } |
|
1181 } |
|
1182 } |
|
1183 |
|
1184 // Handle the local-command-queue |
|
1185 static void NetworkHandleLocalQueue(void) |
|
1186 { |
|
1187 CommandPacket *cp, **cp_prev; |
|
1188 |
|
1189 cp_prev = &_local_command_queue; |
|
1190 |
|
1191 while ( (cp = *cp_prev) != NULL) { |
|
1192 |
|
1193 // The queue is always in order, which means |
|
1194 // that the first element will be executed first. |
|
1195 if (_frame_counter < cp->frame) break; |
|
1196 |
|
1197 if (_frame_counter > cp->frame) { |
|
1198 // If we reach here, it means for whatever reason, we've already executed |
|
1199 // past the command we need to execute. |
|
1200 DEBUG(net, 0, "Trying to execute a packet in the past!"); |
|
1201 assert(0); |
|
1202 } |
|
1203 |
|
1204 // We can execute this command |
|
1205 NetworkExecuteCommand(cp); |
|
1206 |
|
1207 *cp_prev = cp->next; |
|
1208 free(cp); |
|
1209 } |
|
1210 |
|
1211 // Just a safety check, to be removed in the future. |
|
1212 // Make sure that no older command appears towards the end of the queue |
|
1213 // In that case we missed executing it. This will never happen. |
|
1214 for (cp = _local_command_queue; cp; cp = cp->next) { |
|
1215 assert(_frame_counter < cp->frame); |
|
1216 } |
|
1217 |
|
1218 } |
|
1219 |
|
1220 static bool NetworkDoClientLoop(void) |
|
1221 { |
|
1222 _frame_counter++; |
|
1223 |
|
1224 NetworkHandleLocalQueue(); |
|
1225 |
|
1226 StateGameLoop(); |
|
1227 |
|
1228 // Check if we are in sync! |
|
1229 if (_sync_frame != 0) { |
|
1230 if (_sync_frame == _frame_counter) { |
|
1231 #ifdef NETWORK_SEND_DOUBLE_SEED |
|
1232 if (_sync_seed_1 != _random_seeds[0][0] || _sync_seed_2 != _random_seeds[0][1]) { |
|
1233 #else |
|
1234 if (_sync_seed_1 != _random_seeds[0][0]) { |
|
1235 #endif |
|
1236 NetworkError(STR_NETWORK_ERR_DESYNC); |
|
1237 DEBUG(net, 0, "Sync error detected!"); |
|
1238 NetworkClientError(NETWORK_RECV_STATUS_DESYNC, DEREF_CLIENT(0)); |
|
1239 return false; |
|
1240 } |
|
1241 |
|
1242 // If this is the first time we have a sync-frame, we |
|
1243 // need to let the server know that we are ready and at the same |
|
1244 // frame as he is.. so we can start playing! |
|
1245 if (_network_first_time) { |
|
1246 _network_first_time = false; |
|
1247 SEND_COMMAND(PACKET_CLIENT_ACK)(); |
|
1248 } |
|
1249 |
|
1250 _sync_frame = 0; |
|
1251 } else if (_sync_frame < _frame_counter) { |
|
1252 DEBUG(net, 1, "Missed frame for sync-test (%d / %d)", _sync_frame, _frame_counter); |
|
1253 _sync_frame = 0; |
|
1254 } |
|
1255 } |
|
1256 |
|
1257 return true; |
|
1258 } |
|
1259 |
|
1260 // We have to do some UDP checking |
|
1261 void NetworkUDPGameLoop(void) |
|
1262 { |
|
1263 if (_network_udp_server) { |
|
1264 NetworkUDPReceive(_udp_server_socket); |
|
1265 if (_udp_master_socket != INVALID_SOCKET) { |
|
1266 NetworkUDPReceive(_udp_master_socket); |
|
1267 } |
|
1268 } else if (_udp_client_socket != INVALID_SOCKET) { |
|
1269 NetworkUDPReceive(_udp_client_socket); |
|
1270 if (_network_udp_broadcast > 0) _network_udp_broadcast--; |
|
1271 } |
|
1272 } |
|
1273 |
|
1274 // The main loop called from ttd.c |
|
1275 // Here we also have to do StateGameLoop if needed! |
|
1276 void NetworkGameLoop(void) |
|
1277 { |
|
1278 if (!_networking) return; |
|
1279 |
|
1280 if (!NetworkReceive()) return; |
|
1281 |
|
1282 if (_network_server) { |
|
1283 bool send_frame = false; |
|
1284 |
|
1285 // We first increase the _frame_counter |
|
1286 _frame_counter++; |
|
1287 // Update max-frame-counter |
|
1288 if (_frame_counter > _frame_counter_max) { |
|
1289 _frame_counter_max = _frame_counter + _network_frame_freq; |
|
1290 send_frame = true; |
|
1291 } |
|
1292 |
|
1293 NetworkHandleLocalQueue(); |
|
1294 |
|
1295 // Then we make the frame |
|
1296 StateGameLoop(); |
|
1297 |
|
1298 _sync_seed_1 = _random_seeds[0][0]; |
|
1299 #ifdef NETWORK_SEND_DOUBLE_SEED |
|
1300 _sync_seed_2 = _random_seeds[0][1]; |
|
1301 #endif |
|
1302 |
|
1303 NetworkServer_Tick(send_frame); |
|
1304 } else { |
|
1305 // Client |
|
1306 |
|
1307 // Make sure we are at the frame were the server is (quick-frames) |
|
1308 if (_frame_counter_server > _frame_counter) { |
|
1309 while (_frame_counter_server > _frame_counter) { |
|
1310 if (!NetworkDoClientLoop()) break; |
|
1311 } |
|
1312 } else { |
|
1313 // Else, keep on going till _frame_counter_max |
|
1314 if (_frame_counter_max > _frame_counter) NetworkDoClientLoop(); |
|
1315 } |
|
1316 } |
|
1317 |
|
1318 NetworkSend(); |
|
1319 } |
|
1320 |
|
1321 static void NetworkGenerateUniqueId(void) |
|
1322 { |
|
1323 md5_state_t state; |
|
1324 md5_byte_t digest[16]; |
|
1325 char hex_output[16*2 + 1]; |
|
1326 char coding_string[NETWORK_NAME_LENGTH]; |
|
1327 int di; |
|
1328 |
|
1329 snprintf(coding_string, sizeof(coding_string), "%d%s", (uint)Random(), "OpenTTD Unique ID"); |
|
1330 |
|
1331 /* Generate the MD5 hash */ |
|
1332 md5_init(&state); |
|
1333 md5_append(&state, (const md5_byte_t*)coding_string, strlen(coding_string)); |
|
1334 md5_finish(&state, digest); |
|
1335 |
|
1336 for (di = 0; di < 16; ++di) |
|
1337 sprintf(hex_output + di * 2, "%02x", digest[di]); |
|
1338 |
|
1339 /* _network_unique_id is our id */ |
|
1340 snprintf(_network_unique_id, sizeof(_network_unique_id), "%s", hex_output); |
|
1341 } |
|
1342 |
|
1343 // This tries to launch the network for a given OS |
|
1344 void NetworkStartUp(void) |
|
1345 { |
|
1346 DEBUG(net, 3, "[core] starting network..."); |
|
1347 |
|
1348 #if defined(__MORPHOS__) || defined(__AMIGA__) |
|
1349 /* |
|
1350 * IMPORTANT NOTE: SocketBase needs to be initialized before we use _any_ |
|
1351 * network related function, else: crash. |
|
1352 */ |
|
1353 DEBUG(net, 3, "[core] loading bsd socket library"); |
|
1354 SocketBase = OpenLibrary("bsdsocket.library", 4); |
|
1355 if (SocketBase == NULL) { |
|
1356 DEBUG(net, 0, "[core] can't open bsdsocket.library version 4, network unavailable"); |
|
1357 _network_available = false; |
|
1358 return; |
|
1359 } |
|
1360 |
|
1361 #if defined(__AMIGA__) |
|
1362 // for usleep() implementation (only required for legacy AmigaOS builds) |
|
1363 TimerPort = CreateMsgPort(); |
|
1364 if (TimerPort != NULL) { |
|
1365 TimerRequest = (struct timerequest*)CreateIORequest(TimerPort, sizeof(struct timerequest); |
|
1366 if (TimerRequest != NULL) { |
|
1367 if (OpenDevice("timer.device", UNIT_MICROHZ, (struct IORequest*)TimerRequest, 0) == 0) { |
|
1368 TimerBase = TimerRequest->tr_node.io_Device; |
|
1369 if (TimerBase == NULL) { |
|
1370 // free ressources... |
|
1371 DEBUG(net, 0, "[core] can't initialize timer, network unavailable"); |
|
1372 _network_available = false; |
|
1373 return; |
|
1374 } |
|
1375 } |
|
1376 } |
|
1377 } |
|
1378 #endif // __AMIGA__ |
|
1379 #endif // __MORPHOS__ / __AMIGA__ |
|
1380 |
|
1381 // Network is available |
|
1382 _network_available = true; |
|
1383 _network_dedicated = false; |
|
1384 _network_last_advertise_frame = 0; |
|
1385 _network_need_advertise = true; |
|
1386 _network_advertise_retries = 0; |
|
1387 |
|
1388 /* Load the ip from the openttd.cfg */ |
|
1389 _network_server_bind_ip = inet_addr(_network_server_bind_ip_host); |
|
1390 /* And put the data back in it in case it was an invalid ip */ |
|
1391 snprintf(_network_server_bind_ip_host, sizeof(_network_server_bind_ip_host), "%s", inet_ntoa(*(struct in_addr *)&_network_server_bind_ip)); |
|
1392 |
|
1393 /* Generate an unique id when there is none yet */ |
|
1394 if (_network_unique_id[0] == '\0') NetworkGenerateUniqueId(); |
|
1395 |
|
1396 { |
|
1397 byte cl_max = _network_game_info.clients_max; |
|
1398 byte cp_max = _network_game_info.companies_max; |
|
1399 byte sp_max = _network_game_info.spectators_max; |
|
1400 |
|
1401 memset(&_network_game_info, 0, sizeof(_network_game_info)); |
|
1402 _network_game_info.clients_max = cl_max; |
|
1403 _network_game_info.companies_max = cp_max; |
|
1404 _network_game_info.spectators_max = sp_max; |
|
1405 } |
|
1406 |
|
1407 // Let's load the network in windows |
|
1408 #if defined(WIN32) |
|
1409 { |
|
1410 WSADATA wsa; |
|
1411 DEBUG(net, 3, "[core] loading windows socket library"); |
|
1412 if (WSAStartup(MAKEWORD(2,0), &wsa) != 0) { |
|
1413 DEBUG(net, 0, "[core] WSAStartup failed, network unavailable"); |
|
1414 _network_available = false; |
|
1415 return; |
|
1416 } |
|
1417 } |
|
1418 #endif // WIN32 |
|
1419 |
|
1420 NetworkInitialize(); |
|
1421 DEBUG(net, 3, "[core] network online, multiplayer available"); |
|
1422 NetworkFindIPs(); |
|
1423 } |
|
1424 |
|
1425 // This shuts the network down |
|
1426 void NetworkShutDown(void) |
|
1427 { |
|
1428 NetworkDisconnect(); |
|
1429 NetworkUDPClose(); |
|
1430 |
|
1431 DEBUG(net, 3, "[core] shutting down network"); |
|
1432 |
|
1433 _network_available = false; |
|
1434 |
|
1435 #if defined(__MORPHOS__) || defined(__AMIGA__) |
|
1436 // free allocated ressources |
|
1437 #if defined(__AMIGA__) |
|
1438 if (TimerBase != NULL) CloseDevice((struct IORequest*)TimerRequest); // XXX This smells wrong |
|
1439 if (TimerRequest != NULL) DeleteIORequest(TimerRequest); |
|
1440 if (TimerPort != NULL) DeleteMsgPort(TimerPort); |
|
1441 #endif |
|
1442 |
|
1443 if (SocketBase != NULL) CloseLibrary(SocketBase); |
|
1444 #endif |
|
1445 |
|
1446 #if defined(WIN32) |
|
1447 WSACleanup(); |
|
1448 #endif |
|
1449 } |
|
1450 |
|
1451 #endif /* ENABLE_NETWORK */ |