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