diff options
Diffstat (limited to 'src/parsec_server/e_gameserver.cpp')
| -rwxr-xr-x | src/parsec_server/e_gameserver.cpp | 1013 |
1 files changed, 1013 insertions, 0 deletions
diff --git a/src/parsec_server/e_gameserver.cpp b/src/parsec_server/e_gameserver.cpp new file mode 100755 index 0000000..d1fe6ae --- /dev/null +++ b/src/parsec_server/e_gameserver.cpp @@ -0,0 +1,1013 @@ +/* + * PARSEC - Main Server Functions + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:46 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2001 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +// C library +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <sys/types.h> +#include <sys/timeb.h> +#include <unistd.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "objstruc.h" + +// global externals +#include "globals.h" + +// subsystem headers +#include "net_defs.h" +//FIXME: ???? +#include "sys_refframe_sv.h" + +// UNP header +#include "net_wrap.h" + +// server defs +#include "e_defs.h" + +// net game header +#include "net_game_sv.h" + +// mathematics header +#include "utl_math.h" + +// local module header +#include "e_gameserver.h" + +// proprietary module headers +#include "con_arg.h" +#include "con_aux_sv.h" +#include "con_com_sv.h" +#include "con_main_sv.h" +#include "e_colldet.h" +#include "g_extra.h" +#include "inp_main_sv.h" +#include "net_csdf.h" +#include "net_udpdriver.h" +#include "net_util.h" +#include "net_packetdriver.h" +#include "obj_clas.h" +//#include "e_stats.h" +#include "g_main_sv.h" +#include "e_connmanager.h" +#include "e_packethandler.h" +#include "e_simulator.h" +#include "e_simnetinput.h" +#include "e_simnetoutput.h" +#include "sys_refframe_sv.h" +#include "sys_util_sv.h" + + +// defaults ------------------------------------------------------------------- +// +#define DEFAULT_SIM_FREQUENCY 100 +#define DEFAULT_SERVER_TO_CLIENT_HEARTBEAT FRAME_MEASURE_TIMEBASE * 2 +#define DEFAULT_MASTERSERVER_INTERVAL DEFAULT_REFFRAME_FREQUENCY * 10 +#define DEFAULT_MASTERSERVER_NAME "master.openparsec.com" +//#define DEFAULT_MASTERSERVER_NAME "drax" +//#define DEFAULT_MASTERSERVER_NAME "192.168.1.102" + + +// console texts -------------------------------------------------------------- +// +static char error_resolving_masterserver[] = "error resolving masterserver hostname."; + + +// ---------------------------------------------------------------------------- +// ServerConfig methods +// ---------------------------------------------------------------------------- + +// standard ctor -------------------------------------------------------------- +// +ServerConfig::ServerConfig() +{ + SetServername( "Unnamed" ); + m_SimFrequency = 0; + m_nMaxNumClients = 0; + m_ServerIsMaster = 0; + m_ClientUpdateHeartbeat = 2 * DEFAULT_REFFRAME_FREQUENCY; +} + +void ServerConfig::SetServerIsMaster(bool isMaster){ + m_ServerIsMaster = isMaster; + +} +bool ServerConfig::GetServerIsMaster(){ + return m_ServerIsMaster; +} + +// set the # of max players --------------------------------------------------- +// +bool_t ServerConfig::SetMaxNumClients( int nMaxNumClients ) +{ + // we only accept setting the max # of clients once + if ( m_nMaxNumClients == 0 ) { + ASSERT( m_nMaxNumClients <= MAX_NET_ALLOC_SLOTS ); + m_nMaxNumClients = nMaxNumClients; + + // reset the simulation engine + TheSimulator->Reset(); + + return true; + } else { + return false; + } +} + + +// return the max # of players ------------------------------------------------ +// +int ServerConfig::GetMaxNumClients() +{ + if ( m_nMaxNumClients == 0 ) { + // defaults to the upper bound + return MAX_NET_ALLOC_SLOTS; + } + return m_nMaxNumClients; +} + + +// set the simulation frequency ----------------------------------------------- +// +bool_t ServerConfig::SetSimFrequency( int nSimFrequency ) +{ + // we only accept setting the max # of clients once + if ( m_SimFrequency == 0 ) { + m_SimFrequency = nSimFrequency; + return true; + } else { + return false; + } +} + + +// return the simulation frequency -------------------------------------------- +// +int ServerConfig::GetSimFrequency() +{ + // ensure this is set to the default + if ( m_SimFrequency == 0 ) { + m_SimFrequency = DEFAULT_SIM_FREQUENCY; + } + return m_SimFrequency; +} + + +// ---------------------------------------------------------------------------- +// E_GameServer methods +// ---------------------------------------------------------------------------- + +// default ctor --------------------------------------------------------------- +// +E_GameServer::E_GameServer() : +m_bQuit( false ) +{ +} + +// default dtor --------------------------------------------------------------- +// +E_GameServer::~E_GameServer() +{ +} + +// init data prior to running the console script ------------------------------ +// +int E_GameServer::_InitPreConsoleScript() +{ + //NOTE: init all vars that are not modifiable through the console script, + // or set the defaults for the ones that are. + + // default values for user configurable values + m_nInterface = 0; + m_MaintainFrequency = 10; + m_MasterServer_FrameTime = DEFAULT_MASTERSERVER_INTERVAL; + m_nPacketAverageSecs = 3; + strncpy( m_MasterServer_Hostname, DEFAULT_MASTERSERVER_NAME, MAX_MASTERSERVER_NAME ); + m_MasterServer_Hostname[ MAX_MASTERSERVER_NAME ] = 0; + m_nNumServerLinks = 0; + + // non modifiable + m_nServerFrame = 0; + m_CurServerRefFrame = REFFRAME_INVALID; + m_LastServerRefFrame = REFFRAME_INVALID; + + return TRUE; +} + + +// init data post running the console script ---------------------------------- +// +int E_GameServer::_InitPostConsoleScript() +{ + //NOTE: init all vars, that depend on settings set via the boot_sv.con + // console script + + // idle for max. one simframe length + m_ServerIdleTime_msec = ( 1000 / GetSimFrequency() ); + + // must be done before g_RefFrameCount is used the first time ( FRAME_MEASURE_TIMEBASE ) + SYSs_InitRefFrameCount(); + + // default values for internal data + m_Maintain_FrameTime = FRAME_MEASURE_TIMEBASE / m_MaintainFrequency; + m_SimTick_FrameTime = FRAME_MEASURE_TIMEBASE / GetSimFrequency(); + + m_MaintainFrameBase = SYSs_GetRefFrameCount(); + m_MasterServerFrameBase = SYSs_GetRefFrameCount(); + + return TRUE; +} + + +// initialize the server components ------------------------------------------- +// +int E_GameServer::Init() +{ + //NOTE: some of the globals must already exist when parsing the console scripts + + // init all the modules + TheModuleManager->InitAllModules(); + + // Only init the game if I am not a master server... + if(!this->GetServerIsMaster()){ + // init the Game globals + TheGame = G_Main::GetGame(); + TheGameInput = G_Input::GetGameInput(); + TheGameExtraManager = G_ExtraManager::GetExtraManager(); + TheGameCollDet = G_CollDet::GetGameCollDet(); + + // get the World global + TheWorld = E_World::GetWorld(); + } + + // init the network simulation engine, needed for master and game server + TheSimNetInput = E_SimNetInput::GetSimNetInput(); + TheSimNetOutput = E_SimNetOutput::GetSimNetOutput(); + + // init the simulation engine only if we are not running a master server. + if(!this->GetServerIsMaster()){ + TheSimulator = E_Simulator::GetSimulator(); + + // register all AUX/SV vars + CON_AUX_SV_Register(); + } + // init CURSES + CON_InitCurses(); + + // init the input system + INP_Init(); + + // init all data not depending on the console script to be run + _InitPreConsoleScript(); + + // print the copyright + PrintCopyRight(); + + // init the console + CON_InitConsole(); + + if(this->GetServerIsMaster()) { + MSGOUT("--- Open Parsec Master Server Running"); + } + + // init all data depending on console modifiable vars + _InitPostConsoleScript(); + + // if we are in master server mode, set some default values. + if(this->GetServerIsMaster()){ + char tmp_srvname[MAX_SERVER_NAME + 1] = ""; + gethostname(tmp_srvname, MAX_SERVER_NAME); + SV_SERVERID = 1; //master server is always 1 + SV_NETCONF_PORT = 6580; // master server port + SV_MASTERSERVER_SENDHEARTBEAT = FALSE; // disable heartbeat sending. + } + + // init the UDP driver + TheUDPDriver = NET_UDPDriver::GetUDPDriver(); + TheUDPDriver->InitDriver( NULL, SV_NETCONF_PORT ); + + // init the packet driver + ThePacketDriver = NET_PacketDriver::GetPacketDriver(); + + // init the packet handler + ThePacketHandler = E_PacketHandler::GetPacketHandler(); + + // init the connection manager + TheConnManager = E_ConnManager::GetConnManager(); + + // init the statistics manager + //E_StatsManager* pStatsManager = TheStatsManager; + + // set to illegal challenge + m_MasterServer_Challenge = 0; + + // master server not yet resolved + m_bMasterServer_NodeValid = false; + + // init the game, only if in game server mode. + if(!this->GetServerIsMaster()){ + TheGame->Init(); + TheWorld->InitParticleSystem(); + } + return TRUE; +} + +// kill the server ------------------------------------------------------------ +// +int E_GameServer::Kill() +{ + // kill the console + CON_KillConsole(); + + // kill the input system + INP_Kill(); + + // kill CURSES + CON_KillCurses(); + + return TRUE; +} + +// parse relevant commands from commandline ----------------------------------- +// +int E_GameServer::ParseCommandLine( int argc, char** argv ) +{ + // set the programname + sys_ProgramName = argv[ 0 ]; + + // Parse the incoming commandline options and do the stuff. + opterr = 0; + int optchar= getopt(argc, argv, "m"); + + while(optchar != -1){ + + switch(optchar){ + case 'm': + // TODO: set the option in server config to say we are a master server + this->m_ServerIsMaster = 1; + break; + case '?': + // unknown option. + // TODO: print error(maybe) but continue... + break; + default: + break; + } + optchar = getopt(argc, argv, "m"); + + } + + return TRUE; +} + + +// ---------------------------------------------------------------------------- +// +int E_GameServer::PrintCopyRight() +{ + MSGOUT( "\n" ); + MSGOUT( " PARSEC SERVER V0.20 build " SERVER_BUILD_NUMBER "." ); + MSGOUT( "\n" ); + MSGOUT( "See LICENSE file for licensing information. "); + /*MSGOUT( " Copyright (c) 1996-2002 by Alex Mastny, Andreas Varga," ); + MSGOUT( " Clemens Beer, Markus Hadwiger, Stefan Poiss, Michael Woegerbauer." ); + MSGOUT( " All Rights Reserved." );*/ + MSGOUT( "-------------------------------------------------------" ); + MSGOUT( " http://www.openparsec.com " ); + MSGOUT( "-------------------------------------------------------" ); + MSGOUT( "\n" ); + + return TRUE; +} + + +// ---------------------------------------------------------------------------- +// +int E_GameServer::PrintUsage() +{ + PrintCopyRight(); + + return TRUE; +} + + +//FIXME_OSS: move this to seperate module as this is not yet used + +// constant tick function pointer type ---------------------------------------- +// +typedef int (*ConstTickedFunction)( refframe_t now ); + +// class for ensuring constant calls to a tick-function ----------------------- +// +class PU_ConstTicker +{ +protected: + refframe_t m_CurRefFrame; + refframe_t m_LastRefFrame; + refframe_t m_TickRefFrames; + ConstTickedFunction m_pTickedFunction; + +public: + + // standard ctor + // + PU_ConstTicker( refframe_t _TickRefFrames, ConstTickedFunction _TickedFunction ) + { + ASSERT( _TickedFunction != NULL ); + ASSERT( _TickRefFrames > 0 ); + + m_CurRefFrame = REFFRAME_INVALID; + m_LastRefFrame = REFFRAME_INVALID; + m_TickRefFrames = _TickRefFrames; + + m_pTickedFunction = _TickedFunction; + } + + // maintain the tick function to achieve as many ticks as necessary between last call and current call + // + void MaintainTickFunction() + { + // first call ? + if ( m_LastRefFrame == REFFRAME_INVALID ) { + m_LastRefFrame = m_CurRefFrame; + } + + // try to catch up as many ticks as we need to get under the frametime for one tick + // the excess is left in m_LastRefFrame and accumulated in the next tick + while( ( m_CurRefFrame - m_LastRefFrame ) >= m_TickRefFrames ) { + m_LastRefFrame += m_TickRefFrames; + + // call the tick function + m_pTickedFunction( m_LastRefFrame ); + } + } + + // set the current refframe + // + void SetNow( refframe_t now ) + { + m_CurRefFrame = now; + } +}; + + +// maintain the simulation ---------------------------------------------------- +// +void E_GameServer::_MaintainSimulation() +{ + if ( m_LastServerRefFrame == REFFRAME_INVALID ) { + m_LastServerRefFrame = m_CurServerRefFrame; + } + + // catch up m_LastServerRefFrame in m_SimTick_FrameTime steps and run the simulation + // remainging excess will be stored in m_LastServerRefFrame + while ( ( m_CurServerRefFrame - m_LastServerRefFrame ) >= m_SimTick_FrameTime ) { + m_LastServerRefFrame += m_SimTick_FrameTime; + + //MSGOUT( "%d: TheSimulator->DoSim( %d ), cur: %d diff: %d", TheSimulator->GetSimFrame(), m_LastServerRefFrame, m_CurServerRefFrame, m_CurServerRefFrame - m_LastServerRefFrame ); + // run the simulation frame/tick + TheSimulator->DoSim( m_LastServerRefFrame ); + //LOGOUT(( "DoSim(): SimFrame:%d m_LastServerRefFrame: %d, m_nServerFrame: %d", TheSimulator->GetSimFrame(), m_LastServerRefFrame, m_nServerFrame )); + } +} + + +// add a serverlink ----------------------------------------------------------- +// +int E_GameServer::AddServerLink( int serverid, Vector3* pos_spec, Vector3* dir_spec ) +{ + ASSERT( serverid > 0 ); + ASSERT( pos_spec != NULL ); + ASSERT( dir_spec != NULL ); + + if ( m_nNumServerLinks >= MAX_NUM_LINKS ) { + return FALSE; + } + + // norm the direction + NormVctX( dir_spec ); + + m_ServerLinks[ m_nNumServerLinks ].m_serverid = serverid; + + m_ServerLinks[ m_nNumServerLinks ].m_pos.X = pos_spec->X; + m_ServerLinks[ m_nNumServerLinks ].m_pos.Y = pos_spec->Y; + m_ServerLinks[ m_nNumServerLinks ].m_pos.Z = pos_spec->Z; + + m_ServerLinks[ m_nNumServerLinks ].m_dir.X = dir_spec->X; + m_ServerLinks[ m_nNumServerLinks ].m_dir.Y = dir_spec->Y; + m_ServerLinks[ m_nNumServerLinks ].m_dir.Z = dir_spec->Z; + + // create the corresponding stargate + TheGame->CreateStargate( serverid, pos_spec, dir_spec ); + + m_nNumServerLinks++; + + return TRUE; +} + + + +// set the new masterserver info ---------------------------------------------- +// +void E_GameServer::SetMasterServerInfo( char* pHostname, refframe_t _MasterServer_FrameTime ) +{ + ASSERT( pHostname != NULL ); + ASSERT( _MasterServer_FrameTime >= 0 ); + + m_MasterServer_FrameTime = _MasterServer_FrameTime; + strncpy( m_MasterServer_Hostname, pHostname, MAX_MASTERSERVER_NAME ); + m_MasterServer_Hostname[ MAX_MASTERSERVER_NAME ] = 0; +} + + +// check whether the node is the master server node --------------------------- +// +int E_GameServer::IsMasterServerNode( node_t* node ) +{ + return ( NODE_Compare( node, &m_MasterServer_Node ) == NODECMP_EQUAL ); +} + +// set the new MASV challenge and enforce a new announcement to the MASV ------ +// +void E_GameServer::SetMasterServerChallenge( int nMASVChallenge ) +{ + m_MasterServer_Challenge = nMASVChallenge; + // enforce immediate resend of server info + m_MasterServerFrameBase = SYSs_GetRefFrameCount() - m_MasterServer_FrameTime; +} + + +// maintain communication to master server ------------------------------------ +// +int E_GameServer::_MaintainMasterServer() +{ + // check whether to skip heartbeat sending + if ( !SV_MASTERSERVER_SENDHEARTBEAT ) + return FALSE; + + refframe_t CurMasterServerRefFrames = SYSs_GetRefFrameCount() - m_MasterServerFrameBase; + + // check whether maintainance is necessary + if ( CurMasterServerRefFrames >= m_MasterServer_FrameTime ) { + + // advance base + m_MasterServerFrameBase += CurMasterServerRefFrames; + + // try to resolve master server node + if ( !m_bMasterServer_NodeValid ) { + if ( TheUDPDriver->ResolveHostName( m_MasterServer_Hostname, &m_MasterServer_Node ) ) { + NODE_StorePort( &m_MasterServer_Node, DEFAULT_MASTERSERVER_UDP_PORT ); + m_bMasterServer_NodeValid = true; + } else { + CON_AddLine( error_resolving_masterserver ); + } + } + + // send challenge/info packet to masterserver + if ( m_bMasterServer_NodeValid ) { + + // build command + char szBuffer[ MAX_RE_COMMANDINFO_COMMAND_LEN + 1 ]; + snprintf( szBuffer, MAX_RE_COMMANDINFO_COMMAND_LEN, + MASV_CHALLSTRING, + CLSV_PROTOCOL_MAJOR, CLSV_PROTOCOL_MINOR, + m_MasterServer_Challenge, + m_szServername, + TheConnManager->GetNumConnected(), + MAX_NUM_CLIENTS, + SV_SERVERID, + CPU_VENDOR_OS, + SV_NETCONF_PORT + ); + + // append a remote event containing the command + E_REList* pUnreliable = E_REList::CreateAndAddRef( RE_LIST_MAXAVAIL ); + int rc = pUnreliable->NET_Append_RE_CommandInfo( szBuffer ); + ASSERT( rc == TRUE ); + + // only send serverlinks if challenge is valid + if ( m_MasterServer_Challenge != 0 ) { + + // append all links or until packet full + for( int nLink = 0; nLink < m_nNumServerLinks; nLink++ ) { + if ( !pUnreliable->NET_Append_RE_ServerLinkInfo( SV_SERVERID, m_ServerLinks[ nLink ].m_serverid, SERVERLINKINFO_1_TO_2 ) ) { + break; + } + } + } + + // send a datagram + ThePacketHandler->Send_STREAM_Datagram( pUnreliable, &m_MasterServer_Node, PLAYERID_MASTERSERVER ); + + // release the RE list from here + pUnreliable->Release(); + } + } + + return TRUE; +} + + + +// run housekeeping ----------------------------------------------------------- +// +int E_GameServer::_MaintainHousekeeping() +{ + refframe_t CurMaintainRefFrames = SYSs_GetRefFrameCount() - m_MaintainFrameBase; + + // check whether maintainance is necessary + if ( CurMaintainRefFrames >= m_Maintain_FrameTime ) { + + // advance base for frame measurement + m_MaintainFrameBase += CurMaintainRefFrames; + + // check whether clients are alive and timeout if needed + TheConnManager->CheckAliveStatus(); + + // recalulate the average packet sizes sent to each client + TheSimNetOutput->RecalcAveragePacketSizes(); + + // cleanup all zombie distributables + //FIXME: we could call this function after timeout of max. RTT_OF_ALL_CLIENTS + TheSimNetOutput->CleanupZombieDistributables(); + } + + return TRUE; +} + +// run the SERVER frame ------------------------------------------------------- +// +refframe_t E_GameServer::ServerFrame() +{ + m_CurServerRefFrame = SYSs_GetRefFrameCount(); + //MSGOUT( "E_GameServer::ServerFrame(): %d", m_CurServerRefFrame ); + + // run packet chain processing function ( reads network, filters out invalid events, fills input queue ) + ThePacketDriver->NET_ProcessPacketChain(); + + // if we are not a master server, do the game server stuff... + if(!this->GetServerIsMaster()){ + // process queue with input from all clients + TheSimNetInput->ProcessInputREList(); + + // maintain the simulation + _MaintainSimulation(); + + // maintain housekeeping + _MaintainHousekeeping(); + + // maintain info to master server + _MaintainMasterServer(); + + // update clients if necessary + TheSimNetOutput->DoClientUpdates(); + } else { + // TODO: Master server stuff.... + + } + // increment the serverframe counter + //MSGOUT( "m_nServerFrame++" ); + m_nServerFrame++; + + // return the duration of the server frame + return SYSs_GetRefFrameCount() - m_CurServerRefFrame; +} + + + +// run the SERVER mainloop ---------------------------------------------------- +// +int E_GameServer::MainLoop() +{ + //LARGE_INTEGER pfc_freq; + //QueryPerformanceFrequency( &pfc_freq ); + //LOGOUT(( "QPFC-Frequency:%I64d", pfc_freq.QuadPart )); + + refframe_t now = SYSs_GetRefFrameCount(); + + for( ; !m_bQuit ; ) { + + //LOGOUT(( "Before select()." )); + + // check whether we have a network input for at most m_ServerIdleTime + int netInput = TheUDPDriver->SleepUntilNetInput( m_ServerIdleTime_msec ); + if ( netInput < 0 ) { + MSGOUT( "E_GameServer: error checking for network input" ); + } + + //LOGOUT(( "Before ServerFrame(). NETINPUT:%d", netInput )); + + // run one server frame + refframe_t duration = ServerFrame(); + + // just print "TICK" every second + //if ( ( SYSs_GetRefFrameCount() - now ) > ( FRAME_MEASURE_TIMEBASE * 0.2 ) ) { + + // check input + INP_HandleInput(); + + // process console input + CON_ConsoleMain(); + + // now = SYSs_GetRefFrameCount(); + //} + + //LOGOUT(( "After ServerFrame(). NETINPUT:%d", netInput )); + + //SYSTEMTIME st; + //GetLocalTime( &st ); + //LOGOUT(( "The time is %02d:%02d:%02d.%03d. NETINPUT:%d", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, netInput )); + } + + return TRUE; +} + + +// key table for MASTERSERVER command ----------------------------------------- +// +key_value_s masterserver_key_value[] = { + + { "name", NULL, KEYVALFLAG_PARENTHESIZE }, + { "interval", NULL, KEYVALFLAG_NONE }, + + { NULL, NULL, KEYVALFLAG_NONE }, +}; + +enum { + + KEY_MASTERSERVER_NAME, + KEY_MASTERSERVER_INTERVAL +}; + +// min/max interval for heartbeats to the masterserve ------------------------- +// +#define MASTERSERVER_INTERVAL_MIN 5 +#define MASTERSERVER_INTERVAL_MAX 86400 + + +// console command for the masterserver configuration ------------------------- +// +PRIVATE +int Cmd_MASTERSERVER( char* masv_command ) +{ + //NOTE: + //CONCOM: + // masterserver_command ::= 'sv.masterserver.conf' <name_spec> [<interval_spec>] + // name_spec ::= 'name' <masterservername> + // interval_spec ::= 'interval' <sec> + + ASSERT( masv_command != NULL ); + HANDLE_COMMAND_DOMAIN( masv_command ); + + // scan out all values to keys + if ( !ScanKeyValuePairs( masterserver_key_value, masv_command ) ) + return TRUE; + + char* pName = masterserver_key_value[ KEY_MASTERSERVER_NAME ].value; + + refframe_t _MasterServer_FrameTime = TheServer->GetMasterServerFrameTime(); + + // get the interval and adjust for refframes + if ( masterserver_key_value[ KEY_MASTERSERVER_INTERVAL ].value != NULL ) { + + int interval; + ScanKeyValueInt( &masterserver_key_value[ KEY_MASTERSERVER_INTERVAL ], &interval ); + + if ( interval < MASTERSERVER_INTERVAL_MIN ) { + interval = MASTERSERVER_INTERVAL_MIN; + CON_AddLine( "sv.masterserver interval clamped to min" ); + } else if ( interval > MASTERSERVER_INTERVAL_MAX ) { + interval = MASTERSERVER_INTERVAL_MAX; + CON_AddLine( "sv.masterserver interval clamped to max" ); + } + + _MasterServer_FrameTime = interval * DEFAULT_REFFRAME_FREQUENCY; + } + + // set the new masterserver info + TheServer->SetMasterServerInfo( pName, _MasterServer_FrameTime ); + + return TRUE; +} + + +// key table for "SV.CONF" command -------------------------------------------- +// +key_value_s sv_conf_key_value[] = { + + { "name", NULL, KEYVALFLAG_PARENTHESIZE }, + { "maxplayers", NULL, KEYVALFLAG_NONE }, + { "simfreq", NULL, KEYVALFLAG_NONE }, + + { NULL, NULL, KEYVALFLAG_NONE }, +}; + +enum { + + KEY_SERVER_NAME, + KEY_SERVER_MAXPLAYERS, + KEY_SERVER_SIMFREQ +}; + +// min/max interval simulation frequency -------------------------------------- +// +#define SIMFREQ_MIN 5 +#define SIMFREQ_MAX DEFAULT_REFFRAME_FREQUENCY + + +// console command for configuring the server --------------------------------- +// +PRIVATE +int Cmd_SV_CONF( char* sv_conf_command ) +{ + //NOTE: + //CONCOM: + // sv_conf_command ::= 'sv.conf' [<name_spec>] [<maxplayer_spec>] [<simfreq_spec>] + // name_spec ::= 'name' <servername> + // maxplayers_spec ::= 'maxplayers' <maxplayers> + // simfreq_spec ::= 'simfreq' <simfreq> + + ASSERT( sv_conf_command != NULL ); + HANDLE_COMMAND_DOMAIN( sv_conf_command ); + + // scan out all values to keys + if ( !ScanKeyValuePairs( sv_conf_key_value, sv_conf_command ) ) + return TRUE; + + // name specified ? + if ( sv_conf_key_value[ KEY_SERVER_NAME ].value != NULL ) { + TheServer->SetServername( sv_conf_key_value[ KEY_MASTERSERVER_NAME ].value ); + } + + // maxplayer specified ? + if ( sv_conf_key_value[ KEY_SERVER_MAXPLAYERS ].value != NULL ) { + int maxplayers; + ScanKeyValueInt( &sv_conf_key_value[ KEY_SERVER_MAXPLAYERS ], &maxplayers ); + + // set the current # of max. players + if ( !TheServer->SetMaxNumClients( maxplayers ) ) { + CON_AddLine( "the max # of players can only be set once. please restart the server." ); + } + } + + // simfreq specified ? + if ( sv_conf_key_value[ KEY_SERVER_SIMFREQ ].value != NULL ) { + int simfreq; + ScanKeyValueInt( &sv_conf_key_value[ KEY_SERVER_SIMFREQ ], &simfreq ); + + if ( simfreq < SIMFREQ_MIN ) { + CON_AddLine( "simfrequency clamped to min" ); + simfreq = SIMFREQ_MIN; + } else if ( simfreq > SIMFREQ_MAX ) { + CON_AddLine( "simfrequency clamped to max" ); + simfreq = SIMFREQ_MAX; + } + + if ( !TheServer->SetSimFrequency( simfreq ) ) { + CON_AddLine( "the simulation frequency can only be set once. please restart the server." ); + } + } + + return TRUE; +} + +// key table for "SV.LINK" command -------------------------------------------- +// +key_value_s sv_link_key_value[] = { + + { "serverid", NULL, KEYVALFLAG_MANDATORY }, + { "pos", NULL, KEYVALFLAG_PARENTHESIZE }, + { "dir", NULL, KEYVALFLAG_PARENTHESIZE }, + + { NULL, NULL, KEYVALFLAG_NONE }, +}; + +enum { + + KEY_SERVERLINK_SERVERID, + KEY_SERVERLINK_POS, + KEY_SERVERLINK_DIR +}; + + +// console command for specifying server links -------------------------------- +// +PRIVATE +int Cmd_SV_LINK( char* sv_link_command ) +{ + //NOTE: + //CONCOM: + // sv_link_command ::= 'sv.link' <serverid_spec> [<pos_spec>] [<dir_spec>] + // serverid_spec ::= 'serverid' <serverid> + // pos_spec ::= 'pos' '(' <float> <float> <float> ')' + // dir_spec ::= 'dir' '(' <float> <float> <float> ')' + + ASSERT( sv_link_command != NULL ); + HANDLE_COMMAND_DOMAIN( sv_link_command ); + + // scan out all values to keys + if ( !ScanKeyValuePairs( sv_link_key_value, sv_link_command ) ) { + return TRUE; + } + + ASSERT( sv_link_key_value[ KEY_SERVERLINK_SERVERID ].value != NULL ); + int serverid; + ScanKeyValueInt( &sv_link_key_value[ KEY_SERVERLINK_SERVERID ], &serverid ); + if ( serverid == 0 ) { + CON_AddLine( "invalid server id specified" ); + } + + // parse position + Vector3 pos_spec; + if ( sv_link_key_value[ KEY_SERVERLINK_POS ].value != NULL ) { + if ( !ScanKeyValueFloatList( &sv_link_key_value[ KEY_SERVERLINK_POS ], (float*)&pos_spec.X, 3, 3 ) ) { + CON_AddLine( "position invalid" ); + return TRUE; + } + } else { + //FIXME: constants + pos_spec.X = ( RAND() % 1000 ) - 500; + pos_spec.Y = ( RAND() % 1000 ) - 500; + pos_spec.Z = ( RAND() % 1000 ) - 500; + pos_spec.VisibleFrame = 0; + } + + // parse direction + Vector3 dir_spec; + if ( sv_link_key_value[ KEY_SERVERLINK_DIR ].value != NULL ) { + if ( !ScanKeyValueFloatList( &sv_link_key_value[ KEY_SERVERLINK_DIR ], (float*)&dir_spec.X, 3, 3 ) ) { + CON_AddLine( "direction invalid" ); + return TRUE; + } + } else { + // default to point in z direction + dir_spec.X = 0.0f; + dir_spec.Y = 0.0f; + dir_spec.Z = 1.0f; + dir_spec.VisibleFrame = 0; + } + + // add the serverlink + TheServer->AddServerLink( serverid, &pos_spec, &dir_spec ); + + return TRUE; +} + + + +REGISTER_MODULE( E_GAMESERVER ) +{ + user_command_s regcom; + memset( ®com, 0, sizeof( user_command_s ) ); + + // register "sv.masterserver.conf" command + regcom.command = "sv.masterserver.conf"; + regcom.numparams = 1; + regcom.execute = Cmd_MASTERSERVER; + regcom.statedump = NULL; + CON_RegisterUserCommand( ®com ); + + // register "sv.conf" command + regcom.command = "sv.conf"; + regcom.numparams = 1; + regcom.execute = Cmd_SV_CONF; + regcom.statedump = NULL; + CON_RegisterUserCommand( ®com ); + + // register "sv.link" command + regcom.command = "sv.link"; + regcom.numparams = 0; + regcom.execute = Cmd_SV_LINK; + regcom.statedump = NULL; + CON_RegisterUserCommand( ®com ); +} + + + |
