//     ____                _       __               
//    / __ )____  _____   | |     / /___ ___________
//   / __  / __ \/ ___/   | | /| / / __ `/ ___/ ___/
//  / /_/ / /_/ (__  )    | |/ |/ / /_/ / /  (__  ) 
// /_____/\____/____/     |__/|__/\__,_/_/  /____/  
//                                              
//       A futuristic real-time strategy game.
//          This file is part of Bos Wars.
//
/**@name network.cpp - The network. */
//
//      (c) Copyright 2000-2008 by Lutz Sammer, Andreas Arens, and Jimmy Salmon
//
//      This program is free software; you can redistribute it and/or modify
//      it under the terms of the GNU General Public License as published by
//      the Free Software Foundation; only version 2 of the License.
//
//      This program is distributed in the hope that it will be useful,
//      but WITHOUT ANY WARRANTY; without even the implied warranty of
//      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//      GNU General Public License for more details.
//
//      You should have received a copy of the GNU General Public License
//      along with this program; if not, write to the Free Software
//      Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
//      02111-1307, USA.
//

//@{

//----------------------------------------------------------------------------
// Documentation
//----------------------------------------------------------------------------

/**
** @page NetworkModule Module - Network
**
** @section Basics How does it work.
**
** Stratagus uses an UDP peer to peer protocol (p2p). The default port
** is 6660.
**
** @subsection udp_vs_tcp UDP vs. TCP
**
** UDP is a connectionless protocol. This means it does not perform
** retransmission of data and therefore provides very few error recovery
** services. UDP instead offers a direct way to send and receive
** datagrams (packets) over the network; it is used primarily for
** broadcasting messages.
**
** TCP, on the other hand, provides a connection-based, reliable data
** stream.  TCP guarantees delivery of data and also guarantees that
** packets will be delivered in the same order in which they were sent.
**
** TCP is a simple and effective way of transmitting data. For making sure
** that a client and server can talk to each other it is very good.
** However, it carries with it a lot of overhead and extra network lag.
**
** UDP needs less overhead and has a smaller lag. Which is very important
** for real time games. The disadvantages includes:
**
** @li You won't have an individual socket for each client.
** @li Given that clients don't need to open a unique socket in order to
** transmit data there is the very real possibility that a client
** who is not logged into the game will start sending all kinds of
** garbage to your server in some kind of attack. It becomes much
** more difficult to stop them at this point.
** @li Likewise, you won't have a clear disconnect/leave game message
** unless you write one yourself.
** @li Some data may not reach the other machine, so you may have to send
** important stuff many times.
** @li Some data may arrive in the wrong order. Imagine that you get
** package 3 before package 1. Even a package can come duplicate.
** @li UDP is connectionless and therefore has problems with firewalls.
**
** I have choosen UDP. Additional support for the TCP protocol is welcome.
**
** @subsection sc_vs_p2p server/client vs. peer to peer
**
** @li server to client
**
** The player input is send to the server. The server collects the input
** of all players and than send the commands to all clients.
**
** @li peer to peer (p2p)
**
** The player input is direct send to all others clients in game.
**
** p2p has the advantage of a smaller lag, but needs a higher bandwidth
** by the clients.
**
** I have choosen p2p. Additional support for a server to client protocol
** is welcome.
**
** @subsection bandwidth bandwidth
**
** I wanted to support up to 8 players with 28.8kbit modems.
**
** Most modems have a bandwidth of 28.8K bits/second (both directions) to
** 56K bits/second (33.6K uplink) It takes actually 10 bits to send 1 byte.
** This makes calculating how many bytes you are sending easy however, as
** you just need to divide 28800 bits/second by 10 and end up with 2880
** bytes per second.
**
** We want to send many packets, more updated pro second and big packets,
** less protocol overhead.
**
** If we do an update 6 times per second, leaving approximately 480 bytes
** per update in an ideal environment.
**
** For the TCP/IP protocol we need following:
** IP  Header 20 bytes
** UDP Header 8  bytes
**
** With 10 bytes per command and 4 commands this are 68 (20+8+4*10) bytes
** pro packet.  Sending it to 7 other players, gives 476 bytes pro update.
** This means we could do 6 updates (each 166ms) pro second.
**
** @subsection a_packet Network packet
**
** @li [IP  Header - 20 bytes]
** @li [UDP Header -  8 bytes]
** @li [Type 1 byte][Cycle 1 byte][Data 8 bytes] - Slot 0
** @li [Type 1 byte][Cycle 1 byte][Data 8 bytes] - Slot 1
** @li [Type 1 byte][Cycle 1 byte][Data 8 bytes] - Slot 2
** @li [Type 1 byte][Cycle 1 byte][Data 8 bytes] - Slot 3
**
** @subsection internals Putting it together
**
** All computers in play must run absolute syncron. Only user commands
** are send over the network to the other computers. The command needs
** some time to reach the other clients (lag), so the command is not
** executed immediatly on the local computer, it is stored in a delay
** queue and send to all other clients. After a delay of ::NetworkLag
** game cycles the commands of the other players are received and executed
** together with the local command. Each ::NetworkUpdates game cycles there
** must a package send, to keep the clients in sync, if there is no user
** command, a dummy sync package is send.
** If there are missing packages, the game is paused and old commands
** are resend to all clients.
**
** @section missing What features are missing
**
** @li The recover from lost packets can be improved, if the server knows
** which packets the clients have received.
**
** @li The UDP protocol isn't good for firewalls, we need also support
** for the TCP protocol.
**
** @li Add a server / client protocol, which allows more players pro
** game.
**
** @li Lag (latency) and bandwidth are set over the commandline. This
** should be automatic detected during game setup and later during
** game automatic adapted.
**
** @li Also it would be nice, if we support viewing clients. This means
** other people can view the game in progress.
**
** @li The current protocol only uses single cast, for local LAN we
** should also support broadcast and multicast.
**
** @li Proxy and relays should be supported, to improve the playable
** over the internet.
**
** @li The game cycles is transfered for each slot, this is not needed. We
** can save some bytes if we compress this.
**
** @li We can sort the command by importants, currently all commands are
** send in order, only chat messages are send if there are free slots.
**
** @li password protection the login process (optional), to prevent that
** the wrong player join an open network game.
**
** @li add meta server support, i have planned to use bnetd and its
** protocol.
**
** @section api API How should it be used.
**
** ::InitNetwork1()
**
** ::InitNetwork2()
**
** ::ExitNetwork1()
**
** ::NetworkSendCommand()
**
** ::NetworkSendExtendedCommand()
**
** ::NetworkEvent()
**
** ::NetworkQuit()
**
** ::NetworkChatMessage()
**
** ::NetworkEvent()
**
** ::NetworkRecover()
**
** ::NetworkCommands()
**
** ::NetworkFildes
**
** ::NetworkInSync
**
** @todo FIXME: continue docu
*/

