Skip to content

Commit

Permalink
feat(gamestate/server): CREATE_TRAIN(_CARRIAGE) command
Browse files Browse the repository at this point in the history
  • Loading branch information
Ehbw committed Dec 31, 2023
1 parent bab1c17 commit 96b19df
Show file tree
Hide file tree
Showing 7 changed files with 378 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -547,18 +547,18 @@ struct CTrainGameStateDataNodeData
bool isEngine;
bool isCaboose;

bool unk12;
bool isMissionTrain;

bool direction;

bool unk14;
bool hasPassengerCarriages;

bool renderDerailed;

// 2372 {
bool unk198; //unk198
bool highPrecisionBlending; //unk224
bool hasNoThreadId; //unk199
bool allowRemovalByPopulation;
bool highPrecisionBlending;
bool shouldStopAtStations;
// }

bool forceDoorsOpen;
Expand Down Expand Up @@ -648,17 +648,6 @@ enum ePopType
POPTYPE_TOOL
};

//TODO: Probably should be moved out of fx::sync namespace
struct scrVector
{
float x;
int pad;
float y;
int pad2;
float z;
int pad3;
};

struct SyncTreeBase
{
public:
Expand Down Expand Up @@ -1124,6 +1113,9 @@ struct ScriptGuid
}
};

auto GetTrain(fx::ServerGameState* sgs, uint32_t objectId) -> fx::sync::SyncEntityPtr;
auto GetNextTrain(fx::ServerGameState* sgs, const fx::sync::SyncEntityPtr& entity) -> fx::sync::SyncEntityPtr;

