1 /* $Id$ */ |
|
2 |
|
3 #ifdef ENABLE_NETWORK |
|
4 |
|
5 #include "stdafx.h" |
|
6 #include "debug.h" |
|
7 #include "string.h" |
|
8 #include "strings.h" |
|
9 #include "network_data.h" |
|
10 #include "date.h" |
|
11 #include "table/strings.h" |
|
12 #include "functions.h" |
|
13 #include "network_client.h" |
|
14 #include "network_gamelist.h" |
|
15 #include "network_gui.h" |
|
16 #include "saveload.h" |
|
17 #include "command.h" |
|
18 #include "window.h" |
|
19 #include "console.h" |
|
20 #include "variables.h" |
|
21 #include "ai/ai.h" |
|
22 |
|
23 |
|
24 // This file handles all the client-commands |
|
25 |
|
26 |
|
27 // So we don't make too much typos ;) |
|
28 #define MY_CLIENT DEREF_CLIENT(0) |
|
29 |
|
30 static uint32 last_ack_frame; |
|
31 |
|
32 // ********** |
|
33 // Sending functions |
|
34 // DEF_CLIENT_SEND_COMMAND has no parameters |
|
35 // ********** |
|
36 |
|
37 DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_COMPANY_INFO) |
|
38 { |
|
39 // |
|
40 // Packet: CLIENT_COMPANY_INFO |
|
41 // Function: Request company-info (in detail) |
|
42 // Data: |
|
43 // <none> |
|
44 // |
|
45 Packet *p; |
|
46 _network_join_status = NETWORK_JOIN_STATUS_GETTING_COMPANY_INFO; |
|
47 InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0); |
|
48 |
|
49 p = NetworkSend_Init(PACKET_CLIENT_COMPANY_INFO); |
|
50 NetworkSend_Packet(p, MY_CLIENT); |
|
51 } |
|
52 |
|
53 DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_JOIN) |
|
54 { |
|
55 // |
|
56 // Packet: CLIENT_JOIN |
|
57 // Function: Try to join the server |
|
58 // Data: |
|
59 // String: OpenTTD Revision (norev000 if no revision) |
|
60 // String: Player Name (max NETWORK_NAME_LENGTH) |
|
61 // uint8: Play as Player id (1..MAX_PLAYERS) |
|
62 // uint8: Language ID |
|
63 // String: Unique id to find the player back in server-listing |
|
64 // |
|
65 |
|
66 extern const char _openttd_revision[]; |
|
67 Packet *p; |
|
68 _network_join_status = NETWORK_JOIN_STATUS_AUTHORIZING; |
|
69 InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0); |
|
70 |
|
71 p = NetworkSend_Init(PACKET_CLIENT_JOIN); |
|
72 NetworkSend_string(p, _openttd_revision); |
|
73 NetworkSend_string(p, _network_player_name); // Player name |
|
74 NetworkSend_uint8(p, _network_playas); // PlayAs |
|
75 NetworkSend_uint8(p, NETLANG_ANY); // Language |
|
76 NetworkSend_string(p, _network_unique_id); |
|
77 NetworkSend_Packet(p, MY_CLIENT); |
|
78 } |
|
79 |
|
80 DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_PASSWORD)(NetworkPasswordType type, const char *password) |
|
81 { |
|
82 // |
|
83 // Packet: CLIENT_PASSWORD |
|
84 // Function: Send a password to the server to authorize |
|
85 // Data: |
|
86 // uint8: NetworkPasswordType |
|
87 // String: Password |
|
88 // |
|
89 Packet *p = NetworkSend_Init(PACKET_CLIENT_PASSWORD); |
|
90 NetworkSend_uint8(p, type); |
|
91 NetworkSend_string(p, password); |
|
92 NetworkSend_Packet(p, MY_CLIENT); |
|
93 } |
|
94 |
|
95 DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_GETMAP) |
|
96 { |
|
97 // |
|
98 // Packet: CLIENT_GETMAP |
|
99 // Function: Request the map from the server |
|
100 // Data: |
|
101 // <none> |
|
102 // |
|
103 |
|
104 Packet *p = NetworkSend_Init(PACKET_CLIENT_GETMAP); |
|
105 NetworkSend_Packet(p, MY_CLIENT); |
|
106 } |
|
107 |
|
108 DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_MAP_OK) |
|
109 { |
|
110 // |
|
111 // Packet: CLIENT_MAP_OK |
|
112 // Function: Tell the server that we are done receiving/loading the map |
|
113 // Data: |
|
114 // <none> |
|
115 // |
|
116 |
|
117 Packet *p = NetworkSend_Init(PACKET_CLIENT_MAP_OK); |
|
118 NetworkSend_Packet(p, MY_CLIENT); |
|
119 } |
|
120 |
|
121 DEF_CLIENT_SEND_COMMAND(PACKET_CLIENT_ACK) |
|
122 { |
|
123 // |
|
124 // Packet: CLIENT_ACK |
|
125 // Function: Tell the server we are done with this frame |
|
126 // Data: |
|
127 // uint32: current FrameCounter of the client |
|
128 // |
|
129 |
|
130 Packet *p = NetworkSend_Init(PACKET_CLIENT_ACK); |
|
131 |
|
132 NetworkSend_uint32(p, _frame_counter); |
|
133 NetworkSend_Packet(p, MY_CLIENT); |
|
134 } |
|
135 |
|
136 // Send a command packet to the server |
|
137 DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_COMMAND)(CommandPacket *cp) |
|
138 { |
|
139 // |
|
140 // Packet: CLIENT_COMMAND |
|
141 // Function: Send a DoCommand to the Server |
|
142 // Data: |
|
143 // uint8: PlayerID (0..MAX_PLAYERS-1) |
|
144 // uint32: CommandID (see command.h) |
|
145 // uint32: P1 (free variables used in DoCommand) |
|
146 // uint32: P2 |
|
147 // uint32: Tile |
|
148 // string: text |
|
149 // uint8: CallBackID (see callback_table.c) |
|
150 // |
|
151 |
|
152 Packet *p = NetworkSend_Init(PACKET_CLIENT_COMMAND); |
|
153 |
|
154 NetworkSend_uint8(p, cp->player); |
|
155 NetworkSend_uint32(p, cp->cmd); |
|
156 NetworkSend_uint32(p, cp->p1); |
|
157 NetworkSend_uint32(p, cp->p2); |
|
158 NetworkSend_uint32(p, (uint32)cp->tile); |
|
159 NetworkSend_string(p, cp->text); |
|
160 NetworkSend_uint8(p, cp->callback); |
|
161 |
|
162 NetworkSend_Packet(p, MY_CLIENT); |
|
163 } |
|
164 |
|
165 // Send a chat-packet over the network |
|
166 DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_CHAT)(NetworkAction action, DestType type, int dest, const char *msg) |
|
167 { |
|
168 // |
|
169 // Packet: CLIENT_CHAT |
|
170 // Function: Send a chat-packet to the serve |
|
171 // Data: |
|
172 // uint8: ActionID (see network_data.h, NetworkAction) |
|
173 // uint8: Destination Type (see network_data.h, DestType); |
|
174 // uint8: Destination Player (1..MAX_PLAYERS) |
|
175 // String: Message (max MAX_TEXT_MSG_LEN) |
|
176 // |
|
177 |
|
178 Packet *p = NetworkSend_Init(PACKET_CLIENT_CHAT); |
|
179 |
|
180 NetworkSend_uint8(p, action); |
|
181 NetworkSend_uint8(p, type); |
|
182 NetworkSend_uint8(p, dest); |
|
183 NetworkSend_string(p, msg); |
|
184 NetworkSend_Packet(p, MY_CLIENT); |
|
185 } |
|
186 |
|
187 // Send an error-packet over the network |
|
188 DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_ERROR)(NetworkErrorCode errorno) |
|
189 { |
|
190 // |
|
191 // Packet: CLIENT_ERROR |
|
192 // Function: The client made an error and is quiting the game |
|
193 // Data: |
|
194 // uint8: ErrorID (see network_data.h, NetworkErrorCode) |
|
195 // |
|
196 Packet *p = NetworkSend_Init(PACKET_CLIENT_ERROR); |
|
197 |
|
198 NetworkSend_uint8(p, errorno); |
|
199 NetworkSend_Packet(p, MY_CLIENT); |
|
200 } |
|
201 |
|
202 DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_SET_PASSWORD)(const char *password) |
|
203 { |
|
204 // |
|
205 // Packet: PACKET_CLIENT_SET_PASSWORD |
|
206 // Function: Set the password for the clients current company |
|
207 // Data: |
|
208 // String: Password |
|
209 // |
|
210 Packet *p = NetworkSend_Init(PACKET_CLIENT_SET_PASSWORD); |
|
211 |
|
212 NetworkSend_string(p, password); |
|
213 NetworkSend_Packet(p, MY_CLIENT); |
|
214 } |
|
215 |
|
216 DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_SET_NAME)(const char *name) |
|
217 { |
|
218 // |
|
219 // Packet: PACKET_CLIENT_SET_NAME |
|
220 // Function: Gives the player a new name |
|
221 // Data: |
|
222 // String: Name |
|
223 // |
|
224 Packet *p = NetworkSend_Init(PACKET_CLIENT_SET_NAME); |
|
225 |
|
226 NetworkSend_string(p, name); |
|
227 NetworkSend_Packet(p, MY_CLIENT); |
|
228 } |
|
229 |
|
230 // Send an quit-packet over the network |
|
231 DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_QUIT)(const char *leavemsg) |
|
232 { |
|
233 // |
|
234 // Packet: CLIENT_QUIT |
|
235 // Function: The client is quiting the game |
|
236 // Data: |
|
237 // String: leave-message |
|
238 // |
|
239 Packet *p = NetworkSend_Init(PACKET_CLIENT_QUIT); |
|
240 |
|
241 NetworkSend_string(p, leavemsg); |
|
242 NetworkSend_Packet(p, MY_CLIENT); |
|
243 } |
|
244 |
|
245 DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_RCON)(const char *pass, const char *command) |
|
246 { |
|
247 Packet *p = NetworkSend_Init(PACKET_CLIENT_RCON); |
|
248 NetworkSend_string(p, pass); |
|
249 NetworkSend_string(p, command); |
|
250 NetworkSend_Packet(p, MY_CLIENT); |
|
251 } |
|
252 |
|
253 |
|
254 // ********** |
|
255 // Receiving functions |
|
256 // DEF_CLIENT_RECEIVE_COMMAND has parameter: Packet *p |
|
257 // ********** |
|
258 |
|
259 extern bool SafeSaveOrLoad(const char *filename, int mode, int newgm); |
|
260 |
|
261 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_FULL) |
|
262 { |
|
263 // We try to join a server which is full |
|
264 _switch_mode_errorstr = STR_NETWORK_ERR_SERVER_FULL; |
|
265 DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0); |
|
266 |
|
267 return NETWORK_RECV_STATUS_SERVER_FULL; |
|
268 } |
|
269 |
|
270 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_BANNED) |
|
271 { |
|
272 // We try to join a server where we are banned |
|
273 _switch_mode_errorstr = STR_NETWORK_ERR_SERVER_BANNED; |
|
274 DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0); |
|
275 |
|
276 return NETWORK_RECV_STATUS_SERVER_BANNED; |
|
277 } |
|
278 |
|
279 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_COMPANY_INFO) |
|
280 { |
|
281 byte company_info_version; |
|
282 int i; |
|
283 |
|
284 company_info_version = NetworkRecv_uint8(MY_CLIENT, p); |
|
285 |
|
286 if (!MY_CLIENT->has_quit && company_info_version == NETWORK_COMPANY_INFO_VERSION) { |
|
287 byte total; |
|
288 byte current; |
|
289 |
|
290 total = NetworkRecv_uint8(MY_CLIENT, p); |
|
291 |
|
292 // There is no data at all.. |
|
293 if (total == 0) return NETWORK_RECV_STATUS_CLOSE_QUERY; |
|
294 |
|
295 current = NetworkRecv_uint8(MY_CLIENT, p); |
|
296 if (!IsValidPlayer(current)) return NETWORK_RECV_STATUS_CLOSE_QUERY; |
|
297 |
|
298 NetworkRecv_string(MY_CLIENT, p, _network_player_info[current].company_name, sizeof(_network_player_info[current].company_name)); |
|
299 _network_player_info[current].inaugurated_year = NetworkRecv_uint32(MY_CLIENT, p); |
|
300 _network_player_info[current].company_value = NetworkRecv_uint64(MY_CLIENT, p); |
|
301 _network_player_info[current].money = NetworkRecv_uint64(MY_CLIENT, p); |
|
302 _network_player_info[current].income = NetworkRecv_uint64(MY_CLIENT, p); |
|
303 _network_player_info[current].performance = NetworkRecv_uint16(MY_CLIENT, p); |
|
304 _network_player_info[current].use_password = NetworkRecv_uint8(MY_CLIENT, p); |
|
305 for (i = 0; i < NETWORK_VEHICLE_TYPES; i++) |
|
306 _network_player_info[current].num_vehicle[i] = NetworkRecv_uint16(MY_CLIENT, p); |
|
307 for (i = 0; i < NETWORK_STATION_TYPES; i++) |
|
308 _network_player_info[current].num_station[i] = NetworkRecv_uint16(MY_CLIENT, p); |
|
309 |
|
310 NetworkRecv_string(MY_CLIENT, p, _network_player_info[current].players, sizeof(_network_player_info[current].players)); |
|
311 |
|
312 InvalidateWindow(WC_NETWORK_WINDOW, 0); |
|
313 |
|
314 return NETWORK_RECV_STATUS_OKAY; |
|
315 } |
|
316 |
|
317 return NETWORK_RECV_STATUS_CLOSE_QUERY; |
|
318 } |
|
319 |
|
320 // This packet contains info about the client (playas and name) |
|
321 // as client we save this in NetworkClientInfo, linked via 'index' |
|
322 // which is always an unique number on a server. |
|
323 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_CLIENT_INFO) |
|
324 { |
|
325 NetworkClientInfo *ci; |
|
326 uint16 index = NetworkRecv_uint16(MY_CLIENT, p); |
|
327 PlayerID playas = NetworkRecv_uint8(MY_CLIENT, p); |
|
328 char name[NETWORK_NAME_LENGTH]; |
|
329 char unique_id[NETWORK_NAME_LENGTH]; |
|
330 |
|
331 NetworkRecv_string(MY_CLIENT, p, name, sizeof(name)); |
|
332 NetworkRecv_string(MY_CLIENT, p, unique_id, sizeof(unique_id)); |
|
333 |
|
334 if (MY_CLIENT->has_quit) return NETWORK_RECV_STATUS_CONN_LOST; |
|
335 |
|
336 /* Do we receive a change of data? Most likely we changed playas */ |
|
337 if (index == _network_own_client_index) _network_playas = playas; |
|
338 |
|
339 ci = NetworkFindClientInfoFromIndex(index); |
|
340 if (ci != NULL) { |
|
341 if (playas == ci->client_playas && strcmp(name, ci->client_name) != 0) { |
|
342 // Client name changed, display the change |
|
343 NetworkTextMessage(NETWORK_ACTION_NAME_CHANGE, 1, false, ci->client_name, "%s", name); |
|
344 } else if (playas != ci->client_playas) { |
|
345 // The player changed from client-player.. |
|
346 // Do not display that for now |
|
347 } |
|
348 |
|
349 ci->client_playas = playas; |
|
350 ttd_strlcpy(ci->client_name, name, sizeof(ci->client_name)); |
|
351 |
|
352 InvalidateWindow(WC_CLIENT_LIST, 0); |
|
353 |
|
354 return NETWORK_RECV_STATUS_OKAY; |
|
355 } |
|
356 |
|
357 // We don't have this index yet, find an empty index, and put the data there |
|
358 ci = NetworkFindClientInfoFromIndex(NETWORK_EMPTY_INDEX); |
|
359 if (ci != NULL) { |
|
360 ci->client_index = index; |
|
361 ci->client_playas = playas; |
|
362 |
|
363 ttd_strlcpy(ci->client_name, name, sizeof(ci->client_name)); |
|
364 ttd_strlcpy(ci->unique_id, unique_id, sizeof(ci->unique_id)); |
|
365 |
|
366 InvalidateWindow(WC_CLIENT_LIST, 0); |
|
367 |
|
368 return NETWORK_RECV_STATUS_OKAY; |
|
369 } |
|
370 |
|
371 // Here the program should never ever come..... |
|
372 return NETWORK_RECV_STATUS_MALFORMED_PACKET; |
|
373 } |
|
374 |
|
375 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_ERROR) |
|
376 { |
|
377 NetworkErrorCode error = NetworkRecv_uint8(MY_CLIENT, p); |
|
378 |
|
379 switch (error) { |
|
380 /* We made an error in the protocol, and our connection is closed.... */ |
|
381 case NETWORK_ERROR_NOT_AUTHORIZED: |
|
382 case NETWORK_ERROR_NOT_EXPECTED: |
|
383 case NETWORK_ERROR_PLAYER_MISMATCH: |
|
384 _switch_mode_errorstr = STR_NETWORK_ERR_SERVER_ERROR; |
|
385 break; |
|
386 case NETWORK_ERROR_FULL: |
|
387 _switch_mode_errorstr = STR_NETWORK_ERR_SERVER_FULL; |
|
388 break; |
|
389 case NETWORK_ERROR_WRONG_REVISION: |
|
390 _switch_mode_errorstr = STR_NETWORK_ERR_WRONG_REVISION; |
|
391 break; |
|
392 case NETWORK_ERROR_WRONG_PASSWORD: |
|
393 _switch_mode_errorstr = STR_NETWORK_ERR_WRONG_PASSWORD; |
|
394 break; |
|
395 case NETWORK_ERROR_KICKED: |
|
396 _switch_mode_errorstr = STR_NETWORK_ERR_KICKED; |
|
397 break; |
|
398 case NETWORK_ERROR_CHEATER: |
|
399 _switch_mode_errorstr = STR_NETWORK_ERR_CHEATER; |
|
400 break; |
|
401 default: |
|
402 _switch_mode_errorstr = STR_NETWORK_ERR_LOSTCONNECTION; |
|
403 } |
|
404 |
|
405 DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0); |
|
406 |
|
407 return NETWORK_RECV_STATUS_SERVER_ERROR; |
|
408 } |
|
409 |
|
410 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_NEED_PASSWORD) |
|
411 { |
|
412 NetworkPasswordType type = NetworkRecv_uint8(MY_CLIENT, p); |
|
413 |
|
414 switch (type) { |
|
415 case NETWORK_GAME_PASSWORD: |
|
416 case NETWORK_COMPANY_PASSWORD: |
|
417 ShowNetworkNeedPassword(type); |
|
418 return NETWORK_RECV_STATUS_OKAY; |
|
419 |
|
420 default: return NETWORK_RECV_STATUS_MALFORMED_PACKET; |
|
421 } |
|
422 } |
|
423 |
|
424 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_WELCOME) |
|
425 { |
|
426 _network_own_client_index = NetworkRecv_uint16(MY_CLIENT, p); |
|
427 |
|
428 // Start receiving the map |
|
429 SEND_COMMAND(PACKET_CLIENT_GETMAP)(); |
|
430 return NETWORK_RECV_STATUS_OKAY; |
|
431 } |
|
432 |
|
433 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_WAIT) |
|
434 { |
|
435 _network_join_status = NETWORK_JOIN_STATUS_WAITING; |
|
436 _network_join_waiting = NetworkRecv_uint8(MY_CLIENT, p); |
|
437 InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0); |
|
438 |
|
439 // We are put on hold for receiving the map.. we need GUI for this ;) |
|
440 DEBUG(net, 1, "The server is currently busy sending the map to someone else, please wait..." ); |
|
441 DEBUG(net, 1, "There are %d clients in front of you", _network_join_waiting); |
|
442 |
|
443 return NETWORK_RECV_STATUS_OKAY; |
|
444 } |
|
445 |
|
446 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_MAP) |
|
447 { |
|
448 static char filename[256]; |
|
449 static FILE *file_pointer; |
|
450 |
|
451 byte maptype; |
|
452 |
|
453 maptype = NetworkRecv_uint8(MY_CLIENT, p); |
|
454 |
|
455 if (MY_CLIENT->has_quit) return NETWORK_RECV_STATUS_CONN_LOST; |
|
456 |
|
457 // First packet, init some stuff |
|
458 if (maptype == MAP_PACKET_START) { |
|
459 // The name for the temp-map |
|
460 snprintf(filename, lengthof(filename), "%s%snetwork_client.tmp", _paths.autosave_dir, PATHSEP); |
|
461 |
|
462 file_pointer = fopen(filename, "wb"); |
|
463 if (file_pointer == NULL) { |
|
464 _switch_mode_errorstr = STR_NETWORK_ERR_SAVEGAMEERROR; |
|
465 return NETWORK_RECV_STATUS_SAVEGAME; |
|
466 } |
|
467 |
|
468 _frame_counter = _frame_counter_server = _frame_counter_max = NetworkRecv_uint32(MY_CLIENT, p); |
|
469 |
|
470 _network_join_status = NETWORK_JOIN_STATUS_DOWNLOADING; |
|
471 _network_join_kbytes = 0; |
|
472 _network_join_kbytes_total = NetworkRecv_uint32(MY_CLIENT, p) / 1024; |
|
473 InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0); |
|
474 |
|
475 // The first packet does not contain any more data |
|
476 return NETWORK_RECV_STATUS_OKAY; |
|
477 } |
|
478 |
|
479 if (maptype == MAP_PACKET_NORMAL) { |
|
480 // We are still receiving data, put it to the file |
|
481 fwrite(p->buffer + p->pos, 1, p->size - p->pos, file_pointer); |
|
482 |
|
483 _network_join_kbytes = ftell(file_pointer) / 1024; |
|
484 InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0); |
|
485 } |
|
486 |
|
487 // Check if this was the last packet |
|
488 if (maptype == MAP_PACKET_END) { |
|
489 fclose(file_pointer); |
|
490 |
|
491 _network_join_status = NETWORK_JOIN_STATUS_PROCESSING; |
|
492 InvalidateWindow(WC_NETWORK_STATUS_WINDOW, 0); |
|
493 |
|
494 /* The map is done downloading, load it */ |
|
495 if (!SafeSaveOrLoad(filename, SL_LOAD, GM_NORMAL)) { |
|
496 DeleteWindowById(WC_NETWORK_STATUS_WINDOW, 0); |
|
497 _switch_mode_errorstr = STR_NETWORK_ERR_SAVEGAMEERROR; |
|
498 return NETWORK_RECV_STATUS_SAVEGAME; |
|
499 } |
|
500 /* If the savegame has successfully loaded, ALL windows have been removed, |
|
501 * only toolbar/statusbar and gamefield are visible */ |
|
502 |
|
503 _opt_ptr = &_opt; // during a network game you are always in-game |
|
504 |
|
505 // Say we received the map and loaded it correctly! |
|
506 SEND_COMMAND(PACKET_CLIENT_MAP_OK)(); |
|
507 |
|
508 /* New company/spectator (invalid player) or company we want to join is not active |
|
509 * Switch local player to spectator and await the server's judgement */ |
|
510 if (_network_playas == PLAYER_NEW_COMPANY || !IsValidPlayer(_network_playas) || |
|
511 !GetPlayer(_network_playas)->is_active) { |
|
512 |
|
513 SetLocalPlayer(PLAYER_SPECTATOR); |
|
514 |
|
515 if (_network_playas != PLAYER_SPECTATOR) { |
|
516 /* We have arrived and ready to start playing; send a command to make a new player; |
|
517 * the server will give us a client-id and let us in */ |
|
518 _network_join_status = NETWORK_JOIN_STATUS_REGISTERING; |
|
519 ShowJoinStatusWindow(); |
|
520 NetworkSend_Command(0, 0, 0, CMD_PLAYER_CTRL, NULL); |
|
521 } |
|
522 } else { |
|
523 // take control over an existing company |
|
524 SetLocalPlayer(_network_playas); |
|
525 } |
|
526 } |
|
527 |
|
528 return NETWORK_RECV_STATUS_OKAY; |
|
529 } |
|
530 |
|
531 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_FRAME) |
|
532 { |
|
533 _frame_counter_server = NetworkRecv_uint32(MY_CLIENT, p); |
|
534 _frame_counter_max = NetworkRecv_uint32(MY_CLIENT, p); |
|
535 #ifdef ENABLE_NETWORK_SYNC_EVERY_FRAME |
|
536 // Test if the server supports this option |
|
537 // and if we are at the frame the server is |
|
538 if (p->pos < p->size) { |
|
539 _sync_frame = _frame_counter_server; |
|
540 _sync_seed_1 = NetworkRecv_uint32(MY_CLIENT, p); |
|
541 #ifdef NETWORK_SEND_DOUBLE_SEED |
|
542 _sync_seed_2 = NetworkRecv_uint32(MY_CLIENT, p); |
|
543 #endif |
|
544 } |
|
545 #endif |
|
546 DEBUG(net, 5, "Received FRAME %d", _frame_counter_server); |
|
547 |
|
548 // Let the server know that we received this frame correctly |
|
549 // We do this only once per day, to save some bandwidth ;) |
|
550 if (!_network_first_time && last_ack_frame < _frame_counter) { |
|
551 last_ack_frame = _frame_counter + DAY_TICKS; |
|
552 DEBUG(net, 4, "Sent ACK at %d", _frame_counter); |
|
553 SEND_COMMAND(PACKET_CLIENT_ACK)(); |
|
554 } |
|
555 |
|
556 return NETWORK_RECV_STATUS_OKAY; |
|
557 } |
|
558 |
|
559 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_SYNC) |
|
560 { |
|
561 _sync_frame = NetworkRecv_uint32(MY_CLIENT, p); |
|
562 _sync_seed_1 = NetworkRecv_uint32(MY_CLIENT, p); |
|
563 #ifdef NETWORK_SEND_DOUBLE_SEED |
|
564 _sync_seed_2 = NetworkRecv_uint32(MY_CLIENT, p); |
|
565 #endif |
|
566 |
|
567 return NETWORK_RECV_STATUS_OKAY; |
|
568 } |
|
569 |
|
570 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_COMMAND) |
|
571 { |
|
572 CommandPacket *cp = malloc(sizeof(CommandPacket)); |
|
573 cp->player = NetworkRecv_uint8(MY_CLIENT, p); |
|
574 cp->cmd = NetworkRecv_uint32(MY_CLIENT, p); |
|
575 cp->p1 = NetworkRecv_uint32(MY_CLIENT, p); |
|
576 cp->p2 = NetworkRecv_uint32(MY_CLIENT, p); |
|
577 cp->tile = NetworkRecv_uint32(MY_CLIENT, p); |
|
578 NetworkRecv_string(MY_CLIENT, p, cp->text, sizeof(cp->text)); |
|
579 cp->callback = NetworkRecv_uint8(MY_CLIENT, p); |
|
580 cp->frame = NetworkRecv_uint32(MY_CLIENT, p); |
|
581 cp->next = NULL; |
|
582 |
|
583 // The server did send us this command.. |
|
584 // queue it in our own queue, so we can handle it in the upcoming frame! |
|
585 |
|
586 if (_local_command_queue == NULL) { |
|
587 _local_command_queue = cp; |
|
588 } else { |
|
589 // Find last packet |
|
590 CommandPacket *c = _local_command_queue; |
|
591 while (c->next != NULL) c = c->next; |
|
592 c->next = cp; |
|
593 } |
|
594 |
|
595 return NETWORK_RECV_STATUS_OKAY; |
|
596 } |
|
597 |
|
598 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_CHAT) |
|
599 { |
|
600 char name[NETWORK_NAME_LENGTH], msg[MAX_TEXT_MSG_LEN]; |
|
601 const NetworkClientInfo *ci = NULL, *ci_to; |
|
602 |
|
603 NetworkAction action = NetworkRecv_uint8(MY_CLIENT, p); |
|
604 uint16 index = NetworkRecv_uint16(MY_CLIENT, p); |
|
605 bool self_send = NetworkRecv_uint8(MY_CLIENT, p); |
|
606 NetworkRecv_string(MY_CLIENT, p, msg, MAX_TEXT_MSG_LEN); |
|
607 |
|
608 ci_to = NetworkFindClientInfoFromIndex(index); |
|
609 if (ci_to == NULL) return NETWORK_RECV_STATUS_OKAY; |
|
610 |
|
611 /* Did we initiate the action locally? */ |
|
612 if (self_send) { |
|
613 switch (action) { |
|
614 case NETWORK_ACTION_CHAT_CLIENT: |
|
615 /* For speaking to client we need the client-name */ |
|
616 snprintf(name, sizeof(name), "%s", ci_to->client_name); |
|
617 ci = NetworkFindClientInfoFromIndex(_network_own_client_index); |
|
618 break; |
|
619 |
|
620 /* For speaking to company or giving money, we need the player-name */ |
|
621 case NETWORK_ACTION_GIVE_MONEY: |
|
622 if (!IsValidPlayer(ci_to->client_playas)) return NETWORK_RECV_STATUS_OKAY; |
|
623 /* fallthrough */ |
|
624 case NETWORK_ACTION_CHAT_COMPANY: { |
|
625 StringID str = IsValidPlayer(ci_to->client_playas) ? GetPlayer(ci_to->client_playas)->name_1 : STR_NETWORK_SPECTATORS; |
|
626 |
|
627 GetString(name, str, lastof(name)); |
|
628 ci = NetworkFindClientInfoFromIndex(_network_own_client_index); |
|
629 } break; |
|
630 |
|
631 default: NOT_REACHED(); break; |
|
632 } |
|
633 } else { |
|
634 /* Display message from somebody else */ |
|
635 snprintf(name, sizeof(name), "%s", ci_to->client_name); |
|
636 ci = ci_to; |
|
637 } |
|
638 |
|
639 if (ci != NULL) |
|
640 NetworkTextMessage(action, GetDrawStringPlayerColor(ci->client_playas), self_send, name, "%s", msg); |
|
641 return NETWORK_RECV_STATUS_OKAY; |
|
642 } |
|
643 |
|
644 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_ERROR_QUIT) |
|
645 { |
|
646 char str[100]; |
|
647 uint16 index; |
|
648 NetworkClientInfo *ci; |
|
649 |
|
650 index = NetworkRecv_uint16(MY_CLIENT, p); |
|
651 GetNetworkErrorMsg(str, NetworkRecv_uint8(MY_CLIENT, p), lastof(str)); |
|
652 |
|
653 ci = NetworkFindClientInfoFromIndex(index); |
|
654 if (ci != NULL) { |
|
655 NetworkTextMessage(NETWORK_ACTION_LEAVE, 1, false, ci->client_name, "%s", str); |
|
656 |
|
657 // The client is gone, give the NetworkClientInfo free |
|
658 ci->client_index = NETWORK_EMPTY_INDEX; |
|
659 } |
|
660 |
|
661 InvalidateWindow(WC_CLIENT_LIST, 0); |
|
662 |
|
663 return NETWORK_RECV_STATUS_OKAY; |
|
664 } |
|
665 |
|
666 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_QUIT) |
|
667 { |
|
668 char str[100]; |
|
669 uint16 index; |
|
670 NetworkClientInfo *ci; |
|
671 |
|
672 index = NetworkRecv_uint16(MY_CLIENT, p); |
|
673 NetworkRecv_string(MY_CLIENT, p, str, lengthof(str)); |
|
674 |
|
675 ci = NetworkFindClientInfoFromIndex(index); |
|
676 if (ci != NULL) { |
|
677 NetworkTextMessage(NETWORK_ACTION_LEAVE, 1, false, ci->client_name, "%s", str); |
|
678 |
|
679 // The client is gone, give the NetworkClientInfo free |
|
680 ci->client_index = NETWORK_EMPTY_INDEX; |
|
681 } else { |
|
682 DEBUG(net, 0, "Unknown client (%d) is leaving the game", index); |
|
683 } |
|
684 |
|
685 InvalidateWindow(WC_CLIENT_LIST, 0); |
|
686 |
|
687 // If we come here it means we could not locate the client.. strange :s |
|
688 return NETWORK_RECV_STATUS_OKAY; |
|
689 } |
|
690 |
|
691 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_JOIN) |
|
692 { |
|
693 uint16 index; |
|
694 NetworkClientInfo *ci; |
|
695 |
|
696 index = NetworkRecv_uint16(MY_CLIENT, p); |
|
697 |
|
698 ci = NetworkFindClientInfoFromIndex(index); |
|
699 if (ci != NULL) |
|
700 NetworkTextMessage(NETWORK_ACTION_JOIN, 1, false, ci->client_name, ""); |
|
701 |
|
702 InvalidateWindow(WC_CLIENT_LIST, 0); |
|
703 |
|
704 return NETWORK_RECV_STATUS_OKAY; |
|
705 } |
|
706 |
|
707 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_SHUTDOWN) |
|
708 { |
|
709 _switch_mode_errorstr = STR_NETWORK_SERVER_SHUTDOWN; |
|
710 |
|
711 return NETWORK_RECV_STATUS_SERVER_ERROR; |
|
712 } |
|
713 |
|
714 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_NEWGAME) |
|
715 { |
|
716 // To trottle the reconnects a bit, every clients waits |
|
717 // his _local_player value before reconnecting |
|
718 // PLAYER_SPECTATOR is currently 255, so to avoid long wait periods |
|
719 // set the max to 10. |
|
720 _network_reconnect = min(_local_player + 1, 10); |
|
721 _switch_mode_errorstr = STR_NETWORK_SERVER_REBOOT; |
|
722 |
|
723 return NETWORK_RECV_STATUS_SERVER_ERROR; |
|
724 } |
|
725 |
|
726 DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_RCON) |
|
727 { |
|
728 char rcon_out[NETWORK_RCONCOMMAND_LENGTH]; |
|
729 uint16 color_code; |
|
730 |
|
731 color_code = NetworkRecv_uint16(MY_CLIENT, p); |
|
732 NetworkRecv_string(MY_CLIENT, p, rcon_out, sizeof(rcon_out)); |
|
733 |
|
734 IConsolePrint(color_code, rcon_out); |
|
735 |
|
736 return NETWORK_RECV_STATUS_OKAY; |
|
737 } |
|
738 |
|
739 |
|
740 |
|
741 // The layout for the receive-functions by the client |
|
742 typedef NetworkRecvStatus NetworkClientPacket(Packet *p); |
|
743 |
|
744 // This array matches PacketType. At an incoming |
|
745 // packet it is matches against this array |
|
746 // and that way the right function to handle that |
|
747 // packet is found. |
|
748 static NetworkClientPacket* const _network_client_packet[] = { |
|
749 RECEIVE_COMMAND(PACKET_SERVER_FULL), |
|
750 RECEIVE_COMMAND(PACKET_SERVER_BANNED), |
|
751 NULL, /*PACKET_CLIENT_JOIN,*/ |
|
752 RECEIVE_COMMAND(PACKET_SERVER_ERROR), |
|
753 NULL, /*PACKET_CLIENT_COMPANY_INFO,*/ |
|
754 RECEIVE_COMMAND(PACKET_SERVER_COMPANY_INFO), |
|
755 RECEIVE_COMMAND(PACKET_SERVER_CLIENT_INFO), |
|
756 RECEIVE_COMMAND(PACKET_SERVER_NEED_PASSWORD), |
|
757 NULL, /*PACKET_CLIENT_PASSWORD,*/ |
|
758 RECEIVE_COMMAND(PACKET_SERVER_WELCOME), |
|
759 NULL, /*PACKET_CLIENT_GETMAP,*/ |
|
760 RECEIVE_COMMAND(PACKET_SERVER_WAIT), |
|
761 RECEIVE_COMMAND(PACKET_SERVER_MAP), |
|
762 NULL, /*PACKET_CLIENT_MAP_OK,*/ |
|
763 RECEIVE_COMMAND(PACKET_SERVER_JOIN), |
|
764 RECEIVE_COMMAND(PACKET_SERVER_FRAME), |
|
765 RECEIVE_COMMAND(PACKET_SERVER_SYNC), |
|
766 NULL, /*PACKET_CLIENT_ACK,*/ |
|
767 NULL, /*PACKET_CLIENT_COMMAND,*/ |
|
768 RECEIVE_COMMAND(PACKET_SERVER_COMMAND), |
|
769 NULL, /*PACKET_CLIENT_CHAT,*/ |
|
770 RECEIVE_COMMAND(PACKET_SERVER_CHAT), |
|
771 NULL, /*PACKET_CLIENT_SET_PASSWORD,*/ |
|
772 NULL, /*PACKET_CLIENT_SET_NAME,*/ |
|
773 NULL, /*PACKET_CLIENT_QUIT,*/ |
|
774 NULL, /*PACKET_CLIENT_ERROR,*/ |
|
775 RECEIVE_COMMAND(PACKET_SERVER_QUIT), |
|
776 RECEIVE_COMMAND(PACKET_SERVER_ERROR_QUIT), |
|
777 RECEIVE_COMMAND(PACKET_SERVER_SHUTDOWN), |
|
778 RECEIVE_COMMAND(PACKET_SERVER_NEWGAME), |
|
779 RECEIVE_COMMAND(PACKET_SERVER_RCON), |
|
780 NULL, /*PACKET_CLIENT_RCON,*/ |
|
781 }; |
|
782 |
|
783 // If this fails, check the array above with network_data.h |
|
784 assert_compile(lengthof(_network_client_packet) == PACKET_END); |
|
785 |
|
786 // Is called after a client is connected to the server |
|
787 void NetworkClient_Connected(void) |
|
788 { |
|
789 // Set the frame-counter to 0 so nothing happens till we are ready |
|
790 _frame_counter = 0; |
|
791 _frame_counter_server = 0; |
|
792 last_ack_frame = 0; |
|
793 // Request the game-info |
|
794 SEND_COMMAND(PACKET_CLIENT_JOIN)(); |
|
795 } |
|
796 |
|
797 // Reads the packets from the socket-stream, if available |
|
798 NetworkRecvStatus NetworkClient_ReadPackets(NetworkClientState *cs) |
|
799 { |
|
800 Packet *p; |
|
801 NetworkRecvStatus res = NETWORK_RECV_STATUS_OKAY; |
|
802 |
|
803 while (res == NETWORK_RECV_STATUS_OKAY && (p = NetworkRecv_Packet(cs, &res)) != NULL) { |
|
804 byte type = NetworkRecv_uint8(MY_CLIENT, p); |
|
805 if (type < PACKET_END && _network_client_packet[type] != NULL && !MY_CLIENT->has_quit) { |
|
806 res = _network_client_packet[type](p); |
|
807 } else { |
|
808 res = NETWORK_RECV_STATUS_MALFORMED_PACKET; |
|
809 DEBUG(net, 0, "[client] received invalid packet type %d", type); |
|
810 } |
|
811 |
|
812 free(p); |
|
813 } |
|
814 |
|
815 return res; |
|
816 } |
|
817 |
|
818 #endif /* ENABLE_NETWORK */ |
|