Major refactoring - internal server state machine implemented
This commit is contained in:
1 parent
71c654e5b6
commit
66e93d46f4
48 files changed
+3948
-3696
No files matched your search
@@ -8,6 +8,7 @@
|
||||
#include <Eigen/Geometry>
|
||||
#include "../Character.h"
|
||||
#include <SDL.h>
|
||||
#include <unordered_set>
|
||||
|
||||
|
||||
namespace FRG {
|
||||
@@ -91,6 +92,9 @@ namespace FRG {
|
||||
|
||||
for (const auto& item : j["objects"]) {
|
||||
InteractiveObjectData data;
|
||||
data.id = item.at("id").get<std::string>();
|
||||
if (data.id.empty())
|
||||
throw std::runtime_error("Interactive object id must not be empty in " + jsonPath);
|
||||
data.base = parseGameObjectData(item);
|
||||
data.interactionRadius = item.value("interactionRadius", 2.0f);
|
||||
data.approachRadius = item.value("approachRadius", data.interactionRadius);
|
||||
@@ -293,6 +297,7 @@ namespace FRG {
|
||||
std::vector<InteractiveObject> GameObjectLoader::loadAndCreateInteractiveObjects(
|
||||
const std::string& jsonPath,
|
||||
Renderer& renderer,
|
||||
std::unordered_map<std::string, std::shared_ptr<InteractiveObjectState>>& states,
|
||||
const std::string& zipPath)
|
||||
{
|
||||
std::vector<InteractiveObject> interactiveObjects;
|
||||
@@ -300,12 +305,35 @@ namespace FRG {
|
||||
if (jsonPath.empty())
|
||||
return interactiveObjects;
|
||||
|
||||
for (const auto& data : loadInteractiveFromJson(jsonPath, zipPath)) {
|
||||
const auto definitions = loadInteractiveFromJson(jsonPath, zipPath);
|
||||
std::unordered_set<std::string> configuredIds;
|
||||
for (const auto& data : definitions) {
|
||||
if (!configuredIds.insert(data.id).second)
|
||||
throw std::runtime_error(
|
||||
"GameObjectLoader: duplicate interactive object id '" +
|
||||
data.id + "' in " + jsonPath);
|
||||
}
|
||||
for (const auto& [id, state] : states)
|
||||
if (configuredIds.count(id) == 0)
|
||||
throw std::runtime_error(
|
||||
"GameObjectLoader: saved interactive object id '" + id +
|
||||
"' is not present in " + jsonPath);
|
||||
for (const auto& data : definitions) {
|
||||
SDL_PumpEvents();
|
||||
logger() << "Loading interactive object: " << data.base.name << std::endl;
|
||||
try {
|
||||
InteractiveObject intObj = InteractiveObject::createFromState(
|
||||
data.toInteractiveObjectState(), renderer, zipPath);
|
||||
InteractiveObjectState definition = data.toInteractiveObjectState();
|
||||
auto& state = states[data.id];
|
||||
if (!state) state = std::make_shared<InteractiveObjectState>(definition);
|
||||
else {
|
||||
nlohmann::json runtime;
|
||||
state->save(runtime);
|
||||
*state = std::move(definition);
|
||||
state->load(runtime);
|
||||
}
|
||||
InteractiveObject intObj =
|
||||
InteractiveObject::createFromState(*state, renderer, zipPath);
|
||||
intObj.id = data.id;
|
||||
intObj.loadedObject.name = data.base.name;
|
||||
|
||||
if (!data.activateFunctionName.empty())
|
||||
@@ -338,7 +366,9 @@ namespace FRG {
|
||||
for (const auto& item : j["npcs"]) {
|
||||
NpcData data;
|
||||
|
||||
data.id = item.value("id", "npc_unknown");
|
||||
data.id = item.at("id").get<std::string>();
|
||||
if (data.id.empty())
|
||||
throw std::runtime_error("NPC id must not be empty in " + jsonPath);
|
||||
data.name = item.value("name", "Unknown NPC");
|
||||
data.texturePath = item.value("texturePath", "");
|
||||
if (item.contains("meshTextures") && item["meshTextures"].is_object()) {
|
||||
@@ -376,6 +406,7 @@ namespace FRG {
|
||||
logger() << "Successfully loaded " << npcs.size() << " NPCs from " << jsonPath << std::endl;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "Error loading NPCs from JSON: " << e.what() << std::endl;
|
||||
throw;
|
||||
}
|
||||
|
||||
return npcs;
|
||||
@@ -385,9 +416,10 @@ namespace FRG {
|
||||
CharacterState st;
|
||||
|
||||
// Identity + game state
|
||||
st.npcId = id;
|
||||
st.npcName = name;
|
||||
st.position = Eigen::Vector3f(positionX, positionY, positionZ);
|
||||
st.homePosition = Eigen::Vector3f(positionX, 0.f, positionZ);
|
||||
st.stopInPlace();
|
||||
st.facingAngle = facingAngle;
|
||||
st.walkSpeed = walkSpeed;
|
||||
st.rotationSpeed = rotationSpeed;
|
||||
@@ -396,6 +428,7 @@ namespace FRG {
|
||||
st.initialHp = hp;
|
||||
st.visibleRange = visibleRange;
|
||||
st.canAttack = canAttack;
|
||||
if (canAttack) st.attackTargetId = "player";
|
||||
st.enabled = enabled;
|
||||
st.interactionRadius = interactionRadius;
|
||||
|
||||
@@ -433,29 +466,51 @@ namespace FRG {
|
||||
std::vector<std::unique_ptr<Character>> GameObjectLoader::loadAndCreate_Npcs(
|
||||
const std::string& jsonPath,
|
||||
Renderer& renderer,
|
||||
std::unordered_map<std::string, std::shared_ptr<CharacterState>>& states,
|
||||
IGameServer& server,
|
||||
const std::string& locationId,
|
||||
const std::string& zipPath)
|
||||
{
|
||||
std::vector<std::unique_ptr<Character>> npcs;
|
||||
|
||||
int index = 0;
|
||||
|
||||
for (const auto& npcData : loadNpcsFromJson(jsonPath, zipPath)) {
|
||||
const auto definitions = loadNpcsFromJson(jsonPath, zipPath);
|
||||
std::unordered_set<std::string> seenIds;
|
||||
for (const auto& definition : definitions) {
|
||||
const std::string& id = definition.id;
|
||||
if (id == "player" || !seenIds.insert(id).second)
|
||||
throw std::runtime_error(
|
||||
"GameObjectLoader: duplicate or reserved NPC id '" + id +
|
||||
"' in " + jsonPath);
|
||||
}
|
||||
for (const auto& [id, state] : states)
|
||||
if (seenIds.count(id) == 0)
|
||||
throw std::runtime_error(
|
||||
"GameObjectLoader: saved NPC id '" + id +
|
||||
"' is not present in " + jsonPath);
|
||||
for (const auto& npcData : definitions) {
|
||||
SDL_PumpEvents();
|
||||
logger() << "Loading NPC: " << npcData.name << std::endl;
|
||||
try {
|
||||
auto npc = Character::createFromState(npcData.toCharacterState(), renderer, zipPath.c_str());
|
||||
CharacterState definition = npcData.toCharacterState();
|
||||
auto& state = states[npcData.id];
|
||||
if (!state) state = std::make_shared<CharacterState>(definition);
|
||||
else {
|
||||
nlohmann::json runtime;
|
||||
state->save(runtime);
|
||||
*state = std::move(definition);
|
||||
state->load(runtime);
|
||||
}
|
||||
auto npc = Character::createFromState(
|
||||
*state, server, locationId, npcData.id, renderer, zipPath.c_str());
|
||||
if (npc) {
|
||||
logger() << "Successfully loaded NPC: " << npcData.name << " at ("
|
||||
<< npcData.positionX << ", " << npcData.positionY << ", " << npcData.positionZ << ")" << std::endl;
|
||||
npc->index = index;
|
||||
npcs.push_back(std::move(npc));
|
||||
index = index + 1;
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "GameObjectLoader: Failed to load NPC '" << npcData.name << "': " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
logger() << "Total NPCs loaded: " << npcs.size() << std::endl;
|
||||
return npcs;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user