//----------------------------------------------------------------------------
//  Includes
//----------------------------------------------------------------------------

#include "stratagus.h"

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <list>

#include "net_lowlevel.h"
#include "unit.h"
#include "unit_manager.h"
#include "unittype.h"
#include "map.h"
#include "actions.h"
#include "player.h"
#include "network.h"
#include "netconnect.h"
#include "commands.h"
#include "replay.h"
#include "interface.h"
#include "results.h"
#include "sound.h"


//----------------------------------------------------------------------------
//  Declaration
//----------------------------------------------------------------------------

/**
**  Network command input/output queue.
*/
class CNetworkCommandQueue {
public:
	CNetworkCommandQueue() : Time(0), Type(0) {}
	void Clear() { this->Time = this->Type = 0; Data.Clear(); }

	unsigned long Time;     /// time to execute
	unsigned char Type;     /// Command Type
	CNetworkCommand Data;    /// command content
};

//----------------------------------------------------------------------------
//  Variables
//----------------------------------------------------------------------------

int NetworkNumInterfaces;                  /// Network number of interfaces
Socket NetworkFildes = (Socket)-1;         /// Network file descriptor
int NetworkInSync = 1;                     /// Network is in sync
int NetworkUpdates = 5;                    /// Network update each # game cycles
int NetworkLag = 10;                       /// Network lag in # game cycles
unsigned long NetworkStatus[PlayerMax];    /// Network status
unsigned long NetworkLastFrame[PlayerMax]; /// Last frame received packet
int NetworkTimeout = 45;                   /// Number of seconds until player times out

static char NetMsgBuf[PlayerMax][128];     /// Chat message buffers
static int NetMsgBufLen[PlayerMax];        /// Stored chat message length
#ifdef DEBUG
unsigned long MyHost;                      /// My host number.
int MyPort;                                /// My port number.
#endif
static unsigned long NetworkDelay;         /// Delay counter for recover.
static int NetworkSyncSeeds[256];          /// Network sync seeds.
static int NetworkSyncHashs[256];          /// Network sync hashs.
static CNetworkCommandQueue NetworkIn[256][PlayerMax][MaxNetworkCommands]; /// Per-player network packet input queue
std::list<CNetworkCommandQueue *> CommandsIn;   /// Network command input queue
std::list<CNetworkCommandQueue *> MsgCommandsIn;/// Network message input queue

#ifdef DEBUG
static int NetworkReceivedPackets;         /// Packets received packets
static int NetworkReceivedEarly;           /// Packets received too early
static int NetworkReceivedLate;            /// Packets received too late
static int NetworkReceivedDups;            /// Packets received as duplicates
static int NetworkReceivedLost;            /// Packets received packet lost

static int NetworkSendPackets;             /// Packets send packets
static int NetworkSendResend;              /// Packets send to resend
#endif

static int PlayerQuit[PlayerMax];          /// Player quit

#define MAX_NCQS 100
static CNetworkCommandQueue NCQs[MAX_NCQS]; /// CNetworkCommandQueues
static int NumNCQs;                         /// Number of NCQs in use


//----------------------------------------------------------------------------
//  Serialize/Deserialize
//----------------------------------------------------------------------------

void CNetworkCommand::Serialize(unsigned char *p) const
{
	*(Uint16 *)p = this->Unit;
	p += 2;
	*(Uint16 *)p = this->X;
	p += 2;
	*(Uint16 *)p = this->Y;
	p += 2;
	*(Uint16 *)p = this->Dest;
	p += 2;
}

void CNetworkCommand::Deserialize(unsigned char *p)
{
	this->Unit = *(Uint16 *)p;
	p += 2;
	this->X = *(Uint16 *)p;
	p += 2;
	this->Y = *(Uint16 *)p;
	p += 2;
	this->Dest = *(Uint16 *)p;
	p += 2;
}

void CNetworkExtendedCommand::Serialize(unsigned char *p)
{
	*p++ = this->ExtendedType;
	*p++ = this->Arg1;
	*(Uint16 *)p = this->Arg2;
	p += 2;
	*(Uint16 *)p = this->Arg3;
	p += 2;
	*(Uint16 *)p = this->Arg4;
	p += 2;
}

void CNetworkExtendedCommand::Deserialize(unsigned char *p)
{
	this->ExtendedType = *p++;
	this->Arg1 = *p++;
	this->Arg2 = *(Uint16 *)p;
	p += 2;
	this->Arg3 = *(Uint16 *)p;
	p += 2;
	this->Arg4 = *(Uint16 *)p;
	p += 2;
}

