//-----------------------------------------------------------------------------
//
//  $Logfile:: /EF2/Code/DLLs/game/g_main.cpp                                $
// $Revision:: 104                                                            $
//   $Author:: Singlis                                                        $
//     $Date:: 9/26/03 2:36p                                                  $
//
// Copyright (C) 1998 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
//
// DESCRIPTION:
//
#include "_pch_cpp.h"

#define SAVEGAME_VERSION 8
#define PERSISTANT_VERSION 1

#include "g_utils.h"
#include "gamecmds.h"
#include "entity.h"
#include "vector.h"
#include "scriptmaster.h"
#include "navigate.h"
#include "player.h"
#include "gravpath.h"
#include "camera.h"
#include "level.h"
#include "viewthing.h"
#include "ipfilter.h"
#include <qcommon/alias.h>
#include "mp_manager.hpp"
#include <qcommon/gameplaymanager.h>
#include <qcommon/stringresource.hpp>
#include "teammateroster.hpp"

Vector			vec_origin = Vector(0, 0, 0);
Vector			vec_zero = Vector(0, 0, 0);

qboolean		LoadingSavegame = false;
qboolean		LoadingServer = false;
game_import_t	gi;
game_export_t	globals;

gentity_t		*g_entities = NULL;
gentity_t		active_edicts;
gentity_t		free_edicts;

Container<int>	extraEntitiesList;

int				sv_numtraces;

usercmd_t		*current_ucmd;

void (*ServerError )( int level, const char *fmt, ... );

/*
===============
G_Error

Abort the server with a game error
===============
*/
void G_Error( int level, const char *fmt, ... )
{
	va_list	argptr;
	char		error[ 4096 ];
	
	va_start( argptr, fmt );
	vsprintf( error, fmt, argptr );
	va_end( argptr );
	
	assert( 0 );
	
	throw error;
}

/*
===============
G_ExitWithError

Calls the server's error function with the last error that occurred.
Should only be called after an exception.
===============
*/
void G_ExitWithError( const char *error )
{
	static char G_ErrorMessage[ 4096 ];
	
	//ServerError( ERR_DROP, error );
	
	Q_strncpyz( G_ErrorMessage, error, sizeof( G_ErrorMessage ) );
	
	globals.error_message = G_ErrorMessage;
}

/*
=================
G_CleanupGame

Frees up resources from current level
=================
*/
extern "C" void G_CleanupGame( qboolean restart )
{
	try
	{
		gi.DPrintf ("==== CleanupGame ====\n");
		
		level.CleanUp( restart );
	}
	
	catch( const char *error )
	{
		G_ExitWithError( error );
	}
}

int BotAIShutdown( int restart );
/*
=================
G_ShutdownGame

Frees up any resources
=================
*/
extern "C" void G_ShutdownGame( void )
{
	try
	{
		gi.DPrintf ("==== ShutdownGame ====\n");
		
		// close the player log file if necessary
		ClosePlayerLogFile();
		
		// shut down bots
		if ( gi.Cvar_VariableIntegerValue( "bot_enable" ) ) {
			BotAIShutdown( qfalse ); // BOTLIB was restart
		}
		
		// clear out the behavior package list
		ClearBehaviorPackageList();
		
		level.CleanUp( false );
		
		L_ShutdownEvents();
		
		// destroy the game data
		G_DeAllocGameData();
		
		// Delete the GameplayManager
		GameplayManager::shutdown();
	}
	
	
	
	catch( const char *error )
	{
		G_ExitWithError( error );
	}
}

/*
============
G_InitGame

This will be called when the dll is first loaded, which
only happens when a new game is begun
============
*/
extern "C" void G_InitGame( int startTime, int randomSeed )
{
	gi.DPrintf ("==== InitGame ====\n");
	
	// Install our own error handler, since we can't
	// call the EXE's handler from within a C++ class
	ServerError = gi.Error;
	gi.Error    = G_Error;
	
	// If we get an error, call the server's error function
	try
	{
		srand( randomSeed );
		
		// setup all the cvars the game needs
		CVAR_Init();
		
		// Need to fill the BehaviorPackageList from the file
		FillBehaviorPackageList();
		
		// initialize the game variables
		gameVars.ClearList();
		level.intermission_advancetime= (float)g_intermissiontime->integer;
		level.fixedframetime = ( 1.0f / sv_fps->value );
		level.startTime = startTime;
		
		G_InitConsoleCommands();
		L_InitEvents();
		
		sv_numtraces = 0;
		
		// setup ViewMaster
		Viewmodel.Init();
		
		// Initialize deathmatch manager
		multiplayerManager.init();
		
		game.maxentities = maxentities->integer;
		if (maxclients->integer * 8 > game.maxentities)
		{
			game.maxentities = maxclients->integer * 8;
		}
		game.maxclients = maxclients->integer;
		
		GameplayManager::create();
		
		G_AllocGameData();
	}
	
	catch( const char *error )
	{
		G_ExitWithError( error );
	}
}

void G_AllocGameData( void )
{
	int i;
	
	// de-allocate from previous level
	G_DeAllocGameData();
	
	// Initialize debug lines
	G_AllocDebugLines();
	
	// initialize all entities for this game
	game.maxentities     = maxentities->integer;
	g_entities           = ( gentity_t * )gi.Malloc (game.maxentities * sizeof(g_entities[0]) );
	
	// clear out the entities
	memset( g_entities, 0, sizeof( g_entities[ 0 ] ) * game.maxentities );
	globals.gentities    = g_entities;
	globals.max_entities = game.maxentities;
	
	// Add all the edicts to the free list
	LL_Reset( &free_edicts, next, prev );
	LL_Reset( &active_edicts, next, prev );
	for( i = 0; i < game.maxentities; i++ )
	{
		LL_Add( &free_edicts, &g_entities[ i ], next, prev );
	}
	
	// initialize all clients for this game
	game.clients = ( gclient_t * )gi.Malloc( game.maxclients * sizeof( game.clients[ 0 ] ) );
	memset( game.clients, 0, game.maxclients * sizeof( game.clients[ 0 ] ) );
	for( i = 0; i < game.maxclients; i++ )
	{
		// set client fields on player ents
		g_entities[ i ].client = game.clients + i;
		
		G_InitClientPersistant (&game.clients[i]);

		playersLastTeam[ i ] = "";
	}
	globals.num_entities = game.maxclients;
	
	// Tell the server about our data
	gi.LocateGameData( g_entities, globals.num_entities, sizeof( gentity_t ), &game.clients[0].ps, sizeof( game.clients[0] ) );
	
	if ( gi.Cvar_VariableIntegerValue( "bot_enable" ) ) {
		BotAISetup( 0);//restart );
		BotAILoadMap( 0);//restart );
		G_InitBots( 0);//restart );
	}
	
}

void G_DeAllocGameData( void )
{
	// Initialize debug lines
	G_DeAllocDebugLines();
	
	// free up the entities
	if ( g_entities )
	{
		gi.Free( g_entities );
		g_entities = NULL;
	}
	
	// free up the clients
	if ( game.clients )
	{
		gi.Free( game.clients );
		game.clients = NULL;
	}
}

