1 // _________ __ __
2 // / _____// |_____________ _/ |______ ____ __ __ ______
3 // \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
4 // / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
5 // /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
6 // \/ \/ \//_____/ \/
7 // ______________________ ______________________
8 // T H E W A R B E G I N S
9 // Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name netconnect.cpp - The network high level connection code. */
12 //
13 // (c) Copyright 2001-2013 by Lutz Sammer, Andreas Arens, and Jimmy Salmon
14 //
15 // This program is free software; you can redistribute it and/or modify
16 // it under the terms of the GNU General Public License as published by
17 // the Free Software Foundation; only version 2 of the License.
18 //
19 // This program is distributed in the hope that it will be useful,
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 // GNU General Public License for more details.
23 //
24 // You should have received a copy of the GNU General Public License
25 // along with this program; if not, write to the Free Software
26 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 // 02111-1307, USA.
28 //
29
30 //@{
31
32 //----------------------------------------------------------------------------
33 // Includes
34 //----------------------------------------------------------------------------
35
36 #include "stratagus.h"
37
38 #include "netconnect.h"
39
40 #include "map/map.h"
41 #include "master.h"
42 #include "network.h"
43 #include "parameters.h"
44 #include "player.h"
45 #include "script.h"
46 #include "settings.h"
47 #include "ui/interface.h"
48 #include "version.h"
49 #include "video.h"
50
51 //----------------------------------------------------------------------------
52 // Declaration
53 //----------------------------------------------------------------------------
54
55 // received nothing from client for xx frames?
56 #define CLIENT_LIVE_BEAT 60
57 #define CLIENT_IS_DEAD 300
58
59 /**
60 ** Connect state information of network systems active in current game.
61 */
62 struct NetworkState {
ClearNetworkState63 void Clear()
64 {
65 State = ccs_unused;
66 MsgCnt = 0;
67 LastFrame = 0;
68 }
69
70 unsigned char State; /// Menu: ConnectState
71 unsigned short MsgCnt; /// Menu: Counter for state msg of same type (detect unreachable)
72 unsigned long LastFrame; /// Last message received
73 // Fill in here...
74 };
75
76 //----------------------------------------------------------------------------
77 // Variables
78 //----------------------------------------------------------------------------
79
80 int HostsCount; /// Number of hosts.
81 CNetworkHost Hosts[PlayerMax]; /// Host and ports of all players.
82
83 int NetConnectRunning = 0; /// Network menu: Setup mode active
84 int NetConnectType = 0; /// Network menu: Setup mode active
85 int NetLocalHostsSlot; /// Network menu: Slot # in Hosts array of local client
86 int NetLocalPlayerNumber; /// Player number of local client
87
88 int NetPlayers; /// How many network players
89 std::string NetworkMapName; /// Name of the map received with ICMMap
90 static int NoRandomPlacementMultiplayer = 0; /// Disable the random placement of players in muliplayer mode
91
92 CServerSetup ServerSetupState; // Server selection state for Multiplayer clients
93 CServerSetup LocalSetupState; // Local selection state for Multiplayer clients
94
95 class CServer
96 {
97 public:
98 void Init(const std::string &name, CUDPSocket *socket, CServerSetup *serverSetup);
99
100 void Update(unsigned long frameCounter);
101 void Parse(unsigned long frameCounter, const unsigned char *buf, const CHost &host);
102
103 void MarkClientsAsResync();
104 void KickClient(int c);
105 private:
106 int Parse_Hello(int h, const CInitMessage_Hello &msg, const CHost &host);
107 void Parse_Resync(const int h);
108 void Parse_Waiting(const int h);
109 void Parse_Map(const int h);
110 void Parse_State(const int h, const CInitMessage_State &msg);
111 void Parse_GoodBye(const int h);
112 void Parse_SeeYou(const int h);
113
114 void Send_AreYouThere(const CNetworkHost &host);
115 void Send_GameFull(const CHost &host);
116 void Send_Welcome(const CNetworkHost &host, int hostIndex);
117 void Send_Resync(const CNetworkHost &host, int hostIndex);
118 void Send_Map(const CNetworkHost &host);
119 void Send_State(const CNetworkHost &host);
120 void Send_GoodBye(const CNetworkHost &host);
121 private:
122 std::string name;
123 NetworkState networkStates[PlayerMax]; /// Client Host states
124 CUDPSocket *socket;
125 CServerSetup *serverSetup;
126 };
127
128 class CClient
129 {
130 public:
131 void Init(const std::string &name, CUDPSocket *socket, CServerSetup *serverSetup, CServerSetup *localSetup, unsigned long tick);
SetServerHost(const CHost & host)132 void SetServerHost(const CHost &host) { serverHost = host; }
133
134 bool Parse(const unsigned char *buf, const CHost &host);
135 bool Update(unsigned long tick);
136
137 void DetachFromServer();
138
GetNetworkState() const139 int GetNetworkState() const { return networkState.State; }
140
141 private:
142 bool Update_disconnected();
143 bool Update_detaching(unsigned long tick);
144 bool Update_connecting(unsigned long tick);
145 bool Update_connected(unsigned long tick);
146 bool Update_synced(unsigned long tick);
147 bool Update_changed(unsigned long tick);
148 bool Update_async(unsigned long tick);
149 bool Update_mapinfo(unsigned long tick);
150 bool Update_badmap(unsigned long tick);
151 bool Update_goahead(unsigned long tick);
152 bool Update_started(unsigned long tick);
153
154 void Send_Go(unsigned long tick);
155 void Send_Config(unsigned long tick);
156 void Send_MapUidMismatch(unsigned long tick);
157 void Send_Map(unsigned long tick);
158 void Send_Resync(unsigned long tick);
159 void Send_State(unsigned long tick);
160 void Send_Waiting(unsigned long tick, unsigned long msec);
161 void Send_Hello(unsigned long tick);
162 void Send_GoodBye(unsigned long tick);
163
164 template <typename T>
165 void SendRateLimited(const T &msg, unsigned long tick, unsigned long msecs);
166
167 void SetConfig(const CInitMessage_Config &msg);
168
169 void Parse_GameFull();
170 void Parse_ProtocolMismatch(const unsigned char *buf);
171 void Parse_EngineMismatch(const unsigned char *buf);
172 void Parse_Resync(const unsigned char *buf);
173 void Parse_Config(const unsigned char *buf);
174 void Parse_State(const unsigned char *buf);
175 void Parse_Welcome(const unsigned char *buf);
176 void Parse_Map(const unsigned char *buf);
177 void Parse_AreYouThere();
178
179 private:
180 std::string name;
181 CHost serverHost; /// IP:port of server to join
182 NetworkState networkState;
183 unsigned char lastMsgTypeSent; /// Subtype of last InitConfig message sent
184 CUDPSocket *socket;
185 CServerSetup *serverSetup;
186 CServerSetup *localSetup;
187 };
188
189 static CServer Server;
190 static CClient Client;
191
192 //
193 // CClient
194 //
195
196 /**
197 ** Send an InitConfig message across the Network
198 **
199 ** @param host Host to send to (network byte order).
200 ** @param port Port of host to send to (network byte order).
201 ** @param msg The message to send
202 */
203 template <typename T>
NetworkSendICMessage(CUDPSocket & socket,const CHost & host,const T & msg)204 static void NetworkSendICMessage(CUDPSocket &socket, const CHost &host, const T &msg)
205 {
206 const unsigned char *buf = msg.Serialize();
207 socket.Send(host, buf, msg.Size());
208 delete[] buf;
209 }
210
NetworkSendICMessage(CUDPSocket & socket,const CHost & host,const CInitMessage_Header & msg)211 void NetworkSendICMessage(CUDPSocket &socket, const CHost &host, const CInitMessage_Header &msg)
212 {
213 unsigned char *buf = new unsigned char [msg.Size()];
214 msg.Serialize(buf);
215 socket.Send(host, buf, msg.Size());
216 delete[] buf;
217 }
218
219 static const char *ncconstatenames[] = {
220 "ccs_unused",
221 "ccs_connecting", // new client
222 "ccs_connected", // has received slot info
223 "ccs_mapinfo", // has received matching map-info
224 "ccs_badmap", // has received non-matching map-info
225 "ccs_synced", // client is in sync with server
226 "ccs_async", // server user has changed selection
227 "ccs_changed", // client user has made menu selection
228 "ccs_detaching", // client user wants to detach
229 "ccs_disconnected", // client has detached
230 "ccs_unreachable", // server is unreachable
231 "ccs_usercanceled", // user canceled game
232 "ccs_nofreeslots", // server has no more free slots
233 "ccs_serverquits", // server quits
234 "ccs_goahead", // server wants to start game
235 "ccs_started", // server has started game
236 "ccs_incompatibleengine", // incompatible engine version
237 "ccs_incompatiblenetwork", // incompatible network version
238 };
239
240 static const char *icmsgsubtypenames[] = {
241 "Hello", // Client Request
242 "Config", // Setup message configure clients
243
244 "EngineMismatch", // Stratagus engine version doesn't match
245 "ProtocolMismatch", // Network protocol version doesn't match
246 "EngineConfMismatch", // Engine configuration isn't identical
247 "MapUidMismatch", // MAP UID doesn't match
248
249 "GameFull", // No player slots available
250 "Welcome", // Acknowledge for new client connections
251
252 "Waiting", // Client has received Welcome and is waiting for Map/State
253 "Map", // MapInfo (and Mapinfo Ack)
254 "State", // StateInfo
255 "Resync", // Ack StateInfo change
256
257 "ServerQuit", // Server has quit game
258 "GoodBye", // Client wants to leave game
259 "SeeYou", // Client has left game
260
261 "Go", // Client is ready to run
262 "AreYouThere", // Server asks are you there
263 "IAmHere", // Client answers I am here
264 };
265
266 template <typename T>
NetworkSendICMessage_Log(CUDPSocket & socket,const CHost & host,const T & msg)267 static void NetworkSendICMessage_Log(CUDPSocket &socket, const CHost &host, const T &msg)
268 {
269 NetworkSendICMessage(socket, host, msg);
270
271 #ifdef DEBUG
272 const std::string hostStr = host.toString();
273 DebugPrint("Sending to %s -> %s\n" _C_ hostStr.c_str()
274 _C_ icmsgsubtypenames[msg.GetHeader().GetSubType()]);
275 #endif
276 }
277
NetworkSendICMessage_Log(CUDPSocket & socket,const CHost & host,const CInitMessage_Header & msg)278 static void NetworkSendICMessage_Log(CUDPSocket &socket, const CHost &host, const CInitMessage_Header &msg)
279 {
280 NetworkSendICMessage(socket, host, msg);
281
282 #ifdef DEBUG
283 const std::string hostStr = host.toString();
284 DebugPrint("Sending to %s -> %s\n" _C_ hostStr.c_str()
285 _C_ icmsgsubtypenames[msg.GetSubType()]);
286 #endif
287 }
288
289 /**
290 ** Send a message to the server, but only if the last packet was a while ago
291 **
292 ** @param msg The message to send
293 ** @param tick current tick
294 ** @param msecs microseconds to delay
295 */
296 template <typename T>
SendRateLimited(const T & msg,unsigned long tick,unsigned long msecs)297 void CClient::SendRateLimited(const T &msg, unsigned long tick, unsigned long msecs)
298 {
299 const unsigned long now = tick;
300 if (now - networkState.LastFrame < msecs) {
301 return;
302 }
303 networkState.LastFrame = now;
304 const unsigned char subtype = msg.GetHeader().GetSubType();
305 if (subtype == lastMsgTypeSent) {
306 ++networkState.MsgCnt;
307 } else {
308 networkState.MsgCnt = 0;
309 lastMsgTypeSent = subtype;
310 }
311 NetworkSendICMessage(*socket, serverHost, msg);
312 DebugPrint("[%s] Sending (%s:#%d)\n" _C_
313 ncconstatenames[networkState.State] _C_
314 icmsgsubtypenames[subtype] _C_ networkState.MsgCnt);
315 }
316
317 template<>
SendRateLimited(const CInitMessage_Header & msg,unsigned long tick,unsigned long msecs)318 void CClient::SendRateLimited<CInitMessage_Header>(const CInitMessage_Header &msg, unsigned long tick, unsigned long msecs)
319 {
320 const unsigned long now = tick;
321 if (now - networkState.LastFrame < msecs) {
322 return;
323 }
324 networkState.LastFrame = now;
325 const unsigned char subtype = msg.GetSubType();
326 if (subtype == lastMsgTypeSent) {
327 ++networkState.MsgCnt;
328 } else {
329 networkState.MsgCnt = 0;
330 lastMsgTypeSent = subtype;
331 }
332 NetworkSendICMessage(*socket, serverHost, msg);
333 DebugPrint("[%s] Sending (%s:#%d)\n" _C_
334 ncconstatenames[networkState.State] _C_
335 icmsgsubtypenames[subtype] _C_ networkState.MsgCnt);
336 }
337
Init(const std::string & name,CUDPSocket * socket,CServerSetup * serverSetup,CServerSetup * localSetup,unsigned long tick)338 void CClient::Init(const std::string &name, CUDPSocket *socket, CServerSetup *serverSetup, CServerSetup *localSetup, unsigned long tick)
339 {
340 networkState.LastFrame = tick;
341 networkState.State = ccs_connecting;
342 networkState.MsgCnt = 0;
343 lastMsgTypeSent = ICMServerQuit;
344 this->serverSetup = serverSetup;
345 this->localSetup = localSetup;
346 this->name = name;
347 this->socket = socket;
348 }
349
DetachFromServer()350 void CClient::DetachFromServer()
351 {
352 networkState.State = ccs_detaching;
353 networkState.MsgCnt = 0;
354 }
355
Update_disconnected()356 bool CClient::Update_disconnected()
357 {
358 Assert(networkState.State == ccs_disconnected);
359 const CInitMessage_Header message(MessageInit_FromClient, ICMSeeYou);
360
361 // Spew out 5 and trust in God that they arrive
362 for (int i = 0; i < 5; ++i) {
363 NetworkSendICMessage(*socket, serverHost, message);
364 }
365 networkState.State = ccs_usercanceled;
366 return false;
367 }
368
Update_detaching(unsigned long tick)369 bool CClient::Update_detaching(unsigned long tick)
370 {
371 Assert(networkState.State == ccs_detaching);
372
373 if (networkState.MsgCnt < 10) { // 10 retries = 1 second
374 Send_GoodBye(tick);
375 return true;
376 } else {
377 networkState.State = ccs_unreachable;
378 DebugPrint("ccs_detaching: Above message limit %d\n" _C_ networkState.MsgCnt);
379 return false;
380 }
381 }
382
Update_connecting(unsigned long tick)383 bool CClient::Update_connecting(unsigned long tick)
384 {
385 Assert(networkState.State == ccs_connecting);
386
387 if (networkState.MsgCnt < 48) { // 48 retries = 24 seconds
388 Send_Hello(tick);
389 return true;
390 } else {
391 networkState.State = ccs_unreachable;
392 DebugPrint("ccs_connecting: Above message limit %d\n" _C_ networkState.MsgCnt);
393 return false;
394 }
395 }
396
Update_connected(unsigned long tick)397 bool CClient::Update_connected(unsigned long tick)
398 {
399 Assert(networkState.State == ccs_connected);
400
401 if (networkState.MsgCnt < 20) { // 20 retries
402 Send_Waiting(tick, 650);
403 return true;
404 } else {
405 networkState.State = ccs_unreachable;
406 DebugPrint("ccs_connected: Above message limit %d\n" _C_ networkState.MsgCnt);
407 return false;
408 }
409 }
410
IsLocalSetupInSync(const CServerSetup & state1,const CServerSetup & state2,int index)411 static bool IsLocalSetupInSync(const CServerSetup &state1, const CServerSetup &state2, int index)
412 {
413 return (state1.Race[index] == state2.Race[index]
414 && state1.Ready[index] == state2.Ready[index]);
415 }
416
Update_synced(unsigned long tick)417 bool CClient::Update_synced(unsigned long tick)
418 {
419 Assert(networkState.State == ccs_synced);
420
421 if (IsLocalSetupInSync(*serverSetup, *localSetup, NetLocalHostsSlot) == false) {
422 networkState.State = ccs_changed;
423 networkState.MsgCnt = 0;
424 return Update(tick);
425 }
426 Send_Waiting(tick, 850);
427 return true;
428 }
429
Update_changed(unsigned long tick)430 bool CClient::Update_changed(unsigned long tick)
431 {
432 Assert(networkState.State == ccs_changed);
433
434 if (networkState.MsgCnt < 20) { // 20 retries
435 Send_State(tick);
436 return true;
437 } else {
438 networkState.State = ccs_unreachable;
439 DebugPrint("ccs_changed: Above message limit %d\n" _C_ networkState.MsgCnt);
440 return false;
441 }
442 }
443
Update_async(unsigned long tick)444 bool CClient::Update_async(unsigned long tick)
445 {
446 Assert(networkState.State == ccs_async);
447
448 if (networkState.MsgCnt < 20) { // 20 retries
449 Send_Resync(tick);
450 return true;
451 } else {
452 networkState.State = ccs_unreachable;
453 DebugPrint("ccs_async: Above message limit %d\n" _C_ networkState.MsgCnt);
454 return false;
455 }
456 }
457
Update_mapinfo(unsigned long tick)458 bool CClient::Update_mapinfo(unsigned long tick)
459 {
460 Assert(networkState.State == ccs_mapinfo);
461
462 if (networkState.MsgCnt < 20) { // 20 retries
463 // ICMMapAck..
464 Send_Map(tick);
465 return true;
466 } else {
467 networkState.State = ccs_unreachable;
468 DebugPrint("ccs_mapinfo: Above message limit %d\n" _C_ networkState.MsgCnt);
469 return false;
470 }
471 }
472
Update_badmap(unsigned long tick)473 bool CClient::Update_badmap(unsigned long tick)
474 {
475 Assert(networkState.State == ccs_badmap);
476
477 if (networkState.MsgCnt < 20) { // 20 retries
478 Send_MapUidMismatch(tick);
479 return true;
480 } else {
481 networkState.State = ccs_unreachable;
482 DebugPrint("ccs_badmap: Above message limit %d\n" _C_ networkState.MsgCnt);
483 return false;
484 }
485 }
486
Update_goahead(unsigned long tick)487 bool CClient::Update_goahead(unsigned long tick)
488 {
489 Assert(networkState.State == ccs_goahead);
490
491 if (networkState.MsgCnt < 50) { // 50 retries
492 Send_Config(tick);
493 return true;
494 } else {
495 networkState.State = ccs_unreachable;
496 DebugPrint("ccs_goahead: Above message limit %d\n" _C_ networkState.MsgCnt);
497 return false;
498 }
499 }
500
Update_started(unsigned long tick)501 bool CClient::Update_started(unsigned long tick)
502 {
503 Assert(networkState.State == ccs_started);
504
505 if (networkState.MsgCnt < 20) { // 20 retries
506 Send_Go(tick);
507 return true;
508 } else {
509 return false; // End the menu..
510 }
511 }
512
Send_Go(unsigned long tick)513 void CClient::Send_Go(unsigned long tick)
514 {
515 const CInitMessage_Header message(MessageInit_FromClient, ICMGo);
516
517 SendRateLimited(message, tick, 250);
518 }
519
Send_Config(unsigned long tick)520 void CClient::Send_Config(unsigned long tick)
521 {
522 const CInitMessage_Header message(MessageInit_FromClient, ICMConfig);
523
524 SendRateLimited(message, tick, 250);
525 }
526
Send_MapUidMismatch(unsigned long tick)527 void CClient::Send_MapUidMismatch(unsigned long tick)
528 {
529 const CInitMessage_Header message(MessageInit_FromClient, ICMMapUidMismatch); // MAP Uid doesn't match
530
531 SendRateLimited(message, tick, 650);
532 }
533
Send_Map(unsigned long tick)534 void CClient::Send_Map(unsigned long tick)
535 {
536 const CInitMessage_Header message(MessageInit_FromClient, ICMMap);
537
538 SendRateLimited(message, tick, 650);
539 }
540
Send_Resync(unsigned long tick)541 void CClient::Send_Resync(unsigned long tick)
542 {
543 const CInitMessage_Header message(MessageInit_FromClient, ICMResync);
544
545 SendRateLimited(message, tick, 450);
546 }
547
Send_State(unsigned long tick)548 void CClient::Send_State(unsigned long tick)
549 {
550 const CInitMessage_State message(MessageInit_FromClient, *localSetup);
551
552 SendRateLimited(message, tick, 450);
553 }
554
Send_Waiting(unsigned long tick,unsigned long msec)555 void CClient::Send_Waiting(unsigned long tick, unsigned long msec)
556 {
557 const CInitMessage_Header message(MessageInit_FromClient, ICMWaiting);
558
559 SendRateLimited(message, tick, msec);
560 }
561
Send_Hello(unsigned long tick)562 void CClient::Send_Hello(unsigned long tick)
563 {
564 const CInitMessage_Hello message(name.c_str());
565
566 SendRateLimited(message, tick, 500);
567 }
568
Send_GoodBye(unsigned long tick)569 void CClient::Send_GoodBye(unsigned long tick)
570 {
571 const CInitMessage_Header message(MessageInit_FromClient, ICMGoodBye);
572
573 SendRateLimited(message, tick, 100);
574 }
575
576 /*
577 ** @return false when client has finished.
578 */
Update(unsigned long tick)579 bool CClient::Update(unsigned long tick)
580 {
581 switch (networkState.State) {
582 case ccs_disconnected: return Update_disconnected();
583 case ccs_detaching: return Update_detaching(tick);
584 case ccs_connecting: return Update_connecting(tick);
585 case ccs_connected: return Update_connected(tick);
586 case ccs_synced: return Update_synced(tick);
587 case ccs_changed: return Update_changed(tick);
588 case ccs_async: return Update_async(tick);
589 case ccs_mapinfo: return Update_mapinfo(tick);
590 case ccs_badmap: return Update_badmap(tick);
591 case ccs_goahead: return Update_goahead(tick);
592 case ccs_started: return Update_started(tick);
593 default: break;
594 }
595 return true;
596 }
597
SetConfig(const CInitMessage_Config & msg)598 void CClient::SetConfig(const CInitMessage_Config &msg)
599 {
600 HostsCount = 0;
601 for (int i = 0; i < msg.hostsCount - 1; ++i) {
602 if (i != msg.clientIndex) {
603 Hosts[HostsCount] = msg.hosts[i];
604 HostsCount++;
605 #ifdef DEBUG
606 const std::string hostStr = CHost(msg.hosts[i].Host, msg.hosts[i].Port).toString();
607 DebugPrint("Client %d = %s [%.*s]\n" _C_
608 msg.hosts[i].PlyNr _C_ hostStr.c_str() _C_
609 static_cast<int>(sizeof(msg.hosts[i].PlyName)) _C_
610 msg.hosts[i].PlyName);
611 #endif
612 } else { // Own client
613 NetLocalPlayerNumber = msg.hosts[i].PlyNr;
614 DebugPrint("SELF %d [%.*s]\n" _C_ msg.hosts[i].PlyNr _C_
615 static_cast<int>(sizeof(msg.hosts[i].PlyName)) _C_
616 msg.hosts[i].PlyName);
617 }
618 }
619 // server is last:
620 Hosts[HostsCount].Host = serverHost.getIp();
621 Hosts[HostsCount].Port = serverHost.getPort();
622 Hosts[HostsCount].PlyNr = msg.hosts[msg.hostsCount - 1].PlyNr;
623 Hosts[HostsCount].SetName(msg.hosts[msg.hostsCount - 1].PlyName);
624 ++HostsCount;
625 NetPlayers = HostsCount + 1;
626 #ifdef DEBUG
627 const std::string serverHostStr = serverHost.toString();
628 DebugPrint("Server %d = %s [%.*s]\n" _C_
629 msg.hosts[msg.hostsCount - 1].PlyNr _C_
630 serverHostStr.c_str() _C_
631 static_cast<int>(sizeof(msg.hosts[msg.hostsCount - 1].PlyName)) _C_
632 msg.hosts[msg.hostsCount - 1].PlyName);
633 #endif
634 }
635
Parse(const unsigned char * buf,const CHost & host)636 bool CClient::Parse(const unsigned char *buf, const CHost &host)
637 {
638 CInitMessage_Header header;
639 header.Deserialize(buf);
640
641 if (header.GetType() != MessageInit_FromServer) {
642 return true;
643 }
644 // Assert(host == this->serverHost);
645 const unsigned char msgsubtype = header.GetSubType();
646
647 DebugPrint("Received %s in state %s\n" _C_ icmsgsubtypenames[msgsubtype]
648 _C_ ncconstatenames[networkState.State]);
649
650 switch (msgsubtype) {
651 case ICMServerQuit: { // Server user canceled, should work in all states
652 networkState.State = ccs_serverquits;
653 // No ack here - Server will spew out a few Quit msgs, which has to be enough
654 return false;
655 }
656 case ICMAYT: { // Server is checking for our presence
657 Parse_AreYouThere();
658 break;
659 }
660 case ICMGoodBye: { // Server has let us go
661 if (networkState.State == ccs_detaching) {
662 networkState.State = ccs_disconnected;
663 networkState.MsgCnt = 0;
664 }
665 break;
666 }
667 case ICMEngineMismatch: { // Stratagus engine version doesn't match
668 Parse_EngineMismatch(buf);
669 return false;
670 }
671 case ICMProtocolMismatch: { // Network protocol version doesn't match
672 Parse_ProtocolMismatch(buf);
673 return false;
674 }
675 case ICMGameFull: { // Game is full - server rejected connnection
676 Parse_GameFull();
677 return false;
678 }
679 case ICMWelcome: { // Server has accepted us
680 Parse_Welcome(buf);
681 break;
682 }
683 case ICMMap: { // Server has sent us new map info
684 Parse_Map(buf);
685 break;
686 }
687 case ICMState: {
688 Parse_State(buf);
689 break;
690 }
691 case ICMConfig: { // Server gives the go ahead.. - start game
692 Parse_Config(buf);
693 break;
694 }
695 case ICMResync: { // Server has resynced with us and sends resync data
696 Parse_Resync(buf);
697 break;
698 }
699 case ICMGo: { // Server's final go ..
700 // ccs_started
701 DebugPrint("ClientParseStarted ICMGo !!!!!\n");
702 return false;
703 }
704 default: break;
705 }
706 return true;
707 }
708
709 /**
710 ** Check if the map name looks safe.
711 **
712 ** A map name looks safe when there are no special characters
713 ** and no .. or // sequences. This way only real valid
714 ** maps from the map directory will be loaded.
715 **
716 ** @return true if the map name looks safe.
717 */
IsSafeMapName(const char * mapname)718 static bool IsSafeMapName(const char *mapname)
719 {
720 char buf[256];
721
722 if (strncpy_s(buf, sizeof(buf), mapname, sizeof(buf)) != 0) {
723 return false;
724 }
725 if (strstr(buf, "..")) {
726 return false;
727 }
728 if (strstr(buf, "//")) {
729 return false;
730 }
731 if (buf[0] == '\0') {
732 return false;
733 }
734
735 for (const char *ch = buf; *ch != '\0'; ++ch) {
736 if (!isalnum(*ch) && *ch != '/' && *ch != '.' && *ch != '-'
737 && *ch != '(' && *ch != ')' && *ch != '_') {
738 return false;
739 }
740 }
741 return true;
742 }
743
Parse_Map(const unsigned char * buf)744 void CClient::Parse_Map(const unsigned char *buf)
745 {
746 if (networkState.State != ccs_connected) {
747 return;
748 }
749 CInitMessage_Map msg;
750
751 msg.Deserialize(buf);
752 if (!IsSafeMapName(msg.MapPath)) {
753 fprintf(stderr, "Unsecure map name!\n");
754 networkState.State = ccs_badmap;
755 return;
756 }
757 NetworkMapName = std::string(msg.MapPath, sizeof(msg.MapPath));
758 const std::string mappath = StratagusLibPath + "/" + NetworkMapName;
759 LoadStratagusMapInfo(mappath);
760 if (msg.MapUID != Map.Info.MapUID) {
761 networkState.State = ccs_badmap;
762 fprintf(stderr, "Stratagus maps do not match (0x%08x) <-> (0x%08x)\n",
763 Map.Info.MapUID, static_cast<unsigned int>(msg.MapUID));
764 return;
765 }
766 networkState.State = ccs_mapinfo;
767 networkState.MsgCnt = 0;
768 }
769
Parse_Welcome(const unsigned char * buf)770 void CClient::Parse_Welcome(const unsigned char *buf)
771 {
772 if (networkState.State != ccs_connecting) {
773 return;
774 }
775 CInitMessage_Welcome msg;
776
777 msg.Deserialize(buf);
778 networkState.State = ccs_connected;
779 networkState.MsgCnt = 0;
780 NetLocalHostsSlot = msg.hosts[0].PlyNr;
781 Hosts[0].SetName(msg.hosts[0].PlyName); // Name of server player
782 CNetworkParameter::Instance.NetworkLag = msg.Lag;
783 CNetworkParameter::Instance.gameCyclesPerUpdate = msg.gameCyclesPerUpdate;
784
785 Hosts[0].Host = serverHost.getIp();
786 Hosts[0].Port = serverHost.getPort();
787 for (int i = 1; i < PlayerMax; ++i) {
788 if (i != NetLocalHostsSlot) {
789 Hosts[i] = msg.hosts[i];
790 } else {
791 Hosts[i].PlyNr = i;
792 Hosts[i].SetName(name.c_str());
793 }
794 }
795 }
796
Parse_State(const unsigned char * buf)797 void CClient::Parse_State(const unsigned char *buf)
798 {
799 CInitMessage_State msg;
800
801 msg.Deserialize(buf);
802 if (networkState.State == ccs_mapinfo) {
803 // Server has sent us first state info
804 *serverSetup = msg.State;
805 networkState.State = ccs_synced;
806 networkState.MsgCnt = 0;
807 } else if (networkState.State == ccs_synced
808 || networkState.State == ccs_changed) {
809 *serverSetup = msg.State;
810 networkState.State = ccs_async;
811 networkState.MsgCnt = 0;
812 } else if (networkState.State == ccs_goahead) {
813 // Server has sent final state info
814 *serverSetup = msg.State;
815 networkState.State = ccs_started;
816 networkState.MsgCnt = 0;
817 }
818 }
819
Parse_Config(const unsigned char * buf)820 void CClient::Parse_Config(const unsigned char *buf)
821 {
822 if (networkState.State != ccs_synced) {
823 return;
824 }
825 CInitMessage_Config msg;
826
827 msg.Deserialize(buf);
828 SetConfig(msg);
829 networkState.State = ccs_goahead;
830 networkState.MsgCnt = 0;
831 }
832
Parse_Resync(const unsigned char * buf)833 void CClient::Parse_Resync(const unsigned char *buf)
834 {
835 if (networkState.State != ccs_async) {
836 return;
837 }
838 CInitMessage_Resync msg;
839
840 msg.Deserialize(buf);
841 for (int i = 1; i < PlayerMax - 1; ++i) {
842 if (i != NetLocalHostsSlot) {
843 Hosts[i] = msg.hosts[i];
844 } else {
845 Hosts[i].PlyNr = msg.hosts[i].PlyNr;
846 Hosts[i].SetName(name.c_str());
847 }
848 }
849 networkState.State = ccs_synced;
850 networkState.MsgCnt = 0;
851 }
852
Parse_GameFull()853 void CClient::Parse_GameFull()
854 {
855 if (networkState.State != ccs_connecting) {
856 return;
857 }
858 const std::string serverHostStr = serverHost.toString();
859 fprintf(stderr, "Server at %s is full!\n", serverHostStr.c_str());
860 networkState.State = ccs_nofreeslots;
861 }
862
Parse_ProtocolMismatch(const unsigned char * buf)863 void CClient::Parse_ProtocolMismatch(const unsigned char *buf)
864 {
865 if (networkState.State != ccs_connecting) {
866 return;
867 }
868 CInitMessage_ProtocolMismatch msg;
869
870 msg.Deserialize(buf);
871 const std::string serverHostStr = serverHost.toString();
872 fprintf(stderr, "Incompatible network protocol version "
873 NetworkProtocolFormatString " <-> " NetworkProtocolFormatString "\n"
874 "from %s\n",
875 NetworkProtocolFormatArgs(NetworkProtocolVersion), NetworkProtocolFormatArgs(msg.Version),
876 serverHostStr.c_str());
877 networkState.State = ccs_incompatiblenetwork;
878 }
879
Parse_EngineMismatch(const unsigned char * buf)880 void CClient::Parse_EngineMismatch(const unsigned char *buf)
881 {
882 if (networkState.State != ccs_connecting) {
883 return;
884 }
885 CInitMessage_EngineMismatch msg;
886
887 msg.Deserialize(buf);
888 const std::string serverHostStr = serverHost.toString();
889 fprintf(stderr, "Incompatible " NAME " version %d <-> %d\nfrom %s\n",
890 StratagusVersion, msg.Stratagus, serverHostStr.c_str());
891 networkState.State = ccs_incompatibleengine;
892 }
893
894 /**
895 ** Parse a network menu AreYouThere keepalive packet and reply IAmHere.
896 **
897 ** @param msg message received
898 */
Parse_AreYouThere()899 void CClient::Parse_AreYouThere()
900 {
901 const CInitMessage_Header message(MessageInit_FromClient, ICMIAH); // IAmHere
902
903 NetworkSendICMessage(*socket, serverHost, message);
904 }
905
906 //
907 // CServer
908 //
909
KickClient(int c)910 void CServer::KickClient(int c)
911 {
912 DebugPrint("kicking client %d\n" _C_ Hosts[c].PlyNr);
913 Hosts[c].Clear();
914 serverSetup->Ready[c] = 0;
915 serverSetup->Race[c] = 0;
916 networkStates[c].Clear();
917 // Resync other clients
918 for (int n = 1; n < PlayerMax - 1; ++n) {
919 if (n != c && Hosts[n].PlyNr) {
920 networkStates[n].State = ccs_async;
921 }
922 }
923 }
924
Init(const std::string & name,CUDPSocket * socket,CServerSetup * serverSetup)925 void CServer::Init(const std::string &name, CUDPSocket *socket, CServerSetup *serverSetup)
926 {
927 for (int i = 0; i < PlayerMax; ++i) {
928 networkStates[i].Clear();
929 //Hosts[i].Clear();
930 }
931 this->serverSetup = serverSetup;
932 this->name = name;
933 this->socket = socket;
934 }
935
Send_AreYouThere(const CNetworkHost & host)936 void CServer::Send_AreYouThere(const CNetworkHost &host)
937 {
938 const CInitMessage_Header message(MessageInit_FromServer, ICMAYT); // AreYouThere
939
940 NetworkSendICMessage(*socket, CHost(host.Host, host.Port), message);
941 }
942
Send_GameFull(const CHost & host)943 void CServer::Send_GameFull(const CHost &host)
944 {
945 const CInitMessage_Header message(MessageInit_FromServer, ICMGameFull);
946
947 NetworkSendICMessage_Log(*socket, host, message);
948 }
949
Send_Welcome(const CNetworkHost & host,int index)950 void CServer::Send_Welcome(const CNetworkHost &host, int index)
951 {
952 CInitMessage_Welcome message;
953
954 message.hosts[0].PlyNr = index; // Host array slot number
955 message.hosts[0].SetName(name.c_str()); // Name of server player
956 for (int i = 1; i < PlayerMax - 1; ++i) { // Info about other clients
957 if (i != index && Hosts[i].PlyNr) {
958 message.hosts[i] = Hosts[i];
959 }
960 }
961 NetworkSendICMessage_Log(*socket, CHost(host.Host, host.Port), message);
962 }
963
Send_Resync(const CNetworkHost & host,int hostIndex)964 void CServer::Send_Resync(const CNetworkHost &host, int hostIndex)
965 {
966 CInitMessage_Resync message;
967
968 for (int i = 1; i < PlayerMax - 1; ++i) { // Info about other clients
969 if (i != hostIndex && Hosts[i].PlyNr) {
970 message.hosts[i] = Hosts[i];
971 }
972 }
973 NetworkSendICMessage_Log(*socket, CHost(host.Host, host.Port), message);
974 }
975
Send_Map(const CNetworkHost & host)976 void CServer::Send_Map(const CNetworkHost &host)
977 {
978 const CInitMessage_Map message(NetworkMapName.c_str(), Map.Info.MapUID);
979
980 NetworkSendICMessage_Log(*socket, CHost(host.Host, host.Port), message);
981 }
982
Send_State(const CNetworkHost & host)983 void CServer::Send_State(const CNetworkHost &host)
984 {
985 const CInitMessage_State message(MessageInit_FromServer, *serverSetup);
986
987 NetworkSendICMessage_Log(*socket, CHost(host.Host, host.Port), message);
988 }
989
Send_GoodBye(const CNetworkHost & host)990 void CServer::Send_GoodBye(const CNetworkHost &host)
991 {
992 const CInitMessage_Header message(MessageInit_FromServer, ICMGoodBye);
993
994 NetworkSendICMessage_Log(*socket, CHost(host.Host, host.Port), message);
995 }
996
Update(unsigned long frameCounter)997 void CServer::Update(unsigned long frameCounter)
998 {
999 for (int i = 1; i < PlayerMax - 1; ++i) {
1000 if (Hosts[i].PlyNr && Hosts[i].Host && Hosts[i].Port) {
1001 const unsigned long fcd = frameCounter - networkStates[i].LastFrame;
1002 if (fcd >= CLIENT_LIVE_BEAT) {
1003 if (fcd > CLIENT_IS_DEAD) {
1004 KickClient(i);
1005 } else if (fcd % 5 == 0) {
1006 // Probe for the client
1007 Send_AreYouThere(Hosts[i]);
1008 }
1009 }
1010 }
1011 }
1012 }
1013
MarkClientsAsResync()1014 void CServer::MarkClientsAsResync()
1015 {
1016 for (int i = 1; i < PlayerMax - 1; ++i) {
1017 if (Hosts[i].PlyNr && networkStates[i].State == ccs_synced) {
1018 networkStates[i].State = ccs_async;
1019 }
1020 }
1021 }
1022
1023 /**
1024 ** Parse the initial 'Hello' message of new client that wants to join the game
1025 **
1026 ** @param h slot number of host msg originates from
1027 ** @param msg message received
1028 ** @param host host which send the message
1029 **
1030 ** @return host index
1031 */
Parse_Hello(int h,const CInitMessage_Hello & msg,const CHost & host)1032 int CServer::Parse_Hello(int h, const CInitMessage_Hello &msg, const CHost &host)
1033 {
1034 if (h == -1) { // it is a new client
1035 for (int i = 1; i < PlayerMax - 1; ++i) {
1036 // occupy first available slot
1037 if (serverSetup->CompOpt[i] == 0) {
1038 if (Hosts[i].PlyNr == 0) {
1039 h = i;
1040 break;
1041 }
1042 }
1043 }
1044 if (h != -1) {
1045 Hosts[h].Host = host.getIp();
1046 Hosts[h].Port = host.getPort();
1047 Hosts[h].PlyNr = h;
1048 Hosts[h].SetName(msg.PlyName);
1049 #ifdef DEBUG
1050 const std::string hostStr = host.toString();
1051 DebugPrint("New client %s [%s]\n" _C_ hostStr.c_str() _C_ Hosts[h].PlyName);
1052 #endif
1053 networkStates[h].State = ccs_connecting;
1054 networkStates[h].MsgCnt = 0;
1055 } else {
1056 // Game is full - reject connnection
1057 Send_GameFull(host);
1058 return -1;
1059 }
1060 }
1061 // this code path happens until client sends waiting (= has received this message)
1062 Send_Welcome(Hosts[h], h);
1063
1064 networkStates[h].MsgCnt++;
1065 if (networkStates[h].MsgCnt > 48) {
1066 // Detects UDP input firewalled or behind NAT firewall clients
1067 // If packets are missed, clients are kicked by AYT check later..
1068 KickClient(h);
1069 return -1;
1070 }
1071 return h;
1072 }
1073
1074 /**
1075 ** Parse client resync request after client user has changed menu selection
1076 **
1077 ** @param h slot number of host msg originates from
1078 */
Parse_Resync(const int h)1079 void CServer::Parse_Resync(const int h)
1080 {
1081 switch (networkStates[h].State) {
1082 case ccs_mapinfo:
1083 // a delayed ack - fall through..
1084 case ccs_async:
1085 // client has recvd welcome and is waiting for info
1086 networkStates[h].State = ccs_synced;
1087 networkStates[h].MsgCnt = 0;
1088 /* Fall through */
1089 case ccs_synced: {
1090 // this code path happens until client falls back to ICMWaiting
1091 // (indicating Resync has completed)
1092 Send_Resync(Hosts[h], h);
1093
1094 networkStates[h].MsgCnt++;
1095 if (networkStates[h].MsgCnt > 50) {
1096 // FIXME: Client sends resync, but doesn't receive our resync ack....
1097 }
1098 break;
1099 }
1100 default:
1101 DebugPrint("Server: ICMResync: Unhandled state %d Host %d\n" _C_ networkStates[h].State _C_ h);
1102 break;
1103 }
1104 }
1105
1106 /**
1107 ** Parse client heart beat waiting message
1108 **
1109 ** @param h slot number of host msg originates from
1110 */
Parse_Waiting(const int h)1111 void CServer::Parse_Waiting(const int h)
1112 {
1113 switch (networkStates[h].State) {
1114 // client has recvd welcome and is waiting for info
1115 case ccs_connecting:
1116 networkStates[h].State = ccs_connected;
1117 networkStates[h].MsgCnt = 0;
1118 /* Fall through */
1119 case ccs_connected: {
1120 // this code path happens until client acknowledges the map
1121 Send_Map(Hosts[h]);
1122
1123 networkStates[h].MsgCnt++;
1124 if (networkStates[h].MsgCnt > 50) {
1125 // FIXME: Client sends waiting, but doesn't receive our map....
1126 }
1127 break;
1128 }
1129 case ccs_mapinfo:
1130 networkStates[h].State = ccs_synced;
1131 networkStates[h].MsgCnt = 0;
1132 for (int i = 1; i < PlayerMax - 1; ++i) {
1133 if (i != h && Hosts[i].PlyNr) {
1134 // Notify other clients
1135 networkStates[i].State = ccs_async;
1136 }
1137 }
1138 /* Fall through */
1139 case ccs_synced:
1140 // the wanted state - do nothing.. until start...
1141 networkStates[h].MsgCnt = 0;
1142 break;
1143
1144 case ccs_async: {
1145 // Server User has changed menu selection. This state is set by MENU code
1146 // OR we have received a new client/other client has changed data
1147
1148 // this code path happens until client acknoledges the state change
1149 // by sending ICMResync
1150 Send_State(Hosts[h]);
1151
1152 networkStates[h].MsgCnt++;
1153 if (networkStates[h].MsgCnt > 50) {
1154 // FIXME: Client sends waiting, but doesn't receive our state info....
1155 }
1156 break;
1157 }
1158 default:
1159 DebugPrint("Server: ICMWaiting: Unhandled state %d Host %d\n" _C_ networkStates[h].State _C_ h);
1160 break;
1161 }
1162 }
1163
1164 /**
1165 ** Parse client map info acknoledge message
1166 **
1167 ** @param h slot number of host msg originates from
1168 */
Parse_Map(const int h)1169 void CServer::Parse_Map(const int h)
1170 {
1171 switch (networkStates[h].State) {
1172 // client has recvd map info waiting for state info
1173 case ccs_connected:
1174 networkStates[h].State = ccs_mapinfo;
1175 networkStates[h].MsgCnt = 0;
1176 /* Fall through */
1177 case ccs_mapinfo: {
1178 // this code path happens until client acknowledges the state info
1179 // by falling back to ICMWaiting with prev. State synced
1180 Send_State(Hosts[h]);
1181
1182 networkStates[h].MsgCnt++;
1183 if (networkStates[h].MsgCnt > 50) {
1184 // FIXME: Client sends mapinfo, but doesn't receive our state info....
1185 }
1186 break;
1187 }
1188 default:
1189 DebugPrint("Server: ICMMap: Unhandled state %d Host %d\n" _C_ networkStates[h].State _C_ h);
1190 break;
1191 }
1192 }
1193
1194 /**
1195 ** Parse locate state change notifiction or initial state info request of client
1196 **
1197 ** @param h slot number of host msg originates from
1198 ** @param msg message received
1199 */
Parse_State(const int h,const CInitMessage_State & msg)1200 void CServer::Parse_State(const int h, const CInitMessage_State &msg)
1201 {
1202 switch (networkStates[h].State) {
1203 case ccs_mapinfo:
1204 // User State Change right after connect - should not happen, but..
1205 /* Fall through */
1206 case ccs_synced:
1207 // Default case: Client is in sync with us, but notes a local change
1208 // networkStates[h].State = ccs_async;
1209 networkStates[h].MsgCnt = 0;
1210 // Use information supplied by the client:
1211 serverSetup->Ready[h] = msg.State.Ready[h];
1212 serverSetup->Race[h] = msg.State.Race[h];
1213 // Add additional info usage here!
1214
1215 // Resync other clients (and us..)
1216 for (int i = 1; i < PlayerMax - 1; ++i) {
1217 if (Hosts[i].PlyNr) {
1218 networkStates[i].State = ccs_async;
1219 }
1220 }
1221 /* Fall through */
1222 case ccs_async: {
1223 // this code path happens until client acknowledges the state change reply
1224 // by sending ICMResync
1225 Send_State(Hosts[h]);
1226
1227 networkStates[h].MsgCnt++;
1228 if (networkStates[h].MsgCnt > 50) {
1229 // FIXME: Client sends State, but doesn't receive our state info....
1230 }
1231 break;
1232 }
1233 default:
1234 DebugPrint("Server: ICMState: Unhandled state %d Host %d\n" _C_ networkStates[h].State _C_ h);
1235 break;
1236 }
1237 }
1238
1239 /**
1240 ** Parse the disconnect request of a client by sending out good bye
1241 **
1242 ** @param h slot number of host msg originates from
1243 */
Parse_GoodBye(const int h)1244 void CServer::Parse_GoodBye(const int h)
1245 {
1246 switch (networkStates[h].State) {
1247 default:
1248 // We can enter here from _ANY_ state!
1249 networkStates[h].MsgCnt = 0;
1250 networkStates[h].State = ccs_detaching;
1251 /* Fall through */
1252 case ccs_detaching: {
1253 // this code path happens until client acknoledges the GoodBye
1254 // by sending ICMSeeYou;
1255 Send_GoodBye(Hosts[h]);
1256
1257 networkStates[h].MsgCnt++;
1258 if (networkStates[h].MsgCnt > 10) {
1259 // FIXME: Client sends GoodBye, but doesn't receive our GoodBye....
1260 }
1261 break;
1262 }
1263 }
1264 }
1265
1266 /**
1267 ** Parse the final see you msg of a disconnecting client
1268 **
1269 ** @param h slot number of host msg originates from
1270 */
Parse_SeeYou(const int h)1271 void CServer::Parse_SeeYou(const int h)
1272 {
1273 switch (networkStates[h].State) {
1274 case ccs_detaching:
1275 KickClient(h);
1276 break;
1277
1278 default:
1279 DebugPrint("Server: ICMSeeYou: Unhandled state %d Host %d\n" _C_
1280 networkStates[h].State _C_ h);
1281 break;
1282 }
1283 }
1284
1285 /**
1286 ** Check if the Stratagus version and Network Protocol match
1287 **
1288 ** @param msg message received
1289 ** @param host host which send the message
1290 **
1291 ** @return 0 if the versions match, -1 otherwise
1292 */
CheckVersions(const CInitMessage_Hello & msg,CUDPSocket & socket,const CHost & host)1293 static int CheckVersions(const CInitMessage_Hello &msg, CUDPSocket &socket, const CHost &host)
1294 {
1295 if (msg.Stratagus != StratagusVersion) {
1296 const std::string hostStr = host.toString();
1297 fprintf(stderr, "Incompatible " NAME " version %d <-> %d from %s\n",
1298 StratagusVersion, msg.Stratagus, hostStr.c_str());
1299
1300 const CInitMessage_EngineMismatch message;
1301 NetworkSendICMessage_Log(socket, host, message);
1302 return -1;
1303 }
1304
1305 if (msg.Version != NetworkProtocolVersion) {
1306 const std::string hostStr = host.toString();
1307 fprintf(stderr, "Incompatible network protocol version "
1308 NetworkProtocolFormatString " <-> "
1309 NetworkProtocolFormatString "\n"
1310 "from %s\n",
1311 NetworkProtocolFormatArgs(NetworkProtocolVersion),
1312 NetworkProtocolFormatArgs(msg.Version),
1313 hostStr.c_str());
1314
1315 const CInitMessage_ProtocolMismatch message;
1316 NetworkSendICMessage_Log(socket, host, message);
1317 return -1;
1318 }
1319 return 0;
1320 }
1321
Parse(unsigned long frameCounter,const unsigned char * buf,const CHost & host)1322 void CServer::Parse(unsigned long frameCounter, const unsigned char *buf, const CHost &host)
1323 {
1324 const unsigned char msgsubtype = buf[1];
1325 int index = FindHostIndexBy(host);
1326
1327 if (index == -1) {
1328 if (msgsubtype == ICMHello) {
1329 CInitMessage_Hello msg;
1330
1331 msg.Deserialize(buf);
1332 if (CheckVersions(msg, *socket, host)) {
1333 return;
1334 }
1335 // Special case: a new client has arrived
1336 index = Parse_Hello(-1, msg, host);
1337 networkStates[index].LastFrame = frameCounter;
1338 }
1339 return;
1340 }
1341 networkStates[index].LastFrame = frameCounter;
1342 switch (msgsubtype) {
1343 case ICMHello: { // a new client has arrived
1344 CInitMessage_Hello msg;
1345
1346 msg.Deserialize(buf);
1347 Parse_Hello(index, msg, host);
1348 break;
1349 }
1350 case ICMResync: Parse_Resync(index); break;
1351 case ICMWaiting: Parse_Waiting(index); break;
1352 case ICMMap: Parse_Map(index); break;
1353
1354 case ICMState: {
1355 CInitMessage_State msg;
1356
1357 msg.Deserialize(buf);
1358 Parse_State(index, msg);
1359 break;
1360 }
1361 case ICMMapUidMismatch: // Parse_MapUidMismatch(index, buf); break;
1362 case ICMGoodBye: Parse_GoodBye(index); break;
1363 case ICMSeeYou: Parse_SeeYou(index); break;
1364 case ICMIAH: break;
1365
1366 default:
1367 DebugPrint("Server: Unhandled subtype %d from host %d\n" _C_ msgsubtype _C_ index);
1368 break;
1369 }
1370 }
1371
1372 //
1373 // Functions
1374 //
1375
1376 /**
1377 ** Parse a setup event. (Command type <= MessageInitEvent)
1378 **
1379 ** @param buf Packet received
1380 ** @param size size of the received packet.
1381 ** @param host host which send the message
1382 **
1383 ** @return 1 if packet is an InitConfig message, 0 otherwise
1384 */
NetworkParseSetupEvent(const unsigned char * buf,int size,const CHost & host)1385 int NetworkParseSetupEvent(const unsigned char *buf, int size, const CHost &host)
1386 {
1387 Assert(NetConnectRunning != 0);
1388
1389 CInitMessage_Header header;
1390 header.Deserialize(buf);
1391 const unsigned char msgtype = header.GetType();
1392 if ((msgtype == MessageInit_FromClient && NetConnectRunning != 1)
1393 || (msgtype == MessageInit_FromServer && NetConnectRunning != 2)) {
1394 if (NetConnectRunning == 2 && Client.GetNetworkState() == ccs_started) {
1395 // Client has acked ready to start and receives first real network packet.
1396 // This indicates that we missed the 'Go' in started state and the game
1397 // has been started by the server, so do the same for the client.
1398 NetConnectRunning = 0; // End the menu..
1399 }
1400 return 0;
1401 }
1402 #ifdef DEBUG
1403 const unsigned char msgsubtype = header.GetSubType();
1404 const std::string hostStr = host.toString();
1405 DebugPrint("Received %s (%d) from %s\n" _C_
1406 icmsgsubtypenames[int(msgsubtype)] _C_ msgsubtype _C_
1407 hostStr.c_str());
1408 #endif
1409 if (NetConnectRunning == 2) { // client
1410 if (Client.Parse(buf, host) == false) {
1411 NetConnectRunning = 0;
1412 }
1413 } else if (NetConnectRunning == 1) { // server
1414 Server.Parse(FrameCounter, buf, host);
1415 }
1416 return 1;
1417 }
1418
1419 /**
1420 ** Client Menu Loop: Send out client request messages
1421 */
NetworkProcessClientRequest()1422 void NetworkProcessClientRequest()
1423 {
1424 if (Client.Update(GetTicks()) == false) {
1425 NetConnectRunning = 0;
1426 }
1427 }
1428
GetNetworkState()1429 int GetNetworkState()
1430 {
1431 return Client.GetNetworkState();
1432 }
1433
FindHostIndexBy(const CHost & host)1434 int FindHostIndexBy(const CHost &host)
1435 {
1436 for (int i = 0; i != PlayerMax; ++i) {
1437 if (Hosts[i].Host == host.getIp() && Hosts[i].Port == host.getPort()) {
1438 return i;
1439 }
1440 }
1441 return -1;
1442 }
1443
1444 /**
1445 ** Server Menu Loop: Send out server request messages
1446 */
NetworkProcessServerRequest()1447 void NetworkProcessServerRequest()
1448 {
1449 if (GameRunning) {
1450 return;
1451 // Game already started...
1452 }
1453 Server.Update(FrameCounter);
1454 }
1455
1456 /**
1457 ** Setup Network connect state machine for clients
1458 */
NetworkInitClientConnect()1459 void NetworkInitClientConnect()
1460 {
1461 NetConnectRunning = 2;
1462 NetConnectType = 2;
1463
1464 for (int i = 0; i < PlayerMax; ++i) {
1465 Hosts[i].Clear();
1466 }
1467 ServerSetupState.Clear();
1468 LocalSetupState.Clear();
1469 Client.Init(Parameters::Instance.LocalPlayerName, &NetworkFildes, &ServerSetupState, &LocalSetupState, GetTicks());
1470 }
1471
1472 /**
1473 ** Server user has finally hit the start game button
1474 */
NetworkServerStartGame()1475 void NetworkServerStartGame()
1476 {
1477 Assert(ServerSetupState.CompOpt[0] == 0);
1478
1479 // save it first..
1480 LocalSetupState = ServerSetupState;
1481
1482 // Make a list of the available player slots.
1483 int num[PlayerMax];
1484 int rev[PlayerMax];
1485 int h = 0;
1486 for (int i = 0; i < PlayerMax; ++i) {
1487 if (Map.Info.PlayerType[i] == PlayerPerson) {
1488 rev[i] = h;
1489 num[h++] = i;
1490 DebugPrint("Slot %d is available for an interactive player (%d)\n" _C_ i _C_ rev[i]);
1491 }
1492 }
1493 // Make a list of the available computer slots.
1494 int n = h;
1495 for (int i = 0; i < PlayerMax; ++i) {
1496 if (Map.Info.PlayerType[i] == PlayerComputer) {
1497 rev[i] = n++;
1498 DebugPrint("Slot %d is available for an ai computer player (%d)\n" _C_ i _C_ rev[i]);
1499 }
1500 }
1501 // Make a list of the remaining slots.
1502 for (int i = 0; i < PlayerMax; ++i) {
1503 if (Map.Info.PlayerType[i] != PlayerPerson
1504 && Map.Info.PlayerType[i] != PlayerComputer) {
1505 rev[i] = n++;
1506 // PlayerNobody - not available to anything..
1507 }
1508 }
1509
1510 #ifdef DEBUG
1511 printf("INITIAL ServerSetupState:\n");
1512 for (int i = 0; i < PlayerMax - 1; ++i) {
1513 printf("%02d: CO: %d Race: %d Host: ", i, ServerSetupState.CompOpt[i], ServerSetupState.Race[i]);
1514 if (ServerSetupState.CompOpt[i] == 0) {
1515 const std::string hostStr = CHost(Hosts[i].Host, Hosts[i].Port).toString();
1516 printf(" %s %s", hostStr.c_str(), Hosts[i].PlyName);
1517 }
1518 printf("\n");
1519 }
1520 #endif
1521
1522 int org[PlayerMax];
1523 // Reverse to assign slots to menu setup state positions.
1524 for (int i = 0; i < PlayerMax; ++i) {
1525 org[i] = -1;
1526 for (int j = 0; j < PlayerMax; ++j) {
1527 if (rev[j] == i) {
1528 org[i] = j;
1529 break;
1530 }
1531 }
1532 }
1533
1534 // Calculate NetPlayers
1535 NetPlayers = h;
1536 //Wyrmgus start
1537 // int compPlayers = ServerSetupState.Opponents;
1538 bool compPlayers = ServerSetupState.Opponents > 0;
1539 //Wyrmgus end
1540 for (int i = 1; i < h; ++i) {
1541 if (Hosts[i].PlyNr == 0 && ServerSetupState.CompOpt[i] != 0) {
1542 NetPlayers--;
1543 } else if (Hosts[i].PlyName[0] == 0) {
1544 NetPlayers--;
1545 //Wyrmgus start
1546 // if (--compPlayers >= 0) {
1547 if (compPlayers) {
1548 //Wyrmgus end
1549 // Unused slot gets a computer player
1550 ServerSetupState.CompOpt[i] = 1;
1551 LocalSetupState.CompOpt[i] = 1;
1552 } else {
1553 ServerSetupState.CompOpt[i] = 2;
1554 LocalSetupState.CompOpt[i] = 2;
1555 }
1556 }
1557 }
1558
1559 // Compact host list.. (account for computer/closed slots in the middle..)
1560 for (int i = 1; i < h; ++i) {
1561 if (Hosts[i].PlyNr == 0) {
1562 int j;
1563 for (j = i + 1; j < PlayerMax - 1; ++j) {
1564 if (Hosts[j].PlyNr) {
1565 DebugPrint("Compact: Hosts %d -> Hosts %d\n" _C_ j _C_ i);
1566 Hosts[i] = Hosts[j];
1567 Hosts[j].Clear();
1568 std::swap(LocalSetupState.CompOpt[i], LocalSetupState.CompOpt[j]);
1569 std::swap(LocalSetupState.Race[i], LocalSetupState.Race[j]);
1570 break;
1571 }
1572 }
1573 if (j == PlayerMax - 1) {
1574 break;
1575 }
1576 }
1577 }
1578
1579 // Randomize the position.
1580 // It can be disabled by writing NoRandomPlacementMultiplayer() in lua files.
1581 // Players slots are then mapped to players numbers(and colors).
1582
1583 if (NoRandomPlacementMultiplayer == 1) {
1584 for (int i = 0; i < PlayerMax; ++i) {
1585 if (Map.Info.PlayerType[i] != PlayerComputer) {
1586 org[i] = Hosts[i].PlyNr;
1587 }
1588 }
1589 } else {
1590 int j = h;
1591 for (int i = 0; i < NetPlayers; ++i) {
1592 Assert(j > 0);
1593 int chosen = MyRand() % j;
1594
1595 n = num[chosen];
1596 Hosts[i].PlyNr = n;
1597 int k = org[i];
1598 if (k != n) {
1599 for (int o = 0; o < PlayerMax; ++o) {
1600 if (org[o] == n) {
1601 org[o] = k;
1602 break;
1603 }
1604 }
1605 org[i] = n;
1606 }
1607 DebugPrint("Assigning player %d to slot %d (%d)\n" _C_ i _C_ n _C_ org[i]);
1608
1609 num[chosen] = num[--j];
1610 }
1611 }
1612
1613 // Complete all setup states for the assigned slots.
1614 for (int i = 0; i < PlayerMax; ++i) {
1615 num[i] = 1;
1616 n = org[i];
1617 ServerSetupState.CompOpt[n] = LocalSetupState.CompOpt[i];
1618 ServerSetupState.Race[n] = LocalSetupState.Race[i];
1619 }
1620
1621 /* NOW we have NetPlayers in Hosts array, with ServerSetupState shuffled up to match it.. */
1622
1623 //
1624 // Send all clients host:ports to all clients.
1625 // Slot 0 is the server!
1626 //
1627 NetLocalPlayerNumber = Hosts[0].PlyNr;
1628 HostsCount = NetPlayers - 1;
1629
1630 // Move ourselves (server slot 0) to the end of the list
1631 std::swap(Hosts[0], Hosts[HostsCount]);
1632
1633 // Prepare the final config message:
1634 CInitMessage_Config message;
1635 message.hostsCount = NetPlayers;
1636 for (int i = 0; i < NetPlayers; ++i) {
1637 message.hosts[i] = Hosts[i];
1638 message.hosts[i].PlyNr = Hosts[i].PlyNr;
1639 }
1640
1641 // Prepare the final state message:
1642 const CInitMessage_State statemsg(MessageInit_FromServer, ServerSetupState);
1643
1644 DebugPrint("Ready, sending InitConfig to %d host(s)\n" _C_ HostsCount);
1645 // Send all clients host:ports to all clients.
1646 for (int j = HostsCount; j;) {
1647
1648 breakout:
1649 // Send to all clients.
1650 for (int i = 0; i < HostsCount; ++i) {
1651 const CHost host(message.hosts[i].Host, message.hosts[i].Port);
1652
1653 if (num[Hosts[i].PlyNr] == 1) { // not acknowledged yet
1654 message.clientIndex = i;
1655 NetworkSendICMessage_Log(NetworkFildes, host, message);
1656 } else if (num[Hosts[i].PlyNr] == 2) {
1657 NetworkSendICMessage_Log(NetworkFildes, host, statemsg);
1658 }
1659 }
1660
1661 // Wait for acknowledge
1662 unsigned char buf[1024];
1663 while (j && NetworkFildes.HasDataToRead(1000)) {
1664 CHost host;
1665 const int len = NetworkFildes.Recv(buf, sizeof(buf), &host);
1666 if (len < 0) {
1667 #ifdef DEBUG
1668 const std::string hostStr = host.toString();
1669 DebugPrint("*Receive ack failed: (%d) from %s\n" _C_ len _C_ hostStr.c_str());
1670 #endif
1671 continue;
1672 }
1673 CInitMessage_Header header;
1674 header.Deserialize(buf);
1675 const unsigned char type = header.GetType();
1676 const unsigned char subtype = header.GetSubType();
1677
1678 if (type == MessageInit_FromClient) {
1679 switch (subtype) {
1680 case ICMConfig: {
1681 #ifdef DEBUG
1682 const std::string hostStr = host.toString();
1683 DebugPrint("Got ack for InitConfig from %s\n" _C_ hostStr.c_str());
1684 #endif
1685 const int index = FindHostIndexBy(host);
1686 if (index != -1) {
1687 if (num[Hosts[index].PlyNr] == 1) {
1688 num[Hosts[index].PlyNr]++;
1689 }
1690 goto breakout;
1691 }
1692 break;
1693 }
1694 case ICMGo: {
1695 #ifdef DEBUG
1696 const std::string hostStr = host.toString();
1697 DebugPrint("Got ack for InitState from %s\n" _C_ hostStr.c_str());
1698 #endif
1699 const int index = FindHostIndexBy(host);
1700 if (index != -1) {
1701 if (num[Hosts[index].PlyNr] == 2) {
1702 num[Hosts[index].PlyNr] = 0;
1703 --j;
1704 DebugPrint("Removing host %d from waiting list\n" _C_ j);
1705 }
1706 }
1707 break;
1708 }
1709 default:
1710 DebugPrint("Server: Config ACK: Unhandled subtype %d\n" _C_ subtype);
1711 break;
1712 }
1713 } else {
1714 DebugPrint("Unexpected Message Type %d while waiting for Config ACK\n" _C_ type);
1715 }
1716 }
1717 }
1718
1719 DebugPrint("DONE: All configs acked - Now starting..\n");
1720 // Give clients a quick-start kick..
1721 const CInitMessage_Header message_go(MessageInit_FromServer, ICMGo);
1722 for (int i = 0; i < HostsCount; ++i) {
1723 const CHost host(Hosts[i].Host, Hosts[i].Port);
1724 NetworkSendICMessage_Log(NetworkFildes, host, message_go);
1725 }
1726 }
1727
1728 /**
1729 ** Setup the IP-Address of the network server to connect to
1730 **
1731 ** @param serveraddr the serveraddress the user has entered
1732 **
1733 ** @return True, if error; otherwise false.
1734 */
NetworkSetupServerAddress(const std::string & serveraddr,int port)1735 int NetworkSetupServerAddress(const std::string &serveraddr, int port)
1736 {
1737 if (port == 0) {
1738 port = CNetworkParameter::Instance.defaultPort;
1739 }
1740 CHost host(serveraddr.c_str(), port);
1741 if (host.isValid() == false) {
1742 return 1;
1743 }
1744 Client.SetServerHost(host);
1745 #ifdef DEBUG
1746 const std::string hostStr = host.toString();
1747 DebugPrint("SELECTED SERVER: %s [%s]\n" _C_ hostStr.c_str() _C_ serveraddr.c_str());
1748 #endif
1749 return 0;
1750 }
1751
1752 /**
1753 ** Terminate and detach Network connect state machine for the client
1754 */
NetworkDetachFromServer()1755 void NetworkDetachFromServer()
1756 {
1757 Client.DetachFromServer();
1758 }
1759
1760 /**
1761 ** Setup Network connect state machine for the server
1762 */
NetworkInitServerConnect(int openslots)1763 void NetworkInitServerConnect(int openslots)
1764 {
1765 NetConnectRunning = 1;
1766 NetConnectType = 1;
1767
1768 for (int i = 0; i < PlayerMax; ++i) {
1769 Hosts[i].Clear();
1770 }
1771 ServerSetupState.Clear();
1772 LocalSetupState.Clear(); // Unused when we are server
1773 Server.Init(Parameters::Instance.LocalPlayerName, &NetworkFildes, &ServerSetupState);
1774
1775 // preset the server (initially always slot 0)
1776 Hosts[0].SetName(Parameters::Instance.LocalPlayerName.c_str());
1777
1778 for (int i = openslots; i < PlayerMax - 1; ++i) {
1779 ServerSetupState.CompOpt[i] = 1;
1780 }
1781 }
1782
1783 /**
1784 ** Notify state change by menu user to connected clients
1785 */
NetworkServerResyncClients()1786 void NetworkServerResyncClients()
1787 {
1788 if (NetConnectRunning == 1) {
1789 Server.MarkClientsAsResync();
1790 }
1791 }
1792
1793 /**
1794 ** Multiplayer network game final race and player type setup.
1795 */
NetworkGamePrepareGameSettings()1796 void NetworkGamePrepareGameSettings()
1797 {
1798 DebugPrint("NetPlayers = %d\n" _C_ NetPlayers);
1799
1800 GameSettings.NetGameType = SettingsMultiPlayerGame;
1801
1802 #ifdef DEBUG
1803 for (int i = 0; i < PlayerMax - 1; i++) {
1804 printf("%02d: CO: %d Race: %d Name: ", i, ServerSetupState.CompOpt[i], ServerSetupState.Race[i]);
1805 if (ServerSetupState.CompOpt[i] == 0) {
1806 for (int h = 0; h != HostsCount; ++h) {
1807 if (Hosts[h].PlyNr == i) {
1808 printf("%s", Hosts[h].PlyName);
1809 }
1810 }
1811 if (i == NetLocalPlayerNumber) {
1812 printf("%s (localhost)", Parameters::Instance.LocalPlayerName.c_str());
1813 }
1814 }
1815 printf("\n");
1816 }
1817 #endif
1818
1819 // Make a list of the available player slots.
1820 int num[PlayerMax];
1821 int comp[PlayerMax];
1822 int c = 0;
1823 int h = 0;
1824 for (int i = 0; i < PlayerMax; i++) {
1825 if (Map.Info.PlayerType[i] == PlayerPerson) {
1826 num[h++] = i;
1827 }
1828 if (Map.Info.PlayerType[i] == PlayerComputer) {
1829 comp[c++] = i; // available computer player slots
1830 }
1831 }
1832 for (int i = 0; i < h; i++) {
1833 GameSettings.Presets[num[i]].Race = ServerSetupState.Race[num[i]];
1834 switch (ServerSetupState.CompOpt[num[i]]) {
1835 case 0: {
1836 GameSettings.Presets[num[i]].Type = PlayerPerson;
1837 break;
1838 }
1839 case 1:
1840 GameSettings.Presets[num[i]].Type = PlayerComputer;
1841 break;
1842 case 2:
1843 GameSettings.Presets[num[i]].Type = PlayerNobody;
1844 default:
1845 break;
1846 }
1847 }
1848 for (int i = 0; i < c; i++) {
1849 if (ServerSetupState.CompOpt[comp[i]] == 2) { // closed..
1850 GameSettings.Presets[comp[i]].Type = PlayerNobody;
1851 DebugPrint("Settings[%d].Type == Closed\n" _C_ comp[i]);
1852 }
1853 }
1854
1855 #ifdef DEBUG
1856 for (int i = 0; i != HostsCount; ++i) {
1857 Assert(GameSettings.Presets[Hosts[i].PlyNr].Type == PlayerPerson);
1858 }
1859 Assert(GameSettings.Presets[NetLocalPlayerNumber].Type == PlayerPerson);
1860 #endif
1861 }
1862
1863 /**
1864 ** Removes Randomization of Player position in Multiplayer mode
1865 **
1866 ** @param l Lua state.
1867 */
CclNoRandomPlacementMultiplayer(lua_State * l)1868 static int CclNoRandomPlacementMultiplayer(lua_State *l)
1869 {
1870 LuaCheckArgs(l, 0);
1871 NoRandomPlacementMultiplayer = 1;
1872 return 0;
1873 }
1874
NetworkCclRegister()1875 void NetworkCclRegister()
1876 {
1877 lua_register(Lua, "NoRandomPlacementMultiplayer", CclNoRandomPlacementMultiplayer);
1878 }
1879
1880
1881 //@}
1882