aboutsummaryrefslogtreecommitdiff
path: root/src/parsec_server/e_simulator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/parsec_server/e_simulator.cpp')
-rwxr-xr-xsrc/parsec_server/e_simulator.cpp844
1 files changed, 844 insertions, 0 deletions
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;
+}