extern "C" void G_SpawnEntities( const char *mapname, const char *entities, int levelTime )
{
	try
	{
		level.NewMap( mapname, entities, levelTime );
	}
	
	catch( const char *error )
	{
		G_ExitWithError( error );
	}
}

//----------------------------------------------------------------
// Name:			G_PostLoad
// Class:			
//
// Description:		Does everything necessary after a load has happened
//
// Parameters:		None
//
// Returns:			none
//----------------------------------------------------------------

extern "C" void G_PostLoad( void )
{
	try
	{
		level.postLoad();
	}

	catch( const char *error )
	{
		G_ExitWithError( error );
	}
}

//----------------------------------------------------------------
// Name:			G_PostSublevelLoad
// Class:			
//
// Description:		Does everything necessary after a sublevel has beend loaded
//
// Parameters:		const char *mapName		- map name string (also possibly contains spawn position)
//
// Returns:			none
//----------------------------------------------------------------

extern "C" void G_PostSublevelLoad( const char *spawnPosName )
{
	try
	{
		level.postSublevelLoad( spawnPosName );
	}

	catch( const char *error )
	{
		G_ExitWithError( error );
	}
}

void G_ArchivePersistantData( Archiver &arc, qboolean sublevelTransition )
{
	gentity_t   *ed;
	int         i;
	
	for( i = 0; i < game.maxclients; i++ )
	{
		Entity   *ent;
		
		ed = &g_entities[ i ];
		if ( !ed->inuse || !ed->entity )
			continue;
		
		ent = ed->entity;
		if ( !ent->isSubclassOf( Player ) )
			continue;
		( ( Player * )ent )->ArchivePersistantData( arc, sublevelTransition );
	}
}

qboolean G_ArchivePersistant( const char *name, qboolean loading, qboolean sublevelTransition )
{
	int version;
	Archiver arc;
	
	if ( loading )
	{
		if ( !arc.Read( name, false ) )
		{
			return false;
		}
		
		arc.ArchiveInteger( &version );
		if ( version < PERSISTANT_VERSION )
		{
			gi.Printf( "Persistant data from an older version (%d) of %s.\n", version, GAME_NAME );
			arc.Close();
			return false;
		}
		else if ( version > PERSISTANT_VERSION )
		{
			gi.DPrintf( "Persistant data from newer version %d of %s.\n", version, GAME_NAME );
			arc.Close();
			return false;
		}
	}
	else
	{
		arc.Create( name );
		
		version = PERSISTANT_VERSION;
		arc.ArchiveInteger( &version );
	}
	
	
	arc.ArchiveObject( &gameVars );
	G_ArchivePersistantData( arc, sublevelTransition );
	
	arc.Close();
	return true;
}


extern "C" qboolean G_ReadPersistant( const char *name, qboolean sublevelTransition )
{
	try
	{
		return G_ArchivePersistant( name, true, sublevelTransition );
	}
	
	catch( const char *error )
	{
		G_ExitWithError( error );
	}
	return false;
}

/*
============
G_WritePersistant

This will be called whenever the game goes to a new level,

A single player death will automatically restore from the
last save position.
============
*/

extern "C" void G_WritePersistant( const char *name, qboolean sublevelTransition )
{
	try
	{
		G_ArchivePersistant( name, false, sublevelTransition );
	}
	
	catch( const char *error )
	{
		G_ExitWithError( error );
	}
}


/*
=================
LevelArchiveValid
=================
*/
qboolean LevelArchiveValid( Archiver &arc )
{
	int      version;
	int      savegame_version;
	
	// read the version number
	arc.ArchiveInteger( &version );
	arc.ArchiveInteger( &savegame_version );
	
	if ( version < GAME_API_VERSION )
	{
		gi.Printf( "Savegame from an older version (%d) of %s.\n", version, GAME_NAME );
		return false;
	}
	else if ( version > GAME_API_VERSION )
	{
		gi.Printf( "Savegame from version %d of %s.\n", version, GAME_NAME );
		return false;
	}
	
	if ( savegame_version < SAVEGAME_VERSION )
	{
		gi.Printf( "Savegame from an older version (%d) of %s.\n", version, GAME_NAME );
		return false;
	}
	else if ( savegame_version > SAVEGAME_VERSION )
	{
		gi.Printf( "Savegame from version %d of %s.\n", version, GAME_NAME );
		return false;
	}
	return true;
}

str getAliasParmString( AliasListNode_t *aliasNode )
{
	str parms;

	if ( aliasNode->global_flag )
	{
		parms += "global ";
	}

	if ( aliasNode->stop_flag )
	{
		parms += "stop ";
	}

	if ( aliasNode->timeout > 0.0f )
	{
		parms += "timeout ";
		parms += aliasNode->timeout;
		parms += " ";
	}

	if ( aliasNode->maximum_use > 0 )
	{
		parms += "maxuse ";
		parms += aliasNode->maximum_use;
		parms += " ";
	}

	if ( aliasNode->weight > 0 )
	{
		parms += "weight ";
		parms += aliasNode->weight;
		parms += " ";
	}

	if ( strlen( aliasNode->anim_name ) > 0 )
	{
		parms += "anim ";
		parms += aliasNode->anim_name;
		parms += " ";
	}

	if ( aliasNode->loop_anim )
	{
		parms += "loopanim 1 ";
	}

	return parms;
}