void CNetworkChat::Serialize(unsigned char *p)
{
	*p++ = this->Player;
	memcpy(p, this->Text, 7);
	p += 7;
}

void CNetworkChat::Deserialize(unsigned char *p)
{
	this->Player = *p++;
	memcpy(this->Text, p, 7);
	p += 7;
}

void CNetworkPacketHeader::Serialize(unsigned char *p) const
{
	*p++ = this->Cycle;
	for (int i = 0; i < MaxNetworkCommands; ++i) {
		*p++ = this->Type[i];
	}
}

void CNetworkPacketHeader::Deserialize(unsigned char *p)
{
	this->Cycle = *p++;
	for (int i = 0; i < MaxNetworkCommands; ++i) {
		this->Type[i] = *p++;
	}
}

unsigned char *CNetworkPacket::Serialize(int numcommands) const
{
	unsigned char *buf = new unsigned char[CNetworkPacket::Size(numcommands)];
	unsigned char *p = buf;

	this->Header.Serialize(p);
	p += CNetworkPacketHeader::Size();

	for (int i = 0; i < numcommands; ++i) {
		if (this->Header.Type[i] == MessageExtendedCommand) {
			((CNetworkExtendedCommand *)&this->Command[i])->Serialize(p);
		} else if (this->Header.Type[i] == MessageChat) {
			((CNetworkChat *)&this->Command[i])->Serialize(p);
		} else {
			this->Command[i].Serialize(p);
		}
		p += CNetworkCommand::Size();
	}

	return buf;
}

int CNetworkPacket::Deserialize(unsigned char *p, unsigned int len)
{
	// check min and max size
	if (len < CNetworkPacket::Size(1) ||
			len > CNetworkPacket::Size(MaxNetworkCommands)) {
		return -1;
	}

	// can't have partial commands
	len -= CNetworkPacketHeader::Size();
	if ((len / CNetworkCommand::Size()) * CNetworkCommand::Size() != len) {
		return -1;
	}

	this->Header.Deserialize(p);
	p += CNetworkPacketHeader::Size();

	int commands = len / CNetworkCommand::Size();

	for (int i = 0; i < commands; ++i) {
		if (this->Header.Type[i] == MessageExtendedCommand) {
			((CNetworkExtendedCommand *)&this->Command[i])->Deserialize(p);
		} else if (this->Header.Type[i] == MessageChat) {
			((CNetworkChat *)&this->Command[i])->Deserialize(p);
		} else {
			this->Command[i].Deserialize(p);
		}
		p += CNetworkCommand::Size();
	}

	return commands;
}

//----------------------------------------------------------------------------
//  Mid-Level api functions
//----------------------------------------------------------------------------

/**
**  Send message to all clients.
**
**  @param packet       Packet to send.
**  @param numcommands  Number of commands.
*/
static void NetworkBroadcast(const CNetworkPacket *packet, int numcommands)
{
	unsigned char *buf = packet->Serialize(numcommands);

	// Send to all clients.
	for (int i = 0; i < HostsCount; ++i) {
		NetSendUDP(NetworkFildes, Hosts[i].Host, Hosts[i].Port, buf,
			CNetworkPacket::Size(numcommands));
	}

	delete[] buf;
}

/**
**  Network send packet. Build it from queue and broadcast.
**
**  @param ncq  Outgoing network queue start.
*/
static void NetworkSendPacket(const CNetworkCommandQueue *ncq)
{
	CNetworkPacket packet;
	int i;
	int numcommands;

#ifdef DEBUG
	++NetworkSendPackets;
#endif

	//
	// Build packet of up to MaxNetworkCommands messages.
	//
	numcommands = 0;
	packet.Header.Cycle = ncq[0].Time & 0xFF;
	for (i = 0; i < MaxNetworkCommands && ncq[i].Type != MessageNone; ++i) {
		packet.Header.Type[i] = ncq[i].Type;
		packet.Command[i] = ncq[i].Data;
		++numcommands;
	}

	for (; i < MaxNetworkCommands; ++i) {
		packet.Header.Type[i] = MessageNone;
	}

	NetworkBroadcast(&packet, numcommands);
}

//----------------------------------------------------------------------------
//  API init..
//----------------------------------------------------------------------------

/**
**  Initialize network part 1.
*/
void InitNetwork1(void)
{
	int i;
	int port;

	NetworkFildes = (Socket)-1;
	NetworkInSync = 1;
	NetworkNumInterfaces = 0;

	NetInit(); // machine dependent setup

	for (i = 0; i < PlayerMax; ++i) {
		NetMsgBufLen[i] = 0;
	}

	if (NetworkUpdates <= 0) {
		NetworkUpdates = 1;
	}
	// Lag must be multiple of updates
	NetworkLag = (NetworkLag / NetworkUpdates) * NetworkUpdates;

	// Our communication port
	port = NetworkPort;
	for (i = 0; i < 10; ++i) {
		NetworkFildes = NetOpenUDP(port + i);
		if (NetworkFildes != (Socket)-1) {
			break;
		}
	}
	if (i == 10) {
		fprintf(stderr, "NETWORK: No free ports %d-%d available, aborting\n",
			port, port + i);
		NetExit(); // machine dependent network exit
		return;
	}

#if 1
	// FIXME: need a working interface check
	NetworkNumInterfaces = 1;
#else
	NetworkNumInterfaces = NetSocketAddr(NetworkFildes);
	if (NetworkNumInterfaces) {
		DebugPrint("Num IP: %d\n" _C_ NetworkNumInterfaces);
		for (i = 0; i < NetworkNumInterfaces; ++i) {
			DebugPrint("IP: %d.%d.%d.%d\n" _C_ NIPQUAD(ntohl(NetLocalAddrs[i])));
		}
	} else {
		fprintf(stderr, "NETWORK: Not connected to any external IPV4-network, aborting\n");
		ExitNetwork1();
		return;
	}
#endif

#ifdef DEBUG
	{
		char buf[128];

		gethostname(buf, sizeof(buf));
		DebugPrint("%s\n" _C_ buf);
		MyHost = NetResolveHost(buf);
		MyPort = NetLastPort;
		DebugPrint("My host:port %d.%d.%d.%d:%d\n" _C_
			NIPQUAD(ntohl(MyHost)) _C_ ntohs(MyPort));
	}
#endif

	CommandsIn.clear();
	MsgCommandsIn.clear();

	NumNCQs = 0;
}

