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 network.cpp - The network. */
12 //
13 //      (c) Copyright 2000-2008 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 // Documentation
34 //----------------------------------------------------------------------------
35 
36 /**
37 ** @page NetworkModule Module - Network
38 **
39 ** @section Basics How does it work.
40 **
41 ** Stratagus uses an UDP peer to peer protocol (p2p). The default port
42 ** is 6660.
43 **
44 ** @subsection udp_vs_tcp UDP vs. TCP
45 **
46 ** UDP is a connectionless protocol. This means it does not perform
47 ** retransmission of data and therefore provides very few error recovery
48 ** services. UDP instead offers a direct way to send and receive
49 ** datagrams (packets) over the network; it is used primarily for
50 ** broadcasting messages.
51 **
52 ** TCP, on the other hand, provides a connection-based, reliable data
53 ** stream. TCP guarantees delivery of data and also guarantees that
54 ** packets will be delivered in the same order in which they were sent.
55 **
56 ** TCP is a simple and effective way of transmitting data. For making sure
57 ** that a client and server can talk to each other it is very good.
58 ** However, it carries with it a lot of overhead and extra network lag.
59 **
60 ** UDP needs less overhead and has a smaller lag. Which is very important
61 ** for real time games. The disadvantages includes:
62 **
63 ** @li You won't have an individual socket for each client.
64 ** @li Given that clients don't need to open a unique socket in order to
65 ** transmit data there is the very real possibility that a client
66 ** who is not logged into the game will start sending all kinds of
67 ** garbage to your server in some kind of attack. It becomes much
68 ** more difficult to stop them at this point.
69 ** @li Likewise, you won't have a clear disconnect/leave game message
70 ** unless you write one yourself.
71 ** @li Some data may not reach the other machine, so you may have to send
72 ** important stuff many times.
73 ** @li Some data may arrive in the wrong order. Imagine that you get
74 ** package 3 before package 1. Even a package can come duplicate.
75 ** @li UDP is connectionless and therefore has problems with firewalls.
76 **
77 ** I have chosen UDP. Additional support for the TCP protocol is welcome.
78 **
79 ** @subsection sc_vs_p2p server/client vs. peer to peer
80 **
81 ** @li server to client
82 **
83 ** The player input is send to the server. The server collects the input
84 ** of all players and than send the commands to all clients.
85 **
86 ** @li peer to peer (p2p)
87 **
88 ** The player input is direct send to all others clients in game.
89 **
90 ** p2p has the advantage of a smaller lag, but needs a higher bandwidth
91 ** by the clients.
92 **
93 ** p2p has been chosen for in-game.
94 ** s/c for the preparing room.
95 **
96 ** @subsection bandwidth bandwidth
97 **
98 ** I wanted to support up to 8 players with 28.8kbit modems.
99 **
100 ** Most modems have a bandwidth of 28.8K bits/second (both directions) to
101 ** 56K bits/second (33.6K uplink) It takes actually 10 bits to send 1 byte.
102 ** This makes calculating how many bytes you are sending easy however, as
103 ** you just need to divide 28800 bits/second by 10 and end up with 2880
104 ** bytes per second.
105 **
106 ** We want to send many packets, more updated per second and big packets,
107 ** less protocol overhead.
108 **
109 ** If we do an update 6 times per second, leaving approximately 480 bytes
110 ** per update in an ideal environment.
111 **
112 ** For the TCP/IP protocol we need following:
113 ** IP  Header 20 bytes
114 ** UDP Header 8  bytes
115 **
116 ** With ~9 bytes per command and up to N(=9) commands there are 20+8+1+9*N(=120) bytes
117 ** per packet. Sending it to 7 other players, gives 840 bytes per update.
118 ** This means we could do 6 updates (each 166ms) per second (6*840=5040 bytes/s).
119 **
120 ** @subsection a_packet Network packet
121 **
122 ** @li [IP  Header - 20 bytes]
123 ** @li [UDP Header -  8 bytes]
124 ** @li [Header Data:Type - 1 byte]
125 ** if Type is one of the InitConfigMessage
126 ** @li [Header Data:SubType - 1 byte]
127 ** @li [Data - depend of subtype (may be 0 byte)]
128 ** else
129 ** @li [Header Data:Types - N-1 bytes] (for N commands)
130 ** @li [Header Data:Cycle - 1 byte]
131 ** @li [Data:Commands - Sum of Xi bytes for the N Commands]
132 **
133 **
134 ** @subsection internals Putting it together
135 **
136 ** All computers in play must run absolute syncron.
137 ** Only user commands are send over the network to the other computers.
138 ** To reduce network traffic, commands are sent/executed every gameCyclesPerUpdate
139 ** gameCycles. The command needs some time to reach the other clients (lag),
140 ** so the command is not executed immediately on the local computer,
141 ** but a delay (NetworkLag NetUpdates) later. Commands are stored
142 ** in a circular array indexed by update time. Once each other players commands
143 ** are received for a specified gameNetCycle, all commands of this gameNetCycle
144 ** Each gameNetCycle, a package must be send. if there is no user command,
145 ** a "dummy" sync package is send (which checks that all players are still in sync).
146 ** If there are missing packages, the game is paused and old commands
147 ** are resend to all clients.
148 **
149 ** @section missing What features are missing
150 **
151 ** @li The recover from lost packets can be improved, as the player knows
152 ** which packets is missing.
153 **
154 ** @li The UDP protocol isn't good for firewalls, we need also support
155 ** for the TCP protocol.
156 **
157 ** @li Add a server/client protocol, which allows more players per game.
158 **
159 ** @li Lag (latency) and bandwidth should be automatic detected during game setup
160 ** and later during game automatic adapted.
161 **
162 ** @li Also it would be nice, if we support viewing clients. This means
163 ** other people can view the game in progress.
164 **
165 ** @li The current protocol only uses single cast, for local LAN we
166 ** should also support broadcast and multicast.
167 **
168 ** @li Proxy and relays should be supported, to improve the playable
169 ** over the internet.
170 **
171 ** @li We can sort the command by importants, currently all commands are
172 ** send in order, only chat messages are send if there are free slots.
173 **
174 ** @li password protection the login process (optional), to prevent that
175 ** the wrong player join an open network game.
176 **
177 ** @li add meta server support, I have planned to use bnetd and its protocol.
178 **
179 ** @section api API How should it be used.
180 **
181 ** ::InitNetwork1()
182 ** Open port. Must be called by Lua
183 **
184 ** ::ExitNetwork1()
185 ** Close port. Called internally and by Lua
186 **
187 ** ::NetworkOnGameStart()
188 ** Initialize msg stuff for ingame communication.
189 **
190 ** ::NetworkEvent()
191 ** Manage network event (preparation room + ingame).
192 **
193 ** ::NetworkRecover()
194 ** Do stuff to have again NetworkInSync == true
195 **
196 ** ::NetworkCommands()
197 ** Network Updates : exec current command, and send commands to other players
198 **
199 ** ::NetworkFildes
200 ** UDP Socket for communication.
201 **
202 ** ::NetworkInSync
203 ** false when commands of the next gameNetCycle of the other player are not ready.
204 **
205 ** ::NetworkSendCommand()
206 ** Send a normal unit order.
207 **
208 ** ::NetworkSendExtendedCommand()
209 ** Send special command (diplomacy, ...)
210 **
211 ** ::NetworkSendChatMessage()
212 ** Send a chat message to others player
213 **
214 ** ::NetworkQuitGame()
215 ** Warn other users that we leave.
216 */
217 
218 //----------------------------------------------------------------------------
219 //  Includes
220 //----------------------------------------------------------------------------
221 
222 #include "stratagus.h"
223 
224 #include <stddef.h>
225 #include <list>
226 
227 #include "network.h"
228 
229 #include "actions.h"
230 #include "commands.h"
231 #include "map/map.h"
232 #include "net_lowlevel.h"
233 #include "net_message.h"
234 #include "netconnect.h"
235 #include "parameters.h"
236 #include "player.h"
237 #include "replay.h"
238 #include "sound.h"
239 #include "translate.h"
240 #include "ui/interface.h"
241 #include "unit/unit.h"
242 #include "unit/unit_manager.h"
243 #include "unit/unittype.h"
244 #include "video.h"
245 
246 #include <deque>
247 
248 //----------------------------------------------------------------------------
249 //  Declaration
250 //----------------------------------------------------------------------------
251 
252 /**
253 **  Network command input/output queue.
254 */
255 class CNetworkCommandQueue
256 {
257 public:
CNetworkCommandQueue()258 	CNetworkCommandQueue() : Time(0), Type(0) {}
Clear()259 	void Clear() { this->Time = this->Type = 0; Data.clear(); }
260 
operator ==(const CNetworkCommandQueue & rhs) const261 	bool operator == (const CNetworkCommandQueue &rhs) const
262 	{
263 		return Time == rhs.Time && Type == rhs.Type && Data == rhs.Data;
264 	}
operator !=(const CNetworkCommandQueue & rhs) const265 	bool operator != (const CNetworkCommandQueue &rhs) const { return !(*this == rhs); }
266 public:
267 	unsigned long Time;    /// time to execute
268 	unsigned char Type;    /// Command Type
269 	std::vector<unsigned char> Data;  /// command content (network format)
270 };
271 
272 //----------------------------------------------------------------------------
273 //  Variables
274 //----------------------------------------------------------------------------
275 
276 /* static */ CNetworkParameter CNetworkParameter::Instance;
277 
CNetworkParameter()278 CNetworkParameter::CNetworkParameter()
279 {
280 	localHost = "127.0.0.1";
281 	localPort = defaultPort;
282 	gameCyclesPerUpdate = 1;
283 	NetworkLag = 10;
284 	timeoutInS = 45;
285 }
286 
FixValues()287 void CNetworkParameter::FixValues()
288 {
289 	gameCyclesPerUpdate = std::max(gameCyclesPerUpdate, 1u);
290 	NetworkLag = std::max(NetworkLag, 2u * gameCyclesPerUpdate);
291 }
292 
293 bool NetworkInSync = true;                 /// Network is in sync
294 
295 CUDPSocket NetworkFildes;                  /// Network file descriptor
296 
297 static unsigned long NetworkLastFrame[PlayerMax]; /// Last frame received packet
298 static unsigned long NetworkLastCycle[PlayerMax]; /// Last cycle received packet
299 
300 static int NetworkSyncSeeds[256];          /// Network sync seeds.
301 static int NetworkSyncHashs[256];          /// Network sync hashs.
302 static CNetworkCommandQueue NetworkIn[256][PlayerMax][MaxNetworkCommands]; /// Per-player network packet input queue
303 static std::deque<CNetworkCommandQueue> CommandsIn;    /// Network command input queue
304 static std::deque<CNetworkCommandQueue> MsgCommandsIn; /// Network message input queue
305 
306 
307 #ifdef DEBUG
308 class CNetworkStat
309 {
310 public:
CNetworkStat()311 	CNetworkStat() :
312 		resentPacketCount(0)
313 	{}
314 
print() const315 	void print() const
316 	{
317 		DebugPrint("resent: %d packets\n" _C_ resentPacketCount);
318 	}
319 
320 public:
321 	unsigned int resentPacketCount;
322 };
323 
printStatistic(const CUDPSocket::CStatistic & statistic)324 static void printStatistic(const CUDPSocket::CStatistic &statistic)
325 {
326 	DebugPrint("Sent: %d packets %d bytes (max %d bytes).\n"
327 			   _C_ statistic.sentPacketsCount _C_ statistic.sentBytesCount
328 			   _C_ statistic.biggestSentPacketSize);
329 	DebugPrint("Received: %d packets %d bytes (max %d bytes).\n" _C_
330 			   statistic.receivedPacketsCount _C_ statistic.receivedBytesCount
331 			   _C_ statistic.biggestReceivedPacketSize);
332 	DebugPrint("Received: %d error(s).\n" _C_ statistic.receivedErrorCount);
333 }
334 
335 static CNetworkStat NetworkStat;
336 #endif
337 
338 static int PlayerQuit[PlayerMax];          /// Player quit
339 
340 //----------------------------------------------------------------------------
341 //  Mid-Level api functions
342 //----------------------------------------------------------------------------
343 
344 /**
345 **  Send message to all clients.
346 **
347 **  @param packet       Packet to send.
348 **  @param numcommands  Number of commands.
349 */
NetworkBroadcast(const CNetworkPacket & packet,int numcommands,int player=255)350 static void NetworkBroadcast(const CNetworkPacket &packet, int numcommands, int player = 255)
351 {
352 	const unsigned int size = packet.Size(numcommands);
353 	unsigned char *buf = new unsigned char[size];
354 	packet.Serialize(buf, numcommands);
355 
356 	// Send to all clients.
357 	if (NetConnectType == 1) { // server
358 		for (int i = 0; i < HostsCount; ++i) {
359 			const CHost host(Hosts[i].Host, Hosts[i].Port);
360 			if (Hosts[i].PlyNr == player) {
361 				continue;
362 			}
363 			NetworkFildes.Send(host, buf, size);
364 		}
365 	} else { // client
366 		const CHost host(Hosts[HostsCount - 1].Host, Hosts[HostsCount - 1].Port);
367 		NetworkFildes.Send(host, buf, size);
368 	}
369 	delete[] buf;
370 }
371 
372 /**
373 **  Network send packet. Build it from queue and broadcast.
374 **
375 **  @param ncq  Outgoing network queue start.
376 */
NetworkSendPacket(const CNetworkCommandQueue (& ncq)[MaxNetworkCommands])377 static void NetworkSendPacket(const CNetworkCommandQueue(&ncq)[MaxNetworkCommands])
378 {
379 	CNetworkPacket packet;
380 
381 	// Build packet of up to MaxNetworkCommands messages.
382 	int numcommands = 0;
383 	packet.Header.Cycle = ncq[0].Time & 0xFF;
384 	packet.Header.OrigPlayer = ThisPlayer->Index;
385 	int i;
386 	for (i = 0; i < MaxNetworkCommands && ncq[i].Type != MessageNone; ++i) {
387 		packet.Header.Type[i] = ncq[i].Type;
388 		packet.Command[i] = ncq[i].Data;
389 		++numcommands;
390 	}
391 	for (; i < MaxNetworkCommands; ++i) {
392 		packet.Header.Type[i] = MessageNone;
393 	}
394 	NetworkBroadcast(packet, numcommands);
395 }
396 
397 //----------------------------------------------------------------------------
398 //  API init..
399 //----------------------------------------------------------------------------
400 
401 /**
402 **  Initialize network port.
403 */
InitNetwork1()404 void InitNetwork1()
405 {
406 	CNetworkParameter::Instance.FixValues();
407 
408 	NetInit(); // machine dependent setup
409 
410 	// Our communication port
411 	const int port = CNetworkParameter::Instance.localPort;
412 	const char *NetworkAddr = nullptr; // FIXME : bad use
413 	const CHost host(NetworkAddr, port);
414 	NetworkFildes.Open(host);
415 	if (NetworkFildes.IsValid() == false) {
416 		fprintf(stderr, "NETWORK: No free port %d available, aborting\n", port);
417 		NetExit(); // machine dependent network exit
418 		return;
419 	}
420 #ifdef DEBUG
421 	const std::string hostStr = host.toString();
422 	DebugPrint("My host:port %s\n" _C_ hostStr.c_str());
423 #endif
424 
425 #if 0 // FIXME: need a working interface check
426 	unsigned long ips[10];
427 	int networkNumInterfaces = NetSocketAddr(NetworkFildes, ips, 10);
428 	if (networkNumInterfaces) {
429 		DebugPrint("Num IP: %d\n" _C_ networkNumInterfaces);
430 		for (int i = 0; i < networkNumInterfaces; ++i) {
431 			DebugPrint("IP: %d.%d.%d.%d\n" _C_ NIPQUAD(ntohl(ips[i])));
432 		}
433 	} else {
434 		fprintf(stderr, "NETWORK: Not connected to any external IPV4-network, aborting\n");
435 		ExitNetwork1();
436 		return;
437 	}
438 #endif
439 }
440 
441 /**
442 **  Cleanup network.
443 */
ExitNetwork1()444 void ExitNetwork1()
445 {
446 	if (!IsNetworkGame()) { // No network running
447 		return;
448 	}
449 
450 #ifdef DEBUG
451 	printStatistic(NetworkFildes.getStatistic());
452 	NetworkFildes.clearStatistic();
453 	NetworkStat.print();
454 #endif
455 
456 	NetworkFildes.Close();
457 	NetExit(); // machine dependent setup
458 
459 	NetworkInSync = true;
460 	NetPlayers = 0;
461 	HostsCount = 0;
462 }
463 
464 /**
465 **  Game will start now.
466 */
NetworkOnStartGame()467 void NetworkOnStartGame()
468 {
469 	ThisPlayer->SetName(Parameters::Instance.LocalPlayerName);
470 	for (int i = 0; i < HostsCount; ++i) {
471 		Players[Hosts[i].PlyNr].SetName(Hosts[i].PlyName);
472 	}
473 	DebugPrint("Updates %d, Lag %d, Hosts %d\n" _C_
474 			   CNetworkParameter::Instance.gameCyclesPerUpdate _C_
475 			   CNetworkParameter::Instance.NetworkLag _C_ HostsCount);
476 
477 	NetworkInSync = true;
478 	CommandsIn.clear();
479 	MsgCommandsIn.clear();
480 	// Prepare first time without syncs.
481 	for (int i = 0; i != 256; ++i) {
482 		for (int p = 0; p != PlayerMax; ++p) {
483 			for (int j = 0; j != MaxNetworkCommands; ++j) {
484 				NetworkIn[i][p][j].Clear();
485 			}
486 		}
487 	}
488 	CNetworkCommandSync nc;
489 	//nc.syncHash = SyncHash;
490 	//nc.syncSeed = SyncRandSeed;
491 
492 	for (unsigned int i = 0; i <= CNetworkParameter::Instance.NetworkLag; i += CNetworkParameter::Instance.gameCyclesPerUpdate) {
493 		for (int n = 0; n < HostsCount; ++n) {
494 			CNetworkCommandQueue(&ncqs)[MaxNetworkCommands] = NetworkIn[i][Hosts[n].PlyNr];
495 
496 			ncqs[0].Time = i;
497 			ncqs[0].Type = MessageSync;
498 			ncqs[0].Data.resize(nc.Size());
499 			nc.Serialize(&ncqs[0].Data[0]);
500 			ncqs[1].Time = i;
501 			ncqs[1].Type = MessageNone;
502 		}
503 	}
504 	memset(NetworkSyncSeeds, 0, sizeof(NetworkSyncSeeds));
505 	memset(NetworkSyncHashs, 0, sizeof(NetworkSyncHashs));
506 	memset(PlayerQuit, 0, sizeof(PlayerQuit));
507 	memset(NetworkLastFrame, 0, sizeof(NetworkLastFrame));
508 	memset(NetworkLastCycle, 0, sizeof(NetworkLastCycle));
509 }
510 
511 //----------------------------------------------------------------------------
512 //  Commands input
513 //----------------------------------------------------------------------------
514 
515 /**
516 **  Prepare send of command message.
517 **
518 **  Convert arguments into network format and place it into output queue.
519 **
520 **  @param command  Command (Move,Attack,...).
521 **  @param unit     Unit that receive the command.
522 **  @param x        optional X map position.
523 **  @param y        optional y map position.
524 **  @param dest     optional destination unit.
525 **  @param type     optional unit-type argument.
526 **  @param status   Append command or flush old commands.
527 **
528 **  @warning  Destination and unit-type shares the same network slot.
529 */
NetworkSendCommand(int command,const CUnit & unit,int x,int y,const CUnit * dest,const CUnitType * type,int status)530 void NetworkSendCommand(int command, const CUnit &unit, int x, int y,
531 						const CUnit *dest, const CUnitType *type, int status)
532 {
533 	CNetworkCommandQueue ncq;
534 
535 	ncq.Time = GameCycle;
536 	ncq.Type = command;
537 	if (status) {
538 		ncq.Type |= 0x80;
539 	}
540 	CNetworkCommand nc;
541 	nc.Unit = UnitNumber(unit);
542 	nc.X = x;
543 	nc.Y = y;
544 	Assert(!dest || !type); // Both together isn't allowed
545 	if (dest) {
546 		nc.Dest = UnitNumber(*dest);
547 	} else if (type) {
548 		nc.Dest = type->Slot;
549 	} else {
550 		nc.Dest = 0xFFFF; // -1
551 	}
552 	ncq.Data.resize(nc.Size());
553 	nc.Serialize(&ncq.Data[0]);
554 	// Check for duplicate command in queue
555 	if (std::find(CommandsIn.begin(), CommandsIn.end(), ncq) != CommandsIn.end()) {
556 		return;
557 	}
558 	CommandsIn.push_back(ncq);
559 }
560 
561 /**
562 **  Prepare send of extended command message.
563 **
564 **  Convert arguments into network format and place it into output queue.
565 **
566 **  @param command  Command (Move,Attack,...).
567 **  @param arg1     optional argument #1
568 **  @param arg2     optional argument #2
569 **  @param arg3     optional argument #3
570 **  @param arg4     optional argument #4
571 **  @param status   Append command or flush old commands.
572 */
NetworkSendExtendedCommand(int command,int arg1,int arg2,int arg3,int arg4,int status)573 void NetworkSendExtendedCommand(int command, int arg1, int arg2, int arg3,
574 								int arg4, int status)
575 {
576 	CNetworkCommandQueue ncq;
577 
578 	ncq.Time = GameCycle;
579 	ncq.Type = MessageExtendedCommand;
580 	if (status) {
581 		ncq.Type |= 0x80;
582 	}
583 	CNetworkExtendedCommand nec;
584 	nec.ExtendedType = command;
585 	nec.Arg1 = arg1;
586 	nec.Arg2 = arg2;
587 	nec.Arg3 = arg3;
588 	nec.Arg4 = arg4;
589 	ncq.Data.resize(nec.Size());
590 	nec.Serialize(&ncq.Data[0]);
591 	CommandsIn.push_back(ncq);
592 }
593 
594 /**
595 **  Sends my selections to teammates
596 **
597 **  @param units  Units to send
598 **  @param count  Number of units to send
599 */
NetworkSendSelection(CUnit ** units,int count)600 void NetworkSendSelection(CUnit **units, int count)
601 {
602 	// Check if we have any teammates to send to
603 	bool hasteammates = false;
604 	for (int i = 0; i < HostsCount; ++i) {
605 		if (Players[Hosts[i].PlyNr].Team == ThisPlayer->Team) {
606 			hasteammates = true;
607 			break;
608 		}
609 	}
610 	if (!hasteammates) {
611 		return;
612 	}
613 	// Build and send packets to cover all units.
614 	CNetworkSelection ns;
615 
616 	for (int i = 0; i != count; ++i) {
617 		ns.Units.push_back(UnitNumber(*units[i]));
618 	}
619 	CNetworkCommandQueue ncq;
620 	ncq.Time = GameCycle;
621 	ncq.Type = MessageSelection;
622 
623 	ncq.Data.resize(ns.Size());
624 	ns.Serialize(&ncq.Data[0]);
625 	CommandsIn.push_back(ncq);
626 }
627 
628 /**
629 **  Send chat message. (Message is sent with low priority)
630 **
631 **  @param msg  Text message to send.
632 */
NetworkSendChatMessage(const std::string & msg)633 void NetworkSendChatMessage(const std::string &msg)
634 {
635 	if (!IsNetworkGame()) {
636 		return;
637 	}
638 	CNetworkChat nc;
639 	nc.Text = msg;
640 	CNetworkCommandQueue ncq;
641 	ncq.Type = MessageChat;
642 	ncq.Data.resize(nc.Size());
643 	nc.Serialize(&ncq.Data[0]);
644 	MsgCommandsIn.push_back(ncq);
645 }
646 
647 /**
648 **  Remove a player from the game.
649 **
650 **  @param player  Player number
651 */
NetworkRemovePlayer(int player)652 static void NetworkRemovePlayer(int player)
653 {
654 	// Remove player from Hosts and clear NetworkIn
655 	for (int i = 0; i < HostsCount; ++i) {
656 		if (Hosts[i].PlyNr == player) {
657 			Hosts[i] = Hosts[HostsCount - 1];
658 			--HostsCount;
659 			break;
660 		}
661 	}
662 	for (int i = 0; i < 256; ++i) {
663 		for (int c = 0; c < MaxNetworkCommands; ++c) {
664 			NetworkIn[i][player][c].Time = 0;
665 		}
666 	}
667 }
668 
IsNetworkCommandReady(int hostIndex,unsigned long gameNetCycle)669 static bool IsNetworkCommandReady(int hostIndex, unsigned long gameNetCycle)
670 {
671 	const int ply = Hosts[hostIndex].PlyNr;
672 	const CNetworkCommandQueue &ncq = NetworkIn[gameNetCycle & 0xFF][ply][0];
673 
674 	if (ncq.Time != gameNetCycle) {
675 		return false;
676 	}
677 	return true;
678 }
679 
IsNetworkCommandReady(unsigned long gameNetCycle)680 static bool IsNetworkCommandReady(unsigned long gameNetCycle)
681 {
682 	// Check if all next messages are available.
683 	for (int i = 0; i < HostsCount; ++i) {
684 		if (IsNetworkCommandReady(i, gameNetCycle) == false) {
685 			return false;
686 		}
687 	}
688 
689 	return true;
690 }
691 
ParseResendCommand(const CNetworkPacket & packet)692 static void ParseResendCommand(const CNetworkPacket &packet)
693 {
694 	// Destination cycle (time to execute).
695 	unsigned long n = ((GameCycle + 128) & ~0xFF) | packet.Header.Cycle;
696 	if (n > GameCycle + 128) {
697 		n -= 0x100;
698 	}
699 	const unsigned long gameNetCycle = n;
700 	// FIXME: not necessary to send this packet multiple times!!!!
701 	// other side sends re-send until it gets an answer.
702 	if (n != NetworkIn[gameNetCycle & 0xFF][ThisPlayer->Index][0].Time) {
703 		// Asking for a cycle we haven't gotten to yet, ignore for now
704 		return;
705 	}
706 	NetworkSendPacket(NetworkIn[gameNetCycle & 0xFF][ThisPlayer->Index]);
707 	// Check if a player quit this cycle
708 	for (int j = 0; j < HostsCount; ++j) {
709 		for (int c = 0; c < MaxNetworkCommands; ++c) {
710 			const CNetworkCommandQueue *ncq;
711 			ncq = &NetworkIn[gameNetCycle & 0xFF][Hosts[j].PlyNr][c];
712 			if (ncq->Time && ncq->Type == MessageQuit) {
713 				CNetworkPacket np;
714 				np.Header.Cycle = ncq->Time & 0xFF;
715 				np.Header.Type[0] = MessageQuit;
716 				np.Command[0] = ncq->Data;
717 				for (int k = 1; k < MaxNetworkCommands; ++k) {
718 					np.Header.Type[k] = MessageNone;
719 				}
720 				NetworkBroadcast(np, 1);
721 				break;
722 			}
723 		}
724 	}
725 }
726 
IsAValidCommand_Command(const CNetworkPacket & packet,int index,const int player)727 static bool IsAValidCommand_Command(const CNetworkPacket &packet, int index, const int player)
728 {
729 	CNetworkCommand nc;
730 	nc.Deserialize(&packet.Command[index][0]);
731 	const unsigned int slot = nc.Unit;
732 	const CUnit *unit = slot < UnitManager.GetUsedSlotCount() ? &UnitManager.GetSlotUnit(slot) : nullptr;
733 
734 	if (unit && (unit->Player->Index == player
735 				 || Players[player].IsTeamed(*unit) || unit->Player->Type == PlayerNeutral)) {
736 		return true;
737 	} else {
738 		return false;
739 	}
740 }
741 
IsAValidCommand_Dismiss(const CNetworkPacket & packet,int index,const int player)742 static bool IsAValidCommand_Dismiss(const CNetworkPacket &packet, int index, const int player)
743 {
744 	CNetworkCommand nc;
745 	nc.Deserialize(&packet.Command[index][0]);
746 	const unsigned int slot = nc.Unit;
747 	const CUnit *unit = slot < UnitManager.GetUsedSlotCount() ? &UnitManager.GetSlotUnit(slot) : nullptr;
748 
749 	if (unit && unit->Type->ClicksToExplode) {
750 		return true;
751 	}
752 	return IsAValidCommand_Command(packet, index, player);
753 }
754 
IsAValidCommand(const CNetworkPacket & packet,int index,const int player)755 static bool IsAValidCommand(const CNetworkPacket &packet, int index, const int player)
756 {
757 	switch (packet.Header.Type[index] & 0x7F) {
758 		case MessageExtendedCommand: // FIXME: ensure the sender is part of the command
759 		case MessageSync: // Sync does not matter
760 		case MessageSelection: // FIXME: ensure it's from the right player
761 		case MessageQuit:      // FIXME: ensure it's from the right player
762 		case MessageResend:    // FIXME: ensure it's from the right player
763 		case MessageChat:      // FIXME: ensure it's from the right player
764 			return true;
765 		case MessageCommandDismiss: return IsAValidCommand_Dismiss(packet, index, player);
766 		default: return IsAValidCommand_Command(packet, index, player);
767 	}
768 	// FIXME: not all values in nc have been validated
769 }
770 
NetworkParseInGameEvent(const unsigned char * buf,int len,const CHost & host)771 static void NetworkParseInGameEvent(const unsigned char *buf, int len, const CHost &host)
772 {
773 	CNetworkPacket packet;
774 	int commands;
775 	packet.Deserialize(buf, len, &commands);
776 
777 	int player = packet.Header.OrigPlayer;
778 	if (player == 255) {
779 		const int index = FindHostIndexBy(host);
780 		if (index == -1 || PlayerQuit[Hosts[index].PlyNr]) {
781 #ifdef DEBUG
782 			const std::string hostStr = host.toString();
783 			DebugPrint("Not a host in play: %s\n" _C_ hostStr.c_str());
784 #endif
785 			return;
786 		}
787 		player = Hosts[index].PlyNr;
788 	}
789 	if (NetConnectType == 1) {
790 		if (player != 255) {
791 			NetworkBroadcast(packet, commands, player);
792 		}
793 	}
794 	if (commands < 0) {
795 		DebugPrint("Bad packet read\n");
796 		return;
797 	}
798 	NetworkLastCycle[player] = packet.Header.Cycle;
799 	// Parse the packet commands.
800 	for (int i = 0; i != commands; ++i) {
801 		// Handle some messages.
802 		if (packet.Header.Type[i] == MessageQuit) {
803 			CNetworkCommandQuit nc;
804 			nc.Deserialize(&packet.Command[i][0]);
805 			const int playerNum = nc.player;
806 
807 			if (playerNum >= 0 && playerNum < NumPlayers) {
808 				PlayerQuit[playerNum] = 1;
809 			}
810 		}
811 		if (packet.Header.Type[i] == MessageResend) {
812 			ParseResendCommand(packet);
813 			return;
814 		}
815 		// Receive statistic
816 		NetworkLastFrame[player] = FrameCounter;
817 
818 		bool validCommand = IsAValidCommand(packet, i, player);
819 		// Place in network in
820 		if (validCommand) {
821 			// Destination cycle (time to execute).
822 			unsigned long n = ((GameCycle + 128) & ~0xFF) | packet.Header.Cycle;
823 			if (n > GameCycle + 128) {
824 				n -= 0x100;
825 			}
826 			NetworkIn[packet.Header.Cycle][player][i].Time = n;
827 			NetworkIn[packet.Header.Cycle][player][i].Type = packet.Header.Type[i];
828 			NetworkIn[packet.Header.Cycle][player][i].Data = packet.Command[i];
829 		} else {
830 			SetMessage(_("%s sent bad command"), Players[player].Name.c_str());
831 			DebugPrint("%s sent bad command: 0x%x\n" _C_ Players[player].Name.c_str()
832 					   _C_ packet.Header.Type[i] & 0x7F);
833 		}
834 	}
835 	for (int i = commands; i != MaxNetworkCommands; ++i) {
836 		NetworkIn[packet.Header.Cycle][player][i].Time = 0;
837 	}
838 	// Waiting for this time slot
839 	if (!NetworkInSync) {
840 		const int networkUpdates = CNetworkParameter::Instance.gameCyclesPerUpdate;
841 		unsigned long n = ((GameCycle / networkUpdates) + 1) * networkUpdates;
842 		if (IsNetworkCommandReady(n) == true) {
843 			NetworkInSync = true;
844 		}
845 	}
846 }
847 
848 /**
849 **  Called if message for the network is ready.
850 **  (by WaitEventsOneFrame)
851 */
NetworkEvent()852 void NetworkEvent()
853 {
854 	if (!IsNetworkGame()) {
855 		NetworkInSync = true;
856 		return;
857 	}
858 	// Read the packet.
859 	unsigned char buf[1024];
860 	CHost host;
861 	int len = NetworkFildes.Recv(&buf, sizeof(buf), &host);
862 	if (len < 0) {
863 		DebugPrint("Server/Client gone?\n");
864 		// just hope for an automatic recover right now..
865 		NetworkInSync = false;
866 		return;
867 	}
868 
869 	// Setup messages
870 	if (NetConnectRunning) {
871 		if (NetworkParseSetupEvent(buf, len, host)) {
872 			return;
873 		}
874 	}
875 	const unsigned char msgtype = buf[0];
876 	if (msgtype == MessageInit_FromClient || msgtype == MessageInit_FromServer) {
877 		return;
878 	}
879 	NetworkParseInGameEvent(buf, len, host);
880 }
881 
882 /**
883 **  Quit the game.
884 */
NetworkQuitGame()885 void NetworkQuitGame()
886 {
887 	if (!ThisPlayer || IsNetworkGame() == false) {
888 		return;
889 	}
890 	const int gameCyclesPerUpdate = CNetworkParameter::Instance.gameCyclesPerUpdate;
891 	const int NetworkLag = CNetworkParameter::Instance.NetworkLag;
892 	const int n = (GameCycle + gameCyclesPerUpdate) / gameCyclesPerUpdate * gameCyclesPerUpdate + NetworkLag;
893 	CNetworkCommandQueue(&ncqs)[MaxNetworkCommands] = NetworkIn[n & 0xFF][ThisPlayer->Index];
894 	CNetworkCommandQuit nc;
895 	nc.player = ThisPlayer->Index;
896 	ncqs[0].Type = MessageQuit;
897 	ncqs[0].Time = n;
898 	ncqs[0].Data.resize(nc.Size());
899 	nc.Serialize(&ncqs[0].Data[0]);
900 	for (int i = 1; i < MaxNetworkCommands; ++i) {
901 		ncqs[i].Type = MessageNone;
902 		ncqs[i].Data.clear();
903 	}
904 	NetworkSendPacket(ncqs);
905 }
906 
NetworkExecCommand_Sync(const CNetworkCommandQueue & ncq)907 static void NetworkExecCommand_Sync(const CNetworkCommandQueue &ncq)
908 {
909 	Assert((ncq.Type & 0x7F) == MessageSync);
910 
911 	CNetworkCommandSync nc;
912 	nc.Deserialize(&ncq.Data[0]);
913 	const unsigned long gameNetCycle = GameCycle;
914 	const int syncSeed = nc.syncSeed;
915 	const int syncHash = nc.syncHash;
916 
917 	if (syncSeed != NetworkSyncSeeds[gameNetCycle & 0xFF]
918 		|| syncHash != NetworkSyncHashs[gameNetCycle & 0xFF]) {
919 		SetMessage("%s", _("Network out of sync"));
920 		//Wyrmgus start
921 		fprintf(stderr, "Network out of sync %x!=%x! %d!=%d! Cycle %lu\n", syncSeed, NetworkSyncSeeds[gameNetCycle & 0xFF], syncHash, NetworkSyncHashs[gameNetCycle & 0xFF], GameCycle);
922 		//Wyrmgus end
923 		DebugPrint("\nNetwork out of sync %x!=%x! %d!=%d! Cycle %lu\n\n" _C_
924 				   syncSeed _C_ NetworkSyncSeeds[gameNetCycle & 0xFF] _C_
925 				   syncHash _C_ NetworkSyncHashs[gameNetCycle & 0xFF] _C_ GameCycle);
926 	}
927 }
928 
NetworkExecCommand_Selection(const CNetworkCommandQueue & ncq)929 static void NetworkExecCommand_Selection(const CNetworkCommandQueue &ncq)
930 {
931 	Assert((ncq.Type & 0x7F) == MessageSelection);
932 
933 	CNetworkSelection ns;
934 
935 	ns.Deserialize(&ncq.Data[0]);
936 	if (Players[ns.player].Team != ThisPlayer->Team) {
937 		return;
938 	}
939 	std::vector<CUnit *> units;
940 
941 	for (size_t i = 0; i != ns.Units.size(); ++i) {
942 		units.push_back(&UnitManager.GetSlotUnit(ns.Units[i]));
943 	}
944 	ChangeTeamSelectedUnits(Players[ns.player], units);
945 }
946 
NetworkExecCommand_Chat(const CNetworkCommandQueue & ncq)947 static void NetworkExecCommand_Chat(const CNetworkCommandQueue &ncq)
948 {
949 	Assert((ncq.Type & 0x7F) == MessageChat);
950 
951 	CNetworkChat nc;
952 	nc.Deserialize(&ncq.Data[0]);
953 
954 	SetMessage("%s", nc.Text.c_str());
955 	PlayGameSound(GameSounds.ChatMessage.Sound, MaxSampleVolume);
956 	CommandLog("chat", NoUnitP, FlushCommands, -1, -1, NoUnitP, nc.Text.c_str(), -1);
957 }
958 
NetworkExecCommand_Quit(const CNetworkCommandQueue & ncq)959 static void NetworkExecCommand_Quit(const CNetworkCommandQueue &ncq)
960 {
961 	Assert((ncq.Type & 0x7F) == MessageQuit);
962 	CNetworkCommandQuit nc;
963 
964 	nc.Deserialize(&ncq.Data[0]);
965 	NetworkRemovePlayer(nc.player);
966 	CommandLog("quit", NoUnitP, FlushCommands, nc.player, -1, NoUnitP, nullptr, -1);
967 	CommandQuit(nc.player);
968 }
969 
NetworkExecCommand_ExtendedCommand(const CNetworkCommandQueue & ncq)970 static void NetworkExecCommand_ExtendedCommand(const CNetworkCommandQueue &ncq)
971 {
972 	Assert((ncq.Type & 0x7F) == MessageExtendedCommand);
973 	CNetworkExtendedCommand nec;
974 
975 	nec.Deserialize(&ncq.Data[0]);
976 	ExecExtendedCommand(nec.ExtendedType, (ncq.Type & 0x80) >> 7,
977 						nec.Arg1, nec.Arg2, nec.Arg3, nec.Arg4);
978 }
979 
NetworkExecCommand_Command(const CNetworkCommandQueue & ncq)980 static void NetworkExecCommand_Command(const CNetworkCommandQueue &ncq)
981 {
982 	CNetworkCommand nc;
983 
984 	nc.Deserialize(&ncq.Data[0]);
985 	ExecCommand(ncq.Type, nc.Unit, nc.X, nc.Y, nc.Dest);
986 }
987 
988 /**
989 **  Execute a network command.
990 **
991 **  @param ncq  Network command from queue
992 */
NetworkExecCommand(const CNetworkCommandQueue & ncq)993 static void NetworkExecCommand(const CNetworkCommandQueue &ncq)
994 {
995 	switch (ncq.Type & 0x7F) {
996 		case MessageSync: NetworkExecCommand_Sync(ncq); break;
997 		case MessageSelection: NetworkExecCommand_Selection(ncq); break;
998 		case MessageChat: NetworkExecCommand_Chat(ncq); break;
999 		case MessageQuit: NetworkExecCommand_Quit(ncq); break;
1000 		case MessageExtendedCommand: NetworkExecCommand_ExtendedCommand(ncq); break;
1001 		case MessageNone:
1002 			// Nothing to Do, This Message Should Never be Executed
1003 			Assert(0);
1004 			break;
1005 		default: NetworkExecCommand_Command(ncq); break;
1006 	}
1007 }
1008 
1009 /**
1010 **  Network send commands.
1011 */
NetworkSendCommands(unsigned long gameNetCycle)1012 static void NetworkSendCommands(unsigned long gameNetCycle)
1013 {
1014 	// No command available, send sync.
1015 	int numcommands = 0;
1016 	CNetworkCommandQueue(&ncq)[MaxNetworkCommands] = NetworkIn[gameNetCycle & 0xFF][ThisPlayer->Index];
1017 	ncq[0].Clear();
1018 	if (CommandsIn.empty() && MsgCommandsIn.empty()) {
1019 		CNetworkCommandSync nc;
1020 		ncq[0].Type = MessageSync;
1021 		nc.syncHash = SyncHash;
1022 		nc.syncSeed = SyncRandSeed;
1023 		ncq[0].Data.resize(nc.Size());
1024 		nc.Serialize(&ncq[0].Data[0]);
1025 		ncq[0].Time = gameNetCycle;
1026 		numcommands = 1;
1027 	} else {
1028 		while (!CommandsIn.empty() && numcommands < MaxNetworkCommands) {
1029 			const CNetworkCommandQueue &incommand = CommandsIn.front();
1030 #ifdef DEBUG
1031 			if (incommand.Type != MessageExtendedCommand) {
1032 				CNetworkCommand nc;
1033 				nc.Deserialize(&incommand.Data[0]);
1034 
1035 				const CUnit &unit = UnitManager.GetSlotUnit(nc.Unit);
1036 				// FIXME: we can send destoyed units over network :(
1037 				if (unit.Destroyed) {
1038 					DebugPrint("Sending destroyed unit %d over network!!!!!!\n" _C_ nc.Unit);
1039 				}
1040 			}
1041 #endif
1042 			ncq[numcommands] = incommand;
1043 			ncq[numcommands].Time = gameNetCycle;
1044 			++numcommands;
1045 			CommandsIn.pop_front();
1046 		}
1047 		while (!MsgCommandsIn.empty() && numcommands < MaxNetworkCommands) {
1048 			const CNetworkCommandQueue &incommand = MsgCommandsIn.front();
1049 			ncq[numcommands] = incommand;
1050 			ncq[numcommands].Time = gameNetCycle;
1051 			++numcommands;
1052 			MsgCommandsIn.pop_front();
1053 		}
1054 	}
1055 	if (numcommands != MaxNetworkCommands) {
1056 		ncq[numcommands].Type = MessageNone;
1057 	}
1058 	NetworkSyncSeeds[gameNetCycle & 0xFF] = SyncRandSeed;
1059 	NetworkSyncHashs[gameNetCycle & 0xFF] = SyncHash;
1060 	NetworkSendPacket(ncq);
1061 }
1062 
1063 /**
1064 **  Network execute commands.
1065 */
NetworkExecCommands(unsigned long gameNetCycle)1066 static void NetworkExecCommands(unsigned long gameNetCycle)
1067 {
1068 	// Must execute commands on all computers in the same order.
1069 	for (int i = 0; i < NumPlayers; ++i) {
1070 		const CNetworkCommandQueue *ncqs = NetworkIn[gameNetCycle & 0xFF][i];
1071 		for (int c = 0; c < MaxNetworkCommands; ++c) {
1072 			const CNetworkCommandQueue &ncq = ncqs[c];
1073 			if (ncq.Type == MessageNone) {
1074 				break;
1075 			}
1076 			if (ncq.Time && ncq.Time == gameNetCycle) {
1077 				NetworkExecCommand(ncq);
1078 			}
1079 		}
1080 	}
1081 }
1082 
1083 /**
1084 **  Handle network commands.
1085 */
NetworkCommands()1086 void NetworkCommands()
1087 {
1088 	if (!IsNetworkGame()) {
1089 		return;
1090 	}
1091 	if ((GameCycle % CNetworkParameter::Instance.gameCyclesPerUpdate) != 0) {
1092 		return;
1093 	}
1094 	const unsigned long gameNetCycle = GameCycle;
1095 	// Send messages to all clients (other players)
1096 	NetworkSendCommands(gameNetCycle + CNetworkParameter::Instance.NetworkLag);
1097 	NetworkExecCommands(gameNetCycle);
1098 	NetworkInSync = IsNetworkCommandReady(gameNetCycle + CNetworkParameter::Instance.gameCyclesPerUpdate);
1099 }
1100 
CheckPlayerThatTimeOut(int hostIndex)1101 static void CheckPlayerThatTimeOut(int hostIndex)
1102 {
1103 	const int playerIndex = Hosts[hostIndex].PlyNr;
1104 	const unsigned long lastFrame = NetworkLastFrame[playerIndex];
1105 	if (!lastFrame) {
1106 		return;
1107 	}
1108 	const int framesPerSecond = FRAMES_PER_SECOND * VideoSyncSpeed / 100;
1109 	const int secs = (FrameCounter - lastFrame) / framesPerSecond;
1110 	// FIXME: display a menu while we wait
1111 	const int timeoutInS = CNetworkParameter::Instance.timeoutInS;
1112 	if (3 <= secs && secs < timeoutInS && FrameCounter % framesPerSecond == 0) {
1113 		SetMessage(_("Waiting for player \"%s\": %d:%02d"), Hosts[hostIndex].PlyName,
1114 				   (timeoutInS - secs) / 60, (timeoutInS - secs) % 60);
1115 	}
1116 	if (secs >= timeoutInS) {
1117 		const unsigned int nextGameNetCycle = GameCycle / CNetworkParameter::Instance.gameCyclesPerUpdate + 1;
1118 		CNetworkCommandQuit nc;
1119 		nc.player = playerIndex;
1120 		CNetworkCommandQueue *ncq = &NetworkIn[nextGameNetCycle & 0xFF][playerIndex][0];
1121 		ncq->Time = nextGameNetCycle * CNetworkParameter::Instance.gameCyclesPerUpdate;
1122 		ncq->Type = MessageQuit;
1123 		ncq->Data.resize(nc.Size());
1124 		nc.Serialize(&ncq->Data[0]);
1125 		PlayerQuit[playerIndex] = 1;
1126 		SetMessage("%s", _("Timed out"));
1127 
1128 		CNetworkPacket np;
1129 		np.Header.Cycle = ncq->Time & 0xFF;
1130 		np.Header.Type[0] = ncq->Type;
1131 		np.Header.Type[1] = MessageNone;
1132 
1133 		NetworkBroadcast(np, 1);
1134 	}
1135 }
1136 
1137 /**
1138 **  Network resend commands, we have a missing packet send to all clients
1139 **  what packet we are missing.
1140 **
1141 **  @todo
1142 **  We need only send to the clients, that have not delivered the packet.
1143 */
NetworkResendCommands()1144 static void NetworkResendCommands()
1145 {
1146 #ifdef DEBUG
1147 	++NetworkStat.resentPacketCount;
1148 #endif
1149 
1150 	const int networkUpdates = CNetworkParameter::Instance.gameCyclesPerUpdate;
1151 	const int nextGameCycle = ((GameCycle / networkUpdates) + 1) * networkUpdates;
1152 	// Build packet
1153 	CNetworkPacket packet;
1154 	packet.Header.Type[0] = MessageResend;
1155 	packet.Header.Type[1] = MessageNone;
1156 	packet.Header.Cycle = uint8_t(nextGameCycle & 0xFF);
1157 
1158 	NetworkBroadcast(packet, 1);
1159 }
1160 
1161 /**
1162 **  Recover network.
1163 */
NetworkRecover()1164 void NetworkRecover()
1165 {
1166 	if (HostsCount == 0) {
1167 		NetworkInSync = true;
1168 		return;
1169 	}
1170 	if (FrameCounter % CNetworkParameter::Instance.gameCyclesPerUpdate != 0) {
1171 		return;
1172 	}
1173 	for (int i = 0; i != HostsCount; ++i) {
1174 		CheckPlayerThatTimeOut(i);
1175 	}
1176 	NetworkResendCommands();
1177 	const unsigned int nextGameNetCycle = GameCycle / CNetworkParameter::Instance.gameCyclesPerUpdate + 1;
1178 	NetworkInSync = IsNetworkCommandReady(nextGameNetCycle);
1179 }
1180 
1181 //@}
1182