#include <stdarg.h>

#include "doomtype.h"
#include "doomstat.h"
#include "d_player.h"
#include "s_sound.h"
#include "gi.h"
#include "d_net.h"
#include "g_game.h"
#include "p_local.h"
#include "sv_main.h"
#include "i_system.h"
#include "c_console.h"
#include "c_dispatch.h"
#include "m_argv.h"
#include "m_random.h"
#include "s_sndseq.h"
#include "version.h"
#include "m_menu.h"
#include "t_info.h"
#include "v_text.h"
#include "p_acs.h"
#include "p_inter.h"

int     lastreliablenum;
short   parse_cl;

client_t clients[MAXPLAYERS];

int       maxclients;
client_t  tempclient;

#define CLIENT_TIMEOUT 65 // 65 seconds

// Welcome strings
char welcomestring[] = "Version 0.91 Server\n";

int  sv_gametime;

int  gametime;

void SV_UpdateConsolePlayer(client_t* cl);
void SV_Fraglogfile (void);
team_t SV_ChooseTeamForPlayer(player_t *player);
void P_SpawnPlayer (mapthing2_t* mthing);

CVAR (rcon_password, "", CVAR_SERVERINFO)

BEGIN_COMMAND (ip)
{
	Printf(PRINT_HIGH, "IP address is %s\n", NET_AdrToString (NET_GetLocalAddress()) );
}
END_COMMAND (ip)

//
// SV_InitNetwork
//
void SV_InitNetwork (void)
{
    char      *v;
 
	netstate = NS_SERVER;

	v = Args.CheckValue ("-port");
    if (v)
    {
       localport = atoi (v);
       Printf (PRINT_HIGH, "using alternate port %i\n", localport);
    }
	else 
	   localport = SERVERPORT;
	
	// set up a socket and net_message buffer
	InitNetCommon();

	// determine my name & address
//	NET_GetLocalAddress ();

	Printf(PRINT_HIGH, "UDP Initialized\n");

	v = Args.CheckValue ("-maxclients");
    if (v)
    {
		maxclients = atoi(v);
		if (maxclients < 0)
			maxclients = 0;
		if (maxclients > MAXPLAYERS)
			maxclients = MAXPLAYERS;
	}
	else
		maxclients = MAXPLAYERS;

	// initialize clients
	for (int i=0; i<maxclients; i++)
    {
		clients[i].netbuf.data = new byte[MAX_UDP_PACKET];
		clients[i].netbuf.maxsize = MAX_UDP_PACKET;
		SZ_Clear(&clients[i].netbuf);
	   
		clients[i].reliablebuf.data = new byte[MAX_UDP_PACKET];
		clients[i].reliablebuf.maxsize = MAX_UDP_PACKET;
		SZ_Clear(&clients[i].reliablebuf);

		clients[i].relpackets.data = new byte[1024*50];
		clients[i].relpackets.maxsize = 1024*50; // 50 KB

		clients[i].cls = cs_free;
	}

	tempclient.reliablebuf.data = new byte[MAX_UDP_PACKET];
    tempclient.reliablebuf.maxsize = MAX_UDP_PACKET;

	gametime = I_GetTime ();
	
	SV_InitMaster();	
}

int SV_GetFreeClient(void)
{
	int numslots;

	if (oldschool.value)
		numslots = 4;
	else
		numslots = maxclients;

	for (int i=0; i < numslots; i++)
	{
		if (players[i].fragcount)
			continue;

		if (clients[i].cls == cs_free && !playeringame[i])
			return i;
	}

	for (i=0; i < numslots; i++)
	{
		if (clients[i].cls == cs_free && !playeringame[i])
			return i;
	}

	return -1;
}

short SV_FindClientByAddr(void)
{
    int i;

	for (i=0; i<MAXPLAYERS; i++)
	{
	   if (clients[i].cls == cs_free)
	       continue;
		
	   if (NET_CompareAdr(clients[i].address, net_from) )
	       return i;
	}
	
	return -1;
}

//
// SV_CheckTimeouts
// If a packet has not been received from a client in CLIENT_TIMEOUT
// seconds, drop the conneciton.
//
void SV_CheckTimeouts (void)
{
	for (int i=0; i<MAXPLAYERS; i++)
	{
		client_t *cl = &clients[i];

		if (cl->cls < cs_spawned)
			continue;

		if (gametic - cl->last_received >= CLIENT_TIMEOUT*35)
		{
			parse_cl = i;
		    SV_DropClient(parse_cl);
		}
	}
}

//
// SV_GetPackets
//
void SV_GetPackets (void)
{
    while (NET_GetPacket() )
    {
        // packet loss emulation :)
        //if (P_Random(pr_misc) > 150)
		//	continue;

        parse_cl = SV_FindClientByAddr();

//		Printf (PRINT_HIGH, "Someone's trying to connect...\n");
//		Printf (PRINT_HIGH, "%s : connecting\n", NET_AdrToString(net_from) );

		if (parse_cl == -1) // no client with net_from address
		{
//			Printf (PRINT_HIGH, "Someone's trying to connect...\n");
    	    // apparently, someone is trying to connect
    	    if (gamestate == GS_LEVEL)
				SV_GetChallenge();
            continue;
        }

		clients[parse_cl].last_received = gametic;

		if (clients[parse_cl].cls == cs_challenge)
		{
			SV_ConnectClient();
			continue;
		}

		if (clients[parse_cl].cls == cs_connected)
		{
			SV_SpawnClient();
			continue;
		}

		SV_ParseCommands();
    }
}

//
// SV_Sound
//
void SV_Sound (AActor *mo, byte channel, char *name, byte attenuation)
{
	int        sfx_id;
	client_t  *cl;
	int        i;

	if (*name == '*')
	{
		// Sexed sound
		char nametemp[128];
		const char templat[] = "player/%s/%s";
		const char *genders[] = { "male", "female", "cyborg" };
		player_t *player;

		sfx_id = -1;
		if ( (player = mo->player) ) 
		{
			sprintf (nametemp, templat, skins[player->userinfo.skin].name, name + 1);
			sfx_id = S_FindSound (nametemp);
			if (sfx_id == -1) 
			{
				sprintf (nametemp, templat, genders[player->userinfo.gender], name + 1);
				sfx_id = S_FindSound (nametemp);
			}
		}
		if (sfx_id == -1) 
		{
			sprintf (nametemp, templat, "male", name + 1);
			sfx_id = S_FindSound (nametemp);
		}
	} 
	else
		sfx_id = S_FindSound (name);


	if (sfx_id < 0)
	{
		Printf (PRINT_HIGH, "SV_Sound: range error. Sfx_id = %d\n", sfx_id);
		return;
	}

	for (i=0; i<MAXPLAYERS; i++)
	{
		cl = &clients[i];

		if (cl->cls < cs_spawned)
			continue;

		MSG_WriteByte  (&cl->netbuf, svc_startsound);
		MSG_WriteShort (&cl->netbuf, mo->netid);
		MSG_WriteByte  (&cl->netbuf, channel);
		MSG_WriteShort (&cl->netbuf, sfx_id);
		MSG_WriteByte  (&cl->netbuf, attenuation);
	}

}