/**
**  Cleanup network part 1. (to be called _AFTER_ part 2 :)
*/
void ExitNetwork1(void)
{
	if (!IsNetworkGame()) { // No network running
		return;
	}

#ifdef DEBUG
	DebugPrint("Received: %d packets, %d early, %d late, %d dups, %d lost.\n" _C_
		NetworkReceivedPackets _C_ NetworkReceivedEarly _C_ NetworkReceivedLate _C_
		NetworkReceivedDups _C_ NetworkReceivedLost);
	DebugPrint("Send: %d packets, %d resend\n" _C_
		NetworkSendPackets _C_ NetworkSendResend);
#endif

	NetCloseUDP(NetworkFildes);
	NetExit(); // machine dependent setup

	NetworkFildes = (Socket)-1;
	NetworkInSync = 1;
	NetPlayers = 0;
	HostsCount = 0;
}

/**
**  Initialize network part 2.
*/
void InitNetwork2(void)
{
	NetworkConnectSetupGame();

	DebugPrint("Lag %d, Updates %d, Hosts %d\n" _C_
		NetworkLag _C_ NetworkUpdates _C_ HostsCount);

	//
	// Prepare first time without syncs.
	//
	memset(NetworkIn, 0, sizeof(NetworkIn));
	for (int i = 0; i <= NetworkLag; i += NetworkUpdates) {
		for (int n = 0; n < HostsCount; ++n) {
			for (int c = 0; c < MaxNetworkCommands; ++c) {
				NetworkIn[i][Hosts[n].PlyNr][c].Time = i;
				NetworkIn[i][Hosts[n].PlyNr][c].Type = MessageSync;
			}
		}
	}

	memset(NetworkSyncSeeds, 0, sizeof(NetworkSyncSeeds));
	memset(NetworkSyncHashs, 0, sizeof(NetworkSyncHashs));
	memset(PlayerQuit, 0, sizeof(PlayerQuit));
	memset(NetworkStatus, 0, sizeof(NetworkStatus));
	memset(NetworkLastFrame, 0, sizeof(NetworkLastFrame));
}

//----------------------------------------------------------------------------
//  Memory management for CNetworkCommandQueues
//----------------------------------------------------------------------------

/**
**  Allocate a CNetworkCommandQueue
**
**  @return  CNetworkCommandQueue
*/
static CNetworkCommandQueue *AllocNCQ(void)
{
	Assert(NumNCQs != MAX_NCQS);
	CNetworkCommandQueue *ncq = &NCQs[NumNCQs++];
	ncq->Clear();
	return ncq;
}

/**
**  Free a CNetworkCommandQueue
**
**  @param ncq  CNetworkCommandQueue to free
*/
static void FreeNCQ(CNetworkCommandQueue *ncq)
{
	NCQs[ncq - NCQs] = NCQs[--NumNCQs];
}

//----------------------------------------------------------------------------
//  Commands input
//----------------------------------------------------------------------------

/**
**  Prepare send of command message.
**
**  Convert arguments into network format and place it into output queue.
**
**  @param command  Command (Move,Attack,...).
**  @param unit     Unit that receive the command.
**  @param x        optional X map position.
**  @param y        optional y map position.
**  @param dest     optional destination unit.
**  @param type     optional unit-type argument.
**  @param status   Append command or flush old commands.
**
**  @warning  Destination and unit-type shares the same network slot.
*/
void NetworkSendCommand(int command, const CUnit *unit, int x, int y,
	const CUnit *dest, const CUnitType *type, int status)
{
	CNetworkCommandQueue *ncq;
	std::list<CNetworkCommandQueue *>::iterator it;

	// Check for duplicate command in queue
	for (it = CommandsIn.begin(); it != CommandsIn.end(); ++it) {
		ncq = *it;
		if ((ncq->Type & 0x7F) == command &&
				ncq->Data.Unit == htons(unit->Slot) &&
				ncq->Data.X == htons(x) &&
				ncq->Data.Y == htons(y)) {
			if (dest && ncq->Data.Dest == htons(dest->Slot)) {
				return;
			} else if (type && ncq->Data.Dest == htons(type->Slot)) {
				return;
			} else if (ncq->Data.Dest == 0xFFFF) {
				return;
			}
		}
	}

	ncq = AllocNCQ();
	CommandsIn.push_back(ncq);

	ncq->Time = GameCycle;
	ncq->Type = command;
	if (status) {
		ncq->Type |= 0x80;
	}
	ncq->Data.Unit = htons(unit->Slot);
	ncq->Data.X = htons(x);
	ncq->Data.Y = htons(y);
	Assert (!dest || !type); // Both together isn't allowed
	if (dest) {
		ncq->Data.Dest = htons(dest->Slot);
	} else if (type) {
		ncq->Data.Dest = htons(type->Slot);
	} else {
		ncq->Data.Dest = htons(0xFFFF); // -1
	}

}

