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 Mar 20, 2024
1 parent bab1c17 commit 8872682
Show file tree
Hide file tree
Showing 11 changed files with 589 additions and 30 deletions.
3 changes: 2 additions & 1 deletion code/components/citizen-server-impl/component.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"vendor:folly",
"vendor:concurrentqueue",
"vendor:xenium",
"vendor:rocksdb"
"vendor:rocksdb",
"vendor:tinyxml2-dll"
],
"provides": []
}
53 changes: 53 additions & 0 deletions code/components/citizen-server-impl/include/parser/Parser.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#pragma once

#include "StdInc.h"
#include <tinyxml2.h>

#include <ClientRegistry.h>
#include <ResourceManager.h>
#include <VFSManager.h>

namespace fx::data {
template<typename T>
class Parser
{
protected:
T m_data;
public:
inline void LoadFile(fwRefContainer<fx::Resource> resource, std::string_view file)
{
const std::string& rootPath = resource->GetPath();

if (rootPath.empty())
{
return;
}

fwRefContainer<vfs::Stream> stream = vfs::OpenRead(rootPath + "/" + std::string{ file });

if (!stream.GetRef())
{
return;
}

auto data = stream->ReadToEnd();
tinyxml2::XMLDocument document;
tinyxml2::XMLError error = document.Parse(reinterpret_cast<const char*>(data.data()), data.size());

if (error == tinyxml2::XML_SUCCESS)
{
ParseFile(document);
}
};

virtual void ParseFile(tinyxml2::XMLDocument&)
{

}

T GetData()
{
return m_data;
};
};
}
108 changes: 108 additions & 0 deletions code/components/citizen-server-impl/include/parser/TrainParser.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#pragma once

#include "Parser.h"

namespace fx::data
{
struct CTrainCarriageInfo
{
const char* m_modelName;

uint32_t m_maxPedsPerCarriage;
bool m_flipModelDir;
bool m_doInteriorLights;
bool m_carriageVertOffset;
uint32_t m_repeatCount;
};

struct CTrainConfig
{
const char* m_name;
float m_populateTrainDist;
bool m_announceStations;
bool m_doorsBeep;
bool m_carriagesHang;
bool m_carriagesSwing;
bool m_carriagesUseEvenTrackSpacing;
bool m_linkTracksWithAdjacentStations;
bool m_noRandomSpawns;
float m_carriageGap;

std::vector<CTrainCarriageInfo> m_carriages;
};

struct CTrainTrack
{
const char* m_trainConfigName;
bool isPingPongTrack;
bool stopsAtStations;
bool MPStopsAtStations;
uint32_t speed;
uint32_t breakingDist;
};
}

namespace fx
{
class CTrainConfigParser : public fwRefCountable, public data::Parser<std::vector<data::CTrainConfig>>
{
void ParseFile(tinyxml2::XMLDocument& document) override
{
tinyxml2::XMLElement* configs = document.FirstChildElement("train_configs");

if (!configs)
{
return;
}

std::vector<data::CTrainConfig> trainConfigs;
tinyxml2::XMLElement* config = configs->FirstChildElement("train_config");

while (config)
{
data::CTrainConfig trainConfig;
trainConfig.m_name = config->Attribute("name");
trainConfig.m_populateTrainDist = config->IntAttribute("populate_train_dist", 0);
trainConfig.m_announceStations = config->BoolAttribute("announce_stations", false);
trainConfig.m_doorsBeep = config->BoolAttribute("doors_beep", false);
trainConfig.m_carriagesHang = config->BoolAttribute("carriages_hang", false);
trainConfig.m_carriagesSwing = config->BoolAttribute("carriages_swing", false);
trainConfig.m_linkTracksWithAdjacentStations = config->BoolAttribute("link_tracks_with_adjacent_stations", true);
trainConfig.m_noRandomSpawns = config->BoolAttribute("no_random_spawn", true);
trainConfig.m_carriageGap = config->FloatAttribute("carriage_gap", 0.1);

tinyxml2::XMLElement* carriage = config->FirstChildElement("carriage");
while (carriage)
{
data::CTrainCarriageInfo carriageInfo{};
carriageInfo.m_modelName = config->Attribute("model_name");
carriageInfo.m_maxPedsPerCarriage = config->IntAttribute("max_peds_per_carriage", 0);
carriageInfo.m_flipModelDir = config->BoolAttribute("flip_model_dir", false);
carriageInfo.m_doInteriorLights = config->BoolAttribute("do_interior_lights", false);
carriageInfo.m_carriageVertOffset = config->FloatAttribute("carriage_vert_offset", 1.0);
carriageInfo.m_repeatCount = config->IntAttribute("repeat_count", 1);

trainConfig.m_carriages.push_back(carriageInfo);

carriage = carriage->NextSiblingElement("carriage");
}

trainConfigs.push_back(trainConfig);
config = config->NextSiblingElement("train_config");
}

m_data = trainConfigs;
}
};

class CTrainTrackParser : public data::Parser<data::CTrainTrack>
{
void ParseFile(tinyxml2::XMLDocument& document)
{

}
};
}