void SV_Sound (client_t *cl, AActor *mo, byte channel, char *name, byte attenuation)
{
	int sfx_id;

	if (*name == '*')
	{
		// Sexed sound
		char nametemp[128];
		const char templat[] = "player/%s/%s";
		const char *genders[] = { "male", "female", "cyborg" };
		player_t *player;

		sfx_id = -1;
		if ( (player = mo->player) ) 
		{
			sprintf (nametemp, templat, skins[player->userinfo.skin].name, name + 1);
			sfx_id = S_FindSound (nametemp);
			if (sfx_id == -1) 
			{
				sprintf (nametemp, templat, genders[player->userinfo.gender], name + 1);
				sfx_id = S_FindSound (nametemp);
			}
		}
		if (sfx_id == -1) 
		{
			sprintf (nametemp, templat, "male", name + 1);
			sfx_id = S_FindSound (nametemp);
		}
	} 
	else
		sfx_id = S_FindSound (name);

	if (sfx_id < 0)
	{
		Printf (PRINT_HIGH, "SV_Sound: range error. Sfx_id = %d\n", sfx_id);
		return;
	}

	MSG_WriteByte (&cl->netbuf, svc_startsound);
	if (mo == NULL)
		MSG_WriteShort (&cl->netbuf, 0);
	else
		MSG_WriteShort (&cl->netbuf, mo->netid);
	MSG_WriteByte  (&cl->netbuf, channel);
	MSG_WriteShort (&cl->netbuf, sfx_id);
	MSG_WriteByte  (&cl->netbuf, attenuation);
}


//
// SV_SoundAvoidCl
// Sends a sound to clients, but doesn't send it to client 'player'.
//
void SV_SoundAvoidCl (player_t *player, AActor *mo, byte channel, char *name, byte attenuation)
{
	int        i;
	int        sfx_id;
	client_t  *cl;

	if (*name == '*')
	{
		// Sexed sound
		char nametemp[128];
		const char templat[] = "player/%s/%s";
		const char *genders[] = { "male", "female", "cyborg" };
		player_t *player;

		sfx_id = -1;
		if ( (player = mo->player) ) 
		{
			sprintf (nametemp, templat, skins[player->userinfo.skin].name, name + 1);
			sfx_id = S_FindSound (nametemp);
			if (sfx_id == -1) 
			{
				sprintf (nametemp, templat, genders[player->userinfo.gender], name + 1);
				sfx_id = S_FindSound (nametemp);
			}
		}
		if (sfx_id == -1) 
		{
			sprintf (nametemp, templat, "male", name + 1);
			sfx_id = S_FindSound (nametemp);
		}
	} 
	else
		sfx_id = S_FindSound (name);

	if (sfx_id < 0)
	{
		Printf (PRINT_HIGH, "SV_Sound: range error. Sfx_id = %d\n", sfx_id);
		return;
	}

    for (i=0; i<MAXPLAYERS; i++)
    {
		cl = &clients[i];

		if (cl->cls < cs_spawned || player == &players[i])
			continue;

		MSG_WriteByte  (&cl->netbuf, svc_startsound);
		MSG_WriteShort (&cl->netbuf, mo->netid);
		MSG_WriteByte  (&cl->netbuf, channel);
		MSG_WriteShort (&cl->netbuf, sfx_id);
		MSG_WriteByte  (&cl->netbuf, attenuation);
    }
}


void SV_Sound (fixed_t x, fixed_t y, byte channel, char *name, byte attenuation)
{
	int        sfx_id;
	client_t  *cl;
	int        i;

	sfx_id = S_FindSound (name);

	if (sfx_id < 0)
	{
		Printf (PRINT_HIGH, "SV_Sound: range error. Sfx_id = %d\n", sfx_id);
		return;
	}

	for (i=0; i<MAXPLAYERS; i++)
	{
		cl = &clients[i];

		if (cl->cls < cs_spawned)
			continue;

		MSG_WriteByte  (&cl->netbuf, svc_soundorigin);
		MSG_WriteLong  (&cl->netbuf, x);
		MSG_WriteLong  (&cl->netbuf, y);
		MSG_WriteByte  (&cl->netbuf, channel);
		MSG_WriteShort (&cl->netbuf, sfx_id);
		MSG_WriteByte  (&cl->netbuf, attenuation);
	}

}


//
// SV_StartSequence
//
void SV_StartSequence (sector_t *sector, int sequence, seqtype_t type)
{
	int sec, i;

	for (sec=0; sec<numsectors; sec++)
		if (sector == &sectors[sec])
			break;

	for (i=0; i<MAXPLAYERS; i++)
	{
		client_t *cl = &clients[i];

		if (cl->cls < cs_spawned)
			continue;

		MSG_WriteByte (&cl->reliablebuf, svc_startsectorsequence);
		MSG_WriteShort (&cl->reliablebuf, sec);
		MSG_WriteShort (&cl->reliablebuf, sequence);
		MSG_WriteByte (&cl->reliablebuf, (byte)type);
	}
}

//
// SV_StopSequence
//
void SV_StopSequence(sector_t *sector)
{
	int sec, i;

	for (sec=0; sec<numsectors; sec++)
		if (sector == &sectors[sec])
			break;

	for (i=0; i<MAXPLAYERS; i++)
	{
		client_t *cl = &clients[i];

		if (cl->cls < cs_spawned)
			continue;

		MSG_WriteByte (&cl->reliablebuf, svc_stopsectorsequence);
		MSG_WriteShort (&cl->reliablebuf, sec);
	}
}


//
// SV_UpdateFrags
//
void SV_UpdateFrags (player_t *player)
{
    int  playernum;

	if (netstate != NS_SERVER)
		return;

    for (playernum=0; playernum<MAXPLAYERS; playernum++)
    {
        if (player == &players[playernum])
            break;
    }

    for (int i=0; i<MAXPLAYERS; i++)
    {
		client_t *cl = &clients[i];

		if (cl->cls < cs_spawned)
			continue;

        MSG_WriteByte (&cl->reliablebuf, svc_updatefrags);
		MSG_WriteByte (&cl->reliablebuf, playernum);
        MSG_WriteShort (&cl->reliablebuf, player->fragcount);
    }
}

//
// SV_SendUserInfo
//
void SV_SendUserInfo (int player, client_t* cl)
{
	player_t *p = &players[player];

	MSG_WriteByte (&cl->reliablebuf, svc_userinfo);
	MSG_WriteByte (&cl->reliablebuf, player);
    MSG_WriteString (&cl->reliablebuf, p->userinfo.netname);
    MSG_WriteString (&cl->reliablebuf, p->userinfo.team);
    MSG_WriteLong (&cl->reliablebuf, p->userinfo.aimdist);
    MSG_WriteLong (&cl->reliablebuf, p->userinfo.color);
    MSG_WriteLong (&cl->reliablebuf, p->userinfo.railcolor);
    MSG_WriteString (&cl->reliablebuf, skins[p->userinfo.skin].name);
    MSG_WriteLong (&cl->reliablebuf, p->userinfo.gender);
    MSG_WriteLong (&cl->reliablebuf, p->userinfo.neverswitch);
    MSG_WriteLong (&cl->reliablebuf, p->userinfo.plasmapreferred);
    MSG_WriteLong (&cl->reliablebuf, p->userinfo.rocketpreferred);
    MSG_WriteLong (&cl->reliablebuf, p->userinfo.expert);
	MSG_WriteByte (&cl->reliablebuf, (byte)p->team);
}