/**
**  Prepare send of extended command message.
**
**  Convert arguments into network format and place it into output queue.
**
**  @param command  Command (Move,Attack,...).
**  @param arg1     optional argument #1
**  @param arg2     optional argument #2
**  @param arg3     optional argument #3
**  @param arg4     optional argument #4
**  @param status   Append command or flush old commands.
*/
void NetworkSendExtendedCommand(int command, int arg1, int arg2, int arg3,
	int arg4, int status)
{
	CNetworkCommandQueue *ncq;
	CNetworkExtendedCommand *nec;

	ncq = AllocNCQ();
	CommandsIn.push_back(ncq);

	ncq->Time = GameCycle;
	nec = (CNetworkExtendedCommand *)&ncq->Data;

	ncq->Type = MessageExtendedCommand;
	if (status) {
		ncq->Type |= 0x80;
	}
	nec->ExtendedType = command;
	nec->Arg1 = arg1;
	nec->Arg2 = htons(arg2);
	nec->Arg3 = htons(arg3);
	nec->Arg4 = htons(arg4);
}

/**
**  Sends my selections to teammates
**
**  @param units  Units to send
**  @param count  Number of units to send
*/
void NetworkSendSelection(CUnit **units, int count)
{
	CNetworkPacket packet;
	NetworkSelectionHeader *header;
	CNetworkSelection *selection;
	int unitcount;
	int ref;
	int i;
	int teammates[PlayerMax];
	int numteammates;
	int nosent;

	// Check if we have any teammates to send to
	numteammates = 0;
	for (i = 0; i < HostsCount; ++i) {
		if (Players[Hosts[i].PlyNr].Team == ThisPlayer->Team) {
			teammates[numteammates++] = i;
		}
	}
	if (!numteammates) {
		return;
	}

	//
	//  Build and send packets to cover all units.
	//
	unitcount = 0;
	while (unitcount < count) {
		header = (NetworkSelectionHeader *)&(packet.Header);
		if (unitcount == 0) {
			header->Add = 0;
		} else {
			header->Add = 1;
		}
		header->Remove = 0;

		nosent = 0;
		for (i = 0; i < MaxNetworkCommands && unitcount < count; ++i) {
			header->Type[i] = MessageSelection;
			selection = (CNetworkSelection *)&packet.Command[i];
			for (ref = 0; ref < 4 && unitcount < count; ++ref, ++unitcount) {
				selection->Unit[ref] = htons(UnitNumber(units[unitcount]));
				++nosent;
			}
		}

		if (unitcount >= count) {
			// This is the last command
			header->NumberSent = nosent;
		} else {
			header->NumberSent = MaxNetworkCommands * 4;
		}

		for (; i < MaxNetworkCommands; ++i) {
			packet.Header.Type[i] = MessageNone;
		}
		

		//
		// Send the Constructed packet to team members
		//
		int numcommands = (nosent + 3) / 4;
		unsigned char *buf = packet.Serialize(numcommands);

		for (i = 0; i < numteammates; ++i) {
			ref = NetSendUDP(NetworkFildes, Hosts[teammates[i]].Host, Hosts[teammates[i]].Port,
				buf, CNetworkPacketHeader::Size() + CNetworkSelection::Size() * numcommands);
		}
	}

}
/**
**  Process Received Unit Selection
**
**  @param packet  Network Packet to Process
**  @param player  Player number
*/
static void NetworkProcessSelection(CNetworkPacket *packet, int player)
{
	CUnit *units[UnitMax];
	NetworkSelectionHeader *header;
	CNetworkSelection *selection;
	int adjust;
	int count;
	int unitcount;

	header = (NetworkSelectionHeader *)&(packet->Header);
	//
	// Create Unit Array
	//
	count = header->NumberSent;
	adjust = (header->Add << 1) | header->Remove;
	unitcount = 0;

	for (int i = 0; header->Type[i] == MessageSelection; ++i) {
		selection = (CNetworkSelection *)&(packet->Command[i]);
		for (int j = 0; j < 4 && unitcount < count; ++j) {
			units[unitcount++] = Units[ntohs(selection->Unit[j])];
		}
	}
	Assert(count == unitcount);

	ChangeTeamSelectedUnits(&Players[player], units, adjust, count);
}

/**
**  Remove a player from the game.
**
**  @param player  Player number
*/
static void NetworkRemovePlayer(int player)
{
	int i;

	// Remove player from Hosts and clear NetworkIn
	for (i = 0; i < HostsCount; ++i) {
		if (Hosts[i].PlyNr == player) {
			Hosts[i] = Hosts[HostsCount - 1];
			--HostsCount;
			break;
		}
	}
	for (i = 0; i < 256; ++i) {
		for (int c = 0; c < MaxNetworkCommands; ++c) {
			NetworkIn[i][player][c].Time = 0;
		}
	}
}

