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