void ArchiveAliases( Archiver &arc )
{
	int i;
	byte another;
	AliasList_t *alias_list;
	AliasListNode_t *alias_node;
	AliasActorNode_t *actor_node;
	str alias_name;
	str realName;
	str model_name;
	const char *name;
	int model_index;
	int actor_number;
	int number_of_times_played;
	byte been_played_this_loop;
	int last_time_played;
	
	
	if ( arc.Saving() )
	{
		for( i = 0 ; i < MAX_MODELS ; i++ )
		{
			alias_list = (AliasList_t *)gi.Alias_GetList( i );
			
			if ( alias_list )
				alias_node = alias_list->data_list;
			else
				alias_node = NULL;
			
			if ( alias_node )
			{
				name = gi.NameForNum( i );
				
				if ( name )
				{
					another = true;
					arc.ArchiveByte( &another );
					
					model_name = name;
					arc.ArchiveString( &model_name );
					
					// Go through all aliases in this model
					
					while ( alias_node )
					{
						str parms;

						another = true;
						arc.ArchiveByte( &another );
						
						alias_name = alias_node->alias_name;
						realName = alias_node->real_name;
						
						arc.ArchiveString( &alias_name );

						arc.ArchiveString( &realName );

						parms = getAliasParmString( alias_node );

						arc.ArchiveString( &parms );
						
						arc.ArchiveInteger( &alias_node->number_of_times_played );
						arc.ArchiveByte( &alias_node->been_played_this_loop );
						arc.ArchiveInteger( &alias_node->last_time_played );
						
						actor_node = alias_node->actor_list;
						
						// Go through actor info
						
						while ( actor_node )
						{
							another = true;
							arc.ArchiveByte( &another );
							
							arc.ArchiveInteger( &actor_node->actor_number );
							arc.ArchiveInteger( &actor_node->number_of_times_played );
							arc.ArchiveByte( &actor_node->been_played_this_loop );
							arc.ArchiveInteger( &actor_node->last_time_played );
							
							actor_node = actor_node->next;
						}
						
						another = false;
						arc.ArchiveByte( &another );
						
						alias_node = alias_node->next;
					}
					
					another = false;
					arc.ArchiveByte( &another );
				}
			}
		}
		
		another = false;
		arc.ArchiveByte( &another );
	}
	else
	{
		arc.ArchiveByte( &another );
		
		while( another )
		{
			arc.ArchiveString( &model_name );
			
			model_index = gi.modelindex( model_name.c_str() );
			
			arc.ArchiveByte( &another );
			
			while( another )
			{
				str parms;

				// Read in aliases
				
				arc.ArchiveString( &alias_name );

				arc.ArchiveString( &realName );
				arc.ArchiveString( &parms );

				arc.ArchiveInteger( &number_of_times_played );
				arc.ArchiveByte( &been_played_this_loop );
				arc.ArchiveInteger( &last_time_played );
				
				// If the alias doesn't exist yet, add it

				if ( !gi.Alias_Find( model_index, alias_name.c_str() ) )
				{
					gi.Alias_Add( model_index, alias_name, realName, parms );
				}

				// Now update the alias

				gi.Alias_UpdateDialog( model_index, alias_name.c_str(), number_of_times_played, been_played_this_loop, last_time_played );
				
				arc.ArchiveByte( &another );
				
				while( another )
				{
					// Read in actor infos
					
					arc.ArchiveInteger( &actor_number );
					arc.ArchiveInteger( &number_of_times_played );
					arc.ArchiveByte( &been_played_this_loop );
					arc.ArchiveInteger( &last_time_played );
					
					gi.Alias_AddActorDialog( model_index, alias_name.c_str(), actor_number, number_of_times_played, been_played_this_loop, last_time_played );
					
					arc.ArchiveByte( &another );
				}
				
				arc.ArchiveByte( &another );
			}
			
			arc.ArchiveByte( &another );
		}
	}
}

/*
=================
G_ArchiveLevel

=================
*/
qboolean G_ArchiveLevel( const char *filename, qboolean autosave, qboolean loading )
{
	try
	{
		int		i;
		int      num;
		Archiver arc;
		gentity_t  *edict;
		
		// Heuristic Stuff /////////////////////////////////////
		edict = &g_entities[ 0 ];
		
		if ( edict->inuse && edict->entity )
		{
			Player *player = (Player *)edict->entity;
			if ( player->p_heuristics )
				player->p_heuristics->SaveHeuristics( player );			
		}
		//////////////////////////////////////////////
		
		
		if ( loading )
		{
			LoadingSavegame = true;
			LoadingServer = true;
			
			// Get rid of anything left over from the last level
			level.CleanUp( false );
			
			arc.Read( filename );
			
			if ( !LevelArchiveValid( arc ) )
            {
				arc.Close();
				return false;
            }
			
			// Read in the pending events.  These are read in first in case
			// later objects need to post events.
			L_UnarchiveEvents( arc );
		}
		else
		{
			int temp;
			
			if ( autosave )
            {
				for( i = 0; i < game.maxclients; i++ )
				{
					edict = &g_entities[ i ];
					if ( !edict->inuse && !edict->entity )
					{
						continue;
					}
					
					delete edict->entity;
				}
            }
			
			arc.Create( filename );
			
			// write out the version number
			temp = GAME_API_VERSION;
			arc.ArchiveInteger( &temp );
			temp = SAVEGAME_VERSION;
			arc.ArchiveInteger( &temp );
			
			
			
			// Write out the pending events.  These are written first in case
			// later objects need to post events when reading the archive.
			L_ArchiveEvents( arc );
		}
		// archive the game object
		arc.ArchiveObject( &game );
		
		// archive the game variables
		arc.ArchiveObject( &gameVars );
		
		// archive Level
		arc.ArchiveObject( &level );
		
		if ( arc.Loading() )
		{
			// Set up for a new map
			thePathManager.Init( level.mapname.c_str() );
		}
		
		// archive script librarian
		arc.ArchiveObject( &ScriptLib );
		
		// archive gravity paths
		arc.ArchiveObject( &gravPathManager );
		
		// archive camera paths
		arc.ArchiveObject( &CameraMan );
		
		// archive paths
		arc.ArchiveObject( &thePathManager );
		
		// archive script controller
		arc.ArchiveObject( &Director );
		
		// archive lightstyles
		arc.ArchiveObject( &lightStyles );

		// Archive the level vars

		if ( arc.Loading() )
		{
			levelVars.ClearList();
		}

		arc.ArchiveObject( &levelVars );
		
		if ( arc.Saving() )
		{
			// count the entities
			num = 0;
			for( i = 0; i < globals.num_entities; i++ )
			{
				edict = &g_entities[ i ];
				if ( edict->inuse && edict->entity && !( edict->entity->flags & FL_DONTSAVE ) )
				{
					num++;
				}
            }
		}
		
		// archive all the entities
		arc.ArchiveInteger( &globals.num_entities );
		arc.ArchiveInteger( &num );
		
		if ( arc.Saving() )
		{
			// write out the world
			arc.ArchiveObject( world );
			
			for( i = 0; i < globals.num_entities; i++ )
			{
				edict = &g_entities[ i ];
				if ( !edict->inuse || !edict->entity || ( edict->entity->flags & FL_DONTSAVE ) )
				{
					continue;
				}
				
				arc.ArchiveObject( edict->entity );
			}
		}
		else
		{
			// Tell the server about our data
			gi.LocateGameData( g_entities, globals.num_entities, sizeof( gentity_t ), &game.clients[0].ps, sizeof( game.clients[0] ) );
			
			// read in the world
			arc.ReadObject();
			
			for( i = 0; i < num; i++ )
			{
				arc.ReadObject();
			}
		}
		
		if ( arc.Loading() )
			program.FreeData();
		
		arc.ArchiveObject( &program );
		
		// archive script controller
		//arc.ArchiveObject( &Director );
		
		ArchiveAliases( arc );
		
		//Archive the teammate roster
		TeammateRoster::getInstance()->Archive( arc );
		
		// Archive the gameplay manager's database deltas.
		GameplayManager::getTheGameplayManager()->Archive( arc );
		
		arc.Close();
		
		if ( arc.Loading() )
		{
			LoadingSavegame = false;
			
			// call the precache scripts
			level.Precache();
		}
		else
		{
			gi.centerprintf( &g_entities[ 0 ], CENTERPRINT_IMPORTANCE_NORMAL, "@textures/menu/gamesaved" );
		}
		
		if ( arc.Loading() )
		{
			// Make sure all code that needs to setup the player after they have been loaded is run
			
			for( i = 0; i < game.maxclients; i++ )
			{
				edict = &g_entities[ i ];
				
				if ( edict->inuse && edict->entity )
				{
					Player *player = (Player *)edict->entity;
					player->Loaded();
				}
			}
		}
		
		return true;
	}
	  
	catch( const char *error )
	{
		G_ExitWithError( error );
	}
	return false;
}