/**
**  Called if message for the network is ready.
**  (by WaitEventsOneFrame)
**
**  @todo
**  NetworkReceivedEarly NetworkReceivedLate NetworkReceivedDups
**  Must be calculated.
*/
void NetworkEvent(void)
{
	unsigned char buf[1024];
	CNetworkPacket packet;
	int player;
	int i;
	int commands;
	unsigned long n;

	if (!IsNetworkGame()) {
		NetworkInSync = 1;
		return;
	}
	//
	// Read the packet.
	//
	if ((i = NetRecvUDP(NetworkFildes, &buf, sizeof(buf))) < 0) {
		//
		// Server or client gone?
		//
		DebugPrint("Server/Client gone?\n");
		// just hope for an automatic recover right now..
		NetworkInSync = 0;
		return;
	}

#ifdef DEBUG
	++NetworkReceivedPackets;
#endif

	//
	// Setup messages
	//
	if (NetConnectRunning) {
		if (NetworkParseSetupEvent(buf, i)) {
			return;
		}
	}

	commands = packet.Deserialize(buf, i);
	if (commands < 0) {
		DebugPrint("Bad packet read\n");
		return;
	}

	for (i = 0; i < HostsCount; ++i) {
		if (Hosts[i].Host == NetLastHost && Hosts[i].Port == NetLastPort &&
				!PlayerQuit[Hosts[i].PlyNr]) {
			break;
		}
	}
	if (i == HostsCount) {
		DebugPrint("Not a host in play: %d.%d.%d.%d:%d\n" _C_
				NIPQUAD(ntohl(NetLastHost)) _C_ ntohs(NetLastPort));
		return;
	}
	player = Hosts[i].PlyNr;

	// In a normal packet there is a least sync, selection may not have that
	if (packet.Header.Type[0] == MessageSelection || commands == 0) {
		NetworkProcessSelection(&packet, player);
		return;
	}

	//
	// Parse the packet commands.
	//
	for (i = 0; i < commands; ++i) {
		const CNetworkCommand *nc = &packet.Command[i];
		bool validCommand = false;

		//
		// Handle some messages.
		//
		if (packet.Header.Type[i] == MessageQuit) {
			int playerNum = ntohs(nc->X);

			if (playerNum >= 0 && playerNum < NumPlayers) {
				PlayerQuit[playerNum] = 1;
				validCommand = true;
			}
		}

		if (packet.Header.Type[i] == MessageResend) {
			// Destination cycle (time to execute).
			n = ((GameCycle + 128) & ~0xFF) | packet.Header.Cycle;
			if (n > GameCycle + 128) {
				n -= 0x100;
			}

			// FIXME: not necessary to send this packet multiple times!!!!
			// other side sends re-send until it gets an answer.

			if (n != NetworkIn[n & 0xFF][ThisPlayer->Index][0].Time) {
				// Asking for a cycle we haven't gotten to yet, ignore for now
				return;
			}

			NetworkSendPacket(NetworkIn[n & 0xFF][ThisPlayer->Index]);

			// Check if a player quit this cycle
			for (int j = 0; j < HostsCount; ++j) {
				for (int c = 0; c < MaxNetworkCommands; ++c) {
					CNetworkCommandQueue *ncq;
					ncq = &NetworkIn[n & 0xFF][Hosts[j].PlyNr][c];
					if (ncq->Time && ncq->Type == MessageQuit) {
						CNetworkPacket np;
						np.Header.Cycle = ncq->Time & 0xFF;
						np.Header.Type[0] = ncq->Type;
						np.Command[0] = ncq->Data;
						for (int k = 1; k < MaxNetworkCommands; ++k) {
							np.Header.Type[k] = MessageNone;
						}

						NetworkBroadcast(&np, 1);
					}
				}
			}

			return;
		}

		// Destination cycle (time to execute).
		n = ((GameCycle + 128) & ~0xFF) | packet.Header.Cycle;
		if (n > GameCycle + 128) {
			n -= 0x100;
		}

		// Receive statistic
		if (n > NetworkStatus[player]) {
			NetworkStatus[player] = n;
		}
		NetworkLastFrame[player] = FrameCounter;

		// Place in network in
		switch (packet.Header.Type[i] & 0x7F) {
			case MessageExtendedCommand:
				// FIXME: ensure the sender is part of the command
				validCommand = true;
				break;
			case MessageSync:
				// Sync does not matter
				validCommand = true;
				break;
			case MessageQuit:
			case MessageQuitAck:
			case MessageResend:
			case MessageChat:
			case MessageChatTerm:
				// FIXME: ensure it's from the right player
				validCommand = true;
				break;
			case MessageCommandDismiss:
				// Fall through!
			default:
			{
				unsigned int slot = ntohs(nc->Unit);

				if (slot < UnitSlotFree &&
						(UnitSlots[slot]->Player->Index == player ||
							Players[player].IsTeamed(UnitSlots[slot]))) {
					validCommand = true;
				} else {
					validCommand = false;
				}
			}
		}

		// FIXME: not all values in nc have been validated
		if (validCommand) {
			NetworkIn[packet.Header.Cycle][player][i].Time = n;
			NetworkIn[packet.Header.Cycle][player][i].Type = packet.Header.Type[i];
			NetworkIn[packet.Header.Cycle][player][i].Data = *nc;
		} else {
			SetMessage(_("%s sent bad command"), Players[player].Name.c_str());
			DebugPrint("%s sent bad command: 0x%x\n" _C_ 
				Players[player].Name.c_str() _C_ 
				packet.Header.Type[i] & 0x7F);
		}
	}

	for ( ; i < MaxNetworkCommands; ++i) {
		NetworkIn[packet.Header.Cycle][player][i].Time = 0;
	}

	//
	// Waiting for this time slot
	//
	if (!NetworkInSync) {
		NetworkInSync = 1;
		n = (GameCycle / NetworkUpdates) * NetworkUpdates + NetworkUpdates;
		for (player = 0; player < HostsCount; ++player) {
			if (NetworkIn[n & 0xFF][Hosts[player].PlyNr][0].Time != n) {
				NetworkInSync = 0;
				break;
			}
		}
	}
}