DECLARE_INSTANCE_TYPE(fx::CTrainConfigParser);
DECLARE_INSTANCE_TYPE(fx::CTrainTrackParser);
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 @@ -19,6 +19,7 @@
#include <EASTL/fixed_vector.h>

#include <state/Pool.h>
#include <parser/TrainParser.h>

#include <IteratorView.h>

Expand Down Expand Up @@ -839,7 +840,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 +856,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 Expand Up @@ -6940,8 +6942,10 @@ static InitFunction initFunction([]()
g_oneSyncWorkaround763185 = instance->AddVariable<bool>("onesync_workaround763185", ConVar_None, false);

fwRefContainer<fx::ServerGameState> sgs = new fx::ServerGameState();
fwRefContainer<fx::CTrainConfigParser> trainConfigParser = new fx::CTrainConfigParser();
instance->SetComponent(sgs);
instance->SetComponent<fx::ServerGameStatePublic>(sgs);
instance->SetComponent<fx::CTrainConfigParser>(trainConfigParser);

instance->GetComponent<fx::GameServer>()->OnSyncTick.Connect([=]()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@

#include <ServerInstanceBase.h>
#include <ServerInstanceBaseRef.h>
#include <state/ServerGameState.h>
#include <state/ServerGameState.h>
#include <parser/TrainParser.h>

#include <ResourceManager.h>
#include <ScriptEngine.h>

#include <ScriptSerialization.h>
#include <MakeClientFunction.h>
#include <MakePlayerEntityFunction.h>

namespace fx
{
void DisownEntityScript(const fx::sync::SyncEntityPtr& entity);
Expand Down Expand Up @@ -1094,9 +1095,47 @@ static void Init()

fx::ScriptEngine::RegisterNativeHandler("DELETE_ENTITY", makeEntityFunction([](fx::ScriptContext& context, const fx::sync::SyncEntityPtr& entity)
{
auto resourceManager = fx::ResourceManager::GetCurrent();
auto instance = resourceManager->GetComponent<fx::ServerInstanceBaseRef>()->Get();
auto gameState = instance->GetComponent<fx::ServerGameState>();
auto resourceManager = fx::ResourceManager::GetCurrent();
auto instance = resourceManager->GetComponent<fx::ServerInstanceBaseRef>()->Get();
auto gameState = instance->GetComponent<fx::ServerGameState>();

#ifdef STATE_FIVE
// If the entity is a server-owned train, loop through all the train carriages and delete
if (entity->IsOwnedByServerScript() && entity->type == fx::sync::NetObjEntityType::Train)
{
if (auto trainState = entity->syncTree->GetTrainState())
{
auto deleteCarriages = [gameState](const fx::sync::SyncEntityPtr& engine)
{
for (auto link = GetNextTrain(gameState.GetRef(), engine); link; link = GetNextTrain(gameState.GetRef(), link))
{
if (link->handle != engine->handle)
{
gameState->DeleteEntity(link);
}
}

//Clean up the engine afterwards
gameState->DeleteEntity(engine);
};

if (trainState->isEngine)
{
deleteCarriages(entity);
return 0;
}

if (trainState->engineCarriage && trainState->engineCarriage != entity->handle)
{
if (auto engine = GetTrain(gameState.GetRef(), trainState->engineCarriage))
{
deleteCarriages(engine);
return 0;
}
}
}
}
#endif

gameState->DeleteEntity(entity);

Expand Down Expand Up @@ -1446,6 +1485,25 @@ static void Init()

return train ? train->carriageIndex : -1;
}));

fx::ScriptEngine::RegisterNativeHandler("LOAD_TRAIN_CONFIG_FROM_PATH", [](fx::ScriptContext& context)
{
auto resourceManager = fx::ResourceManager::GetCurrent();

fwRefContainer<fx::Resource> resource = resourceManager->GetResource(context.CheckArgument<const char*>(0));

if (!resource.GetRef())
{
return;
}

std::string path = context.CheckArgument<const char*>(1);

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

instance->GetComponent<fx::CTrainConfigParser>()->LoadFile(resource, path);
});

fx::ScriptEngine::RegisterNativeHandler("GET_PLAYER_FAKE_WANTED_LEVEL", MakePlayerEntityFunction([](fx::ScriptContext& context, const fx::sync::SyncEntityPtr& entity)
{
Expand Down
Loading

0 comments on commit 8872682

Please sign in to comment.