/*
=================
G_WriteLevel

=================
*/
extern "C" void G_WriteLevel( const char *filename, qboolean autosave )
{
	game.autosaved = autosave;
	G_ArchiveLevel( filename, autosave, false );
	game.autosaved = false;
}

/*
=================
G_ReadLevel

SpawnEntities will already have been called on the
level the same way it was when the level was saved.

That is necessary to get the baselines set up identically.

The server will have cleared all of the world links before
calling ReadLevel.

No clients are connected yet.
=================
*/
extern "C" qboolean G_ReadLevel( const char *filename )
{
	qboolean status;
	
	status = G_ArchiveLevel( filename, false, true );
	// if the level load failed make sure that these variables are not set
	if ( !status )
	{
		LoadingSavegame = false;
		LoadingServer = false;
	}
	return status;
}

/*
=================
G_LevelArchiveValid
=================
*/
extern "C" qboolean G_LevelArchiveValid( const char *filename )
{
	try
	{
		qboolean ret;
		
		Archiver arc;
		
		if ( !arc.Read( filename ) )
		{
			return false;
		}
		
		ret = LevelArchiveValid( arc );
		
		arc.Close();
		
		return ret;
	}
	
	catch( const char *error )
	{
		G_ExitWithError( error );
		return false;
	}
}

extern "C" qboolean G_InMultiplayerGame( void )
{
	return multiplayerManager.inMultiplayer();
}

// BOTLIB additions
void SV_AddBot_f( void );
int BotAIStartFrame(int time);
//

/*
=================
GetGameAPI

Returns a pointer to the structure with all entry points
and global variables
=================
*/
extern "C" game_export_t *GetGameAPI( game_import_t *import )
{
	gi = *import;
	
	globals.apiversion					= GAME_API_VERSION;
	globals.Init						= G_InitGame;
	globals.Shutdown					= G_ShutdownGame;
	globals.Cleanup						= G_CleanupGame;
	globals.SpawnEntities				= G_SpawnEntities;
	globals.PostLoad					= G_PostLoad;
	globals.PostSublevelLoad			= G_PostSublevelLoad;
	
	globals.WritePersistant				= G_WritePersistant;
	globals.ReadPersistant				= G_ReadPersistant;
	globals.WriteLevel					= G_WriteLevel;
	globals.ReadLevel					= G_ReadLevel;
	globals.LevelArchiveValid			= G_LevelArchiveValid;
	
	globals.inMultiplayerGame			= G_InMultiplayerGame;
	
	globals.ClientThink					= G_ClientThink;
	globals.ClientConnect				= G_ClientConnect;
	globals.ClientUserinfoChanged		= G_ClientUserinfoChanged;
	globals.ClientDisconnect			= G_ClientDisconnect;
	globals.ClientBegin					= G_ClientBegin;
	globals.ClientCommand				= G_ClientCommand;
	
	globals.ConsoleCommand				= G_ConsoleCommand;
	globals.RunFrame					= G_RunFrame;
	globals.SendEntity					= G_SendEntity;
	globals.UpdateEntityStateForClient	= G_UpdateEntityStateForClient;
	globals.UpdatePlayerStateForClient	= G_UpdatePlayerStateForClient;
	globals.ExtraEntitiesToSend			= G_ExtraEntitiesToSend;
	
	globals.GetEntityCurrentAnimFrame	= G_GetEntityCurrentAnimFrame;
	
	// the following are added for BOTLIB support
	globals.AddBot_f					= SV_AddBot_f;
	globals.BotAIStartFrame				= BotAIStartFrame;
	// end botlib additions
	
	globals.GetTotalGameFrames			= G_GetTotalGameFrames;
	
	globals.gentitySize					= sizeof(gentity_t);
	globals.error_message				= NULL;
	
	return &globals;
}

/*
=================
G_ClientEndServerFrames
=================
*/
void G_ClientEndServerFrames( void )
{
	int		i;
	gentity_t  *ent;
	
	// calc the player views now that all pushing
	// and damage has been added
	for( i = 0; i < maxclients->integer; i++ )
	{
		ent = g_entities + i;
		if ( !ent->inuse || !ent->client || !ent->entity )
		{
			continue;
		}
		
		ent->entity->ProcessEvent( EV_ClientEndFrame );
	}
}

//==================
//FindIntermissionPoint
//==================
void FindIntermissionPoint( void )
{
	Entity   *ent, *target;
	vec3_t	dir;
	
	// find the intermission spot
	ent = G_FindClass( NULL, "info_player_intermission" );
	
	if ( ent )
	{
		level.m_intermission_origin = ent->origin;
		level.m_intermission_angle  = ent->angles;
		
		// if it has a target, look towards it
		if ( ent->target )
		{
			target = G_FindTarget( NULL, ent->Target() );
			if ( target )
            {
				VectorSubtract( target->origin, level.m_intermission_origin, dir );
				vectoangles( dir, level.m_intermission_angle );
			}
		}
	   }
	else
	{
		level.m_intermission_origin = vec_zero;
		level.m_intermission_angle  = vec_zero;
	}
}

void G_MoveClientToIntermission( Entity *ent )
{
	assert( ent );
	
	if ( !ent )
		return;
	
	if ( ent->isClient() )
	{
		Player *player = ( Player * )ent;

		if ( multiplayerManager.inMultiplayer() && ( level.m_intermission_angle != vec_zero ) && ( level.m_intermission_origin != vec_zero ) )
		{
			// Each client should now be at the intermission spot
			player->setOrigin( level.m_intermission_origin );
			player->setAngles( level.m_intermission_angle  );
			player->SetViewAngles( level.m_intermission_angle  );
			player->CameraCut();
		}
		
		// Set a PMF flag to allow them to see the scoreboard
		player->client->ps.pm_flags |= PMF_INTERMISSION;
		player->flags |= FL_IMMOBILE;
	}
}

// This is for multiplayer exiting
void G_BeginIntermission2( void )
{
	gentity_t   *client;
	Entity      *ent;
	int         i;
	
	if ( level.intermissiontime )
	{
		// already activated
		return;
	}
	
	// Save the time intermission started
	level.intermissiontime = level.time;
	// Freeze all players
	level.playerfrozen = true;
	// Find an intermission spot
	FindIntermissionPoint();
	
	// find an intermission spot
	ent = G_FindClass( NULL, "info_player_intermission" );
	
	// Only do the camera stuff if the node exists.
	if ( ent )
	{
		SetCamera( ent, CAMERA_SWITCHTIME );
	}
	
	// Display scores for all the clients
	for( i = 0; i < maxclients->integer; i++ )
	{
		client = g_entities + i;
		if ( !client->inuse )
		{
			continue;
		}
		
		ent = G_GetEntity( client->s.number );
		G_MoveClientToIntermission( ent );
	}
	
	// tell the script that the player's not ready so that if we return to this map,
	// we can do something about it.
	Director.PlayerNotReady();
}