/**
**  Quit the game.
*/
void NetworkQuit(void)
{
	if (!ThisPlayer) {
		return;
	}

	int n = (GameCycle + NetworkUpdates) / NetworkUpdates * NetworkUpdates + NetworkLag;
	NetworkIn[n & 0xFF][ThisPlayer->Index][0].Type = MessageQuit;
	NetworkIn[n & 0xFF][ThisPlayer->Index][0].Time = n;
	NetworkIn[n & 0xFF][ThisPlayer->Index][0].Data.X = ThisPlayer->Index;

	for (int i = 1; i < MaxNetworkCommands; ++i) {
		NetworkIn[n & 0xFF][ThisPlayer->Index][i].Type = MessageNone;
	}

	NetworkSendPacket(NetworkIn[n & 0xFF][ThisPlayer->Index]);
}

/**
**  Send chat message. (Message is sent with low priority)
**
**  @param msg  Text message to send.
*/
void NetworkChatMessage(const std::string &msg)
{
	CNetworkCommandQueue *ncq;
	CNetworkChat *ncm;
	const char *cp;
	int n;

	if (IsNetworkGame()) {
		cp = msg.c_str();
		n = msg.size();
		while (n >= (int)sizeof(ncm->Text)) {
			ncq = AllocNCQ();
			MsgCommandsIn.push_back(ncq);
			ncq->Type = MessageChat;
			ncm = (CNetworkChat *)(&ncq->Data);
			ncm->Player = ThisPlayer->Index;
			memcpy(ncm->Text, cp, sizeof(ncm->Text));
			cp += sizeof(ncm->Text);
			n -= sizeof(ncm->Text);
		}
		ncq = AllocNCQ();
		MsgCommandsIn.push_back(ncq);
		ncq->Type = MessageChatTerm;
		ncm = (CNetworkChat *)(&ncq->Data);
		ncm->Player = ThisPlayer->Index;
		memcpy(ncm->Text, cp, n + 1); // see >= above :)
	}
}

/**
**  Parse a network command.
**
**  @param ncq  Network command from queue
*/
static void ParseNetworkCommand(const CNetworkCommandQueue *ncq)
{
	int ply;

	switch (ncq->Type & 0x7F) {
		case MessageSync:
			ply = ntohs(ncq->Data.X) << 16;
			ply |= ntohs(ncq->Data.Y);
			if (ply != NetworkSyncSeeds[GameCycle & 0xFF] ||
					ntohs(ncq->Data.Unit) != NetworkSyncHashs[GameCycle & 0xFF]) {

				SetMessage(_("Network out of sync"));
				DebugPrint("\nNetwork out of sync %x!=%x! %d!=%d! Cycle %lu\n\n" _C_
					ply _C_ NetworkSyncSeeds[GameCycle & 0xFF] _C_
					ntohs(ncq->Data.Unit) _C_ NetworkSyncHashs[GameCycle & 0xFF] _C_ GameCycle);
			}
			return;
		case MessageChat:
		case MessageChatTerm: {
			const CNetworkChat *ncm;

			ncm = (CNetworkChat *)(&ncq->Data);
			ply = ncm->Player;
			if (NetMsgBufLen[ply] + sizeof(ncm->Text) < 128) {
				memcpy(((char *)NetMsgBuf[ply]) + NetMsgBufLen[ply], ncm->Text,
					sizeof(ncm->Text));
			}
			NetMsgBufLen[ply] += sizeof(ncm->Text);
			if (ncq->Type == MessageChatTerm) {
				NetMsgBuf[ply][127] = '\0';
				SetMessage("%s", NetMsgBuf[ply]);
				PlayGameSound(GameSounds.ChatMessage.Sound, MaxSampleVolume);
				NetMsgBufLen[ply] = 0;
			}
			}
			break;
		case MessageQuit:
			NetworkRemovePlayer(ncq->Data.X);
			CommandLog("quit", NoUnitP, FlushCommands, ncq->Data.X, -1, NoUnitP, NULL, -1);
			CommandQuit(ncq->Data.X);
			break;
		case MessageExtendedCommand: {
			const CNetworkExtendedCommand *nec;

			nec = (CNetworkExtendedCommand *)(&ncq->Data);
			ParseExtendedCommand(nec->ExtendedType, (ncq->Type & 0x80) >> 7,
				nec->Arg1, ntohs(nec->Arg2), ntohs(nec->Arg3), ntohs(nec->Arg4));
			}
			break;
		case MessageNone:
			// Nothing to Do, This Message Should Never be Executed
			Assert(0);
			break;
		default:
			ParseCommand(ncq->Type, ntohs(ncq->Data.Unit),
				ntohs(ncq->Data.X), ntohs(ncq->Data.Y), ntohs(ncq->Data.Dest));
			break;
	}
}

/**
**  Network resend commands, we have a missing packet send to all clients
**  what packet we are missing.
**
**  @todo
**  We need only send to the clients, that have not delivered the packet.
*/
static void NetworkResendCommands(void)
{
	CNetworkPacket packet;

#ifdef DEBUG
	++NetworkSendResend;
#endif

	//
	// Build packet
	//
	packet.Header.Type[0] = MessageResend;
	packet.Header.Type[1] = MessageNone;
	packet.Header.Cycle =
		(Uint8)((GameCycle / NetworkUpdates) * NetworkUpdates + NetworkUpdates);

	NetworkBroadcast(&packet, 1);
}