//
// SV_SetupUserInfo
//
void SV_SetupUserInfo(void)
{
    player_t	*p;
	char		*buf;
   
	p = &players[parse_cl];

	buf = MSG_ReadString();
	if (strlen(buf) > MAXPLAYERNAME)
 		buf[MAXPLAYERNAME] = 0;
	
	strcpy(p->userinfo.netname, buf);
 	
	buf = MSG_ReadString();
	if (strlen(buf) > MAXPLAYERNAME)
		buf[MAXPLAYERNAME] = 0;
	
	strcpy(p->userinfo.team, buf);

	p->userinfo.aimdist = MSG_ReadLong();

	if (p->userinfo.aimdist < 0)
		p->userinfo.aimdist = 0;
	if (p->userinfo.aimdist > 5000)
		p->userinfo.aimdist = 5000;

    p->userinfo.color = MSG_ReadLong();
    p->userinfo.railcolor = MSG_ReadLong();
    p->userinfo.skin = R_FindSkin (MSG_ReadString());
    p->userinfo.gender = MSG_ReadLong();
    p->userinfo.neverswitch = !!MSG_ReadLong();
    p->userinfo.plasmapreferred = !!MSG_ReadLong();
    p->userinfo.rocketpreferred = !!MSG_ReadLong();
    p->userinfo.expert = !!MSG_ReadLong();
	p->team = (team_t)MSG_ReadByte();

	// send to other players
	for (int i=0; i<MAXPLAYERS; i++)
	{
		client_t *cl = &clients[i];

		if (cl->cls < cs_spawned || i==parse_cl)
			continue;

		SV_SendUserInfo(parse_cl, &clients[i]);
	}
}

//
// SV_StartChat
//
void SV_StartChat (void)
{
	int player = MSG_ReadByte();

	players[player].chatting = true;

	for (int i=0; i<MAXPLAYERS; i++)
	{
		client_t *cl = &clients[i];

		if (cl->cls < cs_spawned)
			continue;

		MSG_WriteByte (&cl->reliablebuf, svc_startchat);
		MSG_WriteByte (&cl->reliablebuf, player);
	}
}

//
// SV_EndChat
//
void SV_EndChat (void)
{
	int player = MSG_ReadByte();

	players[player].chatting = false;

	for (int i=0; i<MAXPLAYERS; i++)
	{
		client_t *cl = &clients[i];

		if (cl->cls < cs_spawned)
			continue;

		MSG_WriteByte (&cl->reliablebuf, svc_endchat);
		MSG_WriteByte (&cl->reliablebuf, player);
	}
}

//
// SV_NetCommand
//
void SV_NetCommand (void)
{
	short type = MSG_ReadShort();
	byte narg = MSG_ReadByte();
	char *sarg = MSG_ReadString();
	byte plyr = MSG_ReadByte();

	// [BC] Flood protection
	if (type == DEM_SAY && ((gametic - clients[parse_cl].lastsaytic) < TICRATE/2))
		return;

	for (int i=0; i<MAXPLAYERS; i++)
	{
		client_t *cl = &clients[i];

		if (cl->cls < cs_spawned)
			continue;

		MSG_WriteByte (&cl->reliablebuf, svc_netcmd);
		MSG_WriteShort (&cl->reliablebuf, type);
		MSG_WriteByte (&cl->reliablebuf, narg);
		MSG_WriteString (&cl->reliablebuf, sarg);
		MSG_WriteByte (&cl->reliablebuf, plyr);
	}

	Net_Command (type, narg, sarg, plyr, true);
	clients[parse_cl].lastsaytic = gametic;
}
	
//
// SV_ClientFullUpdate
//
void SV_ClientFullUpdate (client_t *cl)
{
	AActor *mo;
	int     i;

	// send players info to the client
	for (i=0; i<MAXPLAYERS; i++)
	{
		//if (clients[i].cls < cs_spawned || cl == &clients[i])
		if (!playeringame[i] || cl == &clients[i])
			continue;

		MSG_WriteByte(&cl->reliablebuf, svc_spawnplayer);
		MSG_WriteByte(&cl->reliablebuf, i);
		MSG_WriteByte (&cl->reliablebuf, PST_REBORN);
		MSG_WriteShort(&cl->reliablebuf, players[i].mo->netid);
		MSG_WriteLong(&cl->reliablebuf, players[i].mo->angle);
		MSG_WriteLong(&cl->reliablebuf, players[i].mo->x);
		MSG_WriteLong(&cl->reliablebuf, players[i].mo->y);
		MSG_WriteLong(&cl->reliablebuf, players[i].mo->z);
		MSG_WriteByte (&cl->reliablebuf, (byte)players[i].isbot);

		SV_SendUserInfo(i, cl);
	}

	if (deathmatch.value)
	{
		// update frags
		for (i=0; i<MAXPLAYERS; i++)
		{
			if (!playeringame[i])
				continue;

			MSG_WriteByte(&cl->reliablebuf, svc_updatefrags);
			MSG_WriteByte(&cl->reliablebuf, i);
			MSG_WriteShort(&cl->reliablebuf, players[i].fragcount);
		}
	}
	else if (teamgame.value)
	{
		// [BC] Update scores
		MSG_WriteByte(&cl->reliablebuf, svc_updatescores);
		MSG_WriteByte(&cl->reliablebuf, TM_BLUE);
		MSG_WriteShort(&cl->reliablebuf, teams[TM_BLUE].points);

		MSG_WriteByte(&cl->reliablebuf, svc_updatescores);
		MSG_WriteByte(&cl->reliablebuf, TM_RED);
		MSG_WriteShort(&cl->reliablebuf, teams[TM_RED].points);

		for (int i=0; i<MAXPLAYERS; i++)
		{
			client_t *cl = &clients[i];

			if (cl->cls < cs_spawned)
				continue;

			MSG_WriteByte(&cl->reliablebuf, svc_updateplayerscores);
			MSG_WriteByte(&cl->reliablebuf, i);
			MSG_WriteByte(&cl->reliablebuf, players[i].pointcount);
		}
	}

	// update things
	TThinkerIterator<AActor> iterator;
    while ( (mo = iterator.Next() ) )
    {
		if (!mo->netid)
			continue;

        if (mo->type != MT_PLAYER)
		{
			MSG_WriteByte(&cl->reliablebuf, svc_spawnmobj);
			MSG_WriteLong(&cl->reliablebuf, mo->x);
			MSG_WriteLong(&cl->reliablebuf, mo->y);
			MSG_WriteLong(&cl->reliablebuf, mo->z);
			MSG_WriteShort(&cl->reliablebuf, mo->type);
			MSG_WriteShort(&cl->reliablebuf, mo->netid);

			// a monster
			if (mo->type == MT_SKULL || (mo->flags & MF_COUNTKILL) )
			{
				MSG_WriteByte(&cl->reliablebuf, svc_mobjangle);
				MSG_WriteShort(&cl->reliablebuf, mo->netid);
				MSG_WriteLong(&cl->reliablebuf, mo->angle);
			}
		}

		// [BC] Corpses are client side now

		if (cl->reliablebuf.cursize >= 4096)
			SV_SendPacket(cl);
    }

	// update sectors
	for (int s=0; s<numsectors; s++)
	{ 
		sector_t* sec = &sectors[s];

	    if (sec->moveable)
		{
			MSG_WriteByte (&cl->reliablebuf, svc_sector);
			MSG_WriteShort (&cl->reliablebuf, s);
			MSG_WriteShort (&cl->reliablebuf, sec->floorheight>>FRACBITS);
			MSG_WriteShort (&cl->reliablebuf, sec->ceilingheight>>FRACBITS);
		}

		if (cl->reliablebuf.cursize >= 4096)
			SV_SendPacket(cl);

	}

	SV_SendPacket(cl);
}