void G_BeginIntermission( const char *map )
{
	gentity_t   *client;
	Entity      *ent;
	Entity      *path;
	int         i;
	Event       *event;
	
	assert( map );
	if ( !map )
	{
		gi.WDPrintf( "G_BeginIntermission : Null map name\n" );
		return;
	}
	
	if ( level.intermissiontime )
	{
		// already activated
		return;
	}
	
	
	if ( multiplayerManager.inMultiplayer() )
	{
		level.intermissiontime = level.time + mp_intermissionTime->integer;
	}
	else
	{
		level.intermissiontime = level.time + 1.0f;
		G_FadeOut( 1.0f );
		G_FadeSound( 1.0f );
	}
	
	level.nextmap = map;
	
	if ( gi.areSublevels( level.mapname, map ) || !level._showIntermission )
	{
		// We don't want a intermission (sublevel or something has specified no intermission )
		
		level.intermissiontime = level.time;
		level.exitintermission = true;
		
		Director.PlayerNotReady();
		
		return;
	}
	
	// find an intermission spot
	ent = G_FindClass( NULL, "info_player_intermission" );
	
	// Only do the camera stuff if the node exists.
	if ( ent )
	{
		SetCamera( ent, CAMERA_SWITCHTIME );
		event = new Event( EV_Camera_Orbit );
		
		// Find the end node
		path = G_FindTarget( NULL, "endnode1" );
		if ( path )
		{
			event->AddEntity( path );
			ent->ProcessEvent( event );
			event = new Event( EV_Camera_Cut );
			ent->ProcessEvent( event );
		}
	}
	
	// Display scores for all the clients
	for( i = 0; i < maxclients->integer; i++ )
	{
		client = g_entities + i;
		if ( !client->inuse )
		{
			continue;
		}
		
		ent = G_GetEntity( client->s.number );
		G_MoveClientToIntermission( ent );
	}
	
	// tell the script that the player's not ready so that if we return to this map,
	// we can do something about it.
	Director.PlayerNotReady();
}

/*
=============
G_ExitLevel
=============
*/
void G_ExitLevel( void )
{
	static const char *seps = " ,\n\r";
	char command[ 256 ];
	int j;
	gentity_t *ent;
	

	// Don't allow exit level if the mission was failed

	if ( level.mission_failed )
		return;

	// close the player log file if necessary
	ClosePlayerLogFile();
	
	// kill the sounds
	Com_sprintf( command, sizeof( command ), "stopsound\n" );
	gi.SendConsoleCommand( command );
	
	if ( multiplayerManager.inMultiplayer() )
	{
		if ( strlen( sv_nextmap->string ) )
		{
			// The nextmap cvar was set (possibly by a vote - so go ahead and use it)
			level.nextmap = sv_nextmap->string;
			gi.cvar_set( "nextmap", "" );
		}
		/* if ( strlen( sv_maplist->string ) ) // Use the next map in the maplist
		{
			char *s,*f,*t;
			
			f = NULL;
			s = strdup( sv_maplist->string );
			t = strtok( s, seps );
			while ( t != NULL )
            {
				if ( !stricmp( t, level.mapname.c_str() ) )
				{
					// it's in the list, go to the next one
					t = strtok( NULL, seps );
					if ( t == NULL ) // end of list, go to first one
					{
						if ( f == NULL ) // there isn't a first one, same level
						{
							level.nextmap = level.mapname;
						}
						else
						{
							level.nextmap = f;
						}
					}
					else
					{
						level.nextmap = t;
					}
					free( s );
					goto out;
				}
				
				// set the first map
				if (!f)
				{
					f = t;
				}
				t = strtok(NULL, seps);
            }
			free( s );
		} 
out: */
		// level.nextmap should be set now, but if it isn't use the same map
		if ( level.nextmap.length() == 0 )
		{
			// Stay on the same map since no nextmap was set
			Com_sprintf( command, sizeof( command ), "gamemap \"%s\"\n", level.mapname.c_str() );
			gi.SendConsoleCommand( command );
		}
		else // use the level.nextmap variable
		{
			Com_sprintf( command, sizeof( command ), "gamemap \"%s\"\n", level.nextmap.c_str() );
			gi.SendConsoleCommand( command );
		}
	}
	else
	{
		Com_sprintf( command, sizeof( command ), "gamemap \"%s\"\n", level.nextmap.c_str() );
		gi.SendConsoleCommand( command );
	}
	
	// Tell all the clients that the level is done
	for( j = 0; j < game.maxclients; j++ )
	{
		ent = &g_entities[ j ];
		if ( !ent->inuse || !ent->entity )
		{
			continue;
		}
		
		ent->entity->ProcessEvent( EV_Player_EndLevel );
	}

	level.nextmap = "";
	
	level.exitintermission = 0;
	level.intermissiontime = 0;
	
	G_ClientEndServerFrames();
	
	// tell the script that the player's not ready so that if we return to this map,
	// we can do something about it.
	Director.PlayerNotReady();
}

void G_CheckIntermissionExit( void )
{
	// Nobody clicked
	if ( !level.exitintermission )
	{
		return;
	}
	
	G_ExitLevel();
}

/*
================
G_RunFrame

Advances the world by one server frame
================
*/
extern "C" void G_RunFrame( int levelTime, int frameTime )
{
	gentity_t  *edict;
	Entity	*ent;
	int		num;
	qboolean showentnums;
	qboolean showactnums;
	int      start;
	int      end;
	
	try
	{
		if ( level.restart )
		{
			level.Restart();
		}
		
		level.update(levelTime, frameTime);
		
		if ( g_showmem->integer )
		{
			DisplayMemoryUsage();
		}
		
		// exit intermissions
		if ( level.exitintermission )
		{
			G_ExitLevel();
			return;
		}
		
		// Reset debug lines
		G_InitDebugLines();
		
		// testing coordinate system
		if ( csys_draw->integer )
		{
			G_DrawCSystem();
		}
		
		thePathManager.ShowNodes();
		
		AI_DisplayInfo();
		
		// don't show entnums during deathmatch
		showentnums = ( sv_showentnums->integer && ( !multiplayerManager.inMultiplayer() || sv_cheats->integer ) );
		showactnums = ( sv_showactnums->integer && ( !multiplayerManager.inMultiplayer() || sv_cheats->integer ) );
		
		// Wake up any monsters in the area
		AI_SenseEnemies();
		
		// Process most of the events before the physics are run
		// so that we can affect the physics immediately
		L_ProcessPendingEvents();
		
		//
		// treat each object in turn
		//
		for( edict = active_edicts.next, num = 0; edict != &active_edicts; edict = level.next_edict, num++ )
		{
			assert( edict );
			assert( edict->inuse );
			assert( edict->entity );
			
			level.next_edict = edict->next;
			
			// Paranoia - It's a way of life
			assert( num <= MAX_GENTITIES );
			if ( num > MAX_GENTITIES )
            {
				gi.WDPrintf( "Possible infinite loop in G_RunFrame.\n");
				break;
            }
			
			ent = edict->entity;
			if ( g_timeents->integer )
            {
				start = gi.Milliseconds();
				G_RunEntity( ent );
				end = gi.Milliseconds();
				
				if ( g_timeents->value <= (float)( end - start ) )
				{
					gi.DPrintf( "%d: '%s'(%d) : %d\n", level.framenum, ent->targetname.c_str(), ent->entnum, end - start );
				}
            }
			else
            {
				G_RunEntity( ent );
            }
			
			if ( ( edict->svflags & SVF_SENDONCE ) && ( edict->svflags & SVF_SENT ) )
            {
				// Entity has been sent once, and is marked as such, then remove it
				ent->PostEvent( EV_Remove, 0.0f );
            }
			
			if ( showentnums )
			{
				G_DrawDebugNumber( ent->origin + Vector( 0.0f, 0.0f, ent->maxs.z + 2.0f ), ent->entnum, 2.0f, 1.0f, 1.0f, 0.0f );
			}
			if ( showactnums && ent->isSubclassOf( Actor ) )
			{
				G_DrawDebugNumber( ent->origin + Vector( 0.0f, 0.0f, ent->maxs.z + 2.0f ), ent->entnum, 2.0f, 1.0f, 0.0f, 0.0f );
			}
		}
		
		multiplayerManager.update( level.frametime );
		
		// Process any pending events that got posted during the physics code.
		L_ProcessPendingEvents();
		
		// build the playerstate_t structures for all players
		G_ClientEndServerFrames();
		
		// see if we should draw the bounding boxes
		G_ClientDrawBoundingBoxes();
		
		// see if we should draw all of the splines 
		G_ClientDrawSplines();
		
		// show how many traces the game code is doing
		if ( sv_traceinfo->integer )
		{
			if ( sv_traceinfo->integer == 3 )
			{
				gi.DebugPrintf( "%0.2f : Total traces %d\n", level.time, sv_numtraces );
			}
			else
			{
				gi.DPrintf( "%0.2f : Total traces %d\n", level.time, sv_numtraces );
			}
		}
		
		// reset out count of the number of game traces
		sv_numtraces = 0;
		
		level.framenum++;
		
		// we increment time so that events that occurr before we think again are automatically on the next frame
		levelTime += frameTime;
		level.setTime( levelTime, frameTime );
	}
	  
	catch( const char *error )
	{
		G_ExitWithError( error );
	}
}