/**
**  Network send commands.
*/
static void NetworkSendCommands(void)
{
	CNetworkCommandQueue *incommand;
	CNetworkCommandQueue *ncq;
	int numcommands;

	//
	// No command available, send sync.
	//
	numcommands = 0;
	incommand = NULL;
	ncq = NetworkIn[(GameCycle + NetworkLag) & 0xFF][ThisPlayer->Index];
	ncq->Clear();
	if (CommandsIn.empty() && MsgCommandsIn.empty()) {
		ncq[0].Type = MessageSync;
		ncq[0].Data.Unit = htons(SyncHash&0xFFFF);
		ncq[0].Data.X = htons(SyncRandSeed>>16);
		ncq[0].Data.Y = htons(SyncRandSeed&0xFFFF);
		ncq[0].Time = GameCycle + NetworkLag;
		numcommands = 1;
	} else {
		while ((!CommandsIn.empty() || !MsgCommandsIn.empty()) &&
				numcommands < MaxNetworkCommands) {
			if (!CommandsIn.empty()) {
				incommand = CommandsIn.front();
#ifdef DEBUG
				if (incommand->Type != MessageExtendedCommand) {
					// FIXME: we can send destoyed units over network :(
					if (UnitSlots[ntohs(ncq->Data.Unit)]->Destroyed) {
						DebugPrint("Sending destroyed unit %d over network!!!!!!\n" _C_
							ntohs(incommand->Data.Unit));
					}
				}
#endif
				CommandsIn.pop_front();
			} else {
				incommand = MsgCommandsIn.front();
				MsgCommandsIn.pop_front();
			}
			ncq[numcommands] = *incommand;
			ncq[numcommands].Time = GameCycle + NetworkLag;
			++numcommands;
			FreeNCQ(incommand);
		}
	}

	if (numcommands != MaxNetworkCommands) {
		ncq[numcommands].Type = MessageNone;
	}

	NetworkSendPacket(ncq);

	NetworkSyncSeeds[(GameCycle + NetworkLag) & 0xFF] = SyncRandSeed;
	NetworkSyncHashs[(GameCycle + NetworkLag) & 0xFF] = SyncHash & 0xFFFF; // FIXME: 32bit later
}

/**
**  Network excecute commands.
*/
static void NetworkExecCommands(void)
{
	CNetworkCommandQueue *ncq;

	//
	// Must execute commands on all computers in the same order.
	//
	for (int i = 0; i < NumPlayers; ++i) {
		//
		// Remove commands.
		//
		for (int c = 0; c < MaxNetworkCommands; ++c) {
			ncq = &NetworkIn[GameCycle & 0xFF][i][c];
			if (ncq->Type == MessageNone) {
				break;
			}
			if (ncq->Time) {
#ifdef DEBUG
				if (ncq->Time != GameCycle) {
					DebugPrint("cycle %lu idx %lu time %lu\n" _C_
						GameCycle _C_ GameCycle & 0xFF _C_ ncq->Time);
					Assert(ncq->Time == GameCycle);
				}
#endif
				ParseNetworkCommand(ncq);
			}
		}
	}
}

/**
**  Network synchronize commands.
*/
static void NetworkSyncCommands(void)
{
	const CNetworkCommandQueue *ncq;
	unsigned long n;

	//
	// Check if all next messages are available.
	//
	NetworkInSync = 1;
	n = GameCycle + NetworkUpdates;
	for (int i = 0; i < HostsCount; ++i) {
		ncq = NetworkIn[n & 0xFF][Hosts[i].PlyNr];
		if (ncq[0].Time != n) {
			NetworkInSync = 0;
			NetworkDelay = FrameCounter + NetworkUpdates;
			// FIXME: should send a resend request.
			break;
		}
	}
}

/**
**  Handle network commands.
*/
void NetworkCommands(void)
{
	if (IsNetworkGame()) {
		if (!(GameCycle % NetworkUpdates)) {
			// Send messages to all clients (other players)
			NetworkSendCommands();
			NetworkExecCommands();
			NetworkSyncCommands();
		}
	}
}


/**
**  Recover network.
*/
void NetworkRecover(void)
{
	if (HostsCount == 0) {
		NetworkInSync = 1;
		return;
	}

	if (FrameCounter > NetworkDelay) {
		NetworkDelay += NetworkUpdates;

		// Check for players that timed out
		for (int i = 0; i < HostsCount; ++i) {
			int secs;

			if (!NetworkLastFrame[Hosts[i].PlyNr]) {
				continue;
			}

			secs = (FrameCounter - NetworkLastFrame[Hosts[i].PlyNr]) /
				(FRAMES_PER_SECOND * VideoSyncSpeed / 100);
			// FIXME: display a menu while we wait
			if (secs >= 3 && secs < NetworkTimeout) {
				if (FrameCounter % FRAMES_PER_SECOND < (unsigned long)NetworkUpdates) {
					SetMessage(_("Waiting for player \"%s\": %d:%02d"), Hosts[i].PlyName,
						(NetworkTimeout - secs) / 60, (NetworkTimeout - secs) % 60);
				}
			}
			if (secs >= NetworkTimeout) {
				CNetworkCommand nc;
				const CNetworkCommandQueue *ncq;
				unsigned long n;
				CNetworkPacket np;

				n = GameCycle + NetworkUpdates;
				nc.X = Hosts[i].PlyNr;
				NetworkIn[n & 0xFF][Hosts[i].PlyNr][0].Time = n;
				NetworkIn[n & 0xFF][Hosts[i].PlyNr][0].Type = MessageQuit;
				NetworkIn[n & 0xFF][Hosts[i].PlyNr][0].Data = nc;
				PlayerQuit[Hosts[i].PlyNr] = 1;
				SetMessage(_("Timed out"));

				ncq = &NetworkIn[n & 0xFF][Hosts[i].PlyNr][0];
				np.Header.Cycle = ncq->Time & 0xFF;
				np.Header.Type[0] = ncq->Type;
				np.Header.Type[1] = MessageNone;

				NetworkBroadcast(&np, 1);

				NetworkSyncCommands();
			}
		}

		// Resend old commands
		NetworkResendCommands();
	}
}

//@}
