Initial public release of rem-essentials
This commit is contained in:
Vendored
+190
@@ -0,0 +1,190 @@
|
||||
#include "Freelancer.h"
|
||||
#include "update.h"
|
||||
#include "utils.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#define M_PIF 3.14159265358979323846f
|
||||
#define MIN_SYNC_INTERVAL_SEC (40.0f / 1000.0f)
|
||||
#define MIN_SYNC_INTERVAL_TLR_SEC (750.0f / 1000.0f)
|
||||
#define MAX_SYNC_INTERVAL_SEC (2000.0f / 1000.0f)
|
||||
#define ROTATION_CHECK_INTERVAL_SEC (250.0f / 1000.0f)
|
||||
|
||||
bool sendUpdateAsap = true;
|
||||
bool engineKillEnabledLastTime = false;
|
||||
|
||||
#define DEFAULT_SHIP_TURN_THRESHOLD 30.0f
|
||||
float shipTurnThreshold = DEFAULT_SHIP_TURN_THRESHOLD;
|
||||
|
||||
Quaternion lastOrientation;
|
||||
float secElapsedSinceLastUpdate = 0.0;
|
||||
|
||||
void ResetTimeSinceLastUpdate()
|
||||
{
|
||||
secElapsedSinceLastUpdate = 0.0;
|
||||
sendUpdateAsap = false;
|
||||
}
|
||||
|
||||
void ForceObjUpdate()
|
||||
{
|
||||
secElapsedSinceLastUpdate = MAX_SYNC_INTERVAL_SEC;
|
||||
sendUpdateAsap = true;
|
||||
}
|
||||
|
||||
bool IsEkEnabled(const CShip& ship)
|
||||
{
|
||||
// This seems to be a relatively fast operation; Freelancer calls it numerous times per frame.
|
||||
CEEngine const * engine = CEEngine::cast(ship.equipManager.FindFirst(ENGINE_TYPE));
|
||||
|
||||
if (!engine)
|
||||
return false;
|
||||
|
||||
return !engine->IsTriggered();
|
||||
}
|
||||
|
||||
// Checks if engine kill has been toggled and update the last known value.
|
||||
bool IsEkToggled(const CShip& ship)
|
||||
{
|
||||
bool engineKillEnabled = IsEkEnabled(ship);
|
||||
|
||||
bool result = engineKillEnabledLastTime != engineKillEnabled;
|
||||
engineKillEnabledLastTime = engineKillEnabled;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool HasOrientationChanged(const CShip& ship, float secElapsed)
|
||||
{
|
||||
if (secElapsed < ROTATION_CHECK_INTERVAL_SEC)
|
||||
return false;
|
||||
|
||||
float rotationDelta = GetRotationDelta(lastOrientation, ship.get_orientation());
|
||||
return rotationDelta >= shipTurnThreshold;
|
||||
}
|
||||
|
||||
float GetShipTurnThreshold(const CShip& ship)
|
||||
{
|
||||
Archetype::Ship const * shipArch = ship.shiparch();
|
||||
|
||||
// TODO: The angular drag is meant to be calculated dynamically using the CShip::get_angular_drag() function.
|
||||
// However, the angular drag factor is kind of an unused feature in FL and not many mods use it.
|
||||
// Though some do for instance to increase the weight of the ship based on the amount of cargo you have.
|
||||
// This means the turn speed should be continuously recalculated instead of only once on launch.
|
||||
float avgDrag = (shipArch->angularDrag.x + shipArch->angularDrag.y) / 2.0f;
|
||||
float avgTorque = (shipArch->steeringTorque.x + shipArch->steeringTorque.y) / 2.0f;
|
||||
float maxTurnSpeed = (avgTorque / avgDrag) * (180.0f / M_PIF);
|
||||
|
||||
return std::min(DEFAULT_SHIP_TURN_THRESHOLD, 15.0f * sqrtf(maxTurnSpeed) / sqrtf(ship.get_radius()));
|
||||
}
|
||||
|
||||
namespace Update
|
||||
{
|
||||
void (*PostInitDealloc_Original)(PVOID obj);
|
||||
|
||||
// Hook for dealloc function that gets called right after initializing the player's ship (undock or load game in space)
|
||||
// This is where we want to calculate the ship's turn threshold and set some default values
|
||||
void PostInitDealloc_Hook(PVOID obj)
|
||||
{
|
||||
// Call original function
|
||||
PostInitDealloc_Original(obj);
|
||||
|
||||
if (SinglePlayer()) // No need to calculate the turn threshold in SP
|
||||
return;
|
||||
|
||||
engineKillEnabledLastTime = false;
|
||||
// TODO: Check if it is really needed to force an update initially.
|
||||
// Does FL already correctly position the ship after undocking with a default velocity?
|
||||
ForceObjUpdate();
|
||||
|
||||
if (CShip* ship = GetPlayerShip())
|
||||
shipTurnThreshold = GetShipTurnThreshold(*ship);
|
||||
else
|
||||
shipTurnThreshold = DEFAULT_SHIP_TURN_THRESHOLD;
|
||||
}
|
||||
}
|
||||
|
||||
bool ShouldSendUpdate(const CShip& ship, float secElapsed)
|
||||
{
|
||||
// Has it been a while since the last update?
|
||||
// Has the orientation been changed to some extent?
|
||||
return (secElapsed >= MAX_SYNC_INTERVAL_SEC) || HasOrientationChanged(ship, secElapsed);
|
||||
}
|
||||
|
||||
inline float GetShipMinSyncInterval(const CShip& ship)
|
||||
{
|
||||
// Ensure updates are sent less frequently when the player ship is taking a tradelane to prevent jitter
|
||||
return ship.is_using_tradelane() ? MIN_SYNC_INTERVAL_TLR_SEC : MIN_SYNC_INTERVAL_SEC;
|
||||
}
|
||||
|
||||
void (*SendUpdatesToServer_Original)(float deltaTime);
|
||||
|
||||
// Hook that keeps track of the elapsed time.
|
||||
void SendUpdatesToServer_Hook(float deltaTime)
|
||||
{
|
||||
if (!SinglePlayer())
|
||||
{
|
||||
secElapsedSinceLastUpdate += deltaTime;
|
||||
SendUpdatesToServer_Original(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
// Hook for function that determines whether an update should be sent to the server
|
||||
bool CRemotePhysicsSimulation::CheckForSync_Hook(const CShip& ship, Vector const &shipPos, Quaternion const &unk)
|
||||
{
|
||||
bool isEkToggled = IsEkToggled(ship);
|
||||
bool syncResult = CheckForSync(shipPos, shipPos, unk);
|
||||
|
||||
if (secElapsedSinceLastUpdate < GetShipMinSyncInterval(ship))
|
||||
{
|
||||
// Prevent the client from sending too many updates in a short amount of time
|
||||
// This resolves the jitter issue that occurs when playing on a high framerate
|
||||
|
||||
// TODO: If EK has been toggled twice before the min sync interval has passed, then an asap update should actually not be sent because of this.
|
||||
// But then you'd also have to check if the asap update *should* be sent because CheckForSync or ShouldSendUpdate returned true. Eh, this sounds complicated.
|
||||
if (!sendUpdateAsap)
|
||||
sendUpdateAsap = syncResult || isEkToggled || ShouldSendUpdate(ship, secElapsedSinceLastUpdate);
|
||||
|
||||
return false;
|
||||
}
|
||||
else if (sendUpdateAsap)
|
||||
{
|
||||
// If an update has been missed, send an update as soon as this becomes possible, but do it only once
|
||||
return true;
|
||||
}
|
||||
|
||||
return syncResult || isEkToggled || ShouldSendUpdate(ship, secElapsedSinceLastUpdate);
|
||||
}
|
||||
|
||||
// Hook for function that sends an update to the server
|
||||
void IServerImpl::SPObjUpdate_Hook(const CShip& ship, SSPObjUpdateInfo &updateInfo, UINT client)
|
||||
{
|
||||
// Get throttle from the ship and set it in the update info if engine kill is currently disabled.
|
||||
// If it's enabled we want to set the throttle value to 0.
|
||||
updateInfo.throttle = engineKillEnabledLastTime ? 0.0f : ship.get_throttle();
|
||||
|
||||
// Send update to the server
|
||||
SPObjUpdate(updateInfo, client);
|
||||
|
||||
ResetTimeSinceLastUpdate();
|
||||
lastOrientation = MatrixToQuaternion(ship.get_orientation());
|
||||
}
|
||||
|
||||
// This allows for extra checks to prevent jitters and allow smoother updates from the client to the server.
|
||||
// Also fixes a bug where the client always sends the throttle state as 0.
|
||||
void InitBetterUpdates()
|
||||
{
|
||||
#define SEND_UPDATES_TO_SERVER_CALL_ADDR (0x54B16D)
|
||||
#define SERVER_UPDATE_SP_CHECK_ADDR (0x54158C)
|
||||
|
||||
SendUpdatesToServer_Original = SetRelPointer(SEND_UPDATES_TO_SERVER_CALL_ADDR + 1, SendUpdatesToServer_Hook);
|
||||
// Wipe out the original single player check because it is already checked for in the hook.
|
||||
Nop(SERVER_UPDATE_SP_CHECK_ADDR, 14);
|
||||
|
||||
Update::PostInitDealloc_Original = SetRelPointer(POST_INIT_DEALLOC_CALL_ADDR + 1, Update::PostInitDealloc_Hook);
|
||||
|
||||
Hook(CHECK_FOR_SYNC_CALL_ADDR, &CRemotePhysicsSimulation::CheckForSync_Hook, 5);
|
||||
Patch<BYTE>(PUSH_SHIP_POS_SYNC_CHECK_ADDR, 0x57); // push eax -> push edi (provide the CShip& to the CheckForSync hook)
|
||||
|
||||
Patch<BYTE>(OBJ_UPDATE_CALL_ADDR, 0x57); // push edi (provide the CShip& to the SPObjUpdate hook)
|
||||
Hook(OBJ_UPDATE_CALL_ADDR + 1, &IServerImpl::SPObjUpdate_Hook, 5);
|
||||
}
|
||||
Reference in New Issue
Block a user