extern "C" qboolean G_SendEntity( gentity_t *clientEntity, gentity_t *entityToSend )
{
	try
	{
		if ( clientEntity->entity && clientEntity->entity->isSubclassOf( Player ) && entityToSend->entity )
		{
			Player *player;
			
			player = ( Player *)clientEntity->entity;
			
			return player->ShouldSendToClient( entityToSend->entity );
		}
	}
	
	catch( const char *error )
	{
		G_ExitWithError( error );
	}
	
	return false;
}

extern "C" void G_UpdateEntityStateForClient( gentity_t *clientEntity, entityState_t *state )
{
	try
	{
		if ( clientEntity->entity && clientEntity->entity->isSubclassOf( Player ) )
		{
			Player *player;
			
			player = ( Player *)clientEntity->entity;
			
			player->UpdateEntityStateForClient( state );
		}
	}
	
	catch( const char *error )
	{
		G_ExitWithError( error );
	}
	
}

extern "C" void G_UpdatePlayerStateForClient( gentity_t *clientEntity, playerState_t *state )
{
	try
	{
		if ( clientEntity->entity && clientEntity->entity->isSubclassOf( Player ) )
		{
			Player *player;
			
			player = ( Player *)clientEntity->entity;
			
			player->UpdatePlayerStateForClient( state );
		}
	}
	
	catch( const char *error )
	{
		G_ExitWithError( error );
	}
	
}

extern "C" void G_ExtraEntitiesToSend( gentity_t *clientEntity, int *numExtraEntities, int *extraEntities )
{
	try
	{
		*numExtraEntities = 0;
		
		if ( !multiplayerManager.inMultiplayer() && clientEntity->entity && clientEntity->entity->isSubclassOf( Player ) )
		{
			Player *player;
			
			player = ( Player *)clientEntity->entity;
			
			player->ExtraEntitiesToSendToClient( numExtraEntities, extraEntities );
			
			//add the extra entities from the extra entity list...
			for(int i = 1; i <= extraEntitiesList.NumObjects(); i++)
			{
				extraEntities[*numExtraEntities] = extraEntitiesList.ObjectAt(i);
				(*numExtraEntities)++;
			}
		}
	}
	
	catch( const char *error )
	{
		G_ExitWithError( error );
	}
}

void G_AddEntityToExtraList( int entityNum )
{
	extraEntitiesList.AddUniqueObject( entityNum );
}


void G_RemoveEntityFromExtraList( int entityNum )
{
	int index;

	index = extraEntitiesList.IndexOfObject( entityNum );

	if ( index > 0 )
	{
		extraEntitiesList.RemoveObjectAt( index );
	}
}

extern "C" int G_GetEntityCurrentAnimFrame( int entnum, int bodyPart )
{
	try
	{
		if ( entnum < 0 || entnum >= ENTITYNUM_NONE )
		{
			return -1;
		}

		if ( !g_entities[ entnum ].inuse || !g_entities[ entnum ].entity )
		{
			return -1;
		}

		return g_entities[ entnum ].entity->CurrentFrame( (bodypart_t)bodyPart );
	}
	
	catch( const char *error )
	{
		G_ExitWithError( error );
	}

	return -1;
}

extern "C" void G_ClientThink( gentity_t *ent, usercmd_t *ucmd )
{
	try
	{
		if ( ent->entity )
		{
			current_ucmd = ucmd;
			ent->entity->ProcessEvent( EV_ClientMove );
			current_ucmd = NULL;
		}
	}
	
	catch( const char *error )
	{
		G_ExitWithError( error );
	}
}


void G_SentInitialMessages( void )
{
}


/*
===========
G_ClientBegin

called when a client has finished connecting, and is ready
to be placed into the game.  This will happen every level load.
============
*/
extern "C" void G_ClientBegin( gentity_t *ent, const usercmd_t *cmd )
{
	try
	{
		if ( ent->inuse && ent->entity )
		{
			// the client has cleared the client side viewangles upon
			// connecting to the server, which is different than the
			// state when the game is saved, so we need to compensate
			// with deltaangles
			if( ent->client->ps.pm_type != PM_SECRET_MOVE_MODE )
				ent->entity->SetDeltaAngles();
		}
		else
		{
			Player *player;
			
			// a spawn point will completely reinitialize the entity
			level.spawn_entnum = ent->s.number;
			player = new Player;
		}
		
		if ( level.intermissiontime && ent->entity )
		{
			G_MoveClientToIntermission( ent->entity );
		}
		else
		{
			// Record the time entered
			ent->client->pers.enterTime = level.time;
			// send effect if in a multiplayer game
			if ( game.maxclients > 1 )
			{
				gi.Printf ( "%s entered the game\n", ent->client->pers.netname );
			}
		}
		
		// make sure all view stuff is valid
		if ( ent->entity )
		{
			ent->entity->ProcessEvent( EV_ClientEndFrame );
			
			if ( !Director.PlayerReady() )
            {
				// let any threads waiting on us know they can go ahead
				Director.PlayerSpawned();
            }
			
			GameplayManager::getTheGameplayManager()->processPendingMessages();
			G_SentInitialMessages( );
		}
	}
	
	catch( const char *error )
	{
		G_ExitWithError( error );
	}
}