//
// SV_LoadLevel
//
void SV_LoadLevel()
{
	int	i;

	for (i=0; i<MAXPLAYERS; i++)
	{
		client_t *cl = &clients[i];

		if (cl->cls < cs_spawned)
			continue;

		// send dmflags
		MSG_WriteByte(&cl->reliablebuf, svc_dmflags);
		MSG_WriteLong(&cl->reliablebuf, dmflags);
		MSG_WriteLong(&cl->reliablebuf, dmflags2);

		// send skill
		MSG_WriteByte(&cl->reliablebuf, svc_gameskill);
		MSG_WriteByte(&cl->reliablebuf, (byte)gameskill.value);

		// [BC] send botskill
		MSG_WriteByte(&cl->reliablebuf, svc_botskill);
		MSG_WriteByte(&cl->reliablebuf, (byte)botskill.value);

		team_t team = SV_ChooseTeamForPlayer(&players[parse_cl]);

		players[parse_cl].team = team;
   
		// Send special settings like teamplay and deathmatch.
		MSG_WriteByte(&cl->reliablebuf, svc_specialsettings);
		MSG_WriteByte(&cl->reliablebuf, (byte)deathmatch.value);
		MSG_WriteByte(&cl->reliablebuf, (byte)terminator.value);
		MSG_WriteByte(&cl->reliablebuf, (byte)instagib.value);
		MSG_WriteByte(&cl->reliablebuf, (byte)scoremode.value);
		MSG_WriteByte(&cl->reliablebuf, (byte)teamplay.value);
		MSG_WriteByte(&cl->reliablebuf, (byte)teamgame.value);
		MSG_WriteByte(&cl->reliablebuf, (byte)oldschool.value);
		MSG_WriteByte(&cl->reliablebuf, (byte)team);

		// Update timelimit, fraglimit, etc.
		MSG_WriteByte(&cl->reliablebuf, svc_updatelimits);
		MSG_WriteByte(&cl->reliablebuf, (byte)fraglimit.value);
		MSG_WriteByte(&cl->reliablebuf, (byte)timelimit.value);
		MSG_WriteByte(&cl->reliablebuf, (byte)scorelimit.value);
		MSG_WriteByte(&cl->reliablebuf, (byte)pointlimit.value);

		// Force the client to load a new map.
		MSG_WriteByte(&cl->reliablebuf, svc_loadmap);
		MSG_WriteString(&cl->reliablebuf, level.mapname);		
	}
}


void SV_NewLevelClientUpdate()
{
	AActor *mo;
	int		i,j;

	for (i=0; i<MAXPLAYERS; i++)
	{
		client_t *cl = &clients[i];

		if (cl->cls < cs_spawned)
			continue;
	
		if (deathmatch.value)
			for (j=0; j<MAXPLAYERS; j++)
			{
				MSG_WriteByte(&cl->reliablebuf, svc_updatefrags);
				MSG_WriteByte(&cl->reliablebuf, j);
				MSG_WriteShort(&cl->reliablebuf, players[j].fragcount);
			}

		// update things
		TThinkerIterator<AActor> iterator;
		while ( (mo = iterator.Next() ) )
		{
			if (!mo->netid)
				continue;

			// a monster
			if (mo->type == MT_SKULL || (mo->flags & MF_COUNTKILL) )
			{
				MSG_WriteByte(&cl->reliablebuf, svc_mobjangle);
				MSG_WriteShort(&cl->reliablebuf, mo->netid);
				MSG_WriteLong(&cl->reliablebuf, mo->angle);
			}
		}

		SV_SendPacket(cl);
	}
}

//
// SV_ChooseTeamForPlayer
//
team_t SV_ChooseTeamForPlayer(player_t *player)
{
	// If a temp start, spawn there
	if (temp_p - tempstarts)
		return TM_NONE;
	else if (P_CountBluePlayers() > P_CountRedPlayers())
		return TM_RED;
	else
		return TM_BLUE;
}

//
// SV_ConnectClient
//
void SV_ConnectClient(void)
{
	client_t  *cl;

	cl = &clients[parse_cl];

	SZ_Clear(&cl->reliablebuf);

	short version = MSG_ReadShort();
	if (version != VERSION)
	{
		MSG_WriteByte(&cl->reliablebuf, svc_disconnect);
		NET_SendPacket(cl->reliablebuf.cursize, cl->reliablebuf.data, cl->address);
		cl->cls = cs_free;
		return;
	}

	MSG_WriteByte(&cl->reliablebuf, S2C_CONNECTION);
	NET_SendPacket(cl->reliablebuf.cursize, cl->reliablebuf.data, cl->address);
	cl->cls = cs_connected;

	Printf(PRINT_HIGH, "%s : connect\n", NET_AdrToString(net_from) );
   
	cl->reliable_bps = 0;
	cl->unreliable_bps = 0;
	cl->lastcmdtic = 0;
	cl->allow_rcon = false;
	cl->lastsaytic = gametic;

	SZ_Clear(&cl->netbuf);
	SZ_Clear(&cl->reliablebuf);
	SZ_Clear(&cl->relpackets);

	memset(cl->packetseq, -1, sizeof(cl->packetseq) );
	memset(cl->packetbegin, 0, sizeof(cl->packetbegin) );
	memset(cl->packetsize, 0, sizeof(cl->packetsize) );
	cl->sequence = 0;
	cl->last_sequence = -1;
	cl->packetnum = 0;
}

