aboutsummaryrefslogtreecommitdiff
path: root/src/parsec_server
diff options
context:
space:
mode:
Diffstat (limited to 'src/parsec_server')
-rw-r--r--src/parsec_server/.cproject44
-rw-r--r--src/parsec_server/.project27
-rw-r--r--src/parsec_server/MasterServer.cpp95
-rw-r--r--src/parsec_server/MasterServerItem.cpp214
-rwxr-xr-xsrc/parsec_server/con_act_sv.cpp3681
-rwxr-xr-xsrc/parsec_server/con_aux_sv.cpp236
-rwxr-xr-xsrc/parsec_server/con_com_sv.cpp849
-rwxr-xr-xsrc/parsec_server/con_ext_sv.cpp1193
-rwxr-xr-xsrc/parsec_server/con_info_sv.cpp2120
-rwxr-xr-xsrc/parsec_server/con_int_sv.cpp402
-rwxr-xr-xsrc/parsec_server/con_list_sv.cpp1253
-rwxr-xr-xsrc/parsec_server/con_load_sv.cpp735
-rwxr-xr-xsrc/parsec_server/con_main_sv.cpp1586
-rwxr-xr-xsrc/parsec_server/con_std_sv.cpp159
-rwxr-xr-xsrc/parsec_server/e_colldet_sv.cpp1251
-rwxr-xr-xsrc/parsec_server/e_connmanager.cpp819
-rwxr-xr-xsrc/parsec_server/e_gameserver.cpp1013
-rwxr-xr-xsrc/parsec_server/e_global_sv.cpp146
-rwxr-xr-xsrc/parsec_server/e_packethandler.cpp1544
-rwxr-xr-xsrc/parsec_server/e_simnetinput.cpp401
-rwxr-xr-xsrc/parsec_server/e_simnetoutput.cpp1195
-rwxr-xr-xsrc/parsec_server/e_simplayerinfo.cpp315
-rwxr-xr-xsrc/parsec_server/e_simulator.cpp844
-rwxr-xr-xsrc/parsec_server/e_world.cpp2710
-rwxr-xr-xsrc/parsec_server/g_main_sv.cpp1192
-rwxr-xr-xsrc/parsec_server/g_player.cpp843
-rwxr-xr-xsrc/parsec_server/g_stgate_sv.cpp672
-rw-r--r--src/parsec_server/include/MasterServer.h32
-rw-r--r--src/parsec_server/include/MasterServerItem.h53
-rw-r--r--src/parsec_server/include/con_act_sv.h239
-rw-r--r--src/parsec_server/include/con_aux_sv.h59
-rw-r--r--src/parsec_server/include/con_com_sv.h71
-rw-r--r--src/parsec_server/include/con_ext_sv.h91
-rw-r--r--src/parsec_server/include/con_info_sv.h63
-rw-r--r--src/parsec_server/include/con_int_sv.h59
-rw-r--r--src/parsec_server/include/con_list_sv.h34
-rw-r--r--src/parsec_server/include/con_load_sv.h16
-rw-r--r--src/parsec_server/include/con_main_sv.h168
-rw-r--r--src/parsec_server/include/con_std_sv.h120
-rwxr-xr-xsrc/parsec_server/include/e_colldet.h75
-rwxr-xr-xsrc/parsec_server/include/e_connmanager.h247
-rw-r--r--src/parsec_server/include/e_defs.h45
-rwxr-xr-xsrc/parsec_server/include/e_gameserver.h204
-rwxr-xr-xsrc/parsec_server/include/e_global_sv.h97
-rwxr-xr-xsrc/parsec_server/include/e_packethandler.h147
-rwxr-xr-xsrc/parsec_server/include/e_simnetinput.h48
-rwxr-xr-xsrc/parsec_server/include/e_simnetoutput.h361
-rwxr-xr-xsrc/parsec_server/include/e_simplayerinfo.h67
-rwxr-xr-xsrc/parsec_server/include/e_simulator.h286
-rwxr-xr-xsrc/parsec_server/include/e_world.h295
-rwxr-xr-xsrc/parsec_server/include/e_world_trans.h33
-rwxr-xr-xsrc/parsec_server/include/g_main_sv.h275
-rwxr-xr-xsrc/parsec_server/include/g_player.h148
-rw-r--r--src/parsec_server/include/inp_defs_sv.h30
-rw-r--r--src/parsec_server/include/inp_keybmap_sv.h559
-rw-r--r--src/parsec_server/include/inp_main_sv.h12
-rw-r--r--src/parsec_server/include/net_game_sv.h30
-rwxr-xr-xsrc/parsec_server/include/net_packetdriver.h138
-rwxr-xr-xsrc/parsec_server/include/net_subh_sv.h128
-rwxr-xr-xsrc/parsec_server/include/net_udpdriver.h120
-rw-r--r--src/parsec_server/include/net_util_sv.h25
-rw-r--r--src/parsec_server/include/sl_util_sv.h9
-rw-r--r--src/parsec_server/include/sys_err_sv.h31
-rw-r--r--src/parsec_server/include/sys_msg_sv.h29
-rw-r--r--src/parsec_server/include/sys_refframe_sv.h29
-rwxr-xr-xsrc/parsec_server/include/sys_util_sv.h18
-rwxr-xr-xsrc/parsec_server/inp_main_sv.cpp117
-rwxr-xr-xsrc/parsec_server/net_packetdriver.cpp755
-rwxr-xr-xsrc/parsec_server/net_util_sv.cpp138
-rwxr-xr-xsrc/parsec_server/nl_udpdriver.cpp528
-rwxr-xr-xsrc/parsec_server/sl_util_sv.cpp106
-rwxr-xr-xsrc/parsec_server/sv_main.cpp92
-rwxr-xr-xsrc/parsec_server/sys_err_sv.cpp185
-rwxr-xr-xsrc/parsec_server/sys_msg_sv.cpp355
-rwxr-xr-xsrc/parsec_server/sys_util_sv.cpp73
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, &param ) ) {
+
+ 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, &param ) ) {
+
+ 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, &param ) ) {
+
+ 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, &param ) ) {
+
+ 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 *) &params[ 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 *) &params[ 1 ];
+ num_scheduled_commands++;
+ }
+ }
+
+ // return number of parameter bytes to skip
+ size_t paramsize = sizeof( int ) * 2;
+ paramsize += GetActionParamSize( actid, (char *) &params[ 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( &regcom, 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( &regcom );
+ }
+}
+
+
+// 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( &regcom );
+
+#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 = &registered_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 == &registered_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 = &registered_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( &regcom, 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( &regcom );
+
+ // register "sv.conf" command
+ regcom.command = "sv.conf";
+ regcom.numparams = 1;
+ regcom.execute = Cmd_SV_CONF;
+ regcom.statedump = NULL;
+ CON_RegisterUserCommand( &regcom );
+
+ // register "sv.link" command
+ regcom.command = "sv.link";
+ regcom.numparams = 0;
+ regcom.execute = Cmd_SV_LINK;
+ regcom.statedump = NULL;
+ CON_RegisterUserCommand( &regcom );
+}
+
+
+
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( &regcom, 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( &regcom );
+
+}
+
+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");
+}
+