diff options
Diffstat (limited to 'src/parsec_server')
75 files changed, 32379 insertions, 0 deletions
diff --git a/src/parsec_server/.cproject b/src/parsec_server/.cproject new file mode 100644 index 0000000..ba443c4 --- /dev/null +++ b/src/parsec_server/.cproject @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<?fileVersion 4.0.0?> + +<cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage"> + <storageModule moduleId="org.eclipse.cdt.core.settings"> + <cconfiguration id="cdt.managedbuild.toolchain.gnu.base.1655346401"> + <storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.base.1655346401" moduleId="org.eclipse.cdt.core.settings" name="Default"> + <externalSettings/> + <extensions> + <extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/> + <extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> + <extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/> + <extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> + <extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> + <extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> + </extensions> + </storageModule> + <storageModule moduleId="cdtBuildSystem" version="4.0.0"> + <configuration buildProperties="" id="cdt.managedbuild.toolchain.gnu.base.1655346401" name="Default" parent="org.eclipse.cdt.build.core.emptycfg"> + <folderInfo id="cdt.managedbuild.toolchain.gnu.base.1655346401.1191214679" name="/" resourcePath=""> + <toolChain id="cdt.managedbuild.toolchain.gnu.base.1696995543" name="cdt.managedbuild.toolchain.gnu.base" superClass="cdt.managedbuild.toolchain.gnu.base"> + <targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.target.gnu.platform.base.1837344144" name="Debug Platform" osList="linux,hpux,aix,qnx" superClass="cdt.managedbuild.target.gnu.platform.base"/> + <builder id="cdt.managedbuild.target.gnu.builder.base.1289008580" managedBuildOn="false" name="Gnu Make Builder.Default" superClass="cdt.managedbuild.target.gnu.builder.base"/> + <tool id="cdt.managedbuild.tool.gnu.archiver.base.1474756114" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/> + <tool id="cdt.managedbuild.tool.gnu.cpp.compiler.base.42832750" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base"/> + <tool id="cdt.managedbuild.tool.gnu.c.compiler.base.1673241698" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.base"/> + <tool id="cdt.managedbuild.tool.gnu.c.linker.base.1225980086" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.base"/> + <tool id="cdt.managedbuild.tool.gnu.cpp.linker.base.1601421533" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.base"/> + <tool id="cdt.managedbuild.tool.gnu.assembler.base.1344124816" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.base"/> + </toolChain> + </folderInfo> + </configuration> + </storageModule> + <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/> + </cconfiguration> + </storageModule> + <storageModule moduleId="cdtBuildSystem" version="4.0.0"> + <project id="OpenParsec_uberlinuxguy.null.358188525" name="OpenParsec_uberlinuxguy"/> + </storageModule> + <storageModule moduleId="scannerConfiguration"> + <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/> + </storageModule> + <storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/> +</cproject> diff --git a/src/parsec_server/.project b/src/parsec_server/.project new file mode 100644 index 0000000..5f7fdd4 --- /dev/null +++ b/src/parsec_server/.project @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>OpenParsec_uberlinuxguy</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name> + <triggers>clean,full,incremental,</triggers> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name> + <triggers>full,incremental,</triggers> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.cdt.core.cnature</nature> + <nature>org.eclipse.cdt.core.ccnature</nature> + <nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature> + <nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature> + </natures> +</projectDescription> diff --git a/src/parsec_server/MasterServer.cpp b/src/parsec_server/MasterServer.cpp new file mode 100644 index 0000000..41bef75 --- /dev/null +++ b/src/parsec_server/MasterServer.cpp @@ -0,0 +1,95 @@ +/* + * MasterServer.cpp + * + * Created on: Jan 2, 2013 + * Author: jasonw + */ +// C library +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <sys/types.h> +#include <sys/timeb.h> + +// C library +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <sys/types.h> +#include <sys/timeb.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_limits.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" + +#include "gd_help.h" + +#include "MasterServer.h" + +MasterServer::MasterServer() { + // TODO Auto-generated constructor stub + +} + +MasterServer::MasterServer(E_GameServer* gameserver) { + + +} + +MasterServer::~MasterServer() { + // TODO Auto-generated destructor stub +} + diff --git a/src/parsec_server/MasterServerItem.cpp b/src/parsec_server/MasterServerItem.cpp new file mode 100644 index 0000000..445e590 --- /dev/null +++ b/src/parsec_server/MasterServerItem.cpp @@ -0,0 +1,214 @@ +/* + * MasterServerList.cpp + * + * Created on: Jan 2, 2013 + * Author: jasonw + */ +// C library +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <sys/types.h> +#include <sys/timeb.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_limits.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" + +#include "gd_help.h" + + +#include "MasterServerItem.h" + +MasterServerItem::MasterServerItem() { + // TODO Auto-generated constructor stub + +} +MasterServerItem::MasterServerItem(int SrvID, int CurrPlayers, int MaxPlayers, + int PMajor, int PMinor, char ServerName[MAX_SERVER_NAME + 1], + char OS[MAX_OSNAME_LEN + 1], node_t *node) { + + _SrvID = SrvID; + _CurrPlayers = CurrPlayers; + _MaxPlayers = MaxPlayers; + _PMajor = PMajor; + _PMinor = PMinor; + strncpy(_ServerName, ServerName, MAX_SERVER_NAME); + _ServerName[MAX_SERVER_NAME] = '\0'; + strncpy(_OS, OS, MAX_OSNAME_LEN); + _OS[MAX_OSNAME_LEN] = '\0'; + NODE_Copy(&_Node, node); + +} +MasterServerItem::~MasterServerItem() { + // TODO Auto-generated destructor stub +} + +bool MasterServerItem::update(int SrvID, int CurrPlayers, int MaxPlayers, + int PMajor, int PMinor, char ServerName[MAX_SERVER_NAME + 1], + char OS[MAX_OSNAME_LEN + 1], node_t *node) { + + _SrvID = SrvID; + _CurrPlayers = CurrPlayers; + _MaxPlayers = MaxPlayers; + _PMajor = PMajor; + _PMinor = PMinor; + SAFE_STR_DUPLICATE(_ServerName, ServerName, MAX_SERVER_NAME-1); + _ServerName[MAX_SERVER_NAME] = '\0'; + SAFE_STR_DUPLICATE(_OS, OS, MAX_OSNAME_LEN-1); + _OS[MAX_OSNAME_LEN] = '\0'; + NODE_Copy(&_Node, node); + + + return true; +} + +bool MasterServerItem::remove() { + + // set the SrvID to -1 to indicate it should be removed. + _SrvID = -1; + + return false; +} + +bool MasterServerItem::operator <(const MasterServerItem& msl) const { + + return false; +} + +bool MasterServerItem::operator ==(const MasterServerItem& msl) const { + + return false; +} + +bool MasterServerItem::operator !=(const MasterServerItem& msl) const { + + return false; +} + + + +bool MasterServerItem::isValid() const { + if(_SrvID > -1) { + return true; + } + return false; +} + +int MasterServerItem::GetSrvID() { + return _SrvID; +} + +int MasterServerItem::GetCurrPlayers() { + return _CurrPlayers; +} + +int MasterServerItem::GetMaxPlayers() { + return _MaxPlayers; +} + +int MasterServerItem::GetPMajor() { + return _PMajor; +} + +int MasterServerItem::GetPMinor() { + return _PMinor; +} + +int MasterServerItem::GetServerName(char *buffer, int buffer_sz) { + + int str_sz = strlen(_ServerName); + if(buffer_sz < str_sz + 1){ + return -1; + } + + strncpy(buffer, _ServerName, str_sz); + buffer[str_sz + 1] = '\0'; + + return 0; +} + +int MasterServerItem::GetOS(char *buffer, int buffer_sz) { + int str_sz = strlen(_OS); + if(buffer_sz < str_sz + 1){ + return -1; + } + + strncpy(buffer, _OS, str_sz); + buffer[str_sz + 1] = '\0'; + + return 0; +} + +MasterServerItem::MasterServerItem(const MasterServerItem& msi_copy) : + _SrvID (msi_copy._SrvID), + _CurrPlayers (msi_copy._CurrPlayers), + _MaxPlayers (msi_copy._MaxPlayers), + _PMajor (msi_copy._PMajor), + _PMinor (msi_copy._PMinor) +{ + strncpy(_ServerName, msi_copy._OS, MAX_SERVER_NAME); + _ServerName[MAX_SERVER_NAME]='\0'; + strncpy( _OS, msi_copy._OS, MAX_OSNAME_LEN); + _OS[MAX_OSNAME_LEN]='\0'; + memcpy(&_Node, &msi_copy._Node, sizeof( node_t )); + +} + +int MasterServerItem::GetNode(node_t* node) { + NODE_Copy(node, &_Node); + + return true; +} diff --git a/src/parsec_server/con_act_sv.cpp b/src/parsec_server/con_act_sv.cpp new file mode 100755 index 0000000..98de7b3 --- /dev/null +++ b/src/parsec_server/con_act_sv.cpp @@ -0,0 +1,3681 @@ +/* + * PARSEC - Server Action Commands + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:47 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2002 + * Copyright (c) Markus Hadwiger <msh@parsec.org> 1997-2000 + * + * 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 <math.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "objstruc.h" + +// global externals +#include "globals.h" +#include "e_world_trans.h" + +// subsystem headers +//#include "aud_defs.h" +//#include "inp_defs.h" +#include "net_defs.h" +#include "sys_defs.h" +//#include "vid_defs.h" + +// mathematics header +#include "utl_math.h" + +// local module header +#include "con_act_sv.h" + +// proprietary module headers +#include "con_aux_sv.h" +#include "con_ext_sv.h" +#include "con_int_sv.h" +#include "con_main_sv.h" +//#include "e_demo.h" +//#include "e_record.h" +//#include "e_replay.h" +//#include "e_supp.h" +//#include "g_camera.h" +//#include "g_supp.h" +//#include "h_drwhud.h" +//#include "h_supp.h" +//#include "h_text.h" +//#include "m_main.h" +//#include "net_game.h" +#ifdef LINKED_PROTOCOL_GAMESERVER +// #include "net_serv.h" +#endif // LINKED_PROTOCOL_GAMESERVER +#include "obj_clas.h" +//#include "obj_ctrl.h" +//#include "obj_xtra.h" +//#include "part_sfx.h" +//#include "part_wfx.h" +//#include "g_emp.h" +#include "sys_file.h" + + +// flags +#define REINIT_LOCAL_SHIP_ON_PREP_RESTORE +#define RECOVER_TARGET_ON_MISSILE_CREATION + + + +// generic string paste area -------------------------------------------------- +// +#define PASTE_STR_LEN 255 +static char paste_str[ PASTE_STR_LEN + 1 ]; + + +// string constants ----------------------------------------------------------- +// +static char invalid_key_id[] = "invalid key identifier."; +static char no_batch_open[] = "no script open."; +static char too_many_pseudos[] = "too many pseudo stars."; +static char no_objcam_allowed[] = "object camera must not be active."; +static char invalid_list[] = "invalid list identifier."; +static char invalid_class[] = "invalid object class identifier."; +static char invalid_action[] = "invalid action."; +static char invalid_id_range[] = "object class id out of range."; +static char no_action_found[] = "no action specified."; +static char non_batch_use[] = "only valid if called from command script."; +static char stream_name_inval[] = "stream name is invalid."; +static char no_stream_started[] = "stream could not be started."; +static char no_stream_stopped[] = "no stream has been stopped."; +static char action_preempted[] = "action command not allowed."; +static char compile_write_err[] = "write error during compilation."; +static char compile_arg_inval[] = "invalid argument found during compilation."; +static char compile_non_comp[] = "non-compilable command found during compilation."; +static char compile_pkt_inval[] = "packet not found during compilation."; +static char only_for_compile[] = "command is only for compilation."; +static char invalid_bin_wait[] = "invalid binary wait argument encountered."; +static char maxplayers_diff[] = "netplayers limit is different from recording."; + + +// pointer to binary parameters ----------------------------------------------- +// +static char *bin_params = NULL; + + +// swap array of int values --------------------------------------------------- +// +INLINE +void SwapParams_int( int num, int32 *source, int32 *dest ) +{ + +#ifdef SYSTEM_BIG_ENDIAN + + for ( int indx = 0; indx < num; indx++ ) { + dest[ indx ] = SWAP_32( source[ indx ] ); + } + +#else + + memcpy( dest, source, sizeof( int32 ) * num ); + +#endif + +} + + +// swap array of hprec_t values ----------------------------------------------- +// +INLINE +void SwapParams_hprec( int num, hprec_t *source, hprec_t *dest ) +{ + +#ifdef SYSTEM_BIG_ENDIAN + + for ( int indx = 0; indx < num; indx++ ) { + + dword *s = (dword *) &source[ indx ]; + dword *d = (dword *) &dest[ indx ]; + + d[ 0 ] = SWAP_32( s[ 1 ] ); + d[ 1 ] = SWAP_32( s[ 0 ] ); + } + +#else + + memcpy( dest, source, sizeof( hprec_t ) * num ); + +#endif + +} + + +// allow no arguments at all instead of all arguments (one-shot flag!!) ------- +// +static int silent_missing_args = FALSE; // all args missing allowed +static int silent_missing_ok; // args were missing, not invalid + + +// check single parameter for action command ---------------------------------- +// +PRIVATE +int AcFetchIntParams( int num, int32 *params ) +{ + ASSERT( num > 0 ); + ASSERT( params != NULL ); + + // assume incorrectly missing arg + silent_missing_ok = FALSE; + + // read parameters binary if possible + if ( bin_params != NULL ) { + SwapParams_int( num, (int32 *) bin_params, params ); + silent_missing_args = FALSE; + return TRUE; + } + + // read specified number of int parameters + while ( num-- ) { + + char *nextparam = strtok( NULL, " " ); + if ( nextparam == NULL ) { + if ( !silent_missing_args ) + CON_AddLine( arg_missing ); + silent_missing_ok = silent_missing_args; + silent_missing_args = FALSE; + return FALSE; + } + + // disabling after first argument enforces either no + // arguments at all, or the entire specified number + // of arguments have to be specified + silent_missing_args = FALSE; + + char *errpart; + int32 ival = (int32) strtol( nextparam, &errpart, int_calc_base ); + + if ( *errpart != 0 ) { + CON_AddLine( invalid_arg ); + return FALSE; + } + + *params++ = ival; + } + + // make sure flag is reset + ASSERT( !silent_missing_args ); + silent_missing_args = FALSE; + + if ( strtok( NULL, " " ) != NULL ) { + CON_AddLine( too_many_args ); + return FALSE; + } + + return TRUE; +} + + +// check float parameters for action command ---------------------------------- +// +PRIVATE +int AcFetchFloatParams( int num, hprec_t *params ) +{ + ASSERT( num > 0 ); + ASSERT( params != NULL ); + + // assume incorrectly missing arg + silent_missing_ok = TRUE; + + // read parameters binary if possible + if ( bin_params != NULL ) { + SwapParams_hprec( num, (hprec_t *) bin_params, params ); + silent_missing_args = FALSE; + return TRUE; + } + + // read specified number of high precision float parameters + while ( num-- ) { + + char *nextparam = strtok( NULL, " " ); + if ( nextparam == NULL ) { + if ( !silent_missing_args ) + CON_AddLine( arg_missing ); + silent_missing_ok = silent_missing_args; + silent_missing_args = FALSE; + return FALSE; + } + + // disabling after first argument enforces either no + // arguments at all, or the entire specified number + // of arguments have to be specified + silent_missing_args = FALSE; + + char *errpart; + hprec_t dval = strtod( nextparam, &errpart ); + + if ( *errpart != 0 ) { + CON_AddLine( invalid_arg ); + return FALSE; + } + + *params++ = dval; + } + + // make sure flag is reset + ASSERT( !silent_missing_args ); + silent_missing_args = FALSE; + + if ( strtok( NULL, " " ) != NULL ) { + CON_AddLine( too_many_args ); + return FALSE; + } + + return TRUE; +} + + +// check string parameter for action command ---------------------------------- +// +PRIVATE +int AcFetchStringParam( char **param, int stripspace ) +{ + ASSERT( param != NULL ); + + // no invalid args + silent_missing_ok = TRUE; + + // read parameter binary if possible + if ( bin_params != NULL ) { + *param = bin_params; + silent_missing_args = FALSE; + return TRUE; + } + + char *nextparam = strtok( NULL, "" ); + if ( nextparam != NULL ) { + + if ( stripspace ) { + + // eat whitespace + char *scan = nextparam; + while ( *scan == ' ' ) + scan++; + + // argument found? + if ( *scan != 0 ) { + + nextparam = scan; + + // cut off trailing whitespace + while ( *scan != 0 ) + scan++; + while ( *--scan == ' ' ) + {} + *( scan + 1 ) = 0; + + } else { + + if ( !silent_missing_args ) + CON_AddLine( arg_missing ); + nextparam = NULL; + } + } + + } else { + + if ( !silent_missing_args ) { + CON_AddLine( arg_missing ); + } + } + + // reset flag + silent_missing_args = FALSE; + + *param = nextparam; + return ( nextparam != NULL ); +} + + +// set automatic pitch (global variable) -------------------------------------- +// +PRIVATE +void AcAutoPitch() +{ + /*bams_t angle; + if ( AcFetchIntParams( 1, &angle ) ) { + AutomaticPitch = angle; + }*/ +} + + +// set automatic yaw (global variable) ---------------------------------------- +// +PRIVATE +void AcAutoYaw() +{ + /*bams_t angle; + if ( AcFetchIntParams( 1, &angle ) ) { + AutomaticYaw = angle; + }*/ +} + + +// set automatic roll (global variable) --------------------------------------- +// +PRIVATE +void AcAutoRoll() +{ + /*bams_t angle; + if ( AcFetchIntParams( 1, &angle ) ) { + AutomaticRoll = angle; + }*/ +} + + +// set automatic horizontal slide (global variable) --------------------------- +// +PRIVATE +void AcAutoSlideX() +{ + /*hprec_t slideval; + if ( AcFetchFloatParams( 1, &slideval ) ) { + AutomaticSlideHorz = FLOAT_TO_GEOMV( slideval ); + }*/ +} + + +// set automatic vertical slide (global variable) ----------------------------- +// +PRIVATE +void AcAutoSlideY() +{ + /*hprec_t slideval; + if ( AcFetchFloatParams( 1, &slideval ) ) { + AutomaticSlideVert = FLOAT_TO_GEOMV( slideval ); + }*/ +} + + +// set automatic movement (global variable and MyShip's speed) ---------------- +// +PRIVATE +void AcAutoMove() +{ + /*fixed_t translation; + if ( AcFetchIntParams( 1, &translation ) ) { + AutomaticMovement = translation; + MyShip->CurSpeed = AutomaticMovement; + }*/ +} + + +// set idle wait count -------------------------------------------------------- +// +PRIVATE +void AcIdleCommand() +{ + /*int32 waitcount; + if ( AcFetchIntParams( 1, &waitcount ) ) { + + // set duration to wait for + ASSERT( waitcount >= 0 ); + CurActionWait = waitcount; + + // remember for info + demoinfo_lastwait = (refframe_t) waitcount; + + // save level recursion is stalled on + idle_reclevel = rec_depth; + }*/ +} + + +// flag for view camera filter reset after matrix init ------------------------ +// +static int reset_viewcam_filter_on_update = FALSE; + + +// set single column of camera matrix ----------------------------------------- +// +INLINE +void SetCameraColumn( Camera cam, int col, hprec_t *v ) +{ + cam[ 0 ][ col ] = FLOAT_TO_GEOMV( v[ 0 ] ); + cam[ 1 ][ col ] = FLOAT_TO_GEOMV( v[ 1 ] ); + cam[ 2 ][ col ] = FLOAT_TO_GEOMV( v[ 2 ] ); +} + + +// set column #1 of camera matrix --------------------------------------------- +// +PRIVATE +void AcSetCamRotCol1() +{ + /*hprec_t v[ 3 ]; + if ( AcFetchFloatParams( 3, v ) ) { + SetCameraColumn( ShipViewCamera, 0, v ); + }*/ +} + + +// set column #2 of camera matrix --------------------------------------------- +// +PRIVATE +void AcSetCamRotCol2() +{ + /*hprec_t v[ 3 ]; + if ( AcFetchFloatParams( 3, v ) ) { + SetCameraColumn( ShipViewCamera, 1, v ); + }*/ +} + + +// set column #3 of camera matrix --------------------------------------------- +// +PRIVATE +void AcSetCamRotCol3() +{ + /*hprec_t v[ 3 ]; + if ( AcFetchFloatParams( 3, v ) ) { + SetCameraColumn( ShipViewCamera, 2, v ); + }*/ +} + + +// set column #4 of camera matrix --------------------------------------------- +// +PRIVATE +void AcSetCamOrigin() +{ + /*hprec_t v[ 3 ]; + if ( AcFetchFloatParams( 3, v ) ) { + SetCameraColumn( ShipViewCamera, 3, v ); + } + + // reset view camera smoothing filter if flag set + // (must be used if matrix is updated discontinuously) + if ( reset_viewcam_filter_on_update ) { + CAMERA_ResetFilter(); + reset_viewcam_filter_on_update = FALSE; + }*/ +} + + +// set single column of object position matrix -------------------------------- +// +PRIVATE +void SetObjectMatrxCol( int listid, int col, geomv_t x, geomv_t y, geomv_t z ) +{ + GenObject *obj = NULL; + + switch ( listid ) { + + case PSHIP_LIST_NO: + obj = (GenObject *) FetchFirstShip(); + break; + + case LASER_LIST_NO: + obj = (GenObject *) FetchFirstLaser(); + break; + + case MISSL_LIST_NO: + obj = (GenObject *) FetchFirstMissile(); + break; + + case EXTRA_LIST_NO: + obj = (GenObject *) FetchFirstExtra(); + break; + + case CUSTM_LIST_NO: + obj = (GenObject *) FetchFirstCustom(); + break; + } + + if ( obj != NULL ) { + obj->ObjPosition[ 0 ][ col ] = x; + obj->ObjPosition[ 1 ][ col ] = y; + obj->ObjPosition[ 2 ][ col ] = z; + } else { + CON_AddLine( invalid_list ); + } +} + + +// mapping table for class ids in demo to corresponding current ids ----------- +// +static dword class_map_id_to_name[ MAX_DISTINCT_OBJCLASSES ]; +static dword class_map_old_id; + + +// reset mapping table for class ids ------------------------------------------ +// +void ResetClassMapTable() +{ + // needed to preserve table across multiple + // demos that are played back-to-back + if ( SV_CLASS_MAP_TABLE_DISABLE_RESET ) + return; + + class_map_old_id = CLASS_ID_INVALID; + + for ( int cid = 0; cid < MAX_DISTINCT_OBJCLASSES; cid++ ) { + class_map_id_to_name[ cid ] = CLASS_ID_INVALID; + } +} + + +// set class id for which class name will be specified ------------------------ +// +void AcClassMapId() +{ + int32 classid; + if ( AcFetchIntParams( 1, &classid ) ) { + if ( (dword)classid < MAX_DISTINCT_OBJCLASSES ) { + class_map_old_id = classid; + } else { + class_map_old_id = CLASS_ID_INVALID; + } + } +} + + +// map class name to current id and set map to corresponding current id ------- +// +void AcClassMapName() +{ + char *classname; + if ( AcFetchStringParam( &classname, ACM_EAT_SPACE( ACM_CLASSMAPNAME ) ) ) { + + dword classid = OBJ_FetchObjectClassId( classname ); + if ( class_map_old_id != CLASS_ID_INVALID ) { + ASSERT( class_map_old_id < MAX_DISTINCT_OBJCLASSES ); + class_map_id_to_name[ class_map_old_id ] = classid; + } + } +} + + +// execute recorded object creation ------------------------------------------- +// +PRIVATE +GenObject *CreateRecordedObject( dword objclass, Xmatrx startmatrx ) +{ + ASSERT( startmatrx != NULL ); + + if ( objclass >= MAX_DISTINCT_OBJCLASSES ) { + CON_AddLine( invalid_id_range ); + return NULL; + } + + // map the recorded class id to the corresponding current class id + if ( class_map_id_to_name[ objclass ] != CLASS_ID_INVALID ) { + objclass = class_map_id_to_name[ objclass ]; + } + + // create object corresponding to original class +#ifdef PARSEC_SERVER + GenObject *obj = CreateObject( objclass, startmatrx, PLAYERID_SERVER ); +#else + GenObject *obj = CreateObject( objclass, startmatrx, LocalPlayerId ); +#endif // PARSEC_SERVER + if ( obj == NULL ) { + + CON_AddLine( invalid_class ); + + } else { + + // play sound effects for certain types + switch ( obj->ObjectType ) { + + case LASER1TYPE: + case LASER2TYPE: + case LASER3TYPE: + //AUD_Laser( obj ); + break; + + case MISSILE1TYPE: + case MISSILE4TYPE: + //AUD_Missile( obj ); + break; + + case MINE1TYPE: + //AUD_Mine( obj ); + break; + } + } + + return obj; +} + + +// for object creation on "ac.objmatrxcol4" instead of "ac.creatobject" ------- +// +static int delayed_object_creation = FALSE; +static dword delayed_object_class; +static dword delayed_object_id; +static dword delayed_object_hostid; +static Xmatrx delayed_object_matrix; + + +// execute delayed creation of recorded object -------------------------------- +// +INLINE +void DelayedObjectCreation() +{ + //NOTE: + // delayed object creation ensures that the objposition matrix + // is already valid on call of OBJ_CTRL::CreateObject(). + + // create object on setting of last matrix column + if ( delayed_object_creation ) { + delayed_object_creation = FALSE; + + GenObject *obj = + CreateRecordedObject( delayed_object_class, delayed_object_matrix ); + + if ( obj != NULL ) { +/* + if ( delayed_object_id != -1 ) { + obj->ObjectNumber = delayed_object_id; + } +*/ + if ( delayed_object_hostid != (dword)-1 ) { + obj->HostObjNumber = delayed_object_hostid; + } + } + } +} + + +// set single column of object position matrix -------------------------------- +// +INLINE +void SetObjMCol( int col ) +{ + hprec_t v[ 4 ]; + if ( AcFetchFloatParams( 4, v ) ) { + + if ( delayed_object_creation ) { + // remember for later use + delayed_object_matrix[ 0 ][ col ] = FLOAT_TO_GEOMV( v[ 1 ] ); + delayed_object_matrix[ 1 ][ col ] = FLOAT_TO_GEOMV( v[ 2 ] ); + delayed_object_matrix[ 2 ][ col ] = FLOAT_TO_GEOMV( v[ 3 ] ); + } else { + // set column directly in object structure + SetObjectMatrxCol( (int) v[ 0 ], col, + FLOAT_TO_GEOMV( v[ 1 ] ), + FLOAT_TO_GEOMV( v[ 2 ] ), + FLOAT_TO_GEOMV( v[ 3 ] ) ); + } + } +} + + +// set single column of object position matrix -------------------------------- +// +PRIVATE +void AcObjectMatrxColumn1() +{ + SetObjMCol( 0 ); +} + +PRIVATE +void AcObjectMatrxColumn2() +{ + SetObjMCol( 1 ); +} + +PRIVATE +void AcObjectMatrxColumn3() +{ + SetObjMCol( 2 ); +} + +PRIVATE +void AcObjectMatrxColumn4() +{ + SetObjMCol( 3 ); + DelayedObjectCreation(); +} + + +// set column #1 of fixed star matrix ----------------------------------------- +// +PRIVATE +void AcSetFixedStarRotCol1() +{ + // dummy for compatibility +} + + +// set column #2 of fixed star matrix ----------------------------------------- +// +PRIVATE +void AcSetFixedStarRotCol2() +{ + // dummy for compatibility +} + + +// set column #3 of fixed star matrix ----------------------------------------- +// +PRIVATE +void AcSetFixedStarRotCol3() +{ + // dummy for compatibility +} + + +// create object of specified class ------------------------------------------- +// +PRIVATE +void AcCreateObject() +{ + /*ASSERT( !delayed_object_creation ); + + int32 objclass; + if ( AcFetchIntParams( 1, &objclass ) ) { + + if ( AUX_NO_DELAYED_CREATE_OBJECT_REPLAY ) { + + // create on "ac.creatobject" + Xmatrx im; + MakeIdMatrx( im ); + CreateRecordedObject( objclass, im ); + + } else { + + // create later on "ac.objmatrxcol4" + delayed_object_creation = TRUE; + delayed_object_class = objclass; + delayed_object_id = -1; // not yet set + delayed_object_hostid = -1; // not yet set + } + }*/ +} + + +// delete all objects from specified object list ------------------------------ +// +PRIVATE +void AcDestroyObjectList() +{ + int32 listid; + if ( AcFetchIntParams( 1, &listid ) ) { + + switch ( listid ) { + + case PSHIP_LIST_NO: + FreeObjList( PShipObjects ); + break; + + case LASER_LIST_NO: + FreeObjList( LaserObjects ); + break; + + case MISSL_LIST_NO: + FreeObjList( MisslObjects ); + break; + + case EXTRA_LIST_NO: + FreeObjList( ExtraObjects ); + break; + + case CUSTM_LIST_NO: + FreeObjList( CustmObjects ); + break; + + default: + CON_AddLine( invalid_list ); + } + } +} + + +// write line 0 in small letters ---------------------------------------------- +// +PRIVATE +void AcWriteSmall() +{ + /*char *nextparam; + if ( AcFetchStringParam( &nextparam, ACM_EAT_SPACE( ACM_WRITESMALL ) ) ) { + HUD_SetDemoText( 0, nextparam ); + }*/ +} + + +// write line 1 in capital letters -------------------------------------------- +// +PRIVATE +void AcWriteBig() +{ + /*char *nextparam; + if ( AcFetchStringParam( &nextparam, ACM_EAT_SPACE( ACM_WRITEBIG1 ) ) ) { + HUD_SetDemoText( 1, nextparam ); + }*/ +} + + +// write line 2 in capital letters -------------------------------------------- +// +PRIVATE +void AcWriteBig2() +{ + /*char *nextparam; + if ( AcFetchStringParam( &nextparam, ACM_EAT_SPACE( ACM_WRITEBIG2 ) ) ) { + HUD_SetDemoText( 2, nextparam ); + }*/ +} + + +// write line 3 in capital letters -------------------------------------------- +// +PRIVATE +void AcWriteBig3() +{ + /*char *nextparam; + if ( AcFetchStringParam( &nextparam, ACM_EAT_SPACE( ACM_WRITEBIG3 ) ) ) { + HUD_SetDemoText( 3, nextparam ); + }*/ +} + + +// scroll demo text out ------------------------------------------------------- +// +PRIVATE +void AcClearText() +{ + //HUD_ClearDemoText(); +} + + +// set global maximum number of remote players (NET_GLOB::CurMaxPlayers) ------ +// +PRIVATE +void AcRemoteMaxPlayers() +{ + /*int32 maxnumplayers; + if ( AcFetchIntParams( 1, &maxnumplayers ) ) { + + if ( NetConnected == NETWORK_GAME_SIMULATED ) { + // simply set it + CurMaxPlayers = maxnumplayers; + } else { + // can't set it, display warning if different + if ( CurMaxPlayers != maxnumplayers ) { + CON_AddLine( maxplayers_diff ); + } + } + }*/ +} + + +// remember id of remote player whose state was set last ---------------------- +// +static int last_remote_playerid = 0; + + +// create remote player with specified state ---------------------------------- +// +PRIVATE +void AcRemotePlayer() +{ + /* + + //NOTE: + // if this is invoked specifying the local player, the + // local id may change. this has to happen in order to + // restore a consistent state with respect to the state + // during recording. the state of the local player will + // also be used to determine whether to activate or + // deactivate entry-mode automatically. + + int32 params[ 4 ]; + if ( AcFetchIntParams( 4, params ) ) { + + // remember id for AcRemoteMatrix(), AcRemoteName(), + // and AcRemoteWeaponsActive() + last_remote_playerid = params[ 0 ]; + + // init state except matrix and name + NET_SetRemotePlayerState( params[ 0 ], params[ 1 ], params[ 2 ], params[ 3 ] ); + } + + */ +} + + +// set remote player matrix --------------------------------------------------- +// +PRIVATE +void AcRemoteMatrix() +{ + /*if ( !NetConnected ) { + CON_AddLine( "AcRemoteMatrix(): no net." ); + return; + } + + hprec_t v[ 12 ]; + if ( AcFetchFloatParams( 12, v ) ) { + + Xmatrx matrix; + matrix[ 0 ][ 0 ] = FLOAT_TO_GEOMV( v[ 0 ] ); + matrix[ 0 ][ 1 ] = FLOAT_TO_GEOMV( v[ 1 ] ); + matrix[ 0 ][ 2 ] = FLOAT_TO_GEOMV( v[ 2 ] ); + matrix[ 0 ][ 3 ] = FLOAT_TO_GEOMV( v[ 3 ] ); + matrix[ 1 ][ 0 ] = FLOAT_TO_GEOMV( v[ 4 ] ); + matrix[ 1 ][ 1 ] = FLOAT_TO_GEOMV( v[ 5 ] ); + matrix[ 1 ][ 2 ] = FLOAT_TO_GEOMV( v[ 6 ] ); + matrix[ 1 ][ 3 ] = FLOAT_TO_GEOMV( v[ 7 ] ); + matrix[ 2 ][ 0 ] = FLOAT_TO_GEOMV( v[ 8 ] ); + matrix[ 2 ][ 1 ] = FLOAT_TO_GEOMV( v[ 9 ] ); + matrix[ 2 ][ 2 ] = FLOAT_TO_GEOMV( v[ 10 ] ); + matrix[ 2 ][ 3 ] = FLOAT_TO_GEOMV( v[ 11 ] ); + + // set matrix of remote player (using id set by AcRemotePlayer()) + NET_SetRemotePlayerMatrix( last_remote_playerid, matrix ); + + // for local player additionally set view camera + if ( Player_ShipId[ last_remote_playerid ] == SHIPID_LOCALPLAYER ) { + + ASSERT( LocalPlayerId == last_remote_playerid ); + ASSERT( Player_Ship[ last_remote_playerid ] == MyShip ); + + ObjCamOff(); + CalcOrthoInverse( MyShip->ObjPosition, DestXmatrx ); + memcpy( ShipViewCamera, DestXmatrx, sizeof( Camera ) ); + CAMERA_ResetFilter(); + } + }*/ +} + + +// set remote player weapons state -------------------------------------------- +// +PRIVATE +void AcRemoteWeaponsActive() +{ + /* + //NOTE: + // if new duration weapons are added this function + // should be updated. + + if ( !NetConnected ) { + CON_AddLine( "AcRemoteWeaponsActive(): no net." ); + return; + } + + int32 weaponsactive; + if ( AcFetchIntParams( 1, &weaponsactive ) ) { + + ASSERT( Player_Ship[ last_remote_playerid ] != NULL ); + ASSERT( Player_ShipId[ last_remote_playerid ] != SHIPID_LOCALPLAYER ); + + ShipObject *shippo = (ShipObject *) Player_Ship[ last_remote_playerid ]; + ASSERT( shippo != NULL ); + ASSERT( shippo != MyShip ); + + // check for lightning state change + dword oldstate = shippo->WeaponsActive & WPMASK_CANNON_LIGHTNING; + dword newstate = weaponsactive & WPMASK_CANNON_LIGHTNING; + if ( ( oldstate == 0 ) && ( newstate != 0 ) ) { + WFX_RemoteActivateLightning( last_remote_playerid ); + } else if ( ( oldstate != 0 ) && ( newstate == 0 ) ) { + WFX_RemoteDeactivateLightning( last_remote_playerid ); + } + + // check for helix state change + oldstate = shippo->WeaponsActive & WPMASK_CANNON_HELIX; + newstate = weaponsactive & WPMASK_CANNON_HELIX; + if ( ( oldstate == 0 ) && ( newstate != 0 ) ) { + WFX_RemoteActivateHelix( last_remote_playerid ); + } else if ( ( oldstate != 0 ) && ( newstate == 0 ) ) { + WFX_RemoteDeactivateHelix( last_remote_playerid ); + } + + // check for photon state change + oldstate = shippo->WeaponsActive & WPMASK_CANNON_PHOTON; + newstate = weaponsactive & WPMASK_CANNON_PHOTON; + if ( ( oldstate == 0 ) && ( newstate != 0 ) ) { + WFX_RemoteActivatePhoton( last_remote_playerid ); + } else if ( ( oldstate != 0 ) && ( newstate == 0 ) ) { + WFX_RemoteDeactivatePhoton( last_remote_playerid ); + } + + // check for emp state change + oldstate = shippo->WeaponsActive & WPMASK_DEVICE_EMP; + newstate = weaponsactive & WPMASK_DEVICE_EMP; + if ( ( oldstate == 0 ) && ( newstate != 0 ) ) { + WFX_RemoteActivateEmp( last_remote_playerid ); + } else if ( ( oldstate != 0 ) && ( newstate == 0 ) ) { + WFX_RemoteDeactivateEmp( last_remote_playerid ); + } + + // fallback for unknown duration weapons + // (simply set/reset activation bits) + dword unknownmask = 0xffffffff; + unknownmask &= ~WPMASK_CANNON_LIGHTNING; + unknownmask &= ~WPMASK_CANNON_HELIX; + unknownmask &= ~WPMASK_CANNON_PHOTON; + unknownmask &= ~WPMASK_DEVICE_EMP; + shippo->WeaponsActive &= ~unknownmask; + shippo->WeaponsActive |= weaponsactive & unknownmask; + } + */ +} + + +// should a join be performed on the next "ac.remotename" ? ------------------- +// +static int join_on_setname = FALSE; + + +// perform recorded join ------------------------------------------------------ +// +PRIVATE +void PerformRecordedJoin() +{ +/* if ( !NetJoined ) { + HideFloatingMenu(); + NETs_Join(); + EntryMode = FALSE; + InFloatingMenu = FALSE; + } + + ASSERT( EntryMode == FALSE ); + ASSERT( InFloatingMenu == FALSE ); +*/ +} + + +// set remote player name ----------------------------------------------------- +// +PRIVATE +void AcRemoteName() +{ + /*if ( !NetConnected ) { + CON_AddLine( "AcRemoteName(): no net." ); + return; + } + + char *nextparam; + if ( AcFetchStringParam( &nextparam, ACM_EAT_SPACE( ACM_REMOTENAME ) ) ) { + + // set name of remote player (using id set by AcRemotePlayer()) + NET_SetRemotePlayerName( last_remote_playerid, nextparam ); + } + + // perform delayed join + if ( join_on_setname ) { + join_on_setname = FALSE; + PerformRecordedJoin(); + }*/ +} + + +// perform join for local player ---------------------------------------------- +// +PRIVATE +void AcRemoteJoin() +{ + /*ASSERT( !join_on_setname ); + + int32 params[ 3 ]; + if ( AcFetchIntParams( 3, params ) ) { + + ASSERT( LocalPlayerId == params[ 0 ] ); + + //NOTE: + // player position has to be set by following "ac.remotematrix". + + // remember id for AcRemoteMatrix(), AcRemoteName(), + // and AcRemoteWeaponsActive() + last_remote_playerid = params[ 0 ]; + + // prepare local ship for join + InitJoinReplay(); + + if ( AUX_NO_DELAYED_JOIN_REPLAY ) { + PerformRecordedJoin(); + } else { + join_on_setname = TRUE; + } + }*/ +} + + +// perform unjoin for local player -------------------------------------------- +// +PRIVATE +void AcRemoteUnjoin() +{ + /*ASSERT( !join_on_setname ); + + if ( !NetConnected ) { + CON_AddLine( "AcRemoteUnjoin(): no net." ); + return; + } + + int32 params[ 3 ]; + if ( AcFetchIntParams( 3, params ) ) { + + ASSERT( LocalPlayerId == params[ 0 ] ); + + // set flags if not already unjoined + if ( NetJoined ) { + ExitGameLoop = 2; + if ( params[ 1 ] == SHIP_DOWNED ) { + ShipDowned = TRUE; + AUD_PlayerKilled(); + } else { + ShipDowned = FALSE; + } + } + }*/ +} + + +// clear remnants of demo replay ---------------------------------------------- +// +PRIVATE +void AcClearDemo() +{ +/* //NOTE: + // this function has to be used after a demo containing network packets + // has been replayed (and ended) to restore/ensure a proper game state. + + //NOTE: + // if the network mode is not active or the net simulation has already + // been stopped (e.g., the demo has finished) the network state will + // be left untouched (i.e., not reset!). + + ASSERT( !join_on_setname ); + + // specify optional argument flag + silent_missing_args = TRUE; + + // default is stop demo if running + int32 stopflag = 1; + AcFetchIntParams( 1, &stopflag ); + + //NOTE: + // ( silent_missing_ok == FALSE ) is alright here, since an error + // message will already have been displayed and we only wanted to + // override a default value anyway. + + // perform actual clear + DEMO_ClearDemo( stopflag ); +*/ +} + + +// start playback of recorded remote packet ----------------------------------- +// +PRIVATE +void AcPlayRemotePacket() +{ +/* int32 params[ 2 ]; + if ( AcFetchIntParams( 2, params ) ) { + + REC_PlayRemotePacket( params[ 0 ], params[ 1 ] ); + } +*/ +} + + +// replay recorded server message --------------------------------------------- +// +PRIVATE +void AcPlayServerMessage() +{ +/* char *nextparam; + if ( AcFetchStringParam( &nextparam, ACM_EAT_SPACE( ACM_SERVERMSG ) ) ) { + +#ifdef LINKED_PROTOCOL_GAMESERVER + if ( NET_ProtocolGMSV() ) { + NET_ServerParseMessage( nextparam ); + } else { + DBGTXT( MSGOUT( "AcPlayServerMessage(): in non GMSV mode !" ); ); + } +#endif // LINKED_PROTOCOL_GAMESERVER + + } +*/ +} + + +// display message in console ------------------------------------------------- +// +PRIVATE +void AcWriteConsoleText() +{ + /*char *msgtext; + if ( AcFetchStringParam( &msgtext, ACM_EAT_SPACE( ACM_WRITECONSOLETEXT ) ) ) { + + CON_AddLine( msgtext ); + }*/ +} + + +// display message in message area -------------------------------------------- +// +PRIVATE +void AcDisplayMessage() +{ + /*char *msgtext; + if ( AcFetchStringParam( &msgtext, ACM_EAT_SPACE( ACM_DISPLAYMESSAGE ) ) ) { + + ShowMessage( msgtext ); + }*/ +} + + +// execute a command supplied as string --------------------------------------- +// +PRIVATE +void AcExecCommandString() +{ + // make sure this is called from within a compiled demo + if ( bin_params == NULL ) { + CON_AddLine( "only valid during binary replay." ); + return; + } + + //NOTE: + // extreme care must be taken which commands are supplied to this + // function. right now, there are no safety checks for commands + // that must not be executed using this function. + + char *command; + if ( AcFetchStringParam( &command, ACM_EAT_SPACE( ACM_EXECCOMMAND ) ) ) { + + // command must belong to the class of non-compilable commands + ProcessExternalLine( command ); + ExecNonCompilableCommand( command ); + } +} + + +// macro to return with error message if recording inactive ------------------- +// +#define CHECK_RECORDING_STATE() \ +\ +if ( !RecordingActive ) { \ + CON_AddLine( no_batch_open ); \ + return; \ +} + + +// save all objects from specified object list -------------------------------- +// +PRIVATE +void AcSaveObjectList() +{ +/* CHECK_RECORDING_STATE(); + + // disallow object camera + if ( ObjCameraActive ) { + CON_AddLine( no_objcam_allowed ); + return; + } + + int32 listid; + if ( AcFetchIntParams( 1, &listid ) ) { + + switch ( listid ) { + + case PSHIP_LIST_NO: + Save_ObjListShips( RecordingFp ); + break; + + case LASER_LIST_NO: + Save_ObjListLasers( RecordingFp ); + break; + + case MISSL_LIST_NO: + Save_ObjListMissls( RecordingFp ); + break; + + case EXTRA_LIST_NO: + Save_ObjListExtras( RecordingFp ); + break; + + case CUSTM_LIST_NO: + Save_ObjListCustom( RecordingFp ); + break; + + default: + CON_AddLine( invalid_list ); + } + } +*/ +} + + +// save pseudo star positions ------------------------------------------------- +// +PRIVATE +void AcSavePseudoStars() +{ +/* + CHECK_RECORDING_STATE(); + Save_PseudoStars( RecordingFp ); +*/ +} + + +// save camera position and orientation --------------------------------------- +// +PRIVATE +void AcSaveCamera() +{ +/* + CHECK_RECORDING_STATE(); + Save_Camera( RecordingFp ); +*/ +} + + +// save fixed stars orientation ----------------------------------------------- +// +PRIVATE +void AcSaveFixedStars() +{ + // dummy for compatibility +} + + +// save state of all remote players ------------------------------------------- +// +PRIVATE +void AcSaveRemoteState() +{ +/* + CHECK_RECORDING_STATE(); + Save_RemoteState( RecordingFp ); +*/ +} + + +// kill all pseudo stars ------------------------------------------------------ +// +PRIVATE +void AcKillPseudoStars() +{ + //NumPseudoStars = 0; +} + + +// add single pseudo star ----------------------------------------------------- +// +PRIVATE +void AcAddPseudoStar() +{ + /*if ( NumPseudoStars < MAX_PSEUDO_STARS ) { + hprec_t pos[ 3 ]; + if ( AcFetchFloatParams( 3, pos ) ) { + int no = NumPseudoStars++; + PseudoStars[ no ].X = FLOAT_TO_GEOMV( pos[ 0 ] ); + PseudoStars[ no ].Y = FLOAT_TO_GEOMV( pos[ 1 ] ); + PseudoStars[ no ].Z = FLOAT_TO_GEOMV( pos[ 2 ] ); + } + } else { + CON_AddLine( too_many_pseudos ); + }*/ +} + + +// prepare restoration of data screen shot ------------------------------------ +// +PRIVATE +void AcPrepareDataRestore() +{ +/* + int shipclass = MyShip->ObjectClass; + + // allow optional specification of ship class + int32 sclass; + if ( AcFetchIntParams( 1, &sclass ) ) { + shipclass = sclass; + } + + // free objects and particles + KillAllObjects(); + + // delete pseudo stars + AcKillPseudoStars(); + +#ifdef REINIT_LOCAL_SHIP_ON_PREP_RESTORE + + // reinit local player's ship + InitLocalShipStatus( shipclass ); + InitHudDisplay(); + +#endif // REINIT_LOCAL_SHIP_ON_PREP_RESTORE + + // globally select shipclass + LocalShipClass = MyShip->ObjectClass; + + // set flag that view camera filter has to be reset + // after the new matrix has been set + reset_viewcam_filter_on_update = TRUE; + + // reset mapping table for class ids + ResetClassMapTable(); +*/ +} + + +// save data screen shot ------------------------------------------------------ +// +PRIVATE +void AcSaveDataInfo() +{ +/* + //NOTE: + // this function tries to create the logical analog to a screenshot. + // for this, it records the current state (that is, commands that + // are able to restore the current state on their execution). + + CHECK_RECORDING_STATE(); + Save_StateInfo( RecordingFp ); +*/ +} + + +// fade screen out (to black) ------------------------------------------------- +// +PRIVATE +void AcFadeOut() +{ +/* + +// VIDs_FadeScreenToBlack( PAL_GAME, FALSE ); + + int32 speed; + if ( AcFetchIntParams( 1, &speed ) ) { + + SetScreenFadeSpeed = speed; + + SetScreenFadeColor.R = 0; + SetScreenFadeColor.G = 0; + SetScreenFadeColor.B = 0; +// SetScreenFadeColor.A = irrelevant; + + // continue fading from current value + if ( SetScreenFade == 0 ) { + SetScreenFade = 0xff00; + } else if ( SetScreenFade < 0 ) { + SetScreenFade += 0xff00; + if ( SetScreenFade < 0 ) { + SetScreenFade = 0; + } + } + } +*/ +} + + +// fade screen in (from black) ------------------------------------------------ +// +PRIVATE +void AcFadeIn() +{ +/* +// VIDs_FadeScreenFromBlack( PAL_GAME ); + + int32 speed; + if ( AcFetchIntParams( 1, &speed ) ) { + + SetScreenFadeSpeed = speed; + + SetScreenFadeColor.R = 0; + SetScreenFadeColor.G = 0; + SetScreenFadeColor.B = 0; +// SetScreenFadeColor.A = irrelevant; + + // continue fading from current value + if ( ( SetScreenFade == 0 ) || ( SetScreenFade == 1 ) ) { + SetScreenFade = -0xff00; + } else if ( SetScreenFade > 0 ) { + SetScreenFade += -0xff00; + if ( SetScreenFade > 0 ) { + SetScreenFade = 0; + } + } + } +*/ +} + + +// fade screen out (to white) ------------------------------------------------- +// +PRIVATE +void AcWhiteFadeOut() +{ +/* +// colrgba_s scol = COLRGBA_WHITE; +// VIDs_SetScreenToColor( scol ); + + int32 speed; + if ( AcFetchIntParams( 1, &speed ) ) { + + SetScreenFadeSpeed = speed; + + SetScreenFadeColor.R = 255; + SetScreenFadeColor.G = 255; + SetScreenFadeColor.B = 255; +// SetScreenFadeColor.A = irrelevant; + + // continue fading from current value + if ( SetScreenFade == 0 ) { + SetScreenFade = 0xff00; + } else if ( SetScreenFade < 0 ) { + SetScreenFade += 0xff00; + if ( SetScreenFade < 0 ) { + SetScreenFade = 0; + } + } + } +*/ +} + + +// fade screen in (from white) ------------------------------------------------ +// +PRIVATE +void AcWhiteFadeIn() +{ +/* +// VIDs_FadeScreenFromWhite( PAL_GAME ); + + int32 speed; + if ( AcFetchIntParams( 1, &speed ) ) { + + SetScreenFadeSpeed = speed; + + SetScreenFadeColor.R = 255; + SetScreenFadeColor.G = 255; + SetScreenFadeColor.B = 255; +// SetScreenFadeColor.A = irrelevant; + + // continue fading from current value + if ( ( SetScreenFade == 0 ) || ( SetScreenFade == 1 ) ) { + SetScreenFade = -0xff00; + } else if ( SetScreenFade > 0 ) { + SetScreenFade += -0xff00; + if ( SetScreenFade > 0 ) { + SetScreenFade = 0; + } + } + } +*/ +} + + +// set standard palette ------------------------------------------------------- +// +PRIVATE +void AcStandardPalette() +{ +/* +// AwaitVertSync(); + + // turn off fading + SetScreenFade = 0; +*/ +} + + +// set entire palette to black ------------------------------------------------ +// +PRIVATE +void AcBlackScreen() +{ +/* +// colrgba_s scol = COLRGBA_BLACK; +// VIDs_SetScreenToColor( scol ); + + SetScreenFadeColor.R = 0; + SetScreenFadeColor.G = 0; + SetScreenFadeColor.B = 0; +// SetScreenFadeColor.A = irrelevant; + + // hold fading at opaque level + SetScreenFade = 1; +*/ +} + + +// set entire palette to white ------------------------------------------------ +// +PRIVATE +void AcWhiteScreen() +{ +/* +// colrgba_s scol = COLRGBA_WHITE; +// VIDs_SetScreenToColor( scol ); + + SetScreenFadeColor.R = 255; + SetScreenFadeColor.G = 255; + SetScreenFadeColor.B = 255; +// SetScreenFadeColor.A = irrelevant; + + // hold fading at opaque level + SetScreenFade = 1; +*/ +} + + +// set speed property of laser object ----------------------------------------- +// +PRIVATE +void AcSetLaserSpeed() +{ + fixed_t speed; + if ( AcFetchIntParams( 1, (int32 *) &speed ) ) { + LaserObject *scanpo = FetchFirstLaser(); + ASSERT( scanpo != NULL ); + scanpo->Speed = speed; + } +} + + +// set direction vector property of laser object ------------------------------ +// +PRIVATE +void AcSetLaserDirVec() +{ + hprec_t vec[ 3 ]; + if ( AcFetchFloatParams( 3, vec ) ) { + LaserObject *scanpo = FetchFirstLaser(); + ASSERT( scanpo != NULL ); + scanpo->DirectionVec.X = FLOAT_TO_GEOMV( vec[ 0 ] ); + scanpo->DirectionVec.Y = FLOAT_TO_GEOMV( vec[ 1 ] ); + scanpo->DirectionVec.Z = FLOAT_TO_GEOMV( vec[ 2 ] ); + } +} + + +// set owner property of laser object ----------------------------------------- +// +PRIVATE +void AcSetLaserOwner() +{ + int32 owner; + if ( AcFetchIntParams( 1, &owner ) ) { + LaserObject *scanpo = FetchFirstLaser(); + ASSERT( scanpo != NULL ); + scanpo->Owner = owner; + } +} + + +// set speed property of missile object --------------------------------------- +// +PRIVATE +void AcSetMisslSpeed() +{ + fixed_t speed; + if ( AcFetchIntParams( 1, (int32 *) &speed ) ) { + MissileObject *scanpo = FetchFirstMissile(); + ASSERT( scanpo != NULL ); + scanpo->Speed = speed; + } + +} + + +// set direction vector property of missile object ---------------------------- +// +PRIVATE +void AcSetMisslDirVec() +{ + hprec_t vec[ 3 ]; + if ( AcFetchFloatParams( 3, vec ) ) { + MissileObject *scanpo = FetchFirstMissile(); + ASSERT( scanpo != NULL ); + scanpo->DirectionVec.X = FLOAT_TO_GEOMV( vec[ 0 ] ); + scanpo->DirectionVec.Y = FLOAT_TO_GEOMV( vec[ 1 ] ); + scanpo->DirectionVec.Z = FLOAT_TO_GEOMV( vec[ 2 ] ); + } +} + + +// set owner property of missile object --------------------------------------- +// +PRIVATE +void AcSetMisslOwner() +{ + int32 owner; + if ( AcFetchIntParams( 1, &owner ) ) { + MissileObject *scanpo = FetchFirstMissile(); + ASSERT( scanpo != NULL ); + scanpo->Owner = owner; + } +} + + +// set target id property of missile object ----------------------------------- +// +PRIVATE +void AcSetMisslTarget() +{ +/* + //NOTE: + // this command will only occur to set the target + // of a guided missile fired by the local player. + + int32 target; + if ( AcFetchIntParams( 1, &target ) ) { + + TargetMissileObject *scanpo = (TargetMissileObject *) FetchFirstMissile(); + ASSERT( scanpo != NULL ); + ASSERT( ( scanpo->ObjectType & TYPECONTROLMASK ) == TYPEMISSILEISHOMING ); + scanpo->TargetObjNumber = target; + +#ifdef RECOVER_TARGET_ON_MISSILE_CREATION + + if ( TargetObjNumber != target ) { + + // set selected target to ensure the current + // selection is not wrong due to a lost keypress + TargetObjNumber = target; + CON_AddLine( "recovering target." ); + } +#endif + + } +*/ +} + + +// set owner property of mine object ------------------------------------------ +// +PRIVATE +void AcSetMineOwner() +{ +/* + int32 owner; + if ( AcFetchIntParams( 1, &owner ) ) { + MineObject *scanpo = (MineObject *) FetchFirstExtra(); + ASSERT( scanpo != NULL ); + scanpo->Owner = owner; + } +*/ +} + + +// create an extra ------------------------------------------------------------ +// +PRIVATE +void AcCreateExtra() +{ +/* + ASSERT( !delayed_object_creation ); + + int32 objclass; + if ( AcFetchIntParams( 1, &objclass ) ) { + + if ( (dword)objclass >= MAX_DISTINCT_OBJCLASSES ) { + CON_AddLine( invalid_id_range ); + return; + } + + // map the recorded class id to the corresponding current class id + if ( class_map_id_to_name[ objclass ] != CLASS_ID_INVALID ) { + objclass = class_map_id_to_name[ objclass ]; + } + + // create extra corresponding to original class + Xmatrx im; + MakeIdMatrx( im ); + ExtraObject *extrapo = (ExtraObject *) CreateObject( objclass, im ); + if ( extrapo == NULL ) { + CON_AddLine( invalid_class ); + } else { + OBJ_FillExtraMemberVars( extrapo ); + } + } +*/ +} + + +// create an energy field ----------------------------------------------------- +// +PRIVATE +void AcEnergyField() +{ +/* + hprec_t vec[ 3 ]; + if ( AcFetchFloatParams( 3, vec ) ) { + Vertex3 origin; + origin.X = FLOAT_TO_GEOMV( vec[ 0 ] ); + origin.Y = FLOAT_TO_GEOMV( vec[ 1 ] ); + origin.Z = FLOAT_TO_GEOMV( vec[ 2 ] ); + SFX_CreateEnergyField( origin ); + } +*/ +} + + +// shoot spreadfire gun ------------------------------------------------------- +// +PRIVATE +void AcSpreadfire() +{ +/* + WFX_ShootParticleWeapon( MyShip, PARTICLEGUN_SPREADFIRE ); +*/ +} + + +// activate lightning --------------------------------------------------------- +// +PRIVATE +void AcLightningOn() +{ +/* + if ( ( MyShip->WeaponsActive & WPMASK_CANNON_LIGHTNING ) == 0 ) { + WFX_ActivateLightning( MyShip ); + } else { + CON_AddLine( "filtering redundant lightning activation." ); + } +*/ +} + + +// deactivate lightning ------------------------------------------------------- +// +PRIVATE +void AcLightningOff() +{ +/* + if ( ( MyShip->WeaponsActive & WPMASK_CANNON_LIGHTNING ) != 0 ) { + WFX_DeactivateLightning( MyShip ); + } else { + CON_AddLine( "filtering redundant lightning deactivation." ); + } +*/ +} + + +// activate helix ------------------------------------------------------------- +// +void AcHelixOn() +{ +/* + if ( ( MyShip->WeaponsActive & WPMASK_CANNON_HELIX ) == 0 ) { + WFX_ActivateHelix( MyShip ); + } else { + CON_AddLine( "filtering redundant helix activation." ); + } +*/ +} + + +// deactivate helix ----------------------------------------------------------- +// +void AcHelixOff() +{ +/* + if ( ( MyShip->WeaponsActive & WPMASK_CANNON_HELIX ) != 0 ) { + WFX_DeactivateHelix( MyShip ); + } else { + CON_AddLine( "filtering redundant helix deactivation." ); + } +*/ +} + + +// activate photon ------------------------------------------------------------ +// +void AcPhotonOn() +{ +/* + if ( ( MyShip->WeaponsActive & WPMASK_CANNON_PHOTON ) == 0 ) { + WFX_ActivatePhoton( MyShip ); + } else { + CON_AddLine( "filtering redundant photon activation." ); + } +*/ +} + + +// deactivate photon ---------------------------------------------------------- +// +void AcPhotonOff() +{ +/* + if ( ( MyShip->WeaponsActive & WPMASK_CANNON_PHOTON ) != 0 ) { + WFX_DeactivatePhoton( MyShip ); + } else { + CON_AddLine( "filtering redundant photon deactivation." ); + } +*/ +} + + +// activate emp --------------------------------------------------------------- +// +void AcEmpOn() +{ +/* + if ( ( MyShip->WeaponsActive & WPMASK_DEVICE_EMP ) == 0 ) { + WFX_ActivateEmp( MyShip ); + } else { + CON_AddLine( "filtering redundant emp activation." ); + } +*/ +} + + +// deactivate emp ------------------------------------------------------------- +// +void AcEmpOff() +{ +/* + if ( ( MyShip->WeaponsActive & WPMASK_DEVICE_EMP) != 0 ) { + WFX_DeactivateEmp( MyShip ); + } else { + CON_AddLine( "filtering redundant emp deactivation." ); + } +*/ +} + + +// fire emp ------------------------------------------------------------------- +// +void AcEmp() +{ +/* + WFX_EmpBlast( MyShip ); +*/ +} + + +// set id of ship object that is first in list to specific value -------------- +// +PRIVATE +void AcSetObjectId() +{ +/* + int32 objid; + if ( AcFetchIntParams( 1, &objid ) ) { + if ( delayed_object_creation ) { + delayed_object_id = objid; + } else { + ShipObject *scanpo = FetchFirstShip(); + // ship must have been created previously + ASSERT( scanpo != NULL ); + if ( scanpo != NULL ) { + scanpo->ObjectNumber = objid; + } + } + } +*/ +} + + +// set host id of object to specific value to enable targeting for missiles --- +// +PRIVATE +void AcSetObjectHostId() +{ +/* + int32 objid; + if ( AcFetchIntParams( 1, &objid ) ) { + if ( delayed_object_creation ) { + delayed_object_hostid = objid; + } else { + ShipObject *scanpo = FetchFirstShip(); + // ship must have been created previously + ASSERT( scanpo != NULL ); + if ( scanpo != NULL ) { + scanpo->HostObjNumber = objid; + } + } + } +*/ +} + + +// simulate depression of single key ------------------------------------------ +// +PRIVATE +void AcSimulateKeyPress() +{ +/* int32 keynum; + if ( AcFetchIntParams( 1, &keynum ) ) { + if ( keynum < sizeof( keyfunc_s ) ) { + + ASSERT( sizeof( DepressedKeys->key_Escape ) == sizeof( byte ) ); + ((byte*)DepressedKeys)[ keynum ] = 1; + ((byte*)key_replay_map)[ keynum ] = 1; + + // explicitly check for object camera toggling + if ( offsetof( keyfunc_s, key_ToggleObjCamera ) == keynum ) { + ReplayObjCamActive = !ReplayObjCamActive; + } + + } else { + CON_AddLine( invalid_key_id ); + } + } +*/ +} + + +// simulate release of single key --------------------------------------------- +// +PRIVATE +void AcSimulateKeyRelease() +{ +/* + int32 keynum; + if ( AcFetchIntParams( 1, &keynum ) ) { + if ( keynum < sizeof( keyfunc_s ) ) { + ASSERT( sizeof( DepressedKeys->key_Escape ) == sizeof( byte ) ); + ((byte*)DepressedKeys)[ keynum ] = 0; + ((byte*)key_replay_map)[ keynum ] = 0; + } else { + CON_AddLine( invalid_key_id ); + } + } +*/ +} + + +// ---------------------------------------------------------------------------- +// local ship configuration commands - +// ---------------------------------------------------------------------------- + + +// current damage ------------------------------------------------------------- +// +PRIVATE +void AcMyShip_CurDamage() +{ +/* + int32 ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + MyShip->CurDamage = ivalue; + } +*/ +} + + +// maximum damage ------------------------------------------------------------- +// +PRIVATE +void AcMyShip_MaxDamage() +{ +/* + int32 ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + MyShip->MaxDamage = ivalue; + } +*/ +} + + +// current energy ------------------------------------------------------------- +// +PRIVATE +void AcMyShip_CurEnergy() +{ +/* + int32 ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + MyShip->CurEnergy = ivalue; + } +*/ +} + + +// maximum energy ------------------------------------------------------------- +// +PRIVATE +void AcMyShip_MaxEnergy() +{ +/* + int32 ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + MyShip->MaxEnergy = ivalue; + } +*/ +} + + +// current speed -------------------------------------------------------------- +// +PRIVATE +void AcMyShip_CurSpeed() +{ +/* + fixed_t ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + MyShip->CurSpeed = ivalue; + } +*/ +} + + +// maximum speed -------------------------------------------------------------- +// +PRIVATE +void AcMyShip_MaxSpeed() +{ +/* + fixed_t ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + MyShip->MaxSpeed = ivalue; + } +*/ +} + + +// ---------------------------------------------------------------------------- +// +PRIVATE +void AcMyShip_NumMissls() +{ +/* + int32 ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + MyShip->NumMissls = ivalue; + } +*/ +} + + +// ---------------------------------------------------------------------------- +// +PRIVATE +void AcMyShip_MaxNumMissls() +{ +/* + int32 ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + MyShip->MaxNumMissls = ivalue; + } +*/ +} + + +// ---------------------------------------------------------------------------- +// +PRIVATE +void AcMyShip_NumHomMissls() +{ +/* + int32 ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + MyShip->NumHomMissls = ivalue; + } +*/ +} + + +// ---------------------------------------------------------------------------- +// +PRIVATE +void AcMyShip_MaxNumHomMissls() +{ +/* + int32 ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + MyShip->MaxNumHomMissls = ivalue; + } +*/ +} + + +// ---------------------------------------------------------------------------- +// +PRIVATE +void AcMyShip_NumPartMissls() +{ +/* + int32 ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + MyShip->NumPartMissls = ivalue; + } +*/ +} + + +// ---------------------------------------------------------------------------- +// +PRIVATE +void AcMyShip_MaxNumPartMissls() +{ +/* + int32 ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + MyShip->MaxNumPartMissls = ivalue; + } +*/ +} + + +// ---------------------------------------------------------------------------- +// +PRIVATE +void AcMyShip_NumMines() +{ +/* + int32 ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + MyShip->NumMines = ivalue; + } +*/ +} + + +// ---------------------------------------------------------------------------- +// +PRIVATE +void AcMyShip_MaxNumMines() +{ +/* + int32 ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + MyShip->MaxNumMines = ivalue; + } +*/ +} + + +// ---------------------------------------------------------------------------- +// +PRIVATE +void AcMyShip_Weapons() +{ +/* + int32 ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + MyShip->Weapons = ivalue; + } +*/ +} + + +// ---------------------------------------------------------------------------- +// +PRIVATE +void AcMyShip_WeaponsActive() +{ +/* + //NOTE: + // if new duration weapons are added this function + // should be updated. + + int32 weaponsactive; + if ( AcFetchIntParams( 1, &weaponsactive ) ) { + + // check for lightning state change + dword oldstate = MyShip->WeaponsActive & WPMASK_CANNON_LIGHTNING; + dword newstate = weaponsactive & WPMASK_CANNON_LIGHTNING; + if ( ( oldstate == 0 ) && ( newstate != 0 ) ) { + WFX_ActivateLightning( MyShip ); + } else if ( ( oldstate != 0 ) && ( newstate == 0 ) ) { + WFX_DeactivateLightning( MyShip ); + } + + // check for helix state change + oldstate = MyShip->WeaponsActive & WPMASK_CANNON_HELIX; + newstate = weaponsactive & WPMASK_CANNON_HELIX; + if ( ( oldstate == 0 ) && ( newstate != 0 ) ) { + WFX_ActivateHelix( MyShip ); + } else if ( ( oldstate != 0 ) && ( newstate == 0 ) ) { + WFX_DeactivateHelix( MyShip ); + } + + // check for photon state change + oldstate = MyShip->WeaponsActive & WPMASK_CANNON_PHOTON; + newstate = weaponsactive & WPMASK_CANNON_PHOTON; + if ( ( oldstate == 0 ) && ( newstate != 0 ) ) { + WFX_ActivatePhoton( MyShip ); + } else if ( ( oldstate != 0 ) && ( newstate == 0 ) ) { + WFX_DeactivatePhoton( MyShip ); + } + + // check for emp state change + oldstate = MyShip->WeaponsActive & WPMASK_DEVICE_EMP; + newstate = weaponsactive & WPMASK_DEVICE_EMP; + if ( ( oldstate == 0 ) && ( newstate != 0 ) ) { + WFX_ActivateEmp( MyShip ); + } else if ( ( oldstate != 0 ) && ( newstate == 0 ) ) { + WFX_DeactivateEmp( MyShip ); + } + + // fallback for unknown duration weapons + // (simply set/reset activation bits) + dword unknownmask = 0xffffffff; + unknownmask &= ~WPMASK_CANNON_LIGHTNING; + unknownmask &= ~WPMASK_CANNON_HELIX; + unknownmask &= ~WPMASK_CANNON_PHOTON; + unknownmask &= ~WPMASK_DEVICE_EMP; + MyShip->WeaponsActive &= ~unknownmask; + MyShip->WeaponsActive |= weaponsactive & unknownmask; + } +*/ +} + + +// ---------------------------------------------------------------------------- +// +PRIVATE +void AcMyShip_Specials() +{ +/* + int32 ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + MyShip->Specials = ivalue; + } +*/ +} + + +// global variables configuration commands pertaining to local ship ----------- +// +PRIVATE +void AcGlobals_SelectedLaser() +{ + /*int32 ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + SelectedLaser = ivalue; + }*/ +} + +PRIVATE +void AcGlobals_SelectedMissile() +{ + /*int32 ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + SelectedMissile = ivalue; + }*/ +} + +PRIVATE +void AcGlobals_CurGun() +{ + /*int32 ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + CurGun = ivalue; + }*/ +} + +PRIVATE +void AcGlobals_CurLauncher() +{ + /*int32 ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + CurLauncher = ivalue; + }*/ +} + +PRIVATE +void AcGlobals_AUX_HUD_PANEL_3_CONTROL() +{ + /*int32 ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + AUX_HUD_PANEL_3_CONTROL = ivalue; + }*/ +} + +PRIVATE +void AcGlobals_AUX_HUD_PANEL_4_CONTROL() +{ + /*int32 ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + AUX_HUD_PANEL_4_CONTROL = ivalue; + }*/ +} + +PRIVATE +void AcGlobals_TargetObjNumber() +{ + /*int32 ivalue; + if ( AcFetchIntParams( 1, &ivalue ) ) { + TargetObjNumber = ivalue; + }*/ +} + + +// play specified sound effect ------------------------------------------------ +// +PRIVATE +void AcPlaySound() +{ + // dummy for compatibility +} + + +// flag whether audio stream has been started with an action command ---------- +// +PUBLIC int con_audio_stream_started = FALSE; + + +// start playing of specific audio stream ------------------------------------- +// +PRIVATE +void AcPlayStream() +{ +/* + char *streamname; + if ( AcFetchStringParam( &streamname, ACM_EAT_SPACE( ACM_PLAYSTREAM ) ) ) { + + if ( strlen( streamname ) + strlen( console_audio_stream_path ) > PATH_MAX ) { + // resulting file name too long + CON_AddLine( stream_name_inval ); + return; + } + + //NOTE: + // apart from its length the filename is + // not checked for validity in any way! + + // create full filename including path + char fname[ PATH_MAX + 1 ]; + strcpy( fname, console_audio_stream_path ); + strcat( fname, streamname ); + + // check first if file exists in pack or current directory + FILE *fp = SYS_fopen( streamname, "rb" ); + if ( fp == NULL ) { + + // check if file exists in audio stream path + fp = SYS_fopen( fname, "rb" ); + if ( fp == NULL ) { + CON_AddLine( stream_name_inval ); + return; + } + + //NOTE: + // we use SYS_fopen() here, because the streaming code + // also uses the SYS_f*() functions. + + } else { + + // strip path + strcpy( fname, streamname ); + } + SYS_fclose( fp ); + + // play audio stream (automatically determines stream's format) + int started = AUDs_PlayAudioStream( fname ); + + if ( started ) { + con_audio_stream_started = TRUE; + } else { + CON_AddLine( no_stream_started ); + } + } +*/ +} + + +// stop playing of currently active audio stream ------------------------------ +// +PRIVATE +void AcStopStream() +{ +/* + int stopped = AUDs_StopAudioStream(); + + if ( stopped ) { + con_audio_stream_started = FALSE; + } else { + CON_AddLine( no_stream_stopped ); + } +*/ +} + + +// set aux flag with ac. command ---------------------------------------------- +// +PRIVATE +void AcSetAuxFlag() +{ +/* + int32 params[ 2 ]; + if ( AcFetchIntParams( 2, params ) ) { + + if ( (dword)params[ 0 ] < MAX_AUX_ENABLING ) { + AuxEnabling[ params[ 0 ] ] = params[ 1 ]; + } + } +*/ +} + + +// set aux data with ac. command ---------------------------------------------- +// +PRIVATE +void AcSetAuxData() +{ +/* + int32 params[ 2 ]; + if ( AcFetchIntParams( 2, params ) ) { + + if ( (dword)params[ 0 ] < MAX_AUX_DATA ) { + AuxData[ params[ 0 ] ] = params[ 1 ]; + } + } +*/ +} + + +// set new refframe frequency ------------------------------------------------- +// +PRIVATE +void AcSetRefFrameFrequency() +{ +/* + int32 param; + if ( AcFetchIntParams( 1, ¶m ) ) { + + if ( RefFrameFrequency != param ) { + + RefFrameFrequency = param; + + // reinit frame timer to apply frequency change + SYSs_InitFrameTimer(); + } + } +*/ +} + + +// alter entry-mode flag ------------------------------------------------------ +// +PRIVATE +void AcSetEntryMode() +{ +/* + int32 param; + if ( AcFetchIntParams( 1, ¶m ) ) { + + EntryMode = param; + InFloatingMenu = EntryMode ? FloatingMenu : FALSE; + + // reset the floating menu state + if ( InFloatingMenu ) { + ActivateFloatingMenu( FALSE ); + } + } +*/ +} + + +// this gets called on execution instead of compilation, which is a nop ------- +// +PRIVATE +void AcIncludeDemo() +{ +/* + //NOTE: + // this command is a nop in a script file and should never + // occur in a compiled demo, since it will be substituted + // by the specified demo on compilation. + + CON_AddLine( only_for_compile ); +*/ +} + + +// this gets called on execution instead of compilation, which is a nop ------- +// +PRIVATE +void AcScheduleCommand() +{ +/* + //NOTE: + // this command is a nop in a script file. + // in a compiled demo this function will not be called at all. + + CON_AddLine( only_for_compile ); +*/ +} + + +// set the ambient light color ------------------------------------------------ +// +PRIVATE +void AcLightAmbient() +{ +/* + int32 params[ 4 ]; + if ( AcFetchIntParams( 4, params ) ) { + + LightColorAmbient.R = params[ 0 ]; + LightColorAmbient.G = params[ 1 ]; + LightColorAmbient.B = params[ 2 ]; + LightColorAmbient.A = params[ 3 ]; + } +*/ +} + + +// set the diffuse light color ------------------------------------------------ +// +PRIVATE +void AcLightDiffuse() +{ +/* + int32 params[ 4 ]; + if ( AcFetchIntParams( 4, params ) ) { + + LightColorDiffuse.R = params[ 0 ]; + LightColorDiffuse.G = params[ 1 ]; + LightColorDiffuse.B = params[ 2 ]; + LightColorDiffuse.A = params[ 3 ]; + } +*/ +} + + +// set the specular light color ----------------------------------------------- +// +PRIVATE +void AcLightSpecular() +{ +/* + int32 params[ 4 ]; + if ( AcFetchIntParams( 4, params ) ) { + + LightColorSpecular.R = params[ 0 ]; + LightColorSpecular.G = params[ 1 ]; + LightColorSpecular.B = params[ 2 ]; + LightColorSpecular.A = params[ 3 ]; + } +*/ +} + + +// cd audio: open drive ------------------------------------------------------- +// +PRIVATE +void AcCD_Open() +{ +/* + int32 param; + if ( AcFetchIntParams( 1, ¶m ) ) { + + AUDs_CDOpen( param ); + } +*/ +} + + +// cd audio: close drive ------------------------------------------------------ +// +PRIVATE +void AcCD_Close() +{ +// AUDs_CDClose(); +} + + +// cd audio: play audio track ------------------------------------------------- +// +PRIVATE +void AcCD_Play() +{ +/* + //NOTE: + // the from and to params (param 1 and 2) cannot be omitted + // when using this command. however, they can be set to -1 + // in order to avoid specifying specific from/to locations. + + int32 params[ 3 ]; + if ( AcFetchIntParams( 3, params ) ) { + + AUDs_CDPlay( params[ 0 ], params[ 1 ], params[ 2 ] ); + } +*/ +} + + +// cd audio: pause playing ---------------------------------------------------- +// +PRIVATE +void AcCD_Pause() +{ + //AUDs_CDPause(); +} + + +// cd audio: resume playing --------------------------------------------------- +// +PRIVATE +void AcCD_Resume() +{ + //AUDs_CDResume(); +} + + +// cd audio: stop playing ----------------------------------------------------- +// +PRIVATE +void AcCD_Stop() +{ + //AUDs_CDStop(); +} + + +// cd audio: set volume ------------------------------------------------------- +// +PRIVATE +void AcCD_Volume() +{ +/* + int32 param; + if ( AcFetchIntParams( 1, ¶m ) ) { + + AUDs_CDSetVolume( param ); + } +*/ +} + + +// list of valid action commands and functions to be called for each ---------- +// +act_command_s action_commands[] = { + + // general signature + { "ac.", NULL, ACF_NODIRECT | ACF_NOCOMPILE | ACF_NOPARAMS, 0 }, + + { "ac.wait", AcIdleCommand, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + + { "ac.pitch", AcAutoPitch, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.yaw", AcAutoYaw, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.roll", AcAutoRoll, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.slidex", AcAutoSlideX, ACF_NODIRECT | ACF_COMPILE | ACF_FLOATPARAMS, 1 }, + { "ac.slidey", AcAutoSlideY, ACF_NODIRECT | ACF_COMPILE | ACF_FLOATPARAMS, 1 }, + { "ac.move", AcAutoMove, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + + { "ac.camrot1", AcSetCamRotCol1, ACF_NODIRECT | ACF_COMPILE | ACF_FLOATPARAMS, 3 }, + { "ac.camrot2", AcSetCamRotCol2, ACF_NODIRECT | ACF_COMPILE | ACF_FLOATPARAMS, 3 }, + { "ac.camrot3", AcSetCamRotCol3, ACF_NODIRECT | ACF_COMPILE | ACF_FLOATPARAMS, 3 }, + { "ac.camorigin", AcSetCamOrigin, ACF_NODIRECT | ACF_COMPILE | ACF_FLOATPARAMS, 3 }, + + { "ac.fixrot1", AcSetFixedStarRotCol1, ACF_NODIRECT | ACF_COMPILE | ACF_FLOATPARAMS, 3 }, + { "ac.fixrot2", AcSetFixedStarRotCol2, ACF_NODIRECT | ACF_COMPILE | ACF_FLOATPARAMS, 3 }, + { "ac.fixrot3", AcSetFixedStarRotCol3, ACF_NODIRECT | ACF_COMPILE | ACF_FLOATPARAMS, 3 }, + + { "ac.packet", AcPlayRemotePacket, ACF_NODIRECT | ACF_COMPILE | ACF_PACKETPARAM, 1 }, + + { "ac.fadeout", AcFadeOut, ACF_DIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.fadein", AcFadeIn, ACF_DIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.wfadeout", AcWhiteFadeOut, ACF_DIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.wfadein", AcWhiteFadeIn, ACF_DIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.black", AcBlackScreen, ACF_DIRECT | ACF_COMPILE | ACF_NOPARAMS, 0 }, + { "ac.white", AcWhiteScreen, ACF_DIRECT | ACF_COMPILE | ACF_NOPARAMS, 0 }, + { "ac.stdpalette", AcStandardPalette, ACF_DIRECT | ACF_COMPILE | ACF_NOPARAMS, 0 }, + + { "ac.savescreen", AcSaveDataInfo, ACF_DIRECT | ACF_NOCOMPILE | ACF_NOPARAMS, 0 }, + { "ac.savelist", AcSaveObjectList, ACF_DIRECT | ACF_NOCOMPILE | ACF_NOPARAMS, 0 }, + { "ac.savecam", AcSaveCamera, ACF_DIRECT | ACF_NOCOMPILE | ACF_NOPARAMS, 0 }, + { "ac.savefstars", AcSaveFixedStars, ACF_DIRECT | ACF_NOCOMPILE | ACF_NOPARAMS, 0 }, + { "ac.savepstars", AcSavePseudoStars, ACF_DIRECT | ACF_NOCOMPILE | ACF_NOPARAMS, 0 }, + { "ac.saveremote", AcSaveRemoteState, ACF_DIRECT | ACF_NOCOMPILE | ACF_NOPARAMS, 0 }, + + { "ac.killpstars", AcKillPseudoStars, ACF_NODIRECT | ACF_COMPILE | ACF_NOPARAMS, 0 }, + { "ac.addpstar", AcAddPseudoStar, ACF_NODIRECT | ACF_COMPILE | ACF_FLOATPARAMS, 3 }, + + { "ac.preprestore", AcPrepareDataRestore, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + + { "ac.creatobj", AcCreateObject, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.creatextra", AcCreateExtra, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.destlist", AcDestroyObjectList, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + + { "ac.remoteplayer", AcRemotePlayer, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 4 }, + { "ac.remotematrix", AcRemoteMatrix, ACF_NODIRECT | ACF_COMPILE | ACF_FLOATPARAMS, 12 }, + { "ac.remotename", AcRemoteName, ACF_NODIRECT | ACF_COMPILE | ACF_STRINGPARAM, 1 }, + { "ac.remotejoin", AcRemoteJoin, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 3 }, + { "ac.remoteunjoin", AcRemoteUnjoin, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 3 }, + + { "ac.cleardemo", AcClearDemo, ACF_DIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + + { "ac.objmatrxcol1", AcObjectMatrxColumn1, ACF_NODIRECT | ACF_COMPILE | ACF_FLOATPARAMS, 4 }, + { "ac.objmatrxcol2", AcObjectMatrxColumn2, ACF_NODIRECT | ACF_COMPILE | ACF_FLOATPARAMS, 4 }, + { "ac.objmatrxcol3", AcObjectMatrxColumn3, ACF_NODIRECT | ACF_COMPILE | ACF_FLOATPARAMS, 4 }, + { "ac.objmatrxcol4", AcObjectMatrxColumn4, ACF_NODIRECT | ACF_COMPILE | ACF_FLOATPARAMS, 4 }, + + { "ac.laserspeed", AcSetLaserSpeed, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.laserdirvec", AcSetLaserDirVec, ACF_NODIRECT | ACF_COMPILE | ACF_FLOATPARAMS, 3 }, + { "ac.laserowner", AcSetLaserOwner, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.misslspeed", AcSetMisslSpeed, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.missldirvec", AcSetMisslDirVec, ACF_NODIRECT | ACF_COMPILE | ACF_FLOATPARAMS, 3 }, + + { "ac.misslowner", AcSetMisslOwner, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.missltarget", AcSetMisslTarget, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.mineowner", AcSetMineOwner, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + + { "ac.energyfield", AcEnergyField, ACF_NODIRECT | ACF_COMPILE | ACF_FLOATPARAMS, 3 }, + { "ac.spreadfire", AcSpreadfire, ACF_NODIRECT | ACF_COMPILE | ACF_NOPARAMS, 0 }, + { "ac.lightningon", AcLightningOn, ACF_NODIRECT | ACF_COMPILE | ACF_NOPARAMS, 0 }, + { "ac.lightningoff", AcLightningOff, ACF_NODIRECT | ACF_COMPILE | ACF_NOPARAMS, 0 }, + + { "ac.setobjid", AcSetObjectId, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.setobjhostid", AcSetObjectHostId, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + + { "ac.keypress", AcSimulateKeyPress, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.keyrelease", AcSimulateKeyRelease, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + + { "ac.ms.curdamage", AcMyShip_CurDamage, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.ms.maxdamage", AcMyShip_MaxDamage, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.ms.curenergy", AcMyShip_CurEnergy, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.ms.maxenergy", AcMyShip_MaxEnergy, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.ms.curspeed", AcMyShip_CurSpeed, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.ms.maxspeed", AcMyShip_MaxSpeed, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.ms.nummissls", AcMyShip_NumMissls, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.ms.maxnummissls", AcMyShip_MaxNumMissls, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.ms.numhommissls", AcMyShip_NumHomMissls, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.ms.maxnumhommissls", AcMyShip_MaxNumHomMissls, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.ms.nummines", AcMyShip_NumMines, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.ms.maxnummines", AcMyShip_MaxNumMines, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.ms.weapons", AcMyShip_Weapons, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.ms.specials", AcMyShip_Specials, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + + { "ac.gl.selectedlaser", AcGlobals_SelectedLaser, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.gl.selectedmissile", AcGlobals_SelectedMissile, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.gl.curgun", AcGlobals_CurGun, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.gl.curlauncher", AcGlobals_CurLauncher, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.gl.panel3control", AcGlobals_AUX_HUD_PANEL_3_CONTROL, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.gl.panel4control", AcGlobals_AUX_HUD_PANEL_4_CONTROL, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.gl.targetobjnumber", AcGlobals_TargetObjNumber, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + + { "ac.writesmall", AcWriteSmall, ACF_DIRECT | ACF_COMPILE | ACF_STRINGPARAM, 1 }, + { "ac.writebig", AcWriteBig, ACF_DIRECT | ACF_COMPILE | ACF_STRINGPARAM, 1 }, + { "ac.writebig2", AcWriteBig2, ACF_DIRECT | ACF_COMPILE | ACF_STRINGPARAM, 1 }, + { "ac.writebig3", AcWriteBig3, ACF_DIRECT | ACF_COMPILE | ACF_STRINGPARAM, 1 }, + { "ac.cleartext", AcClearText, ACF_DIRECT | ACF_COMPILE | ACF_NOPARAMS, 0 }, + + { "ac.playstream", AcPlayStream, ACF_DIRECT | ACF_COMPILE | ACF_STRINGSTRIP, 1 }, + { "ac.stopstream", AcStopStream, ACF_DIRECT | ACF_COMPILE | ACF_NOPARAMS, 0 }, + { "ac.playsound", AcPlaySound, ACF_DIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + + { "ac.aux", AcSetAuxFlag, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 2 }, + { "ac.entrymode", AcSetEntryMode, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + + { "ac.servermsg", AcPlayServerMessage, ACF_NODIRECT | ACF_COMPILE | ACF_STRINGSTRIP, 1 }, + + { "ac.helixon", AcHelixOn, ACF_NODIRECT | ACF_COMPILE | ACF_NOPARAMS, 0 }, + { "ac.helixoff", AcHelixOff, ACF_NODIRECT | ACF_COMPILE | ACF_NOPARAMS, 0 }, + + { "ac.remotemaxplayers", AcRemoteMaxPlayers, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + + { "ac.include", AcIncludeDemo, ACF_NODIRECT | ACF_COMPILE | ACF_DEMOPARAM, 1 }, + + { "ac.ms.weaponsactive", AcMyShip_WeaponsActive, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.remoteweaponsactive", AcRemoteWeaponsActive, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + + { "ac.write", AcWriteConsoleText, ACF_NODIRECT | ACF_COMPILE | ACF_STRINGPARAM, 1 }, + { "ac.display", AcDisplayMessage, ACF_NODIRECT | ACF_COMPILE | ACF_STRINGSTRIP, 1 }, + + { "ac.cd.open", AcCD_Open, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.cd.close", AcCD_Close, ACF_NODIRECT | ACF_COMPILE | ACF_NOPARAMS, 0 }, + { "ac.cd.play", AcCD_Play, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 3 }, + { "ac.cd.pause", AcCD_Pause, ACF_NODIRECT | ACF_COMPILE | ACF_NOPARAMS, 0 }, + { "ac.cd.resume", AcCD_Resume, ACF_NODIRECT | ACF_COMPILE | ACF_NOPARAMS, 0 }, + { "ac.cd.stop", AcCD_Stop, ACF_NODIRECT | ACF_COMPILE | ACF_NOPARAMS, 0 }, + { "ac.cd.volume", AcCD_Volume, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + + { "ac.command", AcExecCommandString, ACF_NODIRECT | ACF_COMPILE | ACF_STRINGPARAM, 1 }, + + { "ac.classmapid", AcClassMapId, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.classmapname", AcClassMapName, ACF_NODIRECT | ACF_COMPILE | ACF_STRINGPARAM, 1 }, + + { "ac.auxdata", AcSetAuxData, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 2 }, + { "ac.refframes", AcSetRefFrameFrequency, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + + { "ac.schedule", AcScheduleCommand, ACF_NODIRECT | ACF_COMPILE | ACF_SCHEDPARAM, 1 }, + + { "ac.ambient", AcLightAmbient, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 4 }, + { "ac.diffuse", AcLightDiffuse, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 4 }, + { "ac.specular", AcLightSpecular, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 4 }, + + { "ac.ms.numpartmissls", AcMyShip_NumPartMissls, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + { "ac.ms.maxnumpartmissls", AcMyShip_MaxNumPartMissls, ACF_NODIRECT | ACF_COMPILE | ACF_INTPARAMS, 1 }, + + { "ac.photonon", AcPhotonOn, ACF_NODIRECT | ACF_COMPILE | ACF_NOPARAMS, 0 }, + { "ac.photonoff", AcPhotonOff, ACF_NODIRECT | ACF_COMPILE | ACF_NOPARAMS, 0 }, + + { "ac.empon", AcEmpOn, ACF_NODIRECT | ACF_COMPILE | ACF_NOPARAMS, 0 }, + { "ac.empoff", AcEmpOff, ACF_NODIRECT | ACF_COMPILE | ACF_NOPARAMS, 0 }, + { "ac.emp", AcEmp, ACF_NODIRECT | ACF_COMPILE | ACF_NOPARAMS, 0 }, + + { NULL, NULL, ACF_NODIRECT | ACF_NOCOMPILE | ACF_NOPARAMS, 0 }, + +}; + + +// determine the number of parameter bytes of an action command --------------- +// +size_t GetActionParamSize( int actid, char *params ) +{ + ASSERT( actid > ACM_SIGNATURE ); + ASSERT( params != NULL ); + + size_t paramsize = 0; + + if ( action_commands[ actid ].flags & ACF_INTPARAMS ) { + paramsize = sizeof( int32 ) * action_commands[ actid ].numparameters; + } else if ( action_commands[ actid ].flags & ACF_FLOATPARAMS ) { + paramsize = sizeof( hprec_t ) * action_commands[ actid ].numparameters; + } else if ( action_commands[ actid ].flags & ACF_STRINGPARAM ) { + paramsize = strlen( params ) + 1; + } + + return paramsize; +} + + +// scheduled action commands -------------------------------------------------- +// +struct scheduled_command_s { + + refframe_t timestamp; + char* command; +}; + +#define MAX_SCHEDULED_COMMANDS 64 +static int num_scheduled_commands = 0; +static scheduled_command_s scheduled_commands[ MAX_SCHEDULED_COMMANDS ]; + + +// remainder of a wait interval split by a scheduled command ------------------ +// +static int scheduled_wait = 0; + + +// clear all scheduled action commands ---------------------------------------- +// +void ResetScheduledActions() +{ + num_scheduled_commands = 0; + scheduled_wait = 0; +} + + +// schedule an action command for automatic execution at a later time --------- +// +PRIVATE +size_t ScheduleCommand( int *params ) +{ + ASSERT( params != NULL ); +/* + // fetch delta time and command to schedule + int timedelta = SWAP_32( params[ 0 ] ); + int actid = SWAP_32( params[ 1 ] ); + + // calc absolute activation timestamp + refframe_t timestamp = demoinfo_curtime + timedelta; + + // insert at correct position if already commands scheduled + for ( int cid = 0; cid < num_scheduled_commands; cid++ ) { + + if ( scheduled_commands[ cid ].timestamp >= timestamp ) { + + // this also ensures the move works correctly + if ( num_scheduled_commands < MAX_SCHEDULED_COMMANDS ) { + num_scheduled_commands++; + } + + // move back commands scheduled for a later time + if ( cid < MAX_SCHEDULED_COMMANDS - 1 ) { + size_t movesize = ( num_scheduled_commands - cid - 1 ); + movesize *= sizeof( scheduled_command_s ); + memmove( &scheduled_commands[ cid + 1 ], &scheduled_commands[ cid ], movesize ); + } + + // store this command + scheduled_commands[ cid ].timestamp = timestamp; + scheduled_commands[ cid ].command = (char *) ¶ms[ 1 ]; + break; + } + } + + // insert if no commands scheduled or position should be last + if ( cid == num_scheduled_commands ) { + + // commands that exceed the available space will be dropped silently + if ( cid < MAX_SCHEDULED_COMMANDS ) { + scheduled_commands[ cid ].timestamp = timestamp; + scheduled_commands[ cid ].command = (char *) ¶ms[ 1 ]; + num_scheduled_commands++; + } + } + + // return number of parameter bytes to skip + size_t paramsize = sizeof( int ) * 2; + paramsize += GetActionParamSize( actid, (char *) ¶ms[ 2 ] ); + return paramsize; +*/ + ASSERT( FALSE ); + return 0; +} + + +// execute command in binary format as written by WriteActionCommand() -------- +// +int ExecBinCommands( char* &data, int interactive ) +{ +/* // non-interactive console mode + con_non_interactive = !interactive; + + // to remember the current replay position when + // it is temporarily set to a scheduled command + char *curpos = NULL; + +restartwait: + + // execute commands until wait encountered + for (;;) { + + // restore position after scheduled command + if ( curpos != NULL ) { + data = curpos; + curpos = NULL; + } + + // check for execution of scheduled command + if ( num_scheduled_commands > 0 ) { + + if ( scheduled_commands[ 0 ].timestamp == demoinfo_curtime ) { + + // temporarily jump to position of scheduled command + curpos = data; + data = scheduled_commands[ 0 ].command; + + // remove this command from scheduled commands + if ( num_scheduled_commands > 1 ) { + size_t movesize = ( num_scheduled_commands - 1 ); + movesize *= sizeof( scheduled_command_s ); + memmove( &scheduled_commands[ 0 ], &scheduled_commands[ 1 ], movesize ); + } + num_scheduled_commands--; + } + } + + // for regular (non-scheduled) commands + if ( curpos == NULL ) { + + // exit loop on implied wait command + if ( scheduled_wait > 0 ) { + break; + } + + // count executed commands + demoinfo_curline++; + } + + // fetch command id + int actid = SWAP_32( *(int *)data ); + ASSERT( ( actid >= ACM_SIGNATURE ) && ( actid < ACM_NUM_COMMANDS ) ); + + // stop execution if end of command file reached + if ( actid == ACM_SIGNATURE ) { + bin_params = NULL; + con_non_interactive = FALSE; + return FALSE; + } + + data += sizeof( actid ); + bin_params = data; + + // exit loop on wait command + if ( actid == ACM_IDLEWAIT ) { + break; + } + + // check for packet replay + if ( action_commands[ actid ].flags & ACF_PACKETPARAM ) { + + // replay packet and get pointer advance + size_t advance = REC_PlayRemotePacketBin( (NetPacketExternal *) bin_params ); + + // check for invalid pointer advance ( -1 ... invalid packet ) + if ( advance == (size_t)-1 ) { + bin_params = NULL; + con_non_interactive = FALSE; + return FALSE; + } + + data += advance; + continue; + } + + // check for command scheduling + if ( action_commands[ actid ].flags & ACF_SCHEDPARAM ) { + // schedule command and advance pointer + data += ScheduleCommand( (int *) bin_params ); + continue; + } + + // determine how many parameter bytes need to be skipped. + // we do this before calling the action function because + // it might destroy its string parameter while parsing. + size_t paramsize = GetActionParamSize( actid, bin_params ); + + // call corresponding action function + // (it knows the type of its parameters) + ASSERT( action_commands[ actid ].func != NULL ); + (*action_commands[ actid ].func)(); + + // skip command's parameters (already read by action function) + data += paramsize; + } + + // wait command must not be scheduled + ASSERT( curpos == NULL ); + + // determine length of next wait interval + int waitcount; + if ( scheduled_wait > 0 ) { + + // completion of wait interval split by scheduled command + waitcount = scheduled_wait; + scheduled_wait = 0; + + } else { + + // preadvance over wait argument + data += sizeof( int32 ); + + // process wait + int32 suppliedwait; + if ( !AcFetchIntParams( 1, &suppliedwait ) ) { + + ASSERT( 0 ); + CON_AddLine( invalid_bin_wait ); + goto restartwait; + } + + waitcount = suppliedwait; + } + + // consume zero-waits transparently + if ( waitcount == 0 ) { + goto restartwait; + } + + ASSERT( waitcount > 0 ); + if ( waitcount < 0 ) { + CON_AddLine( invalid_bin_wait ); + goto restartwait; + } + + // check for scheduled commands in the middle of the next wait interval + if ( num_scheduled_commands > 0 ) { + ASSERT( demoinfo_curtime < scheduled_commands[ 0 ].timestamp ); + if ( demoinfo_curtime + waitcount > scheduled_commands[ 0 ].timestamp ) { + scheduled_wait = waitcount; + waitcount = scheduled_commands[ 0 ].timestamp - demoinfo_curtime; + scheduled_wait -= waitcount; + } + } + + // set duration to wait for + //CurActionWait = waitcount; + + // remember for info + demoinfo_lastwait = (refframe_t) waitcount; + + // flag must be reset + con_non_interactive = FALSE; + + bin_params = NULL; +*/ + return TRUE; +} + + +// save game state to current recording --------------------------------------- +// +void SaveGameState() +{ + //NOTE: + // this function is called by CON_EXT::StartRecording() to save + // the game state before a recording is started. + + AcSaveDataInfo(); +} + + +// include an entire demo file in the current compilation --------------------- +// +PRIVATE +int IncludeDemoFile( FILE *fp ) +{ +/* ASSERT( fp != NULL ); + + // read demo name + char *demoname; + if ( AcFetchStringParam( &demoname, TRUE ) ) { + + // open demo for inclusion + FILE *cpfp = DEMO_BinaryOpenDemo( demoname ); + if ( cpfp == NULL ) { + strcpy( paste_str, "demo file not found. (" ); + strcat( paste_str, demoname ); + strcat( paste_str, CON_FILE_COMPILED_EXTENSION ); + strcat( paste_str, ")" ); + CON_AddLine( paste_str ); + return TRUE; + } + + // skip header if present + DEMO_ParseDemoHeader( cpfp, -1, FALSE ); + + // demo will be copied in chunks + #define INCLUDE_BLOCK_SIZE 16384 + char *cpblock = (char *) ALLOCMEM( INCLUDE_BLOCK_SIZE ); + if ( cpblock == NULL ) + OUTOFMEM( 0 ); + + // paste in entire demo in chunks + size_t numread = 1; + while ( numread > 0 ) { + numread = fread( cpblock, 1, INCLUDE_BLOCK_SIZE, cpfp ); + if ( fwrite( cpblock, 1, numread, fp ) != numread ) { + CON_AddLine( compile_write_err ); + FREEMEM( cpblock ); + return FALSE; + } + } + + FREEMEM( cpblock ); + fclose( cpfp ); + + // strip off terminating eof marker + fseek( fp, -sizeof( int ), SEEK_CUR ); + } +*/ + return TRUE; +} + + +// write int parameters of action command in binary format to file ------------ +// +PRIVATE +int WriteActionIntParams( FILE *fp, int paranum ) +{ + ASSERT( fp != NULL ); + ASSERT( paranum > 0 ); + + // write int parameters + int32 *intparams = (int32 *) ALLOCMEM( sizeof( int32 ) * paranum * 2 ); + int32 *intparams_swapped = &intparams[ paranum ]; + if ( AcFetchIntParams( paranum, intparams ) ) { + + // must be written in little-endian format + SwapParams_int( paranum, intparams, intparams_swapped ); + + if ( fwrite( intparams_swapped, sizeof( int32 ), paranum, fp ) != (unsigned int)paranum ) { + CON_AddLine( compile_write_err ); + FREEMEM( intparams ); + return FALSE; + } + + } else { + + CON_AddLine( compile_arg_inval ); + } + + FREEMEM( intparams ); + + return TRUE; +} + + +// write float parameters of action command in binary format to file ---------- +// +PRIVATE +int WriteActionFloatParams( FILE *fp, int paranum ) +{ + ASSERT( fp != NULL ); + ASSERT( paranum > 0 ); + + // write float parameters + hprec_t *floatparams = (hprec_t *) ALLOCMEM( sizeof( hprec_t ) * paranum * 2 ); + hprec_t *floatparams_swapped = &floatparams[ paranum ]; + if ( AcFetchFloatParams( paranum, floatparams ) ) { + + // must be written in little-endian format + SwapParams_hprec( paranum, floatparams, floatparams_swapped ); + + if ( fwrite( floatparams_swapped, sizeof( hprec_t ), paranum, fp ) != (unsigned int)paranum ) { + CON_AddLine( compile_write_err ); + FREEMEM( floatparams ); + return FALSE; + } + + } else { + + CON_AddLine( compile_arg_inval ); + } + + FREEMEM( floatparams ); + + return TRUE; +} + + +// write string parameter of action command in binary format to file ---------- +// +PRIVATE +int WriteActionStringParam( FILE *fp, dword flags ) +{ + ASSERT( fp != NULL ); + + // read string parameter + char *strparam; + if ( AcFetchStringParam( &strparam, ACF_EAT_SPACE( flags ) ) ) { + + unsigned int slen = strlen( strparam ) + 1; + if ( fwrite( strparam, 1, slen, fp ) != slen ) { + CON_AddLine( compile_write_err ); + return FALSE; + } + + } else { + + CON_AddLine( compile_arg_inval ); + } + + return TRUE; +} + + +// write packet parameter of action command in binary format to file ---------- +// +PRIVATE +int WriteActionPacketParam( FILE *fp ) +{ + ASSERT( fp != NULL ); + + int rc = TRUE; +/* + // read session and packet ids and fetch corresponding packet + int32 params[ 2 ]; + if ( AcFetchIntParams( 2, params ) ) { + + // fetch packet + NetPacketExternal* ext_gamepacket = (NetPacketExternal*) ALLOCMEM( RECORD_PACKET_SIZE ); + size_t psize = REC_FetchRemotePacket( ext_gamepacket, params[ 0 ], params[ 1 ] ); + + // write packet if valid + if ( psize > 0 ) { + if ( fwrite( ext_gamepacket, 1, psize, fp ) != psize ) { + CON_AddLine( compile_write_err ); + rc = FALSE; + } + } else { + CON_AddLine( compile_pkt_inval ); + rc = FALSE; + } + + FREEMEM( ext_gamepacket ); + + } else { + + CON_AddLine( compile_arg_inval ); + } +*/ + ASSERT( FALSE ); + + return rc; +} + + +// forward declaration -------------------------------------------------------- +// +PRIVATE +void ExecActionCommand( char *cstr ); + + +// write a scheduled command into the current compilation --------------------- +// +PRIVATE +int WriteScheduledCommand( FILE *fp ) +{ + ASSERT( fp != NULL ); + + // prevent recursive call (scheduling of schedule command) + static int scheduled_command_writing_active = FALSE; + if ( scheduled_command_writing_active ) { + CON_AddLine( invalid_arg ); + return FALSE; + } + scheduled_command_writing_active = TRUE; + + // parse delta time parameter + char *timeparam = strtok( NULL, " " ); + if ( timeparam == NULL ) { + CON_AddLine( arg_missing ); + return FALSE; + } + + char *errpart; + int timedelta = strtol( timeparam, &errpart, int_calc_base ); + if ( *errpart != 0 ) { + CON_AddLine( invalid_arg ); + return FALSE; + } + + // write delta time in little-endian format + timedelta = SWAP_32( timedelta ); + if ( fwrite( &timedelta, sizeof( timedelta ), 1, fp ) != 1 ) { + CON_AddLine( compile_write_err ); + return FALSE; + } + + // append scheduled command as parameter after scheduling prefix + ExecActionCommand( NULL ); + + scheduled_command_writing_active = FALSE; + return TRUE; +} + + +// write action command in binary format to file ------------------------------ +// +PRIVATE +int WriteActionCommand( FILE *fp, int actid ) +{ + ASSERT( fp != NULL ); + ASSERT( actid > ACM_SIGNATURE ); + + dword flags = action_commands[ actid ].flags; + int paranum = action_commands[ actid ].numparameters; + + // exit if command may not be compiled + if ( ( flags & ACF_COMPILE ) == 0 ) { + CON_AddLine( compile_non_comp ); + return TRUE; + } + + // check for demo inclusion + // (command will be substituted) + if ( flags & ACF_DEMOPARAM ) { + return IncludeDemoFile( fp ); + } + + // must be written in little-endian format + actid = SWAP_32( actid ); + + // write id of command + if ( fwrite( &actid, sizeof( actid ), 1, fp ) != 1 ) { + CON_AddLine( compile_write_err ); + return FALSE; + } + + if ( flags & ACF_INTPARAMS ) { + + // write int parameters to file + if ( !WriteActionIntParams( fp, paranum ) ) { + return FALSE; + } + + } else if ( flags & ACF_FLOATPARAMS ) { + + // write float parameters to file + if ( !WriteActionFloatParams( fp, paranum ) ) { + return FALSE; + } + + } else if ( flags & ACF_STRINGPARAM ) { + + // write string parameter to file + ASSERT( paranum == 1 ); + if ( !WriteActionStringParam( fp, flags ) ) { + return FALSE; + } + + } else if ( flags & ACF_PACKETPARAM ) { + + // write packet parameter to file + if ( !WriteActionPacketParam( fp ) ) { + return FALSE; + } + + } else if ( flags & ACF_SCHEDPARAM ) { + + // write scheduled command to file + if ( !WriteScheduledCommand( fp ) ) { + return FALSE; + } + } + + return TRUE; +} + + +// call action function corresponding to action command or compile it --------- +// +INLINE +void CallActionFunction( int actid ) +{ + ASSERT( actid > ACM_SIGNATURE ); + + if ( compile_script ) { + + // if fp is NULL write error occurred earlier on + if ( compile_fp != NULL ) { + + // write command to file but do not execute it + if ( !WriteActionCommand( compile_fp, actid ) ) { + // write error cleanup + fclose( compile_fp ); + compile_fp = NULL; + } + } + + } else { + + // call corresponding action function + ASSERT( action_commands[ actid ].func != NULL ); + (*action_commands[ actid ].func)(); + } +} + + +// lookup action command and execute it --------------------------------------- +// +PRIVATE +void ExecActionCommand( char *cstr ) +{ + // scan table + char *aspec = strtok( cstr, " " ); + int aid; + for ( aid = ACM_SIGNATURE + 1; ACMSTR( aid ) != NULL; aid++ ) { + + if ( strcmp( ACMSTR( aid ), aspec ) == 0 ) { + + dword flags = action_commands[ aid ].flags; + if ( ( action_commands[ aid ].func != NULL ) && + ( processing_batch || ( flags & ACF_DIRECT ) ) ) { + + CallActionFunction( aid ); + + } else { + + CON_AddLine( non_batch_use ); + } + break; + } + } + + if ( ACMSTR( aid ) == NULL ) { + + // no action command with specified name found + CON_AddLine( invalid_action ); + } +} + + +// exec action command (command prefixed with "ac.") -------------------------- +// +void ActionCommand( char *cstr ) +{ + ASSERT( cstr != NULL ); + + // exit if action commands currently not allowed + if ( preempt_action_commands ) { + CON_AddLine( action_preempted ); + return; + } + + char *bdot = cstr + ACMLEN( ACM_SIGNATURE ); + if ( *bdot != 0 ) { + + // look up command and execute it + ExecActionCommand( cstr ); + + } else { + + // no action specified after "ac." + CON_AddLine( no_action_found ); + } +} + + +// module registration function ----------------------------------------------- +// +REGISTER_MODULE( CON_ACT_SV ) +{ + // reset mapping table for class ids + int oldflag = SV_CLASS_MAP_TABLE_DISABLE_RESET; + SV_CLASS_MAP_TABLE_DISABLE_RESET = FALSE; + ResetClassMapTable(); + SV_CLASS_MAP_TABLE_DISABLE_RESET = oldflag; +} + + + diff --git a/src/parsec_server/con_aux_sv.cpp b/src/parsec_server/con_aux_sv.cpp new file mode 100755 index 0000000..8b73dc6 --- /dev/null +++ b/src/parsec_server/con_aux_sv.cpp @@ -0,0 +1,236 @@ +/* + * PARSEC - SERVER Verbose Server Commands + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:47 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2001 + * Copyright (c) Markus Hadwiger <msh@parsec.org> 1998-2000 + * + * 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 <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "objstruc.h" + +// global externals +#include "globals.h" + +// rendering subsystem +//#include "r_patch.h" +//#include "r_supp.h" + +// subsystem headers +#include "net_defs.h" + +// local module header +#include "con_aux_sv.h" + +// proprietary module headers +#include "con_com_sv.h" +#include "con_int_sv.h" +#include "g_main_sv.h" +//#include "e_supp.h" +//#include "net_csdf.h" +#include "net_ports.h" +#include "e_gameserver.h" + +// flags +#define REGISTER_VERBOSE_SV_COMMANDS +//#define EXCLUDE_SPECIAL_SV_FLAGS + + +//NOTE: +// this module is only loosely related to the CONS_AUX.H +// header. that is, it may be omitted from linking +// entirely, if there should be no verbose SV commands. +// apart from this, project functionality will not be +// reduced. CONS_AUX.H must always be part of the project. + + +/* +//FIXME: +// perform remote event syncing for certain SV flags ------------------------- +// +#define DEF_RMEV_STATESYNC( f, k, v ) \ +PUBLIC \ +void RESync##f() \ +{ \ + if ( !NET_RmEvAllowed( RE_STATESYNC ) ) \ + return; \ + NET_RmEvStateSync( (k), (v) ); \ +} + +//FIXME_GAMESERVER: these fields are all part of the server config object + +DEF_RMEV_STATESYNC( NebulaId, RMEVSTATE_NEBULAID, AUXDATA_BACKGROUND_NEBULA_ID ); +DEF_RMEV_STATESYNC( Amazing, RMEVSTATE_AMAZING, AUXDATA_SKILL_AMAZING_TIME ); +DEF_RMEV_STATESYNC( Brilliant, RMEVSTATE_BRILLIANT, AUXDATA_SKILL_BRILLIANT_TIME ); +DEF_RMEV_STATESYNC( KillLimit, RMEVSTATE_KILLLIMIT, AUX_KILL_LIMIT_FOR_GAME_END ); + +*/ + +void G_RealizeGameVars() +{ + TheGame->RealizeVariables(); +} + +// for registration of SV flags/data as integer variable commands ----------------- +// +PRIVATE +int_command_s verbose_sv_commands[] = { + + { 0x80, "sv.package_scripts", 0, 1, &SV_PACKAGE_SCRIPTS, NULL, NULL, TRUE }, + { 0x80, "sv.console.level_messages", 0, 1, &SV_CONSOLE_LEVEL_MESSAGES, NULL, NULL, TRUE }, + { 0x80, "sv.package_scripts.list", 0, 1, &SV_PACKAGE_SCRIPT_LISTING, NULL, NULL, TRUE }, + { 0x80, "sv.class_map_table.disable_reset", 0, 1, &SV_CLASS_MAP_TABLE_DISABLE_RESET, NULL, NULL, FALSE }, + { 0x80, "aux.netcode.flags", 0, 16, &AUX_NETCODE_FLAGS, NULL, NULL, 0 }, +#ifdef ENABLE_CHEAT_COMMANDS + { 0x80, "sv.cheats.energy_checks", 0, 1, &SV_CHEAT_ENERGY_CHECKS, NULL, NULL, 0 }, + { 0x80, "sv.cheats.device_checks", 0, 1, &SV_CHEAT_DEVICE_CHECKS, NULL, NULL, 0 }, +#endif // ENABLE_CHEAT_COMMANDS + { 0x80, "aux_enable_object_overloading", 0, 1, &SV_OBJECTCLASSES_OVERLOADING, NULL, NULL, FALSE }, + { 0x80, "sv.objectclasses.overloading", 0, 1, &SV_OBJECTCLASSES_OVERLOADING, NULL, NULL, FALSE }, + + { 0x80, "sv.game.extras.autocreate", 0, 1, &SV_GAME_EXTRAS_AUTOCREATE, NULL, NULL, TRUE }, + { 0x80, "sv.game.extras.max", 0, 100, &SV_GAME_EXTRAS_MAXNUM, NULL, NULL, 20 }, + { 0x80, "sv.game.extras.testplace", 0, 1, &SV_GAME_EXTRAS_TESTPLACE, NULL, NULL, 0 }, + { 0x80, "sv.game.killlimit", 1, 4096, &SV_GAME_KILLLIMIT, G_RealizeGameVars, NULL, DEFAULT_KILL_LIMIT }, + { 0x80, "sv.game.timelimit", 1, 86400, &SV_GAME_TIMELIMIT, G_RealizeGameVars, NULL, DEFAULT_GAME_TIMELIMIT_SEC }, + { 0x80, "sv.game.restart.timeout", 0, 600, &SV_GAME_RESTART_TIMEOUT, G_RealizeGameVars, NULL, DEFAULT_RESTART_TIMELIMIT_SEC }, + + { 0x80, "sv.masterserver.sendheartbeat", 0, 1, &SV_MASTERSERVER_SENDHEARTBEAT, NULL, NULL, TRUE }, + { 0x80, "sv.serverid", 0, 0xFFFF, &SV_SERVERID, NULL, NULL, 0 }, + { 0x80, "sv.fedid", 0, 0xFFFF, &SV_FEDID, NULL, NULL, 0 }, + { 0x80, "sv.netconf.port", 1024, 0xFFFF,&SV_NETCONF_PORT, NULL, NULL, DEFAULT_GAMESERVER_UDP_PORT }, + + { 0x80, "sv.debug.netstream.dump", 0, 15, &SV_DEBUG_NETSTREAM_DUMP, NULL, NULL, 0 }, + { 0x80, "sv.debug.console.messages", 0, 1, &SV_DEBUG_CONSOLE_MESSAGES, NULL, NULL, 0 }, + + { 0x80, "sv.game.workdir.force_current", 0, 1, &SV_GAME_WORKDIR_FORCE_CURRENT, NULL, NULL, 0 }, + { 0x80, "sv.game.filepath.force_curent", 0, 1, &SV_GAME_FILEPATH_FORCE_CURRENT, NULL, NULL, 0 }, + + { 0x80, "sv.game.extras.maxnum", 0, 1000, &SV_GAME_EXTRAS_MAXNUM, NULL, NULL, 0 }, + { 0x80, "aux_disable_package_data_files", 0, 1, &AUX_DISABLE_PACKAGE_DATA_FILES , NULL, NULL, 0 }, + { 0x80, "aux_disable_package_scripts", 0, 1, &AUX_DISABLE_PACKAGE_SCRIPTS, NULL, NULL, 0 }, + +}; + +#define NUM_VERBOSE_SV_COMMANDS CALC_NUM_ARRAY_ENTRIES( verbose_sv_commands ) + + +// dummy command handler for all commands that are invalid for the server +// +int Cmd_DUMMY( char *classstr ) +{ + return TRUE; +} + + +// register all commands that are NOP for the server, to allow sharing console scripts +// between server and client +PRIVATE +void RegisterServerNOPCommands() +{ + + const char* szNOPCommands[] = { "shipdesc", + "classiter", + "classpart", + "shader.set", + NULL }; + + user_command_s regcom; + memset( ®com, 0, sizeof( user_command_s ) ); + + for( int nNOPCommand = 0; szNOPCommands[ nNOPCommand ] != NULL; nNOPCommand++ ) { + // register dummy command + regcom.command = szNOPCommands[ nNOPCommand ]; + regcom.numparams = 1; + regcom.execute = Cmd_DUMMY; + regcom.statedump = NULL; + CON_RegisterUserCommand( ®com ); + } +} + + +// module registration function ----------------------------------------------- +// +void CON_AUX_SV_Register() +{ + ASSERT( NUM_VERBOSE_SV_COMMANDS <= AUX_ARRAY_NUM_ENTRIES_USED ); + +#ifdef REGISTER_VERBOSE_SV_COMMANDS + unsigned int csv; + // register SV flags + for ( csv = 0; csv < NUM_VERBOSE_SV_COMMANDS; csv++ ) { + + //NOTE: + // bit 7 of the persistence field is used to denote + // special SV flags that may be excluded from registration. + + #ifdef EXCLUDE_SPECIAL_SV_FLAGS + + if ( verbose_sv_commands[ csv ].persistence & 0x80 ) + continue; + + #endif + + // destructive but doesn't matter here + verbose_sv_commands[ csv ].persistence &= ~0x80; + + CON_RegisterIntCommand( verbose_sv_commands + csv ); + } + +#endif // REGISTER_VERBOSE_SV_COMMANDS + + // init sv flags with default value + for( csv = 0; csv < NUM_VERBOSE_SV_COMMANDS; csv++ ) { + + int_command_s* cmd = &verbose_sv_commands[ csv ]; + + if ( ( cmd->bmin <= cmd->default_val ) && ( cmd->bmax >= cmd->default_val ) ) { + *cmd->intref = cmd->default_val; + + // do not call realize functions if the server is a master + if ( cmd->realize != NULL && !TheServer->GetServerIsMaster()) + cmd->realize(); + + } else { + // default value specified was not inside min/max bounds + ASSERT( FALSE ); + + // default to min + *cmd->intref = cmd->bmin; + + // do not call realize functions if the server is a master + if ( cmd->realize != NULL && !TheServer->GetServerIsMaster()) + cmd->realize(); + } + } + + // register all commands that are NOPs for the server + RegisterServerNOPCommands(); +} diff --git a/src/parsec_server/con_com_sv.cpp b/src/parsec_server/con_com_sv.cpp new file mode 100755 index 0000000..72c4c96 --- /dev/null +++ b/src/parsec_server/con_com_sv.cpp @@ -0,0 +1,849 @@ +/* + * PARSEC - Common Commands + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:47 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2002 + * Copyright (c) Markus Hadwiger <msh@parsec.org> 1996-1999 + * + * 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 <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "objstruc.h" +#include "od_props.h" + +// global externals +#include "globals.h" +#include "e_world_trans.h" + +// subsystem headers +//#include "aud_defs.h" +//#include "inp_defs.h" +#include "net_defs.h" +//#include "vid_defs.h" + +// local module header +#include "con_com_sv.h" + +// proprietary module headers +//#include "con_act.h" +#include "con_arg.h" +#include "con_aux_sv.h" +//#include "con_cani.h" +//#include "con_conf.h" +#include "con_ext_sv.h" +#include "con_info_sv.h" +#include "con_int_sv.h" +#include "con_list_sv.h" +#include "con_load_sv.h" +#include "con_main_sv.h" +//#include "con_say.h" +//#include "con_shad.h" +#include "con_std_sv.h" +//#include "e_demo.h" +//#include "e_supp.h" +//#include "g_supp.h" +//#include "h_supp.h" +#include "inp_main_sv.h" +//#include "m_main.h" +//#include "net_conn.h" +//#include "obj_ctrl.h" +#include "e_gameserver.h" +#include "sys_date.h" +#include "sys_path.h" +//#include "vid_init.h" +//#include "vid_plug.h" + + + +// generic string paste area -------------------------------------------------- +// +#define PASTE_STR_LEN 255 +static char paste_str[ PASTE_STR_LEN + 1 ]; + + +// string constants ----------------------------------------------------------- +// +static char base_str[] = "base is set to dec."; +#define BASE_INSERT_POS 15 +static char base_dec[] = "dec."; +static char base_hex[] = "hex."; +static char base_oct[] = "oct."; +static char base_set_to_dec[] = "base set to dec."; +static char base_set_to_hex[] = "base set to hex."; +static char base_set_to_oct[] = "base set to oct."; +static char info_sep_line[] = "-----------------------------------------------"; +PUBLIC char dont_use_params[] = "this command takes no parameters."; +static char server_name_missing[] = "server must be supplied."; +static char mode_number_invalid[] = "invalid mode number."; +static char mode_spec_invalid[] = "invalid mode specifier. use something like \"640x480x32\"."; +static char mode_spec_unavail[] = "this mode is not available in the current video subsystem."; +static char mode_modifier_invalid[] = "invalid mode modifier. use \"fullscreen\" or \"windowed\"."; +static char only_alpha_chars[] = "name must not contain non-alphabetic characters."; +static char name_change_preempted[] = "name change preempted. please try again."; + + +// remove all objects (command "objectclear") --------------------------------- +// +PRIVATE +void Cmd_OBJECTCLEAR() +{ + // free objects and particles + KillAllObjects(); +} + + +// print object counts for all object lists (command "objectcount") ----------- +// +PRIVATE +void Cmd_OBJECTCOUNT() +{ + sprintf( paste_str, "ships :%3d", TheWorld->GetNumShipObjects() ); + CON_AddLine( paste_str ); + sprintf( paste_str, "lasers :%3d", TheWorld->GetNumLaserObjects() ); + CON_AddLine( paste_str ); + sprintf( paste_str, "missiles:%3d", TheWorld->GetNumMisslObjects() ); + CON_AddLine( paste_str ); + sprintf( paste_str, "extras :%3d (obj, counted %d) and %d particle", TheWorld->GetNumExtraObjects(), TheWorld->m_nCurrentNumExtras, TheWorld->m_nCurrentNumPrtExtras ); + CON_AddLine( paste_str ); + sprintf( paste_str, "customs :%3d", TheWorld->GetNumCustmObjects() ); + CON_AddLine( paste_str ); +} + + +// display heap info (command "heap") ----------------------------------------- +// +#ifdef PARSEC_DEBUG +PRIVATE +int Cmd_HEAP( char *params ) +{ + ASSERT( params != NULL ); + USERCOMMAND_NOPARAM( params ); + + extern unsigned int Dyn_Mem_Size; + extern unsigned int Num_Allocs; + extern unsigned int Num_Frees; + + sprintf( paste_str, "heapsize: %.2fmb :%d", (float) Dyn_Mem_Size / ( 1024 * 1024 ), Dyn_Mem_Size ); + CON_AddLine( paste_str ); + sprintf( paste_str, "memobjs : %d", Num_Allocs - Num_Frees ); + CON_AddLine( paste_str ); + + return TRUE; +} +#endif // PARSEC_DEBUG + + +// show position of local ship (command "showposition") ----------------------- +// +PRIVATE +void Cmd_SHOWPOSITION() +{ + //FIXME: we need a shipindex parameter here + + /*sprintf( paste_str, "x position: %13f", GEOMV_TO_FLOAT( MyShip->ObjPosition[ 0 ][ 3 ] ) ); + CON_AddLine( paste_str ); + + sprintf( paste_str, "y position: %13f", GEOMV_TO_FLOAT( MyShip->ObjPosition[ 1 ][ 3 ] ) ); + CON_AddLine( paste_str ); + + sprintf( paste_str, "z position: %13f", GEOMV_TO_FLOAT( MyShip->ObjPosition[ 2 ][ 3 ] ) ); + CON_AddLine( paste_str );*/ +} + +// open output script for writing (don't overwrite existing file) ------------- +// +PRIVATE +int Cmd_Create( char *command ) +{ + return OpenOutputBatch( command, CM_CREATE ); +} + + +// open output script for writing (overwrite existing file) ------------------- +// +PRIVATE +int Cmd_Open( char *command ) +{ + return OpenOutputBatch( command, CM_OPEN ); +} + + +// append to output script ---------------------------------------------------- +// +PRIVATE +int Cmd_Append( char *command ) +{ + return OpenOutputBatch( command, CM_APPEND ); +} + + +// record demo script after saving state -------------------------------------- +// +PRIVATE +int Cmd_Recdem( char *command ) +{ + return StartRecording( command, TRUE ); +} + + +// record demo script without saving state first ------------------------------ +// +PRIVATE +int Cmd_Record( char *command ) +{ + return StartRecording( command, FALSE ); +} + + +// print parsec build info (command "build") ---------------------------------- +// +PRIVATE +int Cmd_BuildInfo( char *cstr ) +{ + ASSERT( cstr != NULL ); + HANDLE_COMMAND_DOMAIN_SEP( cstr ); + + // split off first token (detail) + char *dstr = strtok( cstr, " " ); + if ( dstr == NULL ) { + // display basic build info + CON_AddLine( build_text ); + CON_AddLine( build_date ); + CON_AddLine( build_time ); + return TRUE; + } + + // check if more than one parameter + if ( strtok( NULL, " " ) != NULL ) { + CON_AddLine( too_many_args ); + return TRUE; + } + + // check for detail spec + if ( strcmp( dstr, "x" ) == 0 ) { + + CON_AddLine( info_sep_line ); + CON_AddLine( build_text ); + CON_AddLine( info_sep_line ); + CON_AddLine( build_date ); + CON_AddLine( build_time ); + CON_AddLine( info_sep_line ); + //DisplayCurrentNetSubsys(); + CON_AddLine( info_sep_line ); + CON_AddLine( build_comp ); + CON_AddLine( build_bind ); + CON_AddLine( build_endn ); + CON_AddLine( info_sep_line ); + + } else { + + CON_AddLine( invalid_arg ); + } + + return TRUE; +} + + +// exec display command (display arbitrary text in message area) -------------- +// +PRIVATE +int Cmd_DisplayText( char *cstr ) +{ + ASSERT( cstr != NULL ); + HANDLE_COMMAND_DOMAIN( cstr ); + + const char *scan = GetStringBehindCommand( cstr, FALSE ); + + if ( ( scan != NULL ) && ( strlen( scan ) <= MAX_MESSAGELEN ) ) { + //FIXME: evt. precede text with "MESSAGE: " + MSGOUT( scan ); + } + + // recognize always (implicitly reserves domain) + return TRUE; +} + + +// exec write command (display arbitrary text in console) --------------------- +// +PRIVATE +int Cmd_WriteText( char *cstr ) +{ + //NOTE: + // this command normally only makes sense if + // used from within a console script. + + ASSERT( cstr != NULL ); + HANDLE_COMMAND_DOMAIN( cstr ); + + const char *scan = GetStringBehindCommand( cstr, TRUE ); + + // output string in console (new-line possible) + if ( scan != NULL ) { + MSGPUT( "\n::%s", scan ); + } + + // recognize always (implicitly reserves domain) + return TRUE; +} + + +// set current working directory (general use) -------------------------------- +// +PRIVATE +int Cmd_SetPathWorkingDirectory( char *command ) +{ + ASSERT( command != NULL ); + HANDLE_COMMAND_DOMAIN( command ); + + if ( SetSingleStringCommand( command, CurWorkDir, PATH_MAX ) ) { + + // process file name + char *path = SYSs_ProcessPathString( CurWorkDir ); + if ( path != CurWorkDir ) { + strncpy( CurWorkDir, path, PATH_MAX ); + CurWorkDir[ PATH_MAX ] = 0; + } + } + + // recognize always (implicitly reserves domain) + return TRUE; +} + + +// set file path for intro animation ------------------------------------------ +// +PRIVATE +int Cmd_SetPathAnimIntro( char *command ) +{ + ASSERT( command != NULL ); + HANDLE_COMMAND_DOMAIN( command ); +/* + extern char demo_movie_name[]; + if ( SetSingleStringCommand( command, demo_movie_name, PATH_MAX ) ) { + + // process file name and use current workdir + strcpy( demo_movie_name, SubstCurWorkDir( demo_movie_name ) ); + } +*/ + // recognize always (implicitly reserves domain) + return TRUE; +} + + +// set file path for all audio streams ---------------------------------------- +// +PRIVATE +int Cmd_SetPathAudioStreams( char *command ) +{ + ASSERT( command != NULL ); + HANDLE_COMMAND_DOMAIN( command ); +/* + if ( SetSingleStringCommand( command, console_audio_stream_path, PATH_MAX ) ) { + + // process file name and use current workdir + strcpy( console_audio_stream_path, SubstCurWorkDir( console_audio_stream_path ) ); + } +*/ + // recognize always (implicitly reserves domain) + return TRUE; +} + + +// set file path for intro audio stream --------------------------------------- +// +PRIVATE +int Cmd_SetPathAudioStreamIntro( char *command ) +{ + ASSERT( command != NULL ); + HANDLE_COMMAND_DOMAIN( command ); +/* + if ( SetSingleStringCommand( command, intro_stream_name, PATH_MAX ) ) { + + // process file name and use current workdir + strcpy( intro_stream_name, SubstCurWorkDir( intro_stream_name ) ); + } +*/ + // recognize always (implicitly reserves domain) + return TRUE; +} + + +// set file path for menu audio stream ---------------------------------------- +// +PRIVATE +int Cmd_SetPathAudioStreamMenu( char *command ) +{ + ASSERT( command != NULL ); + HANDLE_COMMAND_DOMAIN( command ); + +/* + if ( SetSingleStringCommand( command, menu_stream_name, PATH_MAX ) ) { + + // process file name and use current workdir + strcpy( menu_stream_name, SubstCurWorkDir( menu_stream_name ) ); + } +*/ + + // recognize always (implicitly reserves domain) + return TRUE; +} + + +// init console talk mode ----------------------------------------------------- +// +PRIVATE +void InitTalkMode() +{ + ASSERT( con_in_talk_mode ); + + // clear screen, set cursor to screen-bottom line + ClearConScreen(); + + // create separator line + CreateSeparatorLine( con_bottom ); + + // save number of line right below separator line + CON_AddLineFeed(); + CON_DisableLineFeed(); + con_talk_line = con_bottom; +} + + +// command "cls" -------------------------------------------------------------- +// +PRIVATE +void Cmd_CLS() +{ + if ( con_in_talk_mode ) { + + // reactivate talkmode + InitTalkMode(); + + } else { + wclear( g_curses_out_win ); + } +} + + +// command "talk" (toggle talk mode on and off) ------------------------------- +// +PRIVATE +void Cmd_TALK() +{ + // toggle talk mode + con_in_talk_mode = !con_in_talk_mode; + + if ( con_in_talk_mode ) { + // talkmode init clears screen + InitTalkMode(); + } else { + // clear screen + Cmd_CLS(); + } +} + + +// command "quit" ------------------------------------------------------------- +// +PRIVATE +void Cmd_QUIT() +{ + TheServer->SetQuitFlag(); +} + + +// command "hex" -------------------------------------------------------------- +// +PRIVATE +void Cmd_HEX() +{ + int_print_base = "%x"; + int_calc_base = 16; + CON_AddLine( base_set_to_hex ); + strcpy( base_str + BASE_INSERT_POS, base_hex ); +} + + +// command "dec" -------------------------------------------------------------- +// +PRIVATE +void Cmd_DEC() +{ + int_print_base = "%d"; + int_calc_base = 10; + CON_AddLine( base_set_to_dec ); + strcpy( base_str + BASE_INSERT_POS, base_dec ); +} + + +// command "oct" -------------------------------------------------------------- +// +PRIVATE +void Cmd_OCT() +{ + int_print_base = "%o"; + int_calc_base = 8; + CON_AddLine( base_set_to_oct ); + strcpy( base_str + BASE_INSERT_POS, base_oct ); +} + + +// command "base" ------------------------------------------------------------- +// +PRIVATE +void Cmd_BASE() +{ + CON_AddLine( base_str ); +} + +// close network connection ("disconnect") ------------------------------------ +// +PRIVATE +void Cmd_DISCONNECT() +{ +} + + +// commands corresponding to functions with no parameters --------------------- +// +struct cmd_noparam_s { + + int commandid; + void (*commandfunc)(void); +}; + +cmd_noparam_s cmd_noparam[] = { + + //{ CM_REPINFO, Cmd_QueryReplayInfo }, + //{ CM_REPSTOP, Cmd_StopBatchReplay }, + { CM_CLS, Cmd_CLS }, + { CM_CLEAR, Cmd_CLS }, + { CM_QUIT, Cmd_QUIT }, + { CM_EXIT, Cmd_QUIT }, + { CM_HEX, Cmd_HEX }, + { CM_DEC, Cmd_DEC }, + { CM_OCT, Cmd_OCT }, + { CM_BASE, Cmd_BASE }, + { CM_OBJECTCLEAR, Cmd_OBJECTCLEAR }, + { CM_OBJECTCOUNT, Cmd_OBJECTCOUNT }, + { CM_SHIPS, Cmd_ShipInfo }, + { CM_LISTCLASSES, Cmd_ListClasses }, + { CM_LISTTYPES, Cmd_ListTypes }, + //{ CM_LISTTEXTMACS, Cmd_ListSayMacros }, + //{ CM_LISTBINDINGS, Cmd_ListKeyBindings }, + //{ CM_SETORIGIN, Cmd_SetShipOrigin }, + { CM_SHOWPOSITION, Cmd_SHOWPOSITION }, + //{ CM_CLOSE, Cmd_CloseOutputBatch }, + //{ CM_LISTGAMEFUNCKEYS, Cmd_ListKeyFunctions }, + { CM_DISCONNECT, Cmd_DISCONNECT }, + //{ CM_TALKMODE, Cmd_TALK }, + { CM_RESCAN, Cmd_RescanExternalCommands }, + //{ CM_LISTDEMOS, Cmd_ListBinaryDemos }, +}; + +#define NUM_CMDS_NOPARAM CALC_NUM_ARRAY_ENTRIES( cmd_noparam ) + + +// commands corresponding to functions with no parameters --------------------- +// +int CheckCmdsNoParam( char *command ) +{ + //NOTE: + // this function returns TRUE if a valid command + // has been detected and executed. it returns + // FALSE if no valid command has been detected + // and the command string therefore needs + // further processing by the main dispatcher. + + ASSERT( command != NULL ); + + // check for commands in "no param" table + for ( unsigned int tid = 0; tid < NUM_CMDS_NOPARAM; tid++ ) { + + const char *cmdstr = CMSTR( cmd_noparam[ tid ].commandid ); + int cmdlen = CMLEN( cmd_noparam[ tid ].commandid ); + + if ( strncmp( command, cmdstr, cmdlen ) == 0 ) { + + // eat trailing whitespace + char *whsp = command + cmdlen; + for ( ; *whsp; whsp++ ) + if ( *whsp != ' ' ) + break; + if ( *whsp == 0 ) { + // only whitespace -> command valid + (*cmd_noparam[ tid ].commandfunc)(); + return TRUE; + } else if ( command[ cmdlen ] == ' ' ) { + // excess parameters + CON_AddLine( dont_use_params ); + return TRUE; + } + } + } + + return FALSE; +} + + +// commands corresponding to functions with an arbitrary string parameter ----- +// +struct cmd_customstring_s { + + int commandid; + int (*commandfunc)(char*); +}; + +cmd_customstring_s cmd_customstring[] = { + + { CM_BUILD, Cmd_BuildInfo }, + //{ CM_PATH_ANIM_INTRO, Cmd_SetPathAnimIntro }, + //{ CM_PATH_ASTREAM_INTRO, Cmd_SetPathAudioStreamIntro }, + //{ CM_PATH_ASTREAM_MENU, Cmd_SetPathAudioStreamMenu }, + //{ CM_PATH_ASTREAM, Cmd_SetPathAudioStreams }, + { CM_WORKINGDIRECTORY, Cmd_SetPathWorkingDirectory }, + { CM_LISTINTCOMMANDS, Cmd_ListIntCommands }, + { CM_LISTSTDCOMMANDS, Cmd_ListStdCommands }, + { CM_LISTEXTCOMMS, Cmd_ListExternalCommands }, + { CM_DISPLAY, Cmd_DisplayText }, + { CM_CAT, Cmd_CatExternalCommands }, + //{ CM_GAMELOOPBATCH, Cmd_SetGameLoopBatchName }, ???? + //{ CM_SAY, Cmd_SayText }, + { CM_LISTDATA, Cmd_ListDataTable }, + { CM_WRITESTRING, Cmd_WriteText }, + { CM_CREATE, Cmd_Create }, + { CM_OPEN, Cmd_Open }, + { CM_RECDEM, Cmd_Recdem }, + { CM_RECORD, Cmd_Record }, + { CM_APPEND, Cmd_Append }, + //{ CM_PLAYDEMO, Cmd_PlayBinaryDemoFile }, + { CM_LOAD, Cmd_ExecLoadCommand }, +}; + +#define NUM_CMDS_CUSTOMSTRING CALC_NUM_ARRAY_ENTRIES( cmd_customstring ) + + +// commands corresponding to functions taking an arbitrary string parameter --- +// +int CheckCmdsCustomString( char *command ) +{ + ASSERT( command != NULL ); + + //NOTE: + // this function returns TRUE if a valid command + // has been detected and executed. it returns + // FALSE if no valid command has been detected + // and the command string therefore needs + // further processing by the main dispatcher. + + //NOTE: + // a space as delimiter behind a command is not + // enforced by this function. that is, the supplied + // parsing functions can grab an entire domain + // (set of commands starting with the same characters). + // but this also means they have to ensure the delimiter + // is indeed there if there should be only one command. + + //NOTE: + // due to the semantics of this function and most parsing + // functions it calls, most "custom string" commands reserve + // an entire domain. that is, after this function no + // commands starting with the same characters as a + // "custom string" command will be recognized. + // this behavior also depends on HANDLE_COMMAND_DOMAIN(). + + // check for commands in "custom string" table + for ( unsigned int tid = 0; tid < NUM_CMDS_CUSTOMSTRING; tid++ ) { + + const char *cmdstr = CMSTR( cmd_customstring[ tid ].commandid ); + int cmdlen = CMLEN( cmd_customstring[ tid ].commandid ); + + if ( strncmp( command, cmdstr, cmdlen ) == 0 ) { + + ASSERT( cmd_customstring[ tid ].commandfunc != NULL ); + return (*cmd_customstring[ tid ].commandfunc)( command + cmdlen ); + } + } + + return FALSE; +} + + +// user command handling ------------------------------------------------------ +// +PRIVATE user_command_s user_commands_default[ 32 ]; +PUBLIC user_command_s* user_commands = user_commands_default; +PUBLIC int num_user_commands = 0; +PRIVATE int max_user_commands = 32; + + +// check user commands -------------------------------------------------------- +// +int CheckCmdsUserDefined( char *command ) +{ + ASSERT( command != NULL ); + + //NOTE: + // the semantics of this function is basically + // the same as for CheckCmdsCustomString(). + + //NOTE: + // this function will be called very early in + // the sequence of command comparsions. thus, + // it can override most other commands. the + // callback functions have to be very careful + // not to block entire domains without any reason. + + // check for commands in user commands table + for ( int cid = 0; cid < num_user_commands; cid++ ) { + + const char *cmdstr = user_commands[ cid ].command; + int cmdlen = user_commands[ cid ].commlen; + + if ( strncmp( command, cmdstr, cmdlen ) == 0 ) { + + ASSERT( user_commands[ cid ].execute != NULL ); + return (*user_commands[ cid ].execute)( command + cmdlen ); + } + } + + return FALSE; +} + + +// register a new user-defined command ---------------------------------------- +// +int CON_RegisterUserCommand( user_command_s *regcom ) +{ + //NOTE: + //CAVEAT: + // the supplied command string is not copied + // by this function. thus, the caller MUST ENSURE + // that this string is available indefinitely. + // (e.g., allocated statically.) + + //NOTE: + // the commlen field need not be valid, it will be + // set by this function. (in the original struct!) + + ASSERT( regcom != NULL ); + ASSERT( regcom->command != NULL ); + ASSERT( regcom->execute != NULL ); + ASSERT( regcom->numparams >= 0 ); + ASSERT( num_user_commands <= max_user_commands ); + + // expand table memory if already used up + if ( num_user_commands == max_user_commands ) { + + // expand exponentially + int newtabsize = max_user_commands * 2; + + // alloc new table + user_command_s *newlist = (user_command_s *) ALLOCMEM( sizeof( user_command_s ) * newtabsize ); + if ( newlist == NULL ) { + ASSERT( 0 ); + return FALSE; + } + + // set new size + max_user_commands = newtabsize; + + // move old table + memcpy( newlist, user_commands, sizeof( user_command_s ) * num_user_commands ); + if ( user_commands != user_commands_default ) + FREEMEM( user_commands ); + user_commands = newlist; + } + + // append new command + regcom->commlen = strlen( regcom->command ); + ASSERT( num_user_commands < max_user_commands ); + user_commands[ num_user_commands++ ] = *regcom; + + return TRUE; +} + +// replace the entry for an already registered user-defined command ----------- +// +int CON_ReplaceUserCommand( user_command_s* repcom ) +{ + //NOTE: + //CAVEAT: + // the supplied command string is not copied + // by this function. thus, the caller MUST ENSURE + // that this string is available indefinitely. + // (e.g., allocated statically.) + + //NOTE: + // the commlen field need not be valid, it will be + // set by this function. (in the original struct!) + + ASSERT( repcom != NULL ); + ASSERT( repcom->command != NULL ); + ASSERT( repcom->execute != NULL ); + ASSERT( repcom->numparams >= 0 ); + + for( int user_command_index = 0; user_command_index < num_user_commands; user_command_index++ ) { + + // check for equal command string + if( stricmp( user_commands[ user_command_index ].command , repcom->command ) == 0 ) { + repcom->commlen = strlen( repcom->command ); + user_commands[ user_command_index ] = *repcom; + + return TRUE; + } + } + + return FALSE; +} + + +// module registration function ----------------------------------------------- +// +REGISTER_MODULE( CON_COM_SV ) +{ + +#ifdef PARSEC_DEBUG + + // register "heap" command + user_command_s regcom; + regcom.command = "heap"; + regcom.numparams = 0; + regcom.execute = Cmd_HEAP; + regcom.statedump = NULL; + CON_RegisterUserCommand( ®com ); + +#endif // PARSEC_DEBUG + +} + + + diff --git a/src/parsec_server/con_ext_sv.cpp b/src/parsec_server/con_ext_sv.cpp new file mode 100755 index 0000000..c3c556d --- /dev/null +++ b/src/parsec_server/con_ext_sv.cpp @@ -0,0 +1,1193 @@ +/* + * PARSEC - Server External Commands + * + * $Author: uberlinuxguy $ - $Date: 2004/09/15 12:25:44 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2001 + * Copyright (c) Markus Hadwiger <msh@parsec.org> 1996-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 <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "objstruc.h" + +// global externals +#include "globals.h" + +// local module header +#include "con_ext_sv.h" + +// proprietary module headers +//#include "con_act.h" +#include "con_aux_sv.h" +#include "con_list_sv.h" +#include "con_main_sv.h" +#include "con_std_sv.h" +#include "con_vald.h" // shared ! +//#include "e_demo.h" +//#include "e_record.h" +//#include "e_replay.h" +#include "sys_file.h" +#include "sys_path.h" + + + +// maximum depth of recursion in command batch files +#define MAX_BATCH_REC_DEPTH 16 + +// special eof needed in package console scripts +#define PACKAGE_CON_EOF ";eof" +#define PACKAGE_CON_EOF_LEN 4 + +// generic string paste area +#define PASTE_STR_LEN 255 +static char paste_str[ PASTE_STR_LEN + 1 ]; + +// string constants +static char con_extension[] = CON_FILE_EXTENSION; +static char invalid_batch_name[]= "invalid name specifier."; +static char recursion_canceled[]= "command canceled due to recursion."; +static char recursion_maxdepth[]= "maximum recursion depth reached."; +static char replaying_script[] = "replay in progress (script)."; +static char replaying_binary[] = "replay in progress (binary)."; +static char no_replay[] = "no replay in progress."; +static char stopped_replay[] = "replay has been stopped."; +static char record_disallowed[] = "recording not allowed (object camera is active)."; + +static char con_file_wldcard1[] = "*" CON_FILE_EXTENSION; +static char con_file_wldcard2[] = REFCON_COMMANDS_DIR "*" CON_FILE_EXTENSION; +static char con_file_wldcard3[] = STDCON_COMMANDS_DIR "*" CON_FILE_EXTENSION; +static char con_file_wldcard4[] = RECORD_COMMANDS_DIR "*" CON_FILE_EXTENSION; + +// name of batch command file to execute on every gameloop start +char gameloop_start_script[ PATH_MAX + 1 ] = ""; + +// list of external commands +char external_commands[ MAX_EXTERNAL_COMMANDS + 1 ][ COMMAND_NAME_ALLOC_LEN + 1 ]; +int external_command_types[ MAX_EXTERNAL_COMMANDS + 1 ]; +int num_external_commands = 0; + +// external batch execution vars +int processing_batch = 0; // flag that commands are called from batch +int idle_reclevel = 0; // level of recursion idle wait stalled execution on +int rec_depth = 0; // current level of recursion + +// action commands not allowed? +int preempt_action_commands = FALSE; + +// batch execution recursive call stack +static int extcom_on_level[ MAX_BATCH_REC_DEPTH + 1 ]; +static FILE *fp_for_level[ MAX_BATCH_REC_DEPTH + 1 ]; + +// log file (created command batch) +FILE *log_fp; +char log_name[ COMMAND_NAME_ALLOC_LEN + 1 ]; +int log_commands = 0; + +// recording file +static char record_name[ COMMAND_NAME_ALLOC_LEN + 1 ]; + +// packet recording/replay file names +char packet_record_name[ COMMAND_NAME_ALLOC_LEN + 1 ]; +char packet_replay_name[ COMMAND_NAME_ALLOC_LEN + 1 ]; + +// single command line +static char com_line[ MAX_CONSOLE_LINE_LENGTH + 1 ]; + +// cat feature variables +static char cat_name[ MAX_CONSOLE_LINE_LENGTH + 1 ]; +static int cat_pos; + +// current active mod name +PUBLIC char* mod_names[ MAX_REGISTERED_MODS ]; +PUBLIC int mod_numnames = 0; +PUBLIC int mod_override = FALSE; + + +// acquire scripts located in mod directories --------------------------------- +// +PRIVATE +void AcquireModScripts() +{ + for ( int mod = 0; mod < mod_numnames; mod++ ) { + + char *path = (char *) ALLOCMEM( strlen( mod_names[ mod ] ) + strlen( con_file_wldcard1 ) + 2 ); + if ( path == NULL ) { + OUTOFMEM( 0 ); + } + + strcpy( path, mod_names[ mod ] ); + strcat( path, "/" ); + strcat( path, con_file_wldcard1 ); + + char *npath = SYSs_ProcessPathString( path ); + SYSs_AcquireScriptPath( npath, COMTYPE_MODIFICATION, mod_names[ mod ] ); + + FREEMEM( path ); + } +} + + +// build list of externally available commands -------------------------------- +// +void BuildExternalCommandList() +{ + //NOTE: + // the sequence of paths scanned in this function mostly determines the + // display order of console scripts (ls command, tab completion, ...). + // the actual precedence of scripts is determined by the search order + // in OpenScript(). for consistency, the two orders should be the same. + + num_external_commands = 0; + + SYS_AcquirePackageScripts( COMTYPE_PACKAGE ); + + if ( mod_override ) { + AcquireModScripts(); + } + + SYSs_AcquireScriptPath( con_file_wldcard1, COMTYPE_ROOT, NULL ); + SYSs_AcquireScriptPath( con_file_wldcard2, COMTYPE_REFERENCE, NULL ); + SYSs_AcquireScriptPath( con_file_wldcard3, COMTYPE_STANDARD, NULL ); + SYSs_AcquireScriptPath( con_file_wldcard4, COMTYPE_RECORDING, NULL ); + + if ( !mod_override ) { + AcquireModScripts(); + } +} + + +// check if string is valid external command ---------------------------------- +// +int CheckExternalCommand( char *com ) +{ + ASSERT( com != NULL ); + + //NOTE: + // command script directories are only scanned once. therefore, + // new commands copied into a directory while the game is still + // running will not be noticed until next startup or explicit + // rescanning via the "rescan" command. + + // to ensure scripts with explicitly specified paths work + com = SYSs_ProcessPathString( com ); + + // scan list in memory (no directory rescanning!!) + int cid = -1; + for ( int eid = 0; eid < num_external_commands; eid++ ) { + ASSERT( external_commands[ eid ] != NULL ); + if ( stricmp( external_commands[ eid ], com ) == 0 ) { + cid = eid; + break; + } + } + + // return command number + 1 (thus 0 means command is invalid) + return ( cid + 1 ); +} + + +// insert command into log file (command batch) ------------------------------- +// +int InsertCommandLog( char *command ) +{ + ASSERT( command != NULL ); + + if ( log_commands && ( strcmp( command, CMSTR( CM_CLOSE ) ) != 0 ) ) { + // append command to currently open logfile + fprintf( log_fp, "%s\n", command ); + return TRUE; + } + + return FALSE; +} + + +// search all console script paths and open batch if it exists ---------------- +// +PRIVATE +FILE *OpenScript( char *script ) +{ + ASSERT( script != NULL ); + + //NOTE: + // the sequence of paths scanned in this function determines the + // precedence of console scripts of the same name but located in + // different directories. + + //NOTE: + // the path search sequence is: + // 1. data package + // 2. root (local) directory + // 3. reference directory (refs) + // 4. standard directory (cons) + // 5. recording directory (recs) + + //NOTE: + // to avoid inconsistencies the search order in E_RECORD::LoadPacket() + // and E_DEMO::DEMO_BinaryOpenDemo() should be consistent + // with the search order used here. + + FILE *fp = NULL; + + strcpy( paste_str, script ); + strcat( paste_str, con_extension ); + + // to ensure scripts with explicitly specified paths work + char *path = SYSs_ProcessPathString( paste_str ); + + if ( SV_PACKAGE_SCRIPTS ) { + + // try to read from package first + if ( ( fp = SYS_fopen( path, "r" ) ) != NULL ) { + return fp; + } + } + + // search root (local) directory + if ( ( fp = fopen( path, "r" ) ) != NULL ) { + return fp; + } + + strcpy( paste_str, REFCON_COMMANDS_DIR ); + strcat( paste_str, script ); + strcat( paste_str, con_extension ); + + // search reference directory (refs) + if ( ( fp = fopen( paste_str, "r" ) ) != NULL ) { + return fp; + } + + strcpy( paste_str, STDCON_COMMANDS_DIR ); + strcat( paste_str, script ); + strcat( paste_str, con_extension ); + + // search standard directory (cons) + if ( ( fp = fopen( paste_str, "r" ) ) != NULL ) { + return fp; + } + + strcpy( paste_str, RECORD_COMMANDS_DIR ); + strcat( paste_str, script ); + strcat( paste_str, con_extension ); + + // search recording directory (recs) + return fopen( paste_str, "r" ); +} + + +// display content of command batch file -------------------------------------- +// +PRIVATE +void CatExternalCommands_ctd() +{ + int i = 0; + + FILE *fp = OpenScript( cat_name ); + + if ( fp != NULL ) { + + SYS_fseek( fp, cat_pos, SEEK_SET ); + + int packeof = 0; + while ( SYS_fgets( com_line, MAX_CONSOLE_LINE_LENGTH, fp ) != NULL ) { + + // check special package eof + if ( strncmp( com_line, PACKAGE_CON_EOF, PACKAGE_CON_EOF_LEN ) == 0 ) { + packeof = 1; + break; + } + + ProcessExternalLine( com_line ); + CON_AddLine( com_line ); + if ( ++i >= cur_vis_conlines - 1 ) { + break; + } + } + + if ( packeof || SYS_feof( fp ) ) { + CON_ListEndPrompt(); + } else { + CON_ListCtdPrompt(); + cat_pos = SYS_ftell( fp ); + } + + SYS_fclose( fp ); + } +} + + +// display content of command batch file (command "cat") ---------------------- +// +int Cmd_CatExternalCommands( char *cstr ) +{ + ASSERT( cstr != NULL ); + HANDLE_COMMAND_DOMAIN_SEP( cstr ); + + char *name = strtok( cstr, " " ); + + if ( ( name == NULL ) || ( strtok( NULL, " " ) != NULL ) ) { + CON_AddLine( "syntax: cat <script>." ); + return TRUE; + } + + if ( !CheckExternalCommand( name ) ) { + CON_AddLine( "script not found." ); + return TRUE; + } + + strcpy( cat_name, name ); + FILE *fp = OpenScript( cat_name ); + + if ( fp != NULL ) { + + int i = 0; + int packeof = 0; + while ( SYS_fgets( com_line, MAX_CONSOLE_LINE_LENGTH, fp ) != NULL ) { + + // check special package eof + if ( strncmp( com_line, PACKAGE_CON_EOF, PACKAGE_CON_EOF_LEN ) == 0 ) { + packeof = 1; + break; + } + + ProcessExternalLine( com_line ); + CON_AddLine( com_line ); + if ( ++i >= cur_vis_conlines - 1 ) { + break; + } + } + + if ( !packeof && !SYS_feof( fp ) ) { + cat_pos = SYS_ftell( fp ); + await_keypress = TRUE; + com_cont_func = CatExternalCommands_ctd; + } + + SYS_fclose( fp ); + } + + return TRUE; +} + + +// forward declaration -------------------------------------------------------- +// +PRIVATE +int ExecExternalLine( char *line, int echo ); + + +// execute console command file automatically on startup ---------------------- +// +void ExecConsoleFile( char *fname, int echo ) +{ + //NOTE: + // the scripts recognized by this function NEED NOT be + // contained in external_commands[] since files are opened + // directly and no error will result if they are not found. + + //NOTE: + // this function sets the preempt_action_commands flag + // to prevent execution of all (also direct!) action + // commands. the processing_batch flag also will not be + // set by this function. + + ASSERT( fname != NULL ); + ASSERT( ( echo == FALSE ) || ( echo == TRUE ) ); + + FILE *fp = SYS_fopen( fname, "r" ); + + if ( fp != NULL ) { + + // used to determine if invoked from CON_MAIN::InitConsole() + extern int console_init_done; + + // put together console message + sprintf( paste_str, "executing %s", fname ); + + if ( !console_init_done ) { + // invocation from InitConsole() (exec init script) + MSGPUT( "Executing console script (%s)...", fname ); + CON_AddLine( paste_str ); + } else { + if ( !SV_CONSOLE_LEVEL_MESSAGES ) { + // not invoked from InitConsole() + CON_AddMessage( paste_str ); + } + } + + // disallow action commands + preempt_action_commands = TRUE; + + // execute script line for line + strcpy( com_line, con_prompt ); + while ( SYS_fgets( com_line + 2, MAX_CONSOLE_LINE_LENGTH - 2, fp ) != NULL ) { + + // check special package eof + if ( strncmp( com_line + 2, PACKAGE_CON_EOF, PACKAGE_CON_EOF_LEN ) == 0 ) { + break; + } + + ExecExternalLine( com_line, echo ); + } + + // allow action commands once again + preempt_action_commands = FALSE; + + // insert new prompt + if ( console_init_done && echo ) + CON_AddLine( con_prompt ); + + MSGOUT( "\nScript execution done." ); + + SYS_fclose( fp ); + } +} + + +// scan external line, overwrite invalid characters with space ---------------- +// +int ProcessExternalLine( char *scan ) +{ + ASSERT( scan != NULL ); + + int exec = 0; + + while ( *scan != 0 ) { + if ( ( *scan == '\r' ) || ( *scan == '\n' ) ) { + // strip '\r' and '\n' at end of line + *scan-- = 0; + } else if ( !ConsoleASCIIValid[ (byte) *scan ] ) { + if ( ( *scan == '\\' ) && ConsoleASCIIValid[ (byte) '/' ] ) { + // replace backslashes with slashes + *scan = '/'; + } else { + // replace invalid characters with spaces + *scan = ' '; + } + } else { + // convert valid characters to lower-case + *scan = tolower( *scan ); + exec = 1; + } + scan++; + } + + return exec; +} + + +// execute externally obtained console line ----------------------------------- +// +PRIVATE +int ExecExternalLine( char *line, int echo ) +{ + ASSERT( line != NULL ); + ASSERT( ( echo == FALSE ) || ( echo == TRUE ) ); + + //NOTE: + // a ';' in the first column starts a comment + + int exec = FALSE; + if ( *( line + 2 ) != ';' ) { + + if ( ProcessExternalLine( line + 2 ) ) { + if ( echo ) + CON_AddLine( line ); + ExecConsoleCommand( line + 2, echo ); + exec = TRUE; + } + } + +/* + // count executed commands + if ( !DEMO_BinaryReplayActive() ) { + demoinfo_curline++; + } +*/ + + return exec; +} + + +// execute all lines of external command batch -------------------------------- +// +PRIVATE +void ExecLines( int echo ) +{ + //NOTE: + // this function is used by ExecExternalCommand(), + // RestartExternalCommand(), and ExecExternalFile() + // to execute all lines of a single console file. + // it will be called recursively if a script invokes + // another script. + + ASSERT( ( echo == FALSE ) || ( echo == TRUE ) ); + + // go one recursion level down + rec_depth++; + + strcpy( com_line, con_prompt ); + while ( SYS_fgets( com_line + 2, MAX_CONSOLE_LINE_LENGTH - 2, fp_for_level[ rec_depth - 1 ] ) != NULL ) { + + // check special package eof + if ( strncmp( com_line + 2, PACKAGE_CON_EOF, PACKAGE_CON_EOF_LEN ) == 0 ) { + break; + } + + // execute current line + ExecExternalLine( com_line, echo ); + + // stall recursion if idlewait specified + // FIXME: do we need the AC.WAIT command in the server ?? + /*if ( CurActionWait > 0 ) { + break; + }*/ + } + + // go one recursion level up + rec_depth--; +} + + +// execute external command (command batch file) ------------------------------ +// +PRIVATE +int ExecExternalCommand( int extcom, int echo ) +{ + //NOTE: + // this function simply assumes that the current + // recursion level in rec_depth is valid and can + // be used. it is called by CallExternalCommand() + // which performs recursion level management. + + ASSERT( extcom >= 0 ); + ASSERT( ( echo == FALSE ) || ( echo == TRUE ) ); + + // store number of command on this level + extcom_on_level[ rec_depth ] = extcom; + + // try to open command file + fp_for_level[ rec_depth ] = OpenScript( external_commands[ extcom ] ); + + // exec command file + if ( fp_for_level[ rec_depth ] != NULL ) { + + // store name to use as packet replay file + ASSERT( strlen( external_commands[ extcom ] ) <= COMMAND_NAME_ALLOC_LEN ); + strcpy( packet_replay_name, external_commands[ extcom ] ); + + // exec all lines of this batch + ExecLines( echo ); + + // keep file open if recursion stalled + // FIXME: do we need the AC.WAIT command in the server ?? + //if ( CurActionWait == 0 ) { + SYS_fclose( fp_for_level[ rec_depth ] ); + fp_for_level[ rec_depth ] = NULL; + //} + + return TRUE; + } + + return FALSE; +} + + +// execute external command (command batch file) via filename ----------------- +// +int ExecExternalFile( char *command ) +{ + //NOTE: + // this is the main internal interface to execute + // a specified console script. (used to automatically + // execute scripts on game loop entry and video + // subsystem switches, for instance.) the scripts + // recognized by this function need to be contained + // in external_commands[]. + + //NOTE: + // the processing_batch flag will not be set by this + // function. this means that most action commands + // will not be allowed to execute. + + //CAVEAT: + // no checks are performed whether any replay is + // currently in progress and therefore execution + // should be disallowed. + + if ( ( command == NULL ) || ( *command == 0 ) ) + return FALSE; + + int extcom = CheckExternalCommand( command ); + + if ( extcom-- == 0 ) + return FALSE; + + // store number of command on this level + extcom_on_level[ rec_depth ] = extcom; + + // try to open command file + fp_for_level[ rec_depth ] = OpenScript( external_commands[ extcom ] ); + + // exec command file + if ( fp_for_level[ rec_depth ] != NULL ) { + + // exec all lines of this batch + ExecLines( FALSE ); + + // keep file open if recursion stalled + // FIXME: do we need the AC.WAIT command in the server ?? + //if ( CurActionWait == 0 ) { + SYS_fclose( fp_for_level[ rec_depth ] ); + fp_for_level[ rec_depth ] = NULL; + //} + + return TRUE; + } + + return FALSE; +} + + +// compile the state header script necessary for demo cutting ----------------- +// +int CompileDemoCut( FILE *fp, char *script ) +{ + ASSERT( fp != NULL ); + ASSERT( script != NULL ); + + ASSERT( rec_depth == 0 ); + + if ( num_external_commands == MAX_EXTERNAL_COMMANDS ) { + return FALSE; + } + + // make sure the script is known + int extcom = CheckExternalCommand( script ); + if ( extcom == 0 ) { + // if enough space append to list and set type to root + ASSERT( strlen( script ) <= COMMAND_NAME_ALLOC_LEN ); + strcpy( external_commands[ num_external_commands ], script ); + external_command_types[ num_external_commands ] = COMTYPE_ROOT; + extcom = ++num_external_commands; + } + + // compile the script to already open file + compile_fp = fp; + compile_script = TRUE; + processing_batch = TRUE; + + ExecExternalCommand( extcom - 1, FALSE ); + + compile_fp = NULL; + processing_batch = FALSE; + compile_script = FALSE; + + return TRUE; +} + + +// determine if script replay currently in progress --------------------------- +// +int ScriptReplayActive() +{ + // if no recursion is stalled then no + // replay is currently in progress + return ( idle_reclevel > 0 ); +} + + +// restart ExecExternalCommand() ---------------------------------------------- +// +int RestartExternalCommand( int echo ) +{ + //NOTE: + // this function is called by E_REPLAY::FetchNewActions() + // to recommence script execution. it is quite similar + // to ExecExternalCommand() and that function will + // actually be called by ExecLines() if further command + // invocations are encountered. + + ASSERT( ( echo == FALSE ) || ( echo == TRUE ) ); + + // check if command batch execution has been stalled + if ( !ScriptReplayActive() ) + return FALSE; + + // set batch execution flag + processing_batch = TRUE; + + // non-interactive console mode + con_non_interactive = TRUE; + + // start in bottom level of recursion + rec_depth = idle_reclevel; + + // restart stalled execution + idle_reclevel = 0; + + do { + + // set correct recursion level + rec_depth--; + + // exec all lines of this batch + ExecLines( echo ); + + // keep file open if recursion stalled + // FIXME: do we need the AC.WAIT command in the server ?? + //if ( CurActionWait == 0 ) { + SYS_fclose( fp_for_level[ rec_depth ] ); + fp_for_level[ rec_depth ] = NULL; + /*} else { + // simulate unraveling of + // recursion to highest level + rec_depth = 0; + break; + }*/ + + } while ( rec_depth > 0 ); + + // clear batch execution flag + processing_batch = FALSE; + + // back to interactive console mode + con_non_interactive = FALSE; + + // ensure no dangling automatic actions + // if execution finished (not stalled) + if ( idle_reclevel == 0 ) { + //REPLAY_StopAutomaticActions(); + } + + return TRUE; +} + + +// check validity of batchname ------------------------------------------------ +// +PRIVATE +int CheckBatchName( char *name ) +{ + ASSERT( name != NULL ); + + int fnlen = strlen( name ); + + if ( ( fnlen < 1 ) || ( fnlen > COMMAND_NAME_VALID_LEN ) || + ( strtok( NULL, " " ) != NULL ) ) { + CON_AddLine( invalid_batch_name ); + return FALSE; + } + + for ( int pos = 0; pos < fnlen; pos++ ) { + // only alphanumeric characters and underscores are allowed + if ( !isalnum( name[ pos ] ) && ( name[ pos ] != '_' ) ) { + CON_AddLine( invalid_batch_name ); + return FALSE; + } + } + + return TRUE; +} + + +// open output script for writing --------------------------------------------- +// +int OpenOutputBatch( char *cstr, int type ) +{ + ASSERT( cstr != NULL ); + ASSERT( ( type == CM_CREATE ) || ( type == CM_OPEN ) || ( type == CM_APPEND ) ); + HANDLE_COMMAND_DOMAIN_SEP( cstr ); + + if ( log_commands ) { + CON_AddLine( "there is another script open." ); + return TRUE; + } + + char *name = strtok( cstr, " " ); + if ( name == NULL ) { + CON_AddLine( arg_missing ); + return TRUE; + } + + if ( !CheckBatchName( name ) ) + return TRUE; + strcpy( log_name, name ); + + if ( CheckExternalCommand( name ) && ( type == CM_CREATE ) ) { + CON_AddLine( "there is already a script with this name." ); + return TRUE; + } + strcpy( paste_str, name ); + strcat( paste_str, con_extension ); + + const char *mode = ( type == CM_APPEND ) ? "a" : "w"; + if ( ( log_fp = fopen( paste_str, mode ) ) != NULL ) { + log_commands = 1; + } else { + CON_AddLine( "cannot open script with this name." ); + } + + return TRUE; +} + + +// close output batch --------------------------------------------------------- +// +void CloseOutputBatch() +{ + if ( log_commands ) { + + fclose( log_fp ); + log_commands = 0; + sprintf( paste_str, "script %s closed.", log_name ); + CON_AddLine( paste_str ); + + // if command not already in list: append + if ( !CheckExternalCommand( log_name ) && + ( num_external_commands < MAX_EXTERNAL_COMMANDS ) ) { + + // if enough space append to list and set type to root + ASSERT( strlen( log_name ) <= COMMAND_NAME_ALLOC_LEN ); + strcpy( external_commands[ num_external_commands ], log_name ); + external_command_types[ num_external_commands ] = COMTYPE_ROOT; + num_external_commands++; + } + + } else { + CON_AddLine( "there is no script open." ); + } +} + + +// set file name of script automatically executed on start of game loop ------- +// +int Cmd_SetGameLoopBatchName( char *cstr ) +{ + ASSERT( cstr != NULL ); + HANDLE_COMMAND_DOMAIN_SEP( cstr ); + + char *name = strtok( cstr, " " ); + + if ( name == NULL ) { + if ( *gameloop_start_script != 0 ) { + // display name of currently selected + // gameloop start-script + CON_AddLine( gameloop_start_script ); + } else { + CON_AddLine( "no gameloop start-script set." ); + } + return TRUE; + } + + if ( strtok( NULL, " " ) != NULL ) { + CON_AddLine( "syntax: glbatch <script>." ); + return TRUE; + } + + if ( CheckBatchName( name ) ) { + // set new name for gameloop start-script + strcpy( gameloop_start_script, name ); + sprintf( paste_str, "gameloop start-script set to: %s", gameloop_start_script ); + CON_AddLine( paste_str ); + } else { + CON_AddLine( "invalid script name." ); + } + + return TRUE; +} + + +// start recording into command batch ----------------------------------------- +// +int StartRecording( char *cstr, int savestate ) +{ + ASSERT( cstr != NULL ); + HANDLE_COMMAND_DOMAIN_SEP( cstr ); + + if ( !RecordingActive ) { + + // fetch name for recording + char *name = strtok( cstr, " " ); + if ( name == NULL ) { + CON_AddLine( arg_missing ); + return TRUE; + } + if ( !CheckBatchName( name ) ) + return TRUE; + + // store name (needed by CloseRecording()) + strcpy( record_name, name ); + + // store name to use as packet record file + strcpy( packet_record_name, name ); + + // create actual file name + strcpy( paste_str, name ); + strcat( paste_str, con_extension ); + + if ( ( RecordingFp = fopen( paste_str, "w" ) ) != NULL ) { + + RemoteRecPacketId = 0; + RecordingActive = TRUE; + + // warn user if packet recording is off + if ( !RecordRemotePackets ) { + CON_AddLine( "[warning]: remote packet recording is off." ); + } + + // save game state (data screenshot) if desired + if ( savestate ) { +/* + REC_InitMatrices(); + SaveGameState(); +*/ + CON_AddLine( "game state saved." ); + } + + } else { + + CON_AddLine( "cannot start a recording with this name." ); + } + + } else { + + CON_AddLine( "recording already in progress." ); + } + + return TRUE; +} + + +// close recording batch ------------------------------------------------------ +// +void CloseRecording() +{ + fclose( RecordingFp ); + + RecordingActive = 0; + RemoteRecSessionId++; + + sprintf( paste_str, "recording %s closed.", record_name ); + CON_AddLine( paste_str ); + + // if command not already in list: append + if ( !CheckExternalCommand( record_name ) && + ( num_external_commands < MAX_EXTERNAL_COMMANDS ) ) { + + // if enough space append to list and set type to + // root (not recording since it will be placed + // into the root directory!) + ASSERT( strlen( record_name ) <= COMMAND_NAME_ALLOC_LEN ); + strcpy( external_commands[ num_external_commands ], record_name ); + external_command_types[ num_external_commands ] = COMTYPE_ROOT; + num_external_commands++; + } +} + + +// call external command and check recursion; called by ExecConsoleCommand() -- +// +void CallExternalCommand( int extcom, int echo ) +{ + //NOTE: + // no new command can be started if replay in progress. + // unfortunately, this means that no script at all + // can be executed while any other script is stalled. + // alas, this includes utility scripts. + + ASSERT( extcom >= 0 ); + ASSERT( ( echo == FALSE ) || ( echo == TRUE ) ); + +/* + // init demo info + if ( !DEMO_BinaryReplayActive() ) { + DEMO_InitInfo(); + } +*/ + + // assume action commands will be allowed + int actionflag = TRUE; + + // remember action wait + // FIXME: do we need the AC.WAIT command in the server ?? + //int curactionwait = CurActionWait; + + // handle non-recursive call + if ( rec_depth == 0 ) { + + if ( ScriptReplayActive() ) { + CON_AddLine( "script replay in progress. command not executed." ); + return; + } + + /*if ( DEMO_BinaryReplayActive() ) { + + // disallow action commands to allow + // simultaneous playback of binary demo + actionflag = FALSE; + + //NOTE: + // this ensures that no other ac.wait will be + // executed, conflicting with the current one. + + // prevent batch execution from stalling + // FIXME: do we need the AC.WAIT command in the server ?? + CurActionWait = 0; + }*/ + } + + // check if maximum recursion depth reached + if ( rec_depth <= MAX_BATCH_REC_DEPTH ) { + + // set batch execution flag + processing_batch = actionflag; + + // check all recursion levels above this one + int lvl; + for ( lvl = 0; lvl < rec_depth; lvl++ ) + if ( extcom_on_level[ lvl ] == extcom ) + break; + + // open contained batch if no endless recursion detected + if ( lvl == rec_depth ) { + ExecExternalCommand( extcom, echo ); + } else { + CON_AddLine( recursion_canceled ); + } + + } else { + // output error message if no more recursion levels available + CON_AddLine( recursion_maxdepth ); + } + + // handle non-recursive call + if ( rec_depth == 0 ) { + + // clear batch execution flag + processing_batch = FALSE; + + // distinguish script only/simultaneous binary replay + if ( actionflag == FALSE ) { + + // restore wait interval if binary demo + // replay simultaneously in progress + // FIXME: do we need the AC.WAIT command in the server ?? + //CurActionWait = curactionwait; + + } else { + + // ensure no dangling automatic actions + // (only if no simultaneous binary replay) + //REPLAY_StopAutomaticActions(); + } + } +} + + +// kill batch execution that is currently in progress ------------------------- +// +void ScriptStopReplay() +{ + // immediately stop idle waiting + // FIXME: do we need the AC.WAIT command in the server ?? + //CurActionWait = 0; + + // shut down recursive call stack + while ( idle_reclevel > 0 ) { + // step one level up + idle_reclevel--; + // close files kept open to maintain + // state of recursion for script files + if ( fp_for_level[ idle_reclevel ] != NULL ) { + SYS_fclose( fp_for_level[ idle_reclevel ] ); + fp_for_level[ idle_reclevel ] = NULL; + } + } + ASSERT( idle_reclevel == 0 ); + + // kill dangling automatic actions + //REPLAY_StopAutomaticActions(); +} + + +// close either recording or output batch ------------------------------------- +// +void Cmd_CloseOutputBatch() +{ + if ( RecordingActive ) + CloseRecording(); + else + CloseOutputBatch(); +} + + +// stop replay of command batch (command "repstop") --------------------------- +// +void Cmd_StopBatchReplay() +{ +// if ( !DEMO_ReplayActive() ) { + CON_AddLine( no_replay ); +/* } else { + // stop both binary replay and command batch replay + DEMO_StopReplay(); + CON_AddLine( stopped_replay ); + } +*/ +} + + +// query status of command batch replay (command "repinfo") ------------------- +// +void Cmd_QueryReplayInfo() +{ + /*if ( ScriptReplayActive() ) + CON_AddLine( replaying_script ); + else if ( DEMO_BinaryReplayActive() ) + CON_AddLine( replaying_binary ); + else*/ + CON_AddLine( no_replay ); +} + + +// rescan list of external commands (command "rescan") ------------------------ +// +void Cmd_RescanExternalCommands() +{ + // rebuild demo info + //DEMO_RegisterInitialDemos(); + + // rebuild script cache + BuildExternalCommandList(); + CON_AddLine( "script cache rebuilt." ); +} + + + diff --git a/src/parsec_server/con_info_sv.cpp b/src/parsec_server/con_info_sv.cpp new file mode 100755 index 0000000..f11dfb7 --- /dev/null +++ b/src/parsec_server/con_info_sv.cpp @@ -0,0 +1,2120 @@ +/* + * PARSEC - Server Object Info Commands + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:47 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2002 + * Copyright (c) Markus Hadwiger <msh@parsec.org> 1996-2000 + * + * 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 <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "objstruc.h" +#include "od_class.h" +#include "od_props.h" + +// global externals +#include "globals.h" +#include "e_world_trans.h" + +// local module header +#include "con_info_sv.h" + +// proprietary module headers +#include "con_arg.h" +#include "con_aux_sv.h" +#include "con_int_sv.h" +#include "con_main_sv.h" +#include "con_std_sv.h" +//#include "e_supp.h" +#include "obj_clas.h" +//#include "obj_comm.h" +//#include "obj_ctrl.h" +#include "obj_cust.h" + + + +// generic string paste area -------------------------------------------------- +// +#define PASTE_STR_LEN 1023 +static char paste_str[ PASTE_STR_LEN + 1 ]; + + +// string constants ----------------------------------------------------------- +// +static char prop_syntax[] = "syntax: prop <type_name>.<property> <value>"; +static char propc_syntax[] = "syntax: propc <class_name>.<property> <value>"; +static char propo_syntax[] = "syntax: propo <object_id>.<property> <value>"; +static char prop_wrong_type[] = "invalid type name."; +static char prop_wrong_class[] = "invalid class name."; +static char prop_objid_syntax[] = "object id must be an int."; +static char prop_no_object[] = "no object with this id."; +static char prop_wrong_field[] = "invalid property name."; +static char typeinfo_syntax[] = "syntax: typeinfo <type_name>"; +static char classinfo_syntax[] = "syntax: classinfo <class_name>"; +static char objectinfo_syntax[] = "syntax: objectinfo <object_id>"; +static char class_not_loaded[] = "class is not loaded."; +static char no_objs_of_type[] = "there are no objects of this type."; +static char not_accessible[] = "properties of this type are not accessible."; +static char face_id_invalid[] = "face id invalid."; +static char no_texture_found[] = "texture invalid."; +static char no_face_selected[] = "no face selection specified."; +static char base_object_empty[] = "base object is empty."; + + +// property access type specification ----------------------------------------- +// +#define ACCESSTYPE_INT 0 // access property value as int +#define ACCESSTYPE_FLOAT 1 // access property value as float +#define ACCESSTYPE_STRING 2 // access property value as char[] +#define ACCESSTYPE_CHARPTR 3 // access property value as char* + + +// property lists for default types ------------------------------------------- +// +#define Ship1Obj_CurDamage offsetof( ShipObject, CurDamage ) +#define Ship1Obj_MaxDamage offsetof( ShipObject, MaxDamage ) +#define Ship1Obj_Energy offsetof( ShipObject, CurEnergy ) +#define Ship1Obj_MaxEnergy offsetof( ShipObject, MaxEnergy ) +#define Ship1Obj_CurSpeed offsetof( ShipObject, CurSpeed ) +#define Ship1Obj_MaxSpeed offsetof( ShipObject, MaxSpeed ) +#define Ship1Obj_Weapons offsetof( ShipObject, Weapons ) +#define Ship1Obj_Specials offsetof( ShipObject, Specials ) +#define Ship1Obj_NumMissls offsetof( ShipObject, NumMissls ) +#define Ship1Obj_MaxNumMissls offsetof( ShipObject, MaxNumMissls ) +#define Ship1Obj_NumHomMissls offsetof( ShipObject, NumHomMissls ) +#define Ship1Obj_MaxNumHomMissls offsetof( ShipObject, MaxNumHomMissls ) +#define Ship1Obj_NumPartMissls offsetof( ShipObject, NumPartMissls ) +#define Ship1Obj_MaxNumPartMissls offsetof( ShipObject, MaxNumPartMissls ) +#define Ship1Obj_NumMines offsetof( ShipObject, NumMines ) +#define Ship1Obj_MaxNumMines offsetof( ShipObject, MaxNumMines ) +#define Ship1Obj_YawPerRefFrame offsetof( ShipObject, YawPerRefFrame ) +#define Ship1Obj_PitchPerRefFrame offsetof( ShipObject, PitchPerRefFrame ) +#define Ship1Obj_RollPerRefFrame offsetof( ShipObject, RollPerRefFrame ) +#define Ship1Obj_XSlidePerRefFrame offsetof( ShipObject, XSlidePerRefFrame ) +#define Ship1Obj_YSlidePerRefFrame offsetof( ShipObject, YSlidePerRefFrame ) +#define Ship1Obj_SpeedIncPerRefFrame offsetof( ShipObject, SpeedIncPerRefFrame ) +#define Ship1Obj_SpeedDecPerRefFrame offsetof( ShipObject, SpeedDecPerRefFrame ) +#define Ship1Obj_FireRepeatDelay offsetof( ShipObject, FireRepeatDelay ) +#define Ship1Obj_FireDisableDelay offsetof( ShipObject, FireDisableDelay ) +#define Ship1Obj_MissileDisableDelay offsetof( ShipObject, MissileDisableDelay ) +#define Ship1Obj_Laser1X1 offsetof( ShipObject, Laser1_X ) +#define Ship1Obj_Laser1X2 ( offsetof( ShipObject, Laser1_X ) + sizeof( geomv_t ) * 1 ) +#define Ship1Obj_Laser1X3 ( offsetof( ShipObject, Laser1_X ) + sizeof( geomv_t ) * 2 ) +#define Ship1Obj_Laser1X4 ( offsetof( ShipObject, Laser1_X ) + sizeof( geomv_t ) * 3 ) +#define Ship1Obj_Laser2X1 ( offsetof( ShipObject, Laser1_X ) + sizeof( geomv_t ) * 4 ) +#define Ship1Obj_Laser2X2 ( offsetof( ShipObject, Laser1_X ) + sizeof( geomv_t ) * 5 ) +#define Ship1Obj_Laser2X3 ( offsetof( ShipObject, Laser1_X ) + sizeof( geomv_t ) * 6 ) +#define Ship1Obj_Laser2X4 ( offsetof( ShipObject, Laser1_X ) + sizeof( geomv_t ) * 7 ) +#define Ship1Obj_Laser3X1 ( offsetof( ShipObject, Laser1_X ) + sizeof( geomv_t ) * 8 ) +#define Ship1Obj_Laser3X2 ( offsetof( ShipObject, Laser1_X ) + sizeof( geomv_t ) * 9 ) +#define Ship1Obj_Laser3X3 ( offsetof( ShipObject, Laser1_X ) + sizeof( geomv_t ) * 10 ) +#define Ship1Obj_Laser3X4 ( offsetof( ShipObject, Laser1_X ) + sizeof( geomv_t ) * 11 ) +#define Ship1Obj_Laser4X1 ( offsetof( ShipObject, Laser1_X ) + sizeof( geomv_t ) * 12 ) +#define Ship1Obj_Laser4X2 ( offsetof( ShipObject, Laser1_X ) + sizeof( geomv_t ) * 13 ) +#define Ship1Obj_Laser4X3 ( offsetof( ShipObject, Laser1_X ) + sizeof( geomv_t ) * 14 ) +#define Ship1Obj_Laser4X4 ( offsetof( ShipObject, Laser1_X ) + sizeof( geomv_t ) * 15 ) +#define Ship1Obj_Laser1Y1 offsetof( ShipObject, Laser1_Y ) +#define Ship1Obj_Laser1Y2 ( offsetof( ShipObject, Laser1_Y ) + sizeof( geomv_t ) * 1 ) +#define Ship1Obj_Laser1Y3 ( offsetof( ShipObject, Laser1_Y ) + sizeof( geomv_t ) * 2 ) +#define Ship1Obj_Laser1Y4 ( offsetof( ShipObject, Laser1_Y ) + sizeof( geomv_t ) * 3 ) +#define Ship1Obj_Laser2Y1 ( offsetof( ShipObject, Laser1_Y ) + sizeof( geomv_t ) * 4 ) +#define Ship1Obj_Laser2Y2 ( offsetof( ShipObject, Laser1_Y ) + sizeof( geomv_t ) * 5 ) +#define Ship1Obj_Laser2Y3 ( offsetof( ShipObject, Laser1_Y ) + sizeof( geomv_t ) * 6 ) +#define Ship1Obj_Laser2Y4 ( offsetof( ShipObject, Laser1_Y ) + sizeof( geomv_t ) * 7 ) +#define Ship1Obj_Laser3Y1 ( offsetof( ShipObject, Laser1_Y ) + sizeof( geomv_t ) * 8 ) +#define Ship1Obj_Laser3Y2 ( offsetof( ShipObject, Laser1_Y ) + sizeof( geomv_t ) * 9 ) +#define Ship1Obj_Laser3Y3 ( offsetof( ShipObject, Laser1_Y ) + sizeof( geomv_t ) * 10 ) +#define Ship1Obj_Laser3Y4 ( offsetof( ShipObject, Laser1_Y ) + sizeof( geomv_t ) * 11 ) +#define Ship1Obj_Laser4Y1 ( offsetof( ShipObject, Laser1_Y ) + sizeof( geomv_t ) * 12 ) +#define Ship1Obj_Laser4Y2 ( offsetof( ShipObject, Laser1_Y ) + sizeof( geomv_t ) * 13 ) +#define Ship1Obj_Laser4Y3 ( offsetof( ShipObject, Laser1_Y ) + sizeof( geomv_t ) * 14 ) +#define Ship1Obj_Laser4Y4 ( offsetof( ShipObject, Laser1_Y ) + sizeof( geomv_t ) * 15 ) +#define Ship1Obj_Laser1Z1 offsetof( ShipObject, Laser1_Z ) +#define Ship1Obj_Laser1Z2 ( offsetof( ShipObject, Laser1_Z ) + sizeof( geomv_t ) * 1 ) +#define Ship1Obj_Laser1Z3 ( offsetof( ShipObject, Laser1_Z ) + sizeof( geomv_t ) * 2 ) +#define Ship1Obj_Laser1Z4 ( offsetof( ShipObject, Laser1_Z ) + sizeof( geomv_t ) * 3 ) +#define Ship1Obj_Laser2Z1 ( offsetof( ShipObject, Laser1_Z ) + sizeof( geomv_t ) * 4 ) +#define Ship1Obj_Laser2Z2 ( offsetof( ShipObject, Laser1_Z ) + sizeof( geomv_t ) * 5 ) +#define Ship1Obj_Laser2Z3 ( offsetof( ShipObject, Laser1_Z ) + sizeof( geomv_t ) * 6 ) +#define Ship1Obj_Laser2Z4 ( offsetof( ShipObject, Laser1_Z ) + sizeof( geomv_t ) * 7 ) +#define Ship1Obj_Laser3Z1 ( offsetof( ShipObject, Laser1_Z ) + sizeof( geomv_t ) * 8 ) +#define Ship1Obj_Laser3Z2 ( offsetof( ShipObject, Laser1_Z ) + sizeof( geomv_t ) * 9 ) +#define Ship1Obj_Laser3Z3 ( offsetof( ShipObject, Laser1_Z ) + sizeof( geomv_t ) * 10 ) +#define Ship1Obj_Laser3Z4 ( offsetof( ShipObject, Laser1_Z ) + sizeof( geomv_t ) * 11 ) +#define Ship1Obj_Laser4Z1 ( offsetof( ShipObject, Laser1_Z ) + sizeof( geomv_t ) * 12 ) +#define Ship1Obj_Laser4Z2 ( offsetof( ShipObject, Laser1_Z ) + sizeof( geomv_t ) * 13 ) +#define Ship1Obj_Laser4Z3 ( offsetof( ShipObject, Laser1_Z ) + sizeof( geomv_t ) * 14 ) +#define Ship1Obj_Laser4Z4 ( offsetof( ShipObject, Laser1_Z ) + sizeof( geomv_t ) * 15 ) +#define Ship1Obj_Missile1X1 offsetof( ShipObject, Missile1_X ) +#define Ship1Obj_Missile1X2 ( offsetof( ShipObject, Missile1_X ) + sizeof( geomv_t ) * 1 ) +#define Ship1Obj_Missile1X3 ( offsetof( ShipObject, Missile1_X ) + sizeof( geomv_t ) * 2 ) +#define Ship1Obj_Missile1X4 ( offsetof( ShipObject, Missile1_X ) + sizeof( geomv_t ) * 3 ) +#define Ship1Obj_Missile1Y1 offsetof( ShipObject, Missile1_Y ) +#define Ship1Obj_Missile1Y2 ( offsetof( ShipObject, Missile1_Y ) + sizeof( geomv_t ) * 1 ) +#define Ship1Obj_Missile1Y3 ( offsetof( ShipObject, Missile1_Y ) + sizeof( geomv_t ) * 2 ) +#define Ship1Obj_Missile1Y4 ( offsetof( ShipObject, Missile1_Y ) + sizeof( geomv_t ) * 3 ) +#define Ship1Obj_Missile1Z1 offsetof( ShipObject, Missile1_Z ) +#define Ship1Obj_Missile1Z2 ( offsetof( ShipObject, Missile1_Z ) + sizeof( geomv_t ) * 1 ) +#define Ship1Obj_Missile1Z3 ( offsetof( ShipObject, Missile1_Z ) + sizeof( geomv_t ) * 2 ) +#define Ship1Obj_Missile1Z4 ( offsetof( ShipObject, Missile1_Z ) + sizeof( geomv_t ) * 3 ) +#define Ship1Obj_Missile2X1 offsetof( ShipObject, Missile2_X ) +#define Ship1Obj_Missile2X2 ( offsetof( ShipObject, Missile2_X ) + sizeof( geomv_t ) * 1 ) +#define Ship1Obj_Missile2X3 ( offsetof( ShipObject, Missile2_X ) + sizeof( geomv_t ) * 2 ) +#define Ship1Obj_Missile2X4 ( offsetof( ShipObject, Missile2_X ) + sizeof( geomv_t ) * 3 ) +#define Ship1Obj_Missile2Y1 offsetof( ShipObject, Missile2_Y ) +#define Ship1Obj_Missile2Y2 ( offsetof( ShipObject, Missile2_Y ) + sizeof( geomv_t ) * 1 ) +#define Ship1Obj_Missile2Y3 ( offsetof( ShipObject, Missile2_Y ) + sizeof( geomv_t ) * 2 ) +#define Ship1Obj_Missile2Y4 ( offsetof( ShipObject, Missile2_Y ) + sizeof( geomv_t ) * 3 ) +#define Ship1Obj_Missile2Z1 offsetof( ShipObject, Missile2_Z ) +#define Ship1Obj_Missile2Z2 ( offsetof( ShipObject, Missile2_Z ) + sizeof( geomv_t ) * 1 ) +#define Ship1Obj_Missile2Z3 ( offsetof( ShipObject, Missile2_Z ) + sizeof( geomv_t ) * 2 ) +#define Ship1Obj_Missile2Z4 ( offsetof( ShipObject, Missile2_Z ) + sizeof( geomv_t ) * 3 ) +#define Ship1Obj_Mine1X offsetof( ShipObject, Mine1_X ) +#define Ship1Obj_Mine1Y offsetof( ShipObject, Mine1_Y ) +#define Ship1Obj_Mine1Z offsetof( ShipObject, Mine1_Z ) +#define Ship1Obj_BeamX1 offsetof( ShipObject, Beam_X ) +#define Ship1Obj_BeamX2 ( offsetof( ShipObject, Beam_X ) + sizeof( geomv_t ) * 1 ) +#define Ship1Obj_BeamX3 ( offsetof( ShipObject, Beam_X ) + sizeof( geomv_t ) * 2 ) +#define Ship1Obj_BeamX4 ( offsetof( ShipObject, Beam_X ) + sizeof( geomv_t ) * 3 ) +#define Ship1Obj_BeamY offsetof( ShipObject, Beam_Y ) +#define Ship1Obj_BeamZ offsetof( ShipObject, Beam_Z ) +#define Ship1Obj_SpreadX1 offsetof( ShipObject, Spread_X ) +#define Ship1Obj_SpreadX2 ( offsetof( ShipObject, Spread_X ) + sizeof( geomv_t ) * 1 ) +#define Ship1Obj_SpreadX3 ( offsetof( ShipObject, Spread_X ) + sizeof( geomv_t ) * 2 ) +#define Ship1Obj_SpreadX4 ( offsetof( ShipObject, Spread_X ) + sizeof( geomv_t ) * 3 ) +#define Ship1Obj_SpreadY offsetof( ShipObject, Spread_Y ) +#define Ship1Obj_SpreadZ offsetof( ShipObject, Spread_Z ) +#define Ship1Obj_SpreadSpeed offsetof( ShipObject, SpreadSpeed ) +#define Ship1Obj_SpreadLifeTime offsetof( ShipObject, SpreadLifeTime ) +#define Ship1Obj_HelixX offsetof( ShipObject, Helix_X ) +#define Ship1Obj_HelixY offsetof( ShipObject, Helix_Y ) +#define Ship1Obj_HelixZ offsetof( ShipObject, Helix_Z ) +#define Ship1Obj_HelixSpeed offsetof( ShipObject, HelixSpeed ) +#define Ship1Obj_HelixLifeTime offsetof( ShipObject, HelixLifeTime ) +#define Ship1Obj_PhotonSpeed offsetof( ShipObject, PhotonSpeed ) +#define Ship1Obj_PhotonLifeTime offsetof( ShipObject, PhotonLifeTime ) + + +#define Laser1_LifeTimeCount offsetof( LaserObject, LifeTimeCount ) +#define Laser1_Speed offsetof( LaserObject, Speed ) +#define Laser1_HitPoints offsetof( LaserObject, HitPoints ) +#define Laser1_EnergyNeeded offsetof( LaserObject, EnergyNeeded ) + +#define Missile1_LifeTimeCount offsetof( MissileObject, LifeTimeCount ) +#define Missile1_Speed offsetof( MissileObject, Speed ) +#define Missile1_HitPoints offsetof( MissileObject, HitPoints ) +#define Missile4_Latency offsetof( TargetMissileObject, Latency ) +#define Missile4_MaxRotation offsetof( TargetMissileObject, MaxRotation ) + +#define Extra1_LifeTimeCount offsetof( ExtraObject, LifeTimeCount ) +#define Extra1_SelfRotX offsetof( ExtraObject, SelfRotX ) +#define Extra1_SelfRotY offsetof( ExtraObject, SelfRotY ) +#define Extra1_SelfRotZ offsetof( ExtraObject, SelfRotZ ) + +#define Mine1_HitPoints offsetof( MineObject, HitPoints ) + + +//NOTE: +// bmin and bmax (value bounds) for all types other than +// PROPTYPE_INT have to be specified in fixed-point! + +//NOTE: +// not all fields of struct proplist_s will be set explicitly +// in the following arrays. the remaining fields will be NULL. +// THEIR OFF-STATES HAVE TO BE NULL/ZERO. + + +PRIVATE +proplist_s Ship1_PropList[] = { + + { "damage", Ship1Obj_CurDamage, 1, 500, PROPTYPE_INT }, + { "maxdamage", Ship1Obj_MaxDamage, 1, 500, PROPTYPE_INT }, + { "energy", Ship1Obj_Energy, 1, 10000, PROPTYPE_INT }, + { "maxenergy", Ship1Obj_MaxEnergy, 1, 10000, PROPTYPE_INT }, + { "speed", Ship1Obj_CurSpeed, 1, 100000, PROPTYPE_INT }, + { "maxspeed", Ship1Obj_MaxSpeed, 1, 100000, PROPTYPE_INT }, + { "weapons", Ship1Obj_Weapons, 0, 0x70007, PROPTYPE_INT }, + { "specials", Ship1Obj_Specials, 0, 0xfffff, PROPTYPE_INT }, + { "dumb", Ship1Obj_NumMissls, 0, 99, PROPTYPE_INT }, + { "maxdumb", Ship1Obj_MaxNumMissls, 0, 99, PROPTYPE_INT }, + { "guide", Ship1Obj_NumHomMissls, 0, 99, PROPTYPE_INT }, + { "maxguide", Ship1Obj_MaxNumHomMissls, 0, 99, PROPTYPE_INT }, + { "swarm", Ship1Obj_NumPartMissls, 0, 99, PROPTYPE_INT }, + { "maxswarm", Ship1Obj_MaxNumPartMissls, 0, 99, PROPTYPE_INT }, + { "mines", Ship1Obj_NumMines, 0, 99, PROPTYPE_INT }, + { "maxmines", Ship1Obj_MaxNumMines, 0, 99, PROPTYPE_INT }, + { "yawperref", Ship1Obj_YawPerRefFrame, 0, 100, PROPTYPE_INT }, + { "pitchperref", Ship1Obj_PitchPerRefFrame, 0, 100, PROPTYPE_INT }, + { "rollperref", Ship1Obj_RollPerRefFrame, 0, 100, PROPTYPE_INT }, + { "xslideperref", Ship1Obj_XSlidePerRefFrame, 0, 0x7fffffff, PROPTYPE_GEOMV }, + { "yslideperref", Ship1Obj_YSlidePerRefFrame, 0, 0x7fffffff, PROPTYPE_GEOMV }, + { "speedincperref", Ship1Obj_SpeedIncPerRefFrame, 0, 0xffff, PROPTYPE_INT }, + { "speeddecperref", Ship1Obj_SpeedDecPerRefFrame, 0, 0xffff, PROPTYPE_INT }, + { "firerepdelay", Ship1Obj_FireRepeatDelay, 0, 500, PROPTYPE_INT }, + { "firedisdelay", Ship1Obj_FireDisableDelay, 0, 500, PROPTYPE_INT }, + { "missdisdelay", Ship1Obj_MissileDisableDelay, 0, 500, PROPTYPE_INT }, + { "laser1.x1", Ship1Obj_Laser1X1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser1.x2", Ship1Obj_Laser1X2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser1.x3", Ship1Obj_Laser1X3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser1.x4", Ship1Obj_Laser1X4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser1.y1", Ship1Obj_Laser1Y1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser1.y2", Ship1Obj_Laser1Y2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser1.y3", Ship1Obj_Laser1Y3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser1.y4", Ship1Obj_Laser1Y4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser1.z1", Ship1Obj_Laser1Z1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser1.z2", Ship1Obj_Laser1Z2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser1.z3", Ship1Obj_Laser1Z3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser1.z4", Ship1Obj_Laser1Z4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser2.x1", Ship1Obj_Laser2X1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser2.x2", Ship1Obj_Laser2X2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser2.x3", Ship1Obj_Laser2X3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser2.x4", Ship1Obj_Laser2X4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser2.y1", Ship1Obj_Laser2Y1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser2.y2", Ship1Obj_Laser2Y2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser2.y3", Ship1Obj_Laser2Y3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser2.y4", Ship1Obj_Laser2Y4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser2.z1", Ship1Obj_Laser2Z1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser2.z2", Ship1Obj_Laser2Z2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser2.z3", Ship1Obj_Laser2Z3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser2.z4", Ship1Obj_Laser2Z4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.x1", Ship1Obj_Laser3X1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.x2", Ship1Obj_Laser3X2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.x3", Ship1Obj_Laser3X3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.x4", Ship1Obj_Laser3X4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.y1", Ship1Obj_Laser3Y1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.y2", Ship1Obj_Laser3Y2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.y3", Ship1Obj_Laser3Y3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.y4", Ship1Obj_Laser3Y4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.z1", Ship1Obj_Laser3Z1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.z2", Ship1Obj_Laser3Z2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.z3", Ship1Obj_Laser3Z3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.z4", Ship1Obj_Laser3Z4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.x1", Ship1Obj_Laser3X1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.x2", Ship1Obj_Laser3X2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.x3", Ship1Obj_Laser3X3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.x4", Ship1Obj_Laser3X4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.y1", Ship1Obj_Laser3Y1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.y2", Ship1Obj_Laser3Y2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.y3", Ship1Obj_Laser3Y3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.y4", Ship1Obj_Laser3Y4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.z1", Ship1Obj_Laser3Z1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.z2", Ship1Obj_Laser3Z2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.z3", Ship1Obj_Laser3Z3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser3.z4", Ship1Obj_Laser3Z4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser4.x1", Ship1Obj_Laser4X1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser4.x2", Ship1Obj_Laser4X2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser4.x3", Ship1Obj_Laser4X3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser4.x4", Ship1Obj_Laser4X4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser4.y1", Ship1Obj_Laser4Y1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser4.y2", Ship1Obj_Laser4Y2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser4.y3", Ship1Obj_Laser4Y3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser4.y4", Ship1Obj_Laser4Y4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser4.z1", Ship1Obj_Laser4Z1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser4.z2", Ship1Obj_Laser4Z2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser4.z3", Ship1Obj_Laser4Z3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "laser4.z4", Ship1Obj_Laser4Z4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile1.x1", Ship1Obj_Missile1X1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile1.x2", Ship1Obj_Missile1X2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile1.x3", Ship1Obj_Missile1X3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile1.x4", Ship1Obj_Missile1X4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile1.y1", Ship1Obj_Missile1Y1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile1.y2", Ship1Obj_Missile1Y2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile1.y3", Ship1Obj_Missile1Y3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile1.y4", Ship1Obj_Missile1Y4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile1.z1", Ship1Obj_Missile1Z1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile1.z2", Ship1Obj_Missile1Z2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile1.z3", Ship1Obj_Missile1Z3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile1.z4", Ship1Obj_Missile1Z4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile2.x1", Ship1Obj_Missile2X1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile2.x2", Ship1Obj_Missile2X2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile2.x3", Ship1Obj_Missile2X3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile2.x4", Ship1Obj_Missile2X4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile2.y1", Ship1Obj_Missile2Y1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile2.y2", Ship1Obj_Missile2Y2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile2.y3", Ship1Obj_Missile2Y3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile2.y4", Ship1Obj_Missile2Y4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile2.z1", Ship1Obj_Missile2Z1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile2.z2", Ship1Obj_Missile2Z2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile2.z3", Ship1Obj_Missile2Z3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "missile2.z4", Ship1Obj_Missile2Z4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "mine1.x", Ship1Obj_Mine1X, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "mine1.y", Ship1Obj_Mine1Y, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "mine1.z", Ship1Obj_Mine1Z, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "beam.x1", Ship1Obj_BeamX1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "beam.x2", Ship1Obj_BeamX2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "beam.x3", Ship1Obj_BeamX3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "beam.x4", Ship1Obj_BeamX4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "beam.y", Ship1Obj_BeamY, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "beam.z", Ship1Obj_BeamZ, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "spread.x1", Ship1Obj_SpreadX1, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "spread.x2", Ship1Obj_SpreadX2, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "spread.x3", Ship1Obj_SpreadX3, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "spread.x4", Ship1Obj_SpreadX4, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "spread.y", Ship1Obj_SpreadY, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "spread.z", Ship1Obj_SpreadZ, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "spreadspeed", Ship1Obj_SpreadSpeed, 0, 0x1000000, PROPTYPE_INT }, + { "spreadlifetime", Ship1Obj_SpreadLifeTime, 0, 0x1000000, PROPTYPE_INT }, + { "helix.x", Ship1Obj_HelixX, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "helix.y", Ship1Obj_HelixY, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "helix.z", Ship1Obj_HelixZ, -0x7fffffff, 0x7fffffff, PROPTYPE_GEOMV }, + { "helixspeed", Ship1Obj_HelixSpeed, 0, 0x1000000, PROPTYPE_INT }, + { "helixlifetime", Ship1Obj_HelixLifeTime, 0, 0x1000000, PROPTYPE_INT }, + { "photonspeed", Ship1Obj_PhotonSpeed, 0, 0x1000000, PROPTYPE_INT }, + { "photonlifetime", Ship1Obj_PhotonLifeTime, 0, 0x1000000, PROPTYPE_INT }, + + { NULL, 0, 0, 0, 0 } +}; + +PRIVATE +proplist_s Laser1_PropList[] = { + + { "lifetime", Laser1_LifeTimeCount, 100, 100000, 0 }, + { "speed", Laser1_Speed, 0, 0xa0000, 0 }, + { "hitpoints", Laser1_HitPoints, 0, 100, 0 }, + { "energy", Laser1_EnergyNeeded, 0, 100, 0 }, + + { NULL, 0, 0, 0, 0 } +}; + +PRIVATE +proplist_s Laser2_PropList[] = { + + { "lifetime", Laser1_LifeTimeCount, 100, 100000, 0 }, + { "speed", Laser1_Speed, 0, 0xa0000, 0 }, + { "hitpoints", Laser1_HitPoints, 0, 200, 0 }, + { "energy", Laser1_EnergyNeeded, 0, 100, 0 }, + + { NULL, 0, 0, 0, 0 } +}; + +PRIVATE +proplist_s Laser3_PropList[] = { + + { "lifetime", Laser1_LifeTimeCount, 100, 100000, 0 }, + { "speed", Laser1_Speed, 0, 0xa0000, 0 }, + { "hitpoints", Laser1_HitPoints, 0, 1000, 0 }, + { "energy", Laser1_EnergyNeeded, 0, 100, 0 }, + + { NULL, 0, 0, 0, 0 } +}; + +PRIVATE +proplist_s Missile1_PropList[] = { + + { "lifetime", Missile1_LifeTimeCount, 100, 100000, 0 }, + { "speed", Missile1_Speed, 0, 0xa0000, 0 }, + { "hitpoints", Missile1_HitPoints, 0, 100, 0 }, + + { NULL, 0, 0, 0, 0 } +}; + +PRIVATE +proplist_s Missile2_PropList[] = { + + { "lifetime", Missile1_LifeTimeCount, 100, 100000, 0 }, + { "speed", Missile1_Speed, 0, 0xa0000, 0 }, + { "hitpoints", Missile1_HitPoints, 0, 100, 0 }, + + { NULL, 0, 0, 0, 0 } +}; + +PRIVATE +proplist_s Missile3_PropList[] = { + + { "lifetime", Missile1_LifeTimeCount, 100, 100000, 0 }, + { "speed", Missile1_Speed, 0, 0xa0000, 0 }, + { "hitpoints", Missile1_HitPoints, 0, 100, 0 }, + + { NULL, 0, 0, 0, 0 } +}; + +PRIVATE +proplist_s Missile4_PropList[] = { + + { "lifetime", Missile1_LifeTimeCount, 100, 100000, 0 }, + { "speed", Missile1_Speed, 0, 0xa0000, 0 }, + { "hitpoints", Missile1_HitPoints, 0, 100, 0 }, + { "latency", Missile4_Latency, 0, 200, 0 }, + { "maxrot", Missile4_MaxRotation, 0, 0x10000, 0 }, + + { NULL, 0, 0, 0, 0 } +}; + +PRIVATE +proplist_s Missile5_PropList[] = { + + { "lifetime", Missile1_LifeTimeCount, 100, 100000, 0 }, + { "speed", Missile1_Speed, 0, 0xa0000, 0 }, + { "hitpoints", Missile1_HitPoints, 0, 100, 0 }, + { "latency", Missile4_Latency, 0, 200, 0 }, + { "maxrot", Missile4_MaxRotation, 0, 0x10000, 0 }, + + { NULL, 0, 0, 0, 0 } +}; + +PRIVATE +proplist_s Extra1_PropList[] = { + + { "lifetime", Extra1_LifeTimeCount, 0, 100000, 0 }, + { "rotx", Extra1_SelfRotX, 0, 0x200, 0 }, + { "roty", Extra1_SelfRotY, 0, 0x200, 0 }, + { "rotz", Extra1_SelfRotZ, 0, 0x200, 0 }, + + { NULL, 0, 0, 0, 0 } +}; + +PRIVATE +proplist_s Extra2_PropList[] = { + + { "lifetime", Extra1_LifeTimeCount, 0, 100000, 0 }, + { "rotx", Extra1_SelfRotX, 0, 0x200, 0 }, + { "roty", Extra1_SelfRotY, 0, 0x200, 0 }, + { "rotz", Extra1_SelfRotZ, 0, 0x200, 0 }, + + { NULL, 0, 0, 0, 0 } +}; + +PRIVATE +proplist_s Extra3_PropList[] = { + + { "lifetime", Extra1_LifeTimeCount, 0, 100000, 0 }, + { "rotx", Extra1_SelfRotX, 0, 0x200, 0 }, + { "roty", Extra1_SelfRotY, 0, 0x200, 0 }, + { "rotz", Extra1_SelfRotZ, 0, 0x200, 0 }, + + { NULL, 0, 0, 0, 0 } +}; + +PRIVATE +proplist_s Mine1_PropList[] = { + + { "lifetime", Extra1_LifeTimeCount, 0, 100000, 0 }, + { "rotx", Extra1_SelfRotX, 0, 0x200, 0 }, + { "roty", Extra1_SelfRotY, 0, 0x200, 0 }, + { "rotz", Extra1_SelfRotZ, 0, 0x200, 0 }, + { "hitpoints", Mine1_HitPoints, 0, 1000, 0 }, + + { NULL, 0, 0, 0, 0 } +}; + + +// convert default types to proplist_s pointers ------------------------------- +// +PRIVATE +proplist_s *default_types_proplists[ NUM_DISTINCT_OBJTYPES ] = { + + Ship1_PropList, // SHIP_1 + Ship1_PropList, // SHIP_2 + Laser1_PropList, // LASER_1 + Laser2_PropList, // LASER_2 + Laser3_PropList, // LASER_3 + Missile1_PropList, // MISSL_1 + Missile2_PropList, // MISSL_2 + Missile3_PropList, // MISSL_3 + Missile4_PropList, // MISSL_4 + Missile5_PropList, // MISSL_5 + Extra1_PropList, // EXTRA_1 + Extra2_PropList, // EXTRA_2 + Extra3_PropList, // EXTRA_3 + Mine1_PropList, // MINE_1 +}; + + +// registered proplists for custom types -------------------------------------- +// +PRIVATE +proplist_s *custom_types_proplists[ MAX_NUM_CUSTOM_TYPES ]; + + +// register proplist for custom type ------------------------------------------ +// +void CON_RegisterCustomType( dword objtypeid, proplist_s *proplist ) +{ + ASSERT( proplist != NULL ); + + //NOTE: + // a type id of TYPE_ID_INVALID will be ignored silently, + // in order to make the id returned by OBJ_RegisterCustomType() + // directly useable without explicit error checking. + + if ( objtypeid == TYPE_ID_INVALID ) + return; + + int typeindx = objtypeid & TYPENUMBERMASK; + + ASSERT( typeindx >= NUM_DISTINCT_OBJTYPES ); + if ( typeindx < NUM_DISTINCT_OBJTYPES ) + return; + + typeindx -= NUM_DISTINCT_OBJTYPES; + + ASSERT( typeindx < MAX_NUM_CUSTOM_TYPES ); + if ( typeindx >= MAX_NUM_CUSTOM_TYPES ) + return; + + // register each type exactly once + ASSERT( custom_types_proplists[ typeindx ] == NULL ); + if ( custom_types_proplists[ typeindx ] != NULL ) + return; + + custom_types_proplists[ typeindx ] = proplist; +} + + +// fetch pointer to property list for specified type id ----------------------- +// +proplist_s *FetchTypePropertyList( dword objtypeid ) +{ + ASSERT( objtypeid != TYPE_ID_INVALID ); + + //NOTE: + // may return NULL if no proplist is available + // for the requested type (especially if custom). + + int typeindx = objtypeid & TYPENUMBERMASK; + + // default type + if ( typeindx < NUM_DISTINCT_OBJTYPES ) { + return default_types_proplists[ typeindx ]; + } + + // custom type + typeindx -= NUM_DISTINCT_OBJTYPES; + ASSERT( typeindx < MAX_NUM_CUSTOM_TYPES ); + if ( typeindx >= MAX_NUM_CUSTOM_TYPES ) + return NULL; + + // return either NULL or registered proplist + return custom_types_proplists[ typeindx ]; +} + + +// list of alias names for default object types ------------------------------- +// +struct alias_typename_s { + + const char* objtypename; + dword objtypeid; +}; + +PRIVATE +alias_typename_s AliasTypeNames[ NUM_DISTINCT_OBJTYPES ] = { + + { "ship_1", SHIP1TYPE }, + { "ship_2", SHIP2TYPE }, + { "laser_1", LASER1TYPE }, + { "laser_2", LASER2TYPE }, + { "laser_3", LASER3TYPE }, + { "missl_1", MISSILE1TYPE }, + { "missl_2", MISSILE2TYPE }, + { "missl_3", MISSILE3TYPE }, + { "missl_4", MISSILE4TYPE }, + { "missl_5", MISSILE5TYPE }, + { "extra_1", EXTRA1TYPE }, + { "extra_2", EXTRA2TYPE }, + { "extra_3", EXTRA3TYPE }, + { "mine_1", MINE1TYPE }, +}; + + +// fetch name for specified type id ------------------------------------------- +// +PRIVATE +const char *FetchTypeName( dword objtypeid ) +{ + // check default type ids + for ( int tid = 0; tid < NUM_DISTINCT_OBJTYPES; tid++ ) + if ( AliasTypeNames[ tid ].objtypeid == objtypeid ) + return AliasTypeNames[ tid ].objtypename; + + // check custom type ids + dword typeindex = ( objtypeid & TYPENUMBERMASK ) - NUM_DISTINCT_OBJTYPES; + ASSERT( typeindex < (dword)num_custom_types ); + + const char *objtypename = custom_type_info[ typeindex ].type_name; + ASSERT( objtypename != NULL ); + + return objtypename; +} + + +// build type info line for type list ----------------------------------------- +// +char *BuildTypeInfo( int typeindex ) +{ + ASSERT( typeindex >= 0 ); + ASSERT( typeindex < NUM_DISTINCT_OBJTYPES + num_custom_types ); + + if ( typeindex < NUM_DISTINCT_OBJTYPES ) { + sprintf( paste_str, "type %d: %s (id %08x)", typeindex, + AliasTypeNames[ typeindex ].objtypename, + (unsigned int)AliasTypeNames[ typeindex ].objtypeid ); + } else { + int customindex = typeindex - NUM_DISTINCT_OBJTYPES; + sprintf( paste_str, "type %d: %s (id %08x)", typeindex, + custom_type_info[ customindex ].type_name, + (unsigned int)custom_type_info[ customindex ].type_id ); + } + + return paste_str; +} + + +// list of alias names for the default object classes ------------------------- +// +struct alias_classname_s { + + const char* objclassname; + dword objclassid; +}; + +PRIVATE +alias_classname_s AliasClassNames[ NUM_ALTERABLE_CLASSES ] = { + + { "ship1", SHIP_CLASS_1 }, + { "ship2", SHIP_CLASS_2 }, + { "ship3", SHIP_CLASS_3 }, + + { "laser1", LASER0_CLASS_1 }, + { "laser2", LASER0_CLASS_2 }, + { "laser3", LASER1_CLASS_1 }, + { "laser4", LASER2_CLASS_1 }, + + { "missl1", DUMB_CLASS_1 }, + { "missl2", GUIDE_CLASS_1 }, + { "missl3", SWARM_CLASS_1 }, + { "mine1", MINE_CLASS_1 }, + + { "extra1", ENERGY_EXTRA_CLASS }, + { "extra2", DUMB_PACK_CLASS }, + { "extra3", GUIDE_PACK_CLASS }, + { "extra4", HELIX_DEVICE_CLASS }, + { "extra5", LIGHTNING_DEVICE_CLASS }, + { "extra6", MINE_PACK_CLASS }, + { "extra7", REPAIR_EXTRA_CLASS }, + { "extra8", AFTERBURNER_DEVICE_CLASS }, + { "extra9", SWARM_PACK_CLASS }, + { "extra10", INVISIBILITY_CLASS }, + { "extra11", PHOTON_DEVICE_CLASS }, + { "extra12", DECOY_DEVICE_CLASS }, + { "extra13", LASERUPGRADE1_CLASS }, + { "extra14", LASERUPGRADE2_CLASS }, + { "extra15", INVULNERABILITY_CLASS }, +}; + + +// build class info line for class list --------------------------------------- +// +char *BuildClassInfo( int classindex ) +{ + ASSERT( classindex >= 0 ); + ASSERT( classindex < NumObjClasses ); + ASSERT( NumObjClasses >= NUM_ALTERABLE_CLASSES ); + ASSERT( NumObjClasses == NumLoadedObjects ); + + dword objclassid = classindex; + if ( classindex < NUM_ALTERABLE_CLASSES ) { + objclassid = AliasClassNames[ classindex ].objclassid; + } + + ASSERT( objclassid < (dword)NumLoadedObjects ); + dword objtypeid = ObjectInfo[ objclassid ].type; + const char *objtypename = FetchTypeName( objtypeid ); + + #define MAX_CLASSNAME 31 + char classname[ MAX_CLASSNAME + 1 ]; + strncpy( classname, ObjectInfo[ objclassid ].name, MAX_CLASSNAME ); + classname[ MAX_CLASSNAME ] = 0; + + if ( classindex < NUM_ALTERABLE_CLASSES ) { + sprintf( paste_str, "class id %u [%s] (%s) type %s", (unsigned int)objclassid, + AliasClassNames[ classindex ].objclassname, classname, objtypename ); + } else { + sprintf( paste_str, "class id %u [%s] (%s) type %s", (unsigned int)objclassid, + "noalias", classname, objtypename ); + } + + return paste_str; +} + + +// object instance to alter when no class and type is specified below --------- +// +static GenObject *alter_object_instance = NULL; + + +// create a pointer to a property field --------------------------------------- +// +INLINE +char *MakeFieldPointer( dword objclassid, proplist_s *plist ) +{ + ASSERT( plist != NULL ); + + //NOTE: + // ( objclassid == CLASS_ID_INVALID ) means an object + // instance should be accessed instead of the class itself. + + char *fieldpo; + + if ( objclassid == CLASS_ID_INVALID ) { + ASSERT( alter_object_instance != NULL ); + fieldpo = plist->propoffset + (char*)alter_object_instance; + } else { + ASSERT( objclassid < (dword)NumObjClasses ); + fieldpo = plist->propoffset + (char*)ObjClasses[ objclassid ]; + } + + return fieldpo; +} + + +// get value and access-type of class/type property --------------------------- +// +PRIVATE +int GetPropertyValue( dword objclassid, proplist_s *plist, int *pint, float *pflt, char **pstr ) +{ + ASSERT( plist != NULL ); + ASSERT( pint != NULL ); + ASSERT( pflt != NULL ); + ASSERT( pstr != NULL ); + + //NOTE: + // ( objclassid == CLASS_ID_INVALID ) means an object + // instance should be queried instead of the class itself. + + char *fieldpo = MakeFieldPointer( objclassid, plist ); + + int accesstype; + switch ( plist->fieldtype ) { + + case PROPTYPE_INT: + *pint = *(int *)fieldpo; + accesstype = ACCESSTYPE_INT; + break; + + case PROPTYPE_FLOAT: + *pflt = *(float *)fieldpo; + accesstype = ACCESSTYPE_FLOAT; + break; + + case PROPTYPE_GEOMV: + *pflt = GEOMV_TO_FLOAT( *(geomv_t *)fieldpo ); + accesstype = ACCESSTYPE_FLOAT; + break; + + case PROPTYPE_FIXED: + *pflt = FIXED_TO_FLOAT( *(fixed_t *)fieldpo ); + accesstype = ACCESSTYPE_FLOAT; + break; + + case PROPTYPE_STRING: + *pstr = fieldpo; + accesstype = ACCESSTYPE_STRING; + break; + + case PROPTYPE_CHARPTR: + *pstr = *(char**)fieldpo; + accesstype = ACCESSTYPE_CHARPTR; + break; + + default: + PANIC( 0 ); + } + + return accesstype; +} + + +// set value of class/type property after converting to correct type ---------- +// +PRIVATE +void SetPropertyValue( dword objclassid, proplist_s *plist, int pint, float pflt, char *pstr ) +{ + ASSERT( plist != NULL ); + + //NOTE: + // ( objclassid == CLASS_ID_INVALID ) means an object + // instance should be altered instead of the class itself. + + char *fieldpo = MakeFieldPointer( objclassid, plist ); + + // indicate whether the field content has actually changed + // (i.e. was not already set to this value) + int fieldchanged = FALSE; + + switch ( plist->fieldtype ) { + + case PROPTYPE_INT: + fieldchanged = ( *(int *)fieldpo != pint ); + *(int *)fieldpo = pint; + break; + + case PROPTYPE_FLOAT: + fieldchanged = ( *(float *)fieldpo != pflt ); + *(float *)fieldpo = pflt; + break; + + case PROPTYPE_GEOMV: + fieldchanged = ( *(geomv_t *)fieldpo != FLOAT_TO_GEOMV( pflt ) ); + *(geomv_t *)fieldpo = FLOAT_TO_GEOMV( pflt ); + break; + + case PROPTYPE_FIXED: + fieldchanged = ( *(fixed_t *)fieldpo != FLOAT_TO_FIXED( pflt ) ); + *(fixed_t *)fieldpo = FLOAT_TO_FIXED( pflt ); + break; + + case PROPTYPE_STRING: + ASSERT( pstr != NULL ); + if ( strncmp( fieldpo, pstr, plist->bmax ) != 0 ) { + strncpy( fieldpo, pstr, plist->bmax ); + fieldpo[ plist->bmax ] = 0; + fieldchanged = TRUE; + } + break; + + case PROPTYPE_CHARPTR: + ASSERT( pstr != NULL ); + ASSERT( *(char**)fieldpo != NULL ); + if ( strncmp( *(char**)fieldpo, pstr, plist->bmax ) != 0 ) { + FREEMEM( *(char**)fieldpo ); + *(char**)fieldpo = (char *) ALLOCMEM( strlen( pstr ) + 1 ); + if ( *(char**)fieldpo == NULL ) + OUTOFMEM( 0 ); + strcpy( *(char**)fieldpo, pstr ); + fieldchanged = TRUE; + } + break; + + default: + PANIC( 0 ); + } + + if ( fieldchanged ) { + + // only call the notification callback for changed fields + // of object class instances (and type templates) + if ( objclassid == CLASS_ID_INVALID ) { + + ASSERT( alter_object_instance != NULL ); + + // call notification callback function + if ( plist->notify_callback != NULL ) { + (*plist->notify_callback)( alter_object_instance ); + } + } + } +} + + +// set new int-property value if supplied, otherwise just display current ----- +// +INLINE +int SetObjPropertyValueInt( dword objclassid, dword objtypeid, proplist_s *plist, int *pint, const char *query ) +{ + ASSERT( plist != NULL ); + ASSERT( pint != NULL ); + ASSERT( query != NULL ); // all properties must be queriable right now! + + // query/alter intermediate field + char *scan = QueryIntArgument( query, pint ); + if ( scan != NULL ) { + + char *errpart; + long sval = strtol( scan, &errpart, int_calc_base ); + + int argvalid = ( *errpart == 0 ); + + // check for increment/decrement tokens + if ( !argvalid ) { + if( strcmp( scan, "++" ) == 0 ) { + sval = (*pint) + 1; + argvalid = TRUE; + } else if ( strcmp( scan, "--" ) == 0 ) { + sval = (*pint) - 1; + argvalid = TRUE; + } else if ( strncmp( scan, "+=", 2 ) == 0 ) { + char* arg = scan + 2; + long argval = strtol( arg, &errpart, int_calc_base ); + if ( *errpart == 0 ) { + sval = (*pint) + argval; + argvalid = TRUE; + } + } else if ( strncmp( scan, "-=", 2 ) == 0 ) { + char* arg = scan + 2; + long argval = strtol( arg, &errpart, int_calc_base ); + if ( *errpart == 0 ) { + sval = (*pint) - argval; + argvalid = TRUE; + } + } + } + + if ( argvalid ) { + if ( ( sval >= plist->bmin ) && + ( sval <= plist->bmax ) ) { + *pint = sval; + } else { + CON_AddLine( range_error ); + return FALSE; + } + } else { + CON_AddLine( invalid_arg ); + return FALSE; + } + + // transfer intermediate field into actual property + SetPropertyValue( objclassid, plist, *pint, 0, NULL ); + + // scan other classes of this type and alter also + if ( ( objtypeid != TYPE_ID_INVALID ) && + ( objclassid != CLASS_ID_INVALID ) ) { + for ( int cid = objclassid + 1; cid < NumObjClasses; cid++ ) { + if ( ObjClasses[ cid ]->ObjectType == objtypeid ) { + SetPropertyValue( cid, plist, *pint, 0, NULL ); + } + } + } + } + + return TRUE; +} + + +// set new float-property value if supplied, otherwise just display current --- +// +INLINE +int SetObjPropertyValueFlt( dword objclassid, dword objtypeid, proplist_s *plist, float *pflt, const char *query ) +{ + ASSERT( plist != NULL ); + ASSERT( pflt != NULL ); + ASSERT( query != NULL ); // all properties must be queriable right now! + + // query/alter intermediate field + char *scan = QueryFltArgument( query, pflt ); + if ( scan != NULL ) { + + char *errpart; + float sval = strtod( scan, &errpart ); + + int argvalid = ( *errpart == 0 ); + + // check for increment/decrement tokens + if ( !argvalid ) { + if( strcmp( scan, "++" ) == 0 ) { + sval = (*pflt) + 1.0f; + argvalid = TRUE; + } else if ( strcmp( scan, "--" ) == 0 ) { + sval = (*pflt) - 1.0f; + argvalid = TRUE; + } else if ( strncmp( scan, "+=", 2 ) == 0 ) { + char* arg = scan + 2; + float argval = strtod( arg, &errpart ); + if ( *errpart == 0 ) { + sval = (*pflt) + argval; + argvalid = TRUE; + } + } else if ( strncmp( scan, "-=", 2 ) == 0 ) { + char* arg = scan + 2; + float argval = strtod( arg, &errpart ); + if ( *errpart == 0 ) { + sval = (*pflt) - argval; + argvalid = TRUE; + } + } + } + + if ( argvalid ) { + if ( ( FLOAT_TO_FIXED( sval ) >= plist->bmin ) && + ( FLOAT_TO_FIXED( sval ) <= plist->bmax ) ) { + *pflt = sval; + } else { + CON_AddLine( range_error ); + return FALSE; + } + } else { + CON_AddLine( invalid_arg ); + return FALSE; + } + + // transfer intermediate field into actual property + SetPropertyValue( objclassid, plist, 0, *pflt, NULL ); + + // scan other classes of this type and alter also + if ( ( objtypeid != TYPE_ID_INVALID ) && + ( objclassid != CLASS_ID_INVALID ) ) { + for ( int cid = objclassid + 1; cid < NumObjClasses; cid++ ) { + if ( ObjClasses[ cid ]->ObjectType == objtypeid ) { + SetPropertyValue( cid, plist, 0, *pflt, NULL ); + } + } + } + } + + return TRUE; +} + + +// set new float-property value if supplied, otherwise just display current --- +// +INLINE +int SetObjPropertyValueStr( dword objclassid, dword objtypeid, proplist_s *plist, char *pstr ) +{ + ASSERT( plist != NULL ); + ASSERT( pstr != NULL ); + + char *scan = strtok( NULL, "" ); + if ( scan != NULL ) { + + // eat whitespace and leading quotations + while ( ( *scan == ' ' ) || ( *scan == '"' ) ) + scan++; + + // argument found? + if ( *scan != 0 ) { + + // cut off trailing whitespace and quotations + char *start = scan; + while ( *scan != 0 ) + scan++; + scan--; + while ( ( *scan == ' ' ) || ( *scan == '"' ) ) + scan--; + *( scan + 1 ) = 0; + + // store string into field + SetPropertyValue( objclassid, plist, 0, 0, start ); + + // scan other classes of this type and alter also + if ( ( objtypeid != TYPE_ID_INVALID ) && + ( objclassid != CLASS_ID_INVALID ) ) { + for ( int cid = objclassid + 1; cid < NumObjClasses; cid++ ) { + if ( ObjClasses[ cid ]->ObjectType == objtypeid ) { + SetPropertyValue( objclassid, plist, 0, 0, start ); + } + } + } + + return TRUE; + } + } + + // display current value + CON_AddLine( pstr ); + + return TRUE; +} + + +// set new property value if supplied, otherwise just display current --------- +// +PRIVATE +void SetObjPropertyValue( dword objclassid, dword objtypeid, proplist_s *plist, const char *query ) +{ + ASSERT( plist != NULL ); + ASSERT( query != NULL ); // all properties must be queriable right now! + + int pint; + float pflt; + char* pstr; + int acct = GetPropertyValue( objclassid, plist, &pint, &pflt, &pstr ); + + //NOTE: + // ( objclassid == CLASS_ID_INVALID ) && ( objtypeid == TYPE_ID_INVALID ) + // means an object instance should be altered instead of the class itself. + // + // ( objtypeid == TYPE_ID_INVALID ) means a class is being altered, resulting + // in a change of only a single class's property field. + // + // ( objtypeid != TYPE_ID_INVALID ) means a type is being altered, resulting + // in a change of property fields of all classes belonging to this type. + + switch ( acct ) { + + case ACCESSTYPE_INT: + SetObjPropertyValueInt( objclassid, objtypeid, plist, &pint, query ); + break; + + case ACCESSTYPE_FLOAT: + SetObjPropertyValueFlt( objclassid, objtypeid, plist, &pflt, "%f" ); + break; + + case ACCESSTYPE_STRING: + case ACCESSTYPE_CHARPTR: + SetObjPropertyValueStr( objclassid, objtypeid, plist, pstr ); + break; + } +} + + +// resolve type name to type id with aliases and custom support --------------- +// +PRIVATE +dword AliasResolveTypeName( const char *name ) +{ + ASSERT( name != NULL ); + dword tid; + // check type name aliases + for ( tid = 0; tid < NUM_DISTINCT_OBJTYPES; tid++ ) + if ( strcmp( name, AliasTypeNames[ tid ].objtypename ) == 0 ) + break; + if ( tid != NUM_DISTINCT_OBJTYPES ) { + ASSERT( ( AliasTypeNames[ tid ].objtypeid & TYPENUMBERMASK ) == tid ); + return AliasTypeNames[ tid ].objtypeid; + } + + // check custom type names + for ( tid = 0; tid < (dword)num_custom_types; tid++ ) + if ( stricmp( name, custom_type_info[ tid ].type_name ) == 0 ) + break; + if ( tid != (dword)num_custom_types ) { + return custom_type_info[ tid ].type_id; + } + + return TYPE_ID_INVALID; +} + + +// check argument that alters an object type property ------------------------- +// +int CheckSetObjTypeProperty( const char *query, const char *scan ) +{ + ASSERT( query != NULL ); // all properties must be queriable right now! + ASSERT( scan != NULL ); + + // check if prop command + if ( strcmp( scan, CMSTR( CM_PROP ) ) != 0 ) + return FALSE; + + // isolate type name field + char *objtypename = strtok( NULL, "." ); + if ( objtypename == NULL ) { + CON_AddLine( prop_syntax ); + return TRUE; + } + + // resolve name to id + dword objtypeid = AliasResolveTypeName( objtypename ); + if ( objtypeid == TYPE_ID_INVALID ) { + CON_AddLine( prop_wrong_type ); + return TRUE; + } + + // isolate property name field + char *fieldname = strtok( NULL, " " ); + if ( fieldname == NULL ) { + CON_AddLine( prop_syntax ); + return TRUE; + } + + // check if property name exists for specified type + int propfound = FALSE; + + proplist_s *plist = FetchTypePropertyList( objtypeid ); + if ( plist == NULL ) { + CON_AddLine( not_accessible ); + return TRUE; + } + + for ( ; plist->propname; plist++ ) { + if ( stricmp( plist->propname, fieldname ) == 0 ) { + propfound = TRUE; + break; + } + } + if ( !propfound ) { + CON_AddLine( prop_wrong_field ); + return TRUE; + } + + // default: use instance/template + dword objclassid = CLASS_ID_INVALID; + ASSERT( alter_object_instance == NULL ); + + // try to find type template (for custom types only) + if ( TYPEID_TYPE_CUSTOM( objtypeid ) ) { + + // (ab)use instance pointer for template + alter_object_instance = OBJ_FetchCustomTypeTemplate( objtypeid ); + } + + // find class if no template available + if ( alter_object_instance == NULL ) { + + //NOTE: + // the first class of this type encountered is taken + // at this time. still, the selected property of all + // classes of this type will be altered, since + // SetObjPropertyValue() continues the search in the + // class list. + int cid; + // scan object class list + for ( cid = 0; cid < NumObjClasses; cid++ ) + if ( ObjClasses[ cid ]->ObjectType == objtypeid ) + break; + if ( cid == NumObjClasses ) { + CON_AddLine( no_objs_of_type ); + return TRUE; + } + + objclassid = cid; + } + + // set new value if supplied, otherwise just display current value + SetObjPropertyValue( objclassid, objtypeid, plist, query ); + + // reset instance pointer + alter_object_instance = NULL; + + return TRUE; +} + + +// resolve class name to class id with aliases -------------------------------- +// +PRIVATE +dword AliasResolveClassName( const char *name ) +{ + ASSERT( name != NULL ); + int cid; + // check class name aliases + int propindx = -1; + for ( cid = 0; cid < NUM_ALTERABLE_CLASSES; cid++ ) { + if ( strcmp( name, AliasClassNames[ cid ].objclassname ) == 0 ) { + propindx = cid; + break; + } + } + if ( propindx != -1 ) { + return AliasClassNames[ propindx ].objclassid; + } + + // check original class names + ASSERT( NumObjClasses >= NUM_ALTERABLE_CLASSES ); + for ( cid = 0; cid < NumObjClasses; cid++ ) { + if ( stricmp( name, ObjectInfo[ cid ].name ) == 0 ) { + propindx = cid; + break; + } + } + if ( propindx != -1 ) { + return propindx; + } + + return CLASS_ID_INVALID; +} + + +// check argument that alters an object class property ------------------------ +// +int CheckSetObjClassProperty( const char *query, const char *scan ) +{ + ASSERT( query != NULL ); // all properties must be queriable right now! + ASSERT( scan != NULL ); + + // check if propc command + if ( strcmp( scan, CMSTR( CM_PROPC ) ) != 0 ) + return FALSE; + + // isolate class name field + char *objclassname = strtok( NULL, "." ); + if ( objclassname == NULL ) { + CON_AddLine( propc_syntax ); + return TRUE; + } + + // allow class name to be parenthesized to include whitespace + objclassname = GetParenthesizedName( objclassname ); + if ( objclassname == NULL ) { + CON_AddLine( propc_syntax ); + return TRUE; + } + + // resolve name to id + dword objclassid = AliasResolveClassName( objclassname ); + if ( objclassid == CLASS_ID_INVALID ) { + CON_AddLine( prop_wrong_class ); + return TRUE; + } + + // isolate property name field + char *fieldname = strtok( NULL, " " ); + if ( fieldname == NULL ) { + CON_AddLine( propc_syntax ); + return TRUE; + } + + ASSERT( objclassid < (dword)NumObjClasses ); + dword objtypeid = ObjectInfo[ objclassid ].type; + + // check if property name exists for specified class + int propfound = FALSE; + + proplist_s *plist = FetchTypePropertyList( objtypeid ); + if ( plist == NULL ) { + CON_AddLine( not_accessible ); + return TRUE; + } + + for ( ; plist->propname; plist++ ) { + if ( stricmp( plist->propname, fieldname ) == 0 ) { + propfound = TRUE; + break; + } + } + if ( !propfound ) { + CON_AddLine( prop_wrong_field ); + return TRUE; + } + + // check if class is valid (i.e., actually memory resident) + if ( objclassid >= (dword)NumObjClasses ) { + CON_AddLine( class_not_loaded ); + return TRUE; + } + + // set new value if supplied, otherwise just display current value + SetObjPropertyValue( objclassid, TYPE_ID_INVALID, plist, query ); + + return TRUE; +} + + +// check argument that alters an object instance property --------------------- +// +int CheckSetObjInstanceProperty( const char *query, const char *scan ) +{ + ASSERT( query != NULL ); // all properties must be queriable right now! + ASSERT( scan != NULL ); + + // check if propo command + if ( strcmp( scan, CMSTR( CM_PROPO ) ) != 0 ) + return FALSE; + + // isolate object id field + char *objidstr = strtok( NULL, "." ); + if ( objidstr == NULL ) { + CON_AddLine( propo_syntax ); + return TRUE; + } + + // convert object id string to int + char *errpart; + dword objid = strtol( objidstr, &errpart, 10/*int_calc_base*/ ); + if ( *errpart != 0 ) { + CON_AddLine( prop_objid_syntax ); + return TRUE; + } + + //NOTE: + // id 0 means use id of most recently + // summoned object. + + if ( objid == 0 ) { + objid = TheWorld->GetLastSummonedObjectID(); + if ( !SV_CONSOLE_LEVEL_MESSAGES ) { + sprintf( paste_str, "using object id %u.", (unsigned int)objid ); + CON_AddLine( paste_str ); + } + } + + // isolate property name field + char *fieldname = strtok( NULL, " " ); + if ( fieldname == NULL ) { + CON_AddLine( propo_syntax ); + return TRUE; + } + + // find object + GenObject *obj = FetchObject( objid ); + if ( obj == NULL ) { + CON_AddLine( prop_no_object ); + return TRUE; + } + + // check if property name exists + int propfound = FALSE; + + proplist_s *plist = FetchTypePropertyList( obj->ObjectType ); + if ( plist == NULL ) { + CON_AddLine( not_accessible ); + return TRUE; + } + + for ( ; plist->propname; plist++ ) { + if ( stricmp( plist->propname, fieldname ) == 0 ) { + propfound = TRUE; + break; + } + } + if ( !propfound ) { + CON_AddLine( prop_wrong_field ); + return TRUE; + } + + // set new value if supplied, otherwise just display current value + alter_object_instance = obj; + SetObjPropertyValue( CLASS_ID_INVALID, TYPE_ID_INVALID, plist, query ); + alter_object_instance = NULL; + + return TRUE; +} + + +// build description for type property of numeric type ------------------------ +// +PRIVATE +void DescTypeNum( const char *name, proplist_s *plist, int acct ) +{ + static char format[] = "%s.%s %s (min=%d max=%d)"; + const char typespec = ( acct == ACCESSTYPE_INT ) ? int_print_base[ 1 ] : 'f'; + format[ 15 ] = typespec; + format[ 22 ] = typespec; + + if ( acct == ACCESSTYPE_INT ) { + sprintf( paste_str, format, name, plist->propname, "int", + plist->bmin, plist->bmax ); + } else { + sprintf( paste_str, format, name, plist->propname, "float", + FIXED_TO_FLOAT( plist->bmin ), + FIXED_TO_FLOAT( plist->bmax ) ); + } +} + + +// build description for type property of string type ------------------------- +// +PRIVATE +void DescTypeStr( const char *name, proplist_s *plist ) +{ + sprintf( paste_str, "%s.%s string (min=%d max=%d)", + name, plist->propname, plist->bmin, plist->bmax ); +} + + +// build description for class property of numeric type ----------------------- +// +PRIVATE +void DescClassNum( const char *name, proplist_s *plist, int acct, int pint, float pflt ) +{ + static char format[] = "%s.%s %d (%s min=%d max=%d)"; + char typespec = ( acct == ACCESSTYPE_INT ) ? int_print_base[ 1 ] : 'f'; + format[ 7 ] = typespec; + format[ 18 ] = typespec; + format[ 25 ] = typespec; + + if ( acct == ACCESSTYPE_INT ) { + sprintf( paste_str, format, name, plist->propname, pint, "int", + plist->bmin, plist->bmax ); + } else { + sprintf( paste_str, format, name, plist->propname, pflt, "float", + FIXED_TO_FLOAT( plist->bmin ), + FIXED_TO_FLOAT( plist->bmax ) ); + } +} + + +// build description for class property of string type ------------------------ +// +PRIVATE +void DescClassStr( const char *name, proplist_s *plist, const char *pstr ) +{ + sprintf( paste_str, "%s.%s \"%s\" (min=%d max=%d)", + name, plist->propname, pstr, plist->bmin, plist->bmax ); +} + + +// print various info about specific object type ------------------------------ +// +int CheckTypeInfo( const char *scan ) +{ + ASSERT( scan != NULL ); + + // check if typeinfo command + if ( strcmp( scan, CMSTR( CM_TYPEINFO ) ) != 0 ) + return FALSE; + + // isolate type name field + char *objtypename = strtok( NULL, " " ); + if ( objtypename == NULL ) { + CON_AddLine( typeinfo_syntax ); + return TRUE; + } + + //NOTE: + // parenthesized type names are not + // allowed. + + // check if too many fields supplied + if ( strtok( NULL, " " ) != NULL ) { + CON_AddLine( typeinfo_syntax ); + return TRUE; + } + + // resolve name to id + dword objtypeid = AliasResolveTypeName( objtypename ); + if ( objtypeid == TYPE_ID_INVALID ) { + CON_AddLine( prop_wrong_type ); + return TRUE; + } + + // print info header + sprintf( paste_str, "--properties of type %s (%08x_h):", + objtypename, (unsigned int)objtypeid ); + CON_AddLine( paste_str ); + + // default: use instance/template + dword objclassid = CLASS_ID_INVALID; + ASSERT( alter_object_instance == NULL ); + + // try to find type template (for custom types only) + if ( TYPEID_TYPE_CUSTOM( objtypeid ) ) { + + // (ab)use instance pointer for template + alter_object_instance = OBJ_FetchCustomTypeTemplate( objtypeid ); + } + + // find class if no template available + if ( alter_object_instance == NULL ) { + + // determine whether there are classes of this type + // (scan object class list for class of this type) + for ( int cid = 0; cid < NumObjClasses; cid++ ) { + if ( ObjClasses[ cid ]->ObjectType == objtypeid ) { + objclassid = cid; + break; + } + } + + //NOTE: + // to display info about a type any class of this type + // may be used! (that is, the first class of this type + // contained in the class-list is used.) this is the + // behavior that should be expected, but it is only + // guaranteed to actually obtain homogeneous type-info + // if no class of this type has been altered separately! + + if ( objclassid == CLASS_ID_INVALID ) { + CON_AddLine( "(there are no objects of this type)" ); + } + } + + // fetch property descriptions + proplist_s *plist = FetchTypePropertyList( objtypeid ); + if ( plist == NULL ) { + CON_AddLine( not_accessible ); + return TRUE; + } + + // print list of properties (and their current values + // if the type has been instanced/templatized) + for ( ; plist->propname; plist++ ) { + + if ( ( objclassid != CLASS_ID_INVALID ) || + ( alter_object_instance != NULL ) ) { + + int pint = 0; + float pflt = 0.0f; + char* pstr = NULL; + int acct = GetPropertyValue( objclassid, plist, &pint, &pflt, &pstr ); + + switch ( acct ) { + + case ACCESSTYPE_INT: + case ACCESSTYPE_FLOAT: + DescClassNum( objtypename, plist, acct, pint, pflt ); + break; + + case ACCESSTYPE_STRING: + case ACCESSTYPE_CHARPTR: + DescClassStr( objtypename, plist, pstr ); + break; + } + + } else { + + switch ( plist->fieldtype ) { + + case PROPTYPE_INT: + DescTypeNum( objtypename, plist, ACCESSTYPE_INT ); + break; + + case PROPTYPE_FLOAT: + case PROPTYPE_GEOMV: + case PROPTYPE_FIXED: + DescTypeNum( objtypename, plist, ACCESSTYPE_FLOAT ); + break; + + case PROPTYPE_STRING: + case PROPTYPE_CHARPTR: + DescTypeStr( objtypename, plist ); + break; + } + } + + CON_AddLine( paste_str ); + } + + // reset instance pointer + alter_object_instance = NULL; + + return TRUE; +} + + +// print various info about specific object class ----------------------------- +// +int CheckClassInfo( const char *scan ) +{ + ASSERT( scan != NULL ); + + // check if classinfo command + if ( strcmp( scan, CMSTR( CM_CLASSINFO ) ) != 0 ) + return FALSE; + + // isolate class name field + char *objclassname = strtok( NULL, " " ); + if ( objclassname == NULL ) { + CON_AddLine( classinfo_syntax ); + return TRUE; + } + + // allow class name to be parenthesized to include whitespace + objclassname = GetParenthesizedName( objclassname ); + if ( objclassname == NULL ) { + CON_AddLine( classinfo_syntax ); + return TRUE; + } + + // check if too many fields supplied + if ( strtok( NULL, " " ) != NULL ) { + CON_AddLine( classinfo_syntax ); + return TRUE; + } + + // resolve name to id + dword objclassid = AliasResolveClassName( objclassname ); + if ( objclassid == CLASS_ID_INVALID ) { + CON_AddLine( prop_wrong_class ); + return TRUE; + } + + // check if class is valid (i.e., actually memory resident) + if ( objclassid >= (dword)NumObjClasses ) { + CON_AddLine( class_not_loaded ); + return TRUE; + } + + ASSERT( objclassid < (dword)NumObjClasses ); + dword objtypeid = ObjectInfo[ objclassid ].type; + + // print info header + sprintf( paste_str, "--properties of class %s (%02u_d) (type is %s):", + objclassname, (unsigned int)objclassid, FetchTypeName( objtypeid ) ); + CON_AddLine( paste_str ); + + // fetch property descriptions + proplist_s *plist = FetchTypePropertyList( objtypeid ); + if ( plist == NULL ) { + CON_AddLine( not_accessible ); + return TRUE; + } + + // print list of properties with current values + for ( ; plist->propname; plist++ ) { + + int pint = 0; + float pflt = 0.0f; + char* pstr = NULL; + int acct = GetPropertyValue( objclassid, plist, &pint, &pflt, &pstr ); + + switch ( acct ) { + + case ACCESSTYPE_INT: + case ACCESSTYPE_FLOAT: + DescClassNum( objclassname, plist, acct, pint, pflt ); + break; + + case ACCESSTYPE_STRING: + case ACCESSTYPE_CHARPTR: + DescClassStr( objclassname, plist, pstr ); + break; + } + + CON_AddLine( paste_str ); + } + + return TRUE; +} + + +// print various info about specific object (class instance) ------------------ +// +int CheckInstanceInfo( const char *scan ) +{ + ASSERT( scan != NULL ); + + // check if objectinfo command + if ( strcmp( scan, CMSTR( CM_OBJECTINFO ) ) != 0 ) + return FALSE; + + // isolate object id field + char *objidstr = strtok( NULL, " " ); + if ( objidstr == NULL ) { + CON_AddLine( objectinfo_syntax ); + return TRUE; + } + + // convert object id string to int + char *errpart; + dword objid = strtol( objidstr, &errpart, 10/*int_calc_base*/ ); + if ( *errpart != 0 ) { + CON_AddLine( objectinfo_syntax ); + return TRUE; + } + + // check if too many fields supplied + if ( strtok( NULL, " " ) != NULL ) { + CON_AddLine( objectinfo_syntax ); + return TRUE; + } + + //NOTE: + // id 0 means use id of most recently + // summoned object. + + if ( objid == 0 ) { + objid = TheWorld->GetLastSummonedObjectID(); + if ( !SV_CONSOLE_LEVEL_MESSAGES ) { + sprintf( paste_str, "using object id %u.", (unsigned int)objid ); + CON_AddLine( paste_str ); + } + } + + // find object + GenObject *obj = FetchObject( objid ); + if ( obj == NULL ) { + CON_AddLine( prop_no_object ); + return TRUE; + } + + dword objclassid = obj->ObjectClass; + dword objtypeid = obj->ObjectType; + + ASSERT( objclassid < (dword)NumObjClasses ); + char *objclassname = ObjectInfo[ objclassid ].name; + + // print info header + sprintf( paste_str, "--properties of object %u (class %s id %u, type %s):", + (unsigned int)objid, objclassname, (unsigned int)objclassid, FetchTypeName( objtypeid ) ); + CON_AddLine( paste_str ); + + // fetch property descriptions + proplist_s *plist = FetchTypePropertyList( objtypeid ); + if ( plist == NULL ) { + CON_AddLine( not_accessible ); + return TRUE; + } + + // set instance pointer + alter_object_instance = obj; + + // print list of properties with current values + for ( ; plist->propname; plist++ ) { + + int pint = 0; + float pflt = 0.0f; + char* pstr = NULL; + int acct = GetPropertyValue( CLASS_ID_INVALID, + plist, &pint, &pflt, &pstr ); + + switch ( acct ) { + + case ACCESSTYPE_INT: + case ACCESSTYPE_FLOAT: + DescClassNum( objclassname, plist, acct, pint, pflt ); + break; + + case ACCESSTYPE_STRING: + case ACCESSTYPE_CHARPTR: + DescClassStr( objclassname, plist, pstr ); + break; + } + + CON_AddLine( paste_str ); + } + + // reset instance pointer + alter_object_instance = NULL; + + return TRUE; +} + +// write the properties of a instance to a console script ( propo 0.????? ) --- +// +int WritePropList( FILE* fp, dword objid ) +{ + ASSERT( fp != NULL ); + + // find object + GenObject *obj = FetchObject( objid ); + if ( obj == NULL ) { + CON_AddLine( prop_no_object ); + return TRUE; + } + + dword objclassid = obj->ObjectClass; + dword objtypeid = obj->ObjectType; + + ASSERT( objclassid < (dword)NumObjClasses ); + char *objclassname = ObjectInfo[ objclassid ].name; + + // print info header + fprintf( fp, "; properties of object %d (class %s id %d, type %s)\n", + objid, objclassname, objclassid, FetchTypeName( objtypeid ) ); + + // fetch property descriptions + proplist_s *plist = FetchTypePropertyList( objtypeid ); + if ( plist == NULL ) { + CON_AddLine( not_accessible ); + return TRUE; + } + + // set instance pointer + alter_object_instance = obj; + + // print list of properties with current values + for ( ; plist->propname; plist++ ) { + + int pint; + float pflt; + char* pstr; + int acct = GetPropertyValue( CLASS_ID_INVALID, + plist, &pint, &pflt, &pstr ); + switch ( acct ) { + + case ACCESSTYPE_INT: + fprintf( fp, "propo 0.%s %d\n", plist->propname, pint ); + break; + case ACCESSTYPE_FLOAT: + fprintf( fp, "propo 0.%s %f\n", plist->propname, pflt ); + break; + case ACCESSTYPE_STRING: + case ACCESSTYPE_CHARPTR: + fprintf( fp, "propo 0.%s \"%s\"\n", plist->propname, pstr ); + break; + } + } + + // reset instance pointer + alter_object_instance = NULL; + + return TRUE; +} + + + + +// key table for faceinfo command --------------------------------------------- +// +key_value_s faceinfo_key_value[] = { + + { "class", NULL, KEYVALFLAG_PARENTHESIZE }, + { "classid", NULL, KEYVALFLAG_NONE }, + { "faceid", NULL, KEYVALFLAG_PARENTHESIZE }, + { "shader", NULL, KEYVALFLAG_PARENTHESIZE }, + { "texture", NULL, KEYVALFLAG_PARENTHESIZE }, + + { NULL, NULL, KEYVALFLAG_NONE }, +}; + +enum { + + KEY_FACEINFO_CLASS, + KEY_FACEINFO_CLASSID, + KEY_FACEINFO_FACEID, + KEY_FACEINFO_SHADER, + KEY_FACEINFO_TEXTURE +}; + + +// face search callback vars -------------------------------------------------- +// +static int (*face_cmp_function)( GenObject*, int ); +static TextureMap *facesearch_texture; + + +// return whether a face has the criterion texture ---------------------------- +// +PRIVATE +int FaceHasTexture( GenObject *gobj, int faceid ) +{ + ASSERT( gobj != NULL ); + ASSERT( ( faceid >= 0 ) && ( (unsigned int)faceid < gobj->NumFaces ) ); + + return ( gobj->FaceList[ faceid ].TexMap == facesearch_texture ); +} + + +// display list of face ids satisfying a specific criterion ------------------- +// +PRIVATE +void DisplayFaceList( GenObject *gobj ) +{ + ASSERT( gobj != NULL ); + ASSERT( face_cmp_function != NULL ); + + int outpos = 0; + + for ( unsigned int fid = 0; fid < gobj->NumFaces; fid++ ) { + + if ( (*face_cmp_function)( gobj, fid ) ) { + + sprintf( paste_str + outpos, "%3d, ", fid ); + outpos += strlen( paste_str + outpos ); + + if ( outpos > 60 ) { + + // strip ", " and display accumulated line + paste_str[ outpos - 2 ] = 0; + CON_AddLine( paste_str ); + outpos = 0; + } + } + } + + if ( outpos > 0 ) { + + // strip ", " and display last line + paste_str[ outpos - 2 ] = 0; + CON_AddLine( paste_str ); + } +} + +/* +// command "faceinfo" to display various object face information -------------- +// +int Cmd_FaceInfo( const char *command ) +{ + //NOTE: + //CONCOM: + // faceinfo_command ::= 'faceinfo' <class-spec> <face-spec> + // class_spec ::= <class-name> | <class-id> + // class_name ::= 'class' <name> + // class_id ::= 'classid' <int> + // face_spec ::= <face-id-spec> | <attrib-spec> + // face_id_spec ::= 'faceid' <face-id> + // attrib_spec ::= <shader-spec> | <tex-spec> + // shader_spec ::= 'shader' <shader-iter> | <shader-name> + // shader_iter ::= '(' 'iter_xx'* 'flag_xx'* ')' + // shader_name ::= "valid name of shader" + // tex_spec ::= 'texture' <texture-name> + + ASSERT( command != NULL ); + HANDLE_COMMAND_DOMAIN_SEP( command ); + + // scan out all values to keys + if ( !ScanKeyValuePairs( faceinfo_key_value, command ) ) + return TRUE; + + // get object class (either name or id) + dword objclass = ScanKeyValueObjClass( faceinfo_key_value, KEY_FACEINFO_CLASS, KEY_FACEINFO_CLASSID ); + if ( objclass == CLASS_ID_INVALID ) { + return TRUE; + } + + GenObject *gobj = ObjClasses[ objclass ]; + + // guard against empty base objects + if ( gobj->NumFaces == 0 ) { + CON_AddLine( base_object_empty ); + return TRUE; + } + + // display info about specified face + char *faceid = faceinfo_key_value[ KEY_FACEINFO_FACEID ].value; + if ( faceid != NULL ) { + + dword maxfaceid = gobj->NumFaces - 1; + + // read face id + int faceid; + if ( ScanKeyValueInt( &faceinfo_key_value[ KEY_FACEINFO_FACEID ], &faceid ) < 0 ) { + CON_AddLine( face_id_invalid ); + return TRUE; + } + if ( ( faceid < 0 ) || ( faceid > maxfaceid ) ) { + CON_AddLine( face_id_invalid ); + return TRUE; + } + + //TODO: + ASSERT( 0 ); + + return TRUE; + } + + // list all faces with specified texture + char *texname = faceinfo_key_value[ KEY_FACEINFO_TEXTURE ].value; + if ( texname != NULL ) { + + TextureMap *texmap = FetchTextureMap( texname ); + if ( texmap == NULL ) { + CON_AddLine( no_texture_found ); + return TRUE; + } + + face_cmp_function = FaceHasTexture; + facesearch_texture = texmap; + DisplayFaceList( gobj ); + + return TRUE; + } + + // list all faces with specified shader + char *shaderspec = faceinfo_key_value[ KEY_FACEINFO_SHADER ].value; + if ( shaderspec != NULL ) { + + //TODO: + ASSERT( 0 ); + +// face_cmp_function = FaceHasShader; +// facesearch_texture = shader; +// DisplayFaceList( gobj ); + + return TRUE; + } + + CON_AddLine( no_face_selected ); + return TRUE; +} +*/ + + diff --git a/src/parsec_server/con_int_sv.cpp b/src/parsec_server/con_int_sv.cpp new file mode 100755 index 0000000..96e2fe2 --- /dev/null +++ b/src/parsec_server/con_int_sv.cpp @@ -0,0 +1,402 @@ +/* + * PARSEC - SERVER Integer Variable Commands + * + * $Author: uberlinuxguy $ - $Date: 2004/09/15 12:25:44 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2001-2002 + * Copyright (c) Markus Hadwiger <msh@parsec.org> 1996-2000 + * + * 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 <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.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" +//#include "sys_defs.h" +//#include "vid_defs.h" + +// mathematics header +#include "utl_math.h" + +// local module header +#include "con_int_sv.h" + +// proprietary module headers +#include "con_aux_sv.h" +#include "con_main_sv.h" +//#include "e_record.h" +//#include "g_demo.h" +//#include "g_stars.h" +#include "g_extra.h" +#include "net_game_sv.h" +#include "net_util.h" +#include "e_relist.h" +#include "g_main_sv.h" +#include "e_simulator.h" + + +// integer input/output configuration ----------------------------------------- +// +PUBLIC const char* int_print_base = "%d"; +PUBLIC int int_calc_base = 10; + + +// external variables which need to be accessed but are not in GLOBALS.H ------ +// +extern int ConsoleEnterLength; +extern int ConsoleTextX; +extern int ConsoleTextY; +extern int EchoExternalCommands; +extern int HistoryForBatchEntry; + + +// intermediate storage for MyShip integer properties ------------------------- +// +struct myship_int_s { + + int Weapons; + int Specials; + int CurDamage; + int CurEnergy; + int CurSpeed; + int NumMissls; + int NumHomMissls; + int NumPartMissls; + int NumMines; + int MaxDamage; + int MaxEnergy; + int MaxSpeed; + int MaxNumMissls; + int MaxNumHomMissls; + int MaxNumPartMissls; + int MaxNumMines; +}; + +static myship_int_s myship_int; + +/* + +//FIXME: + +#define DEF_MYSHIP_INT( f ) \ +int GetMSInt##f() \ +{ \ + return (int)MyShip->f; \ +} \ +void SetMSInt##f() \ +{ \ + MyShip->f = myship_int.f; \ + Record_MyShipState##f(); \ +} + +*/ + +#define DEF_MYSHIP_INT( f ) \ +int GetMSInt##f() \ +{ \ + return 0; \ +} \ +void SetMSInt##f() \ +{ \ +} + +DEF_MYSHIP_INT( Weapons ) +DEF_MYSHIP_INT( Specials ) +DEF_MYSHIP_INT( CurDamage ) +DEF_MYSHIP_INT( CurEnergy ) +DEF_MYSHIP_INT( CurSpeed ) +DEF_MYSHIP_INT( NumMissls ) +DEF_MYSHIP_INT( NumHomMissls ) +DEF_MYSHIP_INT( NumPartMissls ) +DEF_MYSHIP_INT( NumMines ) +DEF_MYSHIP_INT( MaxDamage ) +DEF_MYSHIP_INT( MaxEnergy ) +DEF_MYSHIP_INT( MaxSpeed ) +DEF_MYSHIP_INT( MaxNumMissls ) +DEF_MYSHIP_INT( MaxNumHomMissls ) +DEF_MYSHIP_INT( MaxNumPartMissls ) +DEF_MYSHIP_INT( MaxNumMines ) + + +//FIXME: get rid of these proxy stuff + +// proxy structure for G_Main data members modifiable through --------------- +// +struct game_proxy_int_s { + + int EnergyExtraBoost; + int RepairExtraBoost; + int DumbPackNumMissls; + int HomPackNumMissls; + int SwarmPackNumMissls; + int ProxPackNumMines; + int m_NebulaID; +}; + +// proxy structure for G_Extra data members modifiable through the console --- +// +struct game_extra_proxy_int_s { + + int MaxExtraArea; + int MinExtraDist; + int ExtraProbability; + int ProbHelixCannon; + int ProbLightningDevice; + int ProbPhotonCannon; + int ProbProximityMine; + int ProbRepairExtra; + int ProbAfterburner; + int ProbHoloDecoy; + int ProbInvisibility; + int ProbInvulnerability; + int ProbEnergyField; + int ProbLaserUpgrade; + int ProbLaserUpgrade1; + int ProbLaserUpgrade2; + int ProbMissilePack; + int ProbDumbMissPack; + int ProbHomMissPack; + int ProbSwarmMissPack; + int ProbEmpUpgrade1; + int ProbEmpUpgrade2; +}; + + +static game_proxy_int_s Game_proxy; +static game_extra_proxy_int_s GameExtraManager_proxy; + +#define DEF_PROXY_INT( name, f ) \ +int Get##name##Int##f() \ +{ \ + return (int)The##name->f; \ +} \ +void Set##name##Int##f() \ +{ \ + The##name->f = name##_proxy.f; \ +} + +DEF_PROXY_INT( GameExtraManager, MaxExtraArea ) +DEF_PROXY_INT( GameExtraManager, MinExtraDist ) +DEF_PROXY_INT( Game, EnergyExtraBoost ) +DEF_PROXY_INT( Game, RepairExtraBoost ) +DEF_PROXY_INT( Game, DumbPackNumMissls ) +DEF_PROXY_INT( Game, HomPackNumMissls ) +DEF_PROXY_INT( Game, SwarmPackNumMissls ) +DEF_PROXY_INT( Game, ProxPackNumMines ) +DEF_PROXY_INT( Game, m_NebulaID) +DEF_PROXY_INT( GameExtraManager, ExtraProbability ) +DEF_PROXY_INT( GameExtraManager, ProbHelixCannon ) +DEF_PROXY_INT( GameExtraManager, ProbLightningDevice ) +DEF_PROXY_INT( GameExtraManager, ProbPhotonCannon ) +DEF_PROXY_INT( GameExtraManager, ProbProximityMine ) +DEF_PROXY_INT( GameExtraManager, ProbRepairExtra ) +DEF_PROXY_INT( GameExtraManager, ProbAfterburner ) +DEF_PROXY_INT( GameExtraManager, ProbHoloDecoy ) +DEF_PROXY_INT( GameExtraManager, ProbInvisibility ) +DEF_PROXY_INT( GameExtraManager, ProbInvulnerability ) +DEF_PROXY_INT( GameExtraManager, ProbEnergyField ) +DEF_PROXY_INT( GameExtraManager, ProbLaserUpgrade ) +DEF_PROXY_INT( GameExtraManager, ProbLaserUpgrade1 ) +DEF_PROXY_INT( GameExtraManager, ProbLaserUpgrade2 ) +DEF_PROXY_INT( GameExtraManager, ProbMissilePack ) +DEF_PROXY_INT( GameExtraManager, ProbDumbMissPack ) +DEF_PROXY_INT( GameExtraManager, ProbHomMissPack ) +DEF_PROXY_INT( GameExtraManager, ProbSwarmMissPack ) +DEF_PROXY_INT( GameExtraManager, ProbEmpUpgrade1 ) +DEF_PROXY_INT( GameExtraManager, ProbEmpUpgrade2 ) + +/* +// perform remote event syncing for certain int vars -------------------------- +// +#define DEF_RMEV_STATESYNC( f, k ) \ +void RESync##f() \ +{ \ + if ( !NET_RmEvAllowed( RE_STATESYNC ) ) \ + return; \ + NET_RmEvStateSync( (k), (TheGame->f) ); \ +} + +DEF_RMEV_STATESYNC( ExtraProbability, RMEVSTATE_PROBEXTRA ); +DEF_RMEV_STATESYNC( ProbHelixCannon, RMEVSTATE_PROBHELIX ); +DEF_RMEV_STATESYNC( ProbLightningDevice, RMEVSTATE_PROBLIGHTNING ); +DEF_RMEV_STATESYNC( ProbPhotonCannon, RMEVSTATE_PROBPHOTON ); +DEF_RMEV_STATESYNC( ProbProximityMine, RMEVSTATE_PROBMINE ); +DEF_RMEV_STATESYNC( ProbRepairExtra, RMEVSTATE_PROBREPAIR ); +DEF_RMEV_STATESYNC( ProbAfterburner, RMEVSTATE_PROBAFTERBURNER ); +DEF_RMEV_STATESYNC( ProbHoloDecoy, RMEVSTATE_PROBHOLODECOY ); +DEF_RMEV_STATESYNC( ProbInvisibility, RMEVSTATE_PROBINVISIBILITY ); +DEF_RMEV_STATESYNC( ProbInvulnerability, RMEVSTATE_PROBINVULNERABILITY ); +DEF_RMEV_STATESYNC( ProbEnergyField, RMEVSTATE_PROBENERGYFIELD ); +DEF_RMEV_STATESYNC( ProbLaserUpgrade, RMEVSTATE_PROBLASERUPGRADE ); +DEF_RMEV_STATESYNC( ProbLaserUpgrade1, RMEVSTATE_PROBLASERUPGRADE1 ); +DEF_RMEV_STATESYNC( ProbLaserUpgrade2, RMEVSTATE_PROBLASERUPGRADE2 ); +DEF_RMEV_STATESYNC( ProbMissilePack, RMEVSTATE_PROBMISSPACK ); +DEF_RMEV_STATESYNC( ProbDumbMissPack, RMEVSTATE_PROBDUMBPACK ); +DEF_RMEV_STATESYNC( ProbHomMissPack, RMEVSTATE_PROBGUIDEPACK ); +DEF_RMEV_STATESYNC( ProbSwarmMissPack, RMEVSTATE_PROBSWARMPACK ); +DEF_RMEV_STATESYNC( ProbEmpUpgrade1, RMEVSTATE_PROBEMPUPGRADE1 ); +DEF_RMEV_STATESYNC( ProbEmpUpgrade2, RMEVSTATE_PROBEMPUPGRADE2 ); +*/ + +// integer variable manipulation commands ------------------------------------- +// +PRIVATE +int_command_s int_commands_default[] = { + +//CAVEAT: +// absolute and relative positions of the following +// 4 commands MUST NOT be altered. the CONINFOINDX_xx +// constants in CON_INT.H must be defined consistently. + + //{ 0x01, "con.winx", 1, 489, &ConsoleTextX, CheckConExtents, NULL }, + //{ 0x01, "con.winy", 1, 295, &ConsoleTextY, CheckConExtents, NULL }, + //{ 0x01, "con.width", 11, 500, &ConsoleEnterLength, CheckConExtents, NULL }, + //{ 0x01, "con.height", 5, 300, &ConsoleHeight, CheckConExtents, NULL }, + + { 0x01, "con.echoextcoms", 0, 1, &EchoExternalCommands, NULL, NULL }, + { 0x01, "con.scripthistory", 0, 1, &HistoryForBatchEntry, NULL, NULL }, + + { 0x01, "net.packetrecording", 0, 1, &RecordRemotePackets, NULL, NULL }, + +#ifdef ENABLE_CHEAT_COMMANDS +/* + { 0x00, ".weapons", 0, 0xfff, &myship_int.Weapons, SetMSIntWeapons, GetMSIntWeapons }, + { 0x00, ".specials", 0, 0xfffff,&myship_int.Specials, SetMSIntSpecials, GetMSIntSpecials }, + { 0x00, ".damage", 0, 10000, &myship_int.CurDamage, SetMSIntCurDamage, GetMSIntCurDamage }, + { 0x00, ".energy", 0, 10000, &myship_int.CurEnergy, SetMSIntCurEnergy, GetMSIntCurEnergy }, + { 0x00, ".speed", 0, 500000, &myship_int.CurSpeed, SetMSIntCurSpeed, GetMSIntCurSpeed }, + { 0x00, ".dumb", 0, 10000, &myship_int.NumMissls, SetMSIntNumMissls, GetMSIntNumMissls }, + { 0x00, ".guide", 0, 10000, &myship_int.NumHomMissls, SetMSIntNumHomMissls, GetMSIntNumHomMissls }, + { 0x00, ".swarm", 0, 10000, &myship_int.NumPartMissls, SetMSIntNumPartMissls, GetMSIntNumPartMissls }, + { 0x00, ".mines", 0, 10000, &myship_int.NumMines, SetMSIntNumMines, GetMSIntNumMines }, + { 0x00, ".damage.max", 0, 10000, &myship_int.MaxDamage, SetMSIntMaxDamage, GetMSIntMaxDamage }, + { 0x00, ".energy.max", 0, 10000, &myship_int.MaxEnergy, SetMSIntMaxEnergy, GetMSIntMaxEnergy }, + { 0x00, ".speed.max", 0, 500000, &myship_int.MaxSpeed, SetMSIntMaxSpeed, GetMSIntMaxSpeed }, + { 0x00, ".dumb.max", 0, 10000, &myship_int.MaxNumMissls, SetMSIntMaxNumMissls, GetMSIntMaxNumMissls }, + { 0x00, ".guide.max", 0, 10000, &myship_int.MaxNumHomMissls, SetMSIntMaxNumHomMissls, GetMSIntMaxNumHomMissls }, + { 0x00, ".swarm.max", 0, 10000, &myship_int.MaxNumPartMissls, SetMSIntMaxNumPartMissls, GetMSIntMaxNumPartMissls }, + { 0x00, ".mines.max", 0, 10000, &myship_int.MaxNumMines, SetMSIntMaxNumMines, GetMSIntMaxNumMines }, +*/ +#endif // ENABLE_CHEAT_COMMANDS + +{ 0x00, "extras.max", 0, 100, &SV_GAME_EXTRAS_MAXNUM, NULL, NULL }, +{ 0x00, "extras.area", 100,10000, &GameExtraManager_proxy.MaxExtraArea, SetGameExtraManagerIntMaxExtraArea, GetGameExtraManagerIntMaxExtraArea }, +{ 0x00, "extras.dist", 50, 1000, &GameExtraManager_proxy.MinExtraDist, SetGameExtraManagerIntMinExtraDist, GetGameExtraManagerIntMinExtraDist }, + +{ 0x00, "nebula.id", 2, 5, &Game_proxy.m_NebulaID, SetGameIntm_NebulaID, GetGameIntm_NebulaID }, +{ 0x00, "energy.boost", 0, 1000, &Game_proxy.EnergyExtraBoost, SetGameIntEnergyExtraBoost, GetGameIntEnergyExtraBoost }, +{ 0x00, "repair.boost", 0, 1000, &Game_proxy.RepairExtraBoost, SetGameIntRepairExtraBoost, GetGameIntRepairExtraBoost }, + +{ 0x00, "pack.dumb.size", 0, 100, &Game_proxy.DumbPackNumMissls, SetGameIntDumbPackNumMissls, GetGameIntDumbPackNumMissls }, +{ 0x00, "pack.guide.size", 0, 100, &Game_proxy.HomPackNumMissls, SetGameIntHomPackNumMissls, GetGameIntHomPackNumMissls }, +{ 0x00, "pack.swarm.size", 0, 100, &Game_proxy.SwarmPackNumMissls, SetGameIntSwarmPackNumMissls, GetGameIntSwarmPackNumMissls}, +{ 0x00, "pack.mine.size", 0, 100, &Game_proxy.ProxPackNumMines, SetGameIntProxPackNumMines, GetGameIntProxPackNumMines }, + +{ 0x00, "prob.extra", 0, 100, &GameExtraManager_proxy.ExtraProbability, SetGameExtraManagerIntExtraProbability, GetGameExtraManagerIntExtraProbability }, + +{ 0x00, "prob.helix", 0, 100, &GameExtraManager_proxy.ProbHelixCannon, SetGameExtraManagerIntProbHelixCannon, GetGameExtraManagerIntProbHelixCannon }, +{ 0x00, "prob.lightning", 0, 100, &GameExtraManager_proxy.ProbLightningDevice, SetGameExtraManagerIntProbLightningDevice, GetGameExtraManagerIntProbLightningDevice}, +{ 0x00, "prob.photon", 0, 100, &GameExtraManager_proxy.ProbPhotonCannon, SetGameExtraManagerIntProbPhotonCannon, GetGameExtraManagerIntProbPhotonCannon }, +{ 0x00, "prob.mine", 0, 100, &GameExtraManager_proxy.ProbProximityMine, SetGameExtraManagerIntProbProximityMine, GetGameExtraManagerIntProbProximityMine }, +{ 0x00, "prob.repair", 0, 100, &GameExtraManager_proxy.ProbRepairExtra, SetGameExtraManagerIntProbRepairExtra, GetGameExtraManagerIntProbRepairExtra }, +{ 0x00, "prob.aburner", 0, 100, &GameExtraManager_proxy.ProbAfterburner, SetGameExtraManagerIntProbAfterburner, GetGameExtraManagerIntProbAfterburner }, +{ 0x00, "prob.decoy", 0, 100, &GameExtraManager_proxy.ProbHoloDecoy, SetGameExtraManagerIntProbHoloDecoy, GetGameExtraManagerIntProbHoloDecoy }, +{ 0x00, "prob.invisible", 0, 100, &GameExtraManager_proxy.ProbInvisibility, SetGameExtraManagerIntProbInvisibility, GetGameExtraManagerIntProbInvisibility }, +{ 0x00, "prob.invulnerable", 0, 100, &GameExtraManager_proxy.ProbInvulnerability, SetGameExtraManagerIntProbInvulnerability, GetGameExtraManagerIntProbInvulnerability}, +{ 0x00, "prob.energyfield", 0, 100, &GameExtraManager_proxy.ProbEnergyField, SetGameExtraManagerIntProbEnergyField, GetGameExtraManagerIntProbEnergyField }, + +{ 0x00, "prob.lupgrade", 0, 100, &GameExtraManager_proxy.ProbLaserUpgrade, SetGameExtraManagerIntProbLaserUpgrade, GetGameExtraManagerIntProbLaserUpgrade }, +{ 0x00, "prob.lupgrade1", 0, 100, &GameExtraManager_proxy.ProbLaserUpgrade1, SetGameExtraManagerIntProbLaserUpgrade1, GetGameExtraManagerIntProbLaserUpgrade1 }, +{ 0x00, "prob.lupgrade2", 0, 100, &GameExtraManager_proxy.ProbLaserUpgrade2, SetGameExtraManagerIntProbLaserUpgrade2, GetGameExtraManagerIntProbLaserUpgrade2 }, + +{ 0x00, "prob.misspack", 0, 100, &GameExtraManager_proxy.ProbMissilePack, SetGameExtraManagerIntProbMissilePack, GetGameExtraManagerIntProbMissilePack }, +{ 0x00, "prob.dumb", 0, 100, &GameExtraManager_proxy.ProbDumbMissPack, SetGameExtraManagerIntProbDumbMissPack, GetGameExtraManagerIntProbDumbMissPack }, +{ 0x00, "prob.guide", 0, 100, &GameExtraManager_proxy.ProbHomMissPack, SetGameExtraManagerIntProbHomMissPack, GetGameExtraManagerIntProbHomMissPack }, +{ 0x00, "prob.swarm", 0, 100, &GameExtraManager_proxy.ProbSwarmMissPack, SetGameExtraManagerIntProbSwarmMissPack, GetGameExtraManagerIntProbSwarmMissPack }, + +{ 0x00, "prob.empupgrade1", 0, 100, &GameExtraManager_proxy.ProbEmpUpgrade1, SetGameExtraManagerIntProbEmpUpgrade1, GetGameExtraManagerIntProbEmpUpgrade1 }, +{ 0x00, "prob.empupgrade2", 0, 100, &GameExtraManager_proxy.ProbEmpUpgrade2, SetGameExtraManagerIntProbEmpUpgrade2, GetGameExtraManagerIntProbEmpUpgrade2 }, +}; + +#define NUM_INT_COMMANDS CALC_NUM_ARRAY_ENTRIES( int_commands_default ) + + +// pointer to current integer variable commands list -------------------------- +// +PUBLIC int_command_s *int_commands = int_commands_default; + + +// current number of integer variable commands -------------------------------- +// +PUBLIC int num_int_commands = NUM_INT_COMMANDS; +PRIVATE int max_int_commands = NUM_INT_COMMANDS; + + +// register a new (user-defined) integer variable command --------------------- +// +void CON_RegisterIntCommand( int_command_s *regcom ) +{ + //NOTE: + //CAVEAT: + // the supplied command string is not copied + // by this function. thus, the caller MUST ENSURE + // that this string is available indefinitely. + // (e.g., allocated statically.) + + ASSERT( regcom != NULL ); + ASSERT( regcom->command != NULL ); + ASSERT( regcom->intref != NULL ); + ASSERT( regcom->bmin <= regcom->bmax ); + ASSERT( num_int_commands <= max_int_commands ); + + // expand table memory if already used up + if ( num_int_commands == max_int_commands ) { + + // expand exponentially + max_int_commands *= 2; + + // alloc new table + int_command_s *newlist = (int_command_s *) ALLOCMEM( sizeof( int_command_s ) * max_int_commands ); + ASSERT( newlist != NULL ); + + // move old table + memcpy( newlist, int_commands, sizeof( int_command_s ) * num_int_commands ); + if ( int_commands != int_commands_default ) + FREEMEM( int_commands ); + int_commands = newlist; + } + + // append new command + ASSERT( num_int_commands < max_int_commands ); + int_commands[ num_int_commands++ ] = *regcom; +} + diff --git a/src/parsec_server/con_list_sv.cpp b/src/parsec_server/con_list_sv.cpp new file mode 100755 index 0000000..47fc7fc --- /dev/null +++ b/src/parsec_server/con_list_sv.cpp @@ -0,0 +1,1253 @@ +/* + * PARSEC - Server List Commands + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:47 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2002 + * Copyright (c) Markus Hadwiger <msh@parsec.org> 1996-1999 + * + * 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 <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "objstruc.h" +#include "od_class.h" +#include "od_props.h" + +// global externals +#include "globals.h" +#include "e_world_trans.h" + +// subsystem headers +//#include "inp_defs.h" +//#include "vid_defs.h" + +// particle types +#include "parttype.h" + +// local module header +#include "con_list_sv.h" + +// proprietary module headers +#include "con_aux_sv.h" +#include "con_ext_sv.h" +#include "con_info_sv.h" +#include "con_int_sv.h" +#include "con_main_sv.h" +//#include "con_say_sv.h" +#include "con_std_sv.h" +//#include "e_demo.h" +//#include "obj_ctrl.h" +#include "obj_cust.h" +#include "obj_type.h" +#include "sys_file.h" + + + +// generic string paste area -------------------------------------------------- +// +#define PASTE_STR_LEN 1023 +static char paste_str[ PASTE_STR_LEN + 1 ]; + + +// string constants ----------------------------------------------------------- +// +static char filter_too_long[] = "specified filter too long."; +static char shiplist_empty[] = "shiplist is empty."; +static char table_name_invalid[] = "invalid data table name."; +static char table_name_syntax[] = "syntax: listdata <tablename>."; +static char valid_tables_list[] = "valid data tables are:"; + + +// list functions administration ---------------------------------------------- +// +int await_keypress = FALSE; +void (*com_cont_func)() = NULL; +int cur_vis_conlines = 0; +int comm_to_print = 0; + +static ShipObject *print_ship_list; + + +// print single ship info ----------------------------------------------------- +// +PRIVATE +void PrintShipInfo( ShipObject *shippo ) +{ + ASSERT( shippo != NULL ); + + sprintf( paste_str, "objnum: %u", (unsigned int)shippo->ObjectNumber ); + CON_AddLine( paste_str ); + sprintf( paste_str, "hostobjnum: %u", (unsigned int)shippo->HostObjNumber ); + CON_AddLine( paste_str ); + sprintf( paste_str, "objtype: %u", (unsigned int)shippo->ObjectType ); + CON_AddLine( paste_str ); + sprintf( paste_str, "objclass: %u", (unsigned int)shippo->ObjectClass ); + CON_AddLine( paste_str ); + sprintf( paste_str, "x position: %13f", GEOMV_TO_FLOAT( shippo->ObjPosition[ 0 ][ 3 ] ) ); + CON_AddLine( paste_str ); + sprintf( paste_str, "y position: %13f", GEOMV_TO_FLOAT( shippo->ObjPosition[ 1 ][ 3 ] ) ); + CON_AddLine( paste_str ); + sprintf( paste_str, "z position: %13f", GEOMV_TO_FLOAT( shippo->ObjPosition[ 2 ][ 3 ] ) ); + CON_AddLine( paste_str ); + sprintf( paste_str, "damage: %d", shippo->CurDamage ); + CON_AddLine( paste_str ); + sprintf( paste_str, "speed: %u", (unsigned int)shippo->CurSpeed ); + CON_AddLine( paste_str ); +} + + +// continue ship info list ---------------------------------------------------- +// +PRIVATE +void ShipInfo_ctd() +{ + print_ship_list = (ShipObject *) print_ship_list->NextObj; + + if ( print_ship_list ) { + CON_AddLine( line_separator ); + PrintShipInfo( print_ship_list ); + if ( print_ship_list->NextObj ) { + CON_ListCtdPrompt(); + } else { + CON_ListEndPrompt(); + } + } else { + CON_ListEndPrompt(); + } +} + + +// print ship info list (command "ships") ------------------------------------- +// +void Cmd_ShipInfo() +{ + print_ship_list = FetchFirstShip(); + + if ( print_ship_list ) { + PrintShipInfo( print_ship_list ); + if ( print_ship_list->NextObj ) { + await_keypress = TRUE; + com_cont_func = ShipInfo_ctd; + } + } else { + CON_AddLine( shiplist_empty ); + } +} + + +// determine how much lines can still be output in console -------------------- +// +INLINE +int CheckVerticalSpace( int numalreadyoutput ) +{ + int vsp = cur_vis_conlines - 1 - numalreadyoutput; + return ( vsp > 0 ) ? vsp : 0; +} + + +// print line that is part of a list (generic) -------------------------------- +// +PRIVATE +int PrintListLine( int *list_lines_printed, const char *line ) +{ + ASSERT( list_lines_printed != NULL ); + ASSERT( line != NULL ); + + //NOTE: + // this function first determines if there is enough + // vertical space left in the console to print the next + // (passed in) line. this is done by using the first + // parameter which has to point to an int parameter that + // is incremented by this function if it was able to + // output the line. therefore, the int parameter counts + // the number of lines already output in the current run. + + if ( *list_lines_printed < cur_vis_conlines - 1 ) { + + // output line and increment externally provided counter + CON_AddLine( line ); + (*list_lines_printed)++; + return 1; + + } else { + + // not enough vertical space to output line + return 0; + } +} + + +// check if list needs to be continued (i.e. there is more than one run) ------ +// +PRIVATE +void CheckListContinuation( int numitems, void (*contfunc)() ) +{ + ASSERT( numitems >= 0 ); + ASSERT( contfunc != NULL ); + + if ( comm_to_print != numitems ) { + await_keypress = TRUE; + com_cont_func = contfunc; + } +} + + +// check if list has ended and stop list function ----------------------------- +// +PRIVATE +void CheckListEnd( int numitems ) +{ + ASSERT( numitems >= 0 ); + + if ( comm_to_print == numitems ) + CON_ListEndPrompt(); + else + CON_ListCtdPrompt(); +} + + +// number of listable types and classes --------------------------------------- +// +static int num_listable_types; +static int num_listable_classes; + + +// continue object type list -------------------------------------------------- +// +PRIVATE +void ListTypes_ctd() +{ + // continued run + int cnt = 0; + while ( ( comm_to_print < num_listable_types ) && + ( PrintListLine( &cnt, BuildTypeInfo( comm_to_print ) ) ) ) + comm_to_print++; + + CheckListEnd( num_listable_types ); +} + + +// list available object types ("listtypes") ---------------------------------- +// +void Cmd_ListTypes() +{ + // list entries already output + comm_to_print = 0; + + // determine number of types + num_listable_types = NUM_DISTINCT_OBJTYPES + num_custom_types; + + // first run + int cnt = 0; + while ( ( comm_to_print < num_listable_types ) && + ( PrintListLine( &cnt, BuildTypeInfo( comm_to_print ) ) ) ) + comm_to_print++; + + CheckListContinuation( num_listable_types, ListTypes_ctd ); +} + + +// continue object class list ------------------------------------------------- +// +PRIVATE +void ListClasses_ctd() +{ + // continued run + int cnt = 0; + while ( ( comm_to_print < num_listable_classes ) && + ( PrintListLine( &cnt, BuildClassInfo( comm_to_print ) ) ) ) + comm_to_print++; + + CheckListEnd( num_listable_classes ); +} + + +// list available object classes ("listclasses") ------------------------------ +// +void Cmd_ListClasses() +{ + // list entries already output + comm_to_print = 0; + + // determine number of classes + num_listable_classes = NumObjClasses; + + // first run + int cnt = 0; + while ( ( comm_to_print < num_listable_classes ) && + ( PrintListLine( &cnt, BuildClassInfo( comm_to_print ) ) ) ) + comm_to_print++; + + CheckListContinuation( num_listable_classes, ListClasses_ctd ); +} + +/* +// display info about say macro ----------------------------------------------- +// +PRIVATE +int DispSayMacro( int *cnt, int num ) +{ + ASSERT( cnt != NULL ); + ASSERT( num >= 0 ); + + sprintf( paste_str, "say%d: %s", num, text_macros[ num ] ); + return PrintListLine( cnt, paste_str ); +} + + +// continue command list ------------------------------------------------------ +// +PRIVATE +void ListSayMacros_ctd() +{ + // continued run + int cnt = 0; + while ( ( comm_to_print < NUM_TEXT_MACROS ) && + ( DispSayMacro( &cnt, comm_to_print ) ) ) + comm_to_print++; + + CheckListEnd( NUM_TEXT_MACROS ); +} + + +// list all available say macros ("listsaymacros") ---------------------------- +// +void Cmd_ListSayMacros() +{ + // list entries already output + comm_to_print = 0; + + // first run + int cnt = 0; + while ( ( comm_to_print < NUM_TEXT_MACROS ) && + ( DispSayMacro( &cnt, comm_to_print ) ) ) + comm_to_print++; + + CheckListContinuation( NUM_TEXT_MACROS, ListSayMacros_ctd ); +} +*/ + +/* +// display info about key binding --------------------------------------------- +// +PRIVATE +int DispKeyBinding( int *cnt, int num ) +{ + ASSERT( cnt != NULL ); + ASSERT( ( num >= 0 ) && + ( num < KEY_ADDITIONS_MAX ) && ( num < KeyAdditional->size ) ); + + if ( key_com_mappings[ num ].command != NULL ) { + const char *fmt = key_com_mappings[ num ].echo ? + "%s: %s [echo on]" : "%s: %s"; + sprintf( paste_str, fmt, + GetAKCDescription( num ), key_com_mappings[ num ].command ); + return PrintListLine( cnt, paste_str ); + } + + return 1; +} + + +// display info about key binding to game function ---------------------------- +// +PRIVATE +int DispKeyFunction( int *cnt, int num ) +{ + ASSERT( cnt != NULL ); + ASSERT( num >= 0 ); + + #define FUNC_DESC_PAD_SIZE 15 + + ASSERT( num < NUM_GAMEFUNC_KEYS ); + + char *fdesc = GetGameFuncDescription( num ); + ASSERT( fdesc != NULL ); + sprintf( paste_str, "%s: ", fdesc ); + int descpad = FUNC_DESC_PAD_SIZE - strlen( fdesc ); + char *ppad = paste_str + strlen( fdesc ) + 2; + while ( descpad-- > 0 ) + *ppad++ = ' '; + strcpy( ppad, GetMKCDescription( num ) ); + + #undef FUNC_DESC_PAD_SIZE + + return PrintListLine( cnt, paste_str ); +} +*/ + +// store alphabetic start sequence -------------------------------------------- +// +#define MAX_ALPHA_START 32 +static char alphabetic_start[ MAX_ALPHA_START + 1 ]; + + +// print command only if starting with a certain alphabetic sequence ---------- +// +PRIVATE +int PrintFilteredCommand( int *count, const char *line ) +{ + ASSERT( count != NULL ); + ASSERT( line != NULL ); + + if ( alphabetic_start[ 0 ] != 0 ) { + if ( strnicmp( alphabetic_start, line, strlen( alphabetic_start ) ) != 0 ) { + return 1; + } + } + + return PrintListLine( count, line ); +} + + +// init alphabetic sequence --------------------------------------------------- +// +PRIVATE +int InitAlphabeticStart( char *scan ) +{ + if ( scan != NULL ) { + + if ( *(scan - 1) != ' ' ) { + CON_AddLine( unknown_command ); + return FALSE; + } + + if ( !strtok( NULL, " " ) ) { + if ( strlen( scan ) > MAX_ALPHA_START ) { + CON_AddLine( filter_too_long ); + return FALSE; + } + strcpy( alphabetic_start, scan ); + } else { + CON_AddLine( too_many_args ); + return FALSE; + } + + } else { + alphabetic_start[ 0 ] = 0; + } + + return TRUE; +} + + +// print line from directory listing annotated with entry type ---------------- +// +PRIVATE +int PrintDirLine( int *iref, int comno ) +{ + ASSERT( iref != NULL ); + ASSERT( ( comno >= 0 ) && ( comno < num_external_commands ) ); + + #define FIELD_SIZE 9 + + strcpy( paste_str, ECMSTR( comno ) ); + + int len = ECMLEN( comno ); + int padsize = ( len < FIELD_SIZE ) ? ( FIELD_SIZE - len ) : 0; + + while ( padsize-- > 0 ) { + paste_str[ len++ ] = ' '; + } + paste_str[ len ] = 0; + + #undef FIELD_SIZE + + switch ( external_command_types[ comno ] ) { + + case COMTYPE_PACKAGE: + if ( SV_PACKAGE_SCRIPT_LISTING ) { + // allow exclusion of package scripts from list + return 1; + } + strcat( paste_str, " (pack)" ); + break; + + case COMTYPE_ROOT: + strcat( paste_str, " (root)" ); + break; + + case COMTYPE_REFERENCE: + strcat( paste_str, " (ref)" ); + break; + + case COMTYPE_STANDARD: + strcat( paste_str, " (std)" ); + break; + + case COMTYPE_RECORDING: + strcat( paste_str, " (rec)" ); + break; + + case COMTYPE_MODIFICATION: + strcat( paste_str, " (mod)" ); + break; + } + + return PrintFilteredCommand( iref, paste_str ); +} + + +// continue command list ------------------------------------------------------ +// +PRIVATE +void ListExternalCommands_ctd() +{ + // continued run + int cnt = 0; + while ( ( comm_to_print < num_external_commands ) && + ( PrintDirLine( &cnt, comm_to_print ) ) ) + comm_to_print++; + + CheckListEnd( num_external_commands ); +} + + +// list all available external console commands ("ls") ------------------------ +// +int Cmd_ListExternalCommands( char *command ) +{ + ASSERT( command != NULL ); + HANDLE_COMMAND_DOMAIN( command ); + + char *scan = strtok( command, " " ); + if ( !InitAlphabeticStart( scan ) ) { + // recognize always (implicitly reserves domain) + return TRUE; + } + + // list entries already output + comm_to_print = 0; + + // first run + int cnt = 0; + while ( ( comm_to_print < num_external_commands ) && + ( PrintDirLine( &cnt, comm_to_print ) ) ) + comm_to_print++; + + CheckListContinuation( num_external_commands, ListExternalCommands_ctd ); + + return TRUE; +} + +/* +// continue command list ------------------------------------------------------ +// +PRIVATE +void ListKeyBindings_ctd() +{ + // continued run + int cnt = 0; + while ( ( comm_to_print < KeyAdditional->size ) && + ( DispKeyBinding( &cnt, comm_to_print ) ) ) + comm_to_print++; + + CheckListEnd( KeyAdditional->size ); +} + + +// list all additional key bindings ("listbindings") -------------------------- +// +void Cmd_ListKeyBindings() +{ + // list entries already output + comm_to_print = 0; + + // first run + int cnt = 0; + while ( ( comm_to_print < KeyAdditional->size ) && + ( DispKeyBinding( &cnt, comm_to_print ) ) ) + comm_to_print++; + + CheckListContinuation( KeyAdditional->size, ListKeyBindings_ctd ); +} + + +// list all keys bound to game functions -------------------------------------- +// +PRIVATE +void ListKeyFunctions_ctd() +{ + // continued run + int cnt = 0; + while ( ( comm_to_print < NUM_GAMEFUNC_KEYS ) && + ( DispKeyFunction( &cnt, comm_to_print ) ) ) + comm_to_print++; + + CheckListEnd( NUM_GAMEFUNC_KEYS ); +} + + +// list all keys bound to game functions ("listgamefunckeys") ----------------- +// +void Cmd_ListKeyFunctions() +{ + // list entries already output + comm_to_print = 0; + + // first run + int cnt = 0; + while ( ( comm_to_print < NUM_GAMEFUNC_KEYS ) && + ( DispKeyFunction( &cnt, comm_to_print ) ) ) + comm_to_print++; + + CheckListContinuation( NUM_GAMEFUNC_KEYS, ListKeyFunctions_ctd ); +} + + +// display single video mode information -------------------------------------- +// +PRIVATE +int DispVidMode( int *cnt, int num ) +{ + ASSERT( cnt != NULL ); + ASSERT( num >= 0 ); + + if ( VID_MODE_AVAILABLE( num ) ) { + + short bytesperscanline = HiresModes[ num ].bytesperscanline; + short colbits = HiresModes[ num ].colbits; + + if ( HiresModes[ num ].flags & HIRESMODEFLAG_VISUALINFO ) { + bytesperscanline = 0; + colbits = HiresModes[ num ].visual_colbits; + } + + if ( bytesperscanline > 0 ) { + sprintf( paste_str, " mode: %4d x %4d %2dbpp %4dbpsl", + HiresModes[ num ].xresolution, + HiresModes[ num ].yresolution, + colbits, bytesperscanline ); + } else { + sprintf( paste_str, " mode: %4d x %4d %2dbpp", + HiresModes[ num ].xresolution, + HiresModes[ num ].yresolution, + colbits ); + } + + if ( VID_MODE_WINDOWED( num ) ) { + strcat( paste_str, " (windowed)" ); + } else { + // display refresh rate if available (and not default) + } + if ( num == VidModeIndex ) { + paste_str[ 0 ] = '*'; + } + + return PrintListLine( cnt, paste_str ); + } + + return 1; +} + + +// continue video mode list ---------------------------------------------------- +// +PRIVATE +void ListVidModes_ctd() +{ + // continued run + int cnt = 0; + while ( ( comm_to_print < MAX_NUM_GRAPH_MODES ) && + ( DispVidMode( &cnt, comm_to_print ) ) ) + comm_to_print++; + + CheckListEnd( MAX_NUM_GRAPH_MODES ); +} + + +// list all available video modes ("vid.listmodes") --------------------------- +// +void Cmd_ListVidModes() +{ + // list entries already output + comm_to_print = 0; + + // first run + int cnt = 0; + while ( ( comm_to_print < MAX_NUM_GRAPH_MODES ) && + ( DispVidMode( &cnt, comm_to_print ) ) ) + comm_to_print++; + + CheckListContinuation( MAX_NUM_GRAPH_MODES, ListVidModes_ctd ); +} + + +// display info about registered demo ----------------------------------------- +// +PRIVATE +int DispDemoInfo( int *cnt, int num ) +{ + ASSERT( cnt != NULL ); + ASSERT( ( num >= 0 ) && ( num < num_registered_demos ) ); + + if ( registered_demo_names[ num ] != NULL ) { + if ( registered_demo_titles[ num ] != NULL ) { + sprintf( paste_str, "%s (%s)", + registered_demo_names[ num ], + registered_demo_titles[ num ] ); + return PrintListLine( cnt, paste_str ); + } else { + sprintf( paste_str, "%s", registered_demo_names[ num ] ); + return PrintListLine( cnt, paste_str ); + } + } + + return 1; +} + + +// continue demo list ---------------------------------------------------------- +// +PRIVATE +void ListBinaryDemos_ctd() +{ + // continued run + int cnt = 0; + while ( ( comm_to_print < num_registered_demos ) && + ( DispDemoInfo( &cnt, comm_to_print ) ) ) + comm_to_print++; + + CheckListEnd( num_registered_demos ); +} + + +// list all registered demos ("listdemos") ------------------------------------ +// +void Cmd_ListBinaryDemos() +{ + // list entries already output + comm_to_print = 0; + + // first run + int cnt = 0; + while ( ( comm_to_print < num_registered_demos ) && + ( DispDemoInfo( &cnt, comm_to_print ) ) ) + comm_to_print++; + + CheckListContinuation( num_registered_demos, ListBinaryDemos_ctd ); +} +*/ + +// display single table line of objects table (object classes) ---------------- +// +PRIVATE +int DispObjectEntry( int *cnt, int num ) +{ + ASSERT( cnt != NULL ); + ASSERT( ( num >= 0 ) && ( num < NumLoadedObjects ) ); + + int numlines = 2; + if ( ObjectInfo[ num ].lodinfo != NULL ) { + ASSERT( ObjectInfo[ num ].lodinfo->numlods > 0 ); + numlines += ObjectInfo[ num ].lodinfo->numlods - 1; + } + + if ( CheckVerticalSpace( *cnt ) < numlines ) { + // no space to output n consecutive lines + return 0; + } + + #define TEMP_TYPE_STRING_MAX 31 + + const char *typestring = NULL; + + // scan table of object type ids to find type description + char temp_string[ TEMP_TYPE_STRING_MAX + 1 ]; + for ( int tid = 0; tid < NUM_DISTINCT_OBJTYPES; tid++ ) { + if ( objtype_id[ tid ] == ObjectInfo[ num ].type ) { + const char *tstr = objtype_name[ tid ]; + ASSERT( tstr != NULL ); + // strip leading asterisk + if ( *tstr == '*' ) + tstr++; + strncpy( temp_string, tstr, TEMP_TYPE_STRING_MAX ); + temp_string[ TEMP_TYPE_STRING_MAX ] = 0; + int tlen = strlen( temp_string ); + ASSERT( tlen > 0 ); + // strip trailing asterisk + if ( temp_string[ tlen - 1 ] == '*' ) + temp_string[ tlen - 1 ] = 0; + typestring = temp_string; + break; + } + } + + #undef TEMP_TYPE_STRING_MAX + + // try custom type if not already found + if ( typestring == NULL ) { + typestring = OBJ_FetchCustomTypeName( ObjectInfo[ num ].type ); + } + + if ( typestring != NULL ) { + sprintf( paste_str, "id %d object (%s) type %s (%u)", num, + ObjectInfo[ num ].name, typestring, + ObjectInfo[ num ].type & TYPENUMBERMASK ); + } else { + sprintf( paste_str, "id %d object (%s) type %08x_h (%u)", num, + ObjectInfo[ num ].name, (unsigned int)ObjectInfo[ num ].type, + ObjectInfo[ num ].type & TYPENUMBERMASK ); + } + + ProcessExternalLine( paste_str ); + int rc = PrintListLine( cnt, paste_str ); + ASSERT( rc ); + + if ( ObjectInfo[ num ].lodinfo == NULL ) { + + sprintf( paste_str, " %d file %s", num, ObjectInfo[ num ].file ); + ProcessExternalLine( paste_str ); + rc = PrintListLine( cnt, paste_str ); + ASSERT( rc ); + + } else { + + lodinfo_s *lodinfo = ObjectInfo[ num ].lodinfo; + for ( int lod = 0; lod < lodinfo->numlods; lod++ ) { + + if ( lod < lodinfo->numlods - 1 ) { + sprintf( paste_str, " %d file %s lodmag %f lodmin %f", num, + lodinfo->filetab[ lod ], + GEOMV_TO_FLOAT( lodinfo->lodmags[ lod ] ), + GEOMV_TO_FLOAT( lodinfo->lodmins[ lod ] ) ); + } else { + sprintf( paste_str, " %d file %s", num, + lodinfo->filetab[ lod ] ); + } + ProcessExternalLine( paste_str ); + rc = PrintListLine( cnt, paste_str ); + ASSERT( rc ); + } + } + + return rc; +} + +/* +// display single table line of textures table -------------------------------- +// +PRIVATE +int DispTextureEntry( int *cnt, int num ) +{ + ASSERT( cnt != NULL ); + ASSERT( ( num >= 0 ) && ( num < NumLoadedTextures ) ); + + if ( CheckVerticalSpace( *cnt ) < 2 ) { + // no space to output two consecutive lines + return 0; + } + + sprintf( paste_str, "id %d texture (%s) width %d height %d", num, + TextureInfo[ num ].name, + TextureInfo[ num ].width, + TextureInfo[ num ].height ); + ProcessExternalLine( paste_str ); + int rc = PrintListLine( cnt, paste_str ); + ASSERT( rc ); + + sprintf( paste_str, " %d file %s", num, TextureInfo[ num ].file ); + ProcessExternalLine( paste_str ); + + return PrintListLine( cnt, paste_str ); +} +*/ + +/* +// display single table line of bitmaps table --------------------------------- +// +PRIVATE +int DispBitmapEntry( int *cnt, int num ) +{ + ASSERT( cnt != NULL ); + ASSERT( ( num >= 0 ) && ( num < NumLoadedBitmaps ) ); + + sprintf( paste_str, "id %d bitmap (%s) width %d height %d file %s", num, + BitmapInfo[ num ].name, + BitmapInfo[ num ].width, + BitmapInfo[ num ].height, + BitmapInfo[ num ].file ); + ProcessExternalLine( paste_str ); + return PrintListLine( cnt, paste_str ); +} +*/ + +/* +// display single table line of samples table --------------------------------- +// +PRIVATE +int DispSampleEntry( int *cnt, int num ) +{ + ASSERT( cnt != NULL ); + ASSERT( ( num >= 0 ) && ( num < NumLoadedSamples ) ); + + sprintf( paste_str, "id %d sample (%s) file %s volume %d", num, + SampleInfo[ num ].name, + SampleInfo[ num ].file, + SampleInfo[ num ].volume ); + ProcessExternalLine( paste_str ); + return PrintListLine( cnt, paste_str ); +} +*/ + +// exported by PART_API.C ----------------------------------------------------- +// +extern int NumParticleDefinitions; +extern pdefref_s ParticleDefinitions[]; + +/* +// display single particle definition ----------------------------------------- +// +PRIVATE +int DispPDefEntry( int *cnt, int num ) +{ + ASSERT( cnt != NULL ); + ASSERT( ( num >= 0 ) && ( num < NumParticleDefinitions ) ); + + ASSERT( ParticleDefinitions[ num ].defname != NULL ); + ASSERT( ParticleDefinitions[ num ].def != NULL ); + pdef_s *pdef = ParticleDefinitions[ num ].def; + + sprintf( paste_str, "id %d pdef (%s) texframes %d xfoframes %d", num, + ParticleDefinitions[ num ].defname, + pdef->tex_table ? pdef->tex_end + 1 : 0, + pdef->xfo_table ? pdef->xfo_end + 1 : 0 ); + ProcessExternalLine( paste_str ); + return PrintListLine( cnt, paste_str ); +} +*/ + +/* +// display single shader ------------------------------------------------------ +// +PRIVATE +int DispShaderEntry( int *cnt, int num ) +{ + ASSERT( cnt != NULL ); + ASSERT( ( num >= 0 ) && ( num < num_registered_shaders ) ); + + shader_s *shader = ®istered_shaders[ num ]; + ASSERT( shader->name != NULL ); + + sprintf( paste_str, "id %d name (%s) shader (%s)", num, + shader->name, GetShaderDescription( num ) ); + + // display face color after shader only if no colanim defined + if ( ( shader->flags & FACE_SHADING_FACECOLOR ) && ( shader->colanim == NULL ) ) { + int endpos = strlen( paste_str ); + sprintf( paste_str + endpos, " color (%d %d %d %d)", + shader->facecolor.R, shader->facecolor.G, + shader->facecolor.B, shader->facecolor.A ); + } + + if ( shader->texanim != NULL ) { + strcat( paste_str, " texanim (" ); + + for ( int pid = 0; pid < NumParticleDefinitions; pid++ ) { + if ( shader->texanim == ParticleDefinitions[ pid ].def ) { + strcat( paste_str, ParticleDefinitions[ pid ].defname ); + break; + } + } + if ( pid == NumParticleDefinitions ) { + strcat( paste_str, "unregistered" ); + } + + strcat( paste_str, ")" ); + } + + if ( shader->colanim != NULL ) { + strcat( paste_str, " colanim (" ); + + for ( int cid = 0; cid < num_registered_colanims; cid++ ) { + if ( shader->colanim == ®istered_colanims[ cid ].colanim ) { + strcat( paste_str, registered_colanims[ cid ].name ); + break; + } + } + if ( cid == num_registered_colanims ) { + strcat( paste_str, "unregistered" ); + } + + strcat( paste_str, ")" ); + + if ( shader->flags & FACE_SHADING_FACECOLOR ) { + // display face color only if it will not be overwritten by colanim + if ( ( shader->colflags & FACE_ANIM_BASE_MASK ) != FACE_ANIM_BASEIGNORE ) { + int endpos = strlen( paste_str ); + sprintf( paste_str + endpos, " color (%d %d %d %d)", + shader->facecolor.R, shader->facecolor.G, + shader->facecolor.B, shader->facecolor.A ); + } + } else { + // if face color is disabled colanim will not be + // visible if the face is textured + strcat( paste_str, " [facecolor disabled]" ); + } + } + + ProcessExternalLine( paste_str ); + return PrintListLine( cnt, paste_str ); +} +*/ + +/* +// display single colanim ----------------------------------------------------- +// +PRIVATE +int DispColAnimEntry( int *cnt, int num ) +{ + ASSERT( cnt != NULL ); + ASSERT( ( num >= 0 ) && ( num < num_registered_colanims ) ); + + ASSERT( registered_colanims[ num ].name != NULL ); + colanim_s *colanim = ®istered_colanims[ num ].colanim; + + sprintf( paste_str, "id %d colanim (%s) coltab0 %d coltab1 %d", num, + registered_colanims[ num ].name, + colanim->col_table0 ? colanim->col_end0 + 1 : 0, + colanim->col_table1 ? colanim->col_end1 + 1 : 0 ); + ProcessExternalLine( paste_str ); + return PrintListLine( cnt, paste_str ); +} +*/ + +// display single data package ------------------------------------------------ +// +PRIVATE +int DispPackageEntry( int *cnt, int num ) +{ + ASSERT( cnt != NULL ); + ASSERT( ( num >= 0 ) && ( num < num_data_packages ) ); + + ASSERT( package_filename[ num ] != NULL ); + + sprintf( paste_str, "id %d package (%s)", num, package_filename[ num ] ); + ProcessExternalLine( paste_str ); + return PrintListLine( cnt, paste_str ); +} + + +// data table descriptors ----------------------------------------------------- +// +typedef int (*dtpf_t)(int*,int); + +struct datatable_desc_s { + + const char* tablename; + int* numentries; + dtpf_t printfunc; + int _padto_16; +}; + +static datatable_desc_s datatable_desc[] = { + + { "objects", &NumLoadedObjects, DispObjectEntry, 0 }, + //{ "textures", &NumLoadedTextures, DispTextureEntry, 0 }, + //{ "bitmaps", &NumLoadedBitmaps, DispBitmapEntry, 0 }, + //{ "samples", &NumLoadedSamples, DispSampleEntry, 0 }, + //{ "pdefs", &NumParticleDefinitions, DispPDefEntry, 0 }, + //{ "shaders", &num_registered_shaders, DispShaderEntry, 0 }, + //{ "colanims", &num_registered_colanims, DispColAnimEntry, 0 }, + { "packages", &num_data_packages, DispPackageEntry, 0 }, +}; + +#define NUM_DATATABLES CALC_NUM_ARRAY_ENTRIES( datatable_desc ) + +static int datatable_entries; +static dtpf_t datatable_entry_print; + + +// continue data table list --------------------------------------------------- +// +PRIVATE +void ListDataTable_ctd() +{ + // continued run + int cnt = 0; + while ( ( comm_to_print < datatable_entries ) && + ( datatable_entry_print( &cnt, comm_to_print ) ) ) + comm_to_print++; + + CheckListEnd( datatable_entries ); +} + + +// display list of valid data table names ------------------------------------- +// +PRIVATE +void ListValidDataTables() +{ + CON_AddLine( valid_tables_list ); + + for ( unsigned int tid = 0; tid < NUM_DATATABLES; tid++ ) { + paste_str[ 0 ] = ' '; + strcpy( paste_str + 1, datatable_desc[ tid ].tablename ); + CON_AddLine( paste_str ); + } +} + + +// list contents of data table ("listdata <tab>") ----------------------------- +// +int Cmd_ListDataTable( char *command ) +{ + ASSERT( command != NULL ); + HANDLE_COMMAND_DOMAIN_SEP( command ); + + // isolate table name + char *tabname = strtok( command, " " ); + if ( tabname == NULL ) { + CON_AddLine( table_name_syntax ); + ListValidDataTables(); + return TRUE; + } + if ( strtok( NULL, " " ) != NULL ) { + CON_AddLine( table_name_syntax ); + return TRUE; + } + unsigned int tid; + // scan valid table names + for ( tid = 0; tid < NUM_DATATABLES; tid++ ) { + if ( strcmp( datatable_desc[ tid ].tablename, tabname ) == 0 ) { + datatable_entries = *datatable_desc[ tid ].numentries; + datatable_entry_print = datatable_desc[ tid ].printfunc; + break; + } + } + + // check if table name found + if ( tid == NUM_DATATABLES ) { + CON_AddLine( table_name_invalid ); + ListValidDataTables(); + return TRUE; + } + + // list entries already output + comm_to_print = 0; + + // first run + int cnt = 0; + while ( ( comm_to_print < datatable_entries ) && + ( datatable_entry_print( &cnt, comm_to_print ) ) ) + comm_to_print++; + + CheckListContinuation( datatable_entries, ListDataTable_ctd ); + + return TRUE; +} + + +// continue command list (integer argument commands) -------------------------- +// +PRIVATE +void ListIntCommands_ctd() +{ + // continued run + int cnt = 0; + while ( ( comm_to_print < num_int_commands ) && + ( PrintFilteredCommand( &cnt, ICMSTR( comm_to_print ) ) ) ) + comm_to_print++; + + CheckListEnd( num_int_commands ); +} + + +// list all available integer argument commands ("listintcommands") ----------- +// +int Cmd_ListIntCommands( char *command ) +{ + ASSERT( command != NULL ); + HANDLE_COMMAND_DOMAIN( command ); + + char *scan = strtok( command, " " ); + if ( !InitAlphabeticStart( scan ) ) { + // recognize always (implicitly reserves domain) + return TRUE; + } + + // list entries already output + comm_to_print = 0; + + // first run + int cnt = 0; + while ( ( comm_to_print < num_int_commands ) && + ( PrintFilteredCommand( &cnt, ICMSTR( comm_to_print ) ) ) ) + comm_to_print++; + + CheckListContinuation( num_int_commands, ListIntCommands_ctd ); + + return TRUE; +} + + +// continue command list (standard commands) ---------------------------------- +// +PRIVATE +void ListStdCommands_ctd() +{ + // continued run + int cnt = 0; + while ( ( comm_to_print < num_std_commands ) && + ( PrintFilteredCommand( &cnt, CMSTR( comm_to_print ) ) ) ) + comm_to_print++; + + CheckListEnd( num_std_commands ); +} + + +// list all available standard console commands ("listcommands") -------------- +// +int Cmd_ListStdCommands( char *command ) +{ + ASSERT( command != NULL ); + HANDLE_COMMAND_DOMAIN( command ); + + char *scan = strtok( command, " " ); + if ( !InitAlphabeticStart( scan ) ) { + // recognize always (implicitly reserves domain) + return TRUE; + } + + // list entries already output + comm_to_print = 0; + + // first run + int cnt = 0; + while ( ( comm_to_print < num_std_commands ) && + ( PrintFilteredCommand( &cnt, CMSTR( comm_to_print ) ) ) ) + comm_to_print++; + + CheckListContinuation( num_std_commands, ListStdCommands_ctd ); + + return TRUE; +} + + + diff --git a/src/parsec_server/con_load_sv.cpp b/src/parsec_server/con_load_sv.cpp new file mode 100755 index 0000000..63deed9 --- /dev/null +++ b/src/parsec_server/con_load_sv.cpp @@ -0,0 +1,735 @@ +/* + * PARSEC - Data Loading Commands + * + * $Author: uberlinuxguy $ - $Date: 2004/09/15 12:25:44 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2002 + * Copyright (c) Markus Hadwiger <msh@parsec.org> 1998-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 <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "gd_heads.h" +#include "objstruc.h" + +// global externals +#include "globals.h" +//#include "e_world_trans.h" + +// subsystem headers +//#include "aud_defs.h" + +// rendering subsystem +//#include "r_patch.h" + +// local module header +#include "con_load_sv.h" + +// proprietary module headers +#include "con_arg.h" +#include "con_aux_sv.h" +#include "con_ext_sv.h" +#include "con_main_sv.h" +//#include "con_shad.h" +#include "con_std_sv.h" +//#include "e_color.h" +#include "net_limits.h" +#include "net_defs.h" +#include "e_gameserver.h" +//#include "img_api.h" +#include "obj_clas.h" +//#include "obj_ctrl.h" +#include "obj_cust.h" +#include "obj_odt.h" +#include "obj_type.h" +#include "sys_file.h" +#include "sys_swap.h" + + + +// generic string paste area +#define PASTE_STR_LEN 255 +static char paste_str[ PASTE_STR_LEN + 1 ]; + +// string constants +static char datatype_object[] = "object"; +static char datatype_texture[] = "texture"; +static char datatype_texfont[] = "texfont"; +static char datatype_bitmap[] = "bitmap"; +static char datatype_sample[] = "sample"; +static char datatype_invalid[] = "unknown data type."; +static char datatype_missing[] = "data type must be specified."; +static char name_missing[] = "name must be specified."; +static char name_invalid[] = "name invalid."; +static char name_already_used[] = "name must be unique."; +static char filename_missing[] = "filename missing."; +static char filename_invalid[] = "filename invalid."; +static char too_many_textures[] = "too many textures."; +static char too_many_texfonts[] = "too many texfonts."; +static char too_many_objects[] = "too many objects."; +static char type_missing[] = "type missing."; +static char type_invalid[] = "type invalid."; +static char type_reused[] = "type invalid. using old type."; +static char no_myship_subst[] = "cannot substitute player ship object."; +static char too_many_bitmaps[] = "too many bitmaps."; +static char too_many_samples[] = "too many samples."; +static char object_not_found[] = "object data file not found."; +static char texture_invalid_file[] = "texture file invalid or not found."; +static char bitmap_invalid_file[] = "bitmap file invalid or not found."; +static char bitmap_geom_inval[] = "bitmap geometry invalid."; +static char bitmap_geom_miss[] = "bitmap geometry missing."; +static char bitmap_geom_over[] = "geometry in file overrides specified geometry."; +static char bitmap_range_error[] = "bitmap geometry out of range."; +static char bitmap_out_of_mem[] = "not enough memory for bitmap."; +static char sample_invalid_file[] = "sample file invalid or not found."; +static char volume_invalid[] = "volume invalid."; +static char volume_range_error[] = "volume out of range."; +static char stdfreq_invalid[] = "standard frequency invalid."; +static char stereolevel_invalid[] = "stereo priority level invalid."; +static char odt_invalid_file[] = "odt file invalid."; +static char odt_invalid_overwrite[] = "invalid file used to overwrite existing object."; +static char too_many_lod_fnames[] = "stripping excessive lods."; +static char ignore_lodmags[] = "ignoring lodmag spec."; +static char ignore_lodmins[] = "ignoring lodmin spec."; +static char invalid_lodmags[] = "lodmag spec missing or invalid."; +static char invalid_lodmins[] = "lodmin spec missing or invalid."; + + + +// set object's name ---------------------------------------------------------- +// +INLINE +void SetObjectName( int idx, char *name ) +{ + ASSERT( (dword)idx < MAX_DISTINCT_OBJCLASSES ); + ASSERT( name != NULL ); + + if ( ObjectInfo[ idx ].name != NULL ) + FREEMEM( ObjectInfo[ idx ].name ); + ObjectInfo[ idx ].name = (char *) ALLOCMEM( strlen( name ) + 1 ); + if ( ObjectInfo[ idx ].name == NULL ) + OUTOFMEM( 0 ); + strcpy( ObjectInfo[ idx ].name, name ); +} + + +// set object's file name ----------------------------------------------------- +// +INLINE +void SetObjectFilename( int idx, char *fname ) +{ + ASSERT( (dword)idx < MAX_DISTINCT_OBJCLASSES ); + ASSERT( fname != NULL ); + + if ( ObjectInfo[ idx ].file != NULL ) + FREEMEM( ObjectInfo[ idx ].file ); + ObjectInfo[ idx ].file = (char *) ALLOCMEM( strlen( fname ) + 1 ); + if ( ObjectInfo[ idx ].file == NULL ) + OUTOFMEM( 0 ); + strcpy( ObjectInfo[ idx ].file, fname ); +} + + +// make error message including the file name that failed --------------------- +// +PRIVATE +char *MakeFileNameMsg( char *msg, char *fname ) +{ + ASSERT( msg != NULL ); + ASSERT( fname != NULL ); + + strcpy( paste_str, msg ); + strcat( paste_str, " (" ); + strcat( paste_str, fname ); + strcat( paste_str, ")" ); + + ProcessExternalLine( paste_str ); + + return paste_str; +} + + +// temporary tables to store info about object lods --------------------------- +// +static int object_lod_numlods; +static char* object_lod_fnames[ MAX_OBJECT_LODS ]; +static char* object_lod_fname_storage = NULL; +static float object_lod_mags[ MAX_OBJECT_LODS ]; +static float object_lod_mins[ MAX_OBJECT_LODS ]; + + +// read object from odt file -------------------------------------------------- +// +PRIVATE +int LoadObjectFromODT( dword objtypeid, int insertindex, dword flags, shader_s *shader ) +{ + ASSERT( (dword)object_lod_numlods <= MAX_OBJECT_LODS ); + ASSERT( (dword)insertindex < MAX_DISTINCT_OBJCLASSES ); + ASSERT( object_lod_numlods > 0 ); + + // set object type + ObjectInfo[ insertindex ].type = objtypeid; + + // enter (base) filename into table + SetObjectFilename( insertindex, object_lod_fnames[ 0 ] ); + + // free old lod table + if ( ObjectInfo[ insertindex ].lodinfo != NULL ) { + FREEMEM( ObjectInfo[ insertindex ].lodinfo ); + ObjectInfo[ insertindex ].lodinfo = NULL; + } + + if ( object_lod_numlods > 1 ) { + int lod; + // alloc and init lodinfo table + size_t nsize = 0; + for ( lod = 0; lod < object_lod_numlods; lod++ ) + nsize += strlen( object_lod_fnames[ lod ] ) + 1; + lodinfo_s *lodinfo = (lodinfo_s *) ALLOCMEM( sizeof( lodinfo_s ) + nsize + + object_lod_numlods * ( sizeof( char* ) + sizeof( geomv_t ) * 2 ) ); + if ( lodinfo == NULL ) { + OUTOFMEM( 0 ); + } + + lodinfo->numlods = object_lod_numlods; + lodinfo->filetab = (char **) ( (char*)lodinfo + sizeof( lodinfo_s ) ); + lodinfo->lodmags = (geomv_t *) ( (char*)lodinfo->filetab + object_lod_numlods * sizeof( char* ) + nsize ); + lodinfo->lodmins = (geomv_t *) ( (char*)lodinfo->lodmags + object_lod_numlods * sizeof( geomv_t ) ); + + char *fnamestore = (char *) ( (char*)lodinfo->filetab + object_lod_numlods * sizeof( char* ) ); + for ( lod = 0; lod < object_lod_numlods; lod++ ) { + + lodinfo->filetab[ lod ] = fnamestore; + strcpy( fnamestore, object_lod_fnames[ lod ] ); + fnamestore += strlen( object_lod_fnames[ lod ] ) + 1; + + if ( lod < object_lod_numlods - 1 ) { + lodinfo->lodmags[ lod ] = FLOAT_TO_GEOMV( object_lod_mags[ lod ] ); + lodinfo->lodmins[ lod ] = FLOAT_TO_GEOMV( object_lod_mins[ lod ] ); + } else { + lodinfo->lodmags[ lod ] = GEOMV_0; + lodinfo->lodmins[ lod ] = GEOMV_0; + } + } + + // attach lodinfo table + ObjectInfo[ insertindex ].lodinfo = lodinfo; + } + + // actually load data and create object + return OBJ_LoadODT( insertindex, flags, shader ); +} + + +// determine lod spec for object, fill tables accordingly --------------------- +// +PRIVATE +int DetermineODTLodSpec( char *fname, key_value_s *lodmags, key_value_s *lodmins ) +{ + ASSERT( fname != NULL ); + ASSERT( lodmags != NULL ); + ASSERT( lodmins != NULL ); + + // parse filename, determine lods + int numlods = 0; + char *fdest = object_lod_fname_storage; + ASSERT( fdest != NULL ); + + for ( char *fscan = fname; ; ) { + + // strip whitespace + while ( *fscan == ' ' ) + fscan++; + fname = fscan; + if ( *fscan == 0 ) + break; + while ( ( *fscan != ' ' ) && ( *fscan != 0 ) ) + fscan++; + + // avoid overflowing tables + if ( numlods >= MAX_OBJECT_LODS ) { + CON_AddLine( too_many_lod_fnames ); + break; + } + + int last = ( *fscan == 0 ); + *fscan++ = 0; + + // process file name and use current workdir + fname = SubstCurWorkDir( fname ); + + int len = strlen( fname ); + if ( ( len < 5 ) || ( fname[ len - 4 ] != '.' ) ) { + CON_AddLine( MakeFileNameMsg( filename_invalid, fname ) ); + return FALSE; + } + + // try to find odt file + FILE *fp = SYS_fopen( fname, "rb" ); + if ( fp == NULL ) { + CON_AddLine( MakeFileNameMsg( object_not_found, fname ) ); + return FALSE; + } + SYS_fclose( fp ); + + //NOTE: + // this check is necessary to prevent OBJ_LoadODT() from + // exiting with an error message to the shell later on if + // the object file is simply missing. + + // store lod file name + strcpy( fdest, fname ); + object_lod_fnames[ numlods++ ] = fdest; + fdest += strlen( fname ) + 1; + + if ( last ) { + break; + } + } + + if ( numlods < 1 ) { + CON_AddLine( filename_missing ); + return FALSE; + } + + // set temporary global + object_lod_numlods = numlods; + + if ( numlods == 1 ) { + if ( lodmags->value != NULL ) + CON_AddLine( ignore_lodmags ); + if ( lodmins->value != NULL ) + CON_AddLine( ignore_lodmins ); + return TRUE; + } + + // thresholds are between lods + int numthresh = numlods - 1; + + // parse lod mags and mins + if ( ScanKeyValueFloatList( lodmags, object_lod_mags, numthresh, numthresh ) != numthresh ) { + CON_AddLine( invalid_lodmags ); + return FALSE; + } + if ( ScanKeyValueFloatList( lodmins, object_lod_mins, numthresh, numthresh ) != numthresh ) { + CON_AddLine( invalid_lodmins ); + return FALSE; + } + + return TRUE; +} + + +// flag map for objload_xx flags ---------------------------------------------- +// +flag_map_s objload_flag_map[] = { + + { "nodefaults", OBJLOAD_NONE, 0 }, + { "w_normals", OBJLOAD_WEDGENORMALS, 0 }, + { "w_colors", OBJLOAD_WEDGECOLORS, 0 }, + { "w_texcoords", OBJLOAD_WEDGETEXCOORDS, 0 }, + { "w_lighted", OBJLOAD_WEDGELIGHTED, 0 }, + { "w_specular", OBJLOAD_WEDGESPECULAR, 0 }, + { "w_fogged", OBJLOAD_WEDGEFOGGED, 0 }, + { "f_anims", OBJLOAD_FACEANIMS, 0 }, + { "v_anims", OBJLOAD_VTXANIMS, 0 }, + { "p_colors", OBJLOAD_POLYCORNERCOLORS, 0 }, + { "p_wedges", OBJLOAD_POLYWEDGEINDEXES, 0 }, + + { NULL, 0, 0 }, +}; + + +// parse objload_xx flags for object ------------------------------------------ +// +PRIVATE +dword DetermineODTFlags( key_value_s *flagspec ) +{ + ASSERT( flagspec != NULL ); + + dword objloadflags = OBJLOAD_DEFAULT; + + if ( flagspec->value != NULL ) { + + // parse flag list, set corresponding bits + objloadflags = OBJLOAD_NONE; + if ( ScanKeyValueFlagList( flagspec, &objloadflags, objload_flag_map ) == 0 ) { + objloadflags = OBJLOAD_DEFAULT; + } + } + + return objloadflags; +} + + +// key table for object loading ----------------------------------------------- +// +key_value_s object_key_value[] = { + + { "file", NULL, KEYVALFLAG_PARENTHESIZE }, + { "type", NULL, KEYVALFLAG_NONE }, + { "flags", NULL, KEYVALFLAG_PARENTHESIZE }, + { "lodmag", NULL, KEYVALFLAG_PARENTHESIZE }, + { "lodmin", NULL, KEYVALFLAG_PARENTHESIZE }, + { "shader", NULL, KEYVALFLAG_PARENTHESIZE }, + { "normth", NULL, KEYVALFLAG_NONE }, + { "maxfanims", NULL, KEYVALFLAG_NONE }, + { "maxvanims", NULL, KEYVALFLAG_NONE }, + + { NULL, NULL, KEYVALFLAG_NONE }, +}; + +enum { + + KEY_OBJ_FILE, + KEY_OBJ_TYPE, + KEY_OBJ_FLAGS, + KEY_OBJ_LODMAG, + KEY_OBJ_LODMIN, + KEY_OBJ_SHADER, + KEY_OBJ_NORMTH, + KEY_OBJ_MAXFANIMS, + KEY_OBJ_MAXVANIMS +}; + + +// load object data file from console ----------------------------------------- +// +PRIVATE +int ConLoadObject( char *name ) +{ + ASSERT( name != NULL ); + + // check if object slot available + if ( NumLoadedObjects >= MAX_DISTINCT_OBJCLASSES - 1 ) { + CON_AddLine( too_many_objects ); + return FALSE; + } + + // check if object of same name already exists + int insertindex = NumLoadedObjects; + for ( int oid = 0; oid < NumLoadedObjects; oid++ ) { + if ( stricmp( ObjectInfo[ oid ].name, name ) == 0 ) { + if ( AUX_ENABLE_OBJECT_OVERLOADING ) { + insertindex = oid; + break; + } else { + CON_AddLine( name_already_used ); + return FALSE; + } + } + } + + // set flag if new object should be appended + int newobject = ( insertindex == NumLoadedObjects ); + + // scan out all values to texture keys + if ( !ScanKeyValuePairs( object_key_value, NULL ) ) + return FALSE; + + // fetch file name/lod file name list + char *fname = object_key_value[ KEY_OBJ_FILE ].value; + if ( fname == NULL ) { + CON_AddLine( filename_missing ); + return FALSE; + } + + // alloc tempmem for lod object file names + object_lod_fname_storage = (char *) ALLOCMEM( MAX_OBJECT_LODS * PATH_MAX ); + if ( object_lod_fname_storage == NULL ) + OUTOFMEM( 0 ); + + // parse lod spec (filenames, mags, mins) + if ( !DetermineODTLodSpec( fname, + &object_key_value[ KEY_OBJ_LODMAG ], + &object_key_value[ KEY_OBJ_LODMIN ] ) ) { + + return FALSE; + } + + // parse flags + dword objloadflags = DetermineODTFlags( &object_key_value[ KEY_OBJ_FLAGS ] ); + + // parse shader + //shader_s *shader = DetermineODTShader( &object_key_value[ KEY_OBJ_SHADER ] ); + shader_s *shader = NULL; + + // determine object's type id and prepare object loading + dword objtypeid; + if ( newobject ) { + + // fetch type specifier + char *typestr = object_key_value[ KEY_OBJ_TYPE ].value; + if ( typestr == NULL ) { + CON_AddLine( type_missing ); + return FALSE; + } + + // translate type specifier into id + objtypeid = OBJ_FetchTypeIdFromName( typestr ); + + // check for invalid type + if ( objtypeid == TYPE_ID_INVALID ) { + CON_AddLine( type_invalid ); + return FALSE; + } + + // enter new object name into table + SetObjectName( insertindex, name ); + + } else { + +#ifdef PARSEC_CLIENT + // prevent elimination of local player's ship by disallowing + // object substitution of player's ship class + if ( MyShip->ObjectClass == insertindex ) { + // allow it on startup, though + if ( !TextModeActive ) { + CON_AddLine( no_myship_subst ); + return FALSE; + } + } +#endif // PARSEC_CLIENT + + // check for optional type specifier + char *typestr = object_key_value[ KEY_OBJ_TYPE ].value; + if ( typestr != NULL ) { + + // translate type specifier into id + objtypeid = OBJ_FetchTypeIdFromName( typestr ); + + // check for invalid type + if ( objtypeid == TYPE_ID_INVALID ) { + CON_AddLine( type_reused ); + objtypeid = ObjectInfo[ insertindex ].type; + } + + // LoadObjectFromODT() also sets this + ObjectInfo[ insertindex ].type = objtypeid; + + } else { + + // use type of object that is being overwritten + objtypeid = ObjectInfo[ insertindex ].type; + } + + // kill all instances of previous object + int killcount = TheWorld->KillClassInstances( insertindex ); + if ( killcount > 0 ) { + sprintf( paste_str, "number of removed class instances: %d.", killcount ); + CON_AddLine( paste_str ); + } + + // free previous object's data + FREEMEM( TheWorld->ObjClasses[ insertindex ] ); + TheWorld->ObjClasses[ insertindex ] = NULL; + } + + // save current loading params + odt_loading_params_s oldloadingparams = odt_loading_params; + + // change normal merging threshold if specified + if ( object_key_value[ KEY_OBJ_NORMTH ].value != NULL ) { + float fthresh; + if ( ScanKeyValueFloat( &object_key_value[ KEY_OBJ_NORMTH ], &fthresh ) == 1 ) { + odt_loading_params.merge_normals_threshold = FLOAT_TO_GEOMV( fthresh ); + } + } + + // change maximum number of face anim states if specified + if ( object_key_value[ KEY_OBJ_MAXFANIMS ].value != NULL ) { + int maxfanims; + if ( ScanKeyValueInt( &object_key_value[ KEY_OBJ_MAXFANIMS ], &maxfanims ) == 1 ) { + odt_loading_params.max_face_anim_states = maxfanims; + } + } + + // change maximum number of vertex anim states if specified + if ( object_key_value[ KEY_OBJ_MAXVANIMS ].value != NULL ) { + int maxvanims; + if ( ScanKeyValueInt( &object_key_value[ KEY_OBJ_MAXVANIMS ], &maxvanims ) == 1 ) { + odt_loading_params.max_vtx_anim_states = maxvanims; + } + } + + // load data + int rc = LoadObjectFromODT( objtypeid, insertindex, objloadflags, shader ); + + // restore loading params + odt_loading_params = oldloadingparams; + + if ( rc <= 0 ) { + + //NOTE: + // reaction to load error depends on whether a new + // object should have been created or an already + // existing substituted. if the latter is the case + // the old object has already been killed and there + // is no valid one to use. so, a not-so-graceful + // program exit is performed. + + if ( newobject ) { + CON_AddLine( MakeFileNameMsg( odt_invalid_file, object_lod_fnames[ -rc ] ) ); + return FALSE; + } else { + PANIC( MakeFileNameMsg( odt_invalid_overwrite, object_lod_fnames[ -rc ] ) ); + } + } + + // free tempmem for lod file names + FREEMEM( object_lod_fname_storage ); + object_lod_fname_storage = NULL; + + if ( newobject ) { + + // increment number of loaded objects + NumLoadedObjects++; + + // display assigned id + sprintf( paste_str, "assigned object class id: %d.", insertindex ); + CON_AddLine( paste_str ); + + } else { + + // display id of overwritten class + sprintf( paste_str, "overwrote object class id: %d.", insertindex ); + CON_AddLine( paste_str ); + } + + // set global number of object classes + NumObjClasses = NumLoadedObjects; + + return TRUE; +} + + + + + + +// main entry point for "load" command (load data via console) ---------------- +// +int Cmd_ExecLoadCommand( char *command ) +{ + if(TheServer->GetServerIsMaster()){ + return false; + } + //NOTE: + //CONCOM: + // load_command ::= 'load' <data_type> <name> [<key> <value>]* + // data_type ::= 'object' | 'texture' | 'texfont' | 'bitmap' | 'sample' + // name ::= "may be parenthesized () to include whitespace" + // key ::= "valid keys depend on data type" + + ASSERT( command != NULL ); + HANDLE_COMMAND_DOMAIN_SEP( command ); + + // split off first token (data type specifier) + char *datatype = strtok( command, " " ); + if ( datatype == NULL ) { + CON_AddLine( datatype_missing ); + return TRUE; + } + +#ifdef PARSEC_CLIENT + // this flag is used for internal data reloading + if ( AUX_ALLOW_ONLY_TEXTURE_LOADING ) { + // disallow everything but textures + if ( strcmp( datatype, datatype_texture ) != 0 ) { + return TRUE; + } + } +#endif // PARSEC_CLIENT + + // create pointer to list of parameters (first is always the name) + char *name = strtok( NULL, " " ); + if ( name == NULL ) { + CON_AddLine( name_missing ); + return TRUE; + } + + // allow name to be parenthesized to include whitespace + name = GetParenthesizedName( name ); + if ( name == NULL ) { + CON_AddLine( name_invalid ); + return TRUE; + } + + +#ifdef PARSEC_CLIENT + + // dispatch to functions for handling different data types + if ( strcmp( datatype, datatype_object ) == 0 ) + ConLoadObject( name ); + else if ( strcmp( datatype, datatype_texture ) == 0 ) + ConLoadTexture( name ); + else if ( strcmp( datatype, datatype_texfont ) == 0 ) + ConLoadTexfont( name ); + else if ( strcmp( datatype, datatype_bitmap ) == 0 ) + ConLoadBitmap( name ); + else if ( strcmp( datatype, datatype_sample ) == 0 ) + ConLoadSample( name ); + else + CON_AddLine( datatype_invalid ); + +#else // !PARSEC_CLIENT + + // dispatch to functions for handling different data types + if ( strcmp( datatype, datatype_object ) == 0 ) + ConLoadObject( name ); + else if ( strcmp( datatype, datatype_texture ) == 0 ) + CON_AddLine( "ignoring textures." ); + else if ( strcmp( datatype, datatype_texfont ) == 0 ) + CON_AddLine( "ignoring textfonts." ); + else if ( strcmp( datatype, datatype_bitmap ) == 0 ) + CON_AddLine( "ignoring bitmaps." ); + else if ( strcmp( datatype, datatype_sample ) == 0 ) + CON_AddLine( "ignoring samples." ); + else + CON_AddLine( datatype_invalid ); + +#endif // PARSEC_CLIENT + + // print dot for every loaded item in text-mode + // (well, also if one of the above routines fails...) + //if ( AUX_CMD_WRITE_ACTIVE_IN_TEXTMODE ) { + MSGPUT( "." ); + //} + return TRUE; +} + + +// module registration function ----------------------------------------------- +// +REGISTER_MODULE( CON_LOAD_SV ) +{ +} + + + diff --git a/src/parsec_server/con_main_sv.cpp b/src/parsec_server/con_main_sv.cpp new file mode 100755 index 0000000..4de9331 --- /dev/null +++ b/src/parsec_server/con_main_sv.cpp @@ -0,0 +1,1586 @@ +/* + * PARSEC - Server Console Main Module + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:47 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2001 + * Copyright (c) Markus Hadwiger <msh@parsec.org> 1996-2000 + * + * 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 <stdlib.h> +#include <string.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "gd_heads.h" +#include "objstruc.h" + +// global externals +#include "globals.h" + +// subsystem headers +#include "inp_defs_sv.h" +//#include "vid_defs.h" + +// drawing subsystem +//#include "d_bmap.h" +//#include "d_font.h" + +// local module header +#include "con_main_sv.h" + +// proprietary module headers +#include "con_act_sv.h" +#include "con_arg.h" +#include "con_aux_sv.h" +#include "con_com_sv.h" +//#include "con_conf.h" +#include "con_ext_sv.h" +#include "con_info_sv.h" +#include "con_int_sv.h" +//#include "con_kmap.h" +#include "con_list_sv.h" +//#include "con_rc.h" +#include "con_std_sv.h" +#include "con_tab.h" +#include "con_vald.h" +//#include "e_color.h" +//#include "e_demo.h" +//#include "e_draw.h" +#include "net_limits.h" +#include "net_defs.h" +#include "e_gameserver.h" +//#include "g_supp.h" +//#include "h_cockpt.h" +//#include "h_strmap.h" +#include "inp_main_sv.h" +#include "keycodes.h" +//#include "m_main.h" +#include "sys_path.h" +#include "sys_swap.h" + +// CURSES library ------------------------------------------------------------- +#ifdef USE_CURSES + #ifdef SYSTEM_WIN32_UNUSED + #pragma comment( lib, "curses.lib" ) + #endif // SYSTEM_WIN32_UNUSED +#endif // USE_CURSES + +// flags +#define PROCESS_STARTUP_SCRIPT // execute startup (boot) script +#define FORCE_CONSOLE_LOGIN // have to login to exec commands +#define LOSE_KEYBOARD_FOCUS_ON_SLIDEUP // console loses focus on slide up +#define CONSOLE_OUTPUT_WORKAROUND + + +// echo commands in startup script +#define ECHO_STARTUP_SCRIPT TRUE // echo+history for startup script + +// speed of console sliding +#define CONSOLE_SLIDE_SPEED 3 // less is faster + +// miscellaneous defines +#define CONSOLE_MIN_ENTER_LENGTH 4 // lower bound for entry area +#define CURSOR_BLINK_SPEED 0x100 // in reference frames +#define CONSOLE_CAPTION_HEIGHT 2 // caption plus one empty line +#define CONSOLE_MAX_BACK_LINES 128 // number of lines to scroll back + + +// macros can be disabled to prevent overriding of internal commands +static int disable_macros = FALSE; + +// flag whether script compilation in progress +PUBLIC int compile_script = FALSE; + +// filepointer for compilation output +PUBLIC FILE *compile_fp = NULL; + +// echo commands executed from file (script) +PUBLIC int EchoExternalCommands = FALSE; + +// enter script commands into history list +PUBLIC int HistoryForBatchEntry = TRUE; + +// startup script file names (normal boot/demo boot) +PUBLIC char boot_script_name[] = "boot_sv" CON_FILE_EXTENSION; +PUBLIC char boot_script_master_name[] = "boot_ms" CON_FILE_EXTENSION; +static char demo_script_name[] = "demo_sv" CON_FILE_EXTENSION; + +// various string constants +PUBLIC char con_prompt[] = "::"; +PUBLIC char ok_prompt[] = "ok."; +PUBLIC char press_space[] = "- press space -"; +PUBLIC char too_many_args[] = "too many arguments."; +PUBLIC char arg_missing[] = "argument missing."; +PUBLIC char invalid_arg[] = "invalid argument."; +PUBLIC char range_error[] = "range error."; +PUBLIC char unknown_command[] = "unknown command: \"%s\""; +PUBLIC char unknown_script[] = "unknown command script."; +PUBLIC char cmd_not_compilable[] = "command cannot be compiled."; +PUBLIC char line_separator[] = "----------------------------------"; +static char console_login_passwd[] = "argument."; +static char recursive_compilation[] = "recursive compilation... rather not."; +static char compile_open_failed[] = "cannot open output file."; + +PUBLIC int press_space_curs_x = strlen( press_space ) - PROMPT_SIZE; + +static char console_caption[] = "parsec command console"; +static char console_ready[] = "console ready..."; + +static char auxflag_array_name[] = ".aux"; +static char auxdata_array_name[] = ".auxd"; + +static char insert_cursor[] = "_"; // will be a underline cursor +static char overwrite_cursor[] = "^"; // will be a block cursor + +static char console_lock_done[] = "console is now locked."; +static char console_unlock_done[] = "console is now unlocked."; +static char console_is_locked[] = "console locked."; + +// generic string paste area +#define PASTE_STR_LEN 255 +static char paste_str[ PASTE_STR_LEN + 1 ]; + +#if ( PASTE_STR_LEN < MAX_CONSOLE_LINE_LENGTH ) + #error "CON_MAIN::paste_str too short!" +#endif + +// console geometry +PRIVATE int console_frameofs_x = 4; +PRIVATE int console_frameofs_y = 0; +PRIVATE int console_frame_left = 8; +PRIVATE int console_frame_top = 5; + +// current console contents +PUBLIC char con_lines[ NUM_CONSOLE_LINES ][ MAX_CONSOLE_LINE_LENGTH + 1 ]; + +// temporary line from which actual drawing will be done +static char draw_line[ MAX_CONSOLE_LINE_LENGTH + 1 ]; + +// temporary line for current command (from console input line or file) +static char command_line[ MAX_CONSOLE_LINE_LENGTH + 1 ]; + +// history buffer (most recent commands history) +static char history_list[ NUM_HISTORY_LINES ][ MAX_CONSOLE_LINE_LENGTH + 1 ]; + +static int history_add = 0; // new entry will be placed here +static int history_scan = 0; // current scroll position in list + +static int insert_mode = TRUE; // default mode: insert + +PUBLIC int con_back_view= 0; // number of lines to view back in console + +PUBLIC int con_content = 1; // valid lines in console buffer +PUBLIC int con_bottom = 0; // current line in console buffer (0..) + +static int last_con_content; // old state +static int last_con_bottom; // (used in lists to overwrite space prompt) + +static int cursor_state = CURSOR_BLINK_SPEED; +PUBLIC int cursor_x = 0; // current cursor position (0==behind prompt) + +static int edit_ofs_x = 0; // x offset for viewing the edit line +static int edit_max_x = MAX_CONSOLE_LINE_LENGTH - PROMPT_SIZE; + +int con_in_talk_mode = FALSE; +int con_talk_line = 0; // line just below separator in talk mode +char talk_escape_char = '/'; // escape char for command input in talk mode + +int con_non_interactive = 0; // CON_AddMessage() instead of CON_AddLine() + +int con_logged_in = 1; // user logged in +int con_login_mode = 0; // login mode (must not display console input) +int con_no_login_check = 0; // used by keymapped commands + +int ConsoleEnterLength = 23; // length of console line (excl. prompt+cursor) +int ConsoleTextX = 3; // x-pos of console in char-multiples +int ConsoleTextY = 7; // y-pos of console in char-multiples + +// execute init only once +int console_init_done = FALSE; + +// scratchpad for path with current working directory prepended +static char curworkdir_path[ PATH_MAX + 1 ]; + + +// CURSES library ------------------------------------------------------------- +// +#ifdef USE_CURSES + WINDOW* g_curses_in_win = NULL; + WINDOW* g_curses_out_win = NULL; +#endif // USE_CURSES + + +// completely erase (fill with zero-bytes) all console lines ------------------ +// +void ClearConScreen() +{ + // clear all lines and make them valid + memset( con_lines, 0, sizeof( con_lines ) ); + con_content = NUM_CONSOLE_LINES; + + // reset back-viewing + con_back_view = 0; +} + + +// completely erase (fill with zero-bytes) an entire console line ------------- +// +void EraseConLine( int line ) +{ + ASSERT( ( line >= 0 ) && ( line < NUM_CONSOLE_LINES ) ); + memset( con_lines[ line ], 0, MAX_CONSOLE_LINE_LENGTH + 1 ); + wclear( g_curses_in_win ); + cursor_x = 0; +} + + +// fill current line with separator chars ------------------------------------- +// +void CreateSeparatorLine( int line ) +{ + ASSERT( ( line >= 0 ) && ( line < NUM_CONSOLE_LINES ) ); + memset( con_lines[ line ], '-', MAX_CONSOLE_LINE_LENGTH ); + con_lines[ line ][ MAX_CONSOLE_LINE_LENGTH ] = 0; +} + + +// return path with current workdir prepended if it starts with "./" ---------- +// +char *SubstCurWorkDir( char *path ) +{ + ASSERT( path != NULL ); + + //NOTE: + // a copy of the path is returned even if it + // has not been altered to achieve the exact + // same semantics as in case of modification. + + //NOTE: + // this function recognizes ".:" as alternative specifier for + // the workdir in order to allow MacOS path specifications in + // console script paths. + + if ( ( strncmp( path, "./", 2 ) == 0 ) || + ( strncmp( path, ".:", 2 ) == 0 ) ) { + + if ( AUX_FORCE_WORKDIR_TO_CURRENT ) { + + // just skip first two chars + strncpy( curworkdir_path, path + 2, PATH_MAX ); + curworkdir_path[ PATH_MAX ] = 0; + + } else { + + // substitute current working directory for "." + strcpy( curworkdir_path, CurWorkDir ); + int cwdlen = strlen( CurWorkDir ); + strncpy( curworkdir_path + cwdlen, path + 1, PATH_MAX - cwdlen ); + curworkdir_path[ PATH_MAX ] = 0; + } + + } else { + + // just copy over unmodified path + strncpy( curworkdir_path, path, PATH_MAX ); + curworkdir_path[ PATH_MAX ] = 0; + } + + // convert path to system format + path = SYSs_ProcessPathString( curworkdir_path ); + if ( path != curworkdir_path ) { + strncpy( curworkdir_path, path, PATH_MAX ); + curworkdir_path[ PATH_MAX ] = 0; + } + + // strip path if flag set + char *substpath = curworkdir_path; + if ( AUX_FORCE_FILEPATH_TO_CURRENT ) + substpath = SYSs_StripPath( curworkdir_path ); + + return substpath; +} + + +// exec startup script (usually boot.con, but also boot scripts of mods) ------ +// +void ExecStartupScript( int echo ) +{ + // exec startup command script + char *startupscript = NULL; + + // start normal or mod boot scripts + if ( mod_numnames > 0 ) { + + ASSERT( mod_names[ 0 ] != NULL ); + + // if mod doesn't override our own packages, execute our boot.con too + if ( !mod_override ) { + //startupscript = PlayDemo ? demo_script_name : boot_script_name; + if(TheServer->GetServerIsMaster()){ + startupscript = boot_script_master_name; + } else { + startupscript = boot_script_name; + } + ExecConsoleFile( startupscript, echo ); + } + + // afterwards, exec <modname>/boot.con for each registered mod + for ( int curmod = 0; curmod < mod_numnames; curmod++ ) { + + ASSERT( mod_names[ curmod ] != NULL ); + startupscript = (char *) ALLOCMEM( + strlen( mod_names[ curmod ] ) + 1 + strlen( boot_script_name ) ); + if ( startupscript == NULL ) { + OUTOFMEM( 0 ); + } + strcpy( startupscript, mod_names[ curmod ] ); + strcat( startupscript, "/" ); + strcat( startupscript, boot_script_name ); + + // must be done to ensure the file can be found independently of + // whether it is read from a package or from a real directory + char *path = SYSs_ProcessPathString( startupscript ); + + ExecConsoleFile( path, echo ); + + FREEMEM( startupscript ); + startupscript = NULL; + } + + } else { + + // if no mod is active, we just exec boot.con + //startupscript = PlayDemo ? demo_script_name : boot_script_name; + if(TheServer->GetServerIsMaster()){ + startupscript = boot_script_master_name; + } else { + startupscript = boot_script_name; + } + ExecConsoleFile( startupscript, echo ); + } +} + +// init CURSES windows -------------------------------------------------------- +// +void CON_InitCurses() +{ +#ifdef USE_CURSES + + initscr(); + cbreak(); + //echo(); + + + // create input window + g_curses_in_win = newwin( 1, COLS, LINES - 1, 0 ); + //wattrset( g_curses_in_win, A_REVERSE ); + keypad( g_curses_in_win, TRUE ); + nodelay( g_curses_in_win, TRUE ); + + // create output window ( -1 to ensure we do not overwrite the input window ) + g_curses_out_win = newwin( LINES - 1, COLS, 0, 0 ); + nodelay( g_curses_out_win, TRUE ); + + // scrolling is ok + scrollok( g_curses_in_win, TRUE ); + scrollok( g_curses_out_win, TRUE ); + + //slk_init( 1 ); + /*slk_set(1, "Insert", 0); + slk_set(2, "Quit", 0); + slk_set(3, "Add", 0); + slk_set(4, "Delete", 0); + slk_set(5, "Undo", 0); + slk_set(6, "Search", 0); + slk_set(7, "Replace", 0); + slk_set(8, "Save", 0); + slk_refresh(); + */ + + +#endif // USE_CURSES +} + + +// kill all CURSES windows and related stuff ---------------------------------- +// +void CON_KillCurses() +{ +#ifdef USE_CURSES + nocbreak(); + echo(); + endwin(); +#endif // USE_CURSES +} + + +// init memory needed for console --------------------------------------------- +// +void CON_InitConsole() +{ + ASSERT( !console_init_done ); + + if ( !console_init_done ) { + int lid; + // erase console contents + for ( lid = 0; lid < NUM_CONSOLE_LINES; lid++ ) { + memset( con_lines[ lid ], 0, MAX_CONSOLE_LINE_LENGTH + 1 ); + } + + // erase history buffer + for ( lid = 0; lid < NUM_HISTORY_LINES; lid++ ) { + history_list[ lid ][ 0 ] = '\0'; + } + + // insert startup message into top line + strcpy( con_lines[ 0 ], console_ready ); + + // make startup message valid + con_content = 1; + con_bottom = 0; + + EraseConLine( con_bottom ); + strcpy( con_lines[ con_bottom ], con_prompt ); + +#ifdef PROCESS_STARTUP_SCRIPT + + // there's no reason to run the external scripts when we are only + // running in master server mode. + if(!TheServer->GetServerIsMaster()) { + // build list of external commands + BuildExternalCommandList(); + + // exec boot.con (also for registered mods) + ExecStartupScript( ECHO_STARTUP_SCRIPT ); + } +#endif // PROCESS_STARTUP_SCRIPT + + //CAVEAT: + // this has to stay last, because CON_EXT::ExecConsoleFile() + // uses it to determine whether it has been called from here. + console_init_done = TRUE; + } +} + + +// make sure no demo is playing when console is inited/reinited/killed -------- +// +PRIVATE +void KillDemoReplay() +{ +//FIXME: needed on server ? + +/* // make sure we call demo stop instead of full clear + int oldclearflag = AUX_DISABLE_CLEARDEMO_ON_REPSTOP; + AUX_DISABLE_CLEARDEMO_ON_REPSTOP = 1; + + // make sure no demo is playing + //DEMO_StopReplay(); + + // restore previous flag state + AUX_DISABLE_CLEARDEMO_ON_REPSTOP = oldclearflag; +*/ +} + + +// (re)init file pointers used by console ------------------------------------- +// +void InitConsoleFileHandling() +{ + // make sure no demo is playing + KillDemoReplay(); + + if ( log_commands ) { + fclose( log_fp ); + log_commands = 0; + } + + if ( RecordingActive ) { + fclose( RecordingFp ); + RecordingActive = 0; + RemoteRecSessionId++; + } +} + + +// kill resources used by console --------------------------------------------- +// +void CON_KillConsole() +{ + if ( console_init_done ) { + console_init_done = FALSE; + + InitConsoleFileHandling(); + } +} + + +// flush keyboard buffer for console ------------------------------------------ +// +void FlushConsoleBuffer() +{ + dword *pos; + + while ( *( pos = &KeybBuffer->Data + KeybBuffer->ReadPos ) ) { + KeybBuffer->ReadPos = ( KeybBuffer->ReadPos + 1 ) % KEYB_BUFF_SIZ; + *pos = 0; + } +} + + +// add new message to console (preserve current input) ------------------------ +// +PUBLIC +void CON_AddMessage( const char *text ) +{ + ASSERT( text != NULL ); + + // normal message output + MSGOUT( text ); +} + + +// flag whether next CON_AddLine() should overwrite the current line ---------- +// +static int skip_next_nl = FALSE; + + +// add new line to console ---------------------------------------------------- +// +PUBLIC +void CON_AddLine( const char *text ) +{ + ASSERT( text != NULL ); + + // normal text output + MSGOUT( text ); +} + + +// add line feed in console --------------------------------------------------- +// +PUBLIC +void CON_AddLineFeed() +{ + // force line feed + skip_next_nl = FALSE; + CON_AddLine( con_prompt ); +} + + +// set flag to disable next line feed in CON_AddLine() ------------------------ +// +PUBLIC +void CON_DisableLineFeed() +{ + // set flag + skip_next_nl = TRUE; +} + + +// set flag to not eat next line feed in CON_AddLine() ------------------------ +// +PUBLIC +void CON_EnableLineFeed() +{ + // clear flag + skip_next_nl = FALSE; +} + + +// delete current console line ------------------------------------------------ +// +PUBLIC +void CON_DelLine() +{ + EraseConLine( con_bottom ); +} + + +// return pointer to current console line ------------------------------------- +// +PUBLIC +char *CON_GetLine() +{ + return con_lines[ con_bottom ]; +} + + +// print prompt for list continuation ----------------------------------------- +// +PUBLIC +void CON_ListCtdPrompt() +{ + CON_AddLine( press_space ); + + // ensure proper view alignment + edit_ofs_x = ( cursor_x > ConsoleEnterLength ) ? ( cursor_x - ConsoleEnterLength ) : 0; +} + + +// print prompt after list has ended ------------------------------------------ +// +PUBLIC +void CON_ListEndPrompt() +{ + await_keypress = FALSE; + com_cont_func = NULL; + + if ( con_in_talk_mode ) { + + // restore talkmode prompt + CON_AddLineFeed(); + CreateSeparatorLine( con_bottom ); + CON_AddLineFeed(); + con_talk_line = con_bottom; + + } else { + + // restore standard prompt + CON_AddLine( con_prompt ); + } +} + + +// check geometric extents of console window ---------------------------------- +// +void CheckConExtents() +{ + ASSERT( FALSE ); +} + + +// check commands with single integer argument/query -------------------------- +// +PRIVATE +int ExecGetSetInteger( const char *scan ) +{ + ASSERT( scan != NULL ); + + ConsoleHeight -= 2; + + for ( int icom = 0; icom < num_int_commands; icom++ ) { + + ASSERT( int_commands[ icom ].command != NULL ); + ASSERT( int_commands[ icom ].intref != NULL ); + + // need fetch function for int vars that need to be + // made current before they contain the correct value + if(strncmp(scan, int_commands[ icom ].command, strlen(int_commands[ icom ].command)) ==0) { + if ( int_commands[ icom ].fetch != NULL ) + *int_commands[ icom ].intref = (*int_commands[ icom ].fetch)(); + + if ( CheckSetIntArgBounded( int_print_base, scan, + (char *) int_commands[ icom ].command, + int_commands[ icom ].intref, + int_commands[ icom ].bmin, + int_commands[ icom ].bmax, + int_commands[ icom ].realize ) ) { + ConsoleHeight += 2; + return TRUE; + } + } + } + + ConsoleHeight += 2; + + return FALSE; +} + + +// temporary storage in which user can alter their name ----------------------- +// +static char temp_playername[ MAX_PLAYER_NAME + 1 ]; + + +// exec command with a single optional parameter ------------------------------ +// +PRIVATE +void ExecGetSetCommand( char *command ) +{ + ASSERT( command != NULL ); + + // split off first token (command specifier) + char *scan = strtok( command, " " ); + + // check different commands + if ( scan != NULL ) { + + if ( ExecGetSetInteger( scan ) ) { + } else if ( CheckSetObjTypeProperty( int_print_base, scan ) ) { + } else if ( CheckSetObjClassProperty( int_print_base, scan ) ) { + } else if ( CheckSetObjInstanceProperty( int_print_base, scan ) ) { + } else if ( CheckTypeInfo( scan ) ) { + } else if ( CheckClassInfo( scan ) ) { + } else if ( CheckInstanceInfo( scan ) ) { +/* } else if ( CheckKeyMappingEcho( scan ) ) { + } else if ( CheckKeyMappingSilent( scan ) ) { + */} else if ( CheckSetIntArray( int_print_base, scan, auxflag_array_name, AuxEnabling, MAX_SV_ARRAY_SIZE ) ) { + } else { + // printout the command that could not be found + char szBuffer[ 512 ]; + sprintf( szBuffer, unknown_command, scan ); + CON_AddLine( szBuffer ); + } + } else { + + CON_AddLine( ok_prompt ); + } +} + + +// key table for compile command ---------------------------------------------- +// +key_value_s compile_key_value[] = { + + { "title", NULL, KEYVALFLAG_PARENTHESIZE }, + { "desc", NULL, KEYVALFLAG_PARENTHESIZE }, + { "author", NULL, KEYVALFLAG_PARENTHESIZE }, + + { NULL, NULL, KEYVALFLAG_NONE }, +}; + +enum { + + KEY_COMPILE_TITLE, + KEY_COMPILE_DESC, + KEY_COMPILE_AUTHOR +}; + + +// write demo header if at least one info field specified --------------------- +// +PRIVATE +void WriteDemoHeader( char *title, char *desc, char *author ) +{ + if ( ( title == NULL ) && ( desc == NULL ) && ( author == NULL ) ) { + return; + } + + ASSERT( strlen( DEM_SIGNATURE ) == 6 ); + + DemHeader hdr; + strcpy( hdr.signature, DEM_SIGNATURE ); + hdr.version = REQUIRED_DEM_VERSION; + + dword headersize = sizeof( DemHeader ); + + headersize += ( title != NULL ) ? strlen( title ) + 2 : 0; + headersize += ( desc != NULL ) ? strlen( desc ) + 2 : 0; + headersize += ( author != NULL ) ? strlen( author ) + 2 : 0; + + hdr.headersize = headersize; + + SYS_SwapDemHeader( &hdr ); + + // write fixed-size part of header + fwrite( &hdr, sizeof( hdr ), 1, compile_fp ); + + // write title + byte demokey = 0; + if ( title != NULL ) { + demokey = DEMO_KEY_TITLE; + fwrite( &demokey, sizeof( demokey ), 1, compile_fp ); + fwrite( title, strlen( title ) + 1, 1, compile_fp ); + } + + // write description + if ( desc != NULL ) { + demokey = DEMO_KEY_DESCRIPTION; + fwrite( &demokey, sizeof( demokey ), 1, compile_fp ); + fwrite( desc, strlen( desc ) + 1, 1, compile_fp ); + } + + // write author + if ( author != NULL ) { + demokey = DEMO_KEY_AUTHOR; + fwrite( &demokey, sizeof( demokey ), 1, compile_fp ); + fwrite( author, strlen( author ) + 1, 1, compile_fp ); + } +} + + +// check compile command and compile script if valid -------------------------- +// +PRIVATE +void CompileCommand( char *command ) +{ + ASSERT( command != NULL ); + + // check for compile command in script to compile + if ( compile_script ) { + CON_AddLine( recursive_compilation ); + return; + } + + // skip command string + command += CMLEN( CM_COMPILE ); + + // check if there is a space between command and argument + if ( ( *command != ' ' ) && ( *command != 0 ) ) { + + // printout the command that could not be found + char szBuffer[ 512 ]; + sprintf( szBuffer, unknown_command, command ); + CON_AddLine( szBuffer ); + + return; + } + + // split off argument (name of command script) + char *scan = strtok( command, " " ); + if ( scan == NULL ) { + CON_AddLine( arg_missing ); + return; + } + + // scan out all values to keys + if ( !ScanKeyValuePairs( compile_key_value, NULL ) ) + return; + + // get demo information strings (not mandatory, might be NULL) + char *title = compile_key_value[ KEY_COMPILE_TITLE ].value; + char *desc = compile_key_value[ KEY_COMPILE_DESC ].value; + char *author = compile_key_value[ KEY_COMPILE_AUTHOR ].value; + + // check if command name is valid + int extcom = CheckExternalCommand( scan ); + + if ( extcom > 0 ) { + + // open output file + strcpy( paste_str, scan ); + strcat( paste_str, CON_FILE_COMPILED_EXTENSION ); + compile_fp = fopen( paste_str, "wb" ); + + if ( compile_fp != NULL ) { + + // write header if at least one info field specified + WriteDemoHeader( title, desc, author ); + + // compile script + compile_script = TRUE; + CallExternalCommand( extcom - 1, FALSE ); + compile_script = FALSE; + + // close output file + if ( compile_fp != NULL ) { + // if no error occurred write eof marker and close file + int endmarker = 0; + fwrite( &endmarker, sizeof( endmarker ), 1, compile_fp ); + fclose( compile_fp ); + } + + } else { + // filepointer for output invalid + CON_AddLine( compile_open_failed ); + } + + } else { + // name of script is not in internal table + CON_AddLine( unknown_script ); + } +} + + +// execute command that cannot be compiled ------------------------------------ +// +void ExecNonCompilableCommand( char *command ) +{ + ASSERT( command != NULL ); + + //NOTE: + // this function is mainly used by ExecConsoleCommand(). + // apart from that it is only used by AcExecCommandString() + // to execute a command supplied as string in a demo. + + // this is first in order to allow user overriding + // of all non-compilable commands + if ( CheckCmdsUserDefined( command ) ) { + + // check commands having no parameters + } else if ( CheckCmdsNoParam( command ) ) { + + // check commands that do their own parameter parsing + } else if ( CheckCmdsCustomString( command ) ) { + + } else { + + ExecGetSetCommand( command ); + } +} + + +// add console line to history list ------------------------------------------- +// +INLINE +void AddLineToHistory( char *command ) +{ + ASSERT( command != NULL ); + ASSERT( strlen( command ) <= MAX_CONSOLE_LINE_LENGTH ); + + // store command in current history position, advance in + // list, and clear history scrolling + strcpy( history_list[ history_add ], command ); + history_add = ( history_add + 1 ) & NUM_HISTORY_LINES_MASK; + history_scan = history_add; + + // clear next two positions in history ring-buffer + // (this has to be done in order to make scrolling + // in both directions work properly) + // this is where entries start to get reused if there + // is not enough space in the history list + *( history_list[ history_add ] ) = 0; + *( history_list[ ( history_add + 1 ) & NUM_HISTORY_LINES_MASK ] ) = 0; +} + + +// check for console login state and login/logout commands -------------------- +// +PRIVATE +int CheckConsoleLogin( char *command ) +{ + ASSERT( command != NULL ); + + // used by keymapped commands + if ( con_no_login_check ) { + return TRUE; + } + + // avoid preempting startup script + if ( console_init_done ) { + + if ( con_login_mode ) { + con_login_mode = 0; + + // check for password + if ( strcmp( command, console_login_passwd ) == 0 ) { + con_logged_in = 1; + CON_AddLine( console_unlock_done ); + } + // erase password to prevent it from being displayed + for ( int spos = 0; spos < ConsoleEnterLength; spos++ ) { + if ( command[ spos ] == 0 ) + break; + command[ spos ] = '*'; + } + return FALSE; + } + + if ( con_logged_in ) { + + if ( strcmp( command, CMSTR( CM_LOGOUT ) ) == 0 ) { + con_logged_in = 0; + CON_AddLine( console_lock_done ); + return FALSE; + } + + } else { + + if ( strcmp( command, CMSTR( CM_LOGIN ) ) == 0 ) { + con_login_mode = 1; + return FALSE; + } + + CON_AddLine( console_is_locked ); + return FALSE; + } + } + + return TRUE; +} + + +// exec console command (this is the main command dispatcher) ----------------- +// +void ExecConsoleCommand( char *command, int echo ) +{ + //NOTE: + // this function may be called recursively from within + // functions called by CON_EXT::CallExternalCommand() + // to allow execution of command scripts. + + ASSERT( command != NULL ); + ASSERT( ( echo == FALSE ) || ( echo == TRUE ) ); + +#ifdef FORCE_CONSOLE_LOGIN + + // check login state and login/logout commands + if ( !CheckConsoleLogin( command ) ) { + return; + } + +#endif // FORCE_CONSOLE_LOGIN + + // fetch console line as command + strncpy( command_line, command, MAX_CONSOLE_LINE_LENGTH ); + command_line[ MAX_CONSOLE_LINE_LENGTH ] = 0; + command = command_line; + + // eat leading whitespace + for ( ; *command; command++ ) { + if ( *command != ' ' ) { + break; + } + } + + // early out for empty lines + if ( *command == 0 ) { + if ( echo ) { + CON_AddLine( ok_prompt ); + } + return; + } + + if ( echo ) { + // add command to history list only if echo is on + AddLineToHistory( command ); + } + + // temp for extcom id + int extcom; + + if ( strcmp( command, CMSTR( CM_NOMACROS ) ) == 0 ) { + + // disable calls to CallExternalCommand() + disable_macros = TRUE; + + } else if ( strcmp( command, CMSTR( CM_ENABLEMACROS ) ) == 0 ) { + + // enable calls to CallExternalCommand() + disable_macros = FALSE; + + } else if ( strncmp( command, CMSTR( CM_COMPILE ), CMLEN( CM_COMPILE ) ) == 0 ) { + + CompileCommand( command ); + + } else if ( strncmp( command, ACMSTR( ACM_SIGNATURE ), ACMLEN( ACM_SIGNATURE ) ) == 0 ) { + + // check/exec commands with "ac." prefix + ActionCommand( command ); + + } else if ( !disable_macros && ( extcom = CheckExternalCommand( command ) ) ) { + + // execute command script + CallExternalCommand( extcom - 1, echo ? EchoExternalCommands : FALSE ); + + } else if ( !compile_script ) { + + // check/exec all other commands + ExecNonCompilableCommand( command ); + + } else { + + // error if compilation in progress and non-compilable command found + CON_AddLine( cmd_not_compilable ); + } +} + + +// handle console input in talk mode ------------------------------------------ +// +PRIVATE +void HandleTalkMode() +{ + if ( con_in_talk_mode ) { + + if ( con_lines[ con_bottom ][ PROMPT_SIZE ] == talk_escape_char ) { + + // remove '/' escape char from input + strcpy( paste_str, con_lines[ con_bottom ] + PROMPT_SIZE + 1 ); + + } else { + + // prepend "say " to input + strcpy( paste_str, CMSTR( CM_SAY ) ); + paste_str[ CMLEN( CM_SAY ) ] = ' '; + strncpy( paste_str + CMLEN( CM_SAY ) + 1, + con_lines[ con_bottom ] + PROMPT_SIZE, + MAX_CONSOLE_LINE_LENGTH - PROMPT_SIZE - CMLEN( CM_SAY ) - 1 ); + paste_str[ MAX_CONSOLE_LINE_LENGTH - PROMPT_SIZE ] = 0; + } + + // set bottom to line just below separator in order to + // overwrite previous output. the current input is also + // copied into this line + con_bottom = con_talk_line; + strcpy( con_lines[ con_bottom ] + PROMPT_SIZE, paste_str ); + + // ensure the input line gets overwritten by the first console + // output the command produces. if the command outputs nothing + // the terminating prompt will overwrite the former input line + CON_DisableLineFeed(); + } +} + + +// <enter> key pressed in console --------------------------------------------- +// +PRIVATE +void EnterConsoleCommand() +{ + // handle input in talkmode + HandleTalkMode(); + + // output command + CON_AddLine( con_lines[ con_bottom ] ); + + // insert command into script if currently open + if ( !InsertCommandLog( con_lines[ con_bottom ] + PROMPT_SIZE ) ) { + + // execute current linecontent + ExecConsoleCommand( con_lines[ con_bottom ] + PROMPT_SIZE, TRUE ); + + if ( await_keypress ) { + // listcommand: print listmode prompt + CON_ListCtdPrompt(); + } else { + // normal command: print standard prompt + //CON_AddLine( con_prompt ); + } + + } else { + + // command was not executed, but only + // written to an output script + + if ( HistoryForBatchEntry ) + AddLineToHistory( con_lines[ con_bottom ] + PROMPT_SIZE ); + //CON_AddLine( con_prompt ); + } + + EraseConLine( con_bottom ); + strcpy( con_lines[ con_bottom ], con_prompt ); +} + + +// fetch history list entry into current console line ------------------------- +// +PRIVATE +void FetchHistoryEntry( int entryid ) +{ + EraseConLine( con_bottom ); + strcpy( con_lines[ con_bottom ], con_prompt ); + + if ( con_in_talk_mode ) { + + // handle talkmode + if ( ( strncmp( history_list[ entryid ], CMSTR( CM_SAY ), CMLEN( CM_SAY ) ) == 0 ) && + ( history_list[ entryid ][ CMLEN( CM_SAY ) ] == ' ' ) ) { + + // strip "say " in order to produce correct talkmode input + strcpy( con_lines[ con_bottom ] + PROMPT_SIZE, history_list[ entryid ] + CMLEN( CM_SAY ) + 1 ); + + } else { + + // prepend '/' escape char in order to produce correct talkmode input + con_lines[ con_bottom ][ PROMPT_SIZE ] = talk_escape_char; + strncpy( con_lines[ con_bottom ] + PROMPT_SIZE + 1, + history_list[ entryid ], + MAX_CONSOLE_LINE_LENGTH - PROMPT_SIZE - 1 ); + con_lines[ con_bottom ][ MAX_CONSOLE_LINE_LENGTH ] = 0; + } + + } else { + + // use history line as is + strcpy( con_lines[ con_bottom ] + PROMPT_SIZE, history_list[ entryid ] ); + } + + // set cursor behind end of command + cursor_x = strlen( con_lines[ con_bottom ] + PROMPT_SIZE ); + + // ensure proper view alignment + edit_ofs_x = ( cursor_x > ConsoleEnterLength ) ? + ( cursor_x - ConsoleEnterLength ) : 0; +} + + +// check input for console window --------------------------------------------- +// +PRIVATE +bool_t CheckConsoleInput() +{ + // scan keyboardbuffer and process key-presses sequentially + dword *pos; + bool_t console_input_done = false; + while ( *( pos = &KeybBuffer->Data + KeybBuffer->ReadPos ) ) { + + int curlinelen = strlen( con_lines[ con_bottom ] + PROMPT_SIZE ); + + if ( await_keypress && com_cont_func ) { + + // <space> or <enter> continues listing + if ( ( *pos == CKC_SPACE ) || ( *pos == CKC_ENTER ) ) { + + EraseConLine( con_bottom ); + strcpy( con_lines[ con_bottom ], con_prompt ); + // ensure wait prompt gets overwritten + con_bottom = last_con_bottom; + con_content = last_con_content; + cursor_x = 0; + com_cont_func(); + + // <escape> returns to command prompt immediately + } else if ( *pos == CKC_ESCAPE ) { + + await_keypress = FALSE; + com_cont_func = NULL; + if ( con_in_talk_mode ) { + // restore talkmode prompt + CreateSeparatorLine( con_bottom ); + CON_AddLineFeed(); + con_talk_line = con_bottom; + } else { + // restore standard prompt + EraseConLine( con_bottom ); + strcpy( con_lines[ con_bottom ], con_prompt ); + } + cursor_x = 0; + } + + } else if ( *pos & CKC_CTRL_MASK ) { + + // control keys (CKC_xx) + switch ( *pos ) { + + // <escape>: erase entire command line + case CKC_ESCAPE: + EraseConLine( con_bottom ); + strcpy( con_lines[ con_bottom ], con_prompt ); + history_scan = history_add; + cursor_x = 0; + con_back_view = 0; + break; + + // <cursor-up>: scroll one line up in history buffer + case CKC_CURSORUP: + if ( !con_logged_in ) + break; + if ( *( history_list[ ( history_scan - 1 ) & NUM_HISTORY_LINES_MASK ] ) != 0 ) { + history_scan = ( history_scan - 1 ) & NUM_HISTORY_LINES_MASK; + FetchHistoryEntry( history_scan ); + } + break; + + // <cursor-down>: scroll one line down in history buffer + case CKC_CURSORDOWN: + if ( !con_logged_in ) + break; + if ( *( history_list[ ( history_scan + 1 ) & NUM_HISTORY_LINES_MASK ] ) != 0 ) { + history_scan = ( history_scan + 1 ) & NUM_HISTORY_LINES_MASK; + FetchHistoryEntry( history_scan ); + } + break; + + // <cursor-left>: set cursor one position left + case CKC_CURSORLEFT: + if ( cursor_x > 0 ) + cursor_x--; + break; + + // <cursor-right>: set cursor one position right + case CKC_CURSORRIGHT: + if ( cursor_x < curlinelen ) + cursor_x++; + break; + + // <shift + cursor-up>: view one line backward + case CKC_CURSORUP_SHIFTED: + if ( !con_logged_in || con_in_talk_mode ) + break; + if ( con_back_view < CONSOLE_MAX_BACK_LINES ) + con_back_view++; + break; + + // <shift + cursor-down>: view one line forward + case CKC_CURSORDOWN_SHIFTED: + if ( !con_logged_in || con_in_talk_mode ) + break; + if ( con_back_view > 0 ) + con_back_view--; + break; + + // <shift + cursor-left>: set cursor one word to the left + case CKC_CURSORLEFT_SHIFTED: + while ( ( cursor_x > 0 ) && + !isalnum( con_lines[ con_bottom ][ PROMPT_SIZE + cursor_x - 1 ] ) ) + cursor_x--; + while ( ( cursor_x > 0 ) && + isalnum( con_lines[ con_bottom ][ PROMPT_SIZE + cursor_x - 1 ] ) ) + cursor_x--; + break; + + // <shift + cursor-right>: set cursor one word to the right + case CKC_CURSORRIGHT_SHIFTED: + while ( ( cursor_x < curlinelen ) && + isalnum( con_lines[ con_bottom ][ PROMPT_SIZE + cursor_x ] ) ) + cursor_x++; + while ( ( cursor_x < curlinelen ) && + !isalnum( con_lines[ con_bottom ][ PROMPT_SIZE + cursor_x ] ) ) + cursor_x++; + break; + + // <home>: set cursor to first column + case CKC_HOME: + cursor_x = 0; + break; + + // <end>: set cursor behind last column + case CKC_END: + cursor_x = curlinelen; + break; + + // <page-up>: view one page backward + case CKC_PAGEUP: + if ( !con_logged_in || con_in_talk_mode ) + break; + if ( cur_vis_conlines <= 0 ) + break; + if ( con_back_view < CONSOLE_MAX_BACK_LINES ) + con_back_view += cur_vis_conlines; + if ( con_back_view > CONSOLE_MAX_BACK_LINES ) + con_back_view = CONSOLE_MAX_BACK_LINES; + break; + + // <page-down>: view one page forward + case CKC_PAGEDOWN: + if ( !con_logged_in || con_in_talk_mode ) + break; + if ( cur_vis_conlines <= 0 ) + break; + if ( con_back_view > 0 ) + con_back_view -= cur_vis_conlines; + if ( con_back_view < 0 ) + con_back_view = 0; + break; + + // <insert>: toggle insert mode + case CKC_INSERT: + insert_mode = !insert_mode; + break; + + // <del>: delete and shift left + case CKC_DELETE: + if ( cursor_x < curlinelen ) { + strcpy( paste_str, con_lines[ con_bottom ] + PROMPT_SIZE + cursor_x + 1 ); + strcpy( con_lines[ con_bottom ] + PROMPT_SIZE + cursor_x, paste_str ); + } + break; + + // <tab>: find completion for current input + case CKC_TAB: + if ( !con_logged_in ) + break; + CompleteCommand(); + break; + + // <enter>: execute current line + case CKC_ENTER: + EnterConsoleCommand(); + break; + + // <backspace>: step left and shift righthand side left + case CKC_BACKSPACE: + if ( cursor_x > 0 ) { + if ( cursor_x == curlinelen ) { + cursor_x--; + con_lines[ con_bottom ][ PROMPT_SIZE + cursor_x ] = 0; + } else { + cursor_x--; + strcpy( paste_str, con_lines[ con_bottom ] + PROMPT_SIZE + cursor_x + 1 ); + strcpy( con_lines[ con_bottom ] + PROMPT_SIZE + cursor_x, paste_str ); + } + } + break; + } + + } else { + + // typeable characters + if ( ( ConsoleASCIIValid[ *pos & CON_ASCII_MASK ] ) && + ( cursor_x < edit_max_x ) ) { + if ( insert_mode && ( cursor_x < curlinelen ) ) { + if ( curlinelen < edit_max_x ) { + strcpy( paste_str, con_lines[ con_bottom ] + PROMPT_SIZE + cursor_x ); + strcpy( con_lines[ con_bottom ] + PROMPT_SIZE + cursor_x + 1, paste_str ); + con_lines[ con_bottom ][ PROMPT_SIZE + cursor_x++ ] = (byte)*pos; + } + } else { + con_lines[ con_bottom ][ PROMPT_SIZE + cursor_x++ ] = (byte)*pos; + } + } + } + + // advance one position in circular keybuffer + KeybBuffer->ReadPos = ( KeybBuffer->ReadPos + 1 ) % KEYB_BUFF_SIZ; + *pos = 0; + + console_input_done = true; + } + + return console_input_done; +} + + +// type for writestring function pointer -------------------------------------- +// +typedef void (*WSFP)( ... ); + +//NOTE: +// this declaration is in global scope because of the extern "C". +// only gcc needs the otherwise redundant curly braces, however. + + + +// process quicksay console input and draw it --------------------------------- +// +int QuicksayConsole() +{ +/* // check whether main console has turned quicksay off + if ( !KeybFlags->ConEnabled || !KeybFlags->ConActive ) { + return FALSE; + } + + dword *pos; + dword readpos = KeybBuffer->ReadPos; + + // scan console input buffer for ESCAPE or ENTER which + // should turn off quicksay mode again + while ( *( pos = &KeybBuffer->Data + readpos ) ) { + + if ( *pos == CKC_ESCAPE || *pos == CKC_ENTER ) { + SetQuicksayConsole( FALSE ); + } + + readpos = ( readpos + 1 ) % KEYB_BUFF_SIZ; + } + + // use standard input handling + CheckConsoleInput(); + + // ensure proper edit line viewing window + if ( cursor_x < edit_ofs_x ) { + edit_ofs_x = cursor_x; + } else if ( cursor_x > edit_ofs_x + ConsoleEnterLength ) { + edit_ofs_x = cursor_x - ConsoleEnterLength; + } + + // determine whether translucency should be used + int translucent = VID_TRANSLUCENCY_SUPPORTED; + + D_SetWStrContext( CharsetInfo[ HUD_CHARSETNO ].charsetpointer, + CharsetInfo[ HUD_CHARSETNO ].geompointer, + NULL, + CharsetInfo[ HUD_CHARSETNO ].width, + CharsetInfo[ HUD_CHARSETNO ].height ); + + // write text translucent only for color depths below 32 bit per pixel + WSFP wsfp = translucent ? (WSFP) &D_WriteTrString : (WSFP) &D_WriteString; + + // determine chars to draw + if ( edit_ofs_x == 0 ) { + memcpy( draw_line, con_lines[ con_bottom ], ConsoleEnterLength + PROMPT_SIZE ); + draw_line[ ConsoleEnterLength + PROMPT_SIZE ] = 0; + } else { + memcpy( draw_line, con_lines[ con_bottom ], PROMPT_SIZE ); + draw_line[ PROMPT_SIZE - 1 ] = '<'; + memcpy( draw_line + PROMPT_SIZE, con_lines[ con_bottom ] + PROMPT_SIZE + edit_ofs_x, ConsoleEnterLength ); + draw_line[ ConsoleEnterLength + PROMPT_SIZE ] = 0; + } + + int xpos = ConsoleTextX * CharsetInfo[ HUD_CHARSETNO ].width + console_frameofs_x; + int ypos = 130; + + // draw background bar + int barwidth = ( ConsoleEnterLength + PROMPT_SIZE + 1 ) * CharsetInfo[ HUD_CHARSETNO ].width + 6; + int barheight = CharsetInfo[ HUD_CHARSETNO ].height + 4; + D_DrawTrRect( xpos - 3, ypos - 2, barwidth, barheight, TRTAB_PANELBACK ); + + // draw line contents + wsfp( draw_line, xpos, ypos, TRTAB_PANELTEXT ); + + // maintain blinking of cursor if not done by main console + if ( ConsoleSliding == 0 ) { + cursor_state += CurScreenRefFrames; + } + + // draw cursor + if ( cursor_state & CURSOR_BLINK_SPEED ) { + + xpos = ConsoleTextX + cursor_x - edit_ofs_x + PROMPT_SIZE; + xpos *= CharsetInfo[ HUD_CHARSETNO ].width; + xpos += console_frameofs_x; + + char *cursorstr = insert_mode ? insert_cursor : overwrite_cursor; + wsfp( cursorstr, xpos, ypos, TRTAB_PANELTEXT ); + } + + // allow caller to check for disabling + return KeybFlags->ConActive; + */ + + return FALSE; +} + + +// draw console window -------------------------------------------------------- +// +PRIVATE +void DrawConsole( bool_t bClear ) +{ + //NOTE: in order to minimize screen flicker, we only clear the inputline, + // if the previous contents was longers + if ( bClear ) + wclear( g_curses_in_win ); + + mvwprintw( g_curses_in_win, 0, 0, con_lines[ con_bottom ] ); + wmove( g_curses_in_win, 0, cursor_x + PROMPT_SIZE ); + wrefresh( g_curses_in_win ); +} + + +// main console function: process keyboard input and draw console window ------ +// +PUBLIC +void CON_ConsoleMain() +{ + static bool_t init_prompt_drawn = false; + + // set number of currently visible console lines + // (negative values allowed!) + cur_vis_conlines = ConsoleHeight; + + unsigned int len_before = strlen( con_lines[ con_bottom ] ); + + // process console keyboard input + if ( CheckConsoleInput() || !init_prompt_drawn ) { + + init_prompt_drawn = true; + // draw console + DrawConsole( len_before > strlen( con_lines[ con_bottom ] ) ); + } +} diff --git a/src/parsec_server/con_std_sv.cpp b/src/parsec_server/con_std_sv.cpp new file mode 100755 index 0000000..d22375e --- /dev/null +++ b/src/parsec_server/con_std_sv.cpp @@ -0,0 +1,159 @@ +/* + * PARSEC - SERVER Standard Commands + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:47 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2001 + * Copyright (c) Markus Hadwiger <msh@parsec.org> 1996-1999 + * + * 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 <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "objstruc.h" + +// local module header +#include "con_std_sv.h" + + +// strings of standard commands ----------------------------------------------- +// +std_command_s std_commands[] = { + + { "append", 0, 1 }, + + { "base", 0, 0 }, + { "bind", 0, 2 }, + { "build", 0, 0 }, + + { "cat", 0, 1 }, + { "classinfo", 0, 1 }, + { "clear", 0, 0 }, + { "close", 0, 0 }, + { "cls", 0, 0 }, + { "colanim", 0, 1 }, + { "compile", 0, 1 }, + { "connect", 0, 1 }, + { "create", 0, 1 }, + + { "dec", 0, 0 }, + { "disable", 0, 0 }, + { "disablemacros", 0, 0 }, + { "disconnect", 0, 0 }, + { "display", 0, 1 }, + + { "enable", 0, 0 }, + { "enablemacros", 0, 0 }, + { "exit", 0, 0 }, + { "exitgame", 0, 0 }, + + { "faceinfo", 0, 1 }, + + { "glbatch", 0, 1 }, + + { "hex", 0, 0 }, + { "hide", 0, 0 }, + + { "key", 0, 2 }, + + { "listbindings", 0, 0 }, + { "listclasses", 0, 0 }, + { "listcommands", 0, 0 }, + { "listdata", 0, 1 }, + { "listdemos", 0, 0 }, + { "listgamefunckeys", 0, 0 }, + { "listintcommands", 0, 0 }, + { "listsaymacros", 0, 0 }, + { "listtypes", 0, 0 }, + { "load", 0, 1 }, + { "login", 0, 0 }, + { "logout", 0, 0 }, + { "ls", 0, 0 }, + + { "name", 0, 1 }, + { "net.subsys", 0, 1 }, + + { "objectclear", 0, 0 }, + { "objectcount", 0, 0 }, + { "objectinfo", 0, 1 }, + { "oct", 0, 0 }, + { "open", 0, 1 }, + + { "path.anim.intro", 0, 1 }, + { "path.astream.intro", 0, 1 }, + { "path.astream.menu", 0, 1 }, + { "path.audiostreams", 0, 1 }, + { "play", 0, 1 }, + { "prop", 0, 1 }, + { "propc", 0, 1 }, + { "propo", 0, 1 }, + + { "quit", 0, 0 }, + + { "recdem", 0, 1 }, + { "record", 0, 1 }, + { "repinfo", 0, 0 }, + { "repstop", 0, 0 }, + { "rescan", 0, 0 }, + + { "say", 0, 0 }, + { "set", 0, 0 }, + { "setorigin", 0, 0 }, + { "shader.def", 0, 1 }, + { "shader.set", 0, 1 }, + { "ships", 0, 0 }, + { "showposition", 0, 0 }, + + { "talk", 0, 0 }, + { "typeinfo", 0, 1 }, + + { "workdir", 0, 1 }, + { "write", 0, 1 }, +}; + +#define NUM_STD_COMMANDS CALC_NUM_ARRAY_ENTRIES( std_commands ) + + +// number of standard commands ------------------------------------------------ +// +int num_std_commands = NUM_STD_COMMANDS; + + +// registration function precalculates string lengths ------------------------- +// +REGISTER_MODULE( CON_STD_SV ) +{ + // ensure table and constants are of consistent length + ASSERT( NUM_STD_COMMANDS == NUM_STD_COMMAND_CONSTANTS ); + + // init string length table + for ( unsigned int cid = 0; cid < NUM_STD_COMMANDS; cid++ ) + std_commands[ cid ].commlen = strlen( std_commands[ cid ].command ); +} + + + diff --git a/src/parsec_server/e_colldet_sv.cpp b/src/parsec_server/e_colldet_sv.cpp new file mode 100755 index 0000000..cd58817 --- /dev/null +++ b/src/parsec_server/e_colldet_sv.cpp @@ -0,0 +1,1251 @@ +/* + * PARSEC - Game collision detection - SERVER + * + * $Author: uberlinuxguy $ - $Date: 2004/09/15 12:25:44 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2002 + * + * 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 <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.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" +#include "e_defs.h" + +// mathematics header +#include "utl_math.h" + +// local module header +#include "e_colldet.h" + +// proprietary module headers +#include "g_extra.h" +#include "net_game_sv.h" +#include "g_main_sv.h" +#include "e_simplayerinfo.h" +#include "e_connmanager.h" +#include "e_simnetoutput.h" +#include "e_simulator.h" +#include "parttype.h" + +#define SET_SHIELD_TESTING() { test_shield = TRUE; } +//#define SET_SHIELD_TESTING() { test_shield = FALSE; } + +#define CONSERVATIVE_POINTSAMPLE_EXPANSION 16 + +// collision detection helper function ---------------------------------------- +// +int G_CollDet::_CheckShipProjectileDisjoint() +{ + // extend sphere for point sampling + geomv_t hugesphere = bd_sphere * CONSERVATIVE_POINTSAMPLE_EXPANSION; + + // vector to center + Vector3 vectest; + vectest.X = obj_pos.X - test_pos.X; + vectest.Y = obj_pos.Y - test_pos.Y; + vectest.Z = obj_pos.Z - test_pos.Z; + + // check enlarged vicinity for early-out + if ( vectest.X >= hugesphere ) + return TRUE; + if ( vectest.Y >= hugesphere ) + return TRUE; + if ( vectest.Z >= hugesphere ) + return TRUE; + + hugesphere = -hugesphere; + if ( vectest.X <= hugesphere ) + return TRUE; + if ( vectest.Y <= hugesphere ) + return TRUE; + if ( vectest.Z <= hugesphere ) + return TRUE; + + // test shield (bounding sphere) + if ( test_shield ) { + + // squared distance to current position + geomv_t vectestl2 = DOT_PRODUCT( &vectest, &vectest ); + + // trivial intersect + if ( vectestl2 < bd_sphere2 ) + return FALSE; + + // squared distance to previous position + Vector3 vecprev; + vecprev.X = obj_pos.X - prev_pos.X; + vecprev.Y = obj_pos.Y - prev_pos.Y; + vecprev.Z = obj_pos.Z - prev_pos.Z; + + geomv_t vecprevl2 = DOT_PRODUCT( &vecprev, &vecprev ); + + // trivial intersect + if ( vecprevl2 < bd_sphere2 ) + return FALSE; + + // calc nearest point on line to center + Vector3 vecline; + vecline.X = test_pos.X - prev_pos.X; + vecline.Y = test_pos.Y - prev_pos.Y; + vecline.Z = test_pos.Z - prev_pos.Z; + + geomv_t scaledt = DOT_PRODUCT( &vecline, &vecprev ); + + // ray pointing away from sphere? + if ( GEOMV_NEGATIVE( scaledt ) ) + return TRUE; + + // ray not yet at the sphere? + geomv_t veclinel2 = DOT_PRODUCT( &vecline, &vecline ); + if ( scaledt >= veclinel2 ) + return TRUE; + ASSERT( veclinel2 > GEOMV_VANISHING ); + + // calc squared distance of nearest point + geomv_t dot2 = GEOMV_MUL( scaledt, scaledt ); + geomv_t tlen2 = GEOMV_DIV( dot2, veclinel2 ); + + //NOTE: + // the actual point of impact could now be + // easily calculated without a sqrt(). + + // collision if nearest point inside + return ( ( vecprevl2 - tlen2 ) >= bd_sphere2 ); + + } else { + + ASSERT( FALSE ); +/* + // shield is down: test actual object + +#ifdef CHECK_BOX_BEFORE_BSP_COLLISION + + // clip lineseg into box + + geomv_t isect; + geomv_t seglen; + geomv_t vtx0t = GEOMV_0; + geomv_t vtx1t = GEOMV_1; + + int collside = -1; + + static geomv_t vanish = FLOAT_TO_GEOMV( 0.00001 ); + + // cull/clip against +Z + geomv_t curax = obj_pos.Z + bd_sphere; + geomv_t dist0 = prev_pos.Z - curax; + geomv_t dist1 = test_pos.Z - curax; + dword outcode = ( ( DW32( dist0 ) & 0x80000000 ) >> 1 ) | + ( DW32( dist1 ) & 0x80000000 ); + // both outside? + if ( outcode == 0x00000000 ) + return TRUE; + // at least one outside? + if ( outcode != 0xc0000000 ) { + if ( outcode == 0x40000000 ) { + // v0 inside, v1 outside + ABS_GEOMV( dist0 ); + seglen = dist0 + dist1; + if ( seglen <= vanish ) + return TRUE; + vtx1t = GEOMV_DIV( dist0, seglen ); + } else { + // v0 outside, v1 inside + ABS_GEOMV( dist1 ); + seglen = dist0 + dist1; + if ( seglen <= vanish ) + return TRUE; + vtx0t = GEOMV_DIV( dist0, seglen ); + collside = 0; + } + } + +#define AXIAL_CLIP_T(s) { \ +\ + outcode = ( ( DW32( dist0 ) & 0x80000000 ) >> 1 ) | \ + ( DW32( dist1 ) & 0x80000000 ); \ + if ( outcode == 0x00000000 ) \ + return TRUE; \ + if ( outcode != 0xc0000000 ) { \ + if ( outcode == 0x40000000 ) { \ + ABS_GEOMV( dist0 ); \ + seglen = dist0 + dist1; \ + if ( seglen <= vanish ) \ + return TRUE; \ + isect = GEOMV_DIV( dist0, seglen ); \ + if ( isect <= vtx0t ) \ + return TRUE; \ + if ( isect < vtx1t ) \ + vtx1t = isect; \ + } else { \ + ABS_GEOMV( dist1 ); \ + seglen = dist0 + dist1; \ + if ( seglen <= vanish ) \ + return TRUE; \ + isect = GEOMV_DIV( dist0, seglen ); \ + if ( isect >= vtx1t ) \ + return TRUE; \ + if ( isect > vtx0t ) { \ + vtx0t = isect; \ + collside = (s); \ + } \ + } \ + } \ +} + // cull/clip against -Z + curax = obj_pos.Z - bd_sphere; + dist0 = curax - prev_pos.Z; + dist1 = curax - test_pos.Z; + AXIAL_CLIP_T( 1 ); + + // cull/clip against +X + curax = obj_pos.X + bd_sphere; + dist0 = prev_pos.X - curax; + dist1 = test_pos.X - curax; + AXIAL_CLIP_T( 2 ); + + // cull/clip against -X + curax = obj_pos.X - bd_sphere; + dist0 = curax - prev_pos.X; + dist1 = curax - test_pos.X; + AXIAL_CLIP_T( 3 ); + + // cull/clip against +Y + curax = obj_pos.Y + bd_sphere; + dist0 = prev_pos.Y - curax; + dist1 = test_pos.Y - curax; + AXIAL_CLIP_T( 4 ); + + // cull/clip against -Y + curax = obj_pos.Y - bd_sphere; + dist0 = curax - prev_pos.Y; + dist1 = curax - test_pos.Y; + AXIAL_CLIP_T( 5 ); + + //NOTE: + // collside now contains the code of the collider side + // (-1 means the lineseg starts in the bounding box) + + //TODO: + // use already clipped segment in BSP test? + +#endif // CHECK_BOX_BEFORE_BSP_COLLISION + + // test actual object (polygon accurate) + + // transform line vertices into object-space + CalcOrthoInverse( cur_ship->ObjPosition, DestXmatrx ); + Vertex3 v0; + MtxVctMUL( DestXmatrx, &prev_pos, &v0 ); + Vertex3 v1; + MtxVctMUL( DestXmatrx, &test_pos, &v1 ); + + // assume collision if no bsp tree + CullBSPNode *node = cur_ship->AuxBSPTree; + if ( node == NULL ) + return FALSE; + + dword nodeid = 0; + geomv_t impact_t; + int collided = BSP_FindColliderLine( node, &v0, &v1, &nodeid, &impact_t ); + + // no collision? + if ( !collided ) + return TRUE; + + // react only if not started in solid leaf + if ( nodeid > 0 ) { + + // fetch collider + node = &cur_ship->AuxBSPTree[ nodeid ]; + + // calc collision position (object-space) + v1.X -= v0.X; + v1.Y -= v0.Y; + v1.Z -= v0.Z; + v1.X = v0.X + GEOMV_MUL( v1.X, impact_t ); + v1.Y = v0.Y + GEOMV_MUL( v1.Y, impact_t ); + v1.Z = v0.Z + GEOMV_MUL( v1.Z, impact_t ); + + // create hull impact particles + SFX_HullImpact( cur_ship, &v1, &node->plane ); + } + + // disable shield + test_shield = FALSE; +*/ + } + + return FALSE; +} + +//Check if any ship is hit by missile +void G_CollDet::_CheckShipMissileCollision() +{ + // check whether any other ship object is hit by missile + for ( cur_ship = TheWorld->FetchFirstShip(); cur_ship != NULL; ) { + + ShipObject *nextship = (ShipObject*) cur_ship->NextObj; + + obj_pos.X = cur_ship->ObjPosition[ 0 ][ 3 ]; + obj_pos.Y = cur_ship->ObjPosition[ 1 ][ 3 ]; + obj_pos.Z = cur_ship->ObjPosition[ 2 ][ 3 ]; + bd_sphere = cur_ship->BoundingSphere; + bd_sphere2 = cur_ship->BoundingSphere2; + + + int cur_ship_owner = GetOwnerFromHostOjbNumber( cur_ship->HostObjNumber ); + + // walk all missiles + MissileObject *walkmissiles = TheWorld->FetchFirstMissile(); + while ( walkmissiles != NULL ) { + + MissileObject *curmissile = walkmissiles; + walkmissiles = (MissileObject *) walkmissiles->NextObj; + + // owner can't be hit by own missile + if ( curmissile->Owner == cur_ship_owner ) { + continue; + } + + test_pos.X = curmissile->ObjPosition[ 0 ][ 3 ]; + test_pos.Y = curmissile->ObjPosition[ 1 ][ 3 ]; + test_pos.Z = curmissile->ObjPosition[ 2 ][ 3 ]; + + prev_pos.X = curmissile->PrevPosition.X; + prev_pos.Y = curmissile->PrevPosition.Y; + prev_pos.Z = curmissile->PrevPosition.Z; + + // shield or actual object? + SET_SHIELD_TESTING(); + + // actual collision detection + if ( _CheckShipProjectileDisjoint() ) { + continue; + } + + // collision response + _CollisionResponse_MissileShip( curmissile ); + } + + cur_ship = nextship; + } + +} + +// check whether any ship is hit by laser beam -------------------------------- +// +void G_CollDet::_CheckShipLaserCollision() +{ +#ifdef PARSEC_CLIENT + // check whether local player is hit by laser + if ( NetConnected ) { + + cur_ship = MyShip; + obj_pos.X = MyShip->ObjPosition[ 0 ][ 3 ]; + obj_pos.Y = MyShip->ObjPosition[ 1 ][ 3 ]; + obj_pos.Z = MyShip->ObjPosition[ 2 ][ 3 ]; + bd_sphere = MyShip->BoundingSphere; + bd_sphere2 = MyShip->BoundingSphere2; + + // walk all laser projectiles + LaserObject *walklasers = TheWorld->FetchFirstLaser(); + while ( walklasers != NULL ) { + + LaserObject *curlaser = walklasers; + walklasers = (LaserObject *) walklasers->NextObj; + + // can't be hit by own laser projectile + if ( curlaser->Owner == OWNER_LOCAL_PLAYER ) { + continue; + } + + test_pos.X = curlaser->ObjPosition[ 0 ][ 3 ]; + test_pos.Y = curlaser->ObjPosition[ 1 ][ 3 ]; + test_pos.Z = curlaser->ObjPosition[ 2 ][ 3 ]; + + prev_pos.X = curlaser->PrevPosition.X; + prev_pos.Y = curlaser->PrevPosition.Y; + prev_pos.Z = curlaser->PrevPosition.Z; + + // shield or actual object? + SET_SHIELD_TESTING(); + + // actual collision detection + if ( CheckShipProjectileDisjoint() ) { + continue; + } + + // collision response + CollisionLaserMyShip( curlaser ); + } + } +#endif // PARSEC_CLIENT + + // check whether any (other) shipobject is hit by laser + for ( cur_ship = TheWorld->FetchFirstShip(); cur_ship != NULL; ) { + + // get pointer to next ship in list, as the current might get removed upon collision + ShipObject* nextship = (ShipObject *) cur_ship->NextObj; + + obj_pos.X = cur_ship->ObjPosition[ 0 ][ 3 ]; + obj_pos.Y = cur_ship->ObjPosition[ 1 ][ 3 ]; + obj_pos.Z = cur_ship->ObjPosition[ 2 ][ 3 ]; + bd_sphere = cur_ship->BoundingSphere; + bd_sphere2 = cur_ship->BoundingSphere2; + + int cur_ship_owner = GetOwnerFromHostOjbNumber( cur_ship->HostObjNumber ); + + // walk all laser projectiles + for ( LaserObject *walklasers = TheWorld->FetchFirstLaser(); walklasers != NULL; ) { + + LaserObject* curlaser = walklasers; + walklasers = (LaserObject *) walklasers->NextObj; + + // owner can't be hit by own laser projectile + if ( curlaser->Owner == cur_ship_owner ) { + continue; + } + + test_pos.X = curlaser->ObjPosition[ 0 ][ 3 ]; + test_pos.Y = curlaser->ObjPosition[ 1 ][ 3 ]; + test_pos.Z = curlaser->ObjPosition[ 2 ][ 3 ]; + + prev_pos.X = curlaser->PrevPosition.X; + prev_pos.Y = curlaser->PrevPosition.Y; + prev_pos.Z = curlaser->PrevPosition.Z; + + // shield or actual object? + SET_SHIELD_TESTING(); + + // actual collision detection + if ( _CheckShipProjectileDisjoint() ) { + continue; + } + + // collision response + _CollisionResponse_LaserShip( curlaser ); + } + + cur_ship = nextship; + } +} + +// emp collision detection ---------------------------------------------------- +// +void G_CollDet::_CheckShipEmpCollision() +{ + // walk the list of EMP objects + ASSERT( TheWorld->m_CustmObjects != NULL ); + + CustomObject *precnode = TheWorld->m_CustmObjects; + CustomObject *walkobjs = (CustomObject *)TheWorld->m_CustmObjects->NextObj; + + // walk all lasers + while ( walkobjs != NULL ) { + if ((walkobjs->ObjectType == emp_type_id[ 0 ]) || + (walkobjs->ObjectType == emp_type_id[ 1 ]) || + (walkobjs->ObjectType == emp_type_id[ 2 ])) { + + Emp *tmpemp = (Emp *)walkobjs; + // got an emp object, now let's walk the ship objects + // and check for collisions. + + for ( cur_ship = TheWorld->FetchFirstShip(); cur_ship != NULL; ) { + // current ship's owner + int cur_ship_owner = GetOwnerFromHostOjbNumber( cur_ship->HostObjNumber ); + + // get pointer to next ship in list, as the current might get removed upon collision + ShipObject* nextship = (ShipObject *) cur_ship->NextObj; + + // cant collide with the owner + if(tmpemp->Owner != cur_ship_owner){ + // do the actual check + if(EmpShipCollision(tmpemp, cur_ship)){ + // HIT! Record damage and send out packets. + _CollisionResponse_EmpShip(tmpemp); + } + } + cur_ship = nextship; + } + } + + precnode = walkobjs; + walkobjs = (CustomObject *)walkobjs->NextObj; + } + + +} +// ship collided with EMP --------------------------------------------------- +// +void G_CollDet::_CollisionResponse_EmpShip( Emp *curemp ) +{ + ASSERT( curemp != NULL ); + ASSERT( cur_ship != NULL ); + + int hitpoints = curemp->damage; + + if ( ( hitpoints -= cur_ship->MegaShieldAbsorption ) < 0 ) { + hitpoints = 0; + } + + // apply damage + if ( cur_ship->CurDamage <= cur_ship->MaxDamage ) { + MSGOUT("Hitpoints deduction is %d", hitpoints); + MSGOUT("Damage Calc Ref Frames is %d",TheSimulator->GetThisFrameRefFrames()*2); + cur_ship->CurDamageFrac += (TheSimulator->GetThisFrameRefFrames()*2) * hitpoints; + MSGOUT("Damage Fractional is %d",cur_ship->CurDamageFrac); + cur_ship->CurDamage += cur_ship->CurDamageFrac >> 16; + MSGOUT("Doing %d damage to ship", cur_ship->CurDamageFrac >> 16); + cur_ship->CurDamageFrac &= 0xffff; + + + // new less confusing shit... + //cur_ship->CurDamage += hitpoints; + } + + // check whether the ship should explode + if ( cur_ship->CurDamage > cur_ship->MaxDamage ) { + int nClientID_Attacker = curemp->Owner; + int nClientID_Downed = GetOwnerFromHostOjbNumber( cur_ship->HostObjNumber ); + + // create the extras this client left + TheGameExtraManager->OBJ_CreateShipExtras( cur_ship ); + + E_SimPlayerInfo* pSimPlayerInfo = TheSimulator->GetSimPlayerInfo( nClientID_Downed ); + + // maintain stats + TheGame->RecordKill ( nClientID_Attacker ); + TheGame->RecordDeath( nClientID_Downed, nClientID_Attacker ); + + // fill rudimentary RE + RE_PlayerStatus ps; + memset( &ps, 0, sizeof ( RE_PlayerStatus ) ); + ps.player_status = PLAYER_CONNECTED; + ps.senderid = nClientID_Downed; + ps.params[ 0 ] = SHIP_DOWNED; + pSimPlayerInfo->PerformUnjoin( &ps ); + + // ignore joins from client, until he himself sent an unjoin + //FIXME: redesign this !! + pSimPlayerInfo->IgnoreJoinUntilUnjoinFromClient(); + + // force a client resync of the downed client + TheSimulator->GetSimClientState( nClientID_Downed )->SetClientResync(); + + //FIXME: + //cur_ship->pDist->UpdateMODE_REFRESH(); + + //FIXME: this should be called GAME_LOGOUT or so to indicate that these messages + // are the most important ones for processing user stats + MSGOUT( "%s was shot down by %s's EMP", TheConnManager->GetClientName( nClientID_Downed ), TheConnManager->GetClientName( nClientID_Attacker ) ); + + } +} + + +// ship collided with Missile --------------------------------------------------- +// +void G_CollDet::_CollisionResponse_MissileShip( MissileObject *curmissile ) +{ + ASSERT( curmissile != NULL ); + ASSERT( cur_ship != NULL ); + + int hitpoints = curmissile->HitPoints; + if ( ( hitpoints -= cur_ship->MegaShieldAbsorption ) < 0 ) { + hitpoints = 0; + } + + // apply damage + if ( cur_ship->CurDamage <= cur_ship->MaxDamage ) { + cur_ship->CurDamage += hitpoints; + } + + // check whehter the ship should explode + if ( cur_ship->CurDamage > cur_ship->MaxDamage ) { + + + int nClientID_Attacker = curmissile->Owner; + int nClientID_Downed = GetOwnerFromHostOjbNumber( cur_ship->HostObjNumber ); + + // create the extras this client left + TheGameExtraManager->OBJ_CreateShipExtras( cur_ship ); + + E_SimPlayerInfo* pSimPlayerInfo = TheSimulator->GetSimPlayerInfo( nClientID_Downed ); + + // maintain stats + TheGame->RecordKill ( nClientID_Attacker ); + TheGame->RecordDeath( nClientID_Downed, nClientID_Attacker ); + + // fill rudimentary RE + RE_PlayerStatus ps; + memset( &ps, 0, sizeof ( RE_PlayerStatus ) ); + ps.player_status = PLAYER_CONNECTED; + ps.senderid = nClientID_Downed; + ps.params[ 0 ] = SHIP_DOWNED; + pSimPlayerInfo->PerformUnjoin( &ps ); + + // ignore joins from client, until he himself sent an unjoin + //FIXME: redesign this !! + pSimPlayerInfo->IgnoreJoinUntilUnjoinFromClient(); + + // force a client resync of the downed client + TheSimulator->GetSimClientState( nClientID_Downed )->SetClientResync(); + + //FIXME: + //cur_ship->pDist->UpdateMODE_REFRESH(); + + //FIXME: this should be called GAME_LOGOUT or so to indicate that these messages + // are the most important ones for processing user stats + MSGOUT( "%s was shot down by %s's Missile", TheConnManager->GetClientName( nClientID_Downed ), TheConnManager->GetClientName( nClientID_Attacker ) ); + } + + // release the E_Distributable ( distribute removal ) + //MSGOUT( "G_CollDet::_CollisionResponse_MissileShip() calls ReleaseDistributable()" ); + TheSimNetOutput->ReleaseDistributable( curmissile->pDist ); + + TheWorld->KillSpecificObject( curmissile->ObjectNumber, TheWorld->m_MisslObjects ); + +} + +// ship collided with laser --------------------------------------------------- +// +void G_CollDet::_CollisionResponse_LaserShip( LaserObject *curlaser ) +{ + ASSERT( curlaser != NULL ); + ASSERT( cur_ship != NULL ); + + int hitpoints = curlaser->HitPoints; + if ( ( hitpoints -= cur_ship->MegaShieldAbsorption ) < 0 ) { + hitpoints = 0; + } + + // apply damage + if ( cur_ship->CurDamage <= cur_ship->MaxDamage ) { + cur_ship->CurDamage += hitpoints; + } + + // check whehter the ship should explode + if ( cur_ship->CurDamage > cur_ship->MaxDamage ) { + +#ifdef PARSEC_CLIENT + KillDurationWeapons( MyShip ); + OBJ_CreateShipExtras( MyShip ); + NET_SetPlayerKillStat( curlaser->Owner, 1 ); + strcpy( paste_str, killed_by_str ); + strcat( paste_str, NET_FetchPlayerName( curlaser->Owner ) ); + ShowMessage( paste_str ); +#else // !PARSEC_CLIENT + + int nClientID_Attacker = curlaser->Owner; + int nClientID_Downed = GetOwnerFromHostOjbNumber( cur_ship->HostObjNumber ); + + // create the extras this client left + TheGameExtraManager->OBJ_CreateShipExtras( cur_ship ); + + E_SimPlayerInfo* pSimPlayerInfo = TheSimulator->GetSimPlayerInfo( nClientID_Downed ); + + // maintain stats + TheGame->RecordKill ( nClientID_Attacker ); + TheGame->RecordDeath( nClientID_Downed, nClientID_Attacker ); + + // fill rudimentary RE + RE_PlayerStatus ps; + memset( &ps, 0, sizeof ( RE_PlayerStatus ) ); + ps.player_status = PLAYER_CONNECTED; + ps.senderid = nClientID_Downed; + ps.params[ 0 ] = SHIP_DOWNED; + pSimPlayerInfo->PerformUnjoin( &ps ); + + // ignore joins from client, until he himself sent an unjoin + //FIXME: redesign this !! + pSimPlayerInfo->IgnoreJoinUntilUnjoinFromClient(); + + // force a client resync of the downed client + TheSimulator->GetSimClientState( nClientID_Downed )->SetClientResync(); + + //FIXME: + //cur_ship->pDist->UpdateMODE_REFRESH(); + + //FIXME: this should be called GAME_LOGOUT or so to indicate that these messages + // are the most important ones for processing user stats + MSGOUT( "%s was shot down by %s's laser", TheConnManager->GetClientName( nClientID_Downed ), + TheConnManager->GetClientName( nClientID_Attacker ) ); + +#endif // !PARSEC_CLIENT + } + + +#ifdef PARSEC_CLIENT + + //if ( cur_ship->CurDamage <= cur_ship->MaxDamage ) { + // OBJ_EventShipImpact( cur_ship, test_shield ); + //} + + NET_RmEvKillObject( curlaser->HostObjNumber, LASER_LIST ); + +#else // !PARSEC_CLIENT + + // release the E_Distributable ( distribute removal ) + //MSGOUT( "G_CollDet::_CollisionResponse_LaserShip() calls ReleaseDistributable()" ); + TheSimNetOutput->ReleaseDistributable( curlaser->pDist ); +#endif // !PARSEC_CLIENT + + TheWorld->KillSpecificObject( curlaser->ObjectNumber, TheWorld->m_LaserObjects ); + +/* if ( cur_ship->ExplosionCount == 0 ) { + +#ifdef PARSEC_CLIENT + + //FIXME: pure client gamecode + OBJ_EventShipImpact( cur_ship, test_shield ); + +#endif // PARSEC_CLIENT + + if ( !NetConnected ) { + cur_ship->CurDamage += hitpoints; + if ( cur_ship->CurDamage > cur_ship->MaxDamage ) { + LetShipExplode( cur_ship ); + ShowMessage( laser_downed_ship_str ); + AUD_ShipDestroyed( cur_ship ); + AUD_JimCommenting( JIM_COMMENT_EATTHIS ); + } + } + } +*/ +} +// local ship collided with proximity mine ------------------------------------ +// +void G_CollDet::_CollisionResponse_MineShip( Mine1Obj *curmine ) +{ + ASSERT( curmine != NULL ); + + int cur_ship_owner = GetOwnerFromHostOjbNumber( cur_ship->HostObjNumber ); + + + int hitpoints = curmine->HitPoints; + if ( ( hitpoints -= cur_ship->MegaShieldAbsorption ) < 0 ) { + hitpoints = 0; + } + + if ( cur_ship->CurDamage <= cur_ship->MaxDamage ) { + + cur_ship->CurDamage += hitpoints; + if ( cur_ship->CurDamage > cur_ship->MaxDamage ) { + int nClientID_Attacker = curmine->Owner; + int nClientID_Downed = GetOwnerFromHostOjbNumber( cur_ship->HostObjNumber ); + + // create the extras this client left + TheGameExtraManager->OBJ_CreateShipExtras( cur_ship ); + + E_SimPlayerInfo* pSimPlayerInfo = TheSimulator->GetSimPlayerInfo( nClientID_Downed ); + + if(cur_ship_owner != curmine->Owner) { + // maintain stats + TheGame->RecordKill ( nClientID_Attacker ); + TheGame->RecordDeath( nClientID_Downed, nClientID_Attacker ); + } + TheGame->RecordDeath( nClientID_Downed, nClientID_Attacker ); + + // fill rudimentary RE + RE_PlayerStatus ps; + memset( &ps, 0, sizeof ( RE_PlayerStatus ) ); + ps.player_status = PLAYER_CONNECTED; + ps.senderid = nClientID_Downed; + ps.params[ 0 ] = SHIP_DOWNED; + pSimPlayerInfo->PerformUnjoin( &ps ); + + // ignore joins from client, until he himself sent an unjoin + //FIXME: redesign this !! + pSimPlayerInfo->IgnoreJoinUntilUnjoinFromClient(); + + // force a client resync of the downed client + TheSimulator->GetSimClientState( nClientID_Downed )->SetClientResync(); + + //FIXME: + //cur_ship->pDist->UpdateMODE_REFRESH(); + + //FIXME: this should be called GAME_LOGOUT or so to indicate that these messages + // are the most important ones for processing user stats + MSGOUT( "%s was destroyed by %s's mine", TheConnManager->GetClientName( nClientID_Downed ), + TheConnManager->GetClientName( nClientID_Attacker ) ); + + } + } + // release the E_Distributable ( distribute removal ) + // MSGOUT( "G_CollDet::_CollisionResponse_MineShip() calls ReleaseDistributable()" ); + curmine->pDist->WillBeSentToOwner(); + TheSimNetOutput->ReleaseDistributable( curmine->pDist ); + TheWorld->KillSpecificObject( curmine->ObjectNumber, TheWorld->m_ExtraObjects ); + + return; +} + +void G_CollDet::OBJ_ShipHelixDamage( ShipObject *shippo, int owner ) +{ + ASSERT( shippo != NULL ); + + //NOTE: + // this function is used by + // PART_ANI::LinearParticleCollision(). + +#define HELIX_HITPOINTS_PER_PARTICLE 1 + + // no damage at all if megashield active + if ( shippo->MegaShieldAbsorption > 0 ) + return; + + if (shippo->ExplosionCount == 0 ) { + + shippo->CurDamage += HELIX_HITPOINTS_PER_PARTICLE; + if ( shippo->CurDamage > shippo->MaxDamage ) { + // needed for explosions caused by particles + shippo->DelayExplosion = 1; + + int nClientID_Attacker = owner; + int nClientID_Downed = GetOwnerFromHostOjbNumber( shippo->HostObjNumber ); + + // create the extras this client left + TheGameExtraManager->OBJ_CreateShipExtras( shippo ); + + E_SimPlayerInfo* pSimPlayerInfo = TheSimulator->GetSimPlayerInfo( nClientID_Downed ); + + // maintain stats + TheGame->RecordKill ( nClientID_Attacker ); + TheGame->RecordDeath( nClientID_Downed, nClientID_Attacker ); + + // fill rudimentary RE + RE_PlayerStatus ps; + memset( &ps, 0, sizeof ( RE_PlayerStatus ) ); + ps.player_status = PLAYER_CONNECTED; + ps.senderid = nClientID_Downed; + ps.params[ 0 ] = SHIP_DOWNED; + pSimPlayerInfo->PerformUnjoin( &ps ); + + // ignore joins from client, until he himself sent an unjoin + //FIXME: redesign this !! + pSimPlayerInfo->IgnoreJoinUntilUnjoinFromClient(); + + // force a client resync of the downed client + TheSimulator->GetSimClientState( nClientID_Downed )->SetClientResync(); + MSGOUT( "%s was shot down by %s's Helix Cannon", TheConnManager->GetClientName( nClientID_Downed ),TheConnManager->GetClientName( nClientID_Attacker ) ); + } + } +} + +// inflict damage upon ship hit by swarm missiles ----------------------------- +// +void G_CollDet::OBJ_ShipSwarmDamage( ShipObject *shippo, int owner ) +{ + ASSERT( shippo != NULL ); + + //NOTE: + // this function is used by + // S_SWARM::SWARM_Animate(). + +#define SWARM_HITPOINTS_PER_PARTICLE 1 + + // no damage at all if megashield active + if ( shippo->MegaShieldAbsorption > 0 ) + return; + + if ( shippo->ExplosionCount == 0 ) { + + shippo->CurDamage += SWARM_HITPOINTS_PER_PARTICLE; + if ( shippo->CurDamage > shippo->MaxDamage ) { + + // schedule explosion + + // needed for explosions caused by particles + shippo->DelayExplosion = 1; + + int nClientID_Attacker = owner; + int nClientID_Downed = GetOwnerFromHostOjbNumber( shippo->HostObjNumber ); + + // create the extras this client left + TheGameExtraManager->OBJ_CreateShipExtras( shippo ); + + E_SimPlayerInfo* pSimPlayerInfo = TheSimulator->GetSimPlayerInfo( nClientID_Downed ); + + // maintain stats + TheGame->RecordKill ( nClientID_Attacker ); + TheGame->RecordDeath( nClientID_Downed, nClientID_Attacker ); + + // fill rudimentary RE + RE_PlayerStatus ps; + memset( &ps, 0, sizeof ( RE_PlayerStatus ) ); + ps.player_status = PLAYER_CONNECTED; + ps.senderid = nClientID_Downed; + ps.params[ 0 ] = SHIP_DOWNED; + pSimPlayerInfo->PerformUnjoin( &ps ); + + // ignore joins from client, until he himself sent an unjoin + //FIXME: redesign this !! + pSimPlayerInfo->IgnoreJoinUntilUnjoinFromClient(); + + // force a client resync of the downed client + TheSimulator->GetSimClientState( nClientID_Downed )->SetClientResync(); + MSGOUT( "%s was destroyed by %s's Swarm missiles", TheConnManager->GetClientName( nClientID_Downed ), TheConnManager->GetClientName( nClientID_Attacker ) ); + } + } +} + +// inflict damage upon ship hit by lightning beam ----------------------------- +// +void G_CollDet::OBJ_ShipLightningDamage( ShipObject *shippo, int owner ) +{ + ASSERT( shippo != NULL ); + + //NOTE: + // this function is used by + // PART_ANI::CheckLightningParticleShipCollision(). + +#define LIGHTNING_HITPOINTS_PER_REFFRAME 6000 + + // no damage at all if megashield active + if ( shippo->MegaShieldAbsorption > 0 ) + return; + + if ( shippo->ExplosionCount == 0 ) { + + shippo->CurDamageFrac += TheSimulator->GetThisFrameRefFrames() * LIGHTNING_HITPOINTS_PER_REFFRAME; + shippo->CurDamage += shippo->CurDamageFrac >> 16; + shippo->CurDamageFrac &= 0xffff; + + if ( shippo->CurDamage > shippo->MaxDamage ) { + // needed for explosions caused by particles + shippo->DelayExplosion = 1; + + int nClientID_Attacker = owner; + int nClientID_Downed = GetOwnerFromHostOjbNumber( shippo->HostObjNumber ); + + // create the extras this client left + TheGameExtraManager->OBJ_CreateShipExtras( shippo ); + + E_SimPlayerInfo* pSimPlayerInfo = TheSimulator->GetSimPlayerInfo( nClientID_Downed ); + + // maintain stats + TheGame->RecordKill ( nClientID_Attacker ); + TheGame->RecordDeath( nClientID_Downed, nClientID_Attacker ); + + // fill rudimentary RE + RE_PlayerStatus ps; + memset( &ps, 0, sizeof ( RE_PlayerStatus ) ); + ps.player_status = PLAYER_CONNECTED; + ps.senderid = nClientID_Downed; + ps.params[ 0 ] = SHIP_DOWNED; + pSimPlayerInfo->PerformUnjoin( &ps ); + + // ignore joins from client, until he himself sent an unjoin + //FIXME: redesign this !! + pSimPlayerInfo->IgnoreJoinUntilUnjoinFromClient(); + + // force a client resync of the downed client + TheSimulator->GetSimClientState( nClientID_Downed )->SetClientResync(); + MSGOUT( "%s was cooked %s's Lightning Cannon", TheConnManager->GetClientName( nClientID_Downed ),TheConnManager->GetClientName( nClientID_Attacker ) ); + } + } +} + +// check if ship collected extra ---------------------------------------------- +// +void G_CollDet::_CheckShipExtraCollision() +{ + // check whether any (other) shipobject is hit by laser + for ( cur_ship = TheWorld->FetchFirstShip(); cur_ship != NULL; ) { + + // fetch location of local ship + geomv_t objX = cur_ship->ObjPosition[ 0 ][ 3 ]; + geomv_t objY = cur_ship->ObjPosition[ 1 ][ 3 ]; + geomv_t objZ = cur_ship->ObjPosition[ 2 ][ 3 ]; + + //MSGOUT( "G_CollDet::_CheckShipExtraCollision(): ship %d is at %.2f/%.2f/%.2f", cur_ship->HostObjNumber, objX, objY, objZ ); + + // fetch radius of bounding sphere of local ship + geomv_t bsphere = cur_ship->BoundingSphere; + + ASSERT( TheWorld->m_ExtraObjects != NULL ); + + // walk list of extras + ExtraObject *precnode = TheWorld->m_ExtraObjects; + while ( precnode->NextObj != NULL ) { + + ASSERT( OBJECT_TYPE_EXTRA( precnode->NextObj ) ); + + // get pointer to current extra + ExtraObject *curextra = (ExtraObject *) precnode->NextObj; + ASSERT( curextra != NULL ); + + // fetch position of extra + geomv_t extraX = curextra->ObjPosition[ 0 ][ 3 ]; + geomv_t extraY = curextra->ObjPosition[ 1 ][ 3 ]; + geomv_t extraZ = curextra->ObjPosition[ 2 ][ 3 ]; + + // do simple collision test (bounding box) + if ( extraX < ( objX - bsphere ) ) { precnode = curextra; continue; } + if ( extraX > ( objX + bsphere ) ) { precnode = curextra; continue; } + if ( extraY < ( objY - bsphere ) ) { precnode = curextra; continue; } + if ( extraY > ( objY + bsphere ) ) { precnode = curextra; continue; } + if ( extraZ < ( objZ - bsphere ) ) { precnode = curextra; continue; } + if ( extraZ > ( objZ + bsphere ) ) { precnode = curextra; continue; } + + // do actual bounding sphere collision test + Vector3 vecdist; + vecdist.X = extraX - objX; + vecdist.Y = extraY - objY; + vecdist.Z = extraZ - objZ; + + geomv_t dist2 = DOT_PRODUCT( &vecdist, &vecdist ); + if ( dist2 > cur_ship->BoundingSphere2 ) { + precnode = curextra; + continue; + } + + // perform action according to extra type and determine message to show + char *text = TheGameExtraManager->CollectExtra( cur_ship, curextra ); + + // show message saying what type of extra has been collected + if (( text != NULL ) || ( curextra->ObjectType == MINE1TYPE )) { + + //FIXME: implement server->client reliable "got ..." message + //fgfg + + } else { + + MSGOUT( "CheckShipExtraCollision(): invalid extra type: %0x.", curextra->ObjectType ); + } + + // remove extra object + if( curextra->ObjectType == MINE1TYPE ) //Mine collision + _CollisionResponse_MineShip((Mine1Obj *) curextra); + else + TheGameExtraManager->OBJ_KillExtra( precnode, TRUE ); + } + + cur_ship = (ShipObject*)cur_ship->NextObj; + } +} + +// check if point is contained in ship's bounding sphere ---------------------- +// +int G_CollDet::PRT_ParticleInBoundingSphere( ShipObject *shippo, Vertex3& point ) +{ + ASSERT( shippo != NULL ); + + geomv_t objX = shippo->ObjPosition[ 0 ][ 3 ]; + geomv_t objY = shippo->ObjPosition[ 1 ][ 3 ]; + geomv_t objZ = shippo->ObjPosition[ 2 ][ 3 ]; + + geomv_t bdsphere = shippo->BoundingSphere; + geomv_t bdsphere2 = shippo->BoundingSphere2; + + if ( point.X < ( objX - bdsphere ) ) return FALSE; + if ( point.X > ( objX + bdsphere ) ) return FALSE; + if ( point.Y < ( objY - bdsphere ) ) return FALSE; + if ( point.Y > ( objY + bdsphere ) ) return FALSE; + if ( point.Z < ( objZ - bdsphere ) ) return FALSE; + if ( point.Z > ( objZ + bdsphere ) ) return FALSE; + + Vector3 dvec; + dvec.X = point.X - objX; + dvec.Y = point.Y - objY; + dvec.Z = point.Z - objZ; + + return ( DOT_PRODUCT( &dvec, &dvec ) < bdsphere2 ); +} + +// brute force collision detection of linear particle with all ships ---------- +// +void G_CollDet::LinearParticleCollision( linear_pcluster_s *cluster, int pid ) +{ + ASSERT( cluster != NULL ); + ASSERT( ( pid >= 0 ) && ( pid < cluster->numel ) ); + ASSERT( cluster->rep[ pid ].flags & PARTICLE_COLLISION ); + + //NOTE: + // this is set as callback by PART_WFX::MaintainHelix() + // to detect collisions of helix particles. + + int owner = cluster->rep[ pid ].owner; + + // check shiplist + ShipObject *walkships = TheWorld->FetchFirstShip(); + for ( ; walkships; walkships = (ShipObject*) walkships->NextObj ) { + + // prevent collision with owner of particle + if ( ( GetObjectOwner( walkships ) == (dword)owner ) ) + continue; + + if ( !PRT_ParticleInBoundingSphere( walkships, cluster->rep[ pid ].position ) ) + continue; + + // disable particle + cluster->rep[ pid ].flags &= ~PARTICLE_ACTIVE; + + switch ( cluster->rep[ pid ].flags & PARTICLE_IS_MASK ) { + + case PARTICLE_IS_HELIX : + // MSGOUT("G_CollDet::LinearParticleCollision(): Helix particle collision with player %d",GetObjectOwner( walkships )); + OBJ_ShipHelixDamage( walkships, owner ); + break; + case PARTICLE_IS_PHOTON : + OBJ_ShipPhotonDamage( walkships, owner ); + break; + default: + break; + } + } +} + +// inflict damage upon ship hit by photon weapon ------------------------------ +// +void G_CollDet::OBJ_ShipPhotonDamage( ShipObject *shippo, int owner ) +{ + ASSERT( shippo != NULL ); + + //NOTE: + // this function is used by + // PART_ANI::LinearParticleCollision(). + +#define PHOTON_HITPOINTS_PER_PARTICLE 3 + + // no damage at all if megashield active + if ( shippo->MegaShieldAbsorption > 0 ) + return; + if (shippo->ExplosionCount == 0 ) { + + shippo->CurDamage += PHOTON_HITPOINTS_PER_PARTICLE; + if ( shippo->CurDamage > shippo->MaxDamage ) { + // needed for explosions caused by particles + shippo->DelayExplosion = 1; + + int nClientID_Attacker = owner; + int nClientID_Downed = GetOwnerFromHostOjbNumber( shippo->HostObjNumber ); + + // create the extras this client left + TheGameExtraManager->OBJ_CreateShipExtras( shippo ); + + E_SimPlayerInfo* pSimPlayerInfo = TheSimulator->GetSimPlayerInfo( nClientID_Downed ); + + // maintain stats + TheGame->RecordKill ( nClientID_Attacker ); + TheGame->RecordDeath( nClientID_Downed, nClientID_Attacker ); + + // fill rudimentary RE + RE_PlayerStatus ps; + memset( &ps, 0, sizeof ( RE_PlayerStatus ) ); + ps.player_status = PLAYER_CONNECTED; + ps.senderid = nClientID_Downed; + ps.params[ 0 ] = SHIP_DOWNED; + pSimPlayerInfo->PerformUnjoin( &ps ); + + // ignore joins from client, until he himself sent an unjoin + //FIXME: redesign this !! + pSimPlayerInfo->IgnoreJoinUntilUnjoinFromClient(); + + // force a client resync of the downed client + TheSimulator->GetSimClientState( nClientID_Downed )->SetClientResync(); + MSGOUT( "%s was mowed down by %s's Photon Cannon", TheConnManager->GetClientName( nClientID_Downed ),TheConnManager->GetClientName( nClientID_Attacker ) ); + } + } +} + + +// check if ship entered energy field ----------------------------------------- +// +void G_CollDet::CheckEnergyField( pcluster_s *cluster ) +{ + if ( ( cluster->type & CT_TYPEMASK ) == CT_PARTICLE_SPHERE ) { + sphereobj_pcluster_s *objcluster = (sphereobj_pcluster_s *) cluster; + if ( objcluster->animtype == SAT_ENERGYFIELD_SPHERE ) { + // check shiplist + ShipObject *walkships = TheWorld->FetchFirstShip(); + for ( ; walkships; walkships = (ShipObject*) walkships->NextObj ) { + if ( PRT_ParticleInBoundingSphere( walkships, objcluster->origin ) ) { + TheGameExtraManager->_CollectBoostEnergyField(walkships, TheSimulator->GetThisFrameRefFrames() * 2 ); + } + } + } + } +} + +int G_CollDet::CheckLightningParticleShipCollision( Vertex3& particlepos, int owner ) +{ + // check shiplist + ShipObject *walkships = TheWorld->FetchFirstShip(); + for ( ; walkships; walkships = (ShipObject*) walkships->NextObj ) { + + // prevent collision with owner of particle + if ( ( GetObjectOwner( walkships ) == (dword)owner ) ) + continue; + + if ( !PRT_ParticleInBoundingSphere( walkships, particlepos ) ) + continue; + + OBJ_ShipLightningDamage( walkships, owner ); + + return TRUE; + } + + return FALSE; +} + +// perform collision detection tests ------------------------------------------ +// +void G_CollDet::OBJ_CheckCollisions() +{ + //NOTE: + // this function gets called once per frame by the + // game loop (G_MAIN.C). + + // check if laser beam hits ship + _CheckShipLaserCollision(); + + // check if missile hits ship + _CheckShipMissileCollision(); + + // check if two ships collide + //CheckShipShipCollision(); + + // check if ship collected extra + _CheckShipExtraCollision(); + + // check collisions for custom objects + //CheckCustomCollisions(); + + // check for emp collisions + _CheckShipEmpCollision(); +} + diff --git a/src/parsec_server/e_connmanager.cpp b/src/parsec_server/e_connmanager.cpp new file mode 100755 index 0000000..77d4042 --- /dev/null +++ b/src/parsec_server/e_connmanager.cpp @@ -0,0 +1,819 @@ +/* + * PARSEC - Connection Manager + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:46 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2001-2002 + * + * 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 <stdlib.h> +#include <string.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" + +// UNP header +#include "net_wrap.h" + +// server defs +#include "e_defs.h" + +// network code config +#include "net_conf.h" + +// net game header +#include "net_game_sv.h" + +// mathematics header +#include "utl_math.h" + +// local module header +#include "e_connmanager.h" + +// proprietary module headers +#include "net_csdf.h" +#include "net_packetdriver.h" +#include "net_util.h" +#include "g_main_sv.h" +#include "e_gameserver.h" +#include "e_simulator.h" +#include "e_simnetoutput.h" +#include "e_packethandler.h" +#include "sys_refframe_sv.h" + +// max. number of challenge infos we keep ------------------------------------- +// +#define MAX_NUM_CHALLENGEINFOS 1024 + +// timeout for sent challenges ------------------------------------------------ +// +#define CHALLENGE_TIMEOUT_FRAMES FRAME_MEASURE_TIMEBASE * 10 + + +// ---------------------------------------------------------------------------- +// E_ClientInfo public methods +// ---------------------------------------------------------------------------- + + +// copy info from the connection information ---------------------------------- +// +void E_ClientInfo::CopyClientConnectInfo( E_ClientConnectInfo* pClientConnectInfo ) +{ + ASSERT( pClientConnectInfo != NULL ); + + m_nVersionMajor = pClientConnectInfo->m_nVersionMajor; + m_nVersionMinor = pClientConnectInfo->m_nVersionMinor; + + strncpy( m_szName, pClientConnectInfo->m_szName, MAX_PLAYER_NAME ); + m_szName[ MAX_PLAYER_NAME ] = 0; + + strncpy( m_szOSLine, pClientConnectInfo->m_szOSLine, MAX_OSNAME_LEN ); + m_szOSLine[ MAX_OSNAME_LEN ] = 0; + + strncpy( m_szHostName, pClientConnectInfo->m_szHostName, MAX_HOSTNAME_LEN ); + m_szHostName[ MAX_HOSTNAME_LEN ] = 0; + + NODE_Copy( &m_node, &pClientConnectInfo->m_node ); + m_challenge = pClientConnectInfo->m_challenge; + m_nRecvRate = pClientConnectInfo->m_nRecvRate; + m_nSendFreq = pClientConnectInfo->m_nSendFreq; +} + +// mark the client alive ------------------------------------------------------ +// +void E_ClientInfo::MarkAlive() +{ + m_nAliveCounter = SYSs_GetRefFrameCount() + MAX_ALIVE_COUNTER; +} + +// check whether the client is alive ------------------------------------------ +// +int E_ClientInfo::IsAlive() +{ + return ( m_nAliveCounter >= SYSs_GetRefFrameCount() ); +} + + +// ---------------------------------------------------------------------------- +// E_ConnManager public methods +// ---------------------------------------------------------------------------- + +// standard ctor -------------------------------------------------------------- +// +E_ConnManager::E_ConnManager() + : m_nMaxNumChallengeInfos( MAX_NUM_CHALLENGEINFOS ), + m_nCurChallengeInfo( 0 ), + m_ClientInfos( NULL ), + m_inDisconnectClient( FALSE ) +{ + m_ChallengInfos = new E_ClientChallengeInfo[ MAX_NUM_CHALLENGEINFOS ]; + m_nNumConnected = 0; + m_ClientInfos = new E_ClientInfo[ MAX_NUM_CLIENTS ]; +} + + +// standard dtor -------------------------------------------------------------- +// +E_ConnManager::~E_ConnManager() +{ + delete []m_ChallengInfos; + delete []m_ClientInfos; +} + + +// request a challenge for a client ------------------------------------------- +// +int E_ConnManager::RequestChallenge( node_t* clientnode ) +{ + ASSERT( clientnode != NULL ); + + E_ClientChallengeInfo* pCurChallengeInfo = &m_ChallengInfos[ m_nCurChallengeInfo ]; + + // generate a new unique challenge + pCurChallengeInfo->m_challenge = RAND(); + pCurChallengeInfo->m_frame_generated = SYSs_GetRefFrameCount(); + NODE_Copy( &pCurChallengeInfo->m_node, clientnode ); + + // point to next challenge info + m_nCurChallengeInfo++; + + // wrap around + if ( m_nCurChallengeInfo == m_nMaxNumChallengeInfos ) { + m_nCurChallengeInfo = 0; + } + + // send the response back to the client + ThePacketHandler->SendChallengeResponse( pCurChallengeInfo, clientnode ); + + return TRUE; +} + +// check whether the client connection is valid ------------------------------- +// +int E_ConnManager::CheckClientConnect( E_ClientConnectInfo* pClientConnectInfo ) +{ + ASSERT( pClientConnectInfo != NULL ); + + // default to connection failed + int rc = FALSE; + + // check whether challenge is found + E_ClientChallengeInfo* pFoundChallengeInfo = NULL; + if ( _IsChallengeCorrect( pClientConnectInfo, &pFoundChallengeInfo ) == FALSE ) { + + // output to logfile + if ( pFoundChallengeInfo == NULL ) { + MSGOUT( "challenge for client %s could not be found\n", pClientConnectInfo->m_szHostName ); + } else { + MSGOUT( "challenge %d for joining client %s invalid - should be %d\n", + pClientConnectInfo->m_challenge, + pClientConnectInfo->m_szHostName, + pFoundChallengeInfo->m_challenge ); + } + + // set return code + ThePacketHandler->SendConnectResponse( CONN_CHALLENGE_INVALID, pClientConnectInfo ); + + // connect failed + rc = FALSE; + } + // check whether client is compatible with this version + else if ( _IsClientCompatible( pClientConnectInfo ) == FALSE ) { + + // output to logfile + MSGOUT( "client %s with incompatible version %d.%d tried to join\n", pClientConnectInfo->m_szHostName, + pClientConnectInfo->m_nVersionMajor, pClientConnectInfo->m_nVersionMinor ); + + // send the response + ThePacketHandler->SendConnectResponse( CONN_CLIENT_INCOMAPTIBLE, pClientConnectInfo ); + + // connect failed + rc = FALSE; + } + // check whether the client is banned + else if ( _IsClientBanned( pClientConnectInfo ) == TRUE ) { + + // output to logfile + MSGOUT( "banned client %s tried to join\n", pClientConnectInfo->m_szHostName ); + + // send the response + ThePacketHandler->SendConnectResponse( CONN_CLIENT_BANNED, pClientConnectInfo ); + + // connect failed + rc = FALSE; + } + // check whether the server is not yet full + else if ( m_nNumConnected < MAX_NUM_CLIENTS ) { + + // output to logfile + MSGOUT( "client %s(%s) wants to connect, name: %s\n", pClientConnectInfo->m_szHostName, NODE_Print( &pClientConnectInfo->m_node ), pClientConnectInfo->m_szName ); + + // ensure the client is not yet connected + _EnsureClientIsDisconnected( pClientConnectInfo ); + + // check whether the name is already taken + if ( ( m_nNumConnected > 0 ) && _FindClientName( pClientConnectInfo->m_szName ) != -1 ) { + + ThePacketHandler->SendConnectResponse( CONN_NAME_TAKEN, pClientConnectInfo ); + + // connect failed + rc = FALSE; + } else { + + // add the client + int nClientID = _ConnectClient( pClientConnectInfo ); + + // send the response + if ( nClientID == -1 ) { + + // output to logfile + MSGOUT( "Error: _ConnectClient() failed but MaxPlayers not reached" ); + MSGOUT( "client %s refused - server full\n", pClientConnectInfo->m_szHostName ); + + // send the SERVER_FULL response + ThePacketHandler->SendConnectResponse( CONN_SERVER_FULL, pClientConnectInfo ); + + // connect failed + rc = FALSE; + + } else { + // send the OK response + ThePacketHandler->SendConnectResponse( CONN_OK, pClientConnectInfo ); + + //FIXME: this is already done in _ConnectClient + // reset the stream to communicate with the client from now on + //ThePacketDriver->ResetStream( pClientConnectInfo->m_selected_slot ); + + // send connect notifications to the other clients and the list of connected clients to the newly connected + _NotifyClientConnected( nClientID ); + + // connect OK + rc = TRUE; + } + } + } + // the server is full + else { + + // output to logfile + MSGOUT( "client %s refused - server full\n", pClientConnectInfo->m_szHostName ); + + // send the response + ThePacketHandler->SendConnectResponse( CONN_SERVER_FULL, pClientConnectInfo ); + + // connect failed + rc = FALSE; + } + + return rc; +} + + +// check for a client disconnect ---------------------------------------------- +// +int E_ConnManager::CheckClientDisconnect( node_t* clientnode ) +{ + ASSERT( clientnode != NULL ); + + // find the slot + int nClientID = _FindSlotWithNode( clientnode ); + + // we could find the slot + if( nClientID != -1 ) { + + // send the response + ThePacketHandler->SendDisconnectResponse( DISC_OK, clientnode, nClientID ); + + // disconnect the client in the slot ( must be done after the packets have been sent ) + DisconnectClient( nClientID ); + + // disconnect was OK + return TRUE; + } else { + + // client was not found + ThePacketHandler->SendDisconnectResponse( DISC_NOT_CONNECTED, clientnode, PLAYERID_ANONYMOUS ); + + return FALSE; + } +} + +// check for a client namechange ---------------------------------------------- +// +int E_ConnManager::CheckNameChange( node_t* clientnode, char* newplayername ) +{ + ASSERT( clientnode != NULL ); + ASSERT( newplayername != NULL ); + + // find the slot + int nSlot = _FindSlotWithNode( clientnode ); + + // we could find the slot + if( nSlot != -1 ) { + + MSGOUT( "client %s wants to be renamed to %s\n", NODE_Print( &m_ClientInfos[ nSlot ].m_node ), newplayername ); + + // only check for name collision if not alone + if ( m_nNumConnected > 1 ) { + + // check all the other connected slots for the same name + for( int nOtherSlot = 0; nOtherSlot < MAX_NUM_CLIENTS; nOtherSlot++ ) { + + // check for different slot + if ( nSlot != nOtherSlot ) { + + // check whether slot is free + if( !m_ClientInfos[ nOtherSlot ].IsSlotFree() ) { + + // check whether this name is already taken + if( strcmp( newplayername, m_ClientInfos[ nOtherSlot ].m_szName) == 0 ) { + + // name already taken + ThePacketHandler->SendNameChangeRepsponse( NAMECHANGE_ALREADY_TAKEN, clientnode, nSlot ); + + return FALSE; + } + } + } + } + } + + // namechange OK + ThePacketHandler->SendNameChangeRepsponse( NAMECHANGE_OK, clientnode, nSlot ); + + // change the name + _ChangeClientName( nSlot, newplayername ); + + // mark the client alive + m_ClientInfos[ nSlot ].MarkAlive(); + + // send namechange notifications to the other clients + _NotifyClientNameChange( nSlot ); + + return TRUE; + + } else { + + // client is not connected + ThePacketHandler->SendNameChangeRepsponse( NAMECHANGE_NOT_CONNECTED, clientnode, nSlot ); + + return FALSE; + } +} + +// get the client information for a specific slot +// +E_ClientInfo* E_ConnManager::GetClientInfo( int nSlot ) +{ + ASSERT( nSlot >= 0 ); + ASSERT( nSlot <= MAX_NUM_CLIENTS ); + + return &m_ClientInfos[ nSlot ]; +} + +// check the alive counter of all connected clients and disconnect any timed out clients +// +int E_ConnManager::CheckAliveStatus() +{ + for( int nClientID = 0; nClientID < MAX_NUM_CLIENTS; nClientID++ ) { + E_ClientInfo* pClientInfo = &m_ClientInfos[ nClientID ]; + + if ( !pClientInfo->IsSlotFree() ) { + + // check for client timeout + if ( !pClientInfo->IsAlive() ) { + + MSGOUT( "force-removing timed-out client %s (name: %s)\n", NODE_Print( &pClientInfo->m_node ), pClientInfo->m_szName ); + + // send the response + ThePacketHandler->SendDisconnectResponse( DISC_OK, &pClientInfo->m_node, nClientID ); + + // and disconnect the client + DisconnectClient( nClientID ); + } + } + } + + return TRUE; +} + +// ---------------------------------------------------------------------------- +// E_ConnManager protected methods +// ---------------------------------------------------------------------------- + + +// send client connected notifications to the other clients ------------------- +// +int E_ConnManager::_NotifyClientConnected( int nSlotConnected ) +{ + ASSERT( nSlotConnected >= 0 ); + ASSERT( nSlotConnected < MAX_NUM_CLIENTS ); + + // send 1 notify to all connected clients and N notifies to the connecting client + for( int nSlot = 0; nSlot < MAX_NUM_CLIENTS; nSlot++ ) { + + if ( !m_ClientInfos[ nSlot ].IsSlotFree() ) { + + if ( nSlot != nSlotConnected ) { + + // send the notification to already connected clients + ThePacketHandler->SendNotifyConnected( nSlot, nSlotConnected ); + + // send the notification to the connecting client + ThePacketHandler->SendNotifyConnected( nSlotConnected, nSlot ); + } + } + } + + return TRUE; +} + + +// send client disconnected notifications to the other clients ---------------- +// +int E_ConnManager::_NotifyClientDisconnected( int nSlotDisconnected ) +{ + ASSERT( nSlotDisconnected >= 0 ); + ASSERT( nSlotDisconnected < MAX_NUM_CLIENTS ); + + // only send, if more than 1 connected + if ( m_nNumConnected > 1 ) { + for( int nSlot = 0; nSlot < MAX_NUM_CLIENTS; nSlot++ ) { + + // do not send to same client AND do not send to unconnected slots + if ( ( nSlot != nSlotDisconnected ) && !m_ClientInfos[ nSlot ].IsSlotFree() ) { + + // send the notification + ThePacketHandler->SendNotifyDisconnected( nSlot, nSlotDisconnected ); + } + } + } + + + return TRUE; +} + +// send client namechange notifications to the other clients ------------------ +// +int E_ConnManager::_NotifyClientNameChange( int nSlotNamechanged ) +{ + ASSERT( nSlotNamechanged >= 0 ); + ASSERT( nSlotNamechanged < MAX_NUM_CLIENTS ); + + // only send, if more than 1 connected + if ( m_nNumConnected > 1 ) { + for( int nSlot = 0; nSlot < MAX_NUM_CLIENTS; nSlot++ ) { + + // do not send to same client AND do not send to unconnected slots + if ( ( nSlot != nSlotNamechanged ) && !m_ClientInfos[ nSlot ].IsSlotFree() ) { + + // send the notification + ThePacketHandler->SendNotifyNameChange( nSlot, nSlotNamechanged ); + } + } + } + + return TRUE; +} + + + +// ensure a client is not connected ------------------------------------------- +// +int E_ConnManager::_EnsureClientIsDisconnected( E_ClientConnectInfo* pClientConnectInfo ) +{ + ASSERT( pClientConnectInfo != NULL ); + ASSERT( pClientConnectInfo->m_szHostName != NULL ); + ASSERT( &pClientConnectInfo->m_node != NULL ); + ASSERT( pClientConnectInfo->m_szName != NULL ); + ASSERT( pClientConnectInfo->m_szOSLine != NULL ); + + // check for duplicate connects from same node + for( int nSlot = 0; nSlot < MAX_NUM_CLIENTS; nSlot++ ) { + + E_ClientInfo* pClientInfo = &m_ClientInfos[ nSlot ]; + + if ( !pClientInfo->IsSlotFree() ) { + if ( NODE_Compare( &pClientInfo->m_node, &pClientConnectInfo->m_node ) == NODECMP_EQUAL ) { + + MSGOUT( "Removing (possibly) dead client %s", NODE_Print( &pClientConnectInfo->m_node ) ); + + // disconnect the client + DisconnectClient( nSlot ); + } + } + } + + return TRUE; +} + + +// connect a client ----------------------------------------------------------- +// +int E_ConnManager::_ConnectClient( E_ClientConnectInfo* pClientConnectInfo ) +{ + ASSERT( pClientConnectInfo != NULL ); + ASSERT( pClientConnectInfo->m_szHostName != NULL ); + ASSERT( &pClientConnectInfo->m_node != NULL ); + ASSERT( pClientConnectInfo->m_szName != NULL ); + ASSERT( pClientConnectInfo->m_szOSLine != NULL ); + + for( int nClientID = 0; nClientID < MAX_NUM_CLIENTS; nClientID++ ) { + + E_ClientInfo* pClientInfo = &m_ClientInfos[ nClientID ]; + + // check for a free slot + if ( pClientInfo->IsSlotFree() ) { + + // copy the connection info + pClientInfo->CopyClientConnectInfo( pClientConnectInfo ); + + // this slot is used + pClientInfo->SetSlotFree( FALSE ); + + // init the alive counter for this client + pClientInfo->MarkAlive(); + + // return the selected slot + pClientConnectInfo->m_selected_slot = nClientID; + + // connect the player in the simulation + TheSimulator->ConnectPlayer( nClientID ); + + // connect the player in the game + TheGame->ConnectPlayer( nClientID ); + + // connect stream for this client + ThePacketDriver->ConnectStream( nClientID ); + + // increase the # of players connected + m_nNumConnected++; + + return nClientID; + } + } + + return -1; +/* + + svg_Player_RcvdPackets[ i ] = 0; + svg_Player_LostPackets[ i ] = 0; + + SVG_PrintClientList(); + #ifdef GENERATE_HTML + SVG_GenerateHTMLList(); + #endif + + if ( register_on_masterserver && masterserver_ok ) { + // update info at masterserver + SVG_SendPlayersToMasterServer( m_servfd ); + } +*/ +} + + +// disconnect a client -------------------------------------------------------- +// +int E_ConnManager::DisconnectClient( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + + // check recurse prevention flag + if ( !m_inDisconnectClient ) { + + // set recurse prevention flag + m_inDisconnectClient = TRUE; + + // send disconnect notifications to the other clients + _NotifyClientDisconnected( nClientID ); + + // clear all the client information + m_ClientInfos[ nClientID ].Reset(); + + // disconnect the player in the simulation + TheSimulator->DisconnectPlayer( nClientID ); + + // disconnect the player in the game + TheGame->DisconnectPlayer( nClientID ); + + // reset stream for this client + ThePacketDriver->ResetStream( nClientID ); + + // decrease the # of players connected + m_nNumConnected--; + + // clear recurse prevention flag + m_inDisconnectClient = FALSE; + } + + return TRUE; +} + +// return the name of the client ---------------------------------------------- +// +char* E_ConnManager::GetClientName( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + return m_ClientInfos[ nClientID ].m_szName; +} + + +// change the name of a client ------------------------------------------------ +// +int E_ConnManager::_ChangeClientName( int nSlot, char* newplayername ) +{ + ASSERT( nSlot >= 0 ); + ASSERT( nSlot < MAX_NUM_CLIENTS ); + ASSERT( newplayername != NULL ); + + // copy the new name + strncpy( m_ClientInfos[ nSlot ].m_szName, newplayername, MAX_PLAYER_NAME ); + m_ClientInfos[ nSlot ].m_szName[ MAX_PLAYER_NAME ] = 0; + + return TRUE; +} + + +// check if client version is compatible -------------------------------------- +// +int E_ConnManager::_IsClientCompatible( E_ClientConnectInfo* pClientConnectInfo ) +{ + ASSERT( pClientConnectInfo != NULL ); + + //NOTE: + // this is subject to change !! + // in the future with same major version number should be compatible + if ( ( pClientConnectInfo->m_nVersionMajor == CLSV_PROTOCOL_MAJOR ) && ( pClientConnectInfo->m_nVersionMinor == CLSV_PROTOCOL_MINOR ) ) { + return TRUE; + } else { + return FALSE; + } +} + +// check if client is banned -------------------------------------------- +// +int E_ConnManager::_IsClientBanned( E_ClientConnectInfo* pClientConnectInfo ) +{ + ASSERT( pClientConnectInfo != NULL ); + + //FIXME: implement + /*for ( int cid = 0; cid < num_banned; cid++ ) { + if ( strcmp( ip_address, banlist[ cid ] ) == 0 ) + return TRUE; +}*/ + + return FALSE; +} + +// check if challenge is correct ---------------------------------------------- +// +int E_ConnManager::_IsChallengeCorrect( E_ClientConnectInfo* pClientConnectInfo, E_ClientChallengeInfo** pFoundChallengeInfo ) +{ + ASSERT( pClientConnectInfo != NULL ); + + node_t* client_node = &pClientConnectInfo->m_node; + int client_challenge = pClientConnectInfo->m_challenge; + + // linear search from newest challenge + for( int cid = ( m_nCurChallengeInfo - 1 ); cid != m_nCurChallengeInfo; cid-- ) { + + // check for wraparound + if ( cid == -1 ) + cid = ( m_nMaxNumChallengeInfos - 1 ); + + E_ClientChallengeInfo* pChallengeInfo = &m_ChallengInfos[ cid ]; + + // check if we have a challenge from this node + if ( NODE_AreSame( client_node, &pChallengeInfo->m_node ) ) { + + // check whether the challenge is the same we sent to the client + if ( pChallengeInfo->m_challenge == client_challenge ) { + + // check whether challenge has not yet timed out + //FIXME: CHALLENGE_TIMEOUT_FRAMES should be server configurable + if ( pChallengeInfo->m_frame_generated >= ( SYSs_GetRefFrameCount() - CHALLENGE_TIMEOUT_FRAMES ) ) { + + // set the found challenge info + *pFoundChallengeInfo = &m_ChallengInfos[ cid ]; + + return TRUE; + } else { + return FALSE; + } + } + } + } + + + + + return FALSE; +} + +// check node of client with node used at connection -------------------------- +// +int E_ConnManager::CheckNodesMatch( int nClientID, node_t* node ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + ASSERT( node != NULL ); + + if ( m_ClientInfos[ nClientID ].IsSlotFree() ) + return FALSE; + + return ( NODE_Compare( &m_ClientInfos[ nClientID ].m_node, node ) == NODECMP_EQUAL ); +} + + +// find the assigned slot for a specific node --------------------------------- +// +int E_ConnManager::_FindSlotWithNode( node_t* clientnode ) +{ + ASSERT( clientnode != NULL ); + + for( int nSlot = 0; nSlot < MAX_NUM_CLIENTS; nSlot++ ) { + E_ClientInfo* pClientInfo = &m_ClientInfos[ nSlot ]; + // only check filled slots + if ( !pClientInfo->IsSlotFree() ) { + // check for connected client node + if( NODE_Compare( clientnode, &pClientInfo->m_node ) == NODECMP_EQUAL ) { + return nSlot; + } + } + } + + return -1; +} + +// find the slot of a connected client by its name ---------------------------- +// +int E_ConnManager::_FindClientName( char* name ) +{ + ASSERT( name != NULL ); + + for( int nSlot = 0; nSlot < MAX_NUM_CLIENTS; nSlot++ ) { + if ( !m_ClientInfos[ nSlot ].IsSlotFree() ) { + // check whether the names match + if ( stricmp( name, m_ClientInfos[ nSlot ].m_szName ) == 0 ) { + return nSlot; + } + } + } + + return -1; +} + +// set some client-info fields according to RE -------------------------------- +// +void E_ConnManager::NET_ExecRmEvClientInfo( int nClientID, RE_ClientInfo* re_clientinfo ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + ASSERT( re_clientinfo != NULL ); + + E_ClientInfo& _ClientInfo = m_ClientInfos[ nClientID ]; + if ( !_ClientInfo.IsSlotFree() ) { + _ClientInfo.m_nSendFreq = re_clientinfo->client_sendfreq; + _ClientInfo.m_nRecvRate = UNPACK_SERVERRATE( re_clientinfo->server_sendrate ); + + //FIXME: resync client with correcter rates, if they are out of bounds + // apply min/max bounds + _ClientInfo.m_nSendFreq = max( _ClientInfo.m_nSendFreq, CLIENT_SEND_FREQUENCY_MIN ); + _ClientInfo.m_nSendFreq = min( _ClientInfo.m_nSendFreq, CLIENT_SEND_FREQUENCY_MIN ); + _ClientInfo.m_nRecvRate = max( _ClientInfo.m_nRecvRate, CLIENT_RECV_RATE_MIN ); + _ClientInfo.m_nRecvRate = min( _ClientInfo.m_nRecvRate, CLIENT_RECV_RATE_MAX ); + + //DBGTXT( MSGOUT( "E_ConnManager::NET_ExecRmEvClientInfo(): client %d: new CLIENTRATE: %d new SERVERRATE: %d", + // nClientID, _ClientInfo.m_nSendFreq, _ClientInfo.m_nRecvRate ); ); + + // reconfigure the network output + TheSimNetOutput->RateChangedForClient( nClientID ); + } +} 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 ); +} + + + diff --git a/src/parsec_server/e_global_sv.cpp b/src/parsec_server/e_global_sv.cpp new file mode 100755 index 0000000..c34f8de --- /dev/null +++ b/src/parsec_server/e_global_sv.cpp @@ -0,0 +1,146 @@ +/* + * PARSEC - Global Variables + * + * $Author: uberlinuxguy $ - $Date: 2004/09/15 12:25:44 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2001 + * Copyright (c) Markus Hadwiger <msh@parsec.org> 1996-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 <stddef.h> +#include <stdio.h> +#include <stdlib.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "objstruc.h" +#include "od_class.h" + +// subsystem headers +//#include "vid_defs.h" + +// local module header +#include "e_global_sv.h" + +// for definition of MAX_AUX_ENABLING and MAX_AUX_DATA +#include "con_aux_sv.h" + +#include "net_limits.h" +#include "net_defs.h" +#include "MasterServer.h" + +// counters for numbers of textures/objects/bitmaps --------------------------- +// +int NumLoadedObjects = 0; + + +// info structures for loaded data -------------------------------------------- +// +objectinfo_s ObjectInfo[ MAX_DISTINCT_OBJCLASSES ]; + +// current working directory (for general use) -------------------------------- +// +char CurWorkDir[ PATH_MAX + 1 ] = "."; + + +// pointer to programname (used in error messages) ---------------------------- +// +char* sys_ProgramName; + +// global color definitions --------------------------------------------------- +// +colrgba_s LightColorAmbient = { 255, 255, 255, 255 }; +colrgba_s LightColorDiffuse = { 255, 255, 255, 255 }; +colrgba_s LightColorSpecular = { 255, 255, 255, 255 }; + + +// global directional light source -------------------------------------------- +// +Vector3 GlobalDirLight = { GEOMV_0, GEOMV_0, GEOMV_1, 0 }; + +// flag if currently recording ------------------------------------------------ +// +int RecordingActive = FALSE; + + +// file pointer to recording file --------------------------------------------- +// +FILE* RecordingFp = NULL; + + +// remote packet recording ---------------------------------------------------- +// +int RecordRemotePackets = FALSE; +int RemoteRecSessionId = 0; +int RemoteRecPacketId = 0; + +// console variables ---------------------------------------------------------- +// +int ConsoleSliding = 0; +int ConsoleHeight = 16; + + +// list of currently visible objects ------------------------------------------ +// +GenObject* VObjList = NULL; // rebuilt each frame + + + + +// number of available object classes (object class table entries) ------------ +// +int NumObjClasses = 0; + + + +// auxiliary enabling flags --------------------------------------------------- +// +int AuxEnabling[ MAX_SV_ARRAY_SIZE ]; + + + +// frame measurement counters ------------------------------------------------- +// +refframe_t RefFrameFrequency = DEFAULT_REFFRAME_FREQUENCY; + + + +// global class vars ---------------------------------------------------------- +// +G_CollDet* TheGameCollDet = NULL; +G_ExtraManager* TheGameExtraManager = NULL; +G_Input* TheGameInput = NULL; +G_Main* TheGame = NULL; +NET_PacketDriver* ThePacketDriver = NULL; +NET_UDPDriver* TheUDPDriver = NULL; +E_DistManager* TheDistManager = NULL; +E_World* TheWorld = NULL; +E_SimNetInput* TheSimNetInput = NULL; +E_SimNetOutput* TheSimNetOutput = NULL; +E_Simulator* TheSimulator = NULL; +E_ConnManager* TheConnManager = NULL; +E_GameServer* TheServer = NULL; +MasterServer* TheMaster = NULL; +E_PacketHandler* ThePacketHandler = NULL; + + diff --git a/src/parsec_server/e_packethandler.cpp b/src/parsec_server/e_packethandler.cpp new file mode 100755 index 0000000..29cd315 --- /dev/null +++ b/src/parsec_server/e_packethandler.cpp @@ -0,0 +1,1544 @@ +/* + * PARSEC - Packet handler + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:46 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2001-2003 + * + * 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 <stdlib.h> +#include <stdio.h> + +#include <vector> + +// 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" + +// UNP header +#include "net_wrap.h" + +// mathematics header +#include "utl_math.h" + +// subsystem & server headers +#include "net_defs.h" +#include "e_defs.h" +#include "e_global_sv.h" +#include "sys_defs.h" + +// network code config +#include "net_conf.h" + +// net game header +#include "net_game_sv.h" + +// local module header +#include "e_packethandler.h" + +// SYS subsystem + +// proprietary module headers +#include "con_aux_sv.h" +#include "net_csdf.h" +#include "net_packetdriver.h" +#include "net_udpdriver.h" +#include "net_stream.h" +#include "e_simplayerinfo.h" +#include "e_simnetinput.h" +#include "g_main_sv.h" +#include "e_connmanager.h" +#include "e_gameserver.h" +#include "e_relist.h" +#include "e_simulator.h" +#include "MasterServer.h" + +// ---------------------------------------------------------------------------- +// public methods +// ---------------------------------------------------------------------------- + +// handle the content of a packet received ------------------------------------ +// +void E_PacketHandler::HandlePacket( PacketChainBlock* block ) +{ + ASSERT( block != NULL ); + + NetPacket_GMSV* gamepacket = block->gamepacket; + int nSenderID = gamepacket->SendPlayerId; + int bufid = block->bufferno; + + UPDTXT2(MSGOUT( "e_PacketHandler::HandlePacket(): command:%d", gamepacket->Command )); + + // check for senderid spoofing + if ( !_IsLegitSender( gamepacket, bufid ) ) { + return; + } + + // if we are a Master server, process packets a bit differently + if(TheServer->GetServerIsMaster()){ + // TODO: Process Master Server Packets + switch( gamepacket->Command ) { + case PKTP_COMMAND: + + + /*if ( nSenderID == PLAYERID_MASTERSERVER ) { + + // Hmmmm... PLAYERID_MASTERSERVER is us.. we shouldn't + // be sending packets to ourselves.... log an error + // and return + MSGOUT("e_PacketHandler::HandlePacket(): We sent ourselves a packet?! I think *not*!!!"); + return; + + } else {*/ + + // handle a command packet from a client + _Handle_COMMAND_MASTER( gamepacket, bufid ); + //} + + + break; + case PKTP_STREAM: + if ( nSenderID == PLAYERID_MASTERSERVER ) { + + // Hmmmm... PLAYERID_MASTERSERVER is us.. we shouldn't + // be sending packets to ourselves.... log an error + // and return + MSGOUT("e_PacketHandler::HandlePacket(): We sent ourselves a packet?! I think *not*!!!"); + return; + + } else { + + // handle a command packet from a client + _Handle_STREAM_MASTER( gamepacket, bufid ); + } + + break; + + default: + MSGOUT("Curious Packet Received: %d", gamepacket->Command); + break; + } + // return to do no further processing. + return; + } + + // perform action according to packet type + switch( gamepacket->Command ) { + + // these must not be received + case PKTP_CONNECT: + case PKTP_CONNECT_REPLY: + case PKTP_DISCONNECT: + case PKTP_SLOT_REQUEST: + case PKTP_SUBDUE_SLAVE: + DBGTXT( MSGOUT( "e_PacketHandler::HandlePacket: received PEER-only packet ( command: %d )", gamepacket->Command ); ); + + //NOTE: + // these should be ignored transparently since somebody + // might try to connect peer-to-peer on the same + // segment + + // ASSERT( 0 ); + break; + + case PKTP_JOIN: + case PKTP_UNJOIN: + case PKTP_GAME_STATE: + case PKTP_NODE_ALIVE: + //NOTE: legacy packet - removed in new GameServer ( >= build 0198 ) + DBGTXT( MSGOUT( "e_PacketHandler::HandlePacket: received legacy packet ( command: %d )", gamepacket->Command ); ); + break; + + case PKTP_STREAM: + // handle all the events/proc.calls in the stream packet + _Handle_STREAM( gamepacket ); + break; + + case PKTP_COMMAND: + + if ( nSenderID == PLAYERID_MASTERSERVER ) { + + // handle a command packet from the masterserver + _Handle_COMMAND_MASV( gamepacket, bufid ); + + } else { + + // handle a command packet from a client + _Handle_COMMAND( gamepacket, bufid ); + } + + break; + + default: + MSGOUT( "gameloop: received packet of unknown type: %d.", gamepacket->Command ); + ASSERT( 0 ); + } +} + +// notify a client that another client connected ------------------------------ +// +void E_PacketHandler::SendNotifyConnected( int nDestSlot, int nClientSlot ) +{ + // get the client information for the client, we want to inform + E_ClientInfo* pDestClientInfo = TheConnManager->GetClientInfo( nDestSlot ); + + // get the client information for the client, we want the destnode to inform about + E_ClientInfo* pAboutClientInfo = TheConnManager->GetClientInfo( nClientSlot ); + + ASSERT( pDestClientInfo != NULL ); + ASSERT( pAboutClientInfo != NULL ); + + ASSERT( !pDestClientInfo->IsSlotFree() ); + + // we do not notify the client about himself + if ( NODE_Compare( &pAboutClientInfo->m_node, &pDestClientInfo->m_node ) == NODECMP_EQUAL ) { + ASSERT( FALSE ); + return; + } + + // prepare the command string + char sendline[ MAX_RE_COMMANDINFO_COMMAND_LEN + 1 ]; + snprintf( sendline, sizeof( sendline ), LISTSTR_ADDED_IN_SLOT, nClientSlot, pAboutClientInfo->m_szName ); + + // send the server command ( RELIABLE ) + Send_COMMAND( sendline, &pDestClientInfo->m_node, nDestSlot, TRUE ); +} + +// notify a client that another client is disconnected ------------------------ +// +void E_PacketHandler::SendNotifyDisconnected( int nDestSlot, int nClientSlot ) +{ + // get the client information for the client, we want to inform + E_ClientInfo* pDestClientInfo = TheConnManager->GetClientInfo( nDestSlot ); + + // get the client information for the client, we want the destnode to inform about + E_ClientInfo* pAboutClientInfo = TheConnManager->GetClientInfo( nClientSlot ); + + ASSERT( pDestClientInfo != NULL ); + ASSERT( pAboutClientInfo != NULL ); + + ASSERT( !pDestClientInfo->IsSlotFree() ); + + // we do not notify the client about himself + if ( NODE_Compare( &pAboutClientInfo->m_node, &pDestClientInfo->m_node ) == NODECMP_EQUAL ) { + ASSERT( FALSE ); + return; + } + + // prepare the command string + char sendline[ MAX_RE_COMMANDINFO_COMMAND_LEN + 1 ]; + snprintf( sendline, sizeof(sendline), LISTSTR_REMOVED_FROM_SLOT, nClientSlot ); + + // send the server command ( RELIABLE ) + Send_COMMAND( sendline, &pDestClientInfo->m_node, nDestSlot, TRUE ); +} + +// notify a client that another client's name changed ------------------------- +// +void E_PacketHandler::SendNotifyNameChange( int nDestSlot, int nClientSlot ) +{ + // get the client information for the client, we want to inform + E_ClientInfo* pDestClientInfo = TheConnManager->GetClientInfo( nDestSlot ); + + // get the client information for the client, we want the destnode to inform about + E_ClientInfo* pAboutClientInfo = TheConnManager->GetClientInfo( nClientSlot ); + + ASSERT( pDestClientInfo != NULL ); + ASSERT( pAboutClientInfo != NULL ); + + // we do not notify the client about himself + if ( NODE_Compare( &pAboutClientInfo->m_node, &pDestClientInfo->m_node ) == NODECMP_EQUAL ) { + ASSERT( FALSE ); + return; + } + + // prepare the command string + char sendline[ MAX_RE_COMMANDINFO_COMMAND_LEN + 1 ]; + snprintf( sendline, sizeof(sendline), LISTSTR_NAME_UPDATED, nClientSlot, pAboutClientInfo->m_szName ); + + // send the server command ( RELIABLE ) + Send_COMMAND( sendline, &pDestClientInfo->m_node, nDestSlot, TRUE ); +} + + +// send a challenge response back to a client --------------------------------- +// +void E_PacketHandler::SendChallengeResponse( E_ClientChallengeInfo* pChallengeInfo, node_t* clientnode ) +{ + ASSERT( pChallengeInfo != NULL ); + ASSERT( clientnode != NULL ); + + // prepare response + char sendline[ MAX_RE_COMMANDINFO_COMMAND_LEN + 1 ]; + snprintf( sendline, sizeof(sendline), RECVSTR_CHALLENGE, pChallengeInfo->m_challenge ); + + // send response datagram + Send_COMMAND_Datagram( sendline, clientnode, PLAYERID_ANONYMOUS ); +} + +// send a connect response back to a client ----------------------------------- +// +int E_PacketHandler::SendConnectResponse( E_ConnManager::ConnResults rc, E_ClientConnectInfo* pClientConnectInfo ) +{ + ASSERT( rc >= E_ConnManager::CONN_CHALLENGE_INVALID ); + ASSERT( rc <= E_ConnManager::CONN_OK ); + ASSERT( pClientConnectInfo != NULL ); + + char sendline[ MAX_RE_COMMANDINFO_COMMAND_LEN + 1 ]; + + // prepare correct response command + switch( rc ) { + case E_ConnManager::CONN_CHALLENGE_INVALID: + ASSERT( pClientConnectInfo->m_selected_slot == -1 ); + snprintf( sendline, sizeof(sendline), RECVSTR_CHALL_INVALID ); + break; + case E_ConnManager::CONN_CLIENT_INCOMAPTIBLE: + ASSERT( pClientConnectInfo->m_selected_slot == -1 ); + snprintf( sendline, sizeof(sendline), RECVSTR_SERVER_INCOMP ); + break; + case E_ConnManager::CONN_CLIENT_BANNED: + ASSERT( pClientConnectInfo->m_selected_slot == -1 ); + snprintf( sendline, sizeof(sendline), RECVSTR_CLIENT_BANNED ); + break; + case E_ConnManager::CONN_SERVER_FULL: + ASSERT( pClientConnectInfo->m_selected_slot == -1 ); + snprintf( sendline, sizeof(sendline), RECVSTR_SERVER_FULL ); + break; + case E_ConnManager::CONN_NAME_TAKEN: + ASSERT( pClientConnectInfo->m_selected_slot == -1 ); + snprintf( sendline, sizeof(sendline), RECVSTR_NAME_INVALID ); + break; + case E_ConnManager::CONN_OK: + ASSERT( pClientConnectInfo->m_selected_slot != -1 ); + + //FIXME: check whether IP/port is necessary in the connect reply packet + //FIXME: NebulaID and other worldstate should be sent as a sync burst at the beginning + snprintf( sendline, sizeof(sendline), RECVSTR_ACCEPTED, + pClientConnectInfo->m_szHostName, + NODE_GetPort( &pClientConnectInfo->m_node ), + pClientConnectInfo->m_selected_slot, + MAX_NUM_CLIENTS, //FIXME: remove this ???? + SV_SERVERID, + TheServer->GetServerName() //FIXME: remove this ???? + ); + + break; + default: + ASSERT( FALSE ); + return FALSE; + } + + // ensure proper destination id ( PLAYERID_ANONYMOUS on connection refused ) + int nClientID = ( pClientConnectInfo->m_selected_slot == -1 ) ? PLAYERID_ANONYMOUS : pClientConnectInfo->m_selected_slot; + + // send response datagram + Send_COMMAND_Datagram( sendline, &pClientConnectInfo->m_node, nClientID ); + + return TRUE; +} + +// send a disconnect request response ------------------------------------------ +// +int E_PacketHandler::SendDisconnectResponse( E_ConnManager::DisconnResults rc, node_t* clientnode, int nClientID ) +{ + ASSERT( rc >= E_ConnManager::DISC_NOT_CONNECTED ); + ASSERT( rc <= E_ConnManager::DISC_OK ); + ASSERT( ( ( nClientID >= 0 ) && ( nClientID <= MAX_NET_ALLOC_SLOTS ) ) || ( nClientID == PLAYERID_ANONYMOUS ) ); + ASSERT( clientnode != NULL ); + + char sendline[ MAX_RE_COMMANDINFO_COMMAND_LEN + 1 ]; + + // prepare correct response command + switch( rc ) { + case E_ConnManager::DISC_NOT_CONNECTED: + snprintf( sendline, sizeof(sendline), RECVSTR_NOT_CONNECTED ); + break; + case E_ConnManager::DISC_OK: + snprintf( sendline, sizeof(sendline), RECVSTR_REMOVE_OK ); + break; + default: + ASSERT( FALSE ); + return FALSE; + } + + // flush any pending NACK reliable + ThePacketDriver->FlushReliableBuffer( nClientID ); + + // send response ( UNRELIABLE ) + //NOTE: disconnect response is not sent reliable, if packet is lost, client gets a "NOT CONNECTED" reply upon next try + Send_COMMAND( sendline, clientnode, nClientID, FALSE ); + + return TRUE; +} + +// send a namechange response back to the client ------------------------------ +// +int E_PacketHandler::SendNameChangeRepsponse( E_ConnManager::NameChangeResults rc, node_t* clientnode, int nClientID ) +{ + ASSERT( rc >= E_ConnManager::NAMECHANGE_NOT_CONNECTED ); + ASSERT( rc <= E_ConnManager::NAMECHANGE_OK ); + ASSERT( ( nClientID >= 0 ) && ( nClientID <= MAX_NET_ALLOC_SLOTS ) ); + ASSERT( clientnode != NULL ); + + char sendline[ MAX_RE_COMMANDINFO_COMMAND_LEN + 1 ]; + + // prepare correct response command + switch( rc ) { + case E_ConnManager::NAMECHANGE_NOT_CONNECTED: + snprintf( sendline, sizeof(sendline), RECVSTR_NOT_CONNECTED ); + break; + case E_ConnManager::NAMECHANGE_ALREADY_TAKEN: + snprintf( sendline, sizeof(sendline), RECVSTR_NAME_INVALID ); + break; + case E_ConnManager::NAMECHANGE_OK: + snprintf( sendline, sizeof(sendline), RECVSTR_NAME_OK ); + break; + default: + ASSERT( FALSE ); + return FALSE; + } + + // send response ( RELIABLE ) + Send_COMMAND( sendline, clientnode, nClientID, TRUE ); + + return TRUE; +} + +// send a INFO response back to the client ------------------------------------ +// +int E_PacketHandler::SendInfoResponse( refframe_t client_ping_send_frame, node_t* clientnode, int nClientID ) +{ + ASSERT( ( ( nClientID >= 0 ) && ( nClientID <= MAX_NET_ALLOC_SLOTS ) ) || ( nClientID == PLAYERID_ANONYMOUS ) ); + ASSERT( clientnode != NULL ); + + E_REList* pUnreliable = E_REList::CreateAndAddRef( RE_LIST_MAXAVAIL ); + + // prepare the command string + char szBuffer[ MAX_RE_COMMANDINFO_COMMAND_LEN + 1 ]; + snprintf( szBuffer, MAX_RE_COMMANDINFO_COMMAND_LEN, + RECVSTR_INFO_REPLY, + SV_SERVERID, + (unsigned int)client_ping_send_frame, + TheConnManager->GetNumConnected(), + MAX_NUM_CLIENTS, + CLSV_PROTOCOL_MAJOR, CLSV_PROTOCOL_MINOR, + TheServer->GetServerName() + ); + + // append a remote event containing the command + int rc = pUnreliable->NET_Append_RE_CommandInfo( szBuffer ); + ASSERT( rc == TRUE ); + + // send a datagram + ThePacketHandler->Send_STREAM_Datagram( pUnreliable, clientnode, nClientID ); + + // release the RE list from here + pUnreliable->Release(); + + return TRUE; +} + + +// send a PING response back to the client ------------------------------------ +// +int E_PacketHandler::SendPingResponse( refframe_t client_ping_send_frame, node_t* clientnode, int nClientID ) +{ + ASSERT( ( ( nClientID >= 0 ) && ( nClientID <= MAX_NET_ALLOC_SLOTS ) ) || ( nClientID == PLAYERID_ANONYMOUS ) ); + ASSERT( clientnode != NULL ); + + E_REList* pUnreliable = E_REList::CreateAndAddRef( RE_LIST_MAXAVAIL ); + + // prepare the command string + char szBuffer[ MAX_RE_COMMANDINFO_COMMAND_LEN + 1 ]; + snprintf( szBuffer, MAX_RE_COMMANDINFO_COMMAND_LEN, + RECVSTR_PING_REPLY, + SV_SERVERID, + (unsigned int)client_ping_send_frame, + TheConnManager->GetNumConnected(), + MAX_NUM_CLIENTS + ); + + // append a remote event containing the command + int rc = pUnreliable->NET_Append_RE_CommandInfo( szBuffer ); + ASSERT( rc == TRUE ); + + // send a datagram + ThePacketHandler->Send_STREAM_Datagram( pUnreliable, clientnode, nClientID ); + + // release the RE list from here + pUnreliable->Release(); + + return TRUE; +} + +/* +// store gamestate as RE_List head -------------------------------------------- +// +RE_GameState* NET_RmEvSingleGameState( NetPacket_GMSV* gamepacket ) +{ + ASSERT( gamepacket != NULL ); + RE_GameState* re_gamestate = (RE_GameState*) &gamepacket->RE_List; + re_gamestate->RE_Type = RE_GAMESTATE; + re_gamestate->RE_BlockSize = sizeof( RE_GameState ); + + // fill in the server gametime + re_gamestate->GameTime = TheServer->GetCurGameTime(); + + re_gamestate++; + re_gamestate->RE_Type = RE_EMPTY; + + return re_gamestate; +} +*/ + +// send a STREAM packet to a client ------------------------------------------- +// +size_t E_PacketHandler::Send_STREAM( int nClientID, E_REList* pReliable, E_REList* pUnreliable ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID <= MAX_NUM_CLIENTS ) ); + + char buffer[ NET_MAX_NETPACKET_INTERNAL_LEN ]; + NetPacket_GMSV* gamepacket = (NetPacket_GMSV*) buffer; + + E_ClientInfo* pClientInfo = TheConnManager->GetClientInfo( nClientID ); + ASSERT( pClientInfo != NULL ); + + node_t* node = &pClientInfo->m_node; + + UPDTXT2( MSGOUT( "PKTP_STREAM S -> C to: %d node: %s", nClientID, NODE_Print( node ) ); ); + + // fill gamepacket header + ThePacketDriver->FillStdGameHeader( PKTP_STREAM, gamepacket ); + + // send the packet + return ThePacketDriver->SendPacket( gamepacket, node, nClientID, pReliable, pUnreliable ); +} + +// ---------------------------------------------------------------------------- +// protected methods +// ---------------------------------------------------------------------------- + +// check whether the sender node matches the node for the senderid ------------ +// +int E_PacketHandler::_IsLegitSender( NetPacket_GMSV* gamepacket, int bufid ) +{ + ASSERT( gamepacket != NULL ); + + // identify sender + int senderid = gamepacket->SendPlayerId; + + if (TheServer->GetServerIsMaster() && senderid == PLAYERID_SERVER) { + // this is a HB packet... + return TRUE; + } + + if(TheServer->GetServerIsMaster() && senderid >= 0) { + // this is for the master server from a client. + return TRUE; + } + + // NODE_ALIVE only valid for normal clients + if ( ( senderid >= 0 ) && ( senderid < MAX_NUM_CLIENTS ) ) { + + // get the clientinfo + E_ClientInfo* pClientInfo = TheConnManager->GetClientInfo( senderid ); + + // get the sending node of the packet + node_t* packetnode = ThePacketDriver->GetPktSender( bufid ); + + // check whether someone tries to spoof the senderid + if ( NODE_AreSame( packetnode, &pClientInfo->m_node ) == FALSE ) { + + char szBuffer1[ 128 ]; + char szBuffer2[ 128 ]; + strcpy( szBuffer1, NODE_Print( packetnode ) ); + strcpy( szBuffer2, NODE_Print( &pClientInfo->m_node ) ); + + MSGOUT( "Filtering spoofed packet for %d. packet from %s - should be %s.\n", + senderid, + szBuffer1, + szBuffer2 ); + + //FIXME: check whether clients behind firewalls keep the same node ( ports might get remapped ) + return FALSE ; + } + } else if ( senderid == PLAYERID_MASTERSERVER ) { + + // get the sending node of the packet + node_t* packetnode = ThePacketDriver->GetPktSender( bufid ); + if ( !TheServer->IsMasterServerNode( packetnode ) ) { + MSGOUT( "Filtering spoofed packet. claims to be from masterserver - is from %s.\n", NODE_Print( packetnode ) ); + return FALSE ; + } + } + + return TRUE; +} + + +// send a datagram to a node -------------------------------------------------- +// +int E_PacketHandler::Send_STREAM_Datagram( E_REList* pUnreliable, node_t* node, int nClientID ) +{ + ASSERT( pUnreliable != NULL ); + ASSERT( node != NULL ); + ASSERT( ( nClientID == PLAYERID_MASTERSERVER ) || ( nClientID == PLAYERID_ANONYMOUS ) || ( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ) ); + + char buffer[ NET_MAX_NETPACKET_INTERNAL_LEN ]; + NetPacket_GMSV* gamepacket = (NetPacket_GMSV *) buffer; + + // fill game data header + ThePacketDriver->FillStdGameHeader( PKTP_STREAM, gamepacket ); + + if ( nClientID == PLAYERID_MASTERSERVER ) { + + UPDTXT2( MSGOUT( "DATAGRAM: PKTP_STREAM S -> M: node: %s id: %d msg: %d", + NODE_Print( node ), + nClientID, + gamepacket->MessageId ); ); + + } else { + + UPDTXT2( MSGOUT( "DATAGRAM: PKTP_STREAM S -> C: node: %s id: %d msg: %d", + NODE_Print( node ), + nClientID, + gamepacket->MessageId ); ); + + } + + + // send the datagram + int rc = ThePacketDriver->SendDatagram( gamepacket, node, nClientID, pUnreliable ); + + return rc; +} + + +// send a datagram command to a specific client node -------------------------- +// +int E_PacketHandler::Send_COMMAND_Datagram( const char* clientcommand, node_t* node, int nClientID ) +{ + char buffer[ NET_MAX_NETPACKET_INTERNAL_LEN ]; + NetPacket_GMSV* gamepacket = (NetPacket_GMSV *) buffer; + + // fill game data header + ThePacketDriver->FillStdGameHeader( PKTP_COMMAND, gamepacket ); + + if ( nClientID == PLAYERID_MASTERSERVER ) { + + UPDTXT2( MSGOUT( "DATAGRAM: PKTP_COMMAND S -> M: node: %s msg: %d cmd: '%s'", + NODE_Print( node ), + nClientID, + clientcommand ); ); + + } else { + + UPDTXT2( MSGOUT( "DATAGRAM: PKTP_COMMAND S -> C: node: %s msg: %d cmd: '%s'", + NODE_Print( node ), + nClientID, + clientcommand ); ); + + } + + // append a remote event containing the command + E_REList* pUnreliable = E_REList::CreateAndAddRef( RE_LIST_MAXAVAIL ); + if ( !pUnreliable->NET_Append_RE_CommandInfo( clientcommand ) ) { + DBGTXT( MSGOUT( "e_PacketHandler::Send_COMMAND_Datagram(): RE list choke." ); ); + } + + // send the packet + int rc = ThePacketDriver->SendDatagram( gamepacket, node, nClientID, pUnreliable ); + + // release the RE list from here + pUnreliable->Release(); + + return rc; +} + + +// send a server command to a specific client node ---------------------------- +// +int E_PacketHandler::Send_COMMAND( char* clientcommand, node_t* node, int nClientID, int reliable ) +{ + char buffer[ NET_MAX_NETPACKET_INTERNAL_LEN ]; + NetPacket_GMSV* gamepacket = (NetPacket_GMSV *) buffer; + + // fill game data header + ThePacketDriver->FillStdGameHeader( PKTP_COMMAND, gamepacket ); + + if ( nClientID == PLAYERID_MASTERSERVER ) { + + UPDTXT2( MSGOUT( "PKTP_COMMAND S -> M: node: %s id: %d msg: %d cmd: '%s'", + NODE_Print( node ), + nClientID, + gamepacket->MessageId, + clientcommand ); ); + + } else { + + UPDTXT2( MSGOUT( "PKTP_COMMAND S -> C: node: %s id: %d msg: %d cmd: '%s'", + NODE_Print( node ), + nClientID, + gamepacket->MessageId, + clientcommand ); ); + + } + + // append a remote event containing the command + E_REList* pREList = E_REList::CreateAndAddRef( RE_LIST_MAXAVAIL ); + if ( !pREList->NET_Append_RE_CommandInfo( clientcommand ) ) { + DBGTXT( MSGOUT( "e_PacketHandler::_Send_COMMAND(): RE list choke." ); ); + } + + // determine whether to send reliable or unreliable + E_REList* pReliable = NULL; + E_REList* pUnreliable = NULL; + if ( reliable ) { + pReliable = pREList; + } else { + pUnreliable = pREList; + } + + // send the packet + int rc = ThePacketDriver->SendPacket( gamepacket, node, nClientID, pReliable, pUnreliable ); + + // release the RE list from here + pREList->Release(); + + return rc; +} + + +// do safe parsing of challenge request --------------------------------------- +// +int E_PacketHandler::_ParseChallengeRequest( char* recvline ) +{ + ASSERT( recvline != NULL ); + + // copy parseline for safe processing + char parseline[ MAX_RE_COMMANDINFO_COMMAND_LEN + 1 ]; + strncpy( parseline, recvline, MAX_RE_COMMANDINFO_COMMAND_LEN ); + parseline[ MAX_RE_COMMANDINFO_COMMAND_LEN ] = 0; + + // check for request identifier + char* ident_str = strtok( parseline, " " ); + if ( ident_str == NULL ) + return FALSE; + + int len = strlen( ident_str ); + if ( strncmp( recvline, GIVECHALLSTRING1, len ) != 0 ) + return FALSE; + + return TRUE; +} + + +// do safe parsing of connect request --------------------------------------- +// +int E_PacketHandler::_ParseConnectRequest( char* recvline, E_ClientConnectInfo* pClientConnectInfo ) +{ + ASSERT( recvline != NULL ); + ASSERT( pClientConnectInfo != NULL ); + + // copy parseline for safe processing + SAFE_STR_DUPLICATE( parseline, recvline, MAX_RE_COMMANDINFO_COMMAND_LEN ); + + // check for request identifier + char* ident_str = strtok( parseline, " " ); + if ( ident_str == NULL ) + return FALSE; + + int len = strlen( ident_str ); + + if ( strncmp( ident_str, CONNSTRING2, len ) != 0 ) { + return FALSE; + } + + // get major version number + char* v_str1 = strtok( NULL, "." ); + if ( v_str1 == NULL ) { + return FALSE; + } + char* errpart; + pClientConnectInfo->m_nVersionMajor = (int) strtol( v_str1, &errpart, 10 ); + if ( *errpart != 0 ) { + return FALSE; + } + + // get minor version number + char* v_str2 = strtok( NULL, " " ); + if ( v_str2 == NULL ) { + return FALSE; + } + pClientConnectInfo->m_nVersionMinor = (int) strtol( v_str2, &errpart, 10 ); + if ( *errpart != 0 ) { + return FALSE; + } + + // get challenge + strtok( NULL, " " ); + char* ch_str = strtok( NULL, " " ); + if ( ch_str == NULL ) { + return FALSE; + } + pClientConnectInfo->m_challenge = (int) strtol( ch_str, &errpart, 10 ); + if ( *errpart != 0 ) { + return FALSE; + } + + // get playername + strtok( NULL, " " ); + char* n_str = strtok( NULL, " " ); + if ( n_str == NULL ) { + return FALSE; + } + strncpy( pClientConnectInfo->m_szName, n_str, MAX_PLAYER_NAME ); + pClientConnectInfo->m_szName[ MAX_PLAYER_NAME ] = 0; + + // get NET.CLIENTRATE + strtok( NULL, " " ); + char* clientrate_str = strtok( NULL, " " ); + if ( clientrate_str == NULL ) { + return FALSE; + } + pClientConnectInfo->m_nSendFreq = (int) strtol( clientrate_str, &errpart, 10 ); + if ( *errpart != 0 ) { + return FALSE; + } + + // get NET.SERVERTRATE + strtok( NULL, " " ); + char* serverrate_str = strtok( NULL, " " ); + if ( serverrate_str == NULL ) { + return FALSE; + } + pClientConnectInfo->m_nRecvRate = (int) strtol( serverrate_str, &errpart, 10 ); + if ( *errpart != 0 ) { + return FALSE; + } + + // get operating system specifier + strtok( NULL, " " ); + char* os_str = strtok( NULL, "\n" ); + if ( os_str == NULL ) { + return FALSE; + } + + strncpy( pClientConnectInfo->m_szOSLine, os_str, MAX_OSNAME_LEN ); + pClientConnectInfo->m_szOSLine[ MAX_OSNAME_LEN ] = 0; + + return TRUE; +} + + +// do safe parsing of disconnect request -------------------------------------- +// +int E_PacketHandler::_ParseDisconnectRequest( char* recvline ) +{ + ASSERT( recvline != NULL ); + + // copy parseline for safe processing + SAFE_STR_DUPLICATE( parseline, recvline, MAX_RE_COMMANDINFO_COMMAND_LEN ); + + // check for request identifier + char* ident_str = strtok( parseline, " " ); + if ( ident_str == NULL ) + return FALSE; + + int len = strlen( ident_str ); + if ( strncmp( recvline, RMVSTRING1, len ) != 0 ) + return FALSE; + + return TRUE; +} + +// do safe parsing of namechange request -------------------------------------- +// +int E_PacketHandler::_ParseNameChangeRequest( char* recvline, char* playername ) +{ + ASSERT( recvline != NULL ); + + // copy parseline for safe processing + SAFE_STR_DUPLICATE( parseline, recvline, MAX_RE_COMMANDINFO_COMMAND_LEN ); + + // check for request identifier + char* ident_str = strtok( parseline, " " ); + if ( ident_str == NULL ) + return FALSE; + + int len = strlen( ident_str ); + if ( strncmp( recvline, NAMESTRING1, len ) != 0 ) + return FALSE; + + //FIXME: we MUST get rid of all NEWLINES in the commands + //NOTE: the order of the token DOES matter here + char *n_str = strtok( NULL, "\n\0" ); + if ( n_str == NULL ) + return FALSE; + + memset( playername, 0, MAX_PLAYER_NAME + 1 ); + strncpy( playername, n_str, MAX_PLAYER_NAME ); + playername[ MAX_PLAYER_NAME ] = 0; + + return TRUE; +} + +// do safe parsing of INFO request -------------------------------------------- +// +int E_PacketHandler::_ParseInfoRequest( char* recvline, refframe_t* client_ping_send_frame ) +{ + ASSERT( recvline != NULL ); + ASSERT( client_ping_send_frame != NULL ); + + // copy parseline for safe processing + SAFE_STR_DUPLICATE( parseline, recvline, MAX_RE_COMMANDINFO_COMMAND_LEN ); + + // check for PING request identifier + char* ident_str = strtok( parseline, " " ); + if ( ident_str == NULL ) + return FALSE; + + int len = strlen( ident_str ); + if ( strncmp( recvline, INFOSTRING1, len ) != 0 ) { + return FALSE; + } + + // get frame + char* frame_str = strtok( NULL, " \n" ); + if ( frame_str == NULL ) { + return FALSE; + } + + char* errpart; + *client_ping_send_frame = (refframe_t) strtol( frame_str, &errpart, 10 ); + if ( *errpart != 0 ) { + return FALSE; + } + + return TRUE; +} + + +// do safe parsing of PING request -------------------------------------------- +// +int E_PacketHandler::_ParsePingRequest( char* recvline, refframe_t* client_ping_send_frame ) +{ + ASSERT( recvline != NULL ); + ASSERT( client_ping_send_frame != NULL ); + + // copy parseline for safe processing + SAFE_STR_DUPLICATE( parseline, recvline, MAX_RE_COMMANDINFO_COMMAND_LEN ); + + // check for PING request identifier + char* ident_str = strtok( parseline, " " ); + if ( ident_str == NULL ) + return FALSE; + + int len = strlen( ident_str ); + if ( strncmp( recvline, PINGSTRING1, len ) != 0 ) { + return FALSE; + } + + // get frame + char* frame_str = strtok( NULL, " \n" ); + if ( frame_str == NULL ) { + return FALSE; + } + + char* errpart; + *client_ping_send_frame = (refframe_t) strtol( frame_str, &errpart, 10 ); + if ( *errpart != 0 ) { + return FALSE; + } + + return TRUE; +} + +int E_PacketHandler::_ParseListRequest_MASTER( char* recvline, int *serverid){ + + ASSERT( recvline != NULL ); + + // copy parseline for safe processing + SAFE_STR_DUPLICATE( parseline, recvline, MAX_RE_COMMANDINFO_COMMAND_LEN ); + MSGOUT("E_PacketHandler::_ParseListRequest_MASTER(): parseline: x%sx", parseline); + + // check for Master Sever List request identifier + char* ident_str = strtok( parseline, " " ); + if ( ident_str == NULL ){ + MSGOUT("Returning NNULL on first strtok"); + return FALSE; + } + + int len = strlen( ident_str ); + if ( strncmp( recvline, MASV_LIST, len ) != 0 ) { + MSGOUT("first STRCMP: x%sx x%sx %d", recvline, MASV_LIST, len); + if (strncmp (recvline, "info", 4) != 0) { + MSGOUT("returning on second strcmp"); + return false; + } + } + + // check for the server id + ident_str = strtok( NULL , " " ); + if(ident_str == NULL){ + *serverid = -1; + return TRUE; + } + // this should be the requested server id + *serverid = atoi(ident_str); + //} + if(*serverid == 0){ + *serverid = -1; + } + + + + return TRUE; +} + +int E_PacketHandler::_ParseHBPacket_MASTER(char* recvline) { + + ASSERT( recvline != NULL ); + + + + // copy parseline for safe processing + SAFE_STR_DUPLICATE( parseline, recvline, MAX_RE_COMMANDINFO_COMMAND_LEN ); + + // check for Master Sever List request identifier + char* ident_str = strtok( parseline, " " ); + if ( ident_str == NULL ) + return FALSE; + + int len = strlen( ident_str ); + if ( strncmp( recvline, MASV_CHALLSTRING, len ) != 0 ) { + return FALSE; + } + + // ok we have a HeartBeat packet, lets get some information... + + // some declares + int PMajor = 0; + int PMinor = 0; + int Challenge = 0; + int CurrPlayers = 0; + int MaxPlayers = 0; + int ServerID = 0; + char ServerName[ MAX_SERVER_NAME + 1 ]; + + node_t _Node; + char OS[MAX_OSNAME_LEN + 1 ]; + int srv_port = 0; + + ident_str = strtok( NULL, "."); + if(ident_str == NULL){ + return FALSE; + } else { + // this should be the Protocol Major number + PMajor = atoi(ident_str); + } + + ident_str = strtok( NULL, " "); + if(ident_str == NULL){ + return FALSE; + } else { + // this should be the Protocol Minor number + PMinor = atoi(ident_str); + } + + ident_str = strtok( NULL, " ");// skip a field, as it should just denote "ch" for the challenge. + // FIXME: Check to make sure this is "ch" for proper packet structure maybe? + + // next.... + ident_str = strtok( NULL, " "); + if(ident_str == NULL){ + return FALSE; + } else { + // this should be the Challenge + Challenge = atoi(ident_str); + } + + ident_str = strtok( NULL, " ");// skip a field, as it should just denote "n" for the name. + // FIXME: Check to make sure this is "n" for proper packet structure maybe? + + // next.... + ident_str = strtok( NULL, " "); + if(ident_str == NULL){ + return FALSE; + } else { + // this should be the Server Name + strncpy(ServerName, ident_str, MAX_SERVER_NAME -1); + ServerName[MAX_SERVER_NAME] = '\0'; + } + + ident_str = strtok( NULL, " ");// skip a field, as it should just denote "p" for the player date + // FIXME: Check to make sure this is "p" for proper packet structure maybe? + + // next.... + ident_str = strtok( NULL, "/"); + if(ident_str == NULL){ + return FALSE; + } else { + // this should be the current players + CurrPlayers = atoi(ident_str); + } + ident_str = strtok( NULL, " "); + if(ident_str == NULL){ + return FALSE; + } else { + // this should be the max players + MaxPlayers = atoi(ident_str); + } + ident_str = strtok( NULL, " ");// skip a field, as it should just denote "s" for the server id + // FIXME: Check to make sure this is "s" for proper packet structure maybe? + + // next.... + ident_str = strtok( NULL, " "); + if(ident_str == NULL){ + return FALSE; + } else { + // this should be the requested server id + ServerID = atoi(ident_str); + } + + ident_str = strtok( NULL, " ");// skip a field, as it should just denote "os" for the server os + // FIXME: Check to make sure this is "os" for proper packet structure maybe? + + // next.... + ident_str = strtok( NULL, " "); + if(ident_str == NULL){ + return FALSE; + } else { + // this should be the server os + strncpy(OS, ident_str, MAX_OSNAME_LEN - 1 ); + OS[MAX_OSNAME_LEN] = '\0'; + } + + // if we get this far, then we should have everything to try to add the node to the MasterServer->ServerList. + // BUT first let's see if the node's IP resolves... + int result = TheUDPDriver->ResolveHostName(ServerName, &_Node); + if(!result) { + return FALSE; + } + + // resolved host name, let's see if they sent us a port + ident_str = strtok( NULL, " ");// skip a field, as it should just denote "p" for the server port + // FIXME: Check to make sure this is "p" for proper packet structure maybe? + if(ident_str == NULL) { + // null here means they didn't denote a port. Let's use the default + // one + NODE_StorePort(&_Node, DEFAULT_GAMESERVER_UDP_PORT); + } else { + // next.... + ident_str = strtok( NULL, " "); + if(ident_str != NULL){ + // this should be the server port + srv_port = atoi(ident_str); + } + if(srv_port > 0 ) { + NODE_StorePort(&_Node, srv_port); + } else { + // error parsing the server port, + // use the default one perhaps. + NODE_StorePort(&_Node, DEFAULT_GAMESERVER_UDP_PORT); + } + } + + + // check to see if the server already exists in the ServerList. If so, update it. If not, add it. + // if the info doesn't match, ignore the packet + int i = 0; + for(i=0; i<TheMaster->ServerList.size(); i++){ + + + word SrvID = (word)TheMaster->ServerList[i].GetSrvID(); + if(SrvID == ServerID) { + // double check the ID against the node address. + node_t node_ck; + TheMaster->ServerList[i].GetNode(&node_ck); + if(NODE_AreSame(&node_ck, &_Node)){ + // they are the same, so update the record in the server list + TheMaster->ServerList[i].update(ServerID,CurrPlayers,MaxPlayers,PMajor,PMinor,ServerName,OS,&_Node); + return TRUE; + } else { + // if we get here, the requested ServerID exists, but the node address + // doesn't match. Ignore the packet + // XXX: Perhaps send a packet back to the server to notify them the ID is taken? + return FALSE; + } + } + + } + + //MasterServerItem *tmp = ; + TheMaster->ServerList.push_back( MasterServerItem (ServerID, + CurrPlayers, + MaxPlayers, + PMajor, + PMinor, + ServerName, + OS, + &_Node )); + + // if we get this far, we should have successfully parsed the packet. So return true. + return TRUE; +} + +void E_PacketHandler::_Handle_STREAM_MASTER(NetPacket_GMSV* gamepacket, int bufid) { + + ASSERT( gamepacket != NULL ); + ASSERT( gamepacket->Command == PKTP_STREAM ); + ASSERT( gamepacket->SendPlayerId != PLAYERID_MASTERSERVER ); + + // handle an incoming stream for the master server. + RE_Header* relist = (RE_Header*)&gamepacket->RE_List; + ASSERT( relist != NULL ); + + // do not handle empty remote event lists + if ( relist->RE_Type == RE_EMPTY ) { + DBGTXT( MSGOUT( "E_PacketHandler::_Handle_STREAM_MASTER(): ignoring - no input" ); ); + return; + } + + int nNumEventsAppended = 0; + RE_CommandInfo * re_commandinfo = NULL; + node_t* clientnode = ThePacketDriver->GetPktSender( bufid ); + + char clientIP [ MAX_IPADDR_LEN + 1 ]; + inet_ntop( AF_INET, &clientnode, clientIP, MAX_IPADDR_LEN + 1 ); + + // process remote event list + while ( relist->RE_Type != RE_EMPTY ) { + + // determine size of the remote event + size_t size = E_REList::RmEvGetSize( relist ); + int serverid = 0; + // check whether RE is valid + if ( !E_REList::ValidateRE( relist, size ) ) { + DBGTXT( MSGOUT( "E_PacketHandler::_Handle_STREAM_MASTER(): ignoring invalid RE %d", relist->RE_Type ); ); + continue; + } + + switch( relist->RE_Type ) { + + case RE_COMMANDINFO: + // valid in master server in streams. + // pass this off to the Master Server Command Packet handler + // trick the packet handler a little bit... just a little... + re_commandinfo = (RE_CommandInfo *)relist; + + if ( _ParseListRequest_MASTER( re_commandinfo->command, &serverid ) ) { + /* + // walk the MasterServer->ServerList and send out IPV4 packets for all + std::vector<MasterServerItem>::iterator MSI; + for(MSI = TheMaster->ServerList.begin(); MSI != TheMaster->ServerList.end(); ++MSI){ + SendIPV4Response(clientnode, (MasterServerItem *)&MSI, nClientID); + }*/ + MSGOUT("E_PacketHandler::_Handle_STREAM_MASTER(): Got LIST from client %s\n", clientIP); + } + if ( _ParseHBPacket_MASTER(re_commandinfo->command)) { + MSGOUT("E_PacketHandler::_Handle_STREAM_MASTER(): Got HB from client: %s\n", clientIP); + return; + } else { + MSGOUT("E_PacketHandler::_Handle_STREAM_MASTER(): Failed to parse HeartBeat Packet from: %s\n", clientIP); + return; + } + + break; + case RE_SERVERLINKINFO: + // server link information. + + break; + + default: + MSGOUT( "E_PacketHandler::_Handle_STREAM_MASTER(): Invalid Packet type %d for Master Server from client %s", relist->RE_Type, clientIP); + break; + + } + + // advance to next event in list + ASSERT( ( relist->RE_BlockSize == RE_BLOCKSIZE_INVALID ) || ( relist->RE_BlockSize == size ) ); + relist = (RE_Header *) ( (char *) relist + size ); + } + + // dump the input RE list + UPDTXT2( m_pInputREList->Dump(); ); + +} + +// do safe parsing of new challenge from MASV --------------------------------- +// +int E_PacketHandler::_ParseMASVChallenge( char* recvline, int* pChallenge ) +{ + ASSERT( recvline != NULL ); + ASSERT( pChallenge != NULL ); + + // copy parseline for safe processing + char parseline[ MAX_RE_COMMANDINFO_COMMAND_LEN + 1 ]; + strncpy( parseline, recvline, MAX_RE_COMMANDINFO_COMMAND_LEN ); + parseline[ MAX_RE_COMMANDINFO_COMMAND_LEN ] = 0; + + // check for string identifier + char *ident_str = strtok( parseline, " " ); + if ( ident_str == NULL ) + return FALSE; + + int len = strlen( ident_str ); + if ( strncmp( ident_str, RECVSTR_CHALLENGE, len ) != 0 ) + return FALSE; + + // get challenge number + char* chall_str = strtok( NULL, "\0" ); + if ( chall_str == NULL ) + return FALSE; + + char *errpart; + *pChallenge = (int) strtol( chall_str, &errpart, 10 ); + if ( *errpart != 0 ) { + return FALSE; + } + + + return TRUE; +} + + +// handle a packet containing a command from the masterserver ----------------- +// +void E_PacketHandler::_Handle_COMMAND_MASV( NetPacket_GMSV* gamepacket, int bufid ) +{ + ASSERT( gamepacket != NULL ); + ASSERT( gamepacket->Command == PKTP_COMMAND ); + ASSERT( gamepacket->SendPlayerId == PLAYERID_MASTERSERVER ); + + // check whether from masterserver + if ( gamepacket->SendPlayerId != PLAYERID_MASTERSERVER ) { + return; + } + + // get the commandstring + RE_CommandInfo* re_commandinfo = (RE_CommandInfo*) &gamepacket->RE_List; + + UPDTXT2( MSGOUT( "PKTP_COMMAND M -> S: id: %d msg: %d cmd: '%s'", nClientID, gamepacket->MessageId, re_commandinfo->command ); ); + + + // parse for challenge request + int nMASVChallenge = 0; + if ( _ParseMASVChallenge( re_commandinfo->command, &nMASVChallenge ) ) { + + // set the new challenge + TheServer->SetMasterServerChallenge( nMASVChallenge ); + } +} + + +// handle a packet containing a client command -------------------------------- +// +void E_PacketHandler::_Handle_COMMAND( NetPacket_GMSV* gamepacket, int bufid ) +{ + ASSERT( gamepacket != NULL ); + ASSERT( gamepacket->Command == PKTP_COMMAND ); + + // identify sender, get the commandstring & get the sender node + int nClientID = gamepacket->SendPlayerId; + RE_CommandInfo* re_commandinfo = (RE_CommandInfo*) &gamepacket->RE_List; + node_t* clientnode = ThePacketDriver->GetPktSender( bufid ); + + UPDTXT2( MSGOUT( "PKTP_COMMAND C -> S: id: %d msg: %d cmd: '%s'", nClientID, gamepacket->MessageId, re_commandinfo->command ); ); + + // temporary storage for client connection info + E_ClientConnectInfo _ClientConnectInfo; + char playername[ MAX_PLAYER_NAME + 1 ]; + + refframe_t client_ping_send_frame; + + // parse for challenge request + if ( _ParseChallengeRequest( re_commandinfo->command ) ) { + + // handle challenge request + ASSERT( TheConnManager != NULL ); + TheConnManager->RequestChallenge( clientnode ); + + // parse for connect request + } else if ( _ParseConnectRequest( re_commandinfo->command, &_ClientConnectInfo ) ) { + + // store the sender node + NODE_Copy( &_ClientConnectInfo.m_node, clientnode ); + + // retrieve client hostname from address structure + inet_ntop( AF_INET, &_ClientConnectInfo.m_node, _ClientConnectInfo.m_szHostName, MAX_IPADDR_LEN + 1 ); + + // check whether client connection is valid ( version/banned/server full etc. ) + ASSERT( TheConnManager != NULL ); + TheConnManager->CheckClientConnect( &_ClientConnectInfo ); + + // parse for disconnect request + } else if ( _ParseDisconnectRequest( re_commandinfo->command ) ) { + + // handle disconnect request + ASSERT( TheConnManager != NULL ); + TheConnManager->CheckClientDisconnect( clientnode ); + + // parse for namechange request + } else if ( _ParseNameChangeRequest( re_commandinfo->command, playername ) ) { + + // handle namechange request + ASSERT( TheConnManager != NULL ); + TheConnManager->CheckNameChange( clientnode, playername ); + + // parse for a PING request + } else if ( _ParsePingRequest( re_commandinfo->command, &client_ping_send_frame ) ) { + + // send the PING response + SendPingResponse( client_ping_send_frame, clientnode, nClientID ); + + // parse for a INFO request + } else if ( _ParseInfoRequest( re_commandinfo->command, &client_ping_send_frame ) ) { + + // send the INFO response + SendInfoResponse( client_ping_send_frame, clientnode, nClientID ); + } +} + +// handle a packet containing a client command -------------------------------- +// +void E_PacketHandler::_Handle_COMMAND_MASTER( NetPacket_GMSV* gamepacket, int bufid ) +{ + ASSERT( gamepacket != NULL ); + ASSERT( gamepacket->Command == PKTP_COMMAND ); + + // identify sender, get the commandstring & get the sender node + int nClientID = gamepacket->SendPlayerId; + RE_CommandInfo* re_commandinfo = (RE_CommandInfo*) &gamepacket->RE_List; + node_t* clientnode = ThePacketDriver->GetPktSender( bufid ); + + char clientIP [ MAX_IPADDR_LEN + 1 ]; + inet_ntop( AF_INET, &clientnode, clientIP, MAX_IPADDR_LEN + 1 ); + + UPDTXT2( MSGOUT( "PKTP_COMMAND C -> S: id: %d msg: %d cmd: '%s'", nClientID, gamepacket->MessageId, re_commandinfo->command ); ); + + // temporary storage for client connection info + E_ClientConnectInfo _ClientConnectInfo; + char playername[ MAX_PLAYER_NAME + 1 ]; + + refframe_t client_ping_send_frame; + + int serverid = 0; + if ( _ParseListRequest_MASTER( re_commandinfo->command, &serverid ) ) { + + // walk the MasterServer->ServerList and send out IPV4 packets for all or the one + + SendIPV4Response(clientnode, nClientID, serverid); + + MSGOUT("E_PacketHandler::_Handle_COMMAND_MASTER(): Got LIST from client: %s\n", clientIP); + return; + } + if ( _ParseHBPacket_MASTER(re_commandinfo->command)) { + MSGOUT("E_PacketHandler::_Handle_COMMAND_MASTER(): Got HB from client: %s\n", clientIP); + return; + } else { + MSGOUT("E_PacketHandler::_Handle_COMMAND_MASTER(): Failed to parse HeartBeat Packet from: %s\n", clientIP); + return; + } + MSGOUT("E_PacketHandler::_Handle_COMMAND_MASTER(): Unimplemented packet %s from client %s\n", re_commandinfo->command, clientIP); + +} + +// handle a stream packet ----------------------------------------------------- +// +void E_PacketHandler::_Handle_STREAM( NetPacket_GMSV* gamepacket ) +{ + ASSERT( gamepacket != NULL ); + ASSERT( gamepacket->Command == PKTP_STREAM ); + ASSERT( gamepacket->SendPlayerId != PLAYERID_MASTERSERVER ); + + // identify sender & get the sender node + int nClientID = gamepacket->SendPlayerId; + + UPDTXT2( MSGOUT( "PKTP_STREAM C -> S: id: %d msg: %d", nClientID, gamepacket->MessageId ); ); + + // ignore PKTP_STREAM packets from invalid senders + if ( ( nClientID < 0 ) || ( nClientID >= MAX_NUM_CLIENTS ) ) { + MSGOUT( "ignoring PKTP_STREAM packet from invalid sender id: %d", nClientID ); + return; + } + + E_ClientInfo* pClientInfo = TheConnManager->GetClientInfo( nClientID ); + + // player must be connected in order to send NODE_ALIVE + if ( pClientInfo->IsSlotFree() ) { + DBGTXT( MSGOUT( "_Handle_STREAM(): filtering GAME_STATE for player who is not connected: %d.", nClientID ); ); + return; + } + + // get the player info + E_SimPlayerInfo* pSimPlayerInfo = TheSimulator->GetSimPlayerInfo( nClientID ); + ASSERT( pSimPlayerInfo != NULL ); + ASSERT( pSimPlayerInfo->IsPlayerDisconnected() == FALSE ); + + // mark the sender alive + pClientInfo->MarkAlive(); + + // handle the network input + TheSimNetInput->HandleOneClient( nClientID, (RE_Header*)&gamepacket->RE_List ); +} + + + +int E_PacketHandler::SendIPV4Response(node_t* clientnode, int nClientID, int serverid) { + + + ASSERT( ( ( nClientID >= 0 ) && ( nClientID <= MAX_NET_ALLOC_SLOTS ) ) || ( nClientID == PLAYERID_ANONYMOUS ) ); + ASSERT( clientnode != NULL ); + int i = 0; + for(i=0; i<TheMaster->ServerList.size(); i++){ + + if(serverid == -1 || serverid == TheMaster->ServerList[i].GetSrvID()){ + + E_REList* pUnreliable = E_REList::CreateAndAddRef( RE_LIST_MAXAVAIL ); + // append a remote event containing the command + + + node_t node; + TheMaster->ServerList[i].GetNode( &node); + word SrvID = (word)TheMaster->ServerList[i].GetSrvID(); + + + int rc = pUnreliable->NET_Append_RE_IPv4ServerInfo( &node, SrvID, SrvID, SrvID, 0 ); + ASSERT( rc == TRUE ); + + // send a datagram + ThePacketHandler->Send_STREAM_Datagram( pUnreliable, clientnode, nClientID ); + + // release the RE list from here + pUnreliable->Release(); + } + } + return TRUE; +} diff --git a/src/parsec_server/e_simnetinput.cpp b/src/parsec_server/e_simnetinput.cpp new file mode 100755 index 0000000..b13919c --- /dev/null +++ b/src/parsec_server/e_simnetinput.cpp @@ -0,0 +1,401 @@ +/* + * PARSEC - Server-side Simulator INPUT + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:46 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2001-2002 + * + * 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +//#include <math.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" + +// UNP header +#include "net_wrap.h" + +// server defs +//#include "e_defs.h" + +// network code config +//#include "net_conf.h" + +// net game header +#include "net_game_sv.h" + +// mathematics header +//#include "utl_math.h" + +// utility headers +#include "utl_list.h" + +// local module header +#include "e_simnetinput.h" + +// proprietary module headers +#include "con_aux_sv.h" +//#include "g_colldet.h" +//#include "g_extra.h" +//#include "net_csdf.h" +#include "net_packetdriver.h" +#include "net_stream.h" +//#include "obj_creg.h" +//#include "e_stats.h" +#include "g_main_sv.h" +#include "e_simplayerinfo.h" +#include "e_simulator.h" +#include "e_simnetoutput.h" +#include "e_connmanager.h" +#include "e_gameserver.h" +#include "e_packethandler.h" +//#include "e_relist.h" +#include "sys_refframe_sv.h" + + +// default ctor --------------------------------------------------------------- +// +E_SimNetInput::E_SimNetInput() : + m_pInputREList( NULL ) +{ +} + + +// default dtor --------------------------------------------------------------- +// +E_SimNetInput::~E_SimNetInput() +{ + if ( m_pInputREList != NULL ) + m_pInputREList->Release(); +} + + +// reset all data ------------------------------------------------------------- +// +void E_SimNetInput::Reset() +{ + if ( m_pInputREList != NULL ) { + m_pInputREList->Release(); + } + // initialize input remote event list + m_pInputREList = E_REList::CreateAndAddRef( (size_t)RE_LIST_MAXAVAIL * ( MAX_NUM_CLIENTS + 1 ) ); +} + + +// walk the input RE list and apply events/states to simulation --------------- +// +void E_SimNetInput::ProcessInputREList() +{ + RE_Header* relist = m_pInputREList->GetData(); + ASSERT( relist != NULL ); + + int nClientID = PLAYERID_ANONYMOUS; + + // process remote event list + while ( relist->RE_Type != RE_EMPTY ) { + + // determine size of the remote event + size_t size = E_REList::RmEvGetSize( relist ); + + switch( relist->RE_Type ) { + case RE_OWNERSECTION: + // set the sender + nClientID = ( (RE_OwnerSection*)relist )->owner; + break; + + case RE_CREATEEXTRA2: + case RE_CREATEEXTRA: + MSGOUT("CREATEEXTRA Event recieved"); + break; + + case RE_IPV4SERVERINFO: + case RE_SERVERLINKINFO: + case RE_MAPOBJECT: + case RE_STARGATE: + // invalid/already processed remote events MUST not be in the input remote event l + ASSERT( FALSE ); + break; + + case RE_CLIENTINFO: + // adjust client-info ( send/recv frequency ) + TheConnManager->NET_ExecRmEvClientInfo( nClientID, (RE_ClientInfo*)relist ); + break; + + case RE_PLAYERSTATUS: + { + ASSERT( nClientID != PLAYERID_ANONYMOUS ); + RE_PlayerStatus* playerstatus = (RE_PlayerStatus*)relist; + + // set the desired status (joined/unjoined) + TheSimulator->GetSimPlayerInfo( nClientID )->SetDesiredPlayerStatus( playerstatus ); + } + break; + + case RE_PLAYERANDSHIPSTATUS: + { + ASSERT( nClientID != PLAYERID_ANONYMOUS ); + + RE_PlayerAndShipStatus* re_pas_status = (RE_PlayerAndShipStatus*)relist; + + //MSGOUT( "_ParseNetInput(): RE_PLAYERANDSHIPSTATUS from %d, state %d", nClientID, re_pas_status->player_status ); + + // set the desired status (joined/unjoined) + E_SimPlayerInfo* pSimPlayerInfo = TheSimulator->GetSimPlayerInfo( nClientID ); + pSimPlayerInfo->SetDesiredPlayerStatus( re_pas_status ); + + // only update player state if player is joined + if( pSimPlayerInfo->IsPlayerJoined() ) { + + E_SimClientState* pSimClientState = TheSimulator->GetSimClientState( nClientID ); + NET_Stream* pStream = ThePacketDriver->GetStream( nClientID ); + + int bAcceptMovement = FALSE; + + dword dwInputMessageID = pStream->GetLastInPacket(); + + // check absolute movement bounds + if ( _CheckAbsoluteMovementBounds( re_pas_status ) ) { + + // check whether the movement from the last received packet to the current packet is possible + if ( pSimClientState->CheckMovementBounds( dwInputMessageID, re_pas_status ) ) { + + // store the received state as the newest + pSimClientState->StoreNewState( dwInputMessageID, re_pas_status ); + + bAcceptMovement = TRUE; + } + } + + // resync client if state has not been accepted + if ( bAcceptMovement == FALSE ) { + + // this client's state differs too much from the one on the server -> resync with server + pSimClientState->SetClientResync(); + MSGOUT( "SetClientResync() for client %d", nClientID ); + } + } + } + break; + case RE_SENDTEXT: + + // multicast RE to all connected clients except sender + //FIXME: determine, whether to send RE_SENDTEXT reliable ? + //FIXME: this appends a RE_OWNERSECTION for each RE we buffer + // evt. keep track of whether we already appended a RE_OWNERSECTION + TheSimNetOutput->BufferForMulticastRE( relist, nClientID/*, append_ownersection*/, FALSE ); + + break; + + case RE_CREATELASER: + { + ASSERT( nClientID != PLAYERID_ANONYMOUS ); + if ( TheSimulator->IsPlayerJoined( nClientID ) ) { + //FIXME: we must determine the exact time the client fired the laser ( relative to when the packet was sent ) + TheGameInput->ActivateGun( nClientID, 0 ); + } + } + break; + case RE_CREATEMISSILE: + { + ASSERT( nClientID != PLAYERID_ANONYMOUS ); + if ( TheSimulator->IsPlayerJoined( nClientID ) ){ + TheGameInput->LaunchMissile( nClientID, ((RE_CreateMissile *)relist)->TargetHostObjId, + ((RE_CreateMissile *)relist)->ObjectClass); + } + } + break; + case RE_CREATEMINE: + { + ASSERT( nClientID != PLAYERID_ANONYMOUS ); + if ( TheSimulator->IsPlayerJoined( nClientID ) ){ + TheGameInput->LaunchMine( nClientID); + } + } + break; + case RE_CREATESWARM: + { + ASSERT( nClientID != PLAYERID_ANONYMOUS ); + if ( TheSimulator->IsPlayerJoined( nClientID ) ){ + TheGameInput->LaunchSwarm( nClientID, ((RE_CreateSwarm *)relist)->TargetHostObjId); //Set up Sim Object + TheSimNetOutput->BufferForMulticastRE( relist, nClientID, FALSE ); //Notify Clients of firing + } + } + case RE_WEAPONSTATE: + { + switch (((RE_WeaponState *)relist)->WeaponMask ) { + case WPMASK_CANNON_LIGHTNING: + if (((RE_WeaponState *) relist)->State == WPSTATE_ON ) + TheGameInput->ActivateGun( nClientID, 2 ); + else + TheGameInput->DeactivateGun( nClientID, 2 ); + break; + + case WPMASK_CANNON_HELIX: + if (((RE_WeaponState *) relist)->State == WPSTATE_ON ) + TheGameInput->ActivateGun( nClientID, 1 ); + else + TheGameInput->DeactivateGun( nClientID, 1 ); + break; + + case WPMASK_CANNON_PHOTON: + if (((RE_WeaponState *) relist)->State == WPSTATE_ON ) + TheGameInput->ActivateGun( nClientID, 3 ); + else + TheGameInput->DeactivateGun( nClientID, 3 ); + break; + + } + TheSimNetOutput->BufferForMulticastRE( relist, nClientID, FALSE ); //Duration weapons are not objects, just tell the client someone has fired/stopped + } + break; + case RE_CREATEEMP: + { + // Create the EMP object + ASSERT( nClientID != PLAYERID_ANONYMOUS ); + if ( TheSimulator->IsPlayerJoined( nClientID ) ){ + TheGameInput->CreateEMP( nClientID, ((RE_CreateEmp *)relist)->Upgradelevel); //Set up Sim Object + TheSimNetOutput->BufferForMulticastRE( relist, nClientID, FALSE ); //Notify Clients of firing + } + + + } + break; + default: + // we must handle all REs in this function + MSGOUT("UNKNOWN RE: %d received. ", relist->RE_Type); + // ASSERT( FALSE ); This is broken but I dont need to kill the game over it + break; + + } + + // advance to next event in list + ASSERT( ( relist->RE_BlockSize == RE_BLOCKSIZE_INVALID ) || ( relist->RE_BlockSize == size ) ); + relist = (RE_Header *) ( (char *) relist + size ); + } + + // clear all the input remote events + m_pInputREList->Clear(); +} + + + +// handle the network input from a client ( filter out invalid events, append valid ones to the input RE list ) +// +int E_SimNetInput::HandleOneClient( int nClientID, RE_Header* relist ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + ASSERT( relist != NULL ); + + // do not handle empty remote event lists + if ( relist->RE_Type == RE_EMPTY ) { + DBGTXT( MSGOUT( "E_Simulator::HandleOneClient(): ignoring - no input" ); ); + return FALSE; + } + + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + ASSERT( relist != NULL ); + + int nNumEventsAppended = 0; + + // process remote event list + while ( relist->RE_Type != RE_EMPTY ) { + + // determine size of the remote event + size_t size = E_REList::RmEvGetSize( relist ); + + // check whether RE is valid + if ( !E_REList::ValidateRE( relist, size ) ) { + DBGTXT( MSGOUT( "E_Simulator::_ParseNetInput(): ignoring invalid RE %d", relist->RE_Type ); ); + continue; + } + + switch( relist->RE_Type ) { + case RE_OWNERSECTION: + // RE_OWNERSECTION packets are invalid from client + DBGTXT( MSGOUT( "WARNING: client packet from %d contained RE_OWNERSECTION", nClientID ); ); + break; + + case RE_CREATEEXTRA: + case RE_CREATEEXTRA2: + { + MSGOUT( "WARNING: client packet from %d contained RE_CREATEEXTRA()", nClientID ); } + break; + + case RE_COMMANDINFO: + + //FIXME: handle RE_COMMANDINFO in normal PKTP_STREAM packets ( _Handle_COMMAND() ) + ASSERT( FALSE ); + break; + + default: + { + // append a RE_OWNERSECTION to sender in input RE list + if ( nNumEventsAppended == 0 ) { + if ( !m_pInputREList->NET_Append_RE_OwnerSection( nClientID ) ) { + ASSERT( FALSE ); + DBGTXT( MSGOUT( "E_Simulator::_ParseNetInput(): NET_Append_RE_OwnerSection failed" ); ); + } + } + + // append the remote event to the input RE list + if ( (size_t)m_pInputREList->AppendEvent( relist, size ) != size ) { + ASSERT( FALSE ); + DBGTXT( MSGOUT( "E_Simulator::_ParseNetInput(): AppendEvent failed" ); ); + } + + // increase event counter + nNumEventsAppended++; + } + break; + + } + + // advance to next event in list + ASSERT( ( relist->RE_BlockSize == RE_BLOCKSIZE_INVALID ) || ( relist->RE_BlockSize == size ) ); + relist = (RE_Header *) ( (char *) relist + size ); + } + + // dump the input RE list + UPDTXT2( m_pInputREList->Dump(); ); + + return TRUE; +} + +// check absolute movement bounds --------------------------------------------- +// +int E_SimNetInput::_CheckAbsoluteMovementBounds( RE_PlayerAndShipStatus* pPAS ) +{ + ASSERT( pPAS != NULL ); + //FIXME: implement + return TRUE; +} + diff --git a/src/parsec_server/e_simnetoutput.cpp b/src/parsec_server/e_simnetoutput.cpp new file mode 100755 index 0000000..1c0afd3 --- /dev/null +++ b/src/parsec_server/e_simnetoutput.cpp @@ -0,0 +1,1195 @@ +/* + * PARSEC - Server-side Simulator OUTPUT + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:46 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2001-2002 + * + * 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +//#include <math.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" + +// UNP header +#include "net_wrap.h" + +// server defs +//#include "e_defs.h" + +// network code config +//#include "net_conf.h" + +// net game header +#include "net_game_sv.h" + +// mathematics header +//#include "utl_math.h" + +// utility headers +#include "utl_list.h" + +// local module header +#include "e_simnetoutput.h" + +// proprietary module headers +#include "con_aux_sv.h" +//#include "g_colldet.h" +#include "g_extra.h" +//#include "net_csdf.h" +#include "net_packetdriver.h" +//#include "net_stream.h" +//#include "obj_creg.h" +//#include "e_stats.h" +#include "g_main_sv.h" +//#include "e_simplayerinfo.h" +#include "e_simulator.h" +//#include "e_simnetinput.h" +#include "e_connmanager.h" +#include "e_gameserver.h" +#include "e_packethandler.h" +//#include "e_relist.h" +#include "sys_refframe_sv.h" + + + + + +// max. packets per client per second ----------------------------------------- +// +#define MAX_PACKETS_PER_SEC TheServer->GetSimFrequency() + +// standard ctor -------------------------------------------------------------- +// +E_SimClientNetOutput::E_SimClientNetOutput() +{ + m_SentPacketInfo = NULL; + m_nPacket = 0; + m_pReliableBuffer = NULL; + m_pUnreliableBuffer = NULL; + m_nNumPacketSlots = 0; + m_AllDistributables = NULL; + m_nDestClientID = PLAYERID_ANONYMOUS; + + m_ClientIDList = NULL; + m_SendReliable = NULL; + m_nNumDistsForNextPacket= 0; + m_nSizeAvail = NET_UDP_DATA_LENGTH; + + m_bIncludeDestClientState= FALSE; +} + + +// standard dtor -------------------------------------------------------------- +// +E_SimClientNetOutput::~E_SimClientNetOutput() +{ + Disconnect(); +} + + +// reset all data members ----------------------------------------------------- +// +void E_SimClientNetOutput::_Reset() +{ + if ( m_pReliableBuffer != NULL ) { + m_pReliableBuffer->Release(); + m_pReliableBuffer = NULL; + } + + if ( m_pUnreliableBuffer != NULL ) { + m_pUnreliableBuffer->Release(); + m_pUnreliableBuffer = NULL; + } + + delete []m_SentPacketInfo; + delete m_AllDistributables; + m_SentPacketInfo = NULL; + m_AllDistributables = NULL; + + m_nDestClientID = PLAYERID_ANONYMOUS; + m_nPacket = 0; + m_LastSendFrame = 0; + m_Send_FrameTime = 0; + m_nNumPacketSlots = 0; + m_nAveragePacketSize = NET_UDP_DATA_LENGTH; + m_Heartbeat_Timeout_Frame = -1; + + delete []m_ClientIDList; + delete []m_SendReliable; + m_ClientIDList = NULL; + m_SendReliable = NULL; +} + + +// connect a client ----------------------------------------------------------- +// +void E_SimClientNetOutput::Connect( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + ASSERT( m_SentPacketInfo == NULL ); + ASSERT( m_AllDistributables == NULL ); + ASSERT( m_pReliableBuffer == NULL ); + ASSERT( m_pUnreliableBuffer == NULL ); + + m_nDestClientID = nClientID; + + m_nNumPacketSlots = MAX_PACKETS_PER_SEC * TheServer->GetPacketAverageSecs(); + m_SentPacketInfo = new sent_packet_info_s[ m_nNumPacketSlots ]; + m_nPacket = 0; + m_nAveragePacketSize = NET_UDP_DATA_LENGTH; + + RateChanged(); + m_LastSendFrame = 0; + + m_AllDistributables = new UTL_List<E_Distributable*>; + + // -- + m_ClientIDList = new int [ MAX_NUM_CLIENTS ]; + m_SendReliable = new bool_t[ MAX_NUM_CLIENTS ]; + _ClearUpdates(); + + _DoHeartbeat(); +} + + +// disconnect a client -------------------------------------------------------- +// +void E_SimClientNetOutput::Disconnect() +{ + _Reset(); +} + + +// choke client update to the rate (byte/sec.) set ---------------------------- +// +int E_SimClientNetOutput::_CanSendPacket() +{ + refframe_t now = SYSs_GetRefFrameCount(); + + //printf( "now: %d, m_LastSendFrame: %d, m_Send_FrameTime: %d\n", now, m_LastSendFrame, m_Send_FrameTime ); + + // check whether frame has already passed + if ( ( now - m_LastSendFrame ) < m_Send_FrameTime ) { + return FALSE; + } else { + m_LastSendFrame = now; + return TRUE; + } +} + + +// set the new heartbeat timeout value ---------------------------------------- +// +void E_SimClientNetOutput::_DoHeartbeat() +{ + //FIXME: change to based timing + m_Heartbeat_Timeout_Frame = SYSs_GetRefFrameCount() + TheServer->GetClientUpdateHeartbeat(); +} + + +// check whether to send a heartbeat to the client ---------------------------- +// +int E_SimClientNetOutput::_ShouldSendHeartbeat() +{ + return ( m_Heartbeat_Timeout_Frame < SYSs_GetRefFrameCount() ); +} + + +// schedule a E_Distributable to be sent to the client -------------------------- +// +void E_SimClientNetOutput::ScheduleDistributable( E_Distributable* pDist, int check_unique /*= FALSE*/ ) +{ + // only allow distributables where destination client is not the owner + if ( ( GetOwnerFromHostOjbNumber( pDist->GetObjectID() ) != (dword)m_nDestClientID ) || pDist->WillBeSentToOwner() ) { + + // check that the E_Distributable is not already in the list for this client ( STATE & REMOVE in same packet-send-frame ) + if ( check_unique ) { + if ( m_AllDistributables->Find( pDist ) != NULL ) { + DBGTXT( MSGOUT( "DIST: E_SimClientNetOutput::ScheduleDistributable(): IGNORING DUPLICATE client %d objectid %x listno %d reliable %d", m_nDestClientID, pDist->GetObjectID(), pDist->GetListNo(), pDist->NeedsReliable() ); ); + return; + } + } + + m_AllDistributables->AppendTail( pDist ); + + DBGTXT( MSGOUT( "DIST: E_SimClientNetOutput::ScheduleDistributable(): client %d objectid %x listno %d reliable %d", m_nDestClientID, pDist->GetObjectID(), pDist->GetListNo(), pDist->NeedsReliable() ); ); + } +} + + +// (re)calculate the average packet size -------------------------------------- +// +void E_SimClientNetOutput::CalculateAveragePacketSize() +{ + size_t nTotalSize = 0; + for( int nPktSlot = 0; nPktSlot < m_nNumPacketSlots; nPktSlot++ ) { + nTotalSize += m_SentPacketInfo[ nPktSlot ].m_size; + } + int nNewAveragePacketSize = nTotalSize / m_nNumPacketSlots; + if ( m_nAveragePacketSize != nNewAveragePacketSize ) { + m_nAveragePacketSize = nNewAveragePacketSize; + //MSGOUT( "NEW AveragePacketSize for %d is %d", m_nDestClientID, m_nAveragePacketSize ); + + // recalc send-frametime + RateChanged(); + } +} + + +// the RATE for this client has changed - recalc frame-time ------------------- +// +void E_SimClientNetOutput::RateChanged() +{ + // get the frequency + float fClientSendFreq = (float)TheConnManager->GetClientInfo( m_nDestClientID )->GetRecvRate() / (float)m_nAveragePacketSize; + + //FIXME: is this the correct value to clamp to ???? + if ( fClientSendFreq > (float)TheServer->GetSimFrequency() ) { + //MSGOUT( "NEW ClientSendFreq for %d clamped to %d", m_nDestClientID, TheServer->GetSimFrequency() ); + fClientSendFreq = (float)TheServer->GetSimFrequency(); + } else { + + //FIXME: clamp client send frequency to 0.5 Hz + //if ( fClientSendFreq < ) + + //MSGOUT( "NEW ClientSendFreq for %d is %f", m_nDestClientID, fClientSendFreq ); + } + + // calculate the refframes between 2 packets to this client + m_Send_FrameTime = (refframe_t)((float)FRAME_MEASURE_TIMEBASE / (float)fClientSendFreq); +} + + +// buffer an RE for output ---------------------------------------------------- +// +void E_SimClientNetOutput::BufferRE( RE_Header* re, int reliable ) +{ + ASSERT( re != NULL ); + + // reserve + size_t resize = E_REList::RmEvGetSize( re ); + size_t size = E_REList::RmEvGetSizeFromType( RE_OWNERSECTION ); + if ( !_ReserveForOutput( size + resize ) ) { + DBGTXT( MSGOUT( "E_SimClientNetOutput::BufferRE(): overflow" ); ); + return; + } + + // create buffers + _CreateBuffers(); + + E_REList* pBufferREList = ( reliable ? m_pReliableBuffer : m_pUnreliableBuffer ); + ASSERT( pBufferREList != NULL ); + + // append RE_OWNERSECTION and supplied remote event to buffer RE list + pBufferREList->NET_Append_RE_OwnerSection( m_nDestClientID ); + pBufferREList->AppendEvent( re, resize ); +} + + +// determine which states must be updated at the destination client ----------- +// +int E_SimClientNetOutput::_PrepareClientUpdateInfo() +{ + //NOTE: this method prepares the update to the client. that is, it decides + // what to send in the next packet ( game-state, kill-statsl, client + // info, destination client info, distributables ). + // this information is stored in m_ClientUpdateInfo and is used + // in E_SimClientNetOutput::_FillPacketForClient() + + ASSERT( ( m_nDestClientID >= 0 ) && ( m_nDestClientID < MAX_NUM_CLIENTS ) ); + + // start with empty update list + _ClearUpdates(); + + //FIXME: here we must add much better heuristic to decide what to send to a + // specific client (e.g. distance based, direction based, joined/unjoined, + // last time info sent etc. ) + + E_SimClientState* pSimClientState = TheSimulator->GetSimClientState( m_nDestClientID ); + + // include the destination client's state if no resync and if player joined + m_bIncludeDestClientState = ( pSimClientState->NeedsResync() == FALSE ) && TheSimulator->IsPlayerJoined( m_nDestClientID ); + +#define LAG_CLIENT_STATE +#ifdef LAG_CLIENT_STATE + m_bIncludeDestClientState &= _ShouldSendHeartbeat(); +#endif // LAG_CLIENT_STATE + + if(!pSimClientState->HasState()) { + size_t state_size = E_REList::RmEvGetSizeFromType(RE_STATESYNC) * 7; //We current send 7 states to the client + if(_ReserveForOutput( state_size )) { + m_bIncludeStateSync = SEND_MODE_UNRELIABLE; + } + + } + + // check whether to include the gamestate ( RE_GameState ) in a heartbeat packet + //FIXME: why only if NeedsResync() == FALSE ? + if ( !pSimClientState->NeedsResync() && _ShouldSendHeartbeat() ) { + + size_t size = E_REList::RmEvGetSizeFromType( RE_KILLSTATS ) + + E_REList::RmEvGetSizeFromType( RE_GAMESTATE ); + + if ( _ReserveForOutput( size ) ) { + + m_bIncludeGameState = SEND_MODE_UNRELIABLE; + m_bIncludeKillStats = SEND_MODE_UNRELIABLE; + + _DoHeartbeat(); + } + } + + // iterate over all connected clients ( check for forced resync ) + for( int nClientID = 0; nClientID < MAX_NUM_CLIENTS; nClientID++ ) { + if( !TheSimulator->IsPlayerDisconnected( nClientID ) ) { + + bool_t bIncludeUnreliable = false; + bool_t bIncludeReliable = false; + + // state information of other clients is sent UNRELIABLE + if ( nClientID != m_nDestClientID ) { + bIncludeUnreliable = true; + } else { + + // send info about client itself, if we need a resync + if ( pSimClientState->NeedsResync() ) { + + // disable client resync + pSimClientState->ClearClientResync(); + + DBGTXT( + { + dword dwMsg = ThePacketDriver->GetNextOutMessageId( nClientID ); + MSGOUT( "NeedsResync() for client %d. including server-state in update packet %d.", m_nDestClientID, dwMsg ); } + ); + + // forced resync ( state information about the destination client ) is sent RELIABLE + bIncludeReliable = true; + } else { + + // include destination clients' state in heartbeat packets + if ( m_bIncludeDestClientState ) { + bIncludeUnreliable = true; + } + } + } + + // include the client in the update ? + if ( bIncludeUnreliable || bIncludeReliable ) { + + if ( _ReserveForOutput( E_REList::RmEvGetSizeFromType( RE_PLAYERANDSHIPSTATUS ) ) ) { + _FlagIncludeClient( nClientID, bIncludeReliable ); + } + } + } + } + + +#ifdef PARSEC_DEBUG + + int nIndex = 0; + for( UTL_listentry_s<E_Distributable*>* entry1 = m_AllDistributables->GetHead(); entry1 != NULL; ) { + E_Distributable* pDist = entry1->m_data; + DBGTXT( MSGOUT( "DIST: DUMP of m_AllDistributables[ %d ]: client %d objectid %x listno %d reliable %d", nIndex, m_nDestClientID, pDist->GetObjectID(), pDist->GetListNo(), pDist->NeedsReliable() ); ); + entry1 = entry1->m_pNext; + nIndex++; + } + +#endif + + + // iterate through all distributables for this client and prioritize them + for( UTL_listentry_s<E_Distributable*>* entry = m_AllDistributables->GetHead(); entry != NULL; ) { + + E_Distributable* pDist = entry->m_data; + ASSERT( pDist->HasUpdate( m_nDestClientID ) ); + + // get next in list + UTL_listentry_s<E_Distributable*>* nextentry = entry->m_pNext; + + // reserve space in output packet + if ( _ReserveForOutput( pDist->DetermineSizeInPacket() ) ) { + + ASSERT( _GetSizeAvail() > 0 ); + + //FIXME: prioritize distributables + _AddDistForOutput( pDist ); + + DBGTXT( MSGOUT( "DIST: E_SimClientNetOutput::_PrepareClientUpdateInfo(): client %d objectid %x listno %d reliable %d", m_nDestClientID, pDist->GetObjectID(), pDist->GetListNo(), pDist->NeedsReliable() ); ); + + // remove from list + m_AllDistributables->RemoveEntry( entry ); + } else { + //FIXMEL + DBGTXT( MSGOUT( "DIST: E_SimClientNetOutput::_PrepareClientUpdateInfo(): SKIPPING due to space constraints. client %d objectid %x listno %d reliable %d", m_nDestClientID, pDist->GetObjectID(), pDist->GetListNo(), pDist->NeedsReliable() ); ); + } + + // step to next entry + entry = nextentry; + } + + + return _HasUpdates(); +} + + +// fill the RE lists to be sent to the client --------------------------------- +// +int E_SimClientNetOutput::_FillPacketForClient( E_REList* pReliable, E_REList* pUnreliable ) +{ + ASSERT( pReliable != NULL ); + ASSERT( pUnreliable != NULL ); + + //FIXME: we want to send the state of all players as unreliable and not necessarely the whole state in one packet + + //FIXME: we get a serious space problem with our default packet size here, if more + // than 10 (=1000/92) players connected. strip size of RE_PlayerAndShipStatus and increase max. + // packetsize to ~1400 bytes ( should be MTU of Ethernet packet minus PPP/IP/UDP headers ) + + // append a RE containing state for each connected player ( all but the destination player ) + for( int nAboutClient = 0; nAboutClient < m_nNumClients; nAboutClient++ ) { + + // get the client-id we want to send information about + int nClientID = m_ClientIDList[ nAboutClient ]; + + // get the result state from last simulation run + // NOTE: as the simframe counter is increased at the end of each sim-frame and the network update + // is done after that, we need to get the state from the last-sim-frame here + E_SimShipState* pPrevState = TheSimulator->GetSimClientState( nClientID )->GetPrevSimFrameStateSlot(); + + //DBGTXT( LOGOUT( "SENDING info about %d to %d: phi: %f", nClientID, nDestClientID, RAD_TO_DEG( acos( pPrevState->m_ObjPosition[ 0 ][ 0 ] ) ) ); ); + + // determine in which RE list we want to stuff the state + E_REList* relist = ( m_SendReliable[ nAboutClient ] ? pReliable : pUnreliable ); + + bool_t bUpdatePropsOnly = ( ( nClientID == m_nDestClientID ) && m_bIncludeDestClientState ); + if ( !relist->NET_Append_RE_PlayerAndShipStatus( nClientID, + TheSimulator->GetSimPlayerInfo( nClientID ), + pPrevState, + TheSimulator->GetCurSimRefFrame(), + bUpdatePropsOnly ) ) { + DBGTXT( MSGOUT( "E_SimClientNetOutput::_FillPacketForClient(): packet choke" ); ); + return FALSE; + } + } + + // include a StateSync ? + if (m_bIncludeStateSync != SEND_MODE_NONE) { + E_SimClientState* pSimClientState = TheSimulator->GetSimClientState( m_nDestClientID ); + E_REList* relist = ( m_bIncludeStateSync == SEND_MODE_UNRELIABLE ? pUnreliable : pReliable ); + if(!relist->RmEvStateSync(RMEVSTATE_NEBULAID,TheGame->m_NebulaID)) { + DBGTXT( MSGOUT( "E_SimClientNetOutput::_FillPacketForClient(): packet choke" ); ); + return FALSE; + } + if(!relist->RmEvStateSync(RMEVSTATE_ENERGYBOOST,TheGame->EnergyExtraBoost)) { + DBGTXT( MSGOUT( "E_SimClientNetOutput::_FillPacketForClient(): packet choke" ); ); + return FALSE; + } + if(!relist->RmEvStateSync(RMEVSTATE_REPAIRBOOST,TheGame->RepairExtraBoost)) { + DBGTXT( MSGOUT( "E_SimClientNetOutput::_FillPacketForClient(): packet choke" ); ); + return FALSE; + } + if(!relist->RmEvStateSync(RMEVSTATE_DUMBPACK,TheGame->DumbPackNumMissls)) { + DBGTXT( MSGOUT( "E_SimClientNetOutput::_FillPacketForClient(): packet choke" ); ); + return FALSE; + } + if(!relist->RmEvStateSync(RMEVSTATE_HOMPACK,TheGame->HomPackNumMissls)) { + DBGTXT( MSGOUT( "E_SimClientNetOutput::_FillPacketForClient(): packet choke" ); ); + return FALSE; + } + if(!relist->RmEvStateSync(RMEVSTATE_SWARMPACK,TheGame->SwarmPackNumMissls)) { + DBGTXT( MSGOUT( "E_SimClientNetOutput::_FillPacketForClient(): packet choke" ); ); + return FALSE; + } + if(!relist->RmEvStateSync(RMEVSTATE_PROXPACK,TheGame->ProxPackNumMines)) { + DBGTXT( MSGOUT( "E_SimClientNetOutput::_FillPacketForClient(): packet choke" ); ); + return FALSE; + } + pSimClientState->SetState(); + } + + // include a RE_GAMESTATE ? + if ( m_bIncludeGameState != SEND_MODE_NONE ) { + E_REList* relist = ( m_bIncludeGameState == SEND_MODE_UNRELIABLE ? pUnreliable : pReliable ); + if ( !relist->NET_Append_RE_GameState() ) { + DBGTXT( MSGOUT( "E_SimClientNetOutput::_FillPacketForClient(): packet choke" ); ); + return FALSE; + } + } + + // include a RE_KILLSTATS ? + if ( m_bIncludeKillStats != SEND_MODE_NONE ) { + E_REList* relist = ( m_bIncludeKillStats == SEND_MODE_UNRELIABLE ? pUnreliable : pReliable ); + if ( !relist->NET_Append_RE_KillStats() ) { + DBGTXT( MSGOUT( "E_SimClientNetOutput::_FillPacketForClient(): packet choke" ); ); + return FALSE; + } + } + + // create remote events for all distributables to send in this packet + for( int nEntry = 0; nEntry < m_nNumDistsForNextPacket; nEntry++ ) { + + E_Distributable* pDist = m_DistsForNextPacket[ nEntry ]; + ASSERT( pDist->HasUpdate( m_nDestClientID ) ); + + E_REList* relist = pDist->NeedsReliable() ? pReliable : pUnreliable; + + // write RE to list + pDist->DistributeToREList( relist ); + + // mark that we sent the update + pDist->MarkUpdateSent( m_nDestClientID ); + } + + return TRUE; +} + + +// create buffer E_RELists for pass-through multicasts ---------------------- +// +void E_SimClientNetOutput::_CreateBuffers() +{ + if ( m_pReliableBuffer == NULL ) + m_pReliableBuffer = E_REList::CreateAndAddRef( RE_LIST_MAXAVAIL ); + + if ( m_pUnreliableBuffer == NULL ) + m_pUnreliableBuffer = E_REList::CreateAndAddRef( RE_LIST_MAXAVAIL ); +} + + +// terminate the multicast data in the E_REList buffers --------------------- +// +void E_SimClientNetOutput::_TerminateBufferMulticast() +{ + // if we have buffered multicasts from other clients, we must append a RE_OWNERSECTION with PLAYERID_SERVER + if ( m_pReliableBuffer && m_pReliableBuffer->HasEvents() ) { + bool_t rc = _ReserveForOutput( E_REList::RmEvGetSizeFromType( RE_OWNERSECTION ) ); + //ASSERT( rc ); + if(!rc) + MSGOUT("CRAZYSPENCE: If an odd issue starts cropping up, line 555 esimnetoutput is where to look"); + m_pReliableBuffer->NET_Append_RE_OwnerSection( PLAYERID_SERVER ); + } + if ( m_pUnreliableBuffer && m_pUnreliableBuffer->HasEvents() ) { + bool_t rc = _ReserveForOutput( E_REList::RmEvGetSizeFromType( RE_OWNERSECTION ) ); + if(!rc) + MSGOUT("CRAZYSPENCE: If an odd issue starts cropping up, line 561 esimnetoutput is where to look"); + m_pUnreliableBuffer->NET_Append_RE_OwnerSection( PLAYERID_SERVER ); + } +} + + +// sent udpate information to a client if bandwidth available ----------------- +// +int E_SimClientNetOutput::SendUpdateToClient() +{ + ASSERT( m_nDestClientID != PLAYERID_ANONYMOUS ); + + bool_t rc = false; + + // choke client to its max. rate + if ( _CanSendPacket() == FALSE ) { + + // clear this packet-slot + sent_packet_info_s* spi = &m_SentPacketInfo[ m_nPacket % m_nNumPacketSlots ]; + spi->m_size = 0; + spi->m_sendtime = SYSs_GetRefFrameCount(); + + //DBGTXT( MSGOUT( "E_SimClientNetOutput::UpdateClient(): RATE_CHOKE" ); ); + //TheStatsManager->IncStats( m_nDestClientID, STAT_RATE_CHOKE, 1 ); + return FALSE; + } + + // create buffers ( buffers evt. already contain pass-through multicasts from other clients ) + _CreateBuffers(); + + // terminate the buffered multicasts by a RE_OWNERSECTION( PLAYERID_SERVER ) + _TerminateBufferMulticast(); + + // prepare the client update + if ( _PrepareClientUpdateInfo() ) { + + // fill the RE lists to be sent to the client + _FillPacketForClient( m_pReliableBuffer, m_pUnreliableBuffer ); + + // send the stream-packet + ssize_t sent_size = ThePacketHandler->Send_STREAM( m_nDestClientID, m_pReliableBuffer, m_pUnreliableBuffer ); + + // check whether the packet could be sent + if ( ( sent_size == -1 ) || ( sent_size == -2 ) ){ + + //TheStatsManager->IncStats( m_nDestClientID, STAT_PACKET_OVERFLOW, 1 ); + + } else { + + //MSGOUT( "SendUpdateToClient(): %d", m_nDestClientID ); + + // store information about the packet we just sent for rate estimation + sent_packet_info_s* spi = &m_SentPacketInfo[ m_nPacket % m_nNumPacketSlots ]; + spi->m_size = sent_size; + spi->m_sendtime = SYSs_GetRefFrameCount(); + + //NOTE: wraparound shouldnt be a problem ( ~500 days with 100 packets/sec. :) ) + m_nPacket++; + //TheStatsManager->IncStats( m_nDestClientID, STAT_PACKET_SENT, 1 ); + + rc = true; + } + + } else { + //DBGTXT( MSGOUT( "_PrepareClientUpdateInfo() for %d had nothing to send", m_nDestClientID ); ); + } + + // release lists + m_pReliableBuffer->Release(); + m_pUnreliableBuffer->Release(); + + m_pReliableBuffer = NULL; + m_pUnreliableBuffer = NULL; + + return rc; +} + + +// empty the list of things to update ----------------------------------------- +// +void E_SimClientNetOutput::_ClearUpdates() +{ + m_nNumClients = 0; + m_bIncludeGameState = SEND_MODE_NONE; + m_bIncludeKillStats = SEND_MODE_NONE; + m_bIncludeStateSync = SEND_MODE_NONE; + m_nNumDistsForNextPacket= 0; + m_nSizeAvail = RE_LIST_MAXAVAIL; +} + + +// check whether there are any updates to send to the client ------------------ +// +int E_SimClientNetOutput::_HasUpdates() +{ + return ( ( m_nNumDistsForNextPacket > 0 ) || ( m_nNumClients > 0 ) || m_bIncludeGameState || m_bIncludeKillStats ); +} + + +// add a E_Distributable to be sent out with the next packet -------------------- +// +void E_SimClientNetOutput::_AddDistForOutput( E_Distributable* pDist ) +{ + ASSERT( pDist != NULL ); + ASSERT( m_nNumDistsForNextPacket < MAX_NUM_DISTRIBUTABLES_TO_SEND_PER_PACKET ); + + m_DistsForNextPacket[ m_nNumDistsForNextPacket ] = pDist; + m_nNumDistsForNextPacket++; +} + + +// include the information about a client in the next update ( reliable or unreliable ) +// +void E_SimClientNetOutput::_FlagIncludeClient( int nClientID, bool_t bReliable ) +{ + m_ClientIDList[ m_nNumClients ] = nClientID; + m_SendReliable[ m_nNumClients ] = bReliable; + m_nNumClients++; +} + + +// check whether we can reserve size for output, and do so if space avail. ---- +// +bool_t E_SimClientNetOutput::_ReserveForOutput( int size ) +{ + ASSERT( size >= 0 ); + + //FIXME: check whether we really need 4 trailing bytes for sentinel RE_EMPTY + int size_after = m_nSizeAvail - size; + if ( size_after >= 4 ) { + m_nSizeAvail -= size; + return true; + } else { + //this may print more often than we'd like but see what it is when e_simnetout ASSERTS line 560 + return false; + } + + return false; +} + + + + + + + +// default ctor --------------------------------------------------------------- +// +E_SimNetOutput::E_SimNetOutput() : + m_SimClientNetOutput( NULL ), + m_Distributables( NULL ) +{ +} + + +// default dtor --------------------------------------------------------------- +// +E_SimNetOutput::~E_SimNetOutput() +{ + delete []m_SimClientNetOutput; + _DestroyDistributables(); +} + + +// reset all data ------------------------------------------------------------- +// +void E_SimNetOutput::Reset() +{ + delete []m_SimClientNetOutput; + m_SimClientNetOutput = new E_SimClientNetOutput[ MAX_NUM_CLIENTS ]; + + // delete all existing distributables + _DestroyDistributables(); + + m_Distributables = new UTL_List<E_Distributable*>; +} + +// connect the player in a specific slot -------------------------------------- +// +int E_SimNetOutput::ConnectPlayer( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + m_SimClientNetOutput[ nClientID ].Connect( nClientID ); + + // schedule all distributables in the world database for delivery to the newly connected client + for( UTL_listentry_s<E_Distributable*>* entry = m_Distributables->GetHead(); entry != NULL; entry = entry->m_pNext ) { + E_Distributable* pDist = entry->m_data; + + // invalidate any previous sent update, as we are newly connected + pDist->MarkForUpdate( nClientID ); + + m_SimClientNetOutput[ nClientID ].ScheduleDistributable( pDist ); + } + + return TRUE; +} + +// disconnect the player in a specific slot ----------------------------------- +// +int E_SimNetOutput::DisconnectPlayer( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + m_SimClientNetOutput[ nClientID ].Disconnect(); + return TRUE; +} + + +// update all clients --------------------------------------------------------- +// +void E_SimNetOutput::DoClientUpdates() +{ + // append a remote event for each connected player + for( int nClientID = 0; nClientID < MAX_NUM_CLIENTS; nClientID++ ) { + if( !TheSimulator->IsPlayerDisconnected( nClientID ) ) { + m_SimClientNetOutput[ nClientID ].SendUpdateToClient(); + } + } +} + + +// update NET.SERVERRATE dependend stuff of E_SimClientNetOutput --------------- +// +void E_SimNetOutput::RateChangedForClient( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + m_SimClientNetOutput[ nClientID ].RateChanged(); +} + + +// recalulate the average packet sizes for all connected clients -------------- +// +void E_SimNetOutput::RecalcAveragePacketSizes() +{ + // for each connected player recalculate the average packetsize + for( int nClientID = 0; nClientID < MAX_NUM_CLIENTS; nClientID++ ) { + if( !TheSimulator->IsPlayerDisconnected( nClientID ) ) { + m_SimClientNetOutput[ nClientID ].CalculateAveragePacketSize(); + } + } +} + + +// buffer the RE for sending to all clients except sender -------------------------------------- +// +void E_SimNetOutput::BufferForMulticastRE( RE_Header* relist, int nSenderClientID, int reliable ) +{ + ASSERT( relist != NULL ); + ASSERT( (( nSenderClientID >= 0 ) && ( nSenderClientID < MAX_NUM_CLIENTS )) || nSenderClientID == PLAYERID_ANONYMOUS ); + //ASSERT( !TheSimulator->IsPlayerDisconnected( nSenderClientID ) || nSenderClientID == PLAYERID_ANONYMOUS ); + + // buffer RE to be distributed to all other clients + for( int nDistClient = 0; nDistClient < MAX_NUM_CLIENTS; nDistClient++ ) { + if( !TheSimulator->IsPlayerDisconnected( nDistClient ) && ( nDistClient != nSenderClientID ) ) { + + DBGTXT( MSGOUT( "distributing RE type %d from %d to %d", relist->RE_Type, nSenderClientID, nDistClient ); ); + + m_SimClientNetOutput[ nDistClient ].BufferRE( relist, reliable ); + } + } +} + +//Send specific RE to specific client, planned for use with state sync on newly connected clients +void E_SimNetOutput::BufferForDirectRE( RE_Header* relist, int nClientID, int reliable ) +{ + ASSERT( relist != NULL ); + ASSERT(( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS )); + ASSERT( !TheSimulator->IsPlayerDisconnected( nClientID ) || nClientID == PLAYERID_ANONYMOUS ); + + // buffer RE to be distributed to specific clients + if( !TheSimulator->IsPlayerDisconnected( nClientID )) { + + DBGTXT( MSGOUT( "distributing RE type %d to %d", relist->RE_Type, nClientID); ); + + m_SimClientNetOutput[ nClientID ].BufferRE( relist, reliable ); + + } +} + +// +// reset the update info ------------------------------------------------------ +// +void E_Distributable::ResetUpdateInfo() +{ + if ( m_update_to_client != NULL ) + delete []m_update_to_client; + + m_update_to_client = new byte[ MAX_NUM_CLIENTS ]; + UpdateMode_STATE(); +} + + +// set updatemode for all clients to STATE ------------------------------------ +// +void E_Distributable::UpdateMode_STATE() +{ + m_update_mode &= ~UPDATEMODE_SENDWHAT_MASK; + m_update_mode |= UPDATEMODE_SENDWHAT_STATE; + memset( m_update_to_client, UPDATE_TODO_SEND, MAX_NUM_CLIENTS * sizeof( byte ) ); +} + + +// set updatemode for all clients to REMOVE ----------------------------------- +// +void E_Distributable::UpdateMode_REMOVE() +{ + m_update_mode &= ~UPDATEMODE_SENDWHAT_MASK; + m_update_mode |= UPDATEMODE_SENDWHAT_REMOVE; + memset( m_update_to_client, UPDATE_TODO_SEND, MAX_NUM_CLIENTS * sizeof( byte ) ); +} + + +// check whether the E_Distributable is a ZOMBIE ( client has received the REMOVE update ) +// +int E_Distributable::IsZombie( int nClientID ) +{ + ASSERT( ( nClientID >=0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + if ( m_update_mode & UPDATEMODE_SENDWHAT_REMOVE ) { + + // owners always have ZOMBIE state, as they never get the removal sent ( unless flag is set ) + if ( GetOwnerFromHostOjbNumber( m_objectid ) == (dword)nClientID ) { + if ( WillBeSentToOwner() == FALSE ) { + return TRUE; + } + } + + return ( m_update_to_client[ nClientID ] == UPDATE_TODO_NOTHING ); + } else { + return FALSE; + } +} + +// check whethe the E_Distributable has an update for a specific client --------- +// +int E_Distributable::HasUpdate( int nClientID ) +{ + ASSERT( ( nClientID >=0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + + // never send an update to the owner of the object, unless the flag is set accordingly + if ( GetOwnerFromHostOjbNumber( m_objectid ) == (dword)nClientID ) { + if ( WillBeSentToOwner() == FALSE ) { + return FALSE; + } + } + + return ( m_update_to_client[ nClientID ] == UPDATE_TODO_SEND ); +} + + +// mark, that we sent an update to a client ----------------------------------- +// +void E_Distributable::MarkUpdateSent( int nClientID ) +{ + ASSERT( ( nClientID >=0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + m_update_to_client[ nClientID ] = UPDATE_TODO_NOTHING; +} + + +// mark, that we need to send an update to a client --------------------------- +// +void E_Distributable::MarkForUpdate( int nClientID ) +{ + ASSERT( ( nClientID >=0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + m_update_to_client[ nClientID ] = UPDATE_TODO_SEND; +} + + +// determine the size this E_Distributable is in the packet --------------------- +// +size_t E_Distributable::DetermineSizeInPacket() +{ + if ( IsInRemoving() == FALSE ) { + switch( m_listno ) { + case SHIP_LIST: + return E_REList::RmEvGetSizeFromType( RE_PLAYERANDSHIPSTATUS ); + case LASER_LIST: + return E_REList::RmEvGetSizeFromType( RE_CREATELASER ); + case MISSL_LIST: + return E_REList::RmEvGetSizeFromType( RE_CREATEMISSILE ); + case EXTRA_LIST: + return E_REList::RmEvGetSizeFromType( RE_CREATEEXTRA2 ); + break; + case CUSTM_LIST: + { + GenObject* pObject = TheWorld->FetchSpecificObject( m_objectid, TheWorld->m_CustmObjects ); + ASSERT( OBJECT_TYPE_CUSTOM( pObject ) ); + + // call persistence function with NULL destination pointer to get the size + // of the information to be stored + CustomObject *custompo = (CustomObject *) pObject; + if ( custompo->callback_persist != NULL ) { + return (*custompo->callback_persist)( custompo, TRUE, NULL ); + } + } + break; + default: + ASSERT(FALSE); + } + + } else { + return E_REList::RmEvGetSizeFromType( RE_KILLOBJECT ); + } + + ASSERT( FALSE ); + return 0; +} + + +// write a corresponding RE to the REList ------------------------------------- +// +void E_Distributable::DistributeToREList( E_REList* relist ) +{ + // send creation RE + if ( IsInRemoving() == FALSE ) { + + GenObject* pObject = NULL; + switch( m_listno ) { + case SHIP_LIST: + //FIXME: implement + //pObject = TheWorld->FetchSpecificObject( objectid, TheWorld->m_PShipObjects ); + break; + case LASER_LIST: + pObject = TheWorld->FetchSpecificObject( m_objectid, TheWorld->m_LaserObjects ); + relist->NET_Append_RE_CreateLaser( (LaserObject*) pObject ); + break; + case MISSL_LIST: + //FIXME: implement + pObject = TheWorld->FetchSpecificObject( m_objectid, TheWorld->m_MisslObjects ); + //ASSERT(FALSE); + relist->NET_Append_RE_CreateMissile( (MissileObject*) pObject, ((TargetMissileObject *)pObject)->TargetObjNumber); //CrazySpence trying to fix Missiles + break; + case EXTRA_LIST: + pObject = TheWorld->FetchSpecificObject( m_objectid, TheWorld->m_ExtraObjects ); + relist->NET_Append_RE_CreateExtra2( (ExtraObject*) pObject ); + break; + case CUSTM_LIST: + { + pObject = TheWorld->FetchSpecificObject( m_objectid, TheWorld->m_CustmObjects ); + ASSERT( OBJECT_TYPE_CUSTOM( pObject ) ); + + // call persistence function + CustomObject *custompo = (CustomObject *) pObject; + if ( custompo->callback_persist != NULL ) { + (*custompo->callback_persist)( custompo, TRUE, relist ); + } + } + break; + default: + ASSERT(FALSE); + } + + } else { + // send deletion RE + relist->NET_Append_RE_KillObject( m_objectid, m_listno ); + } +} + + +// create a new E_Distributable for all connected clients ----------------------- +// +E_Distributable* E_SimNetOutput::CreateDistributable( GenObject* objectpo, int reliable /*= FALSE*/, int send_to_owner) +{ + ASSERT( objectpo != NULL ); + + int listno = -1; + + //FIXME: this must be done by a method + // determine list # + switch ( objectpo->ObjectType & TYPELISTMASK ) { + case PSHIP_LIST_NO: + listno = SHIP_LIST; break; + case LASER_LIST_NO: + listno = LASER_LIST; break; + case MISSL_LIST_NO: + listno = MISSL_LIST; break; + case EXTRA_LIST_NO: + listno = EXTRA_LIST; break; + case CUSTM_LIST_NO: + listno = CUSTM_LIST; break; + default: + ASSERT( FALSE ); + return NULL; + } + + ASSERT( ( listno >= SHIP_LIST ) && ( listno <= CUSTM_LIST ) ); + DBGTXT(MSGOUT( "DIST: E_Simulator::CreateDistributable(): objectid %x listno %d reliable %d", objectpo->HostObjNumber, listno, reliable );); + // create a new E_Distributable ( implicitely calls UpdateMode_STATE() ) + E_Distributable* pDist = new E_Distributable( objectpo->HostObjNumber, listno, reliable, send_to_owner ); + m_Distributables->AppendHead( pDist ); + + // schedule the E_Distributable to all connected clients + for( int nClientID = 0; nClientID < MAX_NUM_CLIENTS; nClientID++ ) { + if( TheSimulator->IsPlayerDisconnected( nClientID ) == FALSE ) { + m_SimClientNetOutput[ nClientID ].ScheduleDistributable( pDist ); + } + } + + return pDist; +} + +// release a E_Distributable ---------------------------------------------------- +// +void E_SimNetOutput::ReleaseDistributable( E_Distributable* pDist ) +{ + ASSERT( pDist != NULL ); + + // find entry in list and set the correct mode + UTL_listentry_s<E_Distributable*>* entry = m_Distributables->Find( pDist ); + if ( entry == NULL ) { + ASSERT( FALSE ); + return; + } + + ASSERT( pDist == entry->m_data); + + DBGTXT( MSGOUT( "DIST: E_Simulator::ReleaseDistributable(): objectid %x listno %d reliable %d", pDist->GetObjectID(), pDist->GetListNo(), pDist->NeedsReliable() ); ); + + pDist->UpdateMode_REMOVE(); + + // schedule the E_Distributable to all connected clients + for( int nClientID = 0; nClientID < MAX_NUM_CLIENTS; nClientID++ ) { + if( TheSimulator->IsPlayerDisconnected( nClientID ) == FALSE ) { + m_SimClientNetOutput[ nClientID ].ScheduleDistributable( pDist, TRUE ); + } + } +} + +// cleanup all distributables, that are complete zombies ---------------------- +// +void E_SimNetOutput::CleanupZombieDistributables() +{ + // cache connected information + //FIXME: should become a member function of the connection manager to + // retrieve a int array indicating whether a client is connected + int* connected = new int[ MAX_NUM_CLIENTS ]; + memset( connected, 0, MAX_NUM_CLIENTS * sizeof( int ) ); + for( int nClientID = 0; nClientID < MAX_NUM_CLIENTS; nClientID++ ) { + connected[ nClientID ] = ( TheSimulator->IsPlayerDisconnected( nClientID ) == FALSE ); + } + + // check all distributables, that are in REMOVE mode, whether the mode for all + // connected clients is already ZOMBIE. delete from list, if so + for( UTL_listentry_s<E_Distributable*>* entry = m_Distributables->GetHead(); entry != NULL; ) { + + E_Distributable* pDist = entry->m_data; + UTL_listentry_s<E_Distributable*>* nextentry = entry->m_pNext; + + if ( pDist->IsInRemoving() ) { + + // detect whether in ZOMBIE state for all clients + int allzombies = TRUE; + for( int nClientID = 0; ( nClientID < MAX_NUM_CLIENTS ) && allzombies; nClientID++ ) { + if ( connected[ nClientID ] && ( pDist->IsZombie( nClientID ) == FALSE ) ) { + allzombies = FALSE; + } + } + + // remove if in ZOMBIE state for all clients + if ( allzombies ) { + + DBGTXT( MSGOUT( "DIST: E_Simulator::CleanupZombieDistributables(): objectid %x listno %d reliable %d", pDist->GetObjectID(), pDist->GetListNo(), pDist->NeedsReliable() ); ); + + m_Distributables->RemoveEntry( entry ); + delete pDist; + } + } + entry = nextentry; + } + + delete []connected; +} + + +// destroy m_Distributables --------------------------------------------------- +// +void E_SimNetOutput::_DestroyDistributables() +{ + if ( m_Distributables == NULL ) + return; + + // delete all remaining distributables + for( UTL_listentry_s<E_Distributable*>* entry = m_Distributables->GetHead(); entry != NULL; entry = entry->m_pNext ) { + delete entry->m_data; + } + delete m_Distributables; + m_Distributables = NULL; +} + + + + diff --git a/src/parsec_server/e_simplayerinfo.cpp b/src/parsec_server/e_simplayerinfo.cpp new file mode 100755 index 0000000..162f888 --- /dev/null +++ b/src/parsec_server/e_simplayerinfo.cpp @@ -0,0 +1,315 @@ +/* + * PARSEC - Player Information (Simulation) + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:46 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2001-2002 + * + * 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.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" + +// local module header +#include "e_simplayerinfo.h" + +// proprietary module headers +#include "con_aux_sv.h" +//#include "g_colldet.h" +//#include "g_extra.h" +////#include "net_csdf.h" +//#include "net_packetdriver.h" +#include "net_game_sv.h" +//#include "net_stream.h" +#include "obj_creg.h" // for ShipClasses +////#include "e_stats.h" +#include "g_main_sv.h" +#include "e_connmanager.h" +#include "e_gameserver.h" +#include "e_simulator.h" +//#include "e_packethandler.h" +//#include "e_relist.h" +//#include "sys_refframe_sv.h" + +// reset all player fields to defaults ( not connected ) ---------------------- +// +void E_SimPlayerInfo::Reset() +{ + m_Status = PLAYER_INACTIVE; + m_objclass = -1; + + m_pShip = NULL; + m_nShipID = SHIPID_NOSHIP; + + m_nClientID = -1; + m_IgnoreJoinUntilUnjoinFromClient = FALSE; +} + +// set the desired player status ( inactive/joined/unjoined ) ----------------- +// +void E_SimPlayerInfo::SetDesiredPlayerStatus( RE_PlayerStatus* playerstatus ) +{ + ASSERT( playerstatus != NULL ); + + // identify sender + int nClientID = playerstatus->senderid; + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + + switch( playerstatus->player_status ) { + // desired player status is INACTIVE (disconnect) + case PLAYER_INACTIVE: + { + ASSERT( FALSE ); + } + break; + // desired player_status is JOINED ( join/ensure joined ) + case PLAYER_JOINED: + { + // player must not be joined already in order to join + if ( m_Status == PLAYER_JOINED ) { + return; + } + + // player must be connected in order to join + if ( m_Status == PLAYER_INACTIVE ) { + DBGTXT( MSGOUT( "SetDesiredPlayerStatus(): filtering join for inactive player: %d.", nClientID ); ); + return; + } + + // check whether to ignore the JOIN + if ( m_IgnoreJoinUntilUnjoinFromClient ) { + DBGTXT( MSGOUT( "SetDesiredPlayerStatus(): filtering join until unjoin is ACK: %d.", nClientID ); ); + return; + } + + // player status must be CONNECTED + ASSERT( m_Status == PLAYER_CONNECTED ); + + // only allow join of players when game is running + if ( TheGame->IsGameRunning() ) { + + // update tables and create ship + PerformJoin( playerstatus ); + + // player status must now be JOINED + ASSERT( m_Status == PLAYER_JOINED ); + + DBGTXT( MSGOUT( "SetDesiredPlayerStatus(): joined player %d.", nClientID ); ); + } + } + break; + // desired player_status is CONNECTED ( unjoin/ensure unjoined ) + case PLAYER_CONNECTED: + { + // check whether this is an ACK package from the client that he received the unjoin packet from the server + if ( m_IgnoreJoinUntilUnjoinFromClient && ( m_Status == PLAYER_CONNECTED ) ) { + + // reset flag ( we now again accept all states from the client ) + m_IgnoreJoinUntilUnjoinFromClient = FALSE; + + // reset last unjoin flag to USER_EXIT + TheGame->ResetDeathInfo( nClientID ); + } else { + + // player must be joined in order to unjoin + if ( m_Status == PLAYER_CONNECTED ) { + return; + } + + // player must be connected in order to unjoin + if ( m_Status == PLAYER_INACTIVE ) { + DBGTXT( MSGOUT( "SetDesiredPlayerStatus(): filtering unjoin for inactive player: %d.", nClientID ); ); + return; + } + + // player status must be JOINED + ASSERT( m_Status == PLAYER_JOINED ); + + // update tables and delete ship + PerformUnjoin( playerstatus ); + + // reset the flag + m_IgnoreJoinUntilUnjoinFromClient = FALSE; + + // player status must now be CONNECTED + ASSERT( m_Status == PLAYER_CONNECTED ); + + DBGTXT( MSGOUT( "SetDesiredPlayerStatus(): unjoined player %d.", nClientID ); ); + } + } + break; + default: + ASSERT( FALSE ); + break; + } +} + +// update tables and create ship for newly joined player ---------------------- +// +void E_SimPlayerInfo::PerformJoin( RE_PlayerStatus* playerstatus ) +{ + //NOTE: nearly identical to NET_GAME::PerformJoin + + ASSERT( playerstatus != NULL ); + ASSERT( playerstatus->player_status == PLAYER_JOINED ); + ASSERT( m_Status == PLAYER_CONNECTED ); + + // update player status + m_Status = PLAYER_JOINED; + + // there must not be a ship for this player + ASSERT( m_nShipID == SHIPID_NOSHIP ); + ASSERT( m_pShip == NULL ); + + // get the objectclass + m_objclass = ShipClasses[ playerstatus->objectindex ]; + + ASSERT( (dword)m_objclass != CLASS_ID_INVALID ); + + // fetch the latest state + E_SimClientState* pSimClientState = TheSimulator->GetSimClientState( m_nClientID ); + + // init the join position for the new player + E_SimShipState _SimShipState; + GAMECODE2( TheGame->JoinPlayer( m_nClientID, &_SimShipState ); ); + + // reset the sim-state of the client + pSimClientState->ForceNewState( &_SimShipState ); + + // force a client resync + pSimClientState->SetClientResync(); + + // create ship object for new player + GenObject* objectpo = TheWorld->CreateObject( m_objclass, _SimShipState.GetObjPosition(), playerstatus->senderid ); + ASSERT( objectpo != NULL ); + + // set correct global objectid for player-ship + objectpo->HostObjNumber = ShipHostObjId( playerstatus->senderid ); + m_pShip = (ShipObject*)objectpo; + m_nShipID = objectpo->ObjectNumber; + + // display object id of remote player's ship + //FIXME: this could go into UI_PlayerJoinedFeedback + DBGTXT( MSGOUT( "player %s joined and got object id %d.", TheConnManager->GetClientName( m_nClientID ), m_nShipID ); ); + + MSGOUT( "joined client %d", m_nClientID ); + + // give UI feedback when player has joined + GAMECODE ( UI_PlayerJoinedFeedback( playerstatus ) ); +} + + +// update tables and delete ship of player who unjoined ----------------------- +// +void E_SimPlayerInfo::PerformUnjoin( RE_PlayerStatus* playerstatus ) +{ + ASSERT( playerstatus != NULL ); + ASSERT( playerstatus->player_status == PLAYER_CONNECTED ); + if(m_Status != PLAYER_JOINED) + return; + ASSERT( m_pShip != NULL ); + ASSERT( m_nShipID != SHIPID_NOSHIP ); + + //FIXME: check whether this should be handled here + + // downing of ship starts explosion + if ( playerstatus->params[ 0 ] == SHIP_DOWNED ) { + + // exec gamecode when player ship is downed + GAMECODE ( GC_UnjoinPlayer_ShipDowned( playerstatus ) ); + + //FIXME: we must not yet kill the ShipObject as the E_Distributable might need it + //FIXME: really ?????????????? + // kill ship object + TheWorld->KillSpecificShipObject( m_nShipID ); + + // user exit opens stargate and deletes ship + } else if ( playerstatus->params[ 0 ] == USER_EXIT ) { + + // exec gamecode when player exits to menu + GAMECODE ( GC_UnjoinPlayer_UserExit( playerstatus ) ); + + // kill ship object + TheWorld->KillSpecificShipObject( m_nShipID ); + } + + // unjoin the player in G_Main + TheGame->UnjoinPlayer( m_nClientID ); + + // signify that player is only connected, but not joined. + m_Status = PLAYER_CONNECTED; + m_pShip = NULL; + m_nShipID = SHIPID_NOSHIP; + m_objclass = -1; + + MSGOUT( "unjoined client %d", m_nClientID ); + + // give UI feedback when player has unjoined + GAMECODE ( UI_PlayerUnjoinedFeedback( playerstatus ) ); +} + +// set the player status to connected ----------------------------------------- +// +void E_SimPlayerInfo::Connect( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + ASSERT( m_Status == PLAYER_INACTIVE ); + + m_nClientID = nClientID; + m_Status = PLAYER_CONNECTED; +} + +// set the player status to disconnected -------------------------------------- +// +void E_SimPlayerInfo::Disconnect() +{ + ASSERT( ( m_Status == PLAYER_CONNECTED ) || ( m_Status == PLAYER_JOINED ) ); + + if ( m_Status == PLAYER_JOINED ) { + + DBGTXT( MSGOUT( "E_SimPlayerInfo::Disconnect() unjoining still joined player before disconnecting" ); ); + + // fill rudimentary RE + RE_PlayerStatus ps; + ps.player_status = PLAYER_CONNECTED; + ps.params[ 0 ] = USER_EXIT; + ps.senderid = m_nClientID; + + // set the state of the player + SetDesiredPlayerStatus( &ps ); + } + + Reset(); +} + diff --git a/src/parsec_server/e_simulator.cpp b/src/parsec_server/e_simulator.cpp new file mode 100755 index 0000000..a192726 --- /dev/null +++ b/src/parsec_server/e_simulator.cpp @@ -0,0 +1,844 @@ +/* + * PARSEC - Server-side Simulator + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:46 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2001-2002 + * + * 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.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" + +// UNP header +#include "net_wrap.h" + +// server defs +#include "e_defs.h" + +// network code config +#include "net_conf.h" + +// net game header +#include "net_game_sv.h" + +// mathematics header +#include "utl_math.h" + +// utility headers +#include "utl_list.h" + +// local module header +#include "e_simulator.h" + +// proprietary module headers +#include "con_aux_sv.h" +#include "e_colldet.h" +#include "g_extra.h" +//#include "net_csdf.h" +#include "net_packetdriver.h" +#include "net_stream.h" +#include "obj_creg.h" +//#include "e_stats.h" +#include "g_main_sv.h" +#include "e_simplayerinfo.h" +#include "e_simnetinput.h" +#include "e_simnetoutput.h" +#include "e_connmanager.h" +#include "e_gameserver.h" +#include "e_packethandler.h" +#include "e_relist.h" +#include "sys_refframe_sv.h" + +// flags ---------------------------------------------------------------------- +// +#define SERVER_SLERP_ORIENTATION 1 +#define _SLERP_DEBUGGING + +#ifdef _SLERP_DEBUGGING + +//FIXME: not needed anymore ? +/*#ifdef SYSTEM_WIN32_UNUSED + #include "windows.h" + #include "mmsystem.h" + #pragma comment( lib, "winmm.lib" ) +#endif // SYSTEM_WIN32_UNUSED +*/ + +#endif // _SLERP_DEBUGGING + + +// reset all data members ----------------------------------------------------- +// +void E_SimShipState::Reset() +{ + MakeIdMatrx( m_ObjPosition ); + + m_CurSpeed = 0; + m_CurYaw = 0; + m_CurPitch = 0; + m_CurRoll = 0; + m_CurSlideHorz = 0; + m_CurSlideVert = 0; +} + + +// connect a client ----------------------------------------------------------- +// +void E_SimClientState::Connect( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + ASSERT( m_States == NULL ); + + m_nClientID = nClientID; + m_nNumStateSlots = TheServer->GetSimFrequency(); + ASSERT( m_nNumStateSlots > 0 ); + m_States = new E_SimShipState[ m_nNumStateSlots ]; + m_ClientMovementMode = CMM_SIMULATING; + m_playerlerp.transition = 0; + m_playerlerp.curalpha = -1.0f; +} + +// check the movement bounds for a client ------------------------------------- +// +int E_SimClientState::CheckMovementBounds( dword MessageID, RE_PlayerAndShipStatus* pPAS ) +{ + ASSERT( pPAS != NULL ); + + //TODO: check whether movement from last to current state is possbile + + return TRUE; +} + + +// check whether the change from the last to the current state involved a movement +// +int E_SimClientState::_CheckForMovement( dword MessageID, RE_PlayerAndShipStatus* pPAS ) +{ + if ( m_InputState.m_CurSpeed != pPAS->CurSpeed ) { /*MSGOUT( "_CheckForMovement(): CurSpeed" );*/ return TRUE; } + if ( m_InputState.m_CurYaw != pPAS->CurYaw ) { /*MSGOUT( "_CheckForMovement(): CurYaw" ); */ return TRUE; } + if ( m_InputState.m_CurPitch != pPAS->CurPitch ) { /*MSGOUT( "_CheckForMovement(): CurPitch" ); */return TRUE; } + if ( m_InputState.m_CurRoll != pPAS->CurRoll ) { /*MSGOUT( "_CheckForMovement(): CurRoll" ); */ return TRUE; } + if ( m_InputState.m_CurSlideHorz != pPAS->CurSlideHorz ) { /*MSGOUT( "_CheckForMovement(): CurSlideHorz" ); */return TRUE; } + if ( m_InputState.m_CurSlideVert != pPAS->CurSlideVert ) { /*MSGOUT( "_CheckForMovement(): CurSlideVert" ); */return TRUE; } + + //geomv_t* last_pos = (geomv_t*)m_InputState.m_ObjPosition; + //geomv_t* new_pos = (geomv_t*)pPAS->ObjPosition; + //for( int i = 0; i < ( 3 * 4 )/* sizeof( Xmatrx ) / sizeof( geomv_t ) */; i++ ) + // if ( ( last_pos[ i ] - new_pos[ i ] ) > GEOMV_VANISHING ) return TRUE; + + //FIXME: enable whole matrix check again ? + //FIXME: relaxed checking of values ( diff must be greater than certain epsilon ) + + // check position only + pXmatrx last_pos = m_InputState.m_ObjPosition; + pXmatrx new_pos = pPAS->ObjPosition; + if ( last_pos[ 0 ][ 3 ] != new_pos[ 0 ][ 3 ] ) { /*MSGOUT( "_CheckForMovement(): pos[ 0 ][ 3 ]" ); */return TRUE; } + if ( last_pos[ 1 ][ 3 ] != new_pos[ 1 ][ 3 ] ) { /*MSGOUT( "_CheckForMovement(): pos[ 1 ][ 3 ]" );*/ return TRUE; } + if ( last_pos[ 2 ][ 3 ] != new_pos[ 2 ][ 3 ] ) { /*MSGOUT( "_CheckForMovement(): pos[ 2 ][ 3 ]" ); */ return TRUE; } + + return FALSE; +} + +// force a new state --------------------------------------------------------- +// +void E_SimClientState::ForceNewState( E_SimShipState* pSimShipState ) +{ + ASSERT( pSimShipState != NULL ); + + // NOTE: this actually is stored in the last-sim-frame as these values get transfered + // to the current-sim-frame in the CalcNewState function + E_SimShipState* pPrevState = GetPrevSimFrameStateSlot(); + + // copy new state over latest simstate and the input state(s) + pPrevState->CopyFrom ( *pSimShipState ); + m_InputState.CopyFrom( *pSimShipState ); + + // abort any smoothing + m_ClientMovementMode = CMM_SIMULATING; + m_playerlerp.transition = 0; + m_playerlerp.curalpha = -1.0f; +} + + +// update the message ids the lateste state comes from ( dead reckoning ) ----- +// +inline +void E_SimClientState::_UpdateStateMessageID( dword MessageID ) +{ + // store the message ID + m_nLastInputStateMessageId = m_nInputStateMessageID; + m_nInputStateMessageID = MessageID; +} + +// store the received state as the newest ------------------------------------- +// +void E_SimClientState::StoreNewState( dword MessageID, RE_PlayerAndShipStatus* pPAS ) +{ + ASSERT( pPAS != NULL ); + + // check whether the change from the last to the current state involved a movement + bool_t bDidMove = _CheckForMovement( MessageID, pPAS ); + + // set the movement parameters for the next simulation step + // NOTE: this actually is stored in the last-sim-frame as these values get transfered + // to the current-sim-frame in the CalcNewState function + E_SimShipState* pPrevState = GetPrevSimFrameStateSlot(); + pPrevState->m_CurSpeed = pPAS->CurSpeed; + pPrevState->m_CurYaw = pPAS->CurYaw; + pPrevState->m_CurPitch = pPAS->CurPitch; + pPrevState->m_CurRoll = pPAS->CurRoll; + pPrevState->m_CurSlideHorz = pPAS->CurSlideHorz; + pPrevState->m_CurSlideVert = pPAS->CurSlideVert; + + // store the input state + m_InputState.CopyFromPlayerAndShipStatus( *pPAS ); + + // store the message ID + _UpdateStateMessageID( MessageID ); + + // calculate the newest smoothing target, if the player moved + if ( bDidMove ) { + _CalcNewSmoothingTarget(); + } +} + + +// dump some SimClientState for debugging purposes ------------------------------- +// +void E_SimClientState::Dump() +{ + MSGOUT( "SimFrame: %d", TheSimulator->GetSimFrame() ); + for( int i = 0; i < m_nNumStateSlots; ) { + MSGOUT( "state %02d: Z: %8.3f %8.3f %8.3f %8.3f %8.3f", + i, + m_States[ i ].m_ObjPosition[ 0 ][ 3 ], + m_States[ i + 1 ].m_ObjPosition[ 0 ][ 3 ], + m_States[ i + 2 ].m_ObjPosition[ 0 ][ 3 ], + m_States[ i + 3 ].m_ObjPosition[ 0 ][ 3 ], + m_States[ i + 4 ].m_ObjPosition[ 0 ][ 3 ] + ); + i += 5; + } +} + +// calculate the newest smooothing target ------------------------------------- +// +void E_SimClientState::_CalcNewSmoothingTarget() +{ + m_playerlerp.curspeed = m_InputState.m_CurSpeed; + m_playerlerp.curyaw = m_InputState.m_CurYaw; + m_playerlerp.curpitch = m_InputState.m_CurPitch; + m_playerlerp.curroll = m_InputState.m_CurRoll; + m_playerlerp.curslidehorz = m_InputState.m_CurSlideHorz; + m_playerlerp.curslidevert = m_InputState.m_CurSlideVert; + +//NOTE: this is the smoothing code, that should prevent the "running ahead" of the server +//#define NEW_SMOOTHING_CODE +#ifdef NEW_SMOOTHING_CODE + + // do nothing on first input message + if ( m_nLastInputStateMessageId == 0 ) { + return; + } + + // determine # of packets between updates + int nNumPackets = m_nInputStateMessageID - m_nLastInputStateMessageId; + ASSERT( nNumPackets != 0 ); + +#define NUM_PACKETS_LOST_BEFORE_RESYNC 5 + ASSERT( nNumPackets < NUM_PACKETS_LOST_BEFORE_RESYNC ); + + // determine smoothing time + int sendfreq = TheConnManager->GetClientInfo( m_nClientID )->GetSendFreq(); + refframe_t smoothing_time = FRAME_MEASURE_TIMEBASE / sendfreq; + + refframe_t nSimRefFrames; + if ( ( m_ClientMovementMode == CMM_SIMULATING ) && ( m_LastSwitchToSimRefFrame != -1 ) ) { + // account for the time we already spent simulating + nSimRefFrames = SYSs_GetRefFrameCount() - m_LastSwitchToSimRefFrame; + } else { + nSimRefFrames = 0; + } + + // calculate the time to smooth to the newly supplied position + m_playerlerp.transition = ( smoothing_time * nNumPackets ) - nSimRefFrames; + + // no smoothing necessary ( input movement is still taken for simulating ) + if ( m_playerlerp.transition <= 0 ) { + m_playerlerp.transition = 0; + return; + } + + // destination position is current input-position + Vector3 vDestPos; + FetchTVector( m_InputState.m_ObjPosition, &vDestPos ); + //FIXME: shouldnt we include the predicted movement from the input position + // in m_playerlerp.transition refframes + +#else // !NEW_SMOOTHING_CODE + +#define DEFAULT_PLAYERLERP_TRANSITION_TIME 200 + + //FIXME: which values are best here ? ( <= or >= client-send-frame-time ??? ) + m_playerlerp.transition = DEFAULT_PLAYERLERP_TRANSITION_TIME; + + + // calculate the movement + Vector3 vMovementOffset; + CalcMovement( &vMovementOffset, + m_InputState.m_ObjPosition, + m_InputState.m_CurSpeed, + m_InputState.m_CurSlideHorz, + m_InputState.m_CurSlideVert, + (refframe_t)m_playerlerp.transition); + + // destination position as contained in last state information from client + Vector3 vDestPos; + FetchTVector( m_InputState.m_ObjPosition, &vDestPos ); + + // predict where destination position will be after transition time + vDestPos.X += vMovementOffset.X; + vDestPos.Y += vMovementOffset.Y; + vDestPos.Z += vMovementOffset.Z; + +#endif // !NEW_SMOOTHING_CODE + + // store corrected (predicted) destination + m_playerlerp.dstposition[ 0 ][ 3 ] = vDestPos.X; + m_playerlerp.dstposition[ 1 ][ 3 ] = vDestPos.Y; + m_playerlerp.dstposition[ 2 ][ 3 ] = vDestPos.Z; + + // position in last (prior to this) sim-frame + E_SimShipState* pPrevState = GetPrevSimFrameStateSlot(); + Vector3 vSimPos; + FetchTVector( pPrevState->m_ObjPosition, &vSimPos ); + + // determine one step on path from current to destination position + float normfac = 1.0f / m_playerlerp.transition; + + //NOTE: the time the server needs for the transition must also include the + // time-difference between the last received packet. + // when the difference exceeds a certain treshold, we must resync the client anyway + + m_playerlerp.transvec_x = FLOAT_TO_GEOMV( GEOMV_TO_FLOAT( vDestPos.X - vSimPos.X ) * normfac ); + m_playerlerp.transvec_y = FLOAT_TO_GEOMV( GEOMV_TO_FLOAT( vDestPos.Y - vSimPos.Y ) * normfac ); + m_playerlerp.transvec_z = FLOAT_TO_GEOMV( GEOMV_TO_FLOAT( vDestPos.Z - vSimPos.Z ) * normfac ); + + DBGTXT( MSGOUT( "new PLAYERLERP: to: %f/%f/%f, from: %f/%f/%f, transvec: (%f/%f/%f)", + vDestPos.X, + vDestPos.Y, + vDestPos.Z, + vSimPos.X, + vSimPos.Y, + vSimPos.Z, + m_playerlerp.transvec_x, + m_playerlerp.transvec_y, + m_playerlerp.transvec_z ); ); + + if ( SERVER_SLERP_ORIENTATION ) { + + // orientation in previous frame + QuaternionFromMatrx( &m_playerlerp.srcquat, pPrevState->m_ObjPosition ); + QuaternionMakeUnit( &m_playerlerp.srcquat ); + + // orientation state from packet + QuaternionFromMatrx( &m_playerlerp.dstquat, m_InputState.m_ObjPosition ); + QuaternionMakeUnit( &m_playerlerp.dstquat ); + + // stepsize currently bound to poslerp speed + //FIXME: how did we come up with this constant ? + //float stepsize = normfac * 1.2f; + float stepsize = normfac; + + m_playerlerp.incalpha = stepsize; + m_playerlerp.curalpha = 0.0f; + + // start with identity for (yaw,pitch,roll) accumulation + m_playerlerp.dstposition[ 0 ][ 0 ] = GEOMV_1; + m_playerlerp.dstposition[ 0 ][ 1 ] = GEOMV_0; + m_playerlerp.dstposition[ 0 ][ 2 ] = GEOMV_0; + m_playerlerp.dstposition[ 1 ][ 0 ] = GEOMV_0; + m_playerlerp.dstposition[ 1 ][ 1 ] = GEOMV_1; + m_playerlerp.dstposition[ 1 ][ 2 ] = GEOMV_0; + m_playerlerp.dstposition[ 2 ][ 0 ] = GEOMV_0; + m_playerlerp.dstposition[ 2 ][ 1 ] = GEOMV_0; + m_playerlerp.dstposition[ 2 ][ 2 ] = GEOMV_1; + + } else { + + // simply assume new orientation + pXmatrx dest = pPrevState->m_ObjPosition; + pXmatrx src = m_InputState.m_ObjPosition; + dest[ 0 ][ 0 ] = src[ 0 ][ 0 ]; + dest[ 0 ][ 1 ] = src[ 0 ][ 1 ]; + dest[ 0 ][ 2 ] = src[ 0 ][ 2 ]; + dest[ 1 ][ 0 ] = src[ 1 ][ 0 ]; + dest[ 1 ][ 1 ] = src[ 1 ][ 1 ]; + dest[ 1 ][ 2 ] = src[ 1 ][ 2 ]; + dest[ 2 ][ 0 ] = src[ 2 ][ 0 ]; + dest[ 2 ][ 1 ] = src[ 2 ][ 1 ]; + dest[ 2 ][ 2 ] = src[ 2 ][ 2 ]; + } + + // set movement sate to smoothing + m_ClientMovementMode = CMM_SMOOTHING; +} + + +// return the current simulation-state-slot ----------------------------------- +// +E_SimShipState* E_SimClientState::GetCurSimFrameStateSlot() +{ + return &m_States[ TheSimulator->GetSimFrame() % m_nNumStateSlots ]; +} + +// return the last simulation-state-slot -------------------------------------- +// +E_SimShipState* E_SimClientState::GetPrevSimFrameStateSlot() +{ + ASSERT( TheSimulator->GetSimFrame() >= 1 ); + return &m_States[ ( TheSimulator->GetSimFrame() - 1 ) % m_nNumStateSlots ]; +} + +// update the shipstate ------------------------------------------------------- +// +void E_SimClientState::CalcNewState( refframe_t CurSimRefFrames ) +{ + E_SimShipState* pPrevState = GetPrevSimFrameStateSlot(); + E_SimShipState* pCurState = GetCurSimFrameStateSlot(); + + // transfer all movements from last-sim-frame-slot to the current + memcpy( pCurState, pPrevState, sizeof( E_SimShipState ) ); + + if ( m_ClientMovementMode == CMM_SIMULATING ) { + + ASSERT( m_ClientMovementMode == CMM_SIMULATING ); + ASSERT( m_playerlerp.transition == 0 ); + + // interpolate yaw/pitch/roll + ObjRotY( pCurState->m_ObjPosition, pCurState->m_CurYaw * CurSimRefFrames ); + ObjRotX( pCurState->m_ObjPosition, pCurState->m_CurPitch * CurSimRefFrames ); + ObjRotZ( pCurState->m_ObjPosition, pCurState->m_CurRoll * CurSimRefFrames ); + + // calculate the movement + Vector3 vMovementOffset; + CalcMovement( &vMovementOffset, + pCurState->m_ObjPosition, + pCurState->m_CurSpeed, + pCurState->m_CurSlideHorz, + pCurState->m_CurSlideVert, + CurSimRefFrames ); + + // apply movement + pCurState->m_ObjPosition[ 0 ][ 3 ] += vMovementOffset.X; + pCurState->m_ObjPosition[ 1 ][ 3 ] += vMovementOffset.Y; + pCurState->m_ObjPosition[ 2 ][ 3 ] += vMovementOffset.Z; + + //LOGOUT(( "(%2d), simulating, simframe: %d, refframes: %d, curspeed: %.4f, shippos: %.2f/%.2f/%.2f", + /*LOGOUT(( "DUMPSTATE: client,smoothing,simframe,refframes,curspeed,shippos %d, 0, %d, %d, %.4f, %.2f,%.2f,%.2f", + m_nClientID, + TheSimulator->GetSimFrame(), + CurSimRefFrames, + FIXED_TO_FLOAT( pCurState->m_CurSpeed ), + pCurState->m_ObjPosition[ 0 ][ 3 ], + pCurState->m_ObjPosition[ 1 ][ 3 ], + pCurState->m_ObjPosition[ 2 ][ 3 ] )); + */ + //LOGOUT(( "%.4f", pCurState->m_ObjPosition[ 2 ][ 3 ] )); + + //MSGOUT( "SimFrame: %6d SIMULATING %d: phi: %f", TheSimulator->GetSimFrame(), m_nClientID, RAD_TO_DEG( acos( pCurState->m_ObjPosition[ 0 ][ 0 ] ) ) ); + //DBGTXT( Dump(); ); + + } else { + + ASSERT( m_ClientMovementMode == CMM_SMOOTHING ); + ASSERT( m_playerlerp.transition > 0 ); + + // check whether slerp in progress + int slerpactive = SERVER_SLERP_ORIENTATION && ( m_playerlerp.curalpha < 1.0f ); + + // determine matrix for (yaw,pitch,roll) accumulation + pXmatrx activematrix = slerpactive ? m_playerlerp.dstposition : pCurState->m_ObjPosition; + + // interpolate yaw/pitch/roll + ObjRotY( activematrix, m_playerlerp.curyaw * CurSimRefFrames ); + ObjRotX( activematrix, m_playerlerp.curpitch * CurSimRefFrames ); + ObjRotZ( activematrix, m_playerlerp.curroll * CurSimRefFrames ); + + // slerp base orientation + if ( slerpactive ) { + + // determine whether slerp info is valid + if ( m_playerlerp.curalpha >= 0.0f ) { + + // advance slerp alpha + m_playerlerp.curalpha += m_playerlerp.incalpha * CurSimRefFrames; + if ( m_playerlerp.curalpha > 1.0f ) { + m_playerlerp.curalpha = 1.0f; + } + + DBGTXT( MSGOUT( "SLERP: %d, curalpha: %f", m_nClientID, m_playerlerp.curalpha ); ); + + // calculate current frame of SLERP + Xmatrx curbasematrix; + CalcSlerpedMatrix( curbasematrix, &m_playerlerp ); + + // concat base with accumulated (yaw,pitch,roll) + MtxMtxMULt( curbasematrix, m_playerlerp.dstposition, DestXmatrx ); + + // set current orientation + pCurState->m_ObjPosition[ 0 ][ 0 ] = DestXmatrx[ 0 ][ 0 ]; + pCurState->m_ObjPosition[ 0 ][ 1 ] = DestXmatrx[ 0 ][ 1 ]; + pCurState->m_ObjPosition[ 0 ][ 2 ] = DestXmatrx[ 0 ][ 2 ]; + pCurState->m_ObjPosition[ 1 ][ 0 ] = DestXmatrx[ 1 ][ 0 ]; + pCurState->m_ObjPosition[ 1 ][ 1 ] = DestXmatrx[ 1 ][ 1 ]; + pCurState->m_ObjPosition[ 1 ][ 2 ] = DestXmatrx[ 1 ][ 2 ]; + pCurState->m_ObjPosition[ 2 ][ 0 ] = DestXmatrx[ 2 ][ 0 ]; + pCurState->m_ObjPosition[ 2 ][ 1 ] = DestXmatrx[ 2 ][ 1 ]; + pCurState->m_ObjPosition[ 2 ][ 2 ] = DestXmatrx[ 2 ][ 2 ]; + + //MSGOUT( "SimFrame: %6d SMOOTHING %d: phi: %f", TheSimulator->GetSimFrame(), m_nClientID, RAD_TO_DEG( acos( pCurState->m_ObjPosition[ 0 ][ 0 ] ) ) ); + } + } + + // account for this frames time + m_playerlerp.transition -= CurSimRefFrames; + + // switch from smoothing to simulating ? + if ( m_playerlerp.transition <= 0 ) { + + // after transition assume destination position + pCurState->m_ObjPosition[ 0 ][ 3 ] = m_playerlerp.dstposition[ 0 ][ 3 ]; + pCurState->m_ObjPosition[ 1 ][ 3 ] = m_playerlerp.dstposition[ 1 ][ 3 ]; + pCurState->m_ObjPosition[ 2 ][ 3 ] = m_playerlerp.dstposition[ 2 ][ 3 ]; + + // remaining frametime: advance in direction of flight + int remainder = -m_playerlerp.transition; + + // calculate the movement + Vector3 vMovementOffset; + CalcMovement( &vMovementOffset, + pCurState->m_ObjPosition, + m_playerlerp.curspeed, + m_playerlerp.curslidehorz, + m_playerlerp.curslidevert, + remainder ); + + pCurState->m_ObjPosition[ 0 ][ 3 ] += vMovementOffset.X; + pCurState->m_ObjPosition[ 1 ][ 3 ] += vMovementOffset.Y; + pCurState->m_ObjPosition[ 2 ][ 3 ] += vMovementOffset.Z; + + // turn transition off + m_playerlerp.transition = 0; + + // set movement state to simulating + m_ClientMovementMode = CMM_SIMULATING; + + // record the time we switched to simulating to account for this, when calculating the new smoothing targe + m_LastSwitchToSimRefFrame = SYSs_GetRefFrameCount(); + + //DBGTXT( MSGOUT( "remaining" ); ); + //DBGTXT( Dump(); ); + + } else { + + // interpolate position transition (along transition vector) + pCurState->m_ObjPosition[ 0 ][ 3 ] += m_playerlerp.transvec_x * CurSimRefFrames; + pCurState->m_ObjPosition[ 1 ][ 3 ] += m_playerlerp.transvec_y * CurSimRefFrames; + pCurState->m_ObjPosition[ 2 ][ 3 ] += m_playerlerp.transvec_z * CurSimRefFrames; + + //DBGTXT( MSGOUT( "CMM_SMOOTHING: %d", m_nClientID ); ); + //DBGTXT( Dump(); ); + } + + //LOGOUT(( "(%2d), smoothing, simframe: %d, refframes: %d, shippos: %.2f/%.2f/%.2f", + /*LOGOUT(( "DUMPSTATE: client,smoothing,simframe,refframes,curspeed,shippos %d, 1, %d, %d, 0, %.2f,%.2f,%.2f", + m_nClientID, + TheSimulator->GetSimFrame(), + CurSimRefFrames, + pCurState->m_ObjPosition[ 0 ][ 3 ], + pCurState->m_ObjPosition[ 1 ][ 3 ], + pCurState->m_ObjPosition[ 2 ][ 3 ] ));*/ + + //LOGOUT(( "%.4f", pCurState->m_ObjPosition[ 2 ][ 3 ] )); + } + +} + +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- + +// standard ctor -------------------------------------------------------------- +// +E_Simulator::E_Simulator() : + m_SimPlayerInfos( NULL ), + m_SimClientState( NULL ) +{ + Reset(); +} + +// standard dtor -------------------------------------------------------------- +// +E_Simulator::~E_Simulator() +{ + delete []m_SimClientState; + delete []m_SimPlayerInfos; +} + + +// reset all data ------------------------------------------------------------- +// +void E_Simulator::Reset() +{ + TheSimNetInput ->Reset(); + TheSimNetOutput->Reset(); + + delete []m_SimPlayerInfos; + delete []m_SimClientState; + + m_SimPlayerInfos = new E_SimPlayerInfo [ MAX_NUM_CLIENTS ]; + m_SimClientState = new E_SimClientState[ MAX_NUM_CLIENTS ]; + + m_CurSimRefFrames = 0; + m_LastSimRefFrame = REFFRAME_INVALID; + m_nSimFrame = 0; +} + +// connect the player in a specific slot -------------------------------------- +// +int E_Simulator::ConnectPlayer( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + m_SimPlayerInfos[ nClientID ].Connect( nClientID ); + m_SimClientState[ nClientID ].Connect( nClientID ); + + TheSimNetOutput->ConnectPlayer( nClientID ); + + DBGOUT( "E_Simulator::ConnectPlayer( %d )", nClientID ); + + return TRUE; +} + +// disconnect the player in a specific slot ----------------------------------- +// +int E_Simulator::DisconnectPlayer( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + + m_SimPlayerInfos[ nClientID ].Disconnect(); + m_SimClientState[ nClientID ].Disconnect(); + + TheSimNetOutput->DisconnectPlayer( nClientID ); + + DBGOUT( "E_Simulator::DisconnectPlayer( %d )", nClientID ); + + return TRUE; +} + + +// check whether a player in a slot is joined --------------------------------- +// +int E_Simulator::IsPlayerJoined( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + return m_SimPlayerInfos[ nClientID ].IsPlayerJoined(); +} + + +// check whether a player in a slot is connected ------------------------------ +// +int E_Simulator::IsPlayerDisconnected( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + return m_SimPlayerInfos[ nClientID ].IsPlayerDisconnected(); +} + + +// get the PlayerInfo for a client -------------------------------------------- +// +E_SimPlayerInfo* E_Simulator::GetSimPlayerInfo( int nClientID ) +{ + ASSERT( nClientID >= 0 ); return &m_SimPlayerInfos[ nClientID ]; +} + + +// get the SimClientState for a specific client ------------------------------- +// +E_SimClientState* E_Simulator::GetSimClientState( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + return &m_SimClientState[ nClientID ]; +} + + +// get the latest state for a client ------------------------------------------ +// +E_SimShipState* E_Simulator::GetLatestSimShipState( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + return m_SimClientState[ nClientID ].GetPrevSimFrameStateSlot(); +} + + +// calculate the refframes we want to apply to this simulation step ----------- +// +int E_Simulator::_CalcSimRefFrames() +{ + // do not simulate on first sim frame + if ( m_LastSimRefFrame == REFFRAME_INVALID ) { + m_LastSimRefFrame = m_CurRefFrame; + return FALSE; + } + + // calc current sim-frametime + m_CurSimRefFrames = m_CurRefFrame - m_LastSimRefFrame; + + // ensure we run with the desired simulation frequency + ASSERT( m_CurSimRefFrames == TheServer->GetSimTickFrameTime() ); + + // save last sim frame + m_LastSimRefFrame = m_CurRefFrame; + + return TRUE; +} + + +// simulate ship movements ---------------------------------------------------- +// +void E_Simulator::_SimulateShips() +{ + //FIXME: use the joined player list + for( int nClientID = 0; nClientID < MAX_NUM_CLIENTS; nClientID++ ) { + if( IsPlayerJoined( nClientID ) ) { + m_SimClientState[ nClientID ].CalcNewState( m_CurSimRefFrames ); + } + } +} + + +// apply the current sim-state to the engine state ( E_SimShipState -> ShipObject ) +// +void E_Simulator::_ApplySimToEngineState() +{ + //FIXME: use the joined player list + for( int nClientID = 0; nClientID < MAX_NUM_CLIENTS; nClientID++ ) { + if( IsPlayerJoined( nClientID ) ) { + + ShipObject* pShipObject = m_SimPlayerInfos[ nClientID ].GetShipObject(); + E_SimShipState* pCurState = m_SimClientState[ nClientID ].GetCurSimFrameStateSlot(); + + // apply the state in the simulation to engine object + memcpy( pShipObject->ObjPosition, pCurState->GetObjPosition(), sizeof( Xmatrx ) ); + } + } +} + + +// apply the current engine state to the sim-state ( ShipObject -> E_SimShipState ) +// +void E_Simulator::_ApplyEngineToSimState() +{ + //NOTE: this function must be called after properties of ShipObject have been altered + // that need to be transferred to the other connected clients + + //FIXME: use the joined player list + for( int nClientID = 0; nClientID < MAX_NUM_CLIENTS; nClientID++ ) { + if( IsPlayerJoined( nClientID ) ) { + + ShipObject* pShipObject = m_SimPlayerInfos[ nClientID ].GetShipObject(); + E_SimShipState* pCurState = m_SimClientState[ nClientID ].GetCurSimFrameStateSlot(); + + // get pos/orientation from engine to simulator + //FIXME: upon collision ship/ship, we must get the newest pos/orientation into pCurState + //memcpy( pCurState->ObjPosition, pShipObject->m_ObjPosition, sizeof( Xmatrx ) ); + } + } +} + + +// main simulation method ----------------------------------------------------- +// +int E_Simulator::DoSim( refframe_t CurSimRefFrame ) +{ + // store the current refframe + m_CurRefFrame = CurSimRefFrame; + + if ( !_CalcSimRefFrames() ) { + return FALSE; + } + + // maintain the game ( time/kill-limit ) + GAMECODE2( TheGame->MaintainGame(); ); + + // maintain the weapon delays + GAMECODE2( TheGame->MaintainWeaponDelays(); ); + + // animate projectile weapons + GAMECODE2( TheGame->OBJ_AnimateProjectiles(); ); + + // animate non-projectile objects + GAMECODE2( TheGame->OBJ_AnimateNonProjectiles(); ); + + // maintain counters for special objects on ships. + TheGame->MaintainSpecialsCounters(); + + // simulate ship movements + //FIXME: GAMECODE2 ?????? ( how do we merge G_Simluator and G_Main ? ) + _SimulateShips(); + + // copy sim-states to engine-states + _ApplySimToEngineState(); + + // object collision detection + GAMECODE2( TheGameCollDet->OBJ_CheckCollisions(); ); + + // place extras around the level + GAMECODE2( TheGameExtraManager->OBJ_DoExtraPlacement(); ); + + // perform ship actions like bouncing + //GAMECODE2( TheGame->OBJ_PerformShipActions(); ); + + // copy engine-states to sim-states + _ApplyEngineToSimState(); + + // increase the simulation-frame number + m_nSimFrame++; + + return TRUE; +} diff --git a/src/parsec_server/e_world.cpp b/src/parsec_server/e_world.cpp new file mode 100755 index 0000000..0195d60 --- /dev/null +++ b/src/parsec_server/e_world.cpp @@ -0,0 +1,2710 @@ +/* + * PARSEC - World representation + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:46 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2002 + * + * 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 <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.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" + +// mathematics header +#include "utl_math.h" + +// local module header +#include "parttype.h" +#include "e_world.h" + +// proprietary module headers +#include "net_game_sv.h" +#include "net_util.h" +#include "e_simulator.h" +#include "e_colldet.h" +#include "g_player.h" +#include "g_main_sv.h" + +// local module header +#include "obj_cust.h" + +// flags ---------------------------------------------------------------------- +// +//#define HACKED_SHIPOBJECT_INSTANTIATION + +// undefine macros used for transition ---------------------------------------- +// +#undef CreateObject +#undef PShipObjects +#undef LaserObjects +#undef MisslObjects +#undef ExtraObjects +#undef CustmObjects + +#define PShipObjects m_PShipObjects +#define LaserObjects m_LaserObjects +#define MisslObjects m_MisslObjects +#define ExtraObjects m_ExtraObjects +#define CustmObjects m_CustmObjects + +// flag if initialization of particle system already done --------------------- +// +static int particle_init_done = FALSE; + +// reference z values for particles ------------------------------------------- +// +float sphere_ref_z = 1.0f; +float lightning_ref_z = 1.0f; +float photon_ref_z = 1.0f; + +void LinearParticleCollision( linear_pcluster_s *cluster, int pid ); +// flags +#define USE_Z_BUFFER // use z buffer to draw particles +//#define STORE_MAX_Z_VALS // log max/min z values +//#define USER_SPHERE_CREATION // enable user sphere creation (console) +#define DO_CLUSTER_CULLING // enable culling of whole clusters +#define USE_NEW_CULLING_CODE // use new culling function +//#define USE_BOUNDING_BOX_CULLING // cull bounding box instead of sphere +#define ENABLE_PARTICLE_TRIANGLES // enable triangle drawing for particles + + +// bitmap size below which it is replaced by a square of a single color +#define BITMAP_SIZE_BOUNDARY 3 + +// typical lower boundary of particle bitmap size +int partbitmap_size_bound = BITMAP_SIZE_BOUNDARY; + +pcluster_s *Particles = NULL; +pcluster_s *CurLinearCluster = NULL; +pcluster_s *CustomDrawCluster = NULL; + +// array of registered particle definitions ----------------------------------- +// +PUBLIC int NumParticleDefinitions = 0; +PUBLIC pdefref_s ParticleDefinitions[ MAX_PARTICLE_DEFS ]; + + +// ---------------------------------------------------------------------------- +// +int E_World::_CountGenObjects( GenObject* walklist ) +{ + int listlength; + + listlength = 0; + for ( ; walklist; walklist = walklist->NextObj ) + listlength++; + + return listlength; +} + +// free memory occupied by an object ------------------------------------------ +// +void E_World::_FreeObjectMem( GenObject* objectpo ) +{ + //NOTE: + // this function should only be used + // from within OBJ_xx modules. + + ASSERT( objectpo != NULL ); + + switch ( objectpo->ObjectType & TYPELISTMASK ) { + + case PSHIP_LIST_NO: + // ensure duration weapons are properly killed + //KillDurationWeapons( (ShipObject *) objectpo ); + break; + + case LASER_LIST_NO: + // no special handling + break; + + case MISSL_LIST_NO: + // no special handling + break; + + case EXTRA_LIST_NO: + // for extras maintain count + m_nCurrentNumExtras--; + break; + + case CUSTM_LIST_NO: + // for custom objects invoke destructor + _InvokeCustomDestructor( objectpo ); + break; + } + + // free attached particle clusters +#ifdef _SERVER_HAS_PARTICLE_CODE + //FIXME: CBX: uncomment this, when particle subsys is integrated into servercode + PRT_FreeAttachedClusterList( objectpo ); +#endif // _SERVER_HAS_PARTICLE_CODE + +#ifndef INSTANTIATE_FACE_STRUCTURES +/*//FIXME: MSH ???? + // non-virtual objects may have additional instance data + dword objclass = objectpo->ObjectClass; + if ( objclass != CLASS_ID_INVALID ) { + + // make sure we take the base lod + Face* basefaces = objectpo->FaceList; + if ( objectpo->NumLodObjects > 0 ) { + ASSERT( objectpo->LodObjects != NULL ); + basefaces = objectpo->LodObjects[ 0 ].LodObject->FaceList; + } + + // free instanced face data if attached separately + ASSERT( objclass < NumObjClasses ); + if ( basefaces != ObjClasses[ objclass ]->FaceList ) { + FREEMEM( basefaces ); + } + } +*/ +#endif + + FREEMEM( objectpo ); +} + + +// invoke constructor of custom object ---------------------------------------- +// +void E_World::_InvokeCustomConstructor( GenObject* objectpo ) +{ + ASSERT( OBJECT_TYPE_CUSTOM( objectpo ) ); + + CustomObject* cobj = (CustomObject *) objectpo; + if ( cobj->callback_instant != NULL ) { + (*cobj->callback_instant)( cobj ); + } +} + + +// invoke destructor of custom object ----------------------------------------- +// +void E_World::_InvokeCustomDestructor( GenObject* objectpo ) +{ + ASSERT( OBJECT_TYPE_CUSTOM( objectpo ) ); + + CustomObject* cobj = (CustomObject *) objectpo; + if ( cobj->callback_destroy != NULL ) { + (*cobj->callback_destroy)( cobj ); + } +} + + +// kill all instances of passed in object class (search only one list) -------- +// +int E_World::_KillClassInstancesFromList( int objclass, GenObject *listpo ) +{ + ASSERT( listpo != NULL ); +#ifdef PARSEC_CLIENT + ASSERT( listpo != MyShip ); +#endif // PARSEC_CLIENT + + GenObject *delpo = listpo; + for ( int killcount = 0; ; killcount++ ) { + + while ( ( delpo->NextObj != NULL ) && + ( delpo->NextObj->ObjectClass != (dword)objclass ) ) + delpo = delpo->NextObj; + + if ( delpo->NextObj == NULL ) + return killcount; + + ASSERT( delpo->NextObj ); +#ifdef PARSEC_CLIENT + ASSERT( delpo->NextObj != MyShip ); +#endif // PARSEC_CLIENT + + GenObject *temppo = delpo->NextObj->NextObj; + FreeObjectMem( delpo->NextObj ); + delpo->NextObj = temppo; + } + + // never reached + ASSERT( 0 ); +} + + +// standard ctor -------------------------------------------------------------- +// +E_World::E_World() +{ + m_PShipObjects = NULL; + m_LaserObjects = NULL; + m_MisslObjects = NULL; + m_ExtraObjects = NULL; + m_CustmObjects = NULL; + + m_last_summoned_objectid = 0; + + // current and maximum number of extras + m_nCurrentNumExtras = 0; + m_nCurrentNumPrtExtras = 0; + //MaxNumExtras = 20; + + NextObjNumber = 1; + + MaxNumShots = 0; + NumShots = 0; + + _InitObjCtrl(); +} + +// correct pointers that have moved after object was instantiated ------------- +// +void E_World::OBJ_CorrectObjectInstance( GenObject *dstobj, GenObject *srcobj ) +{ + ASSERT( dstobj != NULL ); + ASSERT( srcobj != NULL ); + +#ifdef PARSEC_CLIENT + + ptrdiff_t pdiff = (char*)dstobj - (char*)srcobj; + + if ( dstobj->FaceAnimStates != NULL ) { + dstobj->FaceAnimStates = (FaceAnimState *) ( (char*)dstobj->FaceAnimStates + pdiff ); + } + if ( dstobj->VtxAnimStates != NULL ) { + dstobj->VtxAnimStates = (VtxAnimState *) ( (char*)dstobj->VtxAnimStates + pdiff ); + } + +#endif // PARSEC_CLIENT + +} + + +// kill object contained in specific object list (search only one list) ------- +// +int E_World::KillSpecificObject( dword objno, GenObject *listpo ) +{ + ASSERT( listpo != NULL ); +#ifdef PARSEC_CLIENT + ASSERT( listpo != MyShip ); +#endif // PARSEC_CLIENT + + GenObject *delpo = listpo; + while ( ( delpo->NextObj != NULL ) && + ( delpo->NextObj->ObjectNumber != objno ) ) + delpo = delpo->NextObj; + + if ( delpo->NextObj == NULL ) + return 0; + + ASSERT( delpo->NextObj ); +#ifdef PARSEC_CLIENT + ASSERT( delpo->NextObj != MyShip ); +#endif // PARSEC_CLIENT + + GenObject *temppo = delpo->NextObj->NextObj; + FreeObjectMem( delpo->NextObj ); + delpo->NextObj = temppo; + + return 1; +} + + +// kill a specific ship object from a list ------------------------------------ +// +int E_World::KillSpecificShipObject( dword objno ) +{ + return KillSpecificObject( objno, m_PShipObjects ); +} + + +// return pointer to first "real" ship in ship-objects list ------------------- +// +ShipObject* E_World::FetchFirstShip() +{ + // fetch head of ship-objects list + ShipObject* head = (ShipObject *) m_PShipObjects->NextObj; + +#ifdef PARSEC_CLIENT + // if local ship is head of list return next object + if ( head == MyShip ) { + head = (ShipObject *) head->NextObj; + } +#endif // PARSEC_CLIENT + + return head; +} + +// fetch object contained in specific object list ----------------------------- +// +GenObject* E_World::FetchSpecificObject( dword objno, GenObject* listpo ) +{ + ASSERT( listpo != NULL ); + + GenObject* scan = listpo->NextObj; + while ( ( scan != NULL ) && ( scan->ObjectNumber != objno ) ) + scan = scan->NextObj; + +#ifdef PARSEC_CLIENT + // prevent fetching of local ship + if ( scan == MyShip ) + scan = NULL; +#endif // PARSEC_CLIENT + + // return found object or NULL + return scan; +} + + +// fetch object via object id (search in all object lists) -------------------- +// +GenObject* E_World::FetchObject( dword objno ) +{ + GenObject* obj; + + obj = FetchSpecificObject( objno, m_PShipObjects ); + if ( obj != NULL ) + return obj; + obj = FetchSpecificObject( objno, m_LaserObjects ); + if ( obj != NULL ) + return obj; + obj = FetchSpecificObject( objno, m_MisslObjects ); + if ( obj != NULL ) + return obj; + obj = FetchSpecificObject( objno, m_ExtraObjects ); + if ( obj != NULL ) + return obj; + obj = FetchSpecificObject( objno, m_CustmObjects ); + + return obj; +} + + +// free memory occupied by an object ------------------------------------------ +// +void E_World::FreeObjectMem( GenObject *objectpo ) +{ + //NOTE: + // this function should only be used + // from within OBJ_xx modules. + + ASSERT( objectpo != NULL ); + + switch ( objectpo->ObjectType & TYPELISTMASK ) { + + case PSHIP_LIST_NO: +#ifdef PARSEC_CLIENT + // ensure duration weapons are properly killed + KillDurationWeapons( (ShipObject *) objectpo ); +#endif // PARSEC_CLIENT + break; + + case LASER_LIST_NO: + // no special handling + break; + + case MISSL_LIST_NO: + // no special handling + break; + + case EXTRA_LIST_NO: + // for extras maintain count + m_nCurrentNumExtras--; + break; + + case CUSTM_LIST_NO: + // for custom objects invoke destructor + _InvokeCustomDestructor( objectpo ); + break; + } + +#ifdef PARSEC_CLIENT + // free attached particle clusters + PRT_FreeAttachedClusterList( objectpo ); + +#ifndef INSTANTIATE_FACE_STRUCTURES +/*//FIXME: + // non-virtual objects may have additional instance data + dword objclass = objectpo->ObjectClass; + if ( objclass != CLASS_ID_INVALID ) { + + // make sure we take the base lod + Face *basefaces = objectpo->FaceList; + if ( objectpo->NumLodObjects > 0 ) { + ASSERT( objectpo->LodObjects != NULL ); + basefaces = objectpo->LodObjects[ 0 ].LodObject->FaceList; + } + + // free instanced face data if attached separately + ASSERT( objclass < NumObjClasses ); + if ( basefaces != ObjClasses[ objclass ]->FaceList ) { + FREEMEM( basefaces ); + } + } +*/ +#endif // INSTANTIATE_FACE_STRUCTURES +#endif // PARSEC_CLIENT + + FREEMEM( objectpo ); +} + + +// free all object memory blocks contained in specific list ------------------- +// +int E_World::FreeObjList( GenObject* listpo ) +{ + //NOTE: + // in addition to FreeObjects() this is also + // used by CON_ACT::AcDestroyObjectList(). + + ASSERT( listpo != NULL ); +#ifdef PARSEC_CLIENT + ASSERT( listpo != MyShip ); +#endif // PARSEC_CLIENT + + GenObject* delpo = listpo; + while ( delpo->NextObj != NULL ) { + + ASSERT( delpo->NextObj ); +#ifdef PARSEC_CLIENT + ASSERT( delpo->NextObj != MyShip ); +#endif // PARSEC_CLIENT + + GenObject* temppo = delpo->NextObj->NextObj; + _FreeObjectMem( delpo->NextObj ); + delpo->NextObj = temppo; + } + + return 1; +} + + +// free all object memory blocks ---------------------------------------------- +// +int E_World::FreeObjects() +{ + //NOTE: + // this function is called every time the + // game loop is entered (G_BOOT::GameInit()) + // and by CON_ACT::AcPrepareDataRestore() to + // remove all objects. + + int rc = 0; + + rc += FreeObjList( m_PShipObjects ); + rc += FreeObjList( m_LaserObjects ); + rc += FreeObjList( m_MisslObjects ); + rc += FreeObjList( m_ExtraObjects ); + rc += FreeObjList( m_CustmObjects ); + + return ( rc == 5 ); +} + + +// free all objects and particles --------------------------------------------- +// +void E_World::KillAllObjects() +{ + //NOTE: + // the order of the following calls is crucial. + // 3-D objects MUST BE freed before particle objects. + + FreeObjects(); +#ifdef _SERVER_HAS_PARTICLE_CODE + FreeParticles(); +#endif // _SERVER_HAS_PARTICLE_CODE +} + + +// kill all instances of passed in object class (search in all object lists) -- +// +int E_World::KillClassInstances( int objclass ) +{ + //NOTE: + // this function is used by CON_LOAD::ConLoadObject() + // to remove objects prior to overloading them. + + int killcount = 0; + + killcount += _KillClassInstancesFromList( objclass, m_PShipObjects ); + killcount += _KillClassInstancesFromList( objclass, m_LaserObjects ); + killcount += _KillClassInstancesFromList( objclass, m_MisslObjects ); + killcount += _KillClassInstancesFromList( objclass, m_ExtraObjects ); + killcount += _KillClassInstancesFromList( objclass, m_CustmObjects ); + + return killcount; +} + +// ---------------------------------------------------------------------------- +//FIXME: does this belong here ? +void E_World::IncreaseShotCounter() +{ + NumShots++; + if ( NumShots > MaxNumShots ) { + MaxNumShots = NumShots; + } +} + +// ---------------------------------------------------------------------------- +//FIXME: does this belong here ? +void E_World::DecreaseShotCounter() +{ + NumShots--; +} + + + +// init object maintenance structures ----------------------------------------- +// +void E_World::_InitObjCtrl() +{ + //NOTE: (client) + // this function gets called exactly once, + // from G_BOOT::GameBoot(). + + //NOTE: (server) + // this function gets called exactly once, + // from E_World::E_World() + + static int init_valid = TRUE; + + if ( init_valid ) { + init_valid = FALSE; + + // allocate dummy heads of object lists + PShipObjects = (ShipObject *) ALLOCMEM( sizeof( GenObject ) ); + LaserObjects = (LaserObject *) ALLOCMEM( sizeof( GenObject ) ); + MisslObjects = (MissileObject *) ALLOCMEM( sizeof( GenObject ) ); + ExtraObjects = (ExtraObject *) ALLOCMEM( sizeof( GenObject ) ); + CustmObjects = (CustomObject *) ALLOCMEM( sizeof( GenObject ) ); +#ifdef PARSEC_SERVER + // check if memory allocation failed + if ( ( PShipObjects == NULL ) || ( LaserObjects == NULL ) || + ( MisslObjects == NULL ) || ( ExtraObjects == NULL ) || + ( CustmObjects == NULL ) ) { + + OUTOFMEM( 0 ); + } +#else // !PARSEC_SERVER + VObjList = (GenObject *) ALLOCMEM( sizeof( GenObject ) ); + + // check if memory allocation failed + if ( ( PShipObjects == NULL ) || ( LaserObjects == NULL ) || + ( MisslObjects == NULL ) || ( ExtraObjects == NULL ) || + ( CustmObjects == NULL ) || ( VObjList == NULL ) ) { + + OUTOFMEM( 0 ); + } +#endif // !PARSEC_SERVER + + // initialize dummy heads of object lists + memset( PShipObjects, 0, sizeof( GenObject ) ); + memset( LaserObjects, 0, sizeof( GenObject ) ); + memset( MisslObjects, 0, sizeof( GenObject ) ); + memset( ExtraObjects, 0, sizeof( GenObject ) ); + memset( CustmObjects, 0, sizeof( GenObject ) ); +#ifdef PARSEC_CLIENT + memset( VObjList, 0, sizeof( GenObject ) ); +#endif // PARSEC_CLIENT + + } else { + + // never happens + ASSERT( 0 ); + } +} + +// create virtual (type-only) instance of custom object ----------------------- +// +CustomObject *E_World::CreateVirtualObject( dword objtypeid, dword dwOwner ) +{ + ASSERT( TYPEID_TYPE_CUSTOM( objtypeid ) ); + if ( !TYPEID_TYPE_CUSTOM( objtypeid ) ) + return NULL; + + // create new object header + size_t custsize = OBJ_FetchCustomTypeSize( objtypeid ); + CustomObject *newinstance = (CustomObject *) ALLOCMEM( custsize ); + if ( newinstance == NULL ) + OUTOFMEM( "no mem for custom object." ); + + // GenObject::NumVerts==0 ensures object + // will be skipped by BuildVisList() + memset( newinstance, 0, custsize ); + + // init fields that must be valid + newinstance->ObjectType = objtypeid; + newinstance->ObjectClass = CLASS_ID_INVALID; + newinstance->InstanceSize = custsize; + + // would normally have been done on class loading + OBJ_InitCustomType( newinstance ); + + // set object number + newinstance->ObjectNumber = NextObjNumber; + newinstance->HostObjNumber = CreateGlobalObjId( NextObjNumber, dwOwner ); + NextObjNumber++; + + // invoke constructor + if ( newinstance->callback_instant != NULL ) { + (*newinstance->callback_instant)( newinstance ); + } + + // append object at head of list + newinstance->NextObj = CustmObjects->NextObj; + CustmObjects->NextObj = newinstance; + + return newinstance; +} + +// create object of class at position with orientation +GenObject* E_World::CreateObject( int objclass, const Xmatrx startmatrx, dword dwOwner ) +{ + #ifdef HACKED_SHIPOBJECT_INSTANTIATION + + GenObject* newinstance = (ShipObject*)ALLOCMEM( sizeof( ShipObject ) ); + return newinstance; + +#else // !HACKED_SHIPOBJECT_INSTANTIATION + + ASSERT( objclass >= 0 ); + //ASSERT( objclass < NumObjClasses ); + + // check if class number is valid + //if ( ( objclass < 0 ) || ( objclass >= NumObjClasses ) ) + // return NULL; + + size_t allocinstancesize = ObjClasses[ objclass ]->InstanceSize; + +#ifdef INSTANTIATE_FACE_STRUCTURES + + // reserve space for copy of facelist + allocinstancesize += ObjClasses[ objclass ]->NumFaces * sizeof( Face ); + +#endif // INSTANTIATE_FACE_STRUCTURES + + // create new object header + GenObject *newinstance = (GenObject *) ALLOCMEM( allocinstancesize ); + if ( newinstance == NULL ) { + OUTOFMEM( "no mem for object instance." ); + } + + // fill header with class data + memcpy( newinstance, ObjClasses[ objclass ], ObjClasses[ objclass ]->InstanceSize ); + OBJ_CorrectObjectInstance( newinstance, ObjClasses[ objclass ] ); + +#ifdef INSTANTIATE_FACE_STRUCTURES + + // make copy of facelist + Face *instfacelist = (Face *) ( (char*)newinstance + newinstance->InstanceSize ); + memcpy( instfacelist, newinstance->FaceList,newinstance->NumFaces * sizeof( Face ) ); + newinstance->FaceList = instfacelist; + +#endif // INSTANTIATE_FACE_STRUCTURES + +#endif // !HACKED_SHIPOBJECT_INSTANTIATION + + // set object number + //newinstance->ObjectNumber = NextObjNumber; + newinstance->HostObjNumber = CreateGlobalObjId( NextObjNumber, dwOwner ); + // no distinction between local and global id + newinstance->ObjectNumber = newinstance->HostObjNumber; + NextObjNumber++; + + // set start matrix + memcpy( newinstance->ObjPosition, startmatrx, sizeof( Xmatrx ) ); + + // link header into list of existing objects of this type + GenObject *listhead; + switch ( newinstance->ObjectType & TYPELISTMASK ) { + + case PSHIP_LIST_NO: + listhead = PShipObjects; +#ifdef PARSEC_CLIENT + + listhead = ( PShipObjects->NextObj == MyShip ) ? PShipObjects->NextObj : PShipObjects; + // create stargate for newly created spaceship + //FIXME: gamecode + if ( !disable_ship_creation_stargate ) { + SFX_CreateStargate( (ShipObject *) newinstance ); + } +#endif // PARSEC_CLIENT + break; + + case LASER_LIST_NO: + listhead = LaserObjects; + break; + + case MISSL_LIST_NO: + listhead = MisslObjects; +#ifdef PARSEC_CLIENT + + // create missile trail + CreateVaporTrail( newinstance ); + +#endif // PARSEC_CLIENT + break; + + case EXTRA_LIST_NO: + listhead = ExtraObjects; +#ifdef PARSEC_CLIENT + // create generation animation + SFX_CreateExtra( (ExtraObject *) newinstance ); + // for extras maintain count +#endif // PARSEC_CLIENT + m_nCurrentNumExtras++; + break; + + case CUSTM_LIST_NO: + listhead = CustmObjects; + // for custom objects invoke constructor + _InvokeCustomConstructor( newinstance ); + break; + + default: + PANIC( 0 ); + } + +#ifdef PARSEC_CLIENT + // attach static particles that are + // part of the object instance + if ( AUX_ATTACH_OBJECT_PARTICLES ) { + //FIXME: gamecode + OBJ_AttachClassParticles( newinstance ); + } +#endif // PARSEC_CLIENT + + // append object at head of list + newinstance->NextObj = listhead->NextObj; + listhead->NextObj = newinstance; + + return newinstance; +} + +//Particle land +// allocate new particle cluster and insert into list ------------------------- +// +// init particle system ------------------------------------------------------- +// +void E_World::InitParticleSystem() +{ + ASSERT( !particle_init_done ); + + if ( !particle_init_done ) { + + // create single element for dummy head and tail + if ( ( Particles = (pcluster_s *) ALLOCMEM( sizeof( pcluster_s ) ) ) == NULL ) + OUTOFMEM( 0 ); + + // set pointers thusly + Particles->next = (pcluster_s *) ( (char*)Particles + sizeof( pcluster_s* ) ); + Particles->prec = NULL; + Particles->rep = (particle_s *) Particles; + + // initially there are no elements and no allocated storage + Particles->numel = 0; + Particles->maxnumel = 0; + + + particle_init_done = TRUE; + } +} + +pcluster_s * E_World::PRT_NewCluster (dword type,int numelements, size_t auxstorage) +{ + ASSERT( numelements > 0 ); + + // calc size of cluster header according to type + int headsiz; + switch ( type & CT_TYPEMASK ) { + + case CT_CONSTANT_VELOCITY: + headsiz = sizeof( linear_pcluster_s ); + break; + + case CT_LIGHTNING: + headsiz = sizeof( lightning_pcluster_s ); + break; + + case CT_OBJECTCENTERED_SPHERE: + headsiz = sizeof( basesphere_pcluster_s ); + break; + + case CT_PHOTON_SPHERE: + headsiz = sizeof( photon_sphere_pcluster_s ); + break; + + case CT_PARTICLE_SPHERE: + headsiz = sizeof( sphereobj_pcluster_s ); + break; + + case CT_CALLBACK_TRAJECTORY: + headsiz = sizeof( callback_pcluster_s ); + break; + + case CT_CUSTOMDRAW: + headsiz = sizeof( customdraw_pcluster_s ); + break; + + case CT_GENOBJECT_PARTICLES: + headsiz = sizeof( genobject_pcluster_s ); + break; + + default: + PANIC( 0 ); + } + + pcluster_s *temp = (pcluster_s *) ALLOCMEM( headsiz ); + if ( temp == NULL ) + OUTOFMEM( 0 ); + + // clear cluster header (including inherited fields!) + memset( temp, 0, headsiz ); + + // insert cluster at head of cluster list + temp->next = Particles->next; + temp->prec = Particles; + temp->next->prec = temp; + temp->prec->next = temp; + + // size of basic particle structures + size_t storesiz = sizeof( particle_s ) * numelements; + + // size of extinfo if desired + if ( type & CT_EXTINFO_STORAGE ) + storesiz += sizeof( pextinfo_s ) * numelements; + + // size of auxiliary info + size_t auxofs = storesiz; + if ( auxstorage > 0 ) + storesiz += auxstorage + sizeof( pusrinfo_s ); + + // allocate storage for elements of cluster + temp->rep = (particle_s *) ALLOCMEM( storesiz ); + temp->type = type; + temp->numel = 0; + temp->maxnumel = numelements; + + ASSERT( temp->rep != NULL ); + if ( temp->rep == NULL ) + OUTOFMEM( 0 ); + + // init particle storage of cluster to zero + memset( temp->rep, 0, storesiz ); + + // init auxstorage header if allocated + if ( auxstorage > 0 ) { + pusrinfo_s *uinfo = (pusrinfo_s *) ( (char*)temp->rep + auxofs ); + + uinfo->infovalid = FALSE; + uinfo->blocksize = auxstorage; + + temp->userinfo = uinfo; + } + + // add to global maximum number of elements + Particles->maxnumel += numelements; + + // caller has to cast this pointer to its actual type! + return temp; +} + +// set members of particle structure and write into preexisting cluster ------- +// +void E_World::PRT_InitClusterParticle (pcluster_s* cluster,int pid,int bitmap,int color, int sizebound,float refz,Vertex3* position,Vector3* velocity,int lifetime,int owner,pextinfo_s* extinfo) +{ + ASSERT( pid < cluster->maxnumel ); + + //NOTE: + // the specified cluster member (particle) + // simply gets overwritten!! + + PRT_InitParticle(cluster->rep[ pid ],color,sizebound,refz,position,velocity,lifetime,owner,extinfo ); + + // increase number of elements + cluster->numel++; + Particles->numel++; +} + +// set members of particle structure ------------------------------------------ +// +void E_World::PRT_InitParticle (particle_s& particle,int color,int sizebound,float refz,Vertex3* position,Vector3* velocity, int lifetime,int owner,pextinfo_s* extinfo) + +{ + ASSERT( position != NULL ); + + particle.owner = owner; + particle.flags = PARTICLE_ACTIVE; + particle.lifetime = lifetime; + particle.extinfo = extinfo; + //particle.bitmap = bitmap; + particle.color = color; + particle.sizebound = sizebound; + particle.ref_z = refz; + particle.position = *position; + + if ( velocity ) { + particle.velocity = *velocity; + } else { + particle.velocity.X = particle.velocity.Y = particle.velocity.Z = 0; + } +} + +// create new particle with linear animation (insert into next free slot) ----- +// +particle_s * E_World::PRT_CreateLinearParticle (particle_s& particle) { + // try to reuse already allocated cluster + if ( CurLinearCluster != NULL ) { + + ASSERT( ( CurLinearCluster->type & CT_TYPEMASK ) == CT_CONSTANT_VELOCITY ); + + if ( CurLinearCluster->numel < CurLinearCluster->maxnumel ) { + + // copy particle struct into available slot in free cluster + particle_s *pmem = CurLinearCluster->rep + CurLinearCluster->numel; + *pmem = particle; + + // check whether extinfo attached + if ( particle.extinfo != NULL ) { + + if ( ( CurLinearCluster->type & CT_EXTINFO_STORAGE ) == 0 ) { + ASSERT( 0 ); + goto allocnew; + } + + // copy over extinfo + pextinfo_s *curextinfo = + (pextinfo_s *)( CurLinearCluster->rep + CurLinearCluster->maxnumel ); + curextinfo += CurLinearCluster->numel; + memcpy( curextinfo, particle.extinfo, sizeof( pextinfo_s ) ); + + // set new extinfo pointer + pmem->extinfo = curextinfo; + } + + // increase number of cluster elements + CurLinearCluster->numel++; + + // return new particle location + return pmem; + } + } + +allocnew: + + // always allocate clusters with storage for extinfo! + dword clustertype = CT_CONSTANT_VELOCITY | CT_EXTINFO_STORAGE; + int numelements = DEFAULT_CLUSTER_SIZE; + + // create new cluster for linear particles + CurLinearCluster = PRT_NewCluster( clustertype, numelements, 0 ); + + // set callback to default + linear_pcluster_s *lincluster = (linear_pcluster_s *) CurLinearCluster; + ASSERT( lincluster->callback == NULL ); + lincluster->callback = LinearParticleCollision; + //NOTE: + // field bdsphere is zero. + + // insert recursively (tail rec) + return PRT_CreateLinearParticle( particle ); +} + +void E_World::PAN_AnimateParticles() +{ + // walk list of clusters + for ( pcluster_s *cluster = Particles->next; cluster->next; ) { + + // save next pointer to allow animation functions deletion of clusters + pcluster_s *nextcluster = cluster->next; + + // behavior animation: + // call trajectory animation function according to type identifier + if ( ( cluster->type & CT_HINT_NO_POSITIONAL_ANIMATION ) == 0 ) { + AnimateClusterBehavior( cluster ); + } + + TheGameCollDet->CheckEnergyField(cluster); + // advance to next cluster (previously remembered) + cluster = nextcluster; + +#if defined( DEBUG ) && defined( PROTECT_ANIM_CLUSTER_WALK ) + + //NOTE: + // if an animation function has killed a cluster that was not + // the cluster the function has been called for, the pointer + // to the next cluster in the list may already be invalid. + + // assert that next cluster is still part of the list + pcluster_s *tcl; + for ( tcl = Particles->next; tcl; tcl = tcl->next ) + if ( tcl == cluster ) + break; + ASSERT( tcl != NULL ); + +#endif + + } +} + +// particle behavior animation: call trajectory animation function ------------ +// +void E_World::AnimateClusterBehavior( pcluster_s *cluster ) +{ + ASSERT( cluster != NULL ); + + // call trajectory animation function according to type identifier + switch ( cluster->type & CT_TYPEENUMERATIONMASK ) { + + case ( CT_CONSTANT_VELOCITY & CT_TYPEENUMERATIONMASK ): + CalcConstantVelocityAnimation( (linear_pcluster_s *) cluster ); + break; + + case ( CT_LIGHTNING & CT_TYPEENUMERATIONMASK ): + CalcLightningAnimation( (lightning_pcluster_s *) cluster ); + break; + + case ( CT_OBJECTCENTERED_SPHERE & CT_TYPEENUMERATIONMASK ): + // CalcObjectCenteredSphereAnimation( (basesphere_pcluster_s *) cluster ); + break; + + case ( CT_PHOTON_SPHERE & CT_TYPEENUMERATIONMASK ): + // already called in G_SUPP::MaintainDurationWeapons() + //CalcPhotonSphereAnimation( (photon_sphere_pcluster_s *) cluster ); + break; + + case ( CT_PARTICLE_SPHERE & CT_TYPEENUMERATIONMASK ): + CalcSphereObjectAnimation( (sphereobj_pcluster_s *) cluster ); + break; + + case ( CT_CALLBACK_TRAJECTORY & CT_TYPEENUMERATIONMASK ): + CalcCallbackTrajectoryAnimation( (callback_pcluster_s *) cluster ); + break; + + case ( CT_CUSTOMDRAW & CT_TYPEENUMERATIONMASK ): + // CalcCustomDrawAnimation( (customdraw_pcluster_s *) cluster ); + break; + + case ( CT_GENOBJECT_PARTICLES & CT_TYPEENUMERATIONMASK ): + // CalcGenObjectAnimation( (genobject_pcluster_s *) cluster ); + break; + + } +} + +void E_World::CalcLightningAnimation( lightning_pcluster_s *cluster ) +{ + ASSERT( cluster != NULL ); + ASSERT( ( cluster->type & CT_TYPEMASK ) == CT_LIGHTNING ); + + if ( ( cluster->framecount -= TheSimulator->GetThisFrameRefFrames() ) < 0 ) { + cluster->framecount = cluster->sizzlespeed; + + GenObject *shippo = cluster->baseobject; + ASSERT( shippo != NULL ); + ASSERT( OBJECT_TYPE_SHIP( shippo ) ); + + Xmatrx tmatrx; +#if ( CT_LIGHTNING & CT_GENOBJECTRELATIVE_OBJ_MASK ) + MakeIdMatrx( tmatrx ); +#else + MakeNonTranslationMatrx( &shippo->ObjPosition, &tmatrx ); +#endif + + Vertex3 startpos; + +#if ( CT_LIGHTNING & CT_GENOBJECTRELATIVE_OBJ_MASK ) + startpos = cluster->beamstart1; +#else + MtxVctMUL( shippo->ObjPosition, &cluster->beamstart1, &startpos ); +#endif + SetLightningParticlePosition( cluster, cluster->rep, startpos, tmatrx ); + +#if ( CT_LIGHTNING & CT_GENOBJECTRELATIVE_OBJ_MASK ) + startpos = cluster->beamstart2; +#else + MtxVctMUL( shippo->ObjPosition, &cluster->beamstart2, &startpos ); +#endif + SetLightningParticlePosition( cluster, cluster->rep + LIGHTNING_LENGTH, startpos, tmatrx ); + } +} +// flags +//#define ENERGYFIELD_DESTRUCTION_MESSAGE +#define DO_BRUTEFORCE_CDETECTION // do collision detection for lightning +#define PROTECT_ANIM_CLUSTER_WALK + + + +// lightning segment deviation/variation +#define MAX_LIGHTNING_SEG_DEVIATION 1120 +#define LIGHTNING_SEG_DEVIATION_FAC 0x10 + +// reference z of lightning impact particles +#define IMPACT_PARTICLES_REF_Z 3.6f + +void E_World::SetLightningParticlePosition( lightning_pcluster_s *cluster, particle_s particles[], Vertex3& current, Xmatrx tmatrx ) +{ + ASSERT( cluster != NULL ); + + // set position of initial particle + particles[ 0 ].position.X = current.X; + particles[ 0 ].position.Y = current.Y; + particles[ 0 ].position.Z = current.Z; + + // iteratively calculate position of subsequent particles + for ( int pid = 1; pid < LIGHTNING_LENGTH; pid++ ) { + + Vertex3 deviation; + deviation.X = FIXED_TO_GEOMV( + ( (long)( RAND() % MAX_LIGHTNING_SEG_DEVIATION ) - + MAX_LIGHTNING_SEG_DEVIATION / 2 ) * + LIGHTNING_SEG_DEVIATION_FAC ); + deviation.Y = FIXED_TO_GEOMV( + ( (long)( RAND() % MAX_LIGHTNING_SEG_DEVIATION ) - + MAX_LIGHTNING_SEG_DEVIATION / 2 ) * + LIGHTNING_SEG_DEVIATION_FAC ); + deviation.Z = FLOAT_TO_GEOMV( 1.0 ); + + Vertex3 segvec; + MtxVctMUL( tmatrx, &deviation, &segvec ); + + current.X += segvec.X * 2; + current.Y += segvec.Y * 2; + current.Z += segvec.Z * 2; + + particles[ pid ].position.X = current.X; + particles[ pid ].position.Y = current.Y; + particles[ pid ].position.Z = current.Z; + +#ifdef DO_BRUTEFORCE_CDETECTION + + ASSERT( cluster->baseobject != NULL ); + + Vertex3 cpos; + MtxVctMUL( cluster->baseobject->ObjPosition, &particles[ pid ].position, &cpos ); + + if ( TheGameCollDet->CheckLightningParticleShipCollision( cpos, particles[ pid ].owner ) ) { + while ( pid < LIGHTNING_LENGTH ) { + particles[ pid ].flags &= ~PARTICLE_ACTIVE; + pid++; + } + return; + } + + particles[ pid ].flags |= PARTICLE_ACTIVE; +#endif + + } +} + +// calculate position advance of constant velocity particles ------------------ +// +void E_World::CalcConstantVelocityAnimation( linear_pcluster_s *cluster ) +{ + ASSERT( cluster != NULL ); + ASSERT( ( cluster->type & CT_TYPEMASK ) == CT_CONSTANT_VELOCITY ); + + // advance all particles in cluster along their + // local velocity vectors and maintain lifetime + int numactive = 0; + for ( int curp = 0; curp < cluster->numel; curp++ ) { + + // skip already inactive particles + if ( ( cluster->rep[ curp ].flags & PARTICLE_ACTIVE ) == 0 ) { + continue; + } else { + numactive++; + } + + // maintain lifetime + if ( ( cluster->rep[ curp ].lifetime -= TheSimulator->GetThisFrameRefFrames() ) < 0 ) + DisableParticle( cluster, curp ); + + // move along vector + Vector3 advvec; + advvec.X = cluster->rep[ curp ].velocity.X * TheSimulator->GetThisFrameRefFrames(); + advvec.Y = cluster->rep[ curp ].velocity.Y * TheSimulator->GetThisFrameRefFrames(); + advvec.Z = cluster->rep[ curp ].velocity.Z * TheSimulator->GetThisFrameRefFrames(); + + cluster->rep[ curp ].position.X += advvec.X; + cluster->rep[ curp ].position.Y += advvec.Y; + cluster->rep[ curp ].position.Z += advvec.Z; + + // invoke callback for cluster/particle + if ( cluster->rep[ curp ].flags & PARTICLE_COLLISION ) { + if ( cluster->callback != NULL ) { + (*cluster->callback)( cluster, curp ); + } + } + } + + // remove cluster if no active particles contained anymore + if ( numactive == 0 ) { + PRT_DeleteCluster( cluster ); + } +} + +// disable a single particle in a specified cluster --------------------------- +// +void E_World::DisableParticle( pcluster_s *cluster, int pid ) +{ + //NOTE: + // individual particle lifetime is currently only + // used by linear and customdraw particles. all other + // clusters maintain only wholesale cluster lifetime. + + ASSERT( cluster != NULL ); + ASSERT( ( ( cluster->type & CT_TYPEMASK ) == CT_CONSTANT_VELOCITY ) || + ( ( cluster->type & CT_TYPEMASK ) == CT_CUSTOMDRAW ) ); + cluster->rep[ pid ].flags &= ~PARTICLE_ACTIVE; +} + +// delete entire particle cluster --------------------------------------------- +// +void E_World::PRT_DeleteCluster (pcluster_s* cluster) +{ + ASSERT( cluster != NULL ); + + // maintain global cluster pointers + if ( CurLinearCluster == cluster ) + CurLinearCluster = NULL; + if ( CustomDrawCluster == cluster ) + CustomDrawCluster = NULL; + + // if cluster is object-relative remove from list if contained + if ( cluster->type & CT_GENOBJECTRELATIVE_OBJ_MASK ) + PRT_RemoveClusterFromAttachedList( (objectbase_pcluster_s *) cluster ); + + // sub from global current/maximum number of elements + Particles->numel -= cluster->numel; + Particles->maxnumel -= cluster->maxnumel; + + // unlink cluster from list + cluster->prec->next = cluster->next; + cluster->next->prec = cluster->prec; + + // free storage (cluster header and particle storage) + FREEMEM( cluster->rep ); + FREEMEM( cluster ); + MSGOUT("E_World::PRT_DeleteCluster(): Free'd particle cluster"); +} + +// remove cluster from its attachment list if contained in any ---------------- +// +void E_World::PRT_RemoveClusterFromAttachedList (objectbase_pcluster_s* cluster) +{ + ASSERT( cluster != NULL ); + ASSERT( cluster->type & CT_GENOBJECTRELATIVE_OBJ_MASK ); + ASSERT( cluster->baseobject != NULL ); + + objectbase_pcluster_s *scan = cluster->baseobject->AttachedPClusters; + objectbase_pcluster_s *precnode = NULL; + + for ( ; scan; scan = scan->attachlist ) { + + ASSERT( scan->type & CT_GENOBJECTRELATIVE_OBJ_MASK ); + ASSERT( scan->baseobject == cluster->baseobject ); + + if ( scan == cluster ) { + + if ( precnode ) + precnode->attachlist = scan->attachlist; + else + cluster->baseobject->AttachedPClusters = scan->attachlist; + + return; + } + + precnode = scan; + } +} + +// create particle object comprising a sphere --------------------------------- +// +sphereobj_pcluster_s* +E_World::PRT_CreateParticleSphereObject ( + + Vertex3& origin, // origin of particle object + geomv_t radius, // radius of sphere (also used for bounding) + int animtype, // animation type (SAT_xx) + int clustersiz, // cluster size (number of particles in sphere) + int lifetime, // lifetime of sphere (each sphere particle) + pdrwinfo_s* pdinfo, // particle appearance (drawing) info + int owner // owner id (remote player id) + ) + +{ + // check if animation type is allowed for particle sphere objects + if ( ( animtype & SAT_VALID_FOR_PSPHERE_OBJECT ) == 0 ) { + ASSERT( 0 ); + return NULL; + } + + // fetch pdefinfo if supplied + int bitmapindx = pdinfo ? pdinfo->bmindx : 1; + int pcolor = pdinfo ? pdinfo->pcolor : SPHERE_PARTICLE_COLOR; + float refz = pdinfo ? pdinfo->ref_z : sphere_ref_z; + int sizebound = pdinfo ? pdinfo->sizebnd : partbitmap_size_bound; + + // determine sphere's shape + int spheretype = animtype & SAT_SPHERE_TYPE_MASK; + + // determine number of cluster elements + int allocsiz = ( animtype & SAT_NEEDS_REFCOORDS_MASK ) ? + clustersiz * 2 : clustersiz; + + // fetch extinfo if supplied + pextinfo_s *extinfo = pdinfo ? pdinfo->extinfo : NULL; + + // determine cluster hints + dword hints = CT_HINT_PARTICLES_IDENTICAL | CT_CLUSTER_GLOBAL_EXTINFO; + + // determine cluster type + dword clustertype = CT_PARTICLE_SPHERE | hints; + if ( extinfo != NULL ) { + clustertype |= CT_EXTINFO_STORAGE | CT_HINT_PARTICLES_HAVE_EXTINFO; + } + + // create new cluster + sphereobj_pcluster_s *cluster = (sphereobj_pcluster_s *) + PRT_NewCluster( clustertype, allocsiz, 0 ); + + // fill in basic fields + cluster->bdsphere = radius; + cluster->origin = origin; + cluster->animtype = animtype; + cluster->lifetime = lifetime; + cluster->max_life = lifetime; + + // fill in additional fields + switch ( animtype & SAT_BASIC_ANIM_MASK ) { + + case SAT_ROTATING: + cluster->rot.pitch = SPHERE_ROT_PITCH; + cluster->rot.yaw = SPHERE_ROT_YAW; + cluster->rot.roll = SPHERE_ROT_ROLL; + break; + + case SAT_EXPLODING: + cluster->expl.speed = SPHERE_EXPLOSION_SPEED; + break; + + case SAT_PULSATING: + cluster->puls.amplitude = SPHERE_PULSE_AMPLITUDE; + cluster->puls.midradius = radius; + cluster->puls.frequency = SPHERE_PULSE_FREQUENCY; + cluster->puls.current_t = BAMS_DEG0; + cluster->puls.pitch = SPHERE_ROT_PITCH; + cluster->puls.yaw = SPHERE_ROT_YAW; + cluster->puls.roll = SPHERE_ROT_ROLL; + break; + + case SAT_CONTRACTING: + cluster->cont.speed = SPHERE_CONTRACT_SPEED; + cluster->cont.expandtime = CONTRACTING_SPHERE_EXPANSION_TIME; + cluster->cont.pitch = SPHERE_ROT_PITCH; + cluster->cont.yaw = SPHERE_ROT_YAW; + cluster->cont.roll = SPHERE_ROT_ROLL; + break; + } + + // set particle properties + int curp = 0; + for ( curp = 0; curp < clustersiz; curp++ ) { + Vertex3 particlepos; + CalcSphereParticlePosition( particlepos, radius, spheretype ); + + // copy extinfo into cluster + pextinfo_s *curextinfo = NULL; + if ( extinfo != NULL ) { + curextinfo = (pextinfo_s *)( cluster->rep + allocsiz ) + curp; + memcpy( curextinfo, extinfo, sizeof( pextinfo_s ) ); + } + + // init particle in cluster + PRT_InitClusterParticle( cluster, curp, bitmapindx, pcolor, + sizebound, refz, + &particlepos, NULL, + INFINITE_LIFETIME, owner, + curextinfo ); + } + + // make second copy of particles if required by animation type + if ( animtype & SAT_NEEDS_REFCOORDS_MASK ) { + for ( int refp = 0; curp < clustersiz * 2; curp++, refp++ ) { + cluster->rep[ curp ] = cluster->rep[ refp ]; + } + } + + //NOTE: + // the optional second copy of all particles is not drawn + // because the number of cluster elements is not increased + // for them. (the particles are simply copied into the cluster!) + // therefore, this block of particles is inactive and invisible + // and can safely be used for reference purposes. + // note also that the duplicate extinfo pointers pose no problem + // for the exact same reason. + + return cluster; + +} + +// calc init position for particle stochastically placed on spherical surface - +// +void E_World::CalcSphereParticlePosition( Vertex3& position, geomv_t radius, int spheretype ) +{ + Vertex3 sphereloc; + sphereloc.X = 0; + sphereloc.Y = 0; + sphereloc.Z = radius; + + Xmatrx rotmatrx; + MakeIdMatrx( rotmatrx ); + + if ( spheretype == SAT_SPHERETYPE_NORMAL ) { + + bams_t pitch = RAND() * 2 - BAMS_DEG180; + bams_t yaw = RAND() * 2 - BAMS_DEG180; + + CamRotX( rotmatrx, pitch ); + CamRotY( rotmatrx, yaw ); + + } else if ( spheretype == SAT_SPHERETYPE_DISC ) { + + int r = ( RAND() >> 10 ) & 1; + + if ( r == 0 ) { + + sphereloc.Z = FIXED_TO_GEOMV( RAND() * 0x03 + 0x0c0000 ); + + bams_t pitch = RAND() * 2 - BAMS_DEG180; + CamRotX( rotmatrx, pitch ); + + } else { + + sphereloc.Z = FIXED_TO_GEOMV( RAND() * 0x03 + 0x150000 ); + + bams_t pitch = RAND() * 2 - BAMS_DEG180; + CamRotX( rotmatrx, pitch ); + + } + + } else if ( spheretype == SAT_SPHERETYPE_DISCWITHCORE ) { + + int r = RAND() & 3; + + if ( r == 0 ) { + + sphereloc.Z = FIXED_TO_GEOMV( RAND() * 0x03 + 0x0c0000 ); + + bams_t pitch = RAND() * 2 - BAMS_DEG180; + CamRotX( rotmatrx, pitch ); + + } else if ( r == 1 ) { + + sphereloc.Z = FIXED_TO_GEOMV( RAND() * 0x03 + 0x150000 ); + + bams_t pitch = RAND() * 2 - BAMS_DEG180; + CamRotX( rotmatrx, pitch ); + + } else { + + bams_t pitch = RAND() * 2 - BAMS_DEG180; + bams_t yaw = RAND() * 2 - BAMS_DEG180; + + CamRotX( rotmatrx, pitch ); + CamRotY( rotmatrx, yaw ); + } + + } + + MtxVctMUL( rotmatrx, &sphereloc, &position ); +} + +// create an energy field ----------------------------------------------------- +// +void E_World::SFX_CreateEnergyField( Vertex3& origin ) +{ + // some particle appearance properties + int bitmap = 1; + int color = SPHERE_PARTICLE_COLOR; + extern float sphere_ref_z; + float ref_z = sphere_ref_z; + int sizebound = PRTSB_ENERGYFIELD; + int lifetime = ENERGY_FIELD_LIFETIME; + + pextinfo_s *pextinfo = NULL; + + pdrwinfo_s drawinfo; + drawinfo.bmindx = bitmap; + drawinfo.pcolor = color; + drawinfo.ref_z = ref_z; + drawinfo.extinfo = pextinfo; + drawinfo.sizebnd = sizebound; + + PRT_CreateParticleSphereObject( origin, + ENERGY_FIELD_INITIAL_RADIUS, + SAT_ENERGYFIELD_SPHERE, + SPHERE_PARTICLES, + lifetime, &drawinfo, + PLAYERID_ANONYMOUS); + // MSGOUT("E_World::SFX_CreateEnergField() X:%.2f Y:%.2f Z:%.2f",origin.X,origin.Y,origin.Z); + // maintain particle extra count + m_nCurrentNumPrtExtras++; + +} + +// ---------------------------------------------------------------------------- +// +int SWARM_rand() +{ + nextrand = nextrand * 1103515245 + 12345; + + return((nextrand >> 16) & 0x7FFF); +} + + +// ---------------------------------------------------------------------------- +// +void SWARM_srand( unsigned int seed ) +{ + nextrand = seed; +} + +// ---------------------------------------------------------------------------- +// +GenObject* E_World::SWARM_Init( int owner, Vertex3 *origin, ShipObject *targetpo, int randseed ) +{ + static pextinfo_s extinfo; + // set rand seed + SWARM_srand( randseed ); + + size_t swarmsiz = sizeof( swarm_state_s ); + swarmsiz += sizeof( Point3h_f ) * NUM_PARTICLES; + swarmsiz += 3 * sizeof( geomv_t ) * NUM_PARTICLES * TIME_POSITIONS; + swarmsiz += 3 * sizeof( geomv_t ) * NUM_PARTICLES; + + // create new particle cluster with auxstorage for swarm + dword clustertype = CT_CALLBACK_TRAJECTORY | CT_EXTINFO_STORAGE; + + callback_pcluster_s *cluster = + (callback_pcluster_s *) PRT_NewCluster( clustertype, NUM_PARTICLES, swarmsiz ); + + ASSERT( cluster != NULL ); + + // init custom fields + cluster->callback = (callback_pcluster_fpt) SWARM_TimedAnimate; + + // set performance hints + cluster->type |= CT_HINT_PARTICLES_IDENTICAL; + cluster->type |= CT_HINT_PARTICLES_HAVE_EXTINFO; + cluster->type |= CT_HINT_NO_APPEARANCE_ANIMATION; + + cluster->userinfo->infovalid = TRUE; + + char *auxstorage = (char *) &cluster->userinfo[ 1 ]; + + // get pointer to auxstorage + swarm_state_s *swarm = (swarm_state_s *) auxstorage; + auxstorage += sizeof( swarm_state_s ); + + swarm->num = NUM_PARTICLES; + + swarm->pos = (Point3h_f *) auxstorage; + auxstorage += sizeof( Point3h_f ) * NUM_PARTICLES; + + swarm->x = (geomv_t *) auxstorage; + auxstorage += sizeof( geomv_t ) * swarm->num * TIME_POSITIONS; + + swarm->y = (geomv_t *) auxstorage; + auxstorage += sizeof( geomv_t ) * swarm->num * TIME_POSITIONS; + + swarm->z = (geomv_t *) auxstorage;; + auxstorage += sizeof( geomv_t ) * swarm->num * TIME_POSITIONS; + + swarm->xv = (geomv_t *) auxstorage;; + auxstorage += sizeof( geomv_t ) * NUM_PARTICLES; + + swarm->yv = (geomv_t *) auxstorage;; + auxstorage += sizeof( geomv_t ) * NUM_PARTICLES; + + swarm->zv = (geomv_t *) auxstorage;; + + // store pointer to object for use in animation function + swarm->targetpo = targetpo; + + // init dummy object position + swarm->dummyobj.ObjPosition[ 0 ][ 3 ] = origin->X; + swarm->dummyobj.ObjPosition[ 1 ][ 3 ] = origin->Y; + swarm->dummyobj.ObjPosition[ 2 ][ 3 ] = origin->Z; + + // init excess time + swarm->timerest = 0; + + // init target position + swarm->tx[ 0 ] = targetpo->ObjPosition[ 0 ][ 3 ]; + swarm->ty[ 0 ] = targetpo->ObjPosition[ 1 ][ 3 ]; + swarm->tz[ 0 ] = targetpo->ObjPosition[ 2 ][ 3 ]; + swarm->tx[ 1 ] = swarm->tx[ 0 ]; + swarm->ty[ 1 ] = swarm->ty[ 0 ]; + swarm->tz[ 1 ] = swarm->tz[ 0 ]; + swarm->tx[ 2 ] = swarm->tx[ 0 ]; + swarm->ty[ 2 ] = swarm->ty[ 0 ]; + swarm->tz[ 2 ] = swarm->tz[ 0 ]; + + // get pointer to ship of owner + ShipObject *shippo = FetchFirstShip(); + while ( shippo && ( shippo->HostObjNumber != ShipHostObjId( owner ) ) ) { + shippo = (ShipObject *) shippo->NextObj; + } + + Vector3 dirvec; + + if ( shippo != NULL ) { + + DirVctMUL( shippo->ObjPosition, FLOAT_TO_GEOMV( 1.5f ), &dirvec ); + + } else { + + dirvec.X = FLOAT_TO_GEOMV( 1.5f ); + dirvec.Y = FLOAT_TO_GEOMV( 1.5f ); + dirvec.Z = FLOAT_TO_GEOMV( 1.5f ); + + } + + // init particle positions and velocities + int pid = 0; + for ( pid = 0; pid < swarm->num; pid++ ) { + + X( 0, pid ) = origin->X; + X( 1, pid ) = X( 0, pid ); + Y( 0, pid ) = origin->Y; + Y( 1, pid ) = Y( 0, pid ); + Z( 0, pid ) = origin->Z; + Z( 1, pid ) = Z( 0, pid ); + + swarm->xv[ pid ] = dirvec.X; + swarm->yv[ pid ] = dirvec.Y; + swarm->zv[ pid ] = dirvec.Z; + + // swarm->xv[ pid ] = GEOMV_DIV( ( balance_rand( 100 ) * PARTICLE_VELOCITY ), + // 100 * PARTICLE_VELOCITY ); + // swarm->yv[ pid ] = GEOMV_DIV( ( balance_rand( 100 ) * PARTICLE_VELOCITY ), + // 100 * PARTICLE_VELOCITY ); + // swarm->zv[ pid ] = GEOMV_DIV( ( balance_rand( 100 ) * PARTICLE_VELOCITY ), + // 100 * PARTICLE_VELOCITY ); + } + /* + // create particles + pdef_s *pdef = PRT_AcquireParticleDefinition( "swarm1", NULL ); + if ( pdef == NULL ) { + return NULL; + } + + // create extinfo + + PRT_InitParticleExtInfo( &extinfo, pdef, NULL, NULL ); + */ + float cur_particle_resoscale = 1.0f; + + float ref_z = cur_particle_resoscale * PARTICLE_REFZ; + int bitmap = iter_texrgba | iter_specularadd; + int color = 0; + + // create all particles of swarm + for ( pid = 0; pid < swarm->num; pid++ ) { + + Vector3 pos; + pos.X = X( 0, pid ); + pos.Y = Y( 0, pid ); + pos.Z = Z( 0, pid ); + + // init the particles and hook them up with cluster + PRT_InitClusterParticle( cluster, pid, bitmap, color, PRT_NO_SIZEBOUND, + ref_z, &pos, NULL, + PARTICLE_LIFETIME + balance_rand( LIFETIME_VARIANCE ), + owner, &extinfo ); + } + + return &swarm->dummyobj; +} + +// ---------------------------------------------------------------------------- +// +int E_World::SWARM_Animate( callback_pcluster_s* cluster ) +{ + ASSERT( cluster != NULL ); + + // get pointer to auxstorage + swarm_state_s *swarm = (swarm_state_s *) &cluster->userinfo[ 1 ]; + + // age the target arrays + swarm->tx[ 2 ] = swarm->tx[ 1 ]; + swarm->ty[ 2 ] = swarm->ty[ 1 ]; + swarm->tz[ 2 ] = swarm->tz[ 1 ]; + swarm->tx[ 1 ] = swarm->tx[ 0 ]; + swarm->ty[ 1 ] = swarm->ty[ 0 ]; + swarm->tz[ 1 ] = swarm->tz[ 0 ]; + + // check of target is still available + ShipObject *shippo = (ShipObject *) FetchFirstShip(); + while ( shippo && ( shippo != swarm->targetpo ) ) + shippo = (ShipObject *) shippo->NextObj; + + // update target position + if ( ( shippo != NULL ) ) { + swarm->tx[ 0 ] = swarm->targetpo->ObjPosition[ 0 ][ 3 ]; + swarm->ty[ 0 ] = swarm->targetpo->ObjPosition[ 1 ][ 3 ]; + swarm->tz[ 0 ] = swarm->targetpo->ObjPosition[ 2 ][ 3 ]; + } + + // avoid settling + swarm->xv[ SWARM_rand() % swarm->num ] += balance_rand( 3 ); + swarm->yv[ SWARM_rand() % swarm->num ] += balance_rand( 3 ); + swarm->zv[ SWARM_rand() % swarm->num ] += balance_rand( 3 ); + + int numactive = 0; + int refposset = FALSE; + + // update all particles + for ( int pid = 0; pid < swarm->num; pid++ ) { + + particle_s* curparticle = &cluster->rep[ pid ]; + + // skip already inactive particles + if ( ( curparticle->flags & PARTICLE_ACTIVE ) == 0 ) { + continue; + } + + // maintain lifetime + if ( ( curparticle->lifetime -= ANIM_TIMESLICE ) < 0 ) { + curparticle->flags &= ~PARTICLE_ACTIVE; + continue; + } + + // count as still active + numactive++; + + // age the particle arrays + X( 2, pid ) = X( 1, pid ); + Y( 2, pid ) = Y( 1, pid ); + Z( 2, pid ) = Z( 1, pid ); + X( 1, pid ) = X( 0, pid ); + Y( 1, pid ) = Y( 0, pid ); + Z( 1, pid ) = Z( 0, pid ); + + Vector3 delta; + + // accelerate to target position + delta.X = swarm->tx[ 1 ] - X( 1, pid ); + delta.Y = swarm->ty[ 1 ] - Y( 1, pid ); + delta.Z = swarm->tz[ 1 ] - Z( 1, pid ); + + geomv_t distance = VctLenX( &delta ); + if ( distance == GEOMV_0 ) + distance = GEOMV_1; + + swarm->xv[ pid ] += GEOMV_DIV( GEOMV_MUL( delta.X, PARTICLE_ACCEL ) , distance ); + swarm->yv[ pid ] += GEOMV_DIV( GEOMV_MUL( delta.Y, PARTICLE_ACCEL ) , distance ); + swarm->zv[ pid ] += GEOMV_DIV( GEOMV_MUL( delta.Z, PARTICLE_ACCEL ) , distance ); + + // speed limit checks + if ( swarm->xv[ pid ] > PARTICLE_VELOCITY ) + swarm->xv[ pid ] = PARTICLE_VELOCITY; + + if ( swarm->xv[ pid ] < - PARTICLE_VELOCITY ) + swarm->xv[ pid ] = - PARTICLE_VELOCITY; + + if ( swarm->yv[ pid ] > PARTICLE_VELOCITY ) + swarm->yv[ pid ] = PARTICLE_VELOCITY; + + if ( swarm->yv[ pid ] < - PARTICLE_VELOCITY ) + swarm->yv[ pid ] = - PARTICLE_VELOCITY; + + if ( swarm->zv[ pid ] > PARTICLE_VELOCITY ) + swarm->zv[ pid ] = PARTICLE_VELOCITY; + + if ( swarm->zv[ pid ] < - PARTICLE_VELOCITY ) + swarm->zv[ pid ] = - PARTICLE_VELOCITY; + + // move + X( 0, pid ) = X( 1, pid ) + swarm->xv[ pid ] * ANIM_TIMESLICE; + Y( 0, pid ) = Y( 1, pid ) + swarm->yv[ pid ] * ANIM_TIMESLICE; + Z( 0, pid ) = Z( 1, pid ) + swarm->zv[ pid ] * ANIM_TIMESLICE; + + // fill the position list + swarm->pos[ pid ].X = X( 0, pid ); + swarm->pos[ pid ].Y = Y( 0, pid ); + swarm->pos[ pid ].Z = Z( 0, pid ); + + // update particle positions + curparticle->position.X = swarm->pos[ pid ].X; + curparticle->position.Y = swarm->pos[ pid ].Y; + curparticle->position.Z = swarm->pos[ pid ].Z; + + if ( !refposset ) { + + swarm->dummyobj.ObjPosition[ 0 ][ 3 ] = cluster->rep[ pid ].position.X; + swarm->dummyobj.ObjPosition[ 1 ][ 3 ] = cluster->rep[ pid ].position.Y; + swarm->dummyobj.ObjPosition[ 2 ][ 3 ] = cluster->rep[ pid ].position.Z; + + refposset = TRUE; + } + + // check for collision with other ships + ShipObject *walkships = FetchFirstShip(); + for ( ; walkships; walkships = (ShipObject*) walkships->NextObj ) { + + // prevent collision with owner of particle + if ( ( GetObjectOwner( walkships ) == (dword)curparticle->owner ) ) + continue; + + if ( !TheGameCollDet->PRT_ParticleInBoundingSphere( walkships, curparticle->position ) ) + continue; + +#ifdef PARTICLES_DIE_AFTER_FIRST_COLLISION + // disable particle + curparticle->flags &= ~PARTICLE_ACTIVE; + numactive--; +#endif + + TheGameCollDet->OBJ_ShipSwarmDamage( walkships, curparticle->owner ); + + if ( walkships->CurDamage > walkships->MaxDamage ) { + for ( int ppid = 0; ppid < swarm->num; ppid++ ) { + + refframe_t newlifetime = LIFETIME_AFTEREXPL + balance_rand( LIFETIME_VARIANCE ); + + if ( cluster->rep[ ppid ].lifetime >= newlifetime ) + cluster->rep[ ppid ].lifetime = newlifetime; + } + } + } + } + + // destory swarm if no active particles contained anymore + if ( numactive == 0 ) { + PRT_DeleteCluster( cluster ); + return FALSE; + } + + return TRUE; +} + +// invoke callback function for animation of generic callback clusters -------- +// +void E_World::CalcCallbackTrajectoryAnimation( callback_pcluster_s *cluster ) +{ + ASSERT( cluster != NULL ); + ASSERT( ( cluster->type & CT_TYPEMASK ) == CT_CALLBACK_TRAJECTORY ); + ASSERT( cluster->callback != NULL ); + + // invoke callback function + if ( cluster->callback != NULL ) { + cluster->callback( cluster ); + } + + //NOTE: + // the callback function is invoked for the + // entire cluster, not single particles. +} + +// calc animation of autonomous particle sphere ------------------------------- +// +PRIVATE +void E_World::CalcSphereObjectAnimation( sphereobj_pcluster_s *cluster ) +{ + ASSERT( cluster != NULL ); + ASSERT( ( cluster->type & CT_TYPEMASK ) == CT_PARTICLE_SPHERE ); + + if ( cluster->lifetime > 0 ) { + + refframe_t refframes = TheSimulator->GetThisFrameRefFrames(); + if ( ( cluster->lifetime -= refframes ) < 0 ) { + refframes += cluster->lifetime; + } + + // handle optional particle auto-depletion + if ( cluster->animtype & SAT_AUTO_DEPLETE_PARTICLES ) { + + ASSERT( cluster->lifetime <= cluster->max_life ); + float strength = (float)cluster->lifetime / cluster->max_life; + + int maxnumel = cluster->maxnumel; + if ( cluster->animtype & SAT_NEEDS_REFCOORDS_MASK ) + maxnumel /= 2; + int numel = (int)(maxnumel * strength); + if ( numel < cluster->maxnumel ) { + cluster->numel = numel; + } + } + + // perform basic animation + switch ( cluster->animtype & SAT_BASIC_ANIM_MASK ) { + + case SAT_ROTATING: + { + bams_t pitch = cluster->rot.pitch * TheSimulator->GetThisFrameRefFrames(); + bams_t yaw = cluster->rot.yaw * TheSimulator->GetThisFrameRefFrames(); + bams_t roll = cluster->rot.roll * TheSimulator->GetThisFrameRefFrames(); + + for ( int pid = 0; pid < cluster->numel; pid++ ) { + CalcSphereParticleRotation( cluster->rep[ pid ].position, + pitch, yaw, roll ); + } + } + break; + + case SAT_EXPLODING: + { + geomv_t speed = cluster->expl.speed * refframes; + + for ( int pid = 0; pid < cluster->numel; pid++ ) { + CalcSphereParticleExplosion( cluster->rep[ pid ].position, + speed ); + } + // update cluster radius + cluster->bdsphere += speed; + } + break; + + case SAT_PULSATING: + { + sincosval_s sincosv; + GetSinCos( cluster->puls.current_t, &sincosv ); + + geomv_t pulseval = GEOMV_MUL( sincosv.sinval, cluster->puls.amplitude ); + + bams_t pitch = cluster->puls.pitch * TheSimulator->GetThisFrameRefFrames(); + bams_t yaw = cluster->puls.yaw * TheSimulator->GetThisFrameRefFrames(); + bams_t roll = cluster->puls.roll * TheSimulator->GetThisFrameRefFrames(); + + for ( int pid = 0; pid < cluster->numel; pid++ ) { + CalcSphereParticleRotation( cluster->rep[ pid + cluster->numel ].position, + pitch, yaw, roll ); + CalcSpherePulse( cluster->rep[ pid ].position, + cluster->rep[ pid + cluster->numel ].position, + pulseval ); + } + // update cluster radius + cluster->bdsphere = cluster->puls.midradius + pulseval; + + cluster->puls.current_t += cluster->puls.frequency * TheSimulator->GetThisFrameRefFrames(); + } + break; + + case SAT_CONTRACTING: + { + bams_t pitch = cluster->cont.pitch * TheSimulator->GetThisFrameRefFrames(); + bams_t yaw = cluster->cont.yaw * TheSimulator->GetThisFrameRefFrames(); + bams_t roll = cluster->cont.roll * TheSimulator->GetThisFrameRefFrames(); + + for ( int pid = 0; pid < cluster->numel; pid++ ) { + CalcSphereParticleRotation( cluster->rep[ pid ].position, + pitch, yaw, roll ); + } + + if ( cluster->cont.expandtime > 0 ) { + cluster->cont.expandtime -= TheSimulator->GetThisFrameRefFrames(); + + geomv_t contraction = cluster->cont.speed * TheSimulator->GetThisFrameRefFrames(); + for ( int pid = 0; pid < cluster->numel; pid++ ) { + CalcSphereContraction( cluster->rep[ pid ].position, -contraction ); + } + // update cluster radius + cluster->bdsphere += contraction; + } + + } + break; + } + + } else { + + // animation type-dependent behavior after lifetime is spent + switch ( cluster->animtype & SAT_BASIC_ANIM_MASK ) { + + case SAT_CONTRACTING: + { + bams_t pitch = cluster->cont.pitch * TheSimulator->GetThisFrameRefFrames(); + bams_t yaw = cluster->cont.yaw * TheSimulator->GetThisFrameRefFrames(); + bams_t roll = cluster->cont.roll * TheSimulator->GetThisFrameRefFrames(); + + for ( int pid = 0; pid < cluster->numel; pid++ ) { + CalcSphereParticleRotation( cluster->rep[ pid ].position, + pitch, yaw, roll ); + } + int delit = 0; + + { + geomv_t contraction = cluster->cont.speed * TheSimulator->GetThisFrameRefFrames(); + for ( int pid = 0; pid < cluster->numel; pid++ ) { + delit += CalcSphereContraction( cluster->rep[ pid ].position, contraction ); + } + // update cluster radius + cluster->bdsphere -= contraction; + } + + if ( delit ) { + + if ( cluster->animtype & SAT_DECREMENT_EXTRA_COUNTER ) { + m_nCurrentNumPrtExtras--; + + } + + PRT_DeleteCluster( cluster ); + } + + } + break; + + default: + PRT_DeleteCluster( cluster ); + } + } +} + +// calc rotation of sphere particle ------------------------------------------- +// +void E_World::CalcSphereParticleRotation( Vertex3& position, bams_t pitch, bams_t yaw, bams_t roll ) +{ + Xmatrx rotmatrx; + MakeIdMatrx( rotmatrx ); + CamRotX( rotmatrx, pitch ); + CamRotY( rotmatrx, yaw ); + CamRotZ( rotmatrx, roll ); + + Vertex3 rotpos; + MtxVctMUL( rotmatrx, &position, &rotpos ); + position = rotpos; +} + +// calc contraction of sphere ------------------------------------------------- +// +int E_World::CalcSphereContraction( Vertex3& position, geomv_t speed ) +{ + // check for null vector + geomv_t vx = position.X; + geomv_t vy = position.Y; + geomv_t vz = position.Z; + + ABS_GEOMV( vx ); + ABS_GEOMV( vy ); + ABS_GEOMV( vz ); + + if ( ( vx <= GEOMV_VANISHING ) && + ( vy <= GEOMV_VANISHING ) && + ( vz <= GEOMV_VANISHING ) ) { + return TRUE; + } + + // create direction unit-vector + Vector3 vec = position; + NormVctX( &vec ); + + int sign_x = ( position.X < 0 ) ? -1 : 1; + int sign_y = ( position.Y < 0 ) ? -1 : 1; + int sign_z = ( position.Z < 0 ) ? -1 : 1; + + position.X -= GEOMV_MUL( vec.X, speed ); + position.Y -= GEOMV_MUL( vec.Y, speed ); + position.Z -= GEOMV_MUL( vec.Z, speed ); + + int asign_x = ( position.X < 0 ) ? -1 : 1; + int asign_y = ( position.Y < 0 ) ? -1 : 1; + int asign_z = ( position.Z < 0 ) ? -1 : 1; + + int collapsed = FALSE; + + if ( sign_x != asign_x ) collapsed = TRUE; + if ( sign_y != asign_y ) collapsed = TRUE; + if ( sign_z != asign_z ) collapsed = TRUE; + + return collapsed; +} + +lightning_pcluster_s* E_World::CreateLightningParticles( ShipObject *shippo, int owner ) +{ + ASSERT( shippo != NULL ); + + // create appearance + int bitmap = 1; + int color = 1; + float ref_z = lightning_ref_z; + int sizebound = partbitmap_size_bound; + + // create cluster + dword clustertype = CT_LIGHTNING; + + lightning_pcluster_s *cluster = (lightning_pcluster_s *) + PRT_NewCluster( clustertype, LIGHTNING_LENGTH * 2, 0 ); + + // fill in basic fields + cluster->animtype = SAT_LIGHTNING; + cluster->sizzlespeed = LIGHTNING_SIZZLE_SPEED; + + Vertex3 startpos; + + startpos.X = shippo->Beam_X[ 0 ]; + startpos.Y = shippo->Beam_Y; + startpos.Z = shippo->Beam_Z; + cluster->beamstart1 = startpos; + + startpos.X = shippo->Beam_X[ 3 ]; + cluster->beamstart2 = startpos; + + // create particles + + int curp = 0; + + for ( curp = 0; curp < LIGHTNING_LENGTH; curp++ ) + PRT_InitClusterParticle( cluster, curp, bitmap, color, + sizebound, ref_z, + &cluster->beamstart1, NULL, + INFINITE_LIFETIME, owner, + NULL ); + for ( ; curp < LIGHTNING_LENGTH * 2; curp++ ) + PRT_InitClusterParticle( cluster, curp, bitmap, color, + sizebound, ref_z, + &cluster->beamstart2, NULL, + INFINITE_LIFETIME, owner, + NULL ); + // attach lightning particle cluster to ship + PRT_AttachClusterToObject( shippo, cluster ); + + return cluster; +} + +// attach cluster to genobject (insert at head of attachment list) ------------ +// +void E_World::PRT_AttachClusterToObject( GenObject* genobjpo, objectbase_pcluster_s* cluster ) +{ + ASSERT( genobjpo != NULL ); + ASSERT( cluster != NULL ); + ASSERT( cluster->type & CT_GENOBJECTRELATIVE_OBJ_MASK ); + + // set reference to genobject + cluster->baseobject = genobjpo; + + // insert at head of list + cluster->attachlist = genobjpo->AttachedPClusters; + genobjpo->AttachedPClusters = cluster; +} + +// delete all clusters of certain type attached to a genobject ---------------- +// +int E_World::PRT_DeleteAttachedClustersOfType( const GenObject * genobjpo, int animtype ) +{ + ASSERT( genobjpo != NULL ); + + // count number of removed clusters + int numremoved = 0; + + objectbase_pcluster_s *scan = genobjpo->AttachedPClusters; + objectbase_pcluster_s *precnode = NULL; + + while ( scan != NULL ) { + + ASSERT( scan->type & CT_GENOBJECTRELATIVE_OBJ_MASK ); + + if ( scan->animtype == animtype ) { + + if ( precnode ) + precnode->attachlist = scan->attachlist; + else + ((GenObject*)genobjpo)->AttachedPClusters = scan->attachlist; + + pcluster_s *temp = scan; + scan = scan->attachlist; + + PRT_DeleteCluster_NoListRemoval( temp ); + + numremoved++; + continue; + } + + precnode = scan; + scan = scan->attachlist; + } + + // return number of removed clusters + return numremoved; +} + +// delete entire particle cluster (don't check any attachment lists) ---------- +// +void E_World::PRT_DeleteCluster_NoListRemoval( pcluster_s* cluster ) +{ + ASSERT( cluster != NULL ); + + // maintain global cluster pointers + if ( CurLinearCluster == cluster ) + CurLinearCluster = NULL; + if ( CustomDrawCluster == cluster ) + CustomDrawCluster = NULL; + + // sub from global current/maximum number of elements + Particles->numel -= cluster->numel; + Particles->maxnumel -= cluster->maxnumel; + + // unlink cluster from list + cluster->prec->next = cluster->next; + cluster->next->prec = cluster->prec; + + // free storage (cluster header and particle storage) + FREEMEM( cluster->rep ); + FREEMEM( cluster ); +} + +// calculate pulsating sphere ------------------------------------------------- +// +void E_World::CalcSpherePulse( Vertex3& position, Vertex3& pulsebase, geomv_t pulseval ) +{ + // create direction unit-vector + Vector3 vec = pulsebase; + NormVctX( &vec ); + + // pulseval is a sine or cosine function and determines + // the deviation from the base position (radius) + position.X = pulsebase.X + GEOMV_MUL( vec.X, pulseval ); + position.Y = pulsebase.Y + GEOMV_MUL( vec.Y, pulseval ); + position.Z = pulsebase.Z + GEOMV_MUL( vec.Z, pulseval ); +} + +// calc explosion trajectory of sphere particle ------------------------------- +// +void E_World::CalcSphereParticleExplosion( Vertex3& position, geomv_t speed ) +{ + Vector3 vec = position; + NormVctX( &vec ); + + position.X += GEOMV_MUL( vec.X, speed ); + position.Y += GEOMV_MUL( vec.Y, speed ); + position.Z += GEOMV_MUL( vec.Z, speed ); +} + +// acquire already registered particle definition via its id ------------------ +// +pdef_s * E_World::PRT_AcquireParticleDefinitionById( + int pdefid // id of particle definition + ) +{ + // ensure id is valid + if ( ( pdefid < 0 ) || ( pdefid >= NumParticleDefinitions ) ) + return NULL; + + // fetch from table + pdef_s *pdef = ParticleDefinitions[ pdefid ].def; + ASSERT( pdef != NULL ); + + return pdef; +}; + +// acquire already registered particle definition via its unique name --------- +// +pdef_s * E_World::PRT_AcquireParticleDefinition( + const char* pdefname, // unique name for particle definition + int* retpdefid // id of returned particle definition + ) +{ + ASSERT( pdefname != NULL ); + + // scan all registered particle definitions + for ( int curdef = 0; curdef < NumParticleDefinitions; curdef++ ) { + if ( strcmp( ParticleDefinitions[ curdef ].defname, pdefname ) == 0 ) { + + // return id if desired + if ( retpdefid != NULL ) + *retpdefid = curdef; + + // return pointer to pdef + return ParticleDefinitions[ curdef ].def; + } + } + + return NULL; +}; + +// create particle sphere for photon cannon ----------------------------------- +// +photon_sphere_pcluster_s * E_World::CreatePhotonSphere( ShipObject *shippo ) +{ + ASSERT( shippo != NULL ); + ASSERT( OBJECT_TYPE_SHIP( shippo ) ); + + pextinfo_s extinfo; + + /* + // fetch pdef + static int pdefid = -1; + pdef_s *pdef = ( pdefid != -1 ) ? PRT_AcquireParticleDefinitionById( pdefid ) : PRT_AcquireParticleDefinition( "photon1", &pdefid ); + if ( pdef == NULL ) { + MSGOUT( "photon particles invalid." ); + return NULL; + } + + // create extinfo + pextinfo_s extinfo; + //PRT_InitParticleExtInfo( &extinfo, pdef, NULL, NULL ); + */ + // determine cluster type and hints + dword clustertype = CT_PHOTON_SPHERE; + clustertype |= CT_EXTINFO_STORAGE; + clustertype |= CT_CLUSTER_GLOBAL_EXTINFO; + clustertype |= CT_HINT_PARTICLES_HAVE_EXTINFO; + clustertype |= CT_HINT_PARTICLES_IDENTICAL; + + // create new cluster + int clustersiz = PHOTON_SPHERE_PARTICLES; + photon_sphere_pcluster_s *cluster = (photon_sphere_pcluster_s *) + PRT_NewCluster( clustertype, clustersiz, 0 ); + + // fill in basic fields + cluster->animtype = SAT_PHOTON; + cluster->bdsphere = shippo->BoundingSphere; + cluster->contraction_time = PHOTON_CONTRACTION_TIME; + cluster->cur_contraction_time = 0; + cluster->contraction_speed = PHOTON_CONTRACTION_SPEED; + cluster->max_loading_time = PHOTON_MAX_LOADING_TIME; + cluster->firing = FALSE; + cluster->numloads = PHOTON_NUMLOADS; + cluster->alive = 0; + cluster->center.X = 0; + cluster->center.Y = 0; + cluster->center.Z = 0; + cluster->pitch = PHOTON_ROT_PITCH; + cluster->yaw = PHOTON_ROT_YAW; + cluster->roll = PHOTON_ROT_ROLL; + + // set particle properties + for ( int curp = 0; curp < clustersiz; curp++ ) { + + Vertex3 particlepos; + CalcSphereParticlePosition( particlepos, shippo->BoundingSphere, SAT_SPHERETYPE_NORMAL ); + + // copy extinfo into cluster + pextinfo_s *curextinfo = (pextinfo_s *)( cluster->rep + clustersiz ) + curp; + memcpy( curextinfo, &extinfo, sizeof( pextinfo_s ) ); + + // init particle in cluster + cluster->rep[ curp ].owner = GetObjectOwner( shippo ); + cluster->rep[ curp ].flags = PARTICLE_ACTIVE; + cluster->rep[ curp ].lifetime = INFINITE_LIFETIME; + cluster->rep[ curp ].extinfo = curextinfo; + cluster->rep[ curp ].bitmap = iter_texrgba | iter_specularadd; + cluster->rep[ curp ].color = PHOTON_COLOR; + cluster->rep[ curp ].sizebound = PRT_NO_SIZEBOUND; + cluster->rep[ curp ].ref_z = photon_ref_z; + cluster->rep[ curp ].position = particlepos; + cluster->rep[ curp ].velocity.X = GEOMV_0; + cluster->rep[ curp ].velocity.Y = GEOMV_0; + cluster->rep[ curp ].velocity.Z = GEOMV_0; + + // increase number of elements + Particles->numel++; + } + + // at least one particle has to be visible + cluster->numel = 1; + + // attach sphere's particle cluster to object + PRT_AttachClusterToObject( shippo, (objectbase_pcluster_s *)cluster ); + + return cluster; +} + +// calc animation of photon sphere -------------------------------------------- +// +void E_World::CalcPhotonSphereAnimation( photon_sphere_pcluster_s *cluster ) +{ + ASSERT( cluster != NULL ); + ASSERT( ( cluster->type & CT_TYPEMASK ) == CT_PHOTON_SPHERE ); + + ShipObject *shippo = (ShipObject *) cluster->baseobject; + + float part_prf = ( cluster->max_loading_time / cluster->maxnumel ); + Vertex3 old_center; + geomv_t contraction; + + if ( !cluster->firing ) { + + // determine maximum remaining loading time + int working_time = cluster->max_loading_time - cluster->alive; + + // determine actual loading time + if ( working_time > TheSimulator->GetThisFrameRefFrames() ) { + working_time = TheSimulator->GetThisFrameRefFrames(); + } + cluster->alive += working_time; + + dword energy_consumption = shippo->CurEnergyFrac + + ( working_time * PHOTON_ENERGY_CONSUMPTION ); + G_Player* pPlayer = TheGame->GetPlayer( GetObjectOwner( shippo ) ); + + // check if enough energy to build + if ( (dword)shippo->CurEnergy < ( MIN_PHOTON_ENERGY + energy_consumption >> 16 ) ) { + cluster->alive = (int)(cluster->alive -( working_time - ( shippo->CurEnergy - MIN_PHOTON_ENERGY ) / + FIXED_TO_FLOAT( PHOTON_ENERGY_CONSUMPTION ) )); + shippo->CurEnergy = MIN_PHOTON_ENERGY - 1; + pPlayer->_WFX_DeactivatePhoton(); + } else { + shippo->CurEnergyFrac = ( energy_consumption & 0xffff ); + shippo->CurEnergy -= ( energy_consumption >> 16 ); + } + + // calculate number of visible particles + if ( ( cluster->alive < cluster->max_loading_time ) ) { + cluster->numel = (int)( cluster->alive / part_prf ); + if ( cluster->numel == 0 ) { + // at least one particle must be visible + cluster->numel = 1; + } + } + else { + cluster->numel = cluster->maxnumel; + + //NOTE: + // CurScreenRefFrames get added some lines below anyway, + // so we must not add them here + cluster->cur_contraction_time -= working_time; + + pPlayer->_WFX_DeactivatePhoton(); + } + } + + if ( cluster->firing ) { + + // increase counter + cluster->cur_contraction_time += TheSimulator->GetThisFrameRefFrames(); + + // create linear particles + + // number of particles in full load + int single_load = ( cluster->maxnumel / cluster->numloads ); + int old_numel = cluster->numel; + + // number of full loads to be created + int numloads = ( cluster->cur_contraction_time / cluster->contraction_time ); + // minimize on full loads available + if ( numloads > ( old_numel / single_load ) ) { + numloads = ( old_numel / single_load ); + } + + // set particle properties + + int color = PHOTON_COLOR; + float ref_z = photon_ref_z; + int sizebound = PRT_NO_SIZEBOUND; + int lifetime = shippo->PhotonLifeTime; + int playerid = GetObjectOwner( shippo ); + + pextinfo_s extinfo; + + /* + // fetch pdef + static int pdefid = -1; + pdef_s *pdef = ( pdefid != -1 ) ? + PRT_AcquireParticleDefinitionById( pdefid ) : + PRT_AcquireParticleDefinition( "photon1", &pdefid ); + + // create extinfo + pextinfo_s extinfo; + // PRT_InitParticleExtInfo( &extinfo, pdef, NULL, NULL ); + */ + // set speed and direction vector + Vector3 dirvec; + fixed_t speed = shippo->PhotonSpeed + shippo->CurSpeed; + DirVctMUL( shippo->ObjPosition, FIXED_TO_GEOMV( speed ), &dirvec ); + + // radius of load + geomv_t radius = shippo->BoundingSphere - GEOMV_MUL( cluster->contraction_speed, cluster->contraction_time ); + + fixed_t timefrm; + fixed_t timepos; + Vertex3 object_space; + int cur_load = 0; + + // create full loads + for ( cur_load = 0; cur_load < numloads; cur_load++ ) { + timefrm = cluster->cur_contraction_time - ( ( cur_load + 1 ) * cluster->contraction_time ); + timepos = timefrm * shippo->PhotonSpeed; + + // create one full frame set back because the current frame will + // be added by the linear particle animation code in the same frame + timepos -= speed * TheSimulator->GetThisFrameRefFrames(); + + for ( int i = 0 ; i < single_load; i++ ) { + CalcSphereParticlePosition( object_space, radius, SAT_SPHERETYPE_NORMAL ); + object_space.Z += FIXED_TO_GEOMV( 0x10000 * cluster->contraction_time ) + + FIXED_TO_GEOMV( timepos ); + + Vertex3 world_space; + MtxVctMUL( shippo->ObjPosition, &object_space, &world_space ); + + particle_s particle; + PRT_InitParticle( particle, color, sizebound, + ref_z, &world_space, &dirvec, + lifetime, playerid, &extinfo ); + particle.flags |= PARTICLE_COLLISION; + particle.flags |= PARTICLE_IS_PHOTON; + PRT_CreateLinearParticle( particle ); + } + } + + // check if last load should be created + if ( ( cluster->cur_contraction_time / cluster->contraction_time ) > ( old_numel / single_load ) ) { + timefrm = cluster->cur_contraction_time - ( ( cur_load + 1 ) * cluster->contraction_time ); + timepos = timefrm * shippo->PhotonSpeed; + + // create one full frame set back because the current frame will + // be added by the linear particle animation code in the same frame + timepos -= speed * TheSimulator->GetThisFrameRefFrames(); + + for ( int i = 0 ; i < ( old_numel % single_load ); i++ ) { + CalcSphereParticlePosition( object_space, radius, SAT_SPHERETYPE_NORMAL ); + object_space.Z += FIXED_TO_GEOMV( 0x10000 * cluster->contraction_time ) + + FIXED_TO_GEOMV( timepos ); + + Vertex3 world_space; + MtxVctMUL( shippo->ObjPosition, &object_space, &world_space ); + + particle_s particle; + PRT_InitParticle( particle, color, sizebound, + ref_z, &world_space, &dirvec, + lifetime, playerid, &extinfo ); + particle.flags |= PARTICLE_COLLISION; + particle.flags |= PARTICLE_IS_PHOTON; + PRT_CreateLinearParticle( particle ); + } + + } + + // calculate remaining visible sphere particles + cluster->numel -= ( cluster->cur_contraction_time / cluster->contraction_time ) * single_load; + if ( cluster->numel < 0 ) { + cluster->numel = 0; + } + + int next_numel = cluster->numel - single_load; + if ( next_numel < 0 ) { + next_numel = 0; + } + + if ( cluster->numel == 0 ) { + PRT_DeleteCluster(cluster); + } + else { + // contract and rotate appropriate amount of particles + + // set old and new center values + old_center.X = cluster->center.X; + old_center.Y = cluster->center.Y; + old_center.Z = ( cluster->cur_contraction_time / cluster->contraction_time ) ? 0 : cluster->center.Z; + cluster->center.Z = FIXED_TO_GEOMV( 0x10000 + * ( cluster->cur_contraction_time % cluster->contraction_time ) ); + + contraction = ( cluster->cur_contraction_time < cluster->contraction_time ) + ? ( cluster->contraction_speed * TheSimulator->GetThisFrameRefFrames() ) + : ( cluster->contraction_speed * ( cluster->cur_contraction_time % cluster->contraction_time ) ); + + bams_t pitch = cluster->pitch * TheSimulator->GetThisFrameRefFrames(); + bams_t yaw = cluster->yaw * TheSimulator->GetThisFrameRefFrames(); + bams_t roll = cluster->roll * TheSimulator->GetThisFrameRefFrames(); + int pid = 0; + for ( pid = ( cluster->numel - 1 ); pid >= next_numel; pid-- ) { + + cluster->rep[ pid ].position.X -= old_center.X; + cluster->rep[ pid ].position.Y -= old_center.Y; + cluster->rep[ pid ].position.Z -= old_center.Z; + + //FIXME ?: + // particles may not be contained in + // ship-bounding-sphere any more + CalcSphereParticleRotation( cluster->rep[ pid ].position, + pitch, yaw, roll ); + CalcSphereContraction( cluster->rep[ pid ].position, contraction ); + + // move particles forward + cluster->rep[ pid ].position.X += cluster->center.X; + cluster->rep[ pid ].position.Y += cluster->center.Y; + cluster->rep[ pid ].position.Z += cluster->center.Z; + } + for ( pid = next_numel - 1; pid >= 0; pid-- ) { + CalcSphereParticleRotation( cluster->rep[ pid ].position, + pitch, yaw, roll ); + } + // update cluster radius ? + cluster->bdsphere -= contraction; + } + + cluster->cur_contraction_time %= cluster->contraction_time; + } + else { + bams_t pitch = cluster->pitch * TheSimulator->GetThisFrameRefFrames(); + bams_t yaw = cluster->yaw * TheSimulator->GetThisFrameRefFrames(); + bams_t roll = cluster->roll * TheSimulator->GetThisFrameRefFrames(); + + for ( int pid = 0; pid < cluster->numel; pid++ ) { + CalcSphereParticleRotation( cluster->rep[ pid ].position, + pitch, yaw, roll ); + } + } +} + +// check if genobject has attached particle clusters of a certain type -------- +// +objectbase_pcluster_s * E_World::PRT_ObjectHasAttachedClustersOfType( GenObject* genobjpo, int animtype ) +{ + ASSERT( genobjpo != NULL ); + + objectbase_pcluster_s *scan = genobjpo->AttachedPClusters; + for ( ; scan; scan = scan->attachlist ) { + + ASSERT( scan->type & CT_GENOBJECTRELATIVE_OBJ_MASK ); + + if ( scan->animtype == animtype ) + return scan; + } + + return NULL; +} + +void LinearParticleCollision( linear_pcluster_s *cluster, int pid ) { + TheGameCollDet->LinearParticleCollision(cluster, pid); +} + +// ---------------------------------------------------------------------------- +// +void SWARM_TimedAnimate( callback_pcluster_s* cluster ) +{ + ASSERT( cluster != NULL ); + + if ( TheWorld->SWARM_Animate( cluster ) == FALSE ) + return; +} + + diff --git a/src/parsec_server/g_main_sv.cpp b/src/parsec_server/g_main_sv.cpp new file mode 100755 index 0000000..6214d96 --- /dev/null +++ b/src/parsec_server/g_main_sv.cpp @@ -0,0 +1,1192 @@ +/* + * PARSEC - Game logic - SERVER + * + * $Author: uberlinuxguy $ - $Date: 2004/09/15 12:25:45 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2002 + * + * 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 <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.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" +#include "e_defs.h" + +// mathematics header +#include "utl_math.h" + +// utility headers +#include "utl_list.h" + +// local module header +#include "g_main_sv.h" + +// proprietary module headers +#include "con_aux_sv.h" +#include "g_extra.h" +#include "g_player.h" +#include "net_game_sv.h" +//#include "net_util.h" +#include "obj_clas.h" +#include "obj_creg.h" +#include "obj_name.h" +#include "od_props.h" +#include "od_class.h" +#include "g_stgate.h" +#include "e_connmanager.h" +#include "e_gameserver.h" +#include "e_simnetoutput.h" +#include "e_simulator.h" +#include "sys_refframe_sv.h" +#include "g_emp.h" + +#undef PShipObjects +#undef LaserObjects +#undef MisslObjects +#undef ExtraObjects +#undef CustmObjects + +#undef CreateObject +#undef FreeObjList +#undef KillAllObjects +#undef FetchObject +#undef FetchFirstShip +#undef FetchFirstLaser +#undef FetchFirstMissile +#undef FetchFirstExtra +#undef FetchFirstCustom +#undef KillClassInstances + +#undef ObjClasses + + +// ---------------------------------------------------------------------------- +// G_TimeManagement methods +// ---------------------------------------------------------------------------- + +// standard ctor -------------------------------------------------------------- +// +G_TimeManagement::G_TimeManagement() +{ + Reset(); + + // for an endless game, no timelimit + // no kill limit. Just a FFA game :-) + // GameIsEndless=0; +} + + +// set the time limits -------------------------------------------------------- +// +void G_TimeManagement::RealizeVariables() +{ + //FIXME: kill limit ? + m_nSecGameTimeLimit = SV_GAME_TIMELIMIT; + m_nSecRestartTimeLimit = SV_GAME_RESTART_TIMEOUT; + m_GameEndRefFrames = m_nSecGameTimeLimit * FRAME_MEASURE_TIMEBASE; +} + + +// reset the game time management --------------------------------------------- +// +void G_TimeManagement::Reset() +{ + m_GameRefFrames = GAME_NOTSTARTEDYET; + m_RefFrameBase = 0; +} + + +// start a game --------------------------------------------------------------- +// +void G_TimeManagement::StartGame() +{ + m_GameRefFrames = 0; + m_RefFrameBase = SYSs_GetRefFrameCount(); +} + + +// stop the game ( time limit hit ) ------------------------------------------- +// +void G_TimeManagement::StopGame_TimeLimit() +{ + m_GameRefFrames = GAME_FINISHED_TIME; + m_RefFrameBase = SYSs_GetRefFrameCount(); + m_RestartRefFrames = m_nSecRestartTimeLimit * FRAME_MEASURE_TIMEBASE; +} + + +// stop the game ( kill limit hit ) ------------------------------------------- +// +void G_TimeManagement::StopGame_KillLimit() +{ + m_GameRefFrames = GAME_FINISHED_KILLS; + m_RefFrameBase = SYSs_GetRefFrameCount(); + m_RestartRefFrames = m_nSecRestartTimeLimit * FRAME_MEASURE_TIMEBASE; +} + + +// return whether the game is not yet started --------------------------------- +// +int G_TimeManagement::IsNotYetStarted() +{ + return ( m_GameRefFrames == GAME_NOTSTARTEDYET ); +} + + +// return whether currently a game is running --------------------------------- +// +int G_TimeManagement::IsGameRunning() +{ + return ( m_GameRefFrames >= 0 ); +} + + +// return whether the game is finished ---------------------------------------- +// +int G_TimeManagement::IsGameFinished() +{ + return ( ( m_GameRefFrames == GAME_FINISHED_TIME ) || ( m_GameRefFrames == GAME_FINISHED_KILLS ) ); +} + + +// check whether the restart timeout is over ---------------------------------- +// +int G_TimeManagement::IsRestartTimeoutOver() +{ + // check whether game is finished at all + if ( !IsGameFinished() ) { + return FALSE; + } + + // maintain timeout + refframe_t diff = SYSs_GetRefFrameCount() - m_RefFrameBase; + m_RefFrameBase = SYSs_GetRefFrameCount(); + m_RestartRefFrames -= diff; + + // return whether timeout is over + return ( m_RestartRefFrames <= 0 ); +} + + +// check whether the game time limit is hit ----------------------------------- +// +int G_TimeManagement::IsGameTimeLimitHit() +{ + if ( !IsGameRunning() ) { + return FALSE; + } + + // maintain timeout + refframe_t diff = SYSs_GetRefFrameCount() - m_RefFrameBase; + m_RefFrameBase = SYSs_GetRefFrameCount(); + m_GameRefFrames += diff; + + // check whether the game time is longer than the timelimit + return ( m_GameRefFrames >= m_GameEndRefFrames ); +} + + +// return the current gametime in secs or special gametime codes -------------- +// this is sent to the clients +int G_TimeManagement::GetCurGameTime() +{ + // NOTE: the conversion to secs. is needed for distribution to the clients + if ( IsGameRunning() ) { + + refframe_t timeleft = ( m_GameEndRefFrames - m_GameRefFrames ); + if ( timeleft < 0 ) { + timeleft = 0; + } + + return ( timeleft / FRAME_MEASURE_TIMEBASE ); + + } else { + ASSERT( ( m_GameRefFrames == GAME_NOTSTARTEDYET ) || + ( m_GameRefFrames == GAME_FINISHED_TIME ) || + ( m_GameRefFrames == GAME_FINISHED_KILLS ) ); + + return m_GameRefFrames; + } +} + + +// ---------------------------------------------------------------------------- +// G_Main methods +// ---------------------------------------------------------------------------- + +// default ctor --------------------------------------------------------------- +// +G_Main::G_Main() : + m_Players ( NULL ), + m_CurConnectedPlayerList( NULL ), + m_CurJoinedPlayerList ( NULL ) +{ +} + + +// default dtor --------------------------------------------------------------- +// +G_Main::~G_Main() +{ + delete m_CurJoinedPlayerList; + delete m_CurConnectedPlayerList; + delete []m_Players; +} + + +// realize game vars from console vars ---------------------------------------- +// +void G_Main::RealizeVariables() +{ + m_TimeManager.RealizeVariables(); + m_nKillLimit = SV_GAME_KILLLIMIT; +} + + +// create a stargate for a specific server at a position, with a direction ---- +// +void G_Main::CreateStargate( int serverid, Vector3* pos_spec, Vector3* dir_spec ) +{ + ASSERT( pos_spec != NULL ); + ASSERT( dir_spec != NULL ); + + // create corresponding stargate objects + dword objclass = OBJ_FetchObjectClassId( "stargate" ); + if ( objclass != CLASS_ID_INVALID ) { + + Xmatrx startm; + MakeIdMatrx( startm ); + startm[ 0 ][ 3 ] = pos_spec->X; + startm[ 1 ][ 3 ] = pos_spec->Y; + startm[ 2 ][ 3 ] = pos_spec->Z; + + startm[ 0 ][ 2 ] = dir_spec->X; + startm[ 1 ][ 2 ] = dir_spec->Y; + startm[ 2 ][ 2 ] = dir_spec->Z; + + // ensure orthogonal matrix + CrossProduct2( &startm[ 0 ][ 1 ], &startm[ 0 ][ 2 ], &startm[ 0 ][ 0 ] ); + CrossProduct2( &startm[ 0 ][ 0 ], &startm[ 0 ][ 2 ], &startm[ 0 ][ 1 ] ); + + // create the object + Stargate* stargate = (Stargate*)TheWorld->CreateObject( objclass, startm, PLAYERID_SERVER ); + + // store serverid + stargate->serverid = serverid; + + // attach the created E_Distributable for the engine object + // stargates are to be delivered reliable + stargate->pDist = TheSimNetOutput->CreateDistributable( stargate, TRUE ); + + } else { + MSGOUT( "object class stargate could not be found.\n" ); + } +} + + + +// init all game vars --------------------------------------------------------- +// +void G_Main::Init() +{ + ASSERT( m_Players == NULL ); + ASSERT( m_CurConnectedPlayerList == NULL ); + ASSERT( m_CurJoinedPlayerList == NULL ); + + m_Players = new G_Player[ MAX_NUM_CLIENTS ]; + m_CurConnectedPlayerList = new UTL_List<G_Player*>; + m_CurJoinedPlayerList = new UTL_List<G_Player*>; + + EnergyExtraBoost = EnergyExtraBoost; + RepairExtraBoost = RepairExtraBoost; + + DumbPackNumMissls = DumbPackNumMissls; + HomPackNumMissls = HomPackNumMissls; + SwarmPackNumMissls = SwarmPackNumMissls; + ProxPackNumMines = ProxPackNumMines; + + MegaShieldStrength = MEGASHIELD_STRENGTH * FRAME_MEASURE_TIMEBASE; + + m_NebulaID = m_NebulaID; + + m_nKillLimit = DEFAULT_KILL_LIMIT; + + if(m_NebulaID == 0) //Wasn't set with nebula.id command + m_NebulaID = 3; //Default Red system +} + + +// join a player ( init join position ) --------------------------------------- +// +void G_Main::JoinPlayer( int nClientID, E_SimShipState* pSimShipState ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + ASSERT( pSimShipState != NULL ); + + pXmatrx ObjPosition = pSimShipState->GetObjPosition(); + + int xdist = ( RAND() % JP_RANGE ) - JP_OFS; + int ydist = ( RAND() % JP_RANGE ) - JP_OFS; + int zdist = ( RAND() % JP_RANGE ) - JP_OFS; + + xdist += ( xdist < 0 ) ? -JP_M_S_D : JP_M_S_D; + ydist += ( ydist < 0 ) ? -JP_M_S_D : JP_M_S_D; + zdist += ( zdist < 0 ) ? -JP_M_S_D : JP_M_S_D; + + ObjPosition[ 0 ][ 3 ] = INT_TO_GEOMV( xdist ); + ObjPosition[ 1 ][ 3 ] = INT_TO_GEOMV( ydist ); + ObjPosition[ 2 ][ 3 ] = INT_TO_GEOMV( zdist ); + + //ObjPosition[ 0 ][ 3 ] = INT_TO_GEOMV( 0 ); + //ObjPosition[ 1 ][ 3 ] = INT_TO_GEOMV( 0 ); + //ObjPosition[ 2 ][ 3 ] = INT_TO_GEOMV( nClientID * 200 ); + + m_CurJoinedPlayerList->AppendTail( &m_Players[ nClientID ] ); +} + + +// unjoin a player ------------------------------------------------------------ +// +void G_Main::UnjoinPlayer( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + G_Player* pPlayer = &m_Players[ nClientID ]; + int rc = m_CurJoinedPlayerList->Remove( pPlayer ); + ASSERT( rc ); +} + + +// return the # of joined players --------------------------------------------- +// +int G_Main::GetNumJoined() +{ + return m_CurJoinedPlayerList->GetNumEntries(); +} + + +// connect a player ----------------------------------------------------------- +// +void G_Main::ConnectPlayer( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + G_Player* pPlayer = &m_Players[ nClientID ]; + pPlayer->Connect( nClientID ); + m_CurConnectedPlayerList->AppendTail( pPlayer ); + + // start the game, if not yet started + if ( m_TimeManager.IsNotYetStarted() ) { + m_TimeManager.StartGame(); + } +} + + +// disconnect a player -------------------------------------------------------- +// +void G_Main::DisconnectPlayer( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + G_Player* pPlayer = &m_Players[ nClientID ]; + pPlayer->Disconnect(); + int rc = m_CurConnectedPlayerList->Remove( pPlayer ); + ASSERT( rc ); + + // if the last player disconnects, reset the game + if ( TheConnManager->GetNumConnected() == 0 ) { + + // reset the player game vars + _ResetPlayerVars(); + + m_TimeManager.Reset(); + } +} + + +// retrieve the # of kills by this player ------------------------------------- +// +int G_Main::GetPlayerKills( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + return m_Players[ nClientID ].GetKills(); +} + + +// retrieve the last unjoin flag of the player -------------------------------- +// +int G_Main::GetPlayerLastUnjoinFlag( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + return m_Players[ nClientID ].GetLastUnjoinFlag(); +} + + +// retrieve the last killer of the player ------------------------------------- +// +int G_Main::GetPlayerLastKiller( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + return m_Players[ nClientID ].GetLastKiller(); +} + + +// record a kill -------------------------------------------------------------- +// +void G_Main::RecordKill( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + m_Players[ nClientID ].RecordKill(); +} + + +// record a death ------------------------------------------------------------- +// +void G_Main::RecordDeath( int nClientID, int nClientID_Killer ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + m_Players[ nClientID ].RecordDeath( nClientID_Killer ); +} + + +// reset the death info of the client ----------------------------------------- +// +void G_Main::ResetDeathInfo( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + m_Players[ nClientID ].ResetDeathInfo(); +} + + +// get the player ------------------------------------------------------------- +// +G_Player* G_Main::GetPlayer( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + return &m_Players[ nClientID ]; +} + + +// maintain the game ---------------------------------------------------------- +// +void G_Main::MaintainGame() +{ + if ( m_TimeManager.IsGameRunning() ) { + + // check whether the game time-limit is hit + if ( m_TimeManager.IsGameTimeLimitHit() ) { + + m_TimeManager.StopGame_TimeLimit(); + + //FIXME: unjoin all joined players + + } else { + + // check whether the kill-limit is hit + int nMaxKills = 0; + for( int nClientID = 0; nClientID < MAX_NUM_CLIENTS; nClientID++ ) { + G_Player* pPlayer = &m_Players[ nClientID ]; + if ( pPlayer->GetKills() > nMaxKills ) { + nMaxKills = pPlayer->GetKills(); + } + } + + // stop the game if kill limit hit + if ( nMaxKills >= m_nKillLimit ) { + m_TimeManager.StopGame_KillLimit(); + } + } + } else { + + // check whether restart timeout is over + if ( m_TimeManager.IsRestartTimeoutOver() ) { + + // reset the player game vars + _ResetPlayerVars(); + + // if there are still players connected, restart the game + if ( TheConnManager->GetNumConnected() > 0 ) { + m_TimeManager.StartGame(); + } else { + // set to GAME_NOTSTARTEDYET mode + m_TimeManager.Reset(); + } + } + return; + } +} + + +// reset all player game vars ------------------------------------------------- +// +void G_Main::_ResetPlayerVars() +{ + for( int nClientID = 0; nClientID < MAX_NUM_CLIENTS; nClientID++ ) { + m_Players[ nClientID ].ResetGameVars(); + } +} + + +// maintain weapon firing delays ---------------------------------------------- +// +void G_Main::MaintainWeaponDelays() +{ + for( int nClientID = 0; nClientID < MAX_NUM_CLIENTS; nClientID++ ) { + m_Players[ nClientID ].MaintainWeaponDelays(); + } +} + + +// check availability of specified device ------------------------------------- +// +int G_Main::OBJ_DeviceAvailable( ShipObject* pShip, int mask ) +{ + ASSERT( pShip != NULL ); + + if ( SV_CHEAT_DEVICE_CHECKS ) + return TRUE; + + return ( ( pShip->Weapons & mask ) != 0 ); +} + + +// animate projectile objects (lasers and missiles) --------------------------- +// +void G_Main::OBJ_AnimateProjectiles() +{ + //NOTE: + // this function gets called once per frame by the game loop ( E_Simulator::DoSim() ) + + _WalkLaserObjects(); + _WalkMissileObjects(); + FireDurationWeapons(); + TheWorld->PAN_AnimateParticles(); +} + + +// animate non-projectile objects (extras, mines) ----------------------------- +// +void G_Main::OBJ_AnimateNonProjectiles() +{ + //NOTE: + // this function gets called once per frame by the game loop ( E_Simulator::DoSim() ) + + _WalkExtraObjects(); +} + +void G_Main::MaintainSpecialsCounters( ) { + + for( int nClientID = 0; nClientID < MAX_NUM_CLIENTS; nClientID++ ) { + if( TheSimulator->IsPlayerJoined( nClientID ) ) { + ShipObject *pShip = m_Players[nClientID].GetShipObject(); + + // decrement the MegaShieldAbsorption counter + if(pShip->MegaShieldAbsorption > 0){ + pShip->MegaShieldAbsorption -= TheSimulator->GetThisFrameRefFrames(); + DBGTXT(MSGOUT("Client: %d, MegaShield: %d, RefFrameDec: %d", + nClientID, + pShip->MegaShieldAbsorption, + TheSimulator->GetThisFrameRefFrames() + );); + } + } + } + // walk custom objects and decrement any class life identifiers that need decrementing. + _WalkCustomObjects(); +} + +void G_Main::FireDurationWeapons() +{ + for( int nClientID = 0; nClientID < MAX_NUM_CLIENTS; nClientID++ ) { + if( TheSimulator->IsPlayerJoined( nClientID ) ) { + MaintainDurationWeapons( nClientID ); + } + } +} + +void G_Main::MaintainDurationWeapons( int playerid ) +{ + + ShipObject* pShip = m_Players[playerid].GetShipObject(); + ASSERT( pShip != NULL ); + + // maintain helix + if ( pShip->WeaponsActive & WPMASK_CANNON_HELIX ) { + m_Players[playerid]._WFX_MaintainHelix( pShip, playerid ); + } + + // maintain lightning + if ( pShip->WeaponsActive & WPMASK_CANNON_LIGHTNING ) { + m_Players[playerid].WFX_MaintainLightning( pShip ); + } + + // maintain photon + photon_sphere_pcluster_s* cluster = (photon_sphere_pcluster_s *) + TheWorld->PRT_ObjectHasAttachedClustersOfType( pShip, SAT_PHOTON ); + if ( cluster != NULL ) { + TheWorld->CalcPhotonSphereAnimation( cluster ); + } + /* + // maintain emp + if ( shippo->WeaponsActive & WPMASK_DEVICE_EMP ) { + WFX_CreateEmpWaves( shippo ); + } + */ +} + +// walk list of extra objects and advance them ( also handle timeout ) -------- +// +void G_Main::_WalkExtraObjects() +{ + ASSERT( TheWorld->m_ExtraObjects != NULL ); + + // walk list of extras + ExtraObject *precnode = TheWorld->m_ExtraObjects; + while ( precnode->NextObj != NULL ) { + + ASSERT( OBJECT_TYPE_EXTRA( precnode->NextObj ) ); + + // get pointer to current extra + ExtraObject *curextra = (ExtraObject *) precnode->NextObj; + ASSERT( curextra != NULL ); + + // check if lifetime of extra is spent + curextra->LifeTimeCount -= TheSimulator->GetThisFrameRefFrames(); + //MSGOUT( "curextra: %x, curextra->LifeTimeCount: %d", curextra, curextra->LifeTimeCount ); + if ( curextra->LifeTimeCount < 0 ) { + TheGameExtraManager->OBJ_KillExtra( precnode, FALSE ); + continue; + } + + // animate this extra + TheGameExtraManager->OBJ_AnimateExtra( curextra ); + + // advance in list + precnode = curextra; + } +} + + +// walk list of laser objects and advance them ( also handle timeout ) -------- +// +void G_Main::_WalkMissileObjects() +{ + ASSERT( TheWorld->m_MisslObjects != NULL ); + + GenObject *precnode = TheWorld->m_MisslObjects; + GenObject *walkshots = TheWorld->m_MisslObjects->NextObj; + + // walk all Missiles + while ( walkshots != NULL ) { + + ASSERT( OBJECT_TYPE_MISSILE( walkshots ) ); + + MissileObject *missilepo = (MissileObject *) walkshots; + missilepo->LifeTimeCount -= TheSimulator->GetThisFrameRefFrames(); + + if ( missilepo->LifeTimeCount <= 0 ) { + +#ifndef DONT_RESET_SHOTCOUNTER + TheWorld->DecreaseShotCounter(); +#endif // DONT_RESET_SHOTCOUNTER + + // release the E_Distributable ( distribute removal ) + //MSGOUT( "G_Main::_WalkMissileObjects() calls ReleaseDistributable()" ); + TheSimNetOutput->ReleaseDistributable( missilepo->pDist ); + ASSERT( walkshots != NULL ); + precnode->NextObj = walkshots->NextObj; + TheWorld->FreeObjectMem( walkshots ); + walkshots = precnode->NextObj; + + continue; + + } else { + + //FIXME: move to OBJ_AnimateMissile() + Vector3 tempspeed; + switch ( missilepo->ObjectType & TYPECONTROLMASK ) { + case TYPEMISSILEISSTANDARD: + { + tempspeed.X = missilepo->DirectionVec.X * TheSimulator->GetThisFrameRefFrames(); + tempspeed.Y = missilepo->DirectionVec.Y * TheSimulator->GetThisFrameRefFrames(); + tempspeed.Z = missilepo->DirectionVec.Z * TheSimulator->GetThisFrameRefFrames(); + + missilepo->PrevPosition.X = missilepo->ObjPosition[ 0 ][ 3 ]; + missilepo->PrevPosition.Y = missilepo->ObjPosition[ 1 ][ 3 ]; + missilepo->PrevPosition.Z = missilepo->ObjPosition[ 2 ][ 3 ]; + + missilepo->ObjPosition[ 0 ][ 3 ] += tempspeed.X; + missilepo->ObjPosition[ 1 ][ 3 ] += tempspeed.Y; + missilepo->ObjPosition[ 2 ][ 3 ] += tempspeed.Z; + break; + } + case TYPEMISSILEISHOMING: + { + tempspeed.X = missilepo->DirectionVec.X * TheSimulator->GetThisFrameRefFrames(); + tempspeed.Y = missilepo->DirectionVec.Y * TheSimulator->GetThisFrameRefFrames(); + tempspeed.Z = missilepo->DirectionVec.Z * TheSimulator->GetThisFrameRefFrames(); + + missilepo->PrevPosition.X = missilepo->ObjPosition[ 0 ][ 3 ]; + missilepo->PrevPosition.Y = missilepo->ObjPosition[ 1 ][ 3 ]; + missilepo->PrevPosition.Z = missilepo->ObjPosition[ 2 ][ 3 ]; + + missilepo->ObjPosition[ 0 ][ 3 ] += tempspeed.X; + missilepo->ObjPosition[ 1 ][ 3 ] += tempspeed.Y; + missilepo->ObjPosition[ 2 ][ 3 ] += tempspeed.Z; + + TargetMissileObject *tmissilepo = (TargetMissileObject *) missilepo; + GenObject *targetpo = NULL; + + if ( tmissilepo->TargetObjNumber == TARGETID_NO_TARGET ) { + + + // no target (already lost) + targetpo = NULL; + + } else { + + // search for target in shiplist + targetpo = TheWorld->FetchFirstShip(); + while ( ( targetpo != NULL ) && ( targetpo->HostObjNumber != tmissilepo->TargetObjNumber ) ) { + targetpo = targetpo->NextObj; + } + if ( targetpo == NULL ) { + // missile loses target once object not found + + MSGOUT("G_Main::_WalkMissileObjects(): Target lost: No Object found"); + tmissilepo->TargetObjNumber = TARGETID_NO_TARGET; + } + } + + if ( ( targetpo != NULL ) ) { + + Vector3 dirvec; + dirvec.X = targetpo->ObjPosition[ 0 ][ 3 ] - missilepo->ObjPosition[ 0 ][ 3 ]; + dirvec.Y = targetpo->ObjPosition[ 1 ][ 3 ] - missilepo->ObjPosition[ 1 ][ 3 ]; + dirvec.Z = targetpo->ObjPosition[ 2 ][ 3 ] - missilepo->ObjPosition[ 2 ][ 3 ]; + + Vector3 normvec; + normvec.X = missilepo->ObjPosition[ 0 ][ 2 ]; + normvec.Y = missilepo->ObjPosition[ 1 ][ 2 ]; + normvec.Z = missilepo->ObjPosition[ 2 ][ 2 ]; + + if ( DOT_PRODUCT( &dirvec, &normvec ) < 0 ) { + + // lock lost due to position + tmissilepo->TargetObjNumber = TARGETID_NO_TARGET; + MSGOUT("G_Main::_WalkMissileObjects(): Target lost: Out of position"); + + + } else { + + normvec.X = missilepo->ObjPosition[ 0 ][ 1 ]; + normvec.Y = missilepo->ObjPosition[ 1 ][ 1 ]; + normvec.Z = missilepo->ObjPosition[ 2 ][ 1 ]; + if ( DOT_PRODUCT( &dirvec, &normvec ) <= 0 ) { + ObjRotX( missilepo->ObjPosition, tmissilepo->MaxRotation ); + } else { + ObjRotX( missilepo->ObjPosition, -tmissilepo->MaxRotation ); + } + + normvec.X = missilepo->ObjPosition[ 0 ][ 0 ]; + normvec.Y = missilepo->ObjPosition[ 1 ][ 0 ]; + normvec.Z = missilepo->ObjPosition[ 2 ][ 0 ]; + if ( DOT_PRODUCT( &dirvec, &normvec ) <= 0 ) { + ObjRotY( missilepo->ObjPosition, -tmissilepo->MaxRotation ); + } else { + ObjRotY( missilepo->ObjPosition, tmissilepo->MaxRotation ); + } + } + + DirVctMUL( missilepo->ObjPosition, FIXED_TO_GEOMV( missilepo->Speed ), &missilepo->DirectionVec ); + } + + break; + } + } + } + + precnode = walkshots; + walkshots = walkshots->NextObj; + } +} + +// walk the list of custom objects and handle timeout for the type specified. +void G_Main::_WalkCustomObjects() +{ + ASSERT( TheWorld->m_CustmObjects != NULL ); + + CustomObject *precnode = TheWorld->m_CustmObjects; + CustomObject *walkobjs = (CustomObject *)TheWorld->m_CustmObjects->NextObj; + + // walk all lasers + while ( walkobjs != NULL ) { + if ((walkobjs->ObjectType == emp_type_id[ 0 ]) || + (walkobjs->ObjectType == emp_type_id[ 1 ]) || + (walkobjs->ObjectType == emp_type_id[ 2 ])) { + + Emp *tmpemp = (Emp *)walkobjs; + if(!EmpAnimate(tmpemp)) { + // delete the EMP object + DBGTXT(MSGOUT("Deleting EMP OBJ");); + precnode->NextObj = (GenObject *)walkobjs->NextObj; + TheWorld->FreeObjectMem( walkobjs ); + walkobjs = (CustomObject *)precnode->NextObj; + continue; + } + } + + precnode = walkobjs; + walkobjs = (CustomObject *)walkobjs->NextObj; + } +} +// walk list of laser objects and advance them ( also handle timeout ) -------- +// +void G_Main::_WalkLaserObjects() +{ + ASSERT( TheWorld->m_LaserObjects != NULL ); + + GenObject *precnode = TheWorld->m_LaserObjects; + GenObject *walkshots = TheWorld->m_LaserObjects->NextObj; + + // walk all lasers + while ( walkshots != NULL ) { + + ASSERT( OBJECT_TYPE_LASER( walkshots ) ); + + LaserObject *laserpo = (LaserObject *) walkshots; + laserpo->LifeTimeCount -= TheSimulator->GetThisFrameRefFrames(); + + if ( laserpo->LifeTimeCount <= 0 ) { + +#ifndef DONT_RESET_SHOTCOUNTER + TheWorld->DecreaseShotCounter(); +#endif // DONT_RESET_SHOTCOUNTER + + // release the E_Distributable ( distribute removal ) + // MSGOUT( "G_Main::_WalkLaserObjects() calls ReleaseDistributable()" ); + TheSimNetOutput->ReleaseDistributable( laserpo->pDist ); + + ASSERT( walkshots != NULL ); + precnode->NextObj = walkshots->NextObj; + TheWorld->FreeObjectMem( walkshots ); + walkshots = precnode->NextObj; + + continue; + + } else { + + //FIXME: move to OBJ_AnimateLaser() + Vector3 tempspeed; + + tempspeed.X = laserpo->DirectionVec.X * TheSimulator->GetThisFrameRefFrames(); + tempspeed.Y = laserpo->DirectionVec.Y * TheSimulator->GetThisFrameRefFrames(); + tempspeed.Z = laserpo->DirectionVec.Z * TheSimulator->GetThisFrameRefFrames(); + + laserpo->PrevPosition.X = laserpo->ObjPosition[ 0 ][ 3 ]; + laserpo->PrevPosition.Y = laserpo->ObjPosition[ 1 ][ 3 ]; + laserpo->PrevPosition.Z = laserpo->ObjPosition[ 2 ][ 3 ]; + + laserpo->ObjPosition[ 0 ][ 3 ] += tempspeed.X; + laserpo->ObjPosition[ 1 ][ 3 ] += tempspeed.Y; + laserpo->ObjPosition[ 2 ][ 3 ] += tempspeed.Z; + } + + precnode = walkshots; + walkshots = walkshots->NextObj; + } +} + +// create actual laser object ------------------------------------------------- +// +LaserObject* G_Main::OBJ_CreateLaserObject( ShipObject *pShip, int curlevel, int barrel, int nClientID ) +{ + ASSERT( pShip != NULL ); + ASSERT( ( curlevel >= 0 ) && ( curlevel < 4 ) ); + ASSERT( ( barrel >= 0 ) && ( barrel < 4 ) ); + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + + dword laserclass = pShip->Laser1_Class[ curlevel ][ barrel ]; + + // create launch matrix + Xmatrx startm; + MakeIdMatrx( startm ); + startm[ 0 ][ 3 ] = pShip->Laser1_X[ curlevel ][ barrel ]; + startm[ 1 ][ 3 ] = pShip->Laser1_Y[ curlevel ][ barrel ]; + startm[ 2 ][ 3 ] = pShip->Laser1_Z[ curlevel ][ barrel ]; + + // create laser object and calculate position and direction vector + MtxMtxMUL( pShip->ObjPosition, startm, DestXmatrx ); + LaserObject *laserpo = (LaserObject *) TheWorld->CreateObject( laserclass, DestXmatrx, nClientID ); + ASSERT( laserpo != NULL ); + laserpo->Speed += pShip->CurSpeed; + DirVctMUL( laserpo->ObjPosition, FIXED_TO_GEOMV( laserpo->Speed ), &laserpo->DirectionVec ); + laserpo->Owner = nClientID; + + TheWorld->IncreaseShotCounter(); + + //FIXME: should the creation/deletion of distributables move into E_World::CreateObject() ? + // attach the created E_Distributable for the engine object + laserpo->pDist = TheSimNetOutput->CreateDistributable( laserpo ); + + // record create event if recording active + //Record_LaserCreation( laserpo ); + + return laserpo; +} +MissileObject* G_Main::OBJ_CreateMissileObject( ShipObject *pShip, int barrel, int nClientID ) +{ + ASSERT( pShip != NULL ); + + ASSERT( ( barrel >= 0 ) && ( barrel < 4 ) ); + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + + int missileclass = pShip->Missile1_Class[ barrel ]; + + // create launch matrix + Xmatrx startm; + MakeIdMatrx( startm ); + startm[ 0 ][ 3 ] = pShip->Missile1_X[ barrel ]; + startm[ 1 ][ 3 ] = pShip->Missile1_Y[ barrel ]; + startm[ 2 ][ 3 ] = pShip->Missile1_Z[ barrel ]; + + // create missile object and calculate position and direction vector + MtxMtxMUL( pShip->ObjPosition, startm, DestXmatrx ); + MissileObject *missilepo = (MissileObject *) TheWorld->CreateObject( missileclass, DestXmatrx, nClientID ); + ASSERT( missilepo != NULL ); + missilepo->Speed += pShip->CurSpeed; + DirVctMUL( missilepo->ObjPosition, FIXED_TO_GEOMV( missilepo->Speed ), &missilepo->DirectionVec ); + missilepo->Owner = nClientID; + + //FIXME: should the creation/deletion of distributables move into E_World::CreateObject() ? + // attach the created E_Distributable for the engine object + missilepo->pDist = TheSimNetOutput->CreateDistributable( missilepo ); + + return missilepo; +} + +MissileObject* G_Main::OBJ_CreateHomingMissileObject( ShipObject *pShip, int barrel, int nClientID, dword targetid ) +{ + + ASSERT( pShip != NULL ); + + ASSERT( ( barrel >= 0 ) && ( barrel < 4 ) ); + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + + // create launch matrix + Xmatrx startm; + MakeIdMatrx( startm ); + startm[ 0 ][ 3 ] = pShip->Missile2_X[ barrel ]; + startm[ 1 ][ 3 ] = pShip->Missile2_Y[ barrel ]; + startm[ 2 ][ 3 ] = pShip->Missile2_Z[ barrel ]; + + // create missile object and calculate position and direction vector + MtxMtxMUL( pShip->ObjPosition, startm, DestXmatrx ); + TargetMissileObject *missilepo = (TargetMissileObject *) TheWorld->CreateObject( GUIDE_CLASS_1, DestXmatrx, nClientID ); + ASSERT( missilepo != NULL ); + missilepo->Speed += pShip->CurSpeed; + DirVctMUL( missilepo->ObjPosition, FIXED_TO_GEOMV( missilepo->Speed ), &missilepo->DirectionVec ); + missilepo->Owner = nClientID; + missilepo->TargetObjNumber = targetid; + + //FIXME: should the creation/deletion of distributables move into E_World::CreateObject() ? + // attach the created E_Distributable for the engine object + missilepo->pDist = TheSimNetOutput->CreateDistributable( missilepo ); + + return missilepo; +} + +MineObject * G_Main::OBJ_CreateMineObject( ShipObject *pShip, int nClientID ) +{ + // create launch matrix + Xmatrx startm; + MakeIdMatrx( startm ); + +//#ifdef SHIPBOUNDED_MINE_PLACEMENT + startm[ 0 ][ 3 ] = GEOMV_0; + startm[ 1 ][ 3 ] = GEOMV_0; + startm[ 2 ][ 3 ] = -( pShip->BoundingSphere + GEOMV_1 ); +//#else +// startm[ 0 ][ 3 ] = pShip->Mine1_X; +// startm[ 1 ][ 3 ] = pShip->Mine1_Y; +// startm[ 2 ][ 3 ] = pShip->Mine1_Z; +//#endif + + // create mine object + MtxMtxMUL( pShip->ObjPosition, startm, DestXmatrx ); + MineObject *minepo = (MineObject *) TheWorld->CreateObject( MINE_CLASS_1, DestXmatrx, nClientID ); + ASSERT( minepo != NULL ); + minepo->Owner = nClientID; + + minepo->pDist = TheSimNetOutput->CreateDistributable( minepo); + + return minepo; + +} + +GenObject* G_Main::OBJ_CreateSwarm( ShipObject *pShip, int nClientID, dword targetid ) +{ + dword randseed = SYSs_GetRefFrameCount(); + GenObject *dummyobj = NULL; + Vertex3 origin; + origin.X = pShip->ObjPosition[ 0 ][ 3 ]; + origin.Y = pShip->ObjPosition[ 1 ][ 3 ]; + origin.Z = pShip->ObjPosition[ 2 ][ 3 ]; + + // get pointer to ship of target + ShipObject *targetpo = TheWorld->FetchFirstShip(); + while ( targetpo != NULL && ( targetpo->HostObjNumber != targetid ) ) { + targetpo = (ShipObject *) targetpo->NextObj; + } + + if(targetpo == NULL) { + MSGOUT("G_Main::OBJ_CreateSwarm(): Target %d not found for swarm creation",targetid); + return dummyobj; + } + dummyobj = (GenObject *) TheWorld->SWARM_Init( nClientID, &origin, targetpo, randseed ); + // MSGOUT("G_Main::OBJ_CreateSwarm(): %d fired swarm missiles",nClientID); + + return dummyobj; +} + + +// ---------------------------------------------------------------------------- +// G_Input methods +// ---------------------------------------------------------------------------- + +// activate the selected gun for a client ------------------------------------- +// +void G_Input::ActivateGun( int nClientID, int SelectedGun ) +{ + G_Player* pPlayer = TheGame->GetPlayer( nClientID ); + + // fire selected gun + switch ( SelectedGun ) { + + // laser + case 0: + pPlayer->FireLaser(); + break; + + // helix cannon + case 1: + pPlayer->FireHelix(); + break; + + // lightning device + case 2: + pPlayer->FireLightning(); + break; + + // photon cannon + case 3: + pPlayer->FirePhoton(); + break; + + // emp device + case 4: + //User_FireEmp(); + break; + + default: + ASSERT( 0 ); + } +} + +void G_Input::LaunchMissile( int nClientID, dword targetid, int missileclass ) +{ + + // launch a missle, dumb or homing + G_Player* pPlayer = TheGame->GetPlayer( nClientID ); + + if ( missileclass == GUIDE_CLASS_1 ) { + pPlayer->LaunchHomingMissile(nClientID, targetid ); + } + else{ + pPlayer->LaunchMissile(); + } +} + +void G_Input::LaunchMine( int nClientID ) +{ + G_Player* pPlayer = TheGame->GetPlayer( nClientID ); + pPlayer->LaunchMine(); +} + +void G_Input::LaunchSwarm(int nClientID, dword targetid) +{ + G_Player* pPlayer = TheGame->GetPlayer( nClientID ); + pPlayer->LaunchSwarm(targetid); +} + +void G_Input::CreateEMP(int nClientID, byte UpgradeLevel) +{ + G_Player* pPlayer = TheGame->GetPlayer( nClientID ); + pPlayer->FireEMP(UpgradeLevel); + +} + +// activate the selected gun for a client ------------------------------------- +// +void G_Input::DeactivateGun( int nClientID, int SelectedGun ) +{ + G_Player* pPlayer = TheGame->GetPlayer( nClientID ); + + // Deactivate selected gun + switch ( SelectedGun ) { + + // laser + case 0: + break; + // helix cannon + case 1: + pPlayer->_WFX_DeactivateHelix(); + break; + + // lightning device + case 2: + pPlayer->_WFX_DeactivateLightning(); + break; + + // photon cannon + case 3: + pPlayer->_WFX_DeactivatePhoton(); + break; + + // emp device + case 4: + //User_FireEmp(); + break; + + default: + ASSERT( 0 ); + } +} diff --git a/src/parsec_server/g_player.cpp b/src/parsec_server/g_player.cpp new file mode 100755 index 0000000..0e44274 --- /dev/null +++ b/src/parsec_server/g_player.cpp @@ -0,0 +1,843 @@ +/* + * PARSEC - Player logic - + * + * $Author: uberlinuxguy $ - $Date: 2004/09/15 12:25:45 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2002 + * + * 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 <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.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" +#include "e_defs.h" + +// mathematics header +#include "utl_math.h" + +// utility headers +#include "utl_list.h" + +// local module header +#include "g_player.h" + +// proprietary module headers +#include "con_aux_sv.h" +//#include "g_extra.h" +//#include "net_game_sv.h" +//#include "net_util.h" +//#include "obj_clas.h" +//#include "obj_creg.h" +//#include "obj_name.h" +//#include "od_props.h" +//#include "od_class.h" +#include "e_simplayerinfo.h" +#include "g_main_sv.h" +//#include "g_stgate.h" +//#include "e_connmanager.h" +#include "e_gameserver.h" +#include "e_simulator.h" +//#include "sys_refframe_sv.h" + +#include "g_emp.h" + +float spreadfire_ref_z = 1.0f; + +// reset all game variables --------------------------------------------------- +// +void G_Player::ResetGameVars() +{ + m_nKills = 0; + m_nDeaths = 0; + m_nPoints = 0; + m_nLastUnjoinFlag = USER_EXIT; + m_nLastKiller = KILLERID_UNKNOWN; + + m_FireDisableFrames = 1; + m_MissileDisableFrames = 1; + + m_CurGun = 0; + m_CurLauncher = 0; +} + + +// reset all fields to defaults ( not connected ) ----------------------------- +// +void G_Player::Reset() +{ + ResetGameVars(); + m_nClientID = -1; + m_pSimPlayerInfo = NULL; +} + + +// set the player status to connected ----------------------------------------- +// +void G_Player::Connect( int nClientID ) +{ + Reset(); + m_nClientID = nClientID; + m_pSimPlayerInfo = TheSimulator->GetSimPlayerInfo( nClientID ); +} + + +// set the player status to disconnected -------------------------------------- +// +void G_Player::Disconnect() +{ + Reset(); +} + + +// user fired laser ----------------------------------------------------------- +// +void G_Player::FireLaser() +{ + // check whether laser firing is valid + if ( m_FireDisableFrames > 0 ) { + // MSGOUT( "G_Player::FireLaser(): laser firing disabled: m_FireDisableFrames: %d", m_FireDisableFrames ); + return; + } + // MSGOUT( "G_Player::FireLaser(): laser firing OK: m_FireDisableFrames: %d", m_FireDisableFrames ); + + // create laser + _OBJ_ShootLaser(); + + ShipObject* pShip = m_pSimPlayerInfo->GetShipObject(); + if ( ( m_FireDisableFrames += pShip->FireDisableDelay ) <= 0 ) { + m_FireDisableFrames = 1; + } +} + +//user fired helix cannon + +void G_Player::FireHelix() +{ + ShipObject* pShip = m_pSimPlayerInfo->GetShipObject(); + if ( ( pShip->WeaponsActive & WPMASK_CANNON_HELIX ) == 0 ) { + _WFX_ActivateHelix(); + } +} + + +// user fired lightning device ------------------------------------------------ +// +void G_Player::FireLightning() +{ + ShipObject* pShip = m_pSimPlayerInfo->GetShipObject(); + if ( ( pShip->WeaponsActive & WPMASK_CANNON_LIGHTNING ) == 0 ) { + _WFX_ActivateLightning(); + } +} + + +// user fired photon cannon --------------------------------------------------- +// +void G_Player::FirePhoton() +{ + ShipObject* pShip = m_pSimPlayerInfo->GetShipObject(); + if ( (pShip->WeaponsActive & WPMASK_CANNON_PHOTON ) == 0 ) { + _WFX_ActivatePhoton(); + } +} + +// user launched dumb missle ----------------------------------------------------------- +// +void G_Player::LaunchMissile() +{ + // check whether missle launching is valid + if ( m_MissileDisableFrames > 0 ) { + // MSGOUT( "G_Player::LaunchMissle(): missile launching disabled: m_MissileDisableFrames: %d", m_MissileDisableFrames ); + return; + } + // MSGOUT( "G_Player::LaunchMissile(): Missile Launching OK: m_MissileDisableFrames: %d", m_MissileDisableFrames ); + + // create missle + _OBJ_LaunchMissile(); + + ShipObject* pShip = m_pSimPlayerInfo->GetShipObject(); + if ( ( m_MissileDisableFrames += pShip->MissileDisableDelay ) <= 0 ) { + m_MissileDisableFrames = 1; + } +} +// user launched homing missle ----------------------------------------------------------- +// +void G_Player::LaunchHomingMissile(dword launcher, dword targetid) +{ + + // check whether missle launching is valid + if ( m_MissileDisableFrames > 0 ) { + // MSGOUT( "G_Player::LaunchMissle(): missile launching disabled: m_MissileDisableFrames: %d", m_MissileDisableFrames ); + return; + } + //MSGOUT( "G_Player::LaunchHomingMissile(): Missile Launching OK: m_MissileDisableFrames: %d", m_MissileDisableFrames ); + + // create missle + _OBJ_LaunchHomingMissile(launcher, targetid); + + ShipObject* pShip = m_pSimPlayerInfo->GetShipObject(); + if ( ( m_MissileDisableFrames += pShip->MissileDisableDelay ) <= 0 ) { + m_MissileDisableFrames = 1; + } +} + +void G_Player::LaunchMine() +{ + + //MSGOUT( "G_Player::LaunchMine(); Mine Launch Ok"); + + // create the mine object + _OBJ_LaunchMine(); +} + +void G_Player::LaunchSwarm( dword targetid ) +{ + // check whether missle launching is valid + if ( m_MissileDisableFrames > 0 ) { + //MSGOUT( "G_Player::LaunchSwarm(): Swarm missile launching disabled: m_MissileDisableFrames: %d", m_MissileDisableFrames ); + return; + } + //MSGOUT( "G_Player::LaunchSwarm(): Swarm Missile Launching OK: m_MissileDisableFrames: %d", m_MissileDisableFrames ); + + // create missle + _OBJ_LaunchSwarm( targetid ); + + ShipObject* pShip = m_pSimPlayerInfo->GetShipObject(); + if ( ( m_MissileDisableFrames += pShip->MissileDisableDelay ) <= 0 ) { + m_MissileDisableFrames = 1; + } +} + +// create laser originating from specified ship ------------------------------- +// +void G_Player::_OBJ_ShootLaser() +{ + //NOTE: based on OBJ_GAME::OBJ_ShootLaser + + ASSERT( m_pSimPlayerInfo != NULL ); + ShipObject* pShip = m_pSimPlayerInfo->GetShipObject(); + ASSERT( pShip != NULL ); + + //NOTE: + // C: this function is called by INP_USER::User_CheckGunFire(). + // S: this function is called by G_Input::User_FireLaser + + //FIXME: GAMEVAR ( property of ship ) + #define MIN_LASER_ENERGY 20 + +#ifdef PARSEC_SERVER + + // check for availability + if ( !TheGame->OBJ_DeviceAvailable( pShip, WPMASK_CANNON_LASER ) ) { + return; + } + +#else + // check if enough space in RE_List + if ( !NET_RmEvAllowed( RE_CREATELASER ) ) + return; + + // check for availability + if ( !OBJ_DeviceAvailable( pShip, WPMASK_CANNON_LASER ) ) { + if ( pShip == MyShip ) { + ShowMessage( no_standard_str ); + } + return; + } +#endif // PARSEC_SERVER + + // determine current laser upgrade level + int curlevel = 0; + if ( pShip->Specials & SPMASK_LASER_UPGRADE_2 ) { + curlevel = 2; + } else if ( pShip->Specials & SPMASK_LASER_UPGRADE_1 ) { + curlevel = 1; + } + + // sequence of gun (laser) barrels ---------------------------------------- + // + #define MAX_GUN_BARRELS 4 + static int gun_barrels_sequence[ MAX_GUN_BARRELS ] = { 0, 3, 1, 2 }; + + int barrel = gun_barrels_sequence[ m_CurGun % MAX_GUN_BARRELS ]; + dword laserclass = pShip->Laser1_Class[ curlevel ][ barrel ]; + + if ( !SV_CHEAT_ENERGY_CHECKS ) { + + // check if enough energy to shoot laser + //FIXME: TheWorld->ObjClasses ?????????????? + int energyneeded = ( (LaserObject*)TheWorld->ObjClasses[ laserclass ] )->EnergyNeeded; + if ( ( pShip->CurEnergy - energyneeded ) < MIN_LASER_ENERGY ) { + +#ifdef PARSEC_SERVER + //MSGOUT( "G_Player::_OBJ_ShootLaser(): client %d low energy", m_nClientID ); +#else // !PARSEC_SERVER + if ( pShip == MyShip ) { + ShowMessage( low_energy_str ); + AUD_LowEnergy(); + } +#endif // !PARSEC_SERVER + + return; + } + pShip->CurEnergy -= energyneeded; + //MSGOUT( "G_Player::_OBJ_ShootLaser(): client %d energy after shot: %d", m_nClientID, pShip->CurEnergy ); + } + + // create actual laser object(s) + TheGame->OBJ_CreateLaserObject( pShip, curlevel, barrel, m_nClientID ); + m_CurGun++; + + if ( curlevel == 2 ) { + barrel = gun_barrels_sequence[ m_CurGun % MAX_GUN_BARRELS ]; + TheGame->OBJ_CreateLaserObject( pShip, curlevel, barrel, m_nClientID ); + m_CurGun++; + } + +#ifdef PARSEC_CLIENT + // play sound effect + AUD_Laser( laserpo ); +#endif // PARSEC_CLIENT +} + +// helix props ---------------------------------------------------------------- +// +bams_t HelixBamsInc = 0x1500; +int HelixRefFrames = 10; // create one particle every ... frames +geomv_t HelixRadius = INT_TO_GEOMV( 10 ); + + +void G_Player::_WFX_ActivateHelix() +{ + ASSERT( m_pSimPlayerInfo != NULL ); + ShipObject* pShip = m_pSimPlayerInfo->GetShipObject(); + ASSERT( pShip != NULL ); + + // check if helix available + if ( !TheGame->OBJ_DeviceAvailable( pShip, WPMASK_CANNON_HELIX ) ) { + MSGOUT("G_PLAYER::_WFX_ActivateHelix(): client %d trying to fire Helix cannon illegally",m_nClientID); + return; + } + + // check if enough energy to shoot helix + if ( pShip->CurEnergy < MIN_HELIX_ENERGY + HELIX_ENERGY_CONSUMPTION ) { + //MSGOUT("G_PLAYER::_WFX_ActivateHelix(): Not enough energy to activate Helix cannon"); + return; + } + + // set active flag + pShip->WeaponsActive |= WPMASK_CANNON_HELIX; + // MSGOUT("G_PLAYER::_WFX_ActivateHelix(): client %d fired",m_nClientID); + //Create the object/event somehow +} + +// create helix particles for current frame ----------------------------------- +// +int G_Player::_WFX_MaintainHelix( ShipObject *shippo, int playerid ) +{ + ASSERT( shippo != NULL ); + + //int bitmap = SPREADFIRE_BM_INDX; + int color = SPREADFIRE_PARTICLE_COLOR; + float ref_z = spreadfire_ref_z; + int sizebound = partbitmap_size_bound; + int lifetime = HELIX_LIFETIME; + + pextinfo_s *pextinfo = NULL; + + // calc number of particles according to time passed (remember remainder) + refframe_t numrefframes = TheSimulator->GetThisFrameRefFrames() + shippo->helix_refframes_delta; + int numparticles = numrefframes / HelixRefFrames; + shippo->helix_refframes_delta = numrefframes % HelixRefFrames; + Vector3 dirvec; + fixed_t speed = HELIX_SPEED + shippo->CurSpeed; + DirVctMUL( shippo->ObjPosition, FIXED_TO_GEOMV( speed ), &dirvec ); + + for ( int curp = 0; curp < numparticles; curp++ ) { + + // check if enough energy to shoot helix + if ( shippo->CurEnergy < MIN_HELIX_ENERGY + HELIX_ENERGY_CONSUMPTION ) { + + _WFX_DeactivateHelix(); + + return 0; + } + shippo->CurEnergy -= HELIX_ENERGY_CONSUMPTION; + + sincosval_s resultp; + GetSinCos( shippo->HelixCurBams, &resultp ); + + // create oldest particle first + int invcurp = numparticles - curp - 1; + fixed_t timefrm = invcurp * HelixRefFrames + helix_refframes_delta; + fixed_t timepos = timefrm * shippo->HelixSpeed; + + // create one full frame set back because the current frame will + // be added by the linear particle animation code in the same frame + timepos -= speed * TheSimulator->GetThisFrameRefFrames(); + + Vector3 pofsvec; + pofsvec.X = GEOMV_MUL( HelixRadius, resultp.sinval ); + pofsvec.Y = GEOMV_MUL( HelixRadius, resultp.cosval ); + pofsvec.Z = FIXED_TO_GEOMV( timepos ); + + // first particle --- + Vertex3 object_space; + object_space.X = shippo->Helix_X + pofsvec.X; + object_space.Y = shippo->Helix_Y + pofsvec.Y; + object_space.Z = shippo->Helix_Z + pofsvec.Z; + + Vertex3 world_space; + MtxVctMUL( shippo->ObjPosition, &object_space, &world_space ); + + particle_s particle; + TheWorld->PRT_InitParticle( particle, color, sizebound, + ref_z, &world_space, &dirvec, + lifetime, playerid, pextinfo ); + particle.flags |= PARTICLE_COLLISION; + TheWorld->PRT_CreateLinearParticle( particle ); + + // second particle --- + object_space.X = shippo->Helix_X - pofsvec.X; +#ifdef SECOND_HELIX + object_space.Y = shippo->Helix_Y - pofsvec.Y; +#else + object_space.Y = shippo->Helix_Y + pofsvec.Y; +#endif + MtxVctMUL( shippo->ObjPosition, &object_space, &world_space ); + + TheWorld->PRT_InitParticle( particle, color, sizebound, + ref_z, &world_space, &dirvec, + lifetime, playerid, pextinfo ); + particle.flags |= PARTICLE_COLLISION; + particle.flags |= PARTICLE_IS_HELIX; + TheWorld->PRT_CreateLinearParticle( particle ); + + shippo->HelixCurBams = ( shippo->HelixCurBams + HelixBamsInc ) & 0xffff; + } + + return 1; +} + +void G_Player::_WFX_DeactivateHelix() +{ + ASSERT( m_pSimPlayerInfo != NULL ); + ShipObject* pShip = m_pSimPlayerInfo->GetShipObject(); + ASSERT( pShip != NULL ); + + // make sure helix cannon is inactive + if ( pShip->WeaponsActive & WPMASK_CANNON_HELIX ) { + + // reset activation flag + pShip->WeaponsActive &= ~WPMASK_CANNON_HELIX; + //MSGOUT("G_PLAYER::_WFX_DeactivateHelix(): client %d stopped firing",m_nClientID); + + } +} + +// ensure that helix cannon is inactive for local ship ------------------------ +// +void G_Player::WFX_EnsureHelixInactive( ShipObject *shippo ) +{ + ASSERT( shippo != NULL ); + + if ( shippo->WeaponsActive & WPMASK_CANNON_HELIX ) { + shippo->WeaponsActive &= ~WPMASK_CANNON_HELIX; + } + + ASSERT( ( shippo->WeaponsActive & WPMASK_CANNON_HELIX ) == 0 ); +} + + +// remote player activated lightning device ----------------------------------- +// +void G_Player::_WFX_ActivateLightning() +{ + ASSERT( m_pSimPlayerInfo != NULL ); + ShipObject* pShip = m_pSimPlayerInfo->GetShipObject(); + ASSERT( pShip != NULL ); + + + if ( !TheGame->OBJ_DeviceAvailable( pShip, WPMASK_CANNON_LIGHTNING ) ) { + MSGOUT("G_PLAYER::_WFX_ActivateLightning(): client %d trying to fire Lightning cannon illegally",m_nClientID); + return; + } + dword energy_consumption = pShip->CurEnergyFrac + ( TheSimulator->GetThisFrameRefFrames() * LIGHTNING_ENERGY_CONSUMPTION ); + + // check if enough energy to shoot lightning + if ( (dword)pShip->CurEnergy < ( MIN_LIGHTNING_ENERGY + ( energy_consumption >> 16 ) ) ) { + MSGOUT("G_PLAYER::_WFX_ActivateLightning(): client %d low energy",m_nClientID); + return; + } + + + // make sure lightning device is active + if ( ( pShip->WeaponsActive & WPMASK_CANNON_LIGHTNING ) == 0 ) { + + if ( TheWorld->CreateLightningParticles( pShip, m_nClientID ) != NULL ) { + pShip->WeaponsActive |= WPMASK_CANNON_LIGHTNING; + MSGOUT("G_PLAYER::_WFX_ActivateLightning(): client %d fired",m_nClientID); + } + } +} + + +// remote player deactivated lightning device --------------------------------- +// +void G_Player::_WFX_DeactivateLightning() +{ + ASSERT( m_pSimPlayerInfo != NULL ); + ShipObject* pShip = m_pSimPlayerInfo->GetShipObject(); + ASSERT( pShip != NULL ); + + // make sure lightning device is inactive + if ( ( pShip->WeaponsActive & WPMASK_CANNON_LIGHTNING ) != 0 ) { + // remove lightning particles + TheWorld->PRT_DeleteAttachedClustersOfType( pShip, SAT_LIGHTNING ); + // reset activation flag + pShip->WeaponsActive &= ~WPMASK_CANNON_LIGHTNING; + MSGOUT("G_PLAYER::_WFX_DeactivateLightning(): client %d fired",m_nClientID); + //Remote event goes here + } +} + +// ensure that lightning is inactive for local ship --------------------------- +// +void G_Player::WFX_EnsureLightningInactive( ShipObject *shippo ) +{ + ASSERT( shippo != NULL ); + + if ( TheWorld->PRT_DeleteAttachedClustersOfType( shippo, SAT_LIGHTNING ) != 0 ) { + shippo->WeaponsActive &= ~WPMASK_CANNON_LIGHTNING; + } + + ASSERT( ( shippo->WeaponsActive & WPMASK_CANNON_LIGHTNING ) == 0 ); +} + +// check whether to turn off lightning due to too little energy --------------- +// +void G_Player::WFX_MaintainLightning( ShipObject *shippo) +{ + ASSERT( shippo != NULL ); + ASSERT( shippo->WeaponsActive & WPMASK_CANNON_LIGHTNING ); + + dword energy_consumption = shippo->CurEnergyFrac + + ( TheSimulator->GetThisFrameRefFrames() * LIGHTNING_ENERGY_CONSUMPTION ); + + // check if enough energy to shoot lightning + if ( (dword)shippo->CurEnergy < ( MIN_LIGHTNING_ENERGY + ( energy_consumption >> 16 ) ) ) { + _WFX_DeactivateLightning(); + } else { + + shippo->CurEnergyFrac = ( energy_consumption & 0xffff ); + shippo->CurEnergy -= ( energy_consumption >> 16 ); + } +} + +// activate photon cannon of local ship ---------------------------------------- +// +void G_Player::_WFX_ActivatePhoton() +{ + ASSERT( m_pSimPlayerInfo != NULL ); + ShipObject* pShip = m_pSimPlayerInfo->GetShipObject(); + ASSERT( pShip != NULL ); + + // check if photon available + if ( !TheGame->OBJ_DeviceAvailable( pShip, WPMASK_CANNON_PHOTON ) ) { + //MSGOUT("G_Player::_WFX_ActivatePhoton: client %d illigally tried to fire Photon Cannon\n",m_nClientID); + return; + } + + // check if enough energy to shoot photon + if ( pShip->CurEnergy < MIN_PHOTON_ENERGY ) { + MSGOUT("G_Player::_WFX_ActivatePhoton: client %d low energy",m_nClientID); + return; + } + + // check if photon cannon still firing + if ( TheWorld->PRT_ObjectHasAttachedClustersOfType( pShip, SAT_PHOTON ) ) { + return; + } + + //Not sure yet if I even need to simulate this part below + // create particle sphere + if ( !TheWorld->CreatePhotonSphere( pShip ) ) { + return; + } + + // set active flag + pShip->WeaponsActive |= WPMASK_CANNON_PHOTON; + MSGOUT("G_Player::_WFX_ActivatePhoton: client %d charging photon cannon",m_nClientID); + +} + +// deactivate photon cannon of specified ship ---------------------------------- +// +void G_Player::_WFX_DeactivatePhoton() +{ + // anim type for photon particles (no "real" sphere animtype) + #define SAT_PHOTON 0x00000008 + + ASSERT( m_pSimPlayerInfo != NULL ); + ShipObject* pShip = m_pSimPlayerInfo->GetShipObject(); + ASSERT( pShip != NULL ); + + // send remote event to switch photon off + + // reset activation flag + pShip->WeaponsActive &= ~WPMASK_CANNON_PHOTON; + MSGOUT("G_Player::_WFX_DectivatePhoton: client %d fired photon cannon",m_nClientID); + + // start firing + photon_sphere_pcluster_s* cluster = (photon_sphere_pcluster_s *) TheWorld->PRT_ObjectHasAttachedClustersOfType( pShip, SAT_PHOTON ); + if(cluster != NULL) + cluster->firing = TRUE; + +} + +// ensure that photon cannon is inactive for local ship ----------------------- +// +void G_Player::WFX_EnsurePhotonInactive( ShipObject *shippo ) +{ + ASSERT( shippo != NULL ); + + if ( shippo->WeaponsActive & WPMASK_CANNON_PHOTON ) { + shippo->WeaponsActive &= ~WPMASK_CANNON_PHOTON; + + } + + ASSERT( ( shippo->WeaponsActive & WPMASK_CANNON_PHOTON ) == 0 ); +} + +// ensure that no particle weapons are active for local ship ------------------ +// +void G_Player::WFX_EnsureParticleWeaponsInactive( ShipObject *shippo ) +{ + ASSERT( shippo != NULL ); + + // ensure lightning and helix are destroyed + WFX_EnsureLightningInactive( shippo ); + WFX_EnsureHelixInactive( shippo ); + WFX_EnsurePhotonInactive(shippo); +} + +// create missile originating from specified position ------------------------- +// +void G_Player::_OBJ_LaunchMissile() +{ + ASSERT( m_pSimPlayerInfo != NULL ); + ShipObject* pShip = m_pSimPlayerInfo->GetShipObject(); + ASSERT( pShip != NULL ); + + //NOTE: based on OBJ_GAME::OBJ_LaunchMissle + + + // check ammo + if ( pShip->NumMissls <= 0 ) { + return; + } + pShip->NumMissls--; + + + #define MAX_MISSILE_BARRELS_MASK 4 + static int missile_barrel_sequence[ MAX_MISSILE_BARRELS_MASK ] = { 0, 3, 1, 2 }; + + // select correct barrel + int barrel = missile_barrel_sequence[ m_CurLauncher%MAX_MISSILE_BARRELS_MASK ]; + + TheGame->OBJ_CreateMissileObject( pShip, barrel, m_nClientID ); + m_CurLauncher++; +#ifdef PARSEC_CLIENT + // insert remote event + NET_RmEvMissile( missilepo, 0 ); + + // record create event if recording active + Record_MissileCreation( missilepo ); + + // play sound effect + AUD_Missile( missilepo ); +#endif +} + + +// create homing missile originating from specified position ------------------ +// +void G_Player::_OBJ_LaunchHomingMissile( dword launcher, dword targetid ) +{ + ASSERT( m_pSimPlayerInfo != NULL ); + ShipObject* pShip = m_pSimPlayerInfo->GetShipObject(); + ASSERT( pShip != NULL ); + + + // check ammo + if ( pShip->NumHomMissls <= 0 ) { + return; + } + + + /* check if any target locked + if (!targetid ) { + MSGOUT("NO TARGET Targetid: %d", targetid); //This is server side, to be here a client has already fired at a target + return; + }*/ + pShip->NumHomMissls--; + + + #define MAX_MISSILE_BARRELS_MASK 4 + static int missile_barrel_sequence[ MAX_MISSILE_BARRELS_MASK ] = { 0, 3, 1, 2 }; + + // select correct barrel + int barrel = missile_barrel_sequence[ m_CurLauncher%MAX_MISSILE_BARRELS_MASK ]; + + TheGame->OBJ_CreateHomingMissileObject( pShip, barrel, m_nClientID, targetid ); + m_CurLauncher++; + + + +} + + +// create mine object --------------------------------------------------------- +// +void G_Player::_OBJ_LaunchMine() +{ + ASSERT( m_pSimPlayerInfo != NULL ); + ShipObject* pShip = m_pSimPlayerInfo->GetShipObject(); + ASSERT( pShip != NULL ); + + // check ammo + if ( pShip->NumMines <= 0 ) { + return; + } + pShip->NumMines--; + TheGame->OBJ_CreateMineObject( pShip, m_nClientID ); + + +} + + +// create swarm missiles ------------------------------------------------------ +// +void G_Player::_OBJ_LaunchSwarm( dword targetid ) +{ + + ASSERT( m_pSimPlayerInfo != NULL ); + ShipObject* pShip = m_pSimPlayerInfo->GetShipObject(); + ASSERT( pShip != NULL ); + + // check ammo + if ( pShip->NumPartMissls <= 0 ) { + return; + } + + TheGame->OBJ_CreateSwarm(pShip, m_nClientID, targetid); + + pShip->NumPartMissls--; + +} + +void G_Player::FireEMP(byte Upgradelevel) { + ASSERT( m_pSimPlayerInfo != NULL ); + ShipObject* pShip = m_pSimPlayerInfo->GetShipObject(); + ASSERT( pShip != NULL ); + int curdelay = 0; + int energy_consumption = emp_energy[ Upgradelevel ] * emp_waves[ Upgradelevel ]; + + // check if enough energy to shoot emp + if ( pShip->CurEnergy >= energy_consumption ) { + + pShip->CurEnergy -= energy_consumption; + + for ( int i = 0; i < emp_waves[ Upgradelevel ]; i++ ) { + CreateEmp( pShip, curdelay, 0, Upgradelevel, m_nClientID ); + curdelay += emp_delay[ Upgradelevel ]; + } + } +} + +// record a kill -------------------------------------------------------------- +// +void G_Player::RecordKill() +{ + if ( m_pSimPlayerInfo->IsPlayerJoined() ) { + m_nKills++; + } +} + + +// record a death ------------------------------------------------------------- +// +void G_Player::RecordDeath( int nClientID_Killer ) +{ + ASSERT( ( nClientID_Killer >= 0 ) && ( nClientID_Killer < MAX_NUM_CLIENTS ) ); + if ( m_pSimPlayerInfo->IsPlayerJoined() ) { + m_nDeaths++; + m_nLastUnjoinFlag = SHIP_DOWNED; + m_nLastKiller = nClientID_Killer; + WFX_EnsureParticleWeaponsInactive( m_pSimPlayerInfo->GetShipObject() ); + } +} + + +// reset any death info for the player ---------------------------------------- +// +void G_Player::ResetDeathInfo() +{ + m_nLastUnjoinFlag = USER_EXIT; + m_nLastKiller = KILLERID_UNKNOWN; +} + + +// return the ship object assigned to the player ------------------------------ +// +ShipObject* G_Player::GetShipObject() +{ + ASSERT( m_pSimPlayerInfo != NULL ); + return m_pSimPlayerInfo->GetShipObject(); +} + +// maintain weapon firing delays ---------------------------------------------- +// +void G_Player::MaintainWeaponDelays() +{ + // count down fire disable delay + if ( m_FireDisableFrames > 0 ) { + m_FireDisableFrames -= TheSimulator->GetThisFrameRefFrames(); + } + if ( m_MissileDisableFrames > 0 ) { + m_MissileDisableFrames -= TheSimulator->GetThisFrameRefFrames(); + } +} + + diff --git a/src/parsec_server/g_stgate_sv.cpp b/src/parsec_server/g_stgate_sv.cpp new file mode 100755 index 0000000..afdc0db --- /dev/null +++ b/src/parsec_server/g_stgate_sv.cpp @@ -0,0 +1,672 @@ +/* + * PARSEC - Stargate Model + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:47 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 1999-2002 + * Copyright (c) Markus Hadwiger <msh@parsec.org> 1999-2000 + * Copyright (c) Andreas Varga <sid@parsec.org> 2000 + * + * 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 <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.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" +#include "e_defs.h" +#include "sys_defs.h" + +// subsystem linkage info +#include "linkinfo.h" + +// UNP header +#include "net_wrap.h" + +// mathematics header +#include "utl_math.h" + +// model header +//#include "utl_model.h" + +// local module header +#include "g_stgate.h" + +// proprietary module headers +#include "con_info_sv.h" +#include "net_udpdriver.h" +#include "net_util.h" +#include "obj_clas.h" +#include "obj_creg.h" +#include "obj_cust.h" +#include "e_simulator.h" + +// flags + + + +// generic string paste area -------------------------------------------------- +// +#define PASTE_STR_LEN 255 +static char paste_str[ PASTE_STR_LEN + 1 ]; + + +// stargate limits and constants ---------------------------------------------- +// +#define STARGATE_RADIUS 50.0f +#define STARGATE_NUM_PLATES 12 // number of plates the stargate consists of +#define STARGATE_NUM_FLARES STARGATE_NUM_PLATES // number of flares attached to the stargate +#define STARGATE_INTERIOR_NUM_RINGS 15 +#define STARGATE_INTERIOR_VERTS_PER_RING ( STARGATE_NUM_PLATES * 2 ) +#define STARGATE_INTERIOR_NUM_VERTS ( ( STARGATE_INTERIOR_VERTS_PER_RING * STARGATE_INTERIOR_NUM_RINGS ) + 1 ) +#define STARGATE_INTERIOR_RADIUS ( STARGATE_RADIUS * 0.98f ) + +#define STARGATE_RING_NUM_PARTS STARGATE_NUM_PLATES +#define STARGATE_PART_RADIUS STARGATE_RADIUS +#define STARGATE_RADIUS_EXPANSION 1.0f +#define STARGATE_NUM_RINGS 20 +#define STARGATE_RING_PART_ZSPACING 10.0f +#define STARGATE_PART_ZOFFSET INT_TO_GEOMV( -2 ) + +#define STARGATE_INTERIOR_TEXTURE_NAME "sg_int.3df" +#define STARGATE_FLARE_PDEF_NAME "sgateposlt" + + +#if ( STARGATE_INTERIOR_NUM_RINGS > 32 ) + #error "too many rings in stargate interior" +#endif + + +// module local functions ----------------------------------------------------- +// +PRIVATE int StargateInstantiate_InitInteriorVertices( Stargate *stargate ); + +PRIVATE int StargateModify_RealizeNode( GenObject* base ); +PRIVATE int StargateModify_ActiveChanged( GenObject* base ); +PRIVATE int StargateModify_AutoactivateChanged( GenObject* base ); +PRIVATE void StargateModify_CreateActiveParticles( CustomObject *base ); + + +// offset definitions into the Stargate --------------------------------------- +// +#define OFS_ROTSPEED offsetof( Stargate, rotspeed ) +#define OFS_RADIUS offsetof( Stargate, radius ) +#define OFS_DESTNAME offsetof( Stargate, destination_name ) +#define OFS_DESTIP offsetof( Stargate, destination_ip ) +#define OFS_DESTPORT offsetof( Stargate, destination_port ) +#define OFS_ACTDISTANCE offsetof( Stargate, actdistance ) +#define OFS_DORMANT offsetof( Stargate, dormant ) +#define OFS_ACTIVE offsetof( Stargate, active ) +#define OFS_AUTOACTIVATE offsetof( Stargate, autoactivate ) +#define OFS_NUMPARTACTIVE offsetof( Stargate, numpartactive ) +#define OFS_ACTCYLLEN offsetof( Stargate, actcyllen ) +#define OFS_PARTVEL offsetof( Stargate, partvel ) +#define OFS_MODULSPEED offsetof( Stargate, modulspeed ) +#define OFS_MODULRAD1 offsetof( Stargate, modulrad1 ) +#define OFS_MODULRAD2 offsetof( Stargate, modulrad2 ) +#define OFS_ACTTIME offsetof( Stargate, acttime ) +#define OFS_FLARE_NAME offsetof( Stargate, flare_name ) +#define OFS_INTERIOR_NAME offsetof( Stargate, interior_name ) + + +// list of console-accessible properties -------------------------------------- +// +PRIVATE +proplist_s Stargate_PropList[] = { + + { "rotspeed", OFS_ROTSPEED, 0, 0xffff, PROPTYPE_INT, NULL }, + { "radius", OFS_RADIUS, 0x10000, 0x4000000, PROPTYPE_FLOAT, NULL }, + //{ "destname", OFS_DESTNAME, 0, MAX_SERVER_NAME, PROPTYPE_STRING, NULL }, + //{ "destip", OFS_DESTIP, 0, STARGATE_MAX_DEST_IP, PROPTYPE_STRING, StargateModify_RealizeNode }, + //{ "destport", OFS_DESTPORT, 1024, 65535, PROPTYPE_INT, StargateModify_RealizeNode }, + { "actdistance", OFS_ACTDISTANCE, 0x10000, 0x4000000, PROPTYPE_FLOAT, NULL }, + { "dormant", OFS_DORMANT, 0x0, 0x1, PROPTYPE_INT, NULL }, + { "active", OFS_ACTIVE, 0x0, 0x1, PROPTYPE_INT, StargateModify_ActiveChanged }, + { "autoactivate", OFS_AUTOACTIVATE, 0x0, 0x1, PROPTYPE_INT, StargateModify_AutoactivateChanged }, + { "numpartactive", OFS_NUMPARTACTIVE, 1, 2048, PROPTYPE_INT, NULL }, + { "actcyllen", OFS_ACTCYLLEN, 0, 2048, PROPTYPE_INT, NULL }, + { "partvel", OFS_PARTVEL, 0x10000, 0x4000000, PROPTYPE_FLOAT, NULL }, + { "modulspeed", OFS_MODULSPEED, 0, 0xffff, PROPTYPE_INT, NULL }, + { "modulrad1", OFS_MODULRAD1, 0x10000, 0x4000000, PROPTYPE_FLOAT, NULL }, + { "modulrad2", OFS_MODULRAD2, 0x10000, 0x4000000, PROPTYPE_FLOAT, NULL }, + { "acttime", OFS_ACTTIME, 0, FRAME_MEASURE_TIMEBASE*10, PROPTYPE_INT, NULL }, + { "flare_name", OFS_FLARE_NAME, 0, MAX_TEXNAME, PROPTYPE_STRING, NULL }, + { "interior_name", OFS_INTERIOR_NAME, 0, MAX_TEXNAME, PROPTYPE_STRING, NULL }, + + { NULL, 0, 0, 0, 0, NULL }, +}; + + +// type fields init function for stargate ------------------------------------- +// +PRIVATE +void StargateInitType( CustomObject *base ) +{ + ASSERT( base != NULL ); + + Stargate *stargate = (Stargate *) base; + + stargate->rotspeed = 0x0004; + stargate->radius = STARGATE_PART_RADIUS; + stargate->actdistance = 300; + stargate->dormant = FALSE; + stargate->active = FALSE; + stargate->autoactivate = TRUE; + stargate->numpartactive = 600; + stargate->actcyllen = 300; + stargate->partvel = 150; + stargate->modulspeed = DEG_TO_BAMS( 1.0f ); + stargate->modulate_bams = BAMS_DEG0; + stargate->modulrad1 = 5.0f; + stargate->modulrad2 = 2.0f; + stargate->modulfade = 2.0f; + stargate->acttime = FRAME_MEASURE_TIMEBASE; + + stargate->activating = FALSE; + stargate->curactcyllen = 0; + stargate->manually_activated = FALSE; + stargate->actring = 0.0f; + + strcpy( stargate->flare_name, STARGATE_FLARE_PDEF_NAME ); + strcpy( stargate->interior_name, STARGATE_INTERIOR_TEXTURE_NAME ); + + stargate->pcluster = NULL; + stargate->interior_vtxlist = NULL; + stargate->interior_viewvtxs = NULL; + stargate->interior_u = NULL; + stargate->interior_v = NULL; + stargate->interior_texmap = NULL; + + stargate->ping = -1; + stargate->lastpinged = 0; +} + + +/*// notification callback when eihter IP or port is changed -------------------- +// +PRIVATE +int StargateModify_RealizeNode( GenObject* base ) +{ + ASSERT( base != NULL ); + Stargate* stargate = (Stargate *) base; + + // try to resolve DNS name to IP address (still as string) + char resolved_name[ MAX_IPADDR_LEN + 1 ]; + if ( TheUDPDriver->ResolveHostName( stargate->destination_ip, NULL ) ) { + + // save server address for later use by NET_GMSV.C + inet_aton( resolved_name, &stargate->destination_node ); + NODE_StorePort( &stargate->destination_node, stargate->destination_port ); + + MSGOUT( "stargate->destinaiton %s resolved to %s", stargate->destination_ip, NODE_Print( &stargate->destination_node ) ); + } else { + // DNS lookup failed + return FALSE; + } + + return TRUE; +}*/ + + +// notification callback when "active" changed -------------------------------- +// +PRIVATE +int StargateModify_ActiveChanged( GenObject* base ) +{ + ASSERT( base != NULL ); + Stargate *stargate = (Stargate *) base; + + // manual activation/deactivation only if autoactivate off + if ( stargate->autoactivate ) { + return TRUE; + } + + // prevent manual activation when stargate dormant + if ( stargate->dormant ) { + stargate->active = FALSE; + } + + if ( ( stargate->active ) && ( !stargate->manually_activated ) ) { + stargate->manually_activated = TRUE; + } else { + stargate->manually_activated = FALSE; + } + + return TRUE; +} + + +// notification callback when "autoactivate" changed -------------------------- +// +PRIVATE +int StargateModify_AutoactivateChanged( GenObject* base ) +{ + ASSERT( base != NULL ); + Stargate *stargate = (Stargate *) base; + + // deactivate the stargate when it's active due to a manual activation + if ( stargate->autoactivate && stargate->manually_activated ) { + stargate->active = FALSE; + stargate->manually_activated = FALSE; + } + + return TRUE; +} + +// stargate constructor (class instantiation) --------------------------------- +// +PRIVATE +void StargateInstantiate( CustomObject *base ) +{ + ASSERT( base != NULL ); + Stargate *stargate = (Stargate *) base; +} + +/* + +// ---------------------------------------------------------------------------- +// +#define PING_WAIT_MAX (5 * 600) + +static refframe_t stargate_ping_wait = 0; + + +// determine whether stargate is dormant or not ------------------------------- +// +PRIVATE +void DetermineStargateDormantState( Stargate *stargate ) +{ + ASSERT( stargate != NULL ); + +#ifdef LINKED_PROTOCOL_GAMESERVER + + // only while connected + if ( !NET_ConnectedGMSV() ) { + return; + } + + // re-issue a server ping, once every 5 seconds + if ( ( stargate_ping_wait += CurScreenRefFrames ) > PING_WAIT_MAX ) { + + stargate_ping_wait = 0; + + int ping = -1; + if ( stargate->destination_node.address[ 0 ] != 0 ) { + + NET_ServerPing( &stargate->destination_node, TRUE, FALSE ); + } + + //NOTE: + // ping result is not valid here, since we didn't wait + // for the arrival of the ping packet. The ping time will + // be available once the NET_ProcessPingPacket() function + // has processed the returned packet. + + //NOTE: + // when a ping packet arrives, NET_ProcessPingPacket() + // sets Stargate::dormant, Stargate::ping, Stargate::lastpinged. + + refframe_t curtime = SYSs_GetRefFrameCount(); + + // if we didn't get a pingreply for quite some time, we assume the + // server went down or was quit, so we switch the stargate to dormant state + if ( ( curtime - stargate->lastpinged ) > ( PING_WAIT_MAX * 2 ) ) { + + stargate->dormant = TRUE; + stargate->ping = -1; + stargate->active = FALSE; + } + } + +#endif // LINKED_PROTOCOL_GAMESERVER + +} +*/ + +// stargate animation callback ------------------------------------------------ +// +PRIVATE +int StargateAnimate( CustomObject *base ) +{ + ASSERT( base != NULL ); + Stargate *stargate = (Stargate *) base; + + // simply rotate around Z + ObjRotZ( stargate->ObjPosition, stargate->rotspeed * TheSimulator->GetThisFrameRefFrames() ); + + // determine whether stargate is dormant or not + //DetermineStargateDormantState( stargate ); + + return TRUE; +} + + +// stargate destructor (instance destruction) --------------------------------- +// +PRIVATE +void StargateDestroy( CustomObject *base ) +{ + ASSERT( base != NULL ); + Stargate *stargate = (Stargate *) base; +} + + +// check whether a ship is in range of a stargate ----------------------------- +// +PRIVATE +int ShipInStargateRange( Stargate *stargate, ShipObject *ship, geomv_t range ) +{ + ASSERT( stargate != NULL ); + ASSERT( ship != NULL ); + + //NOTE: + // the ship is treated as a sphere for activation + // range detection. + + Vector3 gatenormal; + FetchZVector( stargate->ObjPosition, &gatenormal ); + + Vertex3 gatepos; + FetchTVector( stargate->ObjPosition, &gatepos ); + + Vertex3 shippos; + FetchTVector( ship->ObjPosition, &shippos ); + + geomv_t shipdot = -DOT_PRODUCT( &gatenormal, &shippos ); + geomv_t gatedot = -DOT_PRODUCT( &gatenormal, &gatepos ); + geomv_t distance = shipdot - gatedot; + + // bounding sphere touching in negative halfspace is still ok + distance += ship->BoundingSphere; + + // not in range if ship in wrong halfspace + if ( GEOMV_NEGATIVE( distance ) ) { + return FALSE; + } + + // in range if ship bounding sphere intersects gate range hemisphere + range += ship->BoundingSphere * 2; + return ( distance < range ); +} + + +// check whether a ship is in jump range of a stargate ------------------------ +// +PRIVATE +int ShipInStargateJumpRange( Stargate *stargate, ShipObject *ship, geomv_t range ) +{ + ASSERT( stargate != NULL ); + ASSERT( ship != NULL ); + + //NOTE: + // the ship is treated as a point for jump + // range detection. + + Vector3 gatenormal; + FetchZVector( stargate->ObjPosition, &gatenormal ); + + Vertex3 gatepos; + FetchTVector( stargate->ObjPosition, &gatepos ); + + Vertex3 shippos; + FetchTVector( ship->ObjPosition, &shippos ); + + geomv_t shipdot = -DOT_PRODUCT( &gatenormal, &shippos ); + geomv_t gatedot = -DOT_PRODUCT( &gatenormal, &gatepos ); + geomv_t distance = shipdot - gatedot; + + // not in range if ship in wrong halfspace + if ( GEOMV_NEGATIVE( distance ) ) { + return FALSE; + } + + // check whether inside of boundingsphere around stargate + Vector3 stargate_ship; + VECSUB( &stargate_ship, &shippos, &gatepos ); + + geomv_t stargate_ship_len = VctLenX( &stargate_ship ); + if ( stargate_ship_len > stargate->BoundingSphere ) { + return FALSE; + } + + // inside the activation distance/range ? + if ( distance < range ) { + + Vector3 shipnormal; + FetchZVector( ship->ObjPosition, &shipnormal ); + + // ship must be flying approximately head-on into the gate + //FIXME: cone_angle like for teleporter + geomv_t dirdot = DOT_PRODUCT( &gatenormal, &shipnormal ); + return ( dirdot > FLOAT_TO_GEOMV( 0.7f ) ); + } + + return FALSE; +} + + +// stargate collision callback ------------------------------------------------ +// +PRIVATE +int StargateCollide( CustomObject *base ) +{ + ASSERT( base != NULL ); + + Stargate *stargate = (Stargate *) base; + + // dormant stargate is simply graphics + if ( stargate->dormant ) { + return TRUE; + } + +/* int inrange = 0x00; + + // check local ship + if ( ShipInStargateRange( stargate, MyShip, stargate->actdistance ) ) { + inrange |= 0x01; + } + + // check all other ships + if ( inrange == 0x00 ) { + + ShipObject *walkships = FetchFirstShip(); + for ( ; walkships; walkships = (ShipObject *) walkships->NextObj ) { + + if ( ShipInStargateRange( stargate, walkships, stargate->actdistance ) ) { + inrange |= 0x02; + break; + } + } + } + + // only toggle active when autoactivation is set + if ( stargate->autoactivate ) { + + if ( inrange != 0x00 ) { + + if ( !stargate->active ) { + + // create the active stargate particles + StargateModify_CreateActiveParticles( stargate ); + + // activate stargate + stargate->active = TRUE; + } + + } else { + + // deactivate stargate + stargate->active = FALSE; + + // and clear the activating sequence to stop adding particles + stargate->activating = FALSE; + } + } + + if ( !stargate->active ) { + return TRUE; + } + + if ( inrange == 0x00 ) { + return TRUE; + } + +#ifdef LINKED_PROTOCOL_GAMESERVER + + // check for fly-through sequence if connected to game server + if ( !NET_ConnectedGMSV() ) { + return TRUE; + } + + // radius of hemisphere used for jump range detection + geomv_t jumpdistance = FLOAT_TO_GEOMV( stargate->radius * 0.25f ); + + // check local ship for jump + if ( ( inrange == 0x01 ) && ShipInStargateJumpRange( stargate, MyShip, jumpdistance ) ) { + + // schedule server jump if not already done + if ( CurJumpServer == NULL ) { + + CurJumpServer = (char *) ALLOCMEM( strlen( stargate->destination_ip ) + 1 ); + if ( CurJumpServer == NULL ) + OUTOFMEM( 0 ); + strcpy( CurJumpServer, stargate->destination_ip ); + + //TODO: + // initiate animation and sfx + ShowMessage( "prepare to jump!!!" ); + } + + } else { + + // check all other ships + ShipObject *walkships = FetchFirstShip(); + for ( ; walkships; walkships = (ShipObject *) walkships->NextObj ) { + + if ( ShipInStargateJumpRange( stargate, walkships, jumpdistance ) ) { + + stargate->modulfade = 120.0f; + break; + } + } + } + +#endif // LINKED_PROTOCOL_GAMESERVER +*/ + return TRUE; +} + +// handle persistency --------------------------------------------------------- +// +int StargatePersistToStream( CustomObject* base, int tostream, void* rl ) +{ + ASSERT( base != NULL ); + ASSERT( tostream == TRUE ); + Stargate* stargate = (Stargate*) base; + + // determine size in packet + size_t size = E_REList::RmEvGetSizeFromType( RE_STARGATE ); + + // write to RE list + if ( rl != NULL ) { + + E_REList* pREList = (E_REList*)rl; + + RE_Stargate* re_stg = (RE_Stargate*)pREList->NET_Allocate( RE_STARGATE ); + ASSERT( re_stg != NULL ); + + re_stg->serverid = stargate->serverid; + + re_stg->pos[ 0 ] = stargate->ObjPosition[ 0 ][ 3 ]; + re_stg->pos[ 1 ] = stargate->ObjPosition[ 1 ][ 3 ]; + re_stg->pos[ 2 ] = stargate->ObjPosition[ 2 ][ 3 ]; + + re_stg->dir[ 0 ] = stargate->ObjPosition[ 0 ][ 2 ]; + re_stg->dir[ 1 ] = stargate->ObjPosition[ 1 ][ 2 ]; + re_stg->dir[ 2 ] = stargate->ObjPosition[ 2 ][ 2 ]; + + re_stg->dormant = stargate->dormant; + re_stg->active = stargate->active; + re_stg->rotspeed = stargate->rotspeed; + re_stg->radius = stargate->radius; + re_stg->actdistance = stargate->actdistance; + re_stg->numpartactive = stargate->numpartactive; + re_stg->actcyllen = stargate->actcyllen; + re_stg->partvel = stargate->partvel; + re_stg->modulspeed = stargate->modulspeed; + re_stg->modulrad1 = stargate->modulrad1; + re_stg->modulrad2 = stargate->modulrad2; + re_stg->acttime = stargate->acttime; + re_stg->autoactivate= stargate->autoactivate; + + strncpy( re_stg->flare_name, stargate->flare_name, MAX_TEXNAME ); + re_stg->flare_name[ MAX_TEXNAME ] = 0; + + strncpy( re_stg->interior_name, stargate->interior_name, MAX_TEXNAME ); + re_stg->interior_name[ MAX_TEXNAME ] = 0; + } + + return size; +} + + +// register object type for stargate ------------------------------------------ +// +PRIVATE +void StargateRegisterCustomType() +{ + custom_type_info_s info; + memset( &info, 0, sizeof( info ) ); + + info.type_name = "stargate"; + info.type_id = 0x00000000; + info.type_size = sizeof( Stargate ); + info.type_template = NULL; + info.type_flags = CUSTOM_TYPE_DEFAULT; + info.callback_init = StargateInitType; + info.callback_instant = StargateInstantiate; + info.callback_destroy = StargateDestroy; + info.callback_animate = StargateAnimate; + info.callback_collide = StargateCollide; + info.callback_persist = StargatePersistToStream; + + OBJ_RegisterCustomType( &info ); + CON_RegisterCustomType( info.type_id, Stargate_PropList ); +} + + +// module registration function ----------------------------------------------- +// +REGISTER_MODULE( G_STGATE_SV ) +{ + // register type + StargateRegisterCustomType(); +} diff --git a/src/parsec_server/include/MasterServer.h b/src/parsec_server/include/MasterServer.h new file mode 100644 index 0000000..ed78b47 --- /dev/null +++ b/src/parsec_server/include/MasterServer.h @@ -0,0 +1,32 @@ +/* + * MasterServer.h + * + * Created on: Jan 2, 2013 + * Author: jasonw + */ + +#ifndef MASTERSERVER_H_ +#define MASTERSERVER_H_ + +#include "e_gameserver.h" +#include "MasterServerItem.h" + +// C++ STL vector for the resolution list +#include <vector> + +/* + * + */ +class MasterServer: public E_GameServer { +public: + MasterServer(); + MasterServer(E_GameServer *); + virtual ~MasterServer(); + + int RemoveStaleEntries(); + + + std::vector<MasterServerItem> ServerList; +}; + +#endif /* MASTERSERVER_H_ */ diff --git a/src/parsec_server/include/MasterServerItem.h b/src/parsec_server/include/MasterServerItem.h new file mode 100644 index 0000000..de7419e --- /dev/null +++ b/src/parsec_server/include/MasterServerItem.h @@ -0,0 +1,53 @@ +/* + * MasterServerList.h + * + * Created on: Jan 2, 2013 + * Author: jasonw + */ + +#ifndef MASTERSERVERLIST_H_ +#define MASTERSERVERLIST_H_ + + +/* + * + */ +class MasterServerItem { +public: + MasterServerItem(); + MasterServerItem(int SrvID, int CurrPlayers, int MaxPlayers, int PMajor, int PMinor, char ServerName[ MAX_SERVER_NAME + 1 ], char OS[MAX_OSNAME_LEN + 1 ], node_t *node); + MasterServerItem(const MasterServerItem& msi_copy); + virtual ~MasterServerItem(); + + bool update(int SrvID, int CurrPlayers, int MaxPlayers, int PMajor, int PMinor, char ServerName[ MAX_SERVER_NAME + 1 ], char OS[MAX_OSNAME_LEN + 1 ], node_t *node); + bool remove(); + + bool operator < (const MasterServerItem &msl) const; + bool operator == (const MasterServerItem &msl) const; + bool operator != (const MasterServerItem &msl) const; + + bool isValid() const; + + int GetSrvID(); + int GetCurrPlayers(); + int GetMaxPlayers(); + int GetPMajor(); + int GetPMinor(); + int GetServerName(char *buffer, int buffer_sz); + int GetOS(char *buffer, int buffer_sz); + int GetNode(node_t *node); + + +private: + int _SrvID; + int _CurrPlayers; + int _MaxPlayers; + int _PMajor; + int _PMinor; + char _ServerName[ MAX_SERVER_NAME + 1 ]; + char _OS[MAX_OSNAME_LEN + 1 ]; + node_t _Node; + +}; + +#endif /* MASTERSERVERLIST_H_ */ diff --git a/src/parsec_server/include/con_act_sv.h b/src/parsec_server/include/con_act_sv.h new file mode 100644 index 0000000..fb3a366 --- /dev/null +++ b/src/parsec_server/include/con_act_sv.h @@ -0,0 +1,239 @@ +/* + * PARSEC HEADER: con_act_sv.h + */ + +#ifndef _CON_ACT_SV_H_ +#define _CON_ACT_SV_H_ + + +// action commands + +enum { + + ACM_SIGNATURE, + + ACM_IDLEWAIT, + + ACM_PITCH, + ACM_YAW, + ACM_ROLL, + ACM_SLIDEX, + ACM_SLIDEY, + ACM_MOVE, + + ACM_CAMROT1, + ACM_CAMROT2, + ACM_CAMROT3, + ACM_CAMORIGIN, + + ACM_FIXROT1, + ACM_FIXROT2, + ACM_FIXROT3, + + ACM_PACKET, + + ACM_FADEOUT, + ACM_FADEIN, + ACM_WFADEOUT, + ACM_WFADEIN, + ACM_BLACK, + ACM_WHITE, + ACM_STDPALETTE, + + ACM_SAVESCREEN, + ACM_SAVELIST, + ACM_SAVECAMERA, + ACM_SAVEFSTARS, + ACM_SAVEPSTARS, + ACM_SAVEREMOTE, + + ACM_KILLPSTARS, + ACM_ADDPSTAR, + + ACM_PREPRESTORE, + + ACM_CREATEOBJECT, + ACM_CREATEEXTRA, + ACM_DESTROYLIST, + + ACM_REMOTEPLAYER, + ACM_REMOTEMATRIX, + ACM_REMOTENAME, + ACM_REMOTEJOIN, + ACM_REMOTEUNJOIN, + + ACM_CLEARDEMO, + + ACM_OBJMATRXCOL1, + ACM_OBJMATRXCOL2, + ACM_OBJMATRXCOL3, + ACM_OBJMATRXCOL4, + + ACM_LASER_SPEED, + ACM_LASER_DIRVEC, + ACM_LASER_OWNER, + ACM_MISSL_SPEED, + ACM_MISSL_DIRVEC, + ACM_MISSL_OWNER, + ACM_MISSL_TARGET, + ACM_MINE_OWNER, + + ACM_ENERGYFIELD, + ACM_SPREADFIRE, + ACM_LIGHTNINGON, + ACM_LIGHTNINGOFF, + + ACM_SETOBJECTID, + ACM_SETOBJECTHOSTID, + + ACM_SIMULKEYPRESS, + ACM_SIMULKEYRELEASE, + + ACM_MS_CURDAMAGE, + ACM_MS_MAXDAMAGE, + ACM_MS_CURENERGY, + ACM_MS_MAXENERGY, + ACM_MS_CURSPEED, + ACM_MS_MAXSPEED, + ACM_MS_NUMMISSLS, + ACM_MS_MAXNUMMISSLS, + ACM_MS_NUMHOMMISSLS, + ACM_MS_MAXNUMHOMMISSLS, + ACM_MS_NUMMINES, + ACM_MS_MAXNUMMINES, + ACM_MS_WEAPONS, + ACM_MS_SPECIALS, + + ACM_GL_SELECTEDLASER, + ACM_GL_SELECTEDMISSILE, + ACM_GL_CURGUN, + ACM_GL_CURLAUNCHER, + ACM_GL_PANEL3CONTROL, + ACM_GL_PANEL4CONTROL, + ACM_GL_TARGETOBJNUMBER, + + ACM_WRITESMALL, + ACM_WRITEBIG1, + ACM_WRITEBIG2, + ACM_WRITEBIG3, + ACM_CLEARTEXT, + + ACM_PLAYSTREAM, + ACM_STOPSTREAM, + ACM_PLAYSOUND, + + ACM_AUX, + ACM_ENTRYMODE, + + ACM_SERVERMSG, + + ACM_HELIXON, + ACM_HELIXOFF, + + ACM_REMOTEMAXPLAYERS, + + ACM_INCLUDEDEMO, + + ACM_MS_WEAPONSACTIVE, + ACM_REMOTEWEAPONSACTIVE, + + ACM_WRITECONSOLETEXT, + ACM_DISPLAYMESSAGE, + + ACM_CD_OPEN, + ACM_CD_CLOSE, + ACM_CD_PLAY, + ACM_CD_PAUSE, + ACM_CD_RESUME, + ACM_CD_STOP, + ACM_CD_VOLUME, + + ACM_EXECCOMMAND, + + ACM_CLASSMAPID, + ACM_CLASSMAPNAME, + + ACM_AUXDATA, + ACM_SYSREFFRAMES, + + ACM_SCHEDULECOMMAND, + + ACM_LIGHTAMBIENT, + ACM_LIGHTDIFFUSE, + ACM_LIGHTSPECULAR, + + ACM_MS_NUMPARTMISSLS, + ACM_MS_MAXNUMPARTMISSLS, + + ACM_PHOTONON, + ACM_PHOTONOFF, + + ACM_EMPON, + ACM_EMPOFF, + ACM_EMP, + + ACM_NUM_COMMANDS // must be last!! +}; + + +// action commands flags + +#define ACF_NODIRECT 0x0000 // command not directly callable +#define ACF_DIRECT 0x0001 // command directly callable +#define ACF_NOCOMPILE 0x0000 // command not compilable +#define ACF_COMPILE 0x0002 // command is compilable +#define ACF_NOPARAMS 0x0000 // command has no parameters +#define ACF_INTPARAMS 0x0010 // command has int parameters +#define ACF_FLOATPARAMS 0x0020 // command has float parameters +#define ACF_STRINGPARAM 0x0040 // command has a string parameter +#define ACF_PACKETPARAM 0x0080 // command has a packet attached +#define ACF_DEMOPARAM 0x0100 // command has an entire demo attached +#define ACF_SCHEDPARAM 0x0200 // command has a command to schedule + +#define ACF_STRIP_SPACE 0x1000 // strip leading and trailing whitespace +#define ACF_STRINGSTRIP ( ACF_STRINGPARAM | ACF_STRIP_SPACE ) + +#define ACM_EAT_SPACE(c) ( ( action_commands[ c ].flags & ACF_STRIP_SPACE ) != 0 ) +#define ACF_EAT_SPACE(f) ( ( (f) & ACF_STRIP_SPACE ) != 0 ) + + +// action command description + +struct act_command_s { + + const char *command; // command string + void (*func)(void); // function responsible for command execution + dword flags; // flag word + int numparameters; // number of parameters the command takes +}; + +extern act_command_s action_commands[]; + + +// macro to retrieve action command strings +#define ACMSTR(x) (action_commands[x].command) + +// macro to retrieve string length of action commmands +#define ACMLEN(x) strlen(action_commands[x].command) + +// macro to determine if action command is directly callable +#define ACMCALLABLE(x) ((action_commands[x].flags&ACF_DIRECT)!=0) + + +// external functions + +int ExecBinCommands( char* &data, int interactive ); +void ResetClassMapTable(); +void ResetScheduledActions(); +void SaveGameState(); +void ActionCommand( char *cstr ); + + +// external variables + +extern int con_audio_stream_started; + + +#endif // _CON_ACT_SV_H_ + + diff --git a/src/parsec_server/include/con_aux_sv.h b/src/parsec_server/include/con_aux_sv.h new file mode 100644 index 0000000..c3a2075 --- /dev/null +++ b/src/parsec_server/include/con_aux_sv.h @@ -0,0 +1,59 @@ +/* + * PARSEC HEADER: cons_aux.h + */ + +#ifndef _CON_AUX_SV_H_ +#define _CON_AUX_SV_H_ + + +// size of SV array +// size of aux arrays +#define MAX_SV_ARRAY_SIZE 512 + +#define SV_PACKAGE_SCRIPTS AuxEnabling[ 0 ] +#define SV_CONSOLE_LEVEL_MESSAGES AuxEnabling[ 1 ] +#define SV_PACKAGE_SCRIPT_LISTING AuxEnabling[ 2 ] +#define SV_CLASS_MAP_TABLE_DISABLE_RESET AuxEnabling[ 3 ] +//NOTE: AUX_* flags are common with client code +#define AUX_NETCODE_FLAGS AuxEnabling[ 4 ] +#define AUX_CHEAT_DISABLE_ENERGY_CHECKS AuxEnabling[ 5 ] +#define SV_CHEAT_ENERGY_CHECKS AuxEnabling[ 5 ] +#define AUX_ENABLE_OBJECT_OVERLOADING AuxEnabling[ 6 ] +#define SV_OBJECTCLASSES_OVERLOADING AuxEnabling[ 6 ] +#define SV_CHEAT_DEVICE_CHECKS AuxEnabling[ 7 ] +#define AUX_CHEAT_DISABLE_DEVICE_CHECKS AuxEnabling[ 7 ] +#define SV_GAME_EXTRAS_AUTOCREATE AuxEnabling[ 8 ] +#define SV_MASTERSERVER_SENDHEARTBEAT AuxEnabling[ 9 ] +#define SV_GAME_EXTRAS_TESTPLACE AuxEnabling[ 11 ] +#define SV_GAME_KILLLIMIT AuxEnabling[ 12 ] +#define SV_GAME_TIMELIMIT AuxEnabling[ 13 ] +#define SV_GAME_RESTART_TIMEOUT AuxEnabling[ 14 ] +#define SV_SERVERID AuxEnabling[ 15 ] +#define SV_FEDID AuxEnabling[ 16 ] +#define SV_NETCONF_PORT AuxEnabling[ 17 ] +#define SV_DEBUG_NETSTREAM_DUMP AuxEnabling[ 18 ] +#define AUX_DEBUG_NETSTREAM_DUMP AuxEnabling[ 18 ] // for compatibility in NET_STREAM +#define AUX_ENABLE_CONSOLE_DEBUG_MESSAGES AuxEnabling[ 19 ] +#define SV_DEBUG_CONSOLE_MESSAGES AuxEnabling[ 19 ] +#define AUX_FORCE_WORKDIR_TO_CURRENT AuxEnabling[ 20 ] +#define SV_GAME_WORKDIR_FORCE_CURRENT AuxEnabling[ 20 ] +#define AUX_FORCE_FILEPATH_TO_CURRENT AuxEnabling[ 21 ] +#define SV_GAME_FILEPATH_FORCE_CURRENT AuxEnabling[ 21 ] +#define SV_GAME_EXTRAS_MAXNUM AuxEnabling[ 22 ] +#define AUX_DISABLE_PACKAGE_DATA_FILES AuxEnabling[ 23 ] +#define AUX_DISABLE_PACKAGE_SCRIPTS AuxEnabling[ 24 ] + +#define AUX_ARRAY_NUM_ENTRIES_USED 26 // UPDATE THIS!! <== + +#if ( AUX_ARRAY_NUM_ENTRIES_USED > MAX_SV_ARRAY_SIZE ) + #error "MAX_AUX_ENABLING too small!" +#endif + + +// external functions --------------------------------------------------------- +// +void CON_AUX_SV_Register(); + +#endif // _CON_AUX_SV_H_ + + diff --git a/src/parsec_server/include/con_com_sv.h b/src/parsec_server/include/con_com_sv.h new file mode 100644 index 0000000..9d75eb7 --- /dev/null +++ b/src/parsec_server/include/con_com_sv.h @@ -0,0 +1,71 @@ +/* + * PARSEC HEADER: con_com_sv.h + */ + +#ifndef _CON_COM_SV_H_ +#define _CON_COM_SV_H_ + + +// user commands table entry + +struct user_command_s { + + const char* command; // command string + short commlen; // length of string + short numparams; // num of parameters (used by tab-completion) + int (*execute)(char*); // command function + char* (*statedump)(); // returns statedump string; (NULL==volatile) +}; + + +// handle user commands not taking any parameters + +#define USERCOMMAND_NOPARAM(x) \ +{ \ + char *whsp = (x); \ + for ( ; *whsp; whsp++ ) \ + if ( *whsp != ' ' ) \ + break; \ + if ( *whsp != 0 ) { \ + if ( *(x) == ' ' ) { \ + CON_AddLine( dont_use_params ); \ + return TRUE; \ + } else { \ + return FALSE; \ + } \ + } \ +} \ + + +// macro to retrieve user command strings +#define USRSTR(x) (user_commands[x].command) + +// macro to retrieve string length of user commmands +#define USRLEN(x) (user_commands[x].commlen) + +// macro to determine number of arguments of user commmands +#define USRARG(x) (user_commands[x].numparams) + + +// external variables + +extern user_command_s* user_commands; +extern int num_user_commands; +extern char dont_use_params[]; + + +// external functions + +void CheckPlayerName( const char *name ); + +int CheckCmdsNoParam( char *command ); +int CheckCmdsCustomString( char *command ); +int CheckCmdsUserDefined( char *command ); + +int CON_RegisterUserCommand( user_command_s *regcom ); +int CON_ReplaceUserCommand( user_command_s* repcom ); + + +#endif // _CON_COM_SV_H_ + + diff --git a/src/parsec_server/include/con_ext_sv.h b/src/parsec_server/include/con_ext_sv.h new file mode 100644 index 0000000..b557d7c --- /dev/null +++ b/src/parsec_server/include/con_ext_sv.h @@ -0,0 +1,91 @@ +/* + * PARSEC HEADER: con_ext_sv.h + */ + +#ifndef _CON_EXT_SV_H_ +#define _CON_EXT_SV_H_ + + +// types of external commands (relating to their directory) +enum { + + COMTYPE_PACKAGE, // 0 + COMTYPE_ROOT, // 1 + COMTYPE_REFERENCE, // 2 + COMTYPE_STANDARD, // 3 + COMTYPE_RECORDING, // 4 + COMTYPE_MODIFICATION // 5 +}; + +// maximum valid length of command (script) name (filesystem dependent) +#define COMMAND_NAME_VALID_LEN 8 + +// allocated memory for command (script) name (excluding zero term.) +#define COMMAND_NAME_ALLOC_LEN 63 + +// maximum number of external scripts that will be recognized +#define MAX_EXTERNAL_COMMANDS 1024 + +// maximum number of registered mods (more than one is useful for data only) +#define MAX_REGISTERED_MODS 64 + + +// external variables + +extern int processing_batch; +extern int idle_reclevel; +extern int rec_depth; + +extern int preempt_action_commands; + +extern FILE * log_fp; +extern int log_commands; +extern char external_commands[][ COMMAND_NAME_ALLOC_LEN + 1 ]; +extern int external_command_types[]; +extern int num_external_commands; + +extern char packet_record_name[]; +extern char packet_replay_name[]; + +extern char* mod_names[]; +extern int mod_numnames; +extern int mod_override; + + +// external functions + +int ProcessExternalLine( char *scan ); +void ExecConsoleFile( char *fname, int echo ); +int AddExternalCommands( char *path, int comtype ); +void BuildExternalCommandList(); +int CheckExternalCommand( char *com ); +int InsertCommandLog( char *command ); +int ExecExternalFile( char *command ); +int CompileDemoCut( FILE *fp, char *script ); +int ScriptReplayActive(); +int RestartExternalCommand( int echo ); +int OpenOutputBatch( char *cstr, int type ); +void CloseOutputBatch(); +int StartRecording( char *cstr, int savestate ); +void CloseRecording(); +void CallExternalCommand( int extcom, int echo ); +void ScriptStopReplay(); + +int Cmd_CatExternalCommands( char *cstr ); +int Cmd_SetGameLoopBatchName( char *cstr ); +void Cmd_CloseOutputBatch(); +void Cmd_StopBatchReplay(); +void Cmd_QueryReplayInfo(); +void Cmd_RescanExternalCommands(); + + +// macro to retrieve external command strings +#define ECMSTR(x) (external_commands[x]) + +// macro to retrieve string length of external commmands +#define ECMLEN(x) strlen(external_commands[x]) + + +#endif // _CON_EXT_SV_H_ + + diff --git a/src/parsec_server/include/con_info_sv.h b/src/parsec_server/include/con_info_sv.h new file mode 100644 index 0000000..dec756d --- /dev/null +++ b/src/parsec_server/include/con_info_sv.h @@ -0,0 +1,63 @@ +/* + * PARSEC HEADER: con_info_sv.h + */ + +#ifndef _CON_INFO_SV_H_ +#define _CON_INFO_SV_H_ + + +// callback function type +typedef int (*notify_callback_fpt)( GenObject *base ); + + +// access information for single object property (type/class field) + +struct proplist_s { + + const char* propname; // name property can be accessed by + size_t propoffset; // offset of property in obj structure + int bmin; // minimum value of property + int bmax; // maximum value of property + int fieldtype; // field's data type + notify_callback_fpt notify_callback; // callback on field value changes +}; + +//NOTE: +// bmin and bmax (value bounds) for all types other than +// PROPTYPE_INT have to be specified in fixed-point! + +//NOTE: +// for PROPTYPE_STRING and PROPTYPE_CHARPTR bmin and bmax specify +// minimum and maximum number of chars (excluding the '\0'). + +#define PROPTYPE_INT 0 +#define PROPTYPE_FLOAT 1 +#define PROPTYPE_GEOMV 2 +#define PROPTYPE_FIXED 3 +#define PROPTYPE_STRING 4 +#define PROPTYPE_CHARPTR 5 + + +// external functions + +void CON_RegisterCustomType( dword objtypeid, proplist_s *proplist ); + +char* BuildTypeInfo( int typeindex ); +char* BuildClassInfo( int classindex ); + +int CheckSetObjTypeProperty( const char *query, const char *scan ); +int CheckSetObjClassProperty( const char *query, const char *scan ); +int CheckSetObjInstanceProperty( const char *query, const char *scan ); + +int CheckTypeInfo( const char *scan ); +int CheckClassInfo( const char *scan ); +int CheckInstanceInfo( const char *scan ); + +int WritePropList( FILE* fp, dword objid ); + +int Cmd_FaceInfo( const char *command ); + + +#endif // _CON_INFO_SV_H_ + + diff --git a/src/parsec_server/include/con_int_sv.h b/src/parsec_server/include/con_int_sv.h new file mode 100644 index 0000000..e51f5d2 --- /dev/null +++ b/src/parsec_server/include/con_int_sv.h @@ -0,0 +1,59 @@ +/* + * PARSEC HEADER: con_int_sv.h + */ + +#ifndef _CON_INT_SV_H_ +#define _CON_INT_SV_H_ + + +// integer commands table entry + +struct int_command_s { + + int persistence; // persistent int commands are saved on exit + const char* command; // name of command + int bmin; // lower bound + int bmax; // upper bound + int* intref; // pointer to actual int var + void (*realize)(); // will be called after modification (optional) + int (*fetch)(); // will be called before reading (optional) + int default_val; // default value for int var +}; + + +// integer commands list +extern int_command_s * int_commands; + +// number of integer variable commands +extern int num_int_commands; + +// integer input/output configuration +extern const char * int_print_base; +extern int int_calc_base; + + +// macro to retrieve intvar command strings +#define ICMSTR(x) (int_commands[x].command) + +// macro to retrieve string length of intvar commmands +#define ICMLEN(x) strlen(int_commands[x].command) + + +// position of console geometry variables in table +enum { + + CONINFOINDX_CONWINX = 0, + CONINFOINDX_CONWINY = 1, + CONINFOINDX_CONWIDTH = 2, + CONINFOINDX_CONHEIGHT = 3 +}; + + +// external functions + +void CON_RegisterIntCommand( int_command_s *regcom ); + + +#endif // _CON_INT_SV_H_ + + diff --git a/src/parsec_server/include/con_list_sv.h b/src/parsec_server/include/con_list_sv.h new file mode 100644 index 0000000..c57ac20 --- /dev/null +++ b/src/parsec_server/include/con_list_sv.h @@ -0,0 +1,34 @@ +/* + * PARSEC HEADER: con_list_sv.h + */ + +#ifndef _CON_LIST_SV_H_ +#define _CON_LIST_SV_H_ + + +// external functions + +void Cmd_ShipInfo(); +void Cmd_ListTypes(); +void Cmd_ListClasses(); +void Cmd_ListSayMacros(); +void Cmd_ListKeyBindings(); +void Cmd_ListKeyFunctions(); +void Cmd_ListVidModes(); +void Cmd_ListBinaryDemos(); +int Cmd_ListExternalCommands( char *command ); +int Cmd_ListDataTable( char *command ); +int Cmd_ListIntCommands( char *command ); +int Cmd_ListStdCommands( char *command ); + + +// external variables + +extern int await_keypress; +extern void (*com_cont_func)(); +extern int cur_vis_conlines; + + +#endif // _CON_LIST_SV_H_ + + diff --git a/src/parsec_server/include/con_load_sv.h b/src/parsec_server/include/con_load_sv.h new file mode 100644 index 0000000..9870a3a --- /dev/null +++ b/src/parsec_server/include/con_load_sv.h @@ -0,0 +1,16 @@ +/* + * PARSEC HEADER: con_load_sv.h + */ + +#ifndef _CON_LOAD_SV_H_ +#define _CON_LOAD_SV_H_ + + +// external functions + +int Cmd_ExecLoadCommand( char *command ); + + +#endif // _CON_LOAD_SV_H_ + + diff --git a/src/parsec_server/include/con_main_sv.h b/src/parsec_server/include/con_main_sv.h new file mode 100644 index 0000000..4482251 --- /dev/null +++ b/src/parsec_server/include/con_main_sv.h @@ -0,0 +1,168 @@ +/* + * PARSEC HEADER: con_main_sv.h + */ + +#ifndef _CON_MAIN_SV_H_ +#define _CON_MAIN_SV_H_ + + +// if this is defined command parsing will be a lot slower +//#define NO_UNNECESSARY_COMMAND_DOMAINS + + +// maximum length of single console line +#define MAX_CONSOLE_LINE_LENGTH 255 + +// number of console lines (storage) +#define NUM_CONSOLE_LINES 256 +#define NUM_CONSOLE_LINES_MASK ( NUM_CONSOLE_LINES - 1 ) + +// number of history list lines +#define NUM_HISTORY_LINES 128 +#define NUM_HISTORY_LINES_MASK ( NUM_HISTORY_LINES - 1 ) + +// size of console prompt (in chars) +#define PROMPT_SIZE 2 + + + +// additional directories for console scripts +#define REFCON_COMMANDS_DIR "refs/" +#define STDCON_COMMANDS_DIR "cons/" +#define RECORD_COMMANDS_DIR "recs/" + + +// file extension of console scripts +#define CON_FILE_EXTENSION ".con" + + +// file extension of compiled console scripts +#define CON_FILE_COMPILED_EXTENSION ".dem" + + +// command domain restriction handling + +#ifdef NO_UNNECESSARY_COMMAND_DOMAINS + + #define HANDLE_COMMAND_DOMAIN(x) \ + if ( ( *(x) != ' ' ) && ( *(x) != 0 ) ) { \ + return FALSE; \ + } else { \ + } + + #define HANDLE_COMMAND_DOMAIN_SEP(x) HANDLE_COMMAND_DOMAIN(x) + +#else // NO_UNNECESSARY_COMMAND_DOMAINS + + #define HANDLE_COMMAND_DOMAIN(x) + + #define HANDLE_COMMAND_DOMAIN_SEP(x) \ + if ( ( *(x) != ' ' ) && ( *(x) != 0 ) ) { \ + CON_AddLine( unknown_command ); \ + return TRUE; \ + } else { \ + } + +#endif // NO_UNNECESSARY_COMMAND_DOMAINS + + +// external variables + +extern int compile_script; +extern FILE * compile_fp; + +extern int con_back_view; +extern int con_content; +extern int con_bottom; +extern int cursor_x; +extern int press_space_curs_x; + +extern int con_in_talk_mode; +extern int con_talk_line; +extern char talk_escape_char; + +extern int con_non_interactive; + +extern int con_no_login_check; + +extern char con_prompt[]; +extern char ok_prompt[]; +extern char press_space[]; +extern char too_many_args[]; +extern char arg_missing[]; +extern char invalid_arg[]; +extern char range_error[]; +extern char unknown_command[]; +extern char line_separator[]; + +extern char con_lines[][ MAX_CONSOLE_LINE_LENGTH + 1 ]; + + +// external functions + +/* +void CheckConExtents(); + +void EraseConLine( int line ); + +void WritePersistentCommands(); + + +void FlushConsoleBuffer(); + +int QuicksayConsole(); +void SetQuicksayConsole( int enable ); + +void ConsoleControl(); +void ToggleConsole(); + +void InitConsoleFileHandling(); +*/ + + +void ExecConsoleCommand( char *command, int echo ); +void ExecNonCompilableCommand( char *command ); + +char* SubstCurWorkDir( char *path ); +void ClearConScreen(); +void CreateSeparatorLine( int line ); + +void CON_AddMessage( const char *text ); +void CON_AddLine( const char *text ); +void CON_AddLineFeed(); +void CON_EnableLineFeed(); +void CON_DisableLineFeed(); +void CON_DelLine(); +char* CON_GetLine(); +void CON_ListCtdPrompt(); +void CON_ListEndPrompt(); + + +void ExecStartupScript( int echo ); + + +void CON_InitConsole(); +void CON_KillConsole(); +void CON_ConsoleMain(); + +void CON_InitCurses(); +void CON_KillCurses(); + + +#ifdef USE_CURSES + + #ifdef SYSTEM_WIN32_UNUSED + #include "libpdcurses/curses.h" + #else + #include <curses.h> + #endif + + extern WINDOW* g_curses_in_win; + extern WINDOW* g_curses_out_win; + +#endif // USE_CURSES + + +#endif // _CON_MAIN_SV_H_ + + diff --git a/src/parsec_server/include/con_std_sv.h b/src/parsec_server/include/con_std_sv.h new file mode 100644 index 0000000..75d35a1 --- /dev/null +++ b/src/parsec_server/include/con_std_sv.h @@ -0,0 +1,120 @@ +/* + * PARSEC HEADER: con_std_sv.h + */ + +#ifndef _CON_STD_SV_H_ +#define _CON_STD_SV_H_ + + +// standard commands table entry ---------------------------------------------- +// +struct std_command_s { + + const char* command; // command string + short commlen; // length of string + short numparams; // number of parameters (used by tab-completion) +}; + + +// enumeration of standard commands ------------------------------------------- +// +enum enum_std_commands { + + CM_APPEND, + CM_BASE, + CM_BIND, + CM_BUILD, + CM_CAT, + CM_CLASSINFO, + CM_CLEAR, + CM_CLOSE, + CM_CLS, + CM_COLANIM, + CM_COMPILE, + CM_CONNECT, + CM_CREATE, + CM_DEC, + CM_DISABLE, + CM_NOMACROS, + CM_DISCONNECT, + CM_DISPLAY, + CM_ENABLE, + CM_ENABLEMACROS, + CM_EXIT, + CM_EXITGAME, + CM_FACEINFO, + CM_GAMELOOPBATCH, + CM_HEX, + CM_HIDE, + CM_KEY, + CM_LISTBINDINGS, + CM_LISTCLASSES, + CM_LISTSTDCOMMANDS, + CM_LISTDATA, + CM_LISTDEMOS, + CM_LISTGAMEFUNCKEYS, + CM_LISTINTCOMMANDS, + CM_LISTTEXTMACS, + CM_LISTTYPES, + CM_LOAD, + CM_LOGIN, + CM_LOGOUT, + CM_LISTEXTCOMMS, + CM_NAME, + CM_NET_SUBSYS, + CM_OBJECTCLEAR, + CM_OBJECTCOUNT, + CM_OBJECTINFO, + CM_OCT, + CM_OPEN, + CM_PATH_ANIM_INTRO, + CM_PATH_ASTREAM_INTRO, + CM_PATH_ASTREAM_MENU, + CM_PATH_ASTREAM, + CM_PLAYDEMO, + CM_PROP, + CM_PROPC, + CM_PROPO, + CM_QUIT, + CM_RECDEM, + CM_RECORD, + CM_REPINFO, + CM_REPSTOP, + CM_RESCAN, + CM_SAY, + CM_SET_MACRO, + CM_SETORIGIN, + CM_DEFSHADER, + CM_SETSHADER, + CM_SHIPS, + CM_SHOWPOSITION, + CM_TALKMODE, + CM_TYPEINFO, + CM_WORKINGDIRECTORY, + CM_WRITESTRING, + + // must be last in list!! + NUM_STD_COMMAND_CONSTANTS +}; + + +// command list and length array +extern std_command_s std_commands[]; + +// number of standard commands +extern int num_std_commands; + + +// macro to retrieve standard command strings +#define CMSTR(x) (std_commands[x].command) + +// macro to retrieve string length of standard commmands +#define CMLEN(x) (std_commands[x].commlen) + +// macro to determine number of arguments of standard commmands +#define CMARG(x) (std_commands[x].numparams) + + +#endif // _CON_STD_SV_H_ + + diff --git a/src/parsec_server/include/e_colldet.h b/src/parsec_server/include/e_colldet.h new file mode 100755 index 0000000..9a9bf4e --- /dev/null +++ b/src/parsec_server/include/e_colldet.h @@ -0,0 +1,75 @@ +/* +* PARSEC HEADER: G_CollDet.h +*/ +#include "g_emp.h" +#ifndef _G_COLLDET_H_ +#define _G_COLLDET_H_ + +// class handling the collision detection ------------------------------------- +// +class G_CollDet +{ +protected: + + ShipObject* cur_ship; + Vertex3 obj_pos; + Vertex3 test_pos; + Vertex3 prev_pos; + geomv_t bd_sphere; + geomv_t bd_sphere2; + int test_shield; + +protected: + + // collision detection helper function + int _CheckShipProjectileDisjoint(); + + + // ship collided with laser + void _CollisionResponse_LaserShip( LaserObject *curlaser ); + + // check whether any ship is hit by laser beam + void _CheckShipLaserCollision(); + + // check if ship collected extra + void _CheckShipExtraCollision(); + + // check if ship hit by emp + void _CheckShipEmpCollision(); + + // ship collided with EMP + void _CollisionResponse_EmpShip( Emp *curemp ); + + //Ship collided with missile + void _CheckShipMissileCollision(); + //response definitions + void _CollisionResponse_MissileShip( MissileObject *curmissile ); + void _CollisionResponse_MineShip( Mine1Obj *curmine ); + void OBJ_ShipHelixDamage( ShipObject *shippo, int owner ); + void OBJ_ShipLightningDamage( ShipObject *shippo, int owner ); + void OBJ_ShipPhotonDamage( ShipObject *shippo, int owner ); + + G_CollDet() {} + ~G_CollDet() {} + +public: + // SINGLETON pattern + static G_CollDet* GetGameCollDet() + { + static G_CollDet _TheGameCollDet; + return &_TheGameCollDet; + } + + // perform collision detection tests + void OBJ_CheckCollisions(); + void LinearParticleCollision( linear_pcluster_s *cluster, int pid ); + void CheckEnergyField( pcluster_s *cluster ); + void OBJ_ShipSwarmDamage( ShipObject *shippo, int owner ); + int CheckLightningParticleShipCollision( Vertex3& particlepos, int owner ); + //Particle Collisions + int PRT_ParticleInBoundingSphere( ShipObject *shippo, Vertex3& point ); + +}; + +#endif // _G_COLLDET_H_ + diff --git a/src/parsec_server/include/e_connmanager.h b/src/parsec_server/include/e_connmanager.h new file mode 100755 index 0000000..446a8ae --- /dev/null +++ b/src/parsec_server/include/e_connmanager.h @@ -0,0 +1,247 @@ +/* +* PARSEC HEADER: e_connmanager.h +*/ + +#ifndef _E_CONNMANAGER_H_ +#define _E_CONNMANAGER_H_ + +// forward decls -------------------------------------------------------------- +// +class E_GameServer; +class E_PacketHandler; +class E_SimPlayerInfo; + + +// class holding the challenges sent to requesting clients -------------------- +// +class E_ClientChallengeInfo +{ +public: + int m_challenge; + node_t m_node; + refframe_t m_frame_generated; +public: + E_ClientChallengeInfo() : + m_challenge( -1 ), + m_frame_generated( 0 ) + { + memset( &m_node, 0, sizeof( node_t ) ); + } +}; + +// class holding basic client information ------------------------------------- +// +class E_BasicClientInfo +{ +public: + int m_nVersionMajor; + int m_nVersionMinor; + char m_szName [ MAX_PLAYER_NAME + 1 ]; + char m_szOSLine [ MAX_OSNAME_LEN + 1 ]; + char m_szHostName[ MAX_HOSTNAME_LEN + 1 ]; + node_t m_node; + int m_challenge; + int m_nRecvRate; // byte/sec. this client wants data + int m_nSendFreq; // packets/sec. this client sends data + +public: + E_BasicClientInfo () + { + Reset(); + } + + void Reset() + { + m_nVersionMajor = -1; + m_nVersionMinor = -1; + memset( m_szName, 0, MAX_PLAYER_NAME + 1 ); + memset( m_szOSLine, 0, MAX_OSNAME_LEN + 1 ); + memset( m_szHostName, 0, MAX_HOSTNAME_LEN + 1 ); + memset( &m_node, 0, sizeof( node_t ) ); + m_challenge = -1; + m_nRecvRate = DEFAULT_CLIENT_RECV_RATE; + m_nSendFreq = DEFAULT_CLIENT_SEND_FREQUENCY; + } + + // return the NET.SERVERRATE for this client + int GetRecvRate() { return m_nRecvRate; } + + // return the NET.CLIENTRATE for this client + int GetSendFreq() { return m_nSendFreq; } +}; + + +// class holding client information ( when connecting ) ----------------------- +// +class E_ClientConnectInfo : public E_BasicClientInfo +{ +public: + int m_selected_slot; +public: + E_ClientConnectInfo() : + E_BasicClientInfo(), + m_selected_slot(-1) + { + } +}; + + +// class holding client information ( when connected ) ------------------------ +// +class E_ClientInfo : public E_BasicClientInfo +{ +protected: + int m_slotfree; + int m_nAliveCounter; + E_SimPlayerInfo* m_pSimPlayerInfo; + +public: + E_ClientInfo() : + m_slotfree( TRUE ), + m_nAliveCounter( MAX_ALIVE_COUNTER ), + m_pSimPlayerInfo( NULL ) + { + } + + void CopyClientConnectInfo( E_ClientConnectInfo* pClientConnectInfo ); + + void Reset() + { + E_BasicClientInfo::Reset(); + + m_slotfree = TRUE; + m_nAliveCounter = MAX_ALIVE_COUNTER; + } + + // mark the client alive + void MarkAlive(); + + // check whether the client is alive + int IsAlive(); + + // check whether the slot is free + int IsSlotFree() { return m_slotfree; } + + // set the status of slot + void SetSlotFree( int slotfree ) { m_slotfree = slotfree; } +}; + + +// connection manager class --------------------------------------------------- +// +class E_ConnManager +{ +protected: + + E_ClientChallengeInfo* m_ChallengInfos; + int m_nMaxNumChallengeInfos; + int m_nCurChallengeInfo; + + E_ClientInfo* m_ClientInfos; + int m_nNumConnected; + + int m_inDisconnectClient; + + E_ConnManager(); + ~E_ConnManager(); + +public: + + // SINGLETON pattern + static E_ConnManager* GetConnManager() + { + static E_ConnManager _TheConnManager; + return &_TheConnManager; + } + + // request a challenge for a client + int RequestChallenge( node_t* clientnode ); + + enum ConnResults { + CONN_CHALLENGE_INVALID = 0, + CONN_CLIENT_INCOMAPTIBLE = 1, + CONN_CLIENT_BANNED = 2, + CONN_SERVER_FULL = 3, + CONN_NAME_TAKEN = 4, + CONN_OK = 5 + }; + + enum DisconnResults { + DISC_NOT_CONNECTED = 0, + DISC_OK = 1 + }; + + enum NameChangeResults { + NAMECHANGE_NOT_CONNECTED = 0, + NAMECHANGE_ALREADY_TAKEN = 1, + NAMECHANGE_OK = 2 + }; + + // check whether the client connection is valid + int CheckClientConnect( E_ClientConnectInfo* pClientConnectInfo ); + + // check for a client disconnect + int CheckClientDisconnect( node_t* clientnode ); + + // check for a client namechange + int CheckNameChange( node_t* clientnode, char* newplayername ); + + // get the client information for a specific slot + E_ClientInfo* GetClientInfo( int nSlot ); + + // check the alive counter of all connected clients and disconnect any timed out clients + int CheckAliveStatus(); + + // check node of client with node used at connection + int CheckNodesMatch( int nClientID, node_t* node ); + + // return the # of connected clients + int GetNumConnected() { return m_nNumConnected; } + + // set some client-info fields according to RE + void NET_ExecRmEvClientInfo( int nClientID, RE_ClientInfo* re_clientinfo ); + + // disconnect a client + int DisconnectClient( int nClientID ); + + // return the name of the client + char* GetClientName( int nClientID ); + +protected: + + // check if client version is compatible + int _IsClientCompatible( E_ClientConnectInfo* pClientConnectInfo ); + + // check if client is banned + int _IsClientBanned( E_ClientConnectInfo* pClientConnectInfo ); + + // check if challenge is correct + int _IsChallengeCorrect( E_ClientConnectInfo* pClientConnectInfo, E_ClientChallengeInfo** pChallengeInfo ); + + // ensure a client is not connected + int _EnsureClientIsDisconnected( E_ClientConnectInfo* pClientConnectInfo ); + + // connect a client + int _ConnectClient( E_ClientConnectInfo* pClientConnectInfo ); + + // change the name of a client + int _ChangeClientName( int nSlot, char* newplayername ); + + // send client connected notifications to the other clients + int _NotifyClientConnected( int nSlotConnected ); + + // send client disconnected notifications to the other clients + int _NotifyClientDisconnected( int nSlotDisconnected ); + + // send client namechange notifications to the other clients + int _NotifyClientNameChange( int nSlotNamechanged ); + + // find the assigned slot for a specific node + int _FindSlotWithNode( node_t* clientnode ); + + // find the slot of a connected client by its name + int _FindClientName( char* name ); +}; + + +#endif // _E_CONNMANAGER_H_ diff --git a/src/parsec_server/include/e_defs.h b/src/parsec_server/include/e_defs.h new file mode 100644 index 0000000..e700c39 --- /dev/null +++ b/src/parsec_server/include/e_defs.h @@ -0,0 +1,45 @@ +/* + * PARSEC HEADER: e_defs.h + */ + +#ifndef _E_DEFS_H_ +#define _E_DEFS_H_ + + +// ---------------------------------------------------------------------------- +// GAMESSERVER SUBSYSTEM (SVG) related definitions - +// ---------------------------------------------------------------------------- + + +// default HTML file names ---------------------------------------------------- +// +#define list_html_file "clients.html" +#define header_html_file "g_header.html" +#define footer_html_file "g_footer.html" + +// default log file names ----------------------------------------------------- +// +#define log_file "gamesrv.log" +#define cfg_file "server.cfg" + + +// constants ------------------------------------------------------------------ +// + +// number of remote event lists +#define NUM_RE_LISTS 5 + +// size of masterserver queue +#define MAX_MASTERS 3 + +// maximum number of clients in the banlist +#define MAX_BANNED_CLIENTS 128 + +// number of players that have to be connected for a game to start +#define MIN_PLAYERS_TO_START 2 + +#include "net_ports.h" + +#endif // _E_DEFS_H_ + + diff --git a/src/parsec_server/include/e_gameserver.h b/src/parsec_server/include/e_gameserver.h new file mode 100755 index 0000000..217e1a5 --- /dev/null +++ b/src/parsec_server/include/e_gameserver.h @@ -0,0 +1,204 @@ +/* +* PARSEC HEADER: e_gameserver.h +*/ + +#ifndef _E_GAMESERVER_H_ +#define _E_GAMESERVER_H_ + +// constants ------------------------------------------------------------------ +// +#define MAX_MASTERSERVER_NAME MAX_HOSTNAME_LEN +#define MAX_NUM_CLIENTS TheServer->GetMaxNumClients() + +#include <string.h> + +// class holding the server configuration ------------------------------------- +// +class ServerConfig +{ +private: + int m_SimFrequency; // frequency to run the sim +protected: + + // data that can be modified via commandline/cfg file + //char m_szSelectedInterfaceIP[ MAX_STRLEN_IPADDR ]; // string containing the selected interface IP ( NULL if default ) + word m_nInterface; // zero based interface index + int m_MaintainFrequency; // frequency to run maintainance + int m_nPacketAverageSecs; // time to sample the average packetsize for optimal client update rate + + int m_nMaxNumClients; // current max. # of players + char m_szServername[ MAX_SERVER_NAME + 1 ];// the servername + + refframe_t m_ClientUpdateHeartbeat; + + int m_ServerIsMaster; + +/* char location[ MAX_CFG_LINE + 1 ] = "not specified"; + char admin_name[ MAX_CFG_LINE + 1 ] = "not specified"; + char admin_email[ MAX_CFG_LINE + 1 ] = "not specified"; + char server_info[ MAX_CFG_LINE + 1 ] = ""; + char server_url[ MAX_CFG_LINE + 1 ] = ""; + char messageline1[ MAX_CFG_LINE + 1 ] = ""; + char messageline2[ MAX_CFG_LINE + 1 ] = ""; + char messageline3[ MAX_CFG_LINE + 1 ] = ""; + char messageline4[ MAX_CFG_LINE + 1 ] = ""; + + int time_limit = 0; + int time_startrefframes = 0; + int time_counter = GAME_NOTSTARTEDYET; + int counter_active = FALSE; + int wait_startrefframes = 0; + + int kill_limit = 0; + + char *banlist[ MAX_BANNED_CLIENTS ]; + int num_banned = 0; +*/ + +public: + // standard ctor + ServerConfig(); + + // return the refframes to wait between client update heartbeats + refframe_t GetClientUpdateHeartbeat() { return m_ClientUpdateHeartbeat; } + + // functions to set/get whether this server instance is a master server. + void SetServerIsMaster(bool isMaster); + bool GetServerIsMaster(); + + // set the servername + void SetServername( const char* pName ) + { + strncpy( m_szServername, pName, MAX_SERVER_NAME ); + m_szServername[ MAX_SERVER_NAME ] = 0; + } + + // get the server name + const char* GetServerName() { return m_szServername; } + + // set the # of max players + bool_t SetMaxNumClients( int nMaxNumClients ); + + // return the max # of players + int GetMaxNumClients(); + + // set the simulation frequency + bool_t SetSimFrequency( int nSimFrequency ); + + // return the simulation frequency + int GetSimFrequency(); +}; + +// class for holding a server link -------------------------------------------- +// +class E_ServerLinkInfo +{ +public: + int m_serverid; + Vector3 m_pos; + Vector3 m_dir; + + E_ServerLinkInfo() + { + Reset(); + } + + void Reset() + { + m_serverid = 0; + memset( &m_pos, 0, sizeof( Vector3 ) ); + memset( &m_dir, 0, sizeof( Vector3 ) ); + } +}; + + + +// main gameserver class ------------------------------------------------------ +// +class E_GameServer : public ServerConfig +{ +protected: // data + + // internal data + refframe_t m_Maintain_FrameTime; + refframe_t m_SimTick_FrameTime; // duration in refframes of one simulation tick + int m_Select_Timeout; // timout for select (microseconds) + int m_ServerIdleTime_msec; // idle time ( select timeout ) of server + + int m_nServerFrame; // the current server frame + + refframe_t m_MaintainFrameBase; + refframe_t m_MasterServerFrameBase; + refframe_t m_PacketFrameBase; + refframe_t m_CurPacketRefFrames; + + refframe_t m_CurServerRefFrame; + refframe_t m_LastServerRefFrame; + + refframe_t m_MasterServer_FrameTime; // frametime for master server maintenance + char m_MasterServer_Hostname[ MAX_MASTERSERVER_NAME + 1]; + int m_MasterServer_Challenge; + bool_t m_bMasterServer_NodeValid; + node_t m_MasterServer_Node; + + E_ServerLinkInfo m_ServerLinks[ MAX_NUM_LINKS ]; + int m_nNumServerLinks; + + bool_t m_bQuit; +protected: // methods + + // init pre/post running of console script + int _InitPostConsoleScript(); + int _InitPreConsoleScript(); + + // ------------------------------------------------------------------------ + // main methods called in the server frame + // ------------------------------------------------------------------------ + void _MaintainSimulation(); // maintain the simulation + int _MaintainHousekeeping(); // maintain clients ( client timeouts etc. ) + int _MaintainMasterServer(); // maintain communication to master server + + E_GameServer(); // standard ctor + ~E_GameServer(); // standard dtor + +public: // methods + + // SINGLETON pattern + static E_GameServer* GetGameServer() + { + static E_GameServer _TheGameServer; + return &_TheGameServer; + } + + int Init(); // init the server components + int Kill(); // kill the server components + + int ParseCommandLine( int argc, char** argv ); // parse the command line + + int PrintCopyRight(); // print copyright + int PrintUsage(); // print program usage + + int MainLoop(); // server mainloop + refframe_t ServerFrame(); // server Frame + + // set the new masterserver info + void SetMasterServerInfo( char* pName, refframe_t _MasterServer_FrameTime ); + + // check whether the supplied node matches the stored masterserver node + int IsMasterServerNode( node_t* node ); + + // set a new masterserver challenge + void SetMasterServerChallenge( int nMASVChallenge ); + + // add a serverlink + int AddServerLink( int serverid, Vector3* pos_spec, Vector3* dir_spec ); + + // accessor methods + refframe_t GetSimTickFrameTime() { return m_SimTick_FrameTime; } + int GetPacketAverageSecs() { return m_nPacketAverageSecs; } + refframe_t GetMasterServerFrameTime() { return m_MasterServer_FrameTime; } + + void SetQuitFlag() { m_bQuit = TRUE; } +}; + +#endif // _E_GAMESERVER_H_ diff --git a/src/parsec_server/include/e_global_sv.h b/src/parsec_server/include/e_global_sv.h new file mode 100755 index 0000000..4c313ca --- /dev/null +++ b/src/parsec_server/include/e_global_sv.h @@ -0,0 +1,97 @@ +/* + * PARSEC HEADER: e_global_sv.h + */ + +#ifndef _E_GLOBAL_SV_H_ +#define _E_GLOBAL_SV_H_ + + +// include data info tables ------------------------------- + +#include "gd_tabs.h" + +// global externals (ENGINE CORE) ------------------------- + +extern int NumLoadedObjects; + +extern objectinfo_s ObjectInfo[]; + +extern char CurWorkDir[]; + + +extern char* sys_ProgramName; + +extern colrgba_s LightColorAmbient; +extern colrgba_s LightColorDiffuse; +extern colrgba_s LightColorSpecular; + +extern Vector3 GlobalDirLight; + +extern char* sys_ProgramName; + +#define VISFRAME_NEVER 0xFFFFFFFF + +extern int RecordingActive; +extern FILE* RecordingFp; +extern int RecordRemotePackets; +extern int RemoteRecSessionId; +extern int RemoteRecPacketId; + +extern int ConsoleSliding; +extern int ConsoleHeight; + +extern GenObject* VObjList; +extern GenObject* ObjClasses[]; +extern int NumObjClasses; + +extern dword NextObjNumber; + +extern int AuxEnabling[]; +extern int AuxData[]; + +extern refframe_t RefFrameFrequency; + + + +// forward decls -------------------------------------------------------------- +// +class G_CollDet; +class G_ExtraManager; +class G_Input; +class G_Main; +class NET_PacketDriver; +class NET_UDPDriver; +class E_DistManager; +class E_SimNetInput; +class E_SimNetOutput; +class E_Simulator; +class E_World; +class E_ConnManager; +class E_GameServer; +class MasterServer; +class E_PacketHandler; + + +// global variables, these are set in SV_MAIN::main() and in E_GameServer::Init() +// +extern G_CollDet* TheGameCollDet; +extern G_ExtraManager* TheGameExtraManager; +extern G_Input* TheGameInput; +extern G_Main* TheGame; +extern NET_PacketDriver* ThePacketDriver; +extern NET_UDPDriver* TheUDPDriver; +extern E_DistManager* TheDistManager; +extern E_World* TheWorld; +extern E_SimNetInput* TheSimNetInput; +extern E_SimNetOutput* TheSimNetOutput; +extern E_Simulator* TheSimulator; +extern E_ConnManager* TheConnManager; +extern E_GameServer* TheServer; +extern MasterServer* TheMaster; +extern E_PacketHandler* ThePacketHandler; + + + +#endif // _E_GLOBAL_SV_H_ + + diff --git a/src/parsec_server/include/e_packethandler.h b/src/parsec_server/include/e_packethandler.h new file mode 100755 index 0000000..bd283a1 --- /dev/null +++ b/src/parsec_server/include/e_packethandler.h @@ -0,0 +1,147 @@ +/* +* PARSEC HEADER: e_packethandler.h +*/ + +#ifndef _E_PACKETHANDLER_H_ +#define _E_PACKETHANDLER_H_ + + +// forward decls -------------------------------------------------------------- +// +struct PacketChainBlock; +class NET_PacketDriver; +class E_ClientChallengeInfo; +class E_ClientConnectInfo; +class E_REList; + +// needed includes ------------------------------------------------------------ +// +#include "e_connmanager.h" +#include "MasterServerItem.h" + +#ifndef _NET_GAME_H_ + //FIXME: we MUST not depend on client module NET_GAME + //#include "net_game_sv.h" +#endif // _NET_GAME_H_ + + +// Server packet handler class ------------------------------------------------ +// +class E_PacketHandler +{ +protected: + + E_PacketHandler() + { + } + +public: + + // SINGLETON pattern + static E_PacketHandler* GetPacketHandler() + { + static E_PacketHandler _ThePacketHandler; + return &_ThePacketHandler; + } + + // main packet handling function + void HandlePacket( PacketChainBlock* block ); + + // send a challenge response back to a client + void SendChallengeResponse( E_ClientChallengeInfo* pChallengeInfo, node_t* clientnode ); + + // send a connect response back to a client + int SendConnectResponse( E_ConnManager::ConnResults rc, E_ClientConnectInfo* pClientConnectInfo ); + + // send a disconnect response back to a client + int SendDisconnectResponse( E_ConnManager::DisconnResults rc, node_t* clientnode, int nClientID ); + + // send a namechange response back to the client + int SendNameChangeRepsponse( E_ConnManager::NameChangeResults rc, node_t* clientnode, int nClientID ); + + // send a PING response back to the client + int SendPingResponse( refframe_t client_ping_send_frame, node_t* clientnode, int nClientID ); + + // send a INFO response back to the client + int SendInfoResponse( refframe_t client_ping_send_frame, node_t* clientnode, int nClientID ); + + // send IPV4 info to the client after a master server list command + int SendIPV4Response( node_t* clientnode, int nClientID, int serverid ); + + // notify a client that another client is connected + void SendNotifyConnected( int nDestSlot, int nClientSlot ); + + // notify a client that another client is disconnected + void SendNotifyDisconnected( int nDestSlot, int nClientSlot ); + + // notify a client that another client's name changed + void SendNotifyNameChange( int nDestSlot, int nClientSlot ); + + + // send a STREAM packet to a client + size_t Send_STREAM( int nClientID, E_REList* pReliable, E_REList* pUnreliable ); + + // send a server command to a node + int Send_COMMAND( char* clientcommand, node_t* node, int nClientID, int reliable ); + + // send a datagram to a node + int Send_STREAM_Datagram( E_REList* relist, node_t* node, int nClientID ); + + // send a datagram command to a specific client node + int Send_COMMAND_Datagram( const char* clientcommand, node_t* node, int nClientID ); + + // multicast a packet to all connected clients + //void Multicast_Packet( NetPacket_GMSV* gamepacket, int nSenderClientID ); + +protected: + + // handle a command packet + void _Handle_COMMAND( NetPacket_GMSV* gamepacket, int bufid ); + + // handle a packet containing a command from the masterserver + void _Handle_COMMAND_MASV( NetPacket_GMSV* gamepacket, int bufid ); + + // handle a packet containing a command FOR the master server. + void _Handle_COMMAND_MASTER( NetPacket_GMSV* gamepacket, int bufid ); + + // handle a stream packet + void _Handle_STREAM( NetPacket_GMSV* gamepacket ); + + // handle a stream packet for the master server + void _Handle_STREAM_MASTER( NetPacket_GMSV* gamepacket, int bufid ); + + void _Send_STREAM(); + + // do safe parsing of challenge request + int _ParseChallengeRequest( char* recvline ); + + // do safe parsing of connect request + int _ParseConnectRequest( char* recvline, E_ClientConnectInfo* pClientConnectInfo ); + + // do safe parsing of disconnect request + int _ParseDisconnectRequest( char* recvline ); + + // do safe parsing of namechange request + int _ParseNameChangeRequest( char* recvline, char* playername ); + + // do safe parsing of PING request + int _ParsePingRequest( char* recvline, refframe_t* client_ping_send_frame ); + + // do safe parsing of INFO request + int _ParseInfoRequest( char* recvline, refframe_t* client_ping_send_frame ); + + // do safe parsing of LIST requests for Master Server Mode. + int _ParseListRequest_MASTER( char* recvline, int *serverid); + + // do safe parsing of heartbeat packets for the Master Server. + int _ParseHBPacket_MASTER(char* recvline); + + // check whether the sender node matches the node for the senderid + int _IsLegitSender( NetPacket_GMSV* gamepacket, int bufid ); + + // do safe parsing of new challenge from MASV + int _ParseMASVChallenge( char* recvline, int* pChallenge ); +}; + + +#endif // _E_PACKETHANDLER_H_ diff --git a/src/parsec_server/include/e_simnetinput.h b/src/parsec_server/include/e_simnetinput.h new file mode 100755 index 0000000..889bcf8 --- /dev/null +++ b/src/parsec_server/include/e_simnetinput.h @@ -0,0 +1,48 @@ +#ifndef E_SIMNETINPUT_H_ +#define E_SIMNETINPUT_H_ + +// forward decls -------------------------------------------------------------- +// +class E_REList; +//struct RE_Header; +//struct ShipRemInfo; + + +// class for handling the network input from the simulation ------------------- +// +class E_SimNetInput { +protected: + E_REList* m_pInputREList; + +protected: + // default ctor/dtor + E_SimNetInput(); + ~E_SimNetInput(); + +public: + // SINGLETON access + static E_SimNetInput* GetSimNetInput() + { + static E_SimNetInput _TheSimNetInput; + return &_TheSimNetInput; + } + + // reset all data + void Reset(); + + // get the input remote event list + //E_REList* GetInputREList() { return m_pInputREList; } + + // walk the input RE queue and apply events/states to simulation + void ProcessInputREList(); + + // handle the network input from a client + int HandleOneClient( int nClientID, RE_Header* relist ); + +protected: + // check absolute movement bounds + int _CheckAbsoluteMovementBounds( RE_PlayerAndShipStatus* pPAS ); +}; + +#endif // !E_SIMNETINPUT_H_ + diff --git a/src/parsec_server/include/e_simnetoutput.h b/src/parsec_server/include/e_simnetoutput.h new file mode 100755 index 0000000..db2441b --- /dev/null +++ b/src/parsec_server/include/e_simnetoutput.h @@ -0,0 +1,361 @@ +#ifndef E_SIMNETOUTPUT_H_ +#define E_SIMNETOUTPUT_H_ + + +// update todos --------------------------------------------------------------- +// +#define UPDATE_TODO_NOTHING 0x0000 +#define UPDATE_TODO_SEND 0x0001 +#define UPDATE_TODO_MASK 0x0001 + +// update mode --------------------------------------------------------------- +// +#define UPDATEMODE_SENDMODE_UNRELIABLE 0x0000 +#define UPDATEMODE_SENDMODE_RELIABLE 0x0010 +#define UPDATEMODE_SENDMODE_MASK 0x0010 + +#define UPDATEMODE_SENDWHAT_STATE 0x0000 +#define UPDATEMODE_SENDWHAT_REMOVE 0x0020 +#define UPDATEMODE_SENDWHAT_MASK 0x0020 + +#define UPDATEMODE_FLAGS_SEND_TO_OWNER 0x0100 +#define UPDATEMODE_FLAGS_MASK 0xFF00 + + +enum { + + RMEVSTATE_NEBULAID, + RMEVSTATE_PROBEXTRA, + RMEVSTATE_PROBHELIX, + RMEVSTATE_PROBLIGHTNING, + RMEVSTATE_PROBPHOTON, + RMEVSTATE_PROBMINE, + RMEVSTATE_PROBREPAIR, + RMEVSTATE_PROBAFTERBURNER, + RMEVSTATE_PROBHOLODECOY, + RMEVSTATE_PROBINVISIBILITY, + RMEVSTATE_PROBINVULNERABILITY, + RMEVSTATE_PROBENERGYFIELD, + RMEVSTATE_PROBLASERUPGRADE, + RMEVSTATE_PROBLASERUPGRADE1, + RMEVSTATE_PROBLASERUPGRADE2, + RMEVSTATE_PROBMISSPACK, + RMEVSTATE_PROBDUMBPACK, + RMEVSTATE_PROBGUIDEPACK, + RMEVSTATE_PROBSWARMPACK, + RMEVSTATE_PROBEMPUPGRADE1, + RMEVSTATE_PROBEMPUPGRADE2, + RMEVSTATE_AMAZING, + RMEVSTATE_BRILLIANT, + RMEVSTATE_KILLLIMIT, + RMEVSTATE_ENERGYBOOST, + RMEVSTATE_REPAIRBOOST, + RMEVSTATE_DUMBPACK, + RMEVSTATE_HOMPACK, + RMEVSTATE_SWARMPACK, + RMEVSTATE_PROXPACK, + + RMEVSTATE_NUMSTATES +}; + +// info about a E_Distributable ------------------------------------------------- +// +class E_Distributable +{ +protected: + dword m_objectid; + int m_listno; + int m_update_mode; + byte* m_update_to_client; +public: + E_Distributable( dword objectid, int listno, int reliable, int send_to_owner ) : + m_objectid( objectid ), + m_listno( listno ), + m_update_mode( UPDATE_TODO_NOTHING ), + m_update_to_client( NULL ) + { + // translate to internal format + if ( reliable ) { + m_update_mode |= UPDATEMODE_SENDMODE_RELIABLE; + } + if ( send_to_owner ) { + m_update_mode |= UPDATEMODE_FLAGS_SEND_TO_OWNER; + } + + ResetUpdateInfo(); + } + + ~E_Distributable() + { + delete []m_update_to_client; + } + + // reset the update info + void ResetUpdateInfo(); + + // get the object id + dword GetObjectID() { return m_objectid; } + + // return the list # the object belongs to + int GetListNo() { return m_listno; } + + // set updatemode for all clients to STATE + void UpdateMode_STATE(); + + // set updatemode for all clients to REMOVE + void UpdateMode_REMOVE(); + + // check whether this E_Distributable is marked as REMOVE + int IsInRemoving() + { + return ( ( m_update_mode & UPDATEMODE_SENDWHAT_MASK ) == UPDATEMODE_SENDWHAT_REMOVE ); + } + + // return whether the E_Distributable will be sent to the owner + int WillBeSentToOwner() + { + return ( ( m_update_mode & UPDATEMODE_FLAGS_SEND_TO_OWNER ) != 0 ); + } + + // check whether the E_Distributable is a ZOMBIE ( client has received the REMOVE update ) + int IsZombie( int nClientID ); + + // check whethe the E_Distributable has an update for a specific client + int HasUpdate( int nClientID ); + + // mark, that we sent an update to a client + void MarkUpdateSent( int nClientID ); + + // mark, that we need to send an update to a client + void MarkForUpdate( int nClientID ); + + // check whether the E_Distributable should be sent reliable + int NeedsReliable() + { + return ( ( m_update_mode & UPDATEMODE_SENDMODE_MASK ) == UPDATEMODE_SENDMODE_RELIABLE ); + } + + // write a corresponding RE to the REList + void DistributeToREList( E_REList* relist ); + + // determine the size this E_Distributable is + size_t DetermineSizeInPacket(); +}; + + + + + +// send modi for client updates ----------------------------------------------- +// +#define SEND_MODE_NONE 0 +#define SEND_MODE_UNRELIABLE 1 +#define SEND_MODE_RELIABLE 2 + +#define MAX_NUM_DISTRIBUTABLES_TO_SEND_PER_PACKET 256 + +// information about packets sent out ( time and size ) ----------------------- +// +struct sent_packet_info_s { + + sent_packet_info_s() + { + Reset(); + } + + void Reset() + { + m_size = 0; + m_sendtime = 0; + } + + size_t m_size; + refframe_t m_sendtime; +}; + + +// class handling the network output for a client ----------------------------- +// class with all necessary information on what to send to the destination client +class E_SimClientNetOutput +{ +protected: + + int m_nDestClientID; + + E_REList* m_pReliableBuffer; + E_REList* m_pUnreliableBuffer; + + sent_packet_info_s* m_SentPacketInfo; + unsigned int m_nPacket; + + refframe_t m_LastSendFrame; + refframe_t m_Send_FrameTime; + + refframe_t m_Heartbeat_Timeout_Frame; + + int m_nNumPacketSlots; + int m_nAveragePacketSize; + + // list of all distributables for the client + UTL_List<E_Distributable*>* m_AllDistributables; + +protected: + + // list of client ids the client is to be updated in the next packet + int* m_ClientIDList; + bool_t* m_SendReliable; + int m_nNumClients; + + // inidicate that we should send a the state of the client itself to the destination client ( energy, damage, shields ) + bool_t m_bIncludeDestClientState; + + // indicate whether to send the gamestate to the client + bool_t m_bIncludeGameState; + + // indicate whether to send the killstats to the client + bool_t m_bIncludeKillStats; + + //Indicate whether to send state variables to client + bool_t m_bIncludeStateSync; + + // array of distributables for next packet + E_Distributable* m_DistsForNextPacket[ MAX_NUM_DISTRIBUTABLES_TO_SEND_PER_PACKET ]; + int m_nNumDistsForNextPacket; + + // # of byte available for the update + int m_nSizeAvail; + +public: + + // default ctor/dtor + E_SimClientNetOutput(); + ~E_SimClientNetOutput(); + + // connect a client + void Connect( int nClientID ); + + // disconnect a client + void Disconnect(); + + // sent udpate information to a client if bandwidth available + int SendUpdateToClient(); + + // the NET.SERVERRATE for this client has changed - recalc frame-time + void RateChanged(); + + // (re)calculate the average packet size + void CalculateAveragePacketSize(); + + // schedule a E_Distributable to be sent to the client + void ScheduleDistributable( E_Distributable* pDist, int check_unique = FALSE ); + + // buffer an RE for output + void BufferRE( RE_Header* re, int reliable ); + +protected: + + // reset all data members + void _Reset(); + + // clear the list of things to update + void _ClearUpdates(); + + // check whether there are any updates to send to the client + int _HasUpdates(); + + // add a E_Distributable to be sent out with the next packet + void _AddDistForOutput( E_Distributable* pDist ); + + // include the information about a client in the next update ( reliable or unreliable ) + void _FlagIncludeClient( int nClientID, bool_t bReliable ); + + // check whether we can reserve size for output, and do so if space avail. + bool_t _ReserveForOutput( int size ); + + // fill the RE lists to be sent to the client + int _FillPacketForClient( E_REList* pReliable, E_REList* pUnreliable ); + + // getters/setters + size_t _GetSizeAvail() { return m_nSizeAvail; } + + // check whether we should send a packet to this client + int _CanSendPacket(); + + // create buffer E_RELists for pass-through multicasts + void _CreateBuffers(); + + // terminate the multicast data in the E_REList buffers + void _TerminateBufferMulticast(); + + // determine which states must be updated for this client + int _PrepareClientUpdateInfo(); + + // check whether to send a heartbeat to the client + int _ShouldSendHeartbeat(); + + // set the new heartbeat timeout value + void _DoHeartbeat(); +}; + + + +// class for handling the network output from the simulation ------------------ +// +class E_SimNetOutput { +protected: + E_SimClientNetOutput* m_SimClientNetOutput; + UTL_List<E_Distributable*>* m_Distributables; + +protected: + E_SimNetOutput(); + ~E_SimNetOutput(); + +public: + // SINGLETON access + static E_SimNetOutput* GetSimNetOutput() + { + static E_SimNetOutput _TheSimNetOutput; + return &_TheSimNetOutput; + } + + // reset all data + void Reset(); + + // connect the player in a specific slot + int ConnectPlayer( int nClientID ); + + // disconnect the player in a specific slot + int DisconnectPlayer( int nClientID ); + + // update all clients + void DoClientUpdates(); + + // buffer a RE for sending to all clients minus sender + void BufferForMulticastRE( RE_Header* relist, int nSenderClientID, int reliable ); + + // buffer a RE for sending to a specific client + void BufferForDirectRE( RE_Header* relist, int nClientID, int reliable ); + + // update NET.SERVERRATE dependent stuff of E_SimClientNetOutput + void RateChangedForClient( int nClientID ); + + // recalculate the average packet sizes for all connected clients + void RecalcAveragePacketSizes(); + + // create a new E_Distributable for all connected clients + E_Distributable* CreateDistributable( GenObject* object, int reliable = FALSE, int send_to_owner = FALSE ); + + // release a E_Distributable + void ReleaseDistributable( E_Distributable* pDist ); + + // cleanup all distributables, that are complete zombies + void CleanupZombieDistributables(); + +protected: + // destroy m_Distributables + void _DestroyDistributables(); + +}; + + +#endif // !E_SIMNETOUTPUT_H_ diff --git a/src/parsec_server/include/e_simplayerinfo.h b/src/parsec_server/include/e_simplayerinfo.h new file mode 100755 index 0000000..19e5afa --- /dev/null +++ b/src/parsec_server/include/e_simplayerinfo.h @@ -0,0 +1,67 @@ +#ifndef E_SIMPLAYERINFO_H_ +#define E_SIMPLAYERINFO_H_ + + +// class holding all simulation specific player information ------------------- +// +class E_SimPlayerInfo +{ +protected: + int m_Status; // status of the player (inactive,connected,joined) + int m_objclass; // the objectclass selected as ship when joined + + ShipObject* m_pShip; + int m_nShipID; + int m_nClientID; + + int m_IgnoreJoinUntilUnjoinFromClient; +public: + E_SimPlayerInfo() + { + Reset(); + } + + // reset all player fields to defaults ( not connected ) + void Reset(); + + // check whether a player is disconnected + bool_t IsPlayerDisconnected() { return ( m_Status == PLAYER_INACTIVE ); } + + // check whether a player is connected + bool_t IsPlayerConnected() { return ( m_Status == PLAYER_CONNECTED ); } + + // check whether a player is joined in the game + bool_t IsPlayerJoined() { return ( m_Status == PLAYER_JOINED ); } + + // set the desired player status ( inactive/joined/unjoined ) + void SetDesiredPlayerStatus( RE_PlayerStatus* playerstatus ); + + // update tables and create ship for newly joined player + void PerformJoin( RE_PlayerStatus* pas_status ); + + // update tables and delete ship of player who unjoined + void PerformUnjoin( RE_PlayerStatus* playerstatus ); + + // set the player status to connected + void Connect( int nClientID ); + + // set the player status to disconnected + void Disconnect(); + + // set flag to ignore all joins until we get an unjoin from the client + void IgnoreJoinUntilUnjoinFromClient() + { + m_IgnoreJoinUntilUnjoinFromClient = TRUE; + } + + // get a pointer to the ship object + ShipObject* GetShipObject() { return m_pShip; } + + // get the object class of the ship + int GetShipObjectClass() { return m_objclass; } + + // return the status of the player + int GetStatus() { return m_Status; } +}; + +#endif // E_SIMPLAYERINFO_H_ diff --git a/src/parsec_server/include/e_simulator.h b/src/parsec_server/include/e_simulator.h new file mode 100755 index 0000000..c339cf4 --- /dev/null +++ b/src/parsec_server/include/e_simulator.h @@ -0,0 +1,286 @@ +/* +* PARSEC HEADER: e_simulator.h +*/ + +#ifndef _E_SIMULATOR_H_ +#define _E_SIMULATOR_H_ + +#include "net_util.h" +#include "utl_math.h" // for playerlerp_s + +// forward decls -------------------------------------------------------------- +// +class E_Distributable; +class E_SimClientNetOutput; +class E_ClientInfo; +class E_GameServer; +class E_REList; +template <class T> class UTL_List; + + +//FIXME: +// gamecode stuff is skipped for now +#define GAMECODE( x ) {} + +// gamecode2 is executed +#define GAMECODE2( x ) x + + +// class holding a ship state ------------------------------------------------- +// +class E_SimShipState +{ +protected: + + Xmatrx m_ObjPosition; + fixed_t m_CurSpeed; + bams_t m_CurYaw; + bams_t m_CurPitch; + bams_t m_CurRoll; + geomv_t m_CurSlideHorz; + geomv_t m_CurSlideVert; + +public: + // standard ctor + E_SimShipState() + { + Reset(); + } + + // reset al data members + void Reset(); + + E_SimShipState& operator= ( E_SimShipState& _other ) + { + ASSERT( FALSE ); + return _other;//remove the warning without it + } + + // copy from other + E_SimShipState& CopyFrom( E_SimShipState& _other ) + { + memcpy( &m_ObjPosition, &_other.m_ObjPosition, sizeof( Xmatrx ) ); + m_CurSpeed = _other.m_CurSpeed; + m_CurYaw = _other.m_CurYaw; + m_CurPitch = _other.m_CurPitch; + m_CurRoll = _other.m_CurRoll; + m_CurSlideHorz = _other.m_CurSlideHorz; + m_CurSlideVert = _other.m_CurSlideVert; + + return *this; + } + + // copy information from ShipRemInfo struct + void CopyFromPlayerAndShipStatus( RE_PlayerAndShipStatus& _other ) + { + memcpy( &m_ObjPosition, &_other.ObjPosition, sizeof( Xmatrx ) ); + m_CurSpeed = _other.CurSpeed; + m_CurYaw = _other.CurYaw; + m_CurPitch = _other.CurPitch; + m_CurRoll = _other.CurRoll; + m_CurSlideHorz = _other.CurSlideHorz; + m_CurSlideVert = _other.CurSlideVert; + } + + // various get/set functions + pXmatrx GetObjPosition() { return m_ObjPosition; } + + //FIXME: get rid of friend classes + friend class E_SimClientState; + friend class E_REList; +}; + + +// class handling the network input for a client ----------------------------- +// +class E_SimClientState { +protected: + E_SimShipState* m_States; + int m_nNumStateSlots; + + E_SimShipState m_InputState; + dword m_nInputStateMessageID; + dword m_nLastInputStateMessageId; + + refframe_t m_LastSwitchToSimRefFrame; + + int m_nClientID; + + enum ClientMovementMode_t { CMM_SIMULATING, CMM_SMOOTHING }; + ClientMovementMode_t m_ClientMovementMode; + playerlerp_s m_playerlerp; + + bool_t m_ResyncClient; + bool_t m_ClientHasStateSync; + +public: + E_SimClientState() + { + m_States = NULL; + m_nNumStateSlots = -1; + m_nInputStateMessageID = 0; + m_nLastInputStateMessageId = 0; + m_ClientMovementMode = CMM_SIMULATING; + m_ResyncClient = FALSE; + m_LastSwitchToSimRefFrame = -1; + m_ClientHasStateSync = FALSE; + } + + ~E_SimClientState() + { + Reset(); + } + + // reset all data-members + void Reset() + { + if ( m_States != NULL ) { + delete []m_States; + m_States = NULL; + } + m_nNumStateSlots = -1; + m_nInputStateMessageID = 0; + m_nLastInputStateMessageId = 0; + m_InputState.Reset(); + m_ClientMovementMode = CMM_SIMULATING; + m_ResyncClient = FALSE; + m_LastSwitchToSimRefFrame = -1; + m_ClientHasStateSync = FALSE; + } + + // connect a client + void Connect( int nClientID ); + + // disconnect a client + void Disconnect() + { + Reset(); + } + + // check the movement bounds between the last received state + int CheckMovementBounds( dword MessageID, RE_PlayerAndShipStatus* pPAS ); + + // store the received state as the newest + void StoreNewState( dword MessageID, RE_PlayerAndShipStatus* pPAS ); + + // reset the sim state + void ForceNewState( E_SimShipState* pSimShipState ); + + // either apply simulation or smoothing to get newest client-state + void CalcNewState( refframe_t CurSimRefFrames ); + + // dump some SimClientState for debugging purposes + void Dump(); + + // return the current/last simulation-state-slot + E_SimShipState* GetCurSimFrameStateSlot(); + E_SimShipState* GetPrevSimFrameStateSlot(); + + // set the client resync flag ( RE_PLAYERANDSHIPSTATUS for this client sent from server to client ) + void SetClientResync() { m_ResyncClient = TRUE; } + + // clear the client resync flag + void ClearClientResync() { m_ResyncClient = FALSE; } + + // check whether we must resync this client + int NeedsResync() { return m_ResyncClient; } + + // Check whether state variables (nebula id, ammo pack sizes) have been sent + int HasState() { return m_ClientHasStateSync; } + + void SetState() { m_ClientHasStateSync = TRUE; } + +protected: + // calculate the newest smooothing target + void _CalcNewSmoothingTarget(); + + // check whether the change from the last to the current state involved a movement + int _CheckForMovement( dword MessageID, RE_PlayerAndShipStatus* pPAS ); + + // update the message ids the lateste state comes from ( dead reckoning ) + void _UpdateStateMessageID( dword MessageID ); +}; + + +// class for handling the overall simulation as well as all the states -------- +// +class E_Simulator { +protected: + + refframe_t m_CurRefFrame; // the current RefFrame + refframe_t m_CurSimRefFrames; // refframes to advance in current sim step + refframe_t m_LastSimRefFrame; // last sim refframe + + E_SimPlayerInfo* m_SimPlayerInfos; + E_SimClientState* m_SimClientState; + + int m_nSimFrame; + + // ctor/dtor + E_Simulator(); + ~E_Simulator(); + +public: + // SINGLETON access + static E_Simulator* GetSimulator() + { + static E_Simulator _TheSimulator; + return &_TheSimulator; + } + + // main simulation method + int DoSim( refframe_t CurSimRefFrame ); + + // get the PlayerInfo for a client + E_SimPlayerInfo* GetSimPlayerInfo( int nClientID ); + + // return the current simulation refframe + refframe_t GetCurSimRefFrame() { return m_CurRefFrame; } + + // return the refframes the simulation runs in this sim-frame + refframe_t GetThisFrameRefFrames() { return m_CurSimRefFrames; } + + // get the SimClientState for a specific client + E_SimClientState* GetSimClientState( int nClientID ); + + // get the latest valid player + E_SimShipState* GetLatestSimShipState( int nClientID ); + + // reset all data + void Reset(); + + // connect the player in a specific slot + int ConnectPlayer( int nClientID ); + + // disconnect the player in a specific slot + int DisconnectPlayer( int nClientID ); + + // check whether a player in a slot is joined + int IsPlayerJoined( int nClientID ); + + // check whether a player in a slot is connected + int IsPlayerDisconnected( int nClientID ); + + // return the current simulation frame + int GetSimFrame() { return m_nSimFrame; } + +protected: + + // update the shipstate ( according to the current sim refframes ) + void _UpdateShipState( int nClientID ); + + // simulate ship movements + void _SimulateShips(); + + // calculate the refframes we want to apply to this simulation step + int _CalcSimRefFrames(); + + // apply the current sim-state to the engine state ( E_SimShipState -> ShipObject ) + void _ApplySimToEngineState(); + + // apply the current engine state to the sim-state ( ShipObject -> E_SimShipState ) + void _ApplyEngineToSimState(); +}; + + +#endif // _E_SIMULATOR_H_ diff --git a/src/parsec_server/include/e_world.h b/src/parsec_server/include/e_world.h new file mode 100755 index 0000000..d69fa69 --- /dev/null +++ b/src/parsec_server/include/e_world.h @@ -0,0 +1,295 @@ +/* +* PARSEC HEADER: E_WORLD.H +*/ + +#ifndef _E_WORLD_H_ +#define _E_WORLD_H_ +#include "parttype.h" + +// flags ---------------------------------------------------------------------- +// +//#define _SERVER_HAS_PARTICLE_CODE + +// size of generic particle cluster +#define DEFAULT_CLUSTER_SIZE 256 + +// size of customdraw particle cluster +#define DEFAULT_CUSTOMDRAW_CLUSTER_SIZE 32 + +// size of genobject particle cluster +#define DEFAULT_GENOBJECT_CLUSTER_SIZE 128 + +// particle sphere rotation velocities +#define SPHERE_ROT_PITCH 0x0015 //0x01a0 +#define SPHERE_ROT_YAW -0x0022 //-0x02a0 +#define SPHERE_ROT_ROLL 0x000d //0x0100 + +// properties of pulsating sphere +#define SPHERE_PULSE_AMPLITUDE FLOAT_TO_GEOMV( 12.0f ) +#define SPHERE_PULSE_FREQUENCY 90 + +// properties of stochastic motion sphere +#define SPHERE_STOCHASTIC_MOTION_SPEED 100 + +// maximum number of registered particle definitions +#define MAX_PARTICLE_DEFS 256 + +#define PRTSB_ENERGYFIELD PRT_NO_SIZEBOUND +// properties of energy field +#define ENERGY_FIELD_INITIAL_RADIUS FLOAT_TO_GEOMV( 0.0625f ) +#define ENERGY_FIELD_LIFETIME 10000 +#define SPHERE_PARTICLE_COLOR 255 + +// swarm behaviour constants -------------------------------------------------- +// +#define NUM_PARTICLES 15 // number of particles in swarm +#define TIME_POSITIONS 3 // number of time positions recorded +#define PARTICLE_ACCEL FLOAT_TO_GEOMV( 0.8f ) // acceleration of particles +#define PARTICLE_VELOCITY FLOAT_TO_GEOMV( 2.0f ) // maximum particle velocity +#define PARTICLE_REFZ 300.0f // particle refz +#define PARTICLE_LIFETIME 3000 // average lifetime of particles +#define LIFETIME_VARIANCE 400 // a small variance to avoid that all particles +// vanish at the same time +#define ANIM_TIMESLICE 6 // animate as if CurScreenRefFrames == 6 +#define LIFETIME_AFTEREXPL 1200 // lifetime of particles after ship destruction + + +// swarm state structure ------------------------------------------------------ +// +struct swarm_state_s { + + int num; // number of particles + Point3h_f* pos; // current particle positions + + geomv_t* x; // particle position x[ time ][ partnum ] + geomv_t* y; // particle position y[ time ][ partnum ] + geomv_t* z; // particle position z[ time ][ partnum ] + + geomv_t* xv; // particle velocities xv[ partnum ] + geomv_t* yv; // particle velocities xv[ partnum ] + geomv_t* zv; // particle velocities xv[ partnum ] + + geomv_t tx[ 3 ]; // target positions x + geomv_t ty[ 3 ]; // target positions y + geomv_t tz[ 3 ]; // target positions z + + ShipObject* targetpo; // the target object + refframe_t timerest; // excess time from last animation frame + GenObject dummyobj; // just for sound position tracking +}; + +// photon properties +#define MIN_PHOTON_ENERGY 10 +#define PHOTON_ENERGY_CONSUMPTION 0x6000 // 0x10000 = 1.0 + +#define PHOTON_ROT_PITCH 0x0015 //0x01a0 +#define PHOTON_ROT_YAW -0x0022 //-0x02a0 +#define PHOTON_ROT_ROLL 0x000d //0x0100 + +#define PHOTON_SPHERE_PARTICLES 256 +#define PHOTON_COLOR 123 +#define PHOTON_REF_Z 75.0 +#define PHOTON_CONTRACTION_TIME 50 +#define PHOTON_CONTRACTION_SPEED FIXED_TO_GEOMV( 0xA000 ) +#define PHOTON_MAX_LOADING_TIME 1800 +#define PHOTON_NUMLOADS 32 + +// helper macros -------------------------------------------------------------- +// +#define X( t, b ) ( swarm->x[ ( t ) * swarm->num + ( b ) ] ) +#define Y( t, b ) ( swarm->y[ ( t ) * swarm->num + ( b ) ] ) +#define Z( t, b ) ( swarm->z[ ( t ) * swarm->num + ( b ) ] ) +#define balance_rand( v ) ( ( SWARM_rand() % v ) - ( ( v ) / 2 ) ) + +#define LIGHTNING_SIZZLE_SPEED 10 + +static unsigned long int nextrand = 1; + +int SWARM_rand(); +void SWARM_srand( unsigned int seed ); +void SWARM_TimedAnimate( callback_pcluster_s* cluster ); + +// class representing the PARSEC game world ----------------------------------- +// +class E_World +{ +public: + + // singly-linked lists for specific kinds of geometric objects ------------ + // + ShipObject* m_PShipObjects; // list of existing spacecraft objects + LaserObject* m_LaserObjects; // list of existing laser objects + MissileObject* m_MisslObjects; // list of existing missile objects + ExtraObject* m_ExtraObjects; // list of existing extras (power-ups) + CustomObject* m_CustmObjects; // list of existing custom objects + + int m_last_summoned_objectid; // object id of last object summoned + + // client -> e_global.h + // current and maximum number of extras + int m_nCurrentNumExtras; + int m_nCurrentNumPrtExtras; + + // next object number available + dword NextObjNumber; // 0 is free for special purposes + + // table of object classes (from which objects can be instantiated) ----------- + // + GenObject* ObjClasses[ MAX_DISTINCT_OBJCLASSES ]; + + // counters for concurrently displayed laser objects --------------------- + int MaxNumShots; + int NumShots; + +protected: + int _CountGenObjects( GenObject* walklist ); + + // free memory occupied by an object + void _FreeObjectMem( GenObject* objectpo ); + + // invoke constructor of custom object + void _InvokeCustomConstructor( GenObject* objectpo ); + + // invoke destructor of custom object + void _InvokeCustomDestructor( GenObject* objectpo ); + + // kill all instances of passed in object class (search only one list) + // + int _KillClassInstancesFromList( int objclass, GenObject *listpo ); + + // init object maintenance structures + void _InitObjCtrl(); + + //Cluster behaviour + void AnimateClusterBehavior( pcluster_s *cluster ); + void CalcConstantVelocityAnimation( linear_pcluster_s *cluster ); + void CalcSphereParticlePosition( Vertex3& position, geomv_t radius, int spheretype ); + void CalcSphereParticleExplosion( Vertex3& position, geomv_t speed ); + void CalcSpherePulse( Vertex3& position, Vertex3& pulsebase, geomv_t pulseval ); + int CalcSphereContraction( Vertex3& position, geomv_t speed ); + void CalcSphereParticleRotation( Vertex3& position, bams_t pitch, bams_t yaw, bams_t roll ); + void CalcSphereObjectAnimation( sphereobj_pcluster_s *cluster ); + void CalcCallbackTrajectoryAnimation( callback_pcluster_s *cluster ); + void CalcLightningAnimation( lightning_pcluster_s *cluster ); + void SetLightningParticlePosition( lightning_pcluster_s *cluster, particle_s particles[], Vertex3& current, Xmatrx tmatrx ); + //Particle disable/delete + void DisableParticle( pcluster_s *cluster, int pid ); + void PRT_DeleteCluster (pcluster_s* cluster); + void PRT_RemoveClusterFromAttachedList (objectbase_pcluster_s* cluster); + + + // standard ctor + E_World(); + +public: + + // SINGLETON access + static E_World* GetWorld() + { + static E_World _TheWorld; + return &_TheWorld; + } + + // correct pointers that have moved after object was instantiated + void OBJ_CorrectObjectInstance( GenObject *dstobj, GenObject *srcobj ); + + CustomObject *CreateVirtualObject( dword objtypeid, dword dwOwner ); + + // create object of class at position with orientation + GenObject* CreateObject( int objclass, const Xmatrx startmatrx, dword dwOwner ); + + // kill object contained in specific object list (search only one list) + int KillSpecificObject( dword objno, GenObject *listpo ); + + // kill a specific ship object from a list + int KillSpecificShipObject( dword objno ); + + // object counting + int GetNumShipObjects() { return _CountGenObjects( m_PShipObjects ); } + int GetNumLaserObjects() { return _CountGenObjects( m_LaserObjects ); } + int GetNumMisslObjects() { return _CountGenObjects( m_MisslObjects ); } + int GetNumExtraObjects() { return _CountGenObjects( m_ExtraObjects ); } + int GetNumCustmObjects() { return _CountGenObjects( m_CustmObjects ); } + + // return the object id of the last object that was summoned + int GetLastSummonedObjectID() { return m_last_summoned_objectid; } + + // return pointer to first "real" ship in ship-objects list + ShipObject* FetchFirstShip(); + + // return pointer to first laser in laser-objects list + LaserObject* FetchFirstLaser() { return (LaserObject*) m_LaserObjects->NextObj; } + + // return pointer to first missile in missile-objects list + MissileObject* FetchFirstMissile() { return (MissileObject*) m_MisslObjects->NextObj; } + + // return pointer to first extra in extra-objects list + ExtraObject* FetchFirstExtra() { return (ExtraObject *) m_ExtraObjects->NextObj; } + + // return pointer to first custom object in custom-objects list + CustomObject* FetchFirstCustom() { return (CustomObject *) m_CustmObjects->NextObj; } + + // fetch object contained in specific object list + GenObject* FetchSpecificObject( dword objno, GenObject* listpo ); + + // fetch object via object id (search in all object lists) + GenObject* FetchObject( dword objno ); + GenObject* SWARM_Init( int owner, Vertex3 *origin, ShipObject *targetpo, int randseed ); + + // free memory occupied by an object + void FreeObjectMem( GenObject *objectpo ); + + // free all object memory blocks contained in specific list + int FreeObjList( GenObject* listpo ); + + // free all object memory blocks + int FreeObjects(); + + // free all objects and particles + void KillAllObjects(); + + // kill all instances of passed in object class (search in all object lists) + int KillClassInstances( int objclass ); + + //FIXME: here ? + void IncreaseShotCounter(); + + //FIXME: here ? + void DecreaseShotCounter(); + + //Energy Field + void SFX_CreateEnergyField( Vertex3& origin ); + + //particle man + void InitParticleSystem(); + void PRT_InitParticle (particle_s& particle,int color,int sizebound,float refz,Vertex3* position,Vector3* velocity, int lifetime,int owner,pextinfo_s* extinfo); + void PRT_InitClusterParticle (pcluster_s* cluster,int pid,int bitmap,int color, int sizebound,float refz,Vertex3* position,Vector3* velocity,int lifetime,int owner,pextinfo_s* extinfo); + void PRT_AttachClusterToObject( GenObject* genobjpo, objectbase_pcluster_s* cluster ); + int PRT_DeleteAttachedClustersOfType( const GenObject * genobjpo, int animtype ); + void PRT_DeleteCluster_NoListRemoval( pcluster_s* cluster ); + + // allocate new particle cluster and insert into list ------------------------- + // + pcluster_s* PRT_NewCluster (dword type, int numelements, size_t auxstorage); + + particle_s* PRT_CreateLinearParticle (particle_s& particle); + sphereobj_pcluster_s* PRT_CreateParticleSphereObject (Vertex3& origin,geomv_t radius,int animtype,int clustersiz,int lifetime,pdrwinfo_s* pdinfo,int owner); + + pdef_s* PRT_AcquireParticleDefinitionById(int pdefid ); + pdef_s* PRT_AcquireParticleDefinition(const char* pdefname,int* retpdefid); + objectbase_pcluster_s* PRT_ObjectHasAttachedClustersOfType( GenObject* genobjpo, int animtype ); + + //Particle Animation call + void PAN_AnimateParticles(); + int SWARM_Animate( callback_pcluster_s* cluster ); + + //Lightning particles + lightning_pcluster_s* CreateLightningParticles( ShipObject *shippo, int owner ); + + //Photon + photon_sphere_pcluster_s* CreatePhotonSphere( ShipObject *shippo ); + void CalcPhotonSphereAnimation( photon_sphere_pcluster_s *cluster ); +}; + +#endif // _E_WORLD_H_ + diff --git a/src/parsec_server/include/e_world_trans.h b/src/parsec_server/include/e_world_trans.h new file mode 100755 index 0000000..57b85e0 --- /dev/null +++ b/src/parsec_server/include/e_world_trans.h @@ -0,0 +1,33 @@ +/* +* PARSEC HEADER: e_world_trans.h +*/ + +#ifndef _E_WORLD_TRANS_H_ +#define _E_WORLD_TRANS_H_ + +#ifdef PARSEC_SERVER + + // macros for migration purposes ------------------------------------------ + // + #define PShipObjects TheWorld->m_PShipObjects + #define LaserObjects TheWorld->m_LaserObjects + #define MisslObjects TheWorld->m_MisslObjects + #define ExtraObjects TheWorld->m_ExtraObjects + #define CustmObjects TheWorld->m_CustmObjects + + #define CreateObject TheWorld->CreateObject + #define FreeObjList TheWorld->FreeObjList + #define KillAllObjects TheWorld->KillAllObjects + #define FetchObject TheWorld->FetchObject + #define FetchFirstShip TheWorld->FetchFirstShip + #define FetchFirstLaser TheWorld->FetchFirstLaser + #define FetchFirstMissile TheWorld->FetchFirstMissile + #define FetchFirstExtra TheWorld->FetchFirstExtra + #define FetchFirstCustom TheWorld->FetchFirstCustom + #define KillClassInstances TheWorld->KillClassInstances + + #define ObjClasses TheWorld->ObjClasses + +#endif // PARSEC_SERVER + +#endif // _E_WORLD_TRANS_H_ diff --git a/src/parsec_server/include/g_main_sv.h b/src/parsec_server/include/g_main_sv.h new file mode 100755 index 0000000..2215e9a --- /dev/null +++ b/src/parsec_server/include/g_main_sv.h @@ -0,0 +1,275 @@ +/** PARSEC HEADER: g_main_sv.h +*/ + +#ifndef _G_MAIN_H_ +#define _G_MAIN_H_ + + +// join position control ------------------------------------------------------ +// +#define JP_M_S_D 500 // minimum join position distance +#define JP_RANGE 1000 // maximum join position range +#define JP_OFS (JP_RANGE/2) + + +// afterburner constants ------------------------------------------------------ +// +#define AFTERBURNER_SPEED 150000 +#define AFTERBURNER_ENERGY 2400 + + +// forward decls -------------------------------------------------------------- +// +template <class T> class UTL_List; +class G_Player; +class E_SimShipState; + +// default gametime limits ---------------------------------------------------- +// +#define DEFAULT_GAME_TIMELIMIT_SEC 600 +#define DEFAULT_RESTART_TIMELIMIT_SEC 15 +#define DEFAULT_KILL_LIMIT 10 + +// class handling the gametime management ------------------------------------- +// +class G_TimeManagement +{ +protected: + + int GameIsEndless; + refframe_t m_GameEndRefFrames; // refframes for timelimit + refframe_t m_GameRefFrames; // refframes of current game + refframe_t m_RestartRefFrames; // refframes of current restart timeout + + refframe_t m_RefFrameBase; + + int m_nSecGameTimeLimit; // timelimit for the game in sec. + int m_nSecRestartTimeLimit; // time to wait between restarts in sec. +public: + G_TimeManagement(); + + // realize console variables + void RealizeVariables(); + + // reset the game time management + void Reset(); + + // start a game + void StartGame(); + + // stop the game ( time limit hit ) + void StopGame_TimeLimit(); + + // stop the game ( kill limit hit ) + void StopGame_KillLimit(); + + // return whether the game is not yet started + int IsNotYetStarted(); + + // return whether currently a game is running + int IsGameRunning(); + + // return whether the game is finished + int IsGameFinished(); + + // check whether the game time limit is hit + int IsGameTimeLimitHit(); + + // check whether the restart timeout is over + int IsRestartTimeoutOver(); + + // return the current gametime in secs or special gametime codes + int GetCurGameTime(); +}; + + +// class holding the parsec game code ----------------------------------------- +// +class G_Main +{ +public: + + int EnergyExtraBoost; + int RepairExtraBoost; + int DumbPackNumMissls; + int HomPackNumMissls; + int SwarmPackNumMissls; + int ProxPackNumMines; + + int MegaShieldStrength; + + int m_NebulaID; + + G_Player* m_Players; + UTL_List<G_Player*>* m_CurConnectedPlayerList; // linked list of all currently connected players + UTL_List<G_Player*>* m_CurJoinedPlayerList; // linked list of all currently joined players + + G_TimeManagement m_TimeManager; + int m_nKillLimit; + +protected: + // default ctor/dtor + G_Main(); + ~G_Main(); + + // walk list of extra objects and advance them ( also handle timeout ) + void _WalkExtraObjects(); + + void _WalkCustomObjects(); + + // walk list of laser objects and advance them ( also handle timeout ) + void _WalkLaserObjects(); + + //walk list of missile objects and advance them + void _WalkMissileObjects(); + + //Do particle weapon maintenence + void MaintainDurationWeapons( int playerid ); + +public: + + // SINGLETON pattern + static G_Main* GetGame() + { + static G_Main _TheGame; + return &_TheGame; + } + + void Init(); + + // connect a player + void ConnectPlayer( int nClientID ); + + // disconnect a player + void DisconnectPlayer( int nClientID ); + + // join a player ( init join position ) + void JoinPlayer( int nClientID, E_SimShipState* pSimShipState ); + + // unjoin a player + void UnjoinPlayer( int nClientID ); + + // return the # of joined players + int GetNumJoined(); + + // retrieve the # of kills by this player + int GetPlayerKills( int nClientID ); + + // retrieve the last unjoin flag of the player + int GetPlayerLastUnjoinFlag( int nClientID ); + + // retrieve the last killer of the player + int GetPlayerLastKiller( int nClientID ); + + // record a kill of another client + void RecordKill( int nClientID ); + + // record a death of the client + void RecordDeath( int nClientID, int nClientID_Killer ); + + // reset the death info of the client + void ResetDeathInfo( int nClientID ); + + // get the playerinfo ( GAME ) + G_Player* GetPlayer( int nClientID ); + + // return a list of currently connected players + UTL_List<G_Player*>* GetCurConnectedPlayerList() { return m_CurConnectedPlayerList; } + + // return a list of currently joined players + UTL_List<G_Player*>* GetCurJoinedPlayerList() { return m_CurJoinedPlayerList; } + + // maintain weapon firing delays + void MaintainWeaponDelays(); + + // maintain the game + void MaintainGame(); + + // animate projectile objects (lasers and missiles) + void OBJ_AnimateProjectiles(); + + // animate non-projectile objects (extras, mines) + void OBJ_AnimateNonProjectiles(); + + // for maintaining countes on special powerups for players + void MaintainSpecialsCounters( ); + + //Duration weapons + void FireDurationWeapons(); + + // create actual laser object + LaserObject* OBJ_CreateLaserObject( ShipObject *shippo, int curlevel, int barrel, int nClientID ); + + // create actual dumb missile object + MissileObject* OBJ_CreateMissileObject( ShipObject *pShip, int barrel, int nClientID ); + + // create actual homing missile object + MissileObject* OBJ_CreateHomingMissileObject( ShipObject *pShip, int barrel, int nClientID, dword targetid ); + + // create actual mine object + MineObject* OBJ_CreateMineObject( ShipObject *pShip, int nClientID ); + + // create actual swarm object + GenObject* OBJ_CreateSwarm( ShipObject *pShip, int nClientID, dword targetid ); + + + // check availability of specified device + int OBJ_DeviceAvailable( ShipObject* pShip, int mask ); + + // return the current gametime in secs or special gametime codes + int GetCurGameTime() { return m_TimeManager.GetCurGameTime(); } + + // return whether a game + int IsGameRunning() { return m_TimeManager.IsGameRunning(); } + + // realize game vars from console vars + void RealizeVariables(); + + // create a stargate for a specific server at a position, with a direction + void CreateStargate( int serverid, Vector3* pos_spec, Vector3* dir_spec ); + +protected: + + // reset all player game vars + void _ResetPlayerVars(); +}; + +// class for handling game input ---------------------------------------------- +// +class G_Input +{ +protected: + G_Input() {} + ~G_Input() {} + +public: + // SINGLETON pattern + static G_Input* GetGameInput() + { + static G_Input _TheGameInput; + return &_TheGameInput; + } + + // activate the selected gun for a client + void ActivateGun( int nClientID, int SelectedGun ); + + // deactivate the selected gun for a client + void DeactivateGun( int nClientID, int SelectedGun ); + + // launch a dumb or homing missle + void LaunchMissile( int nClientID, dword targetid, int missileclass ); + + // launch a mine + void LaunchMine( int nClientID ); + + //Swarms + void LaunchSwarm(int nClientID, dword targetid); + + // EMP + void CreateEMP(int nClientID, byte UpgradeLevel); + +}; + + +#endif // _G_MAIN_H_ + diff --git a/src/parsec_server/include/g_player.h b/src/parsec_server/include/g_player.h new file mode 100755 index 0000000..870ce80 --- /dev/null +++ b/src/parsec_server/include/g_player.h @@ -0,0 +1,148 @@ +// class holding all GAME specific player information ------------------------- +// +// helix energy properties +#define HELIX_LIFETIME 1500 +#define HELIX_SPEED 0x10000 +#define MIN_HELIX_ENERGY 10 +#define HELIX_ENERGY_CONSUMPTION 2 +// properties of spreadfire particles Also move this someday +#define SPREADFIRE_PARTICLE_COLOR 174 +#define SPREADFIRE_BM_INDX BM_FIREBALL1 +#define SPREADFIRE_REF_Z 20.0f //400.0f +// lightning energy properties +#define MIN_LIGHTNING_ENERGY 10 +#define LIGHTNING_ENERGY_CONSUMPTION 7000 // 65536 = 1.0 + +class G_Player +{ + //NOTE: this class mirrors all information/actions of a single client ( in the client code ) + +protected: + + int m_nKills; // # of ships this player killed + int m_nDeaths; // # of deaths for this player + int m_nPoints; // # of points for this player + int m_nLastUnjoinFlag; // the last unjoin flag + int m_nLastKiller; // the playerid of last killer + + refframe_t m_FireDisableFrames; // refframes gun-fire is disabled ( = G_GLOBAL::FireDisable in old CLIENT code ) + refframe_t m_MissileDisableFrames; // refframes missile-fire is disabled ( = G_GLOBAL::MissileDisable in old CLIENT code ) + refframe_t helix_refframes_delta; + int m_CurGun; // currently selected gun outlet + int m_CurLauncher; // currently selected missile outlet + + int m_nClientID; + + E_SimPlayerInfo* m_pSimPlayerInfo; +protected: + + // create laser originating from player ship + void _OBJ_ShootLaser(); + + //Activate Helix Cannon + void _WFX_ActivateHelix(); + + //Activate lightning gun + void _WFX_ActivateLightning(); + + //Activate Photon cannon + void _WFX_ActivatePhoton(); + + // create a dumb missle originating from player ship + void _OBJ_LaunchMissile(); + + // create a homing missle originating from player ship + void _OBJ_LaunchHomingMissile( dword launcher, dword targetid ); + + void _OBJ_LaunchMine(); + + void _OBJ_LaunchSwarm( dword targetid ); + + +public: + G_Player() + { + Reset(); + } + + // reset all fields to defaults ( not connected ) + void Reset(); + + // set the player status to connected + void Connect( int nClientID ); + + // set the player status to disconnected + void Disconnect(); + + // user fired laser + void FireLaser(); + + // user fired Helix cannon + void FireHelix(); + int _WFX_MaintainHelix( ShipObject *shippo, int playerid ); + + //Deactivate Helix Cannon + void _WFX_DeactivateHelix(); + + // user fired Lightning cannon + void FireLightning(); + void WFX_MaintainLightning( ShipObject *shippo); + + //Deactivate lightning gun + void _WFX_DeactivateLightning(); + + // user fired Photon cannon + void FirePhoton(); + + //Deactivate Photon Cannon + void _WFX_DeactivatePhoton(); + + // user launched a dumb missle + void LaunchMissile(); + + // user launched a homping missle + void LaunchHomingMissile(dword launcher, dword targetid); + + // user launched a mine + void LaunchMine(); + + // user launched a swarm + void LaunchSwarm(dword targetid); + + // user fired emp + void FireEMP(byte Upgradelevel); + + // record a kill + void RecordKill(); + + // record a death + void RecordDeath( int nClientID_Killer ); + + // reset any death info + void ResetDeathInfo(); + + // reset all game variables + void ResetGameVars(); + + // maintain weapon firing delays + void MaintainWeaponDelays(); + + // return the ship object assigned to the player + ShipObject* GetShipObject(); + + // return # of kills for the player + int GetKills() { return m_nKills; } + + // return the last unjoin flag + int GetLastUnjoinFlag() { return m_nLastUnjoinFlag; } + + // return the player id that last killed this player + int GetLastKiller() { return m_nLastKiller; } + + //weapons cleanup + void WFX_EnsurePhotonInactive( ShipObject *shippo ); + void WFX_EnsureHelixInactive( ShipObject *shippo ); + void WFX_EnsureLightningInactive( ShipObject *shippo ); + void WFX_EnsureParticleWeaponsInactive( ShipObject *shippo ); +}; + diff --git a/src/parsec_server/include/inp_defs_sv.h b/src/parsec_server/include/inp_defs_sv.h new file mode 100644 index 0000000..7aed354 --- /dev/null +++ b/src/parsec_server/include/inp_defs_sv.h @@ -0,0 +1,30 @@ +/* + * PARSEC HEADER: inps_defs.h + */ + +#ifndef _INPS_DEFS_H_ +#define _INPS_DEFS_H_ + + +// ---------------------------------------------------------------------------- +// INPUT SUBSYSTEM (INP) related definitions - +// ---------------------------------------------------------------------------- + +// header of keyboard buffer +struct keybbuffer_s { + + dword ReadPos; + dword WritePos; + dword Data; +}; + +#define KEYB_BUFF_SIZ 16 + + +// global pointer to the keyboard buffer ( for console input ) ---------------- +// +extern keybbuffer_s* KeybBuffer; + +#endif // _INPS_DEFS_H_ + + diff --git a/src/parsec_server/include/inp_keybmap_sv.h b/src/parsec_server/include/inp_keybmap_sv.h new file mode 100644 index 0000000..0032fe2 --- /dev/null +++ b/src/parsec_server/include/inp_keybmap_sv.h @@ -0,0 +1,559 @@ +#ifndef _INPS_KEYBMAP_H_ +#define _INPS_KEYBMAP_H_ + +#ifndef _KEYCODES_H_ + #include "keycodes.h" +#endif // _KEYCODES_H_ + +dword INP_KeybMap[ 547 ] = { + CKC_NIL , // 0 , NO KEY NAME + CKC_NIL , // 1 , NO KEY NAME + CKC_NIL , // 2 , NO KEY NAME + CKC_NIL , // 3 , NO KEY NAME + CKC_NIL , // 4 , NO KEY NAME + CKC_NIL , // 5 , NO KEY NAME + CKC_NIL , // 6 , NO KEY NAME + CKC_NIL , // 7 , NO KEY NAME + CKC_BACKSPACE , // 8 , NO KEY NAME + CKC_TAB , // 9 , NO KEY NAME + CKC_ENTER , // a , NO KEY NAME + CKC_NIL , // b , NO KEY NAME + CKC_NIL , // c , NO KEY NAME + CKC_NIL , // d , NO KEY NAME + CKC_NIL , // e , NO KEY NAME + CKC_NIL , // f , NO KEY NAME + CKC_NIL , // 10 , NO KEY NAME + CKC_NIL , // 11 , NO KEY NAME + CKC_NIL , // 12 , NO KEY NAME + CKC_NIL , // 13 , NO KEY NAME + CKC_NIL , // 14 , NO KEY NAME + CKC_NIL , // 15 , NO KEY NAME + CKC_NIL , // 16 , NO KEY NAME + CKC_NIL , // 17 , NO KEY NAME + CKC_NIL , // 18 , NO KEY NAME + CKC_NIL , // 19 , NO KEY NAME + CKC_NIL , // 1a , NO KEY NAME + CKC_ESCAPE , // 1b , NO KEY NAME + CKC_NIL , // 1c , NO KEY NAME + CKC_NIL , // 1d , NO KEY NAME + CKC_NIL , // 1e , NO KEY NAME + CKC_NIL , // 1f , NO KEY NAME + CKC_SPACE , // 20 , NO KEY NAME + '!' , // 21 , NO KEY NAME + '"' , // 22 , NO KEY NAME + '#' , // 23 , NO KEY NAME + '$' , // 24 , NO KEY NAME + '%' , // 25 , NO KEY NAME + '&' , // 26 , NO KEY NAME + '\\' , // 27 , NO KEY NAME + '(' , // 28 , NO KEY NAME + ')' , // 29 , NO KEY NAME + '*' , // 2a , NO KEY NAME + '+' , // 2b , NO KEY NAME + ',' , // 2c , NO KEY NAME + '-' , // 2d , NO KEY NAME + '.' , // 2e , NO KEY NAME + '/' , // 2f , NO KEY NAME + '0' , // 30 , NO KEY NAME + '1' , // 31 , NO KEY NAME + '2' , // 32 , NO KEY NAME + '3' , // 33 , NO KEY NAME + '4' , // 34 , NO KEY NAME + '5' , // 35 , NO KEY NAME + '6' , // 36 , NO KEY NAME + '7' , // 37 , NO KEY NAME + '8' , // 38 , NO KEY NAME + '9' , // 39 , NO KEY NAME + ':' , // 3a , NO KEY NAME + ';' , // 3b , NO KEY NAME + '<' , // 3c , NO KEY NAME + '=' , // 3d , NO KEY NAME + '>' , // 3e , NO KEY NAME + '?' , // 3f , NO KEY NAME + '@' , // 40 , NO KEY NAME + 'A' , // 41 , NO KEY NAME + 'B' , // 42 , NO KEY NAME + 'C' , // 43 , NO KEY NAME + 'D' , // 44 , NO KEY NAME + 'E' , // 45 , NO KEY NAME + 'F' , // 46 , NO KEY NAME + 'G' , // 47 , NO KEY NAME + 'H' , // 48 , NO KEY NAME + 'I' , // 49 , NO KEY NAME + 'J' , // 4a , NO KEY NAME + 'K' , // 4b , NO KEY NAME + 'L' , // 4c , NO KEY NAME + 'M' , // 4d , NO KEY NAME + 'N' , // 4e , NO KEY NAME + 'O' , // 4f , NO KEY NAME + 'P' , // 50 , NO KEY NAME + 'Q' , // 51 , NO KEY NAME + 'R' , // 52 , NO KEY NAME + 'S' , // 53 , NO KEY NAME + 'T' , // 54 , NO KEY NAME + 'U' , // 55 , NO KEY NAME + 'V' , // 56 , NO KEY NAME + 'W' , // 57 , NO KEY NAME + 'X' , // 58 , NO KEY NAME + 'Y' , // 59 , NO KEY NAME + 'Z' , // 5a , NO KEY NAME + '[' , // 5b , NO KEY NAME + '\\' , // 5c , NO KEY NAME + ']' , // 5d , NO KEY NAME + '^' , // 5e , NO KEY NAME + '_' , // 5f , NO KEY NAME + '`' , // 60 , NO KEY NAME + 'a' , // 61 , NO KEY NAME + 'b' , // 62 , NO KEY NAME + 'c' , // 63 , NO KEY NAME + 'd' , // 64 , NO KEY NAME + 'e' , // 65 , NO KEY NAME + 'f' , // 66 , NO KEY NAME + 'g' , // 67 , NO KEY NAME + 'h' , // 68 , NO KEY NAME + 'i' , // 69 , NO KEY NAME + 'j' , // 6a , NO KEY NAME + 'k' , // 6b , NO KEY NAME + 'l' , // 6c , NO KEY NAME + 'm' , // 6d , NO KEY NAME + 'n' , // 6e , NO KEY NAME + 'o' , // 6f , NO KEY NAME + 'p' , // 70 , NO KEY NAME + 'q' , // 71 , NO KEY NAME + 'r' , // 72 , NO KEY NAME + 's' , // 73 , NO KEY NAME + 't' , // 74 , NO KEY NAME + 'u' , // 75 , NO KEY NAME + 'v' , // 76 , NO KEY NAME + 'w' , // 77 , NO KEY NAME + 'x' , // 78 , NO KEY NAME + 'y' , // 79 , NO KEY NAME + 'z' , // 7a , NO KEY NAME + '{' , // 7b , NO KEY NAME + '|' , // 7c , NO KEY NAME + '}' , // 7d , NO KEY NAME + CKC_NIL , // 7e , NO KEY NAME + CKC_NIL , // 7f , NO KEY NAME + CKC_NIL , // 80 , NO KEY NAME + CKC_NIL , // 81 , NO KEY NAME + CKC_NIL , // 82 , NO KEY NAME + CKC_NIL , // 83 , NO KEY NAME + CKC_NIL , // 84 , NO KEY NAME + CKC_NIL , // 85 , NO KEY NAME + CKC_NIL , // 86 , NO KEY NAME + CKC_NIL , // 87 , NO KEY NAME + CKC_NIL , // 88 , NO KEY NAME + CKC_NIL , // 89 , NO KEY NAME + CKC_NIL , // 8a , NO KEY NAME + CKC_NIL , // 8b , NO KEY NAME + CKC_NIL , // 8c , NO KEY NAME + CKC_NIL , // 8d , NO KEY NAME + CKC_NIL , // 8e , NO KEY NAME + CKC_NIL , // 8f , NO KEY NAME + CKC_NIL , // 90 , NO KEY NAME + CKC_NIL , // 91 , NO KEY NAME + CKC_NIL , // 92 , NO KEY NAME + CKC_NIL , // 93 , NO KEY NAME + CKC_NIL , // 94 , NO KEY NAME + CKC_NIL , // 95 , NO KEY NAME + CKC_NIL , // 96 , NO KEY NAME + CKC_NIL , // 97 , NO KEY NAME + CKC_NIL , // 98 , NO KEY NAME + CKC_NIL , // 99 , NO KEY NAME + CKC_NIL , // 9a , NO KEY NAME + CKC_NIL , // 9b , NO KEY NAME + CKC_NIL , // 9c , NO KEY NAME + CKC_NIL , // 9d , NO KEY NAME + CKC_NIL , // 9e , NO KEY NAME + CKC_NIL , // 9f , NO KEY NAME + CKC_NIL , // a0 , NO KEY NAME + CKC_NIL , // a1 , NO KEY NAME + CKC_NIL , // a2 , NO KEY NAME + CKC_NIL , // a3 , NO KEY NAME + CKC_NIL , // a4 , NO KEY NAME + CKC_NIL , // a5 , NO KEY NAME + CKC_NIL , // a6 , NO KEY NAME + CKC_NIL , // a7 , NO KEY NAME + CKC_NIL , // a8 , NO KEY NAME + CKC_NIL , // a9 , NO KEY NAME + CKC_NIL , // aa , NO KEY NAME + CKC_NIL , // ab , NO KEY NAME + CKC_NIL , // ac , NO KEY NAME + CKC_NIL , // ad , NO KEY NAME + CKC_NIL , // ae , NO KEY NAME + CKC_NIL , // af , NO KEY NAME + CKC_NIL , // b0 , NO KEY NAME + CKC_NIL , // b1 , NO KEY NAME + CKC_NIL , // b2 , NO KEY NAME + CKC_NIL , // b3 , NO KEY NAME + CKC_NIL , // b4 , NO KEY NAME + CKC_NIL , // b5 , NO KEY NAME + CKC_NIL , // b6 , NO KEY NAME + CKC_NIL , // b7 , NO KEY NAME + CKC_NIL , // b8 , NO KEY NAME + CKC_NIL , // b9 , NO KEY NAME + CKC_NIL , // ba , NO KEY NAME + CKC_NIL , // bb , NO KEY NAME + CKC_NIL , // bc , NO KEY NAME + CKC_NIL , // bd , NO KEY NAME + CKC_NIL , // be , NO KEY NAME + CKC_NIL , // bf , NO KEY NAME + CKC_NIL , // c0 , NO KEY NAME + CKC_NIL , // c1 , NO KEY NAME + CKC_NIL , // c2 , NO KEY NAME + CKC_NIL , // c3 , NO KEY NAME + CKC_NIL , // c4 , NO KEY NAME + CKC_NIL , // c5 , NO KEY NAME + CKC_NIL , // c6 , NO KEY NAME + CKC_NIL , // c7 , NO KEY NAME + CKC_NIL , // c8 , NO KEY NAME + CKC_NIL , // c9 , NO KEY NAME + CKC_NIL , // ca , NO KEY NAME + CKC_NIL , // cb , NO KEY NAME + CKC_NIL , // cc , NO KEY NAME + CKC_NIL , // cd , NO KEY NAME + CKC_NIL , // ce , NO KEY NAME + CKC_NIL , // cf , NO KEY NAME + CKC_NIL , // d0 , NO KEY NAME + CKC_NIL , // d1 , NO KEY NAME + CKC_NIL , // d2 , NO KEY NAME + CKC_NIL , // d3 , NO KEY NAME + CKC_NIL , // d4 , NO KEY NAME + CKC_NIL , // d5 , NO KEY NAME + CKC_NIL , // d6 , NO KEY NAME + CKC_NIL , // d7 , NO KEY NAME + CKC_NIL , // d8 , NO KEY NAME + CKC_NIL , // d9 , NO KEY NAME + CKC_NIL , // da , NO KEY NAME + CKC_NIL , // db , NO KEY NAME + CKC_NIL , // dc , NO KEY NAME + CKC_NIL , // dd , NO KEY NAME + CKC_NIL , // de , NO KEY NAME + CKC_NIL , // df , NO KEY NAME + CKC_NIL , // e0 , NO KEY NAME + CKC_NIL , // e1 , NO KEY NAME + CKC_NIL , // e2 , NO KEY NAME + CKC_NIL , // e3 , NO KEY NAME + CKC_NIL , // e4 , NO KEY NAME + CKC_NIL , // e5 , NO KEY NAME + CKC_NIL , // e6 , NO KEY NAME + CKC_NIL , // e7 , NO KEY NAME + CKC_NIL , // e8 , NO KEY NAME + CKC_NIL , // e9 , NO KEY NAME + CKC_NIL , // ea , NO KEY NAME + CKC_NIL , // eb , NO KEY NAME + CKC_NIL , // ec , NO KEY NAME + CKC_NIL , // ed , NO KEY NAME + CKC_NIL , // ee , NO KEY NAME + CKC_NIL , // ef , NO KEY NAME + CKC_NIL , // f0 , NO KEY NAME + CKC_NIL , // f1 , NO KEY NAME + CKC_NIL , // f2 , NO KEY NAME + CKC_NIL , // f3 , NO KEY NAME + CKC_NIL , // f4 , NO KEY NAME + CKC_NIL , // f5 , NO KEY NAME + CKC_NIL , // f6 , NO KEY NAME + CKC_NIL , // f7 , NO KEY NAME + CKC_NIL , // f8 , NO KEY NAME + CKC_NIL , // f9 , NO KEY NAME + CKC_NIL , // fa , NO KEY NAME + CKC_NIL , // fb , NO KEY NAME + CKC_NIL , // fc , NO KEY NAME + CKC_NIL , // fd , NO KEY NAME + CKC_NIL , // fe , NO KEY NAME + CKC_NIL , // ff , NO KEY NAME + CKC_NIL , // 100 , NO KEY NAME + CKC_NIL , // 101 , KEY_BREAK + CKC_CURSORDOWN , // 102 , KEY_DOWN + CKC_CURSORUP , // 103 , KEY_UP + CKC_CURSORLEFT , // 104 , KEY_LEFT + CKC_CURSORRIGHT , // 105 , KEY_RIGHT + CKC_HOME , // 106 , KEY_HOME + CKC_NIL , // 107 , KEY_BACKSPACE + CKC_NIL , // 108 , KEY_F0 + CKC_NIL , // 109 , KEY_F(1) + CKC_NIL , // 10a , KEY_F(2) + CKC_NIL , // 10b , KEY_F(3) + CKC_NIL , // 10c , KEY_F(4) + CKC_NIL , // 10d , KEY_F(5) + CKC_NIL , // 10e , KEY_F(6) + CKC_NIL , // 10f , KEY_F(7) + CKC_NIL , // 110 , KEY_F(8) + CKC_NIL , // 111 , KEY_F(9) + CKC_NIL , // 112 , KEY_F(10) + CKC_NIL , // 113 , KEY_F(11) + CKC_NIL , // 114 , KEY_F(12) + CKC_NIL , // 115 , KEY_F(13) + CKC_NIL , // 116 , KEY_F(14) + CKC_NIL , // 117 , KEY_F(15) + CKC_NIL , // 118 , KEY_F(16) + CKC_NIL , // 119 , KEY_F(17) + CKC_NIL , // 11a , KEY_F(18) + CKC_NIL , // 11b , KEY_F(19) + CKC_NIL , // 11c , KEY_F(20) + CKC_NIL , // 11d , KEY_F(21) + CKC_NIL , // 11e , KEY_F(22) + CKC_NIL , // 11f , KEY_F(23) + CKC_NIL , // 120 , KEY_F(24) + CKC_NIL , // 121 , KEY_F(25) + CKC_NIL , // 122 , KEY_F(26) + CKC_NIL , // 123 , KEY_F(27) + CKC_NIL , // 124 , KEY_F(28) + CKC_NIL , // 125 , KEY_F(29) + CKC_NIL , // 126 , KEY_F(30) + CKC_NIL , // 127 , KEY_F(31) + CKC_NIL , // 128 , KEY_F(32) + CKC_NIL , // 129 , KEY_F(33) + CKC_NIL , // 12a , KEY_F(34) + CKC_NIL , // 12b , KEY_F(35) + CKC_NIL , // 12c , KEY_F(36) + CKC_NIL , // 12d , KEY_F(37) + CKC_NIL , // 12e , KEY_F(38) + CKC_NIL , // 12f , KEY_F(39) + CKC_NIL , // 130 , KEY_F(40) + CKC_NIL , // 131 , KEY_F(41) + CKC_NIL , // 132 , KEY_F(42) + CKC_NIL , // 133 , KEY_F(43) + CKC_NIL , // 134 , KEY_F(44) + CKC_NIL , // 135 , KEY_F(45) + CKC_NIL , // 136 , KEY_F(46) + CKC_NIL , // 137 , KEY_F(47) + CKC_NIL , // 138 , KEY_F(48) + CKC_NIL , // 139 , KEY_F(49) + CKC_NIL , // 13a , KEY_F(50) + CKC_NIL , // 13b , KEY_F(51) + CKC_NIL , // 13c , KEY_F(52) + CKC_NIL , // 13d , KEY_F(53) + CKC_NIL , // 13e , KEY_F(54) + CKC_NIL , // 13f , KEY_F(55) + CKC_NIL , // 140 , KEY_F(56) + CKC_NIL , // 141 , KEY_F(57) + CKC_NIL , // 142 , KEY_F(58) + CKC_NIL , // 143 , KEY_F(59) + CKC_NIL , // 144 , KEY_F(60) + CKC_NIL , // 145 , KEY_F(61) + CKC_NIL , // 146 , KEY_F(62) + CKC_NIL , // 147 , KEY_F(63) + CKC_NIL , // 148 , KEY_DL + CKC_NIL , // 149 , KEY_IL + CKC_DELETE , // 14a , KEY_DC + CKC_INSERT , // 14b , KEY_IC + CKC_NIL , // 14c , KEY_EIC + CKC_NIL , // 14d , KEY_CLEAR + CKC_NIL , // 14e , KEY_EOS + CKC_NIL , // 14f , KEY_EOL + CKC_NIL , // 150 , KEY_SF + CKC_NIL , // 151 , KEY_SR + CKC_PAGEDOWN , // 152 , KEY_NPAGE + CKC_PAGEUP , // 153 , KEY_PPAGE + CKC_NIL , // 154 , KEY_STAB + CKC_NIL , // 155 , KEY_CTAB + CKC_NIL , // 156 , KEY_CATAB + CKC_NIL , // 157 , KEY_ENTER + CKC_NIL , // 158 , KEY_SRESET + CKC_NIL , // 159 , KEY_RESET + CKC_NIL , // 15a , KEY_PRINT + CKC_NIL , // 15b , KEY_LL + CKC_NIL , // 15c , KEY_ABORT + CKC_NIL , // 15d , KEY_SHELP + CKC_NIL , // 15e , KEY_LHELP + CKC_NIL , // 15f , KEY_BTAB + CKC_NIL , // 160 , KEY_BEG + CKC_NIL , // 161 , KEY_CANCEL + CKC_NIL , // 162 , KEY_CLOSE + CKC_NIL , // 163 , KEY_COMMAND + CKC_NIL , // 164 , KEY_COPY + CKC_NIL , // 165 , KEY_CREATE + CKC_END , // 166 , KEY_END + CKC_NIL , // 167 , KEY_EXIT + CKC_NIL , // 168 , KEY_FIND + CKC_NIL , // 169 , KEY_HELP + CKC_NIL , // 16a , KEY_MARK + CKC_NIL , // 16b , KEY_MESSAGE + CKC_NIL , // 16c , KEY_MOVE + CKC_NIL , // 16d , KEY_NEXT + CKC_NIL , // 16e , KEY_OPEN + CKC_NIL , // 16f , KEY_OPTIONS + CKC_NIL , // 170 , KEY_PREVIOUS + CKC_NIL , // 171 , KEY_REDO + CKC_NIL , // 172 , KEY_REFERENCE + CKC_NIL , // 173 , KEY_REFRESH + CKC_NIL , // 174 , KEY_REPLACE + CKC_NIL , // 175 , KEY_RESTART + CKC_NIL , // 176 , KEY_RESUME + CKC_NIL , // 177 , KEY_SAVE + CKC_NIL , // 178 , KEY_SBEG + CKC_NIL , // 179 , KEY_SCANCEL + CKC_NIL , // 17a , KEY_SCOMMAND + CKC_NIL , // 17b , KEY_SCOPY + CKC_NIL , // 17c , KEY_SCREATE + CKC_NIL , // 17d , KEY_SDC + CKC_NIL , // 17e , KEY_SDL + CKC_NIL , // 17f , KEY_SELECT + CKC_NIL , // 180 , KEY_SEND + CKC_NIL , // 181 , KEY_SEOL + CKC_NIL , // 182 , KEY_SEXIT + CKC_NIL , // 183 , KEY_SFIND + CKC_NIL , // 184 , KEY_SHOME + CKC_NIL , // 185 , KEY_SIC + CKC_NIL , // 186 , NO KEY NAME + CKC_CURSORLEFT_SHIFTED,// 187 , KEY_SLEFT + CKC_NIL , // 188 , KEY_SMESSAGE + CKC_NIL , // 189 , KEY_SMOVE + CKC_NIL , // 18a , KEY_SNEXT + CKC_NIL , // 18b , KEY_SOPTIONS + CKC_NIL , // 18c , KEY_SPREVIOUS + CKC_NIL , // 18d , KEY_SPRINT + CKC_NIL , // 18e , KEY_SREDO + CKC_NIL , // 18f , KEY_SREPLACE + CKC_CURSORRIGHT_SHIFTED,// 190 , KEY_SRIGHT + CKC_NIL , // 191 , KEY_SRSUME + CKC_NIL , // 192 , KEY_SSAVE + CKC_NIL , // 193 , KEY_SSUSPEND + CKC_NIL , // 194 , KEY_SUNDO + CKC_NIL , // 195 , KEY_SUSPEND + CKC_NIL , // 196 , KEY_UNDO + CKC_NIL , // 197 , ALT_0 + CKC_NIL , // 198 , ALT_1 + CKC_NIL , // 199 , ALT_2 + CKC_NIL , // 19a , ALT_3 + CKC_NIL , // 19b , ALT_4 + CKC_NIL , // 19c , ALT_5 + CKC_NIL , // 19d , ALT_6 + CKC_NIL , // 19e , ALT_7 + CKC_NIL , // 19f , ALT_8 + CKC_NIL , // 1a0 , ALT_9 + CKC_NIL , // 1a1 , ALT_A + CKC_NIL , // 1a2 , ALT_B + CKC_NIL , // 1a3 , ALT_C + CKC_NIL , // 1a4 , ALT_D + CKC_NIL , // 1a5 , ALT_E + CKC_NIL , // 1a6 , ALT_F + CKC_NIL , // 1a7 , ALT_G + CKC_NIL , // 1a8 , ALT_H + CKC_NIL , // 1a9 , ALT_I + CKC_NIL , // 1aa , ALT_J + CKC_NIL , // 1ab , ALT_K + CKC_NIL , // 1ac , ALT_L + CKC_NIL , // 1ad , ALT_M + CKC_NIL , // 1ae , ALT_N + CKC_NIL , // 1af , ALT_O + CKC_NIL , // 1b0 , ALT_P + CKC_NIL , // 1b1 , ALT_Q + CKC_NIL , // 1b2 , ALT_R + CKC_NIL , // 1b3 , ALT_S + CKC_NIL , // 1b4 , ALT_T + CKC_NIL , // 1b5 , ALT_U + CKC_NIL , // 1b6 , ALT_V + CKC_NIL , // 1b7 , ALT_W + CKC_NIL , // 1b8 , ALT_X + CKC_NIL , // 1b9 , ALT_Y + CKC_NIL , // 1ba , ALT_Z + CKC_NIL , // 1bb , CTL_LEFT + CKC_NIL , // 1bc , CTL_RIGHT + CKC_NIL , // 1bd , CTL_PGUP + CKC_NIL , // 1be , CTL_PGDN + CKC_NIL , // 1bf , CTL_HOME + CKC_NIL , // 1c0 , CTL_END + CKC_NIL , // 1c1 , KEY_A1 + CKC_NIL , // 1c2 , KEY_A2 + CKC_NIL , // 1c3 , KEY_A3 + CKC_NIL , // 1c4 , KEY_B1 + CKC_NIL , // 1c5 , KEY_B2 + CKC_NIL , // 1c6 , KEY_B3 + CKC_NIL , // 1c7 , KEY_C1 + CKC_NIL , // 1c8 , KEY_C2 + CKC_NIL , // 1c9 , KEY_C3 + CKC_NIL , // 1ca , PADSLASH + CKC_NIL , // 1cb , PADENTER + CKC_NIL , // 1cc , CTL_PADENTER + CKC_NIL , // 1cd , ALT_PADENTER + CKC_NIL , // 1ce , PADSTOP + CKC_NIL , // 1cf , PADSTAR + CKC_NIL , // 1d0 , PADMINUS + CKC_NIL , // 1d1 , PADPLUS + CKC_NIL , // 1d2 , CTL_PADSTOP + CKC_NIL , // 1d3 , CTL_PADCENTER + CKC_NIL , // 1d4 , CTL_PADPLUS + CKC_NIL , // 1d5 , CTL_PADMINUS + CKC_NIL , // 1d6 , CTL_PADSLASH + CKC_NIL , // 1d7 , CTL_PADSTAR + CKC_NIL , // 1d8 , ALT_PADPLUS + CKC_NIL , // 1d9 , ALT_PADMINUS + CKC_NIL , // 1da , ALT_PADSLASH + CKC_NIL , // 1db , ALT_PADSTAR + CKC_NIL , // 1dc , ALT_PADSTOP + CKC_NIL , // 1dd , CTL_INS + CKC_NIL , // 1de , ALT_DEL + CKC_NIL , // 1df , ALT_INS + CKC_NIL , // 1e0 , CTL_UP + CKC_NIL , // 1e1 , CTL_DOWN + CKC_NIL , // 1e2 , CTL_TAB + CKC_NIL , // 1e3 , ALT_TAB + CKC_NIL , // 1e4 , ALT_MINUS + CKC_NIL , // 1e5 , ALT_EQUAL + CKC_NIL , // 1e6 , ALT_HOME + CKC_NIL , // 1e7 , ALT_PGUP + CKC_NIL , // 1e8 , ALT_PGDN + CKC_NIL , // 1e9 , ALT_END + CKC_NIL , // 1ea , ALT_UP + CKC_NIL , // 1eb , ALT_DOWN + CKC_NIL , // 1ec , ALT_RIGHT + CKC_NIL , // 1ed , ALT_LEFT + CKC_NIL , // 1ee , ALT_ENTER + CKC_NIL , // 1ef , ALT_ESC + CKC_NIL , // 1f0 , ALT_BQUOTE + CKC_NIL , // 1f1 , ALT_LBRACKET + CKC_NIL , // 1f2 , ALT_RBRACKET + CKC_NIL , // 1f3 , ALT_SEMICOLON + CKC_NIL , // 1f4 , ALT_FQUOTE + CKC_NIL , // 1f5 , ALT_COMMA + CKC_NIL , // 1f6 , ALT_STOP + CKC_NIL , // 1f7 , ALT_FSLASH + CKC_NIL , // 1f8 , ALT_BKSP + CKC_NIL , // 1f9 , CTL_BKSP + CKC_NIL , // 1fa , PAD0 + CKC_NIL , // 1fb , CTL_PAD0 + CKC_NIL , // 1fc , CTL_PAD1 + CKC_NIL , // 1fd , CTL_PAD2 + CKC_NIL , // 1fe , CTL_PAD3 + CKC_NIL , // 1ff , CTL_PAD4 + CKC_NIL , // 200 , CTL_PAD5 + CKC_NIL , // 201 , CTL_PAD6 + CKC_NIL , // 202 , CTL_PAD7 + CKC_NIL , // 203 , CTL_PAD8 + CKC_NIL , // 204 , CTL_PAD9 + CKC_NIL , // 205 , ALT_PAD0 + CKC_NIL , // 206 , ALT_PAD1 + CKC_NIL , // 207 , ALT_PAD2 + CKC_NIL , // 208 , ALT_PAD3 + CKC_NIL , // 209 , ALT_PAD4 + CKC_NIL , // 20a , ALT_PAD5 + CKC_NIL , // 20b , ALT_PAD6 + CKC_NIL , // 20c , ALT_PAD7 + CKC_NIL , // 20d , ALT_PAD8 + CKC_NIL , // 20e , ALT_PAD9 + CKC_NIL , // 20f , CTL_DEL + CKC_NIL , // 210 , ALT_BSLASH + CKC_NIL , // 211 , CTL_ENTER + CKC_NIL , // 212 , SHF_PADENTER + CKC_NIL , // 213 , SHF_PADSLASH + CKC_NIL , // 214 , SHF_PADSTAR + CKC_NIL , // 215 , SHF_PADPLUS + CKC_NIL , // 216 , SHF_PADMINUS + CKC_CURSORUP_SHIFTED, // 217 , SHF_UP + CKC_CURSORDOWN_SHIFTED,// 218 , SHF_DOWN + CKC_NIL , // 219 , SHF_IC + CKC_NIL , // 21a , SHF_DC + CKC_NIL , // 21b , KEY_MOUSE + CKC_NIL , // 21c , KEY_SHIFT_L + CKC_NIL , // 21d , KEY_SHIFT_R + CKC_NIL , // 21e , KEY_CONTROL_L + CKC_NIL , // 21f , KEY_CONTROL_R + CKC_NIL , // 220 , KEY_ALT_L + CKC_NIL , // 221 , KEY_ALT_R + CKC_NIL , // 222 , KEY_RESIZE +}; + + +#endif // _INPS_KEYBMAP_H_ diff --git a/src/parsec_server/include/inp_main_sv.h b/src/parsec_server/include/inp_main_sv.h new file mode 100644 index 0000000..7d12722 --- /dev/null +++ b/src/parsec_server/include/inp_main_sv.h @@ -0,0 +1,12 @@ +/* +* PARSEC HEADER: inp_main_sv.h +*/ + +#ifndef _INP_MAIN_SV_H_ +#define _INP_MAIN_SV_H_ + +void INP_Init(); +void INP_Kill(); +void INP_HandleInput(); + +#endif // _INP_MAIN_SV_H_ diff --git a/src/parsec_server/include/net_game_sv.h b/src/parsec_server/include/net_game_sv.h new file mode 100644 index 0000000..81d5376 --- /dev/null +++ b/src/parsec_server/include/net_game_sv.h @@ -0,0 +1,30 @@ +/* + * PARSEC HEADER: net_game_sv.h + */ + +#ifndef _NET_GAME_SV_H_ +#define _NET_GAME_SV_H_ + + +// value alive counter is set to immediately +// after a packet has been received (default 20s) +#define MAX_ALIVE_COUNTER 60 * 600 + +// bufferno for virtual buffer +#define VIRTUAL_BUFFER_ID -1 // packet is virtual (packet replay) + +// ping packet type +//#define PING_REQUEST 1 // request packet +//#define PING_REPLY 0 // reply packet + +// special player ids +//#define PLAYERID_SERVER -1 // packet destination is the server +//#define PLAYERID_ANONYMOUS -2 // packet sender is not connected + +// special ship ids +#define SHIPID_NOSHIP -1 // there is no ship (player not joined) + + +#endif // _NET_GAME_SV_H_ + + diff --git a/src/parsec_server/include/net_packetdriver.h b/src/parsec_server/include/net_packetdriver.h new file mode 100755 index 0000000..f4b1f1d --- /dev/null +++ b/src/parsec_server/include/net_packetdriver.h @@ -0,0 +1,138 @@ +/* +* PARSEC HEADER: NET_PacketDriver.h +*/ + +#ifndef _NET_PACKETDRIVER_H_ +#define _NET_PACKETDRIVER_H_ + +// forward decls -------------------------------------------------------------- +// +struct NetPacket_GMSV; +class NET_UDPDriver; +class NET_Stream; +class E_GameServer; +class E_PacketHandler; + + +// number of listen buffers in listen buffers array --------------------------- +// +#define NUM_LISTEN_BUFFERS ( MAX_NET_ALLOC_SLOTS * 2 ) + +// high level packet chain (preprocessed to meet ordering requirements) ------- +// +struct PacketChainBlock { + + int bufferno; + NetPacket_GMSV* gamepacket; + int messageid; + PacketChainBlock* nextblock; +}; + +struct PacketChainHead : PacketChainBlock { + int chainlength; +}; + + +// general packet handling function ------------------------------------------- +// +class NET_PacketDriver +{ +protected: + + NetPacketExternal_GMSV* SendNetPacketExternal; + NetPacketExternal_GMSV* RecvNetPacketExternal; + int ListenStatus [ NUM_LISTEN_BUFFERS ]; + sockaddr_in ListenAddress [ NUM_LISTEN_BUFFERS ]; + node_t ListenSenderStorage [ NUM_LISTEN_BUFFERS ]; + NetPacket_GMSV* NetPacketsInternal [ NUM_LISTEN_BUFFERS ]; + + PacketChainHead* ReceivedPacketsChain; + + NET_Stream* m_Streams; // add one player for PLAYERID_ANONYMOUS + +protected: + // retrieve next packet from received packets chain + PacketChainBlock* _FetchPacketFromChain(); + + // allocate received packets chain (head node) + int _AllocPacketsChain(); + + // free all blocks in received packets chain + void _FreePacketsChain(); + + // release block containing pointer and info of packet + void _ReleasePacketFromChain( PacketChainBlock* block ); + + // pre-process received packets + void _BuildReceivedPacketsChain(); + + // insert received packet into chain + int _ChainPacket( NetPacket_GMSV* gamepacket, int bufid ); + + // check regular player packets for duplicates + int _FilterPacketDuplicate( int senderid, int messageid ); + + // process packet before inserting it into packet chain + void _PreprocessInPacket( NetPacket_GMSV* gamepacket ); + + // log packet loss statistics + void _LogPacketLossStats( int numpackets, int packetok, int incoming ); + + // fetch a packet into a listen buffer + int _UDP_FetchPacket( int bufid ); + + // determine if packet should be artificially dropped + int _CheckForcePacketDrop(); + + // record a packet + void _RecordPacket( int bufid ); + + NET_PacketDriver(); + virtual ~NET_PacketDriver(); + +public: + + // SINGLETON pattern + static NET_PacketDriver* GetPacketDriver() + { + static NET_PacketDriver _ThePacketDriver; + return &_ThePacketDriver; + } + + // invoke processing function for all packets currently in chain + void NET_ProcessPacketChain(); + + // retrieve sender's node address from listen header + node_t* GetPktSender( int bufid ); + + // send a packet to a node + int SendPacket( NetPacket_GMSV* gamepacket, node_t* node, int nClientID, E_REList* pReliable, E_REList* pUnreliable ); + + // send a datagram ( unreliable, unnumbered packet ) to a node + int SendDatagram( NetPacket_GMSV* gamepacket, node_t* node, int nClientID, E_REList* pUnreliable ); + + // fill standard fields in gamepacket header + void FillStdGameHeader( byte command, NetPacket_GMSV* gamepacket ); + + // reset all data members + void Reset(); + + // reset the stream of a specific client + void ResetStream( int nClientID ); + + // connect the stream of a client + void ConnectStream( int nClientID ); + + // flush the reliable backbuffer of a stream + void FlushReliableBuffer( int nClientID ); + + // return the message id used in the next packet that is sent + int GetNextOutMessageId( int nClientID ); + + // get the stream message status + NET_Stream* GetStream( int nClientID ); + + friend class E_GameServer; +}; + +#endif // _NET_PACKETDRIVER_H_ diff --git a/src/parsec_server/include/net_subh_sv.h b/src/parsec_server/include/net_subh_sv.h new file mode 100755 index 0000000..e177b11 --- /dev/null +++ b/src/parsec_server/include/net_subh_sv.h @@ -0,0 +1,128 @@ +/* + * PARSEC HEADER: net_subh_sv.h + */ + +#ifndef _NET_SUBH_SV_H_ +#define _NET_SUBH_SV_H_ + + +// ---------------------------------------------------------------------------- +// NETWORKING SUBSYSTEM (NET) system-dependent function declarations - +// ---------------------------------------------------------------------------- +//#define NULL 0 +#include "e_relist.h" + +//// remote event functions (RMEV) +// +#define NET_RmEvAllowed TheSimulator->GetInputREList()->RmEvAllowed +#define NET_RmEvGetSize E_REList::RmEvGetSize +#define NETs_RmEvList_GetMaxSize E_REList::GetMaxSizeInPacket +//int NET_RmEvPlayerName( char *name ); +//int NET_RmEvSendText( const char *text ); +//int NET_RmEvObject( const GenObject *objpo ); +//int NET_RmEvLaser( const LaserObject *laserpo ); +//int NET_RmEvMissile( const MissileObject *missilepo, dword targetobjid ); +//int NET_RmEvExtra( const ExtraObject *extrapo ); +//int NET_RmEvParticleObject( int type, const Vertex3& origin ); +//int NET_RmEvKillObject( dword objid, byte listno ); +//int NET_RmEvWeaponState( dword weapon, byte state, int energy, dword specials ); +#define NET_RmEvStateSync TheSimulator->GetInputREList()->RmEvStateSync +//int NET_RmEvCreateSwarm( Vector3 *origin, dword targetobjid, dword seed ); +//int NET_RmEvCreateEmp( int upgradelevel ); +//RE_PlayerAndShipStatus* NET_RmEvShipStatus( byte last_unjoin_flag ); +// +// +//void NET_ExecRmEvCreateObject ( RE_Header *rmev, int senderid ); +//void NET_ExecRmEvCreateLaser ( RE_Header *rmev, int senderid ); +//void NET_ExecRmEvCreateMissile ( RE_Header *rmev, int senderid ); +//void NET_ExecRmEvParticleObject ( RE_Header *rmev, int senderid ); +//void NET_ExecRmEvCreateExtra ( RE_Header *rmev, int senderid ); +//void NET_ExecRmEvKillObject ( RE_Header *rmev ); +//void NET_ExecRmEvSendText ( RE_Header *rmev, int senderid ); +//void NET_ExecRmEvPlayerName ( RE_Header *rmev, int senderid ); +//void NET_ExecRmEvWeaponState ( RE_Header *rmev, int senderid ); +//void NET_ExecRmEvStateSync ( RE_Header *rmev, int senderid ); +//void NET_ExecRmEvCreateSwarm ( RE_Header *rmev, int senderid ); +//void NET_ExecRmEvCreateEmp ( RE_Header *rmev, int senderid ); +// +//void NET_ExecRmEvGameState ( RE_GameState* gamestate ); +//void NET_ExecRmEvKillStats ( RE_KillStats* killstats ); +//void NET_ExecRmEvCommandInfo ( RE_CommandInfo* commandinfo ); +// +//// utility functions (UTIL) +// +//int NET_ProtocolPEER(); +//int NET_ProtocolGMSV(); +// +//int NET_ConnectedPEER(); +//int NET_ConnectedGMSV(); +// +//char* NET_FetchPlayerName( int playerid ); +//int NET_SetPlayerName( char *name ); +//ShipObject* NET_FetchOwnersShip( int ownerid ); +//void NET_SetPlayerKillStat( int playerid, int amount ); +// +//int NET_KillStatLimitReached( int limit ); +//void NET_KillStatForceIdleZero(); +// +//void NET_DrawEntryModeText(); +// +// +//// protocol api functions +// +//int NETs_Connect(); +//int NETs_Disconnect(); +//int NETs_Join(); +//int NETs_Unjoin( byte flag ); +// +//int NETs_UpdateName(); +// +//void NETs_MaintainNet(); +// +//// protocol api - game functions +// +//size_t NETs_RmEvList_GetMaxSize(); +//void NETs_UpdateKillStats( RE_KillStats* killstats ); +// +// protocol api - packet handling functions + +size_t NETs_HandleOutPacket ( const NetPacket* int_gamepacket, NetPacketExternal* ext_gamepacket ); +//size_t NETs_HandleOutPacket_DEMO ( const NetPacket* int_gamepacket, NetPacketExternal* ext_gamepacket ); +int NETs_HandleInPacket ( const NetPacketExternal* ext_gamepacket, const int ext_pktsize, NetPacket* int_gamepacket ); +//int NETs_HandleInPacket_DEMO ( const NetPacketExternal* ext_gamepacket, NetPacket* int_gamepacket, size_t* psize_external ); +//size_t NETs_NetPacketExternal_DEMO_GetSize( NetPacketExternal* ext_gamepacket ); +//void NETs_StdGameHeader( byte command, NetPacket* gamepacket ); +//void NETs_WritePacketInfo( FILE *fp, NetPacketExternal* ext_gamepacket ); +// +//// packet api functions +// +//int NETs_CompareNodes( node_t *node1, node_t *node2 ); +//int NETs_VirtualNode( node_t *node ); +//void NETs_SetVirtualAddress( node_t *node ); +//void NETs_SetBroadcastAddress( node_t *node ); +// +//void NETs_ResolveNode( node_t* node_dst, node_t* node_src ); +//void NETs_MakeNodeRaw( node_t* node_dst, node_t* node_src ); +//node_t* NETs_GetSender( int bufid ); +//void NETs_ResolveSender( node_t *node, int bufid ); +// +//void NETs_PrintNode( node_t *node ); +// +//void NETs_FlushListenBuffers(); +// +//int NETs_InsertVirtualPacket( NetPacket* gamepacket ); +//int NETs_DeterminePacketLoss( char **info, int *isiz, int incoming ); +// +//void NETs_SendPacket ( NetPacket* gamepacket, node_t* node ); +//void NETs_AuxSendPacket ( NetPacket* gamepacket, node_t* node ); +// +//void NETs_ProcessPacketChain ( void (*procfunc)(NetPacket* gamepacket,int bufid) ); +// +//int NETs_ResetAPI(); +//int NETs_InitAPI(); +//int NETs_KillAPI(); + + +#endif // _NET_SUBH_SV_H_ + + diff --git a/src/parsec_server/include/net_udpdriver.h b/src/parsec_server/include/net_udpdriver.h new file mode 100755 index 0000000..fd2dcc5 --- /dev/null +++ b/src/parsec_server/include/net_udpdriver.h @@ -0,0 +1,120 @@ +/* +* PARSEC HEADER: NET_UDPDriver.h +*/ + +#ifndef _NET_UDPDRIVER_H_ +#define _NET_UDPDRIVER_H_ + +// NOTE: +// +// platform dependent implementations are in e.g. NW_UDPDRIVER.CPP +// +#include <stdio.h> +#include <stdlib.h> +//#include <string.h> +//#include <stdarg.h> +#include <ctype.h> +#include <vector> + +#ifndef PARSEC_SERVER + //FIXME: we should do a e_glob_svm.h just like e_glob_sv.h + #define TheUDPDriver (NET_UDPDriver::GetUDPDriver()) +#endif // !PARSEC_SERVER + + +/* Following shortens all the type casts of pointer arguments */ +#define SA struct sockaddr + + +// class defining a UDP driver ------------------------------------------------ +// +class NET_UDPDriver +{ +protected: + int m_socket; + int m_selected_port; + char m_szInterface[ MAX_IPADDR_LEN + 1 ]; + char m_szIP [ MAX_IPADDR_LEN + 1 ]; + node_t m_Node; // node containing the IP adress + std::vector<node_t > NetworkInterfaces; + int m_PreferredInterface; + bool m_DriverRunning; +protected: + // init the OS API + int _InitOSAPI(); + + // kill the OS API + int _KillOSAPI(); + + // open and initialize the UDP socket + int _OpenSocket(); + + // close the UDP socket + int _CloseSocket(); + + // get the local IP + int _RetrieveLocalIP(); + + // setup the interface + void _SetupInterface(node_t *p_Node); + + NET_UDPDriver(); + ~NET_UDPDriver(); + +public: + + // SINGLETON pattern + static NET_UDPDriver* GetUDPDriver() + { + static NET_UDPDriver _TheUDPDriver; + return &_TheUDPDriver; + } + + // init the UDP driver + int InitDriver( char* szInterface, int selected_port ); + + // kill the UDP driver + int KillDriver(); + + // fetch a UDP packet from the socket + int FetchPacket( char* buf, int maxlen, SA* saddr ); + + // send a UDP packet on the socket + int SendPacket( char* buf, int pktsize, SA* saddr ); + + // sleep until we have a network input or a timeout occurs + int SleepUntilNetInput( int timeout_msec ) + { + fd_set rset; + struct timeval select_timeout; + + // initialize socket set + FD_ZERO( &rset ); + FD_SET( m_socket, &rset ); + + select_timeout.tv_sec = 0; + select_timeout.tv_usec = timeout_msec * 1000; + + // select for readable socket + int rc = select( m_socket + 1, &rset, NULL, NULL, &select_timeout ); + return rc; + } + + // resolve a hostname using the DNS service + int ResolveHostName( char *hostname, node_t* node ); + + + // ------------------------------------------------------------------------ + // accessor methods + // ------------------------------------------------------------------------ + int GetSocket() { return m_socket; } + node_t* GetNode() { return &m_Node; } + int GetPreferredInterface() { return m_PreferredInterface; } + void SetPreferredInterface(int p_Interface) { m_PreferredInterface = p_Interface; } + int IsRunning() { return m_DriverRunning; } +}; + + + +#endif // _NET_UDPDRIVER_H_ + diff --git a/src/parsec_server/include/net_util_sv.h b/src/parsec_server/include/net_util_sv.h new file mode 100644 index 0000000..1463a65 --- /dev/null +++ b/src/parsec_server/include/net_util_sv.h @@ -0,0 +1,25 @@ +/* + * PARSEC HEADER: net_util_sv.h + */ + +#ifndef _NET_UTIL_SV_H_ +#define _NET_UTIL_SV_H_ + +// net_util_sv.c implements the following functions +// ------------------------------------------------ + +// external functions --------------------------------------------------------- +// +void NODE_Copy( node_t* dst, node_t* src ); +void NODE_StorePort( node_t *node, word port ); +word NODE_GetPort( node_t *node ); +int NODE_Compare( node_t *node1, node_t *node2 ); +int NODE_AreSame( node_t *node1, node_t *node2 ); +char* NODE_Print( node_t* node ); + +// external variables --------------------------------------------------------- +// + +#endif // _NET_UTIL_SV_H_ + + diff --git a/src/parsec_server/include/sl_util_sv.h b/src/parsec_server/include/sl_util_sv.h new file mode 100644 index 0000000..219b480 --- /dev/null +++ b/src/parsec_server/include/sl_util_sv.h @@ -0,0 +1,9 @@ +/* + * PARSEC HEADER: sl_util_sv.h + */ + +#ifndef _SL_UTIL_SV_H_ +#define _SL_UTIL_SV_H_ + + +#endif // _SL_UTIL_SV_H_ diff --git a/src/parsec_server/include/sys_err_sv.h b/src/parsec_server/include/sys_err_sv.h new file mode 100644 index 0000000..359dfe1 --- /dev/null +++ b/src/parsec_server/include/sys_err_sv.h @@ -0,0 +1,31 @@ +/* + * PARSEC HEADER: sys_err_sv.h + */ + +#ifndef _SYS_ERR_SV_H_ +#define _SYS_ERR_SV_H_ + + +#include "debug.h" + + +// ---------------------------------------------------------------------------- +// SERVER SYSTEM SUBSYSTEM (SYS) ERROR HANDLING - +// ---------------------------------------------------------------------------- + +void SYSs_CriticalCleanUp(); +FUNCTION_NORETURN(void SYSs_PanicFunction( const char *msg, const char *file, unsigned line )); +FUNCTION_NORETURN(void SYSs_OutOfMemFunction( const char *msg, const char *file, unsigned line )); + +FUNCTION_NORETURN(void SYSs_PError( const char *msg, ... )); + +// wrappers + +#define PANIC(x) SYSs_PanicFunction( (x), __FILE__, __LINE__ ) +#define OUTOFMEM(x) SYSs_OutOfMemFunction( (x), __FILE__, __LINE__ ) +#define PERROR SYSs_PError +#define FERROR SYSs_PError //FIXME: //SYSs_FError + + +#endif // _SYS_ERR_SV_H_ + diff --git a/src/parsec_server/include/sys_msg_sv.h b/src/parsec_server/include/sys_msg_sv.h new file mode 100644 index 0000000..9a7affa --- /dev/null +++ b/src/parsec_server/include/sys_msg_sv.h @@ -0,0 +1,29 @@ +/* + * PARSEC HEADER: sys_msg_sv.h + */ + +#ifndef _SYS_MSG_SV_H_ +#define _SYS_MSG_SV_H_ + +// ---------------------------------------------------------------------------- +// SYSTEM SUBSYSTEM (SYS) MESSAGE OUTPUT - +// ---------------------------------------------------------------------------- + +int SYSs_MsgOut ( const char *format, ... ); +int SYSs_MsgPut ( const char *format, ... ); +int SYS_WriteLogFileMessage ( const char *format, ... ); + +// wrappers + +#define MSGOUT SYSs_MsgOut +#define MSGPUT SYSs_MsgPut + +#define DBGOUT SYSs_MsgOut +#define DMSGOUT SYSs_MsgOut + +#undef LOGOUT +#define LOGOUT(x) SYS_WriteLogFileMessage x + +#endif // _SYS_MSG_SV_H_ + + diff --git a/src/parsec_server/include/sys_refframe_sv.h b/src/parsec_server/include/sys_refframe_sv.h new file mode 100644 index 0000000..6a323e3 --- /dev/null +++ b/src/parsec_server/include/sys_refframe_sv.h @@ -0,0 +1,29 @@ +/* + * PARSEC HEADER: SYSS_REFFRAME.H + */ + +#ifndef _SYSS_REFFRAME_H_ +#define _SYSS_REFFRAME_H_ + + +// init reference frame counting +void SYSs_InitRefFrameCount(); + +// get current reference frame count +refframe_t SYSs_GetRefFrameCount(); + +// wait for specified number of reference frames +void SYSs_Wait( refframe_t refframes ); + +// called once per frame to pass control to a platform-specific handler +int SYSs_Yield(); + +#include "e_global_sv.h" +#include "sl_timer.h" + +// helper macros -------------------------------------------------------------- +// +#define REFFRAMES_TO_USEC( x ) ( ( x ) * 1000000 ) / FRAME_MEASURE_TIMEBASE; + +#endif // !_SYSS_REFFRAME_H_ + diff --git a/src/parsec_server/include/sys_util_sv.h b/src/parsec_server/include/sys_util_sv.h new file mode 100755 index 0000000..6ef4d40 --- /dev/null +++ b/src/parsec_server/include/sys_util_sv.h @@ -0,0 +1,18 @@ +/* +* PARSEC HEADER: sys_util_sv.h +*/ + +#ifndef _SYS_UTIL_SV_H_ +#define _SYS_UTIL_SV_H_ + +// ---------------------------------------------------------------------------- +// SERVER SYSTEM SUBSYSTEM (SYS) UTILITY FUNCTIONS +// ---------------------------------------------------------------------------- + +void SYSs_InstallSignalHandlers(); +void SYSs_CleanUp(); + +void SYS_HexDump( char* data, int len ); + +#endif //_SYS_UTIL_SV_H_ + diff --git a/src/parsec_server/inp_main_sv.cpp b/src/parsec_server/inp_main_sv.cpp new file mode 100755 index 0000000..f670eed --- /dev/null +++ b/src/parsec_server/inp_main_sv.cpp @@ -0,0 +1,117 @@ +/* + * PARSEC - CURSES Input Code + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:48 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2001-2002 + * + * 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 <stdio.h> +#include <string.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" + +// subsystem headers +#include "inp_defs_sv.h" + +// local module header +#include "inp_main_sv.h" + +// proprietary module headers +#include "con_main_sv.h" + +// keyboard mapping ----------------------------------------------------------- +// +#include "inp_keybmap_sv.h" + +// Keyboard buffer ------------------------------------------------------------ +// +PRIVATE dword INPS_KeybBuffer[ 2 + KEYB_BUFF_SIZ ]; + +// global pointer to the keyboard buffer ( for console input ) ---------------- +// +keybbuffer_s* KeybBuffer; + + +// init the input subsystem --------------------------------------------------- +// +PUBLIC +void INP_Init() +{ + memset( INPS_KeybBuffer, 0, sizeof( INPS_KeybBuffer ) ); + KeybBuffer = (keybbuffer_s*) &INPS_KeybBuffer; +} + +// kill the input subsystem --------------------------------------------------- +// +PUBLIC +void INP_Kill() +{ +} + +// stuff key in internal keyboard buffer using repeatcount -------------------- +// +PRIVATE +void _INP_HandleBufferedKey( int key, int nRepcount ) +{ + dword *pos; + + for ( int nCount = 0; nCount < nRepcount; nCount++ ) { + dword wpos = KeybBuffer->WritePos + 1; + wpos = wpos % (KEYB_BUFF_SIZ); + + pos = &KeybBuffer->Data + wpos; + + // prevent overwrite + if ( *pos == 0 ) { + + // map into KeybMap + ASSERT( (dword)key < sizeof( INP_KeybMap ) ); + dword dwKey = INP_KeybMap[ key ]; + + // write into keyboard buffer + if ( dwKey != CKC_NIL ) { + *(dword*)(&KeybBuffer->Data + KeybBuffer->WritePos) = dwKey; + KeybBuffer->WritePos = wpos; + } + } + } +} + + +// handle the input from CURSES ----------------------------------------------- +// +PUBLIC +void INP_HandleInput() +{ + ASSERT( g_curses_in_win != NULL ); + int c = wgetch( g_curses_in_win ); + + if ( c != ERR ) { + _INP_HandleBufferedKey( c, 1 ); + } +} + + + diff --git a/src/parsec_server/net_packetdriver.cpp b/src/parsec_server/net_packetdriver.cpp new file mode 100755 index 0000000..4c6edf7 --- /dev/null +++ b/src/parsec_server/net_packetdriver.cpp @@ -0,0 +1,755 @@ +/* + * PARSEC - Packet driver + * + * $Author: uberlinuxguy $ - $Date: 2004/09/26 03:43:48 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2002 + * + * 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 <stdlib.h> +#include <stdio.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" + +// UNP header +#include "net_wrap.h" + +// mathematics header +#include "utl_math.h" + +// server defs +#include "e_defs.h" + +// network code config +#include "net_conf.h" + +// local module header +#include "net_packetdriver.h" + +// proprietary module headers +#include "con_aux_sv.h" +#include "net_game_sv.h" +#include "net_udpdriver.h" +#include "net_stream.h" +#include "e_gameserver.h" +#include "e_packethandler.h" +#include "e_simulator.h" +#include "sys_util_sv.h" + + +// flags ---------------------------------------------------------------------- +// + +// number of listen buffers in listen buffers array --------------------------- +// +#define NUM_LISTEN_BUFFERS ( MAX_NET_ALLOC_SLOTS * 2 ) + +// history buffer for received message ids to filter out duplicates ----------- +// +#define MSGID_HISTORY_SIZE 64 + +// packet loss meter ---------------------------------------------------------- +// +//FIXME: we need a packet loss meter for every client +#define PACKET_LOSS_METER_LENGTH 100 // horizontal size + + +// string constants ----------------------------------------------------------- +// +static const char pchain_alloc_error[]= "Error allocating packet chain.\n"; + +// ---------------------------------------------------------------------------- +// public methods +// ---------------------------------------------------------------------------- + +// default ctor --------------------------------------------------------------- +// +NET_PacketDriver::NET_PacketDriver() +{ + // init all to NULL for Reset to not delete the data + ReceivedPacketsChain = NULL; + SendNetPacketExternal = NULL; + RecvNetPacketExternal = NULL; + m_Streams = NULL; + + Reset(); +} + + +// reset all data members ----------------------------------------------------- +// +void NET_PacketDriver::Reset() +{ + // free when necessary + if ( SendNetPacketExternal != NULL ) FREEMEM( SendNetPacketExternal ); + if ( RecvNetPacketExternal != NULL ) FREEMEM( RecvNetPacketExternal ); + if ( ReceivedPacketsChain != NULL ) _FreePacketsChain(); + int bid; + for ( bid = 0; bid < NUM_LISTEN_BUFFERS; bid++ ) { + if ( NetPacketsInternal[ bid ] != NULL ) FREEMEM( NetPacketsInternal[ bid ] ); + } + + // allocate the external packets + SendNetPacketExternal = (NetPacketExternal_GMSV *) ALLOCMEM( NET_UDP_DATA_LENGTH ); + RecvNetPacketExternal = (NetPacketExternal_GMSV *) ALLOCMEM( NET_UDP_DATA_LENGTH ); + memset( SendNetPacketExternal, 0, NET_UDP_DATA_LENGTH ); + memset( RecvNetPacketExternal, 0, NET_UDP_DATA_LENGTH ); + + // allocate the internal packets + for ( bid = 0; bid < NUM_LISTEN_BUFFERS; bid++ ) { + NetPacketsInternal[ bid ] = (NetPacket_GMSV *) ALLOCMEM( NET_MAX_NETPACKET_INTERNAL_LEN ); + memset( NetPacketsInternal[ bid ], 0, NET_MAX_NETPACKET_INTERNAL_LEN ); + memset( &ListenAddress[ bid ], 0, sizeof( sockaddr_in ) ); + } + + // init the streams + if ( m_Streams != NULL ) { + delete []m_Streams; + m_Streams = NULL; + } + m_Streams = new NET_Stream[ MAX_NUM_CLIENTS ]; + + // set the client IDs the stream is connected to + for( int nClientID = 0; nClientID < MAX_NUM_CLIENTS; nClientID++ ) { + m_Streams[ nClientID ].SetPeerID( nClientID ); + m_Streams[ nClientID ].SetSenderID( PLAYERID_SERVER ); + } + + // allocate the packet chain + _AllocPacketsChain(); +} + + +// get the stream message status ---------------------------------------------- +// +NET_Stream* NET_PacketDriver::GetStream( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + return &m_Streams[ nClientID ]; +} + +// reset the stream of a specific client -------------------------------------- +// +void NET_PacketDriver::ResetStream( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); +#ifdef PARSEC_DEBUG + MSGOUT( "resetting stream for client %d", nClientID ); +#endif // PARSEC_DEBUG + m_Streams[ nClientID ].Reset(); + m_Streams[ nClientID ].SetPeerID( nClientID ); + m_Streams[ nClientID ].SetSenderID( PLAYERID_SERVER ); +} + + +// connect the stream of a client --------------------------------------------- +// +void NET_PacketDriver::ConnectStream( int nClientID ) +{ + ResetStream( nClientID ); + m_Streams[ nClientID ].SetConnected(); +} + + +// flush the reliable backbuffer of a stream ---------------------------------- +// +void NET_PacketDriver::FlushReliableBuffer( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + m_Streams[ nClientID ].FlushReliableBuffer(); +} + +// default dtor --------------------------------------------------------------- +// +NET_PacketDriver::~NET_PacketDriver() +{ + // free the packet chain + _FreePacketsChain(); + + // free internal/external packets + FREEMEM( SendNetPacketExternal ); + FREEMEM( RecvNetPacketExternal ); + SendNetPacketExternal = NULL; + RecvNetPacketExternal = NULL; + + for ( int bid = 0; bid < NUM_LISTEN_BUFFERS; bid++ ) { + FREEMEM( NetPacketsInternal[ bid ] ); + NetPacketsInternal[ bid ] = NULL; + } + + if ( m_Streams != NULL ) { + delete []m_Streams; + m_Streams = NULL; + } +} + +// invoke processing function for all packets currently in chain -------------- +// +void NET_PacketDriver::NET_ProcessPacketChain() +{ + // build chain of received packets + _BuildReceivedPacketsChain(); + + // invoke processing function for received packets + PacketChainBlock *block; + while ( ( block = _FetchPacketFromChain() ) != NULL ) { + + // invoke packet processing function + ThePacketHandler->HandlePacket( block ); + + // release the packet from the packetchain + _ReleasePacketFromChain( block ); + } +} + + +// retrieve sender's node address from listen header -------------------------- +// +node_t* NET_PacketDriver::GetPktSender( int bufid ) +{ + ASSERT( bufid >= 0 ); + ASSERT( bufid < NUM_LISTEN_BUFFERS ); + + node_t *node; + + //NOTE: + // instead of returning the address directly from ListenAdress[] + // we make a copy here, since the port number will be appended and + // we don't want to assume there is enough memory in the structure + // after the sin_addr field. (usually there are eight irrelevant bytes) + + node = &ListenSenderStorage[ bufid ]; + + memcpy( node, &ListenAddress[ bufid ].sin_addr, IP_ADR_LENGTH ); + NODE_StorePort( node, ntohs( ListenAddress[ bufid ].sin_port ) ); + + return node; +} + +// return the message id used in the next packet that is sent ----------------- +// +int NET_PacketDriver::GetNextOutMessageId( int nClientID ) +{ + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + return m_Streams[ nClientID ].GetNextOutMessageId(); +} + + +// fill standard fields in gamepacket header ---------------------------------- +// +void NET_PacketDriver::FillStdGameHeader( byte command, NetPacket_GMSV* gamepacket ) +{ + ASSERT( gamepacket != NULL ); + + // clear header and remote event list area + memset( gamepacket, 0, NET_MAX_NETPACKET_INTERNAL_LEN ); + + gamepacket->Command = command; + gamepacket->SendPlayerId = PLAYERID_SERVER; + + // RE list size always includes the RE terminator + ((RE_Header* )&gamepacket->RE_List)->RE_Type = RE_EMPTY; + gamepacket->RE_ListSize = sizeof( dword ); +} + + +// determine if packet should be artificially dropped ------------------------- +// +int NET_PacketDriver::_CheckForcePacketDrop() +{ + +#ifdef ENABLE_PACKETDROP_TESTING + + //NOTE: + // SVAUX_PACKETDROP_TESTING sets the probability + // of packet-drop (well, approximately). + + if ( SVAUX_PACKETDROP_TESTING ) { + + int r = RAND() % 100; + if ( r < SVAUX_PACKETDROP_TESTING ) { + return TRUE; + } + } + +#endif // ENABLE_PACKETDROP_TESTING + + return FALSE; +} + + +// send a datagram ( unreliable, unnumbered packet ) to a node ---------------- +// +int NET_PacketDriver::SendDatagram( NetPacket_GMSV* gamepacket, node_t* node, int nClientID, E_REList* pUnreliable ) +{ + ASSERT( gamepacket != NULL ); + ASSERT( node != NULL ); + ASSERT( pUnreliable != NULL ); + + RE_Header* pFillPosition = (RE_Header*)(&gamepacket->RE_List); + size_t maxfillsize = RE_LIST_MAXAVAIL + sizeof( dword ); + + // copy unreliable RE ( account maxsize for dword in NetPacket_GMSV ) + size_t unreliable_size = pUnreliable->WriteTo( pFillPosition, maxfillsize, TRUE ); + + // unreliable overflow ? + if ( unreliable_size == 0 ) { + DBGTXT( MSGOUT( "NET_PacketDriver::SendDatagram(): WARNING: unreliable overflow" ); ); + return FALSE; + } + + // fill message header/#s for datagram + gamepacket->MessageId = MSGID_DATAGRAM; + gamepacket->ReliableMessageId = 0; + gamepacket->AckMessageId = 0; + gamepacket->AckReliableMessageId = 0; + + // pack from internal packet to external packet + size_t pktsize = NETs_HandleOutPacket( (NetPacket*)gamepacket, SendNetPacketExternal ); + + sockaddr_in SendAddress; + bzero( &SendAddress, sizeof( SendAddress ) ); + SendAddress.sin_family = AF_INET; + SendAddress.sin_port = htons( NODE_GetPort( node ) ); + memcpy ( &SendAddress.sin_addr, node, IP_ADR_LENGTH ); + + int rc = TheUDPDriver->SendPacket( (char *) SendNetPacketExternal, pktsize, (SA*) &SendAddress ); + + return pktsize; +} + + +// send a packet to a node ---------------------------------------------------- +// +int NET_PacketDriver::SendPacket( NetPacket_GMSV* gamepacket, node_t* node, int nClientID, E_REList* pReliable, E_REList* pUnreliable ) +{ + ASSERT( gamepacket != NULL ); + ASSERT( node != NULL ); + ASSERT( nClientID != PLAYERID_MASTERSERVER ); + ASSERT( nClientID != PLAYERID_ANONYMOUS ); + ASSERT( ( nClientID >= 0 ) && ( nClientID < MAX_NUM_CLIENTS ) ); + + NET_Stream* pStream = &m_Streams[ nClientID ]; + ASSERT( pStream != NULL ); + + // check for artificial packet drop + if ( _CheckForcePacketDrop() ) { + pStream->LogPacketLossStats( 1, FALSE, FALSE ); + return -1; + } + + //FIXME_MASV: eventually move all E_REList dependent code to util function + + // append reliable data to queue in stream and check for reliable choke + if ( ( pReliable != NULL ) && ( pReliable->GetSize() > 0 ) ) { + if ( pStream->AppendToReliableFIFO( pReliable ) == FALSE ) { + ASSERT( nClientID != PLAYERID_MASTERSERVER ); + + //FIXME: what do we do with the RELIABLE RE_COMMANDINFO containing the DISCONNECT + + // disconnect the client if we have a reliable choke + MSGOUT("Choking"); + TheConnManager->DisconnectClient( nClientID ); + return -1; + } + } + + // retrieve the first reliable RE list + pReliable = pStream->GetNextReliableToSend(); + + RE_Header* pFillPosition = (RE_Header*)(&gamepacket->RE_List); + size_t maxfillsize = RE_LIST_MAXAVAIL + sizeof( dword ); + size_t reliable_size = 0; + size_t unreliable_size = 0; + + if ( ( pReliable != NULL ) && ( pReliable->GetSize() > 0 ) ) { + ASSERT( nClientID != PLAYERID_MASTERSERVER ); + + // copy reliable RE and check for reliable overflow + reliable_size = pReliable->WriteTo( pFillPosition, maxfillsize, FALSE ); + if ( reliable_size == 0 ) { + DBGTXT( MSGOUT( "NET_PacketDriver::SendPacket(): ERROR: reliable overflow" ); ); + return -1; + } + // determine next fill position + pFillPosition = (RE_Header*) ( ((char*)(&gamepacket->RE_List)) + reliable_size ); + maxfillsize -= ( reliable_size + 1 ); + } + + // handle unreliable part + if ( ( pUnreliable != NULL ) && ( pUnreliable->GetSize() > 0 ) ) { + + // copy unreliable RE ( account maxsize for dword in NetPacket_GMSV and size of already inserted reliable ) + unreliable_size = pUnreliable->WriteTo( pFillPosition, maxfillsize, TRUE ); + + // unreliable overflow ? + if ( unreliable_size == 0 ) { + DBGTXT( MSGOUT( "NET_PacketDriver::SendPacket(): WARNING: unreliable overflow" ); ); + } + } + + // we do not send empty ( header only ) packets + if ( ( reliable_size + unreliable_size ) == 0 ) { + DBGTXT( MSGOUT( "NET_PacketDriver::SendPacket(): WARNING: skip sending of empty packet ( header only ) - possibly due to reliable buffering." ); ); + return 0; + } + + // fill in message-IDs and ACKs + pStream->OutPacket( gamepacket, ( pReliable != NULL ) ); + + // pack from internal packet to external packet + size_t pktsize = NETs_HandleOutPacket( (NetPacket*)gamepacket, SendNetPacketExternal ); + + // write local packet to stream - for testing purposes only +#ifdef SAVE_PACKETSTREAM + SaveLocalPacket( SendNetPacketExternal ); +#endif // SAVE_PACKETSTREAM + + sockaddr_in SendAddress; + bzero( &SendAddress, sizeof( SendAddress ) ); + SendAddress.sin_family = AF_INET; + SendAddress.sin_port = htons( NODE_GetPort( node ) ); + memcpy ( &SendAddress.sin_addr, node, IP_ADR_LENGTH ); + + int rc = TheUDPDriver->SendPacket( (char *) SendNetPacketExternal, pktsize, (SA*) &SendAddress ); + + // log packet loss + pStream->LogPacketLossStats( 1, ( rc >= 0 ), FALSE ); + + return pktsize; +} + + +// ---------------------------------------------------------------------------- +// protected methods +// ---------------------------------------------------------------------------- + +// retrieve next packet from received packets chain --------------------------- +// +PacketChainBlock* NET_PacketDriver::_FetchPacketFromChain() +{ + ASSERT( ReceivedPacketsChain != NULL ); + + PacketChainBlock *temp = ReceivedPacketsChain->nextblock; + if ( temp != NULL ) { + ASSERT( ReceivedPacketsChain->chainlength > 0 ); + ReceivedPacketsChain->nextblock = temp->nextblock; + temp->nextblock = NULL; + ReceivedPacketsChain->chainlength--; + } + + return temp; +} + + +// allocate received packets chain (head node) -------------------------------- +// +int NET_PacketDriver::_AllocPacketsChain() +{ + ASSERT( ReceivedPacketsChain == NULL ); + ReceivedPacketsChain = (PacketChainHead *) ALLOCMEM( sizeof( PacketChainHead ) ); + if ( ReceivedPacketsChain == NULL ) { + fprintf( stderr, pchain_alloc_error ); + return FALSE; + } + ReceivedPacketsChain->nextblock = NULL; + ReceivedPacketsChain->chainlength = 0; + + return TRUE; +} + + +// free all blocks in received packets chain ---------------------------------- +// +void NET_PacketDriver::_FreePacketsChain() +{ + ASSERT( ReceivedPacketsChain != NULL ); + while ( ReceivedPacketsChain != NULL ) { + PacketChainBlock *temp = ReceivedPacketsChain->nextblock; + FREEMEM( ReceivedPacketsChain ); + ReceivedPacketsChain = (PacketChainHead *) temp; + } +} + + +// release block containing pointer and info of packet ------------------------ +// +void NET_PacketDriver::_ReleasePacketFromChain( PacketChainBlock *block ) +{ + CHECKHEAPBASEREF( block ); + FREEMEM( block ); +} + + +// pre-process received packets ----------------------------------------------- +// +void NET_PacketDriver::_BuildReceivedPacketsChain() +{ + //NOTE: + // this function checks all packets received since it was last called. + // packets are decrypted, CRC checked and converted from external to internal + // packet format in _UDP_FetchPacket. + // the packets have to be sequenced according to their id, as they may be + // in any order, and then inserted into the chain (this is done by ChainPacket() ). + + ASSERT( ReceivedPacketsChain != NULL ); + +#ifdef ASSERT_PACKET_CHAIN_EMPTY + ASSERT( ReceivedPacketsChain->nextblock == NULL ); + ASSERT( ReceivedPacketsChain->chainlength == 0 ); +#endif + + // scan listen buffers + for ( int bufid = 0; bufid < NUM_LISTEN_BUFFERS; bufid++ ) { + + // try to retrieve packet + if ( !_UDP_FetchPacket( bufid ) ) + break; + + // process received packet + if ( !ListenStatus[ bufid ] ) { + + // insert packet into chain + _ChainPacket( NetPacketsInternal[ bufid ], bufid ); + } + } +} + + +// insert received packet into chain ------------------------------------------ +// +int NET_PacketDriver::_ChainPacket( NetPacket_GMSV* gamepacket, int bufid ) +{ + ASSERT( gamepacket != NULL ); + ASSERT( ReceivedPacketsChain != NULL ); + + PacketChainBlock* scan = ReceivedPacketsChain; + int nClientID = gamepacket->SendPlayerId; + int messageid = gamepacket->MessageId; + + // exclude non-regular packets (negative sender ids) from + // duplicate checking: PLAYERID_SERVER, PLAYERID_ANONYMOUS + switch( nClientID ) { + case PLAYERID_SERVER: + + if(!TheServer->GetServerIsMaster()){ + DBGTXT( MSGOUT( "NET_PacketDriver::_ChainPacket(): filtering packet %d from %d", messageid, nClientID ); ); + return FALSE; + } + break; + case PLAYERID_ANONYMOUS: + MSGOUT("Ignoring Anonymous Packet"); + break; + + case PLAYERID_MASTERSERVER: + { + //FIXME: _IsLegitSender() is also called in E_PacketHandler::HandlePacket(), duplicate work ? + + // check src node with master server node + node_t* node = GetPktSender( bufid ); + if ( !TheServer->IsMasterServerNode( node ) ) { + //DBGTXT( + MSGOUT( "NET_PacketDriver::_ChainPacket(): MasterServer NODE MISMATCH %s", NODE_Print( node ) );// ); + return FALSE; + } + } + break; + + default: + if( ( nClientID < 0 ) || ( nClientID >= MAX_NUM_CLIENTS ) ) { + DBGTXT( MSGOUT( "NET_PacketDriver::_ChainPacket(): filtering packet %d from %d", messageid, nClientID ); ); + return FALSE; + } else { + + if(!TheServer->GetServerIsMaster()){ + // check src node with stored node for already connected client + node_t* clientnode = GetPktSender( bufid ); + if ( !TheConnManager->CheckNodesMatch( nClientID, clientnode ) ) { + DBGTXT( MSGOUT( "NET_PacketDriver::_ChainPacket(): NODE MISMATCH %d: %s", nClientID, NODE_Print( clientnode ) ); ); + return FALSE; + } + + // check regular player packets for duplicates + if ( _FilterPacketDuplicate( nClientID, messageid ) ) { + DBGTXT( MSGOUT( "NET_PacketDriver::_ChainPacket(): filtering duplicate packet %d from %d", messageid, nClientID ); ); + return FALSE; + } + } + } + break; + } + + // no duplicate checking/stream handling for datagrams + if ( (dword)messageid != MSGID_DATAGRAM ) { + + // normal stream packets must come from identified clients + ASSERT( nClientID != PLAYERID_ANONYMOUS ); + + // search for packet of same sender already in chain + for ( ; scan->nextblock; scan = scan->nextblock ) { + if ( NODE_Compare( GetPktSender( scan->nextblock->bufferno ), GetPktSender( bufid ) ) == NODECMP_EQUAL ) { + break; + } + } + + // search to right insert position (according to message id) + for ( ; scan->nextblock; scan = scan->nextblock ) { + if ( NODE_Compare( GetPktSender( scan->nextblock->bufferno ), GetPktSender( bufid ) ) != NODECMP_EQUAL ) { + break; + } + if ( messageid <= scan->nextblock->messageid ) { + if ( messageid == scan->nextblock->messageid ) + DBGTXT( MSGOUT( "NET_PacketDriver::_ChainPacket(): received same message twice: %d.", messageid ); ); + break; + } + } + + // maintain stream IDs/ACKs + NET_Stream* pStream = GetStream( nClientID ); + if ( !pStream->InPacket( gamepacket ) ) { + MSGOUT("Return in maintain stream IDs/ACKs"); + return FALSE; + } + } + + // record packet + if ( bufid != VIRTUAL_BUFFER_ID ) { + //MSGOUT("Recording Packet"); + _RecordPacket( bufid ); + } + + // alloc block in chain + PacketChainBlock *temp = (PacketChainBlock *) ALLOCMEM( sizeof( PacketChainBlock ) ); + if ( temp == NULL ) { + ASSERT( 0 ); + return FALSE; + } + + // store critical info + temp->bufferno = bufid; + temp->gamepacket= gamepacket; + temp->messageid = messageid; + + // link packet into chain + temp->nextblock = scan->nextblock; + scan->nextblock = temp; + ReceivedPacketsChain->chainlength++; + + return TRUE; +} + + +// record packet before inserting it into packet chain ------------------------ +// +void NET_PacketDriver::_RecordPacket( int bufid ) +{ +/*#ifdef ENABLE_PACKET_RECORDING + + ASSERT( bufid != VIRTUAL_BUFFER_ID ); + + // check whether we are currently recording packets + if ( REC_IsRecordingPackets() ) { + + // translate NetPacketsInternal to corresponding DEMO NetPacketExternal + NetPacketExternal* ext_gamepacket = (NetPacketExternal*)ALLOCMEM( RECORD_PACKET_SIZE ); + NETs_HandleOutPacket_DEMO( NetPacketsInternal[ bufid ], ext_gamepacket ); + + + //NOTE: + // packets are recorded in network byte-order, that is, unswapped. + // This is exactly the format that is held in a NetPackeExternal + // ( along with encryption, compression etc ). + + // record the external DEMO packet + REC_RecordRemotePacket( ext_gamepacket ); + } + +#endif // ENABLE_PACKET_RECORDING +*/ +} + + +// check regular player packets for duplicates -------------------------------- +// +int NET_PacketDriver::_FilterPacketDuplicate( int senderid, int messageid ) +{ + ASSERT( ( senderid >= 0 ) && ( senderid < MAX_NUM_CLIENTS ) ); + return m_Streams[ senderid ].FilterPacketDuplicate( messageid ); +} + + +// retrieve received udp packet and store into listen buffer ------------------ +// +int NET_PacketDriver::_UDP_FetchPacket( int bufid ) +{ + // assume buffer is busy (nothing received) + ListenStatus[ bufid ] = TRUE; + + // fetch packet + sockaddr_in from_adress; + //ListenDatLen[ bufid ] = + int rc = TheUDPDriver->FetchPacket( (char *)RecvNetPacketExternal, NET_UDP_DATA_LENGTH, (SA *)&from_adress ); + + if ( rc >= 0 ) { + + // copy from adress + memcpy( &ListenAddress[ bufid ], &from_adress, sizeof( sockaddr_in ) ); + + // filter broadcast packets that are looping back + if ( NODE_Compare( TheUDPDriver->GetNode(), GetPktSender( bufid ) ) == NODECMP_EQUAL ) { + //DBGTXT( + MSGOUT( "NET_PacketDriver::_UDP_FetchPacket(): dropped packet [loop-back]." ); //); + // indicate we want to try to receive more packets + return TRUE; + } + + // convert external packet to internal format + if ( !NETs_HandleInPacket( RecvNetPacketExternal, (size_t )rc, (NetPacket*) NetPacketsInternal[ bufid ] ) ) { + DBGTXT( MSGOUT( "NET_PacketDriver::_UDP_FetchPacket(): dropped packet [conversion error]." ); ); + return FALSE; + } else { + + UPDTXT2(MSGOUT( "NET_PacketDriver::_UDP_FetchPacket(): received packet msg: %d.", RecvNetPacketExternal->MessageId )); + + // indicate that buffer now contains received packet + ListenStatus[ bufid ] = FALSE; + return TRUE; + } + + } else { + + //FIXME: could check for ERRNO_ECONNRESET (ICMP "Port Unreachable"), to identify dead clients + return FALSE; + } +} + + + diff --git a/src/parsec_server/net_util_sv.cpp b/src/parsec_server/net_util_sv.cpp new file mode 100755 index 0000000..88c0468 --- /dev/null +++ b/src/parsec_server/net_util_sv.cpp @@ -0,0 +1,138 @@ +/* + * PARSEC - Utility Functions - server + * + * $Author: uberlinuxguy $ - $Date: 2004/09/15 12:26:06 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2001-2002 + * Copyright (c) Markus Hadwiger <msh@parsec.org> 1996-2000 + * + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "objstruc.h" + +// network subsystem & server headers +#include "net_defs.h" +#include "e_defs.h" + +// network code config +//#include "net_conf.h" + +// UNP header +#include "net_wrap.h" + +// local module header +#include "net_util.h" + + +// flags ---------------------------------------------------------------------- +// + +// copy one node over another ------------------------------------------------- +// +void NODE_Copy( node_t* dst, node_t* src ) +{ + ASSERT( dst != NULL ); + ASSERT( src != NULL ); + + memcpy( dst, src, sizeof( node_t ) ); +} + + +// store port number into node address structure ------------------------------ +// +void NODE_StorePort( node_t *node, word port ) +{ + ASSERT( node != NULL ); + + node->address[ 4 ] = port >> 8; + node->address[ 5 ] = port & 0xff; +} + + +// fetch port number from node address structure ------------------------------ +// +word NODE_GetPort( node_t *node ) +{ + ASSERT( node != NULL ); + + return ( ( (word)node->address[ 4 ] << 8 ) | node->address[ 5 ] ); +} + + +// compare two node addresses (equal, less than, greater than) ---------------- +// +int NODE_Compare( node_t *node1, node_t *node2 ) +{ + byte *ip1 = (byte *) node1; + byte *ip2 = (byte *) node2; + + // compare as many digits as necessary + for ( int pos = 0; pos < NODE_ADR_LENGTH; pos++ ) { + + if ( ip1[ pos ] < ip2[ pos ] ) { + return NODECMP_LESSTHAN; + } else if ( ip1[ pos ] > ip2[ pos ] ) { + return NODECMP_GREATERTHAN; + } + } + + return NODECMP_EQUAL; +} + +// compare two node addresses ( fast version ) -------------------------------- +// +int NODE_AreSame( node_t *node1, node_t *node2 ) +{ + return ( memcmp( (void*)node1, (void*)node2, sizeof( node_t ) ) == 0 ); +} + + +// static print area for a node string ---------------------------------------- +// +//FIXME: IPv6 ? +static char szNodePrintBuffer[ sizeof "255.255.255.255:65535" ]; + +// return a STATIC string that contains the node in printable form ------------ +// +char* NODE_Print( node_t* node ) +{ + ASSERT( node != NULL ); + + // convert IP + char ipstring[ MAX_IPADDR_LEN + 1 ]; + inet_ntop( AF_INET, node, ipstring, MAX_IPADDR_LEN + 1 ); + + //NOTE: this function uses a STATIC string buffer that holds the printable + // form of the node. Do not store the pointer, as it may get overwritten + // by subsequent calls to NODE_Print + + sprintf( szNodePrintBuffer, "%s:%d", ipstring, NODE_GetPort( node ) ); + return szNodePrintBuffer; +} + diff --git a/src/parsec_server/nl_udpdriver.cpp b/src/parsec_server/nl_udpdriver.cpp new file mode 100755 index 0000000..107f200 --- /dev/null +++ b/src/parsec_server/nl_udpdriver.cpp @@ -0,0 +1,528 @@ +/* + * PARSEC - UDP driver + * + * $Author: uberlinuxguy $ - $Date: 2004/09/15 12:26:06 $ + * + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <ctype.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "objstruc.h" + +// network subsystem & server headers +#include "net_defs.h" +#include "e_defs.h" + +// UNP header +#include "net_wrap.h" + +// local module header +#include "net_udpdriver.h" + +// proprietary module headers +#include "net_util.h" + +#include "e_gameserver.h" +#include "e_global_sv.h" + +#include "con_arg.h" +#include "con_main_sv.h" +#include "con_com_sv.h" +#include "con_aux_sv.h" + + +// string constants ----------------------------------------------------------- +// +static const char node_address_str[] = "IP address: %s:%d"; +static const char api_detected[] = "TCP/IP networking support found."; +static const char no_api_detected[] = "ERROR: TCP/IP networking support not found."; +static const char open_socket_error[] = "ERROR: opening UDP socket.\n"; +static const char pchain_alloc_error[] = "ERROR: allocating packet chain.\n"; +static const char queue_alloc_error[] = "ERROR: allocating request queue.\n"; +static const char cannot_get_local_hostname[] = "ERROR: cannot get local host name\n"; +static const char dns_error[] = "ERROR: DNS error: %s %s.\n"; +static const char illegal_adress_type_dns[] = "ERROR: cannot resolve local hostname: illegal address type in DNS entry.\n"; +static const char no_network_interface_found[]= "ERROR: no network interface found."; +static const char selected_interface_invalid[]= "WARNING: selected interface not found - using default interface."; + + +// ---------------------------------------------------------------------------- +// public methods +// ---------------------------------------------------------------------------- + +// default ctor --------------------------------------------------------------- +// +NET_UDPDriver::NET_UDPDriver() + : m_socket( -1 ), + m_selected_port( 0 ) +{ + m_PreferredInterface = 0; + m_DriverRunning = 0; + memset( m_szIP, 0, MAX_IPADDR_LEN + 1 ); + memset( m_szInterface, 0, MAX_IPADDR_LEN + 1 ); +} + +// default dtor --------------------------------------------------------------- +// +NET_UDPDriver::~NET_UDPDriver() +{ + KillDriver(); +} + + +// init the UDP driver -------------------------------------------------------- +// +int NET_UDPDriver::InitDriver( char* szInterface, int selected_port ) +{ + m_selected_port = selected_port; + + // initialize WINSOCK + if ( _InitOSAPI() == FALSE ) { + return FALSE; + } + + // get the local IP corresponding to the (user)configured interface + if ( _RetrieveLocalIP() == FALSE ) { + return FALSE; + } + + // open the UDP server socket + if ( _OpenSocket() == FALSE ) { + return FALSE; + } + + m_DriverRunning = 1; + // print local host address + MSGOUT( node_address_str, m_szIP, m_selected_port ); + + return TRUE; +} + +// kill the UDP driver -------------------------------------------------------- +// +int NET_UDPDriver::KillDriver() +{ + if ( _CloseSocket() == FALSE ) { + return FALSE; + } + + // kill WINSOCK + if ( _KillOSAPI() == FALSE ) { + return FALSE; + } + m_DriverRunning = 0; + + return TRUE; +} + +// fetch a UDP packet from the socket ---------------------------------------- +// +int NET_UDPDriver::FetchPacket( char* buf, int maxlen, SA* saddr ) +{ + int len = sizeof( sockaddr_in ); + int rc = recvfrom( m_socket, buf, maxlen, 0, saddr, (socklen_t *) &len ); + + if ( rc < 0 ) { + + FETCH_ERRNO(); + if ( !ERRNO_EWOULDBLOCK && !ERRNO_ECONNREFUSED && !ERRNO_ECONNRESET ) { + MSGOUT( "NET_UDPDriver::FetchPacket: error reading from UDP socket: %d.", ERRNO ); + } + } + + return rc; +} + +#define UDP_MAX_SEND_RETRIES 10 + +// send a UDP packet on the socket -------------------------------------------- +// +int NET_UDPDriver::SendPacket( char* buf, int pktsize, SA* saddr ) +{ + ASSERT( buf != NULL ); + ASSERT( pktsize >= 0 ); + ASSERT( saddr != NULL ); + + int rc = 0; + + for( int nRetry = 0; nRetry < UDP_MAX_SEND_RETRIES; nRetry++ ) { + rc = sendto( m_socket, buf, pktsize, 0, saddr, sizeof( sockaddr_in ) ); + + //FIXME: do we really need to retry sendto if EWOULDBLOCK ?? + + // check for succesful delivery of packet + if ( rc >= 0 ) + return rc; + + //NOTE: + // if the packet could not be sent (EWOULDBLOCK), + // the sendto() call is retried until it succeeds. + // this is done in order to ensure that no packets + // are thrown away even before they have been sent. + + FETCH_ERRNO(); + if ( !ERRNO_EWOULDBLOCK && !ERRNO_ECONNREFUSED && !ERRNO_ENETDOWN ) { + MSGOUT( "NET_UDPDriver::SendPacket: error writing to UDP socket: %d.", ERRNO ); + return rc; + } else if ( ERRNO_ECONNREFUSED || ERRNO_ENETDOWN ) { + MSGOUT( "NET_UDPDriver::SendPacket: host is unreachable: %d.", ERRNO ); + return rc; + } else { + ASSERT( ERRNO_EWOULDBLOCK ); + MSGOUT( "NET_UDPDriver::SendPacket: send choke: %d.", ERRNO ); + } + } + + // print timeout message + FETCH_ERRNO(); + MSGOUT( "NET_UDPDriver::SendPacket: timeout writing to UDP socket.", ERRNO ); + + return rc; +} + + +// ---------------------------------------------------------------------------- +// protected methods +// ---------------------------------------------------------------------------- + + +// init tcp/ip stack (winsock) ------------------------------------------------ +// +int NET_UDPDriver::_InitOSAPI() +{ + MSGOUT( "Initializing TCP/IP stack..." ); + + return TRUE; +} + +// close tcp/ip stack (winsock) ----------------------------------------------- +// +int NET_UDPDriver::_KillOSAPI() +{ + // winsock cleanup + return TRUE; +} + +// open and initialize the UDP socket ----------------------------------------- +// +int NET_UDPDriver::_OpenSocket() +{ + ASSERT( m_socket == -1 ); + + // open UDP socket + m_socket = socket( AF_INET, SOCK_DGRAM, 0 ); + if ( m_socket < 0 ) { + fprintf( stderr, open_socket_error ); + return FALSE; + } + + // make socket non-blocking + int arg = TRUE; + ioctl( m_socket, FIONBIO, (char *)&arg ); + + // bind listen socket to UDP port on selected interface + sockaddr_in peeraddr; + bzero( &peeraddr, sizeof( peeraddr ) ); + peeraddr.sin_family = AF_INET; + // should bind to 0.0.0.0 + //memcpy ( &peeraddr.sin_addr, &m_Node.address, sizeof( IP_ADR_LENGTH ) ); + peeraddr.sin_port = htons( m_selected_port ); + + // set SO_REUSEADDR option, to avoid bind errors when re-binding + int on = 1; + setsockopt( m_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof( on ) ); + + bind( m_socket, (SA *) &peeraddr, sizeof( peeraddr ) ); + +#if 0 + // get the max msg size for the underlying UDP protocol + unsigned int msgsize; + int cbOpt = sizeof( msgsize ); + int i = getsockopt( m_socket, SOL_SOCKET, SO_MAX_MSG_SIZE, (char*)&msgsize, (int*)&cbOpt ); + printf( "SO_MAX_MSG_SIZE: %d\n", msgsize ); +#endif + + return TRUE; +} + +// close the UDP socket ------------------------------------------------------- +// +int NET_UDPDriver::_CloseSocket() +{ + if ( m_socket != -1 ) { + CLOSESOCKET( m_socket ); + m_socket = -1; + } + + return TRUE; +} + + +// resolve a hostname using the DNS service ----------------------------------- +// +int NET_UDPDriver::ResolveHostName( char *hostname, node_t* node ) +{ + ASSERT( hostname != NULL ); + ASSERT( node != NULL ); + + if ( isdigit( hostname[ 0 ] ) ) { + + // hostname is assumed to be an ip address + // -> just convert to binary representation + + inet_aton( hostname, (in_addr *) node ); + + return TRUE; + } + + hostent *hptr = gethostbyname( hostname ); + + if ( hptr == NULL ) { + MSGOUT( "NET_UDPDriver::ResolveHostName(): DNS error: %s %s.", hostname, hstrerror( h_errno ) ); + return FALSE; + } + + if ( hptr->h_addrtype == AF_INET ) { + char **pptr = hptr->h_addr_list; + if ( *pptr != NULL ) { + memcpy( node, *pptr, IP_ADR_LENGTH ); + return TRUE; + } + } else { + MSGOUT( "NET_UDPDriver::ResolveHostName(): illegal address type in DNS entry." ); + } + + return FALSE; +} + + +// get the local IP corresponding to the configured interface ----------------- +// +int NET_UDPDriver::_RetrieveLocalIP() +{ + //FIXME: implement :) + int selected_interface = 0; + + int sockfd = socket( AF_INET, SOCK_DGRAM, 0 ); + if ( sockfd < 0 ) { + MSGOUT( "NET_UDPDriver::_RetrieveLocalIP(): can't open socket." ); + return FALSE; + } + + int infolen = sizeof( struct ifreq ) * 10; + char* infobuf = NULL; + + struct ifconf ifc; + + infobuf = (char *) ALLOCMEM( infolen ); + if ( infobuf == NULL ) + OUTOFMEM( 0 ); + + ifc.ifc_len = infolen; + ifc.ifc_buf = infobuf; + + + // get all interface configurations into buffer + int lastlen = sizeof( struct ifreq ) * 10; + for ( ;; ) { + + infobuf = (char *) ALLOCMEM( infolen ); + if ( infobuf == NULL ) + OUTOFMEM( 0 ); + + ifc.ifc_len = infolen; + ifc.ifc_buf = infobuf; + + if ( ioctl( sockfd, SIOCGIFCONF, &ifc ) < 0 ) { + if ( ( errno != EINVAL ) || ( lastlen != 0 ) ) { + MSGOUT( "NET_UDPDriver::_RetrieveLocalIP(): ioctl error." ); + FREEMEM( infobuf ); + CLOSESOCKET( sockfd ); + return FALSE; + } + } else { + // if ifc.ifc_len is less than lastlen, we got it all. + if ( ifc.ifc_len < lastlen ){ + break; + } + // if not, save the last length + lastlen = ifc.ifc_len; + + //make it bigger by 1 record + infolen += sizeof( struct ifreq ); + + } + + //infolen += sizeof( struct ifreq ) * 10; + FREEMEM( infobuf ); + } + + int rc = 0; + int ifnum = -1; + + // analyze all interface configurations in buffer + for ( char *ptr = infobuf; ptr < infobuf + ifc.ifc_len; ) { + + struct ifreq* ifr = (struct ifreq *) ptr; + + // advance to next structure in buffer + +#ifdef SYSTEM_MACOSX_UNUSED + int len = max( sizeof(struct sockaddr), ifr->ifr_addr.sa_len ); +#else + int len = sizeof(struct sockaddr); +#endif + + // int len = max( sizeof(struct sockaddr), ifr->ifr_addr.sa_len ); + + ptr += sizeof( ifr->ifr_name ) + len; + + // only handle IPV4 entries + if ( ifr->ifr_addr.sa_family != AF_INET ) { + continue; + } + + char if_addr[ MAX_IPADDR_LEN + 1 ]; + sockaddr_in *sa = (struct sockaddr_in *) &ifr->ifr_addr; + inet_ntop( AF_INET, &sa->sin_addr, if_addr, MAX_IPADDR_LEN + 1 ); + + if ( strcmp( "127.0.0.1", if_addr ) == 0 ) { + MSGOUT( "skipping loopback interface" ); + continue; + } + + + + node_t tmp_node; + memcpy( &tmp_node, &sa->sin_addr, IP_ADR_LENGTH ); + NetworkInterfaces.push_back(tmp_node); + + + ifnum++; + MSGOUT( "found interface #%d: %s", ifnum, if_addr ); + } + + ASSERT(ifnum > -1); + + + // see if our preferred interface is in the vector + if(NetworkInterfaces.size() >= m_PreferredInterface) { + // let's use the preferred interface to set up the bind address + + _SetupInterface(&NetworkInterfaces[m_PreferredInterface]); + + + } else { + // else let's use interface 0 + _SetupInterface(&NetworkInterfaces[0]); // rc checked below + } + + //FREEMEM( infobuf ); + CLOSESOCKET( sockfd ); + + return TRUE; +} + + +// key table for netiface command ----------------------------------------- +// +key_value_s netiface_key_value[] = { + + { "interface", NULL, KEYVALFLAG_NONE }, + + { NULL, NULL, KEYVALFLAG_NONE }, +}; + +enum { + + KEY_NETIFACE_INTERFACE +}; + +PRIVATE +int Cmd_NETIFACE( char* niface_command ) +{ + //NOTE: + //CONCOM: + // netiface_command ::= 'sv.network.iface' <iface spec> + // iface_spec ::= 'interface' <iface num> + + + ASSERT( niface_command != NULL ); + HANDLE_COMMAND_DOMAIN( niface_command ); + + // scan out all values to keys + if ( !ScanKeyValuePairs( netiface_key_value, niface_command ) ) + return TRUE; + + char* pName = netiface_key_value[ KEY_NETIFACE_INTERFACE ].value; + + int selected_interface = atoi(pName); + + // set the selected interface on the UDPDRiver. + TheUDPDriver->SetPreferredInterface(selected_interface); + + // restart the socket on change + if(TheUDPDriver->IsRunning()){ + + TheUDPDriver->KillDriver(); + TheUDPDriver->InitDriver(NULL, SV_NETCONF_PORT); + } + + + return TRUE; +} + +REGISTER_MODULE( NET_UDPDriver ) +{ + user_command_s regcom; + memset( ®com, 0, sizeof( user_command_s ) ); + + // register "sv.network.iface" command + regcom.command = "sv.network.iface"; + regcom.numparams = 1; + regcom.execute = Cmd_NETIFACE; + regcom.statedump = NULL; + CON_RegisterUserCommand( ®com ); + +} + +void NET_UDPDriver::_SetupInterface(node_t *p_Node) { + + // store numeric + NODE_Copy(&m_Node, p_Node); + + // store presentation + inet_ntop( AF_INET, &m_Node, m_szIP, MAX_IPADDR_LEN + 1 ); + + // store port number in ip address + NODE_StorePort( &m_Node, m_selected_port ); + + +} diff --git a/src/parsec_server/sl_util_sv.cpp b/src/parsec_server/sl_util_sv.cpp new file mode 100755 index 0000000..4f15746 --- /dev/null +++ b/src/parsec_server/sl_util_sv.cpp @@ -0,0 +1,106 @@ +/* + * PARSEC - Utils Module + * + * $Author: uberlinuxguy $ - $Date: 2004/09/15 12:26:07 $ + * + * Orginally written by: + * Copyright (c) Markus Hadwiger <msh@parsec.org> 1998-2000 + * + * 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 includes +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "objstruc.h" + +// local module header +#include "sl_util_sv.h" +#include "utl_logfile.h" + + +// emulate missing or differently named c library functions ------------------- +// +int stricmp( const char *s1, const char *s2 ) +{ + return strcasecmp( s1, s2 ); +} + + +int strnicmp( const char *s1, const char *s2, int len ) +{ + return strncasecmp( s1, s2, len ); +} + + +char *strlwr( char *str ) +{ + for ( char *scan = str; *scan; scan++ ) { + *scan = tolower( *scan ); + } + + return str; +} + + +char *strupr( char *str ) +{ + for ( char *scan = str; *scan; scan++ ) { + *scan = toupper( *scan ); + } + + return str; +} + + +// cleanup function ----------------------------------------------------------- +// +void SYSs_CleanUp() +{ + // flush all logfiles + UTL_FlushRegisteredLogfiles(); +} + + +// signal handler to ensure correct termination of server --------------------- +// +void SignalHandler(int signum) +{ + SYSs_CleanUp(); +} + + +// install the signal handlers ------------------------------------------------ +// +void SYSs_InstallSignalHandlers() +{ + signal(SIGINT, SignalHandler ); + signal(SIGTERM, SignalHandler ); +} + + + + + diff --git a/src/parsec_server/sv_main.cpp b/src/parsec_server/sv_main.cpp new file mode 100755 index 0000000..9e5643b --- /dev/null +++ b/src/parsec_server/sv_main.cpp @@ -0,0 +1,92 @@ +/* + * PARSEC - Main Server Functions + * + * $Author: uberlinuxguy $ - $Date: 2004/09/15 12:25:43 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2001-2003 + * + * 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 + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "objstruc.h" + +// global externals +#include "globals.h" + +// network subsystem & server headers +#include "net_defs.h" +#include "e_defs.h" + +// UNP header +#include "net_wrap.h" + +// proprietary module headers +#include "e_gameserver.h" +#include "sys_util_sv.h" + +#include "MasterServer.h" + +// main entrypoint function for the gameserver -------------------------------- +// +int main( int argc, char** argv ) +{ + // install any signal handlers + SYSs_InstallSignalHandlers(); + + // get the server global + TheServer = E_GameServer::GetGameServer(); + + // parsec the commandline and check for proper usage + if ( TheServer->ParseCommandLine( argc, argv ) == FALSE ) { + + // print the program usage + TheServer->PrintUsage(); + + } else { + + // if we get here, then things went ok, so + // let's check if we are running as a Master Server + // and overload ourselves to a MasterServer Object + // if we are. + if(TheServer->GetServerIsMaster()){ + TheMaster = (MasterServer *)TheServer; //clever... + } + + // initialize the server components + if ( TheServer->Init() == FALSE ) { + return 0; + } + + // run the mainloop until server is stopped + TheServer->MainLoop(); + + // kill the server components + TheServer->Kill(); + } + + return 0; +} + diff --git a/src/parsec_server/sys_err_sv.cpp b/src/parsec_server/sys_err_sv.cpp new file mode 100755 index 0000000..42a056d --- /dev/null +++ b/src/parsec_server/sys_err_sv.cpp @@ -0,0 +1,185 @@ +/* + * PARSEC - Error Handling Code + * + * $Author: uberlinuxguy $ - $Date: 2004/09/15 12:26:07 $ + * + * 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 <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "objstruc.h" + +// global externals +#include "globals.h" + +// proprietary module headers +#include "sys_util_sv.h" + + +// common error strings ------------------------------------------------------- +// +static char err_exit[] = "Parsec error exit"; +static char err_emem[] = "Parsec memory error exit"; +static char err_ndef[] = "Unrecoverable error"; +static char err_nmem[] = "Memory allocation failed"; +static char err_msg1[] = "\n%s: %s"; +static char err_msg2[] = "\n%s: %s (%s, line %u)."; + +// cleanup macro -------------------------------------------------------------- +// +#define CLEAN_UP(x) SYSs_CleanUp() + + +// exit function if critical error occurred ----------------------------------- +// +void SYSs_PanicFunction( const char *msg, const char *file, unsigned line ) +{ + // print error message + if ( ( msg != NULL ) && ( *msg != 0 ) ) { + + // print custom message if provided and not empty + fprintf( stderr, err_msg1, err_exit, msg ); + + } else if ( ( msg != NULL ) && ( errno > 0 ) ) { + + // print errno error if provided message is empty + fprintf( stderr, err_msg2, err_exit, strerror( errno ), file, line ); + + } else { + + // print generic error message if no message provided + fprintf( stderr, err_msg2, err_exit, err_ndef, file, line ); + } + fprintf( stderr, "\n" ); + + // just to be sure + fflush( stderr ); + + CLEAN_UP(0); + //FIXME_GAMESERVER: check whether exit also automatically calls the destructor of the GameServer instance + exit( EXIT_FAILURE ); +} + + +// exit function if memory allocation failed ---------------------------------- +// +void SYSs_OutOfMemFunction( const char *msg, const char *file, unsigned line ) +{ + // print error message + if ( ( msg != NULL ) && ( *msg != 0 ) ) { + + // print custom message if provided and not empty + fprintf( stderr, err_msg1, err_emem, msg ); + + } else if ( ( msg != NULL ) && ( errno > 0 ) ) { + + // print errno error if provided message is empty + fprintf( stderr, err_msg2, err_emem, strerror( errno ), file, line ); + + } else { + + // print generic error message if no message provided + fprintf( stderr, err_msg2, err_emem, err_nmem, file, line ); + } + fprintf( stderr, "\n" ); + + // just to be sure + fflush( stderr ); + + CLEAN_UP(0); + //FIXME_GAMESERVER: check whether exit also automatically calls the destructor of the GameServer instance + exit( EXIT_FAILURE ); +} + + +// generic string paste area -------------------------------------------------- +// +#define PASTE_STR_LEN 255 +static char paste_str[ PASTE_STR_LEN + 1 ]; + +// prints error message and exits --------------------------------------------- +// +void SYSs_PError( const char *message, ... ) +{ + va_list ap; + va_start( ap, message ); + + // print custom message if provided + if ( message != NULL ) { + fprintf ( stderr, "\n%s: ", sys_ProgramName ); + vfprintf( stderr, message, ap ); + fprintf ( stderr, "\n" ); + } else { + fprintf( stderr, "\n%s: SYSs_PError().\n", sys_ProgramName ); + } + fflush( stderr ); + + // display error message box + if ( message != NULL ) { + vsprintf( paste_str, message, ap ); + fprintf ( stderr, "SYSs_PError(): %s.", paste_str ); + } else { + fprintf ( stderr, "SYSs_PError(): %s.", err_ndef ); + } + + va_end( ap ); + + CLEAN_UP(0); + //FIXME_GAMESERVER: check whether exit also automatically calls the destructor of the GameServer instance + exit( EXIT_FAILURE ); +} + + +// clean up functions that must be called in any case (if at all possible) ---- +// +void SYSs_CriticalCleanUp() +{ + //NOTE: + // this function is not declared in any header, + // since it is only used by DEBUG.C, which + // contains a prototype in order to call + // SYSs_CriticalCleanUp(). + + int static was_here = FALSE; + + if ( !was_here ) { + was_here = TRUE; + + // kill networking system; + // must not be done after SYSs_KillFrameTimer() + //FIXME_GAMESERVER: ???? + //NETs_KillAPI(); + + // kill timer code + //FIXME_GAMESERVER: ???? + //SYSs_KillFrameTimer(); + } +} + diff --git a/src/parsec_server/sys_msg_sv.cpp b/src/parsec_server/sys_msg_sv.cpp new file mode 100755 index 0000000..3641b6d --- /dev/null +++ b/src/parsec_server/sys_msg_sv.cpp @@ -0,0 +1,355 @@ +/* + * PARSEC - Message Output Function + * + * $Author: uberlinuxguy $ - $Date: 2004/09/15 12:26:07 $ + * + * Orginally written by: + * Copyright (c) Clemens Beer <cbx@parsec.org> 2001-2002 + * + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// C library +#ifdef SYSTEM_WIN32_UNUSED +# include <time.h> +#else +# include <sys/time.h> +#endif + +// general definitions +#include "general.h" +#include "objstruc.h" + +// global externals +#include "globals.h" + +// subsystem headers +#include "net_defs.h" + +// net game header +#include "net_game_sv.h" + +// mathematics header +#include "utl_math.h" + +// local module header +#include "sys_msg_sv.h" + +// proprietary module headers +#include "con_main_sv.h" +#include "e_relist.h" +#include "e_simulator.h" + + +//FIXME: Win32 specific +#ifdef SYSTEM_WIN32_UNUSED + #include "windows.h" +#endif // SYSTEM_WIN32_UNUSED + +// flags ---------------------------------------------------------------------- +// +//#define _DISABLE_ALL_OUTPUT +//#define _DISABLE_SCREEN_OUTPUT + +// name of optional log file ------------------------------------------------- +// +static char log_file[] = "parsec_server.log"; + +// paste buffer --------------------------------------------------------------- +// +#define MAX_MESSAGE_LEN 1024 +static char paste_buffer[ MAX_MESSAGE_LEN ]; + + +// function prototypes -------------------------------------------------------- +// +int SYS_WriteLogFileMessage ( const char *format, ... ); + + +// output messages (append new-line if missing ) ------------------------------ +// +int SYSs_MsgOut( const char *format, ... ) +{ +#ifdef _DISABLE_ALL_OUTPUT + return TRUE; +#endif // _DISABLE_ALL_OUTPUT + + va_list ap; + va_start( ap, format ); + int rc = vsnprintf( paste_buffer, MAX_MESSAGE_LEN, format, ap ); + va_end( ap ); + +#ifndef _DISABLE_SCREEN_OUTPUT + +#if defined ( USE_CURSES ) && defined ( PARSEC_SERVER ) + int x,y; + getyx( g_curses_in_win, y, x ); + + wprintw( g_curses_out_win, paste_buffer ); + + // append new-line if needed + if ( format[ strlen( format ) - 1 ] != '\n' ) + wprintw( g_curses_out_win, "\n" ); + + whline( g_curses_out_win, '-', COLS ); + wrefresh( g_curses_out_win ); + + wmove( g_curses_in_win, y, x ); + wrefresh( g_curses_in_win ); +#else + printf( paste_buffer ); + // append new-line if needed + if ( format[ strlen( format ) - 1 ] != '\n' ) + printf( "\n" ); +#endif // USE_CURSES + +#endif // _DISABLE_SCREEN_OUTPUT + + // write to logfile + SYS_WriteLogFileMessage( paste_buffer ); + + return rc; +} + + +// put messages (output them and flush afterwards) ---------------------------- +// +int SYSs_MsgPut( const char *format, ... ) +{ +#if defined _DISABLE_ALL_OUTPUT || defined _DISABLE_SCREEN_OUTPUT + return TRUE; +#endif // _DISABLE_ALL_OUTPUT + + //NOTE: + // this function does not print a + // new-line at the end of the string, + // but flushes stdout after output. + // this can be used to continue printing + // in the same line. + +#if defined ( USE_CURSES ) && defined ( PARSEC_SERVER ) + + va_list ap; + va_start( ap, format ); + int rc = vsnprintf( paste_buffer, MAX_MESSAGE_LEN, format, ap ); + va_end( ap ); + + int x,y; + getyx( g_curses_in_win, y, x ); + wprintw( g_curses_out_win, paste_buffer ); + whline( g_curses_out_win, '-', COLS ); + wrefresh( g_curses_out_win ); + wmove( g_curses_in_win, y, x ); + wrefresh( g_curses_in_win ); + + return rc; +#else + va_list ap; + va_start( ap, format ); + int rc = vfprintf( stdout, format, ap ); + fflush( stdout ); + va_end( ap ); + + return rc; +#endif +} + +/* +// easy access to the singleton ----------------------------------------------- +// +#define TheMicroSecondTimer SYSs_MicroSecondTimer::GetMicroSecondTimer() + +// a microsecond timer with the best resolution for a specific platform ------- +// +class SYSs_MicroSecondTimer +{ + //NOTE: based on ideas from the tcpdump/windump sources + +protected: + LARGE_INTEGER PTime; + LARGE_INTEGER SystemTime; + LARGE_INTEGER StartTime; + LARGE_INTEGER TimeFreq; + + LARGE_INTEGER m_LastCall; + + SYSs_MicroSecondTimer() + { + // get the absolute value of the system boot time. + QueryPerformanceFrequency( &TimeFreq ); + QueryPerformanceCounter( &PTime ); + + GetSystemTimeAsFileTime( (LPFILETIME)&SystemTime ); + + StartTime.QuadPart = ( ( ( SystemTime.QuadPart ) % 10000000 ) * TimeFreq.QuadPart ) / 10000000; + + // convert from MS (1.1.1601) to UNIX (1.1.1970) seconds + SystemTime.QuadPart = SystemTime.QuadPart / 10000000 - 11644473600; + + StartTime.QuadPart += ( ( SystemTime.QuadPart ) * TimeFreq.QuadPart ) - PTime.QuadPart; + + // init lastcall time to + m_LastCall.QuadPart = -1; + } + + ~SYSs_MicroSecondTimer() {} + +public: + + // SINGLETON pattern + static SYSs_MicroSecondTimer* GetMicroSecondTimer() + { + static SYSs_MicroSecondTimer _TheMicroSecondTimer; + return &_TheMicroSecondTimer; + } + + void FillTimeval( timeval* tv, timeval* tv_diff = NULL ) + { + ASSERT( tv != NULL ); + + // get the current tickcount + LARGE_INTEGER now; + QueryPerformanceCounter( &now ); + + // adjust to ticks since 1.1.1970 + now.QuadPart += StartTime.QuadPart; + tv->tv_usec = (long) ( ( now.QuadPart % TimeFreq.QuadPart * 1000000 ) / TimeFreq.QuadPart ); + tv->tv_sec = (long) ( now.QuadPart / TimeFreq.QuadPart ); + + // fill difference to last call ? + if ( tv_diff != NULL ) { + + // first time call ? + if ( m_LastCall.QuadPart == -1 ) { + tv_diff->tv_sec = 0; + tv_diff->tv_usec = 0; + } else { + // calc diff + LARGE_INTEGER diff; + diff.QuadPart= now.QuadPart - m_LastCall.QuadPart; + tv_diff->tv_usec = (long) ( ( diff.QuadPart % TimeFreq.QuadPart * 1000000 ) / TimeFreq.QuadPart ); + tv_diff->tv_sec = (long) ( diff.QuadPart / TimeFreq.QuadPart ); + } + m_LastCall = now; + } + } +}; + + + + +// get the current time in the format 2002/05/16 16:16:04.800 ----------------- +// +const char* SYSs_GetCurTimeString() +{ + static char szBuffer[ 128 ] = ""; + +#ifdef SYSTEM_WIN32_UNUSED + + + // get the current timeval ( usec resolution ) + timeval tv, tv_diff; + TheMicroSecondTimer->FillTimeval( &tv, &tv_diff ); + + SYSTEMTIME st; + GetLocalTime( &st ); + sprintf( szBuffer, "%4d/%02d/%02d %02d:%02d:%02d.%06d (%d) (%2d.%6d):\t", + st.wYear, + st.wMonth, + st.wDay, + st.wHour, + st.wMinute, + st.wSecond, + tv.tv_usec, + TheSimulator->GetCurSimRefFrame(), + tv_diff.tv_sec, + tv_diff.tv_usec ); + +#endif // SYSTEM_WIN32_UNUSED + + return szBuffer; +} +*/ + +// write message to log file -------------------------------------------------- +// +int SYS_WriteLogFileMessage( const char *format, ... ) +{ +#ifdef _DISABLE_ALL_OUTPUT + return TRUE; +#endif // _DISABLE_ALL_OUTPUT + + FILE *fp = fopen( log_file, "a" ); + if ( fp != NULL ) { + + int formatlen = strlen( format ); + if ( formatlen < 1 ) { + return -1; + } + + // prepend the current date/time + +#ifdef SYSTEM_WIN32_UNUSED + + //FIXME: recheck whether SYSs_GetCurTimeString() works properly + //fprintf( fp, "%s: ", SYSs_GetCurTimeString() ); + + LARGE_INTEGER now; + LARGE_INTEGER TimeFreq; + QueryPerformanceCounter( &now ); + QueryPerformanceFrequency( &TimeFreq ); + + timeval tv; + tv.tv_usec = (long) ( ( now.QuadPart % TimeFreq.QuadPart * 1000000 ) / TimeFreq.QuadPart ); + tv.tv_sec = (long) ( now.QuadPart / TimeFreq.QuadPart ); + +#else + + struct timeval tv; + struct timezone tz; + gettimeofday( &tv, &tz ); + +#endif + + fprintf( fp, "%ld.%06d: ", tv.tv_sec, tv.tv_usec ); + + va_list ap; + va_start( ap, format ); + int rc = vfprintf( fp, format, ap ); + va_end( ap ); + + // append newline in logfile + if ( format[ formatlen - 1 ] != '\n' ) { + fputs( "\n", fp ); + } + + fclose( fp ); + return rc; + } + + return -1; +} + + diff --git a/src/parsec_server/sys_util_sv.cpp b/src/parsec_server/sys_util_sv.cpp new file mode 100755 index 0000000..fe43729 --- /dev/null +++ b/src/parsec_server/sys_util_sv.cpp @@ -0,0 +1,73 @@ +/* + * PARSEC - Server Util Functions + * + * $Author: uberlinuxguy $ - $Date: 2004/09/15 12:26:07 $ + * + * 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> + +// compilation flags/debug support +#include "config.h" +#include "debug.h" + +// general definitions +#include "general.h" +#include "objstruc.h" + +// local module header +#include "sys_util_sv.h" + +// do a hexdump --------------------------------------------------------------- +// +void SYS_HexDump( char* data, int len ) +{ + char szASCII[ 17 ]; + memset( szASCII, 0, 17 ); + + printf( "-----------------------------------------------------------------------------\n"); + unsigned char* p = (unsigned char*)data; + for( int i = 0; i < len; i++ ) { + if ( i % 16 == 0 ) { + printf( "%08X: ", i ); + } + printf( "%02X ", *p ); + szASCII[ i % 16 ] = isprint( *p ) ? *p : '.'; + int newline = ( ( ( i % 16 ) == 15 ) || ( i == ( len - 1 ) ) ) ; + + // align ASCII part in last line + if ( i == ( len - 1 ) ) { + int miss = ( 15 - ( i % 16 ) ); + for( int k = 0; k < miss; k++ ) { + printf( " " ); + } + } + + if( newline ) { + printf( " | %s\n", szASCII ); + memset( szASCII, 0, 17 ); + } + p++; + } + printf( "-----------------------------------------------------------------------------\n"); +} + |