void SV_SpawnClient(void)
{
	client_t *cl = &clients[parse_cl];

	cl->cls = cs_spawned;
	playeringame[parse_cl] = true;
	players[parse_cl].isbot = false;

	// [BC] To restore frags if this player is reconnecting
	char oldname[MAXPLAYERNAME-1];
	int oldfrags;

	strcpy (oldname, players[parse_cl].userinfo.netname);
	oldfrags = players[parse_cl].fragcount;

	// [NightFang]/[BC] Check if two players are connecting from the same port
 	for  (int i=0; i<MAXPLAYERS; i++)
 	{
 		client_t*	cli = &clients[i];
 
 		if (!playeringame[i] || cl == cli)
			continue;
 
 		if (NET_CompareAdr(cli->address, net_from))
 		{
 			Printf(PRINT_HIGH, "SV_SpawnClient: Two clients connecting from the same port!\nDropping old connection.\nOrginal: %s\nDropped: %s\n",
 				NET_AdrToString(net_from), NET_AdrToString(cli->address));
 
			SV_DropClient (i);
 			NET_StringToAdr("0.0.0.0", &clients[i].address);
 			return;
  		}
 	}

	// send welcome message
	MSG_WriteByte (&cl->reliablebuf, svc_print);
	MSG_WriteByte (&cl->reliablebuf, PRINT_HIGH);
  
	MSG_WriteString (&cl->reliablebuf, welcomestring);

	// send dmflags
	MSG_WriteByte(&cl->reliablebuf, svc_dmflags);
	MSG_WriteLong(&cl->reliablebuf, dmflags);
	MSG_WriteLong(&cl->reliablebuf, dmflags2);

	// send consoleplayer number
	MSG_WriteByte(&cl->reliablebuf, svc_consoleplayer);
	MSG_WriteByte(&cl->reliablebuf, parse_cl);

	// send skill
	MSG_WriteByte(&cl->reliablebuf, svc_gameskill);
	MSG_WriteByte(&cl->reliablebuf, (byte)gameskill.value);

	// [BC] send botskill
	MSG_WriteByte(&cl->reliablebuf, svc_botskill);
	MSG_WriteByte(&cl->reliablebuf, (byte)botskill.value);

	players[parse_cl].playerstate = PST_REBORN;
	players[parse_cl].fragcount = 0;

	// get client userinfo
	if (MSG_ReadByte() != clc_userinfo)
	{
		MSG_WriteByte (&cl->reliablebuf, svc_print);
		MSG_WriteByte (&cl->reliablebuf, PRINT_HIGH);
		MSG_WriteString (&cl->reliablebuf, "SV_SpawnClient: Expected clc_userinfo\n");

		SV_DropClient (parse_cl);
		return;
	}

	SV_SetupUserInfo(); // send it to other players
						// if it's a team game, player's color
						// will be set to red or blue

	// [BC] Attempt to restore frags if the player is rejoining
	for (i=0; i<MAXPLAYERS; i++)
	{
		if (playeringame[i] && (i != parse_cl))
			continue;

		if (i == parse_cl)
		{
			if (!stricmp (players[i].userinfo.netname, oldname))
				players[parse_cl].fragcount = oldfrags;
		}
		else
		{
			if (!stricmp (players[i].userinfo.netname, players[parse_cl].userinfo.netname))
				players[parse_cl].fragcount = players[i].fragcount;
		}
	}

	cl->rate = MSG_ReadLong(); // get rate value

	team_t team = SV_ChooseTeamForPlayer(&players[parse_cl]);

	players[parse_cl].team = team;
   
	// Send special settings like teamplay and deathmatch.
	MSG_WriteByte(&cl->reliablebuf, svc_specialsettings);
	MSG_WriteByte(&cl->reliablebuf, (byte)deathmatch.value);
	MSG_WriteByte(&cl->reliablebuf, (byte)terminator.value);
	MSG_WriteByte(&cl->reliablebuf, (byte)instagib.value);
	MSG_WriteByte(&cl->reliablebuf, (byte)scoremode.value);
	MSG_WriteByte(&cl->reliablebuf, (byte)teamplay.value);
	MSG_WriteByte(&cl->reliablebuf, (byte)teamgame.value);
	MSG_WriteByte(&cl->reliablebuf, (byte)oldschool.value);
	MSG_WriteByte(&cl->reliablebuf, (byte)team);

	// Update timelimit, fraglimit, etc.
	MSG_WriteByte(&cl->reliablebuf, svc_updatelimits);
	MSG_WriteByte(&cl->reliablebuf, (byte)fraglimit.value);
	MSG_WriteByte(&cl->reliablebuf, (byte)timelimit.value);
	MSG_WriteByte(&cl->reliablebuf, (byte)scorelimit.value);
	MSG_WriteByte(&cl->reliablebuf, (byte)pointlimit.value);

	// send a map name
	MSG_WriteByte(&cl->reliablebuf, svc_loadmap);
	MSG_WriteString(&cl->reliablebuf, level.mapname);

	if (players[parse_cl].skullb)
	{
		delete players[parse_cl].skullb;
		players[parse_cl].skullb = NULL;
	}

	if (teamgame.value)
	{
		if (team != TM_NONE && team != TM_DUMMY)
			G_TeamGameSpawnPlayer (parse_cl);
		else
			G_TempSpotSpawnPlayer (parse_cl);
	}
	else if (deathmatch.value)
		G_DeathMatchSpawnPlayer(parse_cl);
	else
		P_SpawnPlayer(&playerstarts[parse_cl]);
	 
	SV_ClientFullUpdate(cl);

	// [BC] Let players know who possesses flags/skulls
	if (teamgame.value)
	{
		for (int i=0; i<MAXPLAYERS; i++)
		{
			if (!players[i].team || !playeringame[i] || players[i].team == TM_NONE)
				continue;

			if (players[i].cards[teams[players[i].team].theirs])
			{
					MSG_WriteByte (&cl->reliablebuf, svc_giveitem);
					MSG_WriteByte (&cl->reliablebuf, i);
					MSG_WriteShort (&cl->reliablebuf, (short)teams[players[i].team].theirs);
			}
		}
	}

	// [BC] Update terminator status
	if (terminator.value)
	{
		for (int i=0; i<MAXPLAYERS; i++)
		{
			if (!playeringame[i] || !players[i].powers[pw_terminator])
				continue;

			MSG_WriteByte (&cl->reliablebuf, svc_giveitem);
			MSG_WriteByte (&cl->reliablebuf, i);
			MSG_WriteShort (&cl->reliablebuf, (short)MT_TERMINATOR);
		}
	}

	if (!teamgame.value || team == TM_NONE)
		SV_BroadcastPrintf (PRINT_HIGH, "%s entered the game\n", players[parse_cl].userinfo.netname);
	else
	{ 
		if (team == TM_BLUE)
			SV_BroadcastPrintf (PRINT_HIGH, TEXTCOLOR_BLUE "%s" TEXTCOLOR_NORMAL " entered the game\n", players[parse_cl].userinfo.netname);
		else if (team == TM_RED)
			SV_BroadcastPrintf (PRINT_HIGH, TEXTCOLOR_RED  "%s" TEXTCOLOR_NORMAL " entered the game\n", players[parse_cl].userinfo.netname);
	}

	SV_UpdateFrags (&players[parse_cl]);
}

//
// SV_GetChallenge
//
void SV_GetChallenge(void)
{
	int challenge = MSG_ReadLong();

	if (challenge == LAUNCHER_CHALLENGE)
	{
		SV_SendServerInfo();  // for csDoom Launcher
		return;
	}

	if (challenge != CLIENT_CHALLENGE)
		return;

	int n = SV_GetFreeClient();
	if (n==-1)  // a server is full
	{
		SZ_Clear(&tempclient.reliablebuf);
		MSG_WriteByte(&tempclient.reliablebuf, S2C_FULL);
		NET_SendPacket(tempclient.reliablebuf.cursize, tempclient.reliablebuf.data, net_from);
		return;
	}

	client_t *cl = &clients[n];
	cl->address = net_from;
	cl->last_received = gametic;
	cl->cls = cs_challenge;

	SZ_Clear(&cl->reliablebuf);
	MSG_WriteByte(&cl->reliablebuf, S2C_CHALLENGE);
	NET_SendPacket(cl->reliablebuf.cursize, cl->reliablebuf.data, cl->address);
}

//
// SV_DisconnectClient
//
void SV_DisconnectClient(void)
{
	player_s *player = &players[parse_cl];
	client_t *cl = &clients[parse_cl];

	playeringame[parse_cl] = false;
	players[parse_cl].isbot = false;
	cl->cls = cs_free;
    
	if (gametic - cl->last_received == CLIENT_TIMEOUT*35)
		SV_BroadcastPrintf (PRINT_HIGH, "%s timed out\n", players[parse_cl].userinfo.netname);
	else
        SV_BroadcastPrintf (PRINT_HIGH, "client %s disconnected\n", players[parse_cl].userinfo.netname);

	// tell others clients about it		
	for (int i=0; i<MAXPLAYERS; i++)
	{
		client_t *cl = &clients[i];

		if (cl->cls < cs_spawned)
			continue;

		MSG_WriteByte(&cl->reliablebuf, svc_disconnectclient);
		MSG_WriteByte(&cl->reliablebuf, parse_cl);
	}

	// [BC] Don't take flags with
	if (teamgame.value && player->team != TM_NONE)
	{
		AActor *mo;
		teaminfo_s *other;

		if (player->team == TM_BLUE)
			other = &teams[TM_RED];
		else
			other = &teams[TM_BLUE];

		if (player->cards[other->ourkey])
		{
			player->cards[other->ourkey] = false;
			mo = new AActor (player->mo->x, player->mo->y, ONFLOORZ, other->ours);
			mo->flags |= MF_DROPPED;
			if (other->ourkey == it_blueskull)
				mo->tid = 666;
			else
				mo->tid = 667;
			mo->AddToHash ();
			if (dmflags2 & DF2_YES_IRETURN)
				other->returnroutine;
			else if (!(dmflags2 & DF2_YES_RETURN))
				other->returnTics = SKULLTICS;
		}
	}

	// [BC] Don't take terminator with
	if (terminator.value && player->powers[pw_terminator])
	{
		if (player->icon)
			player->icon->tracer = NULL;

		P_FindTerminator ();
	}

	if (players[parse_cl].mo)
		players[parse_cl].mo->Destroy();
	players[parse_cl].mo = NULL;
}