struct EntityCreationState
{
// TODO: allow resending in case the target client disappears
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2627,22 +2627,23 @@ struct CTrainGameStateDataNode : GenericSerializeDataNode<CTrainGameStateDataNod

//2372 inserted a bool between isEngine and isCaboose
if (Is2372())
{
s.Serialize(data.unk198);
{
//Modified by 0x2310A8F9421EBF43
s.Serialize(data.allowRemovalByPopulation);
}

s.Serialize(data.isCaboose);
s.Serialize(data.unk12);
s.Serialize(data.isMissionTrain);
s.Serialize(data.direction);
s.Serialize(data.unk14);
s.Serialize(data.hasPassengerCarriages);
s.Serialize(data.renderDerailed);

s.Serialize(data.forceDoorsOpen);

if (Is2372())
{
//Always true on randomly spawned trains
s.Serialize(data.hasNoThreadId);
// true on randomly spawned metro trains
s.Serialize(data.shouldStopAtStations);

//Modified by 0xEC0C1D4922AF9754
s.Serialize(data.highPrecisionBlending);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -839,7 +839,8 @@ void sync::SyncCommandList::Execute(const fx::ClientSharedPtr& client)
}

#ifdef STATE_FIVE
static auto GetTrain(fx::ServerGameState* sgs, uint32_t objectId) -> fx::sync::SyncEntityPtr

auto GetTrain(fx::ServerGameState* sgs, uint32_t objectId) -> fx::sync::SyncEntityPtr
{
if (objectId != 0)
{
Expand All @@ -854,7 +855,7 @@ static auto GetTrain(fx::ServerGameState* sgs, uint32_t objectId) -> fx::sync::S
return {};
};

static auto GetNextTrain(fx::ServerGameState* sgs, const fx::sync::SyncEntityPtr& entity) -> fx::sync::SyncEntityPtr
auto GetNextTrain(fx::ServerGameState* sgs, const fx::sync::SyncEntityPtr& entity) -> fx::sync::SyncEntityPtr
{
if (auto trainState = entity->syncTree->GetTrainState())
{
Expand Down
248 changes: 248 additions & 0 deletions code/components/citizen-server-impl/src/state/ServerSetters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@

#include <GameServer.h>

#include <ResourceManager.h>
#include <Resource.h>
#include <fxScripting.h>

#include <boost/mpl/at.hpp>
#include <boost/function_types/parameter_types.hpp>
#include <ServerInstanceBaseRef.h>

namespace fx
{
Expand Down Expand Up @@ -156,6 +158,64 @@ std::shared_ptr<sync::SyncTreeBase> MakeVehicle(uint32_t model, float posX, floa
return tree;
}

template<typename TTree>
std::shared_ptr<sync::CTrainSyncTree> MakeTrain(uint32_t model, float posX, float posY, float posZ, float heading, uint32_t resourceHash, bool isEngine, bool direction, bool stopAtStation, float speed, uint32_t trackid, uint32_t trainConfigIndex)
{
auto tree = std::make_shared<TTree>();

SetupNode(tree, [model](sync::CVehicleCreationDataNode& cdn)
{
cdn.m_model = model;
cdn.m_creationToken = msec().count();
cdn.m_needsToBeHotwired = false;
cdn.m_maxHealth = 1000;
cdn.m_popType = sync::POPTYPE_MISSION;
cdn.m_randomSeed = rand();
cdn.m_tyresDontBurst = false;
cdn.m_vehicleStatus = 2;
cdn.m_unk5 = false;
});

SetupNode(tree, [isEngine, speed, direction, trackid, trainConfigIndex, stopAtStation](sync::CTrainGameStateDataNode& cdn)
{
cdn.data.linkedToBackwardId = 0;
cdn.data.linkedToForwardId = 0;
cdn.data.distanceFromEngine = 0.0f;

cdn.data.trainState = 3;

cdn.data.isEngine = isEngine;
cdn.data.cruiseSpeed = speed;
cdn.data.direction = direction;
cdn.data.trackId = trackid;
cdn.data.trainConfigIndex = trainConfigIndex;

cdn.data.isCaboose = true;

cdn.data.isMissionTrain = true;
cdn.data.hasPassengerCarriages = true;
cdn.data.renderDerailed = false;

if (Is2372())
{
cdn.data.allowRemovalByPopulation = false;
cdn.data.shouldStopAtStations = stopAtStation;
cdn.data.highPrecisionBlending = false;
}
});

SetupPosition<sync::CSectorDataNode, sync::CSectorPositionDataNode>(tree, posX, posY, posZ);
SetupHeading(tree, heading);

SetupNode(tree, [resourceHash](sync::CEntityScriptInfoDataNode& cdn)
{
cdn.m_scriptHash = resourceHash;
cdn.m_timestamp = msec().count();
});

return tree;
}

std::shared_ptr<sync::SyncTreeBase> MakePed(uint32_t model, float posX, float posY, float posZ, uint32_t resourceHash, float heading = 0.0f)
{
auto tree = std::make_shared<sync::CPedSyncTree>();
Expand Down Expand Up @@ -294,6 +354,38 @@ void DisownEntityScript(const fx::sync::SyncEntityPtr& entity)
}
}

static auto GetTrainCarriageCount(fx::ServerGameState* sgs, const fx::sync::SyncEntityPtr& engine) -> int
{
if (engine->type != fx::sync::NetObjEntityType::Train)
{
return -1;
}

int carriageCount = 0;
for (auto link = GetNextTrain(sgs, engine); link; link = GetNextTrain(sgs, link))
{
carriageCount++;
}

return carriageCount;
};

static auto GetTrainCabooseCarriage(fx::ServerGameState* sgs, const fx::sync::SyncEntityPtr& engine) -> fx::sync::SyncEntityPtr
{
fx::sync::SyncEntityPtr caboose = engine;
for (auto link = GetNextTrain(sgs, engine); link; link = GetNextTrain(sgs, link))
{
auto state = link->syncTree->GetTrainState();

if (state->linkedToBackwardId == 0 || state->isCaboose)
{
caboose = link;
break;
}
}
return caboose;
}

static InitFunction initFunction([]()
{
fx::ServerInstanceBase::OnServerCreate.Connect([](fx::ServerInstanceBase* ref)
Expand Down Expand Up @@ -447,6 +539,162 @@ static InitFunction initFunction([]()
ctx.SetResult(sgs->MakeScriptHandle(entity));
});

fx::ScriptEngine::RegisterNativeHandler("CREATE_TRAIN", [=](fx::ScriptContext& ctx)
{
uint32_t resourceHash = 0;

fx::OMPtr<IScriptRuntime> runtime;

if (FX_SUCCEEDED(fx::GetCurrentScriptRuntime(&runtime)))
{
fx::Resource* resource = reinterpret_cast<fx::Resource*>(runtime->GetParentObject());

if (resource)
{
resourceHash = HashString(resource->GetName().c_str());
}
}

uint32_t modelHash = ctx.GetArgument<uint32_t>(0);
float x = ctx.GetArgument<float>(1);
float y = ctx.GetArgument<float>(2);
float z = ctx.GetArgument<float>(3);
bool direction = ctx.GetArgument<bool>(4);
bool stopAtStations = ctx.GetArgument<bool>(5);
float speed = ctx.GetArgument<float>(6);
uint32_t trackId = ctx.GetArgument<uint32_t>(7);
uint32_t trainConfigIndex = ctx.CheckArgument<uint32_t>(8);

//Don't create train if an invalid trackid has been provided.
if (trackId != 0 && trackId != 3)
{
throw std::runtime_error(va("Tried to spawn train on invalid track id: %u", trackId));

ctx.SetResult(0);
return;
}

//Don't create train if an invalid trainConfigIndex has been provided, otherwises the client crashes.
if (trainConfigIndex > (uint32_t)(Is2802() ? 26 : (Is2372() ? 25 : 24)))
{
throw std::runtime_error(va("Tried to spawn train with invalid train config index: %u", trainConfigIndex));

ctx.SetResult(0);
return;
}


auto tree = MakeTrain<sync::CTrainSyncTree>(modelHash, x, y, z, 0.0f, resourceHash, true, direction, stopAtStations, speed, trackId, trainConfigIndex);
auto& sgs = ref->GetComponent<fx::ServerGameState>();
auto entity = sgs->CreateEntityFromTree(sync::NetObjEntityType::Train, tree);

//Edit CTrainGameStateData node to assign engineCarriage to the newly created objectID
SetupNode(tree, [entity](sync::CTrainGameStateDataNode& cdn)
{
cdn.data.engineCarriage = entity->handle;
});

ctx.SetResult(sgs->MakeScriptHandle(entity));
});

fx::ScriptEngine::RegisterNativeHandler("CREATE_TRAIN_CARRIAGE", [=](fx::ScriptContext& ctx)
{
// get the current resource manager
auto resourceManager = fx::ResourceManager::GetCurrent();

// get the owning server instance
auto instance = resourceManager->GetComponent<fx::ServerInstanceBaseRef>()->Get();

// get the server's game state
auto gameState = instance->GetComponent<fx::ServerGameState>();

// parse the client ID
auto id = ctx.GetArgument<uint32_t>(0);

if (!id)
{
ctx.SetResult(0);
return;
}

auto entity = gameState->GetEntity(id);

//Make sure entity exists.
if (!entity)
{
throw std::runtime_error(va("Tried to access invalid entity: %d", id));

ctx.SetResult(0);
return;
}

auto trainState = entity->syncTree->GetTrainState();

// Make sure entity is a train
if (entity->type != fx::sync::NetObjEntityType::Train)
{
throw std::runtime_error(va("Entity is not a train: %d", id));

ctx.SetResult(0);
return;
}

// Make sure entity is the engine carriage
if (!trainState || !trainState->isEngine)
{
throw std::runtime_error(va("Tried to attach train carriage to invalid train entity: %d", id));

ctx.SetResult(0);
return;
}

uint32_t resourceHash = 0;
fx::OMPtr<IScriptRuntime> runtime;

if (FX_SUCCEEDED(fx::GetCurrentScriptRuntime(&runtime)))
{
fx::Resource* resource = reinterpret_cast<fx::Resource*>(runtime->GetParentObject());

if (resource)
{
resourceHash = HashString(resource->GetName().c_str());
}
}

uint32_t modelHash = ctx.GetArgument<uint32_t>(1);
float distanceFromEngine = ctx.GetArgument<float>(2);

float position[3];
entity->syncTree->GetPosition(position);

auto tree = MakeTrain<sync::CTrainSyncTree>(modelHash, position[0], position[1], position[2], 0.0f, resourceHash, false, trainState->direction, trainState->shouldStopAtStations, trainState->cruiseSpeed, trainState->trackId, trainState->trainConfigIndex);
auto& sgs = ref->GetComponent<fx::ServerGameState>();

if (tree)
{
SetupNode(tree, [sgs, entity, distanceFromEngine](sync::CTrainGameStateDataNode& cdn){
cdn.data.engineCarriage = entity->handle;
cdn.data.linkedToForwardId = GetTrainCabooseCarriage(sgs.GetRef(), entity)->handle;
cdn.data.carriageIndex = GetTrainCarriageCount(sgs.GetRef(), entity) + 1;
cdn.data.distanceFromEngine = distanceFromEngine;
cdn.data.isEngine = false;
cdn.data.isCaboose = true;
});
}

auto carriageEntity = sgs->CreateEntityFromTree(fx::sync::NetObjEntityType::Train, tree);
auto caboose = GetTrainCabooseCarriage(sgs.GetRef(), entity);

auto trainSyncTree = std::static_pointer_cast<sync::CTrainSyncTree>(caboose->syncTree);
SetupNode(trainSyncTree, [carriageEntity](sync::CTrainGameStateDataNode& cdn)
{
cdn.data.linkedToBackwardId = carriageEntity->handle;
cdn.data.isCaboose = false;
});

ctx.SetResult(sgs->MakeScriptHandle(carriageEntity));
});

fx::ScriptEngine::RegisterNativeHandler("CREATE_OBJECT_NO_OFFSET", [=](fx::ScriptContext& ctx)
{
uint32_t resourceHash = 0;
Expand Down
Loading

0 comments on commit 96b19df

Please sign in to comment.