载入中...
搜索中...
未找到
WorldInteraction.cpp
浏览该文件的文档.
2
4#include "rpg/GameState.h"
5#include "rpg/Tracker.h"
6#include "rpg/WorldState.h"
7
8#include <cmath>
9#include <optional>
10
11namespace eve::rpg {
12namespace {
13
14eve::Result<int> interactionFailure(eve::DiagnosticCode code, std::string message,
15 std::string path, const WorldLootRequest &request) {
18 {{"mapId", request.mapId}, {"objectId", request.objectId}}, "rpg.world-loot"));
19}
20
21struct TrackerRollback {
22 std::unordered_map<std::string, QuestRuntime> entries;
23 std::vector<std::string> order;
24 std::vector<QuestEvent> pending;
25 std::vector<QuestEvent> polled;
26};
27
28void restoreTracker(Tracker &tracker, TrackerRollback rollback) {
29 tracker.entries = std::move(rollback.entries);
30 tracker.order = std::move(rollback.order);
31 tracker.pending = std::move(rollback.pending);
32 tracker.polled = std::move(rollback.polled);
33}
34
35} // namespace
36
38 inventory::Bag &bag,
40 const bool hasItem = !request.itemId.empty() || request.itemQuantity != 0;
41 const bool hasAttribute = !request.attributeId.empty() || request.attributeAmount != 0.0;
42 const bool hasNotification = !request.notifyTopic.empty() || !request.notifyTarget.empty() ||
43 request.notifyAmount != 0;
44 if (!hasItem && !hasAttribute && !hasNotification)
45 return interactionFailure(eve::DiagnosticCode::InvalidArgument,
46 "world loot must contain at least one effect", "request", request);
47 if (hasItem && (request.itemId.empty() || request.itemQuantity <= 0))
48 return interactionFailure(eve::DiagnosticCode::InvalidArgument,
49 "item loot requires an id and positive quantity", "item", request);
50 if (hasAttribute && (request.attributeId.empty() || !std::isfinite(request.attributeAmount) ||
51 request.attributeAmount <= 0.0))
52 return interactionFailure(eve::DiagnosticCode::InvalidArgument,
53 "attribute loot requires an id and finite positive amount", "attribute", request);
54 if (hasAttribute &&
55 !std::isfinite(gameState.getVariable(request.attributeId) + request.attributeAmount))
56 return interactionFailure(eve::DiagnosticCode::InvalidArgument,
57 "attribute loot would produce a non-finite value", "attribute.amount", request);
58 if (hasNotification && (request.notifyTopic.empty() || request.notifyTarget.empty() ||
59 request.notifyAmount <= 0))
60 return interactionFailure(eve::DiagnosticCode::InvalidArgument,
61 "quest notification requires topic, target, and positive amount",
62 "notification", request);
63 if (!request.requiredQuestId.empty() && tracker.getState(request.requiredQuestId) != "active")
64 return interactionFailure(eve::DiagnosticCode::PreconditionViolation,
65 "required quest must be active before collecting world loot",
66 "requiredQuestId", request);
67
68 auto gameStateBefore = gameState.snapshotJson();
69 if (!gameStateBefore.ok()) return eve::Result<int>::failure(gameStateBefore.status());
70 std::string gameStateSnapshot = std::move(gameStateBefore).takeValue();
71 GameState candidateState;
72 auto candidateRestore = candidateState.restoreSnapshotJson(gameStateSnapshot);
73 if (!candidateRestore.ok()) return eve::Result<int>::failure(candidateRestore.status());
74 WorldState candidateWorld(candidateState);
75 if (candidateWorld.isObjectConsumed(request.mapId, request.objectId))
76 return interactionFailure(eve::DiagnosticCode::Conflict,
77 "world loot object was already consumed", "objectId", request);
78 auto candidateConsume = candidateWorld.consumeObject(request.mapId, request.objectId);
79 if (!candidateConsume.ok()) return eve::Result<int>::failure(candidateConsume.status());
80
81 std::optional<inventory::PreparedInventoryAdd> preparedInventory;
82 if (hasItem) {
84 &bag, {{request.itemId, request.itemQuantity}});
85 if (!prepared.ok()) return eve::Result<int>::failure(prepared.status());
86 preparedInventory.emplace(std::move(prepared).takeValue());
87 }
88
89 TrackerRollback trackerBefore{tracker.entries, tracker.order, tracker.pending, tracker.polled};
90 if (hasAttribute) gameState.addVariable(request.attributeId, request.attributeAmount);
91 if (hasNotification)
92 tracker.notify(request.notifyTopic, request.notifyTarget, request.notifyAmount);
93 WorldState world(gameState);
94 auto consumed = world.consumeObject(request.mapId, request.objectId);
95 if (!consumed.ok() || consumed.code() != eve::StatusCode::Applied) {
96 auto restored = gameState.restoreSnapshotJson(gameStateSnapshot);
97 const bool restoredOk = restored.ok();
98 restoreTracker(tracker, std::move(trackerBefore));
99 if (!restoredOk)
100 return interactionFailure(eve::DiagnosticCode::InvariantViolation,
101 "world loot rollback could not restore GameState", "rollback", request);
102 return interactionFailure(eve::DiagnosticCode::Conflict,
103 "world loot object changed before commit", "objectId", request);
104 }
105
106 if (preparedInventory) {
107 auto committed = inventory::InventorySystem::commitAddBatch(std::move(*preparedInventory));
108 if (!committed.ok()) {
109 auto restored = gameState.restoreSnapshotJson(gameStateSnapshot);
110 const bool restoredOk = restored.ok();
111 restoreTracker(tracker, std::move(trackerBefore));
112 if (!restoredOk)
113 return interactionFailure(eve::DiagnosticCode::InvariantViolation,
114 "world loot rollback could not restore GameState", "rollback", request);
115 return eve::Result<int>::failure(committed.status());
116 }
117 }
118
119 return eve::Result<int>::success(static_cast<int>(hasItem) + static_cast<int>(hasAttribute) +
120 static_cast<int>(hasNotification) + 1,
122}
123
124} // namespace eve::rpg
std::vector< QuestEvent > pending
std::unordered_map< std::string, QuestRuntime > entries
std::vector< QuestEvent > polled
全局游戏状态:开关(bool)、变量(数字)、独立变量(按作用域)。
std::string message
DiagnosticCode code
const GltfImportRequest & request
std::vector< std::int32_t > order
World3D * world
std::string path
Definition PlayHost.cpp:110
目标追踪器运行时实例(玩家日志 / 成就 / 教程可各建一份)。
Atomic settlement of persistent world loot interactions.
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
bool ok() const noexcept
Whether this result represents a non-failure outcome.
Definition Result.h:480
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
格子型物品容器(脚本可直接操作)。
Definition Bag.h:21
static eve::Result< int > commitAddBatch(PreparedInventoryAdd prepared)
Commit one prepared addition and then publish its change events.
static eve::Result< PreparedInventoryAdd > prepareAddBatch(Bag *bag, const std::vector< InventoryItemGrant > &grants)
Validate a multi-item addition against a private candidate Bag.
一份游戏状态(开关/变量/独立变量)。
Definition GameState.h:25
double getVariable(const std::string &name) const
Returns the variable.
Definition GameState.cpp:62
void addVariable(const std::string &name, double delta)
Adds variable.
Definition GameState.cpp:67
eve::Result< void > restoreSnapshotJson(std::string_view json)
Validate and atomically restore a canonical RPG game-state JSON document.
eve::Result< std::string > snapshotJson() const
Serialize this state as the canonical versioned RPG game-state JSON document.
一份任务进度 + 本实例事件队列。
Definition Tracker.h:26
std::unordered_map< std::string, QuestRuntime > entries
运行时条目(QuestSystem 读写;公开以便测试与脚本查询)。
Definition Tracker.h:123
std::vector< std::string > order
Definition Tracker.h:124
std::vector< QuestEvent > pending
Definition Tracker.h:125
void notify(const std::string &topic, const std::string &target, int amount)
Notify.
Definition Tracker.cpp:25
std::string getState(const std::string &id) const
Returns the state.
Definition Tracker.cpp:172
std::vector< QuestEvent > polled
Definition Tracker.h:126
static eve::Result< int > collectLoot(GameState &gameState, Tracker &tracker, inventory::Bag &bag, const WorldLootRequest &request)
Atomically grant loot, notify quest progress, and consume one world object.
Non-owning typed adapter for persistent per-map object lifecycle state.
Definition WorldState.h:19
bool isObjectConsumed(std::string_view mapId, std::string_view objectId) const
Query whether a stable object has been consumed in one map.
eve::Result< void > consumeObject(std::string_view mapId, std::string_view objectId)
Idempotently publish the consumed fact for one stable map object.
RPG 模块的基础实体:把属性 / 状态 / 技能三张表挂到一个 ECS 实体上。
DiagnosticCode
Stable machine-readable diagnostic codes.
Definition Diagnostic.h:47
Complete data needed to settle one persistent loot object.
bool consumed
Definition Graphics.cpp:184