qboolean G_duplicateName( const char *name )
{
	gentity_t *edict;
	int i;

	for ( i = 0; i < game.maxclients; i++ )
	{
		edict = &g_entities[i];
		
		if ( !edict->inuse || !edict->entity || !edict->client )
			continue;

		if ( strcmp( edict->client->pers.netname, name ) == 0 )
		{
			return true;
		}

	}

	return false;
}

void G_BuildUniqueName( const char *newName, char *name )
{
	char builtName[ MAX_NETNAME ];
	int i;

	for ( i = 2 ; i <= 9 ; i++ )
	{
		sprintf( builtName, "%s%d", newName, i );

		if ( !G_duplicateName( builtName ) )
		{
			strcpy( name, builtName );
			return;
		}
	}

	sprintf( builtName, "%s?", newName );

	strcpy( name, builtName );
}

/*
===========
G_ClientUserInfoChanged

called whenever the player updates a userinfo variable.

The game can override any of the settings in place
(forcing skins or names, etc) before copying it off.
============
*/
extern "C" void G_ClientUserinfoChanged( gentity_t *ent, const char *userinfo )
{
	const char	*s;
	int			playernum;
	Player      *player;
	//float       fov;
	//Event       *ev;
	bool			autoSwitchWeapons;
	char tempName[ MAX_NETNAME ];
	int i;
	bool validName;
	
	try
	{
		if ( !ent )
		{
			assert( 0 );
			return;
		}
		
		player = ( Player * )ent->entity;
		
		/*
		if ( !player )
		{
		assert( 0 );
		return;
		}
		*/
		
		// set name
		s = Info_ValueForKey( userinfo, "name" );
		if ( !s )
		{
			assert( 0 );
			return;
		}

		// Setup the name

		strncpy( tempName, s, MAX_NETNAME - 1 - 3 );

		// Make sure there is a valid name

		validName = false;

		for ( i = 0 ; i < strlen( tempName ) ; i++ )
		{
			if ( ( tempName[ i ] == '^' ) && ( tempName[ i + 1 ] >= '0' ) && ( tempName[ i + 1 ] <= '9' ) )
			{
				i++;
				continue;
			}

			validName = true;
		}

		if ( !validName )
		{
			strcpy( tempName, "RedShirt" );
		}

		if ( strcmp( ent->client->pers.netname, tempName ) != 0 )
		{
			if ( G_duplicateName( tempName ) )
				G_BuildUniqueName( tempName, ent->client->pers.netname );
			else
				strncpy( ent->client->pers.netname, tempName, MAX_NETNAME - 1 - 3 );
		}

		// Strip out bad characters

		for ( i = 0 ; i < strlen( ent->client->pers.netname ) ; i++ )
		{
			if ( ent->client->pers.netname[ i ] == ':' )
			{
				ent->client->pers.netname[ i ] = '.';
			}
		}

		// Make sure color returns to normal at end of the name

		if ( strstr( ent->client->pers.netname, "^" ) )
		{
			int length = strlen( ent->client->pers.netname );

			if ( ( length < 2 ) || ( ent->client->pers.netname[ length - 2 ] != '^' ) || ( ent->client->pers.netname[ length - 1 ] != '8' ) )
			{
				ent->client->pers.netname[ length     ] = '^';
				ent->client->pers.netname[ length + 1 ] = '8';
				ent->client->pers.netname[ length + 2 ] = 0;
			}
		}
		
		// set deathmatch model
		s = Info_ValueForKey( userinfo, "mp_playermodel" );
		if ( !s )
		{
			assert( 0 );
			return;
		}
		
		multiplayerManager.changePlayerModel( player, s );
		
		strncpy( ent->client->pers.mp_playermodel, s, sizeof( ent->client->pers.mp_playermodel ) - 1 );
		
		// Low bandwidth
		s = Info_ValueForKey( userinfo, "mp_lowBandwidth" );
		if ( !s )
		{
			assert( 0 );
			return;
		}

		ent->client->pers.mp_lowBandwidth = atoi( s );

		// Persistant morph controllers
		s = Info_ValueForKey( userinfo, "dm_morph_c1" );
		if ( !s )
		{
			assert( 0 );
			return;
		}
		strncpy( ent->client->pers.dm_morph_c1, s, sizeof( ent->client->pers.dm_morph_c1 ) - 1 );
		
		// send over a subset of the userinfo keys so other clients can
		// print scoreboards, display models, and play custom sounds
		playernum = ent - g_entities;
		gi.setConfigstring( CS_PLAYERS + playernum, va( "name\\%s", ent->client->pers.netname ) );
		//gi.setConfigstring( CS_PLAYERS + playernum, va( "%s", ent->client->pers.netname ) );

		multiplayerManager.changePlayerName( player, ent->client->pers.netname );
		
		if ( player )
		{
			float fov;
			Event *ev;

			// Fov
			
			fov = (float)atof( Info_ValueForKey( userinfo, "userFov" ) );
			if ( fov < 1.0f )
            {
				fov = sv_defaultFov->value;
            }
			else if ( fov > 160.0f )
            {
				fov = 160.0f;
            }

			player->userFov = fov;

			ev = new Event( EV_Player_Fov );
			ev->AddFloat( fov );
			player->ProcessEvent( ev );
			
			// autoSwitchWeapons
			
			if ( atoi( Info_ValueForKey( userinfo, "mp_autoSwitchWeapons" ) ) )
				autoSwitchWeapons = true;
			else
				autoSwitchWeapons = false;
			
			player->setAutoSwitchWeapons( autoSwitchWeapons );

			// Saving demo
			s = Info_ValueForKey( userinfo, "mp_savingDemo" );

			if ( !s )
			{
				assert( 0 );
				return;
			}

			player->mp_savingDemo = atoi( s );
		}
		
		// save off the userinfo in case we want to check something later
		strncpy( ent->client->pers.userinfo, userinfo, sizeof( ent->client->pers.userinfo ) - 1 );

		Info_SetValueForKey(ent->client->pers.userinfo, "name", ent->client->pers.netname);
		gi.setUserinfo(playernum, ent->client->pers.userinfo);
	}
	
	catch( const char *error )
	{
		G_ExitWithError( error );
	}
}

qboolean G_BotConnect( int clientNum, qboolean restart );