//
// SV_DropClient
// Called when the player is leaving the server unwillingly. 
//
void SV_DropClient(int playernum)
{
	client_t *cl = &clients[playernum];

	MSG_WriteByte(&cl->reliablebuf, svc_disconnect);
	SV_SendPacket(cl);

	SV_DisconnectClient();
}

//
// SV_SendReconnectSignal
// All clients will reconnect. Called when the server changes a map
//
void SV_SendReconnectSignal()
{
	client_t *cl;

	for (int i=0; i<MAXPLAYERS; i++)
	{
		cl = &clients[i];

		if (!playeringame[i])
			continue;

		if (cl->cls >= cs_spawned)
		{
			MSG_WriteByte(&cl->reliablebuf, svc_reconnect);
			SV_SendPacket(cl);
			cl->cls = cs_free;
		}

		if (!players[i].skullb)
			playeringame[i] = false;

		players[i].mo->Destroy();
		players[i].mo = NULL;
	}

//	for (i=0; i<MAXPLAYERS; i++)
//	{
}


void SV_SendDisconnectSignal()
{
	client_t *cl;

	for (int i=0; i<MAXPLAYERS; i++)
	{
		cl = &clients[i];
		
		if (cl->cls < cs_spawned)
			continue;
		
		MSG_WriteByte(&cl->reliablebuf, svc_disconnect);
		SV_SendPacket(cl);
	   
		playeringame[i] = false;
		players[i].isbot = false;
		cl->cls = cs_free;
	   
		players[i].mo->Destroy();
		players[i].mo = NULL;
	}

}

//
// SV_ExitLevel
// Called when timelimit or fraglimit hit.
//
void SV_ExitLevel()
{
	mapchange = 10*TICRATE;

	for (int i=0; i<MAXPLAYERS; i++)
	{
	   client_t *cl = &clients[i];

	   if (cl->cls < cs_spawned)
		   continue;

	   MSG_WriteByte(&cl->reliablebuf, svc_exitlevel);
	}
}

//
// SV_BroadcastPrintf
// Sends text to all active clients.
//

#define MSGBUFSIZE	1024

void STACK_ARGS SV_BroadcastPrintf (int level, const char *fmt, ...)
{
    va_list       argptr;
    char          string[MSGBUFSIZE];
    client_t     *cl;

    va_start (argptr,fmt);
#ifdef _MSC_VER
 	_vsnprintf (string, MSGBUFSIZE-1, fmt, argptr);
#else
 	vsnprintf  (string, MSGBUFSIZE-1, fmt, argptr);
#endif
    va_end (argptr);

    Printf (level, "%s", string);  // print to the console

    for (int i=0; i<MAXPLAYERS; i++)
    {
		cl = &clients[i];

		if (cl->cls < cs_spawned)
			continue;        

		MSG_WriteByte (&cl->reliablebuf, svc_print);
		MSG_WriteByte (&cl->reliablebuf, level);
		MSG_WriteString (&cl->reliablebuf, string);
    }
}

//
// [BC] SV_MidPrint
// 
// Sends a middle message to a client
void SV_MidPrint (const char *str, int player)
{
    char          string[1024];
    client_t     *cl;

	cl = &clients[player];
	
	if (cl->cls < cs_spawned)
		return;        

	MSG_WriteByte (&cl->reliablebuf, svc_midprint);
	MSG_WriteString (&cl->reliablebuf, string);
}

//
// SV_UpdateMissiles
// Updates missiles position sometimes.
//
void SV_UpdateMissiles(client_t* cl)
{
    AActor *mo;

    TThinkerIterator<AActor> iterator;
    while ( (mo = iterator.Next() ) )
    {
        if (!(mo->flags & MF_MISSILE) )
			continue;

		if (mo->type == MT_PLASMA)
			continue;
            
		// update missile position every 50 tics
		if ( ((gametic + mo->netid) % 50) )
			continue;

		MSG_WriteByte (&cl->netbuf, svc_movemobj);
		MSG_WriteShort (&cl->netbuf, mo->netid);
		MSG_WriteLong (&cl->netbuf, mo->x);
		MSG_WriteLong (&cl->netbuf, mo->y);
		MSG_WriteLong (&cl->netbuf, mo->z);
    }


}


#define MAXCORPSES 15

//
// SV_RemoveCorpses
// Removes some corpses
//
void SV_RemoveCorpses (void)
{
	AActor *mo;
	int     corpses = 0;

	if (gametic%35)
	{
		return;
	}
	else
	{
		TThinkerIterator<AActor> iterator;
		while ( (mo = iterator.Next() ) )
		{
			if (mo->type == MT_PLAYER && (!mo->player || mo->health <=0) )
				corpses++;
		}
	}

	if (corpses > MAXCORPSES)
	{
		TThinkerIterator<AActor> iterator;
		while (corpses > MAXCORPSES && (mo = iterator.Next() ) )
		{
			if (mo->type == MT_PLAYER && !mo->player)
			{
				mo->Destroy();
				corpses--;
			}
		}
	}
}

//
// SV_SendGametic
// Sends gametic to calculate ping
//
void SV_SendGametic(client_t* cl)
{
	if ((gametic%100) != 0)
		return;

	MSG_WriteByte (&cl->reliablebuf, svc_svgametic);
	MSG_WriteLong (&cl->reliablebuf, gametic);
}

// calculates ping using gametic which was sent by SV_UpdatePing and
// current gametic
void SV_CalcPing(void)
{	
	int oldtic = MSG_ReadLong();

	int ping = (gametic - oldtic);
	if (ping > 255) ping = 255;
	players[parse_cl].ping = ping;
}

//
// SV_UpdatePing
// send pings to a client
//
void SV_UpdatePing(client_t* cl)
{
	if ((gametic%101) != 0)
		return;

	for (int j=0; j<MAXPLAYERS; j++)
	{
		if (cl->cls < cs_spawned)
			continue;

	    MSG_WriteByte(&cl->reliablebuf, svc_updateping);
	    MSG_WriteByte(&cl->reliablebuf, j);  // player
	    MSG_WriteByte(&cl->reliablebuf, players[j].ping);
	}
}


