mirror of
https://bitbucket.org/theswgsource/src-1.2.git
synced 2026-10-20 10:02:55 -04:00
Added serverGame library
This commit is contained in:
@@ -0,0 +1,827 @@
|
||||
// ======================================================================
|
||||
//
|
||||
// PlayerShipController.cpp
|
||||
// asommers
|
||||
//
|
||||
// copyright 2003, sony online entertainment
|
||||
//
|
||||
// ======================================================================
|
||||
|
||||
#include "serverGame/FirstServerGame.h"
|
||||
#include "serverGame/PlayerShipController.h"
|
||||
|
||||
#include "serverGame/AiShipBehaviorDock.h"
|
||||
#include "serverGame/ConfigServerGame.h"
|
||||
#include "serverGame/CreatureObject.h"
|
||||
#include "serverGame/PlayerShipTurretTargetingSystem.h"
|
||||
#include "serverGame/ServerShipObjectInterface.h"
|
||||
#include "serverGame/ShipAiEnemySearchManager.h"
|
||||
#include "serverGame/ShipClientUpdateTracker.h"
|
||||
#include "serverGame/ShipObject.h"
|
||||
#include "serverGame/TangibleObject.h"
|
||||
#include "serverScript/GameScriptObject.h"
|
||||
#include "serverScript/ScriptParameters.h"
|
||||
#include "sharedDebug/DebugFlags.h"
|
||||
#include "sharedFoundation/ConstCharCrcString.h"
|
||||
#include "sharedFoundation/GameControllerMessage.h"
|
||||
#include "sharedGame/ShipDynamicsModel.h"
|
||||
#include "sharedLog/Log.h"
|
||||
#include "sharedMathArchive/TransformArchive.h"
|
||||
#include "sharedNetworkMessages/MessageQueueDataTransform.h"
|
||||
#include "sharedNetworkMessages/MessageQueueGenericValueType.h"
|
||||
#include "sharedNetworkMessages/MessageQueueSpaceMiningSellResource.h"
|
||||
#include "sharedNetworkMessages/MessageQueueTeleportAck.h"
|
||||
#include "sharedNetworkMessages/ShipUpdateTransformCollisionMessage.h"
|
||||
#include "sharedNetworkMessages/ShipUpdateTransformMessage.h"
|
||||
#include "sharedObject/AlterResult.h"
|
||||
#include "sharedObject/NetworkIdManager.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
// ======================================================================
|
||||
// PlayerShipControllerNamespace
|
||||
// ======================================================================
|
||||
|
||||
namespace PlayerShipControllerNamespace
|
||||
{
|
||||
bool ms_logHandleNetUpdateTransform;
|
||||
|
||||
void remove();
|
||||
|
||||
float const s_targetedByAiExpireTime = 20.0f;
|
||||
|
||||
int syncStampLongDeltaTime(uint32 stamp1, uint32 stamp2)
|
||||
{
|
||||
uint32 const delta = stamp1 - stamp2;
|
||||
if (delta > 0x7fffffff)
|
||||
return static_cast<int>(0xffffffff-delta);
|
||||
return static_cast<int>(delta);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
using namespace PlayerShipControllerNamespace;
|
||||
|
||||
// ======================================================================
|
||||
|
||||
void PlayerShipController::install()
|
||||
{
|
||||
DebugFlags::registerFlag(ms_logHandleNetUpdateTransform, "ServerGame/RemoteShipController", "logHandleNetUpdateTransform*");
|
||||
ExitChain::add(&remove, "PlayerShipController::remove");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void PlayerShipControllerNamespace::remove()
|
||||
{
|
||||
DebugFlags::unregisterFlag(ms_logHandleNetUpdateTransform);
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// PUBLIC PlayerShipController
|
||||
// ======================================================================
|
||||
|
||||
PlayerShipController::PlayerShipController(ShipObject * const owner) :
|
||||
ShipController(owner),
|
||||
m_clientToServerLastSyncStamp(0),
|
||||
m_teleportIds(),
|
||||
m_shipAccelRate(owner->getShipActualAccelerationRate(), ConfigServerGame::getShipMoveCheckIntervalMs(), ConfigServerGame::getShipMoveCheckFudgeTimeMs()),
|
||||
m_shipDecelRate(owner->getShipActualDecelerationRate(), ConfigServerGame::getShipMoveCheckIntervalMs(), ConfigServerGame::getShipMoveCheckFudgeTimeMs()),
|
||||
m_shipYprMaximum(owner->getShipActualYprRateMaximum(), ConfigServerGame::getShipMoveCheckIntervalMs(), ConfigServerGame::getShipMoveCheckFudgeTimeMs()),
|
||||
m_shipSpeedMaximum(owner->getShipActualSpeedMaximum(), ConfigServerGame::getShipMoveCheckIntervalMs(), ConfigServerGame::getShipMoveCheckFudgeTimeMs()),
|
||||
m_lastVerifiedTransform(),
|
||||
m_lastVerifiedSpeed(0.f),
|
||||
m_lastVerifiedSyncStamp(0),
|
||||
m_gunnerWeaponIndexList(new GunnerWeaponIndexList),
|
||||
m_targetedByAiTimer(s_targetedByAiExpireTime)
|
||||
{
|
||||
preventMovementUpdates();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
PlayerShipController::~PlayerShipController()
|
||||
{
|
||||
delete m_gunnerWeaponIndexList;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void PlayerShipController::endBaselines()
|
||||
{
|
||||
if (getAuthoritative())
|
||||
{
|
||||
ShipObject * const ship = getShipOwner();
|
||||
ship->clearCondition(static_cast<int>(TangibleObject::C_docking));
|
||||
|
||||
m_shipAccelRate.setValue(ship->getShipActualAccelerationRate(), 0);
|
||||
m_shipDecelRate.setValue(ship->getShipActualDecelerationRate(), 0);
|
||||
m_shipYprMaximum.setValue(ship->getShipActualYprRateMaximum(), 0);
|
||||
m_shipSpeedMaximum.setValue(ship->getShipActualSpeedMaximum(), 0);
|
||||
}
|
||||
|
||||
m_yawPosition = 0.f;
|
||||
m_pitchPosition = 0.f;
|
||||
m_rollPosition = 0.f;
|
||||
m_throttlePosition = 0.f;
|
||||
|
||||
ShipController::endBaselines();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void PlayerShipController::setAuthoritative(bool newAuthoritative)
|
||||
{
|
||||
ShipController::setAuthoritative(newAuthoritative);
|
||||
|
||||
if (!newAuthoritative)
|
||||
preventMovementUpdates();
|
||||
else
|
||||
{
|
||||
ShipObject const * const ship = NON_NULL(getShipOwner());
|
||||
if (ship->getClient())
|
||||
resyncMovementUpdates();
|
||||
|
||||
m_shipAccelRate.setValue(ship->getShipActualAccelerationRate(), 0);
|
||||
m_shipDecelRate.setValue(ship->getShipActualDecelerationRate(), 0);
|
||||
m_shipYprMaximum.setValue(ship->getShipActualYprRateMaximum(), 0);
|
||||
m_shipSpeedMaximum.setValue(ship->getShipActualSpeedMaximum(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void PlayerShipController::onClientReady()
|
||||
{
|
||||
m_clientToServerLastSyncStamp = 0;
|
||||
if (getAuthoritative())
|
||||
resyncMovementUpdates();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void PlayerShipController::onClientLost()
|
||||
{
|
||||
m_yawPosition = 0.f;
|
||||
m_pitchPosition = 0.f;
|
||||
m_rollPosition = 0.f;
|
||||
m_throttlePosition = 0.f;
|
||||
preventMovementUpdates();
|
||||
unDock();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void PlayerShipController::receiveTransform(ShipUpdateTransformMessage const & shipUpdateTransformMessage)
|
||||
{
|
||||
if (isTeleporting())
|
||||
return;
|
||||
|
||||
ShipObject * const owner = NON_NULL(getShipOwner());
|
||||
|
||||
if (owner->hasCondition(static_cast<int>(TangibleObject::C_docking)))
|
||||
return;
|
||||
|
||||
uint32 const syncStamp = shipUpdateTransformMessage.getSyncStampLong();
|
||||
if ( m_clientToServerLastSyncStamp == 0
|
||||
|| syncStampLongDeltaTime(m_clientToServerLastSyncStamp, syncStamp) > 0)
|
||||
{
|
||||
m_clientToServerLastSyncStamp = syncStamp;
|
||||
|
||||
Transform const &transform = shipUpdateTransformMessage.getTransform();
|
||||
Vector const &velocity = shipUpdateTransformMessage.getVelocity();
|
||||
float const speed = velocity.magnitude();
|
||||
|
||||
if (!checkValidMove(transform, velocity, speed, syncStamp))
|
||||
teleport(m_lastVerifiedTransform, 0);
|
||||
else
|
||||
{
|
||||
m_shipDynamicsModel->setTransform(transform);
|
||||
m_shipDynamicsModel->setVelocity(velocity);
|
||||
|
||||
ServerShipObjectInterface const serverShipObjectInterface(owner);
|
||||
|
||||
//-- Transform speeds into throttle positions and compute delta time
|
||||
m_throttlePosition = 0.f;
|
||||
m_yawPosition = 0.f;
|
||||
m_pitchPosition = 0.f;
|
||||
m_rollPosition = 0.f;
|
||||
float deltaTime = 0.f;
|
||||
|
||||
Client const * const client = owner->getClient();
|
||||
if (client)
|
||||
{
|
||||
CreatureObject const * const pilot = safe_cast<CreatureObject const *>(client->getCharacterObject());
|
||||
if (pilot && !pilot->getState(States::Frozen))
|
||||
{
|
||||
if (serverShipObjectInterface.getMaximumSpeed() > 0.f)
|
||||
m_throttlePosition = clamp(0.f, speed / serverShipObjectInterface.getMaximumSpeed(), 1.f);
|
||||
if (serverShipObjectInterface.getMaximumYaw() > 0.f)
|
||||
m_yawPosition = clamp(-1.f, shipUpdateTransformMessage.getYawRate() / serverShipObjectInterface.getMaximumYaw(), 1.f);
|
||||
if (serverShipObjectInterface.getMaximumPitch() > 0.f)
|
||||
m_pitchPosition = clamp(-1.f, shipUpdateTransformMessage.getPitchRate() / serverShipObjectInterface.getMaximumPitch(), 1.f);
|
||||
if (serverShipObjectInterface.getMaximumRoll() > 0.f)
|
||||
m_rollPosition = clamp(-1.f, shipUpdateTransformMessage.getRollRate() / serverShipObjectInterface.getMaximumRoll(), 1.f);
|
||||
|
||||
deltaTime = client->computeDeltaTimeInSeconds(syncStamp);
|
||||
}
|
||||
}
|
||||
|
||||
//-- Advance by delta time
|
||||
m_shipDynamicsModel->model(deltaTime, m_yawPosition, m_pitchPosition, m_rollPosition, m_throttlePosition, serverShipObjectInterface);
|
||||
|
||||
DEBUG_REPORT_LOG(ms_logHandleNetUpdateTransform, ("PlayerShipController::handleNetUpdateTransform[%s]: %1.2f\n", owner->getNetworkId().getValueString().c_str(), m_throttlePosition));
|
||||
}
|
||||
}
|
||||
else
|
||||
DEBUG_REPORT_LOG(true, ("PlayerShipController::handleNetUpdateTransform[%s]: disregarding older packet\n", owner->getNetworkId().getValueString().c_str()));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void PlayerShipController::teleport(Transform const &goal, ServerObject *goalObj)
|
||||
{
|
||||
ShipObject * const ship = NON_NULL(getShipOwner());
|
||||
Client * const client = ship->getClient();
|
||||
|
||||
if (client && !goalObj)
|
||||
{
|
||||
int const sequenceId = static_cast<int>(ship->getAndIncrementMoveSequenceNumber());
|
||||
IGNORE_RETURN(m_teleportIds.insert(sequenceId));
|
||||
|
||||
appendMessage(
|
||||
CM_netUpdateTransform,
|
||||
0.f,
|
||||
new MessageQueueDataTransform(0, sequenceId, goal, 0.f, 0.f, false),
|
||||
GameControllerMessageFlags::RELIABLE|GameControllerMessageFlags::SEND|GameControllerMessageFlags::DEST_AUTH_CLIENT);
|
||||
|
||||
m_lastVerifiedTransform = goal;
|
||||
m_lastVerifiedSpeed = 0;
|
||||
m_lastVerifiedSyncStamp = getCurSyncStamp();
|
||||
}
|
||||
|
||||
ShipController::teleport(goal, goalObj);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void PlayerShipController::handleMessage(int const message, float const value, MessageQueue::Data const * const data, uint32 const flags)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case CM_teleportAck:
|
||||
{
|
||||
MessageQueueTeleportAck const * const msg = dynamic_cast<MessageQueueTeleportAck const *>(data);
|
||||
if (msg)
|
||||
handleTeleportAck(msg->getSequenceId());
|
||||
}
|
||||
break;
|
||||
case CM_shipStopFiring:
|
||||
{
|
||||
ShipObject * const ship = NON_NULL(getShipOwner());
|
||||
|
||||
typedef MessageQueueGenericValueType<int> MessageType;
|
||||
MessageType const * const msg = dynamic_cast<MessageType const *>(data);
|
||||
|
||||
if (msg)
|
||||
{
|
||||
int const weaponIndex = msg->getValue();
|
||||
ship->stopFiringWeapon(weaponIndex);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case CM_spaceMiningSaleSellResource:
|
||||
{
|
||||
MessageQueueSpaceMiningSellResource const * const msg = dynamic_cast<MessageQueueSpaceMiningSellResource const *>(data);
|
||||
if (msg != NULL)
|
||||
{
|
||||
ServerObject * const spaceStation = safe_cast<ServerObject *>(NetworkIdManager::getObjectById(msg->m_spaceStationId));
|
||||
if (NULL == spaceStation)
|
||||
{
|
||||
WARNING(true, ("PlayerCreatureController CM_spaceMiningSaleSellResource invalid station [%s]", msg->m_spaceStationId.getValueString().c_str()));
|
||||
}
|
||||
else
|
||||
{
|
||||
GameScriptObject * const scriptObject = spaceStation->getScriptObject();
|
||||
if (scriptObject)
|
||||
{
|
||||
ScriptParams params;
|
||||
params.addParam(getOwner()->getNetworkId());
|
||||
params.addParam(msg->m_shipId);
|
||||
params.addParam(msg->m_spaceStationId);
|
||||
params.addParam(msg->m_resourceId);
|
||||
params.addParam(msg->m_amount);
|
||||
scriptObject->trigAllScripts(Scripting::TRIG_SPACE_MINING_SELL_RESOURCE, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ShipController::handleMessage(message, value, data, flags);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void PlayerShipController::onShipAccelerationRateChanged(float value)
|
||||
{
|
||||
m_shipAccelRate.setValue(value, getCurSyncStamp());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void PlayerShipController::onShipDecelerationRateChanged(float value)
|
||||
{
|
||||
m_shipDecelRate.setValue(value, getCurSyncStamp());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void PlayerShipController::onShipYprMaximumChanged(float value)
|
||||
{
|
||||
m_shipYprMaximum.setValue(value, getCurSyncStamp());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void PlayerShipController::onShipSpeedMaximumChanged(float value)
|
||||
{
|
||||
m_shipSpeedMaximum.setValue(value, getCurSyncStamp());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool PlayerShipController::isTeleporting() const
|
||||
{
|
||||
return !m_teleportIds.empty();
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// PROTECTED PlayerShipController
|
||||
// ======================================================================
|
||||
|
||||
float PlayerShipController::realAlter(float const elapsedTime)
|
||||
{
|
||||
PROFILER_AUTO_BLOCK_DEFINE("PlayerShipController::realAlter");
|
||||
|
||||
ShipObject * const owner = getShipOwner();
|
||||
|
||||
if (!owner || !owner->isInWorld())
|
||||
return ShipController::realAlter(elapsedTime);
|
||||
|
||||
if (owner && owner->isInitialized())
|
||||
{
|
||||
if (m_pendingDockingBehavior != NULL)
|
||||
{
|
||||
delete m_dockingBehavior;
|
||||
m_dockingBehavior = m_pendingDockingBehavior;
|
||||
m_pendingDockingBehavior = NULL;
|
||||
|
||||
getShipOwner()->setCondition(static_cast<int>(TangibleObject::C_docking));
|
||||
Client * const client = owner->getClient();
|
||||
|
||||
if (client != NULL)
|
||||
{
|
||||
ShipClientUpdateTracker::queueForUpdate(*client, *owner);
|
||||
}
|
||||
else
|
||||
{
|
||||
unDock();
|
||||
}
|
||||
}
|
||||
|
||||
if ( (m_dockingBehavior != NULL)
|
||||
&& m_dockingBehavior->isDockFinished())
|
||||
{
|
||||
delete m_dockingBehavior;
|
||||
m_dockingBehavior = NULL;
|
||||
|
||||
getShipOwner()->clearCondition(static_cast<int>(TangibleObject::C_docking));
|
||||
}
|
||||
|
||||
if (m_dockingBehavior != NULL)
|
||||
{
|
||||
//-- The docking behavior will calculate new values for m_yaw/m_pitch/m_roll/m_throttle[Position] implicitly through calling ShipController members
|
||||
m_dockingBehavior->alter(elapsedTime);
|
||||
}
|
||||
|
||||
//-- Update flight model
|
||||
ServerShipObjectInterface const serverShipObjectInterface(owner);
|
||||
m_shipDynamicsModel->model(elapsedTime, m_yawPosition, m_pitchPosition, m_rollPosition, m_throttlePosition, serverShipObjectInterface);
|
||||
|
||||
//-- Update the server position based on the model
|
||||
owner->setTransform_o2p(m_shipDynamicsModel->getTransform());
|
||||
|
||||
// Request new enemies for any turrets
|
||||
|
||||
if ( hasTurretTargetingSystem()
|
||||
&& !m_enemyCheckQueued)
|
||||
{
|
||||
m_enemyCheckQueued = true;
|
||||
ShipAiEnemySearchManager::add(*owner);
|
||||
}
|
||||
|
||||
if (m_targetedByAiTimer.updateNoReset(elapsedTime))
|
||||
{
|
||||
owner->clearCondition(static_cast<int>(TangibleObject::C_spaceCombatMusic));
|
||||
}
|
||||
}
|
||||
|
||||
//-- Chain back up to parent
|
||||
float alterResult = ShipController::realAlter(elapsedTime);
|
||||
|
||||
AlterResult::incorporateAlterResult(alterResult, AlterResult::cms_alterNextFrame);
|
||||
return alterResult;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void PlayerShipController::handleNetUpdateTransform(MessageQueueDataTransform const & /*message*/)
|
||||
{
|
||||
//-- This message should be handled in handleMessage
|
||||
DEBUG_FATAL(true, ("PlayerShipController::handleNetUpdateTransform: not implemented (nor should it be)"));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void PlayerShipController::handleNetUpdateTransformWithParent(MessageQueueDataTransformWithParent const & /*message*/)
|
||||
{
|
||||
//-- This message should be handled in handleMessage
|
||||
DEBUG_FATAL(true, ("PlayerShipController::handleNetUpdateTransformWithParent: not implemented (nor should it be)"));
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
// PRIVATE PlayerShipController
|
||||
// ======================================================================
|
||||
|
||||
void PlayerShipController::preventMovementUpdates()
|
||||
{
|
||||
m_teleportIds.clear();
|
||||
IGNORE_RETURN(m_teleportIds.insert(-1));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void PlayerShipController::handleTeleportAck(int sequenceId)
|
||||
{
|
||||
IGNORE_RETURN(m_teleportIds.erase(sequenceId));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void PlayerShipController::resyncMovementUpdates()
|
||||
{
|
||||
// We resync updates by doing a fake teleport, giving a negative
|
||||
// sequence so the client does not actually do the movement.
|
||||
// This locks out movement updates until the teleport is acknowledged.
|
||||
|
||||
ShipObject * const ship = NON_NULL(getShipOwner());
|
||||
Client * const client = ship->getClient();
|
||||
|
||||
FATAL(!client || !ship->isAuthoritative(), ("Tried to resync ship movement updates either while nonauth or without a client!"));
|
||||
|
||||
m_teleportIds.clear();
|
||||
|
||||
int sequenceId = -1;
|
||||
while (sequenceId == -1)
|
||||
sequenceId = -static_cast<int>(ship->getAndIncrementMoveSequenceNumber());
|
||||
|
||||
IGNORE_RETURN(m_teleportIds.insert(sequenceId));
|
||||
|
||||
appendMessage(
|
||||
CM_netUpdateTransform,
|
||||
0.f,
|
||||
new MessageQueueDataTransform(0, sequenceId, Transform::identity, 0.f, 0.f, false),
|
||||
GameControllerMessageFlags::RELIABLE|GameControllerMessageFlags::SEND|GameControllerMessageFlags::DEST_AUTH_CLIENT);
|
||||
|
||||
m_lastVerifiedTransform = m_shipDynamicsModel->getTransform();
|
||||
m_lastVerifiedSpeed = m_shipDynamicsModel->getSpeed();
|
||||
m_lastVerifiedSyncStamp = getCurSyncStamp();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void PlayerShipController::experiencedCollision()
|
||||
{
|
||||
ShipObject * const owner = getShipOwner();
|
||||
if (owner != 0)
|
||||
{
|
||||
Client* client = owner->getClient();
|
||||
|
||||
if (client != 0)
|
||||
{
|
||||
ShipUpdateTransformCollisionMessage const collisionMessage(
|
||||
owner->getNetworkId(),
|
||||
getTransform(),
|
||||
getVelocity(),
|
||||
client->getServerSyncStampLong());
|
||||
client->send(collisionMessage, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
uint32 PlayerShipController::getCurSyncStamp() const
|
||||
{
|
||||
Client const * const client = NON_NULL(getShipOwner())->getClient();
|
||||
return client ? client->getServerSyncStampLong() : 0;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void PlayerShipController::logMoveFail(char const *reasonFmt, ...) const
|
||||
{
|
||||
ShipObject const * const ship = NON_NULL(getShipOwner());
|
||||
Client const * const client = NON_NULL(ship->getClient());
|
||||
|
||||
char reason[512];
|
||||
va_list ap;
|
||||
va_start(ap, reasonFmt);
|
||||
IGNORE_RETURN(_vsnprintf(reason, sizeof(reason)-1, reasonFmt, ap));
|
||||
reason[sizeof(reason)-1] = '\0';
|
||||
|
||||
LOG(
|
||||
"shipmove",
|
||||
(
|
||||
"fail - ship %s creature %s stationId %u - %s",
|
||||
ship->getNetworkId().getValueString().c_str(),
|
||||
client->getCharacterObjectId().getValueString().c_str(),
|
||||
client->getStationId(),
|
||||
reason));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool PlayerShipController::checkValidMove(Transform const &transform, Vector const &velocity, float speed, uint32 const currentClientSyncStamp)
|
||||
{
|
||||
if (!ConfigServerGame::getShipMoveValidationEnabled())
|
||||
return true;
|
||||
|
||||
ShipObject * const owner = NON_NULL(getShipOwner());
|
||||
Client const * const client = NON_NULL(owner->getClient());
|
||||
|
||||
// gods can move however they like, and assume their moves are valid
|
||||
if (!client->isGod())
|
||||
{
|
||||
uint32 const currentServerSyncStamp = client->getServerSyncStampLong();
|
||||
uint32 const previousClientSyncStamp = m_lastVerifiedSyncStamp;
|
||||
int const timeDiffMs = syncStampLongDeltaTime(previousClientSyncStamp, currentClientSyncStamp);
|
||||
float const timeDiffSec = static_cast<float>(timeDiffMs)*0.001f;
|
||||
float const tolerance = ConfigServerGame::getShipMoveCheckTolerance();
|
||||
float const timeTolerance = ConfigServerGame::getShipMoveCheckTimeToleranceSeconds();
|
||||
|
||||
// is our new speed valid?
|
||||
float const speedMaximum = m_shipSpeedMaximum.getRecentMaximum(currentServerSyncStamp);
|
||||
if (speed > speedMaximum*tolerance)
|
||||
{
|
||||
logMoveFail("too fast (speed=%g, speedMax=%g, timeDiff=%g, time %u)", speed, speedMaximum, timeDiffSec, currentClientSyncStamp);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only do position/rotation validation periodically, since their timestamps
|
||||
// can be affected by latency variation.
|
||||
if (timeDiffMs < ConfigServerGame::getShipMoveCheckIntervalMs())
|
||||
{
|
||||
// assume we're ok to move, but don't update the last verified
|
||||
return true;
|
||||
}
|
||||
|
||||
// check for bogus timestamps, making sure to handle timestamp rollover
|
||||
if (ConfigServerGame::getMoveValidationCheckForPastTimestamp())
|
||||
{
|
||||
// new timestamp from client must be greater than previous timestamp from client (i.e. cannot travel back in time)
|
||||
if (currentClientSyncStamp < previousClientSyncStamp)
|
||||
{
|
||||
uint32 const rolloverFudgeFactor = static_cast<uint32>(100000); // 100 seconds
|
||||
|
||||
// pretty sure that timestamp did not rollover
|
||||
if ((previousClientSyncStamp > rolloverFudgeFactor) && ((std::numeric_limits<uint32>::max() - rolloverFudgeFactor) > previousClientSyncStamp))
|
||||
{
|
||||
int const timeDiffMs = syncStampLongDeltaTime(previousClientSyncStamp, currentClientSyncStamp);
|
||||
|
||||
// only fail movement if the difference is beyond a certain threshold,
|
||||
// to account for network latency, UDP unreliability, clock drift, etc.
|
||||
if (timeDiffMs > ConfigServerGame::getMoveValidationPastTimestampThresholdMs())
|
||||
{
|
||||
logMoveFail("out of order timestamps (%dms), oss=%lu, css=%lu (%lud:%luh:%lum:%lus), ssc=%lu",
|
||||
timeDiffMs,
|
||||
previousClientSyncStamp,
|
||||
currentServerSyncStamp,
|
||||
(currentServerSyncStamp / 1000) / (60 * 60 * 24),
|
||||
((currentServerSyncStamp / 1000) % (60 * 60 * 24)) / (60 * 60),
|
||||
((currentServerSyncStamp / 1000) % (60 * 60)) / 60,
|
||||
(currentServerSyncStamp / 1000) % 60,
|
||||
currentClientSyncStamp);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check for bogus timestamps, making sure to handle timestamp rollover
|
||||
if (ConfigServerGame::getMoveValidationCheckForFutureTimestamp())
|
||||
{
|
||||
// new timestamp from client must be less than the current server timestamp (i.e. cannot travel into the future)
|
||||
if (currentClientSyncStamp > currentServerSyncStamp)
|
||||
{
|
||||
uint32 const rolloverFudgeFactor = static_cast<uint32>(100000); // 100 seconds
|
||||
|
||||
// pretty sure that timestamp did not rollover
|
||||
if ((currentServerSyncStamp > rolloverFudgeFactor) && ((std::numeric_limits<uint32>::max() - rolloverFudgeFactor) > currentServerSyncStamp))
|
||||
{
|
||||
int const timeDiffMs = syncStampLongDeltaTime(currentClientSyncStamp, currentServerSyncStamp);
|
||||
|
||||
// only fail movement if the difference is beyond a certain threshold,
|
||||
// to account for network latency, UDP unreliability, clock drift, etc.
|
||||
if (timeDiffMs > ConfigServerGame::getMoveValidationFutureTimestampThresholdMs())
|
||||
{
|
||||
logMoveFail("timestamp from the future (%dms), oss=%lu, css=%lu (%lud:%luh:%lum:%lus), ssc=%lu",
|
||||
timeDiffMs,
|
||||
previousClientSyncStamp,
|
||||
currentServerSyncStamp,
|
||||
(currentServerSyncStamp / 1000) / (60 * 60 * 24),
|
||||
((currentServerSyncStamp / 1000) % (60 * 60 * 24)) / (60 * 60),
|
||||
((currentServerSyncStamp / 1000) % (60 * 60)) / 60,
|
||||
(currentServerSyncStamp / 1000) % 60,
|
||||
currentClientSyncStamp);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make sure the client and server time hasn't drifted too far apart
|
||||
if (ConfigServerGame::getMoveValidationCheckForTimestampDrift())
|
||||
{
|
||||
int const clientServerTimeDiff = syncStampLongDeltaTime(currentClientSyncStamp, currentServerSyncStamp);
|
||||
if (clientServerTimeDiff > ConfigServerGame::getMoveValidationTimestampDriftThresholdMs())
|
||||
{
|
||||
logMoveFail("timestamp drift (%dms), oss=%lu, css=%lu (%lud:%luh:%lum:%lus), ssc=%lu",
|
||||
clientServerTimeDiff,
|
||||
previousClientSyncStamp,
|
||||
currentServerSyncStamp,
|
||||
(currentServerSyncStamp / 1000) / (60 * 60 * 24),
|
||||
((currentServerSyncStamp / 1000) % (60 * 60 * 24)) / (60 * 60),
|
||||
((currentServerSyncStamp / 1000) % (60 * 60)) / 60,
|
||||
(currentServerSyncStamp / 1000) % 60,
|
||||
currentClientSyncStamp);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// could we possibly have changed position enough?
|
||||
Vector const &lastPosition = m_lastVerifiedTransform.getPosition_p();
|
||||
Vector const &position = transform.getPosition_p();
|
||||
float const distanceSquared = lastPosition.magnitudeBetweenSquared(position);
|
||||
if (distanceSquared > sqr(speedMaximum*(timeDiffSec+timeTolerance)*tolerance)+0.2f)
|
||||
{
|
||||
logMoveFail(
|
||||
"too far (distSquared=%g, maxDistSquared=%g, timeDiff=%g from [%g,%g,%g] to [%g,%g,%g], time %u)",
|
||||
distanceSquared, sqr(speedMaximum*timeDiffSec), timeDiffSec,
|
||||
lastPosition.x, lastPosition.y, lastPosition.z,
|
||||
position.x, position.y, position.z, currentClientSyncStamp);
|
||||
return false;
|
||||
}
|
||||
|
||||
// could we possibly have changed speeds enough?
|
||||
float const oldSpeed = m_lastVerifiedSpeed;
|
||||
if (speed > oldSpeed)
|
||||
{
|
||||
float const accel = (oldSpeed-speed)/(timeDiffSec+timeTolerance);
|
||||
float const maxAccel = m_shipAccelRate.getRecentMaximum(currentServerSyncStamp);
|
||||
if (accel > maxAccel*tolerance)
|
||||
{
|
||||
logMoveFail("too much acceleration (accel=%g, maxAccel=%g, time %u)", accel, maxAccel, currentClientSyncStamp);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (speed < oldSpeed)
|
||||
{
|
||||
float const decel = (speed-oldSpeed)/(timeDiffSec+timeTolerance);
|
||||
float const maxDecel = m_shipDecelRate.getRecentMaximum(currentServerSyncStamp);
|
||||
if (decel > maxDecel*tolerance)
|
||||
{
|
||||
logMoveFail("too much deceleration (decel=%g, maxDecel=%g, time %u)", decel, maxDecel, currentClientSyncStamp);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// could we possibly have changed orientation enough?
|
||||
float const rotation = acos(transform.getLocalFrameK_p().dot(m_lastVerifiedTransform.getLocalFrameK_p()))/(timeDiffSec+timeTolerance);
|
||||
float const maxRotation = m_shipYprMaximum.getRecentMaximum(currentServerSyncStamp);
|
||||
// Actual max rotation is roughly sqrt(2*sqr(maxRotation))+0.001f, to allow
|
||||
// for pitch/yaw/roll together being applied toward the same direction, and
|
||||
// adjust slightly for precision.
|
||||
if (sqr(rotation) > 2*sqr(maxRotation*tolerance)+0.001f)
|
||||
{
|
||||
logMoveFail("too much rotation (rotation=%g, maxRotation=%g, time %u)", rotation, maxRotation, currentClientSyncStamp);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
UNREF(velocity);
|
||||
m_lastVerifiedTransform = transform;
|
||||
m_lastVerifiedSpeed = speed;
|
||||
m_lastVerifiedSyncStamp = currentClientSyncStamp;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
PlayerShipController * PlayerShipController::asPlayerShipController()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
PlayerShipController const * PlayerShipController::asPlayerShipController() const
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
bool PlayerShipController::shouldCheckForEnemies() const
|
||||
{
|
||||
return (getShipOwner()->hasTurrets());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void PlayerShipController::updateGunnerWeaponIndex(NetworkId const & player, int const gunnerWeaponIndex)
|
||||
{
|
||||
GunnerWeaponIndexList::iterator iterGunnerWeaponIndexList = m_gunnerWeaponIndexList->find(player);
|
||||
|
||||
if (iterGunnerWeaponIndexList != m_gunnerWeaponIndexList->end())
|
||||
{
|
||||
int const previousGunnerWeaponIndex = iterGunnerWeaponIndexList->second;
|
||||
iterGunnerWeaponIndexList->second = gunnerWeaponIndex;
|
||||
|
||||
if ( (previousGunnerWeaponIndex >= 0)
|
||||
&& (gunnerWeaponIndex == -1))
|
||||
{
|
||||
// Gunner is exiting a gun
|
||||
|
||||
setWeaponIndexPlayerControlled(previousGunnerWeaponIndex, false);
|
||||
m_gunnerWeaponIndexList->erase(iterGunnerWeaponIndexList);
|
||||
}
|
||||
else if (previousGunnerWeaponIndex != gunnerWeaponIndex)
|
||||
{
|
||||
// Gunner was previously in a gun and is moving to a new gun
|
||||
|
||||
setWeaponIndexPlayerControlled(previousGunnerWeaponIndex, false);
|
||||
setWeaponIndexPlayerControlled(gunnerWeaponIndex, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gunnerWeaponIndex >= 0)
|
||||
{
|
||||
// Gunner is entering a gun and was previously not in a gun
|
||||
|
||||
setWeaponIndexPlayerControlled(gunnerWeaponIndex, true);
|
||||
IGNORE_RETURN(m_gunnerWeaponIndexList->insert(std::make_pair(player, gunnerWeaponIndex)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Gunner is not entering a gun and was not previously in a gun
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void PlayerShipController::setWeaponIndexPlayerControlled(int const weaponIndex, bool const playerControlled)
|
||||
{
|
||||
DEBUG_FATAL((weaponIndex < 0), ("Invalid weaponIndex(%d)", weaponIndex));
|
||||
|
||||
if (m_turretTargetingSystem != NULL)
|
||||
{
|
||||
safe_cast<PlayerShipTurretTargetingSystem*>(m_turretTargetingSystem)->setWeaponIndexPlayerControlled(weaponIndex, playerControlled);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
void PlayerShipController::addAiTargetingMe(NetworkId const & unit)
|
||||
{
|
||||
ShipController::addAiTargetingMe(unit);
|
||||
|
||||
if (m_targetedByAiTimer.isExpired())
|
||||
{
|
||||
getShipOwner()->setCondition(static_cast<int>(TangibleObject::C_spaceCombatMusic));
|
||||
}
|
||||
|
||||
m_targetedByAiTimer.reset();
|
||||
}
|
||||
|
||||
// ======================================================================
|
||||
Reference in New Issue
Block a user