/*
===========
ClientConnect

Called when a player begins connecting to the server.
Called again for every map change or tournement restart.

The session information will be valid after exit.

Return NULL if the client should be allowed, otherwise return
a string with the reason for denial.

Otherwise, the client will be sent the current gamestate
and will eventually get to ClientBegin.

firstTime will be true the very first time a client connects
to the server machine, but false on map changes and tournement
restarts.
============
*/
extern "C" const char *G_ClientConnect( int clientNum, qboolean firstTime, qboolean isBot, qboolean checkPassword )
{
	const char *value;
	gentity_t	*ent;
	char		   userinfo[ MAX_INFO_STRING ];
	gclient_t	*client;
	
	try
	{
		ent = &g_entities[ clientNum ];
		gi.getUserinfo( clientNum, userinfo, sizeof( userinfo ) );
		
		// check to see if they are on the banned IP list
		value = Info_ValueForKey( userinfo, "ip" );
		if ( SV_FilterPacket( value ) )
		{
			return "$$BannedIP$$";
		}
		
		if ( checkPassword )
		{
			// check for a password
			value = Info_ValueForKey( userinfo, "password" );
			if ( strcmp( password->string, value ) != 0 )
			{
				return "$$InvalidPassword$$";
			}
		}
		
		// they can connect
		ent->client = game.clients + clientNum;
		client = ent->client;
		
		// read or initialize the session data
		// if there is already a body waiting for us (a loadgame), just
		// take it, otherwise spawn one from scratch
		if ( firstTime && !ent->entity )
		{
			memset( client, 0, sizeof( *client ) );
			
			// clear the respawning variables
			if ( !game.autosaved )
            {
				G_InitClientPersistant( client );
				playersLastTeam[ clientNum ] = "";
            }
		}
		
		
		if( isBot ) {
			ent->svflags |= SVF_BOT;
			//ent->inuse = qtrue;
			if( !G_BotConnect( clientNum, !firstTime ) ) {
				return "BotConnectfailed";
			}
		}
		
		
		
		G_ClientUserinfoChanged( ent, userinfo );
		
		if ( firstTime && ( game.maxclients > 1 ) )
		{
			gi.Printf( "%s connected\n", ent->client->pers.netname );
		}
		
		LoadingServer = false;
	}
	
	catch( const char *error )
	{
		G_ExitWithError( error );
	}
	
	return NULL;
}

/*
===========
G_ClientDisconnect

called when a player drops from the server

============
*/
extern "C" void G_ClientDisconnect( gentity_t *ent )
{
	try
	{
		if ( !ent || ( !ent->client ) || ( !ent->entity ) )
		{
			return;
		}
		
		Player *player;
		
		player = ( Player * )ent->entity;
		player->Disconnect();
		
		delete ent->entity;
		ent->entity = NULL;
	}
	
	catch( const char *error )
	{
		G_ExitWithError( error );
	}
}

/*
=================
G_ClientDrawSplines
=================
*/
void G_ClientDrawSplines( void )
{
	int i;
	Entity *entity;
	gentity_t *ed;
	
	
	if ( !sv_showsplines->integer )
		return;
	
	for ( i = 0; i < MAX_GENTITIES; i++ )
	{
		ed = &g_entities[i];
		
		if ( !ed->inuse || !ed->entity )
			continue;
		
		entity = g_entities[i].entity;
		
		if ( entity->isSubclassOf( SplinePath ) )
		{
			SplinePath *path = ( SplinePath *)entity;
			
			SplinePath	*node;
			BSpline	*splinePath;
			
			splinePath = new BSpline;
			splinePath->SetType( SPLINE_CLAMP );
			
			node = path;
			
			while( node != NULL )
			{
				splinePath->AppendControlPoint( node->origin, node->angles, node->speed );
				
				node = node->GetNext();
				
				if ( node == path )
					break;
			}
			
			G_Color3f( 1.0f, 1.0f, 0.0f );
			splinePath->DrawCurve( 10 );
			
			delete splinePath;
			continue;
		}
	}
}

/*
=================
G_ClientDrawBoundingBoxes
=================
*/
void G_ClientDrawBoundingBoxes( void )
{
	gentity_t  *edict;
	Entity	*ent;
	Vector	eye;
	
	// don't show bboxes during deathmatch
	if (
        ( !g_showgravpath->integer && !sv_showbboxes->integer ) ||
        ( multiplayerManager.inMultiplayer() && !sv_cheats->integer )
		)
	{
		return;
	}
	
	if ( sv_showbboxes->integer )
	{
		edict = g_entities;
		ent = edict->entity;
		if ( ent )
		{
			eye = ent->origin;
			ent = findradius( NULL, eye, 1000.0f );
			while( ent )
			{
				switch ((int)sv_showbboxes->integer)
				{
				case 1:
					if ( ent->edict != edict && ent->edict->s.solid)
					{
						G_DebugBBox( ent->origin, ent->mins, ent->maxs, 1.0f, 1.0f, 0.0f, 1.0f );
					}
					break;
				case 2:
					if ( ent->edict != edict && ent->edict->s.solid)
					{
						G_DebugBBox( vec_zero, ent->edict->absmin, ent->edict->absmax, 1.0f, 0.0f, 1.0f, 1.0f );
					}
					break;
				case 3:
					if ( ent->edict->s.modelindex && !(ent->edict->s.renderfx & RF_DONTDRAW) )
						G_DebugBBox( ent->origin, ent->mins, ent->maxs, 1.0f, 1.0f, 0.0f, 1.0f );
					break;
				case 4:
					G_DebugBBox( ent->origin, ent->mins, ent->maxs, 1.0f, 1.0f, 0.0f, 1.0f );
					break;
				case 5:
				default:
					if ( ent->animate && gi.IsModel( ent->edict->s.modelindex ) )
					{
						vec3_t mins, maxs;
						
						gi.Frame_Bounds( ent->edict->s.modelindex, ent->CurrentAnim(), ent->CurrentFrame(), ent->edict->s.scale, mins, maxs );
						G_DebugBBox( ent->origin, mins, maxs, 0.0f, 1.0f, 0.0f, 1.0f );
					}
					else
					{
						G_DebugBBox( ent->origin, ent->mins, ent->maxs, 1.0f, 1.0f, 0.0f, 1.0f );
					}
					break;
				}
				ent = findradius( ent, eye, 1000.0f );
			}
		}
	}
	
	if ( g_showgravpath->integer )
	{
		// Draw the gravity node paths
		gravPathManager.DrawGravPaths();
	}
}


//-----------------------------------------------------
//
// Name:		
// Class:		
//
// Description:	
//
// Parameters:	
//
// Returns:		
//-----------------------------------------------------
extern "C" int G_GetTotalGameFrames( void )
{
	Player* player = GetPlayer(0);

	if(player != 0)
		return player->getTotalGameFrames();

	return 0;
}

//======================================================================

#ifndef GAME_HARD_LINKED
// this is only here so the functions in q_shared.c and q_shwin.c can link
void Com_Error ( int level, const char *error, ... ) 
{
	va_list	argptr;
	char		text[4096];

	va_start (argptr, error);
	vsprintf (text, error, argptr);
	va_end (argptr);

	gi.Error( level, "%s", text);
}

void Sys_Error( const char *error, ... )
{
	va_list	argptr;
	char		text[4096];
	
	va_start (argptr, error);
	vsprintf (text, error, argptr);
	va_end (argptr);
	
	gi.Error (ERR_FATAL, "%s", text);
}

void Com_Printf( const char *msg, ... )
{
	va_list	argptr;
	char		text[4096];
	
	va_start (argptr, msg);
	vsprintf (text, msg, argptr);
	va_end (argptr);
	
	gi.DPrintf ("%s", text);
}

void Com_WPrintf( const char *msg, ... )
{
	va_list	argptr;
	char		text[4096];
	
	va_start (argptr, msg);
	vsprintf (text, msg, argptr);
	va_end (argptr);
	
	gi.WDPrintf ("%s", text);
}

#endif