//
// SV_ChangePlayerTeam
//
void SV_ChangePlayerTeam(void)
{
	int player = MSG_ReadByte();
	team_t team = (team_t)MSG_ReadByte();
	AActor *mo = players[player].mo;

	P_DamageMobj (mo, NULL, NULL, mo->health+mo->info->spawnhealth, MOD_UNKNOWN, DMG_NO_INVUL|DMG_NO_ARMOR);
	players[player].team = team;

	if (netstate == NS_SERVER)
	{
		for (int i=0; i<MAXPLAYERS; i++)
		{
			client_t *cl = &clients[i];

			if (cl->cls < cs_spawned)
				continue;

			MSG_WriteByte(&cl->reliablebuf, svc_playerteam);
			MSG_WriteByte(&cl->reliablebuf, player);
			MSG_WriteByte(&cl->reliablebuf, (byte)team);
		}
	}

	if (team == TM_BLUE)
		SV_BroadcastPrintf (PRINT_HIGH, TEXTCOLOR_NORMAL "%s joined the " TEXTCOLOR_BLUE "%s" TEXTCOLOR_NORMAL " team\n", players[player].userinfo.netname, teams[TM_BLUE].name);
	else
		SV_BroadcastPrintf (PRINT_HIGH, TEXTCOLOR_RED "%s joined the " TEXTCOLOR_BLUE "%s" TEXTCOLOR_NORMAL " team\n", players[player].userinfo.netname, teams[TM_RED].name);

}

//
// [BC] SV_Suicide
//
void SV_Suicide ()
{
	int player = MSG_ReadByte();

	P_Suicide (&players[player]);
}

//
// SV_UpdateSectors
// Update doors, moving floors, ceilings etc...
//
void SV_UpdateSectors(client_t* cl)
{
	for (int s=0; s<numsectors; s++)
	{ 
		sector_t *sec = &sectors[s];
		int moving = (sec->floordata && sec->floordata->IsKindOf (RUNTIME_CLASS(DMover))) |
					((sec->ceilingdata && sec->ceilingdata->IsKindOf (RUNTIME_CLASS(DMover)))<<1);

		if (moving == 3)
		{
			MSG_WriteByte (&cl->netbuf, svc_sector);
			MSG_WriteShort (&cl->netbuf, s);
			MSG_WriteShort (&cl->netbuf, sec->floorheight>>FRACBITS);
			MSG_WriteShort (&cl->netbuf, sec->ceilingheight>>FRACBITS);
		}
		else if (moving & 1)
		{
			MSG_WriteByte (&cl->netbuf, svc_floorheight);
			MSG_WriteShort (&cl->netbuf, s);
			MSG_WriteShort (&cl->netbuf, sec->floorheight>>FRACBITS);
		}
		else if (moving & 2)
		{
			MSG_WriteByte (&cl->netbuf, svc_ceilingheight);
			MSG_WriteShort (&cl->netbuf, s);
			MSG_WriteShort (&cl->netbuf, sec->ceilingheight>>FRACBITS);
		}
	}
}

//
// SV_ClearClientsBPS
//
void SV_ClearClientsBPS(void)
{
	if (gametic % 35)
		return;

	for (int i=0; i<MAXPLAYERS; i++)
	{
		client_t *cl = &clients[i];

		if (cl->cls < cs_spawned)
			continue;

		cl->reliable_bps = 0;
		cl->unreliable_bps = 0;
	}
}


//
// SV_WriteCommands
//
void SV_WriteCommands(void)
{
	int        i, j;
	client_t  *cl;

	for (i=0; i<MAXPLAYERS; i++)
	{
		cl = &clients[i];

		if (cl->cls < cs_spawned)
			continue;
		
		// Don't need to update origin every tic.
		// The server sends origin and velocity of a 
		// player and the client always knows origin
		// on the next tic.
		if (gametic % 2)
			for (j=0; j<MAXPLAYERS; j++)
			{
				if (!playeringame[j] || j == i)
					continue;

				AActor *mobj = players[j].mo;

				byte bits = 0;

				MSG_WriteByte(&cl->netbuf, svc_moveplayer);
				MSG_WriteByte(&cl->netbuf, j);     // player number
				
				if (mobj->x != mobj->oldx)
					bits |= CM_X;
				if (mobj->y != mobj->oldy)
					bits |= CM_Y;
				if (mobj->z != mobj->oldz)
					bits |= CM_Z;
				if (mobj->angle != mobj->oldangle)
					bits |= CM_ANGLE;
				if (mobj->frame != mobj->oldframe)
					bits |= CM_FRAME;
				if (mobj->momx != mobj->oldmomx)
					bits |= CM_MOMX;
				if (mobj->momy != mobj->oldmomy)
					bits |= CM_MOMY;
				if (mobj->momz != mobj->oldmomz)
					bits |= CM_MOMZ;

				MSG_WriteByte(&cl->netbuf, bits);

				if (mobj->x != mobj->oldx)
					MSG_WriteLong(&cl->netbuf, mobj->x);				
				if (mobj->y != mobj->oldy)
					MSG_WriteLong(&cl->netbuf, mobj->y);
				if (mobj->z != mobj->oldz)
					MSG_WriteLong(&cl->netbuf, mobj->z);
				
				if (mobj->angle != mobj->oldangle)
					MSG_WriteLong(&cl->netbuf, mobj->angle);

				if (mobj->frame != mobj->oldframe)
				{
					if (mobj->frame == 32773)
						MSG_WriteByte(&cl->netbuf, PLAYER_FULLBRIGHTFRAME);
					else
						MSG_WriteByte(&cl->netbuf, mobj->frame);
				}

				// write velocity
				if (mobj->momx != mobj->oldmomx)
					MSG_WriteLong(&cl->netbuf, mobj->momx);
				if (mobj->momy != mobj->oldmomy)
					MSG_WriteLong(&cl->netbuf, mobj->momy);
				if (mobj->momz != mobj->oldmomz)
					MSG_WriteLong(&cl->netbuf, mobj->momz);

				MSG_WriteByte (&cl->netbuf, mobj->fixedcolormap);
			}


		SV_UpdateConsolePlayer(cl);

		SV_UpdateMissiles(cl);

        SV_UpdateSectors(cl); // update doors, moving floors, ceilings etc		                        

	 	SV_SendGametic(cl); // used to calculate a ping value,
		                    // when a client returns it
     	SV_UpdatePing(cl);
	}

	// save players' origin, velocity, frame and angle
	if (gametic % 2)
		for (i=0; i<MAXPLAYERS; i++)
		{
			if (!playeringame[i])
				continue;

			AActor *mobj = players[i].mo;
		
			mobj->oldx = mobj->x;
			mobj->oldy = mobj->y;
			mobj->oldz = mobj->z;
			mobj->oldangle = mobj->angle;
			mobj->oldframe = mobj->frame;
			mobj->oldmomx = mobj->momx;
			mobj->oldmomy = mobj->momy;
			mobj->oldmomz = mobj->momz;
		}
}

BEGIN_COMMAND (boot)
{
	if (netstate != NS_SERVER)
		return;

	if (argc < 2)
	{
		Printf (PRINT_HIGH, "Usage: boot <playername>\n");
		return;
	}

	for (int i=0; i<MAXPLAYERS; i++)
	{
		client_t *cl = &clients[i];

		if (cl->cls < cs_spawned)
			continue;

		if (!stricmp (players[i].userinfo.netname, argv[1]))
			break;
	}

	if (i == MAXPLAYERS)
	{
		Printf (PRINT_HIGH, "Unknown player: %s\n", argv[1]);
		return;
	}

	SV_BroadcastPrintf(PRINT_HIGH, "%s was given the boot!\n", argv[1]);
	SV_DropClient(i);
}
END_COMMAND (boot)

void SV_PlayerTriedToCheat()
{
	SV_BroadcastPrintf(PRINT_HIGH, "%s tried to cheat!\n", players[parse_cl].userinfo.netname);
	SV_DropClient(parse_cl);
}

/*
struct usercmd_s {
	byte	msec;			// not sure how to use this yet...
	byte	buttons;
	short	pitch;			// up/down. currently just a y-sheering amount
	short	yaw;			// left/right	// If you haven't guessed, I just
	short	roll;			// tilt			// ripped these from Quake2's usercmd.
	short	forwardmove;
	short	sidemove;
	short	upmove;
	byte	impulse;
	byte	use;
};
*/

void SV_GetPlayerCmd(void)
{
	client_t *cl = &clients[parse_cl];
	ticcmd_t *cmd = &players[parse_cl].cmd;
	int       tic;

    tic = MSG_ReadLong(); // get client gametic

	// get the previous cmds and angle
	if (  cl->lastcmdtic && (tic - cl->lastcmdtic >= 2) 
		&& gamestate != GS_INTERMISSION )
	{
		cmd->ucmd.buttons = MSG_ReadByte();
	    players[parse_cl].mo->angle = MSG_ReadShort() << 16;
		
		if (dmflags & DF_NO_FREELOOK || oldschool.value)
		{
			players[parse_cl].mo->pitch = 0;
			MSG_ReadShort();
		}
		else
			players[parse_cl].mo->pitch = MSG_ReadShort() << 16;

	    cmd->ucmd.forwardmove = MSG_ReadShort();
	    cmd->ucmd.sidemove = MSG_ReadShort();

		if ( abs(cmd->ucmd.forwardmove) > 12800 
			|| abs(cmd->ucmd.sidemove) > 12800)
		{
			SV_PlayerTriedToCheat();
			return;
		}

		cmd->ucmd.impulse = MSG_ReadByte();

		P_PlayerThink (&players[parse_cl]);
	    players[parse_cl].mo->RunThink();
	}
	else
	{
		// get 10 bytes
		MSG_ReadLong();
		MSG_ReadLong();
		MSG_ReadShort();
	}

	// get current cmds and angle
	cmd->ucmd.buttons = MSG_ReadByte();
	if (gamestate != GS_INTERMISSION)
	{
		players[parse_cl].mo->angle = MSG_ReadShort() << 16;
		
		if (dmflags & DF_NO_FREELOOK || oldschool.value)
		{
			players[parse_cl].mo->pitch = 0;
			MSG_ReadShort();
		}
		else
			players[parse_cl].mo->pitch = MSG_ReadShort() << 16;
	}
	else
		MSG_ReadLong();

	cmd->ucmd.forwardmove = MSG_ReadShort();
	cmd->ucmd.sidemove = MSG_ReadShort();
	
	cmd->ucmd.impulse = MSG_ReadByte();

	if (abs(cmd->ucmd.forwardmove) > 12800 || abs(cmd->ucmd.sidemove) > 12800)
	{
		SV_PlayerTriedToCheat();
		return;
	}
	

	if (gamestate != GS_INTERMISSION)
	{
		P_PlayerThink (&players[parse_cl]);
		players[parse_cl].mo->RunThink();
	}

	cl->lastcmdtic = tic;
}


void SV_UpdateConsolePlayer(client_t* cl)
{
	int        playernum;
	player_t  *player;

	// It's not a good idea to send 33 bytes every tic.
	if (gametic % 3)
		return;

	for (playernum=0; playernum<MAXPLAYERS; playernum++)
	{
		if (cl->cls < cs_spawned)
			continue;

		if (cl == &clients[playernum])
		{
			player = &players[playernum];
			break;
		}
	}
	
	if (!player)
	    return;
	
	// haven't got any cmds yet
	if (!cl->lastcmdtic)
		return;

	// client player will update his position if packets were missed
	MSG_WriteByte (&cl->netbuf, svc_updatelocalplayer);
	MSG_WriteLong (&cl->netbuf, cl->lastcmdtic);

	MSG_WriteLong (&cl->netbuf, player->mo->x);
	MSG_WriteLong (&cl->netbuf, player->mo->y);
	MSG_WriteLong (&cl->netbuf, player->mo->z);

	MSG_WriteLong (&cl->netbuf, player->mo->momx);
	MSG_WriteLong (&cl->netbuf, player->mo->momy);
	MSG_WriteLong (&cl->netbuf, player->mo->momz);

//	MSG_WriteShort (&cl->netbuf, player->mo->momx >> FRACBITS);
//	MSG_WriteShort (&cl->netbuf, player->mo->momy >> FRACBITS);
//	MSG_WriteShort (&cl->netbuf, player->mo->momz >> FRACBITS);
}


//
// SV_ParseCommands
//
void SV_ParseCommands(void)
{		
	 int  cmd;
	 char *str;
	 
	 while(1)	 
	 {  
 		if (msg_badread)
		{
            Printf (PRINT_HIGH, "SV_ParseCommands: badread\n");
          	SV_DropClient(parse_cl);
            break;
		}

		cmd = MSG_ReadByte();

		if (cmd == -1) // EOM
			break;

		if (clients[parse_cl].cls < cs_spawned)
			break;

	    
		switch(cmd)
		{
		case clc_disconnect:
			SV_DisconnectClient();
			return;

        case clc_userinfo:
			SV_SetupUserInfo();
			break;

		case clc_startchat:
			SV_StartChat();
			break;

		case clc_endchat:
			SV_EndChat();
			break;

		case clc_move:
			SV_GetPlayerCmd();
			break;

		case clc_svgametic:
			SV_CalcPing();
			break;

		case clc_changeplayerteam:
			SV_ChangePlayerTeam();
			break;

		case clc_suicide:
			SV_Suicide();
			break;

		case clc_netcmd:
			SV_NetCommand();
			break;

		case clc_rate:
			clients[parse_cl].rate = MSG_ReadLong();	
			break;

		case clc_ack:
			SV_AcknowledgePacket();
			break;
	
		case clc_rcon:
			str = MSG_ReadString();
			if (clients[parse_cl].allow_rcon)
				AddCommandString (str);
			break;

		case clc_rcon_password:
			str = MSG_ReadString();
			if (strlen(rcon_password.string) >= 5 && !strcmp(rcon_password.string, str) )
				clients[parse_cl].allow_rcon = true;
			break;
		
		default:			
			Printf(PRINT_HIGH, "SV_ParseCommands: Unknown client message: %d\n", cmd);
          	SV_DropClient(parse_cl);
            return;
		}	 
	}
}


//
// SV_RunTics
//
/*
void SV_RunTics (void)
{
    int             entertic;
    static int      oldentertics;
    int             realtics;

    // get real tics
    entertic = I_WaitForTic (oldentertics);
    realtics = entertic - oldentertics;
    oldentertics = entertic;

	realtics = realtics%BACKUPTICS;

	SV_GetPackets(); // get packets

	DObject::BeginFrame ();

    // run the realtics tics
    while (realtics--)
    {
		NetUpdate ();

        C_Ticker ();
        M_Ticker ();
        G_Ticker ();
		SV_UpdateMaster ();
		gametic++;
    }

	DObject::EndFrame ();

//	SV_Ticker ();
}
*/

void SV_RunTics (void)
{
	int              nowtime;
	int              newtics;

	I_DoSelect();

	nowtime = I_GetTime ();
        newtics = nowtime - gametime;

	// console input
	char *cmd = I_ConsoleInput();
	if (cmd)
		AddCommandString (cmd);

	SV_GetPackets(); // get packets

	if (newtics > 0)
	{
		DObject::BeginFrame ();

		// run the newtime tics
		while (newtics--)
		{
			C_Ticker ();
			G_Ticker ();
			SV_UpdateMaster();			
			gametic++;
		}

		DObject::EndFrame ();
	
		gametime = nowtime;
	}
}
