diff --git a/.github/scripts/check-format.sh b/.github/scripts/check-format.sh index 49773159..9eddb378 100644 --- a/.github/scripts/check-format.sh +++ b/.github/scripts/check-format.sh @@ -48,7 +48,7 @@ for file in $CHANGED_FILES; do fi # --diff exits non-zero when differences are found; keep going so we can # print the diff as the failure message. - git clang-format --diff "$MERGE_BASE" -- "$file" >>"$DIFF_FILE" || true + git clang-format --quiet --diff "$MERGE_BASE" -- "$file" >>"$DIFF_FILE" || true done if [ -s "$DIFF_FILE" ]; then diff --git a/cmake/module_manifest.cmake b/cmake/module_manifest.cmake index 95d91da0..5a2ba1e5 100644 --- a/cmake/module_manifest.cmake +++ b/cmake/module_manifest.cmake @@ -131,6 +131,7 @@ eve_declare_module(NAME effects LAYER 0 SCRIPT Effects SLOT effects DEPS definitions GROUP minimal 2d 3d web) eve_declare_module(NAME game_event LAYER 0 SCRIPT GameEvent SLOT game_event + DEPS schema GROUP minimal 2d 3d web) eve_declare_module(NAME settlement LIB EVSettlement LAYER 0 DEPS game_event diff --git a/docs/dev/Eve-Schema子集与绑定契约.md b/docs/dev/Eve-Schema子集与绑定契约.md index a867ef69..55a1f0d1 100644 --- a/docs/dev/Eve-Schema子集与绑定契约.md +++ b/docs/dev/Eve-Schema子集与绑定契约.md @@ -14,10 +14,17 @@ Schema 不是所有序列化格式的第二套解析器。SnapshotEnvelope、Eve decode/restore 边界完成结构、版本、hash 和事务校验;不得为了“使用 Schema” 再维护一份可能漂移的字段真源。 -当前生产接入点是动态 Definition 与 Policy metadata:当写入携带的 -`(type/schemaId, version)` 在 SchemaRegistry 中存在时,registry 在任何状态 -变化前执行校验;失败返回结构化 Diagnostic,并保持原 registry 不变。这里的 -Schema 是内容契约,不是可选的 UI 提示。 +当前生产接入点包括动态 Definition/Policy metadata,以及下面两个持久化 +恢复边界: + +- `definitions:registry` v1:由 Definitions 模块拥有,`DefinitionRegistry::restoreJson` + 在任何 registry 状态变化前校验完整 JSON; +- `game_event:stream` v2:由 GameEvent 模块拥有,事件流恢复在解析和提交前 + 校验当前版本 payload。 + +校验失败必须返回结构化 Diagnostic,并保持原状态不变。这里的 Schema +是实际内容准入契约,不是可选的 UI 提示。`SnapshotEnvelope` 仍由 common 中的 +强类型 codec 校验,不反向依赖 schema 模块。 支持的结构与关键字: @@ -54,22 +61,16 @@ Schema 是内容契约,不是可选的 UI 提示。 - 删除旧 migration 前必须先更新格式文档、fixture 与 release note,并确保受支持窗口内不存在仍依赖该 edge 的版本。 - migration 必须先在 detached owning value 上完成全部转换与目标 schema/codec 校验,再一次性提交;任一步失败不得改变运行时状态。 -## 工具与文档使用的基础格式 +## Schema 所有权 -`schema::registerStandardSchemas()`(Schema 模块构造时也会调用)注册以下 v1 -格式。它们用于生成文档、binding contract 以及外部工具预检;强类型 codec -仍是运行时 envelope 的唯一准入真源。helper 是 schema 模块的责任,不让 -`common` 依赖 snapshot、game_event 或 definitions: +Schema 模块只提供注册、解析、校验、迁移、文档和 binding contract 生成能力, +不内置 snapshot、game_event、definitions 或玩法领域的具体 schema,也不会在 +模块构造时隐式注册领域格式。 -| Schema id | 格式 | -| --- | --- | -| `snapshot:envelope` | `type`、`schema`、`schemaVersion`、`instanceId`、`revision`、`tick`、`contentHash`、`payload` | -| `game_event:envelope` | EventEnvelope 的身份、顺序、因果、schema、tick、flags 与 `payload` | -| `definitions:metadata` | Definition 的 `type`、`id`、`version`、`generation`、`json` | - -领域 payload 仍由 event/snapshot/definition 的拥有模块编码;标准 envelope schema -只约束外层格式和 `payload` 的 JSON-compatible 边界,不把不同领域强行降级为 -同一个业务对象。 +具体 schema 必须由数据格式的权威拥有模块定义和注册,并在真实的导入、反序列化 +或跨进程边界执行校验。只被测试注册、但没有生产数据路径消费的 schema 不得加入; +这可以避免 schema 模块成为领域协议的第二真源,也落实“设计出来的系统必须在恰当 +位置实际使用”的原则。 ## Generated outputs diff --git a/examples/camera-controllers/main.nut b/examples/camera-controllers/main.nut index b7daa847..3eb55573 100644 --- a/examples/camera-controllers/main.nut +++ b/examples/camera-controllers/main.nut @@ -101,7 +101,7 @@ function setupCameraController() { ctrl.addView("close", 0.0, 2.0, 4.0, 0.0, 1.0, 0.0); // Timeline 切换 Rig 并产生可被全局 Event 或 controller 消费的 marker。 - ctrl.setEventSink(event); + ctrl.setEventSink(platform_event); ctrl.addTimelineCut(0.0, "cinematic", 0.0); ctrl.addTimelineEvent(1.0, "camera.beat", "front"); ctrl.addTimelineCut(3.0, "orbit", 0.8); diff --git a/examples/commandery-rts/main.nut b/examples/commandery-rts/main.nut index 6b2a478f..c68f7e8a 100644 --- a/examples/commandery-rts/main.nut +++ b/examples/commandery-rts/main.nut @@ -44,13 +44,15 @@ function makeUnit(id, faction, kind, x, y, commander) { stats.setBase("speed", tank ? 42.0 : 58.0); social.setOwner(id, faction); if (commander != "") social.assign(id, "subordinate", commander); + local queueResult = orders.newQueueOwned(); + if (!queueResult.ok) throw queueResult.status.summary; return { id=id faction=faction kind=kind x=x y=y tx=x ty=y hp=tank ? 180.0 : 85.0 maxHp=tank ? 180.0 : 85.0 damage=tank ? 24.0 : 10.0 speed=tank ? 42.0 : 58.0 range=tank ? 70.0 : 48.0 cooldown=0.0 selected=false alive=true radius=tank ? 17.0 : 10.0 holdFire=false - commander=commander stats=stats queue=orders.newQueue() aimX=1.0 aimY=0.0 + commander=commander stats=stats queue=queueResult.value aimX=1.0 aimY=0.0 }; } @@ -63,6 +65,8 @@ function resetGame() { local authorityStore = authorityResult.ok ? authorityResult.value : null; local mindContext = mindResult.ok ? mindResult.value : null; local statusContainer = statusesResult.ok ? statusesResult.value : null; + local productionResult = production.newWorkQueue(); + if (!productionResult.ok) throw productionResult.status.summary; local generalStats = attributes.newSet("general.arden"); generalStats.setBase("administration", 76.0); generalStats.setBase("command", 84.0); @@ -75,12 +79,12 @@ function resetGame() { crown="faction.crown" frontier="faction.frontier" general="general.arden" baseId="base.north" enemyBase="base.frontier" generalStats=generalStats baseStats=baseStats - authority=authorityStore factory=production.newQueue() + authority=authorityStore factory=productionResult.value sensor=sensingResult.ok ? sensingResult.value : null mind=mindContext - statuses=statusContainer stream=events.newStream() + statuses=statusContainer stream=events.newLog() units=[] projectiles=[] particles=[] selected=null money=520.0 enemyMoney=400.0 incomeTimer=0.0 salaryTimer=0.0 aiTimer=0.0 spawnCursor=0 governor=true rebelled=false - message="北方军区已就绪。左键选兵,右键移动。" time=0.0 + message="北方军区已就绪。左键选兵,右键移动。" time=0.0 productionTick=0 points=[ { id="mine.north" x=300.0 y=210.0 owner="faction.crown" governor="general.arden" capture=0.0 capturing="" contested=false }, { id="mine.center" x=525.0 y=365.0 owner="faction.crown" governor="" capture=0.0 capturing="" contested=false }, @@ -198,7 +202,9 @@ function queueUnit(kind) { function updateProduction(dt) { local speed = game.baseStats.getFinal("production_speed", 1.0); - game.factory.update(dt, speed); + game.productionTick += 1; + local advanceResult = game.factory.advance(game.productionTick, dt * speed); + if (!advanceResult.ok) { game.message = advanceResult.status.summary; return; } while (game.spawnCursor < game.factory.taskCount()) { local task = game.factory.taskAt(game.spawnCursor); if (task.getState() != "completed") break; diff --git a/examples/pcg-biome/main.nut b/examples/pcg-biome/main.nut index ed030ed9..f9077b83 100644 --- a/examples/pcg-biome/main.nut +++ b/examples/pcg-biome/main.nut @@ -17,13 +17,17 @@ function cellKey(request) { } function buildBiomeCell(request) { - local domain = procgen.boxVolume(request.getMinX(), 0.0, request.getMinZ(), - request.getMaxX(), 0.0, request.getMaxZ()); + local domainResult = procgen.boxVolume(request.getMinX(), 0.0, request.getMinZ(), + request.getMaxX(), 0.0, request.getMaxZ()); + if (!domainResult.ok) throw domainResult.status.summary; + local domain = domainResult.value; local spacing = request.getLevel() == 0 ? 56.0 : 18.0; local rules = request.getLevel() == 0 ? coarseBiome : detailBiome; local attributed = rules.generate(domain, spacing, request.getSeed(), 0.72); if (attributed == null) throw rules.getError(); - local graph = procgen.newPointGraph(); + local graphResult = procgen.newPointGraph(); + if (!graphResult.ok) throw graphResult.status.summary; + local graph = graphResult.value; graph.addNode("biome", "input"); graph.setNodePoints("biome", attributed); graph.addNode("prune", "self.prune"); @@ -41,8 +45,9 @@ function pumpRuntime() { request = biomeRuntime.nextGenerate()) { try { local points = buildBiomeCell(request); - if (!procgen.publishCellInstances("biome", request, points, "asset", "unknown")) - throw procgen.lastError(); + local publishResult = procgen.publishCellInstances( + "biome", request, points, "asset", "unknown"); + if (!publishResult.ok) throw publishResult.status.summary; if (!biomeRuntime.completeGeneration(request, points)) throw "stale cell generation request"; biomeCells[cellKey(request)] <- points; @@ -64,7 +69,12 @@ function pumpRuntime() { function resetBiome() { foreach (key, points in biomeCells) procgen.removeInstances("biome/" + key); biomeCells = {}; - biomeRuntime = procgen.newRuntimeGeneration(42017); + local runtimeResult = procgen.newRuntimeGeneration(42017); + if (!runtimeResult.ok) { + biomeStatus = runtimeResult.status.summary; + return; + } + biomeRuntime = runtimeResult.value; biomeRuntime.addLevel(256.0, 430.0, 1.35); biomeRuntime.addLevel(64.0, 150.0, 1.65); biomeRuntime.setDirectionWeight(0.3); @@ -72,21 +82,34 @@ function resetBiome() { biomeRuntime.setMaxGenerating(3); biomeRuntime.setFrameTimeBudget(3.0); - local road = procgen.newPointSet(); + local roadResult = procgen.newPointSet(); + if (!roadResult.ok) { + biomeStatus = roadResult.status.summary; + return; + } + local road = roadResult.value; road.add(-100.0, 0.0, 80.0); road.add(260.0, 0.0, 210.0); road.add(680.0, 0.0, 260.0); road.add(1100.0, 0.0, 500.0); - roadSpatial = procgen.splineData(road, 30.0); + local roadSpatialResult = procgen.splineData(road, 30.0); + if (!roadSpatialResult.ok) { biomeStatus = roadSpatialResult.status.summary; return; } + roadSpatial = roadSpatialResult.value; - local world = procgen.boxVolume(-2048.0, 0.0, -2048.0, 2048.0, 0.0, 2048.0); - coarseBiome = procgen.newBiomeRules(); + local worldResult = procgen.boxVolume(-2048.0, 0.0, -2048.0, 2048.0, 0.0, 2048.0); + if (!worldResult.ok) { biomeStatus = worldResult.status.summary; return; } + local world = worldResult.value; + local coarseResult = procgen.newBiomeRules(); + if (!coarseResult.ok) { biomeStatus = coarseResult.status.summary; return; } + coarseBiome = coarseResult.value; coarseBiome.addLayer("forest", world, 10, 0.72); coarseBiome.addAsset("forest", "oak", 4.0, 0.8, 1.2, true); coarseBiome.addAsset("forest", "pine", 1.0, 0.9, 1.3, true); coarseBiome.addExclusion(roadSpatial); - detailBiome = procgen.newBiomeRules(); + local detailResult = procgen.newBiomeRules(); + if (!detailResult.ok) { biomeStatus = detailResult.status.summary; return; } + detailBiome = detailResult.value; detailBiome.addLayer("undergrowth", world, 10, 0.52); detailBiome.addAsset("undergrowth", "grass", 3.0, 0.7, 1.15, true); detailBiome.addAsset("undergrowth", "rock", 1.0, 0.65, 1.4, true); diff --git a/examples/procgen-script-pipeline/main.nut b/examples/procgen-script-pipeline/main.nut index c1c7ec02..7868b010 100644 --- a/examples/procgen-script-pipeline/main.nut +++ b/examples/procgen-script-pipeline/main.nut @@ -13,8 +13,10 @@ gfx.setBackgroundColor(0.055, 0.075, 0.07, 1.0); function selectDebugStage(name) { debugStage = name; - debugPoints = procgen.getSystemDebugStage("forest", debugStage); - previousDebugPoints = procgen.getPreviousSystemDebugStage("forest", debugStage); + local current = procgen.getSystemDebugStage("forest", debugStage); + local previous = procgen.getPreviousSystemDebugStage("forest", debugStage); + debugPoints = current.ok ? current.value : null; + previousDebugPoints = previous.ok ? previous.value : null; } function rebuildForest() { @@ -66,7 +68,8 @@ function rebuildForest() { local commitResult = procgen.commitSystem(ctx); if (!commitResult.ok) throw commitResult.status.summary; - forestPoints = procgen.getSystemOutput("forest", "trees"); + local outputResult = procgen.getSystemOutput("forest", "trees"); + forestPoints = outputResult.ok ? outputResult.value : null; selectDebugStage(debugStage); pipelineStatus = procgen.getSystemDebugReport("forest"); local diff = procgen.getSystemDebugDiffReport("forest"); @@ -76,7 +79,8 @@ function rebuildForest() { ctx.fail(error.tostring()); // A failed commit closes staging but leaves the last committed forest intact. local rollbackResult = procgen.commitSystem(ctx); - forestPoints = procgen.getSystemOutput("forest", "trees"); + local outputResult = procgen.getSystemOutput("forest", "trees"); + forestPoints = outputResult.ok ? outputResult.value : null; pipelineStatus = "rebuild failed, previous snapshot kept: " + error.tostring(); if (!rollbackResult.ok) pipelineStatus += " (rollback: " + rollbackResult.status.summary + ")"; } diff --git a/scripts/capture_root.nut b/scripts/capture_root.nut index 109180f0..c3b110d8 100644 --- a/scripts/capture_root.nut +++ b/scripts/capture_root.nut @@ -120,12 +120,12 @@ left_to_try <- capture_tries + 1; captured <- false; while (true) { - event.pump(); + platform_event.pump(); local running = true; while (true) { - local name = event.poll(); + local name = platform_event.poll(); if (name == "") break; - local data = event.getLastData(); + local data = platform_event.getLastData(); if ("async_dispatch_event" in getroottable()) async_dispatch_event(name, data); if (name == "quit") { diff --git a/scripts/tests/fixtures_architecture_contracts/invalid_api.h b/scripts/tests/fixtures_architecture_contracts/invalid_api.h index ef7a29a2..1a616cd9 100644 --- a/scripts/tests/fixtures_architecture_contracts/invalid_api.h +++ b/scripts/tests/fixtures_architecture_contracts/invalid_api.h @@ -1,3 +1,3 @@ -Item* borrowItem(); -bool apply(); +Item* borrowItem(); +bool apply(); std::string lastError_; diff --git a/scripts/tests/fixtures_architecture_contracts/valid_api.h b/scripts/tests/fixtures_architecture_contracts/valid_api.h index 269b8cd9..2eabdaea 100644 --- a/scripts/tests/fixtures_architecture_contracts/valid_api.h +++ b/scripts/tests/fixtures_architecture_contracts/valid_api.h @@ -7,32 +7,31 @@ Item* borrowItem(); class Api { public: -/** @brief Test whether the item is ready. */ -bool isReady() const; -bool getBool() const; + /** @brief Test whether the item is ready. */ + bool isReady() const; + bool getBool() const; -// Natural state-query spellings are predicates, not operation outcomes. -bool ok() const; -bool passed() const; -bool paused() const; -bool active() const; -bool ownsResource() const; -bool usingGpu() const; -bool usedBackendFallback() const; -bool critical() const; -bool disposed() const; -bool changed() const; -bool exact() const; -bool asBool() const; -bool initialized() const; -bool activeExecuted() const; -bool transactionStateEquals() const; -bool contextMatches() const; + // Natural state-query spellings are predicates, not operation outcomes. + bool ok() const; + bool passed() const; + bool paused() const; + bool active() const; + bool ownsResource() const; + bool usingGpu() const; + bool usedBackendFallback() const; + bool critical() const; + bool disposed() const; + bool changed() const; + bool exact() const; + bool asBool() const; + bool initialized() const; + bool activeExecuted() const; + bool transactionStateEquals() const; + bool contextMatches() const; private: -bool updateUnchecked(float dt); - -/** @brief Apply an operation and return its checked outcome. */ -[[nodiscard]] Result apply(); + bool updateUnchecked(float dt); + /** @brief Apply an operation and return its checked outcome. */ + [[nodiscard]] Result apply(); }; diff --git a/src/engine/cmdline/Get.cpp b/src/engine/cmdline/Get.cpp index 5a60c356..601d9776 100644 --- a/src/engine/cmdline/Get.cpp +++ b/src/engine/cmdline/Get.cpp @@ -49,8 +49,7 @@ int Cmdline::Get(std::string name) { } auto result = installAndroidSdk(); if (result.ok()) return 0; - cerr << rang::fg::red << "eve get: " << result.status().describe() - << rang::fg::reset << endl; + cerr << rang::fg::red << "eve get: " << result.status().describe() << rang::fg::reset << endl; return 3; } diff --git a/src/engine/cmdline/Run.cpp b/src/engine/cmdline/Run.cpp index 40bef5e5..d1896b23 100644 --- a/src/engine/cmdline/Run.cpp +++ b/src/engine/cmdline/Run.cpp @@ -538,8 +538,7 @@ int Cmdline::Run(std::string path, std::string root, bool debug, int dapPort, in // synthesize a report when none was produced (e.g. a native // std::exception with no script involvement). std::string report = dt.lastReport(); - if (report.empty()) - report = dt.notifyError(what); + if (report.empty()) report = dt.notifyError(what); cerr << report << endl; dt.detach(); } else { diff --git a/src/engine/cmdline/sdk_tools.cpp b/src/engine/cmdline/sdk_tools.cpp index 6a66e51c..f0a49757 100644 --- a/src/engine/cmdline/sdk_tools.cpp +++ b/src/engine/cmdline/sdk_tools.cpp @@ -40,10 +40,9 @@ int processId() { #endif } -[[nodiscard]] eve::Result sdkFailure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - code, std::move(message), std::move(path), {}, "cmdline.sdk")); +[[nodiscard]] eve::Result sdkFailure(eve::DiagnosticCode code, std::string message, std::string path = {}) { + return eve::Result::failure( + eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "cmdline.sdk")); } bool nonEmptyRegularFile(const std::filesystem::path& path, std::error_code& ec) { @@ -106,18 +105,17 @@ bool extractZip(const std::string& zipPath, const std::string& destDir) { size_t pos = cdOffset; const size_t cdEnd = cdOffset + cdSize; - while (cdEnd - pos >= 46 && data[pos] == 'P' && data[pos + 1] == 'K' && - data[pos + 2] == 1 && data[pos + 3] == 2) { - const size_t entryStart = pos; + while (cdEnd - pos >= 46 && data[pos] == 'P' && data[pos + 1] == 'K' && data[pos + 2] == 1 && data[pos + 3] == 2) { + const size_t entryStart = pos; const uint16_t method = le16(pos + 10); - const uint32_t entryCrc = le32(pos + 16); + const uint32_t entryCrc = le32(pos + 16); const uint32_t compSz = le32(pos + 20); const uint32_t uncompSz = le32(pos + 24); const uint16_t nameLen = le16(pos + 28); const uint16_t extraLen = le16(pos + 30); const uint16_t commentLen = le16(pos + 32); const uint32_t localOff = le32(pos + 42); - const size_t entrySize = static_cast(46) + nameLen + extraLen + commentLen; + const size_t entrySize = static_cast(46) + nameLen + extraLen + commentLen; if (entrySize > cdEnd - pos) return false; const std::string name(data.data() + entryStart + 46, nameLen); // Read the central-directory attributes before advancing to the next entry. @@ -139,12 +137,10 @@ bool extractZip(const std::string& zipPath, const std::string& destDir) { if (localOff > data.size() || data.size() - localOff < 30 || data[localOff] != 'P' || data[localOff + 1] != 'K' || data[localOff + 2] != 3 || data[localOff + 3] != 4) return false; - const size_t localHeaderSize = static_cast(30) + le16(localOff + 26) + - le16(localOff + 28); + const size_t localHeaderSize = static_cast(30) + le16(localOff + 26) + le16(localOff + 28); if (localHeaderSize > data.size() - localOff) return false; const size_t lData = localOff + localHeaderSize; - if (compSz > data.size() - lData || - compSz > static_cast(std::numeric_limits::max())) + if (compSz > data.size() - lData || compSz > static_cast(std::numeric_limits::max())) return false; std::vector out; @@ -173,8 +169,8 @@ bool extractZip(const std::string& zipPath, const std::string& destDir) { return false; // 不支持的方法(zip 里极罕见)。 } if (out.size() != uncompSz) return false; - if (static_cast(crc32(0, reinterpret_cast(out.data()), - static_cast(out.size()))) != entryCrc) + if (static_cast( + crc32(0, reinterpret_cast(out.data()), static_cast(out.size()))) != entryCrc) return false; std::filesystem::create_directories(dest.parent_path(), ec); @@ -405,15 +401,12 @@ bool moveOrCopy(const std::filesystem::path& from, const std::filesystem::path& eve::Result installEveSdk(Platform p) { namespace fs = std::filesystem; - if (p == Platform::Unknown) - return sdkFailure(eve::DiagnosticCode::InvalidArgument, - "unknown SDK platform"); + if (p == Platform::Unknown) return sdkFailure(eve::DiagnosticCode::InvalidArgument, "unknown SDK platform"); const std::string plat = platformName(p); const std::string tag = sdkVersionTag(); if (tag.empty() || tag[0] != 'v') { return sdkFailure(eve::DiagnosticCode::InvalidArgument, - "cannot determine the current EVEngine version; set EVE_SDK_TAG", - "EVE_SDK_TAG"); + "cannot determine the current EVEngine version; set EVE_SDK_TAG", "EVE_SDK_TAG"); } const std::string expectedVer = tag.substr(1); const std::string installRoot = eveSdkInstallRoot(); @@ -435,14 +428,12 @@ eve::Result installEveSdk(Platform p) { const auto work = fs::temp_directory_path(ec) / ("eve-get-" + plat + "-" + std::to_string(processId())); if (ec) { - return sdkFailure(eve::DiagnosticCode::Failed, - "no temporary directory available"); + return sdkFailure(eve::DiagnosticCode::Failed, "no temporary directory available"); } fs::remove_all(work, ec); fs::create_directories(work, ec); if (ec) { - return sdkFailure(eve::DiagnosticCode::Failed, - "cannot create SDK download workspace: " + ec.message(), + return sdkFailure(eve::DiagnosticCode::Failed, "cannot create SDK download workspace: " + ec.message(), work.string()); } const std::string zipPath = (work / zipName).string(); @@ -455,19 +446,16 @@ eve::Result installEveSdk(Platform p) { } if (!nonEmptyRegularFile(zipPath, ec)) { fs::remove_all(work, ec); - return sdkFailure(eve::DiagnosticCode::Failed, - "SDK archive download returned an empty or unreadable response", + return sdkFailure(eve::DiagnosticCode::Failed, "SDK archive download returned an empty or unreadable response", zipUrl); } if (runShell("curl -fL --retry 3 -o \"" + sumsPath + "\" \"" + sumsUrl + "\"") != 0) { fs::remove_all(work, ec); - return sdkFailure(eve::DiagnosticCode::Failed, - "SDK checksum file download failed", sumsUrl); + return sdkFailure(eve::DiagnosticCode::Failed, "SDK checksum file download failed", sumsUrl); } if (!nonEmptyRegularFile(sumsPath, ec)) { fs::remove_all(work, ec); - return sdkFailure(eve::DiagnosticCode::Failed, - "SDK checksum download returned an empty or unreadable response", + return sdkFailure(eve::DiagnosticCode::Failed, "SDK checksum download returned an empty or unreadable response", sumsUrl); } { @@ -478,8 +466,8 @@ eve::Result installEveSdk(Platform p) { const std::string actual = fileSha256(zipPath); if (expected.empty() || actual.empty() || lower(expected) != lower(actual)) { fs::remove_all(work, ec); - return sdkFailure(eve::DiagnosticCode::HashMismatch, - "SDK SHA-256 verification failed for " + zipName, zipPath); + return sdkFailure(eve::DiagnosticCode::HashMismatch, "SDK SHA-256 verification failed for " + zipName, + zipPath); } } @@ -487,22 +475,19 @@ eve::Result installEveSdk(Platform p) { fs::create_directories(extractDir, ec); if (ec) { fs::remove_all(work, ec); - return sdkFailure(eve::DiagnosticCode::Failed, - "cannot create SDK extraction directory: " + ec.message(), + return sdkFailure(eve::DiagnosticCode::Failed, "cannot create SDK extraction directory: " + ec.message(), extractDir); } if (!extractZip(zipPath, extractDir)) { fs::remove_all(work, ec); return sdkFailure(eve::DiagnosticCode::ParseError, - "SDK archive is empty, truncated, or not a supported ZIP archive", - zipPath); + "SDK archive is empty, truncated, or not a supported ZIP archive", zipPath); } const auto srcRoot = fs::path(extractDir) / "eve-sdk" / plat; if (!fs::is_directory(srcRoot, ec)) { fs::remove_all(work, ec); return sdkFailure(eve::DiagnosticCode::ParseError, - "unexpected SDK archive layout; expected eve-sdk/" + plat + "/", - zipPath); + "unexpected SDK archive layout; expected eve-sdk/" + plat + "/", zipPath); } { const std::string platMarker = sdkTargetPlatform(srcRoot.string()); @@ -516,8 +501,7 @@ eve::Result installEveSdk(Platform p) { if (verMarker != expectedVer) { fs::remove_all(work, ec); return sdkFailure(eve::DiagnosticCode::InvalidArgument, - "SDK archive contains version '" + verMarker + "', expected '" + - expectedVer + "'", + "SDK archive contains version '" + verMarker + "', expected '" + expectedVer + "'", (srcRoot / "share" / "eve" / "VERSION").string()); } } @@ -525,13 +509,12 @@ eve::Result installEveSdk(Platform p) { fs::create_directories(installRoot, ec); if (ec) { fs::remove_all(work, ec); - return sdkFailure(eve::DiagnosticCode::Failed, - "cannot create SDK install directory: " + ec.message(), installRoot); + return sdkFailure(eve::DiagnosticCode::Failed, "cannot create SDK install directory: " + ec.message(), + installRoot); } if (!moveOrCopy(srcRoot, fs::path(destRoot))) { fs::remove_all(work, ec); - return sdkFailure(eve::DiagnosticCode::Failed, - "failed to install the SDK", destRoot); + return sdkFailure(eve::DiagnosticCode::Failed, "failed to install the SDK", destRoot); } fs::remove_all(work, ec); std::cout << "eve get: EVEngine " << plat << " SDK " << tag << " installed at " @@ -676,31 +659,29 @@ bool jdkHomeExists(const std::string& home) { } // 下载一个压缩包并用系统 tar 解压(Windows 10+ 自带 bsdtar)。 -[[nodiscard]] eve::Result downloadAndExtract(const std::string& url, - const std::string& zipPath, +[[nodiscard]] eve::Result downloadAndExtract(const std::string& url, const std::string& zipPath, const std::string& extractDir) { if (runShell("curl -fL --retry 3 -o \"" + zipPath + "\" \"" + url + "\"") != 0) { return sdkFailure(eve::DiagnosticCode::Failed, "download failed", url); } std::error_code ec; if (!nonEmptyRegularFile(zipPath, ec)) { - return sdkFailure(eve::DiagnosticCode::Failed, - "download returned an empty or unreadable response", url); + return sdkFailure(eve::DiagnosticCode::Failed, "download returned an empty or unreadable response", url); } std::filesystem::remove_all(extractDir, ec); std::filesystem::create_directories(extractDir, ec); if (ec) { - return sdkFailure(eve::DiagnosticCode::Failed, - "cannot create extraction directory: " + ec.message(), extractDir); + return sdkFailure(eve::DiagnosticCode::Failed, "cannot create extraction directory: " + ec.message(), + extractDir); } if (!extractZip(zipPath, extractDir)) { - return sdkFailure(eve::DiagnosticCode::ParseError, - "downloaded archive is empty, truncated, or unsupported", zipPath); + return sdkFailure(eve::DiagnosticCode::ParseError, "downloaded archive is empty, truncated, or unsupported", + zipPath); } std::filesystem::remove(zipPath, ec); if (ec) { - return sdkFailure(eve::DiagnosticCode::Failed, - "cannot remove temporary downloaded archive: " + ec.message(), zipPath); + return sdkFailure(eve::DiagnosticCode::Failed, "cannot remove temporary downloaded archive: " + ec.message(), + zipPath); } return eve::Result::success(); } @@ -723,7 +704,7 @@ bool moveOrCopy(const std::filesystem::path& from, const std::filesystem::path& const std::string url = getEnv("EVE_JDK17_URL", temurinJdk17Url()); std::cout << "eve get: downloading JDK 17 (Temurin)...\n"; const std::string tmp = root + "/.jdk-extract"; - auto downloaded = downloadAndExtract(url, root + "/jdk17.zip", tmp); + auto downloaded = downloadAndExtract(url, root + "/jdk17.zip", tmp); if (!downloaded.ok()) return eve::Result::failure(downloaded.status()); std::error_code ec; std::filesystem::path jdkDir; @@ -734,8 +715,7 @@ bool moveOrCopy(const std::filesystem::path& from, const std::filesystem::path& } } if (ec || jdkDir.empty() || !moveOrCopy(jdkDir, std::filesystem::path(root) / "jdk17")) { - return sdkFailure(eve::DiagnosticCode::ParseError, - "unexpected JDK archive layout", tmp); + return sdkFailure(eve::DiagnosticCode::ParseError, "unexpected JDK archive layout", tmp); } std::filesystem::remove_all(tmp, ec); return eve::Result::success(); @@ -745,14 +725,13 @@ bool moveOrCopy(const std::filesystem::path& from, const std::filesystem::path& const std::string url = getEnv("EVE_GRADLE_URL", gradleUrl()); std::cout << "eve get: downloading Gradle " << kGradleVersion << "...\n"; const std::string tmp = root + "/.gradle-extract"; - auto downloaded = downloadAndExtract(url, root + "/gradle.zip", tmp); + auto downloaded = downloadAndExtract(url, root + "/gradle.zip", tmp); if (!downloaded.ok()) return eve::Result::failure(downloaded.status()); std::error_code ec; const auto gradleDir = std::filesystem::path(root) / ("gradle-" + std::string(kGradleVersion)); if (!moveOrCopy(std::filesystem::path(tmp) / ("gradle-" + std::string(kGradleVersion)), gradleDir)) { - return sdkFailure(eve::DiagnosticCode::ParseError, - "unexpected Gradle archive layout", tmp); + return sdkFailure(eve::DiagnosticCode::ParseError, "unexpected Gradle archive layout", tmp); } std::filesystem::remove_all(tmp, ec); return eve::Result::success(); @@ -786,8 +765,7 @@ eve::Result installAndroidSdk() { std::error_code ec; std::filesystem::create_directories(root, ec); if (ec) { - return sdkFailure(eve::DiagnosticCode::Failed, - "cannot create Android SDK directory: " + ec.message(), root); + return sdkFailure(eve::DiagnosticCode::Failed, "cannot create Android SDK directory: " + ec.message(), root); } const std::string sdkManager = @@ -799,16 +777,15 @@ eve::Result installAndroidSdk() { const std::string url = getEnv("EVE_ANDROID_CMDLINE_TOOLS_URL", androidCmdlineToolsUrl()); const std::string tmp = root + "/.cmdline-tools-extract"; - auto downloaded = downloadAndExtract(url, root + "/cmdline-tools.zip", tmp); + auto downloaded = downloadAndExtract(url, root + "/cmdline-tools.zip", tmp); if (!downloaded.ok()) return eve::Result::failure(downloaded.status()); if (!std::filesystem::is_directory(std::filesystem::path(tmp) / "cmdline-tools", ec)) { - return sdkFailure(eve::DiagnosticCode::ParseError, - "unexpected Android command-line tools archive layout", tmp); + return sdkFailure(eve::DiagnosticCode::ParseError, "unexpected Android command-line tools archive layout", + tmp); } if (!moveOrCopy(std::filesystem::path(tmp) / "cmdline-tools", std::filesystem::path(root) / "cmdline-tools" / "latest")) { - return sdkFailure(eve::DiagnosticCode::Failed, - "failed to install Android command-line tools", + return sdkFailure(eve::DiagnosticCode::Failed, "failed to install Android command-line tools", root + "/cmdline-tools/latest"); } std::filesystem::remove_all(tmp, ec); @@ -833,13 +810,11 @@ eve::Result installAndroidSdk() { { std::ofstream f(lic, std::ios::binary | std::ios::trunc); if (!f) { - return sdkFailure(eve::DiagnosticCode::Failed, - "cannot create Android SDK license input", lic); + return sdkFailure(eve::DiagnosticCode::Failed, "cannot create Android SDK license input", lic); } for (int i = 0; i < 200; ++i) f << "y\n"; if (!f) { - return sdkFailure(eve::DiagnosticCode::Failed, - "cannot write Android SDK license input", lic); + return sdkFailure(eve::DiagnosticCode::Failed, "cannot write Android SDK license input", lic); } } #if defined(_WIN32) @@ -851,16 +826,14 @@ eve::Result installAndroidSdk() { #endif std::cout << "eve get: accepting Android SDK licenses...\n"; if (runShell(sdkCmd + " --licenses < \"" + lic + "\"") != 0) { - return sdkFailure(eve::DiagnosticCode::Failed, - "failed to accept Android SDK licenses", sdkManager); + return sdkFailure(eve::DiagnosticCode::Failed, "failed to accept Android SDK licenses", sdkManager); } std::cout << "eve get: installing SDK packages (platform-tools, android-34, " "build-tools)...\n"; const std::string packages = "\"platform-tools\" \"" + std::string(kAndroidPlatform) + "\" \"" + std::string(kAndroidBuildTools) + "\""; if (runShell(sdkCmd + " " + packages) != 0) { - return sdkFailure(eve::DiagnosticCode::Failed, - "failed to install Android SDK packages", sdkManager); + return sdkFailure(eve::DiagnosticCode::Failed, "failed to install Android SDK packages", sdkManager); } std::filesystem::remove(lic, ec); @@ -878,8 +851,7 @@ eve::Result installAndroidSdk() { std::ofstream f(std::filesystem::path(root) / "eve-android.env", std::ios::binary | std::ios::trunc); if (!f) { - return sdkFailure(eve::DiagnosticCode::Failed, - "cannot write Android SDK environment file", + return sdkFailure(eve::DiagnosticCode::Failed, "cannot write Android SDK environment file", (std::filesystem::path(root) / "eve-android.env").string()); } f << "ANDROID_HOME=" << root << "\n" @@ -888,8 +860,7 @@ eve::Result installAndroidSdk() { << "GRADLE_HOME=" << gradleHome << "\n" << "EVENGINE_SDK=" << eveSdkRoot << "\n"; if (!f) { - return sdkFailure(eve::DiagnosticCode::Failed, - "cannot finish writing Android SDK environment file", + return sdkFailure(eve::DiagnosticCode::Failed, "cannot finish writing Android SDK environment file", (std::filesystem::path(root) / "eve-android.env").string()); } } diff --git a/src/engine/common/ArtifactPublication.cpp b/src/engine/common/ArtifactPublication.cpp index 8218d701..0f4b4025 100644 --- a/src/engine/common/ArtifactPublication.cpp +++ b/src/engine/common/ArtifactPublication.cpp @@ -3,15 +3,15 @@ namespace eve::artifact { eve::Result ProviderContract::snapshotState() const { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Unsupported, - "artifact provider does not expose snapshot state", "artifact.provider.snapshot")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::Unsupported, + "artifact provider does not expose snapshot state", + "artifact.provider.snapshot")); } eve::Result ProviderContract::restoreState(const eve::Value&) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Unsupported, - "artifact provider does not support snapshot restore", "artifact.provider.restore")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::Unsupported, + "artifact provider does not support snapshot restore", + "artifact.provider.restore")); } } // namespace eve::artifact diff --git a/src/engine/common/ArtifactPublication.h b/src/engine/common/ArtifactPublication.h index bfaa5f8a..5962ade3 100644 --- a/src/engine/common/ArtifactPublication.h +++ b/src/engine/common/ArtifactPublication.h @@ -33,13 +33,13 @@ enum class PartKind : std::uint8_t { /** @brief Backend-neutral axis-aligned bounds copied from a generated part. */ struct Bounds { - float minX = 0.f; - float minY = 0.f; - float minZ = 0.f; - float maxX = 0.f; - float maxY = 0.f; - float maxZ = 0.f; - bool valid = false; + float minX = 0.f; + float minY = 0.f; + float minZ = 0.f; + float maxX = 0.f; + float maxY = 0.f; + float maxZ = 0.f; + bool valid = false; }; /** @brief Minimal point representation used at the publication boundary. */ @@ -57,21 +57,21 @@ struct Point { * string views. */ struct PartView { - PersistentId id; - std::string_view role; - PartKind kind = PartKind::Grid; - std::uint64_t schemaVersion = 1; - std::string_view buildKey; - Bounds bounds; - std::span positions; - std::span normals; - std::span uvs; + PersistentId id; + std::string_view role; + PartKind kind = PartKind::Grid; + std::uint64_t schemaVersion = 1; + std::string_view buildKey; + Bounds bounds; + std::span positions; + std::span normals; + std::span uvs; std::span indices; - std::int32_t width = 0; - std::int32_t height = 0; + std::int32_t width = 0; + std::int32_t height = 0; std::span cells; - std::span points; - std::string_view metadataJson; + std::span points; + std::string_view metadataJson; }; /** @@ -81,12 +81,12 @@ struct PartView { * Dense arrays stay typed and are never routed through JSON. */ struct PublicationView { - PersistentId id; - std::uint64_t schemaVersion = 1; - std::string_view buildKey; - Bounds bounds; + PersistentId id; + std::uint64_t schemaVersion = 1; + std::string_view buildKey; + Bounds bounds; std::span parts; - std::string_view metadataJson; + std::string_view metadataJson; }; /** @@ -143,35 +143,35 @@ public: class ISceneArtifactAdapter : public ProviderContract { public: static constexpr const char* capabilityName = "eve.procgen.ISceneArtifactAdapter"; - ~ISceneArtifactAdapter() override = default; + ~ISceneArtifactAdapter() override = default; }; /** @brief Tile/map publication capability, implemented by the map module. */ class IMapArtifactAdapter : public ProviderContract { public: static constexpr const char* capabilityName = "eve.procgen.IMapArtifactAdapter"; - ~IMapArtifactAdapter() override = default; + ~IMapArtifactAdapter() override = default; }; /** @brief Graphics resource publication capability, implemented by graphics. */ class IGraphicsArtifactAdapter : public ProviderContract { public: static constexpr const char* capabilityName = "eve.procgen.IGraphicsArtifactAdapter"; - ~IGraphicsArtifactAdapter() override = default; + ~IGraphicsArtifactAdapter() override = default; }; /** @brief Collider publication capability, implemented by physics. */ class IPhysicsArtifactAdapter : public ProviderContract { public: static constexpr const char* capabilityName = "eve.procgen.IPhysicsArtifactAdapter"; - ~IPhysicsArtifactAdapter() override = default; + ~IPhysicsArtifactAdapter() override = default; }; /** @brief Required provider set for one artifact publication transaction. */ struct PublishOptions { - bool scene = false; + bool scene = false; bool graphics = false; - bool physics = false; + bool physics = false; /** @brief Map is kept last for aggregate-initializer compatibility. */ bool map = false; }; @@ -179,10 +179,10 @@ struct PublishOptions { /** @brief Observable receipt from a successful provider transaction. */ struct PublishReceipt { PersistentId id; - bool scenePublished = false; - bool graphicsPublished = false; - bool physicsPublished = false; - bool mapPublished = false; + bool scenePublished = false; + bool graphicsPublished = false; + bool physicsPublished = false; + bool mapPublished = false; }; } // namespace eve::artifact diff --git a/src/engine/common/Container.cpp b/src/engine/common/Container.cpp index bec233a9..9ec25cfe 100644 --- a/src/engine/common/Container.cpp +++ b/src/engine/common/Container.cpp @@ -9,14 +9,11 @@ namespace eve::container { namespace { -[[nodiscard]] Result failure(DiagnosticCode code, std::string message, - std::string path = {}) { - return Result::failure( - Diagnostic::error(code, std::move(message), std::move(path))); +[[nodiscard]] Result failure(DiagnosticCode code, std::string message, std::string path = {}) { + return Result::failure(Diagnostic::error(code, std::move(message), std::move(path))); } -[[nodiscard]] const MembershipEntry* findEntry(const ContainerSnapshot& snapshot, - const MembershipId& id) { +[[nodiscard]] const MembershipEntry* findEntry(const ContainerSnapshot& snapshot, const MembershipId& id) { const MembershipEntry* found = nullptr; for (const auto& entry : snapshot.entries) { if (entry.membership.object != id) continue; @@ -30,11 +27,8 @@ namespace { std::unordered_set ids; std::unordered_set slots; for (const auto& entry : snapshot.entries) { - if (!entry.membership.object.isValid() || !ids.insert(entry.membership.object).second) - return true; - if (!entry.membership.slot.isValid() || - !slots.insert(entry.membership.slot.value()).second) - return true; + if (!entry.membership.object.isValid() || !ids.insert(entry.membership.object).second) return true; + if (!entry.membership.slot.isValid() || !slots.insert(entry.membership.slot.value()).second) return true; if (entry.object.id != entry.membership.object || entry.object.quantity == 0 || entry.membership.generation.isZero()) return true; @@ -42,21 +36,17 @@ namespace { return false; } -[[nodiscard]] Result validateSnapshot(const ContainerSnapshot& snapshot, - const IContainer& container) { +[[nodiscard]] Result validateSnapshot(const ContainerSnapshot& snapshot, const IContainer& container) { if (!snapshot.id.isValid() || snapshot.id != container.descriptor().id) - return failure(DiagnosticCode::InvariantViolation, - "container snapshot identity does not match adapter"); + return failure(DiagnosticCode::InvariantViolation, "container snapshot identity does not match adapter"); if (hasDuplicateMemberships(snapshot)) - return failure(DiagnosticCode::Conflict, - "container snapshot contains duplicate or invalid membership"); + return failure(DiagnosticCode::Conflict, "container snapshot contains duplicate or invalid membership"); const auto& descriptor = container.descriptor(); if (!descriptor.capacity.isUnlimited() && snapshot.entries.size() > descriptor.capacity.value()) return failure(DiagnosticCode::InvariantViolation, "container snapshot exceeds capacity"); if (!descriptor.capacity.isUnlimited()) { for (const auto& entry : snapshot.entries) { - if (static_cast(entry.membership.slot.value()) >= - descriptor.capacity.value()) + if (static_cast(entry.membership.slot.value()) >= descriptor.capacity.value()) return failure(DiagnosticCode::InvariantViolation, "container snapshot contains a slot outside capacity"); } @@ -64,33 +54,28 @@ namespace { return Result::success(); } -[[nodiscard]] Result removeEntry(const ContainerSnapshot& original, - const ContainerDescriptor& descriptor, - const MembershipId& object) { +[[nodiscard]] Result removeEntry(const ContainerSnapshot& original, + const ContainerDescriptor& descriptor, const MembershipId& object) { ContainerSnapshot candidate = original; - const auto it = std::find_if(candidate.entries.begin(), candidate.entries.end(), - [&](const MembershipEntry& entry) { - return entry.membership.object == object; - }); + const auto it = std::find_if(candidate.entries.begin(), candidate.entries.end(), + [&](const MembershipEntry& entry) { return entry.membership.object == object; }); if (it == candidate.entries.end()) return Result::failure( Diagnostic::error(DiagnosticCode::NotFound, "snapshot membership was not found")); candidate.entries.erase(it); if (descriptor.ordering != Ordering::ExplicitSlots) { - if (candidate.entries.size() > - static_cast(std::numeric_limits::max())) - return Result::failure(Diagnostic::error( - DiagnosticCode::InvariantViolation, "container has too many indexed memberships")); + if (candidate.entries.size() > static_cast(std::numeric_limits::max())) + return Result::failure( + Diagnostic::error(DiagnosticCode::InvariantViolation, "container has too many indexed memberships")); for (std::size_t index = 0; index < candidate.entries.size(); ++index) - candidate.entries[index].membership.slot = - SlotIndex(static_cast(index)); + candidate.entries[index].membership.slot = SlotIndex(static_cast(index)); } return Result::success(std::move(candidate)); } -[[nodiscard]] Result chooseDestinationSlot( - const ContainerSnapshot& snapshot, const ContainerDescriptor& descriptor, - std::optional requested) { +[[nodiscard]] Result chooseDestinationSlot(const ContainerSnapshot& snapshot, + const ContainerDescriptor& descriptor, + std::optional requested) { if (requested) { if (!requested->isValid()) return Result::failure( @@ -98,8 +83,8 @@ namespace { const auto value = static_cast(requested->value()); if (descriptor.ordering == Ordering::ExplicitSlots) { if (!descriptor.capacity.isUnlimited() && value >= descriptor.capacity.value()) - return Result::failure(Diagnostic::error( - DiagnosticCode::Conflict, "destination slot exceeds container capacity")); + return Result::failure( + Diagnostic::error(DiagnosticCode::Conflict, "destination slot exceeds container capacity")); for (const auto& entry : snapshot.entries) if (entry.membership.slot == *requested) return Result::failure( @@ -113,17 +98,14 @@ namespace { } if (descriptor.ordering != Ordering::ExplicitSlots) { - if (snapshot.entries.size() > - static_cast(std::numeric_limits::max())) - return Result::failure(Diagnostic::error( - DiagnosticCode::InvariantViolation, "container has too many indexed memberships")); - return Result::success( - SlotIndex(static_cast(snapshot.entries.size()))); + if (snapshot.entries.size() > static_cast(std::numeric_limits::max())) + return Result::failure( + Diagnostic::error(DiagnosticCode::InvariantViolation, "container has too many indexed memberships")); + return Result::success(SlotIndex(static_cast(snapshot.entries.size()))); } - const std::size_t limit = descriptor.capacity.isUnlimited() - ? snapshot.entries.size() + 1u - : descriptor.capacity.value(); + const std::size_t limit = + descriptor.capacity.isUnlimited() ? snapshot.entries.size() + 1u : descriptor.capacity.value(); std::unordered_set occupied; for (const auto& entry : snapshot.entries) occupied.insert(entry.membership.slot.value()); for (std::size_t index = 0; index < limit; ++index) { @@ -131,19 +113,17 @@ namespace { if (!occupied.contains(static_cast(index))) return Result::success(SlotIndex(static_cast(index))); } - return Result::failure( - Diagnostic::error(DiagnosticCode::Conflict, "container has no available slot")); + return Result::failure(Diagnostic::error(DiagnosticCode::Conflict, "container has no available slot")); } -[[nodiscard]] Result insertEntry( - ContainerSnapshot base, const ContainerDescriptor& descriptor, - const MembershipEntry& sourceEntry, std::optional requested) { +[[nodiscard]] Result insertEntry(ContainerSnapshot base, const ContainerDescriptor& descriptor, + const MembershipEntry& sourceEntry, + std::optional requested) { auto destinationResult = chooseDestinationSlot(base, descriptor, requested); - if (!destinationResult) - return Result::failure(destinationResult.status()); + if (!destinationResult) return Result::failure(destinationResult.status()); const SlotIndex destination = destinationResult.value(); - MembershipEntry moved = sourceEntry; - moved.membership.slot = destination; + MembershipEntry moved = sourceEntry; + moved.membership.slot = destination; if (descriptor.ordering == Ordering::ExplicitSlots) { base.entries.push_back(std::move(moved)); } else { @@ -156,17 +136,14 @@ namespace { } // namespace -Result TransferService::transfer(const TransferRequest& request, - TransferEventSink eventSink) { - if (request.source == nullptr || request.destination == nullptr || - !request.object.isValid()) { - return Result::failure( - Diagnostic::error(DiagnosticCode::InvalidArgument, - "transfer requires two containers and a membership object")); +Result TransferService::transfer(const TransferRequest& request, TransferEventSink eventSink) { + if (request.source == nullptr || request.destination == nullptr || !request.object.isValid()) { + return Result::failure(Diagnostic::error( + DiagnosticCode::InvalidArgument, "transfer requires two containers and a membership object")); } - const bool sameContainer = request.source == request.destination; - auto sourceSnapshotResult = request.source->snapshot(); + const bool sameContainer = request.source == request.destination; + auto sourceSnapshotResult = request.source->snapshot(); if (!sourceSnapshotResult) { return Result::failure(sourceSnapshotResult.status()); } @@ -190,16 +167,13 @@ Result TransferService::transfer(const TransferRequest& request return Result::failure(destinationSnapshotValid.status()); } - if (request.expectedSourceRevision && - *request.expectedSourceRevision != sourceSnapshot.revision) { + if (request.expectedSourceRevision && *request.expectedSourceRevision != sourceSnapshot.revision) { return Result::failure( Diagnostic::error(DiagnosticCode::StaleHandle, "source container revision is stale")); } - if (request.expectedDestinationRevision && - *request.expectedDestinationRevision != destinationSnapshot.revision) { + if (request.expectedDestinationRevision && *request.expectedDestinationRevision != destinationSnapshot.revision) { return Result::failure( - Diagnostic::error(DiagnosticCode::StaleHandle, - "destination container revision is stale")); + Diagnostic::error(DiagnosticCode::StaleHandle, "destination container revision is stale")); } const MembershipEntry* sourceEntry = findEntry(sourceSnapshot, request.object); @@ -207,9 +181,9 @@ Result TransferService::transfer(const TransferRequest& request bool duplicate = false; for (const auto& entry : sourceSnapshot.entries) if (entry.membership.object == request.object) duplicate = true; - return Result::failure(Diagnostic::error( - duplicate ? DiagnosticCode::Conflict : DiagnosticCode::NotFound, - duplicate ? "source contains duplicate membership" : "source membership was not found")); + return Result::failure( + Diagnostic::error(duplicate ? DiagnosticCode::Conflict : DiagnosticCode::NotFound, + duplicate ? "source contains duplicate membership" : "source membership was not found")); } if (request.sourceSlot && *request.sourceSlot != sourceEntry->membership.slot) { return Result::failure( @@ -218,14 +192,12 @@ Result TransferService::transfer(const TransferRequest& request if (!sameContainer && request.source->descriptor().id == request.destination->descriptor().id) { return Result::failure(Diagnostic::error( - DiagnosticCode::Conflict, - "different container adapters must not expose the same container identity")); + DiagnosticCode::Conflict, "different container adapters must not expose the same container identity")); } for (const auto& entry : destinationSnapshot.entries) { if (entry.membership.object == request.object && !sameContainer) return Result::failure( - Diagnostic::error(DiagnosticCode::Conflict, - "destination already contains this membership object")); + Diagnostic::error(DiagnosticCode::Conflict, "destination already contains this membership object")); if (request.destinationSlot && entry.membership.slot == *request.destinationSlot && !(sameContainer && entry.membership.object == request.object)) { return Result::failure( @@ -233,64 +205,62 @@ Result TransferService::transfer(const TransferRequest& request } } - auto accepted = request.destination->validateInsert( - sourceEntry->object, request.destinationSlot, - sameContainer ? std::optional(request.object) : std::nullopt); + auto accepted = + request.destination->validateInsert(sourceEntry->object, request.destinationSlot, + sameContainer ? std::optional(request.object) : std::nullopt); if (!accepted) { return Result::failure(accepted.status()); } - if (sameContainer && request.destinationSlot && - sourceEntry->membership.slot == *request.destinationSlot) { - TransferReceipt receipt{sourceSnapshot.id, destinationSnapshot.id, request.object, - sourceEntry->membership.slot, sourceEntry->membership.slot, - sourceSnapshot.revision, destinationSnapshot.revision}; - return Result::success(std::move(receipt), - Status::success(StatusCode::NoOp)); + if (sameContainer && request.destinationSlot && sourceEntry->membership.slot == *request.destinationSlot) { + TransferReceipt receipt{sourceSnapshot.id, + destinationSnapshot.id, + request.object, + sourceEntry->membership.slot, + sourceEntry->membership.slot, + sourceSnapshot.revision, + destinationSnapshot.revision}; + return Result::success(std::move(receipt), Status::success(StatusCode::NoOp)); } auto sourceRevision = sourceSnapshot.revision.incremented(); if (!sourceRevision) return Result::failure( Diagnostic::error(DiagnosticCode::InvariantViolation, "source revision exhausted")); - auto sourceCandidateResult = removeEntry(sourceSnapshot, request.source->descriptor(), - request.object); - if (!sourceCandidateResult) - return Result::failure(sourceCandidateResult.status()); + auto sourceCandidateResult = removeEntry(sourceSnapshot, request.source->descriptor(), request.object); + if (!sourceCandidateResult) return Result::failure(sourceCandidateResult.status()); ContainerSnapshot sourceCandidate = std::move(sourceCandidateResult).takeValue(); - sourceCandidate.revision = *sourceRevision; + sourceCandidate.revision = *sourceRevision; ContainerSnapshot destinationCandidate; - SlotIndex destinationSlot = SlotIndex::invalid(); + SlotIndex destinationSlot = SlotIndex::invalid(); if (sameContainer) { - auto candidateResult = insertEntry(std::move(sourceCandidate), - request.source->descriptor(), *sourceEntry, + auto candidateResult = insertEntry(std::move(sourceCandidate), request.source->descriptor(), *sourceEntry, request.destinationSlot); if (!candidateResult) { return Result::failure(candidateResult.status()); } - destinationCandidate = std::move(candidateResult).takeValue(); + destinationCandidate = std::move(candidateResult).takeValue(); destinationCandidate.revision = *sourceRevision; - const auto* moved = findEntry(destinationCandidate, request.object); + const auto* moved = findEntry(destinationCandidate, request.object); if (moved == nullptr) - return Result::failure(Diagnostic::error( - DiagnosticCode::InvariantViolation, "same-container candidate lost membership")); + return Result::failure( + Diagnostic::error(DiagnosticCode::InvariantViolation, "same-container candidate lost membership")); destinationSlot = moved->membership.slot; } else { auto destinationRevision = destinationSnapshot.revision.incremented(); if (!destinationRevision) - return Result::failure(Diagnostic::error( - DiagnosticCode::InvariantViolation, "destination revision exhausted")); - auto candidateResult = insertEntry(destinationSnapshot, request.destination->descriptor(), - *sourceEntry, request.destinationSlot); - if (!candidateResult) - return Result::failure(candidateResult.status()); - destinationCandidate = std::move(candidateResult).takeValue(); + return Result::failure( + Diagnostic::error(DiagnosticCode::InvariantViolation, "destination revision exhausted")); + auto candidateResult = + insertEntry(destinationSnapshot, request.destination->descriptor(), *sourceEntry, request.destinationSlot); + if (!candidateResult) return Result::failure(candidateResult.status()); + destinationCandidate = std::move(candidateResult).takeValue(); destinationCandidate.revision = *destinationRevision; - const auto* moved = findEntry(destinationCandidate, request.object); + const auto* moved = findEntry(destinationCandidate, request.object); if (moved == nullptr) - return Result::failure(Diagnostic::error( - DiagnosticCode::InvariantViolation, "destination candidate lost membership")); + return Result::failure( + Diagnostic::error(DiagnosticCode::InvariantViolation, "destination candidate lost membership")); destinationSlot = moved->membership.slot; } @@ -298,16 +268,14 @@ Result TransferService::transfer(const TransferRequest& request // the final candidate. The adapter must validate and commit that final // layout, not the moved-from removal-only intermediate. const auto& sourceCommitCandidate = sameContainer ? destinationCandidate : sourceCandidate; - auto sourcePreparedResult = request.source->prepare(sourceSnapshot, sourceCommitCandidate); - if (!sourcePreparedResult) - return Result::failure(sourcePreparedResult.status()); + auto sourcePreparedResult = request.source->prepare(sourceSnapshot, sourceCommitCandidate); + if (!sourcePreparedResult) return Result::failure(sourcePreparedResult.status()); auto sourcePrepared = std::move(sourcePreparedResult).takeValue(); if (sameContainer) { sourcePrepared->commit(); } else { - auto destinationPreparedResult = - request.destination->prepare(destinationSnapshot, destinationCandidate); + auto destinationPreparedResult = request.destination->prepare(destinationSnapshot, destinationCandidate); if (!destinationPreparedResult) { sourcePrepared->rollback(); return Result::failure(destinationPreparedResult.status()); @@ -324,18 +292,16 @@ Result TransferService::transfer(const TransferRequest& request destinationSlot, *sourceRevision, sameContainer ? *sourceRevision : destinationCandidate.revision}; - Status resultStatus = Status::success(StatusCode::Applied); + Status resultStatus = Status::success(StatusCode::Applied); if (eventSink) { try { - eventSink(TransferEvent{receipt.source, receipt.destination, receipt.object, - receipt.sourceSlot, receipt.destinationSlot, - receipt.sourceRevision, receipt.destinationRevision}); + eventSink(TransferEvent{receipt.source, receipt.destination, receipt.object, receipt.sourceSlot, + receipt.destinationSlot, receipt.sourceRevision, receipt.destinationRevision}); } catch (const std::exception&) { try { std::vector diagnostics; - diagnostics.emplace_back(Diagnostic::warning( - DiagnosticCode::CallbackFailure, - "transfer committed but its event callback threw")); + diagnostics.emplace_back(Diagnostic::warning(DiagnosticCode::CallbackFailure, + "transfer committed but its event callback threw")); resultStatus = Status(StatusCode::Applied, std::move(diagnostics)); } catch (...) { resultStatus = Status::success(StatusCode::Applied); @@ -343,9 +309,8 @@ Result TransferService::transfer(const TransferRequest& request } catch (...) { try { std::vector diagnostics; - diagnostics.emplace_back(Diagnostic::warning( - DiagnosticCode::CallbackFailure, - "transfer committed but its event callback threw")); + diagnostics.emplace_back(Diagnostic::warning(DiagnosticCode::CallbackFailure, + "transfer committed but its event callback threw")); resultStatus = Status(StatusCode::Applied, std::move(diagnostics)); } catch (...) { resultStatus = Status::success(StatusCode::Applied); diff --git a/src/engine/common/Container.h b/src/engine/common/Container.h index d4292076..46bd455a 100644 --- a/src/engine/common/Container.h +++ b/src/engine/common/Container.h @@ -11,15 +11,15 @@ * candidate state before exposing a staged, non-throwing commit. */ +#include "common/Generation.h" #include "common/Identity.h" #include "common/Result.h" -#include "common/Generation.h" #include "common/Revision.h" -#include -#include -#include #include +#include +#include +#include #include #include #include @@ -53,7 +53,7 @@ public: [[nodiscard]] bool isValid() const noexcept { return !value_.empty(); } /** @brief Return the owned stable spelling. */ [[nodiscard]] const std::string& value() const noexcept { return value_; } - friend bool operator==(const ContainerId&, const ContainerId&) noexcept = default; + friend bool operator==(const ContainerId&, const ContainerId&) noexcept = default; private: std::string value_; @@ -74,7 +74,7 @@ public: [[nodiscard]] bool isValid() const noexcept { return !value_.empty(); } /** @brief Return the owned stable spelling. */ [[nodiscard]] const std::string& value() const noexcept { return value_; } - friend bool operator==(const MembershipId&, const MembershipId&) noexcept = default; + friend bool operator==(const MembershipId&, const MembershipId&) noexcept = default; private: std::string value_; @@ -84,19 +84,16 @@ private: class Capacity { public: /** @brief Construct a fixed capacity. */ - [[nodiscard]] static constexpr Capacity fixed(std::size_t value) noexcept { - return Capacity(value, false); - } + [[nodiscard]] static constexpr Capacity fixed(std::size_t value) noexcept { return Capacity(value, false); } /** @brief Construct an unbounded capacity. */ [[nodiscard]] static constexpr Capacity unlimited() noexcept { return Capacity(0, true); } - [[nodiscard]] constexpr bool isUnlimited() const noexcept { return unlimited_; } - [[nodiscard]] constexpr std::size_t value() const noexcept { return value_; } + [[nodiscard]] constexpr bool isUnlimited() const noexcept { return unlimited_; } + [[nodiscard]] constexpr std::size_t value() const noexcept { return value_; } private: - constexpr Capacity(std::size_t value, bool unlimited) noexcept - : value_(value), unlimited_(unlimited) {} - std::size_t value_ = 0; - bool unlimited_ = false; + constexpr Capacity(std::size_t value, bool unlimited) noexcept : value_(value), unlimited_(unlimited) {} + std::size_t value_ = 0; + bool unlimited_ = false; }; /** @brief Stable order semantics owned by a container adapter. */ @@ -114,10 +111,10 @@ public: constexpr SlotIndex() noexcept = default; explicit constexpr SlotIndex(std::int32_t value) noexcept : value_(value) {} [[nodiscard]] static constexpr SlotIndex invalid() noexcept { return SlotIndex(-1); } - [[nodiscard]] constexpr bool isValid() const noexcept { return value_ >= 0; } - [[nodiscard]] constexpr std::int32_t value() const noexcept { return value_; } - friend constexpr bool operator==(const SlotIndex&, const SlotIndex&) noexcept = default; - friend constexpr auto operator<=>(const SlotIndex&, const SlotIndex&) noexcept = default; + [[nodiscard]] constexpr bool isValid() const noexcept { return value_ >= 0; } + [[nodiscard]] constexpr std::int32_t value() const noexcept { return value_; } + friend constexpr bool operator==(const SlotIndex&, const SlotIndex&) noexcept = default; + friend constexpr auto operator<=>(const SlotIndex&, const SlotIndex&) noexcept = default; private: std::int32_t value_ = -1; @@ -149,39 +146,39 @@ struct ContainerObjectPayload { /** @brief Generic object facts used by filters and transfer diagnostics. */ struct ContainerObject { - MembershipId id; - std::string type; - std::vector tags; - std::uint32_t quantity = 1; + MembershipId id; + std::string type; + std::vector tags; + std::uint32_t quantity = 1; std::shared_ptr payload; }; /** @brief One object-to-slot relationship, including the observed generation. */ struct Membership { MembershipId object; - SlotIndex slot; - Generation generation = Generation(1); + SlotIndex slot; + Generation generation = Generation(1); }; /** @brief Complete adapter-owned state used to build a candidate replacement. */ struct MembershipEntry { - Membership membership; + Membership membership; ContainerObject object; }; /** @brief Lossless snapshot of one container at one revision. */ struct ContainerSnapshot { - ContainerId id; - Revision revision = Revision(0); + ContainerId id; + Revision revision = Revision(0); std::vector entries; }; /** @brief Static metadata and acceptance policy of a container. */ struct ContainerDescriptor { ContainerId id; - Capacity capacity = Capacity::unlimited(); - Ordering ordering = Ordering::Unordered; - Filter filter; + Capacity capacity = Capacity::unlimited(); + Ordering ordering = Ordering::Unordered; + Filter filter; }; /** @brief Lifecycle outcome represented by every container-domain event. */ @@ -203,10 +200,10 @@ enum class ContainerEventKind : std::uint8_t { */ [[nodiscard]] inline const char* containerEventTypeName(ContainerEventKind kind) noexcept { switch (kind) { - case ContainerEventKind::Enter: return "container.enter"; - case ContainerEventKind::Exit: return "container.exit"; - case ContainerEventKind::Accepted: return "container.accepted"; - case ContainerEventKind::Rejected: return "container.rejected"; + case ContainerEventKind::Enter: return "container.enter"; + case ContainerEventKind::Exit: return "container.exit"; + case ContainerEventKind::Accepted: return "container.accepted"; + case ContainerEventKind::Rejected: return "container.rejected"; } return "container.rejected"; } @@ -237,9 +234,8 @@ using GameEventSink = std::function; * @remarks This is identity derivation, not gameplay randomness. The serial is * owned by the adapter and is not persisted as membership state. */ -[[nodiscard]] inline EventId deterministicContainerEventId( - const ContainerId& container, const MembershipId& object, ContainerEventKind kind, - std::uint64_t serial) noexcept { +[[nodiscard]] inline EventId deterministicContainerEventId(const ContainerId& container, const MembershipId& object, + ContainerEventKind kind, std::uint64_t serial) noexcept { auto mix = [](std::uint64_t value, std::string_view text) noexcept { for (const unsigned char byte : text) { value ^= byte; @@ -247,19 +243,18 @@ using GameEventSink = std::function; } return value; }; - std::uint64_t first = mix(14695981039346656037ull, container.value()); - first = mix(first, object.value()); - first = mix(first, std::string_view(containerEventTypeName(kind))); + std::uint64_t first = mix(14695981039346656037ull, container.value()); + first = mix(first, object.value()); + first = mix(first, std::string_view(containerEventTypeName(kind))); std::uint64_t second = mix(1099511628211ull ^ serial, object.value()); - second = mix(second, container.value()); + second = mix(second, container.value()); second ^= serial + 0x9e3779b97f4a7c15ull; EventId::Bytes bytes{}; for (std::size_t index = 0; index < sizeof(first); ++index) bytes[index] = static_cast((first >> (index * 8u)) & 0xffu); for (std::size_t index = 0; index < sizeof(second); ++index) - bytes[sizeof(first) + index] = - static_cast((second >> (index * 8u)) & 0xffu); + bytes[sizeof(first) + index] = static_cast((second >> (index * 8u)) & 0xffu); // Mark the derived value as a conventional UUID variant/version while // retaining deterministic bytes. bytes[6] = static_cast((bytes[6] & 0x0fu) | 0x50u); @@ -309,19 +304,19 @@ public: * adapter is borrowed and must outlive the returned state; the * caller must externally serialize access to the adapter. */ - [[nodiscard]] virtual Result> prepare( - const ContainerSnapshot& expected, const ContainerSnapshot& candidate) = 0; + [[nodiscard]] virtual Result> prepare(const ContainerSnapshot& expected, + const ContainerSnapshot& candidate) = 0; }; /** @brief Strong event payload emitted only after a transfer is fully committed. */ struct TransferEvent { - ContainerId source; - ContainerId destination; + ContainerId source; + ContainerId destination; MembershipId object; - SlotIndex sourceSlot; - SlotIndex destinationSlot; - Revision sourceRevision; - Revision destinationRevision; + SlotIndex sourceSlot; + SlotIndex destinationSlot; + Revision sourceRevision; + Revision destinationRevision; }; /** @@ -335,24 +330,24 @@ using TransferEventSink = std::function; /** @brief Requested optimistic-concurrency transfer. All pointers are borrowed. */ struct TransferRequest { - IContainer* source = nullptr; - IContainer* destination = nullptr; - MembershipId object; + IContainer* source = nullptr; + IContainer* destination = nullptr; + MembershipId object; std::optional sourceSlot; std::optional destinationSlot; - std::optional expectedSourceRevision; - std::optional expectedDestinationRevision; + std::optional expectedSourceRevision; + std::optional expectedDestinationRevision; }; /** @brief Observable receipt for one committed or explicit no-op transfer. */ struct TransferReceipt { - ContainerId source; - ContainerId destination; + ContainerId source; + ContainerId destination; MembershipId object; - SlotIndex sourceSlot; - SlotIndex destinationSlot; - Revision sourceRevision; - Revision destinationRevision; + SlotIndex sourceSlot; + SlotIndex destinationSlot; + Revision sourceRevision; + Revision destinationRevision; }; /** @@ -369,8 +364,8 @@ public: * supported as one participant. A callback warning does not make * an already committed transfer appear to have failed. */ - [[nodiscard]] static Result transfer( - const TransferRequest& request, TransferEventSink eventSink = {}); + [[nodiscard]] static Result transfer(const TransferRequest& request, + TransferEventSink eventSink = {}); }; // ---- Strong coordinate-space zone contract -------------------------------- @@ -398,28 +393,28 @@ struct Coordinate { template struct Rectangle { Coordinate origin; - float width = 0.f; - float height = 0.f; + float width = 0.f; + float height = 0.f; }; template struct Circle { Coordinate center; - float radius = 0.f; + float radius = 0.f; }; /** @brief Axis-aligned 3D box for World3DSpace zones. */ struct Box3D { Coordinate origin; - float width = 0.f; - float height = 0.f; - float depth = 0.f; + float width = 0.f; + float height = 0.f; + float depth = 0.f; }; /** @brief Spherical 3D shape for World3DSpace zones. */ struct Sphere3D { Coordinate center; - float radius = 0.f; + float radius = 0.f; }; template @@ -453,19 +448,18 @@ public: * @param accepted Side-effect-free condition evaluated before entry. * @return A valid zone or a structured validation failure. */ - [[nodiscard]] static Result create( - ContainerId id, ZoneShape shape, - Capacity capacity = Capacity::unlimited(), AcceptedCondition accepted = {}) { + [[nodiscard]] static Result create(ContainerId id, ZoneShape shape, + Capacity capacity = Capacity::unlimited(), + AcceptedCondition accepted = {}) { if (!id.isValid()) { - return Result::failure(Diagnostic::error( - DiagnosticCode::InvalidArgument, "zone id must not be empty")); + return Result::failure( + Diagnostic::error(DiagnosticCode::InvalidArgument, "zone id must not be empty")); } if (!validShape(shape)) { - return Result::failure(Diagnostic::error( - DiagnosticCode::InvalidArgument, "zone shape must have positive dimensions")); + return Result::failure( + Diagnostic::error(DiagnosticCode::InvalidArgument, "zone shape must have positive dimensions")); } - return Result::success( - Zone(std::move(id), std::move(shape), capacity, std::move(accepted))); + return Result::success(Zone(std::move(id), std::move(shape), capacity, std::move(accepted))); } /** @@ -474,11 +468,9 @@ public: * overload. The compatibility identity is local to this value and * must not be used as a registry key. */ - [[nodiscard]] static Result create( - ZoneShape shape, Capacity capacity = Capacity::unlimited(), - AcceptedCondition accepted = {}) { - return create(ContainerId("zone:anonymous"), std::move(shape), capacity, - std::move(accepted)); + [[nodiscard]] static Result create(ZoneShape shape, Capacity capacity = Capacity::unlimited(), + AcceptedCondition accepted = {}) { + return create(ContainerId("zone:anonymous"), std::move(shape), capacity, std::move(accepted)); } /** @brief Return whether this factory-created zone contains valid geometry. */ @@ -497,10 +489,8 @@ public: using Shape = std::decay_t; if constexpr (std::is_same_v) { if constexpr (std::is_same_v) { - return point.x >= shape.origin.x && point.y >= shape.origin.y && - point.z >= shape.origin.z && - point.x <= shape.origin.x + shape.width && - point.y <= shape.origin.y + shape.height && + return point.x >= shape.origin.x && point.y >= shape.origin.y && point.z >= shape.origin.z && + point.x <= shape.origin.x + shape.width && point.y <= shape.origin.y + shape.height && point.z <= shape.origin.z + shape.depth; } else { const float dx = point.x - shape.center.x; @@ -510,8 +500,7 @@ public: } } else if constexpr (std::is_same_v>) { return point.x >= shape.origin.x && point.y >= shape.origin.y && - point.x <= shape.origin.x + shape.width && - point.y <= shape.origin.y + shape.height; + point.x <= shape.origin.x + shape.width && point.y <= shape.origin.y + shape.height; } else { const float dx = point.x - shape.center.x; const float dy = point.y - shape.center.y; @@ -522,13 +511,9 @@ public: } /** @brief Test the zone's accepted-condition without mutating membership. */ - [[nodiscard]] Result accepts(const ContainerObject& object) const { - return accepted_.evaluate(object); - } + [[nodiscard]] Result accepts(const ContainerObject& object) const { return accepted_.evaluate(object); } /** @brief Return the side-effect-free accepted condition. */ - [[nodiscard]] const AcceptedCondition& acceptedCondition() const noexcept { - return accepted_; - } + [[nodiscard]] const AcceptedCondition& acceptedCondition() const noexcept { return accepted_; } /** @brief Return the maximum number of memberships accepted by this zone. */ [[nodiscard]] Capacity capacity() const noexcept { return capacity_; } /** @brief Return the immutable shape in this zone's coordinate space. */ @@ -536,8 +521,11 @@ public: private: Zone(ContainerId id, ZoneShape shape, Capacity capacity, AcceptedCondition accepted) - : id_(std::move(id)), shape_(std::move(shape)), capacity_(capacity), - accepted_(std::move(accepted)), valid_(true) {} + : id_(std::move(id)), + shape_(std::move(shape)), + capacity_(capacity), + accepted_(std::move(accepted)), + valid_(true) {} [[nodiscard]] static bool validShape(const ZoneShape& shape) noexcept { return std::visit( @@ -545,17 +533,15 @@ private: using Shape = std::decay_t; if constexpr (std::is_same_v>) { return std::isfinite(value.origin.x) && std::isfinite(value.origin.y) && - std::isfinite(value.width) && std::isfinite(value.height) && - value.width > 0.f && value.height > 0.f; + std::isfinite(value.width) && std::isfinite(value.height) && value.width > 0.f && + value.height > 0.f; } else if constexpr (std::is_same_v) { return std::isfinite(value.origin.x) && std::isfinite(value.origin.y) && - std::isfinite(value.origin.z) && std::isfinite(value.width) && - std::isfinite(value.height) && std::isfinite(value.depth) && - value.width > 0.f && value.height > 0.f && value.depth > 0.f; + std::isfinite(value.origin.z) && std::isfinite(value.width) && std::isfinite(value.height) && + std::isfinite(value.depth) && value.width > 0.f && value.height > 0.f && value.depth > 0.f; } else if constexpr (std::is_same_v) { return std::isfinite(value.center.x) && std::isfinite(value.center.y) && - std::isfinite(value.center.z) && std::isfinite(value.radius) && - value.radius > 0.f; + std::isfinite(value.center.z) && std::isfinite(value.radius) && value.radius > 0.f; } else { return std::isfinite(value.center.x) && std::isfinite(value.center.y) && std::isfinite(value.radius) && value.radius > 0.f; @@ -564,11 +550,11 @@ private: shape); } - ContainerId id_; - ZoneShape shape_; - Capacity capacity_; + ContainerId id_; + ZoneShape shape_; + Capacity capacity_; AcceptedCondition accepted_; - bool valid_ = false; + bool valid_ = false; }; } // namespace eve::container @@ -576,9 +562,7 @@ private: namespace std { template <> struct hash { - size_t operator()(const eve::container::ContainerId& value) const noexcept { - return hash{}(value.value()); - } + size_t operator()(const eve::container::ContainerId& value) const noexcept { return hash{}(value.value()); } }; template <> struct hash { diff --git a/src/engine/common/Diagnostic.h b/src/engine/common/Diagnostic.h index 0791c888..3aed7ffc 100644 --- a/src/engine/common/Diagnostic.h +++ b/src/engine/common/Diagnostic.h @@ -27,18 +27,16 @@ enum class Severity : uint8_t { /** @brief Returns the stable lowercase severity spelling. */ [[nodiscard]] constexpr std::string_view severityName(Severity severity) noexcept { switch (severity) { - case Severity::Info: return "info"; - case Severity::Warning: return "warning"; - case Severity::Error: return "error"; - case Severity::Fatal: return "fatal"; + case Severity::Info: return "info"; + case Severity::Warning: return "warning"; + case Severity::Error: return "error"; + case Severity::Fatal: return "fatal"; } return "unknown"; } /** @brief Writes a severity for diagnostics and test output. */ -inline std::ostream& operator<<(std::ostream& stream, Severity severity) { - return stream << severityName(severity); -} +inline std::ostream& operator<<(std::ostream& stream, Severity severity) { return stream << severityName(severity); } /** * @brief Stable machine-readable diagnostic codes. @@ -53,16 +51,16 @@ enum class DiagnosticCode : uint32_t { NotFound = 3, AlreadyExists = 4, Conflict = 5, - Unsupported = 6, - Cancelled = 7, - StaleHandle = 8, - ParseError = 9, - SerializationError = 10, - InvariantViolation = 11, - Failed = 12, - HashMismatch = 13, - UnknownVersion = 14, - CallbackFailure = 15, + Unsupported = 6, + Cancelled = 7, + StaleHandle = 8, + ParseError = 9, + SerializationError = 10, + InvariantViolation = 11, + Failed = 12, + HashMismatch = 13, + UnknownVersion = 14, + CallbackFailure = 15, /** @brief Dialogue runner is not positioned at a choice suspension point. */ DialogueNotWaitingForChoice = 16, /** @brief Dialogue runner is not positioned at an asynchronous command. */ @@ -106,23 +104,27 @@ public: * @param source Optional producer/boundary name, such as a Squirrel * binding or a serialized document importer. */ - Diagnostic(DiagnosticCode code, Severity severity, std::string message, - std::string path = {}, DiagnosticDetails details = {}, std::string source = {}) - : code_(code), severity_(severity), message_(std::move(message)), path_(std::move(path)), - details_(std::move(details)), source_(std::move(source)) {} + Diagnostic(DiagnosticCode code, Severity severity, std::string message, std::string path = {}, + DiagnosticDetails details = {}, std::string source = {}) + : code_(code), + severity_(severity), + message_(std::move(message)), + path_(std::move(path)), + details_(std::move(details)), + source_(std::move(source)) {} /** @brief Construct an error diagnostic with the standard error severity. */ static Diagnostic error(DiagnosticCode code, std::string message, std::string path = {}, DiagnosticDetails details = {}, std::string source = {}) { - return Diagnostic(code, Severity::Error, std::move(message), std::move(path), - std::move(details), std::move(source)); + return Diagnostic(code, Severity::Error, std::move(message), std::move(path), std::move(details), + std::move(source)); } /** @brief Construct a warning diagnostic. */ static Diagnostic warning(DiagnosticCode code, std::string message, std::string path = {}, DiagnosticDetails details = {}, std::string source = {}) { - return Diagnostic(code, Severity::Warning, std::move(message), std::move(path), - std::move(details), std::move(source)); + return Diagnostic(code, Severity::Warning, std::move(message), std::move(path), std::move(details), + std::move(source)); } /** @brief Stable machine-readable code. */ @@ -139,22 +141,18 @@ public: const std::string& source() const noexcept { return source_; } /** @brief Add structured context without changing the stable code. */ - void addDetail(std::string key, std::string value) { - details_.emplace_back(std::move(key), std::move(value)); - } + void addDetail(std::string key, std::string value) { details_.emplace_back(std::move(key), std::move(value)); } /** @brief Whether the diagnostic represents an error or fatal condition. */ - bool isError() const noexcept { - return severity_ == Severity::Error || severity_ == Severity::Fatal; - } + bool isError() const noexcept { return severity_ == Severity::Error || severity_ == Severity::Fatal; } private: - DiagnosticCode code_ = DiagnosticCode::None; - Severity severity_ = Severity::Info; - std::string message_; - std::string path_; + DiagnosticCode code_ = DiagnosticCode::None; + Severity severity_ = Severity::Info; + std::string message_; + std::string path_; DiagnosticDetails details_; - std::string source_; + std::string source_; }; /** @@ -165,48 +163,27 @@ private: */ [[nodiscard]] constexpr std::string_view diagnosticCodeName(DiagnosticCode code) noexcept { switch (code) { - case DiagnosticCode::None: - return "none"; - case DiagnosticCode::InvalidArgument: - return "invalid_argument"; - case DiagnosticCode::PreconditionViolation: - return "precondition_violation"; - case DiagnosticCode::NotFound: - return "not_found"; - case DiagnosticCode::AlreadyExists: - return "already_exists"; - case DiagnosticCode::Conflict: - return "conflict"; - case DiagnosticCode::Unsupported: - return "unsupported"; - case DiagnosticCode::Cancelled: - return "cancelled"; - case DiagnosticCode::StaleHandle: - return "stale_handle"; - case DiagnosticCode::ParseError: - return "parse_error"; - case DiagnosticCode::SerializationError: - return "serialization_error"; - case DiagnosticCode::InvariantViolation: - return "invariant_violation"; - case DiagnosticCode::Failed: - return "failed"; - case DiagnosticCode::HashMismatch: - return "hash_mismatch"; - case DiagnosticCode::UnknownVersion: - return "unknown_version"; - case DiagnosticCode::CallbackFailure: - return "callback_failure"; - case DiagnosticCode::DialogueNotWaitingForChoice: - return "dialogue_not_waiting_for_choice"; - case DiagnosticCode::DialogueNotWaitingForCommand: - return "dialogue_not_waiting_for_command"; - case DiagnosticCode::DialogueConditionRejected: - return "dialogue_condition_rejected"; - case DiagnosticCode::DialogueRouteNotFound: - return "dialogue_route_not_found"; - case DiagnosticCode::ProcgenGroupDataInvalid: - return "procgen_group_data_invalid"; + case DiagnosticCode::None: return "none"; + case DiagnosticCode::InvalidArgument: return "invalid_argument"; + case DiagnosticCode::PreconditionViolation: return "precondition_violation"; + case DiagnosticCode::NotFound: return "not_found"; + case DiagnosticCode::AlreadyExists: return "already_exists"; + case DiagnosticCode::Conflict: return "conflict"; + case DiagnosticCode::Unsupported: return "unsupported"; + case DiagnosticCode::Cancelled: return "cancelled"; + case DiagnosticCode::StaleHandle: return "stale_handle"; + case DiagnosticCode::ParseError: return "parse_error"; + case DiagnosticCode::SerializationError: return "serialization_error"; + case DiagnosticCode::InvariantViolation: return "invariant_violation"; + case DiagnosticCode::Failed: return "failed"; + case DiagnosticCode::HashMismatch: return "hash_mismatch"; + case DiagnosticCode::UnknownVersion: return "unknown_version"; + case DiagnosticCode::CallbackFailure: return "callback_failure"; + case DiagnosticCode::DialogueNotWaitingForChoice: return "dialogue_not_waiting_for_choice"; + case DiagnosticCode::DialogueNotWaitingForCommand: return "dialogue_not_waiting_for_command"; + case DiagnosticCode::DialogueConditionRejected: return "dialogue_condition_rejected"; + case DiagnosticCode::DialogueRouteNotFound: return "dialogue_route_not_found"; + case DiagnosticCode::ProcgenGroupDataInvalid: return "procgen_group_data_invalid"; } return "unknown"; } diff --git a/src/engine/common/EventSequence.h b/src/engine/common/EventSequence.h index 0000f6f6..9a28f33e 100644 --- a/src/engine/common/EventSequence.h +++ b/src/engine/common/EventSequence.h @@ -7,7 +7,7 @@ namespace eve { namespace detail { struct EventSequenceTag {}; -} +} // namespace detail /** @brief Stream-local event ordering value; it is not a global event identity. */ using EventSequence = detail::StrongUint64; } // namespace eve diff --git a/src/engine/common/Generation.h b/src/engine/common/Generation.h index 0caedd7d..f76aa4fa 100644 --- a/src/engine/common/Generation.h +++ b/src/engine/common/Generation.h @@ -7,7 +7,7 @@ namespace eve { namespace detail { struct GenerationTag {}; -} +} // namespace detail /** @brief Registry/object replacement generation used to reject stale handles. */ using Generation = detail::StrongUint64; } // namespace eve diff --git a/src/engine/common/Identity.cpp b/src/engine/common/Identity.cpp index 705ac340..0437c699 100644 --- a/src/engine/common/Identity.cpp +++ b/src/engine/common/Identity.cpp @@ -9,16 +9,14 @@ namespace { [[nodiscard]] bool isLogicalName(std::string_view value) noexcept { if (value.empty()) return false; for (const char character : value) { - const bool alpha = (character >= 'a' && character <= 'z') || - (character >= 'A' && character <= 'Z'); + const bool alpha = (character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z'); const bool digit = character >= '0' && character <= '9'; if (!(alpha || digit || character == '_' || character == '-' || character == '.')) { return false; } } const char first = value.front(); - return (first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z') || - (first >= '0' && first <= '9'); + return (first >= 'a' && first <= 'z') || (first >= 'A' && first <= 'Z') || (first >= '0' && first <= '9'); } [[nodiscard]] bool isLogicalNamespace(std::string_view value) noexcept { @@ -84,13 +82,12 @@ std::optional UuidV7Generator::generate() const { return generate(timestamp); } -std::optional UuidV7Generator::generate( - std::chrono::system_clock::time_point timestamp) const { +std::optional UuidV7Generator::generate(std::chrono::system_clock::time_point timestamp) const { return generateUuidV7(timestamp, entropy_); } -std::optional generateUuidV7( - std::chrono::system_clock::time_point timestamp, const UuidEntropySource& entropy) { +std::optional generateUuidV7(std::chrono::system_clock::time_point timestamp, + const UuidEntropySource& entropy) { if (!entropy) return std::nullopt; if (timestamp.time_since_epoch() < std::chrono::system_clock::duration::zero()) return std::nullopt; diff --git a/src/engine/common/Identity.h b/src/engine/common/Identity.h index a6b052a5..d07c6f0f 100644 --- a/src/engine/common/Identity.h +++ b/src/engine/common/Identity.h @@ -13,9 +13,9 @@ #include #include +#include #include #include -#include #include #include #include @@ -78,12 +78,11 @@ public: * does not accept braces or non-canonical separators. */ [[nodiscard]] static std::optional parse(std::string_view text) noexcept { - if (text.size() != 36 || text[8] != '-' || text[13] != '-' || text[18] != '-' || - text[23] != '-') { + if (text.size() != 36 || text[8] != '-' || text[13] != '-' || text[18] != '-' || text[23] != '-') { return std::nullopt; } - Bytes bytes{}; + Bytes bytes{}; std::size_t byteIndex = 0; for (std::size_t i = 0; i < text.size();) { if (text[i] == '-') { @@ -93,7 +92,7 @@ public: if (i + 1 >= text.size()) return std::nullopt; const auto high = hexValue(text[i]); - const auto low = hexValue(text[i + 1]); + const auto low = hexValue(text[i + 1]); if (!high || !low || byteIndex >= bytes.size()) { return std::nullopt; } @@ -122,7 +121,7 @@ public: */ [[nodiscard]] std::string format() const { static constexpr char digits[] = "0123456789abcdef"; - std::string result; + std::string result; result.reserve(36); for (std::size_t i = 0; i < bytes_.size(); ++i) { if (i == 4 || i == 6 || i == 8 || i == 10) result.push_back('-'); @@ -152,8 +151,8 @@ public: [[nodiscard]] Id128 child(std::string_view role) const noexcept { if (isNil() || role.empty()) return Id128::nil(); - Bytes result = bytes_; - std::uint64_t hash = 14695981039346656037ull; + Bytes result = bytes_; + std::uint64_t hash = 14695981039346656037ull; for (const unsigned char byte : role) { hash ^= byte; hash *= 1099511628211ull; @@ -190,9 +189,7 @@ public: friend constexpr bool operator==(const Id128&, const Id128&) noexcept = default; /** @brief Orders UUID bytes in network order for ordered containers. */ - friend constexpr auto operator<=>(const Id128& lhs, const Id128& rhs) noexcept { - return lhs.bytes_ <=> rhs.bytes_; - } + friend constexpr auto operator<=>(const Id128& lhs, const Id128& rhs) noexcept { return lhs.bytes_ <=> rhs.bytes_; } private: [[nodiscard]] static constexpr std::optional hexValue(char value) noexcept { @@ -432,8 +429,7 @@ public: * @param name The name within that namespace. * @return The logical ID, or empty when either component is invalid. */ - [[nodiscard]] static std::optional fromParts(std::string_view namespaceName, - std::string_view name); + [[nodiscard]] static std::optional fromParts(std::string_view namespaceName, std::string_view name); /** @brief Returns whether this value contains a valid namespace and name. */ [[nodiscard]] bool isValid() const noexcept { return !value_.empty(); } @@ -501,12 +497,11 @@ public: * @param timestamp Unix time used for the UUIDv7 millisecond field. * @return A generated PersistentId, or empty when entropy/time is unavailable or out of range. */ - [[nodiscard]] std::optional generate( - std::chrono::system_clock::time_point timestamp) const; + [[nodiscard]] std::optional generate(std::chrono::system_clock::time_point timestamp) const; private: UuidEntropySource entropy_; - UuidClock clock_; + UuidClock clock_; }; /** @@ -515,8 +510,8 @@ private: * @param entropy Source for the UUID random portion. * @return A generated PersistentId, or empty when entropy/time is unavailable or out of range. */ -[[nodiscard]] EVENGINE_API std::optional generateUuidV7( - std::chrono::system_clock::time_point timestamp, const UuidEntropySource& entropy); +[[nodiscard]] EVENGINE_API std::optional generateUuidV7(std::chrono::system_clock::time_point timestamp, + const UuidEntropySource& entropy); } // namespace eve diff --git a/src/engine/common/Json.cpp b/src/engine/common/Json.cpp index d8c2121f..88acb4b3 100644 --- a/src/engine/common/Json.cpp +++ b/src/engine/common/Json.cpp @@ -25,10 +25,10 @@ struct Node { bool boolVal = false; double numberVal = 0.0; /** Set when the literal had no fraction or exponent. */ - bool integerLiteral = false; + bool integerLiteral = false; /** Set when the integer literal fits the canonical Int64 range. */ - bool hasInt64 = false; - std::int64_t intVal = 0; + bool hasInt64 = false; + std::int64_t intVal = 0; std::string stringVal; std::vector memberNames; // object keys, document order std::vector memberValues; // object values, parallel to memberNames @@ -141,7 +141,7 @@ private: out.integerLiteral = integral; if (integral) { try { - out.intVal = std::stoll(num); + out.intVal = std::stoll(num); out.hasInt64 = true; } catch (...) { // Keep the finite double for compatibility with the @@ -326,9 +326,7 @@ bool Value::isNull() const { return !node_ || node_->kind == Node::Kind::Null; } bool Value::isBool() const { return node_ && node_->kind == Node::Kind::Bool; } bool Value::isNumber() const { return node_ && node_->kind == Node::Kind::Number; } bool Value::isInt64() const { return node_ && node_->kind == Node::Kind::Number && node_->hasInt64; } -bool Value::isIntegerLiteral() const { - return node_ && node_->kind == Node::Kind::Number && node_->integerLiteral; -} +bool Value::isIntegerLiteral() const { return node_ && node_->kind == Node::Kind::Number && node_->integerLiteral; } bool Value::isString() const { return node_ && node_->kind == Node::Kind::String; } bool Value::isObject() const { return node_ && node_->kind == Node::Kind::Object; } bool Value::isArray() const { return node_ && node_->kind == Node::Kind::Array; } @@ -389,9 +387,7 @@ double Value::asDouble(double fallback) const { } } -std::int64_t Value::asInt64(std::int64_t fallback) const { - return isInt64() ? node_->intVal : fallback; -} +std::int64_t Value::asInt64(std::int64_t fallback) const { return isInt64() ? node_->intVal : fallback; } int Value::asInt(int fallback) const { if (!node_) return fallback; @@ -485,22 +481,22 @@ void appendEscapedString(std::string_view value, std::string& output) { output.push_back('"'); for (const unsigned char byte : value) { switch (byte) { - case '"': output += "\\\""; break; - case '\\': output += "\\\\"; break; - case '\b': output += "\\b"; break; - case '\f': output += "\\f"; break; - case '\n': output += "\\n"; break; - case '\r': output += "\\r"; break; - case '\t': output += "\\t"; break; - default: - if (byte < 0x20) { - output += "\\u00"; - output.push_back(hex[byte >> 4]); - output.push_back(hex[byte & 0x0f]); - } else { - output.push_back(static_cast(byte)); - } - break; + case '"': output += "\\\""; break; + case '\\': output += "\\\\"; break; + case '\b': output += "\\b"; break; + case '\f': output += "\\f"; break; + case '\n': output += "\\n"; break; + case '\r': output += "\\r"; break; + case '\t': output += "\\t"; break; + default: + if (byte < 0x20) { + output += "\\u00"; + output.push_back(hex[byte >> 4]); + output.push_back(hex[byte & 0x0f]); + } else { + output.push_back(static_cast(byte)); + } + break; } } output.push_back('"'); @@ -568,8 +564,7 @@ Result stringify(const eve::Value& value) { std::string output; if (!appendCanonicalJson(value, output)) { return Result::failure(Diagnostic::error( - DiagnosticCode::SerializationError, - "Value contains a non-finite Double; JSON requires finite numbers")); + DiagnosticCode::SerializationError, "Value contains a non-finite Double; JSON requires finite numbers")); } return Result::success(std::move(output)); } diff --git a/src/engine/common/Module.cpp b/src/engine/common/Module.cpp index b763941e..c4147fcf 100644 --- a/src/engine/common/Module.cpp +++ b/src/engine/common/Module.cpp @@ -87,10 +87,9 @@ eve::Result ModuleManager::finishPluginRegistration(bool commit) { manager.plugin_registration_added_.clear(); manager.plugin_registration_error_.clear(); if (!error.empty()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, error, {}, {}, "module")); - return eve::Result::success(eve::Status::success( - commit ? eve::StatusCode::Applied : eve::StatusCode::NoOp)); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, error, {}, {}, "module")); + return eve::Result::success(eve::Status::success(commit ? eve::StatusCode::Applied : eve::StatusCode::NoOp)); } void ModuleManager::exposeVM(ssq::VM& vm) { diff --git a/src/engine/common/Module.h b/src/engine/common/Module.h index 0d38195c..8324b87c 100644 --- a/src/engine/common/Module.h +++ b/src/engine/common/Module.h @@ -17,17 +17,17 @@ class Class; static void expose(ssq::Table& table); \ static void expose(ssq::Class& vm); -#define Module_REG(ModuleName) \ - SSQ_REG \ - virtual std::string getName() const override { return name; } \ - /** @brief Return the manager-owned module instance for this registration. \ - * @ownership Borrowed; ModuleManager retains ownership. \ - * @nullable No after successful registration; factory failure is an invariant. \ - * @lifetime Valid until module shutdown or registry teardown. \ - * @thread Main/composition thread only. \ - * @reentrancy Must not re-enter module registration. */ \ +#define Module_REG(ModuleName) \ + SSQ_REG \ + virtual std::string getName() const override { return name; } \ + /** @brief Return the manager-owned module instance for this registration. \ + * @ownership Borrowed; ModuleManager retains ownership. \ + * @nullable No after successful registration; factory failure is an invariant. \ + * @lifetime Valid until module shutdown or registry teardown. \ + * @thread Main/composition thread only. \ + * @reentrancy Must not re-enter module registration. */ \ [[nodiscard("module instance ownership must be retained or explicitly handled")]] static ModuleName* create(); \ - static const char* name + static const char* name #define Module_IMPL(ModuleName, newExpr) \ ModuleRegister ModuleName##_register(ModuleName::name, \ @@ -78,15 +78,14 @@ public: /** @brief Registers a module factory (creator + script exposer) by name. */ static void register_module(const char* name, creator_t c, exposer_t e); /** @brief Begins an atomic native-plugin module registration transaction. */ - [[nodiscard("plugin registration outcome must be checked")]] static eve::Result - beginPluginRegistration(); + [[nodiscard("plugin registration outcome must be checked")]] static eve::Result beginPluginRegistration(); /** @brief Commits or rolls back the active plugin registration transaction. * @param commit Whether to keep newly registered modules. * @return Applied when committed, NoOp when explicitly rolled back, or a * structured registration conflict/failure. */ - [[nodiscard("plugin registration outcome must be checked")]] static eve::Result - finishPluginRegistration(bool commit); + [[nodiscard("plugin registration outcome must be checked")]] static eve::Result finishPluginRegistration( + bool commit); /** @brief Exposes every registered module into the given runtime's root table. */ static void expose(Runtime& runtime); // Compatibility for embedders that still own their ssq::VM directly. diff --git a/src/engine/common/Resource.cpp b/src/engine/common/Resource.cpp index e520ff27..7f42a10b 100644 --- a/src/engine/common/Resource.cpp +++ b/src/engine/common/Resource.cpp @@ -195,8 +195,7 @@ eve::Result ResourceManager::reload(const std::string &normPath) { std::lock_guard lock(mu_); for (const auto &item : prepared) { auto it = resources.find(item.key); - if (it == resources.end() || it->second.get() != item.cached) - return eve::Result::success(false); + if (it == resources.end() || it->second.get() != item.cached) return eve::Result::success(false); } size_t committed = 0; diff --git a/src/engine/common/Resource.h b/src/engine/common/Resource.h index fa8ce3b3..554db9b5 100644 --- a/src/engine/common/Resource.h +++ b/src/engine/common/Resource.h @@ -99,31 +99,31 @@ public: /** @brief The path part of a cache key (everything before the first '?'). */ static std::string pathOfKey(const std::string& key); - /** - * @brief Get the cached resource for `key`; on a miss, load it through the - * registered IAssetReloader providers and cache the result. - * @param key A normalized VFS path, optionally with `?params`. - * @return The shared resource, or nullptr when no provider claims the key. - * Loader errors propagate to the caller. The returned pointer is - * owned by the cache (kept alive while the entry exists); callers - * must not delete it, and it stays valid until unload() or process - * exit. - * @ownership Borrowed from the cache; ResourceManager is the owning authority. - * @nullable Yes when no registered provider claims `key`. - * @lifetime Valid while the cache entry remains loaded and until manager teardown; - * callers must not retain it across unload or reload replacement. - * @thread Lookup may be called by concurrent readers; cache mutation and reload - * commit are serialized by ResourceManager. - * @reentrancy The returned resource must not be used to re-enter cache mutation - * while a ResourceManager operation is holding its internal lock. - */ - [[nodiscard("resource lookup ownership must be retained or explicitly handled")]] Resource *get(std::string key); + /** + * @brief Get the cached resource for `key`; on a miss, load it through the + * registered IAssetReloader providers and cache the result. + * @param key A normalized VFS path, optionally with `?params`. + * @return The shared resource, or nullptr when no provider claims the key. + * Loader errors propagate to the caller. The returned pointer is + * owned by the cache (kept alive while the entry exists); callers + * must not delete it, and it stays valid until unload() or process + * exit. + * @ownership Borrowed from the cache; ResourceManager is the owning authority. + * @nullable Yes when no registered provider claims `key`. + * @lifetime Valid while the cache entry remains loaded and until manager teardown; + * callers must not retain it across unload or reload replacement. + * @thread Lookup may be called by concurrent readers; cache mutation and reload + * commit are serialized by ResourceManager. + * @reentrancy The returned resource must not be used to re-enter cache mutation + * while a ResourceManager operation is holding its internal lock. + */ + [[nodiscard("resource lookup ownership must be retained or explicitly handled")]] Resource* get(std::string key); - /** - * @brief Drop the exact cache entry `key` (parameters included). - * The resource stays alive while other holders still reference it. - */ - void unload(std::string key); + /** + * @brief Drop the exact cache entry `key` (parameters included). + * The resource stays alive while other holders still reference it. + */ + void unload(std::string key); /** @brief Drop every cache entry whose path matches, whatever its parameters. */ void unloadPath(const std::string& path); diff --git a/src/engine/common/ResourceAccount.cpp b/src/engine/common/ResourceAccount.cpp index 49dd9eba..b65295e8 100644 --- a/src/engine/common/ResourceAccount.cpp +++ b/src/engine/common/ResourceAccount.cpp @@ -15,77 +15,65 @@ std::atomic& nextAccountNonce() { } eve::Diagnostic invalidArgument(std::string message, std::string path = {}) { - return eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, std::move(message), - std::move(path)); + return eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, std::move(message), std::move(path)); } eve::Diagnostic invariantFailure(std::string message, std::string path = {}) { - return eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, std::move(message), - std::move(path)); + return eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, std::move(message), std::move(path)); } } // namespace eve::Result allocateAccountNonce() { - auto& counter = nextAccountNonce(); - std::uint64_t value = counter.load(std::memory_order_relaxed); + auto& counter = nextAccountNonce(); + std::uint64_t value = counter.load(std::memory_order_relaxed); for (;;) { if (value == 0 || value == std::numeric_limits::max()) - return eve::Result::failure( - invariantFailure("resource account nonce exhausted", "account")); - if (counter.compare_exchange_weak(value, value + 1, std::memory_order_relaxed, - std::memory_order_relaxed)) + return eve::Result::failure(invariantFailure("resource account nonce exhausted", "account")); + if (counter.compare_exchange_weak(value, value + 1, std::memory_order_relaxed, std::memory_order_relaxed)) return eve::Result::success(AccountNonce(value)); } } eve::Result ResourceId::parse(std::string_view text) { if (text.empty()) - return eve::Result::failure( - invalidArgument("resource id must not be empty", "resource")); + return eve::Result::failure(invalidArgument("resource id must not be empty", "resource")); if (text.size() > 128) - return eve::Result::failure( - invalidArgument("resource id is too long", "resource")); + return eve::Result::failure(invalidArgument("resource id is too long", "resource")); for (const unsigned char character : text) { if (std::iscntrl(character) || std::isspace(character)) return eve::Result::failure( - invalidArgument("resource id must not contain whitespace or control characters", - "resource")); + invalidArgument("resource id must not contain whitespace or control characters", "resource")); } return eve::Result::success(ResourceId(std::string(text))); } eve::Result Amount::from(std::int64_t value) { if (value < 0) - return eve::Result::failure( - invalidArgument("resource amount must not be negative", "amount")); + return eve::Result::failure(invalidArgument("resource amount must not be negative", "amount")); return eve::Result::success(Amount(value)); } eve::Result Amount::checkedAdd(Amount other) const { if (other.value_ > std::numeric_limits::max() - value_) - return eve::Result::failure( - invariantFailure("resource amount addition overflowed", "amount")); + return eve::Result::failure(invariantFailure("resource amount addition overflowed", "amount")); return eve::Result::success(Amount(value_ + other.value_)); } -eve::Result ResourceCost::create(std::string_view resourceId, - std::int64_t amountValue) { +eve::Result ResourceCost::create(std::string_view resourceId, std::int64_t amountValue) { auto resource = ResourceId::parse(resourceId); if (!resource) return eve::Result::failure(resource.status()); auto amount = Amount::from(amountValue); if (!amount) return eve::Result::failure(amount.status()); if (amount.value().isZero()) - return eve::Result::failure( - invalidArgument("resource cost amount must be positive", "amount")); + return eve::Result::failure(invalidArgument("resource cost amount must be positive", "amount")); return eve::Result::success( ResourceCost{std::move(resource).takeValue(), std::move(amount).takeValue()}); } eve::Result CostSpec::create(std::vector items) { if (items.empty()) - return eve::Result::failure( - invalidArgument("resource cost must contain at least one item", "cost")); + return eve::Result::failure(invalidArgument("resource cost must contain at least one item", "cost")); for (const auto& item : items) { if (!item.resource.isValid()) return eve::Result::failure( @@ -95,9 +83,8 @@ eve::Result CostSpec::create(std::vector items) { invalidArgument("resource cost amounts must be positive", "cost.amount")); } - std::sort(items.begin(), items.end(), [](const auto& left, const auto& right) { - return left.resource.value() < right.resource.value(); - }); + std::sort(items.begin(), items.end(), + [](const auto& left, const auto& right) { return left.resource.value() < right.resource.value(); }); std::vector canonical; canonical.reserve(items.size()); @@ -115,8 +102,7 @@ eve::Result CostSpec::create(std::vector items) { eve::Result CostSpec::from(std::initializer_list items) { if (items.size() == 0) - return eve::Result::failure( - invalidArgument("resource cost must contain at least one item", "cost")); + return eve::Result::failure(invalidArgument("resource cost must contain at least one item", "cost")); std::vector parsed; parsed.reserve(items.size()); diff --git a/src/engine/common/ResourceAccount.h b/src/engine/common/ResourceAccount.h index e979e8cc..7ed470ae 100644 --- a/src/engine/common/ResourceAccount.h +++ b/src/engine/common/ResourceAccount.h @@ -51,7 +51,7 @@ public: /** @brief Return the canonical owned spelling. */ [[nodiscard]] const std::string& value() const noexcept { return value_; } - friend bool operator==(const ResourceId&, const ResourceId&) noexcept = default; + friend bool operator==(const ResourceId&, const ResourceId&) noexcept = default; friend auto operator<=>(const ResourceId&, const ResourceId&) noexcept = default; private: @@ -101,7 +101,7 @@ public: */ [[nodiscard]] eve::Result checkedAdd(Amount other) const; - friend constexpr bool operator==(const Amount&, const Amount&) noexcept = default; + friend constexpr bool operator==(const Amount&, const Amount&) noexcept = default; friend constexpr auto operator<=>(const Amount&, const Amount&) noexcept = default; private: @@ -121,8 +121,7 @@ struct ResourceCost { * @param amountValue Positive quantity. * @return A validated item or a rejected Result. */ - [[nodiscard]] static eve::Result create(std::string_view resourceId, - std::int64_t amountValue); + [[nodiscard]] static eve::Result create(std::string_view resourceId, std::int64_t amountValue); }; /** @brief Convenient unvalidated input used by CostSpec::from(). */ @@ -163,8 +162,7 @@ public: * @param amount Positive quantity. * @return A canonical cost or a rejected Result. */ - [[nodiscard]] static eve::Result single(std::string_view resource, - std::int64_t amount); + [[nodiscard]] static eve::Result single(std::string_view resource, std::int64_t amount); /** @brief Whether this value contains a validated non-empty cost. */ [[nodiscard]] bool isValid() const noexcept { return !items_.empty(); } @@ -205,7 +203,7 @@ struct ResourceShortfall { * Result failure instead. */ struct Affordability { - bool affordable = false; + bool affordable = false; std::vector shortfalls; /** @brief Return whether every item in the queried cost is available. */ @@ -240,7 +238,7 @@ public: /** @brief Whether this nonce is invalid/zero. */ [[nodiscard]] constexpr bool isZero() const noexcept { return value_ == 0; } - friend constexpr bool operator==(const AccountNonce&, const AccountNonce&) noexcept = default; + friend constexpr bool operator==(const AccountNonce&, const AccountNonce&) noexcept = default; friend constexpr auto operator<=>(const AccountNonce&, const AccountNonce&) noexcept = default; private: @@ -276,9 +274,7 @@ struct Reservation { CostSpec cost; /** @brief Whether this credential has valid account/id identity and cost. */ - [[nodiscard]] bool isValid() const noexcept { - return account.value() != 0 && id.value() != 0 && cost.isValid(); - } + [[nodiscard]] bool isValid() const noexcept { return account.value() != 0 && id.value() != 0 && cost.isValid(); } }; /** @@ -295,9 +291,7 @@ struct Receipt { CostSpec cost; /** @brief Whether this receipt contains a valid operation identity. */ - [[nodiscard]] bool isValid() const noexcept { - return account.value() != 0 && id.value() != 0 && cost.isValid(); - } + [[nodiscard]] bool isValid() const noexcept { return account.value() != 0 && id.value() != 0 && cost.isValid(); } }; /** diff --git a/src/engine/common/ResourceRef.cpp b/src/engine/common/ResourceRef.cpp index 5cad4adc..d04e4c4d 100644 --- a/src/engine/common/ResourceRef.cpp +++ b/src/engine/common/ResourceRef.cpp @@ -7,8 +7,7 @@ namespace { template [[nodiscard]] Result parseFailure(std::string message, std::string_view path) { - return Result::failure( - Diagnostic::error(DiagnosticCode::ParseError, std::move(message), std::string(path))); + return Result::failure(Diagnostic::error(DiagnosticCode::ParseError, std::move(message), std::string(path))); } template @@ -31,8 +30,7 @@ Result AssetRef::fromId(PersistentId id) { Result AssetRef::fromUri(const ResourceUri& uri) { if (uri.scheme() != UriScheme::Asset || !uri.query().empty() || !uri.fragment().empty()) { - return parseFailure("AssetRef requires an asset:// UUID without query or fragment", - uri.format()); + return parseFailure("AssetRef requires an asset:// UUID without query or fragment", uri.format()); } const auto parsed = PersistentId::parse(uri.path()); if (!parsed || parsed->isNil()) { @@ -88,9 +86,7 @@ const AssetRef* ResourceRef::asset() const noexcept { return std::get_if(&value_); } -const DefinitionRef* ResourceRef::definition() const noexcept { - return std::get_if(&value_); -} +const DefinitionRef* ResourceRef::definition() const noexcept { return std::get_if(&value_); } const ObjectRef* ResourceRef::object() const noexcept { return std::get_if(&value_); } diff --git a/src/engine/common/ResourceRef.h b/src/engine/common/ResourceRef.h index 1656e4cb..12d4d660 100644 --- a/src/engine/common/ResourceRef.h +++ b/src/engine/common/ResourceRef.h @@ -28,10 +28,10 @@ enum class ResourceRefKind : std::uint8_t { /** @brief Returns the stable resource-reference alternative spelling. */ [[nodiscard]] constexpr std::string_view resourceRefKindName(ResourceRefKind kind) noexcept { switch (kind) { - case ResourceRefKind::Asset: return "asset"; - case ResourceRefKind::Uri: return "uri"; - case ResourceRefKind::Definition: return "definition"; - case ResourceRefKind::Object: return "object"; + case ResourceRefKind::Asset: return "asset"; + case ResourceRefKind::Uri: return "uri"; + case ResourceRefKind::Definition: return "definition"; + case ResourceRefKind::Object: return "object"; } return "unknown"; } @@ -155,9 +155,7 @@ public: [[nodiscard]] static ResourceRef fromUri(ResourceUri value) { return ResourceRef(std::move(value)); } /** @brief Wrap a definition reference in the generic reference union. */ - [[nodiscard]] static ResourceRef fromDefinition(DefinitionRef value) { - return ResourceRef(std::move(value)); - } + [[nodiscard]] static ResourceRef fromDefinition(DefinitionRef value) { return ResourceRef(std::move(value)); } /** @brief Wrap an object reference in the generic reference union. */ [[nodiscard]] static ResourceRef fromObject(ObjectRef value) { return ResourceRef(std::move(value)); } diff --git a/src/engine/common/Result.h b/src/engine/common/Result.h index 1f2823ec..1fab663e 100644 --- a/src/engine/common/Result.h +++ b/src/engine/common/Result.h @@ -45,8 +45,7 @@ public: #if !defined(ZEROERR_NO_ASSERT) ~ResultObservation() noexcept(false) { const bool observationSatisfied = !mustObserve_ || observed_; - EV_ASSERT(observationSatisfied, - "Result destroyed without checking; inspect it or call ignore() explicitly"); + EV_ASSERT(observationSatisfied, "Result destroyed without checking; inspect it or call ignore() explicitly"); } #else ~ResultObservation() noexcept = default; @@ -62,15 +61,14 @@ protected: void assertCanBeOverwritten() const { #if !defined(ZEROERR_NO_ASSERT) const bool observationSatisfied = !mustObserve_ || observed_; - EV_ASSERT(observationSatisfied, - "move assignment would overwrite an unchecked Result"); + EV_ASSERT(observationSatisfied, "move assignment would overwrite an unchecked Result"); #endif } void adoptObservationFrom(ResultObservation& other) noexcept { #if !defined(ZEROERR_NO_ASSERT) - observed_ = other.observed_; - mustObserve_ = other.mustObserve_; + observed_ = other.observed_; + mustObserve_ = other.mustObserve_; other.mustObserve_ = false; #else (void)other; @@ -101,16 +99,13 @@ using ResultReturn = std::invoke_result_t; * @tparam T Owning value type. References and void use a different form. */ template -class [[nodiscard("Result must be checked or explicitly ignored")]] Result - : private detail::ResultObservation { +class [[nodiscard("Result must be checked or explicitly ignored")]] Result : private detail::ResultObservation { static_assert(!std::is_reference_v, "Result cannot hold a reference; use a handle or value"); static_assert(!std::is_void_v, "Result has a dedicated specialization"); public: /** @brief Construct a successful result owning `value`. */ - static Result success(T value) { - return Result(Status::success(), std::optional(std::move(value))); - } + static Result success(T value) { return Result(Status::success(), std::optional(std::move(value))); } /** @brief Construct a successful result with an explicit non-error status. */ static Result success(T value, Status status) { @@ -125,17 +120,16 @@ public: } /** @brief Construct a failed result from one structured diagnostic. */ - static Result failure(Diagnostic diagnostic) { - return failure(Status::failure(std::move(diagnostic))); - } + static Result failure(Diagnostic diagnostic) { return failure(Status::failure(std::move(diagnostic))); } - Result(const Result&) = delete; - Result& operator=(const Result&) = delete; + Result(const Result&) = delete; + Result& operator=(const Result&) = delete; /** @brief Move a result and transfer its debug observation responsibility. */ Result(Result&& other) : detail::ResultObservation(detail::ResultObservation::InactiveTag{}), - status_(std::move(other.status_)), value_(std::move(other.value_)) { + status_(std::move(other.status_)), + value_(std::move(other.value_)) { adoptObservationFrom(other); } @@ -145,8 +139,7 @@ public: * either result's observation responsibility is changed. */ Result& operator=(Result&& other) { - if (this == &other) - return *this; + if (this == &other) return *this; assertCanBeOverwritten(); status_ = std::move(other.status_); value_ = std::move(other.value_); @@ -202,11 +195,10 @@ public: explicit operator bool() const noexcept { return ok(); } /** @brief Borrow the value from a const lvalue after checking success. */ - const T& value() const & { + const T& value() const& { observe(); const bool hasSuccessfulValue = status_.isSuccess() && value_.has_value(); - EV_ASSERT(hasSuccessfulValue, - "Result::value requires a successful Result with a value"); + EV_ASSERT(hasSuccessfulValue, "Result::value requires a successful Result with a value"); return value_.value(); } @@ -214,8 +206,7 @@ public: T& value() & { observe(); const bool hasSuccessfulValue = status_.isSuccess() && value_.has_value(); - EV_ASSERT(hasSuccessfulValue, - "Result::value requires a successful Result with a value"); + EV_ASSERT(hasSuccessfulValue, "Result::value requires a successful Result with a value"); return value_.value(); } @@ -223,8 +214,7 @@ public: T&& value() && { observe(); const bool hasSuccessfulValue = status_.isSuccess() && value_.has_value(); - EV_ASSERT(hasSuccessfulValue, - "Result::value requires a successful Result with a value"); + EV_ASSERT(hasSuccessfulValue, "Result::value requires a successful Result with a value"); return std::move(value_.value()); } @@ -232,8 +222,7 @@ public: T takeValue() && { observe(); const bool hasSuccessfulValue = status_.isSuccess() && value_.has_value(); - EV_ASSERT(hasSuccessfulValue, - "Result::takeValue requires a successful Result with a value"); + EV_ASSERT(hasSuccessfulValue, "Result::takeValue requires a successful Result with a value"); T result = std::move(value_.value()); value_.reset(); return result; @@ -242,8 +231,7 @@ public: /** @brief Return the value or an explicit alternate value, consuming this Result. */ T valueOr(T fallback) && { observe(); - if (status_.isSuccess() && value_.has_value()) - return std::move(value_.value()); + if (status_.isSuccess() && value_.has_value()) return std::move(value_.value()); return fallback; } @@ -256,8 +244,7 @@ public: auto andThen(Function&& function) && -> std::invoke_result_t { observe(); using Return = std::invoke_result_t; - if (!status_.isSuccess() || !value_.has_value()) - return Return::failure(status_); + if (!status_.isSuccess() || !value_.has_value()) return Return::failure(status_); return std::invoke(std::forward(function), std::move(value_.value())); } @@ -269,8 +256,7 @@ public: template Result orElse(Function&& function) && { observe(); - if (status_.isSuccess() && value_.has_value()) - return std::move(*this); + if (status_.isSuccess() && value_.has_value()) return std::move(*this); return std::invoke(std::forward(function), status_); } @@ -301,10 +287,9 @@ public: } private: - Result(Status status, std::optional value) - : status_(std::move(status)), value_(std::move(value)) {} + Result(Status status, std::optional value) : status_(std::move(status)), value_(std::move(value)) {} - Status status_; + Status status_; std::optional value_; }; @@ -312,8 +297,7 @@ private: * @brief Move-only operation result for actions with no value payload. */ template <> -class [[nodiscard("Result must be checked or explicitly ignored")]] Result - : private detail::ResultObservation { +class [[nodiscard("Result must be checked or explicitly ignored")]] Result : private detail::ResultObservation { public: /** @brief Construct a successful void result. */ static Result success() { return Result(Status::success()); } @@ -331,24 +315,20 @@ public: } /** @brief Construct a failed void result from one structured diagnostic. */ - static Result failure(Diagnostic diagnostic) { - return failure(Status::failure(std::move(diagnostic))); - } + static Result failure(Diagnostic diagnostic) { return failure(Status::failure(std::move(diagnostic))); } Result(const Result&) = delete; Result& operator=(const Result&) = delete; /** @brief Move a result and transfer its debug observation responsibility. */ Result(Result&& other) - : detail::ResultObservation(detail::ResultObservation::InactiveTag{}), - status_(std::move(other.status_)) { + : detail::ResultObservation(detail::ResultObservation::InactiveTag{}), status_(std::move(other.status_)) { adoptObservationFrom(other); } /** @brief Move-assign after checking the destination's old result. */ Result& operator=(Result&& other) { - if (this == &other) - return *this; + if (this == &other) return *this; assertCanBeOverwritten(); status_ = std::move(other.status_); adoptObservationFrom(other); diff --git a/src/engine/common/Revision.h b/src/engine/common/Revision.h index 8e59244f..52f39b35 100644 --- a/src/engine/common/Revision.h +++ b/src/engine/common/Revision.h @@ -7,7 +7,7 @@ namespace eve { namespace detail { struct RevisionTag {}; -} +} // namespace detail /** @brief Monotonic content/state revision used for optimistic-concurrency checks. */ using Revision = detail::StrongUint64; } // namespace eve diff --git a/src/engine/common/RuntimeHandle.h b/src/engine/common/RuntimeHandle.h index 3429b0c8..d78680ae 100644 --- a/src/engine/common/RuntimeHandle.h +++ b/src/engine/common/RuntimeHandle.h @@ -37,7 +37,7 @@ template class RuntimeHandle { public: /** @brief Unsigned owner-local slot index type. */ - using index_type = std::uint32_t; + using index_type = std::uint32_t; /** @brief Unsigned owner-local generation type. */ using generation_type = std::uint32_t; @@ -82,8 +82,7 @@ public: * @param current The current slot generation. * @return The next generation, or empty when the slot must be retired. */ - [[nodiscard]] static constexpr std::optional nextGeneration( - generation_type current) noexcept { + [[nodiscard]] static constexpr std::optional nextGeneration(generation_type current) noexcept { if (current == std::numeric_limits::max()) return std::nullopt; return static_cast(current + 1u); } @@ -105,8 +104,7 @@ public: * conversion operator and must not be used as a persistent ID. */ [[nodiscard]] constexpr std::uint64_t packed() const noexcept { - return (static_cast(generation_) << 32u) | - static_cast(index_); + return (static_cast(generation_) << 32u) | static_cast(index_); } /** @@ -119,7 +117,7 @@ public: static_cast(value >> 32u)); } - friend constexpr bool operator==(const RuntimeHandle&, const RuntimeHandle&) noexcept = default; + friend constexpr bool operator==(const RuntimeHandle&, const RuntimeHandle&) noexcept = default; friend constexpr auto operator<=>(const RuntimeHandle&, const RuntimeHandle&) noexcept = default; private: diff --git a/src/engine/common/SchemaVersion.h b/src/engine/common/SchemaVersion.h index 82b02e0f..19af677f 100644 --- a/src/engine/common/SchemaVersion.h +++ b/src/engine/common/SchemaVersion.h @@ -7,7 +7,7 @@ namespace eve { namespace detail { struct SchemaVersionTag {}; -} +} // namespace detail /** @brief Persistent data-format version; not a runtime replacement generation. */ using SchemaVersion = detail::StrongUint64; } // namespace eve diff --git a/src/engine/common/Snapshot.cpp b/src/engine/common/Snapshot.cpp index e800138b..72f82ddf 100644 --- a/src/engine/common/Snapshot.cpp +++ b/src/engine/common/Snapshot.cpp @@ -15,8 +15,8 @@ Result failure(DiagnosticCode code, std::string message, std::string path = { } bool hasOnlyEnvelopeFields(const Value::Object& object) { - static const std::set fields = { - "contentHash", "instanceId", "payload", "revision", "schema", "schemaVersion", "tick", "type"}; + static const std::set fields = {"contentHash", "instanceId", "payload", "revision", + "schema", "schemaVersion", "tick", "type"}; for (const auto& entry : object) if (!fields.contains(entry.first)) return false; return true; @@ -27,7 +27,7 @@ Result readUint64(const Value& value, std::string_view field) { if (!text || text->empty()) return failure(DiagnosticCode::ParseError, "snapshot integer must be a decimal string", std::string(field)); - std::uint64_t output = 0; + std::uint64_t output = 0; const auto [end, error] = std::from_chars(text->data(), text->data() + text->size(), output); if (error != std::errc{} || end != text->data() + text->size()) return failure(DiagnosticCode::ParseError, "snapshot integer is out of range", @@ -89,8 +89,7 @@ Result validateSnapshotPayloadMetadata(const Value& payload, Revision revi if (!parsed.ok()) return Result::failure(parsed.status()); if (parsed.value() != expected) return failure(DiagnosticCode::Conflict, - "snapshot payload " + std::string(field) + - " disagrees with the envelope", + "snapshot payload " + std::string(field) + " disagrees with the envelope", "payload." + std::string(field)); return Result::success(); }; @@ -102,12 +101,12 @@ Result validateSnapshotPayloadMetadata(const Value& payload, Revision revi return Result::success(); } -Result makeSnapshotEnvelope( - std::string type, LogicalId schema, SchemaVersion schemaVersion, PersistentId instanceId, - Revision revision, SimulationTick tick, Value payload, const SnapshotHashProvider& hashProvider) { - SnapshotEnvelope snapshot{std::move(type), std::move(schema), schemaVersion, instanceId, revision, tick, +Result makeSnapshotEnvelope(std::string type, LogicalId schema, SchemaVersion schemaVersion, + PersistentId instanceId, Revision revision, SimulationTick tick, + Value payload, const SnapshotHashProvider& hashProvider) { + SnapshotEnvelope snapshot{std::move(type), std::move(schema), schemaVersion, instanceId, revision, tick, ContentId::nil(), std::move(payload)}; - auto valid = validateHeader(snapshot); + auto valid = validateHeader(snapshot); if (!valid.ok()) return Result::failure(valid.status()); if (!hashProvider) return failure(DiagnosticCode::Unsupported, "snapshot hash provider is required", @@ -141,23 +140,23 @@ Result parseSnapshotEnvelopeValue(const Value& value, const Sn return failure(DiagnosticCode::ParseError, "snapshot envelope must be an object with the exact public fields"); - const auto type = value.getIf()->find("type"); - const auto schemaText = object->find("schema"); + const auto type = value.getIf()->find("type"); + const auto schemaText = object->find("schema"); const auto schemaVersion = object->find("schemaVersion"); - const auto instanceId = object->find("instanceId"); - const auto revision = object->find("revision"); - const auto tick = object->find("tick"); - const auto contentHash = object->find("contentHash"); - const auto payload = object->find("payload"); + const auto instanceId = object->find("instanceId"); + const auto revision = object->find("revision"); + const auto tick = object->find("tick"); + const auto contentHash = object->find("contentHash"); + const auto payload = object->find("payload"); if (type == object->end() || schemaText == object->end() || schemaVersion == object->end() || instanceId == object->end() || revision == object->end() || tick == object->end() || contentHash == object->end() || payload == object->end() || !type->second.isString() || !schemaText->second.isString() || !instanceId->second.isString() || !contentHash->second.isString()) return failure(DiagnosticCode::ParseError, "snapshot envelope has invalid fields"); - const auto schema = LogicalId::parse(*schemaText->second.getIf()); + const auto schema = LogicalId::parse(*schemaText->second.getIf()); const auto persistentId = PersistentId::parse(*instanceId->second.getIf()); - const auto contentId = ContentId::parse(*contentHash->second.getIf()); + const auto contentId = ContentId::parse(*contentHash->second.getIf()); if (!schema || !persistentId || !contentId) return failure(DiagnosticCode::ParseError, "snapshot envelope has invalid identity fields"); @@ -168,12 +167,15 @@ Result parseSnapshotEnvelopeValue(const Value& value, const Sn auto tickValue = readUint64(tick->second, "tick"); if (!tickValue.ok()) return Result::failure(tickValue.status()); - SnapshotEnvelope snapshot{*type->second.getIf(), *schema, - SchemaVersion(std::move(version).takeValue()), *persistentId, + SnapshotEnvelope snapshot{*type->second.getIf(), + *schema, + SchemaVersion(std::move(version).takeValue()), + *persistentId, Revision(std::move(revisionValue).takeValue()), - SimulationTick(std::move(tickValue).takeValue()), *contentId, + SimulationTick(std::move(tickValue).takeValue()), + *contentId, payload->second}; - auto verified = verifySnapshotEnvelope(snapshot, hashProvider); + auto verified = verifySnapshotEnvelope(snapshot, hashProvider); if (!verified.ok()) return Result::failure(verified.status()); return Result::success(std::move(snapshot)); } @@ -184,26 +186,23 @@ Result serializeSnapshotEnvelope(const SnapshotEnvelope& snapshot) return std::move(value).andThen([](Value&& owned) { return owned.toJson(); }); } -Result parseSnapshotEnvelope( - std::string_view json, const SnapshotHashProvider& hashProvider) { +Result parseSnapshotEnvelope(std::string_view json, const SnapshotHashProvider& hashProvider) { auto value = Value::fromJson(json); if (!value.ok()) return Result::failure(value.status()); return parseSnapshotEnvelopeValue(value.value(), hashProvider); } -Result SnapshotMigrationChain::add(LogicalId schema, SchemaVersion from, SchemaVersion to, - Migration migration) { +Result SnapshotMigrationChain::add(LogicalId schema, SchemaVersion from, SchemaVersion to, Migration migration) { if (!schema.isValid() || from.value() >= to.value() || !migration) return failure(DiagnosticCode::InvalidArgument, "invalid snapshot migration edge"); const Key key{schema.format(), from.value()}; - if (steps_.contains(key)) - return failure(DiagnosticCode::Conflict, "snapshot migration edge already exists"); + if (steps_.contains(key)) return failure(DiagnosticCode::Conflict, "snapshot migration edge already exists"); steps_.emplace(key, Step{to, std::move(migration)}); return Result::success(); } -Result SnapshotMigrationChain::migrate( - SnapshotEnvelope snapshot, SchemaVersion targetVersion, const SnapshotHashProvider& hashProvider) const { +Result SnapshotMigrationChain::migrate(SnapshotEnvelope snapshot, SchemaVersion targetVersion, + const SnapshotHashProvider& hashProvider) const { auto verified = verifySnapshotEnvelope(snapshot, hashProvider); if (!verified.ok()) return Result::failure(verified.status()); if (snapshot.schemaVersion.value() > targetVersion.value()) @@ -212,7 +211,7 @@ Result SnapshotMigrationChain::migrate( if (snapshot.schemaVersion == targetVersion) return Result::success(std::move(snapshot)); while (snapshot.schemaVersion.value() < targetVersion.value()) { - const Key key{snapshot.schema.format(), snapshot.schemaVersion.value()}; + const Key key{snapshot.schema.format(), snapshot.schemaVersion.value()}; const auto step = steps_.find(key); if (step == steps_.end()) return failure(DiagnosticCode::Unsupported, @@ -224,7 +223,7 @@ Result SnapshotMigrationChain::migrate( "schemaVersion"); auto payload = step->second.migration(snapshot.payload); if (!payload.ok()) return Result::failure(payload.status()); - snapshot.payload = std::move(payload).takeValue(); + snapshot.payload = std::move(payload).takeValue(); snapshot.schemaVersion = step->second.to; } diff --git a/src/engine/common/Snapshot.h b/src/engine/common/Snapshot.h index 19289fde..c472b8f8 100644 --- a/src/engine/common/Snapshot.h +++ b/src/engine/common/Snapshot.h @@ -14,10 +14,10 @@ #include "common/Export.h" #include "common/Identity.h" #include "common/Result.h" -#include "common/Value.h" #include "common/Revision.h" #include "common/SchemaVersion.h" #include "common/Time.h" +#include "common/Value.h" #include #include @@ -44,14 +44,14 @@ using SnapshotHashProvider = std::function(std::string_view ca * envelope fields are rejected by the strict parser. */ struct EVENGINE_API SnapshotEnvelope { - std::string type; - LogicalId schema; - SchemaVersion schemaVersion; - PersistentId instanceId; - Revision revision; + std::string type; + LogicalId schema; + SchemaVersion schemaVersion; + PersistentId instanceId; + Revision revision; SimulationTick tick; - ContentId contentHash; - Value payload; + ContentId contentHash; + Value payload; }; /** @@ -66,9 +66,11 @@ struct EVENGINE_API SnapshotEnvelope { * @param hashProvider Injected digest provider; must not be empty. * @return A sealed envelope, or a structured validation/hash failure. */ -[[nodiscard]] EVENGINE_API Result makeSnapshotEnvelope( - std::string type, LogicalId schema, SchemaVersion schemaVersion, PersistentId instanceId, - Revision revision, SimulationTick tick, Value payload, const SnapshotHashProvider& hashProvider); +[[nodiscard]] EVENGINE_API Result makeSnapshotEnvelope(std::string type, LogicalId schema, + SchemaVersion schemaVersion, + PersistentId instanceId, Revision revision, + SimulationTick tick, Value payload, + const SnapshotHashProvider& hashProvider); /** * @brief Produce the canonical JSON input used for a snapshot content hash. @@ -84,8 +86,8 @@ struct EVENGINE_API SnapshotEnvelope { * @param hashProvider Injected digest provider used when the envelope was sealed. * @return Success when the computed digest equals contentHash. */ -[[nodiscard]] EVENGINE_API Result verifySnapshotEnvelope( - const SnapshotEnvelope& snapshot, const SnapshotHashProvider& hashProvider); +[[nodiscard]] EVENGINE_API Result verifySnapshotEnvelope(const SnapshotEnvelope& snapshot, + const SnapshotHashProvider& hashProvider); /** * @brief Validate optional payload copies of envelope revision and tick. @@ -98,8 +100,8 @@ struct EVENGINE_API SnapshotEnvelope { * helper gives legacy payloads one uniform compatibility rule before * any consumer state is mutated. */ -[[nodiscard]] EVENGINE_API Result validateSnapshotPayloadMetadata( - const Value& payload, Revision revision, SimulationTick tick); +[[nodiscard]] EVENGINE_API Result validateSnapshotPayloadMetadata(const Value& payload, Revision revision, + SimulationTick tick); /** * @brief Convert an envelope to its strict canonical value representation. @@ -132,8 +134,8 @@ struct EVENGINE_API SnapshotEnvelope { * @param hashProvider Provider used to verify contentHash. * @return A verified envelope, or a parse/version/hash failure. */ -[[nodiscard]] EVENGINE_API Result parseSnapshotEnvelope( - std::string_view json, const SnapshotHashProvider& hashProvider); +[[nodiscard]] EVENGINE_API Result parseSnapshotEnvelope(std::string_view json, + const SnapshotHashProvider& hashProvider); /** * @brief One directed payload migration step. @@ -153,8 +155,7 @@ public: * @param migration Pure function that returns a new owning payload. * @return Success, or Conflict/InvalidArgument when the edge is invalid or duplicated. */ - [[nodiscard]] Result add(LogicalId schema, SchemaVersion from, SchemaVersion to, - Migration migration); + [[nodiscard]] Result add(LogicalId schema, SchemaVersion from, SchemaVersion to, Migration migration); /** * @brief Migrate and reseal an envelope to an exact target version. @@ -164,18 +165,16 @@ public: * @return Migrated envelope, or UnknownVersion/Unsupported/failure. * @remarks If source is newer than target, it is rejected as an unknown new version. */ - [[nodiscard]] Result migrate( - SnapshotEnvelope snapshot, SchemaVersion targetVersion, - const SnapshotHashProvider& hashProvider) const; + [[nodiscard]] Result migrate(SnapshotEnvelope snapshot, SchemaVersion targetVersion, + const SnapshotHashProvider& hashProvider) const; private: struct Key { - std::string schema; + std::string schema; std::uint64_t from = 0; friend bool operator<(const Key& left, const Key& right) noexcept { - return left.schema < right.schema || - (left.schema == right.schema && left.from < right.from); + return left.schema < right.schema || (left.schema == right.schema && left.from < right.from); } }; diff --git a/src/engine/common/SquirrelBinding.cpp b/src/engine/common/SquirrelBinding.cpp index 8acdbf6f..969fe078 100644 --- a/src/engine/common/SquirrelBinding.cpp +++ b/src/engine/common/SquirrelBinding.cpp @@ -15,12 +15,12 @@ namespace { struct ConversionContext { const SquirrelValueOptions& options; - std::size_t elements = 0; - std::vector activeContainers; + std::size_t elements = 0; + std::vector activeContainers; }; -Diagnostic conversionError(const ConversionContext& context, DiagnosticCode code, - std::string message, std::string path) { +Diagnostic conversionError(const ConversionContext& context, DiagnosticCode code, std::string message, + std::string path) { return Diagnostic::error(code, std::move(message), std::move(path), {}, context.options.source); } @@ -29,29 +29,26 @@ std::string childPath(std::string_view parent, std::size_t index) { } std::string memberPath(std::string_view parent, std::string_view key) { - if (!key.empty() && (std::isalpha(static_cast(key.front())) != 0 || - key.front() == '_') && - std::all_of(key.begin() + 1, key.end(), [](unsigned char value) { - return std::isalnum(value) != 0 || value == '_'; - })) { + if (!key.empty() && (std::isalpha(static_cast(key.front())) != 0 || key.front() == '_') && + std::all_of(key.begin() + 1, key.end(), + [](unsigned char value) { return std::isalnum(value) != 0 || value == '_'; })) { return std::string(parent) + "." + std::string(key); } return std::string(parent) + "[\"" + std::string(key) + "\"]"; } -bool takeObject(HSQUIRRELVM vm, SQInteger index, HSQOBJECT& object, - Diagnostic& diagnostic, const ConversionContext& context, - const std::string& path) { +bool takeObject(HSQUIRRELVM vm, SQInteger index, HSQOBJECT& object, Diagnostic& diagnostic, + const ConversionContext& context, const std::string& path) { if (!vm || SQ_FAILED(sq_getstackobj(vm, index, &object))) { - diagnostic = conversionError(context, DiagnosticCode::InvalidArgument, - "Squirrel stack value is unavailable", path); + diagnostic = + conversionError(context, DiagnosticCode::InvalidArgument, "Squirrel stack value is unavailable", path); return false; } return true; } -bool enterContainer(const HSQOBJECT& object, ConversionContext& context, - Diagnostic& diagnostic, const std::string& path) { +bool enterContainer(const HSQOBJECT& object, ConversionContext& context, Diagnostic& diagnostic, + const std::string& path) { if (context.activeContainers.size() >= context.options.maxDepth) { diagnostic = conversionError(context, DiagnosticCode::InvalidArgument, "Squirrel value exceeds maximum conversion depth", path); @@ -62,9 +59,8 @@ bool enterContainer(const HSQOBJECT& object, ConversionContext& context, identity = static_cast(object._unVal.pArray); else if (object._type == OT_TABLE) identity = static_cast(object._unVal.pTable); - if (identity && std::find(context.activeContainers.begin(), - context.activeContainers.end(), identity) != - context.activeContainers.end()) { + if (identity && std::find(context.activeContainers.begin(), context.activeContainers.end(), identity) != + context.activeContainers.end()) { diagnostic = conversionError(context, DiagnosticCode::InvalidArgument, "cyclic Squirrel container cannot become an owning Value", path); return false; @@ -74,14 +70,11 @@ bool enterContainer(const HSQOBJECT& object, ConversionContext& context, } void leaveContainer(const HSQOBJECT& object, ConversionContext& context) { - if ((object._type != OT_ARRAY && object._type != OT_TABLE) || - context.activeContainers.empty()) - return; + if ((object._type != OT_ARRAY && object._type != OT_TABLE) || context.activeContainers.empty()) return; context.activeContainers.pop_back(); } -bool countElement(ConversionContext& context, Diagnostic& diagnostic, - const std::string& path) { +bool countElement(ConversionContext& context, Diagnostic& diagnostic, const std::string& path) { if (context.elements >= context.options.maxElements) { diagnostic = conversionError(context, DiagnosticCode::InvalidArgument, "Squirrel value exceeds maximum element count", path); @@ -91,69 +84,66 @@ bool countElement(ConversionContext& context, Diagnostic& diagnostic, return true; } -bool convertAt(HSQUIRRELVM vm, SQInteger index, ConversionContext& context, - const std::string& path, Value& output, Diagnostic& diagnostic) { +bool convertAt(HSQUIRRELVM vm, SQInteger index, ConversionContext& context, const std::string& path, Value& output, + Diagnostic& diagnostic) { if (!countElement(context, diagnostic, path)) return false; const SQObjectType type = sq_gettype(vm, index); switch (type) { - case OT_NULL: - output = Value(); - return true; - case OT_BOOL: { - SQBool value = SQFalse; - if (SQ_FAILED(sq_getbool(vm, index, &value))) { - diagnostic = conversionError(context, DiagnosticCode::ParseError, - "failed to read Squirrel boolean", path); - return false; + case OT_NULL: output = Value(); return true; + case OT_BOOL: { + SQBool value = SQFalse; + if (SQ_FAILED(sq_getbool(vm, index, &value))) { + diagnostic = + conversionError(context, DiagnosticCode::ParseError, "failed to read Squirrel boolean", path); + return false; + } + output = Value(value != SQFalse); + return true; } - output = Value(value != SQFalse); - return true; - } - case OT_INTEGER: { - SQInteger value = 0; - if (SQ_FAILED(sq_getinteger(vm, index, &value))) { - diagnostic = conversionError(context, DiagnosticCode::ParseError, - "failed to read Squirrel integer", path); - return false; + case OT_INTEGER: { + SQInteger value = 0; + if (SQ_FAILED(sq_getinteger(vm, index, &value))) { + diagnostic = + conversionError(context, DiagnosticCode::ParseError, "failed to read Squirrel integer", path); + return false; + } + output = Value(static_cast(value)); + return true; } - output = Value(static_cast(value)); - return true; - } - case OT_FLOAT: { - SQFloat value = 0; - if (SQ_FAILED(sq_getfloat(vm, index, &value))) { - diagnostic = conversionError(context, DiagnosticCode::ParseError, - "failed to read Squirrel number", path); - return false; + case OT_FLOAT: { + SQFloat value = 0; + if (SQ_FAILED(sq_getfloat(vm, index, &value))) { + diagnostic = + conversionError(context, DiagnosticCode::ParseError, "failed to read Squirrel number", path); + return false; + } + const double converted = static_cast(value); + if (!std::isfinite(converted)) { + diagnostic = conversionError(context, DiagnosticCode::InvalidArgument, + "non-finite Squirrel number is not representable", path); + return false; + } + output = Value(converted); + return true; } - const double converted = static_cast(value); - if (!std::isfinite(converted)) { - diagnostic = conversionError(context, DiagnosticCode::InvalidArgument, - "non-finite Squirrel number is not representable", path); - return false; + case OT_STRING: { + const SQChar* value = nullptr; + SQInteger size = 0; + if (SQ_FAILED(sq_getstringandsize(vm, index, &value, &size)) || !value || size < 0) { + diagnostic = + conversionError(context, DiagnosticCode::ParseError, "failed to read Squirrel string", path); + return false; + } + output = Value(std::string(value, static_cast(size))); + return true; } - output = Value(converted); - return true; - } - case OT_STRING: { - const SQChar* value = nullptr; - SQInteger size = 0; - if (SQ_FAILED(sq_getstringandsize(vm, index, &value, &size)) || !value || size < 0) { - diagnostic = conversionError(context, DiagnosticCode::ParseError, - "failed to read Squirrel string", path); + case OT_ARRAY: + case OT_TABLE: break; + default: + diagnostic = conversionError(context, DiagnosticCode::Unsupported, + "Squirrel value kind is not part of eve::Value", path); return false; - } - output = Value(std::string(value, static_cast(size))); - return true; - } - case OT_ARRAY: - case OT_TABLE: - break; - default: - diagnostic = conversionError(context, DiagnosticCode::Unsupported, - "Squirrel value kind is not part of eve::Value", path); - return false; } HSQOBJECT object{}; @@ -164,8 +154,8 @@ bool convertAt(HSQUIRRELVM vm, SQInteger index, ConversionContext& context, if (type == OT_ARRAY) { const SQInteger size = sq_getsize(vm, index); if (size < 0) { - diagnostic = conversionError(context, DiagnosticCode::ParseError, - "failed to read Squirrel array size", path); + diagnostic = + conversionError(context, DiagnosticCode::ParseError, "failed to read Squirrel array size", path); converted = false; } else { Value::Array values; @@ -174,46 +164,44 @@ bool convertAt(HSQUIRRELVM vm, SQInteger index, ConversionContext& context, for (SQInteger i = 0; converted && i < size; ++i) { sq_pushinteger(vm, i); if (SQ_FAILED(sq_get(vm, absolute))) { - diagnostic = conversionError(context, DiagnosticCode::ParseError, - "failed to read Squirrel array element", - childPath(path, static_cast(i))); + diagnostic = + conversionError(context, DiagnosticCode::ParseError, "failed to read Squirrel array element", + childPath(path, static_cast(i))); converted = false; break; } Value element; - converted = convertAt(vm, -1, context, - childPath(path, static_cast(i)), element, - diagnostic); + converted = + convertAt(vm, -1, context, childPath(path, static_cast(i)), element, diagnostic); sq_pop(vm, 1); if (converted) values.push_back(std::move(element)); } if (converted) output = Value(std::move(values)); } } else { - Value::Object fields; + Value::Object fields; const SQInteger absolute = index > 0 ? index : sq_gettop(vm) + index + 1; sq_pushnull(vm); while (converted && SQ_SUCCEEDED(sq_next(vm, absolute))) { if (sq_gettype(vm, -2) != OT_STRING) { diagnostic = conversionError(context, DiagnosticCode::Unsupported, - "Squirrel table keys must be strings", - path + "[]"); - converted = false; + "Squirrel table keys must be strings", path + "[]"); + converted = false; sq_pop(vm, 2); break; } - const SQChar* key = nullptr; - SQInteger keySize = 0; + const SQChar* key = nullptr; + SQInteger keySize = 0; if (SQ_FAILED(sq_getstringandsize(vm, -2, &key, &keySize)) || !key || keySize < 0) { - diagnostic = conversionError(context, DiagnosticCode::ParseError, - "failed to read Squirrel table key", path); + diagnostic = + conversionError(context, DiagnosticCode::ParseError, "failed to read Squirrel table key", path); converted = false; sq_pop(vm, 2); break; } const std::string keyText(key, static_cast(keySize)); const std::string field = memberPath(path, keyText); - Value value; + Value value; converted = convertAt(vm, -1, context, field, value, diagnostic); sq_pop(vm, 2); if (converted) fields.emplace(keyText, std::move(value)); @@ -225,33 +213,28 @@ bool convertAt(HSQUIRRELVM vm, SQInteger index, ConversionContext& context, return converted; } -bool validateValue(const Value& value, const SquirrelValueOptions& options, - std::size_t depth, std::size_t& elements, const std::string& path, - Diagnostic& diagnostic) { +bool validateValue(const Value& value, const SquirrelValueOptions& options, std::size_t depth, std::size_t& elements, + const std::string& path, Diagnostic& diagnostic) { if (elements >= options.maxElements) { - diagnostic = Diagnostic::error(DiagnosticCode::InvalidArgument, - "Value exceeds maximum Squirrel element count", path, - {}, options.source); + diagnostic = Diagnostic::error(DiagnosticCode::InvalidArgument, "Value exceeds maximum Squirrel element count", + path, {}, options.source); return false; } ++elements; if (value.isDouble() && !std::isfinite(value.asDouble())) { - diagnostic = Diagnostic::error(DiagnosticCode::InvalidArgument, - "non-finite Value cannot be pushed to Squirrel", path, - {}, options.source); + diagnostic = Diagnostic::error(DiagnosticCode::InvalidArgument, "non-finite Value cannot be pushed to Squirrel", + path, {}, options.source); return false; } if (!value.isArray() && !value.isObject()) return true; if (depth >= options.maxDepth) { diagnostic = Diagnostic::error(DiagnosticCode::InvalidArgument, - "Value exceeds maximum Squirrel conversion depth", path, - {}, options.source); + "Value exceeds maximum Squirrel conversion depth", path, {}, options.source); return false; } if (value.isArray()) { for (std::size_t index = 0; index < value.arraySize(); ++index) { - if (!validateValue(value.at(index), options, depth + 1, elements, - childPath(path, index), diagnostic)) + if (!validateValue(value.at(index), options, depth + 1, elements, childPath(path, index), diagnostic)) return false; } return true; @@ -259,15 +242,13 @@ bool validateValue(const Value& value, const SquirrelValueOptions& options, for (const std::string& key : value.keys()) { const Value* member = value.find(key); if (!member) continue; - if (!validateValue(*member, options, depth + 1, elements, - memberPath(path, key), diagnostic)) - return false; + if (!validateValue(*member, options, depth + 1, elements, memberPath(path, key), diagnostic)) return false; } return true; } -bool pushImpl(HSQUIRRELVM vm, const Value& value, const SquirrelValueOptions& options, - std::size_t depth, const std::string& path, Diagnostic& diagnostic) { +bool pushImpl(HSQUIRRELVM vm, const Value& value, const SquirrelValueOptions& options, std::size_t depth, + const std::string& path, Diagnostic& diagnostic) { if (value.isNull()) { sq_pushnull(vm); return true; @@ -280,9 +261,8 @@ bool pushImpl(HSQUIRRELVM vm, const Value& value, const SquirrelValueOptions& op const auto integer = value.asInt(); if (integer < static_cast(std::numeric_limits::min()) || integer > static_cast(std::numeric_limits::max())) { - diagnostic = Diagnostic::error(DiagnosticCode::InvalidArgument, - "Int64 value does not fit Squirrel integer", path, - {}, options.source); + diagnostic = Diagnostic::error(DiagnosticCode::InvalidArgument, "Int64 value does not fit Squirrel integer", + path, {}, options.source); return false; } sq_pushinteger(vm, static_cast(integer)); @@ -299,20 +279,17 @@ bool pushImpl(HSQUIRRELVM vm, const Value& value, const SquirrelValueOptions& op } if (depth >= options.maxDepth) { diagnostic = Diagnostic::error(DiagnosticCode::InvalidArgument, - "Value exceeds maximum Squirrel conversion depth", path, - {}, options.source); + "Value exceeds maximum Squirrel conversion depth", path, {}, options.source); return false; } if (value.isArray()) { sq_newarray(vm, 0); for (std::size_t index = 0; index < value.arraySize(); ++index) { - if (!pushImpl(vm, value.at(index), options, depth + 1, - childPath(path, index), diagnostic)) - return false; + if (!pushImpl(vm, value.at(index), options, depth + 1, childPath(path, index), diagnostic)) return false; if (SQ_FAILED(sq_arrayappend(vm, -2))) { - diagnostic = Diagnostic::error(DiagnosticCode::Failed, - "failed to append Value array element to Squirrel", - childPath(path, index), {}, options.source); + diagnostic = + Diagnostic::error(DiagnosticCode::Failed, "failed to append Value array element to Squirrel", + childPath(path, index), {}, options.source); return false; } } @@ -323,12 +300,9 @@ bool pushImpl(HSQUIRRELVM vm, const Value& value, const SquirrelValueOptions& op const Value* member = value.find(key); if (!member) continue; sq_pushstring(vm, key.c_str(), static_cast(key.size())); - if (!pushImpl(vm, *member, options, depth + 1, - memberPath(path, key), diagnostic)) - return false; + if (!pushImpl(vm, *member, options, depth + 1, memberPath(path, key), diagnostic)) return false; if (SQ_FAILED(sq_newslot(vm, -3, SQFalse))) { - diagnostic = Diagnostic::error(DiagnosticCode::Failed, - "failed to add Value object member to Squirrel", + diagnostic = Diagnostic::error(DiagnosticCode::Failed, "failed to add Value object member to Squirrel", memberPath(path, key), {}, options.source); return false; } @@ -347,8 +321,8 @@ ssq::Object objectFromTop(HSQUIRRELVM vm, SQInteger top) { ssq::Object projectValueObject(HSQUIRRELVM vm, const Value& value) { if (!vm) return ssq::Object(); - const SQInteger top = sq_gettop(vm); - auto pushed = pushValue(vm, value); + const SQInteger top = sq_gettop(vm); + auto pushed = pushValue(vm, value); if (!pushed.ok()) { pushed.ignore("Result projection could not push its value payload"); sq_settop(vm, top); @@ -365,27 +339,25 @@ bool readBoolField(const ssq::Object& object, const char* name) { } // namespace -Result valueFromSquirrel(HSQUIRRELVM vm, SQInteger index, - const SquirrelValueOptions& options) { +Result valueFromSquirrel(HSQUIRRELVM vm, SQInteger index, const SquirrelValueOptions& options) { if (!vm) - return Result::failure(Diagnostic::error( - DiagnosticCode::InvalidArgument, "Squirrel VM must not be null", "$", {}, options.source)); - const SQInteger top = sq_gettop(vm); + return Result::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "Squirrel VM must not be null", + "$", {}, options.source)); + const SQInteger top = sq_gettop(vm); ConversionContext context{options, 0, {}}; - Value output; - Diagnostic diagnostic; - const bool converted = convertAt(vm, index, context, "$", output, diagnostic); + Value output; + Diagnostic diagnostic; + const bool converted = convertAt(vm, index, context, "$", output, diagnostic); sq_settop(vm, top); if (!converted) return Result::failure(std::move(diagnostic)); return Result::success(std::move(output)); } -Result valueFromSquirrel(const ssq::Object& object, - const SquirrelValueOptions& options) { +Result valueFromSquirrel(const ssq::Object& object, const SquirrelValueOptions& options) { const HSQUIRRELVM vm = object.getHandle(); if (!vm || object.isEmpty()) - return Result::failure(Diagnostic::error( - DiagnosticCode::InvalidArgument, "Squirrel object is empty", "$", {}, options.source)); + return Result::failure( + Diagnostic::error(DiagnosticCode::InvalidArgument, "Squirrel object is empty", "$", {}, options.source)); const SQInteger top = sq_gettop(vm); sq_pushobject(vm, object.getRaw()); auto result = valueFromSquirrel(vm, -1, options); @@ -394,13 +366,12 @@ Result valueFromSquirrel(const ssq::Object& object, return result; } -Result pushValue(HSQUIRRELVM vm, const Value& value, - const SquirrelValueOptions& options) { +Result pushValue(HSQUIRRELVM vm, const Value& value, const SquirrelValueOptions& options) { if (!vm) - return Result::failure(Diagnostic::error( - DiagnosticCode::InvalidArgument, "Squirrel VM must not be null", "$", {}, options.source)); + return Result::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "Squirrel VM must not be null", + "$", {}, options.source)); std::size_t elements = 0; - Diagnostic diagnostic; + Diagnostic diagnostic; if (!validateValue(value, options, 0, elements, "$", diagnostic)) return Result::failure(std::move(diagnostic)); @@ -417,10 +388,10 @@ ssq::Table projectDiagnostic(HSQUIRRELVM vm, const Diagnostic& diagnostic) { result.set("code", std::string(diagnosticCodeName(diagnostic.code()))); result.set("severity", [&diagnostic] { switch (diagnostic.severity()) { - case Severity::Info: return std::string("info"); - case Severity::Warning: return std::string("warning"); - case Severity::Error: return std::string("error"); - case Severity::Fatal: return std::string("fatal"); + case Severity::Info: return std::string("info"); + case Severity::Warning: return std::string("warning"); + case Severity::Error: return std::string("error"); + case Severity::Fatal: return std::string("fatal"); } return std::string("unknown"); }()); @@ -439,15 +410,13 @@ ssq::Table projectStatus(HSQUIRRELVM vm, const Status& status) { result.set("code", std::string(statusCodeName(status.code()))); result.set("summary", status.describe()); ssq::Array diagnostics(vm); - for (const Diagnostic& diagnostic : status.diagnostics()) - diagnostics.push(projectDiagnostic(vm, diagnostic)); + for (const Diagnostic& diagnostic : status.diagnostics()) diagnostics.push(projectDiagnostic(vm, diagnostic)); result.set("diagnostics", diagnostics); result.set("diagnosticCount", static_cast(status.diagnostics().size())); return result; } -ssq::Table projectStatusResult(HSQUIRRELVM vm, const Status& status, - bool ok, bool hasValue, const Value& value) { +ssq::Table projectStatusResult(HSQUIRRELVM vm, const Status& status, bool ok, bool hasValue, const Value& value) { ssq::Table result(vm); result.set("ok", ok); result.set("code", std::string(statusCodeName(status.code()))); @@ -457,16 +426,17 @@ ssq::Table projectStatusResult(HSQUIRRELVM vm, const Status& status, result.set("ignoreReason", std::string{}); result.set("status", projectStatus(vm, status)); ssq::Array diagnostics(vm); - for (const Diagnostic& diagnostic : status.diagnostics()) - diagnostics.push(projectDiagnostic(vm, diagnostic)); + for (const Diagnostic& diagnostic : status.diagnostics()) diagnostics.push(projectDiagnostic(vm, diagnostic)); result.set("diagnostics", diagnostics); - if (hasValue) result.set("value", projectValueObject(vm, value)); - else result.set("value", ssq::Object(vm)); + if (hasValue) + result.set("value", projectValueObject(vm, value)); + else + result.set("value", ssq::Object(vm)); return result; } ssq::Table projectResult(HSQUIRRELVM vm, Result&& result) { - const bool ok = result.ok(); + const bool ok = result.ok(); const Status status = result.status(); return projectStatusResult(vm, status, ok, false); } @@ -503,15 +473,9 @@ bool ignoreResult(const ssq::Object& result, const std::string& reason) { void exposeResultBindings(ssq::Table& eveTable) { ssq::Table result = eveTable.addTable("result"); - result.addFunc("ignore", [](ssq::Object value, const std::string& reason) { - return ignoreResult(value, reason); - }); - result.addFunc("isChecked", [](ssq::Object value) { - return readBoolField(value, "checked"); - }); - result.addFunc("isIgnored", [](ssq::Object value) { - return readBoolField(value, "ignored"); - }); + result.addFunc("ignore", [](ssq::Object value, const std::string& reason) { return ignoreResult(value, reason); }); + result.addFunc("isChecked", [](ssq::Object value) { return readBoolField(value, "checked"); }); + result.addFunc("isIgnored", [](ssq::Object value) { return readBoolField(value, "ignored"); }); } } // namespace eve::script diff --git a/src/engine/common/SquirrelBinding.h b/src/engine/common/SquirrelBinding.h index 92a0c7b6..07b39a74 100644 --- a/src/engine/common/SquirrelBinding.h +++ b/src/engine/common/SquirrelBinding.h @@ -14,8 +14,8 @@ #include "common/Result.h" #include "common/Value.h" -#include #include +#include #include #include @@ -33,9 +33,9 @@ namespace eve::script { * being silently coerced. */ struct EVENGINE_API SquirrelValueOptions { - std::size_t maxDepth = 64; + std::size_t maxDepth = 64; std::size_t maxElements = 100000; - std::string source = "squirrel.binding"; + std::string source = "squirrel.binding"; }; /** @@ -47,8 +47,8 @@ struct EVENGINE_API SquirrelValueOptions { * @return An owning Value, or a path-aware conversion diagnostic. * @remarks The VM stack is restored to its original height before returning. */ -[[nodiscard]] EVENGINE_API Result valueFromSquirrel( - HSQUIRRELVM vm, SQInteger index, const SquirrelValueOptions& options = {}); +[[nodiscard]] EVENGINE_API Result valueFromSquirrel(HSQUIRRELVM vm, SQInteger index, + const SquirrelValueOptions& options = {}); /** * @brief Convert a rooted Squirrel object into the canonical owning Value tree. @@ -56,8 +56,8 @@ struct EVENGINE_API SquirrelValueOptions { * @param options Recursion, element-count and diagnostic-source policy. * @return An owning Value, or a path-aware conversion diagnostic. */ -[[nodiscard]] EVENGINE_API Result valueFromSquirrel( - const ssq::Object& object, const SquirrelValueOptions& options = {}); +[[nodiscard]] EVENGINE_API Result valueFromSquirrel(const ssq::Object& object, + const SquirrelValueOptions& options = {}); /** * @brief Push a canonical Value into the active Squirrel VM. @@ -67,16 +67,14 @@ struct EVENGINE_API SquirrelValueOptions { * @return Success with exactly one value pushed, or a diagnostic; on failure * the VM stack is restored to its original height. */ -[[nodiscard]] EVENGINE_API Result pushValue( - HSQUIRRELVM vm, const Value& value, const SquirrelValueOptions& options = {}); +[[nodiscard]] EVENGINE_API Result pushValue(HSQUIRRELVM vm, const Value& value, + const SquirrelValueOptions& options = {}); /** @brief Project one Diagnostic using the common script table schema. */ -[[nodiscard]] EVENGINE_API ssq::Table projectDiagnostic( - HSQUIRRELVM vm, const Diagnostic& diagnostic); +[[nodiscard]] EVENGINE_API ssq::Table projectDiagnostic(HSQUIRRELVM vm, const Diagnostic& diagnostic); /** @brief Project one Status using the common script table schema. */ -[[nodiscard]] EVENGINE_API ssq::Table projectStatus( - HSQUIRRELVM vm, const Status& status); +[[nodiscard]] EVENGINE_API ssq::Table projectStatus(HSQUIRRELVM vm, const Status& status); /** * @brief Project an already-consumed native status and optional Value payload. @@ -87,9 +85,8 @@ struct EVENGINE_API SquirrelValueOptions { * @param value Payload to expose when `hasValue` is true. * @return A table with the stable Result projection schema. */ -[[nodiscard]] EVENGINE_API ssq::Table projectStatusResult( - HSQUIRRELVM vm, const Status& status, bool ok, bool hasValue, - const Value& value = {}); +[[nodiscard]] EVENGINE_API ssq::Table projectStatusResult(HSQUIRRELVM vm, const Status& status, bool ok, bool hasValue, + const Value& value = {}); /** * @brief Consume and project a value-bearing native Result. @@ -103,22 +100,18 @@ struct EVENGINE_API SquirrelValueOptions { * failure. It therefore never relies on a domain `lastError` slot. */ template -[[nodiscard]] ssq::Table projectResult(HSQUIRRELVM vm, Result&& result, - Projector&& projector) { - const bool hasValue = result.ok(); - const Status status = result.status(); - if (!hasValue) - return projectStatusResult(vm, status, false, false); +[[nodiscard]] ssq::Table projectResult(HSQUIRRELVM vm, Result&& result, Projector&& projector) { + const bool hasValue = result.ok(); + const Status status = result.status(); + if (!hasValue) return projectStatusResult(vm, status, false, false); T payload = std::move(result).takeValue(); - return projectStatusResult( - vm, status, true, true, - std::invoke(std::forward(projector), std::move(payload))); + return projectStatusResult(vm, status, true, true, + std::invoke(std::forward(projector), std::move(payload))); } /** @brief Consume and project a void native Result using the common schema. */ -[[nodiscard]] EVENGINE_API ssq::Table projectResult( - HSQUIRRELVM vm, Result&& result); +[[nodiscard]] EVENGINE_API ssq::Table projectResult(HSQUIRRELVM vm, Result&& result); /** * @brief Mark a projected Result table as intentionally ignored by script. @@ -126,8 +119,7 @@ template * @param reason Non-empty reason retained in `ignoreReason`. * @return True when the table was marked; false for a non-table or empty reason. */ -[[nodiscard]] EVENGINE_API bool ignoreResult( - const ssq::Object& result, const std::string& reason); +[[nodiscard]] EVENGINE_API bool ignoreResult(const ssq::Object& result, const std::string& reason); /** * @brief Expose the common script helpers under `eve.result`. diff --git a/src/engine/common/SquirrelOwnership.h b/src/engine/common/SquirrelOwnership.h index 1669e7cb..5e6a2c55 100644 --- a/src/engine/common/SquirrelOwnership.h +++ b/src/engine/common/SquirrelOwnership.h @@ -10,8 +10,8 @@ #include "common/RuntimeHandle.h" #include "common/Value.h" -#include #include +#include #include #include @@ -20,8 +20,8 @@ #include #include #include -#include #include +#include #include #include @@ -101,7 +101,7 @@ public: constexpr T& operator*() const noexcept { return *value_; } private: - T* value_ = nullptr; + T* value_ = nullptr; std::uint64_t ownerEpoch_ = 0; }; @@ -117,8 +117,7 @@ using ScriptValue = eve::Value; class EVENGINE_API BorrowedSquirrelObject { public: constexpr BorrowedSquirrelObject() noexcept = default; - explicit constexpr BorrowedSquirrelObject(const ssq::Object* object) noexcept - : object_(object) {} + explicit constexpr BorrowedSquirrelObject(const ssq::Object* object) noexcept : object_(object) {} /** * @brief Returns the borrowed object, or `nullptr` when unbound. @@ -133,17 +132,14 @@ public: */ [[nodiscard]] constexpr const ssq::Object* get() const noexcept { return object_; } /** @brief Returns whether the view is bound to a non-empty object. */ - [[nodiscard]] bool isBound() const noexcept { - return object_ != nullptr && !object_->isEmpty(); - } + [[nodiscard]] bool isBound() const noexcept { return object_ != nullptr && !object_->isEmpty(); } private: const ssq::Object* object_ = nullptr; }; /** @brief Creates a one-call Borrowed view without retaining a VM reference. */ -[[nodiscard]] inline BorrowedSquirrelObject borrowSquirrelObject( - const ssq::Object& object) noexcept { +[[nodiscard]] inline BorrowedSquirrelObject borrowSquirrelObject(const ssq::Object& object) noexcept { return BorrowedSquirrelObject(&object); } @@ -157,14 +153,13 @@ private: class EVENGINE_API OwnedSquirrelObject { public: OwnedSquirrelObject() = default; - explicit OwnedSquirrelObject(ssq::Object object) noexcept - : object_(std::move(object)) {} + explicit OwnedSquirrelObject(ssq::Object object) noexcept : object_(std::move(object)) {} - OwnedSquirrelObject(const OwnedSquirrelObject&) = delete; - OwnedSquirrelObject& operator=(const OwnedSquirrelObject&) = delete; - OwnedSquirrelObject(OwnedSquirrelObject&&) noexcept = default; + OwnedSquirrelObject(const OwnedSquirrelObject&) = delete; + OwnedSquirrelObject& operator=(const OwnedSquirrelObject&) = delete; + OwnedSquirrelObject(OwnedSquirrelObject&&) noexcept = default; OwnedSquirrelObject& operator=(OwnedSquirrelObject&&) noexcept = default; - ~OwnedSquirrelObject() = default; + ~OwnedSquirrelObject() = default; /** @brief Returns the rooted object without transferring ownership. */ [[nodiscard]] const ssq::Object& get() const noexcept { return object_; } @@ -182,16 +177,13 @@ private: * @param borrowed Object valid for the duration of this call. * @return A move-only owner, or a structured invalid-argument failure. */ -[[nodiscard]] inline eve::Result ownSquirrelObject( - BorrowedSquirrelObject borrowed) { +[[nodiscard]] inline eve::Result ownSquirrelObject(BorrowedSquirrelObject borrowed) { if (!borrowed.isBound()) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "cannot own an empty or unbound Squirrel object", {}, {}, - "squirrel.ownership")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "cannot own an empty or unbound Squirrel object", {}, {}, "squirrel.ownership")); } - return eve::Result::success( - OwnedSquirrelObject(*borrowed.get())); + return eve::Result::success(OwnedSquirrelObject(*borrowed.get())); } /** @@ -204,17 +196,13 @@ private: */ template struct RuntimeHandleRef { - RuntimeHandle handle = RuntimeHandle::invalid(); - std::uint64_t ownerEpoch = 0; + RuntimeHandle handle = RuntimeHandle::invalid(); + std::uint64_t ownerEpoch = 0; /** @brief Returns whether both the handle and its registry epoch are set. */ - [[nodiscard]] constexpr bool isValid() const noexcept { - return handle.isValid() && ownerEpoch != 0; - } + [[nodiscard]] constexpr bool isValid() const noexcept { return handle.isValid() && ownerEpoch != 0; } /** @brief Returns the packed slot/generation projection. */ - [[nodiscard]] constexpr std::uint64_t packed() const noexcept { - return handle.packed(); - } + [[nodiscard]] constexpr std::uint64_t packed() const noexcept { return handle.packed(); } friend constexpr bool operator==(const RuntimeHandleRef&, const RuntimeHandleRef&) noexcept = default; }; @@ -231,13 +219,13 @@ template class RuntimeObjectRegistry { public: using Handle = RuntimeHandle; - using Ref = RuntimeHandleRef; + using Ref = RuntimeHandleRef; /** @brief Creates an empty registry with a unique owner-lifetime epoch. */ RuntimeObjectRegistry() : ownerEpoch_(nextEpoch()) {} - RuntimeObjectRegistry(const RuntimeObjectRegistry&) = delete; - RuntimeObjectRegistry& operator=(const RuntimeObjectRegistry&) = delete; - RuntimeObjectRegistry(RuntimeObjectRegistry&&) noexcept = default; + RuntimeObjectRegistry(const RuntimeObjectRegistry&) = delete; + RuntimeObjectRegistry& operator=(const RuntimeObjectRegistry&) = delete; + RuntimeObjectRegistry(RuntimeObjectRegistry&&) noexcept = default; RuntimeObjectRegistry& operator=(RuntimeObjectRegistry&&) noexcept = default; /** @@ -247,21 +235,19 @@ public: */ [[nodiscard]] eve::Result emplace(Owned object) { if (!object) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "runtime registry cannot own a null object", {}, {}, - "runtime.registry")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "runtime registry cannot own a null object", {}, {}, + "runtime.registry")); } try { - std::uint32_t index = Handle::invalidIndex; - bool appended = false; + std::uint32_t index = Handle::invalidIndex; + bool appended = false; if (!freeSlots_.empty()) { index = freeSlots_.back(); } else { if (slots_.size() >= Handle::invalidIndex) { - return failure(eve::DiagnosticCode::Failed, - "runtime registry exhausted its slot index space"); + return failure(eve::DiagnosticCode::Failed, "runtime registry exhausted its slot index space"); } index = static_cast(slots_.size()); slots_.emplace_back(); @@ -272,19 +258,17 @@ public: if (slot.retired || slot.object) { if (appended) slots_.pop_back(); return failure(eve::DiagnosticCode::InvariantViolation, - "runtime registry selected an occupied slot"); + "runtime registry selected an occupied slot"); } const Handle handle(index, slot.generation); slot.object = std::move(object); - if (!freeSlots_.empty() && freeSlots_.back() == index) - freeSlots_.pop_back(); + if (!freeSlots_.empty() && freeSlots_.back() == index) freeSlots_.pop_back(); return eve::Result::success(Ref{handle, ownerEpoch_}); } catch (const std::exception& error) { return failure(eve::DiagnosticCode::Failed, - std::string("runtime registry allocation failed: ") + error.what()); + std::string("runtime registry allocation failed: ") + error.what()); } catch (...) { - return failure(eve::DiagnosticCode::Failed, - "runtime registry allocation failed"); + return failure(eve::DiagnosticCode::Failed, "runtime registry allocation failed"); } } @@ -304,9 +288,8 @@ public: const auto slotIndex = findSlotIndex(ref); if (!slotIndex) return Borrowed(); Slot& slot = slots_[*slotIndex]; - return slot.object && slot.generation == ref.handle.generation() - ? Borrowed(slot.object.get(), ownerEpoch_) - : Borrowed(); + return slot.object && slot.generation == ref.handle.generation() ? Borrowed(slot.object.get(), ownerEpoch_) + : Borrowed(); } /** * @brief Resolves a live object as a const non-owning observation. @@ -334,37 +317,36 @@ public: */ [[nodiscard]] eve::Result erase(Ref ref) { if (!ref.handle.isValid()) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "cannot erase an invalid runtime handle", {}, {}, - "runtime.registry")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "cannot erase an invalid runtime handle", {}, {}, + "runtime.registry")); } if (ref.ownerEpoch != ownerEpoch_) return staleFailure(); const auto slotIndex = findSlotIndex(ref); if (!slotIndex) return staleFailure(); Slot& slot = slots_[*slotIndex]; - if (!slot.object || slot.generation != ref.handle.generation()) - return staleFailure(); + if (!slot.object || slot.generation != ref.handle.generation()) return staleFailure(); const auto next = Handle::nextGeneration(slot.generation); if (next) { try { freeSlots_.push_back(ref.handle.index()); } catch (const std::exception& error) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, - std::string("runtime registry release bookkeeping failed: ") + error.what(), - {}, {}, "runtime.registry")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, + std::string("runtime registry release bookkeeping failed: ") + error.what(), + {}, {}, "runtime.registry")); } catch (...) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, - "runtime registry release bookkeeping failed", {}, {}, - "runtime.registry")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::Failed, + "runtime registry release bookkeeping failed", + {}, {}, "runtime.registry")); } } slot.object.reset(); - if (next) slot.generation = *next; - else slot.retired = true; + if (next) + slot.generation = *next; + else + slot.retired = true; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } @@ -377,37 +359,36 @@ public: */ [[nodiscard]] eve::Result> take(Ref ref) { if (!ref.handle.isValid()) { - return eve::Result>::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "cannot transfer an invalid runtime handle", {}, {}, - "runtime.registry")); + return eve::Result>::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "cannot transfer an invalid runtime handle", + {}, {}, "runtime.registry")); } if (ref.ownerEpoch != ownerEpoch_) return staleFailure>(); const auto slotIndex = findSlotIndex(ref); if (!slotIndex) return staleFailure>(); Slot& slot = slots_[*slotIndex]; - if (!slot.object || slot.generation != ref.handle.generation()) - return staleFailure>(); + if (!slot.object || slot.generation != ref.handle.generation()) return staleFailure>(); const auto next = Handle::nextGeneration(slot.generation); if (next) { try { freeSlots_.push_back(ref.handle.index()); } catch (const std::exception& error) { - return eve::Result>::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, - std::string("runtime registry release bookkeeping failed: ") + error.what(), - {}, {}, "runtime.registry")); + return eve::Result>::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, + std::string("runtime registry release bookkeeping failed: ") + error.what(), + {}, {}, "runtime.registry")); } catch (...) { - return eve::Result>::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, - "runtime registry release bookkeeping failed", {}, {}, - "runtime.registry")); + return eve::Result>::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "runtime registry release bookkeeping failed", + {}, {}, "runtime.registry")); } } Owned object = std::move(slot.object); - if (next) slot.generation = *next; - else slot.retired = true; + if (next) + slot.generation = *next; + else + slot.retired = true; return eve::Result>::success(std::move(object)); } @@ -441,21 +422,18 @@ public: } /** @brief Returns the non-reusable lifetime epoch of this registry. */ - [[nodiscard]] constexpr std::uint64_t ownerEpoch() const noexcept { - return ownerEpoch_; - } + [[nodiscard]] constexpr std::uint64_t ownerEpoch() const noexcept { return ownerEpoch_; } private: struct Slot { std::uint32_t generation = 1; - bool retired = false; - Owned object; + bool retired = false; + Owned object; }; template static eve::Result failure(eve::DiagnosticCode code, std::string message) { - return eve::Result::failure(eve::Diagnostic::error( - code, std::move(message), {}, {}, "runtime.registry")); + return eve::Result::failure(eve::Diagnostic::error(code, std::move(message), {}, {}, "runtime.registry")); } template @@ -465,13 +443,13 @@ private: } [[nodiscard]] std::optional findSlotIndex(Ref ref) noexcept { - if (ref.ownerEpoch != ownerEpoch_ || !ref.handle.isValid() || - ref.handle.index() >= slots_.size()) return std::nullopt; + if (ref.ownerEpoch != ownerEpoch_ || !ref.handle.isValid() || ref.handle.index() >= slots_.size()) + return std::nullopt; return ref.handle.index(); } [[nodiscard]] std::optional findSlotIndex(Ref ref) const noexcept { - if (ref.ownerEpoch != ownerEpoch_ || !ref.handle.isValid() || - ref.handle.index() >= slots_.size()) return std::nullopt; + if (ref.ownerEpoch != ownerEpoch_ || !ref.handle.isValid() || ref.handle.index() >= slots_.size()) + return std::nullopt; return ref.handle.index(); } @@ -482,9 +460,9 @@ private: } inline static std::atomic nextEpoch_{1}; - std::uint64_t ownerEpoch_; - std::vector slots_; - std::vector freeSlots_; + std::uint64_t ownerEpoch_; + std::vector slots_; + std::vector freeSlots_; }; namespace detail { @@ -505,29 +483,24 @@ SQInteger ownedInstanceReleaseHook(SQUserPointer pointer, SQInteger) noexcept { * owner and can safely cross the current native call into script storage. */ template -[[nodiscard]] eve::Result makeOwnedSquirrelInstance( - HSQUIRRELVM vm, Owned object) { +[[nodiscard]] eve::Result makeOwnedSquirrelInstance(HSQUIRRELVM vm, Owned object) { if (!vm || !object) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "owned Squirrel instance requires a VM and non-null object", {}, {}, + eve::DiagnosticCode::InvalidArgument, "owned Squirrel instance requires a VM and non-null object", {}, {}, "squirrel.ownership")); } - const SQInteger top = sq_gettop(vm); - bool transferred = false; + const SQInteger top = sq_gettop(vm); + bool transferred = false; try { - const HSQOBJECT& classObject = - ssq::detail::getClassObj(vm, typeid(T*).hash_code()); + const HSQOBJECT& classObject = ssq::detail::getClassObj(vm, typeid(T*).hash_code()); sq_pushobject(vm, classObject); if (SQ_FAILED(sq_createinstance(vm, -1))) throw ssq::RuntimeException("failed to create owned Squirrel instance"); sq_remove(vm, -2); - if (SQ_FAILED(sq_setinstanceup(vm, -1, - static_cast(object.get())))) + if (SQ_FAILED(sq_setinstanceup(vm, -1, static_cast(object.get())))) throw ssq::RuntimeException("failed to attach owned Squirrel instance"); - sq_settypetag(vm, -1, - reinterpret_cast(typeid(T*).hash_code())); + sq_settypetag(vm, -1, reinterpret_cast(typeid(T*).hash_code())); sq_setreleasehook(vm, -1, &detail::ownedInstanceReleaseHook); object.release(); transferred = true; @@ -542,20 +515,16 @@ template sq_settop(vm, top); if (!transferred) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, - std::string("owned Squirrel instance creation failed: ") + error.what(), + eve::DiagnosticCode::Failed, std::string("owned Squirrel instance creation failed: ") + error.what(), {}, {}, "squirrel.ownership")); } return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, - std::string("owned Squirrel instance rooting failed: ") + error.what(), - {}, {}, "squirrel.ownership")); + eve::DiagnosticCode::Failed, std::string("owned Squirrel instance rooting failed: ") + error.what(), {}, {}, + "squirrel.ownership")); } catch (...) { sq_settop(vm, top); return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, - "owned Squirrel instance creation failed", {}, {}, - "squirrel.ownership")); + eve::DiagnosticCode::Failed, "owned Squirrel instance creation failed", {}, {}, "squirrel.ownership")); } } diff --git a/src/engine/common/StateAccess.h b/src/engine/common/StateAccess.h index 80edc6e3..a54609eb 100644 --- a/src/engine/common/StateAccess.h +++ b/src/engine/common/StateAccess.h @@ -40,11 +40,11 @@ enum class MutationKind : std::uint8_t { * accepted by a StatePatch-backed provider; volatile providers reject it. */ struct StateMutation { - SubjectId subject; - std::string key; - eve::Value value; - MutationKind kind = MutationKind::Set; - bool persistent = false; + SubjectId subject; + std::string key; + eve::Value value; + MutationKind kind = MutationKind::Set; + bool persistent = false; }; /** @@ -79,26 +79,20 @@ struct MutationReceipt { class EVENGINE_API IStateQuery { public: static constexpr const char* capabilityName = "eve.state.IStateQuery"; - virtual ~IStateQuery() = default; + virtual ~IStateQuery() = default; /** @brief Query a generic world value. */ - [[nodiscard]] virtual std::optional value(std::string_view subject, - std::string_view key) const = 0; + [[nodiscard]] virtual std::optional value(std::string_view subject, std::string_view key) const = 0; /** @brief Query exact tag membership. */ - [[nodiscard]] virtual std::optional hasTag(std::string_view subject, - std::string_view tag) const = 0; + [[nodiscard]] virtual std::optional hasTag(std::string_view subject, std::string_view tag) const = 0; /** @brief Query a numeric or structured attribute. */ - [[nodiscard]] virtual std::optional attribute(std::string_view subject, - std::string_view key) const = 0; + [[nodiscard]] virtual std::optional attribute(std::string_view subject, std::string_view key) const = 0; /** @brief Query a resource value or marker. */ - [[nodiscard]] virtual std::optional resource(std::string_view subject, - std::string_view key) const = 0; + [[nodiscard]] virtual std::optional resource(std::string_view subject, std::string_view key) const = 0; /** @brief Query a persistent/world state value. */ - [[nodiscard]] virtual std::optional state(std::string_view subject, - std::string_view key) const = 0; + [[nodiscard]] virtual std::optional state(std::string_view subject, std::string_view key) const = 0; /** @brief Query read-only authority for a scope. */ - [[nodiscard]] virtual std::optional authority(std::string_view subject, - std::string_view scope) const = 0; + [[nodiscard]] virtual std::optional authority(std::string_view subject, std::string_view scope) const = 0; }; /** @@ -112,11 +106,11 @@ public: class EVENGINE_API IStateMutation { public: static constexpr const char* capabilityName = "eve.state.IStateMutation"; - virtual ~IStateMutation() = default; + virtual ~IStateMutation() = default; /** @brief Apply a complete mutation set atomically. */ - [[nodiscard]] virtual eve::Result apply( - std::span mutations, const MutationContext& context) = 0; + [[nodiscard]] virtual eve::Result apply(std::span mutations, + const MutationContext& context) = 0; }; } // namespace eve::state @@ -125,10 +119,10 @@ public: // interfaces such as eve::ISceneQuery while retaining the state namespace as // the canonical spelling for new code. namespace eve { -using IStateQuery = state::IStateQuery; -using IStateMutation = state::IStateMutation; -using StateMutation = state::StateMutation; -using MutationKind = state::MutationKind; +using IStateQuery = state::IStateQuery; +using IStateMutation = state::IStateMutation; +using StateMutation = state::StateMutation; +using MutationKind = state::MutationKind; using MutationContext = state::MutationContext; using MutationReceipt = state::MutationReceipt; } // namespace eve diff --git a/src/engine/common/Status.h b/src/engine/common/Status.h index d259d593..9c9184c2 100644 --- a/src/engine/common/Status.h +++ b/src/engine/common/Status.h @@ -44,34 +44,22 @@ enum class StatusCode : uint8_t { */ [[nodiscard]] constexpr std::string_view statusCodeName(StatusCode code) noexcept { switch (code) { - case StatusCode::Ok: - return "ok"; - case StatusCode::Applied: - return "applied"; - case StatusCode::NoOp: - return "no_op"; - case StatusCode::Pending: - return "pending"; - case StatusCode::Rejected: - return "rejected"; - case StatusCode::Conflict: - return "conflict"; - case StatusCode::NotFound: - return "not_found"; - case StatusCode::Unsupported: - return "unsupported"; - case StatusCode::Cancelled: - return "cancelled"; - case StatusCode::Failed: - return "failed"; + case StatusCode::Ok: return "ok"; + case StatusCode::Applied: return "applied"; + case StatusCode::NoOp: return "no_op"; + case StatusCode::Pending: return "pending"; + case StatusCode::Rejected: return "rejected"; + case StatusCode::Conflict: return "conflict"; + case StatusCode::NotFound: return "not_found"; + case StatusCode::Unsupported: return "unsupported"; + case StatusCode::Cancelled: return "cancelled"; + case StatusCode::Failed: return "failed"; } return "unknown"; } /** @brief Write the stable StatusCode spelling to a stream. */ -inline std::ostream& operator<<(std::ostream& stream, StatusCode code) { - return stream << statusCodeName(code); -} +inline std::ostream& operator<<(std::ostream& stream, StatusCode code) { return stream << statusCodeName(code); } /** * @brief Structured status and zero or more diagnostics for an operation. @@ -87,13 +75,10 @@ public: * @param code Stable operation outcome. * @param diagnostics Structured explanations and context. */ - Status(StatusCode code, std::vector diagnostics) - : code_(code), diagnostics_(std::move(diagnostics)) {} + Status(StatusCode code, std::vector diagnostics) : code_(code), diagnostics_(std::move(diagnostics)) {} /** @brief Construct a successful status with an explicit non-error outcome. */ - static Status success(StatusCode code = StatusCode::Ok) { - return Status(code, {}); - } + static Status success(StatusCode code = StatusCode::Ok) { return Status(code, {}); } /** @brief Construct a failed status with one diagnostic. */ static Status failure(StatusCode code, Diagnostic diagnostic) { @@ -113,18 +98,16 @@ public: /** @brief Whether the operation completed without a failure outcome. */ bool isSuccess() const noexcept { switch (code_) { - case StatusCode::Ok: - case StatusCode::Applied: - case StatusCode::NoOp: - case StatusCode::Pending: - return true; - case StatusCode::Rejected: - case StatusCode::Conflict: - case StatusCode::NotFound: - case StatusCode::Unsupported: - case StatusCode::Cancelled: - case StatusCode::Failed: - return false; + case StatusCode::Ok: + case StatusCode::Applied: + case StatusCode::NoOp: + case StatusCode::Pending: return true; + case StatusCode::Rejected: + case StatusCode::Conflict: + case StatusCode::NotFound: + case StatusCode::Unsupported: + case StatusCode::Cancelled: + case StatusCode::Failed: return false; } return false; } @@ -172,35 +155,27 @@ public: private: static StatusCode statusCodeFor(DiagnosticCode code) noexcept { switch (code) { - case DiagnosticCode::InvalidArgument: - case DiagnosticCode::PreconditionViolation: - return StatusCode::Rejected; - case DiagnosticCode::NotFound: - return StatusCode::NotFound; - case DiagnosticCode::AlreadyExists: - case DiagnosticCode::Conflict: - return StatusCode::Conflict; - case DiagnosticCode::Unsupported: - return StatusCode::Unsupported; - case DiagnosticCode::Cancelled: - return StatusCode::Cancelled; - case DiagnosticCode::StaleHandle: - return StatusCode::Rejected; - case DiagnosticCode::ParseError: - case DiagnosticCode::SerializationError: - case DiagnosticCode::InvariantViolation: - case DiagnosticCode::Failed: - case DiagnosticCode::HashMismatch: - case DiagnosticCode::UnknownVersion: - case DiagnosticCode::CallbackFailure: - case DiagnosticCode::DialogueNotWaitingForChoice: - case DiagnosticCode::DialogueNotWaitingForCommand: - case DiagnosticCode::DialogueConditionRejected: - case DiagnosticCode::ProcgenGroupDataInvalid: - case DiagnosticCode::None: - return StatusCode::Failed; - case DiagnosticCode::DialogueRouteNotFound: - return StatusCode::NotFound; + case DiagnosticCode::InvalidArgument: + case DiagnosticCode::PreconditionViolation: return StatusCode::Rejected; + case DiagnosticCode::NotFound: return StatusCode::NotFound; + case DiagnosticCode::AlreadyExists: + case DiagnosticCode::Conflict: return StatusCode::Conflict; + case DiagnosticCode::Unsupported: return StatusCode::Unsupported; + case DiagnosticCode::Cancelled: return StatusCode::Cancelled; + case DiagnosticCode::StaleHandle: return StatusCode::Rejected; + case DiagnosticCode::ParseError: + case DiagnosticCode::SerializationError: + case DiagnosticCode::InvariantViolation: + case DiagnosticCode::Failed: + case DiagnosticCode::HashMismatch: + case DiagnosticCode::UnknownVersion: + case DiagnosticCode::CallbackFailure: + case DiagnosticCode::DialogueNotWaitingForChoice: + case DiagnosticCode::DialogueNotWaitingForCommand: + case DiagnosticCode::DialogueConditionRejected: + case DiagnosticCode::ProcgenGroupDataInvalid: + case DiagnosticCode::None: return StatusCode::Failed; + case DiagnosticCode::DialogueRouteNotFound: return StatusCode::NotFound; } return StatusCode::Failed; } diff --git a/src/engine/common/StrongUint64.h b/src/engine/common/StrongUint64.h index f72a1470..0e6095fc 100644 --- a/src/engine/common/StrongUint64.h +++ b/src/engine/common/StrongUint64.h @@ -31,7 +31,7 @@ public: return StrongUint64(value_ + 1); } - friend constexpr bool operator==(const StrongUint64&, const StrongUint64&) noexcept = default; + friend constexpr bool operator==(const StrongUint64&, const StrongUint64&) noexcept = default; friend constexpr auto operator<=>(const StrongUint64&, const StrongUint64&) noexcept = default; private: diff --git a/src/engine/common/SubjectRef.h b/src/engine/common/SubjectRef.h index 2f48c3b3..86c246f9 100644 --- a/src/engine/common/SubjectRef.h +++ b/src/engine/common/SubjectRef.h @@ -29,9 +29,7 @@ public: SubjectRef() = default; /** @brief Wrap a persistent identity without changing its bytes. */ - [[nodiscard]] static SubjectRef fromPersistentId(PersistentId id) noexcept { - return SubjectRef(id); - } + [[nodiscard]] static SubjectRef fromPersistentId(PersistentId id) noexcept { return SubjectRef(id); } /** @brief Return the invalid nil reference. */ [[nodiscard]] static SubjectRef nil() noexcept { return {}; } diff --git a/src/engine/common/Subscription.cpp b/src/engine/common/Subscription.cpp index b5d8e8ee..29b7abba 100644 --- a/src/engine/common/Subscription.cpp +++ b/src/engine/common/Subscription.cpp @@ -4,8 +4,7 @@ namespace eve { -Subscription::Subscription(Cancel cancel) - : cancel_(std::move(cancel)), disposed_(!cancel_) {} +Subscription::Subscription(Cancel cancel) : cancel_(std::move(cancel)), disposed_(!cancel_) {} Subscription::Subscription(Subscription &&other) noexcept : cancel_(std::move(other.cancel_)), disposed_(other.disposed_) { @@ -15,8 +14,8 @@ Subscription::Subscription(Subscription &&other) noexcept Subscription &Subscription::operator=(Subscription &&other) noexcept { if (this == &other) return *this; dispose(); - cancel_ = std::move(other.cancel_); - disposed_ = other.disposed_; + cancel_ = std::move(other.cancel_); + disposed_ = other.disposed_; other.disposed_ = true; return *this; } diff --git a/src/engine/common/Subscription.h b/src/engine/common/Subscription.h index 315c1c6b..7348db2c 100644 --- a/src/engine/common/Subscription.h +++ b/src/engine/common/Subscription.h @@ -37,7 +37,7 @@ public: */ explicit Subscription(Cancel cancel); - Subscription(const Subscription &) = delete; + Subscription(const Subscription &) = delete; Subscription &operator=(const Subscription &) = delete; Subscription(Subscription &&other) noexcept; Subscription &operator=(Subscription &&other) noexcept; @@ -80,11 +80,11 @@ public: using Callback = std::function; Observer() : state_(std::make_shared()) {} - Observer(const Observer &) = delete; + Observer(const Observer &) = delete; Observer &operator=(const Observer &) = delete; - Observer(Observer &&) = delete; - Observer &operator=(Observer &&) = delete; - ~Observer() = default; + Observer(Observer &&) = delete; + Observer &operator=(Observer &&) = delete; + ~Observer() = default; /** * @brief Registers a callback owned by the returned Subscription. @@ -96,8 +96,8 @@ public: compactInactive(); if (!callback) return {}; - auto entry = std::make_shared(); - entry->id = state_->nextId++; + auto entry = std::make_shared(); + entry->id = state_->nextId++; entry->callback = std::move(callback); state_->entries.push_back(entry); @@ -105,9 +105,8 @@ public: return Subscription([weakState, entry]() noexcept { entry->active = false; if (const auto state = weakState.lock()) { - std::erase_if(state->entries, [&entry](const std::shared_ptr &candidate) { - return candidate == entry; - }); + std::erase_if(state->entries, + [&entry](const std::shared_ptr &candidate) { return candidate == entry; }); } }); } @@ -139,8 +138,8 @@ public: * next-dispatch rules as notify(). */ template - [[nodiscard]] std::size_t notifyChecked(FailureHandler&& onFailure, const Args &...args) { - const auto snapshot = state_->entries; + [[nodiscard]] std::size_t notifyChecked(FailureHandler &&onFailure, const Args &...args) { + const auto snapshot = state_->entries; std::size_t failures = 0; for (const auto &entry : snapshot) { if (!entry->active || !entry->callback) continue; @@ -166,8 +165,7 @@ public: /** @brief Removes inactive entries without invoking user callbacks. */ void compactInactive() { - std::erase_if(state_->entries, - [](const std::shared_ptr &entry) { return !entry->active; }); + std::erase_if(state_->entries, [](const std::shared_ptr &entry) { return !entry->active; }); } private: @@ -178,7 +176,7 @@ private: }; struct State { - std::uint64_t nextId = 1; + std::uint64_t nextId = 1; std::vector> entries; }; diff --git a/src/engine/common/Time.cpp b/src/engine/common/Time.cpp index 763434f0..d7d4da2d 100644 --- a/src/engine/common/Time.cpp +++ b/src/engine/common/Time.cpp @@ -14,21 +14,18 @@ Diagnostic invalidTime(std::string message) { } // namespace Result Duration::fromSeconds(double seconds) { - if (!std::isfinite(seconds)) - return Result::failure(invalidTime("duration seconds must be finite")); + if (!std::isfinite(seconds)) return Result::failure(invalidTime("duration seconds must be finite")); - const long double nanoseconds = static_cast(seconds) * 1000000000.0L; - constexpr long double minimum = static_cast(std::numeric_limits::min()); - constexpr long double maximum = static_cast(std::numeric_limits::max()); + const long double nanoseconds = static_cast(seconds) * 1000000000.0L; + constexpr long double minimum = static_cast(std::numeric_limits::min()); + constexpr long double maximum = static_cast(std::numeric_limits::max()); if (nanoseconds < minimum || nanoseconds > maximum) return Result::failure(invalidTime("duration seconds are out of range")); return Result::success(Duration(static_cast(std::round(nanoseconds)))); } -double Duration::seconds() const noexcept { - return static_cast(nanoseconds_) / 1000000000.0; -} +double Duration::seconds() const noexcept { return static_cast(nanoseconds_) / 1000000000.0; } Result Duration::tryAdd(Duration other) const { if ((other.nanoseconds_ > 0 && nanoseconds_ > std::numeric_limits::max() - other.nanoseconds_) || @@ -40,9 +37,9 @@ Result Duration::tryAdd(Duration other) const { Result Duration::scaled(double rate) const { if (!std::isfinite(rate) || rate < 0.0) return Result::failure(invalidTime("duration rate must be finite and non-negative")); - const long double nanoseconds = static_cast(nanoseconds_) * static_cast(rate); - constexpr long double minimum = static_cast(std::numeric_limits::min()); - constexpr long double maximum = static_cast(std::numeric_limits::max()); + const long double nanoseconds = static_cast(nanoseconds_) * static_cast(rate); + constexpr long double minimum = static_cast(std::numeric_limits::min()); + constexpr long double maximum = static_cast(std::numeric_limits::max()); if (nanoseconds < minimum || nanoseconds > maximum) return Result::failure(invalidTime("scaled duration is out of range")); return Result::success(Duration(static_cast(std::round(nanoseconds)))); @@ -57,16 +54,15 @@ Result MonotonicTimestamp::since(MonotonicTimestamp earlier) const { // subtraction is the exact distance for two ordered int64 values, while // the explicit bound check prevents converting a distance larger than the // Duration domain back to int64_t. - const std::uint64_t distance = static_cast(nanoseconds_) - - static_cast(earlier.nanoseconds_); + const std::uint64_t distance = + static_cast(nanoseconds_) - static_cast(earlier.nanoseconds_); if (distance > static_cast(std::numeric_limits::max())) - return Result::failure(Diagnostic::error( - DiagnosticCode::InvariantViolation, "monotonic timestamp distance exceeds Duration range")); + return Result::failure(Diagnostic::error(DiagnosticCode::InvariantViolation, + "monotonic timestamp distance exceeds Duration range")); return Result::success(Duration(static_cast(distance))); } -SimulationClock::SimulationClock(ITimeSource& source, Duration fixedStep) - : source_(source), fixedStep_(fixedStep) { +SimulationClock::SimulationClock(ITimeSource& source, Duration fixedStep) : source_(source), fixedStep_(fixedStep) { EV_ASSERT(fixedStep_.nanoseconds() > 0, "SimulationClock fixed step must be positive"); if (fixedStep_.nanoseconds() <= 0) fixedStep_ = Duration::fromNanoseconds(16666667); } @@ -100,17 +96,17 @@ Result> SimulationClock::advance(Duration realDelta) const Duration scaledDelta = std::move(scaled).takeValue(); if (paused_) { - frame_ = *nextFrame; + frame_ = *nextFrame; lastFrameDelta_ = realDelta; return Result>::success({}); } auto total = accumulator_.tryAdd(scaledDelta); if (!total) return Result>::failure(total.status()); - const std::int64_t accumulated = std::move(total).takeValue().nanoseconds(); + const std::int64_t accumulated = std::move(total).takeValue().nanoseconds(); const std::uint64_t accumulatedUnsigned = static_cast(accumulated); - const std::uint64_t fixedStepUnsigned = static_cast(fixedStep_.nanoseconds()); - const std::uint64_t stepCount = accumulatedUnsigned / fixedStepUnsigned; + const std::uint64_t fixedStepUnsigned = static_cast(fixedStep_.nanoseconds()); + const std::uint64_t stepCount = accumulatedUnsigned / fixedStepUnsigned; if (stepCount > std::numeric_limits::max() || stepCount > std::numeric_limits::max() - tick_.value()) return Result>::failure( @@ -125,7 +121,7 @@ Result> SimulationClock::advance(Duration realDelta) if (stepCount > std::numeric_limits::max() / fixedStepUnsigned) return Result>::failure( Diagnostic::error(DiagnosticCode::InvariantViolation, "simulation duration multiplication overflow")); - const std::uint64_t consumedUnsigned = stepCount * fixedStepUnsigned; + const std::uint64_t consumedUnsigned = stepCount * fixedStepUnsigned; const std::uint64_t remainderUnsigned = accumulatedUnsigned - consumedUnsigned; if (remainderUnsigned > static_cast(std::numeric_limits::max())) return Result>::failure( @@ -133,12 +129,11 @@ Result> SimulationClock::advance(Duration realDelta) std::vector steps; steps.reserve(static_cast(stepCount)); - for (std::uint64_t i = 0; i < stepCount; ++i) - steps.push_back({SimulationTick(tick_.value() + i + 1), fixedStep_}); + for (std::uint64_t i = 0; i < stepCount; ++i) steps.push_back({SimulationTick(tick_.value() + i + 1), fixedStep_}); - tick_ = SimulationTick(tick_.value() + stepCount); - accumulator_ = Duration(static_cast(remainderUnsigned)); - frame_ = *nextFrame; + tick_ = SimulationTick(tick_.value() + stepCount); + accumulator_ = Duration(static_cast(remainderUnsigned)); + frame_ = *nextFrame; lastFrameDelta_ = realDelta; return Result>::success(std::move(steps)); } @@ -154,17 +149,17 @@ Result> SimulationClock::sample() { auto elapsed = now.since(*previousSourceTime_); if (!elapsed) return Result>::failure(elapsed.status()); - Duration delta = std::move(elapsed).takeValue(); - auto result = advance(delta); + Duration delta = std::move(elapsed).takeValue(); + auto result = advance(delta); if (!result) return result; previousSourceTime_ = now; return result; } void SimulationClock::reset(SimulationTick tick) noexcept { - tick_ = tick; - frame_ = FrameIndex::zero(); - accumulator_ = Duration::zero(); + tick_ = tick; + frame_ = FrameIndex::zero(); + accumulator_ = Duration::zero(); lastFrameDelta_ = Duration::zero(); previousSourceTime_.reset(); } diff --git a/src/engine/common/Time.h b/src/engine/common/Time.h index cfdd3278..cdf1d5b0 100644 --- a/src/engine/common/Time.h +++ b/src/engine/common/Time.h @@ -85,7 +85,7 @@ public: */ [[nodiscard]] Result scaled(double rate) const; - friend constexpr bool operator==(Duration, Duration) noexcept = default; + friend constexpr bool operator==(Duration, Duration) noexcept = default; friend constexpr auto operator<=>(Duration, Duration) noexcept = default; private: @@ -119,7 +119,7 @@ public: */ [[nodiscard]] Result since(MonotonicTimestamp earlier) const; - friend constexpr bool operator==(MonotonicTimestamp, MonotonicTimestamp) noexcept = default; + friend constexpr bool operator==(MonotonicTimestamp, MonotonicTimestamp) noexcept = default; friend constexpr auto operator<=>(MonotonicTimestamp, MonotonicTimestamp) noexcept = default; private: @@ -138,13 +138,12 @@ public: constexpr WallClockTimestamp() noexcept = default; /** @brief Construct a timestamp from Unix-epoch nanoseconds. */ - explicit constexpr WallClockTimestamp(std::int64_t unixNanoseconds) noexcept - : unixNanoseconds_(unixNanoseconds) {} + explicit constexpr WallClockTimestamp(std::int64_t unixNanoseconds) noexcept : unixNanoseconds_(unixNanoseconds) {} /** @brief Return Unix-epoch nanoseconds for metadata serialization. */ [[nodiscard]] constexpr std::int64_t unixNanoseconds() const noexcept { return unixNanoseconds_; } - friend constexpr bool operator==(WallClockTimestamp, WallClockTimestamp) noexcept = default; + friend constexpr bool operator==(WallClockTimestamp, WallClockTimestamp) noexcept = default; friend constexpr auto operator<=>(WallClockTimestamp, WallClockTimestamp) noexcept = default; private: @@ -169,8 +168,8 @@ struct EVENGINE_API SimulationStep { */ class EVENGINE_API ITimeSource { public: - static constexpr const char *capabilityName = "ITimeSource"; - virtual ~ITimeSource() = default; + static constexpr const char* capabilityName = "ITimeSource"; + virtual ~ITimeSource() = default; /** * @brief Read the non-decreasing source-relative timestamp. @@ -201,8 +200,7 @@ public: * @param source Borrowed source; it must outlive this clock. * @param fixedStep Positive fixed simulation duration. */ - explicit SimulationClock(ITimeSource& source, - Duration fixedStep = Duration::fromNanoseconds(16666667)); + explicit SimulationClock(ITimeSource& source, Duration fixedStep = Duration::fromNanoseconds(16666667)); /** @brief Read the source and emit deterministic steps for one frame. */ [[nodiscard]] Result> sample(); @@ -238,14 +236,14 @@ public: [[nodiscard]] Duration lastFrameDelta() const noexcept { return lastFrameDelta_; } private: - ITimeSource& source_; - Duration fixedStep_; - Duration accumulator_; - Duration lastFrameDelta_; - double rate_ = 1.0; - SimulationTick tick_ = SimulationTick::zero(); - FrameIndex frame_ = FrameIndex::zero(); - bool paused_ = false; + ITimeSource& source_; + Duration fixedStep_; + Duration accumulator_; + Duration lastFrameDelta_; + double rate_ = 1.0; + SimulationTick tick_ = SimulationTick::zero(); + FrameIndex frame_ = FrameIndex::zero(); + bool paused_ = false; std::optional previousSourceTime_; }; diff --git a/src/engine/common/Uri.cpp b/src/engine/common/Uri.cpp index ffe216f0..f35579e9 100644 --- a/src/engine/common/Uri.cpp +++ b/src/engine/common/Uri.cpp @@ -9,12 +9,10 @@ namespace eve { namespace { [[nodiscard]] Result uriFailure(std::string message, std::string_view path = {}) { - return Result::failure( - Diagnostic::error(DiagnosticCode::ParseError, std::move(message), std::string(path))); + return Result::failure(Diagnostic::error(DiagnosticCode::ParseError, std::move(message), std::string(path))); } -[[nodiscard]] Result resourceUriFailure(std::string message, - std::string_view path = {}) { +[[nodiscard]] Result resourceUriFailure(std::string message, std::string_view path = {}) { return Result::failure( Diagnostic::error(DiagnosticCode::ParseError, std::move(message), std::string(path))); } @@ -29,16 +27,14 @@ namespace { const auto byte = static_cast(value); if (byte < 0x21 || byte > 0x7e) return false; switch (value) { - case '/': - case '_': - case '-': - case '.': - case '~': - case '+': - case '@': - return true; - default: - return std::isalnum(byte) != 0; + case '/': + case '_': + case '-': + case '.': + case '~': + case '+': + case '@': return true; + default: return std::isalnum(byte) != 0; } } @@ -59,19 +55,18 @@ namespace { [[nodiscard]] bool isKnownScheme(UriScheme scheme) noexcept { switch (scheme) { - case UriScheme::Asset: - case UriScheme::Project: - case UriScheme::Builtin: - case UriScheme::Generated: - case UriScheme::Memory: - return true; + case UriScheme::Asset: + case UriScheme::Project: + case UriScheme::Builtin: + case UriScheme::Generated: + case UriScheme::Memory: return true; } return false; } [[nodiscard]] bool validPath(std::string_view path) noexcept { if (path.empty() || path.front() == '/' || path.back() == '/') return false; - char previous = '\0'; + char previous = '\0'; std::size_t segmentStart = 0; for (std::size_t i = 0; i < path.size(); ++i) { const char value = path[i]; @@ -104,25 +99,24 @@ namespace { } std::string scheme(text.substr(0, delimiter)); - std::transform(scheme.begin(), scheme.end(), scheme.begin(), [](unsigned char value) { - return static_cast(std::tolower(value)); - }); + std::transform(scheme.begin(), scheme.end(), scheme.begin(), + [](unsigned char value) { return static_cast(std::tolower(value)); }); const auto schemeKind = schemeFromText(scheme); if (!schemeKind) return uriFailure("unsupported resource URI scheme", text); - std::string_view remainder = text.substr(delimiter + 3); - const auto fragmentDelimiter = remainder.find('#'); + std::string_view remainder = text.substr(delimiter + 3); + const auto fragmentDelimiter = remainder.find('#'); std::string_view fragment; if (fragmentDelimiter != std::string_view::npos) { - fragment = remainder.substr(fragmentDelimiter + 1); + fragment = remainder.substr(fragmentDelimiter + 1); remainder = remainder.substr(0, fragmentDelimiter); if (!validQueryOrFragment(fragment)) return uriFailure("URI fragment is empty or invalid", text); } - const auto queryDelimiter = remainder.find('?'); + const auto queryDelimiter = remainder.find('?'); std::string_view query; if (queryDelimiter != std::string_view::npos) { - query = remainder.substr(queryDelimiter + 1); + query = remainder.substr(queryDelimiter + 1); remainder = remainder.substr(0, queryDelimiter); if (!validQueryOrFragment(query)) return uriFailure("URI query is empty or invalid", text); } @@ -135,23 +129,17 @@ namespace { const char* uriSchemeName(UriScheme scheme) noexcept { switch (scheme) { - case UriScheme::Asset: - return "asset"; - case UriScheme::Project: - return "project"; - case UriScheme::Builtin: - return "builtin"; - case UriScheme::Generated: - return "generated"; - case UriScheme::Memory: - return "memory"; + case UriScheme::Asset: return "asset"; + case UriScheme::Project: return "project"; + case UriScheme::Builtin: return "builtin"; + case UriScheme::Generated: return "generated"; + case UriScheme::Memory: return "memory"; } return "unknown"; } Uri::Uri(UriScheme scheme, std::string path, std::string query, std::string fragment) - : scheme_(scheme), path_(std::move(path)), query_(std::move(query)), - fragment_(std::move(fragment)) { + : scheme_(scheme), path_(std::move(path)), query_(std::move(query)), fragment_(std::move(fragment)) { formatted_.reserve(3 + path_.size() + query_.size() + fragment_.size() + 16); formatted_ += uriSchemeName(scheme_); formatted_ += "://"; @@ -168,16 +156,13 @@ Uri::Uri(UriScheme scheme, std::string path, std::string query, std::string frag Result Uri::parse(std::string_view text) { return parseUri(text); } -Result Uri::fromParts(UriScheme scheme, std::string_view path, std::string_view query, - std::string_view fragment) { +Result Uri::fromParts(UriScheme scheme, std::string_view path, std::string_view query, std::string_view fragment) { if (!isKnownScheme(scheme)) return uriFailure("unknown resource URI scheme", path); if (!validPath(path)) return uriFailure("URI path is empty, absolute, or invalid", path); - if ((!query.empty() && !validQueryOrFragment(query)) || - (!fragment.empty() && !validQueryOrFragment(fragment))) { + if ((!query.empty() && !validQueryOrFragment(query)) || (!fragment.empty() && !validQueryOrFragment(fragment))) { return uriFailure("URI query or fragment is invalid", path); } - return Result::success(Uri(scheme, std::string(path), std::string(query), - std::string(fragment))); + return Result::success(Uri(scheme, std::string(path), std::string(query), std::string(fragment))); } Result ResourceUri::parse(std::string_view text) { @@ -189,8 +174,8 @@ Result ResourceUri::parse(std::string_view text) { return Result::success(ResourceUri(std::move(parsed).takeValue())); } -Result ResourceUri::fromParts(UriScheme scheme, std::string_view path, - std::string_view query, std::string_view fragment) { +Result ResourceUri::fromParts(UriScheme scheme, std::string_view path, std::string_view query, + std::string_view fragment) { auto parsed = Uri::fromParts(scheme, path, query, fragment); if (!parsed.ok()) { const auto* diagnostic = parsed.error(); diff --git a/src/engine/common/Uri.h b/src/engine/common/Uri.h index 36b589eb..6e32c9ce 100644 --- a/src/engine/common/Uri.h +++ b/src/engine/common/Uri.h @@ -59,9 +59,8 @@ public: * @param fragment Optional fragment component without the leading `#`. * @return A canonical URI or a structured parse diagnostic. */ - [[nodiscard]] static Result fromParts(UriScheme scheme, std::string_view path, - std::string_view query = {}, - std::string_view fragment = {}); + [[nodiscard]] static Result fromParts(UriScheme scheme, std::string_view path, std::string_view query = {}, + std::string_view fragment = {}); /** @brief Return the parsed scheme. */ [[nodiscard]] UriScheme scheme() const noexcept { return scheme_; } @@ -117,8 +116,7 @@ public: * @return A checked resource URI or a parse diagnostic. */ [[nodiscard]] static Result fromParts(UriScheme scheme, std::string_view path, - std::string_view query = {}, - std::string_view fragment = {}); + std::string_view query = {}, std::string_view fragment = {}); /** * @brief Explicitly convert a legacy project-relative path. diff --git a/src/engine/common/Value.cpp b/src/engine/common/Value.cpp index aa5b4975..55f9c1e0 100644 --- a/src/engine/common/Value.cpp +++ b/src/engine/common/Value.cpp @@ -3,8 +3,8 @@ #include "common/Json.h" #include -#include #include +#include #include #include #include @@ -20,9 +20,8 @@ Result copyJsonValue(const json::Value& source) { if (source.isNumber()) { if (source.isIntegerLiteral()) { if (!source.isInt64()) { - return Result::failure(Diagnostic::error( - DiagnosticCode::ParseError, - "JSON integer is outside the canonical Int64 range")); + return Result::failure( + Diagnostic::error(DiagnosticCode::ParseError, "JSON integer is outside the canonical Int64 range")); } return Result::success(Value(source.asInt64())); } @@ -50,19 +49,17 @@ Result copyJsonValue(const json::Value& source) { return Result::success(Value(std::move(result))); } - return Result::failure( - Diagnostic::error(DiagnosticCode::ParseError, "unsupported JSON value kind")); + return Result::failure(Diagnostic::error(DiagnosticCode::ParseError, "unsupported JSON value kind")); } } // namespace Result Value::fromJson(std::string_view jsonText) { const std::string input(jsonText); - std::string error; - json::Document document = json::Document::parse(input, &error); + std::string error; + json::Document document = json::Document::parse(input, &error); if (!document.valid()) { - return Result::failure( - Diagnostic::error(DiagnosticCode::ParseError, std::move(error))); + return Result::failure(Diagnostic::error(DiagnosticCode::ParseError, std::move(error))); } return copyJsonValue(document.root()); } @@ -152,13 +149,13 @@ std::vector Value::keys() const { std::string Value::typeName() const { switch (type()) { - case Type::Null: return "null"; - case Type::Bool: return "bool"; - case Type::Int64: return "int"; - case Type::Double: return "float"; - case Type::String: return "string"; - case Type::Array: return "array"; - case Type::Object: return "object"; + case Type::Null: return "null"; + case Type::Bool: return "bool"; + case Type::Int64: return "int"; + case Type::Double: return "float"; + case Type::String: return "string"; + case Type::Array: return "array"; + case Type::Object: return "object"; } return "null"; } diff --git a/src/engine/common/VersionedRegistry.h b/src/engine/common/VersionedRegistry.h index c442490d..23df5067 100644 --- a/src/engine/common/VersionedRegistry.h +++ b/src/engine/common/VersionedRegistry.h @@ -12,10 +12,10 @@ */ #include "common/BorrowedRef.h" -#include "common/Subscription.h" #include "common/EventSequence.h" #include "common/Generation.h" #include "common/SchemaVersion.h" +#include "common/Subscription.h" #include #include @@ -51,12 +51,9 @@ enum class RegistryOperation : std::uint8_t { */ inline const char* registryOperationName(RegistryOperation operation) noexcept { switch (operation) { - case RegistryOperation::Insert: - return "insert"; - case RegistryOperation::Replace: - return "replace"; - case RegistryOperation::Remove: - return "remove"; + case RegistryOperation::Insert: return "insert"; + case RegistryOperation::Replace: return "replace"; + case RegistryOperation::Remove: return "remove"; } return "unknown"; } @@ -93,8 +90,7 @@ inline std::ostream& operator<<(std::ostream& stream, RegistryOperation operatio * @tparam Compare Strict weak ordering for keys; its State must be * nothrow-swappable so the commit boundary cannot throw. */ -template > +template > class VersionedRegistry { static_assert(std::is_copy_constructible_v, "VersionedRegistry keys must be copyable"); static_assert(std::is_copy_constructible_v, "VersionedRegistry values must be copyable"); @@ -112,13 +108,13 @@ public: /** @brief One canonical mutation event retained by the registry. */ struct Event { - EventSequence sequence; + EventSequence sequence; RegistryOperation operation = RegistryOperation::Insert; - Key key; - Generation generation; - bool tombstone = false; - std::string label; - EventData data{}; + Key key; + Generation generation; + bool tombstone = false; + std::string label; + EventData data{}; /** @brief Whether this event advanced a key to a removed tombstone. */ [[nodiscard]] bool isTombstone() const noexcept { return tombstone; } @@ -126,7 +122,7 @@ public: /** @brief One stored key slot; absent value means the slot is a tombstone. */ struct Entry { - Generation generation; + Generation generation; std::optional value; }; @@ -147,15 +143,15 @@ public: "VersionedRegistry comparator must be nothrow-swappable"); entries.swap(other.entries); events.swap(other.events); - const auto sequence = nextEventSequence; - nextEventSequence = other.nextEventSequence; + const auto sequence = nextEventSequence; + nextEventSequence = other.nextEventSequence; other.nextEventSequence = sequence; } }; /** @brief Callback used to keep a legacy value field as a read-only projection of generation. */ using GenerationProjector = std::function; - using ChangeCallback = std::function; + using ChangeCallback = std::function; /** * @brief Constructs an empty registry. @@ -166,11 +162,11 @@ public: explicit VersionedRegistry(GenerationProjector generationProjector = {}) : generationProjector_(std::move(generationProjector)), observers_() {} - VersionedRegistry(const VersionedRegistry&) = delete; + VersionedRegistry(const VersionedRegistry&) = delete; VersionedRegistry& operator=(const VersionedRegistry&) = delete; - VersionedRegistry(VersionedRegistry&&) = delete; - VersionedRegistry& operator=(VersionedRegistry&&) = delete; - ~VersionedRegistry() = default; + VersionedRegistry(VersionedRegistry&&) = delete; + VersionedRegistry& operator=(VersionedRegistry&&) = delete; + ~VersionedRegistry() = default; /** * @brief Inserts a new live value, or revives a tombstoned key. @@ -180,18 +176,14 @@ public: * @param label Optional domain event label; the operation remains Insert. * @return A handle for the new incarnation, or Conflict/Failed. */ - [[nodiscard]] Result insert(Key key, Value value, EventData data = {}, - std::string label = {}) { + [[nodiscard]] Result insert(Key key, Value value, EventData data = {}, std::string label = {}) { try { - return mutate(RegistryOperation::Insert, std::move(key), - std::optional(std::move(value)), std::move(data), - std::move(label)); + return mutate(RegistryOperation::Insert, std::move(key), std::optional(std::move(value)), + std::move(data), std::move(label)); } catch (const std::exception&) { - return failure(DiagnosticCode::Failed, - "registry mutation argument preparation failed"); + return failure(DiagnosticCode::Failed, "registry mutation argument preparation failed"); } catch (...) { - return failure(DiagnosticCode::Failed, - "registry mutation argument preparation failed"); + return failure(DiagnosticCode::Failed, "registry mutation argument preparation failed"); } } @@ -203,18 +195,14 @@ public: * @param label Optional domain event label; the operation remains Replace. * @return A handle for the replacement incarnation, or NotFound/Failed. */ - [[nodiscard]] Result replace(Key key, Value value, EventData data = {}, - std::string label = {}) { + [[nodiscard]] Result replace(Key key, Value value, EventData data = {}, std::string label = {}) { try { - return mutate(RegistryOperation::Replace, std::move(key), - std::optional(std::move(value)), std::move(data), - std::move(label)); + return mutate(RegistryOperation::Replace, std::move(key), std::optional(std::move(value)), + std::move(data), std::move(label)); } catch (const std::exception&) { - return failure(DiagnosticCode::Failed, - "registry mutation argument preparation failed"); + return failure(DiagnosticCode::Failed, "registry mutation argument preparation failed"); } catch (...) { - return failure(DiagnosticCode::Failed, - "registry mutation argument preparation failed"); + return failure(DiagnosticCode::Failed, "registry mutation argument preparation failed"); } } @@ -227,14 +215,11 @@ public: */ [[nodiscard]] Result remove(Key key, EventData data = {}, std::string label = {}) { try { - return mutate(RegistryOperation::Remove, std::move(key), std::nullopt, - std::move(data), std::move(label)); + return mutate(RegistryOperation::Remove, std::move(key), std::nullopt, std::move(data), std::move(label)); } catch (const std::exception&) { - return failure(DiagnosticCode::Failed, - "registry mutation argument preparation failed"); + return failure(DiagnosticCode::Failed, "registry mutation argument preparation failed"); } catch (...) { - return failure(DiagnosticCode::Failed, - "registry mutation argument preparation failed"); + return failure(DiagnosticCode::Failed, "registry mutation argument preparation failed"); } } @@ -246,8 +231,7 @@ public: [[nodiscard]] ResultRef resolve(const Key& key) const { const auto it = state_.entries.find(key); if (it == state_.entries.end() || !it->second.value.has_value()) - return failure>(DiagnosticCode::NotFound, - "registry key is not live"); + return failure>(DiagnosticCode::NotFound, "registry key is not live"); return ResultRef::success(std::cref(*it->second.value)); } @@ -260,11 +244,10 @@ public: const auto it = state_.entries.find(handle.key); if (it == state_.entries.end()) return failure>(DiagnosticCode::NotFound, - "registry key is not present"); - if (handle.generation.isZero() || it->second.generation != handle.generation || - !it->second.value.has_value()) + "registry key is not present"); + if (handle.generation.isZero() || it->second.generation != handle.generation || !it->second.value.has_value()) return failure>(DiagnosticCode::StaleHandle, - "registry handle is stale"); + "registry handle is stale"); return ResultRef::success(std::cref(*it->second.value)); } @@ -302,8 +285,8 @@ public: /** @brief Returns true when a handle cannot resolve to its exact live incarnation. */ [[nodiscard]] bool isStale(const Handle& handle) const noexcept { const auto it = state_.entries.find(handle.key); - return it == state_.entries.end() || handle.generation.isZero() || - it->second.generation != handle.generation || !it->second.value.has_value(); + return it == state_.entries.end() || handle.generation.isZero() || it->second.generation != handle.generation || + !it->second.value.has_value(); } /** @brief Number of live values, excluding tombstones. */ @@ -459,9 +442,9 @@ private: // candidate. The live state is touched only by the nothrow swap // below, so Value/EventData/projector failures cannot partially // publish a registry mutation. - State candidate = state_; - auto it = candidate.entries.find(key); - const bool live = it != candidate.entries.end() && it->second.value.has_value(); + State candidate = state_; + auto it = candidate.entries.find(key); + const bool live = it != candidate.entries.end() && it->second.value.has_value(); if (operation == RegistryOperation::Insert && live) return failure(DiagnosticCode::AlreadyExists, "registry key already exists"); if (operation == RegistryOperation::Replace && !live) @@ -472,21 +455,25 @@ private: return failure(DiagnosticCode::InvariantViolation, "live registry mutation has no value"); const auto current = it == candidate.entries.end() ? nullptr : &it->second; - auto next = nextGeneration(current); + auto next = nextGeneration(current); if (!next.ok()) return Result::failure(next.status()); const auto nextSequence = candidate.nextEventSequence.incremented(); - if (!nextSequence) - return failure(DiagnosticCode::Failed, "registry event sequence exhausted"); + if (!nextSequence) return failure(DiagnosticCode::Failed, "registry event sequence exhausted"); if (operation != RegistryOperation::Remove) generationProject(*value, next.value()); candidate.events.reserve(candidate.events.size() + 1); - Event event{candidate.nextEventSequence, operation, key, next.value(), - operation == RegistryOperation::Remove, std::move(label), std::move(data)}; + Event event{candidate.nextEventSequence, + operation, + key, + next.value(), + operation == RegistryOperation::Remove, + std::move(label), + std::move(data)}; if (operation == RegistryOperation::Insert && it == candidate.entries.end()) { - auto [inserted, wasInserted] = candidate.entries.try_emplace( - std::move(key), Entry{next.value(), std::move(value)}); + auto [inserted, wasInserted] = + candidate.entries.try_emplace(std::move(key), Entry{next.value(), std::move(value)}); (void)wasInserted; it = inserted; } else if (operation == RegistryOperation::Remove) { @@ -494,7 +481,7 @@ private: it->second.value.reset(); } else { it->second.generation = next.value(); - it->second.value = std::move(value); + it->second.value = std::move(value); } candidate.events.push_back(std::move(event)); @@ -502,13 +489,12 @@ private: // Keep the notification independent of state_.events. Observer // dispatch is reentrant; a nested mutation is allowed to swap or // reallocate the canonical event vector while callbacks run. - const Event notification = candidate.events.back(); + const Event notification = candidate.events.back(); const Handle handle{notification.key, notification.generation}; - Status appliedStatus = Status::success(StatusCode::Applied); - Status callbackWarningStatus( - StatusCode::Applied, - {Diagnostic::warning(DiagnosticCode::CallbackFailure, - "registry mutation committed; observer callback failed")}); + Status appliedStatus = Status::success(StatusCode::Applied); + Status callbackWarningStatus( + StatusCode::Applied, {Diagnostic::warning(DiagnosticCode::CallbackFailure, + "registry mutation committed; observer callback failed")}); state_.swap(candidate); // State is already committed. A user callback is outside the diff --git a/src/engine/common/definitions/DefinitionRuntime.h b/src/engine/common/definitions/DefinitionRuntime.h index b3e8a06a..2ec79107 100644 --- a/src/engine/common/definitions/DefinitionRuntime.h +++ b/src/engine/common/definitions/DefinitionRuntime.h @@ -11,9 +11,9 @@ * definition parser. */ +#include "common/Generation.h" #include "common/ResourceRef.h" #include "common/Result.h" -#include "common/Generation.h" #include #include @@ -49,10 +49,10 @@ enum class ReloadPolicy : std::uint8_t { */ inline const char* reloadPolicyName(ReloadPolicy policy) noexcept { switch (policy) { - case ReloadPolicy::KeepInstanceValues: return "keep_instance_values"; - case ReloadPolicy::ReapplyDefaults: return "reapply_defaults"; - case ReloadPolicy::RebuildInstance: return "rebuild_instance"; - case ReloadPolicy::RejectWhileActive: return "reject_while_active"; + case ReloadPolicy::KeepInstanceValues: return "keep_instance_values"; + case ReloadPolicy::ReapplyDefaults: return "reapply_defaults"; + case ReloadPolicy::RebuildInstance: return "rebuild_instance"; + case ReloadPolicy::RejectWhileActive: return "reject_while_active"; } return "unknown"; } @@ -67,12 +67,10 @@ inline const char* reloadPolicyName(ReloadPolicy policy) noexcept { */ struct DefinitionHandle { eve::DefinitionRef reference; - eve::Generation generation; + eve::Generation generation; /** @brief Whether both the logical reference and generation are valid. */ - [[nodiscard]] bool isValid() const noexcept { - return reference.id().isValid() && !generation.isZero(); - } + [[nodiscard]] bool isValid() const noexcept { return reference.id().isValid() && !generation.isZero(); } friend bool operator==(const DefinitionHandle&, const DefinitionHandle&) noexcept = default; }; @@ -86,27 +84,26 @@ struct DefinitionHandle { * when any of these fields is missing. */ struct InstanceIdentity { - eve::PersistentId instanceId; + eve::PersistentId instanceId; eve::DefinitionRef definition; - eve::Generation definitionGeneration; + eve::Generation definitionGeneration; /** @brief Construct and validate one complete instance identity. */ - [[nodiscard]] static eve::Result create( - eve::PersistentId instanceId, eve::DefinitionRef definition, - eve::Generation definitionGeneration) { + [[nodiscard]] static eve::Result create(eve::PersistentId instanceId, + eve::DefinitionRef definition, + eve::Generation definitionGeneration) { if (instanceId.isNil()) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "runtime instance identity must not be nil", - "instanceId", {}, "common.definitions")); + eve::DiagnosticCode::InvalidArgument, "runtime instance identity must not be nil", "instanceId", {}, + "common.definitions")); if (!definition.id().isValid()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "runtime definition reference is invalid", - "definition", {}, "common.definitions")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "runtime definition reference is invalid", + "definition", {}, "common.definitions")); if (definitionGeneration.isZero()) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "runtime definition generation must be positive", "definitionGeneration", {}, - "common.definitions")); + eve::DiagnosticCode::InvalidArgument, "runtime definition generation must be positive", + "definitionGeneration", {}, "common.definitions")); return eve::Result::success( InstanceIdentity{instanceId, std::move(definition), definitionGeneration}); } @@ -134,12 +131,12 @@ enum class ReloadDisposition : std::uint8_t { /** @brief Identity transition emitted by a typed runtime reload operation. */ struct ReloadOutcome { - eve::PersistentId instanceId; + eve::PersistentId instanceId; eve::DefinitionRef definition; - eve::Generation oldGeneration; - eve::Generation newGeneration; - ReloadPolicy policy = ReloadPolicy::KeepInstanceValues; - ReloadDisposition disposition = ReloadDisposition::Unchanged; + eve::Generation oldGeneration; + eve::Generation newGeneration; + ReloadPolicy policy = ReloadPolicy::KeepInstanceValues; + ReloadDisposition disposition = ReloadDisposition::Unchanged; /** @brief Whether the reload changed the definition generation. */ [[nodiscard]] bool changed() const noexcept { return oldGeneration != newGeneration; } @@ -164,24 +161,21 @@ struct ReloadOutcome { */ template class RuntimeInstance { - static_assert(std::is_copy_constructible_v, - "RuntimeInstance state must be copy constructible"); + static_assert(std::is_copy_constructible_v, "RuntimeInstance state must be copy constructible"); public: - using RebuildFunction = std::function( - const State&, const InstanceIdentity&, const DefinitionHandle&)>; + using RebuildFunction = + std::function(const State&, const InstanceIdentity&, const DefinitionHandle&)>; private: /** @brief Construct a runtime instance from a validated identity and typed state. */ RuntimeInstance(InstanceIdentity identity, State state, bool active = true) - : identity_(std::move(identity)), state_(std::make_unique(std::move(state))), - active_(active) {} + : identity_(std::move(identity)), state_(std::make_unique(std::move(state))), active_(active) {} public: /** @brief Copy a typed runtime instance. */ RuntimeInstance(const RuntimeInstance& other) - : identity_(other.identity_), state_(std::make_unique(*other.state_)), - active_(other.active_) {} + : identity_(other.identity_), state_(std::make_unique(*other.state_)), active_(other.active_) {} /** @brief Copy-assign a typed runtime instance. */ RuntimeInstance& operator=(const RuntimeInstance& other) { if (this == &other) return *this; @@ -202,11 +196,11 @@ public: * @brief Construct a runtime instance after validating its identity. * @return An owning typed instance or a structured invalid-identity error. */ - [[nodiscard]] static eve::Result create( - eve::PersistentId instanceId, eve::DefinitionRef definition, - eve::Generation definitionGeneration, State state, bool active = true) { - auto identity = InstanceIdentity::create(instanceId, std::move(definition), - definitionGeneration); + [[nodiscard]] static eve::Result create(eve::PersistentId instanceId, + eve::DefinitionRef definition, + eve::Generation definitionGeneration, State state, + bool active = true) { + auto identity = InstanceIdentity::create(instanceId, std::move(definition), definitionGeneration); if (!identity) return eve::Result::failure(identity.status()); return eve::Result::success( RuntimeInstance(std::move(identity).takeValue(), std::move(state), active)); @@ -229,14 +223,13 @@ public: */ [[nodiscard]] eve::Result checkDefinition(const DefinitionHandle& handle) const { if (!handle.isValid()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "definition handle is invalid", "handle", - {}, "common.definitions")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "definition handle is invalid", "handle", {}, + "common.definitions")); if (handle != identity_.definitionHandle()) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::StaleHandle, - "runtime instance refers to a different definition generation", "handle", {}, - "common.definitions")); + eve::DiagnosticCode::StaleHandle, "runtime instance refers to a different definition generation", + "handle", {}, "common.definitions")); return eve::Result::success(); } @@ -252,52 +245,46 @@ public: * @remarks `RejectWhileActive` rejects active instances. When inactive it * intentionally keeps values and only advances the generation. */ - [[nodiscard]] eve::Result reload( - const DefinitionHandle& next, ReloadPolicy policy, const State& defaults, - RebuildFunction rebuild = {}) { + [[nodiscard]] eve::Result reload(const DefinitionHandle& next, ReloadPolicy policy, + const State& defaults, RebuildFunction rebuild = {}) { if (!next.isValid()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "next definition handle is invalid", "next", - {}, "common.definitions")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "next definition handle is invalid", + "next", {}, "common.definitions")); if (next.reference != identity_.definition) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, - "definition reload targets a different logical definition", "next.reference", {}, - "common.definitions")); + eve::DiagnosticCode::Conflict, "definition reload targets a different logical definition", + "next.reference", {}, "common.definitions")); if (next.generation < identity_.definitionGeneration) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::StaleHandle, - "definition reload handle is older than the instance generation", "next.generation", - {}, "common.definitions")); + eve::DiagnosticCode::StaleHandle, "definition reload handle is older than the instance generation", + "next.generation", {}, "common.definitions")); - ReloadOutcome outcome{identity_.instanceId, identity_.definition, - identity_.definitionGeneration, next.generation, policy, - ReloadDisposition::Unchanged}; + ReloadOutcome outcome{ + identity_.instanceId, identity_.definition, identity_.definitionGeneration, next.generation, policy, + ReloadDisposition::Unchanged}; if (next.generation == identity_.definitionGeneration) - return eve::Result::success( - std::move(outcome), eve::Status::success(eve::StatusCode::NoOp)); + return eve::Result::success(std::move(outcome), eve::Status::success(eve::StatusCode::NoOp)); if (policy == ReloadPolicy::RejectWhileActive && active_) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, - "active runtime instance rejects definition reload", "policy", {}, + eve::DiagnosticCode::Conflict, "active runtime instance rejects definition reload", "policy", {}, "common.definitions")); try { - State candidate(*state_); + State candidate(*state_); ReloadDisposition disposition = ReloadDisposition::Kept; if (policy == ReloadPolicy::ReapplyDefaults) { - candidate = defaults; + candidate = defaults; disposition = ReloadDisposition::DefaultsReapplied; } else if (policy == ReloadPolicy::RebuildInstance) { if (!rebuild) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "rebuild policy requires a rebuild callback", "rebuild", {}, - "common.definitions")); + eve::DiagnosticCode::InvalidArgument, "rebuild policy requires a rebuild callback", "rebuild", + {}, "common.definitions")); auto rebuilt = rebuild(*state_, identity_, next); if (!rebuilt) return eve::Result::failure(rebuilt.status()); - candidate = std::move(rebuilt).takeValue(); + candidate = std::move(rebuilt).takeValue(); disposition = ReloadDisposition::Rebuilt; } else if (policy == ReloadPolicy::RejectWhileActive) { // The active guard was handled above. Inactive instances have @@ -308,17 +295,17 @@ public: auto committed = std::make_unique(std::move(candidate)); state_.swap(committed); identity_.definitionGeneration = next.generation; - outcome.disposition = disposition; - return eve::Result::success( - std::move(outcome), eve::Status::success(eve::StatusCode::Applied)); + outcome.disposition = disposition; + return eve::Result::success(std::move(outcome), + eve::Status::success(eve::StatusCode::Applied)); } catch (const std::exception&) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "definition reload candidate preparation failed", {}, {}, - "common.definitions")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "definition reload candidate preparation failed", + {}, {}, "common.definitions")); } catch (...) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "definition reload candidate preparation failed", {}, {}, - "common.definitions")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "definition reload candidate preparation failed", + {}, {}, "common.definitions")); } } @@ -335,22 +322,19 @@ public: * that wants to apply a newer definition must first perform the * normal reload protocol and then capture a new snapshot. */ - [[nodiscard]] eve::Result restoreExact(const InstanceIdentity& identity, State state, - bool active) { + [[nodiscard]] eve::Result restoreExact(const InstanceIdentity& identity, State state, bool active) { if (!identity.isValid()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "runtime snapshot identity is invalid", - "identity", {}, "common.definitions")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "runtime snapshot identity is invalid", "identity", + {}, "common.definitions")); if (identity.instanceId != identity_.instanceId || identity.definition != identity_.definition) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, - "runtime snapshot belongs to a different instance or definition", "identity", {}, - "common.definitions")); + eve::DiagnosticCode::Conflict, "runtime snapshot belongs to a different instance or definition", + "identity", {}, "common.definitions")); if (identity.definitionGeneration != identity_.definitionGeneration) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::StaleHandle, - "runtime snapshot definition generation is not current", "identity.definitionGeneration", - {}, "common.definitions")); + eve::DiagnosticCode::StaleHandle, "runtime snapshot definition generation is not current", + "identity.definitionGeneration", {}, "common.definitions")); auto committed = std::make_unique(std::move(state)); state_.swap(committed); @@ -359,9 +343,9 @@ public: } private: - InstanceIdentity identity_; + InstanceIdentity identity_; std::unique_ptr state_; - bool active_ = true; + bool active_ = true; }; } // namespace eve::definition diff --git a/src/engine/common/definitions/RuntimeSnapshot.h b/src/engine/common/definitions/RuntimeSnapshot.h index 2123b6d6..301d32d7 100644 --- a/src/engine/common/definitions/RuntimeSnapshot.h +++ b/src/engine/common/definitions/RuntimeSnapshot.h @@ -32,40 +32,35 @@ using RuntimeStateDecoder = std::function(const eve::Value&)> namespace detail { -inline eve::Result runtimeSnapshotUint(const eve::Value& value, - std::string_view path) { +inline eve::Result runtimeSnapshotUint(const eve::Value& value, std::string_view path) { const auto text = value.getIf(); if (text == nullptr || text->empty()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "runtime snapshot integer must be a decimal string", - std::string(path), {}, "common.definitions.snapshot")); - std::uint64_t result = 0; - const auto [end, error] = - std::from_chars(text->data(), text->data() + text->size(), result); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "runtime snapshot integer must be a decimal string", + std::string(path), {}, "common.definitions.snapshot")); + std::uint64_t result = 0; + const auto [end, error] = std::from_chars(text->data(), text->data() + text->size(), result); if (error != std::errc{} || end != text->data() + text->size()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "runtime snapshot integer is out of range", - std::string(path), {}, "common.definitions.snapshot")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "runtime snapshot integer is out of range", + std::string(path), {}, "common.definitions.snapshot")); return eve::Result::success(result); } inline eve::Result runtimeSnapshotShape(const eve::Value::Object& object) { - static const std::set fields = { - "active", "definition", "definitionGeneration", "instanceId", "state"}; + static const std::set fields = {"active", "definition", "definitionGeneration", "instanceId", "state"}; for (const auto& [name, unused] : object) { (void)unused; if (!fields.contains(name)) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, - "runtime snapshot payload contains an unknown field", "state." + name, {}, - "common.definitions.snapshot")); + eve::DiagnosticCode::ParseError, "runtime snapshot payload contains an unknown field", "state." + name, + {}, "common.definitions.snapshot")); } for (const auto& name : fields) { if (!object.contains(name)) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, - "runtime snapshot payload is missing a required field", "state." + name, {}, - "common.definitions.snapshot")); + eve::DiagnosticCode::ParseError, "runtime snapshot payload is missing a required field", + "state." + name, {}, "common.definitions.snapshot")); } return eve::Result::success(); } @@ -86,13 +81,13 @@ inline eve::Result runtimeSnapshotShape(const eve::Value::Object& object) */ template [[nodiscard]] eve::Result snapshotRuntimeInstance( - const RuntimeInstance& runtime, std::string type, eve::LogicalId schema, - eve::SchemaVersion schemaVersion, eve::Revision revision, eve::SimulationTick tick, - const eve::SnapshotHashProvider& hashProvider, const RuntimeStateEncoder& encode) { + const RuntimeInstance& runtime, std::string type, eve::LogicalId schema, eve::SchemaVersion schemaVersion, + eve::Revision revision, eve::SimulationTick tick, const eve::SnapshotHashProvider& hashProvider, + const RuntimeStateEncoder& encode) { if (!encode) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "runtime snapshot encoder is required", "encode", - {}, "common.definitions.snapshot")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "runtime snapshot encoder is required", + "encode", {}, "common.definitions.snapshot")); auto encoded = encode(runtime.state()); if (!encoded) return eve::Result::failure(encoded.status()); eve::Value::Object payload; @@ -102,9 +97,8 @@ template eve::Value(std::to_string(runtime.identity().definitionGeneration.value()))); payload.emplace("instanceId", eve::Value(runtime.identity().instanceId.format())); payload.emplace("state", std::move(encoded).takeValue()); - return eve::makeSnapshotEnvelope(std::move(type), std::move(schema), schemaVersion, - runtime.identity().instanceId, revision, tick, - eve::Value(std::move(payload)), hashProvider); + return eve::makeSnapshotEnvelope(std::move(type), std::move(schema), schemaVersion, runtime.identity().instanceId, + revision, tick, eve::Value(std::move(payload)), hashProvider); } /** @@ -121,71 +115,65 @@ template */ template [[nodiscard]] eve::Result restoreRuntimeInstance( - RuntimeInstance& runtime, const eve::SnapshotEnvelope& snapshot, - std::string_view expectedType, const eve::LogicalId& expectedSchema, - eve::SchemaVersion expectedVersion, const eve::SnapshotHashProvider& hashProvider, - const RuntimeStateDecoder& decode) { + RuntimeInstance& runtime, const eve::SnapshotEnvelope& snapshot, std::string_view expectedType, + const eve::LogicalId& expectedSchema, eve::SchemaVersion expectedVersion, + const eve::SnapshotHashProvider& hashProvider, const RuntimeStateDecoder& decode) { if (snapshot.type != expectedType || snapshot.schema != expectedSchema) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "runtime snapshot type or schema does not match", - "schema", {}, "common.definitions.snapshot")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "runtime snapshot type or schema does not match", + "schema", {}, "common.definitions.snapshot")); if (snapshot.schemaVersion != expectedVersion) return eve::Result::failure(eve::Diagnostic::error( snapshot.schemaVersion > expectedVersion ? eve::DiagnosticCode::UnknownVersion - : eve::DiagnosticCode::Unsupported, - "runtime snapshot schema version is not supported", "schemaVersion", {}, - "common.definitions.snapshot")); + : eve::DiagnosticCode::Unsupported, + "runtime snapshot schema version is not supported", "schemaVersion", {}, "common.definitions.snapshot")); auto verified = eve::verifySnapshotEnvelope(snapshot, hashProvider); if (!verified) return verified; - auto metadata = eve::validateSnapshotPayloadMetadata(snapshot.payload, snapshot.revision, - snapshot.tick); + auto metadata = eve::validateSnapshotPayloadMetadata(snapshot.payload, snapshot.revision, snapshot.tick); if (!metadata) return metadata; const auto object = snapshot.payload.getIf(); if (object == nullptr) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "runtime snapshot payload must be an object", "payload", - {}, "common.definitions.snapshot")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::ParseError, + "runtime snapshot payload must be an object", + "payload", {}, "common.definitions.snapshot")); } auto shape = detail::runtimeSnapshotShape(*object); if (!shape) return shape; - const auto active = object->at("active").getIf(); + const auto active = object->at("active").getIf(); const auto definitionText = object->at("definition").getIf(); - const auto instanceText = object->at("instanceId").getIf(); + const auto instanceText = object->at("instanceId").getIf(); if (active == nullptr || definitionText == nullptr || instanceText == nullptr) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "runtime snapshot identity fields have invalid types", - "payload", {}, "common.definitions.snapshot")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::ParseError, + "runtime snapshot identity fields have invalid types", + "payload", {}, "common.definitions.snapshot")); const auto instanceId = eve::PersistentId::parse(*instanceText); if (!instanceId) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "runtime snapshot instanceId is invalid", - "payload.instanceId", {}, "common.definitions.snapshot")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "runtime snapshot instanceId is invalid", + "payload.instanceId", {}, "common.definitions.snapshot")); auto definition = eve::DefinitionRef::parse(*definitionText); if (!definition) return eve::Result::failure(definition.status()); - auto generation = detail::runtimeSnapshotUint(object->at("definitionGeneration"), - "payload.definitionGeneration"); + auto generation = detail::runtimeSnapshotUint(object->at("definitionGeneration"), "payload.definitionGeneration"); if (!generation) return eve::Result::failure(generation.status()); - const eve::definition::InstanceIdentity identity{ - *instanceId, std::move(definition).takeValue(), - eve::Generation(std::move(generation).takeValue())}; + const eve::definition::InstanceIdentity identity{*instanceId, std::move(definition).takeValue(), + eve::Generation(std::move(generation).takeValue())}; if (snapshot.instanceId != identity.instanceId || identity.instanceId != runtime.identity().instanceId) return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::Conflict, "runtime snapshot instanceId does not match the destination", "payload.instanceId", {}, "common.definitions.snapshot")); if (identity.definition != runtime.identity().definition) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, "runtime snapshot definition reference does not match", - "payload.definition", {}, "common.definitions.snapshot")); + eve::DiagnosticCode::Conflict, "runtime snapshot definition reference does not match", "payload.definition", + {}, "common.definitions.snapshot")); if (identity.definitionGeneration != runtime.identity().definitionGeneration) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::StaleHandle, - "runtime snapshot definition generation does not match the destination", + eve::DiagnosticCode::StaleHandle, "runtime snapshot definition generation does not match the destination", "payload.definitionGeneration", {}, "common.definitions.snapshot")); if (!decode) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "runtime snapshot decoder is required", "decode", - {}, "common.definitions.snapshot")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "runtime snapshot decoder is required", "decode", {}, + "common.definitions.snapshot")); auto candidate = decode(object->at("state")); if (!candidate) return eve::Result::failure(candidate.status()); return runtime.restoreExact(identity, std::move(candidate).takeValue(), *active); diff --git a/src/engine/devtools/ScenarioRecorder.cpp b/src/engine/devtools/ScenarioRecorder.cpp index a59ee10c..0936f1f9 100644 --- a/src/engine/devtools/ScenarioRecorder.cpp +++ b/src/engine/devtools/ScenarioRecorder.cpp @@ -23,8 +23,7 @@ void ScenarioRecorder::setObserver() { eventModule_ = eve::ModuleManager::requireInstance("PlatformEvent"); if (!eventModule_) return; savedObserver_ = eventModule_->pollObserver(); - eventModule_->setPollObserver( - [this](const eve::platform_event::Message& m) { onEventConsumed(m); }); + eventModule_->setPollObserver([this](const eve::platform_event::Message& m) { onEventConsumed(m); }); } void ScenarioRecorder::detachObserver() { @@ -143,8 +142,7 @@ bool ScenarioRecorder::stageFrame() { const ScenarioFrame& frame = frames_[replayIndex_++]; for (const auto& ev : frame.events) { std::vector args; - for (const auto& a : ev.args) - args.push_back(eve::platform_event::Variant::makeString(a)); + for (const auto& a : ev.args) args.push_back(eve::platform_event::Variant::makeString(a)); eventModule_->push(new eve::platform_event::Message(ev.name, args)); } return true; diff --git a/src/engine/devtools/ScenarioRecorder.h b/src/engine/devtools/ScenarioRecorder.h index cffed0e7..416beb6f 100644 --- a/src/engine/devtools/ScenarioRecorder.h +++ b/src/engine/devtools/ScenarioRecorder.h @@ -124,7 +124,7 @@ private: std::string errorReport_; std::string errorSite_; std::function savedObserver_; - eve::platform_event::PlatformEvent* eventModule_ = nullptr; + eve::platform_event::PlatformEvent* eventModule_ = nullptr; }; } // namespace eve::dev diff --git a/src/modules/action/Action.cpp b/src/modules/action/Action.cpp index 39328686..8f3105db 100644 --- a/src/modules/action/Action.cpp +++ b/src/modules/action/Action.cpp @@ -12,13 +12,11 @@ namespace { template Result failure(DiagnosticCode code, std::string message, std::string path = {}) { - return Result::failure( - Diagnostic::error(code, std::move(message), std::move(path))); + return Result::failure(Diagnostic::error(code, std::move(message), std::move(path))); } Result failure(DiagnosticCode code, std::string message, std::string path = {}) { - return Result::failure( - Diagnostic::error(code, std::move(message), std::move(path))); + return Result::failure(Diagnostic::error(code, std::move(message), std::move(path))); } Result failureFrom(const Status& status) { return Result::failure(status); } @@ -29,31 +27,26 @@ Result failureFrom(const Status& status) { } bool isEmptyCondition(const decision::Condition& condition) { - return condition.kind() == decision::ConditionKind::All && condition.children().empty() && - condition.isValid(); + return condition.kind() == decision::ConditionKind::All && condition.children().empty() && condition.isValid(); } bool isTerminal(ActionPhase phase) { - return phase == ActionPhase::Completed || phase == ActionPhase::Cancelled || - phase == ActionPhase::Failed; + return phase == ActionPhase::Completed || phase == ActionPhase::Cancelled || phase == ActionPhase::Failed; } Status cancelledStatus() { - return Status::failure( - StatusCode::Cancelled, - Diagnostic::error(DiagnosticCode::Cancelled, "action execution was cancelled", "action")); + return Status::failure(StatusCode::Cancelled, + Diagnostic::error(DiagnosticCode::Cancelled, "action execution was cancelled", "action")); } Status notFoundStatus() { - return Status::failure( - StatusCode::NotFound, - Diagnostic::error(DiagnosticCode::NotFound, "action execution was not found", "execution")); + return Status::failure(StatusCode::NotFound, + Diagnostic::error(DiagnosticCode::NotFound, "action execution was not found", "execution")); } Status invalidStatus(std::string message, std::string path = {}) { return Status::failure(StatusCode::Rejected, - Diagnostic::error(DiagnosticCode::InvalidArgument, - std::move(message), std::move(path))); + Diagnostic::error(DiagnosticCode::InvalidArgument, std::move(message), std::move(path))); } Status pendingStatus() { return Status::success(StatusCode::Pending); } @@ -62,14 +55,14 @@ Status pendingStatus() { return Status::success(StatusCode::Pending); } const char* actionPhaseName(ActionPhase phase) noexcept { switch (phase) { - case ActionPhase::Requested: return "requested"; - case ActionPhase::Validating: return "validating"; - case ActionPhase::Windup: return "windup"; - case ActionPhase::Active: return "active"; - case ActionPhase::Recover: return "recover"; - case ActionPhase::Completed: return "completed"; - case ActionPhase::Cancelled: return "cancelled"; - case ActionPhase::Failed: return "failed"; + case ActionPhase::Requested: return "requested"; + case ActionPhase::Validating: return "validating"; + case ActionPhase::Windup: return "windup"; + case ActionPhase::Active: return "active"; + case ActionPhase::Recover: return "recover"; + case ActionPhase::Completed: return "completed"; + case ActionPhase::Cancelled: return "cancelled"; + case ActionPhase::Failed: return "failed"; } return "unknown"; } @@ -78,36 +71,32 @@ Result ActionDefinition::validate() const { if (!id.isValid()) return failure(DiagnosticCode::InvalidArgument, "action definition id is invalid", "id"); if (!condition.isValid()) return failure(DiagnosticCode::InvalidArgument, "action condition is invalid", "condition"); - if (timing.windup.nanoseconds() < 0 || timing.active.nanoseconds() < 0 || - timing.recover.nanoseconds() < 0) - return failure(DiagnosticCode::InvalidArgument, - "action phase durations must be non-negative", "timing"); + if (timing.windup.nanoseconds() < 0 || timing.active.nanoseconds() < 0 || timing.recover.nanoseconds() < 0) + return failure(DiagnosticCode::InvalidArgument, "action phase durations must be non-negative", "timing"); switch (targetingMode) { - case TargetingMode::None: - case TargetingMode::Explicit: - if (targetingSpec) - return failure(DiagnosticCode::InvalidArgument, - "targetingSpec is only valid for Query actions", "targetingSpec"); - break; - case TargetingMode::Query: - if (!targetingSpec) - return failure(DiagnosticCode::InvalidArgument, - "Query actions require a targetingSpec", "targetingSpec"); - { - auto valid = targetingSpec->validate(); - if (!valid) return failureFrom(valid.status()); - } - break; + case TargetingMode::None: + case TargetingMode::Explicit: + if (targetingSpec) + return failure(DiagnosticCode::InvalidArgument, "targetingSpec is only valid for Query actions", + "targetingSpec"); + break; + case TargetingMode::Query: + if (!targetingSpec) + return failure(DiagnosticCode::InvalidArgument, "Query actions require a targetingSpec", + "targetingSpec"); + { + auto valid = targetingSpec->validate(); + if (!valid) return failureFrom(valid.status()); + } + break; } if (cost && !cost->isValid()) - return failure(DiagnosticCode::InvalidArgument, - "action cost must be a validated non-empty CostSpec", "cost"); + return failure(DiagnosticCode::InvalidArgument, "action cost must be a validated non-empty CostSpec", "cost"); for (const auto& effectId : effectIds) { if (effectId.empty()) - return failure(DiagnosticCode::InvalidArgument, - "action effect ids must not be empty", "effectIds"); + return failure(DiagnosticCode::InvalidArgument, "action effect ids must not be empty", "effectIds"); } return Result::success(); } @@ -116,59 +105,56 @@ Result ActionRequest::validate(const ActionDefinition& definition) const { auto definitionValid = definition.validate(); if (!definitionValid) return failureFrom(definitionValid.status()); if (actionId != definition.id) - return failure(DiagnosticCode::InvalidArgument, - "action request id does not match its definition", "actionId"); + return failure(DiagnosticCode::InvalidArgument, "action request id does not match its definition", "actionId"); switch (definition.targetingMode) { - case TargetingMode::None: - if (!targetEntities.empty() || targetingQuery) - return failure(DiagnosticCode::InvalidArgument, - "a non-targeted action cannot carry target selection", "targets"); - break; - case TargetingMode::Explicit: - if (targetEntities.empty()) - return failure(DiagnosticCode::PreconditionViolation, - "an Explicit action requires at least one target", "targetEntities"); - if (targetingQuery) - return failure(DiagnosticCode::InvalidArgument, - "Explicit actions cannot carry a targeting query", "targetingQuery"); - break; - case TargetingMode::Query: - if (!targetingQuery) - return failure(DiagnosticCode::PreconditionViolation, - "a Query action requires a targeting query", "targetingQuery"); - if (!targetEntities.empty()) - return failure(DiagnosticCode::InvalidArgument, - "Query actions cannot carry explicit targets", "targetEntities"); - { - auto valid = targetingQuery->validate(); - if (!valid) return failureFrom(valid.status()); - } - break; + case TargetingMode::None: + if (!targetEntities.empty() || targetingQuery) + return failure(DiagnosticCode::InvalidArgument, "a non-targeted action cannot carry target selection", + "targets"); + break; + case TargetingMode::Explicit: + if (targetEntities.empty()) + return failure(DiagnosticCode::PreconditionViolation, "an Explicit action requires at least one target", + "targetEntities"); + if (targetingQuery) + return failure(DiagnosticCode::InvalidArgument, "Explicit actions cannot carry a targeting query", + "targetingQuery"); + break; + case TargetingMode::Query: + if (!targetingQuery) + return failure(DiagnosticCode::PreconditionViolation, "a Query action requires a targeting query", + "targetingQuery"); + if (!targetEntities.empty()) + return failure(DiagnosticCode::InvalidArgument, "Query actions cannot carry explicit targets", + "targetEntities"); + { + auto valid = targetingQuery->validate(); + if (!valid) return failureFrom(valid.status()); + } + break; } return Result::success(); } -Result SensingTargetingAdapter::resolve( - const sensing::TargetingQuery& query) const { +Result SensingTargetingAdapter::resolve(const sensing::TargetingQuery& query) const { return resolver_.resolve(query); } Result ActionRuntime::nextExecutionId() { if (nextId_.isZero()) - return failure(DiagnosticCode::InvariantViolation, - "action execution id is zero", "execution.id"); - const ActionExecutionId id = nextId_; - const auto next = nextId_.incremented(); + return failure(DiagnosticCode::InvariantViolation, "action execution id is zero", + "execution.id"); + const ActionExecutionId id = nextId_; + const auto next = nextId_.incremented(); if (!next) - return failure(DiagnosticCode::InvariantViolation, - "action execution id exhausted", "execution.id"); + return failure(DiagnosticCode::InvariantViolation, "action execution id exhausted", + "execution.id"); nextId_ = *next; return Result::success(id); } -Result ActionRuntime::submit(ActionDefinition definition, - ActionRequest request) { +Result ActionRuntime::submit(ActionDefinition definition, ActionRequest request) { auto definitionValid = definition.validate(); if (!definitionValid) return failureFrom(definitionValid.status()); auto requestValid = request.validate(definition); @@ -180,12 +166,10 @@ Result ActionRuntime::submit(ActionDefinition definition, // Construct inside this friend member rather than using make_unique: // ActionExecution's constructor is intentionally private so adapters // cannot manufacture lifecycle owners. - std::unique_ptr execution( - new ActionExecution(id, std::move(definition), std::move(request))); + std::unique_ptr execution(new ActionExecution(id, std::move(definition), std::move(request))); const auto [it, inserted] = executions_.emplace(id, std::move(execution)); if (!inserted) - return failure(DiagnosticCode::Conflict, - "action execution id collided", "execution.id"); + return failure(DiagnosticCode::Conflict, "action execution id collided", "execution.id"); return Result::success(it->first, Status::success(StatusCode::Pending)); } @@ -201,50 +185,50 @@ ActionExecution* ActionRuntime::find(ActionExecutionId id) noexcept { Result ActionRuntime::validateExecution(ActionExecution& execution) { const auto& definition = execution.definition_; - const auto& request = execution.request_; + const auto& request = execution.request_; if (!isEmptyCondition(definition.condition)) { if (services_.conditions == nullptr) - return failure(DiagnosticCode::Unsupported, - "action condition requires an evaluator", "services.conditions"); + return failure(DiagnosticCode::Unsupported, "action condition requires an evaluator", + "services.conditions"); auto condition = services_.conditions->evaluate(definition, request); if (!condition) return failureFrom(condition.status()); auto checked = std::move(condition).takeValue(); if (!checked.passed()) { DiagnosticDetails details; details.emplace_back("reason", decision::conditionReasonCodeName(checked.reasonCode())); - return Result::failure(Diagnostic::error( - DiagnosticCode::PreconditionViolation, "action condition was rejected", "condition", - std::move(details))); + return Result::failure(Diagnostic::error(DiagnosticCode::PreconditionViolation, + "action condition was rejected", "condition", + std::move(details))); } } if (definition.targetingMode == TargetingMode::Query) { if (services_.targeting == nullptr) - return failure(DiagnosticCode::Unsupported, - "Query action requires a target resolver", "services.targeting"); + return failure(DiagnosticCode::Unsupported, "Query action requires a target resolver", + "services.targeting"); sensing::TargetingQuery query = *request.targetingQuery; - query.spec = *definition.targetingSpec; - auto targets = services_.targeting->resolve(query); + query.spec = *definition.targetingSpec; + auto targets = services_.targeting->resolve(query); if (!targets) return failureFrom(targets.status()); execution.resolvedTargets_ = std::move(targets).takeValue(); } if (definition.activeExecutionRequired || !definition.effectIds.empty()) { if (services_.effects == nullptr && services_.transactionEffect == nullptr) - return failure(DiagnosticCode::Unsupported, - "action Active phase requires an effect executor", "services.effects"); + return failure(DiagnosticCode::Unsupported, "action Active phase requires an effect executor", + "services.effects"); } if (definition.cost) { - const bool transactionBacked = services_.transactionEffect != nullptr && - services_.transactionAccount != nullptr; + const bool transactionBacked = + services_.transactionEffect != nullptr && services_.transactionAccount != nullptr; if (services_.resources == nullptr && !transactionBacked) - return failure(DiagnosticCode::Unsupported, - "cost-bearing action requires a resource provider", "services.resources"); + return failure(DiagnosticCode::Unsupported, "cost-bearing action requires a resource provider", + "services.resources"); auto affordability = services_.resources != nullptr - ? services_.resources->canAfford(definition, request, *definition.cost) - : services_.transactionAccount->canAfford(*definition.cost); + ? services_.resources->canAfford(definition, request, *definition.cost) + : services_.transactionAccount->canAfford(*definition.cost); if (!affordability) return failureFrom(affordability.status()); auto checked = std::move(affordability).takeValue(); if (!checked.affordable) { @@ -254,9 +238,9 @@ Result ActionRuntime::validateExecution(ActionExecution& execution) { details.emplace_back("required", std::to_string(shortfall.required.value())); details.emplace_back("available", std::to_string(shortfall.available.value())); } - return Result::failure(Diagnostic::error( - DiagnosticCode::PreconditionViolation, "action resource cost is not affordable", - "cost", std::move(details))); + return Result::failure(Diagnostic::error(DiagnosticCode::PreconditionViolation, + "action resource cost is not affordable", "cost", + std::move(details))); } } return Result::success(Status::success(StatusCode::Applied)); @@ -264,51 +248,47 @@ Result ActionRuntime::validateExecution(ActionExecution& execution) { Result ActionRuntime::enterActive(ActionExecution& execution, SimulationTick tick) { const auto& definition = execution.definition_; - const auto& request = execution.request_; + const auto& request = execution.request_; if (services_.transactionEffect != nullptr) { if (definition.cost && services_.transactionAccount == nullptr) - return failure(DiagnosticCode::Unsupported, - "transaction-backed action cost requires an account", + return failure(DiagnosticCode::Unsupported, "transaction-backed action cost requires an account", "services.transactionAccount"); - const std::string transactionId = request.transactionId.empty() - ? "action." + definition.id.format() + "." + std::to_string(execution.id_.value()) - : request.transactionId; + const std::string transactionId = request.transactionId.empty() ? "action." + definition.id.format() + "." + + std::to_string(execution.id_.value()) + : request.transactionId; transaction::TransactionContext context(transactionId); - auto transactionResult = definition.cost - ? transaction::AtomicResourcePayment::execute( - context, *services_.transactionAccount, *definition.cost, - *services_.transactionEffect) - : transaction::AtomicResourcePayment::execute( - context, *services_.transactionEffect); + auto transactionResult = + definition.cost + ? transaction::AtomicResourcePayment::execute(context, *services_.transactionAccount, *definition.cost, + *services_.transactionEffect) + : transaction::AtomicResourcePayment::execute(context, *services_.transactionEffect); if (!transactionResult) return failureFrom(transactionResult.status()); execution.transactionReceipt_ = std::move(transactionResult).takeValue(); - execution.activeExecuted_ = true; + execution.activeExecuted_ = true; return Result::success(Status::success(StatusCode::Applied)); } std::unique_ptr stagedEffect; if (definition.activeExecutionRequired || !definition.effectIds.empty()) { if (services_.effects == nullptr) - return failure(DiagnosticCode::Unsupported, - "action Active phase requires an effect executor", "services.effects"); + return failure(DiagnosticCode::Unsupported, "action Active phase requires an effect executor", + "services.effects"); auto prepared = services_.effects->prepare( - definition, request, - execution.resolvedTargets_ ? &*execution.resolvedTargets_ : nullptr, tick); + definition, request, execution.resolvedTargets_ ? &*execution.resolvedTargets_ : nullptr, tick); if (!prepared) return failureFrom(prepared.status()); stagedEffect = std::move(prepared).takeValue(); if (!stagedEffect) - return failure(DiagnosticCode::InvariantViolation, - "effect executor returned an empty staged operation", "effects"); + return failure(DiagnosticCode::InvariantViolation, "effect executor returned an empty staged operation", + "effects"); } std::optional reservation; if (definition.cost) { if (services_.resources == nullptr) { if (stagedEffect) stagedEffect->rollback(); - return failure(DiagnosticCode::Unsupported, - "activating a cost-bearing action requires a resource provider", + return failure(DiagnosticCode::Unsupported, "activating a cost-bearing action requires a resource provider", "services.resources"); } auto reserved = services_.resources->reserve(definition, request, *definition.cost); @@ -319,8 +299,8 @@ Result ActionRuntime::enterActive(ActionExecution& execution, SimulationTi auto credential = std::move(reserved).takeValue(); if (!credential.isValid()) { if (stagedEffect) stagedEffect->rollback(); - return failure(DiagnosticCode::InvariantViolation, - "resource provider returned an invalid reservation", "cost.reservation"); + return failure(DiagnosticCode::InvariantViolation, "resource provider returned an invalid reservation", + "cost.reservation"); } reservation = std::move(credential); @@ -336,7 +316,7 @@ Result ActionRuntime::enterActive(ActionExecution& execution, SimulationTi if (stagedEffect) stagedEffect->rollback(); return failureFrom(committed.status()); } - auto receipt = std::move(committed).takeValue(); + auto receipt = std::move(committed).takeValue(); execution.receipt_ = std::move(receipt); } @@ -363,25 +343,24 @@ Result ActionRuntime::addElapsed(ActionExecution& execution, Duration amou return Result::success(); } -void ActionRuntime::transition(ActionExecution& execution, ActionPhase next, - SimulationTick tick, std::vector& transitions) { +void ActionRuntime::transition(ActionExecution& execution, ActionPhase next, SimulationTick tick, + std::vector& transitions) { if (execution.phase_ == next) return; transitions.push_back({execution.phase_, next, tick}); - execution.phase_ = next; + execution.phase_ = next; execution.phaseElapsed_ = Duration::zero(); if (next == ActionPhase::Completed) execution.status_ = Status::success(StatusCode::Applied); - else if (next == ActionPhase::Requested || next == ActionPhase::Validating || - next == ActionPhase::Windup || next == ActionPhase::Active || next == ActionPhase::Recover) + else if (next == ActionPhase::Requested || next == ActionPhase::Validating || next == ActionPhase::Windup || + next == ActionPhase::Active || next == ActionPhase::Recover) execution.status_ = pendingStatus(); } -void ActionRuntime::failExecution(ActionExecution& execution, Status status, - SimulationTick tick, std::vector* transitions) { +void ActionRuntime::failExecution(ActionExecution& execution, Status status, SimulationTick tick, + std::vector* transitions) { if (status.isSuccess()) - status = Status::failure( - StatusCode::Failed, - Diagnostic::error(DiagnosticCode::Failed, "action execution failed", "action")); + status = Status::failure(StatusCode::Failed, + Diagnostic::error(DiagnosticCode::Failed, "action execution failed", "action")); if (transitions && execution.phase_ != ActionPhase::Failed) transition(execution, ActionPhase::Failed, tick, *transitions); else @@ -389,16 +368,13 @@ void ActionRuntime::failExecution(ActionExecution& execution, Status status, execution.status_ = std::move(status); } -Result ActionRuntime::advance(ActionExecutionId id, SimulationTick tick, - Duration delta) { +Result ActionRuntime::advance(ActionExecutionId id, SimulationTick tick, Duration delta) { ActionExecution* execution = find(id); if (execution == nullptr) return failureFrom(notFoundStatus()); if (delta.nanoseconds() < 0) - return failureFrom( - invalidStatus("action advance duration must be non-negative", "delta")); + return failureFrom(invalidStatus("action advance duration must be non-negative", "delta")); if (tick < execution->lastTick_) - return failureFrom( - invalidStatus("action simulation tick moved backwards", "tick")); + return failureFrom(invalidStatus("action simulation tick moved backwards", "tick")); execution->lastTick_ = tick; if (execution->phase_ == ActionPhase::Completed) { @@ -408,7 +384,7 @@ Result ActionRuntime::advance(ActionExecutionId id, SimulationTic if (execution->phase_ == ActionPhase::Cancelled || execution->phase_ == ActionPhase::Failed) return failureFrom(execution->status()); - Duration remaining = delta; + Duration remaining = delta; std::vector transitions; for (int guard = 0; guard < 8; ++guard) { if (execution->phase_ == ActionPhase::Requested) { @@ -430,8 +406,8 @@ Result ActionRuntime::advance(ActionExecutionId id, SimulationTic } if (execution->phase_ == ActionPhase::Windup) { - const Duration phase = execution->definition_.timing.windup; - const auto needed = phase.nanoseconds() - execution->phaseElapsed_.nanoseconds(); + const Duration phase = execution->definition_.timing.windup; + const auto needed = phase.nanoseconds() - execution->phaseElapsed_.nanoseconds(); if (remaining.nanoseconds() < needed) { auto added = addElapsed(*execution, remaining); if (!added) { @@ -459,8 +435,8 @@ Result ActionRuntime::advance(ActionExecutionId id, SimulationTic return failureFrom(execution->status()); } } - const Duration phase = execution->definition_.timing.active; - const auto needed = phase.nanoseconds() - execution->phaseElapsed_.nanoseconds(); + const Duration phase = execution->definition_.timing.active; + const auto needed = phase.nanoseconds() - execution->phaseElapsed_.nanoseconds(); if (needed == 0) { transition(*execution, ActionPhase::Recover, tick, transitions); continue; @@ -485,8 +461,8 @@ Result ActionRuntime::advance(ActionExecutionId id, SimulationTic } if (execution->phase_ == ActionPhase::Recover) { - const Duration phase = execution->definition_.timing.recover; - const auto needed = phase.nanoseconds() - execution->phaseElapsed_.nanoseconds(); + const Duration phase = execution->definition_.timing.recover; + const auto needed = phase.nanoseconds() - execution->phaseElapsed_.nanoseconds(); if (needed == 0) { transition(*execution, ActionPhase::Completed, tick, transitions); continue; @@ -514,12 +490,12 @@ Result ActionRuntime::advance(ActionExecutionId id, SimulationTic } if (execution->phase_ == ActionPhase::Completed) { - ActionAdvance result{id, execution->phase_, std::move(transitions), - execution->phaseElapsed_, execution->totalElapsed_}; + ActionAdvance result{id, execution->phase_, std::move(transitions), execution->phaseElapsed_, + execution->totalElapsed_}; return Result::success(std::move(result), Status::success(StatusCode::Applied)); } - ActionAdvance result{id, execution->phase_, std::move(transitions), - execution->phaseElapsed_, execution->totalElapsed_}; + ActionAdvance result{id, execution->phase_, std::move(transitions), execution->phaseElapsed_, + execution->totalElapsed_}; return Result::success(std::move(result), pendingStatus()); } @@ -529,17 +505,14 @@ Result ActionRuntime::cancel(ActionExecutionId id, SimulationTick tick) { if (execution->phase_ == ActionPhase::Completed) return Result::failure(Status::failure( StatusCode::Conflict, - Diagnostic::error(DiagnosticCode::Conflict, - "completed action execution cannot be cancelled", "action"))); - if (execution->phase_ == ActionPhase::Failed) - return Result::failure(execution->status_); - if (execution->phase_ == ActionPhase::Cancelled) - return Result::success(Status::success(StatusCode::NoOp)); + Diagnostic::error(DiagnosticCode::Conflict, "completed action execution cannot be cancelled", "action"))); + if (execution->phase_ == ActionPhase::Failed) return Result::failure(execution->status_); + if (execution->phase_ == ActionPhase::Cancelled) return Result::success(Status::success(StatusCode::NoOp)); - execution->phase_ = ActionPhase::Cancelled; + execution->phase_ = ActionPhase::Cancelled; execution->phaseElapsed_ = Duration::zero(); - execution->lastTick_ = tick; - execution->status_ = cancelledStatus(); + execution->lastTick_ = tick; + execution->status_ = cancelledStatus(); return Result::success(Status::success(StatusCode::Applied)); } diff --git a/src/modules/action/Action.h b/src/modules/action/Action.h index 1db37f8a..92ac652b 100644 --- a/src/modules/action/Action.h +++ b/src/modules/action/Action.h @@ -196,8 +196,8 @@ public: * @brief Evaluate one action condition. * @return ConditionResult or a structured provider failure. */ - [[nodiscard]] virtual Result evaluate( - const ActionDefinition& definition, const ActionRequest& request) const = 0; + [[nodiscard]] virtual Result evaluate(const ActionDefinition& definition, + const ActionRequest& request) const = 0; }; /** @@ -214,8 +214,7 @@ public: * @brief Resolve a validated targeting query. * @return An owning TargetSet or a structured target failure. */ - [[nodiscard]] virtual Result resolve( - const sensing::TargetingQuery& query) const = 0; + [[nodiscard]] virtual Result resolve(const sensing::TargetingQuery& query) const = 0; }; /** @@ -237,9 +236,9 @@ public: * @param cost Canonical cost borrowed for this call. * @return Affordability or a structured provider failure. */ - [[nodiscard]] virtual Result canAfford( - const ActionDefinition& definition, const ActionRequest& request, - const resource::CostSpec& cost) const = 0; + [[nodiscard]] virtual Result canAfford(const ActionDefinition& definition, + const ActionRequest& request, + const resource::CostSpec& cost) const = 0; /** * @brief Reserve a cost at the Active boundary. @@ -252,9 +251,9 @@ public: * an account in the current selection, or unloading that default * account must not discard the route before commit/rollback. */ - [[nodiscard]] virtual Result reserve( - const ActionDefinition& definition, const ActionRequest& request, - const resource::CostSpec& cost) = 0; + [[nodiscard]] virtual Result reserve(const ActionDefinition& definition, + const ActionRequest& request, + const resource::CostSpec& cost) = 0; /** * @brief Commit a reservation by its account nonce. @@ -265,8 +264,7 @@ public: * must leave the reservation rollback-able; a provider must not * report failure after exposing a debit without compensation. */ - [[nodiscard]] virtual Result commit( - const resource::Reservation& reservation) = 0; + [[nodiscard]] virtual Result commit(const resource::Reservation& reservation) = 0; /** * @brief Roll back a reservation by its account nonce. @@ -277,8 +275,7 @@ public: * default account has been unloaded. An unknown nonce must fail * explicitly and must never fall back to the current account. */ - [[nodiscard]] virtual Result rollback( - const resource::Reservation& reservation) = 0; + [[nodiscard]] virtual Result rollback(const resource::Reservation& reservation) = 0; }; /** @@ -293,10 +290,10 @@ class IActionEffectOperation { public: virtual ~IActionEffectOperation() = default; - IActionEffectOperation(const IActionEffectOperation&) = delete; + IActionEffectOperation(const IActionEffectOperation&) = delete; IActionEffectOperation& operator=(const IActionEffectOperation&) = delete; - IActionEffectOperation(IActionEffectOperation&&) = delete; - IActionEffectOperation& operator=(IActionEffectOperation&&) = delete; + IActionEffectOperation(IActionEffectOperation&&) = delete; + IActionEffectOperation& operator=(IActionEffectOperation&&) = delete; /** @brief Apply the prepared operation; must not fail or throw. */ virtual void commit() noexcept = 0; @@ -329,9 +326,10 @@ public: * @param tick Deterministic simulation tick of the Active transition. * @return A move-only staged operation or a structured preparation failure. */ - [[nodiscard]] virtual Result> prepare( - const ActionDefinition& definition, const ActionRequest& request, - const sensing::TargetSet* targets, SimulationTick tick) = 0; + [[nodiscard]] virtual Result> prepare(const ActionDefinition& definition, + const ActionRequest& request, + const sensing::TargetSet* targets, + SimulationTick tick) = 0; }; /** @brief Borrowed composition ports used by one ActionRuntime owner. */ @@ -422,21 +420,24 @@ private: friend class ActionRuntime; ActionExecution(ActionExecutionId id, ActionDefinition definition, ActionRequest request) - : id_(id), definition_(std::move(definition)), request_(std::move(request)), - lastTick_(request_.requestedTick), status_(Status::success(StatusCode::Pending)) {} + : id_(id), + definition_(std::move(definition)), + request_(std::move(request)), + lastTick_(request_.requestedTick), + status_(Status::success(StatusCode::Pending)) {} - ActionExecutionId id_; - ActionDefinition definition_; - ActionRequest request_; - ActionPhase phase_ = ActionPhase::Requested; - Duration phaseElapsed_ = Duration::zero(); - Duration totalElapsed_ = Duration::zero(); - SimulationTick lastTick_ = SimulationTick::zero(); - Status status_; - std::optional resolvedTargets_; - std::optional receipt_; + ActionExecutionId id_; + ActionDefinition definition_; + ActionRequest request_; + ActionPhase phase_ = ActionPhase::Requested; + Duration phaseElapsed_ = Duration::zero(); + Duration totalElapsed_ = Duration::zero(); + SimulationTick lastTick_ = SimulationTick::zero(); + Status status_; + std::optional resolvedTargets_; + std::optional receipt_; std::optional transactionReceipt_; - bool activeExecuted_ = false; + bool activeExecuted_ = false; }; /** @@ -451,8 +452,7 @@ public: SensingTargetingAdapter() = default; /** @copydoc IActionTargetResolver::resolve */ - [[nodiscard]] Result resolve( - const sensing::TargetingQuery& query) const override; + [[nodiscard]] Result resolve(const sensing::TargetingQuery& query) const override; private: sensing::TargetingResolver resolver_; @@ -475,11 +475,11 @@ public: */ explicit ActionRuntime(ActionServices services = {}) : services_(services) {} - ActionRuntime(const ActionRuntime&) = delete; + ActionRuntime(const ActionRuntime&) = delete; ActionRuntime& operator=(const ActionRuntime&) = delete; - ActionRuntime(ActionRuntime&&) = delete; - ActionRuntime& operator=(ActionRuntime&&) = delete; - ~ActionRuntime() = default; + ActionRuntime(ActionRuntime&&) = delete; + ActionRuntime& operator=(ActionRuntime&&) = delete; + ~ActionRuntime() = default; /** * @brief Submit a validated definition/request pair in Requested phase. @@ -488,8 +488,7 @@ public: * @return New execution id or a structured rejection; no state is created * when validation fails. */ - [[nodiscard]] Result submit(ActionDefinition definition, - ActionRequest request); + [[nodiscard]] Result submit(ActionDefinition definition, ActionRequest request); /** * @brief Advance one execution by an injected simulation duration. @@ -499,8 +498,7 @@ public: * @return Transition summary, or a structured failure. A failed/cancelled * execution is observable through find() after the failure. */ - [[nodiscard]] Result advance(ActionExecutionId id, SimulationTick tick, - Duration delta); + [[nodiscard]] Result advance(ActionExecutionId id, SimulationTick tick, Duration delta); /** * @brief Cancel a non-terminal execution before its Active transaction. @@ -535,14 +533,14 @@ private: [[nodiscard]] Result validateExecution(ActionExecution& execution); [[nodiscard]] Result enterActive(ActionExecution& execution, SimulationTick tick); [[nodiscard]] Result addElapsed(ActionExecution& execution, Duration amount); - void transition(ActionExecution& execution, ActionPhase next, SimulationTick tick, - std::vector& transitions); - void failExecution(ActionExecution& execution, Status status, - SimulationTick tick, std::vector* transitions = nullptr); + void transition(ActionExecution& execution, ActionPhase next, SimulationTick tick, + std::vector& transitions); + void failExecution(ActionExecution& execution, Status status, SimulationTick tick, + std::vector* transitions = nullptr); [[nodiscard]] Result nextExecutionId(); - ActionServices services_; - ExecutionStore executions_; + ActionServices services_; + ExecutionStore executions_; ActionExecutionId nextId_{1}; }; diff --git a/src/modules/animation/AnimClipRegistry.cpp b/src/modules/animation/AnimClipRegistry.cpp index ba94a975..697916d0 100644 --- a/src/modules/animation/AnimClipRegistry.cpp +++ b/src/modules/animation/AnimClipRegistry.cpp @@ -33,8 +33,7 @@ std::uint64_t& reloadCallbackFailures() { } void notifyReload(AnimClipRegistry::ReloadEvent event) { - static_cast(reloadObservers().notifyChecked( - []() noexcept { ++reloadCallbackFailures(); }, event)); + static_cast(reloadObservers().notifyChecked([]() noexcept { ++reloadCallbackFailures(); }, event)); } bool isEvaPath(const std::string& path) { @@ -117,14 +116,14 @@ eve::Result AnimClipRegistry::reloadPath(const std::string& path) { delete skeleton; delete fresh; notifyReload({norm, 0, false}); - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "animation clip reload import failed", norm)); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "animation clip reload import failed", norm)); } if (!fresh) { delete skeleton; notifyReload({norm, 0, false}); - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "animation clip reload produced no clip", norm)); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "animation clip reload produced no clip", norm)); } for (AnimClip* clip : it->second) clip->adopt(*fresh); const int refreshed = static_cast(it->second.size()); diff --git a/src/modules/animation/AnimGraph.cpp b/src/modules/animation/AnimGraph.cpp index 5d9afe74..2ba9fbfa 100644 --- a/src/modules/animation/AnimGraph.cpp +++ b/src/modules/animation/AnimGraph.cpp @@ -1,8 +1,8 @@ #include "animation/AnimGraph.h" #include "animation/AnimClip.h" -#include "animation/AnimationTime.h" #include "animation/AnimSkeleton.h" +#include "animation/AnimationTime.h" #include "common/Exception.h" #include @@ -276,7 +276,7 @@ eve::Result AnimGraph::advance(const eve::SimulationStep& step) { auto seconds = detail::secondsForStep(step, hasLastTick_, lastTick_, "AnimGraph"); if (!seconds) return eve::Result::failure(seconds.status()); updateUnchecked(std::move(seconds).takeValue()); - lastTick_ = step.tick; + lastTick_ = step.tick; hasLastTick_ = true; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } diff --git a/src/modules/animation/AnimGraph.h b/src/modules/animation/AnimGraph.h index 7db81980..5d23824f 100644 --- a/src/modules/animation/AnimGraph.h +++ b/src/modules/animation/AnimGraph.h @@ -96,8 +96,8 @@ private: AnimPose output_; int root_ = -1; unsigned generation_ = 0; - eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); - bool hasLastTick_ = false; + eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); + bool hasLastTick_ = false; void updateUnchecked(float dt); }; diff --git a/src/modules/animation/AnimLayerMixer.cpp b/src/modules/animation/AnimLayerMixer.cpp index 73a8e7bc..731d14e9 100644 --- a/src/modules/animation/AnimLayerMixer.cpp +++ b/src/modules/animation/AnimLayerMixer.cpp @@ -1,8 +1,8 @@ #include "animation/AnimLayerMixer.h" -#include "animation/AnimationTime.h" #include "animation/AnimPlayer.h" #include "animation/AnimSkeleton.h" +#include "animation/AnimationTime.h" #include "common/Exception.h" #include @@ -195,8 +195,7 @@ eve::Result AnimLayerMixer::advance(const eve::SimulationStep& step) { return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::Conflict, "animation mixer base player already consumed this tick")); for (const Layer& layer : layers_) { - if (layer.enabled && layer.player && layer.player->hasCurrentTick() && - step.tick <= layer.player->currentTick()) + if (layer.enabled && layer.player && layer.player->hasCurrentTick() && step.tick <= layer.player->currentTick()) return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::Conflict, "animation mixer layer player already consumed this tick")); } @@ -210,7 +209,7 @@ eve::Result AnimLayerMixer::advance(const eve::SimulationStep& step) { if (!result) return eve::Result::failure(result.status()); } compose(); - lastTick_ = step.tick; + lastTick_ = step.tick; hasLastTick_ = true; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } diff --git a/src/modules/animation/AnimLayerMixer.h b/src/modules/animation/AnimLayerMixer.h index e68c3c42..0048f1e2 100644 --- a/src/modules/animation/AnimLayerMixer.h +++ b/src/modules/animation/AnimLayerMixer.h @@ -111,8 +111,8 @@ private: AnimPose pose_; std::vector layers_; std::vector events_; - eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); - bool hasLastTick_ = false; + eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); + bool hasLastTick_ = false; void compose(); }; diff --git a/src/modules/animation/AnimPlayer.cpp b/src/modules/animation/AnimPlayer.cpp index 244780c0..c5c714dd 100644 --- a/src/modules/animation/AnimPlayer.cpp +++ b/src/modules/animation/AnimPlayer.cpp @@ -223,18 +223,18 @@ void AnimPlayer::updateUnchecked(float dt) { eve::Result AnimPlayer::advance(const eve::SimulationStep& step) { if (step.delta.nanoseconds() < 0) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "animation player delta must be non-negative")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "animation player delta must be non-negative")); if (hasLastTick_ && step.tick <= lastTick_) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, "animation player tick must advance monotonically")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "animation player tick must advance monotonically")); const float dt = static_cast(step.delta.seconds()); if (!std::isfinite(dt)) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "animation player delta is outside float range")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "animation player delta is outside float range")); updateUnchecked(dt); - lastTick_ = step.tick; + lastTick_ = step.tick; hasLastTick_ = true; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } @@ -245,17 +245,14 @@ void AnimPlayer::update(float dt) { duration.ignore("legacy animation player update received an invalid duration"); return; } - auto nextTick = hasLastTick_ ? lastTick_.incremented() - : std::optional(eve::SimulationTick(1)); + auto nextTick = hasLastTick_ ? lastTick_.incremented() : std::optional(eve::SimulationTick(1)); if (!nextTick) { - eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, - "animation player simulation tick overflow")) + eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, + "animation player simulation tick overflow")) .ignore("legacy animation player update tick overflow"); return; } - advance({*nextTick, std::move(duration).takeValue()}) - .ignore("legacy animation player update facade"); + advance({*nextTick, std::move(duration).takeValue()}).ignore("legacy animation player update facade"); } } // namespace eve::animation diff --git a/src/modules/animation/AnimStateMachine.cpp b/src/modules/animation/AnimStateMachine.cpp index 3d966c5a..9e3505ce 100644 --- a/src/modules/animation/AnimStateMachine.cpp +++ b/src/modules/animation/AnimStateMachine.cpp @@ -304,8 +304,8 @@ void AnimStateMachine::updateUnchecked(float dt) { eve::Result AnimStateMachine::advance(const eve::SimulationStep& step) { if (step.delta.nanoseconds() < 0) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "animation state-machine delta must be non-negative")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "animation state-machine delta must be non-negative")); if (hasLastTick_ && step.tick <= lastTick_) return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::Conflict, "animation state-machine tick must advance monotonically")); @@ -315,7 +315,7 @@ eve::Result AnimStateMachine::advance(const eve::SimulationStep& step) { return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::InvalidArgument, "animation state-machine delta is outside float range")); updateUnchecked(dt); - lastTick_ = step.tick; + lastTick_ = step.tick; hasLastTick_ = true; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } @@ -326,17 +326,14 @@ void AnimStateMachine::update(float dt) { duration.ignore("legacy animation state-machine update received an invalid duration"); return; } - auto nextTick = hasLastTick_ ? lastTick_.incremented() - : std::optional(eve::SimulationTick(1)); + auto nextTick = hasLastTick_ ? lastTick_.incremented() : std::optional(eve::SimulationTick(1)); if (!nextTick) { - eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, - "animation state-machine simulation tick overflow")) + eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, + "animation state-machine simulation tick overflow")) .ignore("legacy animation state-machine update tick overflow"); return; } - advance({*nextTick, std::move(duration).takeValue()}) - .ignore("legacy animation state-machine update facade"); + advance({*nextTick, std::move(duration).takeValue()}).ignore("legacy animation state-machine update facade"); } bool AnimStateMachine::captureState(StateValue& out) const { @@ -383,8 +380,8 @@ bool AnimStateMachine::restoreState(const StateValue& in, std::string* err) { return false; } - eve::SimulationTick restoredTick = eve::SimulationTick::zero(); - bool restoredHasTick = false; + eve::SimulationTick restoredTick = eve::SimulationTick::zero(); + bool restoredHasTick = false; if (const StateValue* hasTick = in.find("hasSimulationTick"); hasTick && hasTick->isBool()) restoredHasTick = hasTick->asBool(); if (restoredHasTick) { @@ -394,8 +391,8 @@ bool AnimStateMachine::restoreState(const StateValue& in, std::string* err) { return false; } try { - std::size_t parsed = 0; - const std::uint64_t value = std::stoull(tick->asString(), &parsed); + std::size_t parsed = 0; + const std::uint64_t value = std::stoull(tick->asString(), &parsed); if (parsed != tick->asString().size()) throw std::invalid_argument("trailing characters"); restoredTick = eve::SimulationTick(value); } catch (...) { @@ -437,7 +434,7 @@ bool AnimStateMachine::restoreState(const StateValue& in, std::string* err) { blendElapsed_ = static_cast(stateNumber(*be)); if (const StateValue* bl = in.find("blending"); bl && bl->isBool()) blending_ = bl->asBool(); started_ = wasStarted; - lastTick_ = restoredTick; + lastTick_ = restoredTick; hasLastTick_ = restoredHasTick; if (started_ && !currentState_.empty()) { @@ -455,8 +452,8 @@ bool AnimStateMachine::resetToDefaults() { stateTime_ = 0.f; blendDuration_ = 0.f; blendElapsed_ = 0.f; - lastTick_ = eve::SimulationTick::zero(); - hasLastTick_ = false; + lastTick_ = eve::SimulationTick::zero(); + hasLastTick_ = false; if (!stateOrder_.empty()) { setEntry(stateOrder_.front()); return true; diff --git a/src/modules/animation/AnimStateMachine.h b/src/modules/animation/AnimStateMachine.h index b0bbc2d2..b07ff4c5 100644 --- a/src/modules/animation/AnimStateMachine.h +++ b/src/modules/animation/AnimStateMachine.h @@ -1,8 +1,8 @@ #pragma once #include "animation/AnimPose.h" -#include "common/Time.h" #include "common/StateValue.h" +#include "common/Time.h" #include #include @@ -65,7 +65,7 @@ public: bool isBlending() const { return blending_; } /** @brief Advance state and pose using one scheduler-owned deterministic step. */ - [[nodiscard]] eve::Result advance(const eve::SimulationStep& step); + [[nodiscard]] eve::Result advance(const eve::SimulationStep &step); /** @brief Last scheduler tick consumed by the checked state-machine API. */ [[nodiscard]] eve::SimulationTick currentTick() const noexcept { return lastTick_; } @@ -139,8 +139,8 @@ private: float blendElapsed_ = 0.f; bool blending_ = false; bool started_ = false; - eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); - bool hasLastTick_ = false; + eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); + bool hasLastTick_ = false; }; } // namespace eve::animation diff --git a/src/modules/animation/AnimSyncGroup.cpp b/src/modules/animation/AnimSyncGroup.cpp index b9fe7260..0ccb6388 100644 --- a/src/modules/animation/AnimSyncGroup.cpp +++ b/src/modules/animation/AnimSyncGroup.cpp @@ -1,8 +1,8 @@ #include "animation/AnimSyncGroup.h" -#include "animation/AnimationTime.h" #include "animation/AnimClip.h" #include "animation/AnimPlayer.h" +#include "animation/AnimationTime.h" #include "common/Exception.h" #include @@ -25,7 +25,7 @@ eve::Result AnimSyncGroup::advance(const eve::SimulationStep& step) { (void)std::move(seconds).takeValue(); usedMarkerSync_ = false; if (entries_.empty()) { - lastTick_ = step.tick; + lastTick_ = step.tick; hasLastTick_ = true; return eve::Result::success(eve::Status::success(eve::StatusCode::NoOp)); } @@ -35,11 +35,11 @@ eve::Result AnimSyncGroup::advance(const eve::SimulationStep& step) { eve::DiagnosticCode::Conflict, "animation sync-group player already consumed this tick")); } Entry& leader = entries_[static_cast(leader_)]; - auto leaderResult = leader.player->advance(step); + auto leaderResult = leader.player->advance(step); if (!leaderResult) return eve::Result::failure(leaderResult.status()); AnimClip* leaderClip = leader.player->getClip(); if (!leaderClip || leaderClip->getDuration() <= 1e-8f) { - lastTick_ = step.tick; + lastTick_ = step.tick; hasLastTick_ = true; return eve::Result::success(eve::Status::success(eve::StatusCode::NoOp)); } @@ -47,7 +47,7 @@ eve::Result AnimSyncGroup::advance(const eve::SimulationStep& step) { for (int i = 0; i < getCount(); ++i) { if (i == leader_) continue; AnimPlayer* player = entries_[static_cast(i)].player; - auto playerResult = player->advance(step); + auto playerResult = player->advance(step); if (!playerResult) return eve::Result::failure(playerResult.status()); AnimClip* clip = player->getClip(); if (!clip || clip->getDuration() <= 1e-8f) continue; @@ -61,7 +61,7 @@ eve::Result AnimSyncGroup::advance(const eve::SimulationStep& step) { mappedTime += entries_[static_cast(i)].phaseOffset * clip->getDuration(); player->setTime(clip->wrapTime(mappedTime)); } - lastTick_ = step.tick; + lastTick_ = step.tick; hasLastTick_ = true; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } diff --git a/src/modules/animation/AnimSyncGroup.h b/src/modules/animation/AnimSyncGroup.h index d273f4d7..ae6325a4 100644 --- a/src/modules/animation/AnimSyncGroup.h +++ b/src/modules/animation/AnimSyncGroup.h @@ -30,8 +30,8 @@ private: int leader_ = 0; float phase_ = 0.f; bool usedMarkerSync_ = false; - eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); - bool hasLastTick_ = false; + eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); + bool hasLastTick_ = false; }; } // namespace eve::animation diff --git a/src/modules/animation/AnimTrail.cpp b/src/modules/animation/AnimTrail.cpp index 9f5e9570..b23c0bee 100644 --- a/src/modules/animation/AnimTrail.cpp +++ b/src/modules/animation/AnimTrail.cpp @@ -1,7 +1,7 @@ #include "animation/AnimTrail.h" -#include "animation/AnimationTime.h" #include "animation/AnimPose.h" +#include "animation/AnimationTime.h" #include "common/Exception.h" #include "graphics/Graphics.h" @@ -155,11 +155,11 @@ void AnimTrail::updateUnchecked(float dt) { } } -eve::Result AnimTrail::advance(const eve::SimulationStep& step) { +eve::Result AnimTrail::advance(const eve::SimulationStep &step) { auto seconds = detail::secondsForStep(step, hasLastTick_, lastTick_, "AnimTrail"); if (!seconds) return eve::Result::failure(seconds.status()); updateUnchecked(std::move(seconds).takeValue()); - lastTick_ = step.tick; + lastTick_ = step.tick; hasLastTick_ = true; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } diff --git a/src/modules/animation/AnimTrail.h b/src/modules/animation/AnimTrail.h index 8f6af26a..275ae351 100644 --- a/src/modules/animation/AnimTrail.h +++ b/src/modules/animation/AnimTrail.h @@ -95,7 +95,7 @@ public: void clear(); /** @brief Age samples by one scheduler-owned deterministic step. */ - [[nodiscard]] eve::Result advance(const eve::SimulationStep& step); + [[nodiscard]] eve::Result advance(const eve::SimulationStep &step); /** @brief Legacy seconds facade; explicitly forwards to advance(). */ void update(float dt); @@ -143,8 +143,8 @@ private: float drawOffsetY_ = 0.f; std::vector points_; - eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); - bool hasLastTick_ = false; + eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); + bool hasLastTick_ = false; void updateUnchecked(float dt); }; diff --git a/src/modules/animation/Animation.cpp b/src/modules/animation/Animation.cpp index 2586fcd1..6c378c65 100644 --- a/src/modules/animation/Animation.cpp +++ b/src/modules/animation/Animation.cpp @@ -429,18 +429,18 @@ void Animation::unregisterSpineAnim(SpineAnim *a) { if (a->owner() == this) a->setOwner(nullptr); } -eve::Result Animation::advance(const eve::SimulationStep& step) { +eve::Result Animation::advance(const eve::SimulationStep &step) { if (step.delta.nanoseconds() < 0) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "animation simulation delta must be non-negative")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "animation simulation delta must be non-negative")); if (hasLastTick_ && step.tick <= lastTick_) return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::Conflict, "animation simulation tick must advance monotonically")); const float dt = static_cast(step.delta.seconds()); if (!std::isfinite(dt)) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "animation simulation delta is outside float range")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "animation simulation delta is outside float range")); // Preflight every live child before mutating any of them. A child may also // be driven directly by a caller, so the host must preserve all-or-none @@ -485,7 +485,7 @@ eve::Result Animation::advance(const eve::SimulationStep& step) { } } - lastTick_ = step.tick; + lastTick_ = step.tick; hasLastTick_ = true; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } @@ -497,17 +497,14 @@ void Animation::update(float dt) { duration.ignore("legacy animation update received an invalid duration"); return; } - auto nextTick = hasLastTick_ ? lastTick_.incremented() - : std::optional(eve::SimulationTick(1)); + auto nextTick = hasLastTick_ ? lastTick_.incremented() : std::optional(eve::SimulationTick(1)); if (!nextTick) { - eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, - "animation simulation tick overflow")) + eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "animation simulation tick overflow")) .ignore("legacy animation update tick overflow"); return; } - advance({*nextTick, std::move(duration).takeValue()}) - .ignore("legacy animation update facade"); + advance({*nextTick, std::move(duration).takeValue()}).ignore("legacy animation update facade"); } int Animation::getActiveCount() const { diff --git a/src/modules/animation/Animation.h b/src/modules/animation/Animation.h index e5858fd5..4b5bc543 100644 --- a/src/modules/animation/Animation.h +++ b/src/modules/animation/Animation.h @@ -1,8 +1,8 @@ #pragma once +#include "animation/Tween.h" #include "common/Module.h" #include "common/Time.h" -#include "animation/Tween.h" #include #include @@ -174,7 +174,7 @@ public: AnimTrail *newTrail(int capacity = 64); /** @brief Advance all registered tweens, sprite anims, and spine anims. */ - [[nodiscard]] eve::Result advance(const eve::SimulationStep& step); + [[nodiscard]] eve::Result advance(const eve::SimulationStep &step); /** @brief Last scheduler tick consumed by the checked module pump. */ [[nodiscard]] eve::SimulationTick currentTick() const noexcept { return lastTick_; } diff --git a/src/modules/animation/AnimationTime.h b/src/modules/animation/AnimationTime.h index 91351b55..1c6ed3a0 100644 --- a/src/modules/animation/AnimationTime.h +++ b/src/modules/animation/AnimationTime.h @@ -3,49 +3,39 @@ #include "common/Time.h" #include +#include #include #include -#include namespace eve::animation::detail { /** @brief Validate a scheduler step before an animation object mutates state. */ -inline eve::Result secondsForStep(const eve::SimulationStep& step, - bool hasLastTick, - eve::SimulationTick lastTick, - const char* owner) { +inline eve::Result secondsForStep(const eve::SimulationStep& step, bool hasLastTick, + eve::SimulationTick lastTick, const char* owner) { if (step.delta.nanoseconds() < 0) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - std::string(owner) + " simulation delta must be non-negative")); + eve::DiagnosticCode::InvalidArgument, std::string(owner) + " simulation delta must be non-negative")); if (hasLastTick && step.tick <= lastTick) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, - std::string(owner) + " simulation tick must advance monotonically")); + eve::DiagnosticCode::Conflict, std::string(owner) + " simulation tick must advance monotonically")); const float seconds = static_cast(step.delta.seconds()); if (!std::isfinite(seconds)) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - std::string(owner) + " simulation delta is outside float range")); + eve::DiagnosticCode::InvalidArgument, std::string(owner) + " simulation delta is outside float range")); return eve::Result::success(seconds); } /** @brief Convert a legacy seconds call to the next local scheduler step. */ -inline eve::Result legacyStep(float seconds, - bool hasLastTick, - eve::SimulationTick lastTick, +inline eve::Result legacyStep(float seconds, bool hasLastTick, eve::SimulationTick lastTick, const char* owner) { auto duration = eve::Duration::fromSeconds(seconds); if (!duration) return eve::Result::failure(duration.status()); - auto nextTick = hasLastTick ? lastTick.incremented() - : std::optional(eve::SimulationTick(1)); + auto nextTick = hasLastTick ? lastTick.incremented() : std::optional(eve::SimulationTick(1)); if (!nextTick) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, - std::string(owner) + " simulation tick overflow")); - return eve::Result::success( - eve::SimulationStep{*nextTick, std::move(duration).takeValue()}); + eve::DiagnosticCode::InvariantViolation, std::string(owner) + " simulation tick overflow")); + return eve::Result::success(eve::SimulationStep{*nextTick, std::move(duration).takeValue()}); } } // namespace eve::animation::detail diff --git a/src/modules/animation/ControlAnim.cpp b/src/modules/animation/ControlAnim.cpp index 048ad303..612dcf62 100644 --- a/src/modules/animation/ControlAnim.cpp +++ b/src/modules/animation/ControlAnim.cpp @@ -159,11 +159,11 @@ void ControlAnim::updateUnchecked(float dt) { } } -eve::Result ControlAnim::advance(const eve::SimulationStep& step) { +eve::Result ControlAnim::advance(const eve::SimulationStep &step) { auto seconds = detail::secondsForStep(step, hasLastTick_, lastTick_, "ControlAnim"); if (!seconds) return eve::Result::failure(seconds.status()); updateUnchecked(std::move(seconds).takeValue()); - lastTick_ = step.tick; + lastTick_ = step.tick; hasLastTick_ = true; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } diff --git a/src/modules/animation/ControlAnim.h b/src/modules/animation/ControlAnim.h index 48ae8691..91b4c32a 100644 --- a/src/modules/animation/ControlAnim.h +++ b/src/modules/animation/ControlAnim.h @@ -60,7 +60,7 @@ public: std::string getPropertyName(int index) const; /** @brief Advance all channels by one scheduler-owned deterministic step. */ - [[nodiscard]] eve::Result advance(const eve::SimulationStep& step); + [[nodiscard]] eve::Result advance(const eve::SimulationStep &step); /** @brief Legacy seconds facade; explicitly forwards to advance(). */ void update(float dt); @@ -90,8 +90,8 @@ private: std::unordered_map channels_; std::vector order_; - eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); - bool hasLastTick_ = false; + eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); + bool hasLastTick_ = false; void updateUnchecked(float dt); }; diff --git a/src/modules/animation/ControlPose.cpp b/src/modules/animation/ControlPose.cpp index 105a94cb..709662aa 100644 --- a/src/modules/animation/ControlPose.cpp +++ b/src/modules/animation/ControlPose.cpp @@ -300,11 +300,11 @@ void ControlPose::updateUnchecked(float dt) { writePoseFromState(); } -eve::Result ControlPose::advance(const eve::SimulationStep& step) { +eve::Result ControlPose::advance(const eve::SimulationStep &step) { auto seconds = detail::secondsForStep(step, hasLastTick_, lastTick_, "ControlPose"); if (!seconds) return eve::Result::failure(seconds.status()); updateUnchecked(std::move(seconds).takeValue()); - lastTick_ = step.tick; + lastTick_ = step.tick; hasLastTick_ = true; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } diff --git a/src/modules/animation/ControlPose.h b/src/modules/animation/ControlPose.h index 0a44bb34..37bbe10a 100644 --- a/src/modules/animation/ControlPose.h +++ b/src/modules/animation/ControlPose.h @@ -52,7 +52,7 @@ public: AnimPose *getTargetPose(); /** @brief Advance pose dynamics by one scheduler-owned deterministic step. */ - [[nodiscard]] eve::Result advance(const eve::SimulationStep& step); + [[nodiscard]] eve::Result advance(const eve::SimulationStep &step); /** @brief Legacy seconds facade; explicitly forwards to advance(). */ void update(float dt); @@ -94,8 +94,8 @@ private: std::vector bones_; bool hasTarget_ = false; - eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); - bool hasLastTick_ = false; + eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); + bool hasLastTick_ = false; void updateUnchecked(float dt); }; diff --git a/src/modules/animation/MotionMatcher.cpp b/src/modules/animation/MotionMatcher.cpp index 783fed0c..c687b450 100644 --- a/src/modules/animation/MotionMatcher.cpp +++ b/src/modules/animation/MotionMatcher.cpp @@ -1,8 +1,8 @@ #include "animation/MotionMatcher.h" -#include "animation/AnimationTime.h" #include "animation/AnimClip.h" #include "animation/AnimSkeleton.h" +#include "animation/AnimationTime.h" #include "animation/MotionDatabase.h" #include "common/Exception.h" @@ -241,7 +241,7 @@ eve::Result MotionMatcher::advance(const eve::SimulationStep& step) { auto seconds = detail::secondsForStep(step, hasLastTick_, lastTick_, "MotionMatcher"); if (!seconds) return eve::Result::failure(seconds.status()); updateUnchecked(std::move(seconds).takeValue()); - lastTick_ = step.tick; + lastTick_ = step.tick; hasLastTick_ = true; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } diff --git a/src/modules/animation/MotionMatcher.h b/src/modules/animation/MotionMatcher.h index c118e0fd..1a7b5808 100644 --- a/src/modules/animation/MotionMatcher.h +++ b/src/modules/animation/MotionMatcher.h @@ -95,8 +95,8 @@ private: float matchedTime_ = 0.f; float lastCost_ = 0.f; bool playing_ = false; - eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); - bool hasLastTick_ = false; + eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); + bool hasLastTick_ = false; void updateUnchecked(float dt); }; diff --git a/src/modules/animation/SpineAnim.cpp b/src/modules/animation/SpineAnim.cpp index 3288ad26..95748b8b 100644 --- a/src/modules/animation/SpineAnim.cpp +++ b/src/modules/animation/SpineAnim.cpp @@ -291,11 +291,11 @@ bool SpineAnim::updateUnchecked(float dt) { return true; } -eve::Result SpineAnim::advance(const eve::SimulationStep& step) { +eve::Result SpineAnim::advance(const eve::SimulationStep &step) { auto seconds = detail::secondsForStep(step, hasLastTick_, lastTick_, "SpineAnim"); if (!seconds) return eve::Result::failure(seconds.status()); updateUnchecked(std::move(seconds).takeValue()); - lastTick_ = step.tick; + lastTick_ = step.tick; hasLastTick_ = true; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } diff --git a/src/modules/animation/SpineAnim.h b/src/modules/animation/SpineAnim.h index 29302443..3199b77e 100644 --- a/src/modules/animation/SpineAnim.h +++ b/src/modules/animation/SpineAnim.h @@ -85,7 +85,7 @@ public: void apply(); /** @brief Advance playback by one scheduler-owned deterministic step. */ - [[nodiscard]] eve::Result advance(const eve::SimulationStep& step); + [[nodiscard]] eve::Result advance(const eve::SimulationStep &step); /** @brief Whether this Spine animation has consumed a scheduler step. */ [[nodiscard]] bool hasCurrentTick() const noexcept { return hasLastTick_; } /** @brief Last scheduler tick consumed by this Spine animation. */ @@ -159,7 +159,7 @@ private: int layer_ = 0; float r_ = 1.f, g_ = 1.f, b_ = 1.f, a_ = 1.f; std::vector drawSlots_; - eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); + eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); bool hasLastTick_ = false; bool updateUnchecked(float dt); diff --git a/src/modules/animation/SpriteAnim.cpp b/src/modules/animation/SpriteAnim.cpp index 17abe88e..6c0649b5 100644 --- a/src/modules/animation/SpriteAnim.cpp +++ b/src/modules/animation/SpriteAnim.cpp @@ -288,11 +288,11 @@ bool SpriteAnim::updateUnchecked(float dt) { return true; } -eve::Result SpriteAnim::advance(const eve::SimulationStep& step) { +eve::Result SpriteAnim::advance(const eve::SimulationStep &step) { auto seconds = detail::secondsForStep(step, hasLastTick_, lastTick_, "SpriteAnim"); if (!seconds) return eve::Result::failure(seconds.status()); updateUnchecked(std::move(seconds).takeValue()); - lastTick_ = step.tick; + lastTick_ = step.tick; hasLastTick_ = true; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } diff --git a/src/modules/animation/SpriteAnim.h b/src/modules/animation/SpriteAnim.h index 2f6a8c7d..2fcf1904 100644 --- a/src/modules/animation/SpriteAnim.h +++ b/src/modules/animation/SpriteAnim.h @@ -97,7 +97,7 @@ public: * Auto-applies to boundQuad when sheet is set. */ /** @brief Advance playback by one scheduler-owned deterministic step. */ - [[nodiscard]] eve::Result advance(const eve::SimulationStep& step); + [[nodiscard]] eve::Result advance(const eve::SimulationStep &step); /** @brief Whether this sprite animation has consumed a scheduler step. */ [[nodiscard]] bool hasCurrentTick() const noexcept { return hasLastTick_; } /** @brief Last scheduler tick consumed by this sprite animation. */ @@ -142,8 +142,8 @@ private: std::string speedCurveInterpolation_ = "linear"; SpriteClip *queuedClip_ = nullptr; std::string pendingEvent_; - eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); - bool hasLastTick_ = false; + eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); + bool hasLastTick_ = false; bool updateUnchecked(float dt); }; diff --git a/src/modules/animation/Tween.cpp b/src/modules/animation/Tween.cpp index df7fd719..059147cc 100644 --- a/src/modules/animation/Tween.cpp +++ b/src/modules/animation/Tween.cpp @@ -1,7 +1,7 @@ #include "animation/Tween.h" -#include "animation/AnimationTime.h" #include "animation/Animation.h" +#include "animation/AnimationTime.h" #include "common/Exception.h" @@ -292,11 +292,11 @@ bool Tween::updateUnchecked(float dt) { return isActive(); } -eve::Result Tween::advance(const eve::SimulationStep& step) { +eve::Result Tween::advance(const eve::SimulationStep &step) { auto seconds = detail::secondsForStep(step, hasLastTick_, lastTick_, "Tween"); if (!seconds) return eve::Result::failure(seconds.status()); updateUnchecked(std::move(seconds).takeValue()); - lastTick_ = step.tick; + lastTick_ = step.tick; hasLastTick_ = true; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } diff --git a/src/modules/animation/Tween.h b/src/modules/animation/Tween.h index 9fadd5c6..94d71cd6 100644 --- a/src/modules/animation/Tween.h +++ b/src/modules/animation/Tween.h @@ -82,7 +82,7 @@ public: float getEasedProgress() const; /** @brief Advance by one scheduler-owned deterministic step. */ - [[nodiscard]] eve::Result advance(const eve::SimulationStep& step); + [[nodiscard]] eve::Result advance(const eve::SimulationStep &step); /** @brief Whether this tween has consumed a scheduler step. */ [[nodiscard]] bool hasCurrentTick() const noexcept { return hasLastTick_; } /** @brief Last scheduler tick consumed by this tween. */ @@ -139,7 +139,7 @@ private: State state_ = State::Idle; std::unordered_map tracks_; std::vector order_; - eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); + eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); bool hasLastTick_ = false; bool updateUnchecked(float dt); diff --git a/src/modules/attributes/AttributeProjection.cpp b/src/modules/attributes/AttributeProjection.cpp index 2f37c802..6161e9f6 100644 --- a/src/modules/attributes/AttributeProjection.cpp +++ b/src/modules/attributes/AttributeProjection.cpp @@ -11,13 +11,13 @@ namespace { template Result failure(DiagnosticCode code, std::string message, std::string path = {}) { - return Result::failure(Diagnostic::error(code, std::move(message), std::move(path), {}, - "attributes.projection")); + return Result::failure( + Diagnostic::error(code, std::move(message), std::move(path), {}, "attributes.projection")); } Result failureVoid(DiagnosticCode code, std::string message, std::string path = {}) { - return Result::failure(Diagnostic::error(code, std::move(message), std::move(path), {}, - "attributes.projection")); + return Result::failure( + Diagnostic::error(code, std::move(message), std::move(path), {}, "attributes.projection")); } bool sameOwner(const ecs::EntityHandle& left, const ecs::EntityHandle& right) noexcept { @@ -35,15 +35,15 @@ AttributeProjection::AttributeProjection(std::string subject) : values_(std::mov Result AttributeProjection::bindOwner(ecs::EntityHandle owner) { if (owner.table == nullptr || ecs::try_get(owner) == nullptr) - return failureVoid(DiagnosticCode::StaleHandle, - "attribute projection owner is not a live ECS generation", "owner"); + return failureVoid(DiagnosticCode::StaleHandle, "attribute projection owner is not a live ECS generation", + "owner"); if (owner_.table == nullptr) { owner_ = owner; return Result::success(Status::success(StatusCode::Applied)); } if (!sameOwner(owner_, owner)) - return failureVoid(DiagnosticCode::Conflict, - "attribute projection cannot be rebound to another owner", "owner"); + return failureVoid(DiagnosticCode::Conflict, "attribute projection cannot be rebound to another owner", + "owner"); return Result::success(Status::success(StatusCode::NoOp)); } @@ -58,31 +58,29 @@ bool AttributeProjection::ownsSameLiveEntity(ecs::EntityHandle owner) const noex Result AttributeProjection::advanceRevision() { const auto next = revision_.incremented(); if (!next) - return failureVoid(DiagnosticCode::InvariantViolation, - "attribute projection revision overflowed", "revision"); + return failureVoid(DiagnosticCode::InvariantViolation, "attribute projection revision overflowed", "revision"); revision_ = *next; return Result::success(Status::success(StatusCode::Applied)); } Result AttributeProjection::initialize(std::span values) { - if (initialized_) - return Result::success(Status::success(StatusCode::NoOp)); + if (initialized_) return Result::success(Status::success(StatusCode::NoOp)); AttributeSet candidate = values_; for (const auto& entry : values) { if (entry.attribute.empty() || !std::isfinite(entry.base)) - return failureVoid(DiagnosticCode::InvalidArgument, - "attribute projection seed requires finite named bases", "bases"); + return failureVoid(DiagnosticCode::InvalidArgument, "attribute projection seed requires finite named bases", + "bases"); candidate.setBase(entry.attribute, entry.base); } - values_ = std::move(candidate); + values_ = std::move(candidate); initialized_ = true; return advanceRevision(); } Result AttributeProjection::setBase(std::string_view attribute, double value) { if (attribute.empty() || !std::isfinite(value)) - return failureVoid(DiagnosticCode::InvalidArgument, - "attribute base requires a non-empty finite value", "attribute"); + return failureVoid(DiagnosticCode::InvalidArgument, "attribute base requires a non-empty finite value", + "attribute"); auto next = advanceRevision(); if (!next) return next; values_.setBase(std::string(attribute), value); @@ -92,8 +90,8 @@ Result AttributeProjection::setBase(std::string_view attribute, double val Result AttributeProjection::modifyBase(std::string_view attribute, double delta) { if (attribute.empty() || !std::isfinite(delta)) - return failureVoid(DiagnosticCode::InvalidArgument, - "attribute delta requires a non-empty finite value", "attribute"); + return failureVoid(DiagnosticCode::InvalidArgument, "attribute delta requires a non-empty finite value", + "attribute"); auto next = advanceRevision(); if (!next) return next; values_.modifyBase(std::string(attribute), delta); @@ -104,8 +102,7 @@ Result AttributeProjection::modifyBase(std::string_view attribute, double Result AttributeProjection::getFinal(std::string_view attribute, double fallback) const { if (attribute.empty() || !std::isfinite(fallback)) return failure(DiagnosticCode::InvalidArgument, - "attribute query requires a non-empty finite key/fallback", - "attribute"); + "attribute query requires a non-empty finite key/fallback", "attribute"); return Result::success(values_.getFinal(std::string(attribute), fallback)); } @@ -122,15 +119,14 @@ Result AttributeProjection::addModifier(AttributeModifier modifier) return result; } -Result AttributeProjection::snapshot( - std::span attributes) const { +Result AttributeProjection::snapshot(std::span attributes) const { for (const auto name : attributes) { if (name.empty()) return failure(DiagnosticCode::InvalidArgument, - "snapshot attribute name is empty", "attributes"); + "snapshot attribute name is empty", "attributes"); } AttributeProjectionSnapshot result; - result.owner = owner_; + result.owner = owner_; result.capturedRevision = revision_; result.bases.reserve(attributes.size()); for (const auto name : attributes) @@ -138,31 +134,28 @@ Result AttributeProjection::snapshot( for (int index = 0; index < values_.modifierCount(); ++index) { const auto* modifier = values_.modifierAt(index); - if (modifier != nullptr && contains(attributes, modifier->attribute)) - result.modifiers.push_back(*modifier); + if (modifier != nullptr && contains(attributes, modifier->attribute)) result.modifiers.push_back(*modifier); } return Result::success(std::move(result)); } -Result AttributeProjection::restore(const AttributeProjectionSnapshot& snapshotValue, - Revision expectedRevision) { +Result AttributeProjection::restore(const AttributeProjectionSnapshot& snapshotValue, Revision expectedRevision) { if (!ownsSameLiveEntity(snapshotValue.owner)) - return failureVoid(DiagnosticCode::StaleHandle, - "attribute snapshot owner is stale or does not match", "owner"); + return failureVoid(DiagnosticCode::StaleHandle, "attribute snapshot owner is stale or does not match", "owner"); if (revision_ != expectedRevision) - return failureVoid(DiagnosticCode::Conflict, - "attribute snapshot revision does not match current state", "revision"); + return failureVoid(DiagnosticCode::Conflict, "attribute snapshot revision does not match current state", + "revision"); std::vector names; names.reserve(snapshotValue.bases.size()); for (const auto& base : snapshotValue.bases) { if (base.attribute.empty() || !std::isfinite(base.base)) - return failureVoid(DiagnosticCode::InvalidArgument, - "attribute snapshot contains invalid base data", "bases"); + return failureVoid(DiagnosticCode::InvalidArgument, "attribute snapshot contains invalid base data", + "bases"); names.push_back(base.attribute); } - AttributeSet candidate = values_; + AttributeSet candidate = values_; std::vector toRemove; for (int index = 0; index < candidate.modifierCount(); ++index) { const auto* modifier = candidate.modifierAt(index); @@ -170,28 +163,28 @@ Result AttributeProjection::restore(const AttributeProjectionSnapshot& sna } for (const auto& id : toRemove) { auto removed = candidate.removeModifier(id); - if (!removed) return failureVoid(DiagnosticCode::InvariantViolation, - "attribute snapshot could not remove candidate modifier", "modifiers"); + if (!removed) + return failureVoid(DiagnosticCode::InvariantViolation, + "attribute snapshot could not remove candidate modifier", "modifiers"); } for (const auto& base : snapshotValue.bases) candidate.setBase(base.attribute, base.base); auto modifiers = snapshotValue.modifiers; std::stable_sort(modifiers.begin(), modifiers.end(), - [](const auto& left, const auto& right) { - return left.sequence < right.sequence; - }); + [](const auto& left, const auto& right) { return left.sequence < right.sequence; }); for (const auto& modifier : modifiers) { if (!contains(names, modifier.attribute)) - return failureVoid(DiagnosticCode::InvalidArgument, - "attribute snapshot modifier is outside its allow-list", "modifiers"); + return failureVoid(DiagnosticCode::InvalidArgument, "attribute snapshot modifier is outside its allow-list", + "modifiers"); auto added = candidate.addModifier(modifier); - if (!added) return failureVoid(DiagnosticCode::InvalidArgument, - "attribute snapshot contains an invalid modifier", "modifiers"); + if (!added) + return failureVoid(DiagnosticCode::InvalidArgument, "attribute snapshot contains an invalid modifier", + "modifiers"); } auto next = advanceRevision(); if (!next) return next; - values_ = std::move(candidate); + values_ = std::move(candidate); initialized_ = true; return next; } diff --git a/src/modules/attributes/AttributeProjection.h b/src/modules/attributes/AttributeProjection.h index 4589181b..90a81d99 100644 --- a/src/modules/attributes/AttributeProjection.h +++ b/src/modules/attributes/AttributeProjection.h @@ -25,7 +25,7 @@ namespace eve::attributes { /** @brief One base value captured by an attribute projection snapshot. */ struct AttributeSnapshotBase { AttributeId attribute; - double base = 0.0; + double base = 0.0; }; /** @@ -36,10 +36,10 @@ struct AttributeSnapshotBase { * with the ECS generation. */ struct AttributeProjectionSnapshot { - ecs::EntityHandle owner{}; - Revision capturedRevision = Revision::zero(); + ecs::EntityHandle owner{}; + Revision capturedRevision = Revision::zero(); std::vector bases; - std::vector modifiers; + std::vector modifiers; }; /** @@ -83,8 +83,7 @@ public: [[nodiscard]] Result modifyBase(std::string_view attribute, double delta); /** @brief Read a deterministic final value, or the supplied default when absent. */ - [[nodiscard]] Result getFinal(std::string_view attribute, - double fallback = 0.0) const; + [[nodiscard]] Result getFinal(std::string_view attribute, double fallback = 0.0) const; /** @brief Return whether the canonical set contains a base or modifier for the name. */ [[nodiscard]] bool has(std::string_view attribute) const; @@ -107,16 +106,13 @@ public: * @thread Owner-thread-affine; no synchronization is provided. * @reentrancy Side-effect free and does not invoke callbacks. */ - [[nodiscard]] const AttributeModifier* modifierAt(int index) const { - return values_.modifierAt(index); - } + [[nodiscard]] const AttributeModifier* modifierAt(int index) const { return values_.modifierAt(index); } /** * @brief Capture only named attributes and their modifiers. * @param attributes Stable allow-list owned by the caller for this synchronous call. */ - [[nodiscard]] Result snapshot( - std::span attributes) const; + [[nodiscard]] Result snapshot(std::span attributes) const; /** * @brief Restore a snapshot after owner and optimistic revision validation. @@ -124,33 +120,32 @@ public: * @param expectedRevision Current revision the caller intends to replace. * @return Conflict/stale failure leaves every observable value unchanged. */ - [[nodiscard]] Result restore(const AttributeProjectionSnapshot& snapshot, - Revision expectedRevision); + [[nodiscard]] Result restore(const AttributeProjectionSnapshot& snapshot, Revision expectedRevision); private: [[nodiscard]] Result advanceRevision(); - [[nodiscard]] bool ownsSameLiveEntity(ecs::EntityHandle owner) const noexcept; + [[nodiscard]] bool ownsSameLiveEntity(ecs::EntityHandle owner) const noexcept; - AttributeSet values_; + AttributeSet values_; ecs::EntityHandle owner_{}; - Revision revision_ = Revision::zero(); - bool initialized_ = false; + Revision revision_ = Revision::zero(); + bool initialized_ = false; }; /** @brief Stable source labels used by all domain attribute adapters. */ namespace source { inline constexpr std::string_view definition = "definition"; -inline constexpr std::string_view runtime = "runtime"; -inline constexpr std::string_view effect = "effect"; -inline constexpr std::string_view equipment = "equipment"; +inline constexpr std::string_view runtime = "runtime"; +inline constexpr std::string_view effect = "effect"; +inline constexpr std::string_view equipment = "equipment"; } // namespace source /** @brief Shared default priorities for domain adapters. */ namespace priority { inline constexpr ModifierPriority definition = 0; -inline constexpr ModifierPriority runtime = 100; -inline constexpr ModifierPriority effect = 200; -inline constexpr ModifierPriority equipment = 300; +inline constexpr ModifierPriority runtime = 100; +inline constexpr ModifierPriority effect = 200; +inline constexpr ModifierPriority equipment = 300; } // namespace priority } // namespace eve::attributes diff --git a/src/modules/attributes/AttributeResourceAccount.cpp b/src/modules/attributes/AttributeResourceAccount.cpp index 5174f3e2..bb5df45e 100644 --- a/src/modules/attributes/AttributeResourceAccount.cpp +++ b/src/modules/attributes/AttributeResourceAccount.cpp @@ -9,49 +9,43 @@ namespace { template eve::Result invalid(std::string message, std::string path) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, std::move(message), std::move(path))); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, std::move(message), std::move(path))); } } // namespace -AttributeResourceAccountAdapter::AttributeResourceAccountAdapter( - AttributeSetResourceAccount& account, AttributeResourceKind kind) noexcept +AttributeResourceAccountAdapter::AttributeResourceAccountAdapter(AttributeSetResourceAccount& account, + AttributeResourceKind kind) noexcept : account_(account), kind_(kind) {} -const char* AttributeResourceAccountAdapter::resourceName( - AttributeResourceKind kind) noexcept { +const char* AttributeResourceAccountAdapter::resourceName(AttributeResourceKind kind) noexcept { switch (kind) { - case AttributeResourceKind::Mana: return "mana"; - case AttributeResourceKind::Stamina: return "stamina"; + case AttributeResourceKind::Mana: return "mana"; + case AttributeResourceKind::Stamina: return "stamina"; } return nullptr; } -eve::Result AttributeResourceAccountAdapter::makeCost( - AttributeResourceKind kind, std::int64_t amount) { +eve::Result AttributeResourceAccountAdapter::makeCost(AttributeResourceKind kind, + std::int64_t amount) { const char* name = resourceName(kind); - if (name == nullptr) - return invalid( - "attribute resource kind is invalid", "resource.kind"); + if (name == nullptr) return invalid("attribute resource kind is invalid", "resource.kind"); return eve::resource::CostSpec::single(name, amount); } -eve::Result AttributeResourceAccountAdapter::validateCost( - const eve::resource::CostSpec& cost) const { +eve::Result AttributeResourceAccountAdapter::validateCost(const eve::resource::CostSpec& cost) const { const char* name = resourceName(kind_); if (name == nullptr) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "attribute resource kind is invalid", - "resource.kind")); + eve::DiagnosticCode::InvalidArgument, "attribute resource kind is invalid", "resource.kind")); if (!cost.isValid()) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "attribute resource view requires a validated CostSpec", "cost")); + eve::DiagnosticCode::InvalidArgument, "attribute resource view requires a validated CostSpec", "cost")); if (cost.size() != 1 || cost.items().front().resource.value() != name) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "named attribute resource view received a different resource", "cost.resource")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "named attribute resource view received a different resource", "cost.resource")); return eve::Result::success(); } @@ -62,22 +56,19 @@ eve::Result AttributeResourceAccountAdapter::canAf return account_.canAfford(cost); } -eve::Result AttributeResourceAccountAdapter::reserve( - const eve::resource::CostSpec& cost) { +eve::Result AttributeResourceAccountAdapter::reserve(const eve::resource::CostSpec& cost) { auto valid = validateCost(cost); if (!valid) return eve::Result::failure(valid.status()); return account_.reserve(cost); } -eve::Result AttributeResourceAccountAdapter::debit( - const eve::resource::CostSpec& cost) { +eve::Result AttributeResourceAccountAdapter::debit(const eve::resource::CostSpec& cost) { auto valid = validateCost(cost); if (!valid) return eve::Result::failure(valid.status()); return account_.debit(cost); } -eve::Result AttributeResourceAccountAdapter::credit( - const eve::resource::CostSpec& cost) { +eve::Result AttributeResourceAccountAdapter::credit(const eve::resource::CostSpec& cost) { auto valid = validateCost(cost); if (!valid) return eve::Result::failure(valid.status()); return account_.credit(cost); @@ -89,20 +80,17 @@ eve::Result AttributeResourceAccountAdapter::commit( if (name == nullptr || !reservation.isValid() || reservation.cost.size() != 1 || reservation.cost.items().front().resource.value() != name) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "named attribute resource view received a foreign reservation cost", + eve::DiagnosticCode::InvalidArgument, "named attribute resource view received a foreign reservation cost", "reservation.cost")); return account_.commit(reservation); } -eve::Result AttributeResourceAccountAdapter::rollback( - const eve::resource::Reservation& reservation) { +eve::Result AttributeResourceAccountAdapter::rollback(const eve::resource::Reservation& reservation) { const char* name = resourceName(kind_); if (name == nullptr || !reservation.isValid() || reservation.cost.size() != 1 || reservation.cost.items().front().resource.value() != name) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "named attribute resource view received a foreign reservation cost", + eve::DiagnosticCode::InvalidArgument, "named attribute resource view received a foreign reservation cost", "reservation.cost")); return account_.rollback(reservation); } diff --git a/src/modules/attributes/AttributeResourceAccount.h b/src/modules/attributes/AttributeResourceAccount.h index f7c9576a..484dba27 100644 --- a/src/modules/attributes/AttributeResourceAccount.h +++ b/src/modules/attributes/AttributeResourceAccount.h @@ -30,14 +30,13 @@ enum class AttributeResourceKind : unsigned char { class AttributeResourceAccountAdapter final : public eve::resource::IResourceAccount { public: /** @brief Bind a named view to an existing AttributeSet resource account. */ - AttributeResourceAccountAdapter(AttributeSetResourceAccount& account, - AttributeResourceKind kind) noexcept; + AttributeResourceAccountAdapter(AttributeSetResourceAccount& account, AttributeResourceKind kind) noexcept; - AttributeResourceAccountAdapter(const AttributeResourceAccountAdapter&) = delete; + AttributeResourceAccountAdapter(const AttributeResourceAccountAdapter&) = delete; AttributeResourceAccountAdapter& operator=(const AttributeResourceAccountAdapter&) = delete; - AttributeResourceAccountAdapter(AttributeResourceAccountAdapter&&) = delete; - AttributeResourceAccountAdapter& operator=(AttributeResourceAccountAdapter&&) = delete; - ~AttributeResourceAccountAdapter() override = default; + AttributeResourceAccountAdapter(AttributeResourceAccountAdapter&&) = delete; + AttributeResourceAccountAdapter& operator=(AttributeResourceAccountAdapter&&) = delete; + ~AttributeResourceAccountAdapter() override = default; /** @brief Return the resource exposed by this view. */ [[nodiscard]] AttributeResourceKind kind() const noexcept { return kind_; } @@ -48,27 +47,21 @@ public: * @param amount Positive integer quantity. * @return Canonical CostSpec or a validation failure. */ - [[nodiscard]] static eve::Result makeCost( - AttributeResourceKind kind, std::int64_t amount); + [[nodiscard]] static eve::Result makeCost(AttributeResourceKind kind, std::int64_t amount); /** @copydoc eve::resource::IResourceAccount::canAfford */ [[nodiscard]] eve::Result canAfford( const eve::resource::CostSpec& cost) const override; /** @copydoc eve::resource::IResourceAccount::reserve */ - [[nodiscard]] eve::Result reserve( - const eve::resource::CostSpec& cost) override; + [[nodiscard]] eve::Result reserve(const eve::resource::CostSpec& cost) override; /** @copydoc eve::resource::IResourceAccount::debit */ - [[nodiscard]] eve::Result debit( - const eve::resource::CostSpec& cost) override; + [[nodiscard]] eve::Result debit(const eve::resource::CostSpec& cost) override; /** @copydoc eve::resource::IResourceAccount::credit */ - [[nodiscard]] eve::Result credit( - const eve::resource::CostSpec& cost) override; + [[nodiscard]] eve::Result credit(const eve::resource::CostSpec& cost) override; /** @copydoc eve::resource::IResourceAccount::commit */ - [[nodiscard]] eve::Result commit( - const eve::resource::Reservation& reservation) override; + [[nodiscard]] eve::Result commit(const eve::resource::Reservation& reservation) override; /** @copydoc eve::resource::IResourceAccount::rollback */ - [[nodiscard]] eve::Result rollback( - const eve::resource::Reservation& reservation) override; + [[nodiscard]] eve::Result rollback(const eve::resource::Reservation& reservation) override; private: /** @@ -84,7 +77,7 @@ private: [[nodiscard]] eve::Result validateCost(const eve::resource::CostSpec& cost) const; AttributeSetResourceAccount& account_; - AttributeResourceKind kind_; + AttributeResourceKind kind_; }; /** @brief Explicit alias used by code that wants a Mana-only view. */ diff --git a/src/modules/attributes/AttributeSet.cpp b/src/modules/attributes/AttributeSet.cpp index 9bd5ef6d..3e426d06 100644 --- a/src/modules/attributes/AttributeSet.cpp +++ b/src/modules/attributes/AttributeSet.cpp @@ -9,34 +9,25 @@ namespace eve::attributes { namespace { eve::Diagnostic invalidArgument(std::string message, std::string path = {}) { - return eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, std::move(message), - std::move(path)); + return eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, std::move(message), std::move(path)); } } // namespace const char* attributeOperationName(AttributeOperation operation) noexcept { switch (operation) { - case AttributeOperation::Add: - return "add"; - case AttributeOperation::AdditivePercent: - return "additive_percent"; - case AttributeOperation::MultiplicativePercent: - return "multiplicative_percent"; - case AttributeOperation::Override: - return "override"; - case AttributeOperation::ClampMin: - return "clamp_min"; - case AttributeOperation::ClampMax: - return "clamp_max"; - case AttributeOperation::Custom: - return "custom"; + case AttributeOperation::Add: return "add"; + case AttributeOperation::AdditivePercent: return "additive_percent"; + case AttributeOperation::MultiplicativePercent: return "multiplicative_percent"; + case AttributeOperation::Override: return "override"; + case AttributeOperation::ClampMin: return "clamp_min"; + case AttributeOperation::ClampMax: return "clamp_max"; + case AttributeOperation::Custom: return "custom"; } return "unknown"; } -eve::Result parseAttributeOperation(std::string_view operation, - double value) { +eve::Result parseAttributeOperation(std::string_view operation, double value) { ParsedAttributeOperation parsed; if (operation == "add") { parsed.operation = AttributeOperation::Add; @@ -65,16 +56,13 @@ void AttributeOperationRegistry::registerOperation(const std::string& policyId, if (!policyId.empty() && function) operations_[policyId] = std::move(function); } -void AttributeOperationRegistry::unregisterOperation(const std::string& policyId) { - operations_.erase(policyId); -} +void AttributeOperationRegistry::unregisterOperation(const std::string& policyId) { operations_.erase(policyId); } bool AttributeOperationRegistry::has(const std::string& policyId) const { return operations_.find(policyId) != operations_.end(); } -double AttributeOperationRegistry::apply(const std::string& policyId, double current, - double value) const { +double AttributeOperationRegistry::apply(const std::string& policyId, double current, double value) const { const auto it = operations_.find(policyId); return it == operations_.end() ? current : it->second(current, value); } @@ -83,15 +71,11 @@ void AttributeOperationRegistry::registerOp(const std::string& policyId, Functio registerOperation(policyId, std::move(function)); } -void AttributeOperationRegistry::unregisterOp(const std::string& policyId) { - unregisterOperation(policyId); -} +void AttributeOperationRegistry::unregisterOp(const std::string& policyId) { unregisterOperation(policyId); } -AttributeModifier::AttributeModifier(ModifierId modifierId, SourceId sourceId, - std::string_view operationName, double modifierValue, - ModifierPriority priorityValue) - : id(std::move(modifierId)), source(std::move(sourceId)), value(modifierValue), - priority(priorityValue) { +AttributeModifier::AttributeModifier(ModifierId modifierId, SourceId sourceId, std::string_view operationName, + double modifierValue, ModifierPriority priorityValue) + : id(std::move(modifierId)), source(std::move(sourceId)), value(modifierValue), priority(priorityValue) { if (operationName == "add") { operation = AttributeOperation::Add; } else if (operationName == "mulAdd") { @@ -109,35 +93,26 @@ AttributeModifier::AttributeModifier(ModifierId modifierId, SourceId sourceId, operation = AttributeOperation::ClampMax; } else { operation = AttributeOperation::Custom; - policyId = std::string(operationName); + policyId = std::string(operationName); } } -double computeAttributeValue(const AttributeValue& attribute, - const AttributeOperationRegistry* customOperations) { - double additive = 0.0; - double additivePercent = 0.0; - double multiplicativePercent = 1.0; +double computeAttributeValue(const AttributeValue& attribute, const AttributeOperationRegistry* customOperations) { + double additive = 0.0; + double additivePercent = 0.0; + double multiplicativePercent = 1.0; std::vector ordered; ordered.reserve(attribute.modifiers.size()); for (const auto& modifier : attribute.modifiers) { switch (modifier.operation) { - case AttributeOperation::Add: - additive += modifier.value; - break; - case AttributeOperation::AdditivePercent: - additivePercent += modifier.value; - break; - case AttributeOperation::MultiplicativePercent: - multiplicativePercent *= 1.0 + modifier.value; - break; - case AttributeOperation::Override: - case AttributeOperation::ClampMin: - case AttributeOperation::ClampMax: - case AttributeOperation::Custom: - ordered.push_back(&modifier); - break; + case AttributeOperation::Add: additive += modifier.value; break; + case AttributeOperation::AdditivePercent: additivePercent += modifier.value; break; + case AttributeOperation::MultiplicativePercent: multiplicativePercent *= 1.0 + modifier.value; break; + case AttributeOperation::Override: + case AttributeOperation::ClampMin: + case AttributeOperation::ClampMax: + case AttributeOperation::Custom: ordered.push_back(&modifier); break; } } @@ -151,23 +126,16 @@ double computeAttributeValue(const AttributeValue& attribute, for (const auto* modifier : ordered) { switch (modifier->operation) { - case AttributeOperation::Override: - result = modifier->value; - break; - case AttributeOperation::ClampMin: - result = std::max(result, modifier->value); - break; - case AttributeOperation::ClampMax: - result = std::min(result, modifier->value); - break; - case AttributeOperation::Custom: - if (customOperations && customOperations->has(modifier->policyId)) - result = customOperations->apply(modifier->policyId, result, modifier->value); - break; - case AttributeOperation::Add: - case AttributeOperation::AdditivePercent: - case AttributeOperation::MultiplicativePercent: - break; + case AttributeOperation::Override: result = modifier->value; break; + case AttributeOperation::ClampMin: result = std::max(result, modifier->value); break; + case AttributeOperation::ClampMax: result = std::min(result, modifier->value); break; + case AttributeOperation::Custom: + if (customOperations && customOperations->has(modifier->policyId)) + result = customOperations->apply(modifier->policyId, result, modifier->value); + break; + case AttributeOperation::Add: + case AttributeOperation::AdditivePercent: + case AttributeOperation::MultiplicativePercent: break; } } return result; @@ -176,14 +144,16 @@ double computeAttributeValue(const AttributeValue& attribute, AttributeSet::AttributeSet(std::string subject) : subject_(std::move(subject)) {} AttributeSet::AttributeSet(const AttributeSet& other) - : subject_(other.subject_), values_(other.values_), nextSequence_(other.nextSequence_), + : subject_(other.subject_), + values_(other.values_), + nextSequence_(other.nextSequence_), nextModifierId_(other.nextModifierId_) {} AttributeSet& AttributeSet::operator=(const AttributeSet& other) { if (this == &other) return *this; - subject_ = other.subject_; - values_ = other.values_; - nextSequence_ = other.nextSequence_; + subject_ = other.subject_; + values_ = other.values_; + nextSequence_ = other.nextSequence_; nextModifierId_ = other.nextModifierId_; order_.clear(); orderDirty_ = true; @@ -191,14 +161,16 @@ AttributeSet& AttributeSet::operator=(const AttributeSet& other) { } AttributeSet::AttributeSet(AttributeSet&& other) noexcept - : subject_(std::move(other.subject_)), values_(std::move(other.values_)), - nextSequence_(other.nextSequence_), nextModifierId_(other.nextModifierId_) {} + : subject_(std::move(other.subject_)), + values_(std::move(other.values_)), + nextSequence_(other.nextSequence_), + nextModifierId_(other.nextModifierId_) {} AttributeSet& AttributeSet::operator=(AttributeSet&& other) noexcept { if (this == &other) return *this; - subject_ = std::move(other.subject_); - values_ = std::move(other.values_); - nextSequence_ = other.nextSequence_; + subject_ = std::move(other.subject_); + values_ = std::move(other.values_); + nextSequence_ = other.nextSequence_; nextModifierId_ = other.nextModifierId_; order_.clear(); orderDirty_ = true; @@ -210,7 +182,7 @@ const std::string& AttributeSet::subject() const { return subject_; } void AttributeSet::setBase(const AttributeId& attribute, double value) { if (!attribute.empty()) { auto& state = values_[attribute]; - state.base = value; + state.base = value; state.dirty = true; } } @@ -240,22 +212,20 @@ double AttributeSet::getFinal(const AttributeId& attribute, double fallback, if (customOperations) return computeAttributeValue(state, customOperations); if (state.dirty) { state.cached = computeAttributeValue(state); - state.dirty = false; + state.dirty = false; } return state.cached; } bool AttributeSet::isValidOperation(const AttributeModifier& modifier) noexcept { switch (modifier.operation) { - case AttributeOperation::Add: - case AttributeOperation::AdditivePercent: - case AttributeOperation::MultiplicativePercent: - case AttributeOperation::Override: - case AttributeOperation::ClampMin: - case AttributeOperation::ClampMax: - return true; - case AttributeOperation::Custom: - return !modifier.policyId.empty(); + case AttributeOperation::Add: + case AttributeOperation::AdditivePercent: + case AttributeOperation::MultiplicativePercent: + case AttributeOperation::Override: + case AttributeOperation::ClampMin: + case AttributeOperation::ClampMax: return true; + case AttributeOperation::Custom: return !modifier.policyId.empty(); } return false; } @@ -268,8 +238,7 @@ eve::Result AttributeSet::addModifier(AttributeModifier modifier) { return eve::Result::failure( invalidArgument("attribute modifier has an invalid operation", "operation")); if (!std::isfinite(modifier.value)) - return eve::Result::failure( - invalidArgument("attribute modifier value must be finite", "value")); + return eve::Result::failure(invalidArgument("attribute modifier value must be finite", "value")); if (modifier.id.empty()) { do { @@ -283,33 +252,29 @@ eve::Result AttributeSet::addModifier(AttributeModifier modifier) { return false; }()); } - const ModifierId modifierId = modifier.id; + const ModifierId modifierId = modifier.id; const AttributeId attributeId = modifier.attribute; removeModifier(modifierId).ignore("replace existing attribute modifier"); modifier.sequence = nextSequence_++; - auto& state = values_[attributeId]; + auto& state = values_[attributeId]; state.modifiers.push_back(std::move(modifier)); state.dirty = true; orderDirty_ = true; - return eve::Result::success(modifierId, - eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(modifierId, eve::Status::success(eve::StatusCode::Applied)); } -bool AttributeSet::addModifier(const std::string& id, const std::string& attribute, - const std::string& source, const std::string& operation, - double value, ModifierPriority priority) { +bool AttributeSet::addModifier(const std::string& id, const std::string& attribute, const std::string& source, + const std::string& operation, double value, ModifierPriority priority) { if (id.empty()) return false; auto parsed = parseAttributeOperation(operation, value); if (!parsed.ok()) return false; - AttributeModifier modifier{id, attribute, source, parsed.value().operation, parsed.value().value, - priority}; - auto result = addModifier(std::move(modifier)); + AttributeModifier modifier{id, attribute, source, parsed.value().operation, parsed.value().value, priority}; + auto result = addModifier(std::move(modifier)); return result.ok(); } eve::Result AttributeSet::removeModifier(const ModifierId& id) { - if (id.empty()) - return eve::Result::failure(invalidArgument("modifier id must not be empty", "id")); + if (id.empty()) return eve::Result::failure(invalidArgument("modifier id must not be empty", "id")); for (auto& [attribute, state] : values_) { const auto it = std::find_if(state.modifiers.begin(), state.modifiers.end(), [&](const auto& modifier) { return modifier.id == id; }); @@ -320,48 +285,42 @@ eve::Result AttributeSet::removeModifier(const ModifierId& id) { return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } } - return eve::Result::failure( - eve::Status::failure(eve::StatusCode::NotFound, - eve::Diagnostic::error(eve::DiagnosticCode::NotFound, - "attribute modifier was not found", "id"))); + return eve::Result::failure(eve::Status::failure( + eve::StatusCode::NotFound, + eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "attribute modifier was not found", "id"))); } eve::Result AttributeSet::removeModifier(const AttributeId& attribute, const ModifierId& id) { if (attribute.empty()) return eve::Result::failure(invalidArgument("attribute id must not be empty", "attribute")); - if (id.empty()) - return eve::Result::failure(invalidArgument("modifier id must not be empty", "id")); + if (id.empty()) return eve::Result::failure(invalidArgument("modifier id must not be empty", "id")); const auto valueIt = values_.find(attribute); if (valueIt == values_.end()) - return eve::Result::failure( - eve::Status::failure(eve::StatusCode::NotFound, - eve::Diagnostic::error(eve::DiagnosticCode::NotFound, - "attribute modifier was not found", "id"))); - auto& modifiers = valueIt->second.modifiers; - const auto modifierIt = std::find_if(modifiers.begin(), modifiers.end(), - [&](const auto& modifier) { return modifier.id == id; }); + return eve::Result::failure(eve::Status::failure( + eve::StatusCode::NotFound, + eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "attribute modifier was not found", "id"))); + auto& modifiers = valueIt->second.modifiers; + const auto modifierIt = + std::find_if(modifiers.begin(), modifiers.end(), [&](const auto& modifier) { return modifier.id == id; }); if (modifierIt == modifiers.end()) - return eve::Result::failure( - eve::Status::failure(eve::StatusCode::NotFound, - eve::Diagnostic::error(eve::DiagnosticCode::NotFound, - "attribute modifier was not found", "id"))); + return eve::Result::failure(eve::Status::failure( + eve::StatusCode::NotFound, + eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "attribute modifier was not found", "id"))); modifiers.erase(modifierIt); valueIt->second.dirty = true; - orderDirty_ = true; + orderDirty_ = true; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } eve::Result AttributeSet::removeBySource(const SourceId& source, const AttributeId& attribute) { - if (source.empty()) - return eve::Result::failure(invalidArgument("source id must not be empty", "source")); + if (source.empty()) return eve::Result::failure(invalidArgument("source id must not be empty", "source")); int removed = 0; for (auto& [attributeId, state] : values_) { if (!attribute.empty() && attributeId != attribute) continue; const auto oldSize = state.modifiers.size(); - state.modifiers.erase( - std::remove_if(state.modifiers.begin(), state.modifiers.end(), - [&](const auto& modifier) { return modifier.source == source; }), - state.modifiers.end()); + state.modifiers.erase(std::remove_if(state.modifiers.begin(), state.modifiers.end(), + [&](const auto& modifier) { return modifier.source == source; }), + state.modifiers.end()); if (state.modifiers.size() != oldSize) { state.dirty = true; removed += static_cast(oldSize - state.modifiers.size()); @@ -397,9 +356,8 @@ void AttributeSet::rebuildOrder() const { order_.clear(); for (const auto& [attribute, state] : values_) for (const auto& modifier : state.modifiers) order_.push_back(&modifier); - std::sort(order_.begin(), order_.end(), [](const auto* left, const auto* right) { - return left->sequence < right->sequence; - }); + std::sort(order_.begin(), order_.end(), + [](const auto* left, const auto* right) { return left->sequence < right->sequence; }); orderDirty_ = false; } diff --git a/src/modules/attributes/AttributeSet.h b/src/modules/attributes/AttributeSet.h index d2278445..10e53c35 100644 --- a/src/modules/attributes/AttributeSet.h +++ b/src/modules/attributes/AttributeSet.h @@ -12,9 +12,9 @@ namespace eve::attributes { -using AttributeId = std::string; -using ModifierId = std::string; -using SourceId = std::string; +using AttributeId = std::string; +using ModifierId = std::string; +using SourceId = std::string; using ModifierPriority = std::int32_t; using ModifierSequence = std::uint64_t; @@ -50,15 +50,14 @@ enum class AttributeOperation : std::uint8_t { */ struct ParsedAttributeOperation { AttributeOperation operation = AttributeOperation::Add; - double value = 0.0; + double value = 0.0; }; /** * @brief Parse a built-in operation spelling used by existing data and APIs. * @return A normalized operation, or a rejected Result for an unknown spelling. */ -[[nodiscard]] eve::Result parseAttributeOperation(std::string_view operation, - double value); +[[nodiscard]] eve::Result parseAttributeOperation(std::string_view operation, double value); /** @brief Registry for explicitly named custom attribute policies. */ class AttributeOperationRegistry { @@ -91,42 +90,45 @@ private: * `policyId` names a registered AttributeOperationRegistry policy. */ struct AttributeModifier { - ModifierId id; - AttributeId attribute; - SourceId source; + ModifierId id; + AttributeId attribute; + SourceId source; AttributeOperation operation = AttributeOperation::Add; - double value = 0.0; - ModifierPriority priority = 0; - ModifierSequence sequence = 0; - std::string policyId; + double value = 0.0; + ModifierPriority priority = 0; + ModifierSequence sequence = 0; + std::string policyId; AttributeModifier() = default; /** @brief Construct a canonical modifier with an explicit operation. */ AttributeModifier(ModifierId modifierId, AttributeId attributeId, SourceId sourceId, - AttributeOperation operationValue, double modifierValue, - ModifierPriority priorityValue = 0, - ModifierSequence sequenceValue = 0, - std::string policy = {}) - : id(std::move(modifierId)), attribute(std::move(attributeId)), source(std::move(sourceId)), - operation(operationValue), value(modifierValue), priority(priorityValue), - sequence(sequenceValue), policyId(std::move(policy)) {} + AttributeOperation operationValue, double modifierValue, ModifierPriority priorityValue = 0, + ModifierSequence sequenceValue = 0, std::string policy = {}) + : id(std::move(modifierId)), + attribute(std::move(attributeId)), + source(std::move(sourceId)), + operation(operationValue), + value(modifierValue), + priority(priorityValue), + sequence(sequenceValue), + policyId(std::move(policy)) {} /** * @brief Construct the former per-attribute RPG test shape. * @remarks The attribute is intentionally empty and must be supplied before * inserting into an AttributeSet. */ - AttributeModifier(ModifierId modifierId, SourceId sourceId, std::string_view operationName, - double modifierValue, ModifierPriority priorityValue = 0); + AttributeModifier(ModifierId modifierId, SourceId sourceId, std::string_view operationName, double modifierValue, + ModifierPriority priorityValue = 0); }; /** @brief Runtime value and modifiers for exactly one attribute. */ struct AttributeValue { - double base = 0.0; + double base = 0.0; std::vector modifiers; - mutable double cached = 0.0; - mutable bool dirty = true; + mutable double cached = 0.0; + mutable bool dirty = true; }; /** @@ -136,8 +138,8 @@ struct AttributeValue { * MultiplicativePercent factors, then priority/sequence ordered Override and * Clamp operations. Custom policies participate in the final ordered phase. */ -[[nodiscard]] double computeAttributeValue(const AttributeValue& attribute, - const AttributeOperationRegistry* customOperations = nullptr); +[[nodiscard]] double computeAttributeValue(const AttributeValue& attribute, + const AttributeOperationRegistry* customOperations = nullptr); /** @brief Generic owning collection of canonical attributes and modifiers. */ class AttributeSet { @@ -170,7 +172,7 @@ public: * registry changes. */ double getFinal(const AttributeId& attribute, double fallback = 0.0, - const AttributeOperationRegistry* customOperations = nullptr) const; + const AttributeOperationRegistry* customOperations = nullptr) const; /** * @brief Add or replace a canonical modifier and return its stable id. @@ -190,11 +192,9 @@ public: /** @brief Remove one modifier with a structured result. */ [[nodiscard]] eve::Result removeModifier(const ModifierId& id); /** @brief Remove one modifier from a named attribute with a structured result. */ - [[nodiscard]] eve::Result removeModifier(const AttributeId& attribute, - const ModifierId& id); + [[nodiscard]] eve::Result removeModifier(const AttributeId& attribute, const ModifierId& id); /** @brief Remove source modifiers with a structured result containing the count. */ - [[nodiscard]] eve::Result removeBySource(const SourceId& source, - const AttributeId& attribute = {}); + [[nodiscard]] eve::Result removeBySource(const SourceId& source, const AttributeId& attribute = {}); /** @brief Mark one attribute dirty after an external policy or restore change. */ void invalidate(const AttributeId& attribute); /** @brief Remove all modifiers. */ @@ -208,12 +208,12 @@ private: void rebuildOrder() const; static bool isValidOperation(const AttributeModifier& modifier) noexcept; - std::string subject_; + std::string subject_; std::unordered_map values_; - mutable std::vector order_; - mutable bool orderDirty_ = true; - ModifierSequence nextSequence_ = 1; - ModifierSequence nextModifierId_ = 1; + mutable std::vector order_; + mutable bool orderDirty_ = true; + ModifierSequence nextSequence_ = 1; + ModifierSequence nextModifierId_ = 1; }; } // namespace eve::attributes diff --git a/src/modules/attributes/AttributeSetResourceAccount.cpp b/src/modules/attributes/AttributeSetResourceAccount.cpp index 1ed3cacc..82cca29e 100644 --- a/src/modules/attributes/AttributeSetResourceAccount.cpp +++ b/src/modules/attributes/AttributeSetResourceAccount.cpp @@ -21,50 +21,42 @@ using eve::resource::ResourceId; constexpr double kMaxExactlyRepresentableInteger = 9007199254740991.0; // 2^53 - 1 eve::Diagnostic invalidArgument(std::string message, std::string path = {}) { - return eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, std::move(message), - std::move(path)); + return eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, std::move(message), std::move(path)); } eve::Diagnostic conflict(std::string message, std::string path = {}) { - return eve::Diagnostic::error(eve::DiagnosticCode::Conflict, std::move(message), - std::move(path)); + return eve::Diagnostic::error(eve::DiagnosticCode::Conflict, std::move(message), std::move(path)); } eve::Diagnostic invariantFailure(std::string message, std::string path = {}) { - return eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, std::move(message), - std::move(path)); + return eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, std::move(message), std::move(path)); } template eve::Result exceptionFailure(std::string_view operation, const std::exception& exception) { return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::Failed, - std::string(operation) + " failed while staging an atomic candidate: " + exception.what(), - "resource.account")); + std::string(operation) + " failed while staging an atomic candidate: " + exception.what(), "resource.account")); } template <> -eve::Result exceptionFailure(std::string_view operation, - const std::exception& exception) { +eve::Result exceptionFailure(std::string_view operation, const std::exception& exception) { return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::Failed, - std::string(operation) + " failed while staging an atomic candidate: " + exception.what(), - "resource.account")); + std::string(operation) + " failed while staging an atomic candidate: " + exception.what(), "resource.account")); } template eve::Result unknownExceptionFailure(std::string_view operation) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, - std::string(operation) + " failed while staging an atomic candidate", + eve::DiagnosticCode::Failed, std::string(operation) + " failed while staging an atomic candidate", "resource.account")); } template <> eve::Result unknownExceptionFailure(std::string_view operation) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, - std::string(operation) + " failed while staging an atomic candidate", + eve::DiagnosticCode::Failed, std::string(operation) + " failed while staging an atomic candidate", "resource.account")); } @@ -84,58 +76,49 @@ eve::Status insufficientStatus(const Affordability& affordability) { } return eve::Status::failure( eve::StatusCode::Rejected, - eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, - "resource cost exceeds available balance", "cost", - std::move(details))); + eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, "resource cost exceeds available balance", + "cost", std::move(details))); } eve::Status unknownReservationStatus() { return eve::Status::failure( eve::StatusCode::NotFound, - eve::Diagnostic::error(eve::DiagnosticCode::NotFound, - "resource reservation does not belong to this account", + eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "resource reservation does not belong to this account", "reservation")); } eve::Status foreignReservationStatus() { return eve::Status::failure( eve::StatusCode::Conflict, - eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "resource reservation belongs to a different account", + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "resource reservation belongs to a different account", "reservation.account")); } eve::Status duplicateReservationStatus() { - return eve::Status::failure( - eve::StatusCode::Conflict, - eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "resource reservation is no longer active", "reservation")); + return eve::Status::failure(eve::StatusCode::Conflict, + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, + "resource reservation is no longer active", "reservation")); } eve::Status mismatchedReservationStatus() { return eve::Status::failure( eve::StatusCode::Conflict, eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "resource reservation cost does not match its account record", - "reservation.cost")); + "resource reservation cost does not match its account record", "reservation.cost")); } eve::Status receiptIdExhaustedStatus() { - return eve::Status::failure( - eve::StatusCode::Failed, - invariantFailure("resource receipt id exhausted", "receipt")); + return eve::Status::failure(eve::StatusCode::Failed, invariantFailure("resource receipt id exhausted", "receipt")); } eve::Status accountIdentityUnavailableStatus() { - return eve::Status::failure( - eve::StatusCode::Failed, - invariantFailure("resource account has no valid identity nonce", "account")); + return eve::Status::failure(eve::StatusCode::Failed, + invariantFailure("resource account has no valid identity nonce", "account")); } } // namespace -AttributeSetResourceAccount::AttributeSetResourceAccount(AttributeSet& attributes) - : attributes_(attributes) { +AttributeSetResourceAccount::AttributeSetResourceAccount(AttributeSet& attributes) : attributes_(attributes) { auto nonce = eve::resource::allocateAccountNonce(); if (nonce) accountNonce_ = std::move(nonce).takeValue(); @@ -145,17 +128,14 @@ AttributeSetResourceAccount::AttributeSetResourceAccount(AttributeSet& attribute eve::Result AttributeSetResourceAccount::balanceOf(const ResourceId& resource) const { const double value = attributes_.getBase(resource.value(), 0.0); - if (!std::isfinite(value) || value < 0.0 || std::floor(value) != value || - value > kMaxExactlyRepresentableInteger) { + if (!std::isfinite(value) || value < 0.0 || std::floor(value) != value || value > kMaxExactlyRepresentableInteger) { return eve::Result::failure( - invariantFailure("resource-backed attribute must be a finite non-negative integer", - resource.value())); + invariantFailure("resource-backed attribute must be a finite non-negative integer", resource.value())); } return eve::Result::success(static_cast(value)); } -eve::Result AttributeSetResourceAccount::activeReservationsFor( - const ResourceId& resource) const { +eve::Result AttributeSetResourceAccount::activeReservationsFor(const ResourceId& resource) const { std::int64_t total = 0; for (const auto& [id, record] : reservations_) { (void)id; @@ -173,8 +153,7 @@ eve::Result AttributeSetResourceAccount::activeReservationsFor( eve::Result AttributeSetResourceAccount::canAfford(const CostSpec& cost) const { try { - if (accountNonce_.isZero()) - return eve::Result::failure(accountIdentityUnavailableStatus()); + if (accountNonce_.isZero()) return eve::Result::failure(accountIdentityUnavailableStatus()); auto valid = validateCost(cost); if (!valid) return eve::Result::failure(valid.status()); @@ -186,9 +165,7 @@ eve::Result AttributeSetResourceAccount::canAfford(const CostSpec auto reserved = activeReservationsFor(item.resource); if (!reserved) return eve::Result::failure(reserved.status()); - const std::int64_t available = balance.value() >= reserved.value() - ? balance.value() - reserved.value() - : 0; + const std::int64_t available = balance.value() >= reserved.value() ? balance.value() - reserved.value() : 0; if (available < item.amount.value()) { result.affordable = false; result.shortfalls.push_back({item.resource, item.amount, Amount(available)}); @@ -207,27 +184,20 @@ eve::Result AttributeSetResourceAccount::reserve(const CostSpec& co auto affordability = canAfford(cost); if (!affordability) return eve::Result::failure(affordability.status()); const auto& checked = affordability.value(); - if (!checked.affordable) - return eve::Result::failure(insufficientStatus(checked)); + if (!checked.affordable) return eve::Result::failure(insufficientStatus(checked)); if (nextReservation_.value() == std::numeric_limits::max()) - return eve::Result::failure( - eve::Status::failure(eve::StatusCode::Failed, - invariantFailure("resource reservation id exhausted", - "reservation"))); + return eve::Result::failure(eve::Status::failure( + eve::StatusCode::Failed, invariantFailure("resource reservation id exhausted", "reservation"))); - const ReservationId id = nextReservation_; - const auto [it, inserted] = reservations_.emplace( - id, ReservationRecord{cost, ReservationState::Active}); + const ReservationId id = nextReservation_; + const auto [it, inserted] = reservations_.emplace(id, ReservationRecord{cost, ReservationState::Active}); if (!inserted) - return eve::Result::failure( - eve::Status::failure(eve::StatusCode::Conflict, - conflict("resource reservation id was already used", - "reservation"))); + return eve::Result::failure(eve::Status::failure( + eve::StatusCode::Conflict, conflict("resource reservation id was already used", "reservation"))); nextReservation_ = ReservationId(id.value() + 1); - return eve::Result::success( - Reservation{accountNonce_, id, it->second.cost}, - eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(Reservation{accountNonce_, id, it->second.cost}, + eve::Status::success(eve::StatusCode::Applied)); } catch (const std::exception& exception) { return exceptionFailure("reserve", exception); } catch (...) { @@ -244,23 +214,17 @@ eve::Result AttributeSetResourceAccount::applyDelta(const CostSpec& cost, const auto amount = item.amount.value(); if (debit) { if (balance.value() < amount) - return eve::Result::failure( - eve::Status::failure( - eve::StatusCode::Rejected, - eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, - "resource debit would make balance negative", - item.resource.value()))); - candidate.setBase(item.resource.value(), - static_cast(balance.value() - amount)); + return eve::Result::failure(eve::Status::failure( + eve::StatusCode::Rejected, + eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, + "resource debit would make balance negative", item.resource.value()))); + candidate.setBase(item.resource.value(), static_cast(balance.value() - amount)); } else { if (amount > std::numeric_limits::max() - balance.value()) return eve::Result::failure( - eve::Status::failure( - eve::StatusCode::Failed, - invariantFailure("resource credit overflowed", item.resource.value()))); - candidate.setBase(item.resource.value(), - static_cast(balance.value() + amount)); + eve::Status::failure(eve::StatusCode::Failed, + invariantFailure("resource credit overflowed", item.resource.value()))); + candidate.setBase(item.resource.value(), static_cast(balance.value() + amount)); } } attributes_ = std::move(candidate); @@ -277,17 +241,15 @@ eve::Result AttributeSetResourceAccount::debit(const CostSpec& cost) { auto affordability = canAfford(cost); if (!affordability) return eve::Result::failure(affordability.status()); const auto& checked = affordability.value(); - if (!checked.affordable) - return eve::Result::failure(insufficientStatus(checked)); + if (!checked.affordable) return eve::Result::failure(insufficientStatus(checked)); if (nextReceipt_.value() == std::numeric_limits::max()) return eve::Result::failure(receiptIdExhaustedStatus()); Receipt receipt{accountNonce_, nextReceipt_, ReservationId{}, ReceiptOperation::Debit, cost}; - auto applied = applyDelta(cost, true); + auto applied = applyDelta(cost, true); if (!applied) return eve::Result::failure(applied.status()); nextReceipt_ = eve::resource::ReceiptId(nextReceipt_.value() + 1); - return eve::Result::success( - std::move(receipt), eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(std::move(receipt), eve::Status::success(eve::StatusCode::Applied)); } catch (const std::exception& exception) { return exceptionFailure("debit", exception); } catch (...) { @@ -297,28 +259,24 @@ eve::Result AttributeSetResourceAccount::debit(const CostSpec& cost) { eve::Result AttributeSetResourceAccount::credit(const CostSpec& cost) { try { - if (accountNonce_.isZero()) - return eve::Result::failure(accountIdentityUnavailableStatus()); + if (accountNonce_.isZero()) return eve::Result::failure(accountIdentityUnavailableStatus()); auto valid = validateCost(cost); if (!valid) return eve::Result::failure(valid.status()); for (const auto& item : cost.items()) { auto balance = balanceOf(item.resource); if (!balance) return eve::Result::failure(balance.status()); if (item.amount.value() > std::numeric_limits::max() - balance.value()) - return eve::Result::failure( - eve::Status::failure( - eve::StatusCode::Failed, - invariantFailure("resource credit overflowed", item.resource.value()))); + return eve::Result::failure(eve::Status::failure( + eve::StatusCode::Failed, invariantFailure("resource credit overflowed", item.resource.value()))); } if (nextReceipt_.value() == std::numeric_limits::max()) return eve::Result::failure(receiptIdExhaustedStatus()); Receipt receipt{accountNonce_, nextReceipt_, ReservationId{}, ReceiptOperation::Credit, cost}; - auto applied = applyDelta(cost, false); + auto applied = applyDelta(cost, false); if (!applied) return eve::Result::failure(applied.status()); nextReceipt_ = eve::resource::ReceiptId(nextReceipt_.value() + 1); - return eve::Result::success( - std::move(receipt), eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(std::move(receipt), eve::Status::success(eve::StatusCode::Applied)); } catch (const std::exception& exception) { return exceptionFailure("credit", exception); } catch (...) { @@ -337,11 +295,9 @@ eve::Result AttributeSetResourceAccount::activeReservationsAreCovered() co auto reserved = activeReservationsFor(item.resource); if (!reserved) return eve::Result::failure(reserved.status()); if (balance.value() < reserved.value()) - return eve::Result::failure( - eve::Status::failure( - eve::StatusCode::Conflict, - conflict("external attribute change invalidated active reservations", - item.resource.value()))); + return eve::Result::failure(eve::Status::failure( + eve::StatusCode::Conflict, + conflict("external attribute change invalidated active reservations", item.resource.value()))); } } return eve::Result::success(); @@ -354,18 +310,13 @@ eve::Result AttributeSetResourceAccount::activeReservationsAreCovered() co eve::Result AttributeSetResourceAccount::commit(const Reservation& reservation) { try { - if (accountNonce_.isZero()) - return eve::Result::failure(accountIdentityUnavailableStatus()); + if (accountNonce_.isZero()) return eve::Result::failure(accountIdentityUnavailableStatus()); if (!reservation.isValid()) - return eve::Result::failure( - invalidArgument("resource reservation is invalid", "reservation")); - if (reservation.account != accountNonce_) - return eve::Result::failure(foreignReservationStatus()); + return eve::Result::failure(invalidArgument("resource reservation is invalid", "reservation")); + if (reservation.account != accountNonce_) return eve::Result::failure(foreignReservationStatus()); const auto it = reservations_.find(reservation.id); - if (it == reservations_.end()) - return eve::Result::failure(unknownReservationStatus()); - if (it->second.cost != reservation.cost) - return eve::Result::failure(mismatchedReservationStatus()); + if (it == reservations_.end()) return eve::Result::failure(unknownReservationStatus()); + if (it->second.cost != reservation.cost) return eve::Result::failure(mismatchedReservationStatus()); if (it->second.state != ReservationState::Active) return eve::Result::failure(duplicateReservationStatus()); @@ -374,14 +325,12 @@ eve::Result AttributeSetResourceAccount::commit(const Reservation& rese if (nextReceipt_.value() == std::numeric_limits::max()) return eve::Result::failure(receiptIdExhaustedStatus()); - Receipt receipt{accountNonce_, nextReceipt_, reservation.id, ReceiptOperation::Debit, - it->second.cost}; - auto applied = applyDelta(it->second.cost, true); + Receipt receipt{accountNonce_, nextReceipt_, reservation.id, ReceiptOperation::Debit, it->second.cost}; + auto applied = applyDelta(it->second.cost, true); if (!applied) return eve::Result::failure(applied.status()); it->second.state = ReservationState::Committed; - nextReceipt_ = eve::resource::ReceiptId(nextReceipt_.value() + 1); - return eve::Result::success( - std::move(receipt), eve::Status::success(eve::StatusCode::Applied)); + nextReceipt_ = eve::resource::ReceiptId(nextReceipt_.value() + 1); + return eve::Result::success(std::move(receipt), eve::Status::success(eve::StatusCode::Applied)); } catch (const std::exception& exception) { return exceptionFailure("commit", exception); } catch (...) { @@ -391,17 +340,13 @@ eve::Result AttributeSetResourceAccount::commit(const Reservation& rese eve::Result AttributeSetResourceAccount::rollback(const Reservation& reservation) { try { - if (accountNonce_.isZero()) - return eve::Result::failure(accountIdentityUnavailableStatus()); + if (accountNonce_.isZero()) return eve::Result::failure(accountIdentityUnavailableStatus()); if (!reservation.isValid()) - return eve::Result::failure( - invalidArgument("resource reservation is invalid", "reservation")); - if (reservation.account != accountNonce_) - return eve::Result::failure(foreignReservationStatus()); + return eve::Result::failure(invalidArgument("resource reservation is invalid", "reservation")); + if (reservation.account != accountNonce_) return eve::Result::failure(foreignReservationStatus()); const auto it = reservations_.find(reservation.id); if (it == reservations_.end()) return eve::Result::failure(unknownReservationStatus()); - if (it->second.cost != reservation.cost) - return eve::Result::failure(mismatchedReservationStatus()); + if (it->second.cost != reservation.cost) return eve::Result::failure(mismatchedReservationStatus()); if (it->second.state != ReservationState::Active) return eve::Result::failure(duplicateReservationStatus()); it->second.state = ReservationState::RolledBack; diff --git a/src/modules/attributes/AttributeSetResourceAccount.h b/src/modules/attributes/AttributeSetResourceAccount.h index 2af1ec01..cfef4c77 100644 --- a/src/modules/attributes/AttributeSetResourceAccount.h +++ b/src/modules/attributes/AttributeSetResourceAccount.h @@ -30,10 +30,10 @@ public: /** @brief Bind an account adapter to an existing caller-owned AttributeSet. */ explicit AttributeSetResourceAccount(AttributeSet& attributes); - AttributeSetResourceAccount(const AttributeSetResourceAccount&) = delete; + AttributeSetResourceAccount(const AttributeSetResourceAccount&) = delete; AttributeSetResourceAccount& operator=(const AttributeSetResourceAccount&) = delete; - AttributeSetResourceAccount(AttributeSetResourceAccount&&) = delete; - AttributeSetResourceAccount& operator=(AttributeSetResourceAccount&&) = delete; + AttributeSetResourceAccount(AttributeSetResourceAccount&&) = delete; + AttributeSetResourceAccount& operator=(AttributeSetResourceAccount&&) = delete; ~AttributeSetResourceAccount() override = default; @@ -42,46 +42,38 @@ public: const eve::resource::CostSpec& cost) const override; /** @copydoc eve::resource::IResourceAccount::reserve */ - [[nodiscard]] eve::Result reserve( - const eve::resource::CostSpec& cost) override; + [[nodiscard]] eve::Result reserve(const eve::resource::CostSpec& cost) override; /** @copydoc eve::resource::IResourceAccount::debit */ - [[nodiscard]] eve::Result debit( - const eve::resource::CostSpec& cost) override; + [[nodiscard]] eve::Result debit(const eve::resource::CostSpec& cost) override; /** @copydoc eve::resource::IResourceAccount::credit */ - [[nodiscard]] eve::Result credit( - const eve::resource::CostSpec& cost) override; + [[nodiscard]] eve::Result credit(const eve::resource::CostSpec& cost) override; /** @copydoc eve::resource::IResourceAccount::commit */ - [[nodiscard]] eve::Result commit( - const eve::resource::Reservation& reservation) override; + [[nodiscard]] eve::Result commit(const eve::resource::Reservation& reservation) override; /** @copydoc eve::resource::IResourceAccount::rollback */ - [[nodiscard]] eve::Result rollback( - const eve::resource::Reservation& reservation) override; + [[nodiscard]] eve::Result rollback(const eve::resource::Reservation& reservation) override; private: enum class ReservationState : std::uint8_t { Active, Committed, RolledBack }; struct ReservationRecord { eve::resource::CostSpec cost; - ReservationState state = ReservationState::Active; + ReservationState state = ReservationState::Active; }; - [[nodiscard]] eve::Result balanceOf( - const eve::resource::ResourceId& resource) const; - [[nodiscard]] eve::Result activeReservationsFor( - const eve::resource::ResourceId& resource) const; - [[nodiscard]] eve::Result activeReservationsAreCovered() const; - [[nodiscard]] eve::Result applyDelta(const eve::resource::CostSpec& cost, - bool debit); + [[nodiscard]] eve::Result balanceOf(const eve::resource::ResourceId& resource) const; + [[nodiscard]] eve::Result activeReservationsFor(const eve::resource::ResourceId& resource) const; + [[nodiscard]] eve::Result activeReservationsAreCovered() const; + [[nodiscard]] eve::Result applyDelta(const eve::resource::CostSpec& cost, bool debit); - AttributeSet& attributes_; + AttributeSet& attributes_; std::unordered_map reservations_; - eve::resource::AccountNonce accountNonce_; - eve::resource::ReservationId nextReservation_{1}; - eve::resource::ReceiptId nextReceipt_{1}; + eve::resource::AccountNonce accountNonce_; + eve::resource::ReservationId nextReservation_{1}; + eve::resource::ReceiptId nextReceipt_{1}; }; } // namespace eve::attributes diff --git a/src/modules/attributes/Attributes.cpp b/src/modules/attributes/Attributes.cpp index 70016b02..c364aa97 100644 --- a/src/modules/attributes/Attributes.cpp +++ b/src/modules/attributes/Attributes.cpp @@ -32,11 +32,9 @@ void Attributes::expose(ssq::Table& table) { const std::string& source, const std::string& operation, float value, int priority) { return s && s->addModifier(id, attribute, source, operation, value, priority); }); - set.addFunc("removeModifier", [](AttributeSet *s, const std::string &id) { - return s && s->removeModifier(id).ok(); - }); - set.addFunc("removeBySource", [](AttributeSet *s, const std::string &source, - const std::string &attribute) { + set.addFunc("removeModifier", + [](AttributeSet* s, const std::string& id) { return s && s->removeModifier(id).ok(); }); + set.addFunc("removeBySource", [](AttributeSet* s, const std::string& source, const std::string& attribute) { if (!s) return 0; auto result = s->removeBySource(source, attribute); return result.ok() ? result.value() : 0; diff --git a/src/modules/attributes/StateAccessAdapter.cpp b/src/modules/attributes/StateAccessAdapter.cpp index 454b9d8d..cc959160 100644 --- a/src/modules/attributes/StateAccessAdapter.cpp +++ b/src/modules/attributes/StateAccessAdapter.cpp @@ -7,8 +7,7 @@ namespace eve::attributes { namespace { -eve::Result failure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { +eve::Result failure(eve::DiagnosticCode code, std::string message, std::string path = {}) { return eve::Result::failure( eve::Diagnostic::error(code, std::move(message), std::move(path))); } @@ -31,24 +30,21 @@ bool AttributeSetStateAdapter::owns(std::string_view subject) const noexcept { return !subject.empty() && !attributes_.subject().empty() && attributes_.subject() == subject; } -std::optional AttributeSetStateAdapter::value(std::string_view subject, - std::string_view key) const { +std::optional AttributeSetStateAdapter::value(std::string_view subject, std::string_view key) const { if (!owns(subject) || key.empty() || !attributes_.has(std::string(key))) return std::nullopt; return eve::Value(attributes_.getFinal(std::string(key))); } -std::optional AttributeSetStateAdapter::attribute(std::string_view subject, - std::string_view key) const { +std::optional AttributeSetStateAdapter::attribute(std::string_view subject, std::string_view key) const { return value(subject, key); } -std::optional AttributeSetStateAdapter::state(std::string_view subject, - std::string_view key) const { +std::optional AttributeSetStateAdapter::state(std::string_view subject, std::string_view key) const { return value(subject, key); } -eve::Result AttributeSetStateAdapter::apply( - std::span mutations, const eve::MutationContext& context) { +eve::Result AttributeSetStateAdapter::apply(std::span mutations, + const eve::MutationContext& context) { for (std::size_t index = 0; index < mutations.size(); ++index) { const auto& mutation = mutations[index]; if (mutation.persistent) @@ -60,21 +56,19 @@ eve::Result AttributeSetStateAdapter::apply( "attribute mutation requires this set's subject and a key", "mutations[" + std::to_string(index) + "]"); if (mutation.kind != eve::MutationKind::Set && mutation.kind != eve::MutationKind::AddNumber) - return failure(eve::DiagnosticCode::Unsupported, - "AttributeSet supports Set and AddNumber only", + return failure(eve::DiagnosticCode::Unsupported, "AttributeSet supports Set and AddNumber only", "mutations[" + std::to_string(index) + "]"); double ignored = 0.0; if (!numeric(mutation.value, ignored)) - return failure(eve::DiagnosticCode::InvalidArgument, - "attribute mutation value must be a finite number", + return failure(eve::DiagnosticCode::InvalidArgument, "attribute mutation value must be a finite number", "mutations[" + std::to_string(index) + "].value"); } AttributeSet candidate = attributes_; - std::size_t changed = 0; + std::size_t changed = 0; for (const auto& mutation : mutations) { - const double amount = mutation.value.isInt64() ? static_cast(mutation.value.asInt()) - : mutation.value.asDouble(); + const double amount = + mutation.value.isInt64() ? static_cast(mutation.value.asInt()) : mutation.value.asDouble(); const double before = candidate.getFinal(mutation.key); if (mutation.kind == eve::MutationKind::Set) candidate.setBase(mutation.key, amount); diff --git a/src/modules/attributes/StateAccessAdapter.h b/src/modules/attributes/StateAccessAdapter.h index 8c8c09ae..83ad1af6 100644 --- a/src/modules/attributes/StateAccessAdapter.h +++ b/src/modules/attributes/StateAccessAdapter.h @@ -24,36 +24,28 @@ public: explicit AttributeSetStateAdapter(AttributeSet& attributes) noexcept : attributes_(attributes) {} /** @copydoc eve::IStateQuery::value */ - [[nodiscard]] std::optional value(std::string_view subject, - std::string_view key) const override; + [[nodiscard]] std::optional value(std::string_view subject, std::string_view key) const override; /** @copydoc eve::IStateQuery::hasTag */ - [[nodiscard]] std::optional hasTag(std::string_view, - std::string_view) const override { - return std::nullopt; - } + [[nodiscard]] std::optional hasTag(std::string_view, std::string_view) const override { return std::nullopt; } /** @copydoc eve::IStateQuery::attribute */ - [[nodiscard]] std::optional attribute(std::string_view subject, - std::string_view key) const override; + [[nodiscard]] std::optional attribute(std::string_view subject, std::string_view key) const override; /** @copydoc eve::IStateQuery::resource */ - [[nodiscard]] std::optional resource(std::string_view, - std::string_view) const override { + [[nodiscard]] std::optional resource(std::string_view, std::string_view) const override { return std::nullopt; } /** @copydoc eve::IStateQuery::state */ - [[nodiscard]] std::optional state(std::string_view subject, - std::string_view key) const override; + [[nodiscard]] std::optional state(std::string_view subject, std::string_view key) const override; /** @copydoc eve::IStateQuery::authority */ - [[nodiscard]] std::optional authority(std::string_view, - std::string_view) const override { + [[nodiscard]] std::optional authority(std::string_view, std::string_view) const override { return std::nullopt; } /** @copydoc eve::IStateMutation::apply */ - [[nodiscard]] eve::Result apply( - std::span mutations, const eve::MutationContext& context) override; + [[nodiscard]] eve::Result apply(std::span mutations, + const eve::MutationContext& context) override; private: - bool owns(std::string_view subject) const noexcept; + bool owns(std::string_view subject) const noexcept; AttributeSet& attributes_; }; diff --git a/src/modules/authority/Authority.cpp b/src/modules/authority/Authority.cpp index 6061e9c8..9a7c2b88 100644 --- a/src/modules/authority/Authority.cpp +++ b/src/modules/authority/Authority.cpp @@ -25,29 +25,24 @@ struct ScriptAuthorityStore { }; template -eve::Result authorityBindingFailure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - code, std::move(message), std::move(path), {}, "authority.squirrel")); +eve::Result authorityBindingFailure(eve::DiagnosticCode code, std::string message, std::string path = {}) { + return eve::Result::failure( + eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "authority.squirrel")); } template ssq::Table makeOwnedProxy(HSQUIRRELVM vm, eve::Result&& reference, Release&& release) { - if (!reference) - return eve::script::projectStatusResult(vm, reference.status(), false, false); - const Ref ref = std::move(reference).takeValue(); - auto object = eve::script::makeOwnedSquirrelInstance( - vm, std::make_unique(ref)); + if (!reference) return eve::script::projectStatusResult(vm, reference.status(), false, false); + const Ref ref = std::move(reference).takeValue(); + auto object = eve::script::makeOwnedSquirrelInstance(vm, std::make_unique(ref)); if (!object) { const eve::Status status = object.status(); object.ignore("failed to create owned authority proxy"); - std::invoke(std::forward(release), ref).ignore( - "rollback failed owned authority allocation"); + std::invoke(std::forward(release), ref).ignore("rollback failed owned authority allocation"); return eve::script::projectStatusResult(vm, status, false, false); } ssq::Object owned = std::move(object).takeValue(); - auto result = eve::script::projectStatusResult( - vm, eve::Status::success(eve::StatusCode::Applied), true, false); + auto result = eve::script::projectStatusResult(vm, eve::Status::success(eve::StatusCode::Applied), true, false); result.set("value", owned); result.set("ownership", std::string("owned")); result.set("ownerEpoch", static_cast(ref.ownerEpoch)); @@ -119,17 +114,17 @@ eve::LogicalId authoritySchema() { const eve::SnapshotMigrationChain& authorityMigrations() { static const eve::SnapshotMigrationChain chain = [] { eve::SnapshotMigrationChain result; - const auto registration = result.add( - authoritySchema(), eve::SchemaVersion(0), eve::SchemaVersion(1), - [](const eve::Value& payload) -> eve::Result { - const auto* object = payload.getIf(); - if (!object) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "authority snapshot payload must be an object")); - eve::Value::Object migrated = *object; - migrated["version"] = eve::Value(std::int64_t(1)); - return eve::Result::success(eve::Value(std::move(migrated))); - }); + const auto registration = + result.add(authoritySchema(), eve::SchemaVersion(0), eve::SchemaVersion(1), + [](const eve::Value& payload) -> eve::Result { + const auto* object = payload.getIf(); + if (!object) + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::ParseError, "authority snapshot payload must be an object")); + eve::Value::Object migrated = *object; + migrated["version"] = eve::Value(std::int64_t(1)); + return eve::Result::success(eve::Value(std::move(migrated))); + }); if (!registration.ok()) std::terminate(); return result; }(); @@ -226,7 +221,7 @@ void Store::update(double dtSeconds) { continue; } it->remaining = std::max(0.0, it->remaining - dtSeconds); - changed = true; + changed = true; if (it->remaining == 0.0) { emit(EventKind::Expired, *it, "duration_elapsed"); it = rules_.erase(it); @@ -307,10 +302,9 @@ eve::Result Store::restoreJson(const std::string& json) { if (!document.valid() || !root.isObject() || root.getInt("version") != 1 || !parseU64(root.get("nextId"), nextId) || !parseU64(root.get("nextOrder"), nextOrder) || !parseU64(root.get("nextSequence"), nextSequence) || nextId == 0 || nextOrder == 0 || nextSequence == 0 || !root.get("rules").isArray()) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, - parseError.empty() ? "invalid authority snapshot" : parseError, - "authority.store")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::ParseError, + parseError.empty() ? "invalid authority snapshot" : parseError, "authority.store")); } std::deque restored; const auto values = root.get("rules"); @@ -324,10 +318,9 @@ eve::Result Store::restoreJson(const std::string& json) { (effect != "grant" && effect != "deny") || !parseU64(item.get("order"), order) || order == 0 || order >= nextOrder || !parseRuleId(item.getString("id"), numericId) || numericId >= nextId || !item.get("priority").isNumber() || !item.get("duration").isNumber() || !item.get("remaining").isNumber()) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, - "invalid rule at index " + std::to_string(i), - "authority.store.rules[" + std::to_string(i) + "]")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "invalid rule at index " + std::to_string(i), + "authority.store.rules[" + std::to_string(i) + "]")); } rule.id = item.getString("id"); rule.actor = item.getString("actor"); @@ -345,8 +338,7 @@ eve::Result Store::restoreJson(const std::string& json) { restored.begin(), restored.end(), [&](const Rule& old) { return old.id == rule.id || old.order == rule.order; })) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, - "invalid rule state at index " + std::to_string(i), + eve::DiagnosticCode::InvariantViolation, "invalid rule state at index " + std::to_string(i), "authority.store.rules[" + std::to_string(i) + "]")); } restored.push_back(std::move(rule)); @@ -358,20 +350,19 @@ eve::Result Store::restoreJson(const std::string& json) { events_.clear(); query_.clear(); revision_ = eve::Revision(nextSequence > 0 ? nextSequence - 1 : 0); - tick_ = eve::SimulationTick{}; + tick_ = eve::SimulationTick{}; return eve::Result::success(); } -eve::Result Store::snapshot( - const eve::SnapshotHashProvider& hashProvider) const { +eve::Result Store::snapshot(const eve::SnapshotHashProvider& hashProvider) const { auto payload = eve::Value::fromJson(snapshotJson()); if (!payload.ok()) return eve::Result::failure(payload.status()); return eve::makeSnapshotEnvelope("authority.store", authoritySchema(), eve::SchemaVersion(1), instanceId_, revision_, tick_, std::move(payload).takeValue(), hashProvider); } -eve::Result Store::restoreSnapshot( - const eve::SnapshotEnvelope& source, const eve::SnapshotHashProvider& hashProvider) { +eve::Result Store::restoreSnapshot(const eve::SnapshotEnvelope& source, + const eve::SnapshotHashProvider& hashProvider) { if (source.type != "authority.store" || source.schema != authoritySchema()) return snapshotFailure(eve::DiagnosticCode::InvalidArgument, "snapshot does not belong to authority::Store"); @@ -382,9 +373,9 @@ eve::Result Store::restoreSnapshot( auto migrated = authorityMigrations().migrate(source, eve::SchemaVersion(1), hashProvider); if (!migrated.ok()) return eve::Result::failure(migrated.status()); const auto& candidate = migrated.value(); - auto metadata = eve::validateSnapshotPayloadMetadata(candidate.payload, candidate.revision, candidate.tick); + auto metadata = eve::validateSnapshotPayloadMetadata(candidate.payload, candidate.revision, candidate.tick); if (!metadata.ok()) return eve::Result::failure(metadata.status()); - auto payload = candidate.payload.toJson(); + auto payload = candidate.payload.toJson(); if (!payload.ok()) return eve::Result::failure(payload.status()); const std::string payloadJson = std::move(payload).takeValue(); @@ -399,16 +390,14 @@ eve::Result Store::restoreSnapshot( return eve::Result::success(); } -eve::Result Store::snapshotEnvelopeJson( - const eve::SnapshotHashProvider& hashProvider) const { +eve::Result Store::snapshotEnvelopeJson(const eve::SnapshotHashProvider& hashProvider) const { auto value = snapshot(hashProvider); if (!value.ok()) return eve::Result::failure(value.status()); return std::move(value).andThen( [](eve::SnapshotEnvelope&& envelope) { return eve::serializeSnapshotEnvelope(envelope); }); } -eve::Result Store::restoreSnapshotJson( - std::string_view json, const eve::SnapshotHashProvider& hashProvider) { +eve::Result Store::restoreSnapshotJson(std::string_view json, const eve::SnapshotHashProvider& hashProvider) { auto source = eve::parseSnapshotEnvelope(json, hashProvider); if (!source.ok()) return eve::Result::failure(source.status()); return restoreSnapshot(std::move(source).takeValue(), hashProvider); @@ -419,8 +408,7 @@ eve::Result Authority::newStore() { return module->stores_.emplace(std::make_unique()); } -eve::script::Borrowed Authority::resolve( - AuthorityStoreHandleRef reference) noexcept { +eve::script::Borrowed Authority::resolve(AuthorityStoreHandleRef reference) noexcept { Authority* module = ModuleManager::getInstance("Authority"); if (!module) return {}; return module->stores_.resolve(reference); @@ -429,8 +417,8 @@ eve::script::Borrowed Authority::resolve( eve::Result Authority::release(AuthorityStoreHandleRef reference) { Authority* module = ModuleManager::getInstance("Authority"); if (!module) - return authorityBindingFailure(eve::DiagnosticCode::StaleHandle, - "Authority module is no longer loaded", "store"); + return authorityBindingFailure(eve::DiagnosticCode::StaleHandle, "Authority module is no longer loaded", + "store"); return module->stores_.erase(reference); } @@ -443,7 +431,7 @@ bool Authority::isStale(AuthorityStoreHandleRef reference) noexcept { Module_IMPL(Authority, new Authority()); void Authority::expose(ssq::Table& table) { - const HSQUIRRELVM vm = table.getHandle(); + const HSQUIRRELVM vm = table.getHandle(); auto rule = table.addClass("AuthorityRule", std::function([] { return nullptr; }), false); rule.addFunc("getId", [](Rule* r) { return r ? r->id : std::string{}; }); rule.addFunc("getActor", [](Rule* r) { return r ? r->actor : std::string{}; }); @@ -501,13 +489,12 @@ void Authority::expose(ssq::Table& table) { if (!value) return eve::script::projectResult( vm, authorityBindingFailure(eve::DiagnosticCode::InvalidArgument, - "authority store must not be null", "store")); + "authority store must not be null", "store")); return eve::script::projectResult(vm, value->restoreJson(json)); }); auto ownedStore = table.addClass( - "AuthorityStoreProxy", - std::function([] { return nullptr; }), true); + "AuthorityStoreProxy", std::function([] { return nullptr; }), true); ownedStore.addFunc("ownership", [](ScriptAuthorityStore*) { return std::string("owned"); }); ownedStore.addFunc("ownerEpoch", [](ScriptAuthorityStore* value) { return value ? static_cast(value->reference.ownerEpoch) : int64_t{0}; @@ -515,82 +502,85 @@ void Authority::expose(ssq::Table& table) { ownedStore.addFunc("handle", [](ScriptAuthorityStore* value) { return value ? static_cast(value->reference.packed()) : int64_t{0}; }); - ownedStore.addFunc("isStale", [](ScriptAuthorityStore* value) { - return !value || Authority::isStale(value->reference); - }); + ownedStore.addFunc("isStale", + [](ScriptAuthorityStore* value) { return !value || Authority::isStale(value->reference); }); ownedStore.addFunc("release", [vm](ScriptAuthorityStore* value) { if (!value) return eve::script::projectResult( vm, authorityBindingFailure(eve::DiagnosticCode::InvalidArgument, - "owned authority store proxy must not be null", "store")); + "owned authority store proxy must not be null", "store")); return eve::script::projectResult(vm, Authority::release(value->reference)); }); - ownedStore.addFunc("grant", [vm](ScriptAuthorityStore* value, const std::string& actor, - const std::string& scope, const std::string& capability, - const std::string& source, int priority, float duration) { - if (!value) - return eve::script::projectStatusResult( - vm, authorityBindingFailure(eve::DiagnosticCode::InvalidArgument, - "owned authority store proxy must not be null", "store") - .status(), false, false); - auto view = Authority::resolve(value->reference); - if (!view.isBound()) - return eve::script::projectStatusResult( - vm, authorityBindingFailure(eve::DiagnosticCode::StaleHandle, - "owned authority store handle is stale", "store") - .status(), false, false); - const std::string id = view->grant(actor, scope, capability, source, priority, duration); - const auto status = id.empty() - ? eve::Status::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "authority rule was rejected", "store", {}, "authority.squirrel")) - : eve::Status::success(eve::StatusCode::Applied); - return eve::script::projectStatusResult(vm, status, !id.empty(), !id.empty(), - eve::Value(id)); - }); - ownedStore.addFunc("deny", [vm](ScriptAuthorityStore* value, const std::string& actor, - const std::string& scope, const std::string& capability, - const std::string& source, int priority, float duration) { - if (!value) - return eve::script::projectStatusResult( - vm, authorityBindingFailure(eve::DiagnosticCode::InvalidArgument, - "authority store proxy must not be null", "store") - .status(), false, false); - auto view = Authority::resolve(value->reference); - if (!view.isBound()) - return eve::script::projectStatusResult( - vm, authorityBindingFailure(eve::DiagnosticCode::StaleHandle, - "authority store handle is stale", "store") - .status(), false, false); - const std::string id = view->deny(actor, scope, capability, source, priority, duration); - const auto status = id.empty() - ? eve::Status::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "authority rule was rejected", "store", {}, "authority.squirrel")) - : eve::Status::success(eve::StatusCode::Applied); - return eve::script::projectStatusResult(vm, status, !id.empty(), !id.empty(), eve::Value(id)); - }); - ownedStore.addFunc("revoke", [](ScriptAuthorityStore* value, const std::string& id, - const std::string& reason) { + ownedStore.addFunc( + "grant", [vm](ScriptAuthorityStore* value, const std::string& actor, const std::string& scope, + const std::string& capability, const std::string& source, int priority, float duration) { + if (!value) + return eve::script::projectStatusResult( + vm, + authorityBindingFailure(eve::DiagnosticCode::InvalidArgument, + "owned authority store proxy must not be null", "store") + .status(), + false, false); + auto view = Authority::resolve(value->reference); + if (!view.isBound()) + return eve::script::projectStatusResult( + vm, + authorityBindingFailure(eve::DiagnosticCode::StaleHandle, + "owned authority store handle is stale", "store") + .status(), + false, false); + const std::string id = view->grant(actor, scope, capability, source, priority, duration); + const auto status = id.empty() ? eve::Status::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvalidArgument, "authority rule was rejected", + "store", {}, "authority.squirrel")) + : eve::Status::success(eve::StatusCode::Applied); + return eve::script::projectStatusResult(vm, status, !id.empty(), !id.empty(), eve::Value(id)); + }); + ownedStore.addFunc( + "deny", [vm](ScriptAuthorityStore* value, const std::string& actor, const std::string& scope, + const std::string& capability, const std::string& source, int priority, float duration) { + if (!value) + return eve::script::projectStatusResult( + vm, + authorityBindingFailure(eve::DiagnosticCode::InvalidArgument, + "authority store proxy must not be null", "store") + .status(), + false, false); + auto view = Authority::resolve(value->reference); + if (!view.isBound()) + return eve::script::projectStatusResult( + vm, + authorityBindingFailure(eve::DiagnosticCode::StaleHandle, "authority store handle is stale", + "store") + .status(), + false, false); + const std::string id = view->deny(actor, scope, capability, source, priority, duration); + const auto status = id.empty() ? eve::Status::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvalidArgument, "authority rule was rejected", + "store", {}, "authority.squirrel")) + : eve::Status::success(eve::StatusCode::Applied); + return eve::script::projectStatusResult(vm, status, !id.empty(), !id.empty(), eve::Value(id)); + }); + ownedStore.addFunc("revoke", [](ScriptAuthorityStore* value, const std::string& id, const std::string& reason) { auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed(); return view.isBound() && view->revoke(id, reason); }); - ownedStore.addFunc("revokeBySource", [](ScriptAuthorityStore* value, const std::string& source, - const std::string& reason) { - auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed(); - return view.isBound() ? view->revokeBySource(source, reason) : 0; - }); + ownedStore.addFunc("revokeBySource", + [](ScriptAuthorityStore* value, const std::string& source, const std::string& reason) { + auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() ? view->revokeBySource(source, reason) : 0; + }); ownedStore.addFunc("update", [](ScriptAuthorityStore* value, float dt) { auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed(); if (view.isBound()) view->update(dt); }); - ownedStore.addFunc("can", [](ScriptAuthorityStore* value, const std::string& actor, - const std::string& scope, const std::string& capability) { + ownedStore.addFunc("can", [](ScriptAuthorityStore* value, const std::string& actor, const std::string& scope, + const std::string& capability) { auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed(); return view.isBound() && view->can(actor, scope, capability); }); - ownedStore.addFunc("explain", [](ScriptAuthorityStore* value, const std::string& actor, - const std::string& scope, const std::string& capability) { + ownedStore.addFunc("explain", [](ScriptAuthorityStore* value, const std::string& actor, const std::string& scope, + const std::string& capability) { auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed(); return view.isBound() ? view->explain(actor, scope, capability) : Decision{}; }); @@ -610,8 +600,8 @@ void Authority::expose(ssq::Table& table) { auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed(); return view.isBound() ? view->queryCapability(capability) : 0; }); - ownedStore.addFunc("query", [](ScriptAuthorityStore* value, const std::string& actor, - const std::string& scope, const std::string& capability) { + ownedStore.addFunc("query", [](ScriptAuthorityStore* value, const std::string& actor, const std::string& scope, + const std::string& capability) { auto view = value ? Authority::resolve(value->reference) : eve::script::Borrowed(); return view.isBound() ? view->query(actor, scope, capability) : 0; }); @@ -640,7 +630,7 @@ void Authority::expose(ssq::Table& table) { if (!view.isBound()) return eve::script::projectResult( vm, authorityBindingFailure(eve::DiagnosticCode::StaleHandle, - "owned authority store handle is stale", "store")); + "owned authority store handle is stale", "store")); return eve::script::projectResult(vm, view->restoreJson(json)); }); @@ -651,8 +641,7 @@ void Authority::expose(ssq::Class& cls) { cls.addFunc("getName", &Authority::getName); cls.addFunc("newStore", [vm = cls.getHandle()](Authority*) -> ssq::Table { return makeOwnedProxy( - vm, Authority::newStore(), - [](AuthorityStoreHandleRef ref) { return Authority::release(ref); }); + vm, Authority::newStore(), [](AuthorityStoreHandleRef ref) { return Authority::release(ref); }); }); } diff --git a/src/modules/authority/Authority.h b/src/modules/authority/Authority.h index 1a445a76..136cb94d 100644 --- a/src/modules/authority/Authority.h +++ b/src/modules/authority/Authority.h @@ -122,8 +122,7 @@ public: * @param hashProvider Explicit content-digest provider; it is never defaulted silently. * @return A sealed snapshot, or a structured serialization/hash failure. */ - [[nodiscard]] eve::Result snapshot( - const eve::SnapshotHashProvider& hashProvider) const; + [[nodiscard]] eve::Result snapshot(const eve::SnapshotHashProvider& hashProvider) const; /** * @brief Restores a verified or migratable snapshot transactionally. @@ -131,16 +130,15 @@ public: * @param hashProvider Explicit provider used to verify and reseal the payload. * @return Success, or a failure leaving all store state unchanged. */ - [[nodiscard]] eve::Result restoreSnapshot( - const eve::SnapshotEnvelope& snapshot, const eve::SnapshotHashProvider& hashProvider); + [[nodiscard]] eve::Result restoreSnapshot(const eve::SnapshotEnvelope& snapshot, + const eve::SnapshotHashProvider& hashProvider); /** * @brief Serializes the common snapshot envelope as canonical JSON. * @param hashProvider Explicit content-digest provider. * @return Canonical envelope JSON or a structured failure. */ - [[nodiscard]] eve::Result snapshotEnvelopeJson( - const eve::SnapshotHashProvider& hashProvider) const; + [[nodiscard]] eve::Result snapshotEnvelopeJson(const eve::SnapshotHashProvider& hashProvider) const; /** * @brief Parses and transactionally restores a common snapshot envelope. @@ -148,8 +146,8 @@ public: * @param hashProvider Explicit provider used to verify contentHash. * @return Success, or a failure leaving all store state unchanged. */ - [[nodiscard]] eve::Result restoreSnapshotJson( - std::string_view json, const eve::SnapshotHashProvider& hashProvider); + [[nodiscard]] eve::Result restoreSnapshotJson(std::string_view json, + const eve::SnapshotHashProvider& hashProvider); private: std::string add(RuleEffect effect, const std::string& actor, const std::string& scope, @@ -160,8 +158,8 @@ private: uint64_t nextOrder_ = 1; uint64_t nextSequence_ = 1; eve::PersistentId instanceId_; - eve::Revision revision_; - eve::SimulationTick tick_; + eve::Revision revision_; + eve::SimulationTick tick_; std::deque rules_; std::deque events_; std::vector query_; @@ -186,8 +184,7 @@ public: */ [[nodiscard]] static eve::Result newStore(); /** @brief Resolves a live store as a non-owning observation. */ - [[nodiscard]] static eve::script::Borrowed resolve( - AuthorityStoreHandleRef reference) noexcept; + [[nodiscard]] static eve::script::Borrowed resolve(AuthorityStoreHandleRef reference) noexcept; /** @brief Releases a module-owned store. */ [[nodiscard]] static eve::Result release(AuthorityStoreHandleRef reference); /** @brief Reports whether a store reference is stale for the current module. */ diff --git a/src/modules/building/BuildingTypes.h b/src/modules/building/BuildingTypes.h index 57f6fce9..e95005b2 100644 --- a/src/modules/building/BuildingTypes.h +++ b/src/modules/building/BuildingTypes.h @@ -67,8 +67,8 @@ struct BuildingDefinition { * the member's stable id and are resolved by that domain. */ struct GarrisonMember { - std::string id; - std::string type = "building.garrison.member"; + std::string id; + std::string type = "building.garrison.member"; std::vector tags; }; diff --git a/src/modules/building/ContainerAdapters.cpp b/src/modules/building/ContainerAdapters.cpp index a1e758fd..ffced6be 100644 --- a/src/modules/building/ContainerAdapters.cpp +++ b/src/modules/building/ContainerAdapters.cpp @@ -19,30 +19,29 @@ namespace { result.push_back('"'); for (const unsigned char byte : value) { switch (byte) { - case '"': result += "\\\""; break; - case '\\': result += "\\\\"; break; - case '\n': result += "\\n"; break; - case '\r': result += "\\r"; break; - case '\t': result += "\\t"; break; - default: - if (byte < 0x20u) { - result += "\\u00"; - constexpr char digits[] = "0123456789abcdef"; - result.push_back(digits[(byte >> 4u) & 0x0fu]); - result.push_back(digits[byte & 0x0fu]); - } else { - result.push_back(static_cast(byte)); - } + case '"': result += "\\\""; break; + case '\\': result += "\\\\"; break; + case '\n': result += "\\n"; break; + case '\r': result += "\\r"; break; + case '\t': result += "\\t"; break; + default: + if (byte < 0x20u) { + result += "\\u00"; + constexpr char digits[] = "0123456789abcdef"; + result.push_back(digits[(byte >> 4u) & 0x0fu]); + result.push_back(digits[byte & 0x0fu]); + } else { + result.push_back(static_cast(byte)); + } } } result.push_back('"'); return result; } -[[nodiscard]] const GarrisonMember* payloadMember( - const eve::container::ContainerObject& object, GarrisonMember& fallback) { - const auto* payload = - dynamic_cast(object.payload.get()); +[[nodiscard]] const GarrisonMember* payloadMember(const eve::container::ContainerObject& object, + GarrisonMember& fallback) { + const auto* payload = dynamic_cast(object.payload.get()); if (payload != nullptr) { if (payload->member.id != object.id.value() || payload->member.type != object.type || payload->member.tags != object.tags) @@ -50,7 +49,7 @@ namespace { return &payload->member; } if (!object.id.isValid() || object.type.empty()) return nullptr; - fallback.id = object.id.value(); + fallback.id = object.id.value(); fallback.type = object.type; fallback.tags = object.tags; return &fallback; @@ -58,54 +57,48 @@ namespace { [[nodiscard]] bool sameLayout(const eve::container::ContainerSnapshot& lhs, const eve::container::ContainerSnapshot& rhs) { - if (lhs.id != rhs.id || lhs.revision != rhs.revision || - lhs.entries.size() != rhs.entries.size()) - return false; + if (lhs.id != rhs.id || lhs.revision != rhs.revision || lhs.entries.size() != rhs.entries.size()) return false; for (const auto& left : lhs.entries) { - const auto right = std::find_if( - rhs.entries.begin(), rhs.entries.end(), [&](const auto& entry) { - return entry.membership.object == left.membership.object; - }); + const auto right = std::find_if(rhs.entries.begin(), rhs.entries.end(), [&](const auto& entry) { + return entry.membership.object == left.membership.object; + }); if (right == rhs.entries.end() || left.membership.slot != right->membership.slot || - left.membership.generation != right->membership.generation || - left.object.id != right->object.id || left.object.type != right->object.type || - left.object.quantity != right->object.quantity || + left.membership.generation != right->membership.generation || left.object.id != right->object.id || + left.object.type != right->object.type || left.object.quantity != right->object.quantity || left.object.tags != right->object.tags) return false; } return true; } -[[nodiscard]] game_event::GameEvent makeEvent( - const eve::container::ContainerId& container, const eve::container::MembershipId& object, - eve::container::ContainerEventKind kind, eve::container::SlotIndex slot, - eve::Revision revision, std::uint64_t serial, eve::SimulationTick tick, - std::string_view reason = {}) { +[[nodiscard]] game_event::GameEvent makeEvent(const eve::container::ContainerId& container, + const eve::container::MembershipId& object, + eve::container::ContainerEventKind kind, eve::container::SlotIndex slot, + eve::Revision revision, std::uint64_t serial, eve::SimulationTick tick, + std::string_view reason = {}) { const auto schema = eve::LogicalId::parse(eve::container::containerEventSchemaName); if (!schema) std::terminate(); game_event::GameEvent event; - event.eventId = eve::container::deterministicContainerEventId(container, object, kind, serial); - event.type = eve::container::containerEventTypeName(kind); - event.source = container.value(); - event.subject = object.value(); - event.schemaId = *schema; + event.eventId = eve::container::deterministicContainerEventId(container, object, kind, serial); + event.type = eve::container::containerEventTypeName(kind); + event.source = container.value(); + event.subject = object.value(); + event.schemaId = *schema; event.schemaVersion = eve::SchemaVersion(1); - event.tick = tick; - event.payload = "{\"containerId\":" + quote(container.value()) + - ",\"objectId\":" + quote(object.value()) + - ",\"slot\":" + std::to_string(slot.isValid() ? slot.value() : -1) + - ",\"revision\":\"" + std::to_string(revision.value()) + "\""; + event.tick = tick; + event.payload = "{\"containerId\":" + quote(container.value()) + ",\"objectId\":" + quote(object.value()) + + ",\"slot\":" + std::to_string(slot.isValid() ? slot.value() : -1) + ",\"revision\":\"" + + std::to_string(revision.value()) + "\""; if (!reason.empty()) event.payload += ",\"reason\":" + quote(reason); event.payload += '}'; return event; } -void notify(const eve::container::GameEventSink& sink, - const eve::container::ContainerId& container, +void notify(const eve::container::GameEventSink& sink, const eve::container::ContainerId& container, const eve::container::MembershipId& object, eve::container::ContainerEventKind kind, - eve::container::SlotIndex slot, eve::Revision revision, std::uint64_t serial, - eve::SimulationTick tick, std::string_view reason, bool& callbackFailed) noexcept { + eve::container::SlotIndex slot, eve::Revision revision, std::uint64_t serial, eve::SimulationTick tick, + std::string_view reason, bool& callbackFailed) noexcept { if (!sink) return; try { const auto event = makeEvent(container, object, kind, slot, revision, serial, tick, reason); @@ -120,8 +113,7 @@ void notify(const eve::container::GameEventSink& sink, try { std::vector diagnostics; diagnostics.emplace_back(eve::Diagnostic::warning( - eve::DiagnosticCode::CallbackFailure, - "container membership committed but an event observer threw")); + eve::DiagnosticCode::CallbackFailure, "container membership committed but an event observer threw")); return eve::Status(eve::StatusCode::Applied, std::move(diagnostics)); } catch (...) { return eve::Status::success(eve::StatusCode::Applied); @@ -130,14 +122,15 @@ void notify(const eve::container::GameEventSink& sink, } // namespace -class BuildingGarrisonContainerAdapter::PreparedState final - : public eve::container::IContainer::PreparedState { +class BuildingGarrisonContainerAdapter::PreparedState final : public eve::container::IContainer::PreparedState { public: PreparedState(BuildingGarrisonContainerAdapter& owner, std::vector staged, - eve::Revision expectedRevision, std::size_t expectedSize, - eve::Revision revision) - : owner_(owner), staged_(std::move(staged)), expectedRevision_(expectedRevision), - expectedSize_(expectedSize), revision_(revision) {} + eve::Revision expectedRevision, std::size_t expectedSize, eve::Revision revision) + : owner_(owner), + staged_(std::move(staged)), + expectedRevision_(expectedRevision), + expectedSize_(expectedSize), + revision_(revision) {} void commit() noexcept override { if (committed_) return; @@ -147,7 +140,7 @@ public: std::terminate(); building->garrison.swap(staged_); building->garrisonRevision = revision_; - committed_ = true; + committed_ = true; } void rollback() noexcept override { @@ -156,31 +149,32 @@ public: private: BuildingGarrisonContainerAdapter& owner_; - std::vector staged_; - eve::Revision expectedRevision_; - std::size_t expectedSize_ = 0; - eve::Revision revision_; - bool committed_ = false; + std::vector staged_; + eve::Revision expectedRevision_; + std::size_t expectedSize_ = 0; + eve::Revision revision_; + bool committed_ = false; }; -BuildingGarrisonContainerAdapter::BuildingGarrisonContainerAdapter( - eve::container::ContainerId id, PlacementWorld* world, int instanceId, - eve::container::Capacity capacity, eve::container::AcceptedCondition accepted) - : descriptor_{std::move(id), capacity, eve::container::Ordering::Insertion, - std::move(accepted)}, - world_(world), instanceId_(instanceId) {} +BuildingGarrisonContainerAdapter::BuildingGarrisonContainerAdapter(eve::container::ContainerId id, + PlacementWorld* world, int instanceId, + eve::container::Capacity capacity, + eve::container::AcceptedCondition accepted) + : descriptor_{std::move(id), capacity, eve::container::Ordering::Insertion, std::move(accepted)}, + world_(world), + instanceId_(instanceId) {} const PlacedBuilding* BuildingGarrisonContainerAdapter::building() const noexcept { if (world_ == nullptr || instanceId_ <= 0) return nullptr; const auto& values = world_->buildings(); - const auto it = values.find(instanceId_); + const auto it = values.find(instanceId_); return it == values.end() ? nullptr : &it->second; } PlacedBuilding* BuildingGarrisonContainerAdapter::building() noexcept { if (world_ == nullptr || instanceId_ <= 0) return nullptr; - auto& values = world_->buildings(); - const auto it = values.find(instanceId_); + auto& values = world_->buildings(); + const auto it = values.find(instanceId_); return it == values.end() ? nullptr : &it->second; } @@ -194,123 +188,103 @@ std::uint64_t BuildingGarrisonContainerAdapter::nextEventSerial() noexcept { return eventSerial_; } -eve::container::ContainerObject BuildingGarrisonContainerAdapter::describe( - const GarrisonMember& member) { +eve::container::ContainerObject BuildingGarrisonContainerAdapter::describe(const GarrisonMember& member) { eve::container::ContainerObject object; - object.id = eve::container::MembershipId(member.id); - object.type = member.type; - object.tags = member.tags; + object.id = eve::container::MembershipId(member.id); + object.type = member.type; + object.tags = member.tags; object.quantity = 1; - auto payload = std::make_shared(); + auto payload = std::make_shared(); payload->member = member; - object.payload = std::move(payload); + object.payload = std::move(payload); return object; } -eve::Result -BuildingGarrisonContainerAdapter::snapshot() const { +eve::Result BuildingGarrisonContainerAdapter::snapshot() const { const auto* value = building(); if (!descriptor_.id.isValid() || value == nullptr) return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, - "building garrison adapter is stale or unbound")); - if (!descriptor_.capacity.isUnlimited() && - value->garrison.size() > descriptor_.capacity.value()) - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, - "building garrison exceeds configured capacity")); + eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, "building garrison adapter is stale or unbound")); + if (!descriptor_.capacity.isUnlimited() && value->garrison.size() > descriptor_.capacity.value()) + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvariantViolation, "building garrison exceeds configured capacity")); eve::container::ContainerSnapshot result{descriptor_.id, value->garrisonRevision, {}}; result.entries.reserve(value->garrison.size()); std::unordered_set identities; for (std::size_t index = 0; index < value->garrison.size(); ++index) { if (index > static_cast(std::numeric_limits::max())) - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, - "building garrison has too many indexed memberships")); + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvariantViolation, "building garrison has too many indexed memberships")); const auto& member = value->garrison[index]; if (!eve::container::MembershipId::from(member.id) || member.type.empty() || !identities.insert(eve::container::MembershipId(member.id)).second) - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "building garrison contains invalid or duplicate member")); + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::Conflict, "building garrison contains invalid or duplicate member")); const auto id = eve::container::MembershipId(member.id); - result.entries.push_back({{id, eve::container::SlotIndex(static_cast(index)), - eve::Generation(1)}, - describe(member)}); + result.entries.push_back( + {{id, eve::container::SlotIndex(static_cast(index)), eve::Generation(1)}, describe(member)}); } return eve::Result::success(std::move(result)); } eve::Result BuildingGarrisonContainerAdapter::validateInsert( - const eve::container::ContainerObject& object, - std::optional destination, + const eve::container::ContainerObject& object, std::optional destination, std::optional ignoredObject) const { const auto* value = building(); if (!descriptor_.id.isValid() || value == nullptr) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::StaleHandle, "building garrison adapter is stale or unbound")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, "building garrison adapter is stale or unbound")); if (!object.id.isValid() || object.type.empty() || object.quantity != 1u) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "building garrison object facts are invalid")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "building garrison object facts are invalid")); GarrisonMember fallback; if (payloadMember(object, fallback) == nullptr) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::StaleHandle, "building garrison payload is stale")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, "building garrison payload is stale")); for (const auto& member : value->garrison) { - if (member.id == object.id.value() && - (!ignoredObject || *ignoredObject != object.id)) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, "building garrison already contains member")); + if (member.id == object.id.value() && (!ignoredObject || *ignoredObject != object.id)) + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "building garrison already contains member")); } - const std::size_t effectiveSize = value->garrison.size() - + const std::size_t effectiveSize = + value->garrison.size() - ((ignoredObject && std::any_of(value->garrison.begin(), value->garrison.end(), - [&](const auto& member) { - return member.id == ignoredObject->value(); - })) + [&](const auto& member) { return member.id == ignoredObject->value(); })) ? 1u : 0u); - if (destination && - (!destination->isValid() || static_cast(destination->value()) > effectiveSize)) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "building garrison slot is out of range")); + if (destination && (!destination->isValid() || static_cast(destination->value()) > effectiveSize)) + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "building garrison slot is out of range")); auto accepted = descriptor_.filter.evaluate(object); if (!accepted) return accepted; if (!descriptor_.capacity.isUnlimited() && effectiveSize >= descriptor_.capacity.value()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, "building garrison capacity is full")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "building garrison capacity is full")); return eve::Result::success(); } -eve::Result> -BuildingGarrisonContainerAdapter::prepare(const eve::container::ContainerSnapshot& expected, - const eve::container::ContainerSnapshot& candidate) { +eve::Result> BuildingGarrisonContainerAdapter::prepare( + const eve::container::ContainerSnapshot& expected, const eve::container::ContainerSnapshot& candidate) { const auto* value = building(); if (!descriptor_.id.isValid() || value == nullptr) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, - "building garrison adapter is stale or unbound")); + eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, "building garrison adapter is stale or unbound")); if (expected.id != descriptor_.id || candidate.id != descriptor_.id) - return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, - "building garrison prepare targets another container")); + return eve::Result>::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvalidArgument, "building garrison prepare targets another container")); auto current = snapshot(); if (!current) - return eve::Result>::failure( - current.status()); + return eve::Result>::failure(current.status()); if (!sameLayout(current.value(), expected)) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, - "building garrison expected snapshot is stale")); + eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, "building garrison expected snapshot is stale")); const auto next = expected.revision.incremented(); if (!next || candidate.revision != *next) + return eve::Result>::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvariantViolation, "building garrison candidate revision is not the next revision")); + if (!descriptor_.capacity.isUnlimited() && candidate.entries.size() > descriptor_.capacity.value()) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, - "building garrison candidate revision is not the next revision")); - if (!descriptor_.capacity.isUnlimited() && - candidate.entries.size() > descriptor_.capacity.value()) - return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "building garrison candidate exceeds capacity")); + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "building garrison candidate exceeds capacity")); std::vector staged; staged.reserve(candidate.entries.size()); @@ -318,14 +292,14 @@ BuildingGarrisonContainerAdapter::prepare(const eve::container::ContainerSnapsho for (std::size_t index = 0; index < candidate.entries.size(); ++index) { const auto& entry = candidate.entries[index]; if (!entry.membership.object.isValid() || !entry.membership.slot.isValid() || - entry.membership.slot.value() != static_cast(index) || - entry.membership.generation.isZero() || entry.object.id != entry.membership.object || - entry.object.quantity != 1u || !identities.insert(entry.membership.object).second) + entry.membership.slot.value() != static_cast(index) || entry.membership.generation.isZero() || + entry.object.id != entry.membership.object || entry.object.quantity != 1u || + !identities.insert(entry.membership.object).second) return eve::Result>::failure( eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "building garrison candidate contains invalid membership")); GarrisonMember fallback; - const auto* member = payloadMember(entry.object, fallback); + const auto* member = payloadMember(entry.object, fallback); if (member == nullptr) return eve::Result>::failure( eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, @@ -333,21 +307,21 @@ BuildingGarrisonContainerAdapter::prepare(const eve::container::ContainerSnapsho staged.push_back(*member); } std::unique_ptr prepared = - std::make_unique(*this, std::move(staged), expected.revision, - expected.entries.size(), *next); - return eve::Result>::success( - std::move(prepared)); + std::make_unique(*this, std::move(staged), expected.revision, expected.entries.size(), *next); + return eve::Result>::success(std::move(prepared)); } -eve::Result BuildingGarrisonContainerAdapter::enter( - std::string memberId, std::string type, std::vector tags, - eve::container::GameEventSink sink, eve::SimulationTick tick) { - const auto serial = nextEventSerial(); +eve::Result BuildingGarrisonContainerAdapter::enter(std::string memberId, + std::string type, + std::vector tags, + eve::container::GameEventSink sink, + eve::SimulationTick tick) { + const auto serial = nextEventSerial(); const eve::container::MembershipId objectId(std::move(memberId)); - auto current = snapshot(); + auto current = snapshot(); if (!current) { - const auto status = current.status(); - bool ignored = false; + const auto status = current.status(); + bool ignored = false; const auto* diagnostic = status.primaryDiagnostic(); notify(sink, descriptor_.id, objectId, eve::container::ContainerEventKind::Rejected, eve::container::SlotIndex::invalid(), revision(), serial, tick, @@ -355,41 +329,39 @@ eve::Result BuildingGarrisonContainerAdapter::ente return eve::Result::failure(status); } eve::container::ContainerObject object; - object.id = objectId; - object.type = type.empty() ? "building.garrison.member" : std::move(type); - object.tags = std::move(tags); + object.id = objectId; + object.type = type.empty() ? "building.garrison.member" : std::move(type); + object.tags = std::move(tags); object.quantity = 1; - auto accepted = validateInsert(object, std::nullopt, std::nullopt); + auto accepted = validateInsert(object, std::nullopt, std::nullopt); if (!accepted) { - const auto status = accepted.status(); - bool ignored = false; + const auto status = accepted.status(); + bool ignored = false; const auto* diagnostic = status.primaryDiagnostic(); notify(sink, descriptor_.id, objectId, eve::container::ContainerEventKind::Rejected, eve::container::SlotIndex::invalid(), current.value().revision, serial, tick, diagnostic == nullptr ? "rejected" : diagnostic->message(), ignored); return eve::Result::failure(status); } - auto candidate = current.value(); - const auto next = candidate.revision.incremented(); + auto candidate = current.value(); + const auto next = candidate.revision.incremented(); if (!next) { - const auto status = eve::Status::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, "building garrison revision exhausted")); + const auto status = eve::Status::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "building garrison revision exhausted")); bool ignored = false; notify(sink, descriptor_.id, objectId, eve::container::ContainerEventKind::Rejected, - eve::container::SlotIndex::invalid(), candidate.revision, serial, tick, - "revision_exhausted", ignored); + eve::container::SlotIndex::invalid(), candidate.revision, serial, tick, "revision_exhausted", ignored); return eve::Result::failure(status); } candidate.revision = *next; - candidate.entries.push_back({{objectId, - eve::container::SlotIndex(static_cast( - current.value().entries.size())), - eve::Generation(1)}, - std::move(object)}); + candidate.entries.push_back( + {{objectId, eve::container::SlotIndex(static_cast(current.value().entries.size())), + eve::Generation(1)}, + std::move(object)}); auto prepared = prepare(current.value(), candidate); if (!prepared) { - const auto status = prepared.status(); - bool ignored = false; + const auto status = prepared.status(); + bool ignored = false; const auto* diagnostic = status.primaryDiagnostic(); notify(sink, descriptor_.id, objectId, eve::container::ContainerEventKind::Rejected, eve::container::SlotIndex::invalid(), current.value().revision, serial, tick, @@ -398,22 +370,21 @@ eve::Result BuildingGarrisonContainerAdapter::ente } auto staged = std::move(prepared).takeValue(); staged->commit(); - bool callbackFailed = false; - const auto slot = candidate.entries.back().membership.slot; - notify(sink, descriptor_.id, objectId, eve::container::ContainerEventKind::Accepted, slot, - *next, serial, tick, {}, callbackFailed); - notify(sink, descriptor_.id, objectId, eve::container::ContainerEventKind::Enter, slot, - *next, serial, tick, {}, callbackFailed); - return eve::Result::success( - objectId, appliedStatus(callbackFailed)); + bool callbackFailed = false; + const auto slot = candidate.entries.back().membership.slot; + notify(sink, descriptor_.id, objectId, eve::container::ContainerEventKind::Accepted, slot, *next, serial, tick, {}, + callbackFailed); + notify(sink, descriptor_.id, objectId, eve::container::ContainerEventKind::Enter, slot, *next, serial, tick, {}, + callbackFailed); + return eve::Result::success(objectId, appliedStatus(callbackFailed)); } -eve::Result BuildingGarrisonContainerAdapter::exit( - eve::container::MembershipId memberId, eve::container::GameEventSink sink, - eve::SimulationTick tick) { - const auto serial = nextEventSerial(); - auto current = snapshot(); - eve::container::SlotIndex slot = eve::container::SlotIndex::invalid(); +eve::Result BuildingGarrisonContainerAdapter::exit(eve::container::MembershipId memberId, + eve::container::GameEventSink sink, + eve::SimulationTick tick) { + const auto serial = nextEventSerial(); + auto current = snapshot(); + eve::container::SlotIndex slot = eve::container::SlotIndex::invalid(); if (current) { for (const auto& entry : current.value().entries) { if (entry.membership.object == memberId) { @@ -423,56 +394,49 @@ eve::Result BuildingGarrisonContainerAdapter::exit } } if (!current || !slot.isValid()) { - const auto status = current - ? eve::Status::failure(eve::Diagnostic::error( - eve::DiagnosticCode::NotFound, - "building garrison member was not found")) - : current.status(); - bool ignored = false; + const auto status = current ? eve::Status::failure(eve::Diagnostic::error( + eve::DiagnosticCode::NotFound, "building garrison member was not found")) + : current.status(); + bool ignored = false; const auto* diagnostic = status.primaryDiagnostic(); - notify(sink, descriptor_.id, memberId, eve::container::ContainerEventKind::Rejected, slot, - revision(), serial, tick, - diagnostic == nullptr ? "rejected" : diagnostic->message(), ignored); + notify(sink, descriptor_.id, memberId, eve::container::ContainerEventKind::Rejected, slot, revision(), serial, + tick, diagnostic == nullptr ? "rejected" : diagnostic->message(), ignored); return eve::Result::failure(status); } auto candidate = current.value(); candidate.entries.erase(std::remove_if(candidate.entries.begin(), candidate.entries.end(), - [&](const auto& entry) { - return entry.membership.object == memberId; - }), - candidate.entries.end()); + [&](const auto& entry) { return entry.membership.object == memberId; }), + candidate.entries.end()); for (std::size_t index = 0; index < candidate.entries.size(); ++index) - candidate.entries[index].membership.slot = - eve::container::SlotIndex(static_cast(index)); + candidate.entries[index].membership.slot = eve::container::SlotIndex(static_cast(index)); const auto next = candidate.revision.incremented(); if (!next) { - const auto status = eve::Status::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, "building garrison revision exhausted")); + const auto status = eve::Status::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "building garrison revision exhausted")); bool ignored = false; notify(sink, descriptor_.id, memberId, eve::container::ContainerEventKind::Rejected, slot, current.value().revision, serial, tick, "revision_exhausted", ignored); return eve::Result::failure(status); } candidate.revision = *next; - auto prepared = prepare(current.value(), candidate); + auto prepared = prepare(current.value(), candidate); if (!prepared) { - const auto status = prepared.status(); - bool ignored = false; + const auto status = prepared.status(); + bool ignored = false; const auto* diagnostic = status.primaryDiagnostic(); notify(sink, descriptor_.id, memberId, eve::container::ContainerEventKind::Rejected, slot, - current.value().revision, serial, tick, - diagnostic == nullptr ? "prepare_failed" : diagnostic->message(), ignored); + current.value().revision, serial, tick, diagnostic == nullptr ? "prepare_failed" : diagnostic->message(), + ignored); return eve::Result::failure(status); } auto staged = std::move(prepared).takeValue(); staged->commit(); bool callbackFailed = false; - notify(sink, descriptor_.id, memberId, eve::container::ContainerEventKind::Accepted, slot, - *next, serial, tick, {}, callbackFailed); - notify(sink, descriptor_.id, memberId, eve::container::ContainerEventKind::Exit, slot, *next, - serial, tick, {}, callbackFailed); - return eve::Result::success( - memberId, appliedStatus(callbackFailed)); + notify(sink, descriptor_.id, memberId, eve::container::ContainerEventKind::Accepted, slot, *next, serial, tick, {}, + callbackFailed); + notify(sink, descriptor_.id, memberId, eve::container::ContainerEventKind::Exit, slot, *next, serial, tick, {}, + callbackFailed); + return eve::Result::success(memberId, appliedStatus(callbackFailed)); } } // namespace eve::building diff --git a/src/modules/building/ContainerAdapters.h b/src/modules/building/ContainerAdapters.h index 89b60816..e4d22089 100644 --- a/src/modules/building/ContainerAdapters.h +++ b/src/modules/building/ContainerAdapters.h @@ -41,23 +41,20 @@ public: * @param capacity Maximum member count. * @param accepted Side-effect-free condition applied before entry. */ - BuildingGarrisonContainerAdapter( - eve::container::ContainerId id, PlacementWorld* world, int instanceId, - eve::container::Capacity capacity = eve::container::Capacity::unlimited(), - eve::container::AcceptedCondition accepted = {}); + BuildingGarrisonContainerAdapter(eve::container::ContainerId id, PlacementWorld* world, int instanceId, + eve::container::Capacity capacity = eve::container::Capacity::unlimited(), + eve::container::AcceptedCondition accepted = {}); [[nodiscard]] const eve::container::ContainerDescriptor& descriptor() const noexcept override { return descriptor_; } [[nodiscard]] eve::Result snapshot() const override; - [[nodiscard]] eve::Result validateInsert( - const eve::container::ContainerObject& object, - std::optional destination, - std::optional ignoredObject = std::nullopt) const override; + [[nodiscard]] eve::Result validateInsert( + const eve::container::ContainerObject& object, std::optional destination, + std::optional ignoredObject = std::nullopt) const override; /** @copydoc eve::container::IContainer::prepare */ [[nodiscard]] eve::Result> prepare( - const eve::container::ContainerSnapshot& expected, - const eve::container::ContainerSnapshot& candidate) override; + const eve::container::ContainerSnapshot& expected, const eve::container::ContainerSnapshot& candidate) override; /** @brief Return the authoritative garrison revision, or zero when stale. */ [[nodiscard]] eve::Revision revision() const noexcept; @@ -72,9 +69,8 @@ public: * @return The member identity, or a rejection with no membership mutation. */ [[nodiscard]] eve::Result enter( - std::string memberId, std::string type = "building.garrison.member", - std::vector tags = {}, eve::container::GameEventSink sink = {}, - eve::SimulationTick tick = eve::SimulationTick::zero()); + std::string memberId, std::string type = "building.garrison.member", std::vector tags = {}, + eve::container::GameEventSink sink = {}, eve::SimulationTick tick = eve::SimulationTick::zero()); /** * @brief Stage and publish removal of one member. @@ -101,14 +97,14 @@ private: */ [[nodiscard]] const PlacedBuilding* building() const noexcept; /** @copydoc BuildingGarrisonContainerAdapter::building() */ - [[nodiscard]] PlacedBuilding* building() noexcept; - [[nodiscard]] std::uint64_t nextEventSerial() noexcept; + [[nodiscard]] PlacedBuilding* building() noexcept; + [[nodiscard]] std::uint64_t nextEventSerial() noexcept; [[nodiscard]] static eve::container::ContainerObject describe(const GarrisonMember& member); eve::container::ContainerDescriptor descriptor_; - PlacementWorld* world_ = nullptr; - int instanceId_ = 0; - std::uint64_t eventSerial_ = 0; + PlacementWorld* world_ = nullptr; + int instanceId_ = 0; + std::uint64_t eventSerial_ = 0; }; } // namespace eve::building diff --git a/src/modules/camera/CameraController.cpp b/src/modules/camera/CameraController.cpp index 885ff9ac..430702c8 100644 --- a/src/modules/camera/CameraController.cpp +++ b/src/modules/camera/CameraController.cpp @@ -3,8 +3,8 @@ #include "common/CameraObstruction.h" #include "common/Capability.h" #include "common/Json.h" -#include "platform_event/PlatformEvent.h" #include "graphics/RenderSystem3D.h" +#include "platform_event/PlatformEvent.h" #include "scene/SceneNodeRef.h" #include diff --git a/src/modules/camera/CameraController.h b/src/modules/camera/CameraController.h index cc4da568..9ed7b17a 100644 --- a/src/modules/camera/CameraController.h +++ b/src/modules/camera/CameraController.h @@ -278,7 +278,7 @@ private: std::vector timelineCuts_; std::vector timelineEvents_; std::vector timelineFloats_; - platform_event::PlatformEvent* eventSink_ = nullptr; + platform_event::PlatformEvent* eventSink_ = nullptr; float timelineTime_ = 0.f; float timelineDuration_ = 0.f; bool timelinePlaying_ = false; diff --git a/src/modules/card/Card.cpp b/src/modules/card/Card.cpp index c8b9c2bf..f8d2425a 100644 --- a/src/modules/card/Card.cpp +++ b/src/modules/card/Card.cpp @@ -79,7 +79,7 @@ bool parseDefinition(Value o, std::unordered_map &d def.cost = o.getInt("cost"); def.attack = o.getInt("attack"); def.health = o.getInt("health"); - def.tags = o.getStringArray("tags"); + def.tags = o.getStringArray("tags"); auto tint = o.getFloatArray("tint"); if (tint.size() >= 3) def.tint = glm::vec3(tint[0], tint[1], tint[2]); defs[id] = def; @@ -219,11 +219,11 @@ bool Card::removeCardDefinition(const std::string &id) { eve::Result Card::setCardPlayCondition(const std::string &id, decision::Condition condition) { auto it = defs_.find(id); if (it == defs_.end()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::NotFound, "card definition was not found", id)); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "card definition was not found", id)); if (!condition.isValid()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "card play condition is invalid", id)); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "card play condition is invalid", id)); it->second.playCondition = std::move(condition); return eve::Result::success(); } @@ -233,37 +233,34 @@ const CardDefinition *Card::findDef(const std::string &id) const { return it == defs_.end() ? nullptr : &it->second; } -decision::ConditionResult Card::evaluatePlay(const CardData* card, - CardPlayConditionQueries queries) const { +decision::ConditionResult Card::evaluatePlay(const CardData *card, CardPlayConditionQueries queries) const { if (!card) return decision::ConditionResult::failed(decision::ConditionReasonCode::InvalidCondition); - const auto* definition = findDef(const_cast(card)->identity()->definitionId); + const auto *definition = findDef(const_cast(card)->identity()->definitionId); if (!definition) return decision::ConditionResult::failed(decision::ConditionReasonCode::InvalidCondition); return CardPlayConditionAdapter::evaluate(card, *definition, definition->playCondition, std::move(queries)); } eve::Result Card::getCardAttribute(CardData &card, std::string_view attribute) const { if (!ownsCard(cards_, card)) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::StaleHandle, "card does not belong to this Card facade", "card")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, + "card does not belong to this Card facade", "card")); return CardAttributeAdapter::read(card, attribute); } -eve::Result Card::setCardAttribute(CardData &card, std::string_view attribute, - double value) const { +eve::Result Card::setCardAttribute(CardData &card, std::string_view attribute, double value) const { if (!ownsCard(cards_, card)) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::StaleHandle, "card does not belong to this Card facade", "card")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, + "card does not belong to this Card facade", "card")); return CardAttributeAdapter::setBase(card, attribute, value); } -eve::Result Card::applyEffect( - CardData &card, const CardEffectDefinition &definition, eve::SubjectRef subject) const { +eve::Result Card::applyEffect(CardData &card, const CardEffectDefinition &definition, + eve::SubjectRef subject) const { if (!ownsCard(cards_, card)) return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::StaleHandle, "card does not belong to this Card facade", "card")); auto attributes = CardAttributeAdapter::ensure(card); - if (!attributes) - return eve::Result::failure(attributes.status()); + if (!attributes) return eve::Result::failure(attributes.status()); return card.effects()->values.apply(definition, subject); } @@ -274,36 +271,32 @@ eve::Result Card::step(const eve::SimulationStep &simulationStep) { if (card == nullptr || card->effects()->values.count() == 0) continue; auto updated = card->effects()->values.advance(simulationStep); if (!updated) return eve::Result::failure(updated.status()); - auto result = std::move(updated).takeValue(); - auto projected = CardAttributeAdapter::setBase( - *card, CardAttributeAdapter::healthAttribute, - static_cast(card->effects()->values.target().health)); - if (!projected) - return eve::Result::failure(projected.status()); + auto result = std::move(updated).takeValue(); + auto projected = CardAttributeAdapter::setBase(*card, CardAttributeAdapter::healthAttribute, + static_cast(card->effects()->values.target().health)); + if (!projected) return eve::Result::failure(projected.status()); settled += result.settled; } return eve::Result::success( - settled, eve::Status::success(settled == 0 ? eve::StatusCode::NoOp - : eve::StatusCode::Applied)); + settled, eve::Status::success(settled == 0 ? eve::StatusCode::NoOp : eve::StatusCode::Applied)); } -eve::Result Card::play( - CardData &card, eve::resource::IResourceAccount &playerAccount, - CardPlayComposition composition, std::string transactionId) { +eve::Result Card::play(CardData &card, + eve::resource::IResourceAccount &playerAccount, + CardPlayComposition composition, + std::string transactionId) { if (!ownsCard(cards_, card)) - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, - "card does not belong to this Card facade", "card")); + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::StaleHandle, "card does not belong to this Card facade", "card")); const auto *definition = findDef(card.identity()->definitionId); if (definition == nullptr) return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::NotFound, - "card definition was not found", "definition")); + eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "card definition was not found", "definition")); CardPlayRequest request; - request.card = &card; - request.definition = definition; + request.card = &card; + request.definition = definition; request.playerAccount = &playerAccount; - request.composition = std::move(composition); + request.composition = std::move(composition); request.transactionId = std::move(transactionId); return CardPlayPaymentAdapter::play(std::move(request)); } @@ -372,15 +365,14 @@ CardData *Card::newCard(const std::string &defId) { c->stats()->attack = d->attack; c->stats()->health = d->health; c->visual()->tint = d->tint; - auto attributes = CardAttributeAdapter::ensure(*c); + auto attributes = CardAttributeAdapter::ensure(*c); if (!attributes) { attributes.ignore("Card::newCard could not seed canonical attributes"); c->release(); return nullptr; } if (d->health > 0) { - auto target = c->effects()->values.initializeTarget( - CardEffectTarget{d->health, d->health, 0, 0}); + auto target = c->effects()->values.initializeTarget(CardEffectTarget{d->health, d->health, 0, 0}); if (!target) { target.ignore("Card::newCard could not seed the effect target"); c->release(); diff --git a/src/modules/card/Card.h b/src/modules/card/Card.h index ae9bca6a..055db23a 100644 --- a/src/modules/card/Card.h +++ b/src/modules/card/Card.h @@ -7,11 +7,11 @@ * 数据模型与渲染/交互逻辑见 CardTypes.h。 */ +#include "card/CardPlay.h" +#include "card/CardTypes.h" #include "common/ECS.h" #include "common/Module.h" #include "common/Result.h" -#include "card/CardTypes.h" -#include "card/CardPlay.h" #include #include @@ -125,8 +125,7 @@ public: * @param condition Owning condition tree; it is copied into the definition. * @return Success, or NotFound/Rejected when the definition or tree is invalid. */ - [[nodiscard]] eve::Result setCardPlayCondition(const std::string &id, - decision::Condition condition); + [[nodiscard]] eve::Result setCardPlayCondition(const std::string &id, decision::Condition condition); std::string getCardDefinitionName(const std::string &id); std::string getCardDefinitionKind(const std::string &id); int getCardDefinitionCost(const std::string &id); @@ -165,8 +164,8 @@ public: * @param queries Optional read-only resource, authority and policy providers. * @return Stable passed/rejected result with evidence and UI details. */ - [[nodiscard]] decision::ConditionResult evaluatePlay( - const CardData* card, CardPlayConditionQueries queries = {}) const; + [[nodiscard]] decision::ConditionResult evaluatePlay(const CardData *card, + CardPlayConditionQueries queries = {}) const; /** * @brief Read one canonical combat attribute through the Card facade. @@ -174,8 +173,7 @@ public: * @param attribute Selected attribute name such as `attack` or `health`. * @return The canonical final value, or a structured rejection. */ - [[nodiscard]] eve::Result getCardAttribute( - CardData& card, std::string_view attribute) const; + [[nodiscard]] eve::Result getCardAttribute(CardData &card, std::string_view attribute) const; /** * @brief Set one canonical combat attribute through the Card facade. @@ -184,8 +182,7 @@ public: * @param value New canonical base value. * @return Applied, or a structured rejection without a legacy side channel. */ - [[nodiscard]] eve::Result setCardAttribute( - CardData& card, std::string_view attribute, double value) const; + [[nodiscard]] eve::Result setCardAttribute(CardData &card, std::string_view attribute, double value) const; /** * @brief Apply a domain card effect to the card-owned lifecycle component. @@ -194,15 +191,15 @@ public: * @param subject Stable subject identity used by the common effect store. * @return Generation-qualified effect handle owned by the caller. */ - [[nodiscard]] eve::Result applyEffect( - CardData& card, const CardEffectDefinition& definition, eve::SubjectRef subject) const; + [[nodiscard]] eve::Result applyEffect(CardData &card, const CardEffectDefinition &definition, + eve::SubjectRef subject) const; /** * @brief Advance all card-owned effects and project current health. * @param step Injected deterministic simulation tick and duration. * @return Number of settled periodic triggers, or a structured failure. */ - [[nodiscard]] eve::Result step(const eve::SimulationStep& step); + [[nodiscard]] eve::Result step(const eve::SimulationStep &step); /** * @brief Play a card through the facade's condition, container and payment transaction. @@ -212,9 +209,10 @@ public: * @param transactionId Optional non-empty transaction correlation id. * @return Committed transaction receipt, or a failure with no partial play. */ - [[nodiscard]] eve::Result play( - CardData& card, eve::resource::IResourceAccount& playerAccount, - CardPlayComposition composition = {}, std::string transactionId = {}); + [[nodiscard]] eve::Result play(CardData &card, + eve::resource::IResourceAccount &playerAccount, + CardPlayComposition composition = {}, + std::string transactionId = {}); /** @brief Capture all live cards into a contiguous presentation snapshot buffer. */ int capturePresentation(); diff --git a/src/modules/card/CardAttributes.cpp b/src/modules/card/CardAttributes.cpp index db17d8b6..d8e8418b 100644 --- a/src/modules/card/CardAttributes.cpp +++ b/src/modules/card/CardAttributes.cpp @@ -24,13 +24,12 @@ eve::Result failure(eve::Status status, std::string, std::string = {}) { template eve::Result failure(eve::StatusCode code, std::string message, std::string path = {}) { - return eve::Result::failure(eve::Status::failure( - code, eve::Diagnostic::error(eve::DiagnosticCode::Failed, std::move(message), - std::move(path), {}, "card.attributes"))); + return eve::Result::failure( + eve::Status::failure(code, eve::Diagnostic::error(eve::DiagnosticCode::Failed, std::move(message), + std::move(path), {}, "card.attributes"))); } -eve::Result failureVoid(eve::DiagnosticCode code, std::string message, - std::string path = {}) { +eve::Result failureVoid(eve::DiagnosticCode code, std::string message, std::string path = {}) { return eve::Result::failure( eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "card.attributes")); } @@ -39,16 +38,14 @@ eve::Result failureVoid(eve::Status status, std::string, std::string = {}) return eve::Result::failure(std::move(status)); } -eve::Result failureVoid(eve::StatusCode code, std::string message, - std::string path = {}) { - return eve::Result::failure(eve::Status::failure( - code, eve::Diagnostic::error(eve::DiagnosticCode::Failed, std::move(message), - std::move(path), {}, "card.attributes"))); +eve::Result failureVoid(eve::StatusCode code, std::string message, std::string path = {}) { + return eve::Result::failure( + eve::Status::failure(code, eve::Diagnostic::error(eve::DiagnosticCode::Failed, std::move(message), + std::move(path), {}, "card.attributes"))); } bool selected(std::string_view name) noexcept { - return name == CardAttributeAdapter::attackAttribute || - name == CardAttributeAdapter::healthAttribute; + return name == CardAttributeAdapter::attackAttribute || name == CardAttributeAdapter::healthAttribute; } std::array selectedAttributes() { @@ -63,12 +60,12 @@ eve::Result CardAttributeAdapter::ensure(CardData& card) { if (!card.attributes()->values.initialized()) { const std::array bases = { eve::attributes::AttributeSnapshotBase{std::string(attackAttribute), - static_cast(card.stats()->attack)}, + static_cast(card.stats()->attack)}, eve::attributes::AttributeSnapshotBase{std::string(healthAttribute), - static_cast(card.stats()->health)}}; + static_cast(card.stats()->health)}}; auto initialized = card.attributes()->values.initialize(bases); - if (!initialized) return failureVoid(initialized.code(), - "card attribute state could not be initialized", "attributes"); + if (!initialized) + return failureVoid(initialized.code(), "card attribute state could not be initialized", "attributes"); } return eve::Result::success(eve::Status::success(eve::StatusCode::NoOp)); } @@ -86,8 +83,7 @@ eve::Result CardAttributeAdapter::project(CardData& card) { if (!std::isfinite(value) || value < static_cast(std::numeric_limits::min()) || value > static_cast(std::numeric_limits::max())) return failureVoid(eve::DiagnosticCode::InvariantViolation, - "card attribute cannot be represented by the legacy integer projection", - "stats"); + "card attribute cannot be represented by the legacy integer projection", "stats"); } card.stats()->attack = static_cast(std::llround(attack.value())); card.stats()->health = static_cast(std::llround(health.value())); @@ -99,15 +95,13 @@ eve::Result CardAttributeAdapter::read(CardData& card, std::string_view return failure(eve::DiagnosticCode::Unsupported, "card attribute is outside the selective combat projection", "attribute"); auto projected = project(card); - if (!projected) - return failure(projected.code(), "card compatibility projection failed", "stats"); + if (!projected) return failure(projected.code(), "card compatibility projection failed", "stats"); auto value = card.attributes()->values.getFinal(attribute); if (!value) return failure(value.code(), "card attribute read failed", "attribute"); return eve::Result::success(value.value()); } -eve::Result CardAttributeAdapter::setBase(CardData& card, std::string_view attribute, - double value) { +eve::Result CardAttributeAdapter::setBase(CardData& card, std::string_view attribute, double value) { if (!selected(attribute)) return failureVoid(eve::DiagnosticCode::Unsupported, "card attribute is outside the selective combat projection", "attribute"); @@ -120,40 +114,35 @@ eve::Result CardAttributeAdapter::setBase(CardData& card, std::string_view eve::Result CardAttributeAdapter::addModifier( CardData& card, std::string id, std::string_view attribute, std::string source, - eve::attributes::AttributeOperation operation, double value, - eve::attributes::ModifierPriority priority) { + eve::attributes::AttributeOperation operation, double value, eve::attributes::ModifierPriority priority) { if (!selected(attribute) || source.empty()) - return failure( - eve::DiagnosticCode::InvalidArgument, - "card modifier requires a selected attribute and non-empty source", "modifier"); + return failure(eve::DiagnosticCode::InvalidArgument, + "card modifier requires a selected attribute and non-empty source", + "modifier"); auto ready = ensure(card); if (!ready) - return failure(ready.code(), "card attributes are not available", - "attributes"); - auto added = card.attributes()->values.addModifier( - eve::attributes::AttributeModifier(std::move(id), std::string(attribute), std::move(source), - operation, value, priority)); + return failure(ready.code(), "card attributes are not available", "attributes"); + auto added = card.attributes()->values.addModifier(eve::attributes::AttributeModifier( + std::move(id), std::string(attribute), std::move(source), operation, value, priority)); if (!added) return added; auto projected = project(card); if (!projected) - return failure(projected.code(), - "card compatibility projection failed", "stats"); + return failure(projected.code(), "card compatibility projection failed", "stats"); return added; } -eve::Result CardAttributeAdapter::snapshot( - CardData& card) { +eve::Result CardAttributeAdapter::snapshot(CardData& card) { auto ready = ensure(card); if (!ready) - return failure( - ready.code(), "card attributes are not available", "attributes"); + return failure(ready.code(), "card attributes are not available", + "attributes"); const auto names = selectedAttributes(); return card.attributes()->values.snapshot(names); } -eve::Result CardAttributeAdapter::restore( - CardData& card, const eve::attributes::AttributeProjectionSnapshot& snapshotValue, - eve::Revision expectedRevision) { +eve::Result CardAttributeAdapter::restore(CardData& card, + const eve::attributes::AttributeProjectionSnapshot& snapshotValue, + eve::Revision expectedRevision) { auto ready = ensure(card); if (!ready) return failureVoid(ready.code(), "card attributes are not available", "attributes"); auto restored = card.attributes()->values.restore(snapshotValue, expectedRevision); diff --git a/src/modules/card/CardAttributes.h b/src/modules/card/CardAttributes.h index 3bb053f3..71ff8ed9 100644 --- a/src/modules/card/CardAttributes.h +++ b/src/modules/card/CardAttributes.h @@ -30,30 +30,27 @@ public: [[nodiscard]] static eve::Result ensure(CardData& card); /** @brief Read a selected final card attribute and refresh compatibility fields. */ - [[nodiscard]] static eve::Result read(CardData& card, - std::string_view attribute); + [[nodiscard]] static eve::Result read(CardData& card, std::string_view attribute); /** @brief Set a selected canonical base and refresh the legacy projection. */ - [[nodiscard]] static eve::Result setBase(CardData& card, - std::string_view attribute, double value); + [[nodiscard]] static eve::Result setBase(CardData& card, std::string_view attribute, double value); /** @brief Add a modifier using the shared attribute operation ordering. */ [[nodiscard]] static eve::Result addModifier( - CardData& card, std::string id, std::string_view attribute, - std::string source, eve::attributes::AttributeOperation operation, double value, + CardData& card, std::string id, std::string_view attribute, std::string source, + eve::attributes::AttributeOperation operation, double value, eve::attributes::ModifierPriority priority = eve::attributes::priority::runtime); /** @brief Project canonical values into the legacy integer Stats fields. */ [[nodiscard]] static eve::Result project(CardData& card); /** @brief Capture attack/health with the owning ECS generation and revision. */ - [[nodiscard]] static eve::Result snapshot( - CardData& card); + [[nodiscard]] static eve::Result snapshot(CardData& card); /** @brief Restore a card snapshot only when owner and revision still match. */ - [[nodiscard]] static eve::Result restore( - CardData& card, const eve::attributes::AttributeProjectionSnapshot& snapshot, - eve::Revision expectedRevision); + [[nodiscard]] static eve::Result restore(CardData& card, + const eve::attributes::AttributeProjectionSnapshot& snapshot, + eve::Revision expectedRevision); }; } // namespace eve::card diff --git a/src/modules/card/CardContainers.cpp b/src/modules/card/CardContainers.cpp index f0db68a6..8ef18892 100644 --- a/src/modules/card/CardContainers.cpp +++ b/src/modules/card/CardContainers.cpp @@ -12,8 +12,7 @@ namespace { return eve::Result::failure(eve::Diagnostic::error(code, std::move(message))); } -[[nodiscard]] bool hasId(const std::vector& cards, - const eve::container::MembershipId& id) { +[[nodiscard]] bool hasId(const std::vector& cards, const eve::container::MembershipId& id) { return std::any_of(cards.begin(), cards.end(), [&](const CardData* card) { return card != nullptr && const_cast(card)->identity()->id == id.value(); }); @@ -21,16 +20,13 @@ namespace { [[nodiscard]] bool sameLayout(const eve::container::ContainerSnapshot& lhs, const eve::container::ContainerSnapshot& rhs) { - if (lhs.id != rhs.id || lhs.revision != rhs.revision || lhs.entries.size() != rhs.entries.size()) - return false; + if (lhs.id != rhs.id || lhs.revision != rhs.revision || lhs.entries.size() != rhs.entries.size()) return false; for (std::size_t index = 0; index < lhs.entries.size(); ++index) { - const auto& left = lhs.entries[index]; + const auto& left = lhs.entries[index]; const auto& right = rhs.entries[index]; - if (left.membership.object != right.membership.object || - left.membership.slot != right.membership.slot || - left.membership.generation != right.membership.generation || - left.object.id != right.object.id || left.object.type != right.object.type || - left.object.quantity != right.object.quantity) + if (left.membership.object != right.membership.object || left.membership.slot != right.membership.slot || + left.membership.generation != right.membership.generation || left.object.id != right.object.id || + left.object.type != right.object.type || left.object.quantity != right.object.quantity) return false; } return true; @@ -38,11 +34,9 @@ namespace { } // namespace -class CardContainerAdapter::PreparedState final - : public eve::container::IContainer::PreparedState { +class CardContainerAdapter::PreparedState final : public eve::container::IContainer::PreparedState { public: - PreparedState(CardContainerAdapter& owner, std::vector staged, - eve::Revision revision) + PreparedState(CardContainerAdapter& owner, std::vector staged, eve::Revision revision) : owner_(owner), staged_(std::move(staged)), revision_(revision) {} void commit() noexcept override { @@ -60,48 +54,46 @@ public: } private: - CardContainerAdapter& owner_; + CardContainerAdapter& owner_; std::vector staged_; - eve::Revision revision_; - bool committed_ = false; + eve::Revision revision_; + bool committed_ = false; }; -CardContainerAdapter::CardContainerAdapter(eve::container::ContainerId id, CardContainerKind kind, - Deck* deck, Hand* hand, - std::vector* discard, - eve::container::Capacity capacity, - std::vector acceptedKinds) +CardContainerAdapter::CardContainerAdapter(eve::container::ContainerId id, CardContainerKind kind, Deck* deck, + Hand* hand, std::vector* discard, + eve::container::Capacity capacity, std::vector acceptedKinds) : descriptor_{std::move(id), capacity, - kind == CardContainerKind::Deck ? eve::container::Ordering::Stack - : eve::container::Ordering::Insertion, - eve::container::Filter{}}, - kind_(kind), deck_(deck), hand_(hand), discard_(discard) { + kind == CardContainerKind::Deck ? eve::container::Ordering::Stack + : eve::container::Ordering::Insertion, + eve::container::Filter{}}, + kind_(kind), + deck_(deck), + hand_(hand), + discard_(discard) { if (!acceptedKinds.empty()) { descriptor_.filter = eve::container::Filter( - [acceptedKinds = std::move(acceptedKinds)]( - const eve::container::ContainerObject& object) { - if (std::find(acceptedKinds.begin(), acceptedKinds.end(), object.type) == - acceptedKinds.end()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, - "card kind is not accepted by container")); + [acceptedKinds = std::move(acceptedKinds)](const eve::container::ContainerObject& object) { + if (std::find(acceptedKinds.begin(), acceptedKinds.end(), object.type) == acceptedKinds.end()) + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, + "card kind is not accepted by container")); return eve::Result::success(); }); } if (kind_ == CardContainerKind::Deck && deck_ == nullptr) descriptor_.filter = eve::container::Filter([](const eve::container::ContainerObject&) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "card deck adapter has no Deck")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "card deck adapter has no Deck")); }); if (kind_ == CardContainerKind::Hand && hand_ == nullptr) descriptor_.filter = eve::container::Filter([](const eve::container::ContainerObject&) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "card hand adapter has no Hand")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "card hand adapter has no Hand")); }); if (kind_ == CardContainerKind::Discard && discard_ == nullptr) descriptor_.filter = eve::container::Filter([](const eve::container::ContainerObject&) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "card discard adapter has no storage")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "card discard adapter has no storage")); }); } @@ -112,8 +104,7 @@ std::vector* CardContainerAdapter::cards() noexcept { } const std::vector* CardContainerAdapter::cards() const noexcept { - if (kind_ == CardContainerKind::Deck) - return deck_ ? &deck_->membership()->cards : nullptr; + if (kind_ == CardContainerKind::Deck) return deck_ ? &deck_->membership()->cards : nullptr; if (kind_ == CardContainerKind::Hand) return hand_ ? &hand_->membership()->cards : nullptr; return discard_; } @@ -125,12 +116,12 @@ eve::container::MembershipId CardContainerAdapter::objectId(const CardData& card eve::container::ContainerObject CardContainerAdapter::describe(CardData* card) { eve::container::ContainerObject object; if (card == nullptr) return object; - object.id = objectId(*card); - object.type = card->identity()->kind; + object.id = objectId(*card); + object.type = card->identity()->kind; object.quantity = 1; - auto payload = std::make_shared(); - payload->card = ecs::handle_of(card); - object.payload = std::move(payload); + auto payload = std::make_shared(); + payload->card = ecs::handle_of(card); + object.payload = std::move(payload); return object; } @@ -148,40 +139,33 @@ eve::Result CardContainerAdapter::snapshot() const auto* values = cards(); if (!descriptor_.id.isValid() || values == nullptr) return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, - "card container adapter is not bound")); + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "card container adapter is not bound")); if (!descriptor_.capacity.isUnlimited() && values->size() > descriptor_.capacity.value()) - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, - "card container exceeds configured capacity")); - eve::container::ContainerSnapshot snapshot{descriptor_.id, revision_, {}}; + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvariantViolation, "card container exceeds configured capacity")); + eve::container::ContainerSnapshot snapshot{descriptor_.id, revision_, {}}; std::unordered_set ids; snapshot.entries.reserve(values->size()); for (std::size_t i = 0; i < values->size(); ++i) { if (i > static_cast(std::numeric_limits::max())) - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, - "card container has too many indexed memberships")); + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvariantViolation, "card container has too many indexed memberships")); CardData* card = (*values)[i]; if (card == nullptr || card->identity()->id.empty()) - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, - "card container contains null or unidentified card")); + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvariantViolation, "card container contains null or unidentified card")); const auto id = objectId(*card); if (!ids.insert(id).second) - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "card container contains duplicate card identity")); - snapshot.entries.push_back({{id, eve::container::SlotIndex(static_cast(i)), - eve::Generation(1)}, - describe(card)}); + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::Conflict, "card container contains duplicate card identity")); + snapshot.entries.push_back( + {{id, eve::container::SlotIndex(static_cast(i)), eve::Generation(1)}, describe(card)}); } return eve::Result::success(std::move(snapshot)); } eve::Result CardContainerAdapter::validateInsert( - const eve::container::ContainerObject& object, - std::optional destination, + const eve::container::ContainerObject& object, std::optional destination, std::optional ignoredObject) const { const auto* values = cards(); if (values == nullptr || !descriptor_.id.isValid()) @@ -189,21 +173,17 @@ eve::Result CardContainerAdapter::validateInsert( if (!object.id.isValid() || !object.payload) return error(eve::DiagnosticCode::InvalidArgument, "card transfer object is incomplete"); if (object.quantity != 1u) - return error(eve::DiagnosticCode::InvalidArgument, - "a card container membership must have quantity one"); + return error(eve::DiagnosticCode::InvalidArgument, "a card container membership must have quantity one"); const auto* payload = dynamic_cast(object.payload.get()); - auto* card = payload == nullptr ? nullptr - : static_cast(ecs::try_get(payload->card)); + auto* card = payload == nullptr ? nullptr : static_cast(ecs::try_get(payload->card)); if (payload == nullptr || card == nullptr || card->identity()->id != object.id.value() || card->identity()->kind != object.type) return error(eve::DiagnosticCode::StaleHandle, "card transfer payload is stale"); if (hasId(*values, object.id) && (!ignoredObject || *ignoredObject != object.id)) return error(eve::DiagnosticCode::Conflict, "destination already contains card"); - const std::size_t effectiveSize = values->size() - - ((ignoredObject && hasId(*values, *ignoredObject)) ? 1u : 0u); + const std::size_t effectiveSize = values->size() - ((ignoredObject && hasId(*values, *ignoredObject)) ? 1u : 0u); if (destination) { - if (!destination->isValid() || - static_cast(destination->value()) > effectiveSize) + if (!destination->isValid() || static_cast(destination->value()) > effectiveSize) return error(eve::DiagnosticCode::InvalidArgument, "destination card slot is out of range"); } auto accepted = descriptor_.filter.evaluate(object); @@ -213,43 +193,34 @@ eve::Result CardContainerAdapter::validateInsert( return eve::Result::success(); } -eve::Result> -CardContainerAdapter::prepare(const eve::container::ContainerSnapshot& expected, - const eve::container::ContainerSnapshot& candidate) { +eve::Result> CardContainerAdapter::prepare( + const eve::container::ContainerSnapshot& expected, const eve::container::ContainerSnapshot& candidate) { const auto* values = cards(); if (values == nullptr || !descriptor_.id.isValid()) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, - "card container adapter is not bound")); + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "card container adapter is not bound")); if (expected.id != descriptor_.id || candidate.id != descriptor_.id) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, - "card prepare targets another container")); + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "card prepare targets another container")); auto currentResult = snapshot(); if (!currentResult) - return eve::Result>::failure( - currentResult.status()); + return eve::Result>::failure(currentResult.status()); if (!sameLayout(currentResult.value(), expected)) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, - "card prepare expected snapshot is stale")); + eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, "card prepare expected snapshot is stale")); const auto nextRevision = expected.revision.incremented(); if (!nextRevision || candidate.revision != *nextRevision) + return eve::Result>::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvariantViolation, "card candidate revision is not the next revision")); + if (!descriptor_.capacity.isUnlimited() && candidate.entries.size() > descriptor_.capacity.value()) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, - "card candidate revision is not the next revision")); - if (!descriptor_.capacity.isUnlimited() && - candidate.entries.size() > descriptor_.capacity.value()) - return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "card candidate exceeds container capacity")); + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "card candidate exceeds container capacity")); std::vector ordered; ordered.reserve(candidate.entries.size()); for (const auto& entry : candidate.entries) ordered.push_back(&entry); - std::sort(ordered.begin(), ordered.end(), [](const auto* lhs, const auto* rhs) { - return lhs->membership.slot < rhs->membership.slot; - }); + std::sort(ordered.begin(), ordered.end(), + [](const auto* lhs, const auto* rhs) { return lhs->membership.slot < rhs->membership.slot; }); std::vector staged; staged.reserve(ordered.size()); std::unordered_set ids; @@ -263,20 +234,16 @@ CardContainerAdapter::prepare(const eve::container::ContainerSnapshot& expected, eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "card candidate contains invalid membership")); const auto* payload = dynamic_cast(entry->object.payload.get()); - auto* card = payload == nullptr ? nullptr - : static_cast(ecs::try_get(payload->card)); - if (payload == nullptr || card == nullptr || - card->identity()->id != entry->membership.object.value() || + auto* card = payload == nullptr ? nullptr : static_cast(ecs::try_get(payload->card)); + if (payload == nullptr || card == nullptr || card->identity()->id != entry->membership.object.value() || entry->object.type != card->identity()->kind) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, - "card candidate payload is stale")); + eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, "card candidate payload is stale")); staged.push_back(card); } std::unique_ptr prepared = std::make_unique(*this, std::move(staged), *nextRevision); - return eve::Result>::success( - std::move(prepared)); + return eve::Result>::success(std::move(prepared)); } } // namespace eve::card diff --git a/src/modules/card/CardContainers.h b/src/modules/card/CardContainers.h index 4e00f383..a5c73dc3 100644 --- a/src/modules/card/CardContainers.h +++ b/src/modules/card/CardContainers.h @@ -56,24 +56,21 @@ public: * @param capacity Optional maximum number of cards. * @param acceptedKinds Optional card-kind allowlist. */ - CardContainerAdapter(eve::container::ContainerId id, CardContainerKind kind, - Deck* deck = nullptr, Hand* hand = nullptr, - std::vector* discard = nullptr, - eve::container::Capacity capacity = eve::container::Capacity::unlimited(), + CardContainerAdapter(eve::container::ContainerId id, CardContainerKind kind, Deck* deck = nullptr, + Hand* hand = nullptr, std::vector* discard = nullptr, + eve::container::Capacity capacity = eve::container::Capacity::unlimited(), std::vector acceptedKinds = {}); [[nodiscard]] const eve::container::ContainerDescriptor& descriptor() const noexcept override { return descriptor_; } [[nodiscard]] eve::Result snapshot() const override; - [[nodiscard]] eve::Result validateInsert( - const eve::container::ContainerObject& object, - std::optional destination, - std::optional ignoredObject = std::nullopt) const override; + [[nodiscard]] eve::Result validateInsert( + const eve::container::ContainerObject& object, std::optional destination, + std::optional ignoredObject = std::nullopt) const override; /** @copydoc eve::container::IContainer::prepare */ [[nodiscard]] eve::Result> prepare( - const eve::container::ContainerSnapshot& expected, - const eve::container::ContainerSnapshot& candidate) override; + const eve::container::ContainerSnapshot& expected, const eve::container::ContainerSnapshot& candidate) override; /** @brief Return the current adapter revision for optimistic requests. */ [[nodiscard]] eve::Revision revision() const noexcept { return revision_; } @@ -93,17 +90,17 @@ private: */ [[nodiscard]] std::vector* cards() noexcept; /** @copydoc CardContainerAdapter::cards() */ - [[nodiscard]] const std::vector* cards() const noexcept; - [[nodiscard]] static eve::container::MembershipId objectId(const CardData& card); + [[nodiscard]] const std::vector* cards() const noexcept; + [[nodiscard]] static eve::container::MembershipId objectId(const CardData& card); [[nodiscard]] static eve::container::ContainerObject describe(CardData* card); - void updateState(CardData* card) const noexcept; + void updateState(CardData* card) const noexcept; eve::container::ContainerDescriptor descriptor_; - CardContainerKind kind_; - Deck* deck_ = nullptr; - Hand* hand_ = nullptr; - std::vector* discard_ = nullptr; - eve::Revision revision_ = eve::Revision(0); + CardContainerKind kind_; + Deck* deck_ = nullptr; + Hand* hand_ = nullptr; + std::vector* discard_ = nullptr; + eve::Revision revision_ = eve::Revision(0); }; } // namespace eve::card diff --git a/src/modules/card/CardDefinitionRuntime.cpp b/src/modules/card/CardDefinitionRuntime.cpp index a02e71c0..510e5919 100644 --- a/src/modules/card/CardDefinitionRuntime.cpp +++ b/src/modules/card/CardDefinitionRuntime.cpp @@ -14,13 +14,12 @@ namespace { template eve::Result invalid(std::string message, std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, std::move(message), std::move(path), {}, - "card.definition_runtime")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, std::move(message), + std::move(path), {}, "card.definition_runtime")); } -eve::Result currentHandle( - eve::definitions::DefinitionRegistry& registry, const eve::DefinitionRef& reference) { +eve::Result currentHandle(eve::definitions::DefinitionRegistry& registry, + const eve::DefinitionRef& reference) { const auto& logical = reference.id(); return registry.handle(std::string(logical.namespaceName()), std::string(logical.name())); } @@ -33,8 +32,7 @@ const eve::Value* field(const eve::Value::Object& object, const char* name) { bool readInt(const eve::Value::Object& object, const char* name, int& output) { const auto* value = field(object, name); if (const auto* integer = value ? value->getIf() : nullptr) { - if (*integer < std::numeric_limits::min() || *integer > std::numeric_limits::max()) - return false; + if (*integer < std::numeric_limits::min() || *integer > std::numeric_limits::max()) return false; output = static_cast(*integer); return true; } @@ -42,10 +40,10 @@ bool readInt(const eve::Value::Object& object, const char* name, int& output) { } eve::Result parseState(const eve::definitions::Definition& definition, - const eve::DefinitionRef& reference) { + const eve::DefinitionRef& reference) { auto parsed = eve::Value::fromJson(definition.json); if (!parsed) return eve::Result::failure(parsed.status()); - auto value = std::move(parsed).takeValue(); + auto value = std::move(parsed).takeValue(); const auto* object = value.getIf(); if (!object) return invalid("card definition payload must be an object", "json"); @@ -74,8 +72,8 @@ eve::Result parseState(const eve::definitions::Definition& def return invalid("card tint must contain three numeric channels", "tint"); float* channels[] = {&state.tint.r, &state.tint.g, &state.tint.b}; for (std::size_t i = 0; i < 3; ++i) { - const auto* number = (*array)[i].getIf(); - const auto* integer = (*array)[i].getIf(); + const auto* number = (*array)[i].getIf(); + const auto* integer = (*array)[i].getIf(); const double channel = number ? *number : integer ? static_cast(*integer) : -1.0; if (!std::isfinite(channel) || channel < 0.0 || channel > 1.0) return invalid("card tint channel must be finite in [0,1]", "tint"); @@ -87,8 +85,7 @@ eve::Result parseState(const eve::definitions::Definition& def if (!array) return invalid("card tags must be an array", "tags"); for (const auto& item : *array) { const auto* tag = item.getIf(); - if (!tag || tag->empty()) - return invalid("card tag must be a non-empty string", "tags"); + if (!tag || tag->empty()) return invalid("card tag must be a non-empty string", "tags"); state.tags.push_back(*tag); } std::sort(state.tags.begin(), state.tags.end()); @@ -97,8 +94,8 @@ eve::Result parseState(const eve::definitions::Definition& def return eve::Result::success(std::move(state)); } -eve::Result resolveState(eve::definitions::DefinitionRegistry& registry, - const eve::DefinitionRef& reference, +eve::Result resolveState(eve::definitions::DefinitionRegistry& registry, + const eve::DefinitionRef& reference, const eve::definition::DefinitionHandle& handle) { auto definition = registry.resolveHandle(handle); if (!definition) return eve::Result::failure(definition.status()); @@ -115,8 +112,7 @@ eve::LogicalId cardSchema() { } eve::Value encodeState(const CardRuntimeState& state) { - eve::Value::Array tint{eve::Value(static_cast(state.tint.r)), - eve::Value(static_cast(state.tint.g)), + eve::Value::Array tint{eve::Value(static_cast(state.tint.r)), eve::Value(static_cast(state.tint.g)), eve::Value(static_cast(state.tint.b))}; eve::Value::Array tags; for (const auto& tag : state.tags) tags.emplace_back(tag); @@ -124,8 +120,10 @@ eve::Value encodeState(const CardRuntimeState& state) { {"attack", eve::Value(static_cast(state.attack))}, {"cost", eve::Value(static_cast(state.cost))}, {"currentHealth", eve::Value(static_cast(state.currentHealth))}, - {"kind", eve::Value(state.kind)}, {"maxHealth", eve::Value(static_cast(state.maxHealth))}, - {"name", eve::Value(state.name)}, {"tags", eve::Value(std::move(tags))}, + {"kind", eve::Value(state.kind)}, + {"maxHealth", eve::Value(static_cast(state.maxHealth))}, + {"name", eve::Value(state.name)}, + {"tags", eve::Value(std::move(tags))}, {"tint", eve::Value(std::move(tint))}, }); } @@ -133,17 +131,22 @@ eve::Value encodeState(const CardRuntimeState& state) { eve::Result decodeState(const eve::Value& value) { const auto* object = value.getIf(); if (object == nullptr) return invalid("card runtime state must be an object", "state"); - static const std::set fields = {"attack", "cost", "currentHealth", "kind", "maxHealth", "name", "tags", "tint"}; + static const std::set fields = {"attack", "cost", "currentHealth", "kind", + "maxHealth", "name", "tags", "tint"}; for (const auto& [name, unused] : *object) { (void)unused; - if (!fields.contains(name)) return invalid("unknown card runtime state field", "state." + name); + if (!fields.contains(name)) + return invalid("unknown card runtime state field", "state." + name); } for (const auto& name : fields) - if (!object->contains(name)) return invalid("card runtime state is missing a field", "state." + name); + if (!object->contains(name)) + return invalid("card runtime state is missing a field", "state." + name); CardRuntimeState result; - const auto readIntField = [&](const char* name, int& output) { + const auto readIntField = [&](const char* name, int& output) { const auto* integer = object->at(name).getIf(); - if (integer == nullptr || *integer < std::numeric_limits::min() || *integer > std::numeric_limits::max()) return false; + if (integer == nullptr || *integer < std::numeric_limits::min() || + *integer > std::numeric_limits::max()) + return false; output = static_cast(*integer); return true; }; @@ -157,17 +160,20 @@ eve::Result decodeState(const eve::Value& value) { return invalid("card runtime state field is invalid", "state"); result.name = *name; result.kind = *kind; - if (result.cost < 0 || result.attack < 0 || result.maxHealth < 0 || result.currentHealth < 0 || result.currentHealth > result.maxHealth) + if (result.cost < 0 || result.attack < 0 || result.maxHealth < 0 || result.currentHealth < 0 || + result.currentHealth > result.maxHealth) return invalid("card runtime health/stat values are invalid", "state"); float* channels[] = {&result.tint.r, &result.tint.g, &result.tint.b}; for (std::size_t index = 0; index < 3; ++index) { const auto* number = (*tint)[index].getIf(); - if (number == nullptr || !std::isfinite(*number) || *number < 0.0 || *number > 1.0) return invalid("card tint channel is invalid", "state.tint"); + if (number == nullptr || !std::isfinite(*number) || *number < 0.0 || *number > 1.0) + return invalid("card tint channel is invalid", "state.tint"); *channels[index] = static_cast(*number); } for (const auto& tag : *tags) { const auto* text = tag.getIf(); - if (text == nullptr || text->empty()) return invalid("card runtime tag is invalid", "state.tags"); + if (text == nullptr || text->empty()) + return invalid("card runtime tag is invalid", "state.tags"); result.tags.push_back(*text); } return eve::Result::success(std::move(result)); @@ -175,9 +181,10 @@ eve::Result decodeState(const eve::Value& value) { } // namespace -eve::Result CardDefinitionRuntime::create( - eve::definitions::DefinitionRegistry& registry, eve::DefinitionRef definition, - eve::PersistentId instanceId, eve::definition::ReloadPolicy policy) { +eve::Result CardDefinitionRuntime::create(eve::definitions::DefinitionRegistry& registry, + eve::DefinitionRef definition, + eve::PersistentId instanceId, + eve::definition::ReloadPolicy policy) { if (!definition.id().isValid()) return invalid("card definition reference is invalid"); auto handle = currentHandle(registry, definition); if (!handle) return eve::Result::failure(handle.status()); @@ -208,25 +215,25 @@ bool CardDefinitionRuntime::isActive() const noexcept { return runtime_.isActive eve::Result CardDefinitionRuntime::applyTo(CardData* card) const { if (card == nullptr) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "card runtime cannot project to a null CardData", "card", - {}, "card.definition_runtime")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "card runtime cannot project to a null CardData", + "card", {}, "card.definition_runtime")); - CardData::Identity identity = *card->identity().operator->(); - CardData::Stats stats = *card->stats().operator->(); - CardData::Visual visual = *card->visual().operator->(); - CardData::DefinitionBinding binding = *card->definitionBinding().operator->(); + CardData::Identity identity = *card->identity().operator->(); + CardData::Stats stats = *card->stats().operator->(); + CardData::Visual visual = *card->visual().operator->(); + CardData::DefinitionBinding binding = *card->definitionBinding().operator->(); identity.definitionId = this->identity().definition.id().name(); - identity.name = state().name; - identity.kind = state().kind; - stats.cost = state().cost; - stats.attack = state().attack; - stats.health = state().currentHealth; - visual.tint = state().tint; - binding.identity = this->identity(); - binding.reloadPolicy = policy_; - binding.active = isActive(); + identity.name = state().name; + identity.kind = state().kind; + stats.cost = state().cost; + stats.attack = state().attack; + stats.health = state().currentHealth; + visual.tint = state().tint; + binding.identity = this->identity(); + binding.reloadPolicy = policy_; + binding.active = isActive(); using std::swap; swap(*card->identity().operator->(), identity); @@ -236,8 +243,7 @@ eve::Result CardDefinitionRuntime::applyTo(CardData* card) const { return eve::Result::success(); } -eve::Result CardDefinitionRuntime::reload( - eve::definition::ReloadPolicy policy) { +eve::Result CardDefinitionRuntime::reload(eve::definition::ReloadPolicy policy) { if (registry_ == nullptr) return invalid("card definition registry is not bound", "registry"); auto next = currentHandle(*registry_, identity().definition); @@ -245,12 +251,10 @@ eve::Result CardDefinitionRuntime::reload( auto defaults = resolveState(*registry_, identity().definition, next.value()); if (!defaults) return eve::Result::failure(defaults.status()); CardRuntimeState defaultState = std::move(defaults).takeValue(); - auto rebuild = [defaultState](const CardRuntimeState& oldState, - const eve::definition::InstanceIdentity&, - const eve::definition::DefinitionHandle&) mutable - -> eve::Result { + auto rebuild = [defaultState](const CardRuntimeState& oldState, const eve::definition::InstanceIdentity&, + const eve::definition::DefinitionHandle&) mutable -> eve::Result { CardRuntimeState rebuilt = defaultState; - rebuilt.currentHealth = std::clamp(oldState.currentHealth, 0, rebuilt.maxHealth); + rebuilt.currentHealth = std::clamp(oldState.currentHealth, 0, rebuilt.maxHealth); return eve::Result::success(std::move(rebuilt)); }; auto result = runtime_.reload(next.value(), policy, defaultState, std::move(rebuild)); @@ -259,26 +263,24 @@ eve::Result CardDefinitionRuntime::reload( } eve::Result CardDefinitionRuntime::snapshot( - eve::Revision revision, eve::SimulationTick tick, - const eve::SnapshotHashProvider& hashProvider) const { - const eve::definition::RuntimeStateEncoder encoder = - [](const CardRuntimeState& value) { return eve::Result::success(encodeState(value)); }; - return eve::definition::snapshotRuntimeInstance( - runtime_, "card.runtime", cardSchema(), eve::SchemaVersion(1), revision, tick, - hashProvider, encoder); + eve::Revision revision, eve::SimulationTick tick, const eve::SnapshotHashProvider& hashProvider) const { + const eve::definition::RuntimeStateEncoder encoder = [](const CardRuntimeState& value) { + return eve::Result::success(encodeState(value)); + }; + return eve::definition::snapshotRuntimeInstance(runtime_, "card.runtime", cardSchema(), eve::SchemaVersion(1), + revision, tick, hashProvider, encoder); } -eve::Result CardDefinitionRuntime::snapshotJson( - eve::Revision revision, eve::SimulationTick tick, - const eve::SnapshotHashProvider& hashProvider) const { +eve::Result CardDefinitionRuntime::snapshotJson(eve::Revision revision, eve::SimulationTick tick, + const eve::SnapshotHashProvider& hashProvider) const { auto result = snapshot(revision, tick, hashProvider); if (!result) return eve::Result::failure(result.status()); return std::move(result).andThen( [](eve::SnapshotEnvelope&& value) { return eve::serializeSnapshotEnvelope(value); }); } -eve::Result CardDefinitionRuntime::restore( - const eve::SnapshotEnvelope& snapshotValue, const eve::SnapshotHashProvider& hashProvider) { +eve::Result CardDefinitionRuntime::restore(const eve::SnapshotEnvelope& snapshotValue, + const eve::SnapshotHashProvider& hashProvider) { if (registry_ == nullptr) return invalid("card definition registry is not bound", "registry"); auto current = currentHandle(*registry_, identity().definition); if (!current) return eve::Result::failure(current.status()); @@ -287,9 +289,8 @@ eve::Result CardDefinitionRuntime::restore( eve::DiagnosticCode::StaleHandle, "card snapshot cannot restore against a replaced definition", "definitionGeneration", {}, "card.definition_runtime")); const eve::definition::RuntimeStateDecoder decoder = decodeState; - return eve::definition::restoreRuntimeInstance( - runtime_, snapshotValue, "card.runtime", cardSchema(), eve::SchemaVersion(1), - hashProvider, decoder); + return eve::definition::restoreRuntimeInstance(runtime_, snapshotValue, "card.runtime", cardSchema(), + eve::SchemaVersion(1), hashProvider, decoder); } } // namespace eve::card diff --git a/src/modules/card/CardDefinitionRuntime.h b/src/modules/card/CardDefinitionRuntime.h index a476e295..ff493b5f 100644 --- a/src/modules/card/CardDefinitionRuntime.h +++ b/src/modules/card/CardDefinitionRuntime.h @@ -24,13 +24,13 @@ namespace eve::card { * definition projection used to initialize or rebuild a CardData entity. */ struct CardRuntimeState { - std::string name; - std::string kind = "creature"; - int cost = 0; - int attack = 0; - int maxHealth = 0; - int currentHealth = 0; - glm::vec3 tint{0.62f, 0.50f, 0.40f}; + std::string name; + std::string kind = "creature"; + int cost = 0; + int attack = 0; + int maxHealth = 0; + int currentHealth = 0; + glm::vec3 tint{0.62f, 0.50f, 0.40f}; std::vector tags; }; @@ -53,8 +53,7 @@ public: * @return A typed runtime or a structured resolution/parse failure. */ [[nodiscard]] static eve::Result create( - eve::definitions::DefinitionRegistry& registry, eve::DefinitionRef definition, - eve::PersistentId instanceId, + eve::definitions::DefinitionRegistry& registry, eve::DefinitionRef definition, eve::PersistentId instanceId, eve::definition::ReloadPolicy policy = eve::definition::ReloadPolicy::KeepInstanceValues); /** @brief Move a card runtime while retaining its borrowed registry binding. */ @@ -96,30 +95,27 @@ public: * @return A typed reload outcome, or a stale/parse/policy failure. On * failure the adapter state and generation remain unchanged. */ - [[nodiscard]] eve::Result reload( - eve::definition::ReloadPolicy policy); + [[nodiscard]] eve::Result reload(eve::definition::ReloadPolicy policy); /** @brief Capture card instance state in the common SnapshotEnvelope. */ - [[nodiscard]] eve::Result snapshot( - eve::Revision revision, eve::SimulationTick tick, - const eve::SnapshotHashProvider& hashProvider) const; + [[nodiscard]] eve::Result snapshot(eve::Revision revision, eve::SimulationTick tick, + const eve::SnapshotHashProvider& hashProvider) const; /** @brief Serialize snapshot() as canonical JSON. */ - [[nodiscard]] eve::Result snapshotJson( - eve::Revision revision, eve::SimulationTick tick, - const eve::SnapshotHashProvider& hashProvider) const; + [[nodiscard]] eve::Result snapshotJson(eve::Revision revision, eve::SimulationTick tick, + const eve::SnapshotHashProvider& hashProvider) const; /** @brief Verify and atomically restore a same-generation card snapshot. */ - [[nodiscard]] eve::Result restore( - const eve::SnapshotEnvelope& snapshot, const eve::SnapshotHashProvider& hashProvider); + [[nodiscard]] eve::Result restore(const eve::SnapshotEnvelope& snapshot, + const eve::SnapshotHashProvider& hashProvider); private: - CardDefinitionRuntime(eve::definitions::DefinitionRegistry& registry, + CardDefinitionRuntime(eve::definitions::DefinitionRegistry& registry, eve::definition::RuntimeInstance runtime, - eve::definition::ReloadPolicy policy) + eve::definition::ReloadPolicy policy) : registry_(®istry), runtime_(std::move(runtime)), policy_(policy) {} - eve::definitions::DefinitionRegistry* registry_ = nullptr; // borrowed + eve::definitions::DefinitionRegistry* registry_ = nullptr; // borrowed eve::definition::RuntimeInstance runtime_; - eve::definition::ReloadPolicy policy_; + eve::definition::ReloadPolicy policy_; }; } // namespace eve::card diff --git a/src/modules/card/CardEffects.cpp b/src/modules/card/CardEffects.cpp index d0b8bccd..1dbe4fc6 100644 --- a/src/modules/card/CardEffects.cpp +++ b/src/modules/card/CardEffects.cpp @@ -9,32 +9,30 @@ namespace eve::card { namespace { eve::Result invalid(const char* message, const char* path = "effect") { - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, message, path)); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, message, path)); } bool hasKind(const effects::EffectInstance& effect, CardEffectKind kind) { const char* name = kind == CardEffectKind::Damage ? "card:damage" - : kind == CardEffectKind::Heal ? "card:heal" - : "card:shield"; + : kind == CardEffectKind::Heal ? "card:heal" + : "card:shield"; return effect.hasTag(name); } } // namespace eve::Result CardEffectAdapter::initializeTarget(CardEffectTarget target) { - if (target.maxHealth <= 0 || target.health < 0 || target.health > target.maxHealth || - target.barrier < 0) + if (target.maxHealth <= 0 || target.health < 0 || target.health > target.maxHealth || target.barrier < 0) return invalid("card effect target state is invalid", "target"); if (container_.effectCount() != 0) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, - "card effect target cannot be reinitialized while effects are active", "target")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, + "card effect target cannot be reinitialized while effects are active", "target")); target_ = target; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result CardEffectExecutor::applyImmediate(CardEffectTarget& target, +eve::Result CardEffectExecutor::applyImmediate(CardEffectTarget& target, const effects::EffectInstance& effect) const { if (target.health < 0 || target.maxHealth <= 0 || target.health > target.maxHealth || target.barrier < 0) return invalid("card effect target state is invalid", "target"); @@ -48,11 +46,11 @@ eve::Result CardEffectExecutor::applyImmediate(CardEffectTarget& target, return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result CardEffectExecutor::settle(CardEffectTarget& target, - effects::EffectUpdateSummary lifecycle) const { +eve::Result CardEffectExecutor::settle(CardEffectTarget& target, + effects::EffectUpdateSummary lifecycle) const { if (target.health < 0 || target.maxHealth <= 0 || target.health > target.maxHealth || target.barrier < 0) - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "card effect target state is invalid", "target")); + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvalidArgument, "card effect target state is invalid", "target")); CardEffectUpdate result; result.lifecycle = std::move(lifecycle); for (const auto& tick : result.lifecycle.periodicTicks) { @@ -64,21 +62,23 @@ eve::Result CardEffectExecutor::settle(CardEffectTarget& targe return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::InvalidArgument, "card periodic settlement overflows", "periodic")); if (tick.effectId.empty()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, "card periodic trigger has no effect identity", "periodic.effectId")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, + "card periodic trigger has no effect identity", "periodic.effectId")); const auto tagged = [&tick](const char* value) { return std::find(tick.tags.begin(), tick.tags.end(), value) != tick.tags.end(); }; if (tagged("card:shield")) { const auto shield = static_cast(amount); if (shield > std::numeric_limits::max() - target.barrier) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "card periodic barrier addition overflows", "target.barrier")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "card periodic barrier addition overflows", "target.barrier")); target.barrier += shield; } else if (tagged("card:heal")) { target.health = std::min(target.maxHealth, target.health + static_cast(amount)); } else { - const auto damage = static_cast(amount); + const auto damage = static_cast(amount); const auto absorbed = std::min(target.barrier, damage); target.barrier -= absorbed; target.health = std::max(0, target.health - (damage - absorbed)); @@ -90,12 +90,11 @@ eve::Result CardEffectExecutor::settle(CardEffectTarget& targe } ++result.settled; } - return eve::Result::success(std::move(result), - eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(std::move(result), eve::Status::success(eve::StatusCode::Applied)); } eve::Result CardEffectAdapter::apply(const CardEffectDefinition& definition, - eve::SubjectRef subject) { + eve::SubjectRef subject) { if (definition.id.empty()) return eve::Result::failure( eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "card effect id must not be empty", "id")); @@ -103,32 +102,34 @@ eve::Result CardEffectAdapter::apply(const CardEffectDefi return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::InvalidArgument, "card effect subject must be valid", "subject")); effects::EffectDefinition common; - common.id = definition.id; - common.stackKey = definition.id; - common.duration = definition.duration; - common.period = definition.period; + common.id = definition.id; + common.stackKey = definition.id; + common.duration = definition.duration; + common.period = definition.period; common.magnitude = definition.magnitude; - common.policy = definition.policy; - common.tags = definition.tags; + common.policy = definition.policy; + common.tags = definition.tags; common.tags.push_back(definition.kind == CardEffectKind::Damage ? "card:damage" - : definition.kind == CardEffectKind::Heal ? "card:heal" : "card:shield"); + : definition.kind == CardEffectKind::Heal ? "card:heal" + : "card:shield"); if (definition.deathTrigger) common.tags.push_back("card:death-trigger"); auto valid = common.validate(); if (!valid) return eve::Result::failure(valid.status()); auto candidate = container_.clone(); - auto applied = candidate.apply(common, subject.format(), definition.source); + auto applied = candidate.apply(common, subject.format(), definition.source); if (!applied) return eve::Result::failure(applied.status()); - const std::string id = std::move(applied).takeValue(); - auto instance = candidate.find(id); - if (!instance) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, "card effect candidate disappeared", "effect")); + const std::string id = std::move(applied).takeValue(); + auto instance = candidate.find(id); + if (!instance) + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvariantViolation, "card effect candidate disappeared", "effect")); CardEffectTarget targetCandidate = target_; - auto immediate = executor_.applyImmediate(targetCandidate, *instance); + auto immediate = executor_.applyImmediate(targetCandidate, *instance); if (!immediate) return eve::Result::failure(immediate.status()); auto handle = candidate.handleFor(id); if (!handle) return eve::Result::failure(handle.status()); - target_ = std::move(targetCandidate); + target_ = std::move(targetCandidate); container_ = std::move(candidate); return eve::Result::success(std::move(handle).takeValue(), eve::Status::success(eve::StatusCode::Applied)); @@ -145,18 +146,15 @@ eve::Result CardEffectAdapter::advance(const eve::SimulationSt auto lifecycle = effects::EffectExecutor{}.advance(candidate, step); if (!lifecycle) return eve::Result::failure(lifecycle.status()); auto targetCandidate = target_; - auto settled = executor_.settle(targetCandidate, std::move(lifecycle).takeValue()); + auto settled = executor_.settle(targetCandidate, std::move(lifecycle).takeValue()); if (!settled) return settled; auto result = std::move(settled).takeValue(); - target_ = std::move(targetCandidate); - container_ = std::move(candidate); - return eve::Result::success(std::move(result), - eve::Status::success(eve::StatusCode::Applied)); + target_ = std::move(targetCandidate); + container_ = std::move(candidate); + return eve::Result::success(std::move(result), eve::Status::success(eve::StatusCode::Applied)); } -std::size_t CardEffectAdapter::count() const noexcept { - return static_cast(container_.effectCount()); -} +std::size_t CardEffectAdapter::count() const noexcept { return static_cast(container_.effectCount()); } CardEffectSnapshot CardEffectAdapter::snapshot() const { return {container_.snapshot(), target_}; } diff --git a/src/modules/card/CardEffects.h b/src/modules/card/CardEffects.h index 402f13a9..68e2f5e8 100644 --- a/src/modules/card/CardEffects.h +++ b/src/modules/card/CardEffects.h @@ -26,48 +26,48 @@ enum class CardEffectKind : std::uint8_t { Damage, Heal, Shield }; /** @brief Mutable card combat state changed by the card effect executor. */ struct CardEffectTarget { - int health = 100; - int maxHealth = 100; - int barrier = 0; + int health = 100; + int maxHealth = 100; + int barrier = 0; std::uint32_t deathTriggers = 0; }; /** @brief Strong card definition projected into the common effect definition. */ struct CardEffectDefinition { - std::string id; - std::string source; - double duration = 0.0; - double period = 0.0; - double magnitude = 0.0; - effects::EffectPolicy policy; - CardEffectKind kind = CardEffectKind::Damage; - bool deathTrigger = true; + std::string id; + std::string source; + double duration = 0.0; + double period = 0.0; + double magnitude = 0.0; + effects::EffectPolicy policy; + CardEffectKind kind = CardEffectKind::Damage; + bool deathTrigger = true; std::vector tags; }; /** @brief Result of one card effect lifecycle step and its domain settlement. */ struct CardEffectUpdate { effects::EffectUpdateSummary lifecycle; - std::uint32_t settled = 0; - std::uint32_t absorbed = 0; - bool deathTriggered = false; + std::uint32_t settled = 0; + std::uint32_t absorbed = 0; + bool deathTriggered = false; }; /** @brief Serializable in-memory card effect snapshot. */ struct CardEffectSnapshot { effects::EffectContainer effects; - CardEffectTarget target; + CardEffectTarget target; }; /** @brief Card executor: interprets card policy without owning effect instances. */ class CardEffectExecutor { public: /** @brief Apply immediate shield semantics to a staged target. */ - [[nodiscard]] eve::Result applyImmediate(CardEffectTarget& target, + [[nodiscard]] eve::Result applyImmediate(CardEffectTarget& target, const effects::EffectInstance& effect) const; /** @brief Settle all common periodic triggers using card shield/death rules. */ - [[nodiscard]] eve::Result settle(CardEffectTarget& target, + [[nodiscard]] eve::Result settle(CardEffectTarget& target, effects::EffectUpdateSummary lifecycle) const; }; @@ -82,7 +82,7 @@ public: [[nodiscard]] eve::Result initializeTarget(CardEffectTarget target); /** @brief Apply one typed card effect and return its generation-qualified handle. */ [[nodiscard]] eve::Result apply(const CardEffectDefinition& definition, - eve::SubjectRef subject); + eve::SubjectRef subject); /** @brief Remove one effect handle; stale handles are rejected. */ [[nodiscard]] eve::Result remove(effects::EffectHandle handle); /** @brief Advance and settle card effects atomically for one simulation step. */ @@ -102,8 +102,8 @@ public: private: effects::EffectContainer container_; - CardEffectExecutor executor_; - CardEffectTarget target_; + CardEffectExecutor executor_; + CardEffectTarget target_; }; } // namespace eve::card diff --git a/src/modules/card/CardPlay.cpp b/src/modules/card/CardPlay.cpp index c2c69190..79b6887d 100644 --- a/src/modules/card/CardPlay.cpp +++ b/src/modules/card/CardPlay.cpp @@ -9,8 +9,7 @@ namespace eve::card { namespace { -std::optional cardValue(const CardData* card, const CardDefinition& definition, - std::string_view key) { +std::optional cardValue(const CardData* card, const CardDefinition& definition, std::string_view key) { if (!card) return std::nullopt; const auto state = const_cast(card)->state(); if (key == "card.id") return eve::Value(const_cast(card)->identity()->id); @@ -60,16 +59,15 @@ std::optional CardPlayConditionContext::authority(std::string_view scope) return queries_.authority(scope); } -std::optional CardPlayConditionContext::policy(std::string_view name, - const eve::Value& arguments) const { +std::optional CardPlayConditionContext::policy(std::string_view name, + const eve::Value& arguments) const { if (!queries_.policy) return std::nullopt; return queries_.policy(name, arguments); } -decision::ConditionResult CardPlayConditionAdapter::evaluate(const CardData* card, - const CardDefinition& definition, - const decision::Condition& condition, - CardPlayConditionQueries queries) { +decision::ConditionResult CardPlayConditionAdapter::evaluate(const CardData* card, const CardDefinition& definition, + const decision::Condition& condition, + CardPlayConditionQueries queries) { CardPlayConditionContext context(card, definition, std::move(queries)); return condition.evaluate(context); } @@ -77,86 +75,72 @@ decision::ConditionResult CardPlayConditionAdapter::evaluate(const CardData* car namespace { template -eve::Result cardFailure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { - return eve::Result::failure( - eve::Diagnostic::error(code, std::move(message), std::move(path))); +eve::Result cardFailure(eve::DiagnosticCode code, std::string message, std::string path = {}) { + return eve::Result::failure(eve::Diagnostic::error(code, std::move(message), std::move(path))); } eve::Result cardLifecycleConflict(std::string message) { - return eve::Result::failure(eve::Status::failure( - eve::StatusCode::Conflict, - eve::Diagnostic::error(eve::DiagnosticCode::Conflict, std::move(message), "card.play"))); + return eve::Result::failure( + eve::Status::failure(eve::StatusCode::Conflict, + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, std::move(message), "card.play"))); } class CardTransferParticipant final : public eve::transaction::ITransactionParticipant { public: - CardTransferParticipant(CardData& card, eve::container::IContainer& source, - eve::container::IContainer& destination, + CardTransferParticipant(CardData& card, eve::container::IContainer& source, eve::container::IContainer& destination, std::optional sourceSlot, std::optional destinationSlot) - : card_(card), source_(source), destination_(destination), - sourceSlot_(sourceSlot), destinationSlot_(destinationSlot) {} + : card_(card), + source_(source), + destination_(destination), + sourceSlot_(sourceSlot), + destinationSlot_(destinationSlot) {} - [[nodiscard]] std::string_view name() const noexcept override { - return "card-container-transfer"; - } + [[nodiscard]] std::string_view name() const noexcept override { return "card-container-transfer"; } - [[nodiscard]] eve::Result prepare( - const eve::transaction::TransactionContext&) override { - if (prepared_ || committed_) - return cardLifecycleConflict("card transfer participant is already in flight"); + [[nodiscard]] eve::Result prepare(const eve::transaction::TransactionContext&) override { + if (prepared_ || committed_) return cardLifecycleConflict("card transfer participant is already in flight"); if (&source_ == &destination_) - return cardFailure(eve::DiagnosticCode::Conflict, - "card play source and destination must differ"); + return cardFailure(eve::DiagnosticCode::Conflict, "card play source and destination must differ"); auto sourceResult = source_.snapshot(); if (!sourceResult) return eve::Result::failure(sourceResult.status()); auto destinationResult = destination_.snapshot(); if (!destinationResult) return eve::Result::failure(destinationResult.status()); - beforeSource_ = std::move(sourceResult).takeValue(); + beforeSource_ = std::move(sourceResult).takeValue(); beforeDestination_ = std::move(destinationResult).takeValue(); const eve::container::MembershipId object(card_.identity()->id); - const auto sourceIt = std::find_if( - beforeSource_.entries.begin(), beforeSource_.entries.end(), - [&object](const auto& entry) { return entry.membership.object == object; }); + const auto sourceIt = std::find_if(beforeSource_.entries.begin(), beforeSource_.entries.end(), + [&object](const auto& entry) { return entry.membership.object == object; }); if (sourceIt == beforeSource_.entries.end()) - return cardFailure(eve::DiagnosticCode::NotFound, - "card is not present in the source container", "source"); + return cardFailure(eve::DiagnosticCode::NotFound, "card is not present in the source container", + "source"); if (sourceSlot_ && *sourceSlot_ != sourceIt->membership.slot) - return cardFailure(eve::DiagnosticCode::StaleHandle, - "card source slot is stale", "sourceSlot"); + return cardFailure(eve::DiagnosticCode::StaleHandle, "card source slot is stale", "sourceSlot"); - auto accepted = destination_.validateInsert( - sourceIt->object, destinationSlot_, std::nullopt); + auto accepted = destination_.validateInsert(sourceIt->object, destinationSlot_, std::nullopt); if (!accepted) return accepted; - if (destinationSlot_ && - (destinationSlot_->value() < 0 || - static_cast(destinationSlot_->value()) > - beforeDestination_.entries.size())) - return cardFailure(eve::DiagnosticCode::InvalidArgument, - "card destination slot is out of range", "destinationSlot"); + if (destinationSlot_ && (destinationSlot_->value() < 0 || static_cast(destinationSlot_->value()) > + beforeDestination_.entries.size())) + return cardFailure(eve::DiagnosticCode::InvalidArgument, "card destination slot is out of range", + "destinationSlot"); afterSource_ = beforeSource_; - afterSource_.entries.erase( - afterSource_.entries.begin() + (sourceIt - beforeSource_.entries.begin())); + afterSource_.entries.erase(afterSource_.entries.begin() + (sourceIt - beforeSource_.entries.begin())); renumber(afterSource_); afterSource_.revision = nextRevision(beforeSource_.revision); if (afterSource_.revision == beforeSource_.revision) - return cardFailure(eve::DiagnosticCode::InvariantViolation, - "source container revision is exhausted"); + return cardFailure(eve::DiagnosticCode::InvariantViolation, "source container revision is exhausted"); - afterDestination_ = beforeDestination_; - auto moved = *sourceIt; - const std::size_t insertion = destinationSlot_ - ? static_cast(destinationSlot_->value()) - : afterDestination_.entries.size(); + afterDestination_ = beforeDestination_; + auto moved = *sourceIt; + const std::size_t insertion = + destinationSlot_ ? static_cast(destinationSlot_->value()) : afterDestination_.entries.size(); if (insertion > afterDestination_.entries.size()) - return cardFailure(eve::DiagnosticCode::InvalidArgument, - "card destination slot is out of range", "destinationSlot"); - afterDestination_.entries.insert(afterDestination_.entries.begin() + insertion, - std::move(moved)); + return cardFailure(eve::DiagnosticCode::InvalidArgument, "card destination slot is out of range", + "destinationSlot"); + afterDestination_.entries.insert(afterDestination_.entries.begin() + insertion, std::move(moved)); renumber(afterDestination_); afterDestination_.revision = nextRevision(beforeDestination_.revision); if (afterDestination_.revision == beforeDestination_.revision) @@ -186,20 +170,16 @@ public: return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } - [[nodiscard]] eve::Result commit( - const eve::transaction::TransactionContext&) override { - if (!prepared_ || committed_) - return cardLifecycleConflict("card transfer has no prepared stage"); + [[nodiscard]] eve::Result commit(const eve::transaction::TransactionContext&) override { + if (!prepared_ || committed_) return cardLifecycleConflict("card transfer has no prepared stage"); destinationStage_->commit(); sourceStage_->commit(); committed_ = true; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } - [[nodiscard]] eve::Result rollback( - const eve::transaction::TransactionContext&) override { - if (committed_) - return cardLifecycleConflict("committed card transfer requires compensation"); + [[nodiscard]] eve::Result rollback(const eve::transaction::TransactionContext&) override { + if (committed_) return cardLifecycleConflict("committed card transfer requires compensation"); if (destinationStage_) destinationStage_->rollback(); if (sourceStage_) sourceStage_->rollback(); sourceStage_.reset(); @@ -208,10 +188,8 @@ public: return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } - [[nodiscard]] eve::Result compensate( - const eve::transaction::TransactionContext&) override { - if (!committed_) - return cardLifecycleConflict("card transfer has no committed state to compensate"); + [[nodiscard]] eve::Result compensate(const eve::transaction::TransactionContext&) override { + if (!committed_) return cardLifecycleConflict("card transfer has no committed state to compensate"); auto currentSource = source_.snapshot(); if (!currentSource) return eve::Result::failure(currentSource.status()); @@ -219,11 +197,10 @@ public: if (!currentDestination) return eve::Result::failure(currentDestination.status()); if (currentSource.value().revision != afterSource_.revision || currentDestination.value().revision != afterDestination_.revision) - return cardFailure(eve::DiagnosticCode::StaleHandle, - "card containers changed before compensation"); - auto sourceCandidate = beforeSource_; - auto destinationCandidate = beforeDestination_; - sourceCandidate.revision = nextRevision(currentSource.value().revision); + return cardFailure(eve::DiagnosticCode::StaleHandle, "card containers changed before compensation"); + auto sourceCandidate = beforeSource_; + auto destinationCandidate = beforeDestination_; + sourceCandidate.revision = nextRevision(currentSource.value().revision); destinationCandidate.revision = nextRevision(currentDestination.value().revision); if (sourceCandidate.revision == currentSource.value().revision || destinationCandidate.revision == currentDestination.value().revision) @@ -251,7 +228,7 @@ public: sourceStage->commit(); sourceStage_.reset(); destinationStage_.reset(); - prepared_ = false; + prepared_ = false; committed_ = false; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } @@ -264,60 +241,54 @@ private: static void renumber(eve::container::ContainerSnapshot& snapshot) noexcept { for (std::size_t index = 0; index < snapshot.entries.size(); ++index) - snapshot.entries[index].membership.slot = - eve::container::SlotIndex(static_cast(index)); + snapshot.entries[index].membership.slot = eve::container::SlotIndex(static_cast(index)); } - CardData& card_; - eve::container::IContainer& source_; - eve::container::IContainer& destination_; - std::optional sourceSlot_; - std::optional destinationSlot_; - eve::container::ContainerSnapshot beforeSource_; - eve::container::ContainerSnapshot beforeDestination_; - eve::container::ContainerSnapshot afterSource_; - eve::container::ContainerSnapshot afterDestination_; + CardData& card_; + eve::container::IContainer& source_; + eve::container::IContainer& destination_; + std::optional sourceSlot_; + std::optional destinationSlot_; + eve::container::ContainerSnapshot beforeSource_; + eve::container::ContainerSnapshot beforeDestination_; + eve::container::ContainerSnapshot afterSource_; + eve::container::ContainerSnapshot afterDestination_; std::unique_ptr sourceStage_; std::unique_ptr destinationStage_; - bool prepared_ = false; - bool committed_ = false; + bool prepared_ = false; + bool committed_ = false; }; } // namespace CardPlayParticipant::CardPlayParticipant(CardData& card) : card_(card) {} -eve::Result CardPlayParticipant::prepare( - const eve::transaction::TransactionContext& context) { +eve::Result CardPlayParticipant::prepare(const eve::transaction::TransactionContext& context) { (void)context; - if (prepared_ || committed_) - return cardLifecycleConflict("card play participant is already in flight or terminal"); + if (prepared_ || committed_) return cardLifecycleConflict("card play participant is already in flight or terminal"); auto state = card_.state(); if (state->phase == CardState::Played || state->phase == CardState::Discarded || state->phase == CardState::Disabled) return cardLifecycleConflict("card is not playable in its current state"); previousState_ = state->phase; - prepared_ = true; + prepared_ = true; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result CardPlayParticipant::commit( - const eve::transaction::TransactionContext& context) { +eve::Result CardPlayParticipant::commit(const eve::transaction::TransactionContext& context) { (void)context; - if (!prepared_ || committed_) - return cardLifecycleConflict("card play participant has no uncommitted stage"); + if (!prepared_ || committed_) return cardLifecycleConflict("card play participant has no uncommitted stage"); auto state = card_.state(); // A preceding container-transfer participant deliberately projects the // card's destination phase during the same synchronous transaction. The // play participant is the final authority and publishes Played once all // preceding participants have committed successfully. state->phase = CardState::Played; - committed_ = true; + committed_ = true; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result CardPlayParticipant::rollback( - const eve::transaction::TransactionContext& context) { +eve::Result CardPlayParticipant::rollback(const eve::transaction::TransactionContext& context) { (void)context; if (!prepared_ || committed_) return cardLifecycleConflict("card play participant has no prepared stage to roll back"); @@ -325,90 +296,77 @@ eve::Result CardPlayParticipant::rollback( return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result CardPlayParticipant::compensate( - const eve::transaction::TransactionContext& context) { +eve::Result CardPlayParticipant::compensate(const eve::transaction::TransactionContext& context) { (void)context; - if (!committed_) - return cardLifecycleConflict("card play participant has no committed effect to compensate"); - auto state = card_.state(); + if (!committed_) return cardLifecycleConflict("card play participant has no committed effect to compensate"); + auto state = card_.state(); state->phase = previousState_; - committed_ = false; - prepared_ = false; + committed_ = false; + prepared_ = false; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result> CardPlayPaymentAdapter::manaCost( - const CardDefinition& definition) { +eve::Result> CardPlayPaymentAdapter::manaCost(const CardDefinition& definition) { if (definition.id.empty()) return cardFailure>( - eve::DiagnosticCode::InvalidArgument, - "card definition id must not be empty", "definition.id"); + eve::DiagnosticCode::InvalidArgument, "card definition id must not be empty", "definition.id"); if (definition.cost < 0) return cardFailure>( - eve::DiagnosticCode::InvalidArgument, - "card mana cost must not be negative", "definition.cost"); - if (definition.cost == 0) - return eve::Result>::success(std::nullopt); + eve::DiagnosticCode::InvalidArgument, "card mana cost must not be negative", "definition.cost"); + if (definition.cost == 0) return eve::Result>::success(std::nullopt); auto cost = eve::resource::CostSpec::single("mana", definition.cost); - if (!cost) - return eve::Result>::failure(cost.status()); - return eve::Result>::success( - std::move(cost).takeValue()); + if (!cost) return eve::Result>::failure(cost.status()); + return eve::Result>::success(std::move(cost).takeValue()); } eve::Result CardPlayPaymentAdapter::play( - CardData& card, const CardDefinition& definition, - eve::resource::IResourceAccount& playerAccount, std::string transactionId) { + CardData& card, const CardDefinition& definition, eve::resource::IResourceAccount& playerAccount, + std::string transactionId) { CardPlayRequest request; - request.card = &card; - request.definition = &definition; + request.card = &card; + request.definition = &definition; request.playerAccount = &playerAccount; request.transactionId = std::move(transactionId); return play(std::move(request)); } -eve::Result CardPlayPaymentAdapter::play( - CardPlayRequest request) { +eve::Result CardPlayPaymentAdapter::play(CardPlayRequest request) { if (request.card == nullptr || request.definition == nullptr) - return cardFailure( - eve::DiagnosticCode::InvalidArgument, - "card play requires a card and definition", "request"); - auto& card = *request.card; + return cardFailure(eve::DiagnosticCode::InvalidArgument, + "card play requires a card and definition", "request"); + auto& card = *request.card; const auto& definition = *request.definition; - if ((request.composition.source == nullptr) != - (request.composition.destination == nullptr)) + if ((request.composition.source == nullptr) != (request.composition.destination == nullptr)) return cardFailure( - eve::DiagnosticCode::InvalidArgument, - "card play requires both source and destination containers", "composition"); + eve::DiagnosticCode::InvalidArgument, "card play requires both source and destination containers", + "composition"); - const auto condition = CardPlayConditionAdapter::evaluate( - &card, definition, definition.playCondition, - request.composition.conditionQueries); + const auto condition = CardPlayConditionAdapter::evaluate(&card, definition, definition.playCondition, + request.composition.conditionQueries); if (!condition.passed()) { DiagnosticDetails details; details.emplace_back("reason", decision::conditionReasonCodeName(condition.reasonCode())); return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, - "card play condition was rejected", "condition", - std::move(details))); + eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, "card play condition was rejected", + "condition", std::move(details))); } auto cost = manaCost(definition); if (!cost) return eve::Result::failure(cost.status()); if (cost.value() && request.playerAccount == nullptr) - return cardFailure( - eve::DiagnosticCode::InvalidArgument, - "a mana-bearing card play requires a player account", "playerAccount"); + return cardFailure(eve::DiagnosticCode::InvalidArgument, + "a mana-bearing card play requires a player account", + "playerAccount"); if (request.transactionId.empty()) request.transactionId = "card.play." + definition.id; - CardPlayParticipant state(card); - std::unique_ptr transfer; + CardPlayParticipant state(card); + std::unique_ptr transfer; std::vector participants; participants.reserve(request.composition.effect ? 3u : 2u); if (request.composition.source != nullptr) { transfer = std::make_unique( - card, *request.composition.source, *request.composition.destination, - request.composition.sourceSlot, request.composition.destinationSlot); + card, *request.composition.source, *request.composition.destination, request.composition.sourceSlot, + request.composition.destinationSlot); participants.push_back(transfer.get()); } participants.push_back(&state); diff --git a/src/modules/card/CardPlay.h b/src/modules/card/CardPlay.h index aa66fe1d..d63f1a13 100644 --- a/src/modules/card/CardPlay.h +++ b/src/modules/card/CardPlay.h @@ -25,14 +25,14 @@ namespace eve::card { * a lock. An empty provider means that capability is unavailable. */ struct CardPlayConditionQueries { - using Resource = std::function(std::string_view key)>; + using Resource = std::function(std::string_view key)>; using Authority = std::function(std::string_view scope)>; - using Policy = std::function(std::string_view name, - const eve::Value& arguments)>; + using Policy = + std::function(std::string_view name, const eve::Value& arguments)>; - Resource resource; + Resource resource; Authority authority; - Policy policy; + Policy policy; }; /** @@ -66,12 +66,12 @@ public: /** @copydoc decision::EvaluationContext::authority */ [[nodiscard]] std::optional authority(std::string_view scope) const override; /** @copydoc decision::EvaluationContext::policy */ - [[nodiscard]] std::optional policy(std::string_view name, - const eve::Value& arguments) const override; + [[nodiscard]] std::optional policy(std::string_view name, + const eve::Value& arguments) const override; private: - const CardData* card_ = nullptr; - const CardDefinition* definition_ = nullptr; + const CardData* card_ = nullptr; + const CardDefinition* definition_ = nullptr; CardPlayConditionQueries queries_; }; @@ -86,10 +86,9 @@ public: * @param queries Optional read-only resource, authority and policy providers. * @return A stable explanation suitable for UI rejection text. */ - [[nodiscard]] static decision::ConditionResult evaluate(const CardData* card, - const CardDefinition& definition, - const decision::Condition& condition, - CardPlayConditionQueries queries = {}); + [[nodiscard]] static decision::ConditionResult evaluate(const CardData* card, const CardDefinition& definition, + const decision::Condition& condition, + CardPlayConditionQueries queries = {}); }; /** @@ -108,23 +107,19 @@ public: /** @brief Stable transaction diagnostic name. */ [[nodiscard]] std::string_view name() const noexcept override { return "card-play-effect"; } /** @copydoc eve::transaction::ITransactionParticipant::prepare */ - [[nodiscard]] eve::Result prepare( - const eve::transaction::TransactionContext& context) override; + [[nodiscard]] eve::Result prepare(const eve::transaction::TransactionContext& context) override; /** @copydoc eve::transaction::ITransactionParticipant::commit */ - [[nodiscard]] eve::Result commit( - const eve::transaction::TransactionContext& context) override; + [[nodiscard]] eve::Result commit(const eve::transaction::TransactionContext& context) override; /** @copydoc eve::transaction::ITransactionParticipant::rollback */ - [[nodiscard]] eve::Result rollback( - const eve::transaction::TransactionContext& context) override; + [[nodiscard]] eve::Result rollback(const eve::transaction::TransactionContext& context) override; /** @copydoc eve::transaction::ITransactionParticipant::compensate */ - [[nodiscard]] eve::Result compensate( - const eve::transaction::TransactionContext& context) override; + [[nodiscard]] eve::Result compensate(const eve::transaction::TransactionContext& context) override; private: CardData& card_; CardState previousState_ = CardState::Hand; - bool prepared_ = false; - bool committed_ = false; + bool prepared_ = false; + bool committed_ = false; }; /** @@ -137,11 +132,11 @@ private: * card state transition. */ struct CardPlayComposition { - CardPlayConditionQueries conditionQueries; - eve::container::IContainer* source = nullptr; - eve::container::IContainer* destination = nullptr; - std::optional sourceSlot; - std::optional destinationSlot; + CardPlayConditionQueries conditionQueries; + eve::container::IContainer* source = nullptr; + eve::container::IContainer* destination = nullptr; + std::optional sourceSlot; + std::optional destinationSlot; eve::transaction::ITransactionParticipant* effect = nullptr; }; @@ -153,11 +148,11 @@ struct CardPlayComposition { * this type does not perform implicit player lookup. */ struct CardPlayRequest { - CardData* card = nullptr; - const CardDefinition* definition = nullptr; + CardData* card = nullptr; + const CardDefinition* definition = nullptr; eve::resource::IResourceAccount* playerAccount = nullptr; - CardPlayComposition composition; - std::string transactionId; + CardPlayComposition composition; + std::string transactionId; }; /** @@ -179,16 +174,14 @@ public: * pure preflight; it cannot debit or move a card. No caller-side * participant assembly is required. */ - [[nodiscard]] static eve::Result play( - CardPlayRequest request); + [[nodiscard]] static eve::Result play(CardPlayRequest request); /** * @brief Convert a non-negative card mana value into a canonical cost. * @param definition Borrowed card definition. * @return A mana CostSpec, or an empty optional for a free card. */ - [[nodiscard]] static eve::Result> manaCost( - const CardDefinition& definition); + [[nodiscard]] static eve::Result> manaCost(const CardDefinition& definition); /** * @brief Play one card and charge its player's mana atomically. @@ -200,8 +193,8 @@ public: * condition/effect/payment leaves both card and account unchanged. */ [[nodiscard]] static eve::Result play( - CardData& card, const CardDefinition& definition, - eve::resource::IResourceAccount& playerAccount, std::string transactionId = {}); + CardData& card, const CardDefinition& definition, eve::resource::IResourceAccount& playerAccount, + std::string transactionId = {}); }; } // namespace eve::card diff --git a/src/modules/card/CardTypes.cpp b/src/modules/card/CardTypes.cpp index d3bf1b54..4f135927 100644 --- a/src/modules/card/CardTypes.cpp +++ b/src/modules/card/CardTypes.cpp @@ -123,7 +123,7 @@ bool CardData::hit(float px, float py) { std::string CardData::describe() { const auto *I = identity().operator->(); - auto projected = CardAttributeAdapter::project(*this); + auto projected = CardAttributeAdapter::project(*this); if (!projected) projected.ignore("card description retained legacy projection after attribute failure"); const auto *S = stats().operator->(); if (I->kind == "spell") return I->name + "(法术 · 费用 " + std::to_string(S->cost) + ")"; @@ -236,7 +236,7 @@ void renderCardBack(graphics::Graphics *gfx, float x, float y, float w, float h, void renderCard(graphics::Graphics *gfx, const CardData &cardRef, const LayoutConfig &cfg, bool back) { (void)cfg; CardData &card = const_cast(cardRef); - auto projected = CardAttributeAdapter::project(card); + auto projected = CardAttributeAdapter::project(card); if (!projected) { projected.ignore("card renderer skipped an invalid attribute projection"); return; diff --git a/src/modules/card/CardTypes.h b/src/modules/card/CardTypes.h index 9c543ed5..407429fc 100644 --- a/src/modules/card/CardTypes.h +++ b/src/modules/card/CardTypes.h @@ -5,10 +5,10 @@ * 模块入口 eve.Card() 见 Card.h;本文件是 CardData/Deck/Zone/Hand 等 ECS 实体与布局渲染实现。 */ -#include "common/ECS.h" -#include "common/definitions/DefinitionRuntime.h" #include "attributes/AttributeProjection.h" #include "card/CardEffects.h" +#include "common/ECS.h" +#include "common/definitions/DefinitionRuntime.h" #include "decision/Condition.h" #include @@ -158,9 +158,8 @@ public: */ struct DefinitionBinding { eve::definition::InstanceIdentity identity; - eve::definition::ReloadPolicy reloadPolicy = - eve::definition::ReloadPolicy::KeepInstanceValues; - bool active = true; + eve::definition::ReloadPolicy reloadPolicy = eve::definition::ReloadPolicy::KeepInstanceValues; + bool active = true; }; COMPONENT(Identity, identity) diff --git a/src/modules/decision/Condition.cpp b/src/modules/decision/Condition.cpp index 676612cc..e1c62e56 100644 --- a/src/modules/decision/Condition.cpp +++ b/src/modules/decision/Condition.cpp @@ -26,17 +26,17 @@ bool number(const Value& value, double& result) { } bool compareValues(const Value& left, CompareOperator op, const Value& right) { - double leftNumber = 0.0; + double leftNumber = 0.0; double rightNumber = 0.0; - bool numeric = number(left, leftNumber) && number(right, rightNumber); + bool numeric = number(left, leftNumber) && number(right, rightNumber); if (numeric) { switch (op) { - case CompareOperator::Equal: return leftNumber == rightNumber; - case CompareOperator::NotEqual: return leftNumber != rightNumber; - case CompareOperator::Less: return leftNumber < rightNumber; - case CompareOperator::LessEqual: return leftNumber <= rightNumber; - case CompareOperator::Greater: return leftNumber > rightNumber; - case CompareOperator::GreaterEqual: return leftNumber >= rightNumber; + case CompareOperator::Equal: return leftNumber == rightNumber; + case CompareOperator::NotEqual: return leftNumber != rightNumber; + case CompareOperator::Less: return leftNumber < rightNumber; + case CompareOperator::LessEqual: return leftNumber <= rightNumber; + case CompareOperator::Greater: return leftNumber > rightNumber; + case CompareOperator::GreaterEqual: return leftNumber >= rightNumber; } } if (op == CompareOperator::Equal) return left == right; @@ -45,11 +45,11 @@ bool compareValues(const Value& left, CompareOperator op, const Value& right) { const auto& l = *left.getIf(); const auto& r = *right.getIf(); switch (op) { - case CompareOperator::Less: return l < r; - case CompareOperator::LessEqual: return l <= r; - case CompareOperator::Greater: return l > r; - case CompareOperator::GreaterEqual: return l >= r; - default: break; + case CompareOperator::Less: return l < r; + case CompareOperator::LessEqual: return l <= r; + case CompareOperator::Greater: return l > r; + case CompareOperator::GreaterEqual: return l >= r; + default: break; } } return false; @@ -70,61 +70,62 @@ Value childDetails(const std::vector& results) { } ConditionResult unavailable(ConditionReasonCode reason, std::string_view kind, std::string_view key) { - return ConditionResult::failed(reason, {}, object({ - {"kind", Value(std::string(kind))}, - {"key", Value(std::string(key))}, - })); + return ConditionResult::failed(reason, {}, + object({ + {"kind", Value(std::string(kind))}, + {"key", Value(std::string(key))}, + })); } } // namespace const char* conditionReasonCodeName(ConditionReasonCode code) noexcept { switch (code) { - case ConditionReasonCode::Passed: return "passed"; - case ConditionReasonCode::ChildFailed: return "child_failed"; - case ConditionReasonCode::NoChildPassed: return "no_child_passed"; - case ConditionReasonCode::Negated: return "negated"; - case ConditionReasonCode::MissingValue: return "missing_value"; - case ConditionReasonCode::ValueMismatch: return "value_mismatch"; - case ConditionReasonCode::TagMissing: return "tag_missing"; - case ConditionReasonCode::TagUnavailable: return "tag_unavailable"; - case ConditionReasonCode::AttributeMissing: return "attribute_missing"; - case ConditionReasonCode::ResourceMissing: return "resource_missing"; - case ConditionReasonCode::StateMissing: return "state_missing"; - case ConditionReasonCode::StateMismatch: return "state_mismatch"; - case ConditionReasonCode::AuthorityDenied: return "authority_denied"; - case ConditionReasonCode::AuthorityUnavailable: return "authority_unavailable"; - case ConditionReasonCode::PolicyRejected: return "policy_rejected"; - case ConditionReasonCode::PolicyUnavailable: return "policy_unavailable"; - case ConditionReasonCode::InvalidCondition: return "invalid_condition"; + case ConditionReasonCode::Passed: return "passed"; + case ConditionReasonCode::ChildFailed: return "child_failed"; + case ConditionReasonCode::NoChildPassed: return "no_child_passed"; + case ConditionReasonCode::Negated: return "negated"; + case ConditionReasonCode::MissingValue: return "missing_value"; + case ConditionReasonCode::ValueMismatch: return "value_mismatch"; + case ConditionReasonCode::TagMissing: return "tag_missing"; + case ConditionReasonCode::TagUnavailable: return "tag_unavailable"; + case ConditionReasonCode::AttributeMissing: return "attribute_missing"; + case ConditionReasonCode::ResourceMissing: return "resource_missing"; + case ConditionReasonCode::StateMissing: return "state_missing"; + case ConditionReasonCode::StateMismatch: return "state_mismatch"; + case ConditionReasonCode::AuthorityDenied: return "authority_denied"; + case ConditionReasonCode::AuthorityUnavailable: return "authority_unavailable"; + case ConditionReasonCode::PolicyRejected: return "policy_rejected"; + case ConditionReasonCode::PolicyUnavailable: return "policy_unavailable"; + case ConditionReasonCode::InvalidCondition: return "invalid_condition"; } return "unknown"; } const char* conditionKindName(ConditionKind kind) noexcept { switch (kind) { - case ConditionKind::All: return "all"; - case ConditionKind::Any: return "any"; - case ConditionKind::Not: return "not"; - case ConditionKind::Compare: return "compare"; - case ConditionKind::HasTag: return "has_tag"; - case ConditionKind::HasAttribute: return "has_attribute"; - case ConditionKind::HasResource: return "has_resource"; - case ConditionKind::StateEquals: return "state_equals"; - case ConditionKind::AuthorityCheck: return "authority_check"; - case ConditionKind::PolicyCall: return "policy_call"; + case ConditionKind::All: return "all"; + case ConditionKind::Any: return "any"; + case ConditionKind::Not: return "not"; + case ConditionKind::Compare: return "compare"; + case ConditionKind::HasTag: return "has_tag"; + case ConditionKind::HasAttribute: return "has_attribute"; + case ConditionKind::HasResource: return "has_resource"; + case ConditionKind::StateEquals: return "state_equals"; + case ConditionKind::AuthorityCheck: return "authority_check"; + case ConditionKind::PolicyCall: return "policy_call"; } return "unknown"; } const char* compareOperatorName(CompareOperator op) noexcept { switch (op) { - case CompareOperator::Equal: return "eq"; - case CompareOperator::NotEqual: return "ne"; - case CompareOperator::Less: return "lt"; - case CompareOperator::LessEqual: return "le"; - case CompareOperator::Greater: return "gt"; - case CompareOperator::GreaterEqual: return "ge"; + case CompareOperator::Equal: return "eq"; + case CompareOperator::NotEqual: return "ne"; + case CompareOperator::Less: return "lt"; + case CompareOperator::LessEqual: return "le"; + case CompareOperator::Greater: return "gt"; + case CompareOperator::GreaterEqual: return "ge"; } return "unknown"; } @@ -157,8 +158,8 @@ Condition Condition::not_(Condition child) { Condition Condition::compare(std::string key, CompareOperator op, Value expected) { Condition result(ConditionKind::Compare); - result.key_ = std::move(key); - result.compare_ = op; + result.key_ = std::move(key); + result.compare_ = op; result.expected_ = std::move(expected); return result; } @@ -183,7 +184,7 @@ Condition Condition::hasResource(std::string resource) { Condition Condition::stateEquals(std::string key, Value expected) { Condition result(ConditionKind::StateEquals); - result.key_ = std::move(key); + result.key_ = std::move(key); result.expected_ = std::move(expected); return result; } @@ -197,32 +198,29 @@ Condition Condition::authorityCheck(std::string scope) { Condition Condition::policyCall(std::string name, Value arguments, std::optional scriptDeclaration) { Condition result(ConditionKind::PolicyCall); - result.key_ = std::move(name); - result.arguments_ = std::move(arguments); + result.key_ = std::move(name); + result.arguments_ = std::move(arguments); result.scriptDeclaration_ = std::move(scriptDeclaration); return result; } bool Condition::isValid() const noexcept { switch (kind_) { - case ConditionKind::All: - case ConditionKind::Any: - for (const auto& child : children_) - if (!child.isValid()) return false; - return true; - case ConditionKind::Not: - return children_.size() == 1 && children_.front().isValid(); - case ConditionKind::Compare: - case ConditionKind::StateEquals: - return !key_.empty(); - case ConditionKind::HasTag: - case ConditionKind::HasAttribute: - case ConditionKind::HasResource: - case ConditionKind::AuthorityCheck: - return !key_.empty(); - case ConditionKind::PolicyCall: - return !key_.empty() && (!scriptDeclaration_ || - (scriptDeclaration_->name.empty() || scriptDeclaration_->name == key_)); + case ConditionKind::All: + case ConditionKind::Any: + for (const auto& child : children_) + if (!child.isValid()) return false; + return true; + case ConditionKind::Not: return children_.size() == 1 && children_.front().isValid(); + case ConditionKind::Compare: + case ConditionKind::StateEquals: return !key_.empty(); + case ConditionKind::HasTag: + case ConditionKind::HasAttribute: + case ConditionKind::HasResource: + case ConditionKind::AuthorityCheck: return !key_.empty(); + case ConditionKind::PolicyCall: + return !key_.empty() && + (!scriptDeclaration_ || (scriptDeclaration_->name.empty() || scriptDeclaration_->name == key_)); } return false; } @@ -231,91 +229,95 @@ ConditionResult Condition::evaluate(const EvaluationContext& context) const { if (!isValid()) return ConditionResult::failed(ConditionReasonCode::InvalidCondition); switch (kind_) { - case ConditionKind::All: { - std::vector results; - results.reserve(children_.size()); - for (const auto& child : children_) { - auto result = child.evaluate(context); - if (!result.passed()) { - const auto reason = result.reasonCode(); + case ConditionKind::All: { + std::vector results; + results.reserve(children_.size()); + for (const auto& child : children_) { + auto result = child.evaluate(context); + if (!result.passed()) { + const auto reason = result.reasonCode(); + results.push_back(std::move(result)); + return ConditionResult::failed( + reason == ConditionReasonCode::InvalidCondition ? reason : ConditionReasonCode::ChildFailed, + results.back().evidence(), + object({{"kind", Value("all")}, {"children", childDetails(results)}})); + } results.push_back(std::move(result)); - return ConditionResult::failed(reason == ConditionReasonCode::InvalidCondition - ? reason - : ConditionReasonCode::ChildFailed, - results.back().evidence(), - object({{"kind", Value("all")}, {"children", childDetails(results)}})); } - results.push_back(std::move(result)); + return ConditionResult::success({}, object({{"kind", Value("all")}, {"children", childDetails(results)}})); } - return ConditionResult::success({}, object({{"kind", Value("all")}, {"children", childDetails(results)}})); - } - case ConditionKind::Any: { - std::vector results; - results.reserve(children_.size()); - for (const auto& child : children_) { - auto result = child.evaluate(context); + case ConditionKind::Any: { + std::vector results; + results.reserve(children_.size()); + for (const auto& child : children_) { + auto result = child.evaluate(context); + if (result.passed()) + return ConditionResult::success( + result.evidence(), object({{"kind", Value("any")}, {"children", childDetails(results)}})); + results.push_back(std::move(result)); + } + return ConditionResult::failed(ConditionReasonCode::NoChildPassed, {}, + object({{"kind", Value("any")}, {"children", childDetails(results)}})); + } + case ConditionKind::Not: { + auto result = children_.front().evaluate(context); if (result.passed()) - return ConditionResult::success(result.evidence(), - object({{"kind", Value("any")}, {"children", childDetails(results)}})); - results.push_back(std::move(result)); + return ConditionResult::failed(ConditionReasonCode::Negated, result.evidence(), + object({{"kind", Value("not")}, {"child", result.details()}})); + return ConditionResult::success(result.evidence(), + object({{"kind", Value("not")}, {"child", result.details()}})); + } + case ConditionKind::Compare: { + auto actual = context.value(key_); + if (!actual) return unavailable(ConditionReasonCode::MissingValue, "compare", key_); + const bool equal = compareValues(*actual, compare_, expected_); + return equal ? ConditionResult::success(*actual, object({{"key", Value(key_)}, + {"operator", Value(compareOperatorName(compare_))}, + {"expected", expected_}})) + : ConditionResult::failed(ConditionReasonCode::ValueMismatch, *actual, + object({{"key", Value(key_)}, + {"operator", Value(compareOperatorName(compare_))}, + {"expected", expected_}})); + } + case ConditionKind::HasTag: { + auto actual = context.hasTag(key_); + if (!actual) return unavailable(ConditionReasonCode::TagUnavailable, "has_tag", key_); + return *actual ? ConditionResult::success(Value(true), object({{"tag", Value(key_)}})) + : ConditionResult::failed(ConditionReasonCode::TagMissing, Value(false), + object({{"tag", Value(key_)}})); + } + case ConditionKind::HasAttribute: { + auto actual = context.attribute(key_); + if (!actual) return unavailable(ConditionReasonCode::AttributeMissing, "has_attribute", key_); + return ConditionResult::success(*actual, object({{"attribute", Value(key_)}})); + } + case ConditionKind::HasResource: { + auto actual = context.resource(key_); + if (!actual) return unavailable(ConditionReasonCode::ResourceMissing, "has_resource", key_); + return ConditionResult::success(*actual, object({{"resource", Value(key_)}})); + } + case ConditionKind::StateEquals: { + auto actual = context.state(key_); + if (!actual) return unavailable(ConditionReasonCode::StateMissing, "state_equals", key_); + return compareValues(*actual, CompareOperator::Equal, expected_) + ? ConditionResult::success(*actual, object({{"state", Value(key_)}, {"expected", expected_}})) + : ConditionResult::failed(ConditionReasonCode::StateMismatch, *actual, + object({{"state", Value(key_)}, {"expected", expected_}})); + } + case ConditionKind::AuthorityCheck: { + auto allowed = context.authority(key_); + if (!allowed) return unavailable(ConditionReasonCode::AuthorityUnavailable, "authority_check", key_); + return *allowed ? ConditionResult::success(Value(true), object({{"scope", Value(key_)}})) + : ConditionResult::failed(ConditionReasonCode::AuthorityDenied, Value(false), + object({{"scope", Value(key_)}})); + } + case ConditionKind::PolicyCall: { + auto result = context.policy(key_, arguments_); + if (!result) return unavailable(ConditionReasonCode::PolicyUnavailable, "policy_call", key_); + if (result->passed()) return std::move(*result); + return ConditionResult::failed(result->reasonCode(), result->evidence(), + object({{"policy", Value(key_)}, {"details", result->details()}})); } - return ConditionResult::failed(ConditionReasonCode::NoChildPassed, {}, - object({{"kind", Value("any")}, {"children", childDetails(results)}})); - } - case ConditionKind::Not: { - auto result = children_.front().evaluate(context); - if (result.passed()) - return ConditionResult::failed(ConditionReasonCode::Negated, result.evidence(), - object({{"kind", Value("not")}, {"child", result.details()}})); - return ConditionResult::success(result.evidence(), - object({{"kind", Value("not")}, {"child", result.details()}})); - } - case ConditionKind::Compare: { - auto actual = context.value(key_); - if (!actual) return unavailable(ConditionReasonCode::MissingValue, "compare", key_); - const bool equal = compareValues(*actual, compare_, expected_); - return equal - ? ConditionResult::success(*actual, object({{"key", Value(key_)}, {"operator", Value(compareOperatorName(compare_))}, {"expected", expected_}})) - : ConditionResult::failed(ConditionReasonCode::ValueMismatch, *actual, - object({{"key", Value(key_)}, {"operator", Value(compareOperatorName(compare_))}, {"expected", expected_}})); - } - case ConditionKind::HasTag: { - auto actual = context.hasTag(key_); - if (!actual) return unavailable(ConditionReasonCode::TagUnavailable, "has_tag", key_); - return *actual ? ConditionResult::success(Value(true), object({{"tag", Value(key_)}})) - : ConditionResult::failed(ConditionReasonCode::TagMissing, Value(false), object({{"tag", Value(key_)}})); - } - case ConditionKind::HasAttribute: { - auto actual = context.attribute(key_); - if (!actual) return unavailable(ConditionReasonCode::AttributeMissing, "has_attribute", key_); - return ConditionResult::success(*actual, object({{"attribute", Value(key_)}})); - } - case ConditionKind::HasResource: { - auto actual = context.resource(key_); - if (!actual) return unavailable(ConditionReasonCode::ResourceMissing, "has_resource", key_); - return ConditionResult::success(*actual, object({{"resource", Value(key_)}})); - } - case ConditionKind::StateEquals: { - auto actual = context.state(key_); - if (!actual) return unavailable(ConditionReasonCode::StateMissing, "state_equals", key_); - return compareValues(*actual, CompareOperator::Equal, expected_) - ? ConditionResult::success(*actual, object({{"state", Value(key_)}, {"expected", expected_}})) - : ConditionResult::failed(ConditionReasonCode::StateMismatch, *actual, - object({{"state", Value(key_)}, {"expected", expected_}})); - } - case ConditionKind::AuthorityCheck: { - auto allowed = context.authority(key_); - if (!allowed) return unavailable(ConditionReasonCode::AuthorityUnavailable, "authority_check", key_); - return *allowed ? ConditionResult::success(Value(true), object({{"scope", Value(key_)}})) - : ConditionResult::failed(ConditionReasonCode::AuthorityDenied, Value(false), object({{"scope", Value(key_)}})); - } - case ConditionKind::PolicyCall: { - auto result = context.policy(key_, arguments_); - if (!result) return unavailable(ConditionReasonCode::PolicyUnavailable, "policy_call", key_); - if (result->passed()) return std::move(*result); - return ConditionResult::failed(result->reasonCode(), result->evidence(), - object({{"policy", Value(key_)}, {"details", result->details()}})); - } } return ConditionResult::failed(ConditionReasonCode::InvalidCondition); } diff --git a/src/modules/decision/Condition.h b/src/modules/decision/Condition.h index 66b654fd..5db2895e 100644 --- a/src/modules/decision/Condition.h +++ b/src/modules/decision/Condition.h @@ -70,23 +70,23 @@ struct ScriptConditionDeclaration { /** @brief Stable machine-readable explanation code for a condition outcome. */ enum class ConditionReasonCode : std::uint32_t { - Passed = 0, - ChildFailed = 1, - NoChildPassed = 2, - Negated = 3, - MissingValue = 4, - ValueMismatch = 5, - TagMissing = 6, - TagUnavailable = 7, - AttributeMissing = 8, - ResourceMissing = 9, - StateMissing = 10, - StateMismatch = 11, - AuthorityDenied = 12, + Passed = 0, + ChildFailed = 1, + NoChildPassed = 2, + Negated = 3, + MissingValue = 4, + ValueMismatch = 5, + TagMissing = 6, + TagUnavailable = 7, + AttributeMissing = 8, + ResourceMissing = 9, + StateMissing = 10, + StateMismatch = 11, + AuthorityDenied = 12, AuthorityUnavailable = 13, - PolicyRejected = 14, - PolicyUnavailable = 15, - InvalidCondition = 16, + PolicyRejected = 14, + PolicyUnavailable = 15, + InvalidCondition = 16, }; /** @@ -128,8 +128,7 @@ public: /** @brief Construct a passed result with optional evidence and details. */ static ConditionResult success(Value evidence = {}, Value details = Value::Object{}); /** @brief Construct a rejected result with a stable reason and explanation. */ - static ConditionResult failed(ConditionReasonCode reason, Value evidence = {}, - Value details = Value::Object{}); + static ConditionResult failed(ConditionReasonCode reason, Value evidence = {}, Value details = Value::Object{}); /** @brief Whether the condition passed. */ bool passed() const noexcept { return passed_; } @@ -144,10 +143,10 @@ private: ConditionResult(bool passed, ConditionReasonCode reason, Value evidence, Value details) : passed_(passed), reason_(reason), evidence_(std::move(evidence)), details_(std::move(details)) {} - bool passed_ = false; - ConditionReasonCode reason_ = ConditionReasonCode::InvalidCondition; - Value evidence_; - Value details_; + bool passed_ = false; + ConditionReasonCode reason_ = ConditionReasonCode::InvalidCondition; + Value evidence_; + Value details_; }; /** @@ -177,7 +176,7 @@ public: [[nodiscard]] virtual std::optional authority(std::string_view scope) const = 0; /** @brief Evaluate a named read-only policy with owned arguments. */ [[nodiscard]] virtual std::optional policy(std::string_view name, - const Value& arguments) const = 0; + const Value& arguments) const = 0; }; /** @@ -233,9 +232,7 @@ public: /** @brief Return owned PolicyCall arguments. */ const Value& arguments() const noexcept { return arguments_; } /** @brief Return an optional script dependency/determinism declaration. */ - const std::optional& scriptDeclaration() const noexcept { - return scriptDeclaration_; - } + const std::optional& scriptDeclaration() const noexcept { return scriptDeclaration_; } /** @brief Return whether the factory invariants for this node hold. */ [[nodiscard]] bool isValid() const noexcept; /** @brief Evaluate this tree against a read-only context. */ @@ -244,13 +241,13 @@ public: private: explicit Condition(ConditionKind kind) : kind_(kind) {} - ConditionKind kind_ = ConditionKind::All; - std::vector children_; - std::string key_; - CompareOperator compare_ = CompareOperator::Equal; - Value expected_; - Value arguments_ = Value::Object{}; - std::optional scriptDeclaration_; + ConditionKind kind_ = ConditionKind::All; + std::vector children_; + std::string key_; + CompareOperator compare_ = CompareOperator::Equal; + Value expected_; + Value arguments_ = Value::Object{}; + std::optional scriptDeclaration_; }; } // namespace eve::decision diff --git a/src/modules/decision/Decision.cpp b/src/modules/decision/Decision.cpp index c1d26fac..489fce75 100644 --- a/src/modules/decision/Decision.cpp +++ b/src/modules/decision/Decision.cpp @@ -1,5 +1,4 @@ #include "decision/Decision.h" -#include "common/SquirrelBinding.h" #include #include #include @@ -9,6 +8,7 @@ #include #include #include "common/Json.h" +#include "common/SquirrelBinding.h" namespace eve::decision { namespace { @@ -22,10 +22,8 @@ struct ScriptDecisionContext { }; template -eve::Result decisionFailure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - code, std::move(message), std::move(path), {}, "decision")); +eve::Result decisionFailure(eve::DiagnosticCode code, std::string message, std::string path = {}) { + return eve::Result::failure(eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "decision")); } eve::Result decisionApplied() { @@ -38,27 +36,22 @@ eve::Result decisionTriggered(bool triggered) { } eve::Result snapshotFailure(std::string path, std::string message) { - return decisionFailure(eve::DiagnosticCode::SerializationError, - std::move(message), std::move(path)); + return decisionFailure(eve::DiagnosticCode::SerializationError, std::move(message), std::move(path)); } template ssq::Table makeOwnedProxy(HSQUIRRELVM vm, eve::Result&& reference, Release&& release) { - if (!reference) - return eve::script::projectStatusResult(vm, reference.status(), false, false); - const Ref ref = std::move(reference).takeValue(); - auto object = eve::script::makeOwnedSquirrelInstance( - vm, std::make_unique(ref)); + if (!reference) return eve::script::projectStatusResult(vm, reference.status(), false, false); + const Ref ref = std::move(reference).takeValue(); + auto object = eve::script::makeOwnedSquirrelInstance(vm, std::make_unique(ref)); if (!object) { const eve::Status status = object.status(); object.ignore("failed to create owned decision proxy"); - std::invoke(std::forward(release), ref).ignore( - "rollback failed owned decision allocation"); + std::invoke(std::forward(release), ref).ignore("rollback failed owned decision allocation"); return eve::script::projectStatusResult(vm, status, false, false); } ssq::Object owned = std::move(object).takeValue(); - auto result = eve::script::projectStatusResult( - vm, eve::Status::success(eve::StatusCode::Applied), true, false); + auto result = eve::script::projectStatusResult(vm, eve::Status::success(eve::StatusCode::Applied), true, false); result.set("value", owned); result.set("ownership", std::string("owned")); result.set("ownerEpoch", static_cast(ref.ownerEpoch)); @@ -95,17 +88,16 @@ eve::Result DecisionContext::set(const std::string& b, const std::string& std::string e; auto d = eve::json::Document::parse(j, &e); if (b.empty()) - return decisionFailure(eve::DiagnosticCode::InvalidArgument, - "blackboard name must not be empty", "board"); + return decisionFailure(eve::DiagnosticCode::InvalidArgument, "blackboard name must not be empty", + "board"); if (k.empty()) - return decisionFailure(eve::DiagnosticCode::InvalidArgument, - "blackboard key must not be empty", "key"); + return decisionFailure(eve::DiagnosticCode::InvalidArgument, "blackboard key must not be empty", "key"); if (!d.valid()) - return decisionFailure(eve::DiagnosticCode::ParseError, - e.empty() ? "invalid blackboard JSON" : e, "value"); + return decisionFailure(eve::DiagnosticCode::ParseError, e.empty() ? "invalid blackboard JSON" : e, + "value"); if (!scalar(d.root())) - return decisionFailure(eve::DiagnosticCode::InvalidArgument, - "blackboard value must be a JSON scalar", "value"); + return decisionFailure(eve::DiagnosticCode::InvalidArgument, "blackboard value must be a JSON scalar", + "value"); boards_[b][k] = canon(d.root()); return decisionApplied(); } @@ -116,10 +108,10 @@ std::string DecisionContext::get(const std::string& b, const std::string& k, con return j == i->second.end() ? f : j->second; } eve::Result DecisionContext::addTransition(const std::string& m, const std::string& f, const std::string& t, - const std::string& to) { + const std::string& to) { if (m.empty() || f.empty() || t.empty() || to.empty()) - return decisionFailure(eve::DiagnosticCode::InvalidArgument, - "FSM transition names must not be empty", "transition"); + return decisionFailure(eve::DiagnosticCode::InvalidArgument, "FSM transition names must not be empty", + "transition"); transitions_[m][{f, t}] = to; return decisionApplied(); } @@ -137,8 +129,7 @@ eve::Result DecisionContext::trigger(const std::string& m, const std::stri auto s = states_.find(m); auto a = transitions_.find(m); if (s == states_.end()) - return decisionFailure(eve::DiagnosticCode::NotFound, - "FSM machine has no current state", "machine"); + return decisionFailure(eve::DiagnosticCode::NotFound, "FSM machine has no current state", "machine"); if (a == transitions_.end()) return decisionTriggered(false); auto i = a->second.find({s->second, t}); if (i == a->second.end()) return decisionTriggered(false); @@ -182,40 +173,38 @@ std::string DecisionContext::choose(const std::string& s) { } eve::Result DecisionContext::newGrid(const std::string& n, int w, int h, float c, float x, float y) { if (n.empty()) - return decisionFailure(eve::DiagnosticCode::InvalidArgument, - "influence grid name must not be empty", "name"); + return decisionFailure(eve::DiagnosticCode::InvalidArgument, "influence grid name must not be empty", + "name"); if (w <= 0 || h <= 0 || size_t(w) > 10000000 / size_t(h)) return decisionFailure(eve::DiagnosticCode::InvalidArgument, "influence grid dimensions are invalid or too large", "dimensions"); if (!std::isfinite(c) || c <= 0 || !std::isfinite(x) || !std::isfinite(y)) return decisionFailure(eve::DiagnosticCode::InvalidArgument, - "influence grid geometry must be finite and have a positive cell size", "geometry"); + "influence grid geometry must be finite and have a positive cell size", + "geometry"); grids_[n] = {w, h, c, x, y, std::vector(size_t(w) * h)}; return decisionApplied(); } eve::Result DecisionContext::setCell(const std::string& n, int x, int y, float v) { auto i = grids_.find(n); if (i == grids_.end()) - return decisionFailure(eve::DiagnosticCode::NotFound, - "influence grid does not exist", "grid"); + return decisionFailure(eve::DiagnosticCode::NotFound, "influence grid does not exist", "grid"); if (x < 0 || y < 0 || x >= i->second.w || y >= i->second.h || !std::isfinite(v)) - return decisionFailure(eve::DiagnosticCode::InvalidArgument, - "influence grid cell or value is invalid", "cell"); + return decisionFailure(eve::DiagnosticCode::InvalidArgument, "influence grid cell or value is invalid", + "cell"); i->second.values[size_t(y) * i->second.w + x] = v; return decisionApplied(); } eve::Result DecisionContext::addCell(const std::string& n, int x, int y, float v) { auto i = grids_.find(n); if (i == grids_.end()) - return decisionFailure(eve::DiagnosticCode::NotFound, - "influence grid does not exist", "grid"); + return decisionFailure(eve::DiagnosticCode::NotFound, "influence grid does not exist", "grid"); if (x < 0 || y < 0 || x >= i->second.w || y >= i->second.h || !std::isfinite(v)) - return decisionFailure(eve::DiagnosticCode::InvalidArgument, - "influence grid cell or value is invalid", "cell"); + return decisionFailure(eve::DiagnosticCode::InvalidArgument, "influence grid cell or value is invalid", + "cell"); auto& z = i->second.values[size_t(y) * i->second.w + x]; if (!std::isfinite(z + v)) - return decisionFailure(eve::DiagnosticCode::Failed, - "influence grid cell update overflowed", "cell"); + return decisionFailure(eve::DiagnosticCode::Failed, "influence grid cell update overflowed", "cell"); z += v; return decisionApplied(); } @@ -277,13 +266,13 @@ eve::Result DecisionContext::restoreJson(const std::string& j) { std::string e; auto d = eve::json::Document::parse(j, &e); if (!d.valid()) - return decisionFailure(eve::DiagnosticCode::ParseError, - e.empty() ? "invalid decision snapshot JSON" : e, "snapshot"); + return decisionFailure(eve::DiagnosticCode::ParseError, e.empty() ? "invalid decision snapshot JSON" : e, + "snapshot"); if (!d.root().isObject()) return snapshotFailure("snapshot", "decision snapshot must be a JSON object"); const auto version = d.root().get("version"); if (!version.isIntegerLiteral() || version.asInt() != 1) - return decisionFailure(eve::DiagnosticCode::UnknownVersion, - "unsupported decision snapshot version", "version"); + return decisionFailure(eve::DiagnosticCode::UnknownVersion, "unsupported decision snapshot version", + "version"); DecisionContext n; auto b = d.root().get("boards"); if (!b.isObject()) return snapshotFailure("boards", "decision snapshot boards must be an object"); @@ -292,8 +281,8 @@ eve::Result DecisionContext::restoreJson(const std::string& j) { if (!v.isObject()) return snapshotFailure("boards." + bn, "blackboard must be an object"); for (auto& k : v.keys()) { const auto value = v.get(k.c_str()); - if (!scalar(value)) return snapshotFailure("boards." + bn + "." + k, - "blackboard value must be a JSON scalar"); + if (!scalar(value)) + return snapshotFailure("boards." + bn + "." + k, "blackboard value must be a JSON scalar"); auto result = n.set(bn, k, canon(value)); if (!result.ok()) return eve::Result::failure(result.status()); } @@ -329,12 +318,12 @@ eve::Result DecisionContext::restoreJson(const std::string& j) { int w = v.getInt("w"), h = v.getInt("h"); const auto name = v.get("name"); const auto cell = v.get("cell"); - const auto ox = v.get("ox"); - const auto oy = v.get("oy"); + const auto ox = v.get("ox"); + const auto oy = v.get("oy"); if (!name.isString() || !cell.isNumber() || !ox.isNumber() || !oy.isNumber()) return snapshotFailure("grids[" + std::to_string(i) + "]", "influence grid fields are malformed"); - auto grid = n.newGrid(name.asString(), w, h, float(cell.asDouble()), float(ox.asDouble()), - float(oy.asDouble())); + auto grid = + n.newGrid(name.asString(), w, h, float(cell.asDouble()), float(ox.asDouble()), float(oy.asDouble())); if (!grid.ok()) return eve::Result::failure(grid.status()); auto vals = v.get("values"); if (!vals.isArray() || vals.size() != size_t(w) * h) @@ -345,8 +334,7 @@ eve::Result DecisionContext::restoreJson(const std::string& j) { if (!value.isNumber() || !std::isfinite(value.asDouble())) return snapshotFailure("grids[" + std::to_string(i) + "].values[" + std::to_string(z) + "]", "influence grid cell must be a finite number"); - auto result = n.setCell(name.asString(), int(z % size_t(w)), int(z / size_t(w)), - float(value.asDouble())); + auto result = n.setCell(name.asString(), int(z % size_t(w)), int(z / size_t(w)), float(value.asDouble())); if (!result.ok()) return eve::Result::failure(result.status()); } } @@ -361,8 +349,7 @@ eve::Result Decision::newContext() { return module->contexts_.emplace(std::make_unique()); } -eve::script::Borrowed Decision::resolve( - DecisionContextHandleRef reference) noexcept { +eve::script::Borrowed Decision::resolve(DecisionContextHandleRef reference) noexcept { Decision* module = ModuleManager::getInstance("Decision"); if (!module) return {}; return module->contexts_.resolve(reference); @@ -371,8 +358,8 @@ eve::script::Borrowed Decision::resolve( eve::Result Decision::release(DecisionContextHandleRef reference) { Decision* module = ModuleManager::getInstance("Decision"); if (!module) - return decisionFailure(eve::DiagnosticCode::StaleHandle, - "Decision module is no longer loaded", "context"); + return decisionFailure(eve::DiagnosticCode::StaleHandle, "Decision module is no longer loaded", + "context"); return module->contexts_.erase(reference); } @@ -387,67 +374,62 @@ void Decision::expose(ssq::Table& t) { const HSQUIRRELVM vm = t.getHandle(); auto x = t.addClass("DecisionContext", std::function([]() { return nullptr; }), false); - x.addFunc("set", [vm](DecisionContext* value, const std::string& board, - const std::string& key, const std::string& json) { - if (!value) - return eve::script::projectResult( - vm, decisionFailure(eve::DiagnosticCode::InvalidArgument, - "decision context must not be null", "context")); - return eve::script::projectResult(vm, value->set(board, key, json)); - }); + x.addFunc("set", + [vm](DecisionContext* value, const std::string& board, const std::string& key, const std::string& json) { + if (!value) + return eve::script::projectResult( + vm, decisionFailure(eve::DiagnosticCode::InvalidArgument, + "decision context must not be null", "context")); + return eve::script::projectResult(vm, value->set(board, key, json)); + }); x.addFunc("get", &DecisionContext::get); - x.addFunc("addTransition", [vm](DecisionContext* value, const std::string& machine, - const std::string& from, const std::string& trigger, - const std::string& to) { + x.addFunc("addTransition", [vm](DecisionContext* value, const std::string& machine, const std::string& from, + const std::string& trigger, const std::string& to) { if (!value) return eve::script::projectResult( - vm, decisionFailure(eve::DiagnosticCode::InvalidArgument, - "decision context must not be null", "context")); + vm, decisionFailure(eve::DiagnosticCode::InvalidArgument, "decision context must not be null", + "context")); return eve::script::projectResult(vm, value->addTransition(machine, from, trigger, to)); }); - x.addFunc("setState", [vm](DecisionContext* value, const std::string& machine, - const std::string& state) { + x.addFunc("setState", [vm](DecisionContext* value, const std::string& machine, const std::string& state) { if (!value) return eve::script::projectResult( - vm, decisionFailure(eve::DiagnosticCode::InvalidArgument, - "decision context must not be null", "context")); + vm, decisionFailure(eve::DiagnosticCode::InvalidArgument, "decision context must not be null", + "context")); return eve::script::projectResult(vm, value->setState(machine, state)); }); - x.addFunc("trigger", [vm](DecisionContext* value, const std::string& machine, - const std::string& trigger) { + x.addFunc("trigger", [vm](DecisionContext* value, const std::string& machine, const std::string& trigger) { if (!value) - return eve::script::projectResult( - vm, decisionFailure(eve::DiagnosticCode::InvalidArgument, - "decision context must not be null", "context"), - [](bool fired) { return eve::Value(fired); }); + return eve::script::projectResult(vm, + decisionFailure(eve::DiagnosticCode::InvalidArgument, + "decision context must not be null", "context"), + [](bool fired) { return eve::Value(fired); }); return eve::script::projectResult(vm, value->trigger(machine, trigger), [](bool fired) { return eve::Value(fired); }); }); x.addFunc("state", &DecisionContext::state); x.addFunc("utility", [](DecisionContext*, const std::string& s) { return DecisionContext::utility(s); }); x.addFunc("choose", [](DecisionContext*, const std::string& s) { return DecisionContext::choose(s); }); - x.addFunc("newGrid", [vm](DecisionContext* value, const std::string& name, - int width, int height, float cellSize, float originX, float originY) { + x.addFunc("newGrid", [vm](DecisionContext* value, const std::string& name, int width, int height, float cellSize, + float originX, float originY) { if (!value) return eve::script::projectResult( - vm, decisionFailure(eve::DiagnosticCode::InvalidArgument, - "decision context must not be null", "context")); + vm, decisionFailure(eve::DiagnosticCode::InvalidArgument, "decision context must not be null", + "context")); return eve::script::projectResult(vm, value->newGrid(name, width, height, cellSize, originX, originY)); }); - x.addFunc("setCell", [vm](DecisionContext* value, const std::string& name, - int x, int y, float cell) { + x.addFunc("setCell", [vm](DecisionContext* value, const std::string& name, int x, int y, float cell) { if (!value) return eve::script::projectResult( - vm, decisionFailure(eve::DiagnosticCode::InvalidArgument, - "decision context must not be null", "context")); + vm, decisionFailure(eve::DiagnosticCode::InvalidArgument, "decision context must not be null", + "context")); return eve::script::projectResult(vm, value->setCell(name, x, y, cell)); }); - x.addFunc("addCell", [vm](DecisionContext* value, const std::string& name, - int x, int y, float delta) { + x.addFunc("addCell", [vm](DecisionContext* value, const std::string& name, int x, int y, float delta) { if (!value) return eve::script::projectResult( - vm, decisionFailure(eve::DiagnosticCode::InvalidArgument, - "decision context must not be null", "context")); + vm, decisionFailure(eve::DiagnosticCode::InvalidArgument, "decision context must not be null", + "context")); return eve::script::projectResult(vm, value->addCell(name, x, y, delta)); }); x.addFunc("sample", &DecisionContext::sample); @@ -455,13 +437,12 @@ void Decision::expose(ssq::Table& t) { x.addFunc("restoreJson", [vm](DecisionContext* value, const std::string& json) { if (!value) return eve::script::projectResult( - vm, decisionFailure(eve::DiagnosticCode::InvalidArgument, - "decision context must not be null", "context")); + vm, decisionFailure(eve::DiagnosticCode::InvalidArgument, "decision context must not be null", + "context")); return eve::script::projectResult(vm, value->restoreJson(json)); }); auto owned = t.addClass( - "DecisionContextProxy", - std::function([] { return nullptr; }), true); + "DecisionContextProxy", std::function([] { return nullptr; }), true); owned.addFunc("ownership", [](ScriptDecisionContext*) { return std::string("owned"); }); owned.addFunc("ownerEpoch", [](ScriptDecisionContext* value) { return value ? static_cast(value->reference.ownerEpoch) : int64_t{0}; @@ -469,9 +450,8 @@ void Decision::expose(ssq::Table& t) { owned.addFunc("handle", [](ScriptDecisionContext* value) { return value ? static_cast(value->reference.packed()) : int64_t{0}; }); - owned.addFunc("isStale", [](ScriptDecisionContext* value) { - return !value || Decision::isStale(value->reference); - }); + owned.addFunc("isStale", + [](ScriptDecisionContext* value) { return !value || Decision::isStale(value->reference); }); owned.addFunc("release", [vm](ScriptDecisionContext* value) { if (!value) return eve::script::projectResult( @@ -479,8 +459,7 @@ void Decision::expose(ssq::Table& t) { "owned decision context proxy must not be null", "context")); return eve::script::projectResult(vm, Decision::release(value->reference)); }); - owned.addFunc("setState", [vm](ScriptDecisionContext* value, const std::string& machine, - const std::string& state) { + owned.addFunc("setState", [vm](ScriptDecisionContext* value, const std::string& machine, const std::string& state) { if (!value) return eve::script::projectResult( vm, decisionFailure(eve::DiagnosticCode::InvalidArgument, @@ -488,109 +467,95 @@ void Decision::expose(ssq::Table& t) { auto view = Decision::resolve(value->reference); if (!view.isBound()) return eve::script::projectResult( - vm, decisionFailure(eve::DiagnosticCode::StaleHandle, - "owned decision context handle is stale", "context")); + vm, decisionFailure(eve::DiagnosticCode::StaleHandle, "owned decision context handle is stale", + "context")); return eve::script::projectResult(vm, view->setState(machine, state)); }); - owned.addFunc("set", [vm](ScriptDecisionContext* value, const std::string& board, - const std::string& key, const std::string& json) { - auto view = value ? Decision::resolve(value->reference) - : eve::script::Borrowed(); + owned.addFunc("set", [vm](ScriptDecisionContext* value, const std::string& board, const std::string& key, + const std::string& json) { + auto view = value ? Decision::resolve(value->reference) : eve::script::Borrowed(); if (!view.isBound()) return eve::script::projectResult( - vm, decisionFailure(eve::DiagnosticCode::StaleHandle, - "owned decision context handle is stale", "context")); + vm, decisionFailure(eve::DiagnosticCode::StaleHandle, "owned decision context handle is stale", + "context")); return eve::script::projectResult(vm, view->set(board, key, json)); }); - owned.addFunc("get", [](ScriptDecisionContext* value, const std::string& board, - const std::string& key, const std::string& fallback) { - auto view = value ? Decision::resolve(value->reference) - : eve::script::Borrowed(); + owned.addFunc("get", [](ScriptDecisionContext* value, const std::string& board, const std::string& key, + const std::string& fallback) { + auto view = value ? Decision::resolve(value->reference) : eve::script::Borrowed(); return view.isBound() ? view->get(board, key, fallback) : fallback; }); owned.addFunc("addTransition", [vm](ScriptDecisionContext* value, const std::string& machine, - const std::string& from, const std::string& trigger, - const std::string& to) { - auto view = value ? Decision::resolve(value->reference) - : eve::script::Borrowed(); + const std::string& from, const std::string& trigger, const std::string& to) { + auto view = value ? Decision::resolve(value->reference) : eve::script::Borrowed(); if (!view.isBound()) return eve::script::projectResult( - vm, decisionFailure(eve::DiagnosticCode::StaleHandle, - "owned decision context handle is stale", "context")); + vm, decisionFailure(eve::DiagnosticCode::StaleHandle, "owned decision context handle is stale", + "context")); return eve::script::projectResult(vm, view->addTransition(machine, from, trigger, to)); }); - owned.addFunc("trigger", [vm](ScriptDecisionContext* value, const std::string& machine, - const std::string& trigger) { - auto view = value ? Decision::resolve(value->reference) - : eve::script::Borrowed(); - if (!view.isBound()) - return eve::script::projectResult( - vm, decisionFailure(eve::DiagnosticCode::StaleHandle, - "owned decision context handle is stale", "context"), - [](bool fired) { return eve::Value(fired); }); - return eve::script::projectResult(vm, view->trigger(machine, trigger), - [](bool fired) { return eve::Value(fired); }); - }); + owned.addFunc( + "trigger", [vm](ScriptDecisionContext* value, const std::string& machine, const std::string& trigger) { + auto view = value ? Decision::resolve(value->reference) : eve::script::Borrowed(); + if (!view.isBound()) + return eve::script::projectResult( + vm, + decisionFailure(eve::DiagnosticCode::StaleHandle, "owned decision context handle is stale", + "context"), + [](bool fired) { return eve::Value(fired); }); + return eve::script::projectResult(vm, view->trigger(machine, trigger), + [](bool fired) { return eve::Value(fired); }); + }); owned.addFunc("state", [](ScriptDecisionContext* value, const std::string& machine) { auto view = value ? Decision::resolve(value->reference) : eve::script::Borrowed(); return view.isBound() ? view->state(machine) : std::string{}; }); - owned.addFunc("newGrid", [vm](ScriptDecisionContext* value, const std::string& name, - int width, int height, float cellSize, float originX, float originY) { - auto view = value ? Decision::resolve(value->reference) - : eve::script::Borrowed(); + owned.addFunc("newGrid", [vm](ScriptDecisionContext* value, const std::string& name, int width, int height, + float cellSize, float originX, float originY) { + auto view = value ? Decision::resolve(value->reference) : eve::script::Borrowed(); if (!view.isBound()) return eve::script::projectResult( - vm, decisionFailure(eve::DiagnosticCode::StaleHandle, - "owned decision context handle is stale", "context")); + vm, decisionFailure(eve::DiagnosticCode::StaleHandle, "owned decision context handle is stale", + "context")); return eve::script::projectResult(vm, view->newGrid(name, width, height, cellSize, originX, originY)); }); - owned.addFunc("setCell", [vm](ScriptDecisionContext* value, const std::string& name, - int x, int y, float cell) { - auto view = value ? Decision::resolve(value->reference) - : eve::script::Borrowed(); + owned.addFunc("setCell", [vm](ScriptDecisionContext* value, const std::string& name, int x, int y, float cell) { + auto view = value ? Decision::resolve(value->reference) : eve::script::Borrowed(); if (!view.isBound()) return eve::script::projectResult( - vm, decisionFailure(eve::DiagnosticCode::StaleHandle, - "owned decision context handle is stale", "context")); + vm, decisionFailure(eve::DiagnosticCode::StaleHandle, "owned decision context handle is stale", + "context")); return eve::script::projectResult(vm, view->setCell(name, x, y, cell)); }); - owned.addFunc("addCell", [vm](ScriptDecisionContext* value, const std::string& name, - int x, int y, float delta) { - auto view = value ? Decision::resolve(value->reference) - : eve::script::Borrowed(); + owned.addFunc("addCell", [vm](ScriptDecisionContext* value, const std::string& name, int x, int y, float delta) { + auto view = value ? Decision::resolve(value->reference) : eve::script::Borrowed(); if (!view.isBound()) return eve::script::projectResult( - vm, decisionFailure(eve::DiagnosticCode::StaleHandle, - "owned decision context handle is stale", "context")); + vm, decisionFailure(eve::DiagnosticCode::StaleHandle, "owned decision context handle is stale", + "context")); return eve::script::projectResult(vm, view->addCell(name, x, y, delta)); }); - owned.addFunc("sample", [](ScriptDecisionContext* value, const std::string& name, - float worldX, float worldY, float fallback) { - auto view = value ? Decision::resolve(value->reference) - : eve::script::Borrowed(); + owned.addFunc("sample", [](ScriptDecisionContext* value, const std::string& name, float worldX, float worldY, + float fallback) { + auto view = value ? Decision::resolve(value->reference) : eve::script::Borrowed(); return view.isBound() ? view->sample(name, worldX, worldY, fallback) : fallback; }); owned.addFunc("snapshotJson", [](ScriptDecisionContext* value) { - auto view = value ? Decision::resolve(value->reference) - : eve::script::Borrowed(); + auto view = value ? Decision::resolve(value->reference) : eve::script::Borrowed(); return view.isBound() ? view->snapshotJson() : std::string{}; }); owned.addFunc("restoreJson", [vm](ScriptDecisionContext* value, const std::string& json) { - auto view = value ? Decision::resolve(value->reference) - : eve::script::Borrowed(); + auto view = value ? Decision::resolve(value->reference) : eve::script::Borrowed(); if (!view.isBound()) return eve::script::projectResult( - vm, decisionFailure(eve::DiagnosticCode::StaleHandle, - "owned decision context handle is stale", "context")); + vm, decisionFailure(eve::DiagnosticCode::StaleHandle, "owned decision context handle is stale", + "context")); return eve::script::projectResult(vm, view->restoreJson(json)); }); - owned.addFunc("utility", [](ScriptDecisionContext*, const std::string& input) { - return DecisionContext::utility(input); - }); - owned.addFunc("choose", [](ScriptDecisionContext*, const std::string& input) { - return DecisionContext::choose(input); - }); + owned.addFunc("utility", + [](ScriptDecisionContext*, const std::string& input) { return DecisionContext::utility(input); }); + owned.addFunc("choose", + [](ScriptDecisionContext*, const std::string& input) { return DecisionContext::choose(input); }); auto c = t.addClass(name, Decision::create, false); expose(c); } @@ -598,8 +563,7 @@ void Decision::expose(ssq::Class& c) { c.addFunc("getName", &Decision::getName); c.addFunc("newContext", [vm = c.getHandle()](Decision*) -> ssq::Table { return makeOwnedProxy( - vm, Decision::newContext(), - [](DecisionContextHandleRef ref) { return Decision::release(ref); }); + vm, Decision::newContext(), [](DecisionContextHandleRef ref) { return Decision::release(ref); }); }); } } // namespace eve::decision diff --git a/src/modules/decision/Decision.h b/src/modules/decision/Decision.h index 1eb0738b..fdb79947 100644 --- a/src/modules/decision/Decision.h +++ b/src/modules/decision/Decision.h @@ -19,8 +19,7 @@ public: * @return Success when the value is valid and the board entry was written; * otherwise a structured validation diagnostic. */ - [[nodiscard]] eve::Result set( - const std::string& board, const std::string& key, const std::string& valueJson); + [[nodiscard]] eve::Result set(const std::string& board, const std::string& key, const std::string& valueJson); /** @brief Gets canonical scalar JSON or the supplied fallback. */ std::string get( const std::string& board, const std::string& key, const std::string& fallbackJson) const; /** @@ -28,10 +27,8 @@ public: * @return Success when the transition was stored; invalid names return a * structured validation diagnostic. */ - [[nodiscard]] eve::Result addTransition(const std::string& machine, - const std::string& from, - const std::string& trigger, - const std::string& to); + [[nodiscard]] eve::Result addTransition(const std::string& machine, const std::string& from, + const std::string& trigger, const std::string& to); /** * @brief Sets the current state explicitly. * @return Success when the state was stored, otherwise a validation diagnostic. @@ -43,8 +40,7 @@ public: * or `false`/`NoOp` when no transition matches. Missing machines and * invalid arguments are reported as structured failures. */ - [[nodiscard]] eve::Result trigger( - const std::string& machine, const std::string& trigger); + [[nodiscard]] eve::Result trigger(const std::string& machine, const std::string& trigger); /** @brief Gets current machine state. */ std::string state(const std::string& machine) const; /** @brief Scores CSV considerations formatted value:weight using a weighted mean. */ static float utility( const std::string& considerationsCsv); @@ -88,12 +84,12 @@ public: /** @brief Allocates a module-owned decision context and returns its handle. */ [[nodiscard]] static eve::Result newContext(); /** @brief Resolves a live context as a non-owning observation. */ - [[nodiscard]] static eve::script::Borrowed resolve( - DecisionContextHandleRef reference) noexcept; + [[nodiscard]] static eve::script::Borrowed resolve(DecisionContextHandleRef reference) noexcept; /** @brief Releases a module-owned decision context. */ [[nodiscard]] static eve::Result release(DecisionContextHandleRef reference); /** @brief Reports whether a decision context reference is stale. */ [[nodiscard]] static bool isStale(DecisionContextHandleRef reference) noexcept; + private: eve::script::RuntimeObjectRegistry contexts_; }; diff --git a/src/modules/definitions/Definitions.cpp b/src/modules/definitions/Definitions.cpp index 53d27e4a..765a819e 100644 --- a/src/modules/definitions/Definitions.cpp +++ b/src/modules/definitions/Definitions.cpp @@ -14,11 +14,92 @@ namespace eve::definitions { namespace { +eve::schema::FieldDefinition schemaField(std::string name, eve::schema::ValueType type, bool required = true) { + eve::schema::FieldDefinition result; + result.name = std::move(name); + result.type = type; + result.required = required; + return result; +} + +eve::schema::SchemaNode arrayOfObject(std::vector fields) { + eve::schema::SchemaDefinition object; + object.additionalProperties = false; + object.fields = std::move(fields); + eve::schema::SchemaNode item; + item.type = eve::schema::ValueType::Object; + item.objectSchema = std::make_shared(std::move(object)); + eve::schema::SchemaNode array; + array.type = eve::schema::ValueType::Array; + array.itemSchema = std::make_shared(std::move(item)); + return array; +} + +eve::schema::SchemaDefinition definitionsSnapshotSchema() { + eve::schema::SchemaDefinition schema; + schema.id = "definitions:registry"; + schema.version = 1; + schema.title = "Definition Registry Snapshot"; + schema.description = "Persistent state owned by DefinitionRegistry."; + schema.additionalProperties = false; + auto definitions = schemaField("definitions", eve::schema::ValueType::Array); + static_cast(definitions) = arrayOfObject({ + schemaField("type", eve::schema::ValueType::String), + schemaField("id", eve::schema::ValueType::String), + schemaField("version", eve::schema::ValueType::Integer), + schemaField("generation", eve::schema::ValueType::String), + schemaField("json", eve::schema::ValueType::Any), + }); + definitions.name = "definitions"; + definitions.required = true; + auto generations = schemaField("generations", eve::schema::ValueType::Array); + static_cast(generations) = arrayOfObject({ + schemaField("type", eve::schema::ValueType::String), + schemaField("id", eve::schema::ValueType::String), + schemaField("generation", eve::schema::ValueType::String), + }); + generations.name = "generations"; + generations.required = true; + auto events = schemaField("events", eve::schema::ValueType::Array); + static_cast(events) = arrayOfObject({ + schemaField("sequence", eve::schema::ValueType::String), + schemaField("name", eve::schema::ValueType::String), + schemaField("type", eve::schema::ValueType::String), + schemaField("id", eve::schema::ValueType::String), + schemaField("version", eve::schema::ValueType::Integer), + schemaField("generation", eve::schema::ValueType::String), + }); + events.name = "events"; + events.required = true; + schema.fields = { + schemaField("version", eve::schema::ValueType::Integer), + std::move(definitions), + std::move(generations), + std::move(events), + schemaField("nextEventSequence", eve::schema::ValueType::String), + }; + return schema; +} + +eve::Result validateDefinitionsSnapshot(const std::string& json) { + constexpr auto id = "definitions:registry"; + if (!eve::schema::SchemaRegistry::resolve(id, 1)) { + auto registration = eve::schema::SchemaRegistry::registerVersioned(definitionsSnapshotSchema()); + if (!registration.ok()) return eve::Result::failure(registration.status()); + } + const auto errors = eve::schema::SchemaRegistry::validate(id, 1, json); + if (errors.empty()) return eve::Result::success(); + const auto& error = errors.front(); + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::ParseError, error.message, error.path, + eve::DiagnosticDetails{{"schemaId", id}, {"schemaVersion", "1"}, {"validationCode", error.code}}, + "definitions.snapshot.schema")); +} + template -eve::Result definitionsBindingFailure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - code, std::move(message), std::move(path), {}, "definitions.squirrel")); +eve::Result definitionsBindingFailure(eve::DiagnosticCode code, std::string message, std::string path = {}) { + return eve::Result::failure( + eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "definitions.squirrel")); } eve::Value definitionHandleProjection(const DefinitionHandle& handle) { @@ -53,7 +134,7 @@ const eve::Value* field(const eve::Value::Object& object, std::string_view name) bool readString(const eve::Value::Object& object, std::string_view name, std::string& output) { const auto* value = field(object, name); - const auto* text = value ? value->getIf() : nullptr; + const auto* text = value ? value->getIf() : nullptr; if (!text) return false; output = *text; return true; @@ -101,17 +182,17 @@ DefinitionRef makeDefinitionReference(std::string_view type, std::string_view id const eve::SnapshotMigrationChain& definitionsMigrations() { static const eve::SnapshotMigrationChain chain = [] { eve::SnapshotMigrationChain result; - const auto registration = result.add( - definitionsSchema(), eve::SchemaVersion(0), eve::SchemaVersion(1), - [](const eve::Value& payload) -> eve::Result { - const auto* object = payload.getIf(); - if (!object) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "definitions snapshot payload must be an object")); - eve::Value::Object migrated = *object; - migrated["version"] = eve::Value(std::int64_t(1)); - return eve::Result::success(eve::Value(std::move(migrated))); - }); + const auto registration = + result.add(definitionsSchema(), eve::SchemaVersion(0), eve::SchemaVersion(1), + [](const eve::Value& payload) -> eve::Result { + const auto* object = payload.getIf(); + if (!object) + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::ParseError, "definitions snapshot payload must be an object")); + eve::Value::Object migrated = *object; + migrated["version"] = eve::Value(std::int64_t(1)); + return eve::Result::success(eve::Value(std::move(migrated))); + }); if (!registration.ok()) std::terminate(); return result; }(); @@ -121,18 +202,16 @@ const eve::SnapshotMigrationChain& definitionsMigrations() { eve::Result makeDefinition(const std::string& type, const std::string& id, int version, const std::string& json) { if (type.empty() || id.empty()) - return failure(eve::DiagnosticCode::InvalidArgument, - "definition type and id must not be empty"); + return failure(eve::DiagnosticCode::InvalidArgument, "definition type and id must not be empty"); if (!eve::LogicalId::fromParts(type, id)) return failure(eve::DiagnosticCode::InvalidArgument, "definition type and id must form a valid namespace:name reference"); if (version <= 0) - return failure(eve::DiagnosticCode::InvalidArgument, - "definition schema version must be positive"); + return failure(eve::DiagnosticCode::InvalidArgument, "definition schema version must be positive"); auto parsed = eve::Value::fromJson(json); if (!parsed.ok()) return eve::Result::failure(parsed.status()); - auto value = std::move(parsed).takeValue(); + auto value = std::move(parsed).takeValue(); auto canonical = value.toJson(); if (!canonical.ok()) return eve::Result::failure(canonical.status()); @@ -142,18 +221,17 @@ eve::Result makeDefinition(const std::string& type, const std::strin const auto& error = errors.front(); return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::InvalidArgument, error.message, error.path, - eve::DiagnosticDetails{{"schemaId", type}, - {"schemaVersion", std::to_string(version)}, - {"validationCode", error.code}}, + eve::DiagnosticDetails{ + {"schemaId", type}, {"schemaVersion", std::to_string(version)}, {"validationCode", error.code}}, "definitions.schema")); } } Definition definition; - definition.type = type; - definition.id = id; + definition.type = type; + definition.id = id; definition.version = eve::SchemaVersion(static_cast(version)); - definition.json = std::move(canonical).takeValue(); + definition.json = std::move(canonical).takeValue(); return eve::Result::success(std::move(definition)); } @@ -166,11 +244,10 @@ DefinitionRegistry::DefinitionRegistry(eve::PersistentId instanceId) DefinitionEvent DefinitionRegistry::projectEvent(const Storage::Event& event) { DefinitionEvent result; result.sequence = event.sequence; - result.name = event.operation == eve::RegistryOperation::Remove ? "definition_removed" - : "definition_reloaded"; - result.type = event.key.first; - result.id = event.key.second; - result.version = event.data.version; + result.name = event.operation == eve::RegistryOperation::Remove ? "definition_removed" : "definition_reloaded"; + result.type = event.key.first; + result.id = event.key.second; + result.version = event.data.version; result.generation = event.generation; return result; } @@ -181,45 +258,44 @@ DefinitionHandle DefinitionRegistry::projectHandle(const Storage::Handle& handle DefinitionRegistry::Storage::Handle DefinitionRegistry::storageHandle(const DefinitionHandle& handle) { const auto& logical = handle.reference.id(); - return Storage::Handle{Key{std::string(logical.namespaceName()), std::string(logical.name())}, - handle.generation}; + return Storage::Handle{Key{std::string(logical.namespaceName()), std::string(logical.name())}, handle.generation}; } void DefinitionRegistry::clearEventProjection() const { eventProjection_.clear(); } -eve::Result DefinitionRegistry::insert( - const std::string& type, const std::string& id, int version, const std::string& json) { +eve::Result DefinitionRegistry::insert(const std::string& type, const std::string& id, int version, + const std::string& json) { auto definition = makeDefinition(type, id, version, json); if (!definition.ok()) { return eve::Result::failure(definition.status()); } - Definition value = std::move(definition).takeValue(); + Definition value = std::move(definition).takeValue(); const EventData data{value.version}; - auto result = storage_.insert(Key{type, id}, std::move(value), data); + auto result = storage_.insert(Key{type, id}, std::move(value), data); if (!result.ok()) { return eve::Result::failure(result.status()); } const eve::Status mutationStatus = result.status(); - const auto handle = std::move(result).takeValue(); + const auto handle = std::move(result).takeValue(); clearEventProjection(); if (const auto next = revision_.incremented()) revision_ = *next; return eve::Result::success(projectHandle(handle), mutationStatus); } -eve::Result DefinitionRegistry::replace( - const std::string& type, const std::string& id, int version, const std::string& json) { +eve::Result DefinitionRegistry::replace(const std::string& type, const std::string& id, int version, + const std::string& json) { auto definition = makeDefinition(type, id, version, json); if (!definition.ok()) { return eve::Result::failure(definition.status()); } - Definition value = std::move(definition).takeValue(); + Definition value = std::move(definition).takeValue(); const EventData data{value.version}; - auto result = storage_.replace(Key{type, id}, std::move(value), data); + auto result = storage_.replace(Key{type, id}, std::move(value), data); if (!result.ok()) { return eve::Result::failure(result.status()); } const eve::Status mutationStatus = result.status(); - const auto handle = std::move(result).takeValue(); + const auto handle = std::move(result).takeValue(); clearEventProjection(); if (const auto next = revision_.incremented()) revision_ = *next; return eve::Result::success(projectHandle(handle), mutationStatus); @@ -227,45 +303,41 @@ eve::Result DefinitionRegistry::replace( eve::Result DefinitionRegistry::remove(const std::string& type, const std::string& id) { const Key key{type, id}; - auto current = storage_.resolve(key); + auto current = storage_.resolve(key); if (!current.ok()) { return eve::Result::failure(current.status()); } const EventData data{current.value().get().version}; - auto result = storage_.remove(key, data); + auto result = storage_.remove(key, data); if (!result.ok()) { return eve::Result::failure(result.status()); } const eve::Status mutationStatus = result.status(); - const auto handle = std::move(result).takeValue(); + const auto handle = std::move(result).takeValue(); clearEventProjection(); if (const auto next = revision_.incremented()) revision_ = *next; return eve::Result::success(projectHandle(handle), mutationStatus); } -eve::ResultRef DefinitionRegistry::resolve( - const std::string& type, const std::string& id) const { +eve::ResultRef DefinitionRegistry::resolve(const std::string& type, const std::string& id) const { auto result = storage_.resolve(Key{type, id}); if (!result.ok()) return eve::ResultRef::failure(result.status()); return eve::ResultRef::success(std::cref(result.value().get())); } -eve::ResultRef DefinitionRegistry::resolveHandle( - const DefinitionHandle& handle) const { +eve::ResultRef DefinitionRegistry::resolveHandle(const DefinitionHandle& handle) const { auto result = storage_.resolve(storageHandle(handle)); if (!result.ok()) return eve::ResultRef::failure(result.status()); return eve::ResultRef::success(std::cref(result.value().get())); } -eve::Result DefinitionRegistry::handle( - const std::string& type, const std::string& id) const { +eve::Result DefinitionRegistry::handle(const std::string& type, const std::string& id) const { auto result = storage_.handle(Key{type, id}); if (!result.ok()) return eve::Result::failure(result.status()); return eve::Result::success(projectHandle(result.value())); } -eve::Result DefinitionRegistry::generationOf( - const std::string& type, const std::string& id) const { +eve::Result DefinitionRegistry::generationOf(const std::string& type, const std::string& id) const { return storage_.generationOf(Key{type, id}); } @@ -330,7 +402,7 @@ void DefinitionRegistry::clearEvents() { } std::string DefinitionRegistry::snapshotJson() const { - const auto state = storage_.snapshotState(); + const auto state = storage_.snapshotState(); eve::Value::Array definitions; eve::Value::Array events; eve::Value::Array generations; @@ -346,7 +418,7 @@ std::string DefinitionRegistry::snapshotJson() const { generations.emplace_back(std::move(generation)); if (!entry.value.has_value()) continue; - const auto& definition = *entry.value; + const auto& definition = *entry.value; eve::Value::Object item; item.emplace("generation", eve::Value(std::to_string(entry.generation.value()))); item.emplace("id", eve::Value(definition.id)); @@ -357,7 +429,7 @@ std::string DefinitionRegistry::snapshotJson() const { } for (const auto& raw : state.events) { - const auto event = projectEvent(raw); + const auto event = projectEvent(raw); eve::Value::Object item; item.emplace("generation", eve::Value(std::to_string(event.generation.value()))); item.emplace("id", eve::Value(event.id)); @@ -380,25 +452,27 @@ std::string DefinitionRegistry::snapshotJson() const { } eve::Result DefinitionRegistry::restoreJson(const std::string& json) { + auto schemaValidation = validateDefinitionsSnapshot(json); + if (!schemaValidation.ok()) return schemaValidation; auto parsed = eve::Value::fromJson(json); if (!parsed.ok()) { return eve::Result::failure(parsed.status()); } const eve::Value value = std::move(parsed).takeValue(); - const auto* root = value.getIf(); + const auto* root = value.getIf(); if (!root) { return failure(eve::DiagnosticCode::ParseError, "definitions snapshot must be an object"); } - const auto* snapshotVersion = field(*root, "version"); - const auto* versionNumber = snapshotVersion ? snapshotVersion->getIf() : nullptr; - const auto* definitions = field(*root, "definitions"); - const auto* generations = field(*root, "generations"); - const auto* events = field(*root, "events"); - const auto* nextSequenceValue = field(*root, "nextEventSequence"); - const auto* definitionArray = definitions ? definitions->getIf() : nullptr; - const auto* generationArray = generations ? generations->getIf() : nullptr; - const auto* eventArray = events ? events->getIf() : nullptr; - std::uint64_t nextSequence = 0; + const auto* snapshotVersion = field(*root, "version"); + const auto* versionNumber = snapshotVersion ? snapshotVersion->getIf() : nullptr; + const auto* definitions = field(*root, "definitions"); + const auto* generations = field(*root, "generations"); + const auto* events = field(*root, "events"); + const auto* nextSequenceValue = field(*root, "nextEventSequence"); + const auto* definitionArray = definitions ? definitions->getIf() : nullptr; + const auto* generationArray = generations ? generations->getIf() : nullptr; + const auto* eventArray = events ? events->getIf() : nullptr; + std::uint64_t nextSequence = 0; if (!versionNumber || *versionNumber != 1 || !definitionArray || !generationArray || !eventArray || !nextSequenceValue || !readUint64String(*nextSequenceValue, nextSequence) || nextSequence == 0) { return failure(eve::DiagnosticCode::ParseError, "invalid definitions snapshot fields"); @@ -406,30 +480,29 @@ eve::Result DefinitionRegistry::restoreJson(const std::string& json) { Storage::State candidate; for (std::size_t index = 0; index < generationArray->size(); ++index) { - const auto* item = (*generationArray)[index].getIf(); - std::string type; - std::string id; - std::uint64_t generation = 0; - const auto* generationValue = item ? field(*item, "generation") : nullptr; + const auto* item = (*generationArray)[index].getIf(); + std::string type; + std::string id; + std::uint64_t generation = 0; + const auto* generationValue = item ? field(*item, "generation") : nullptr; if (!item || !readString(*item, "type", type) || !readString(*item, "id", id) || type.empty() || id.empty() || !generationValue || !readUint64String(*generationValue, generation) || generation == 0 || - !candidate.entries.emplace(Key{type, id}, Storage::Entry{ - eve::Generation(generation), std::nullopt}) - .second) { + !candidate.entries.emplace(Key{type, id}, Storage::Entry{eve::Generation(generation), std::nullopt}) + .second) { return failure(eve::DiagnosticCode::ParseError, "invalid definition generation at index " + std::to_string(index)); } } for (std::size_t index = 0; index < definitionArray->size(); ++index) { - const auto* item = (*definitionArray)[index].getIf(); - std::string type; - std::string id; + const auto* item = (*definitionArray)[index].getIf(); + std::string type; + std::string id; eve::SchemaVersion versionValue; - std::uint64_t generation = 0; - const auto* jsonValue = item ? field(*item, "json") : nullptr; - const auto* versionField = item ? field(*item, "version") : nullptr; - const auto* generationField = item ? field(*item, "generation") : nullptr; + std::uint64_t generation = 0; + const auto* jsonValue = item ? field(*item, "json") : nullptr; + const auto* versionField = item ? field(*item, "version") : nullptr; + const auto* generationField = item ? field(*item, "generation") : nullptr; if (!item || !readString(*item, "type", type) || !readString(*item, "id", id) || type.empty() || id.empty() || !versionField || !readPositiveSchemaVersion(*versionField, versionValue) || !generationField || !readUint64String(*generationField, generation) || generation == 0 || !jsonValue) { @@ -437,7 +510,7 @@ eve::Result DefinitionRegistry::restoreJson(const std::string& json) { "invalid definition at index " + std::to_string(index)); } const Key key{type, id}; - auto entry = candidate.entries.find(key); + auto entry = candidate.entries.find(key); if (entry == candidate.entries.end() || entry->second.generation != eve::Generation(generation) || entry->second.value.has_value()) { return failure(eve::DiagnosticCode::InvariantViolation, @@ -448,45 +521,44 @@ eve::Result DefinitionRegistry::restoreJson(const std::string& json) { return eve::Result::failure(canonical.status()); } Definition definition; - definition.type = type; - definition.id = id; - definition.version = versionValue; + definition.type = type; + definition.id = id; + definition.version = versionValue; definition.generation = eve::Generation(generation); - definition.json = std::move(canonical).takeValue(); - entry->second.value = std::move(definition); + definition.json = std::move(canonical).takeValue(); + entry->second.value = std::move(definition); } eve::EventSequence previousSequence; for (std::size_t index = 0; index < eventArray->size(); ++index) { - const auto* item = (*eventArray)[index].getIf(); - std::string name; - std::string type; - std::string id; + const auto* item = (*eventArray)[index].getIf(); + std::string name; + std::string type; + std::string id; eve::SchemaVersion versionValue; - std::uint64_t sequence = 0; - std::uint64_t generation = 0; - const auto* versionField = item ? field(*item, "version") : nullptr; - const auto* sequenceField = item ? field(*item, "sequence") : nullptr; - const auto* generationField = item ? field(*item, "generation") : nullptr; + std::uint64_t sequence = 0; + std::uint64_t generation = 0; + const auto* versionField = item ? field(*item, "version") : nullptr; + const auto* sequenceField = item ? field(*item, "sequence") : nullptr; + const auto* generationField = item ? field(*item, "generation") : nullptr; if (!item || !readString(*item, "name", name) || !readString(*item, "type", type) || - !readString(*item, "id", id) || - (name != "definition_reloaded" && name != "definition_removed") || type.empty() || id.empty() || - !versionField || !readPositiveSchemaVersion(*versionField, versionValue) || !sequenceField || - !readUint64String(*sequenceField, sequence) || sequence == 0 || + !readString(*item, "id", id) || (name != "definition_reloaded" && name != "definition_removed") || + type.empty() || id.empty() || !versionField || !readPositiveSchemaVersion(*versionField, versionValue) || + !sequenceField || !readUint64String(*sequenceField, sequence) || sequence == 0 || (!previousSequence.isZero() && eve::EventSequence(sequence) <= previousSequence) || !generationField || !readUint64String(*generationField, generation) || generation == 0) { return failure(eve::DiagnosticCode::ParseError, "invalid definition event at index " + std::to_string(index)); } - const bool removed = name == "definition_removed"; + const bool removed = name == "definition_removed"; Storage::Event event; - event.sequence = eve::EventSequence(sequence); - event.operation = removed ? eve::RegistryOperation::Remove - : (generation == 1 ? eve::RegistryOperation::Insert - : eve::RegistryOperation::Replace); - event.key = Key{type, id}; - event.generation = eve::Generation(generation); - event.tombstone = removed; + event.sequence = eve::EventSequence(sequence); + event.operation = removed + ? eve::RegistryOperation::Remove + : (generation == 1 ? eve::RegistryOperation::Insert : eve::RegistryOperation::Replace); + event.key = Key{type, id}; + event.generation = eve::Generation(generation); + event.tombstone = removed; event.data.version = versionValue; candidate.events.push_back(std::move(event)); previousSequence = eve::EventSequence(sequence); @@ -503,20 +575,19 @@ eve::Result DefinitionRegistry::restoreJson(const std::string& json) { } clearEventProjection(); revision_ = eve::Revision(nextSequence - 1); - tick_ = eve::SimulationTick{}; + tick_ = eve::SimulationTick{}; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result DefinitionRegistry::snapshot( - const eve::SnapshotHashProvider& hashProvider) const { +eve::Result DefinitionRegistry::snapshot(const eve::SnapshotHashProvider& hashProvider) const { auto payload = eve::Value::fromJson(snapshotJson()); if (!payload.ok()) return eve::Result::failure(payload.status()); return eve::makeSnapshotEnvelope("definitions.registry", definitionsSchema(), eve::SchemaVersion(1), instanceId_, revision_, tick_, std::move(payload).takeValue(), hashProvider); } -eve::Result DefinitionRegistry::restoreSnapshot( - const eve::SnapshotEnvelope& source, const eve::SnapshotHashProvider& hashProvider) { +eve::Result DefinitionRegistry::restoreSnapshot(const eve::SnapshotEnvelope& source, + const eve::SnapshotHashProvider& hashProvider) { if (source.type != "definitions.registry" || source.schema != definitionsSchema()) return failure(eve::DiagnosticCode::InvalidArgument, "snapshot does not belong to definitions::DefinitionRegistry"); @@ -527,7 +598,7 @@ eve::Result DefinitionRegistry::restoreSnapshot( auto migrated = definitionsMigrations().migrate(source, eve::SchemaVersion(1), hashProvider); if (!migrated.ok()) return eve::Result::failure(migrated.status()); const auto& candidate = migrated.value(); - auto metadata = eve::validateSnapshotPayloadMetadata(candidate.payload, candidate.revision, candidate.tick); + auto metadata = eve::validateSnapshotPayloadMetadata(candidate.payload, candidate.revision, candidate.tick); if (!metadata.ok()) return eve::Result::failure(metadata.status()); auto payload = candidate.payload.toJson(); if (!payload.ok()) return eve::Result::failure(payload.status()); @@ -538,21 +609,20 @@ eve::Result DefinitionRegistry::restoreSnapshot( if (!restored.ok()) return eve::Result::failure(restored.status()); instanceId_ = candidate.instanceId; - revision_ = candidate.revision; - tick_ = candidate.tick; + revision_ = candidate.revision; + tick_ = candidate.tick; return eve::Result::success(); } -eve::Result DefinitionRegistry::snapshotEnvelopeJson( - const eve::SnapshotHashProvider& hashProvider) const { +eve::Result DefinitionRegistry::snapshotEnvelopeJson(const eve::SnapshotHashProvider& hashProvider) const { auto value = snapshot(hashProvider); if (!value.ok()) return eve::Result::failure(value.status()); return std::move(value).andThen( [](eve::SnapshotEnvelope&& envelope) { return eve::serializeSnapshotEnvelope(envelope); }); } -eve::Result DefinitionRegistry::restoreSnapshotJson( - std::string_view json, const eve::SnapshotHashProvider& hashProvider) { +eve::Result DefinitionRegistry::restoreSnapshotJson(std::string_view json, + const eve::SnapshotHashProvider& hashProvider) { auto source = eve::parseSnapshotEnvelope(json, hashProvider); if (!source.ok()) return eve::Result::failure(source.status()); return restoreSnapshot(std::move(source).takeValue(), hashProvider); @@ -574,7 +644,7 @@ DefinitionRegistry* Definitions::newRegistry() { Module_IMPL(Definitions, new Definitions()); void Definitions::expose(ssq::Table& table) { - const HSQUIRRELVM vm = table.getHandle(); + const HSQUIRRELVM vm = table.getHandle(); auto definition = table.addClass( "Definition", std::function([]() -> Definition* { return nullptr; }), false); definition.addFunc("getType", [](Definition* value) { return value ? value->type : std::string{}; }); @@ -605,81 +675,74 @@ void Definitions::expose(ssq::Table& table) { auto registry = table.addClass( "DefinitionRegistry", std::function([]() -> DefinitionRegistry* { return nullptr; }), false); - registry.addFunc("insert", [vm](DefinitionRegistry* value, const std::string& type, - const std::string& id, int version, const std::string& json) { + registry.addFunc("insert", [vm](DefinitionRegistry* value, const std::string& type, const std::string& id, + int version, const std::string& json) { if (!value) return eve::script::projectResult( - vm, definitionsBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "definition registry must not be null", "registry"), + vm, + definitionsBindingFailure(eve::DiagnosticCode::InvalidArgument, + "definition registry must not be null", "registry"), [](DefinitionHandle&& handle) { return definitionHandleProjection(handle); }); - return eve::script::projectResult( - vm, value->insert(type, id, version, json), - [](DefinitionHandle&& handle) { return definitionHandleProjection(handle); }); + return eve::script::projectResult(vm, value->insert(type, id, version, json), + [](DefinitionHandle&& handle) { return definitionHandleProjection(handle); }); }); - registry.addFunc("replace", [vm](DefinitionRegistry* value, const std::string& type, - const std::string& id, int version, const std::string& json) { + registry.addFunc("replace", [vm](DefinitionRegistry* value, const std::string& type, const std::string& id, + int version, const std::string& json) { if (!value) return eve::script::projectResult( - vm, definitionsBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "definition registry must not be null", "registry"), + vm, + definitionsBindingFailure(eve::DiagnosticCode::InvalidArgument, + "definition registry must not be null", "registry"), [](DefinitionHandle&& handle) { return definitionHandleProjection(handle); }); - return eve::script::projectResult( - vm, value->replace(type, id, version, json), - [](DefinitionHandle&& handle) { return definitionHandleProjection(handle); }); + return eve::script::projectResult(vm, value->replace(type, id, version, json), + [](DefinitionHandle&& handle) { return definitionHandleProjection(handle); }); }); - registry.addFunc("remove", [vm](DefinitionRegistry* value, const std::string& type, - const std::string& id) { + registry.addFunc("remove", [vm](DefinitionRegistry* value, const std::string& type, const std::string& id) { if (!value) return eve::script::projectResult( - vm, definitionsBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "definition registry must not be null", "registry"), + vm, + definitionsBindingFailure(eve::DiagnosticCode::InvalidArgument, + "definition registry must not be null", "registry"), [](DefinitionHandle&& handle) { return definitionHandleProjection(handle); }); - return eve::script::projectResult( - vm, value->remove(type, id), - [](DefinitionHandle&& handle) { return definitionHandleProjection(handle); }); + return eve::script::projectResult(vm, value->remove(type, id), + [](DefinitionHandle&& handle) { return definitionHandleProjection(handle); }); }); - registry.addFunc("resolve", [vm](DefinitionRegistry* value, const std::string& type, - const std::string& id) { + registry.addFunc("resolve", [vm](DefinitionRegistry* value, const std::string& type, const std::string& id) { if (!value) return eve::script::projectResult( - vm, definitionsBindingFailure>( - eve::DiagnosticCode::InvalidArgument, - "definition registry must not be null", "registry"), + vm, + definitionsBindingFailure>( + eve::DiagnosticCode::InvalidArgument, "definition registry must not be null", "registry"), [](std::reference_wrapper&& definition) { return definitionProjection(definition.get()); }); - return eve::script::projectResult( - vm, value->resolve(type, id), - [](std::reference_wrapper&& definition) { - return definitionProjection(definition.get()); - }); + return eve::script::projectResult(vm, value->resolve(type, id), + [](std::reference_wrapper&& definition) { + return definitionProjection(definition.get()); + }); }); - registry.addFunc("resolveHandle", [vm](DefinitionRegistry* value, - const std::string& type, - const std::string& id, - std::int64_t generation) { + registry.addFunc("resolveHandle", [vm](DefinitionRegistry* value, const std::string& type, const std::string& id, + std::int64_t generation) { if (!value) return eve::script::projectResult( - vm, definitionsBindingFailure>( - eve::DiagnosticCode::InvalidArgument, - "definition registry must not be null", "registry"), + vm, + definitionsBindingFailure>( + eve::DiagnosticCode::InvalidArgument, "definition registry must not be null", "registry"), [](std::reference_wrapper&& definition) { return definitionProjection(definition.get()); }); if (generation <= 0) return eve::script::projectResult( - vm, definitionsBindingFailure>( - eve::DiagnosticCode::InvalidArgument, - "definition generation must be positive", "generation"), + vm, + definitionsBindingFailure>( + eve::DiagnosticCode::InvalidArgument, "definition generation must be positive", "generation"), [](std::reference_wrapper&& definition) { return definitionProjection(definition.get()); }); return eve::script::projectResult( - vm, value->resolveHandle(DefinitionHandle{ - value->reference(type, id), eve::Generation(static_cast(generation))}), + vm, + value->resolveHandle( + DefinitionHandle{value->reference(type, id), eve::Generation(static_cast(generation))}), [](std::reference_wrapper&& definition) { return definitionProjection(definition.get()); }); @@ -702,36 +765,29 @@ void Definitions::expose(ssq::Table& table) { if (!value) return eve::script::projectResult( vm, definitionsBindingFailure(eve::DiagnosticCode::InvalidArgument, - "definition registry must not be null", "registry")); + "definition registry must not be null", "registry")); return eve::script::projectResult(vm, value->restoreJson(json)); }); - registry.addFunc("handle", [vm](DefinitionRegistry* value, const std::string& type, - const std::string& id) { + registry.addFunc("handle", [vm](DefinitionRegistry* value, const std::string& type, const std::string& id) { if (!value) return eve::script::projectResult( - vm, definitionsBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "definition registry must not be null", "registry"), + vm, + definitionsBindingFailure(eve::DiagnosticCode::InvalidArgument, + "definition registry must not be null", "registry"), [](DefinitionHandle&& handle) { return definitionHandleProjection(handle); }); - return eve::script::projectResult( - vm, value->handle(type, id), - [](DefinitionHandle&& handle) { return definitionHandleProjection(handle); }); + return eve::script::projectResult(vm, value->handle(type, id), + [](DefinitionHandle&& handle) { return definitionHandleProjection(handle); }); }); - registry.addFunc("generationOf", [vm](DefinitionRegistry* value, const std::string& type, - const std::string& id) { + registry.addFunc("generationOf", [vm](DefinitionRegistry* value, const std::string& type, const std::string& id) { if (!value) return eve::script::projectResult( - vm, definitionsBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "definition registry must not be null", "registry"), - [](eve::Generation generation) { - return eve::Value(static_cast(generation.value())); - }); - return eve::script::projectResult( - vm, value->generationOf(type, id), - [](eve::Generation generation) { - return eve::Value(static_cast(generation.value())); - }); + vm, + definitionsBindingFailure(eve::DiagnosticCode::InvalidArgument, + "definition registry must not be null", "registry"), + [](eve::Generation generation) { return eve::Value(static_cast(generation.value())); }); + return eve::script::projectResult(vm, value->generationOf(type, id), [](eve::Generation generation) { + return eve::Value(static_cast(generation.value())); + }); }); auto cls = table.addClass(name, Definitions::create, false); diff --git a/src/modules/definitions/Definitions.h b/src/modules/definitions/Definitions.h index 17bc410a..913c569b 100644 --- a/src/modules/definitions/Definitions.h +++ b/src/modules/definitions/Definitions.h @@ -22,7 +22,7 @@ namespace eve::definitions { * directly. These aliases keep the Definitions module API source-compatible * while ensuring it cannot grow a second logical-reference representation. */ -using DefinitionRef = eve::DefinitionRef; +using DefinitionRef = eve::DefinitionRef; using DefinitionHandle = eve::definition::DefinitionHandle; /** @brief One immutable view of a registered versioned JSON definition. */ @@ -43,7 +43,7 @@ struct DefinitionEvent { std::string type; std::string id; eve::SchemaVersion version; - eve::Generation generation; + eve::Generation generation; }; /** @@ -61,47 +61,42 @@ public: */ explicit DefinitionRegistry(eve::PersistentId instanceId = {}); - using ChangeCallback = std::function; + using ChangeCallback = std::function; /** * @brief Inserts a definition using the common generation-qualified registry. * @return The new handle, or a structured validation/conflict failure. */ - [[nodiscard]] eve::Result insert( - const std::string& type, const std::string& id, int version, const std::string& json); + [[nodiscard]] eve::Result insert(const std::string& type, const std::string& id, int version, + const std::string& json); /** * @brief Replaces a live definition and invalidates its previous handle. * @return The replacement handle, or a structured failure. */ - [[nodiscard]] eve::Result replace( - const std::string& type, const std::string& id, int version, const std::string& json); + [[nodiscard]] eve::Result replace(const std::string& type, const std::string& id, int version, + const std::string& json); /** * @brief Removes a definition and returns the retained tombstone handle. * @return A stale tombstone handle, or a structured failure. */ - [[nodiscard]] eve::Result remove( - const std::string& type, const std::string& id); + [[nodiscard]] eve::Result remove(const std::string& type, const std::string& id); /** * @brief Resolves a definition through the common registry API. * @return A borrowed reference valid until the next registry mutation. */ - [[nodiscard]] eve::ResultRef resolve( - const std::string& type, const std::string& id) const; + [[nodiscard]] eve::ResultRef resolve(const std::string& type, const std::string& id) const; /** @brief Resolves a generation-qualified handle and reports stale identities. */ - [[nodiscard]] eve::ResultRef resolveHandle( - const DefinitionHandle& handle) const; + [[nodiscard]] eve::ResultRef resolveHandle(const DefinitionHandle& handle) const; /** @brief Returns the current generation-qualified handle through Result. */ - [[nodiscard]] eve::Result handle( - const std::string& type, const std::string& id) const; + [[nodiscard]] eve::Result handle(const std::string& type, const std::string& id) const; /** @brief Returns the latest generation, including a retained tombstone. */ - [[nodiscard]] eve::Result generationOf( - const std::string& type, const std::string& id) const; + [[nodiscard]] eve::Result generationOf(const std::string& type, const std::string& id) const; /** @brief Returns whether a key is retained as a tombstone. */ [[nodiscard]] bool isTombstone(const std::string& type, const std::string& id) const noexcept; @@ -135,8 +130,7 @@ public: * @param hashProvider Explicit content-digest provider; it is never defaulted silently. * @return A sealed snapshot, or a structured serialization/hash failure. */ - [[nodiscard]] eve::Result snapshot( - const eve::SnapshotHashProvider& hashProvider) const; + [[nodiscard]] eve::Result snapshot(const eve::SnapshotHashProvider& hashProvider) const; /** * @brief Restores a verified or migratable snapshot transactionally. @@ -144,16 +138,15 @@ public: * @param hashProvider Explicit provider used to verify and reseal the payload. * @return Success, or a failure leaving all registry state unchanged. */ - [[nodiscard]] eve::Result restoreSnapshot( - const eve::SnapshotEnvelope& snapshot, const eve::SnapshotHashProvider& hashProvider); + [[nodiscard]] eve::Result restoreSnapshot(const eve::SnapshotEnvelope& snapshot, + const eve::SnapshotHashProvider& hashProvider); /** * @brief Serializes the common snapshot envelope as canonical JSON. * @param hashProvider Explicit content-digest provider. * @return Canonical envelope JSON or a structured failure. */ - [[nodiscard]] eve::Result snapshotEnvelopeJson( - const eve::SnapshotHashProvider& hashProvider) const; + [[nodiscard]] eve::Result snapshotEnvelopeJson(const eve::SnapshotHashProvider& hashProvider) const; /** * @brief Parses and transactionally restores a common snapshot envelope. @@ -161,8 +154,8 @@ public: * @param hashProvider Explicit provider used to verify contentHash. * @return Success, or a failure leaving all registry state unchanged. */ - [[nodiscard]] eve::Result restoreSnapshotJson( - std::string_view json, const eve::SnapshotHashProvider& hashProvider); + [[nodiscard]] eve::Result restoreSnapshotJson(std::string_view json, + const eve::SnapshotHashProvider& hashProvider); /** * @brief Subscribes to successful definition register, replace and remove operations. @@ -179,8 +172,7 @@ public: * synthesize per-definition events; callers must re-query after * restore because previously borrowed Definition references expire. */ - [[nodiscard("retain Subscription or explicitly dispose it")]] eve::Subscription subscribe( - ChangeCallback callback); + [[nodiscard("retain Subscription or explicitly dispose it")]] eve::Subscription subscribe(ChangeCallback callback); private: using Key = std::pair; @@ -189,16 +181,16 @@ private: }; using Storage = eve::VersionedRegistry; - static DefinitionEvent projectEvent(const Storage::Event& event); + static DefinitionEvent projectEvent(const Storage::Event& event); static DefinitionHandle projectHandle(const Storage::Handle& handle); - static Storage::Handle storageHandle(const DefinitionHandle& handle); - void clearEventProjection() const; + static Storage::Handle storageHandle(const DefinitionHandle& handle); + void clearEventProjection() const; - Storage storage_; - eve::PersistentId instanceId_; - eve::Revision revision_; - eve::SimulationTick tick_; - mutable std::vector eventProjection_; + Storage storage_; + eve::PersistentId instanceId_; + eve::Revision revision_; + eve::SimulationTick tick_; + mutable std::vector eventProjection_; }; /** @brief Script module factory for independent definition registries. */ diff --git a/src/modules/dialogue/Conversation.cpp b/src/modules/dialogue/Conversation.cpp index 99e229ba..f17f36a4 100644 --- a/src/modules/dialogue/Conversation.cpp +++ b/src/modules/dialogue/Conversation.cpp @@ -7,10 +7,9 @@ namespace eve::dialogue { namespace { -eve::Result runnerFailure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - code, std::move(message), std::move(path), {}, "dialogue.conversation")); +eve::Result runnerFailure(eve::DiagnosticCode code, std::string message, std::string path = {}) { + return eve::Result::failure( + eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "dialogue.conversation")); } } // namespace @@ -107,14 +106,11 @@ void ConversationRunner::unregisterCommand(const std::string& name) { commandHandlers_.erase(name); } -void ConversationRunner::registerCommandRequest(const std::string& name, - CommandRequestHandler handler) { +void ConversationRunner::registerCommandRequest(const std::string& name, CommandRequestHandler handler) { if (!name.empty() && handler) commandRequestHandlers_[name] = std::move(handler); } -void ConversationRunner::unregisterCommandRequest(const std::string& name) { - commandRequestHandlers_.erase(name); -} +void ConversationRunner::unregisterCommandRequest(const std::string& name) { commandRequestHandlers_.erase(name); } void ConversationRunner::emit(Event::Kind kind, const ConversationAsset::Node* node, const std::string& name) const { @@ -141,8 +137,7 @@ bool ConversationRunner::enter(const std::string& nodeId, std::string* error) { return true; } -std::string ConversationRunner::evaluateRoute(const ConversationAsset::Node& node, - std::string* error) { +std::string ConversationRunner::evaluateRoute(const ConversationAsset::Node& node, std::string* error) { for (const auto& route : node.routes) { if (!route.condition.isNull()) { if (!conditionEvaluator_) { @@ -150,7 +145,7 @@ std::string ConversationRunner::evaluateRoute(const ConversationAsset::Node& nod return {}; } eve::decision::ConditionResult result = conditionEvaluator_(route.condition); - lastConditionResult_ = result; + lastConditionResult_ = result; if (result.passed()) return route.second; continue; } @@ -230,7 +225,7 @@ bool ConversationRunner::runUntilBlocked(std::string* error) { } case ConversationAsset::Node::Kind::Command: { const auto requestIt = commandRequestHandlers_.find(node->target); - const auto legacyIt = commandHandlers_.find(node->target); + const auto legacyIt = commandHandlers_.find(node->target); if (requestIt == commandRequestHandlers_.end() && legacyIt == commandHandlers_.end() && !commandRequestDispatcher_) return fail(error, "conversation: command '" + node->target + @@ -238,40 +233,35 @@ bool ConversationRunner::runUntilBlocked(std::string* error) { emit(Event::Kind::Command, node, node->target); if (requestIt != commandRequestHandlers_.end() || commandRequestDispatcher_) { CommandRequest request; - request.name = node->target; - request.kind = node->commandKind; - request.arguments = toCanonicalValue(node->arguments); - request.bindings = toCanonicalValue(bindings_); - request.locals = toCanonicalValue(locals_); - request.payment = node->payment; + request.name = node->target; + request.kind = node->commandKind; + request.arguments = toCanonicalValue(node->arguments); + request.bindings = toCanonicalValue(bindings_); + request.locals = toCanonicalValue(locals_); + request.payment = node->payment; request.stateMutations = node->stateMutations; - lastCommandRequest_ = request; - const CommandRequestHandler& handler = requestIt != commandRequestHandlers_.end() - ? requestIt->second - : commandRequestDispatcher_; + lastCommandRequest_ = request; + const CommandRequestHandler& handler = + requestIt != commandRequestHandlers_.end() ? requestIt->second : commandRequestDispatcher_; CommandResponse response = handler(request); if (response.status == CommandResponse::Status::Failed) - return fail(error, response.error.empty() ? "conversation: command failed" - : response.error); + return fail(error, response.error.empty() ? "conversation: command failed" : response.error); if (response.status == CommandResponse::Status::Blocked) { - blocked_ = true; + blocked_ = true; waitingCommand_ = true; return true; } - if (!node->expression.empty()) - locals_.set(node->expression, toDialogueStateValue(response.value)); + if (!node->expression.empty()) locals_.set(node->expression, toDialogueStateValue(response.value)); } else { CommandResult result = legacyIt->second(node->arguments, bindings_, locals_); if (result.status == CommandResult::Status::Failed) - return fail(error, result.error.empty() ? "conversation: command failed" - : result.error); + return fail(error, result.error.empty() ? "conversation: command failed" : result.error); if (result.status == CommandResult::Status::Blocked) { - blocked_ = true; + blocked_ = true; waitingCommand_ = true; return true; } - if (!node->expression.empty()) - locals_.set(node->expression, std::move(result.value)); + if (!node->expression.empty()) locals_.set(node->expression, std::move(result.value)); } if (!enter(node->next, error)) return false; break; @@ -389,8 +379,7 @@ eve::Result ConversationRunner::resumeCommand(StateValue result) { StateValue before; if (!captureState(before)) - return runnerFailure(eve::DiagnosticCode::Failed, - "conversation: could not capture command state", "command"); + return runnerFailure(eve::DiagnosticCode::Failed, "conversation: could not capture command state", "command"); if (!node->expression.empty()) locals_.set(node->expression, std::move(result)); std::string error; if (enter(node->next, &error) && runUntilBlocked(&error)) @@ -399,8 +388,7 @@ eve::Result ConversationRunner::resumeCommand(StateValue result) { std::string restoreError; if (!restoreState(before, &restoreError) && error.empty()) error = restoreError; return runnerFailure(eve::DiagnosticCode::Failed, - error.empty() ? "conversation: command resume failed" : std::move(error), - "command"); + error.empty() ? "conversation: command resume failed" : std::move(error), "command"); } eve::Result ConversationRunner::resumeCommand(eve::Value result) { @@ -446,19 +434,17 @@ eve::Result ConversationRunner::selectRouteForTransaction(const std::strin "conversation: structured choice requires a condition evaluator", "route.condition"); eve::decision::ConditionResult result = conditionEvaluator_(route.condition); - lastConditionResult_ = result; + lastConditionResult_ = result; if (!result.passed()) - return runnerFailure( - eve::DiagnosticCode::DialogueConditionRejected, - "conversation: choice condition rejected (" + - std::string(eve::decision::conditionReasonCodeName(result.reasonCode())) + ")", - "route.condition"); + return runnerFailure(eve::DiagnosticCode::DialogueConditionRejected, + "conversation: choice condition rejected (" + + std::string(eve::decision::conditionReasonCodeName(result.reasonCode())) + ")", + "route.condition"); } StateValue before; if (!captureState(before)) - return runnerFailure(eve::DiagnosticCode::Failed, - "conversation: could not capture choice state", "route"); + return runnerFailure(eve::DiagnosticCode::Failed, "conversation: could not capture choice state", "route"); std::string error; if (enter(route.second, &error) && runUntilBlocked(&error)) return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); @@ -466,11 +452,10 @@ eve::Result ConversationRunner::selectRouteForTransaction(const std::strin std::string restoreError; if (!restoreState(before, &restoreError) && error.empty()) error = restoreError; return runnerFailure(eve::DiagnosticCode::Failed, - error.empty() ? "conversation: choice selection failed" : std::move(error), - "route"); + error.empty() ? "conversation: choice selection failed" : std::move(error), "route"); } - return runnerFailure(eve::DiagnosticCode::DialogueRouteNotFound, - "conversation: unknown choice '" + routeId + "'", "route"); + return runnerFailure(eve::DiagnosticCode::DialogueRouteNotFound, "conversation: unknown choice '" + routeId + "'", + "route"); } } // namespace eve::dialogue diff --git a/src/modules/dialogue/Conversation.h b/src/modules/dialogue/Conversation.h index 09e1022c..62472db2 100644 --- a/src/modules/dialogue/Conversation.h +++ b/src/modules/dialogue/Conversation.h @@ -1,8 +1,8 @@ #pragma once -#include "dialogue/DialogueState.h" #include "common/Result.h" #include "common/StateValue.h" +#include "dialogue/DialogueState.h" #include #include @@ -22,7 +22,7 @@ namespace eve::dialogue { struct ConversationRoute { std::string first; std::string second; - eve::Value condition; + eve::Value condition; /** @brief Optional money/reputation charge committed before entering the target. */ PaymentSpec payment; /** @brief Optional authoritative mutations committed with this route. */ @@ -55,7 +55,7 @@ struct ConversationAsset { std::string returnNode; StateValue arguments = StateValue::object(); std::vector routes; - CommandRequestKind commandKind = CommandRequestKind::Operation; + CommandRequestKind commandKind = CommandRequestKind::Operation; /** @brief Optional money/reputation charge for a command node. */ PaymentSpec payment; /** @brief Optional authoritative mutations committed with a command. */ @@ -95,7 +95,7 @@ public: }; using CommandHandler = std::function; - using ConditionEvaluator = std::function; + using ConditionEvaluator = std::function; using CommandRequestDispatcher = std::function; using EventSink = std::function; @@ -143,9 +143,7 @@ public: expressionEvaluator_ = std::move(evaluator); } /** @brief Evaluate structured route conditions through decision::Condition. */ - void setConditionEvaluator(ConditionEvaluator evaluator) { - conditionEvaluator_ = std::move(evaluator); - } + void setConditionEvaluator(ConditionEvaluator evaluator) { conditionEvaluator_ = std::move(evaluator); } /** @brief Register a cross-module command without introducing module includes. */ void registerCommand(const std::string& name, CommandHandler handler); void unregisterCommand(const std::string& name); @@ -195,9 +193,7 @@ public: * destruction. * @thread Affine to the runner's owner thread. */ - const CommandRequest* lastCommandRequest() const { - return lastCommandRequest_ ? &*lastCommandRequest_ : nullptr; - } + const CommandRequest* lastCommandRequest() const { return lastCommandRequest_ ? &*lastCommandRequest_ : nullptr; } private: struct Frame { @@ -221,14 +217,14 @@ private: std::vector callStack_; AssetResolver assetResolver_; ExpressionEvaluator expressionEvaluator_; - ConditionEvaluator conditionEvaluator_; + ConditionEvaluator conditionEvaluator_; std::unordered_map commandHandlers_; std::unordered_map commandRequestHandlers_; - CommandRequestDispatcher commandRequestDispatcher_; + CommandRequestDispatcher commandRequestDispatcher_; EventSink eventSink_; bool waitingCommand_ = false; - std::optional lastConditionResult_; - std::optional lastCommandRequest_; + std::optional lastConditionResult_; + std::optional lastCommandRequest_; }; } // namespace eve::dialogue diff --git a/src/modules/dialogue/ConversationAuthoring.cpp b/src/modules/dialogue/ConversationAuthoring.cpp index 8fb2a61c..2a1dbe8c 100644 --- a/src/modules/dialogue/ConversationAuthoring.cpp +++ b/src/modules/dialogue/ConversationAuthoring.cpp @@ -306,7 +306,7 @@ bool ConversationDocument::removeRoute(const std::string& nodeId, int index) { bool ConversationDocument::validate() { diagnostics_.clear(); const bool valid = lintConversations({asset_}, asset_.id, diagnostics_); - failureMessage_ = valid || diagnostics_.empty() ? std::string{} : diagnostics_.front().message; + failureMessage_ = valid || diagnostics_.empty() ? std::string{} : diagnostics_.front().message; return valid; } diff --git a/src/modules/dialogue/ConversationAuthoring.h b/src/modules/dialogue/ConversationAuthoring.h index d3f0be31..7b28b034 100644 --- a/src/modules/dialogue/ConversationAuthoring.h +++ b/src/modules/dialogue/ConversationAuthoring.h @@ -92,6 +92,7 @@ public: int getDiagnosticLine(int index) const; /** @brief Return a diagnostic message. */ std::string getDiagnosticMessage(int index) const; + private: ConversationAsset::Node* findNode(const std::string& nodeId); const ConversationAsset::Node* findNode(const std::string& nodeId) const; diff --git a/src/modules/dialogue/Dialogue.cpp b/src/modules/dialogue/Dialogue.cpp index e2468717..c6449ae0 100644 --- a/src/modules/dialogue/Dialogue.cpp +++ b/src/modules/dialogue/Dialogue.cpp @@ -31,7 +31,7 @@ std::string floatToString(double v) { return buf; } -bool squirrelToVarValueAt(HSQUIRRELVM vm, SQInteger index, Dialogue::VarValue& out) { +bool squirrelToVarValueAt(HSQUIRRELVM vm, SQInteger index, Dialogue::VarValue &out) { auto converted = eve::script::valueFromSquirrel(vm, index); if (!converted.ok()) { converted.ignore("Dialogue scalar compatibility adapter rejected the value"); @@ -44,7 +44,7 @@ bool squirrelToVarValueAt(HSQUIRRELVM vm, SQInteger index, Dialogue::VarValue& o } /** Convert a Squirrel value through the canonical common Value adapter. */ -bool objectToDataValue(HSQUIRRELVM vm, const ssq::Object& object, DataValue& out) { +bool objectToDataValue(HSQUIRRELVM vm, const ssq::Object &object, DataValue &out) { if (!vm || object.isEmpty()) return false; auto converted = eve::script::valueFromSquirrel(object); if (!converted.ok()) { @@ -55,8 +55,8 @@ bool objectToDataValue(HSQUIRRELVM vm, const ssq::Object& object, DataValue& out return true; } -bool objectToVarParams(HSQUIRRELVM vm, const ssq::Object& object, - std::unordered_map& out) { +bool objectToVarParams(HSQUIRRELVM vm, const ssq::Object &object, + std::unordered_map &out) { out.clear(); if (!vm || object.isEmpty() || object.getType() != ssq::Type::TABLE) return true; auto converted = eve::script::valueFromSquirrel(object); @@ -64,16 +64,16 @@ bool objectToVarParams(HSQUIRRELVM vm, const ssq::Object& object, converted.ignore("Dialogue parameter compatibility adapter rejected the table"); return true; } - Dialogue::VarValue value = std::move(converted).takeValue(); - const auto* fields = value.getIf(); + Dialogue::VarValue value = std::move(converted).takeValue(); + const auto *fields = value.getIf(); if (!fields) return true; - for (const auto& [key, field] : *fields) { + for (const auto &[key, field] : *fields) { if (!field.isNull() && !field.isArray() && !field.isObject()) out[key] = field; } return true; } -void pushVarValue(HSQUIRRELVM vm, const Dialogue::VarValue& value) { +void pushVarValue(HSQUIRRELVM vm, const Dialogue::VarValue &value) { auto pushed = eve::script::pushValue(vm, value); if (!pushed.ok()) { pushed.ignore("Dialogue scalar compatibility adapter could not push the value"); @@ -81,10 +81,9 @@ void pushVarValue(HSQUIRRELVM vm, const Dialogue::VarValue& value) { } } -void pushVarTable(HSQUIRRELVM vm, - const std::unordered_map& vars) { +void pushVarTable(HSQUIRRELVM vm, const std::unordered_map &vars) { eve::Value::Object fields; - for (const auto& [key, value] : vars) fields.emplace(key, value); + for (const auto &[key, value] : vars) fields.emplace(key, value); auto pushed = eve::script::pushValue(vm, eve::Value(std::move(fields))); if (!pushed.ok()) { pushed.ignore("Dialogue parameter compatibility adapter could not push the table"); @@ -363,35 +362,26 @@ bool Dialogue::parseCondition(const DataValue &v, Condition &out, std::string &e } if (const DataValue *var = v.find("var")) { const DataValue *op = v.find("op"); - if (var->kind() != DataValue::Kind::String || !op || - op->kind() != DataValue::Kind::String) { + if (var->kind() != DataValue::Kind::String || !op || op->kind() != DataValue::Kind::String) { error = "var/op condition requires string var and op"; return false; } static const char *kOps[] = {"eq", "ne", "gt", "ge", "lt", "le", "has", "missing"}; - const std::string opStr = op->asString(); + const std::string opStr = op->asString(); if (std::find(std::begin(kOps), std::end(kOps), opStr) == std::end(kOps)) { error = "unknown condition op: " + opStr; return false; } out.kind = Condition::Kind::Cmp; - out.var = var->asString(); + out.var = var->asString(); out.op = opStr; out.value = VarValue::string(""); if (const DataValue *val = v.find("value")) { switch (val->kind()) { - case DataValue::Kind::Int: - out.value = VarValue::integer(val->asInt()); - break; - case DataValue::Kind::Float: - out.value = VarValue::number(val->asDouble()); - break; - case DataValue::Kind::Bool: - out.value = VarValue::boolean(val->asBool()); - break; - case DataValue::Kind::String: - out.value = VarValue::string(val->asString()); - break; + case DataValue::Kind::Int: out.value = VarValue::integer(val->asInt()); break; + case DataValue::Kind::Float: out.value = VarValue::number(val->asDouble()); break; + case DataValue::Kind::Bool: out.value = VarValue::boolean(val->asBool()); break; + case DataValue::Kind::String: out.value = VarValue::string(val->asString()); break; default: break; } @@ -408,10 +398,8 @@ bool Dialogue::compareEq(const VarValue &a, const VarValue &b) const { const double y = b.type() == VarValue::Type::Int ? double(b.asInt()) : b.asDouble(); return x == y; } - if (a.type() == VarValue::Type::Bool && b.type() == VarValue::Type::Bool) - return a.asBool() == b.asBool(); - if (a.type() == VarValue::Type::String && b.type() == VarValue::Type::String) - return a.asString() == b.asString(); + if (a.type() == VarValue::Type::Bool && b.type() == VarValue::Type::Bool) return a.asBool() == b.asBool(); + if (a.type() == VarValue::Type::String && b.type() == VarValue::Type::String) return a.asString() == b.asString(); return false; } @@ -539,8 +527,10 @@ bool Dialogue::parseLineData(const std::string &poolId, const DataValue &v, int } if (const DataValue *w = v.find("weight")) { - if (w->kind() == DataValue::Kind::Int) line.weight = double(w->asInt()); - else if (w->kind() == DataValue::Kind::Float) line.weight = w->asDouble(); + if (w->kind() == DataValue::Kind::Int) + line.weight = double(w->asInt()); + else if (w->kind() == DataValue::Kind::Float) + line.weight = w->asDouble(); else { error = "line '" + line.id + "': weight must be a number"; return false; @@ -563,18 +553,10 @@ bool Dialogue::parseLineData(const std::string &poolId, const DataValue &v, int for (const auto &mkv : *fields) { std::string val; switch (mkv.second.kind()) { - case DataValue::Kind::String: - val = mkv.second.asString(); - break; - case DataValue::Kind::Int: - val = std::to_string(mkv.second.asInt()); - break; - case DataValue::Kind::Float: - val = floatToString(mkv.second.asDouble()); - break; - case DataValue::Kind::Bool: - val = mkv.second.asBool() ? "true" : "false"; - break; + case DataValue::Kind::String: val = mkv.second.asString(); break; + case DataValue::Kind::Int: val = std::to_string(mkv.second.asInt()); break; + case DataValue::Kind::Float: val = floatToString(mkv.second.asDouble()); break; + case DataValue::Kind::Bool: val = mkv.second.asBool() ? "true" : "false"; break; default: break; } @@ -617,8 +599,10 @@ int Dialogue::loadPoolsFromData(const DataValue &root) { continue; } if (const DataValue *nr = kv.second.find("noRepeat")) { - if (nr->kind() == DataValue::Kind::Int) pool.noRepeat = int(nr->asInt()); - else if (nr->kind() == DataValue::Kind::Float) pool.noRepeat = int(nr->asDouble()); + if (nr->kind() == DataValue::Kind::Int) + pool.noRepeat = int(nr->asInt()); + else if (nr->kind() == DataValue::Kind::Float) + pool.noRepeat = int(nr->asDouble()); if (pool.noRepeat < 0) pool.noRepeat = 0; } const DataValue *lines = kv.second.find("lines"); diff --git a/src/modules/dialogue/Dialogue.h b/src/modules/dialogue/Dialogue.h index 1d373979..c31519c5 100644 --- a/src/modules/dialogue/Dialogue.h +++ b/src/modules/dialogue/Dialogue.h @@ -45,7 +45,7 @@ class Dialogue : public Module { public: // Keep the historical nested names as zero-cost aliases for C++ callers. using DataValue = eve::Value; - using VarValue = eve::Value; + using VarValue = eve::Value; Module_REG(Dialogue); Dialogue(); diff --git a/src/modules/dialogue/DialogueFlow.cpp b/src/modules/dialogue/DialogueFlow.cpp index a66376b9..c11f7a01 100644 --- a/src/modules/dialogue/DialogueFlow.cpp +++ b/src/modules/dialogue/DialogueFlow.cpp @@ -105,8 +105,7 @@ std::string kindName(ConversationAsset::Node::Kind kind) { return {}; } -eve::Result dialogueFailure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { +eve::Result dialogueFailure(eve::DiagnosticCode code, std::string message, std::string path = {}) { return eve::Result::failure( eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "dialogue.flow")); } @@ -116,19 +115,18 @@ public: DialogueSelectionParticipant(ConversationRunner& runner, std::string routeId, StateValue before) : runner_(runner), routeId_(std::move(routeId)), before_(std::move(before)) {} - [[nodiscard]] std::string_view name() const noexcept override { return "dialogue.choice"; } - [[nodiscard]] eve::Result prepare( - const eve::transaction::TransactionContext& context) override { + [[nodiscard]] std::string_view name() const noexcept override { return "dialogue.choice"; } + [[nodiscard]] eve::Result prepare(const eve::transaction::TransactionContext& context) override { if (context.transactionId().empty()) return dialogueFailure(eve::DiagnosticCode::InvalidArgument, "dialogue choice transaction requires a transaction id", "transactionId"); if (phase_ != Phase::Idle) - return dialogueFailure(eve::DiagnosticCode::Conflict, "dialogue choice is not idle", "transaction.lifecycle"); + return dialogueFailure(eve::DiagnosticCode::Conflict, "dialogue choice is not idle", + "transaction.lifecycle"); phase_ = Phase::Prepared; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } - [[nodiscard]] eve::Result commit( - const eve::transaction::TransactionContext&) override { + [[nodiscard]] eve::Result commit(const eve::transaction::TransactionContext&) override { if (phase_ != Phase::Prepared) return dialogueFailure(eve::DiagnosticCode::Conflict, "dialogue choice has no prepared stage", "transaction.lifecycle"); @@ -143,19 +141,17 @@ public: phase_ = Phase::Committed; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } - [[nodiscard]] eve::Result rollback( - const eve::transaction::TransactionContext&) override { + [[nodiscard]] eve::Result rollback(const eve::transaction::TransactionContext&) override { if (phase_ != Phase::Prepared) - return dialogueFailure(eve::DiagnosticCode::Conflict, - "dialogue choice has no prepared stage to roll back", "transaction.lifecycle"); + return dialogueFailure(eve::DiagnosticCode::Conflict, "dialogue choice has no prepared stage to roll back", + "transaction.lifecycle"); phase_ = Phase::RolledBack; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } - [[nodiscard]] eve::Result compensate( - const eve::transaction::TransactionContext&) override { + [[nodiscard]] eve::Result compensate(const eve::transaction::TransactionContext&) override { if (phase_ != Phase::Committed) - return dialogueFailure(eve::DiagnosticCode::Conflict, - "dialogue choice is not committed", "transaction.lifecycle"); + return dialogueFailure(eve::DiagnosticCode::Conflict, "dialogue choice is not committed", + "transaction.lifecycle"); std::string error; if (!runner_.restoreState(before_, &error)) return dialogueFailure(eve::DiagnosticCode::Failed, @@ -167,9 +163,9 @@ public: private: enum class Phase : std::uint8_t { Idle, Prepared, Committed, RolledBack }; ConversationRunner& runner_; - std::string routeId_; - StateValue before_; - Phase phase_ = Phase::Idle; + std::string routeId_; + StateValue before_; + Phase phase_ = Phase::Idle; }; } // namespace @@ -192,22 +188,20 @@ void DialogueFlow::configureIntegration(IntegrationConfig config) { stateContext_.setMutationProvider(config.stateMutation); configuredConditionEvaluator_ = std::move(config.conditionEvaluator); operationRequestHandler_ = std::move(config.operationHandler); - gameplayActionHandler_ = std::move(config.gameplayActionHandler); - commandParticipantFactory_ = std::move(config.commandParticipantFactory); - stateMutationProvider_ = config.stateMutation; + gameplayActionHandler_ = std::move(config.gameplayActionHandler); + commandParticipantFactory_ = std::move(config.commandParticipantFactory); + stateMutationProvider_ = config.stateMutation; paymentAdapter_.setBindings(std::move(config.accounts)); if (configuredConditionEvaluator_) { runner_.setConditionEvaluator(configuredConditionEvaluator_); } else { - runner_.setConditionEvaluator([this](const eve::Value& specification) { - return stateContext_.evaluate(specification); - }); + runner_.setConditionEvaluator( + [this](const eve::Value& specification) { return stateContext_.evaluate(specification); }); } if (operationRequestHandler_ || gameplayActionHandler_ || commandParticipantFactory_ || stateMutationProvider_) { - runner_.setCommandRequestDispatcher( - [this](const CommandRequest& request) { return dispatchCommand(request); }); + runner_.setCommandRequestDispatcher([this](const CommandRequest& request) { return dispatchCommand(request); }); } else { runner_.clearCommandRequestDispatcher(); } @@ -215,8 +209,8 @@ void DialogueFlow::configureIntegration(IntegrationConfig config) { void DialogueFlow::clearIntegration() { configureIntegration({}); } -eve::Result DialogueFlow::applyStateMutations( - std::span mutations, const eve::MutationContext& context) const { +eve::Result DialogueFlow::applyStateMutations(std::span mutations, + const eve::MutationContext& context) const { return stateContext_.apply(mutations, context); } @@ -275,7 +269,7 @@ int DialogueFlow::reloadFromDnut(const std::string& source, const std::string& p std::vector candidateDiagnostics; if (!compileDnutConversations(source, path, compiled, candidateDiagnostics)) { diagnostics_ = std::move(candidateDiagnostics); - failureMessage_ = diagnostics_.empty() ? "conversation compilation failed" : diagnostics_.front().message; + failureMessage_ = diagnostics_.empty() ? "conversation compilation failed" : diagnostics_.front().message; lastLoadChanged_ = false; return 0; } @@ -301,7 +295,7 @@ int DialogueFlow::reloadFromDnut(const std::string& source, const std::string& p } if (!lintConversationWorkspace(candidate, path, candidateDiagnostics)) { diagnostics_ = std::move(candidateDiagnostics); - failureMessage_ = diagnostics_.empty() ? "conversation workspace lint failed" : diagnostics_.front().message; + failureMessage_ = diagnostics_.empty() ? "conversation workspace lint failed" : diagnostics_.front().message; lastLoadChanged_ = false; return 0; } @@ -320,7 +314,7 @@ int DialogueFlow::reloadFromDnut(const std::string& source, const std::string& p !runner_.restoreState(migrated, &restoreError)) { assets_ = std::move(previous); runner_.restoreState(activeState, nullptr); - failureMessage_ = "conversation hot reload rolled back: " + restoreError; + failureMessage_ = "conversation hot reload rolled back: " + restoreError; lastLoadChanged_ = false; return 0; } @@ -352,7 +346,7 @@ bool DialogueFlow::removeSource(const std::string& path) { bool DialogueFlow::lintAll() { diagnostics_.clear(); const bool valid = lintConversationWorkspace(assets_, "", diagnostics_); - failureMessage_ = valid || diagnostics_.empty() ? std::string{} : diagnostics_.front().message; + failureMessage_ = valid || diagnostics_.empty() ? std::string{} : diagnostics_.front().message; return valid; } @@ -452,7 +446,7 @@ std::string DialogueFlow::exportLocalizationCsv() const { return exportConversat int DialogueFlow::importLocalizationCsv(const std::string& csv, const std::string& defaultLocale) { diagnostics_.clear(); const int count = localization_.importCsv(csv, defaultLocale, diagnostics_); - failureMessage_ = count > 0 || diagnostics_.empty() ? std::string{} : diagnostics_.front().message; + failureMessage_ = count > 0 || diagnostics_.empty() ? std::string{} : diagnostics_.front().message; return count; } @@ -502,7 +496,8 @@ bool DialogueFlow::applyDocument(ConversationDocument* document) { std::vector candidateDiagnostics; if (!lintConversationWorkspace(candidate, "authoring", candidateDiagnostics)) { diagnostics_ = std::move(candidateDiagnostics); - failureMessage_ = diagnostics_.empty() ? "conversation document validation failed" : diagnostics_.front().message; + failureMessage_ = + diagnostics_.empty() ? "conversation document validation failed" : diagnostics_.front().message; return false; } assets_ = std::move(candidate); @@ -533,7 +528,7 @@ bool DialogueFlow::start(const std::string& id, ssq::Object bindings) { return runner_.start(asset, std::move(converted), &failureMessage_); } -bool DialogueFlow::advance() { return runner_.advance(&failureMessage_); } +bool DialogueFlow::advance() { return runner_.advance(&failureMessage_); } eve::Result DialogueFlow::select(const std::string& routeId) { const auto* node = runner_.currentNode(); if (!node) return runner_.selectRouteForTransaction(routeId); @@ -557,8 +552,8 @@ eve::Result DialogueFlow::select(const std::string& routeId) { return dialogueFailure(eve::DiagnosticCode::Failed, "dialogue choice could not capture its pre-transaction state", "route"); } - DialogueSelectionParticipant selection(runner_, routeId, std::move(before)); - std::unique_ptr state; + DialogueSelectionParticipant selection(runner_, routeId, std::move(before)); + std::unique_ptr state; std::vector effects{&selection}; if (!route->stateMutations.empty()) { state = std::make_unique(*stateMutationProvider_, route->stateMutations); @@ -575,43 +570,42 @@ std::string DialogueFlow::nextTransactionId(const char* purpose) { } CommandResponse DialogueFlow::dispatchCommand(const CommandRequest& request) { - const bool transactional = !request.payment.empty() || !request.stateMutations.empty(); - CommandRequestHandler* legacy = request.kind == CommandRequestKind::Operation - ? &operationRequestHandler_ - : &gameplayActionHandler_; + const bool transactional = !request.payment.empty() || !request.stateMutations.empty(); + CommandRequestHandler* legacy = + request.kind == CommandRequestKind::Operation ? &operationRequestHandler_ : &gameplayActionHandler_; if (!transactional && *legacy) return (*legacy)(request); if (!transactional && !commandParticipantFactory_) { CommandResponse response; response.status = CommandResponse::Status::Failed; - response.error = request.kind == CommandRequestKind::Operation - ? "dialogue operation handler is not configured" - : "dialogue gameplay-action handler is not configured"; + response.error = request.kind == CommandRequestKind::Operation + ? "dialogue operation handler is not configured" + : "dialogue gameplay-action handler is not configured"; return response; } if (!request.payment.empty() && !commandParticipantFactory_ && request.stateMutations.empty()) { CommandResponse response; response.status = CommandResponse::Status::Failed; - response.error = "payment-bearing dialogue command requires an Action/operation participant"; + response.error = "payment-bearing dialogue command requires an Action/operation participant"; return response; } std::vector> owned; - std::vector effects; + std::vector effects; if (commandParticipantFactory_) { auto participant = commandParticipantFactory_(request); if (!participant) { CommandResponse response; response.status = CommandResponse::Status::Failed; - response.error = participant.status().describe(); + response.error = participant.status().describe(); return response; } auto action = std::move(participant).takeValue(); if (!action) { CommandResponse response; response.status = CommandResponse::Status::Failed; - response.error = "dialogue command participant factory returned null"; + response.error = "dialogue command participant factory returned null"; return response; } effects.push_back(action.get()); @@ -621,11 +615,11 @@ CommandResponse DialogueFlow::dispatchCommand(const CommandRequest& request) { if (!stateMutationProvider_) { CommandResponse response; response.status = CommandResponse::Status::Failed; - response.error = "dialogue command state mutations require a StatePatch-compatible provider"; + response.error = "dialogue command state mutations require a StatePatch-compatible provider"; return response; } - auto state = std::make_unique(*stateMutationProvider_, - request.stateMutations); + auto state = + std::make_unique(*stateMutationProvider_, request.stateMutations); effects.push_back(state.get()); owned.push_back(std::move(state)); } @@ -633,7 +627,7 @@ CommandResponse DialogueFlow::dispatchCommand(const CommandRequest& request) { if (!committed) { CommandResponse response; response.status = CommandResponse::Status::Failed; - response.error = committed.status().describe(); + response.error = committed.status().describe(); return response; } (void)std::move(committed).takeValue(); @@ -839,12 +833,10 @@ void DialogueFlow::expose(ssq::Class& cls) { cls.addFunc("applyDocument", &DialogueFlow::applyDocument); cls.addFunc("start", &DialogueFlow::start); cls.addFunc("advance", &DialogueFlow::advance); - cls.addFunc("select", [vm = cls.getHandle()](DialogueFlow* value, - const std::string& routeId) { + cls.addFunc("select", [vm = cls.getHandle()](DialogueFlow* value, const std::string& routeId) { if (!value) - return eve::script::projectResult( - vm, dialogueFailure(eve::DiagnosticCode::InvalidArgument, - "dialogue flow must not be null", "dialogue")); + return eve::script::projectResult(vm, dialogueFailure(eve::DiagnosticCode::InvalidArgument, + "dialogue flow must not be null", "dialogue")); return eve::script::projectResult(vm, value->select(routeId)); }); cls.addFunc("isActive", &DialogueFlow::isActive); diff --git a/src/modules/dialogue/DialogueFlow.h b/src/modules/dialogue/DialogueFlow.h index be8538ce..57e901e7 100644 --- a/src/modules/dialogue/DialogueFlow.h +++ b/src/modules/dialogue/DialogueFlow.h @@ -54,8 +54,8 @@ public: * @remarks The returned participant is owned for the synchronous transaction only; * it must implement compensation when later state effects can fail. */ - using CommandParticipantFactory = std::function< - eve::Result>( + using CommandParticipantFactory = + std::function>( const CommandRequest&)>; /** @brief Optional factory used to stage an atomic operation or Action. */ CommandParticipantFactory commandParticipantFactory; @@ -86,8 +86,8 @@ public: /** @brief Return the facade-owned state boundary; providers remain borrowed. */ const DialogueStateContext& stateContext() const noexcept { return stateContext_; } /** @brief Apply mutations through the configured authoritative provider. */ - [[nodiscard]] eve::Result applyStateMutations( - std::span mutations, const eve::MutationContext& context) const; + [[nodiscard]] eve::Result applyStateMutations(std::span mutations, + const eve::MutationContext& context) const; int loadFromDnut(const std::string& source, const std::string& path); int reloadFromDnut(const std::string& source, const std::string& path); @@ -164,20 +164,20 @@ private: int mergeImported(std::vector imported); const ConversationAsset* find(const std::string& id) const; StateValue evaluate(const std::string& expression, const StateValue& bindings, const StateValue& locals); - CommandResponse dispatchCommand(const CommandRequest& request); - std::string nextTransactionId(const char* purpose); + CommandResponse dispatchCommand(const CommandRequest& request); + std::string nextTransactionId(const char* purpose); std::vector assets_; std::vector diagnostics_; ConversationRunner runner_; - DialogueStateContext stateContext_; + DialogueStateContext stateContext_; ConversationRunner::ConditionEvaluator configuredConditionEvaluator_; - CommandRequestHandler operationRequestHandler_; - CommandRequestHandler gameplayActionHandler_; + CommandRequestHandler operationRequestHandler_; + CommandRequestHandler gameplayActionHandler_; IntegrationConfig::CommandParticipantFactory commandParticipantFactory_; - eve::IStateMutation* stateMutationProvider_ = nullptr; - DialoguePaymentAdapter paymentAdapter_; - std::uint64_t transactionSequence_ = 1; + eve::IStateMutation* stateMutationProvider_ = nullptr; + DialoguePaymentAdapter paymentAdapter_; + std::uint64_t transactionSequence_ = 1; std::string failureMessage_; HSQUIRRELVM vm_ = nullptr; HSQOBJECT evaluator_{}; diff --git a/src/modules/dialogue/DialoguePayment.cpp b/src/modules/dialogue/DialoguePayment.cpp index 5c23e2d2..8b3a6c52 100644 --- a/src/modules/dialogue/DialoguePayment.cpp +++ b/src/modules/dialogue/DialoguePayment.cpp @@ -16,30 +16,28 @@ eve::Result failure(eve::DiagnosticCode code, std::string message, std::strin eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "dialogue.payment")); } -eve::Result failureVoid(eve::DiagnosticCode code, std::string message, - std::string path = {}) { +eve::Result failureVoid(eve::DiagnosticCode code, std::string message, std::string path = {}) { return eve::Result::failure( eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "dialogue.payment")); } eve::Result positiveInteger(const eve::Value& value, std::string_view path) { if (!value.isInt64()) - return failure(eve::DiagnosticCode::InvalidArgument, - "dialogue payment amount must be an Int64", std::string(path)); + return failure(eve::DiagnosticCode::InvalidArgument, "dialogue payment amount must be an Int64", + std::string(path)); const std::int64_t amount = value.asInt(); if (amount <= 0) - return failure(eve::DiagnosticCode::InvalidArgument, - "dialogue payment amount must be positive", std::string(path)); + return failure(eve::DiagnosticCode::InvalidArgument, "dialogue payment amount must be positive", + std::string(path)); return eve::Result::success(amount); } struct AccountCost { eve::resource::IResourceAccount* account = nullptr; - eve::resource::CostSpec cost; + eve::resource::CostSpec cost; }; -eve::Result> buildCosts(const DialogueAccountBindings& bindings, - const PaymentSpec& payment) { +eve::Result> buildCosts(const DialogueAccountBindings& bindings, const PaymentSpec& payment) { auto valid = payment.validate(); if (!valid) return eve::Result>::failure(valid.status()); @@ -47,7 +45,7 @@ eve::Result> buildCosts(const DialogueAccountBindings& eve::resource::IResourceAccount* account; const std::string* resource; std::optional amount; - const char* path; + const char* path; }; const std::array raw{{ {bindings.money, &bindings.moneyResource, payment.money, "money"}, @@ -58,16 +56,14 @@ eve::Result> buildCosts(const DialogueAccountBindings& for (const auto& item : raw) { if (!item.amount) continue; if (item.account == nullptr) - return failure>(eve::DiagnosticCode::Unsupported, - std::string("dialogue ") + item.path + - " payment has no account adapter", - item.path); + return failure>( + eve::DiagnosticCode::Unsupported, + std::string("dialogue ") + item.path + " payment has no account adapter", item.path); auto costItem = eve::resource::ResourceCost::create(*item.resource, *item.amount); if (!costItem) return eve::Result>::failure(costItem.status()); - auto found = std::find_if(result.begin(), result.end(), [&](const AccountCost& candidate) { - return candidate.account == item.account; - }); + auto found = std::find_if(result.begin(), result.end(), + [&](const AccountCost& candidate) { return candidate.account == item.account; }); if (found == result.end()) { std::vector items; items.push_back(std::move(costItem).takeValue()); @@ -93,18 +89,17 @@ eve::Result lifecycleFailure(std::string message) { eve::Result PaymentSpec::validate() const { if (money && *money <= 0) - return failureVoid(eve::DiagnosticCode::InvalidArgument, - "dialogue money payment must be positive", "money"); + return failureVoid(eve::DiagnosticCode::InvalidArgument, "dialogue money payment must be positive", "money"); if (reputation && *reputation <= 0) - return failureVoid(eve::DiagnosticCode::InvalidArgument, - "dialogue reputation payment must be positive", "reputation"); + return failureVoid(eve::DiagnosticCode::InvalidArgument, "dialogue reputation payment must be positive", + "reputation"); return eve::Result::success(); } eve::Result PaymentSpec::fromValue(const eve::Value& value) { if (!value.isObject()) - return failure(eve::DiagnosticCode::InvalidArgument, - "dialogue payment must be an object", "payment"); + return failure(eve::DiagnosticCode::InvalidArgument, "dialogue payment must be an object", + "payment"); PaymentSpec result; for (const auto& key : value.keys()) { const eve::Value* member = value.find(key); @@ -117,8 +112,8 @@ eve::Result PaymentSpec::fromValue(const eve::Value& value) { if (!amount) return eve::Result::failure(amount.status()); result.reputation = std::move(amount).takeValue(); } else { - return failure(eve::DiagnosticCode::InvalidArgument, - "unknown dialogue payment field", "payment." + key); + return failure(eve::DiagnosticCode::InvalidArgument, "unknown dialogue payment field", + "payment." + key); } } return eve::Result::success(std::move(result)); @@ -131,25 +126,21 @@ eve::Value PaymentSpec::toValue() const { return eve::Value(std::move(object)); } -DialogueStateMutationParticipant::DialogueStateMutationParticipant( - eve::IStateMutation& provider, std::span mutations) +DialogueStateMutationParticipant::DialogueStateMutationParticipant(eve::IStateMutation& provider, + std::span mutations) : provider_(provider), mutations_(mutations.begin(), mutations.end()) {} -eve::Result DialogueStateMutationParticipant::prepare( - const eve::transaction::TransactionContext& context) { +eve::Result DialogueStateMutationParticipant::prepare(const eve::transaction::TransactionContext& context) { if (context.transactionId().empty()) - return failureVoid(eve::DiagnosticCode::InvalidArgument, - "dialogue state transaction requires a transaction id", "transactionId"); - if (phase_ != Phase::Idle) - return lifecycleFailure("dialogue state participant is not idle"); + return failureVoid(eve::DiagnosticCode::InvalidArgument, "dialogue state transaction requires a transaction id", + "transactionId"); + if (phase_ != Phase::Idle) return lifecycleFailure("dialogue state participant is not idle"); phase_ = Phase::Prepared; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result DialogueStateMutationParticipant::commit( - const eve::transaction::TransactionContext& context) { - if (phase_ != Phase::Prepared) - return lifecycleFailure("dialogue state participant has no prepared stage"); +eve::Result DialogueStateMutationParticipant::commit(const eve::transaction::TransactionContext& context) { + if (phase_ != Phase::Prepared) return lifecycleFailure("dialogue state participant has no prepared stage"); auto applied = provider_.apply( mutations_, eve::MutationContext{context.transactionId(), context.correlationId(), context.causationId()}); if (!applied) return eve::Result::failure(applied.status()); @@ -158,46 +149,39 @@ eve::Result DialogueStateMutationParticipant::commit( return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result DialogueStateMutationParticipant::rollback( - const eve::transaction::TransactionContext&) { +eve::Result DialogueStateMutationParticipant::rollback(const eve::transaction::TransactionContext&) { if (phase_ != Phase::Prepared) return lifecycleFailure("dialogue state participant has no prepared stage to roll back"); phase_ = Phase::RolledBack; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result DialogueStateMutationParticipant::compensate( - const eve::transaction::TransactionContext&) { +eve::Result DialogueStateMutationParticipant::compensate(const eve::transaction::TransactionContext&) { return failureVoid(eve::DiagnosticCode::Unsupported, - "dialogue state mutation has no generic inverse; it must be last", - "transaction.compensation"); + "dialogue state mutation has no generic inverse; it must be last", "transaction.compensation"); } -DialoguePaymentAdapter::DialoguePaymentAdapter(DialogueAccountBindings bindings) - : bindings_(std::move(bindings)) {} +DialoguePaymentAdapter::DialoguePaymentAdapter(DialogueAccountBindings bindings) : bindings_(std::move(bindings)) {} -void DialoguePaymentAdapter::setBindings(DialogueAccountBindings bindings) { - bindings_ = std::move(bindings); -} +void DialoguePaymentAdapter::setBindings(DialogueAccountBindings bindings) { bindings_ = std::move(bindings); } eve::Result DialoguePaymentAdapter::execute( std::string transactionId, const PaymentSpec& payment, std::span effects) const { if (transactionId.empty()) return failure( - eve::DiagnosticCode::InvalidArgument, "dialogue payment transaction id must not be empty", - "transactionId"); + eve::DiagnosticCode::InvalidArgument, "dialogue payment transaction id must not be empty", "transactionId"); for (std::size_t index = 0; index < effects.size(); ++index) { if (effects[index] == nullptr) - return failure( - eve::DiagnosticCode::InvalidArgument, "dialogue payment effect must not be null", - "effects[" + std::to_string(index) + "]"); + return failure(eve::DiagnosticCode::InvalidArgument, + "dialogue payment effect must not be null", + "effects[" + std::to_string(index) + "]"); for (std::size_t prior = 0; prior < index; ++prior) { if (effects[prior] == effects[index]) - return failure( - eve::DiagnosticCode::Conflict, "dialogue payment effects must be unique", - "effects[" + std::to_string(index) + "]"); + return failure(eve::DiagnosticCode::Conflict, + "dialogue payment effects must be unique", + "effects[" + std::to_string(index) + "]"); } } @@ -217,8 +201,8 @@ eve::Result DialoguePaymentAdapter::execut } for (auto* effect : effects) participants.push_back(effect); if (participants.empty()) - return failure( - eve::DiagnosticCode::InvalidArgument, "dialogue payment has no account or effect", "payment"); + return failure(eve::DiagnosticCode::InvalidArgument, + "dialogue payment has no account or effect", "payment"); eve::transaction::Coordinator coordinator; return coordinator.execute(eve::transaction::TransactionContext(std::move(transactionId)), participants); @@ -227,7 +211,7 @@ eve::Result DialoguePaymentAdapter::execut eve::Result DialoguePaymentAdapter::executeStateMutation( std::string transactionId, const PaymentSpec& payment, eve::IStateMutation& provider, std::span mutations) const { - DialogueStateMutationParticipant state(provider, mutations); + DialogueStateMutationParticipant state(provider, mutations); std::array effects{&state}; return execute(std::move(transactionId), payment, effects); } diff --git a/src/modules/dialogue/DialoguePayment.h b/src/modules/dialogue/DialoguePayment.h index 4a44faee..ced92bd5 100644 --- a/src/modules/dialogue/DialoguePayment.h +++ b/src/modules/dialogue/DialoguePayment.h @@ -83,38 +83,31 @@ public: * @param provider Authoritative StatePatch-compatible mutation provider. * @param mutations Persistent or volatile mutations owned by this participant. */ - DialogueStateMutationParticipant(eve::IStateMutation& provider, - std::span mutations); + DialogueStateMutationParticipant(eve::IStateMutation& provider, std::span mutations); - DialogueStateMutationParticipant(const DialogueStateMutationParticipant&) = delete; + DialogueStateMutationParticipant(const DialogueStateMutationParticipant&) = delete; DialogueStateMutationParticipant& operator=(const DialogueStateMutationParticipant&) = delete; - DialogueStateMutationParticipant(DialogueStateMutationParticipant&&) = delete; - DialogueStateMutationParticipant& operator=(DialogueStateMutationParticipant&&) = delete; - ~DialogueStateMutationParticipant() override = default; + DialogueStateMutationParticipant(DialogueStateMutationParticipant&&) = delete; + DialogueStateMutationParticipant& operator=(DialogueStateMutationParticipant&&) = delete; + ~DialogueStateMutationParticipant() override = default; /** @brief Stable diagnostic name. */ - [[nodiscard]] std::string_view name() const noexcept override { - return "dialogue.state-mutation"; - } + [[nodiscard]] std::string_view name() const noexcept override { return "dialogue.state-mutation"; } /** @copydoc eve::transaction::ITransactionParticipant::prepare */ - [[nodiscard]] eve::Result prepare( - const eve::transaction::TransactionContext& context) override; + [[nodiscard]] eve::Result prepare(const eve::transaction::TransactionContext& context) override; /** @copydoc eve::transaction::ITransactionParticipant::commit */ - [[nodiscard]] eve::Result commit( - const eve::transaction::TransactionContext& context) override; + [[nodiscard]] eve::Result commit(const eve::transaction::TransactionContext& context) override; /** @copydoc eve::transaction::ITransactionParticipant::rollback */ - [[nodiscard]] eve::Result rollback( - const eve::transaction::TransactionContext& context) override; + [[nodiscard]] eve::Result rollback(const eve::transaction::TransactionContext& context) override; /** @copydoc eve::transaction::ITransactionParticipant::compensate */ - [[nodiscard]] eve::Result compensate( - const eve::transaction::TransactionContext& context) override; + [[nodiscard]] eve::Result compensate(const eve::transaction::TransactionContext& context) override; private: enum class Phase : std::uint8_t { Idle, Prepared, Committed, RolledBack }; - eve::IStateMutation& provider_; - std::vector mutations_; - Phase phase_ = Phase::Idle; + eve::IStateMutation& provider_; + std::vector mutations_; + Phase phase_ = Phase::Idle; }; /** diff --git a/src/modules/dialogue/DialogueState.cpp b/src/modules/dialogue/DialogueState.cpp index f980cdfd..e14040e5 100644 --- a/src/modules/dialogue/DialogueState.cpp +++ b/src/modules/dialogue/DialogueState.cpp @@ -11,8 +11,7 @@ namespace eve::dialogue { namespace { template -std::optional queryProviders(const std::string& subject, eve::IStateQuery* explicitProvider, - Query&& query) { +std::optional queryProviders(const std::string& subject, eve::IStateQuery* explicitProvider, Query&& query) { if (explicitProvider) { if (auto result = query(explicitProvider, subject); result.has_value()) return result; } @@ -79,8 +78,7 @@ eve::Result compileCondition(const eve::Value& specifi if (!child) return eve::Result::failure(child.status()); children.push_back(std::move(child).takeValue()); } - return eve::Result::success( - eve::decision::Condition::all(std::move(children))); + return eve::Result::success(eve::decision::Condition::all(std::move(children))); } if (!specification.isObject()) return conditionError("dialogue condition must be an object or array"); @@ -93,8 +91,7 @@ eve::Result compileCondition(const eve::Value& specifi if (!child) return eve::Result::failure(child.status()); children.push_back(std::move(child).takeValue()); } - return eve::Result::success( - eve::decision::Condition::all(std::move(children))); + return eve::Result::success(eve::decision::Condition::all(std::move(children))); } if (const eve::Value* any = specification.find("any")) { if (!any->isArray()) return conditionError("condition 'any' must be an array", "any"); @@ -105,8 +102,7 @@ eve::Result compileCondition(const eve::Value& specifi if (!child) return eve::Result::failure(child.status()); children.push_back(std::move(child).takeValue()); } - return eve::Result::success( - eve::decision::Condition::any(std::move(children))); + return eve::Result::success(eve::decision::Condition::any(std::move(children))); } if (const eve::Value* negated = specification.find("not")) { auto child = compileCondition(*negated); @@ -119,14 +115,12 @@ eve::Result compileCondition(const eve::Value& specifi if (!tag) tag = specification.find("hasTag"); if (tag) { if (!tag->isString()) return conditionError("tag condition requires a string tag", "tag"); - return eve::Result::success( - eve::decision::Condition::hasTag(tag->asString())); + return eve::Result::success(eve::decision::Condition::hasTag(tag->asString())); } const eve::Value* authority = specification.find("authority"); if (authority) { - if (!authority->isString()) - return conditionError("authority condition requires a string scope", "authority"); + if (!authority->isString()) return conditionError("authority condition requires a string scope", "authority"); return eve::Result::success( eve::decision::Condition::authorityCheck(authority->asString())); } @@ -162,10 +156,9 @@ eve::Result compileCondition(const eve::Value& specifi if (!nameValue->isString() || !operation->isString()) return conditionError("comparison condition requires string key and op"); const std::string key = nameValue->asString(); - const std::string op = operation->asString(); + const std::string op = operation->asString(); if (op == "has") - return eve::Result::success( - eve::decision::Condition::hasAttribute(key)); + return eve::Result::success(eve::decision::Condition::hasAttribute(key)); if (op == "missing") return eve::Result::success( eve::decision::Condition::not_(eve::decision::Condition::hasAttribute(key))); @@ -201,8 +194,7 @@ eve::Result compileCondition(const eve::Value& specifi eve::Result mutationUnavailable() { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Unsupported, - "dialogue world mutation requires an IStateMutation capability", + eve::DiagnosticCode::Unsupported, "dialogue world mutation requires an IStateMutation capability", "dialogue.state.mutation")); } @@ -210,43 +202,43 @@ eve::Result mutationUnavailable() { eve::Value toCanonicalValue(const eve::StateValue& value) { switch (value.kind()) { - case eve::StateValue::Kind::Null: return eve::Value::null(); - case eve::StateValue::Kind::Int: return eve::Value::integer(value.asInt()); - case eve::StateValue::Kind::Float: return eve::Value::number(value.asDouble()); - case eve::StateValue::Kind::Bool: return eve::Value::boolean(value.asBool()); - case eve::StateValue::Kind::String: return eve::Value::string(value.asString()); - case eve::StateValue::Kind::Array: { - eve::Value::Array result; - result.reserve(value.arraySize()); - for (std::size_t i = 0; i < value.arraySize(); ++i) result.push_back(toCanonicalValue(value.at(i))); - return eve::Value::array(std::move(result)); - } - case eve::StateValue::Kind::Object: { - eve::Value::Object result; - for (const auto& key : value.keys()) result.emplace(key, toCanonicalValue(*value.find(key))); - return eve::Value::object(std::move(result)); - } + case eve::StateValue::Kind::Null: return eve::Value::null(); + case eve::StateValue::Kind::Int: return eve::Value::integer(value.asInt()); + case eve::StateValue::Kind::Float: return eve::Value::number(value.asDouble()); + case eve::StateValue::Kind::Bool: return eve::Value::boolean(value.asBool()); + case eve::StateValue::Kind::String: return eve::Value::string(value.asString()); + case eve::StateValue::Kind::Array: { + eve::Value::Array result; + result.reserve(value.arraySize()); + for (std::size_t i = 0; i < value.arraySize(); ++i) result.push_back(toCanonicalValue(value.at(i))); + return eve::Value::array(std::move(result)); + } + case eve::StateValue::Kind::Object: { + eve::Value::Object result; + for (const auto& key : value.keys()) result.emplace(key, toCanonicalValue(*value.find(key))); + return eve::Value::object(std::move(result)); + } } return eve::Value::null(); } eve::StateValue toDialogueStateValue(const eve::Value& value) { switch (value.type()) { - case eve::Value::Type::Null: return eve::StateValue::null(); - case eve::Value::Type::Int64: return eve::StateValue::integer(value.asInt()); - case eve::Value::Type::Double: return eve::StateValue::number(value.asDouble()); - case eve::Value::Type::Bool: return eve::StateValue::boolean(value.asBool()); - case eve::Value::Type::String: return eve::StateValue::string(value.asString()); - case eve::Value::Type::Array: { - eve::StateValue result = eve::StateValue::array(); - for (std::size_t i = 0; i < value.arraySize(); ++i) result.pushBack(toDialogueStateValue(value.at(i))); - return result; - } - case eve::Value::Type::Object: { - eve::StateValue result = eve::StateValue::object(); - for (const auto& key : value.keys()) result.set(key, toDialogueStateValue(*value.find(key))); - return result; - } + case eve::Value::Type::Null: return eve::StateValue::null(); + case eve::Value::Type::Int64: return eve::StateValue::integer(value.asInt()); + case eve::Value::Type::Double: return eve::StateValue::number(value.asDouble()); + case eve::Value::Type::Bool: return eve::StateValue::boolean(value.asBool()); + case eve::Value::Type::String: return eve::StateValue::string(value.asString()); + case eve::Value::Type::Array: { + eve::StateValue result = eve::StateValue::array(); + for (std::size_t i = 0; i < value.arraySize(); ++i) result.pushBack(toDialogueStateValue(value.at(i))); + return result; + } + case eve::Value::Type::Object: { + eve::StateValue result = eve::StateValue::object(); + for (const auto& key : value.keys()) result.set(key, toDialogueStateValue(*value.find(key))); + return result; + } } return eve::StateValue::null(); } @@ -270,9 +262,8 @@ std::optional StateEvaluationContext::hasTag(std::string_view tag) const { std::optional StateEvaluationContext::attribute(std::string_view key) const { auto result = queryProviders( - subject_, provider_, [&](eve::IStateQuery* provider, const std::string& subject) { - return provider->attribute(subject, key); - }); + subject_, provider_, + [&](eve::IStateQuery* provider, const std::string& subject) { return provider->attribute(subject, key); }); if (result.has_value()) return result; return value(key); } @@ -285,9 +276,8 @@ std::optional StateEvaluationContext::resource(std::string_view key) std::optional StateEvaluationContext::state(std::string_view key) const { auto result = queryProviders( - subject_, provider_, [&](eve::IStateQuery* provider, const std::string& subject) { - return provider->state(subject, key); - }); + subject_, provider_, + [&](eve::IStateQuery* provider, const std::string& subject) { return provider->state(subject, key); }); if (result.has_value()) return result; return value(key); } @@ -298,8 +288,8 @@ std::optional StateEvaluationContext::authority(std::string_view scope) co }); } -std::optional StateEvaluationContext::policy( - std::string_view name, const eve::Value& arguments) const { +std::optional StateEvaluationContext::policy(std::string_view name, + const eve::Value& arguments) const { if (!policyEvaluator_) return std::nullopt; return policyEvaluator_(name, arguments); } @@ -339,13 +329,11 @@ std::optional DialogueStateContext::state(std::string_view key) cons return context.state(key); } -eve::Result DialogueStateContext::compileCondition( - const eve::Value& specification) const { +eve::Result DialogueStateContext::compileCondition(const eve::Value& specification) const { return ::eve::dialogue::compileCondition(specification); } -eve::decision::ConditionResult DialogueStateContext::evaluate( - const eve::decision::Condition& condition) const { +eve::decision::ConditionResult DialogueStateContext::evaluate(const eve::decision::Condition& condition) const { auto context = evaluationContext(); return condition.evaluate(context); } @@ -354,17 +342,16 @@ eve::decision::ConditionResult DialogueStateContext::evaluate(const eve::Value& auto compiled = compileCondition(specification); if (!compiled) { const eve::Diagnostic* diagnostic = compiled.error(); - const std::string message = diagnostic ? diagnostic->message() : "invalid dialogue condition"; - return eve::decision::ConditionResult::failed( - eve::decision::ConditionReasonCode::InvalidCondition, eve::Value(), - eve::Value::Object{{"error", eve::Value(message)}}); + const std::string message = diagnostic ? diagnostic->message() : "invalid dialogue condition"; + return eve::decision::ConditionResult::failed(eve::decision::ConditionReasonCode::InvalidCondition, + eve::Value(), eve::Value::Object{{"error", eve::Value(message)}}); } eve::decision::Condition condition = std::move(compiled).takeValue(); return evaluate(condition); } -eve::Result DialogueStateContext::apply( - std::span mutations, const eve::MutationContext& context) const { +eve::Result DialogueStateContext::apply(std::span mutations, + const eve::MutationContext& context) const { eve::IStateMutation* provider = mutationProvider_; if (!provider) provider = eve::cap::query(); if (!provider) return mutationUnavailable(); diff --git a/src/modules/dialogue/DialogueState.h b/src/modules/dialogue/DialogueState.h index 9ce81551..df64358e 100644 --- a/src/modules/dialogue/DialogueState.h +++ b/src/modules/dialogue/DialogueState.h @@ -34,11 +34,11 @@ enum class CommandRequestKind : std::uint8_t { * operation/action in their own module. */ struct CommandRequest { - std::string name; - CommandRequestKind kind = CommandRequestKind::Operation; - eve::Value arguments = eve::Value::Object{}; - eve::Value bindings = eve::Value::Object{}; - eve::Value locals = eve::Value::Object{}; + std::string name; + CommandRequestKind kind = CommandRequestKind::Operation; + eve::Value arguments = eve::Value::Object{}; + eve::Value bindings = eve::Value::Object{}; + eve::Value locals = eve::Value::Object{}; /** @brief Optional atomic money/reputation payment for this command. */ PaymentSpec payment; /** @brief Optional StatePatch mutations committed in the same transaction. */ @@ -49,8 +49,8 @@ struct CommandRequest { struct CommandResponse { enum class Status : std::uint8_t { Completed, Blocked, Failed }; - Status status = Status::Completed; - eve::Value value; + Status status = Status::Completed; + eve::Value value; std::string error; }; @@ -89,8 +89,7 @@ public: void setProvider(eve::IStateQuery* provider) noexcept { provider_ = provider; } /** @brief Install a read-only policy callback without retaining its arguments. */ void setPolicyEvaluator( - std::function(std::string_view, const eve::Value&)> - evaluator) { + std::function(std::string_view, const eve::Value&)> evaluator) { policyEvaluator_ = std::move(evaluator); } @@ -107,14 +106,13 @@ public: /** @copydoc eve::decision::EvaluationContext::authority */ [[nodiscard]] std::optional authority(std::string_view scope) const override; /** @copydoc eve::decision::EvaluationContext::policy */ - [[nodiscard]] std::optional policy( - std::string_view name, const eve::Value& arguments) const override; + [[nodiscard]] std::optional policy(std::string_view name, + const eve::Value& arguments) const override; private: - std::string subject_; + std::string subject_; eve::IStateQuery* provider_ = nullptr; - std::function(std::string_view, const eve::Value&)> - policyEvaluator_; + std::function(std::string_view, const eve::Value&)> policyEvaluator_; }; /** @@ -141,8 +139,7 @@ public: void setMutationProvider(eve::IStateMutation* provider) noexcept { mutationProvider_ = provider; } /** @brief Install a read-only policy evaluator used by PolicyCall conditions. */ void setPolicyEvaluator( - std::function(std::string_view, const eve::Value&)> - evaluator) { + std::function(std::string_view, const eve::Value&)> evaluator) { policyEvaluator_ = std::move(evaluator); } @@ -158,26 +155,23 @@ public: [[nodiscard]] std::optional state(std::string_view key) const; /** @brief Compile a canonical condition specification to decision::Condition. */ - [[nodiscard]] eve::Result compileCondition( - const eve::Value& specification) const; + [[nodiscard]] eve::Result compileCondition(const eve::Value& specification) const; /** @brief Evaluate an already compiled shared condition with explanation. */ - [[nodiscard]] eve::decision::ConditionResult evaluate( - const eve::decision::Condition& condition) const; + [[nodiscard]] eve::decision::ConditionResult evaluate(const eve::decision::Condition& condition) const; /** @brief Compile and evaluate a canonical condition specification. */ [[nodiscard]] eve::decision::ConditionResult evaluate(const eve::Value& specification) const; /** @brief Apply a complete mutation set through one authoritative provider. */ - [[nodiscard]] eve::Result apply( - std::span mutations, const eve::MutationContext& context) const; + [[nodiscard]] eve::Result apply(std::span mutations, + const eve::MutationContext& context) const; private: StateEvaluationContext evaluationContext() const; - std::string subject_; - eve::IStateQuery* queryProvider_ = nullptr; + std::string subject_; + eve::IStateQuery* queryProvider_ = nullptr; eve::IStateMutation* mutationProvider_ = nullptr; - std::function(std::string_view, const eve::Value&)> - policyEvaluator_; + std::function(std::string_view, const eve::Value&)> policyEvaluator_; }; } // namespace eve::dialogue diff --git a/src/modules/dialogue/DnutParser.cpp b/src/modules/dialogue/DnutParser.cpp index dc1d5a15..ceea465c 100644 --- a/src/modules/dialogue/DnutParser.cpp +++ b/src/modules/dialogue/DnutParser.cpp @@ -27,14 +27,10 @@ std::string floatToString(double v) { std::string scalarToString(const DataValue &v) { switch (v.kind()) { - case DataValue::Kind::String: - return v.asString(); - case DataValue::Kind::Int: - return std::to_string(v.asInt()); - case DataValue::Kind::Float: - return floatToString(v.asDouble()); - case DataValue::Kind::Bool: - return v.asBool() ? "true" : "false"; + case DataValue::Kind::String: return v.asString(); + case DataValue::Kind::Int: return std::to_string(v.asInt()); + case DataValue::Kind::Float: return floatToString(v.asDouble()); + case DataValue::Kind::Bool: return v.asBool() ? "true" : "false"; default: return {}; } diff --git a/src/modules/economy/EconomyLedgerResourceAccount.cpp b/src/modules/economy/EconomyLedgerResourceAccount.cpp index d1e7ae2c..b0d5c341 100644 --- a/src/modules/economy/EconomyLedgerResourceAccount.cpp +++ b/src/modules/economy/EconomyLedgerResourceAccount.cpp @@ -18,57 +18,48 @@ using eve::resource::ReservationId; using eve::resource::ResourceId; eve::Diagnostic invalidArgument(std::string message, std::string path = {}) { - return eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, std::move(message), - std::move(path)); + return eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, std::move(message), std::move(path)); } eve::Diagnostic unsupported(std::string message, std::string path = {}) { - return eve::Diagnostic::error(eve::DiagnosticCode::Unsupported, std::move(message), - std::move(path)); + return eve::Diagnostic::error(eve::DiagnosticCode::Unsupported, std::move(message), std::move(path)); } eve::Diagnostic conflict(std::string message, std::string path = {}) { - return eve::Diagnostic::error(eve::DiagnosticCode::Conflict, std::move(message), - std::move(path)); + return eve::Diagnostic::error(eve::DiagnosticCode::Conflict, std::move(message), std::move(path)); } eve::Diagnostic invariantFailure(std::string message, std::string path = {}) { - return eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, std::move(message), - std::move(path)); + return eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, std::move(message), std::move(path)); } template eve::Result exceptionFailure(std::string_view operation, const std::exception& exception) { return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::Failed, - std::string(operation) + " failed while staging an atomic ledger candidate: " + - exception.what(), + std::string(operation) + " failed while staging an atomic ledger candidate: " + exception.what(), "resource.account")); } template <> -eve::Result exceptionFailure(std::string_view operation, - const std::exception& exception) { +eve::Result exceptionFailure(std::string_view operation, const std::exception& exception) { return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::Failed, - std::string(operation) + " failed while staging an atomic ledger candidate: " + - exception.what(), + std::string(operation) + " failed while staging an atomic ledger candidate: " + exception.what(), "resource.account")); } template eve::Result unknownExceptionFailure(std::string_view operation) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, - std::string(operation) + " failed while staging an atomic ledger candidate", + eve::DiagnosticCode::Failed, std::string(operation) + " failed while staging an atomic ledger candidate", "resource.account")); } template <> eve::Result unknownExceptionFailure(std::string_view operation) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, - std::string(operation) + " failed while staging an atomic ledger candidate", + eve::DiagnosticCode::Failed, std::string(operation) + " failed while staging an atomic ledger candidate", "resource.account")); } @@ -88,58 +79,49 @@ eve::Status insufficientStatus(const Affordability& affordability) { } return eve::Status::failure( eve::StatusCode::Rejected, - eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, - "resource cost exceeds available balance", "cost", - std::move(details))); + eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, "resource cost exceeds available balance", + "cost", std::move(details))); } eve::Status unknownReservationStatus() { return eve::Status::failure( eve::StatusCode::NotFound, - eve::Diagnostic::error(eve::DiagnosticCode::NotFound, - "resource reservation does not belong to this account", + eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "resource reservation does not belong to this account", "reservation")); } eve::Status foreignReservationStatus() { return eve::Status::failure( eve::StatusCode::Conflict, - eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "resource reservation belongs to a different account", + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "resource reservation belongs to a different account", "reservation.account")); } eve::Status duplicateReservationStatus() { - return eve::Status::failure( - eve::StatusCode::Conflict, - eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "resource reservation is no longer active", "reservation")); + return eve::Status::failure(eve::StatusCode::Conflict, + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, + "resource reservation is no longer active", "reservation")); } eve::Status mismatchedReservationStatus() { return eve::Status::failure( eve::StatusCode::Conflict, eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "resource reservation cost does not match its account record", - "reservation.cost")); + "resource reservation cost does not match its account record", "reservation.cost")); } eve::Status receiptIdExhaustedStatus() { - return eve::Status::failure( - eve::StatusCode::Failed, - invariantFailure("resource receipt id exhausted", "receipt")); + return eve::Status::failure(eve::StatusCode::Failed, invariantFailure("resource receipt id exhausted", "receipt")); } eve::Status accountIdentityUnavailableStatus() { - return eve::Status::failure( - eve::StatusCode::Failed, - invariantFailure("resource account has no valid identity nonce", "account")); + return eve::Status::failure(eve::StatusCode::Failed, + invariantFailure("resource account has no valid identity nonce", "account")); } } // namespace -EconomyLedgerResourceAccount::EconomyLedgerResourceAccount(EconomyLedger& ledger) - : ledger_(ledger) { +EconomyLedgerResourceAccount::EconomyLedgerResourceAccount(EconomyLedger& ledger) : ledger_(ledger) { auto nonce = eve::resource::allocateAccountNonce(); if (nonce) accountNonce_ = std::move(nonce).takeValue(); @@ -147,8 +129,7 @@ EconomyLedgerResourceAccount::EconomyLedgerResourceAccount(EconomyLedger& ledger nonce.ignore("resource account identity exhausted; adapter remains unusable"); } -eve::Result EconomyLedgerResourceAccount::balanceOf( - const ResourceId& resource) const { +eve::Result EconomyLedgerResourceAccount::balanceOf(const ResourceId& resource) const { const int value = ledger_.get(resource.value()); if (value < 0) return eve::Result::failure( @@ -156,8 +137,7 @@ eve::Result EconomyLedgerResourceAccount::balanceOf( return eve::Result::success(static_cast(value)); } -eve::Result EconomyLedgerResourceAccount::activeReservationsFor( - const ResourceId& resource) const { +eve::Result EconomyLedgerResourceAccount::activeReservationsFor(const ResourceId& resource) const { std::int64_t total = 0; for (const auto& [id, record] : reservations_) { (void)id; @@ -173,17 +153,14 @@ eve::Result EconomyLedgerResourceAccount::activeReservationsFor( return eve::Result::success(total); } -eve::Result EconomyLedgerResourceAccount::validateLedgerRange( - const CostSpec& cost, bool debit) const { +eve::Result EconomyLedgerResourceAccount::validateLedgerRange(const CostSpec& cost, bool debit) const { auto valid = validateCost(cost); if (!valid) return eve::Result::failure(valid.status()); for (const auto& item : cost.items()) { if (item.amount.value() > std::numeric_limits::max()) - return eve::Result::failure( - eve::Status::failure( - eve::StatusCode::Unsupported, - unsupported("EconomyLedger cannot represent this resource amount", - item.resource.value()))); + return eve::Result::failure(eve::Status::failure( + eve::StatusCode::Unsupported, + unsupported("EconomyLedger cannot represent this resource amount", item.resource.value()))); if (debit) continue; auto balance = balanceOf(item.resource); @@ -191,29 +168,23 @@ eve::Result EconomyLedgerResourceAccount::validateLedgerRange( const auto next = balance.value() + item.amount.value(); if (next > std::numeric_limits::max()) return eve::Result::failure( - eve::Status::failure( - eve::StatusCode::Failed, - invariantFailure("economy ledger credit would overflow", item.resource.value()))); + eve::Status::failure(eve::StatusCode::Failed, + invariantFailure("economy ledger credit would overflow", item.resource.value()))); const int cap = ledger_.getCap(item.resource.value()); if (cap > 0 && next > cap) - return eve::Result::failure( - eve::Status::failure( - eve::StatusCode::Rejected, - eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, - "economy ledger capacity would be exceeded", item.resource.value()))); + return eve::Result::failure(eve::Status::failure( + eve::StatusCode::Rejected, + eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, + "economy ledger capacity would be exceeded", item.resource.value()))); } return eve::Result::success(); } -eve::Result EconomyLedgerResourceAccount::canAfford( - const CostSpec& cost) const { +eve::Result EconomyLedgerResourceAccount::canAfford(const CostSpec& cost) const { try { - if (accountNonce_.isZero()) - return eve::Result::failure(accountIdentityUnavailableStatus()); + if (accountNonce_.isZero()) return eve::Result::failure(accountIdentityUnavailableStatus()); auto representable = validateLedgerRange(cost, true); - if (!representable) - return eve::Result::failure(representable.status()); + if (!representable) return eve::Result::failure(representable.status()); Affordability result; result.affordable = true; @@ -223,13 +194,10 @@ eve::Result EconomyLedgerResourceAccount::canAfford( auto reserved = activeReservationsFor(item.resource); if (!reserved) return eve::Result::failure(reserved.status()); - const std::int64_t available = balance.value() >= reserved.value() - ? balance.value() - reserved.value() - : 0; + const std::int64_t available = balance.value() >= reserved.value() ? balance.value() - reserved.value() : 0; if (available < item.amount.value()) { result.affordable = false; - result.shortfalls.push_back( - {item.resource, item.amount, Amount(available)}); + result.shortfalls.push_back({item.resource, item.amount, Amount(available)}); } } return eve::Result::success(std::move(result)); @@ -245,27 +213,20 @@ eve::Result EconomyLedgerResourceAccount::reserve(const CostSpec& c auto affordability = canAfford(cost); if (!affordability) return eve::Result::failure(affordability.status()); const auto& checked = affordability.value(); - if (!checked.affordable) - return eve::Result::failure(insufficientStatus(checked)); + if (!checked.affordable) return eve::Result::failure(insufficientStatus(checked)); if (nextReservation_.value() == std::numeric_limits::max()) - return eve::Result::failure( - eve::Status::failure(eve::StatusCode::Failed, - invariantFailure("resource reservation id exhausted", - "reservation"))); + return eve::Result::failure(eve::Status::failure( + eve::StatusCode::Failed, invariantFailure("resource reservation id exhausted", "reservation"))); - const ReservationId id = nextReservation_; - const auto [it, inserted] = reservations_.emplace( - id, ReservationRecord{cost, ReservationState::Active}); + const ReservationId id = nextReservation_; + const auto [it, inserted] = reservations_.emplace(id, ReservationRecord{cost, ReservationState::Active}); if (!inserted) - return eve::Result::failure( - eve::Status::failure(eve::StatusCode::Conflict, - conflict("resource reservation id was already used", - "reservation"))); + return eve::Result::failure(eve::Status::failure( + eve::StatusCode::Conflict, conflict("resource reservation id was already used", "reservation"))); nextReservation_ = ReservationId(id.value() + 1); - return eve::Result::success( - Reservation{accountNonce_, id, it->second.cost}, - eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(Reservation{accountNonce_, id, it->second.cost}, + eve::Status::success(eve::StatusCode::Applied)); } catch (const std::exception& exception) { return exceptionFailure("reserve", exception); } catch (...) { @@ -280,22 +241,16 @@ eve::Result EconomyLedgerResourceAccount::applyDelta(const CostSpec& cost, const int amount = static_cast(item.amount.value()); if (debit) { if (!candidate.debit(item.resource.value(), amount)) - return eve::Result::failure( - eve::Status::failure( - eve::StatusCode::Rejected, - eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, - "resource debit would make balance negative", - item.resource.value()))); + return eve::Result::failure(eve::Status::failure( + eve::StatusCode::Rejected, + eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, + "resource debit would make balance negative", item.resource.value()))); } else { if (candidate.credit(item.resource.value(), amount) != amount) - return eve::Result::failure( - eve::Status::failure( - eve::StatusCode::Rejected, - eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, - "resource credit was not applied in full", - item.resource.value()))); + return eve::Result::failure(eve::Status::failure( + eve::StatusCode::Rejected, + eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, + "resource credit was not applied in full", item.resource.value()))); } } ledger_.swap(candidate); @@ -312,17 +267,15 @@ eve::Result EconomyLedgerResourceAccount::debit(const CostSpec& cost) { auto affordability = canAfford(cost); if (!affordability) return eve::Result::failure(affordability.status()); const auto& checked = affordability.value(); - if (!checked.affordable) - return eve::Result::failure(insufficientStatus(checked)); + if (!checked.affordable) return eve::Result::failure(insufficientStatus(checked)); if (nextReceipt_.value() == std::numeric_limits::max()) return eve::Result::failure(receiptIdExhaustedStatus()); Receipt receipt{accountNonce_, nextReceipt_, ReservationId{}, ReceiptOperation::Debit, cost}; - auto applied = applyDelta(cost, true); + auto applied = applyDelta(cost, true); if (!applied) return eve::Result::failure(applied.status()); nextReceipt_ = eve::resource::ReceiptId(nextReceipt_.value() + 1); - return eve::Result::success( - std::move(receipt), eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(std::move(receipt), eve::Status::success(eve::StatusCode::Applied)); } catch (const std::exception& exception) { return exceptionFailure("debit", exception); } catch (...) { @@ -332,19 +285,17 @@ eve::Result EconomyLedgerResourceAccount::debit(const CostSpec& cost) { eve::Result EconomyLedgerResourceAccount::credit(const CostSpec& cost) { try { - if (accountNonce_.isZero()) - return eve::Result::failure(accountIdentityUnavailableStatus()); + if (accountNonce_.isZero()) return eve::Result::failure(accountIdentityUnavailableStatus()); auto representable = validateLedgerRange(cost, false); if (!representable) return eve::Result::failure(representable.status()); if (nextReceipt_.value() == std::numeric_limits::max()) return eve::Result::failure(receiptIdExhaustedStatus()); Receipt receipt{accountNonce_, nextReceipt_, ReservationId{}, ReceiptOperation::Credit, cost}; - auto applied = applyDelta(cost, false); + auto applied = applyDelta(cost, false); if (!applied) return eve::Result::failure(applied.status()); nextReceipt_ = eve::resource::ReceiptId(nextReceipt_.value() + 1); - return eve::Result::success( - std::move(receipt), eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(std::move(receipt), eve::Status::success(eve::StatusCode::Applied)); } catch (const std::exception& exception) { return exceptionFailure("credit", exception); } catch (...) { @@ -363,11 +314,9 @@ eve::Result EconomyLedgerResourceAccount::activeReservationsAreCovered() c auto reserved = activeReservationsFor(item.resource); if (!reserved) return eve::Result::failure(reserved.status()); if (balance.value() < reserved.value()) - return eve::Result::failure( - eve::Status::failure( - eve::StatusCode::Conflict, - conflict("external ledger change invalidated active reservations", - item.resource.value()))); + return eve::Result::failure(eve::Status::failure( + eve::StatusCode::Conflict, + conflict("external ledger change invalidated active reservations", item.resource.value()))); } } return eve::Result::success(); @@ -380,18 +329,13 @@ eve::Result EconomyLedgerResourceAccount::activeReservationsAreCovered() c eve::Result EconomyLedgerResourceAccount::commit(const Reservation& reservation) { try { - if (accountNonce_.isZero()) - return eve::Result::failure(accountIdentityUnavailableStatus()); + if (accountNonce_.isZero()) return eve::Result::failure(accountIdentityUnavailableStatus()); if (!reservation.isValid()) - return eve::Result::failure( - invalidArgument("resource reservation is invalid", "reservation")); - if (reservation.account != accountNonce_) - return eve::Result::failure(foreignReservationStatus()); + return eve::Result::failure(invalidArgument("resource reservation is invalid", "reservation")); + if (reservation.account != accountNonce_) return eve::Result::failure(foreignReservationStatus()); const auto it = reservations_.find(reservation.id); - if (it == reservations_.end()) - return eve::Result::failure(unknownReservationStatus()); - if (it->second.cost != reservation.cost) - return eve::Result::failure(mismatchedReservationStatus()); + if (it == reservations_.end()) return eve::Result::failure(unknownReservationStatus()); + if (it->second.cost != reservation.cost) return eve::Result::failure(mismatchedReservationStatus()); if (it->second.state != ReservationState::Active) return eve::Result::failure(duplicateReservationStatus()); @@ -402,14 +346,12 @@ eve::Result EconomyLedgerResourceAccount::commit(const Reservation& res if (nextReceipt_.value() == std::numeric_limits::max()) return eve::Result::failure(receiptIdExhaustedStatus()); - Receipt receipt{accountNonce_, nextReceipt_, reservation.id, ReceiptOperation::Debit, - it->second.cost}; - auto applied = applyDelta(it->second.cost, true); + Receipt receipt{accountNonce_, nextReceipt_, reservation.id, ReceiptOperation::Debit, it->second.cost}; + auto applied = applyDelta(it->second.cost, true); if (!applied) return eve::Result::failure(applied.status()); it->second.state = ReservationState::Committed; - nextReceipt_ = eve::resource::ReceiptId(nextReceipt_.value() + 1); - return eve::Result::success( - std::move(receipt), eve::Status::success(eve::StatusCode::Applied)); + nextReceipt_ = eve::resource::ReceiptId(nextReceipt_.value() + 1); + return eve::Result::success(std::move(receipt), eve::Status::success(eve::StatusCode::Applied)); } catch (const std::exception& exception) { return exceptionFailure("commit", exception); } catch (...) { @@ -419,17 +361,13 @@ eve::Result EconomyLedgerResourceAccount::commit(const Reservation& res eve::Result EconomyLedgerResourceAccount::rollback(const Reservation& reservation) { try { - if (accountNonce_.isZero()) - return eve::Result::failure(accountIdentityUnavailableStatus()); + if (accountNonce_.isZero()) return eve::Result::failure(accountIdentityUnavailableStatus()); if (!reservation.isValid()) - return eve::Result::failure( - invalidArgument("resource reservation is invalid", "reservation")); - if (reservation.account != accountNonce_) - return eve::Result::failure(foreignReservationStatus()); + return eve::Result::failure(invalidArgument("resource reservation is invalid", "reservation")); + if (reservation.account != accountNonce_) return eve::Result::failure(foreignReservationStatus()); const auto it = reservations_.find(reservation.id); if (it == reservations_.end()) return eve::Result::failure(unknownReservationStatus()); - if (it->second.cost != reservation.cost) - return eve::Result::failure(mismatchedReservationStatus()); + if (it->second.cost != reservation.cost) return eve::Result::failure(mismatchedReservationStatus()); if (it->second.state != ReservationState::Active) return eve::Result::failure(duplicateReservationStatus()); it->second.state = ReservationState::RolledBack; diff --git a/src/modules/economy/EconomyLedgerResourceAccount.h b/src/modules/economy/EconomyLedgerResourceAccount.h index 3592c3ce..c874df31 100644 --- a/src/modules/economy/EconomyLedgerResourceAccount.h +++ b/src/modules/economy/EconomyLedgerResourceAccount.h @@ -31,10 +31,10 @@ public: /** @brief Bind an account adapter to an existing caller-owned ledger. */ explicit EconomyLedgerResourceAccount(EconomyLedger& ledger); - EconomyLedgerResourceAccount(const EconomyLedgerResourceAccount&) = delete; + EconomyLedgerResourceAccount(const EconomyLedgerResourceAccount&) = delete; EconomyLedgerResourceAccount& operator=(const EconomyLedgerResourceAccount&) = delete; - EconomyLedgerResourceAccount(EconomyLedgerResourceAccount&&) = delete; - EconomyLedgerResourceAccount& operator=(EconomyLedgerResourceAccount&&) = delete; + EconomyLedgerResourceAccount(EconomyLedgerResourceAccount&&) = delete; + EconomyLedgerResourceAccount& operator=(EconomyLedgerResourceAccount&&) = delete; ~EconomyLedgerResourceAccount() override = default; @@ -43,48 +43,39 @@ public: const eve::resource::CostSpec& cost) const override; /** @copydoc eve::resource::IResourceAccount::reserve */ - [[nodiscard]] eve::Result reserve( - const eve::resource::CostSpec& cost) override; + [[nodiscard]] eve::Result reserve(const eve::resource::CostSpec& cost) override; /** @copydoc eve::resource::IResourceAccount::debit */ - [[nodiscard]] eve::Result debit( - const eve::resource::CostSpec& cost) override; + [[nodiscard]] eve::Result debit(const eve::resource::CostSpec& cost) override; /** @copydoc eve::resource::IResourceAccount::credit */ - [[nodiscard]] eve::Result credit( - const eve::resource::CostSpec& cost) override; + [[nodiscard]] eve::Result credit(const eve::resource::CostSpec& cost) override; /** @copydoc eve::resource::IResourceAccount::commit */ - [[nodiscard]] eve::Result commit( - const eve::resource::Reservation& reservation) override; + [[nodiscard]] eve::Result commit(const eve::resource::Reservation& reservation) override; /** @copydoc eve::resource::IResourceAccount::rollback */ - [[nodiscard]] eve::Result rollback( - const eve::resource::Reservation& reservation) override; + [[nodiscard]] eve::Result rollback(const eve::resource::Reservation& reservation) override; private: enum class ReservationState : std::uint8_t { Active, Committed, RolledBack }; struct ReservationRecord { eve::resource::CostSpec cost; - ReservationState state = ReservationState::Active; + ReservationState state = ReservationState::Active; }; - [[nodiscard]] eve::Result balanceOf( - const eve::resource::ResourceId& resource) const; - [[nodiscard]] eve::Result activeReservationsFor( - const eve::resource::ResourceId& resource) const; - [[nodiscard]] eve::Result activeReservationsAreCovered() const; - [[nodiscard]] eve::Result applyDelta(const eve::resource::CostSpec& cost, - bool debit); - [[nodiscard]] eve::Result validateLedgerRange( - const eve::resource::CostSpec& cost, bool debit) const; + [[nodiscard]] eve::Result balanceOf(const eve::resource::ResourceId& resource) const; + [[nodiscard]] eve::Result activeReservationsFor(const eve::resource::ResourceId& resource) const; + [[nodiscard]] eve::Result activeReservationsAreCovered() const; + [[nodiscard]] eve::Result applyDelta(const eve::resource::CostSpec& cost, bool debit); + [[nodiscard]] eve::Result validateLedgerRange(const eve::resource::CostSpec& cost, bool debit) const; - EconomyLedger& ledger_; + EconomyLedger& ledger_; std::unordered_map reservations_; - eve::resource::AccountNonce accountNonce_; - eve::resource::ReservationId nextReservation_{1}; - eve::resource::ReceiptId nextReceipt_{1}; + eve::resource::AccountNonce accountNonce_; + eve::resource::ReservationId nextReservation_{1}; + eve::resource::ReceiptId nextReceipt_{1}; }; } // namespace eve::economy diff --git a/src/modules/editor/EditorAuthority.cpp b/src/modules/editor/EditorAuthority.cpp index 4c042678..fb4cae25 100644 --- a/src/modules/editor/EditorAuthority.cpp +++ b/src/modules/editor/EditorAuthority.cpp @@ -121,14 +121,12 @@ EditorResult LocalWorldAuthority::compensate(const Transacti } if (entry->second.receipt.afterRevision != target_->revision()) - return authorityError(EditorStatus::Conflict, - "editor.authority.revision-conflict", + return authorityError(EditorStatus::Conflict, "editor.authority.revision-conflict", "Target changed after the committed transaction"); auto* staging = dynamic_cast(target_); if (!staging) - return authorityError(EditorStatus::Unsupported, - "editor.authority.staging-unavailable", + return authorityError(EditorStatus::Unsupported, "editor.authority.staging-unavailable", "Target cannot stage a complete compensation candidate"); TransactionReceipt compensation; @@ -172,15 +170,14 @@ EditorResult LocalWorldAuthority::compensate(const Transacti try { result = candidate->applyDomainOperation(inverseOf(*operation)); } catch (const std::exception& exception) { - compensation.diagnostics.push_back( - {RuleId("editor.authority.candidate-exception"), DiagnosticSeverity::Error, - std::string("Compensation candidate threw: ") + exception.what()}); - return failed(EditorStatus::Failed, "editor.authority.candidate-exception", - "Compensation candidate threw"); + compensation.diagnostics.push_back({RuleId("editor.authority.candidate-exception"), + DiagnosticSeverity::Error, + std::string("Compensation candidate threw: ") + exception.what()}); + return failed(EditorStatus::Failed, "editor.authority.candidate-exception", "Compensation candidate threw"); } catch (...) { - compensation.diagnostics.push_back( - {RuleId("editor.authority.candidate-exception"), DiagnosticSeverity::Error, - "Compensation candidate threw an unknown exception"}); + compensation.diagnostics.push_back({RuleId("editor.authority.candidate-exception"), + DiagnosticSeverity::Error, + "Compensation candidate threw an unknown exception"}); return failed(EditorStatus::Failed, "editor.authority.candidate-exception", "Compensation candidate threw an unknown exception"); } diff --git a/src/modules/editor/EditorAuthority.h b/src/modules/editor/EditorAuthority.h index ea97556e..3e74a9ee 100644 --- a/src/modules/editor/EditorAuthority.h +++ b/src/modules/editor/EditorAuthority.h @@ -45,8 +45,7 @@ public: * @return Applied when the candidate is installed, otherwise a failure * with the original target still unchanged. */ - [[nodiscard]] virtual EditorResult commitDomainState( - std::unique_ptr candidate) = 0; + [[nodiscard]] virtual EditorResult commitDomainState(std::unique_ptr candidate) = 0; }; /** @brief Final validation and commit boundary for editor mutations. */ diff --git a/src/modules/editor/EditorIds.h b/src/modules/editor/EditorIds.h index 5e10988e..f69d9e82 100644 --- a/src/modules/editor/EditorIds.h +++ b/src/modules/editor/EditorIds.h @@ -19,9 +19,7 @@ using StrongEditorId = eve::UuidIdAdapter; /** @brief Hash functor for a strong editor identifier. */ template struct StrongEditorIdHash { - [[nodiscard]] std::size_t operator()(const Id& id) const noexcept { - return static_cast(id.hash()); - } + [[nodiscard]] std::size_t operator()(const Id& id) const noexcept { return static_cast(id.hash()); } }; /** diff --git a/src/modules/editor/EditorProperty.cpp b/src/modules/editor/EditorProperty.cpp index c6a3a8c0..2638ce3b 100644 --- a/src/modules/editor/EditorProperty.cpp +++ b/src/modules/editor/EditorProperty.cpp @@ -40,7 +40,7 @@ property_access::PropertyKind convertKind(PropertyType type) { } property_access::PropertyFlag convertFlags(PropertyFlag flags) { - using Flag = property_access::PropertyFlag; + using Flag = property_access::PropertyFlag; Flag result = Flag::None; if (hasPropertyFlag(flags, PropertyFlag::ReadOnly)) result = result | Flag::ReadOnly; if (hasPropertyFlag(flags, PropertyFlag::Advanced)) result = result | Flag::Advanced; @@ -64,7 +64,7 @@ const char *legacyRule(const std::string &code) { template EditorResult editorValidationError(const property_access::WriteResult &validation, - const PropertyDescriptor &descriptor) { + const PropertyDescriptor &descriptor) { EditorResult result; result.status = EditorStatus::Rejected; result.diagnostics.push_back({RuleId(legacyRule(validation.code)), DiagnosticSeverity::Error, @@ -76,20 +76,20 @@ EditorResult editorValidationError(const property_access::WriteResu property_access::PropertyDescriptor toPresentationDescriptor(const PropertyDescriptor &source) { property_access::PropertyDescriptor result; - result.path = source.path.value(); - result.displayName = source.displayNameKey; - result.description = source.descriptionKey; - result.category = source.category; - result.kind = convertKind(source.type); - result.flags = convertFlags(source.flags); - result.defaultValue = toPresentationValue(source.defaultValue); - result.numeric.minimum = source.numeric.minimum; - result.numeric.maximum = source.numeric.maximum; - result.numeric.step = source.numeric.step; - result.numeric.units = source.numeric.units; + result.path = source.path.value(); + result.displayName = source.displayNameKey; + result.description = source.descriptionKey; + result.category = source.category; + result.kind = convertKind(source.type); + result.flags = convertFlags(source.flags); + result.defaultValue = toPresentationValue(source.defaultValue); + result.numeric.minimum = source.numeric.minimum; + result.numeric.maximum = source.numeric.maximum; + result.numeric.step = source.numeric.step; + result.numeric.units = source.numeric.units; result.numeric.precision = source.numeric.precision; - result.choices = source.enumItems; - result.presenterHint = source.presenterHint; + result.choices = source.enumItems; + result.presenterHint = source.presenterHint; return result; } diff --git a/src/modules/editor/EditorProperty.h b/src/modules/editor/EditorProperty.h index 0bc0b16f..b85efc8b 100644 --- a/src/modules/editor/EditorProperty.h +++ b/src/modules/editor/EditorProperty.h @@ -119,8 +119,7 @@ public: * edited must override this method and return their authoritative * revision, including a non-zero initial revision. */ - [[nodiscard]] virtual eve::Result currentRevision( - const SelectionSnapshot& selection) const { + [[nodiscard]] virtual eve::Result currentRevision(const SelectionSnapshot& selection) const { (void)selection; return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::Unsupported, @@ -162,12 +161,12 @@ property_access::PropertyDescriptor toPresentationDescriptor(const PropertyDescr * @param value Editor-owned deterministic value tree. * @return Equivalent renderer-independent value tree. */ -eve::Value toPresentationValue(const EditorValue &value); +eve::Value toPresentationValue(const EditorValue& value); /** * @brief Convert a shared presentation value tree to the editor value tree. * @param value Renderer-independent deterministic value tree. * @return Equivalent editor value tree. */ -EditorValue toEditorValue(const eve::Value &value); +EditorValue toEditorValue(const eve::Value& value); } // namespace eve::editor diff --git a/src/modules/editor/EditorPropertyModel.cpp b/src/modules/editor/EditorPropertyModel.cpp index 37b2f785..cb077427 100644 --- a/src/modules/editor/EditorPropertyModel.cpp +++ b/src/modules/editor/EditorPropertyModel.cpp @@ -13,7 +13,7 @@ namespace { template property_access::WriteResult writeFailure(const EditorResult &result, std::string fallbackCode, - std::string fallbackMessage) { + std::string fallbackMessage) { if (!result.diagnostics.empty()) { const EditorDiagnostic &diagnostic = result.diagnostics.front(); if (!diagnostic.rule.empty() && !diagnostic.message.empty()) @@ -26,13 +26,13 @@ property_access::WriteResult writeFailure(const EditorResult &result, std::st TransactionId newPropertyTransactionId() { static std::atomic entropySequence{1}; - const eve::UuidV7Generator generator([](std::span bytes) { + const eve::UuidV7Generator generator([](std::span bytes) { const std::uint64_t seed = entropySequence.fetch_add(1, std::memory_order_relaxed); for (std::size_t index = 0; index < bytes.size(); ++index) bytes[index] = static_cast(seed >> ((index % sizeof(seed)) * 8u)); return true; }); - const auto generated = generator.generate(std::chrono::system_clock::now()); + const auto generated = generator.generate(std::chrono::system_clock::now()); if (!generated) { // A clock before the UUID epoch is not expected for a live editor, but // retain a valid canonical UUID path if a platform clock is malformed. @@ -54,16 +54,16 @@ TargetId selectionTarget(const SelectionSnapshot &selection) { EditorStatus editorStatusFor(eve::StatusCode code) { switch (code) { - case eve::StatusCode::Conflict: return EditorStatus::Conflict; - case eve::StatusCode::NotFound: return EditorStatus::NotFound; - case eve::StatusCode::Unsupported: return EditorStatus::Unsupported; - case eve::StatusCode::Cancelled: return EditorStatus::Cancelled; - case eve::StatusCode::Rejected: return EditorStatus::Rejected; - case eve::StatusCode::Ok: - case eve::StatusCode::Applied: - case eve::StatusCode::NoOp: - case eve::StatusCode::Pending: - case eve::StatusCode::Failed: return EditorStatus::Failed; + case eve::StatusCode::Conflict: return EditorStatus::Conflict; + case eve::StatusCode::NotFound: return EditorStatus::NotFound; + case eve::StatusCode::Unsupported: return EditorStatus::Unsupported; + case eve::StatusCode::Cancelled: return EditorStatus::Cancelled; + case eve::StatusCode::Rejected: return EditorStatus::Rejected; + case eve::StatusCode::Ok: + case eve::StatusCode::Applied: + case eve::StatusCode::NoOp: + case eve::StatusCode::Pending: + case eve::StatusCode::Failed: return EditorStatus::Failed; } return EditorStatus::Failed; } @@ -71,21 +71,20 @@ EditorStatus editorStatusFor(eve::StatusCode code) { template EditorResult providerRevisionFailure(const eve::Result &result) { const eve::Status &status = result.status(); - const char *rule = "editor.property.current-revision"; + const char *rule = "editor.property.current-revision"; switch (status.code()) { - case eve::StatusCode::Conflict: rule = "editor.property.revision-conflict"; break; - case eve::StatusCode::Unsupported: rule = "editor.property.revision-unsupported"; break; - case eve::StatusCode::NotFound: rule = "editor.property.target"; break; - default: break; + case eve::StatusCode::Conflict: rule = "editor.property.revision-conflict"; break; + case eve::StatusCode::Unsupported: rule = "editor.property.revision-unsupported"; break; + case eve::StatusCode::NotFound: rule = "editor.property.target"; break; + default: break; } return EditorResult::error(editorStatusFor(status.code()), RuleId(rule), status.describe()); } EditorResult externalRevisionConflict(eve::Revision expected, eve::Revision actual) { - return EditorResult::error( - EditorStatus::Conflict, RuleId("editor.property.revision-conflict"), - "Property target revision changed from " + std::to_string(expected.value()) + " to " + - std::to_string(actual.value()) + "; refresh/rebase before writing"); + return EditorResult::error(EditorStatus::Conflict, RuleId("editor.property.revision-conflict"), + "Property target revision changed from " + std::to_string(expected.value()) + + " to " + std::to_string(actual.value()) + "; refresh/rebase before writing"); } } // namespace @@ -100,11 +99,14 @@ struct EditorPropertyModel::ObserverState { }; EditorPropertyModel::EditorPropertyModel(PropertySchema schema, SelectionSnapshot selection, - const IPropertyProvider *provider, - PropertyModelSurface surface, HostProfile profile, - IEditorTransactionBackend *transactionBackend) - : editorSchema_(std::move(schema)), selection_(std::move(selection)), provider_(provider), - surface_(surface), profile_(std::move(profile)), transactionBackend_(transactionBackend), + const IPropertyProvider *provider, PropertyModelSurface surface, + HostProfile profile, IEditorTransactionBackend *transactionBackend) + : editorSchema_(std::move(schema)), + selection_(std::move(selection)), + provider_(provider), + surface_(surface), + profile_(std::move(profile)), + transactionBackend_(transactionBackend), observers_(std::make_shared()) { rebuildSchema(); const EditorResult initialBinding = bind(); @@ -136,22 +138,20 @@ std::optional EditorPropertyModel::read(const std::string &path) con return toPresentationValue(result.value); } -property_access::WriteResult EditorPropertyModel::write(const std::string &path, - const eve::Value &value) { +property_access::WriteResult EditorPropertyModel::write(const std::string &path, const eve::Value &value) { if (!provider_) return property_access::WriteResult::reject("editor.property.provider", "No property provider"); const EditorResult revisionCheck = ensureCurrentRevision(); if (!revisionCheck.accepted()) return writeFailure(revisionCheck, "editor.property.revision", "Property revision is unavailable"); if (transactionBackend_ && transactionBackend_->active() && pendingPaths_.contains(path)) return property_access::WriteResult::reject("editor.property.duplicate-path", - "A property may be written only once per transaction"); + "A property may be written only once per transaction"); - const EditorValue editorValue = toEditorValue(value); + const EditorValue editorValue = toEditorValue(value); EditorResult intent; if (surface_ == PropertyModelSurface::Runtime) { RuntimePropertyPresenter presenter; - intent = presenter.editIntent(editorSchema_, selection_, PropertyPath(path), - editorValue, profile_); + intent = presenter.editIntent(editorSchema_, selection_, PropertyPath(path), editorValue, profile_); } else { DeveloperPropertyPresenter presenter; intent = presenter.editIntent(editorSchema_, selection_, PropertyPath(path), editorValue); @@ -161,14 +161,13 @@ property_access::WriteResult EditorPropertyModel::write(const std::string &path, } if (transactionBackend_) { - EditorResult operation = provider_->makeSet( - selection_, PropertyPath(path), editorValue, PropertySetMode::Absolute); + EditorResult operation = + provider_->makeSet(selection_, PropertyPath(path), editorValue, PropertySetMode::Absolute); if (!operation.accepted() || !operation.value) return writeFailure(operation, "editor.property.operation", "Property operation was rejected"); if (operation.value->target.empty()) return property_access::WriteResult::reject("editor.property.target", "Property operation has no target"); - if (operation.value->mergeKey.empty()) - operation.value->mergeKey = intent.value->command.value() + ":" + path; + if (operation.value->mergeKey.empty()) operation.value->mergeKey = intent.value->command.value() + ":" + path; bool started = false; if (!transactionBackend_->active()) { @@ -180,7 +179,7 @@ property_access::WriteResult EditorPropertyModel::write(const std::string &path, specification.baseRevision = targetRevision_.value(); if (specification.id.empty()) return property_access::WriteResult::reject("editor.property.transaction-id", - "Could not allocate a property transaction identity"); + "Could not allocate a property transaction identity"); EditorResult begun = transactionBackend_->begin(std::move(specification)); if (!begun.accepted() || !begun.value) return writeFailure(begun, "editor.property.transaction.begin", "Could not begin property transaction"); @@ -193,10 +192,9 @@ property_access::WriteResult EditorPropertyModel::write(const std::string &path, EditorResult discarded = transactionBackend_->discard(); if (!discarded.accepted()) return writeFailure(discarded, "editor.property.transaction.discard", - "Could not discard rejected property transaction"); + "Could not discard rejected property transaction"); } - return writeFailure(appended, "editor.property.transaction.append", - "Could not stage property operation"); + return writeFailure(appended, "editor.property.transaction.append", "Could not stage property operation"); } pendingPaths_.insert(path); @@ -219,8 +217,7 @@ property_access::WriteResult EditorPropertyModel::write(const std::string &path, if (!committed.accepted() || !committed.value) // Commit failure is deliberately retained by the backend so the // caller can inspect diagnostics, retry, or explicitly discard. - return writeFailure(committed, "editor.property.transaction.commit", - "Property transaction commit failed"); + return writeFailure(committed, "editor.property.transaction.commit", "Property transaction commit failed"); targetRevision_ = eve::Revision(committed.value->afterRevision); pendingPaths_.clear(); // The mutation already committed. Do not report a successful commit @@ -229,8 +226,7 @@ property_access::WriteResult EditorPropertyModel::write(const std::string &path, return property_access::WriteResult::success(); } - if (!sink_) - return property_access::WriteResult::reject("editor.property.sink", "No command sink is connected"); + if (!sink_) return property_access::WriteResult::reject("editor.property.sink", "No command sink is connected"); // The sink is a compatibility path and may not have an authority that can // perform a commit-time CAS. Recheck immediately before handing it the // intent; stale compatibility writes must fail closed as well. @@ -238,8 +234,7 @@ property_access::WriteResult EditorPropertyModel::write(const std::string &path, if (!sinkRevisionCheck.accepted()) return writeFailure(sinkRevisionCheck, "editor.property.revision", "Property revision is unavailable"); const EditorResult applied = sink_(*intent.value); - if (!applied.accepted()) - return writeFailure(applied, "editor.property.command", "Property command failed"); + if (!applied.accepted()) return writeFailure(applied, "editor.property.command", "Property command failed"); // A legacy sink may mutate the provider without returning a receipt. Read // the authoritative post-command revision so the next write does not use // the pre-command baseline. If observation fails, retain command success @@ -266,13 +261,11 @@ EditorResult EditorPropertyModel::beginTransaction(std::string la RuleId("editor.property.transaction-backend"), "A transaction backend is not configured"); if (transactionBackend_->active()) - return EditorResult::error(EditorStatus::Conflict, - RuleId("editor.property.transaction-active"), + return EditorResult::error(EditorStatus::Conflict, RuleId("editor.property.transaction-active"), "A transaction is already active"); const TargetId target = selectionTarget(selection_); if (target.empty()) - return EditorResult::error(EditorStatus::Rejected, - RuleId("editor.property.target"), + return EditorResult::error(EditorStatus::Rejected, RuleId("editor.property.target"), "An explicit property transaction requires a selected target"); const EditorResult revisionCheck = ensureCurrentRevision(); if (!revisionCheck.accepted()) { @@ -282,14 +275,13 @@ EditorResult EditorPropertyModel::beginTransaction(std::string la return result; } TransactionSpec specification; - specification.id = newPropertyTransactionId(); - specification.label = std::move(label); - specification.target = target; + specification.id = newPropertyTransactionId(); + specification.label = std::move(label); + specification.target = target; specification.baseRevision = targetRevision_.value(); - specification.mergeKey = "editor.property"; + specification.mergeKey = "editor.property"; if (specification.id.empty()) - return EditorResult::error(EditorStatus::Failed, - RuleId("editor.property.transaction-id"), + return EditorResult::error(EditorStatus::Failed, RuleId("editor.property.transaction-id"), "Could not allocate a property transaction identity"); EditorResult result = transactionBackend_->begin(std::move(specification)); if (result.accepted() && result.value) pendingPaths_.clear(); @@ -323,8 +315,7 @@ EditorResult EditorPropertyModel::commitTransaction() { EditorResult EditorPropertyModel::rollbackTransaction() { if (!transactionBackend_) - return EditorResult::error(EditorStatus::Unsupported, - RuleId("editor.property.transaction-backend"), + return EditorResult::error(EditorStatus::Unsupported, RuleId("editor.property.transaction-backend"), "A transaction backend is not configured"); EditorResult result = transactionBackend_->discard(); if (result.accepted()) pendingPaths_.clear(); @@ -353,7 +344,7 @@ EditorResult EditorPropertyModel::undo() { "A transaction backend is not configured"); EditorResult result = transactionBackend_->undo(); if (!result.accepted() || !result.value) return result; - targetRevision_ = eve::Revision(result.value->afterRevision); + targetRevision_ = eve::Revision(result.value->afterRevision); const EditorResult refreshed = refresh(); if (!refreshed.accepted()) result.diagnostics.insert(result.diagnostics.end(), refreshed.diagnostics.begin(), refreshed.diagnostics.end()); @@ -367,7 +358,7 @@ EditorResult EditorPropertyModel::redo() { "A transaction backend is not configured"); EditorResult result = transactionBackend_->redo(); if (!result.accepted() || !result.value) return result; - targetRevision_ = eve::Revision(result.value->afterRevision); + targetRevision_ = eve::Revision(result.value->afterRevision); const EditorResult refreshed = refresh(); if (!refreshed.accepted()) result.diagnostics.insert(result.diagnostics.end(), refreshed.diagnostics.begin(), refreshed.diagnostics.end()); @@ -387,8 +378,7 @@ property_access::Subscription EditorPropertyModel::subscribe(ChangeCallback call EditorResult EditorPropertyModel::readProviderRevision() const { if (!provider_) - return EditorResult::error(EditorStatus::Unsupported, - RuleId("editor.property.provider"), + return EditorResult::error(EditorStatus::Unsupported, RuleId("editor.property.provider"), "No property provider is connected"); eve::Result result = provider_->currentRevision(selection_); if (!result.ok()) return providerRevisionFailure(result); @@ -410,13 +400,9 @@ EditorResult EditorPropertyModel::ensureCurrentRevision() const { return EditorResult::applied(); } -EditorResult EditorPropertyModel::bind() { - return refresh(); -} +EditorResult EditorPropertyModel::bind() { return refresh(); } -EditorResult EditorPropertyModel::rebase() { - return refresh(); -} +EditorResult EditorPropertyModel::rebase() { return refresh(); } EditorResult EditorPropertyModel::refresh() { if (!provider_) diff --git a/src/modules/editor/EditorPropertyModel.h b/src/modules/editor/EditorPropertyModel.h index 9f5387c2..06d78718 100644 --- a/src/modules/editor/EditorPropertyModel.h +++ b/src/modules/editor/EditorPropertyModel.h @@ -31,19 +31,17 @@ class EditorPropertyModel final : public property_access::IPropertyAccess { public: using EditSink = std::function(const PropertyEditIntent &)>; - EditorPropertyModel(PropertySchema schema, SelectionSnapshot selection, - const IPropertyProvider *provider, - PropertyModelSurface surface = PropertyModelSurface::Developer, - HostProfile profile = HostProfile::developer(), + EditorPropertyModel(PropertySchema schema, SelectionSnapshot selection, const IPropertyProvider *provider, + PropertyModelSurface surface = PropertyModelSurface::Developer, + HostProfile profile = HostProfile::developer(), IEditorTransactionBackend *transactionBackend = nullptr); ~EditorPropertyModel() override; - const property_access::PropertySchema &schema() const override { return presentationSchema_; } - std::optional read(const std::string &path) const override; - [[nodiscard]] property_access::WriteResult write(const std::string &path, - const eve::Value &value) override; + const property_access::PropertySchema &schema() const override { return presentationSchema_; } + std::optional read(const std::string &path) const override; + [[nodiscard]] property_access::WriteResult write(const std::string &path, const eve::Value &value) override; std::uint64_t revision() const override { return revision_; } - property_access::Subscription subscribe(ChangeCallback callback) override; + property_access::Subscription subscribe(ChangeCallback callback) override; /** @brief Return the authoritative provider revision captured by this model. */ [[nodiscard]] eve::Revision targetRevision() const noexcept { return targetRevision_; } @@ -95,22 +93,22 @@ public: private: struct ObserverState; void rebuildSchema(); - void emit(const std::string &path, const eve::Value &value); + void emit(const std::string &path, const eve::Value &value); [[nodiscard]] EditorResult readProviderRevision() const; - [[nodiscard]] EditorResult ensureCurrentRevision() const; + [[nodiscard]] EditorResult ensureCurrentRevision() const; PropertySchema editorSchema_; SelectionSnapshot selection_; const IPropertyProvider *provider_ = nullptr; PropertyModelSurface surface_ = PropertyModelSurface::Developer; HostProfile profile_; - property_access::PropertySchema presentationSchema_; + property_access::PropertySchema presentationSchema_; std::map cachedValues_; EditSink sink_; - IEditorTransactionBackend *transactionBackend_ = nullptr; - std::set pendingPaths_; - eve::Revision targetRevision_; - bool bound_ = false; + IEditorTransactionBackend *transactionBackend_ = nullptr; + std::set pendingPaths_; + eve::Revision targetRevision_; + bool bound_ = false; std::uint64_t revision_ = 0; std::shared_ptr observers_; }; diff --git a/src/modules/editor/EditorSceneTarget.cpp b/src/modules/editor/EditorSceneTarget.cpp index 2daad23d..7178f625 100644 --- a/src/modules/editor/EditorSceneTarget.cpp +++ b/src/modules/editor/EditorSceneTarget.cpp @@ -109,8 +109,7 @@ std::unique_ptr SceneTargetBase::cloneDomainState() cons return std::make_unique(*this); } -EditorResult SceneTargetBase::commitDomainState( - std::unique_ptr candidate) { +EditorResult SceneTargetBase::commitDomainState(std::unique_ptr candidate) { auto* staged = dynamic_cast(candidate.get()); if (!staged || staged->id_ != id_ || staged->type_ != type_) return sceneError(EditorStatus::Conflict, "editor.scene.candidate-mismatch", diff --git a/src/modules/editor/EditorSceneTarget.h b/src/modules/editor/EditorSceneTarget.h index dcafbbba..c7df170a 100644 --- a/src/modules/editor/EditorSceneTarget.h +++ b/src/modules/editor/EditorSceneTarget.h @@ -85,8 +85,7 @@ public: void* queryCapability(const CapabilityId& capability) override; EditorResult applyDomainOperation(const DomainOperation& operation) override; [[nodiscard]] std::unique_ptr cloneDomainState() const override; - [[nodiscard]] EditorResult commitDomainState( - std::unique_ptr candidate) override; + [[nodiscard]] EditorResult commitDomainState(std::unique_ptr candidate) override; EditorResult sceneObject(const ObjectId& id) const override; std::vector sceneChildren(const ObjectId& parent) const override; diff --git a/src/modules/editor/EditorTransactionConsumer.cpp b/src/modules/editor/EditorTransactionConsumer.cpp index 1214f42c..09b60fd5 100644 --- a/src/modules/editor/EditorTransactionConsumer.cpp +++ b/src/modules/editor/EditorTransactionConsumer.cpp @@ -17,30 +17,30 @@ eve::Result failure(eve::DiagnosticCode code, std::string message, std::strin eve::StatusCode commonStatus(EditorStatus status) noexcept { switch (status) { - case EditorStatus::Rejected: return eve::StatusCode::Rejected; - case EditorStatus::Conflict: return eve::StatusCode::Conflict; - case EditorStatus::NotFound: return eve::StatusCode::NotFound; - case EditorStatus::Unsupported: return eve::StatusCode::Unsupported; - case EditorStatus::Cancelled: return eve::StatusCode::Cancelled; - case EditorStatus::Applied: - case EditorStatus::Pending: - case EditorStatus::NoOp: - case EditorStatus::Failed: return eve::StatusCode::Failed; + case EditorStatus::Rejected: return eve::StatusCode::Rejected; + case EditorStatus::Conflict: return eve::StatusCode::Conflict; + case EditorStatus::NotFound: return eve::StatusCode::NotFound; + case EditorStatus::Unsupported: return eve::StatusCode::Unsupported; + case EditorStatus::Cancelled: return eve::StatusCode::Cancelled; + case EditorStatus::Applied: + case EditorStatus::Pending: + case EditorStatus::NoOp: + case EditorStatus::Failed: return eve::StatusCode::Failed; } return eve::StatusCode::Failed; } eve::DiagnosticCode commonDiagnostic(EditorStatus status) noexcept { switch (status) { - case EditorStatus::Rejected: return eve::DiagnosticCode::InvalidArgument; - case EditorStatus::Conflict: return eve::DiagnosticCode::Conflict; - case EditorStatus::NotFound: return eve::DiagnosticCode::NotFound; - case EditorStatus::Unsupported: return eve::DiagnosticCode::Unsupported; - case EditorStatus::Cancelled: return eve::DiagnosticCode::Cancelled; - case EditorStatus::Applied: - case EditorStatus::Pending: - case EditorStatus::NoOp: - case EditorStatus::Failed: return eve::DiagnosticCode::Failed; + case EditorStatus::Rejected: return eve::DiagnosticCode::InvalidArgument; + case EditorStatus::Conflict: return eve::DiagnosticCode::Conflict; + case EditorStatus::NotFound: return eve::DiagnosticCode::NotFound; + case EditorStatus::Unsupported: return eve::DiagnosticCode::Unsupported; + case EditorStatus::Cancelled: return eve::DiagnosticCode::Cancelled; + case EditorStatus::Applied: + case EditorStatus::Pending: + case EditorStatus::NoOp: + case EditorStatus::Failed: return eve::DiagnosticCode::Failed; } return eve::DiagnosticCode::Failed; } @@ -48,13 +48,12 @@ eve::DiagnosticCode commonDiagnostic(EditorStatus status) noexcept { template eve::Result convertEditorFailure(const EditorResult& source, std::string_view context) { std::vector diagnostics; - const eve::DiagnosticCode fallbackCode = commonDiagnostic(source.status); + const eve::DiagnosticCode fallbackCode = commonDiagnostic(source.status); for (const auto& diagnostic : source.diagnostics) { - diagnostics.push_back(eve::Diagnostic::error( - fallbackCode, std::string(context) + ": " + diagnostic.message, diagnostic.rule.value())); + diagnostics.push_back(eve::Diagnostic::error(fallbackCode, std::string(context) + ": " + diagnostic.message, + diagnostic.rule.value())); } - if (diagnostics.empty()) - diagnostics.push_back(eve::Diagnostic::error(fallbackCode, std::string(context))); + if (diagnostics.empty()) diagnostics.push_back(eve::Diagnostic::error(fallbackCode, std::string(context))); return eve::Result::failure(eve::Status(commonStatus(source.status), std::move(diagnostics))); } @@ -72,9 +71,9 @@ std::optional coordinatorFailedCommitIndex(const eve::Status& statu for (const auto& diagnostic : status.diagnostics()) { const std::string& path = diagnostic.path(); if (!path.starts_with(prefix) || path.back() != ']') continue; - std::uint64_t index = 0; - const char* first = path.data() + prefix.size(); - const char* last = path.data() + path.size() - 1; + std::uint64_t index = 0; + const char* first = path.data() + prefix.size(); + const char* last = path.data() + path.size() - 1; const auto [end, error] = std::from_chars(first, last, index); if (error == std::errc{} && end == last && index <= static_cast(std::numeric_limits::max())) @@ -84,7 +83,7 @@ std::optional coordinatorFailedCommitIndex(const eve::Status& statu } bool hasPreviouslyCommittedAdditionalParticipant(const eve::Status& status, - std::size_t editorParticipantCount) noexcept { + std::size_t editorParticipantCount) noexcept { const auto failedIndex = coordinatorFailedCommitIndex(status); // Editor-owned participants are rebuilt for every attempt. A borrowed // additional participant that precedes the failing commit is different: @@ -96,7 +95,7 @@ bool hasPreviouslyCommittedAdditionalParticipant(const eve::Status& status, struct CommandBatch { std::vector> commands; - bool previewApplied = false; + bool previewApplied = false; }; class CommandParticipant final : public transaction::ITransactionParticipant { @@ -110,15 +109,15 @@ public: [[nodiscard]] eve::Result prepare(const transaction::TransactionContext&) override { if (phase_ != Phase::Idle) - return failure(eve::DiagnosticCode::PreconditionViolation, - "editor command participant is not idle", "editor.commands.prepare"); + return failure(eve::DiagnosticCode::PreconditionViolation, "editor command participant is not idle", + "editor.commands.prepare"); if (batch_.commands.empty()) return failure(eve::DiagnosticCode::InvalidArgument, "editor transaction must contain at least one command", "editor.commands"); for (const auto& command : batch_.commands) { if (!command) - return failure(eve::DiagnosticCode::InvalidArgument, - "editor command must not be null", "editor.commands"); + return failure(eve::DiagnosticCode::InvalidArgument, "editor command must not be null", + "editor.commands"); } phase_ = Phase::Prepared; return eve::Result::success(); @@ -152,8 +151,8 @@ public: } catch (...) { const bool reverted = revertPrefix(applied); if (!reverted) batch_.previewApplied = true; - return failure(eve::DiagnosticCode::Failed, - "editor command threw during commit", "editor.commands.commit"); + return failure(eve::DiagnosticCode::Failed, "editor command threw during commit", + "editor.commands.commit"); } batch_.previewApplied = true; } @@ -166,10 +165,10 @@ public: return failure(eve::DiagnosticCode::PreconditionViolation, "editor command participant is not prepared", "editor.commands.rollback"); if (batch_.previewApplied && !revertAll()) - return failure(eve::DiagnosticCode::Failed, - "editor command preview rollback was incomplete", "editor.commands.rollback"); + return failure(eve::DiagnosticCode::Failed, "editor command preview rollback was incomplete", + "editor.commands.rollback"); batch_.previewApplied = false; - phase_ = Phase::RolledBack; + phase_ = Phase::RolledBack; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } @@ -178,10 +177,10 @@ public: return failure(eve::DiagnosticCode::PreconditionViolation, "editor command participant is not committed", "editor.commands.compensate"); if (!batch_.previewApplied || !revertAll()) - return failure(eve::DiagnosticCode::Failed, - "editor command compensation was incomplete", "editor.commands.compensate"); + return failure(eve::DiagnosticCode::Failed, "editor command compensation was incomplete", + "editor.commands.compensate"); batch_.previewApplied = false; - phase_ = Phase::Compensated; + phase_ = Phase::Compensated; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } @@ -211,21 +210,23 @@ public: AuthorityParticipant(IEditAuthority& authority, TransactionSpec specification, std::vector operations, TransactionReceipt receipt) - : authority_(&authority), specification_(std::move(specification)), operations_(std::move(operations)), - receipt_(std::move(receipt)), phase_(Phase::Committed) {} + : authority_(&authority), + specification_(std::move(specification)), + operations_(std::move(operations)), + receipt_(std::move(receipt)), + phase_(Phase::Committed) {} std::string_view name() const noexcept override { return "editor.authority"; } [[nodiscard]] eve::Result prepare(const transaction::TransactionContext&) override { if (phase_ != Phase::Idle) - return failure(eve::DiagnosticCode::PreconditionViolation, - "editor authority participant is not idle", "editor.authority.prepare"); + return failure(eve::DiagnosticCode::PreconditionViolation, "editor authority participant is not idle", + "editor.authority.prepare"); if (!authority_) - return failure(eve::DiagnosticCode::Failed, - "editor authority participant has no authority", "editor.authority"); + return failure(eve::DiagnosticCode::Failed, "editor authority participant has no authority", + "editor.authority"); auto plan = authority_->preflight(specification_, operations_); - if (!plan.accepted() || !plan.value) - return convertEditorFailure(plan, "editor authority preflight"); + if (!plan.accepted() || !plan.value) return convertEditorFailure(plan, "editor authority preflight"); plan_ = std::move(*plan.value); phase_ = Phase::Prepared; return eve::Result::success(); @@ -236,8 +237,7 @@ public: return failure(eve::DiagnosticCode::PreconditionViolation, "editor authority participant is not prepared", "editor.authority.commit"); auto result = authority_->commit(*plan_); - if (!result.accepted() || !result.value) - return convertEditorFailure(result, "editor authority commit"); + if (!result.accepted() || !result.value) return convertEditorFailure(result, "editor authority commit"); receipt_ = std::move(*result.value); phase_ = Phase::Committed; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); @@ -264,7 +264,7 @@ public: // latest effect is the compensation receipt, whose afterRevision is // also the base revision required by a later redo. receipt_ = std::move(*result.value); - phase_ = Phase::Compensated; + phase_ = Phase::Compensated; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } @@ -273,47 +273,47 @@ public: private: enum class Phase { Idle, Prepared, Committed, RolledBack, Compensated }; - IEditAuthority* authority_ = nullptr; - TransactionSpec specification_; - std::vector operations_; - std::optional plan_; - std::optional receipt_; - Phase phase_ = Phase::Idle; + IEditAuthority* authority_ = nullptr; + TransactionSpec specification_; + std::vector operations_; + std::optional plan_; + std::optional receipt_; + Phase phase_ = Phase::Idle; }; } // namespace struct EditorTransactionConsumer::Impl { struct Pending { - TransactionSpec specification; + TransactionSpec specification; std::unique_ptr commands; - std::vector operations; - bool commandModeSet = false; - bool previewMode = false; - std::vector additionalNames; - std::uint64_t attemptSequence = 0; + std::vector operations; + bool commandModeSet = false; + bool previewMode = false; + std::vector additionalNames; + std::uint64_t attemptSequence = 0; }; struct HistoryEntry { - TransactionSpec specification; - std::unique_ptr commands; - std::vector operations; + TransactionSpec specification; + std::unique_ptr commands; + std::vector operations; std::optional authorityReceipt; }; explicit Impl(IEditAuthority* authorityValue) : authority(authorityValue) {} - IEditAuthority* authority = nullptr; - std::optional pending; - std::vector undo; - std::vector redo; - transaction::Coordinator coordinator; - std::uint64_t nextLegacyId = 1; - std::uint64_t redoSequence = 0; - EditorCommitState state = EditorCommitState::Discarded; + IEditAuthority* authority = nullptr; + std::optional pending; + std::vector undo; + std::vector redo; + transaction::Coordinator coordinator; + std::uint64_t nextLegacyId = 1; + std::uint64_t redoSequence = 0; + EditorCommitState state = EditorCommitState::Discarded; std::vector diagnostics; - bool retryAllowed = false; - bool retryBlockedByCommittedParticipant = false; + bool retryAllowed = false; + bool retryBlockedByCommittedParticipant = false; }; EditorTransactionConsumer::EditorTransactionConsumer(IEditAuthority* authority) @@ -324,8 +324,7 @@ EditorTransactionConsumer::~EditorTransactionConsumer() = default; eve::Result EditorTransactionConsumer::setAuthority(IEditAuthority* authority) { if (impl_->pending) return failure(eve::DiagnosticCode::PreconditionViolation, - "editor authority cannot change during an active transaction", - "editor.authority"); + "editor authority cannot change during an active transaction", "editor.authority"); impl_->authority = authority; return eve::Result::success(); } @@ -333,17 +332,17 @@ eve::Result EditorTransactionConsumer::setAuthority(IEditAuthority* author eve::Result EditorTransactionConsumer::begin(TransactionSpec specification) { if (impl_->pending) return failure(eve::DiagnosticCode::PreconditionViolation, - "an editor transaction is already active", "editor.transaction"); + "an editor transaction is already active", "editor.transaction"); if (specification.id.empty()) - return failure(eve::DiagnosticCode::InvalidArgument, - "editor transaction id is required", "editor.transaction.id"); + return failure(eve::DiagnosticCode::InvalidArgument, "editor transaction id is required", + "editor.transaction.id"); const TransactionId id = specification.id; - Impl::Pending pending; + Impl::Pending pending; pending.specification = std::move(specification); impl_->pending = std::move(pending); - impl_->state = EditorCommitState::Pending; + impl_->state = EditorCommitState::Pending; impl_->diagnostics.clear(); - impl_->retryAllowed = false; + impl_->retryAllowed = false; impl_->retryBlockedByCommittedParticipant = false; return eve::Result::success(id); } @@ -351,8 +350,7 @@ eve::Result EditorTransactionConsumer::begin(TransactionSpec spec eve::Result EditorTransactionConsumer::beginLegacy(std::string label) { if (impl_->nextLegacyId == std::numeric_limits::max()) return failure(eve::DiagnosticCode::Failed, - "legacy editor transaction id allocator is exhausted", - "editor.transaction.id"); + "legacy editor transaction id allocator is exhausted", "editor.transaction.id"); TransactionSpec specification; specification.id = TransactionId("editor.legacy." + std::to_string(impl_->nextLegacyId++)); specification.label = std::move(label); @@ -361,32 +359,29 @@ eve::Result EditorTransactionConsumer::beginLegacy(std::string la eve::Result EditorTransactionConsumer::append(std::unique_ptr command) { if (!impl_->pending) - return failure(eve::DiagnosticCode::PreconditionViolation, - "no editor transaction is active", "editor.transaction"); + return failure(eve::DiagnosticCode::PreconditionViolation, "no editor transaction is active", + "editor.transaction"); if (!command) - return failure(eve::DiagnosticCode::InvalidArgument, "editor command must not be null", - "editor.command"); + return failure(eve::DiagnosticCode::InvalidArgument, "editor command must not be null", "editor.command"); if (!impl_->pending->operations.empty()) return failure(eve::DiagnosticCode::Conflict, "commands and authority operations cannot share one editor transaction", "editor.transaction"); if (impl_->pending->commandModeSet && impl_->pending->previewMode) - return failure(eve::DiagnosticCode::Conflict, - "a strict command cannot follow a preview command", "editor.transaction"); + return failure(eve::DiagnosticCode::Conflict, "a strict command cannot follow a preview command", + "editor.transaction"); impl_->pending->commandModeSet = true; impl_->pending->previewMode = false; if (!impl_->pending->commands) impl_->pending->commands = std::make_unique(); if (!impl_->pending->commands->commands.empty()) { try { - if (impl_->pending->commands->commands.back()->mergeWith(*command)) - return eve::Result::success(); + if (impl_->pending->commands->commands.back()->mergeWith(*command)) return eve::Result::success(); } catch (const std::exception& exception) { return failure(eve::DiagnosticCode::Failed, std::string("editor command merge threw: ") + exception.what(), "editor.command.merge"); } catch (...) { - return failure(eve::DiagnosticCode::Failed, "editor command merge threw", - "editor.command.merge"); + return failure(eve::DiagnosticCode::Failed, "editor command merge threw", "editor.command.merge"); } } impl_->pending->commands->commands.push_back(std::move(command)); @@ -395,18 +390,17 @@ eve::Result EditorTransactionConsumer::append(std::unique_ptr EditorTransactionConsumer::appendPreview(std::unique_ptr command) { if (!impl_->pending) - return failure(eve::DiagnosticCode::PreconditionViolation, - "no editor transaction is active", "editor.transaction"); + return failure(eve::DiagnosticCode::PreconditionViolation, "no editor transaction is active", + "editor.transaction"); if (!command) - return failure(eve::DiagnosticCode::InvalidArgument, "editor command must not be null", - "editor.command"); + return failure(eve::DiagnosticCode::InvalidArgument, "editor command must not be null", "editor.command"); if (!impl_->pending->operations.empty()) return failure(eve::DiagnosticCode::Conflict, "commands and authority operations cannot share one editor transaction", "editor.transaction"); if (impl_->pending->commandModeSet && !impl_->pending->previewMode) - return failure(eve::DiagnosticCode::Conflict, - "a preview command cannot follow a strict command", "editor.transaction"); + return failure(eve::DiagnosticCode::Conflict, "a preview command cannot follow a strict command", + "editor.transaction"); if (!impl_->pending->commands) impl_->pending->commands = std::make_unique(); // A command is applied only after merge evaluation has completed on an @@ -422,8 +416,7 @@ eve::Result EditorTransactionConsumer::appendPreview(std::unique_ptr(eve::DiagnosticCode::Failed, "editor command staging threw", - "editor.command.stage"); + return failure(eve::DiagnosticCode::Failed, "editor command staging threw", "editor.command.stage"); } if (mergedCandidate) { try { @@ -468,9 +461,9 @@ eve::Result EditorTransactionConsumer::appendPreview(std::unique_ptrpending->commands->commands.back() = std::move(mergedCandidate); - impl_->pending->commands->previewApplied = true; - impl_->pending->commandModeSet = true; - impl_->pending->previewMode = true; + impl_->pending->commands->previewApplied = true; + impl_->pending->commandModeSet = true; + impl_->pending->previewMode = true; return eve::Result::success(); } } catch (const std::exception& exception) { @@ -478,8 +471,7 @@ eve::Result EditorTransactionConsumer::appendPreview(std::unique_ptr(eve::DiagnosticCode::Failed, "editor command merge threw", - "editor.command.merge"); + return failure(eve::DiagnosticCode::Failed, "editor command merge threw", "editor.command.merge"); } } } @@ -487,8 +479,7 @@ eve::Result EditorTransactionConsumer::appendPreview(std::unique_ptrpending->commands->commands.reserve( - impl_->pending->commands->commands.size() + 1); + impl_->pending->commands->commands.reserve(impl_->pending->commands->commands.size() + 1); } catch (const std::exception& exception) { return failure(eve::DiagnosticCode::Failed, std::string("editor command staging allocation threw: ") + exception.what(), @@ -505,8 +496,7 @@ eve::Result EditorTransactionConsumer::appendPreview(std::unique_ptrrevert(); } catch (...) { - return failure(eve::DiagnosticCode::Failed, - "editor command preview failed and could not be reverted", + return failure(eve::DiagnosticCode::Failed, "editor command preview failed and could not be reverted", "editor.command.preview"); } return failure(eve::DiagnosticCode::Failed, @@ -516,12 +506,10 @@ eve::Result EditorTransactionConsumer::appendPreview(std::unique_ptrrevert(); } catch (...) { - return failure(eve::DiagnosticCode::Failed, - "editor command preview failed and could not be reverted", + return failure(eve::DiagnosticCode::Failed, "editor command preview failed and could not be reverted", "editor.command.preview"); } - return failure(eve::DiagnosticCode::Failed, "editor command preview threw", - "editor.command.preview"); + return failure(eve::DiagnosticCode::Failed, "editor command preview threw", "editor.command.preview"); } if (!applied) { try { @@ -536,25 +524,25 @@ eve::Result EditorTransactionConsumer::appendPreview(std::unique_ptrpending->commands->commands.push_back(std::move(command)); impl_->pending->commands->previewApplied = true; - impl_->pending->commandModeSet = true; - impl_->pending->previewMode = true; + impl_->pending->commandModeSet = true; + impl_->pending->previewMode = true; return eve::Result::success(); } eve::Result EditorTransactionConsumer::append(DomainOperation operation) { if (!impl_->pending) - return failure(eve::DiagnosticCode::PreconditionViolation, - "no editor transaction is active", "editor.transaction"); + return failure(eve::DiagnosticCode::PreconditionViolation, "no editor transaction is active", + "editor.transaction"); if (operation.type.empty() || operation.target.empty()) - return failure(eve::DiagnosticCode::InvalidArgument, - "authority operation type and target are required", "editor.operation"); + return failure(eve::DiagnosticCode::InvalidArgument, "authority operation type and target are required", + "editor.operation"); if (impl_->pending->commands) return failure(eve::DiagnosticCode::Conflict, "commands and authority operations cannot share one editor transaction", "editor.transaction"); if (impl_->pending->specification.target.empty()) - return failure(eve::DiagnosticCode::InvalidArgument, - "authority transaction target is required", "editor.transaction.target"); + return failure(eve::DiagnosticCode::InvalidArgument, "authority transaction target is required", + "editor.transaction.target"); if (operation.target != impl_->pending->specification.target) return failure(eve::DiagnosticCode::Conflict, "authority operation target does not match the transaction target", @@ -567,14 +555,14 @@ eve::Result EditorTransactionConsumer::dryRun() const { if (!impl_->pending) return failure(eve::DiagnosticCode::PreconditionViolation, "no editor transaction is active", "editor.transaction"); - const auto& pending = *impl_->pending; + const auto& pending = *impl_->pending; const std::size_t commandCount = pending.commands ? pending.commands->commands.size() : 0; if (commandCount == 0 && pending.operations.empty()) - return failure(eve::DiagnosticCode::InvalidArgument, - "editor transaction has no work", "editor.transaction"); + return failure(eve::DiagnosticCode::InvalidArgument, "editor transaction has no work", + "editor.transaction"); EditorDryRunReport report; - report.specification = pending.specification; - report.commandCount = commandCount; + report.specification = pending.specification; + report.commandCount = commandCount; report.operationCount = pending.operations.size(); if (!pending.operations.empty()) { if (!impl_->authority) @@ -602,17 +590,16 @@ eve::Result EditorTransactionConsumer::retry( std::span additional) { if (!impl_->pending) return failure(eve::DiagnosticCode::PreconditionViolation, - "no failed editor transaction is pending", - "editor.transaction.retry"); + "no failed editor transaction is pending", "editor.transaction.retry"); if (impl_->state != EditorCommitState::FailedRetryable) return failure(eve::DiagnosticCode::PreconditionViolation, - "editor transaction is not awaiting retry", - "editor.transaction.retry"); + "editor transaction is not awaiting retry", "editor.transaction.retry"); if (!impl_->retryAllowed) return failure( eve::DiagnosticCode::Conflict, impl_->retryBlockedByCommittedParticipant - ? "editor transaction cannot be retried after an additional participant committed; reconcile or discard it" + ? "editor transaction cannot be retried after an additional participant committed; reconcile or " + "discard it" : "editor transaction cannot be retried after incomplete cleanup; discard it", "editor.transaction.retry"); return commitAttempt(additional, true); @@ -625,21 +612,21 @@ eve::Result EditorTransactionConsumer::commitAttempt( "no editor transaction is active", "editor.transaction"); auto retainFailure = [&](const eve::Status& status, bool retryAllowed) { - impl_->state = EditorCommitState::FailedRetryable; - impl_->retryAllowed = retryAllowed; + impl_->state = EditorCommitState::FailedRetryable; + impl_->retryAllowed = retryAllowed; impl_->retryBlockedByCommittedParticipant = false; - impl_->diagnostics = status.diagnostics(); + impl_->diagnostics = status.diagnostics(); return eve::Result::failure(status); }; auto reject = [&](eve::DiagnosticCode code, std::string message, std::string path) { - auto result = failure(code, std::move(message), std::move(path)); + auto result = failure(code, std::move(message), std::move(path)); const bool preserveRetryBlock = impl_->state == EditorCommitState::FailedRetryable; - impl_->state = EditorCommitState::FailedRetryable; + impl_->state = EditorCommitState::FailedRetryable; if (!preserveRetryBlock) { - impl_->retryAllowed = true; + impl_->retryAllowed = true; impl_->retryBlockedByCommittedParticipant = false; } - impl_->diagnostics = result.diagnostics(); + impl_->diagnostics = result.diagnostics(); return result; }; @@ -648,16 +635,16 @@ eve::Result EditorTransactionConsumer::commitAttempt( "editor commit failed; call retry() or discard() before another commit attempt", "editor.transaction.commit"); if (retryAttempt && impl_->state != EditorCommitState::FailedRetryable) - return reject(eve::DiagnosticCode::PreconditionViolation, - "editor transaction is not awaiting retry", "editor.transaction.retry"); + return reject(eve::DiagnosticCode::PreconditionViolation, "editor transaction is not awaiting retry", + "editor.transaction.retry"); - Impl::Pending& pending = *impl_->pending; + Impl::Pending& pending = *impl_->pending; const std::size_t commandCount = pending.commands ? pending.commands->commands.size() : 0; if (commandCount == 0 && pending.operations.empty()) return reject(eve::DiagnosticCode::InvalidArgument, "editor transaction has no work", "editor.transaction"); if (!pending.operations.empty() && !impl_->authority) - return reject(eve::DiagnosticCode::Unsupported, - "authority commit requires an injected authority", "editor.authority"); + return reject(eve::DiagnosticCode::Unsupported, "authority commit requires an injected authority", + "editor.authority"); for (std::size_t i = 0; i < additional.size(); ++i) { if (!additional[i]) @@ -674,12 +661,11 @@ eve::Result EditorTransactionConsumer::commitAttempt( if (!retryAttempt) { pending.additionalNames.clear(); pending.additionalNames.reserve(additional.size()); - for (auto* participant : additional) - pending.additionalNames.emplace_back(participant->name()); + for (auto* participant : additional) pending.additionalNames.emplace_back(participant->name()); } else { if (additional.size() != pending.additionalNames.size()) - return reject(eve::DiagnosticCode::Conflict, - "retry must provide the same additional participant set", "editor.transaction.retry"); + return reject(eve::DiagnosticCode::Conflict, "retry must provide the same additional participant set", + "editor.transaction.retry"); for (std::size_t i = 0; i < additional.size(); ++i) { if (additional[i]->name() != pending.additionalNames[i]) return reject(eve::DiagnosticCode::Conflict, @@ -688,12 +674,12 @@ eve::Result EditorTransactionConsumer::commitAttempt( } } - std::unique_ptr authorityParticipant; - std::unique_ptr commandParticipant; + std::unique_ptr authorityParticipant; + std::unique_ptr commandParticipant; std::vector participants; if (!pending.operations.empty()) { - authorityParticipant = std::make_unique( - *impl_->authority, pending.specification, pending.operations); + authorityParticipant = + std::make_unique(*impl_->authority, pending.specification, pending.operations); participants.push_back(authorityParticipant.get()); } if (pending.commands) { @@ -712,13 +698,12 @@ eve::Result EditorTransactionConsumer::commitAttempt( ++pending.attemptSequence; } std::string attemptId = pending.specification.id.value(); - if (retryAttempt) - attemptId += ".retry." + std::to_string(pending.attemptSequence); + if (retryAttempt) attemptId += ".retry." + std::to_string(pending.attemptSequence); transaction::TransactionContext context(std::move(attemptId)); - auto coordinated = impl_->coordinator.execute(context, participants); + auto coordinated = impl_->coordinator.execute(context, participants); if (!coordinated.ok()) { - const eve::Status& status = coordinated.status(); - const bool committedAdditional = hasPreviouslyCommittedAdditionalParticipant( + const eve::Status& status = coordinated.status(); + const bool committedAdditional = hasPreviouslyCommittedAdditionalParticipant( status, (pending.operations.empty() ? 0u : 1u) + (pending.commands ? 1u : 0u)); auto result = retainFailure(status, !hasCoordinatorCleanupFailure(status) && !committedAdditional); impl_->retryBlockedByCommittedParticipant = committedAdditional; @@ -727,67 +712,65 @@ eve::Result EditorTransactionConsumer::commitAttempt( transaction::TransactionReceipt coordinatorReceipt = std::move(coordinated).takeValue(); Impl::HistoryEntry history; - history.specification = pending.specification; - history.commands = std::move(pending.commands); - history.operations = std::move(pending.operations); + history.specification = pending.specification; + history.commands = std::move(pending.commands); + history.operations = std::move(pending.operations); if (authorityParticipant) history.authorityReceipt = authorityParticipant->receipt(); impl_->undo.push_back(std::move(history)); impl_->redo.clear(); impl_->pending.reset(); - impl_->state = EditorCommitState::Committed; - impl_->retryAllowed = false; + impl_->state = EditorCommitState::Committed; + impl_->retryAllowed = false; impl_->retryBlockedByCommittedParticipant = false; impl_->diagnostics.clear(); EditorTransactionRecord record; - record.coordinator = std::move(coordinatorReceipt); - record.specification = impl_->undo.back().specification; + record.coordinator = std::move(coordinatorReceipt); + record.specification = impl_->undo.back().specification; record.authorityReceipt = impl_->undo.back().authorityReceipt; - record.commandCount = commandCount; - record.operationCount = impl_->undo.back().operations.size(); + record.commandCount = commandCount; + record.operationCount = impl_->undo.back().operations.size(); return eve::Result::success(std::move(record)); } eve::Result EditorTransactionConsumer::discard() { if (!impl_->pending) - return failure(eve::DiagnosticCode::PreconditionViolation, - "no editor transaction is active", "editor.transaction"); + return failure(eve::DiagnosticCode::PreconditionViolation, "no editor transaction is active", + "editor.transaction"); Impl::Pending& pending = *impl_->pending; if (pending.commands && pending.commands->previewApplied) { try { for (auto it = pending.commands->commands.rbegin(); it != pending.commands->commands.rend(); ++it) (*it)->revert(); } catch (const std::exception& exception) { - auto result = failure(eve::DiagnosticCode::Failed, - std::string("editor preview rollback threw: ") + exception.what(), - "editor.transaction.discard"); - impl_->state = EditorCommitState::FailedRetryable; - impl_->retryAllowed = false; + auto result = failure(eve::DiagnosticCode::Failed, + std::string("editor preview rollback threw: ") + exception.what(), + "editor.transaction.discard"); + impl_->state = EditorCommitState::FailedRetryable; + impl_->retryAllowed = false; impl_->retryBlockedByCommittedParticipant = false; - impl_->diagnostics = result.diagnostics(); + impl_->diagnostics = result.diagnostics(); return result; } catch (...) { - auto result = failure(eve::DiagnosticCode::Failed, "editor preview rollback threw", - "editor.transaction.discard"); + auto result = failure(eve::DiagnosticCode::Failed, "editor preview rollback threw", + "editor.transaction.discard"); impl_->state = EditorCommitState::FailedRetryable; impl_->retryAllowed = false; impl_->retryBlockedByCommittedParticipant = false; - impl_->diagnostics = result.diagnostics(); + impl_->diagnostics = result.diagnostics(); return result; } pending.commands->previewApplied = false; } impl_->pending.reset(); - impl_->state = EditorCommitState::Discarded; - impl_->retryAllowed = false; + impl_->state = EditorCommitState::Discarded; + impl_->retryAllowed = false; impl_->retryBlockedByCommittedParticipant = false; impl_->diagnostics.clear(); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result EditorTransactionConsumer::rollback() { - return discard(); -} +eve::Result EditorTransactionConsumer::rollback() { return discard(); } eve::Result EditorTransactionConsumer::undo() { if (impl_->pending) @@ -795,22 +778,22 @@ eve::Result EditorTransactionConsumer::undo() { "cannot undo while an editor transaction is active", "editor.transaction.undo"); if (impl_->undo.empty()) - return failure(eve::DiagnosticCode::NotFound, - "there is no editor transaction to undo", "editor.history.undo"); + return failure(eve::DiagnosticCode::NotFound, "there is no editor transaction to undo", + "editor.history.undo"); Impl::HistoryEntry history = std::move(impl_->undo.back()); impl_->undo.pop_back(); - std::unique_ptr authorityParticipant; - std::unique_ptr commandParticipant; + std::unique_ptr authorityParticipant; + std::unique_ptr commandParticipant; std::vector participants; if (history.authorityReceipt && impl_->authority) { - authorityParticipant = std::make_unique( - *impl_->authority, history.specification, history.operations, *history.authorityReceipt); + authorityParticipant = std::make_unique(*impl_->authority, history.specification, + history.operations, *history.authorityReceipt); participants.push_back(authorityParticipant.get()); } if (history.commands) { - commandParticipant = std::make_unique( - *history.commands, CommandParticipant::InitialPhase::Committed); + commandParticipant = + std::make_unique(*history.commands, CommandParticipant::InitialPhase::Committed); participants.push_back(commandParticipant.get()); } if (participants.empty()) { @@ -819,10 +802,10 @@ eve::Result EditorTransactionConsumer::undo() { "editor history entry has no compensatable participant", "editor.history.undo"); } - const std::string compensationId = history.specification.id.value() + ".undo." + - std::to_string(++impl_->redoSequence); + const std::string compensationId = + history.specification.id.value() + ".undo." + std::to_string(++impl_->redoSequence); transaction::TransactionContext context(compensationId); - auto compensated = impl_->coordinator.compensate(context, participants); + auto compensated = impl_->coordinator.compensate(context, participants); if (!compensated.ok()) { impl_->undo.push_back(std::move(history)); return eve::Result::failure(compensated.status()); @@ -831,7 +814,7 @@ eve::Result EditorTransactionConsumer::undo() { if (authorityParticipant && authorityParticipant->receipt()) history.specification.baseRevision = authorityParticipant->receipt()->afterRevision; impl_->redo.push_back(std::move(history)); - const auto& moved = impl_->redo.back(); + const auto& moved = impl_->redo.back(); EditorTransactionRecord record; record.coordinator = std::move(receipt); record.specification = moved.specification; @@ -847,8 +830,8 @@ eve::Result EditorTransactionConsumer::redo() { "cannot redo while an editor transaction is active", "editor.transaction.redo"); if (impl_->redo.empty()) - return failure(eve::DiagnosticCode::NotFound, - "there is no editor transaction to redo", "editor.history.redo"); + return failure(eve::DiagnosticCode::NotFound, "there is no editor transaction to redo", + "editor.history.redo"); Impl::HistoryEntry history = std::move(impl_->redo.back()); impl_->redo.pop_back(); if (history.operations.empty() && !history.commands) { @@ -860,19 +843,17 @@ eve::Result EditorTransactionConsumer::redo() { if (!history.operations.empty() && !impl_->authority) { impl_->redo.push_back(std::move(history)); return failure(eve::DiagnosticCode::Unsupported, - "authority redo requires an injected authority", - "editor.authority"); + "authority redo requires an injected authority", "editor.authority"); } TransactionSpec specification = history.specification; - specification.id = TransactionId(specification.id.value() + ".redo." + - std::to_string(++impl_->redoSequence)); - std::unique_ptr authorityParticipant; - std::unique_ptr commandParticipant; + specification.id = TransactionId(specification.id.value() + ".redo." + std::to_string(++impl_->redoSequence)); + std::unique_ptr authorityParticipant; + std::unique_ptr commandParticipant; std::vector participants; if (!history.operations.empty()) { - authorityParticipant = std::make_unique( - *impl_->authority, specification, history.operations); + authorityParticipant = + std::make_unique(*impl_->authority, specification, history.operations); participants.push_back(authorityParticipant.get()); } if (history.commands) { @@ -880,16 +861,16 @@ eve::Result EditorTransactionConsumer::redo() { participants.push_back(commandParticipant.get()); } transaction::TransactionContext context(specification.id.value()); - auto replayed = impl_->coordinator.execute(context, participants); + auto replayed = impl_->coordinator.execute(context, participants); if (!replayed.ok()) { impl_->redo.push_back(std::move(history)); return eve::Result::failure(replayed.status()); } transaction::TransactionReceipt receipt = std::move(replayed).takeValue(); - history.specification = specification; + history.specification = specification; if (authorityParticipant) history.authorityReceipt = authorityParticipant->receipt(); impl_->undo.push_back(std::move(history)); - const auto& moved = impl_->undo.back(); + const auto& moved = impl_->undo.back(); EditorTransactionRecord record; record.coordinator = std::move(receipt); record.specification = moved.specification; @@ -899,11 +880,11 @@ eve::Result EditorTransactionConsumer::redo() { return eve::Result::success(std::move(record)); } -bool EditorTransactionConsumer::active() const noexcept { return impl_->pending.has_value(); } -bool EditorTransactionConsumer::canUndo() const noexcept { return !impl_->undo.empty(); } -bool EditorTransactionConsumer::canRedo() const noexcept { return !impl_->redo.empty(); } -std::size_t EditorTransactionConsumer::undoCount() const noexcept { return impl_->undo.size(); } -std::size_t EditorTransactionConsumer::redoCount() const noexcept { return impl_->redo.size(); } +bool EditorTransactionConsumer::active() const noexcept { return impl_->pending.has_value(); } +bool EditorTransactionConsumer::canUndo() const noexcept { return !impl_->undo.empty(); } +bool EditorTransactionConsumer::canRedo() const noexcept { return !impl_->redo.empty(); } +std::size_t EditorTransactionConsumer::undoCount() const noexcept { return impl_->undo.size(); } +std::size_t EditorTransactionConsumer::redoCount() const noexcept { return impl_->redo.size(); } EditorCommitState EditorTransactionConsumer::state() const noexcept { return impl_->state; } const std::vector& EditorTransactionConsumer::diagnostics() const noexcept { return impl_->diagnostics; diff --git a/src/modules/editor/EditorTransactionConsumer.h b/src/modules/editor/EditorTransactionConsumer.h index 0d807633..253f3c4c 100644 --- a/src/modules/editor/EditorTransactionConsumer.h +++ b/src/modules/editor/EditorTransactionConsumer.h @@ -1,7 +1,7 @@ #pragma once -#include "editor/EditorAuthority.h" #include "editor/EditCommand.h" +#include "editor/EditorAuthority.h" #include "transaction/Transaction.h" #include @@ -21,11 +21,11 @@ namespace eve::editor { * replay an authority-backed transaction; it is not a second commit state. */ struct EditorTransactionRecord { - transaction::TransactionReceipt coordinator; - TransactionSpec specification; + transaction::TransactionReceipt coordinator; + TransactionSpec specification; std::optional authorityReceipt; - std::size_t commandCount = 0; - std::size_t operationCount = 0; + std::size_t commandCount = 0; + std::size_t operationCount = 0; }; /** @@ -36,10 +36,10 @@ struct EditorTransactionRecord { * preflight. Neither path publishes target state. */ struct EditorDryRunReport { - TransactionSpec specification; - std::size_t commandCount = 0; - std::size_t operationCount = 0; - std::optional authorityPlan; + TransactionSpec specification; + std::size_t commandCount = 0; + std::size_t operationCount = 0; + std::optional authorityPlan; }; /** @brief Lifecycle state of the editor consumer's active commit attempt. */ diff --git a/src/modules/editor/EditorTransactionService.cpp b/src/modules/editor/EditorTransactionService.cpp index 5774b936..f55cac9c 100644 --- a/src/modules/editor/EditorTransactionService.cpp +++ b/src/modules/editor/EditorTransactionService.cpp @@ -7,16 +7,16 @@ namespace { EditorStatus editorStatus(eve::StatusCode status) noexcept { switch (status) { - case eve::StatusCode::Rejected: return EditorStatus::Rejected; - case eve::StatusCode::Conflict: return EditorStatus::Conflict; - case eve::StatusCode::NotFound: return EditorStatus::NotFound; - case eve::StatusCode::Unsupported: return EditorStatus::Unsupported; - case eve::StatusCode::Cancelled: return EditorStatus::Cancelled; - case eve::StatusCode::Ok: - case eve::StatusCode::Applied: - case eve::StatusCode::NoOp: - case eve::StatusCode::Pending: - case eve::StatusCode::Failed: return EditorStatus::Failed; + case eve::StatusCode::Rejected: return EditorStatus::Rejected; + case eve::StatusCode::Conflict: return EditorStatus::Conflict; + case eve::StatusCode::NotFound: return EditorStatus::NotFound; + case eve::StatusCode::Unsupported: return EditorStatus::Unsupported; + case eve::StatusCode::Cancelled: return EditorStatus::Cancelled; + case eve::StatusCode::Ok: + case eve::StatusCode::Applied: + case eve::StatusCode::NoOp: + case eve::StatusCode::Pending: + case eve::StatusCode::Failed: return EditorStatus::Failed; } return EditorStatus::Failed; } @@ -33,19 +33,18 @@ EditorResult projectFailure(const eve::Status& status) { EditorResult result; result.status = editorStatus(status.code()); for (const auto& diagnostic : status.diagnostics()) - result.diagnostics.push_back( - {diagnosticRule(diagnostic), DiagnosticSeverity::Error, diagnostic.message()}); + result.diagnostics.push_back({diagnosticRule(diagnostic), DiagnosticSeverity::Error, diagnostic.message()}); return result; } TransactionState transactionState(eve::transaction::CoordinatorState state) noexcept { switch (state) { - case eve::transaction::CoordinatorState::Committed: return TransactionState::Committed; - case eve::transaction::CoordinatorState::Compensated: return TransactionState::RolledBack; - case eve::transaction::CoordinatorState::CompensationFailed: - case eve::transaction::CoordinatorState::RollbackFailed: - case eve::transaction::CoordinatorState::PartiallyCommitted: return TransactionState::Failed; - case eve::transaction::CoordinatorState::RolledBack: return TransactionState::RolledBack; + case eve::transaction::CoordinatorState::Committed: return TransactionState::Committed; + case eve::transaction::CoordinatorState::Compensated: return TransactionState::RolledBack; + case eve::transaction::CoordinatorState::CompensationFailed: + case eve::transaction::CoordinatorState::RollbackFailed: + case eve::transaction::CoordinatorState::PartiallyCommitted: return TransactionState::Failed; + case eve::transaction::CoordinatorState::RolledBack: return TransactionState::RolledBack; } return TransactionState::Failed; } @@ -55,9 +54,9 @@ TransactionReceipt projectRecord(const EditorTransactionRecord& record) { result.id = record.specification.id; result.state = transactionState(record.coordinator.state); if (record.authorityReceipt) { - result.beforeRevision = record.authorityReceipt->beforeRevision; - result.afterRevision = record.authorityReceipt->afterRevision; - result.affectedObjects = record.authorityReceipt->affectedObjects; + result.beforeRevision = record.authorityReceipt->beforeRevision; + result.afterRevision = record.authorityReceipt->afterRevision; + result.affectedObjects = record.authorityReceipt->affectedObjects; result.diagnostics = record.authorityReceipt->diagnostics; result.authorityReceipt = record.authorityReceipt->authorityReceipt; } @@ -76,15 +75,13 @@ EditorResult LocalTransactionBackend::project(eve::Result&& result) return EditorResult::applied(); } -EditorResult LocalTransactionBackend::project( - eve::Result&& result) { +EditorResult LocalTransactionBackend::project(eve::Result&& result) { if (!result.ok()) return projectFailure(result.status()); EditorTransactionRecord record = std::move(result).takeValue(); return EditorResult::applied(projectRecord(record)); } -EditorResult LocalTransactionBackend::project( - eve::Result&& result) { +EditorResult LocalTransactionBackend::project(eve::Result&& result) { if (!result.ok()) return projectFailure(result.status()); return EditorResult::applied(std::move(result).takeValue()); } @@ -105,32 +102,18 @@ EditorResult LocalTransactionBackend::append(DomainOperation operation) { return project(consumer_.append(std::move(operation))); } -EditorResult LocalTransactionBackend::preview() { - return project(consumer_.dryRun()); -} +EditorResult LocalTransactionBackend::preview() { return project(consumer_.dryRun()); } -EditorResult LocalTransactionBackend::commit() { - return project(consumer_.commit()); -} +EditorResult LocalTransactionBackend::commit() { return project(consumer_.commit()); } -EditorResult LocalTransactionBackend::discard() { - return project(consumer_.discard()); -} +EditorResult LocalTransactionBackend::discard() { return project(consumer_.discard()); } -EditorResult LocalTransactionBackend::rollback() { - return discard(); -} +EditorResult LocalTransactionBackend::rollback() { return discard(); } -EditorResult LocalTransactionBackend::retry() { - return project(consumer_.retry()); -} +EditorResult LocalTransactionBackend::retry() { return project(consumer_.retry()); } -EditorResult LocalTransactionBackend::undo() { - return project(consumer_.undo()); -} +EditorResult LocalTransactionBackend::undo() { return project(consumer_.undo()); } -EditorResult LocalTransactionBackend::redo() { - return project(consumer_.redo()); -} +EditorResult LocalTransactionBackend::redo() { return project(consumer_.redo()); } } // namespace eve::editor diff --git a/src/modules/editor/EditorTransactionService.h b/src/modules/editor/EditorTransactionService.h index 63adf8d7..7d1c2d9d 100644 --- a/src/modules/editor/EditorTransactionService.h +++ b/src/modules/editor/EditorTransactionService.h @@ -78,8 +78,8 @@ public: private: static EditorResult project(eve::Result&& result); static EditorResult project(eve::Result&& result); - static EditorResult project(eve::Result&& result); - static EditorResult project(eve::Result&& result); + static EditorResult project(eve::Result&& result); + static EditorResult project(eve::Result&& result); EditorTransactionConsumer consumer_; }; diff --git a/src/modules/editor/EditorTransactions.cpp b/src/modules/editor/EditorTransactions.cpp index 0591127e..67eb3057 100644 --- a/src/modules/editor/EditorTransactions.cpp +++ b/src/modules/editor/EditorTransactions.cpp @@ -26,8 +26,6 @@ bool EditorTransactions::redo() { auto result = consumer_.redo(); return result.ok(); } -void EditorTransactions::clear() { - consumer_.clear(); -} +void EditorTransactions::clear() { consumer_.clear(); } } // namespace eve::editor diff --git a/src/modules/editor/EditorTransactions.h b/src/modules/editor/EditorTransactions.h index f58bd6bb..71f2d78b 100644 --- a/src/modules/editor/EditorTransactions.h +++ b/src/modules/editor/EditorTransactions.h @@ -26,8 +26,8 @@ public: bool isActive() const { return consumer_.active(); } bool canUndo() const { return consumer_.canUndo(); } bool canRedo() const { return consumer_.canRedo(); } - int undoCount() const { return static_cast(consumer_.undoCount()); } - int redoCount() const { return static_cast(consumer_.redoCount()); } + int undoCount() const { return static_cast(consumer_.undoCount()); } + int redoCount() const { return static_cast(consumer_.redoCount()); } private: EditorTransactionConsumer consumer_; diff --git a/src/modules/effects/EffectContainer.cpp b/src/modules/effects/EffectContainer.cpp index d62b5e42..cad63053 100644 --- a/src/modules/effects/EffectContainer.cpp +++ b/src/modules/effects/EffectContainer.cpp @@ -106,9 +106,13 @@ EffectContainer::EffectContainer(eve::UuidEntropySource entropy, eve::UuidClock } EffectContainer::EffectContainer(const EffectContainer& other) - : nextId_(other.nextId_), nextSequence_(other.nextSequence_), - effectIdGenerator_(other.effectIdGenerator_), generation_(other.generation_), - lastTick_(other.lastTick_), hasLastTick_(other.hasLastTick_), events_(other.events_) { + : nextId_(other.nextId_), + nextSequence_(other.nextSequence_), + effectIdGenerator_(other.effectIdGenerator_), + generation_(other.generation_), + lastTick_(other.lastTick_), + hasLastTick_(other.hasLastTick_), + events_(other.events_) { for (const auto& effect : other.effects_) effects_.push_back(effect ? std::make_unique(*effect) : nullptr); } @@ -136,18 +140,18 @@ EffectContainer EffectContainer::snapshot() const { return clone(); } eve::Result EffectContainer::restore(const EffectContainer& snapshotValue) { if (this == &snapshotValue) { if (generation_ == std::numeric_limits::max()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, "effect container generation overflow")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, + "effect container generation overflow")); ++generation_; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } if (generation_ == std::numeric_limits::max()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, "effect container generation overflow")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "effect container generation overflow")); EffectContainer candidate(snapshotValue); candidate.generation_ = generation_ + 1; - *this = std::move(candidate); + *this = std::move(candidate); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } @@ -178,15 +182,13 @@ EffectContainer::Store::const_iterator EffectContainer::findIterator(const std:: } EffectContainer::Store::iterator EffectContainer::findIterator(eve::EffectId id) { - return std::find_if(effects_.begin(), effects_.end(), [id](const auto& effect) { - return !id.isNil() && effect->identity == id; - }); + return std::find_if(effects_.begin(), effects_.end(), + [id](const auto& effect) { return !id.isNil() && effect->identity == id; }); } EffectContainer::Store::const_iterator EffectContainer::findIterator(eve::EffectId id) const { - return std::find_if(effects_.begin(), effects_.end(), [id](const auto& effect) { - return !id.isNil() && effect->identity == id; - }); + return std::find_if(effects_.begin(), effects_.end(), + [id](const auto& effect) { return !id.isNil() && effect->identity == id; }); } eve::Result EffectContainer::apply(const EffectDefinition& definition, const std::string& subject, @@ -224,13 +226,13 @@ eve::Result EffectContainer::apply(const EffectDefinition& definiti } else { auto stacks = mergeStackCount(current, definition); if (!stacks) return eve::Result::failure(stacks.status()); - current.priority = definition.priority; - current.duration = definition.duration; - current.period = definition.period; + current.priority = definition.priority; + current.duration = definition.duration; + current.period = definition.period; current.periodElapsed = 0.0; - current.remaining = std::move(duration).takeValue(); - current.magnitude = std::move(magnitude).takeValue(); - current.stackCount = std::move(stacks).takeValue(); + current.remaining = std::move(duration).takeValue(); + current.magnitude = std::move(magnitude).takeValue(); + current.stackCount = std::move(stacks).takeValue(); emit(EffectEventKind::Refreshed, current); return eve::Result::success(current.id, eve::Status::success(eve::StatusCode::Applied)); } @@ -253,13 +255,13 @@ eve::Result EffectContainer::apply(const EffectDefinition& definiti return conflict("effect stack count exceeds its maximum"); return eve::Result::failure(stacks.status()); } - current.priority = definition.priority; - current.duration = definition.duration; - current.period = definition.period; + current.priority = definition.priority; + current.duration = definition.duration; + current.period = definition.period; current.periodElapsed = 0.0; - current.remaining = std::move(duration).takeValue(); - current.magnitude = std::move(magnitude).takeValue(); - current.stackCount = std::move(stacks).takeValue(); + current.remaining = std::move(duration).takeValue(); + current.magnitude = std::move(magnitude).takeValue(); + current.stackCount = std::move(stacks).takeValue(); emit(EffectEventKind::Stacked, current); return eve::Result::success(current.id, eve::Status::success(eve::StatusCode::Applied)); } @@ -280,31 +282,31 @@ eve::Result EffectContainer::apply(const EffectDefinition& definiti if (effectIdGenerator_) { const auto generated = effectIdGenerator_->generate(); if (!generated) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "canonical effect identity generation failed")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "canonical effect identity generation failed")); effect->identity = eve::EffectId::fromUuid(*generated); if (find(effect->identity) != nullptr) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, "canonical effect identity already exists")); - effect->id = effect->identity.format(); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "canonical effect identity already exists")); + effect->id = effect->identity.format(); } else { std::ostringstream id; id << "effect-" << std::setw(16) << std::setfill('0') << nextId_++; effect->id = id.str(); } - effect->subject = subject; - effect->type = definition.id; - effect->source = source; - effect->stackKey = key; - effect->priority = definition.priority; - effect->duration = std::max(0.0, definition.duration); - effect->remaining = normalisedRemaining(definition.duration); - effect->period = definition.period; + effect->subject = subject; + effect->type = definition.id; + effect->source = source; + effect->stackKey = key; + effect->priority = definition.priority; + effect->duration = std::max(0.0, definition.duration); + effect->remaining = normalisedRemaining(definition.duration); + effect->period = definition.period; effect->periodElapsed = 0.0; - effect->magnitude = definition.magnitude; - effect->stackCount = initialStacks; - effect->payload = definition.payload; - effect->tags = definition.tags; + effect->magnitude = definition.magnitude; + effect->stackCount = initialStacks; + effect->payload = definition.payload; + effect->tags = definition.tags; std::sort(effect->tags.begin(), effect->tags.end()); effect->tags.erase(std::unique(effect->tags.begin(), effect->tags.end()), effect->tags.end()); @@ -315,12 +317,10 @@ eve::Result EffectContainer::apply(const EffectDefinition& definiti } eve::Result EffectContainer::applyCanonical(const EffectDefinition& definition, - const std::string& subject, - const std::string& source) { + const std::string& subject, const std::string& source) { if (!effectIdGenerator_) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Unsupported, - "canonical effect identity requires an injected UUID entropy source")); + eve::DiagnosticCode::Unsupported, "canonical effect identity requires an injected UUID entropy source")); auto result = apply(definition, subject, source); if (!result) return eve::Result::failure(result.status()); const auto id = eve::EffectId::parse(std::move(result).takeValue()); @@ -332,19 +332,19 @@ eve::Result EffectContainer::applyCanonical(const EffectDefinitio eve::Result EffectContainer::handleFor(const std::string& id) const { if (id.empty()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "effect handle id must not be empty", "id")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "effect handle id must not be empty", "id")); if (find(id) == nullptr) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::NotFound, "effect instance is not active", "id")); - return eve::Result::success( - EffectHandle{id, generation_}, eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "effect instance is not active", "id")); + return eve::Result::success(EffectHandle{id, generation_}, + eve::Status::success(eve::StatusCode::Applied)); } eve::Result EffectContainer::resolve(EffectHandle handle) const { if (!handle.isValid()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "effect handle is invalid", "handle")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "effect handle is invalid", "handle")); if (handle.containerGeneration != generation_) return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::StaleHandle, "effect handle belongs to an older container generation", "handle")); @@ -352,8 +352,7 @@ eve::Result EffectContainer::resolve(EffectHandle handle) if (instance == nullptr) return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::NotFound, "effect instance is not active", "handle.instanceId")); - return eve::Result::success(instance, - eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(instance, eve::Status::success(eve::StatusCode::Applied)); } eve::Result EffectContainer::apply(const std::string& subject, const std::string& type, @@ -397,8 +396,8 @@ eve::Result EffectContainer::advanceInstances(double dtSeco return eve::Result::success(summary, eve::Status::success(eve::StatusCode::NoOp)); for (auto it = effects_.begin(); it != effects_.end();) { - EffectInstance& effect = **it; - const double activeDelta = effect.remaining < 0.0 ? dtSeconds : std::min(dtSeconds, effect.remaining); + EffectInstance& effect = **it; + const double activeDelta = effect.remaining < 0.0 ? dtSeconds : std::min(dtSeconds, effect.remaining); if (effect.period > 0.0 && activeDelta > 0.0) { if (!std::isfinite(effect.periodElapsed) || effect.periodElapsed > std::numeric_limits::max() - activeDelta) @@ -415,7 +414,7 @@ eve::Result EffectContainer::advanceInstances(double dtSeco periodic.stackCount = effect.stackCount; periodic.tags = effect.tags; periodic.effectIdentity = effect.identity; - periodic.tick = tick; + periodic.tick = tick; summary.periodicTicks.push_back(std::move(periodic)); emit(EffectEventKind::Periodic, effect, "periodic"); } @@ -440,11 +439,10 @@ eve::Result EffectContainer::update(double dtSeconds) { auto duration = eve::Duration::fromSeconds(dtSeconds); if (!duration) return eve::Result::failure(duration.status()); - auto nextTick = hasLastTick_ ? lastTick_.incremented() - : std::optional(eve::SimulationTick(1)); + auto nextTick = hasLastTick_ ? lastTick_.incremented() : std::optional(eve::SimulationTick(1)); if (!nextTick) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, "effect simulation tick overflow")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "effect simulation tick overflow")); return advance({*nextTick, std::move(duration).takeValue()}); } @@ -454,21 +452,21 @@ eve::Result EffectContainer::advance(const eve::SimulationS return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::InvalidArgument, "effect simulation delta must be non-negative")); if (hasLastTick_ && step.tick <= lastTick_) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, "effect simulation tick must advance monotonically")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "effect simulation tick must advance monotonically")); // Stage the lifecycle mutation so a failure does not publish partial state. - auto candidate = clone(); - candidate.lastTick_ = step.tick; + auto candidate = clone(); + candidate.lastTick_ = step.tick; candidate.hasLastTick_ = true; - auto result = candidate.advanceInstances(step.delta.seconds(), step.tick); + auto result = candidate.advanceInstances(step.delta.seconds(), step.tick); if (!result) return eve::Result::failure(result.status()); - auto status = result.status(); + auto status = result.status(); EffectUpdateSummary summary = std::move(result).takeValue(); - summary.tick = step.tick; - candidate.lastTick_ = step.tick; - candidate.hasLastTick_ = true; - *this = std::move(candidate); + summary.tick = step.tick; + candidate.lastTick_ = step.tick; + candidate.hasLastTick_ = true; + *this = std::move(candidate); return eve::Result::success(std::move(summary), std::move(status)); } @@ -476,8 +474,8 @@ void EffectContainer::clear() { effects_.clear(); events_.clear(); nextId_ = nextSequence_ = 1; - lastTick_ = eve::SimulationTick::zero(); - hasLastTick_ = false; + lastTick_ = eve::SimulationTick::zero(); + hasLastTick_ = false; if (generation_ != std::numeric_limits::max()) ++generation_; } @@ -572,8 +570,8 @@ eve::Result EffectExecutor::advance(EffectContainer& contai return container.update(dtSeconds); } -eve::Result EffectExecutor::advance(EffectContainer& container, - const eve::SimulationStep& step) const { +eve::Result EffectExecutor::advance(EffectContainer& container, + const eve::SimulationStep& step) const { return container.advance(step); } diff --git a/src/modules/effects/EffectContainer.h b/src/modules/effects/EffectContainer.h index c10edb0a..68cecc8d 100644 --- a/src/modules/effects/EffectContainer.h +++ b/src/modules/effects/EffectContainer.h @@ -104,8 +104,7 @@ public: * returned UUID's `format()` is stored as the legacy projection. */ [[nodiscard]] eve::Result applyCanonical(const EffectDefinition& definition, - const std::string& subject, - const std::string& source = {}); + const std::string& subject, const std::string& source = {}); /** * @brief Return a generation-qualified handle for a live instance. @@ -128,9 +127,9 @@ public: * @brief Applies scalar effect fields using the canonical Result contract. * @return The active instance id, or a structured rejection. */ - [[nodiscard]] eve::Result apply( - const std::string& subject, const std::string& type, const std::string& source, int priority, double duration, - const std::string& stackKey, StackPolicy policy); + [[nodiscard]] eve::Result apply(const std::string& subject, const std::string& type, + const std::string& source, int priority, double duration, + const std::string& stackKey, StackPolicy policy); /** * @brief Removes one active instance and emits a Removed event. @@ -256,14 +255,14 @@ private: std::string effectiveKey(const EffectDefinition& definition) const; eve::Result advanceInstances(double dtSeconds, eve::SimulationTick tick); - std::uint64_t nextId_ = 1; - std::uint64_t nextSequence_ = 1; + std::uint64_t nextId_ = 1; + std::uint64_t nextSequence_ = 1; std::optional effectIdGenerator_; - std::uint64_t generation_ = 1; - eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); - bool hasLastTick_ = false; - Store effects_; - std::deque events_; + std::uint64_t generation_ = 1; + eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); + bool hasLastTick_ = false; + Store effects_; + std::deque events_; }; /** @@ -285,8 +284,8 @@ public: [[nodiscard]] eve::Result advance(EffectContainer& container, double dtSeconds) const; /** @brief Advances using the scheduler-owned deterministic step contract. */ - [[nodiscard]] eve::Result advance(EffectContainer& container, - const eve::SimulationStep& step) const; + [[nodiscard]] eve::Result advance(EffectContainer& container, + const eve::SimulationStep& step) const; }; } // namespace eve::effects diff --git a/src/modules/effects/EffectDefinitionRuntime.cpp b/src/modules/effects/EffectDefinitionRuntime.cpp index 5b2ebedd..4337efd3 100644 --- a/src/modules/effects/EffectDefinitionRuntime.cpp +++ b/src/modules/effects/EffectDefinitionRuntime.cpp @@ -13,13 +13,12 @@ namespace { template eve::Result invalid(std::string message, std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, std::move(message), std::move(path), {}, - "effects.definition_runtime")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, std::move(message), + std::move(path), {}, "effects.definition_runtime")); } -eve::Result currentHandle( - eve::definitions::DefinitionRegistry& registry, const eve::DefinitionRef& reference) { +eve::Result currentHandle(eve::definitions::DefinitionRegistry& registry, + const eve::DefinitionRef& reference) { const auto& logical = reference.id(); return registry.handle(std::string(logical.namespaceName()), std::string(logical.name())); } @@ -31,7 +30,7 @@ const eve::Value* field(const eve::Value::Object& object, const char* name) { bool readString(const eve::Value::Object& object, const char* name, std::string& output) { const auto* value = field(object, name); - const auto* text = value ? value->getIf() : nullptr; + const auto* text = value ? value->getIf() : nullptr; if (!text) return false; output = *text; return true; @@ -40,8 +39,7 @@ bool readString(const eve::Value::Object& object, const char* name, std::string& bool readInt(const eve::Value::Object& object, const char* name, int& output) { const auto* value = field(object, name); if (const auto* integer = value ? value->getIf() : nullptr) { - if (*integer < std::numeric_limits::min() || *integer > std::numeric_limits::max()) - return false; + if (*integer < std::numeric_limits::min() || *integer > std::numeric_limits::max()) return false; output = static_cast(*integer); return true; } @@ -49,7 +47,7 @@ bool readInt(const eve::Value::Object& object, const char* name, int& output) { } bool readUInt(const eve::Value::Object& object, const char* name, std::uint32_t& output) { - const auto* value = field(object, name); + const auto* value = field(object, name); const auto* integer = value ? value->getIf() : nullptr; if (!integer || *integer < 0 || static_cast(*integer) > std::numeric_limits::max()) return false; @@ -71,10 +69,10 @@ bool readDouble(const eve::Value::Object& object, const char* name, double& outp } template -bool readEnum(const eve::Value::Object& object, const char* name, const char* const* names, - const Enum* values, std::size_t count, Enum& output) { +bool readEnum(const eve::Value::Object& object, const char* name, const char* const* names, const Enum* values, + std::size_t count, Enum& output) { const auto* value = field(object, name); - const auto* text = value ? value->getIf() : nullptr; + const auto* text = value ? value->getIf() : nullptr; if (!text) return false; for (std::size_t i = 0; i < count; ++i) { if (*text == names[i]) { @@ -86,19 +84,18 @@ bool readEnum(const eve::Value::Object& object, const char* name, const char* co } template -bool readOptionalEnum(const eve::Value::Object& object, const char* name, - const char* const* names, const Enum* values, std::size_t count, - Enum& output) { +bool readOptionalEnum(const eve::Value::Object& object, const char* name, const char* const* names, const Enum* values, + std::size_t count, Enum& output) { const auto* value = field(object, name); if (value == nullptr) return true; return readEnum(object, name, names, values, count, output); } eve::Result parseState(const eve::definitions::Definition& definition, - const eve::DefinitionRef& reference) { + const eve::DefinitionRef& reference) { auto parsed = eve::Value::fromJson(definition.json); if (!parsed) return eve::Result::failure(parsed.status()); - auto value = std::move(parsed).takeValue(); + auto value = std::move(parsed).takeValue(); const auto* object = value.getIf(); if (!object) return invalid("effect definition payload must be an object", "json"); @@ -135,50 +132,46 @@ eve::Result parseState(const eve::definitions::Definition& d } if (state.maxStacks != 0 && state.stackCount > state.maxStacks) return invalid("effect stackCount exceeds maxStacks", "stackCount"); - constexpr const char* stackModeNames[] = {"replace", "new_instance", "reuse", "accumulate"}; - constexpr StackMode stackModeValues[] = {StackMode::Replace, StackMode::NewInstance, - StackMode::Reuse, StackMode::Accumulate}; - if (!readOptionalEnum(*object, "stackMode", stackModeNames, stackModeValues, - std::size(stackModeNames), state.policy.stackMode)) + constexpr const char* stackModeNames[] = {"replace", "new_instance", "reuse", "accumulate"}; + constexpr StackMode stackModeValues[] = {StackMode::Replace, StackMode::NewInstance, StackMode::Reuse, + StackMode::Accumulate}; + if (!readOptionalEnum(*object, "stackMode", stackModeNames, stackModeValues, std::size(stackModeNames), + state.policy.stackMode)) return invalid("effect stackMode is invalid", "stackMode"); - constexpr const char* stackCountPolicyNames[] = {"keep", "increment", "set"}; - constexpr StackCountPolicy stackCountPolicyValues[] = { - StackCountPolicy::Keep, StackCountPolicy::Increment, StackCountPolicy::Set}; - if (!readOptionalEnum(*object, "stackCountPolicy", stackCountPolicyNames, - stackCountPolicyValues, std::size(stackCountPolicyNames), - state.policy.stackCount)) + constexpr const char* stackCountPolicyNames[] = {"keep", "increment", "set"}; + constexpr StackCountPolicy stackCountPolicyValues[] = {StackCountPolicy::Keep, StackCountPolicy::Increment, + StackCountPolicy::Set}; + if (!readOptionalEnum(*object, "stackCountPolicy", stackCountPolicyNames, stackCountPolicyValues, + std::size(stackCountPolicyNames), state.policy.stackCount)) return invalid("effect stackCountPolicy is invalid", "stackCountPolicy"); - constexpr const char* durationPolicyNames[] = {"keep", "replace", "extend"}; - constexpr DurationPolicy durationPolicyValues[] = { - DurationPolicy::Keep, DurationPolicy::Replace, DurationPolicy::Extend}; - if (!readOptionalEnum(*object, "durationPolicy", durationPolicyNames, - durationPolicyValues, std::size(durationPolicyNames), - state.policy.duration)) + constexpr const char* durationPolicyNames[] = {"keep", "replace", "extend"}; + constexpr DurationPolicy durationPolicyValues[] = {DurationPolicy::Keep, DurationPolicy::Replace, + DurationPolicy::Extend}; + if (!readOptionalEnum(*object, "durationPolicy", durationPolicyNames, durationPolicyValues, + std::size(durationPolicyNames), state.policy.duration)) return invalid("effect durationPolicy is invalid", "durationPolicy"); - constexpr const char* magnitudePolicyNames[] = {"keep", "replace", "add", "max"}; - constexpr MagnitudePolicy magnitudePolicyValues[] = { - MagnitudePolicy::Keep, MagnitudePolicy::Replace, MagnitudePolicy::Add, - MagnitudePolicy::Max}; - if (!readOptionalEnum(*object, "magnitudePolicy", magnitudePolicyNames, - magnitudePolicyValues, std::size(magnitudePolicyNames), - state.policy.magnitude)) + constexpr const char* magnitudePolicyNames[] = {"keep", "replace", "add", "max"}; + constexpr MagnitudePolicy magnitudePolicyValues[] = {MagnitudePolicy::Keep, MagnitudePolicy::Replace, + MagnitudePolicy::Add, MagnitudePolicy::Max}; + if (!readOptionalEnum(*object, "magnitudePolicy", magnitudePolicyNames, magnitudePolicyValues, + std::size(magnitudePolicyNames), state.policy.magnitude)) return invalid("effect magnitudePolicy is invalid", "magnitudePolicy"); - constexpr const char* overflowPolicyNames[] = {"reject", "clamp", "replace_oldest"}; - constexpr OverflowPolicy overflowPolicyValues[] = { - OverflowPolicy::Reject, OverflowPolicy::Clamp, OverflowPolicy::ReplaceOldest}; - if (!readOptionalEnum(*object, "overflowPolicy", overflowPolicyNames, - overflowPolicyValues, std::size(overflowPolicyNames), - state.policy.overflow)) + constexpr const char* overflowPolicyNames[] = {"reject", "clamp", "replace_oldest"}; + constexpr OverflowPolicy overflowPolicyValues[] = {OverflowPolicy::Reject, OverflowPolicy::Clamp, + OverflowPolicy::ReplaceOldest}; + if (!readOptionalEnum(*object, "overflowPolicy", overflowPolicyNames, overflowPolicyValues, + std::size(overflowPolicyNames), state.policy.overflow)) return invalid("effect overflowPolicy is invalid", "overflowPolicy"); state.policy.maxStacks = state.maxStacks; - state.remaining = state.duration > 0.0 ? state.duration : -1.0; + state.remaining = state.duration > 0.0 ? state.duration : -1.0; if (const auto* value = field(*object, "tags")) { const auto* array = value->getIf(); if (!array) return invalid("effect tags must be an array", "tags"); for (const auto& item : *array) { const auto* tag = item.getIf(); - if (!tag || tag->empty()) return invalid("effect tag must be a non-empty string", "tags"); + if (!tag || tag->empty()) + return invalid("effect tag must be a non-empty string", "tags"); state.tags.push_back(*tag); } std::sort(state.tags.begin(), state.tags.end()); @@ -198,8 +191,8 @@ eve::Result parseState(const eve::definitions::Definition& d return eve::Result::success(std::move(state)); } -eve::Result resolveState(eve::definitions::DefinitionRegistry& registry, - const eve::DefinitionRef& reference, +eve::Result resolveState(eve::definitions::DefinitionRegistry& registry, + const eve::DefinitionRef& reference, const eve::definition::DefinitionHandle& handle) { auto definition = registry.resolveHandle(handle); if (!definition) return eve::Result::failure(definition.status()); @@ -208,10 +201,10 @@ eve::Result resolveState(eve::definitions::DefinitionRegistr } // namespace -eve::Result EffectDefinitionRuntime::create( - eve::definitions::DefinitionRegistry& registry, eve::DefinitionRef definition, - std::string subject, std::string source, eve::PersistentId instanceId, - eve::definition::ReloadPolicy policy) { +eve::Result EffectDefinitionRuntime::create(eve::definitions::DefinitionRegistry& registry, + eve::DefinitionRef definition, std::string subject, + std::string source, eve::PersistentId instanceId, + eve::definition::ReloadPolicy policy) { if (!definition.id().isValid()) return invalid("effect definition reference is invalid"); if (subject.empty()) return invalid("effect subject must not be empty", "subject"); auto handle = currentHandle(registry, definition); @@ -221,9 +214,8 @@ eve::Result EffectDefinitionRuntime::create( auto runtime = eve::definition::RuntimeInstance::create( instanceId, std::move(definition), handle.value().generation, std::move(state).takeValue()); if (!runtime) return eve::Result::failure(runtime.status()); - return eve::Result::success( - EffectDefinitionRuntime(registry, std::move(runtime).takeValue(), std::move(subject), - std::move(source), policy)); + return eve::Result::success(EffectDefinitionRuntime( + registry, std::move(runtime).takeValue(), std::move(subject), std::move(source), policy)); } const eve::definition::InstanceIdentity& EffectDefinitionRuntime::identity() const noexcept { @@ -244,33 +236,32 @@ bool EffectDefinitionRuntime::isActive() const noexcept { return runtime_.isActi eve::Result EffectDefinitionRuntime::applyTo(EffectInstance* effect) const { if (effect == nullptr) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "effect runtime cannot project to a null instance", "effect", - {}, "effects.definition_runtime")); - EffectInstance candidate = *effect; - candidate.id = identity().instanceId.format(); - candidate.subject = subject_; - candidate.type = identity().definition.id().name(); - candidate.source = source_; - candidate.stackKey = state().stackKey; - candidate.priority = state().priority; - candidate.duration = state().duration; - candidate.remaining = state().remaining; - candidate.magnitude = state().magnitude; - candidate.stackCount = state().stackCount; - candidate.policy = state().policy; - candidate.payload = state().payload; - candidate.tags = state().tags; - candidate.identity = eve::EffectId::fromUuid(identity().instanceId); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "effect runtime cannot project to a null instance", + "effect", {}, "effects.definition_runtime")); + EffectInstance candidate = *effect; + candidate.id = identity().instanceId.format(); + candidate.subject = subject_; + candidate.type = identity().definition.id().name(); + candidate.source = source_; + candidate.stackKey = state().stackKey; + candidate.priority = state().priority; + candidate.duration = state().duration; + candidate.remaining = state().remaining; + candidate.magnitude = state().magnitude; + candidate.stackCount = state().stackCount; + candidate.policy = state().policy; + candidate.payload = state().payload; + candidate.tags = state().tags; + candidate.identity = eve::EffectId::fromUuid(identity().instanceId); candidate.definitionIdentity = identity(); - candidate.reloadPolicy = policy_; + candidate.reloadPolicy = policy_; using std::swap; swap(*effect, candidate); return eve::Result::success(); } -eve::Result EffectDefinitionRuntime::reload( - eve::definition::ReloadPolicy policy) { +eve::Result EffectDefinitionRuntime::reload(eve::definition::ReloadPolicy policy) { if (registry_ == nullptr) return invalid("effect definition registry is not bound", "registry"); auto next = currentHandle(*registry_, identity().definition); @@ -278,16 +269,13 @@ eve::Result EffectDefinitionRuntime::reload( auto defaults = resolveState(*registry_, identity().definition, next.value()); if (!defaults) return eve::Result::failure(defaults.status()); EffectRuntimeState defaultState = std::move(defaults).takeValue(); - auto rebuild = [defaultState](const EffectRuntimeState& oldState, - const eve::definition::InstanceIdentity&, - const eve::definition::DefinitionHandle&) mutable - -> eve::Result { + auto rebuild = [defaultState](const EffectRuntimeState& oldState, const eve::definition::InstanceIdentity&, + const eve::definition::DefinitionHandle&) mutable -> eve::Result { EffectRuntimeState rebuilt = defaultState; if (oldState.remaining >= 0.0) rebuilt.remaining = rebuilt.duration > 0.0 ? std::min(oldState.remaining, rebuilt.duration) : -1.0; - rebuilt.stackCount = rebuilt.maxStacks == 0 - ? oldState.stackCount - : std::min(oldState.stackCount, rebuilt.maxStacks); + rebuilt.stackCount = + rebuilt.maxStacks == 0 ? oldState.stackCount : std::min(oldState.stackCount, rebuilt.maxStacks); if (rebuilt.stackCount == 0) rebuilt.stackCount = 1; return eve::Result::success(std::move(rebuilt)); }; diff --git a/src/modules/effects/EffectDefinitionRuntime.h b/src/modules/effects/EffectDefinitionRuntime.h index 913c9438..96720361 100644 --- a/src/modules/effects/EffectDefinitionRuntime.h +++ b/src/modules/effects/EffectDefinitionRuntime.h @@ -20,15 +20,15 @@ namespace eve::effects { * dynamic field representation. */ struct EffectRuntimeState { - std::string stackKey; - int priority = 0; - double duration = 0.0; - double remaining = -1.0; - double magnitude = 0.0; - std::uint32_t stackCount = 1; - std::uint32_t maxStacks = 1; - EffectPolicy policy; - EffectPayload payload; + std::string stackKey; + int priority = 0; + double duration = 0.0; + double remaining = -1.0; + double magnitude = 0.0; + std::uint32_t stackCount = 1; + std::uint32_t maxStacks = 1; + EffectPolicy policy; + EffectPayload payload; std::vector tags; }; @@ -53,8 +53,8 @@ public: * @return A typed runtime or a structured parse/resolution failure. */ [[nodiscard]] static eve::Result create( - eve::definitions::DefinitionRegistry& registry, eve::DefinitionRef definition, - std::string subject, std::string source, eve::PersistentId instanceId, + eve::definitions::DefinitionRegistry& registry, eve::DefinitionRef definition, std::string subject, + std::string source, eve::PersistentId instanceId, eve::definition::ReloadPolicy policy = eve::definition::ReloadPolicy::KeepInstanceValues); /** @brief Move an effect runtime while retaining its borrowed registry binding. */ @@ -94,22 +94,23 @@ public: * @brief Resolve the registry's current generation and execute a policy. * @return Reload outcome, or a failure leaving typed state and identity unchanged. */ - [[nodiscard]] eve::Result reload( - eve::definition::ReloadPolicy policy); + [[nodiscard]] eve::Result reload(eve::definition::ReloadPolicy policy); private: - EffectDefinitionRuntime(eve::definitions::DefinitionRegistry& registry, - eve::definition::RuntimeInstance runtime, - std::string subject, std::string source, - eve::definition::ReloadPolicy policy) - : registry_(®istry), runtime_(std::move(runtime)), subject_(std::move(subject)), - source_(std::move(source)), policy_(policy) {} + EffectDefinitionRuntime(eve::definitions::DefinitionRegistry& registry, + eve::definition::RuntimeInstance runtime, std::string subject, + std::string source, eve::definition::ReloadPolicy policy) + : registry_(®istry), + runtime_(std::move(runtime)), + subject_(std::move(subject)), + source_(std::move(source)), + policy_(policy) {} - eve::definitions::DefinitionRegistry* registry_ = nullptr; // borrowed + eve::definitions::DefinitionRegistry* registry_ = nullptr; // borrowed eve::definition::RuntimeInstance runtime_; - std::string subject_; - std::string source_; - eve::definition::ReloadPolicy policy_; + std::string subject_; + std::string source_; + eve::definition::ReloadPolicy policy_; }; } // namespace eve::effects diff --git a/src/modules/effects/EffectTypes.cpp b/src/modules/effects/EffectTypes.cpp index e534308e..36aa0bac 100644 --- a/src/modules/effects/EffectTypes.cpp +++ b/src/modules/effects/EffectTypes.cpp @@ -8,8 +8,7 @@ namespace eve::effects { namespace { eve::Result invalid(std::string message) { - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, std::move(message))); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, std::move(message))); } } // namespace @@ -44,8 +43,7 @@ bool EffectPayload::has(const std::string& key) const { return values_.contains( eve::Result EffectPayload::erase(const std::string& key) { if (key.empty()) return invalid("effect payload key must not be empty"); const bool removed = values_.erase(key) != 0; - return eve::Result::success( - eve::Status::success(removed ? eve::StatusCode::Applied : eve::StatusCode::NoOp)); + return eve::Result::success(eve::Status::success(removed ? eve::StatusCode::Applied : eve::StatusCode::NoOp)); } std::string EffectPayload::getJson(const std::string& key) const { @@ -83,23 +81,19 @@ eve::Result EffectDefinition::validate() const { eve::Result EffectInstance::addTag(const std::string& tag) { if (tag.empty()) return invalid("effect tag must not be empty"); const auto it = std::lower_bound(tags.begin(), tags.end(), tag); - if (it != tags.end() && *it == tag) - return eve::Result::success(eve::Status::success(eve::StatusCode::NoOp)); + if (it != tags.end() && *it == tag) return eve::Result::success(eve::Status::success(eve::StatusCode::NoOp)); tags.insert(it, tag); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } eve::Result EffectInstance::removeTag(const std::string& tag) { const auto it = std::lower_bound(tags.begin(), tags.end(), tag); - if (it == tags.end() || *it != tag) - return eve::Result::success(eve::Status::success(eve::StatusCode::NoOp)); + if (it == tags.end() || *it != tag) return eve::Result::success(eve::Status::success(eve::StatusCode::NoOp)); tags.erase(it); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -bool EffectInstance::hasTag(const std::string& tag) const { - return std::binary_search(tags.begin(), tags.end(), tag); -} +bool EffectInstance::hasTag(const std::string& tag) const { return std::binary_search(tags.begin(), tags.end(), tag); } int EffectInstance::tagCount() const { return static_cast(tags.size()); } @@ -110,9 +104,9 @@ std::string EffectInstance::tagAt(int index) const { std::string policyName(StackPolicy policy) { switch (policy) { - case StackPolicy::Replace: return "replace"; - case StackPolicy::Stack: return "stack"; - case StackPolicy::Refresh: return "refresh"; + case StackPolicy::Replace: return "replace"; + case StackPolicy::Stack: return "stack"; + case StackPolicy::Refresh: return "refresh"; } return "unknown"; } @@ -131,12 +125,12 @@ bool parsePolicy(const std::string& name, StackPolicy& policy) { std::string eventKindName(EffectEventKind kind) { switch (kind) { - case EffectEventKind::Applied: return "applied"; - case EffectEventKind::Refreshed: return "refreshed"; - case EffectEventKind::Stacked: return "stacked"; - case EffectEventKind::Periodic: return "periodic"; - case EffectEventKind::Expired: return "expired"; - case EffectEventKind::Removed: return "removed"; + case EffectEventKind::Applied: return "applied"; + case EffectEventKind::Refreshed: return "refreshed"; + case EffectEventKind::Stacked: return "stacked"; + case EffectEventKind::Periodic: return "periodic"; + case EffectEventKind::Expired: return "expired"; + case EffectEventKind::Removed: return "removed"; } return "unknown"; } diff --git a/src/modules/effects/EffectTypes.h b/src/modules/effects/EffectTypes.h index f5ac68e7..28e2a88d 100644 --- a/src/modules/effects/EffectTypes.h +++ b/src/modules/effects/EffectTypes.h @@ -9,9 +9,9 @@ * magnitude and tags later. */ -#include "common/Time.h" #include "common/Identity.h" #include "common/Result.h" +#include "common/Time.h" #include "common/Value.h" #include "common/definitions/DefinitionRuntime.h" @@ -49,12 +49,12 @@ enum class OverflowPolicy { Reject, Clamp, ReplaceOldest }; * stack count for one instance. */ struct EffectPolicy { - StackMode stackMode = StackMode::Replace; + StackMode stackMode = StackMode::Replace; StackCountPolicy stackCount = StackCountPolicy::Keep; - DurationPolicy duration = DurationPolicy::Replace; - MagnitudePolicy magnitude = MagnitudePolicy::Replace; - OverflowPolicy overflow = OverflowPolicy::Reject; - std::uint32_t maxStacks = 1; + DurationPolicy duration = DurationPolicy::Replace; + MagnitudePolicy magnitude = MagnitudePolicy::Replace; + OverflowPolicy overflow = OverflowPolicy::Reject; + std::uint32_t maxStacks = 1; }; /** @brief Deterministic JSON-compatible payload carried by a definition or instance. */ @@ -96,13 +96,13 @@ private: * definition does not mutate gameplay attributes. */ struct EffectDefinition { - std::string id; - std::string stackKey; - int priority = 0; - double duration = 0.0; + std::string id; + std::string stackKey; + int priority = 0; + double duration = 0.0; /** @brief Period in seconds for domain executor settlement; zero disables periodic ticks. */ - double period = 0.0; - double magnitude = 0.0; + double period = 0.0; + double magnitude = 0.0; std::uint32_t stackCount = 1; EffectPolicy policy; EffectPayload payload; @@ -123,24 +123,24 @@ struct EffectDefinition { * settled, how tags affect an actor, or how a period causes damage/healing. */ struct EffectInstance { - std::string id; - std::string subject; - std::string type; - std::string source; - std::string stackKey; - int priority = 0; - double duration = 0.0; - double remaining = -1.0; + std::string id; + std::string subject; + std::string type; + std::string source; + std::string stackKey; + int priority = 0; + double duration = 0.0; + double remaining = -1.0; /** @brief Copied periodic interval; the common container owns its accumulator. */ - double period = 0.0; + double period = 0.0; /** @brief Elapsed time since the last periodic tick, owned by the instance lifecycle. */ double periodElapsed = 0.0; - double magnitude = 0.0; - std::uint32_t stackCount = 1; + double magnitude = 0.0; + std::uint32_t stackCount = 1; EffectPayload payload; std::vector tags; /** @brief Typed application policy projected from the current definition. */ - EffectPolicy policy; + EffectPolicy policy; /** @brief UUID-backed identity for persistence/event boundaries; nil in the legacy facade. */ eve::EffectId identity; /** @@ -150,8 +150,7 @@ struct EffectInstance { * new code checks this identity against the current definition generation. */ eve::definition::InstanceIdentity definitionIdentity; - eve::definition::ReloadPolicy reloadPolicy = - eve::definition::ReloadPolicy::KeepInstanceValues; + eve::definition::ReloadPolicy reloadPolicy = eve::definition::ReloadPolicy::KeepInstanceValues; /** @brief Adds a tag while retaining deterministic lexical order. */ [[nodiscard]] eve::Result addTag(const std::string& tag); @@ -170,13 +169,11 @@ using Effect = EffectInstance; /** @brief Generation-qualified local reference to a canonical effect instance. */ struct EffectHandle { - std::string instanceId; + std::string instanceId; std::uint64_t containerGeneration = 0; /** @brief Whether this handle contains a local instance id and generation. */ - [[nodiscard]] bool isValid() const noexcept { - return !instanceId.empty() && containerGeneration != 0; - } + [[nodiscard]] bool isValid() const noexcept { return !instanceId.empty() && containerGeneration != 0; } }; /** @brief Lifecycle event kind emitted by an effect container. */ @@ -184,13 +181,13 @@ enum class EffectEventKind { Applied, Refreshed, Stacked, Periodic, Expired, Rem /** @brief Deterministically sequenced effect lifecycle event. */ struct EffectEvent { - std::uint64_t sequence = 0; - EffectEventKind kind = EffectEventKind::Applied; - std::string effectId; - std::string subject; - std::string type; - std::string source; - std::string reason; + std::uint64_t sequence = 0; + EffectEventKind kind = EffectEventKind::Applied; + std::string effectId; + std::string subject; + std::string type; + std::string source; + std::string reason; /** @brief UUID-backed effect identity; the string field remains a compatibility projection. */ eve::EffectId effectIdentity; /** @brief Scheduler tick at which the lifecycle event was committed. */ @@ -199,20 +196,20 @@ struct EffectEvent { /** @brief One periodic trigger handed to a domain executor for settlement. */ struct EffectPeriodicTick { - std::string effectId; - std::string subject; - std::string source; - double magnitude = 0.0; - std::uint32_t stackCount = 1; + std::string effectId; + std::string subject; + std::string source; + double magnitude = 0.0; + std::uint32_t stackCount = 1; std::vector tags; - eve::EffectId effectIdentity; - eve::SimulationTick tick = eve::SimulationTick::zero(); + eve::EffectId effectIdentity; + eve::SimulationTick tick = eve::SimulationTick::zero(); }; /** @brief Summary returned by the lifecycle executor after one simulation step. */ struct EffectUpdateSummary { - std::uint32_t expired = 0; - double elapsedSeconds = 0.0; + std::uint32_t expired = 0; + double elapsedSeconds = 0.0; /** @brief Periodic triggers in deterministic container order. */ std::vector periodicTicks; /** @brief Scheduler tick that produced this summary. */ @@ -227,8 +224,6 @@ bool parsePolicy(const std::string& name, StackPolicy& policy); std::string eventKindName(EffectEventKind kind); /** @brief Writes the stable lifecycle-event spelling to a stream. */ -inline std::ostream& operator<<(std::ostream& stream, EffectEventKind kind) { - return stream << eventKindName(kind); -} +inline std::ostream& operator<<(std::ostream& stream, EffectEventKind kind) { return stream << eventKindName(kind); } } // namespace eve::effects diff --git a/src/modules/effects/Effects.cpp b/src/modules/effects/Effects.cpp index eca07f7f..28febf42 100644 --- a/src/modules/effects/Effects.cpp +++ b/src/modules/effects/Effects.cpp @@ -22,34 +22,31 @@ struct ScriptEffectContainer { }; template -eve::Result effectsBindingFailure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - code, std::move(message), std::move(path), {}, "effects.squirrel")); +eve::Result effectsBindingFailure(eve::DiagnosticCode code, std::string message, std::string path = {}) { + return eve::Result::failure( + eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "effects.squirrel")); } -eve::Result effectDefinitionFromBinding( - const std::string& type, int priority, float duration, - const std::string& stackKey, const std::string& policyNameValue) { +eve::Result effectDefinitionFromBinding(const std::string& type, int priority, float duration, + const std::string& stackKey, + const std::string& policyNameValue) { StackPolicy policy; if (!parsePolicy(policyNameValue, policy)) - return effectsBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "unknown effect stack policy", "policy"); + return effectsBindingFailure(eve::DiagnosticCode::InvalidArgument, + "unknown effect stack policy", "policy"); EffectDefinition definition; - definition.id = type; + definition.id = type; definition.stackKey = stackKey; definition.priority = priority; definition.duration = duration; switch (policy) { - case StackPolicy::Replace: definition.policy.stackMode = StackMode::Replace; break; - case StackPolicy::Stack: definition.policy.stackMode = StackMode::NewInstance; break; - case StackPolicy::Refresh: definition.policy.stackMode = StackMode::Reuse; break; + case StackPolicy::Replace: definition.policy.stackMode = StackMode::Replace; break; + case StackPolicy::Stack: definition.policy.stackMode = StackMode::NewInstance; break; + case StackPolicy::Refresh: definition.policy.stackMode = StackMode::Reuse; break; } auto validation = definition.validate(); - if (!validation.ok()) - return eve::Result::failure(validation.status()); + if (!validation.ok()) return eve::Result::failure(validation.status()); return eve::Result::success(std::move(definition)); } @@ -63,21 +60,17 @@ eve::Value effectUpdateProjection(EffectUpdateSummary summary) { template ssq::Table makeOwnedProxy(HSQUIRRELVM vm, eve::Result&& reference, Release&& release) { - if (!reference) - return eve::script::projectStatusResult(vm, reference.status(), false, false); - const Ref ref = std::move(reference).takeValue(); - auto object = eve::script::makeOwnedSquirrelInstance( - vm, std::make_unique(ref)); + if (!reference) return eve::script::projectStatusResult(vm, reference.status(), false, false); + const Ref ref = std::move(reference).takeValue(); + auto object = eve::script::makeOwnedSquirrelInstance(vm, std::make_unique(ref)); if (!object) { const eve::Status status = object.status(); object.ignore("failed to create owned effects proxy"); - std::invoke(std::forward(release), ref).ignore( - "rollback failed owned effects allocation"); + std::invoke(std::forward(release), ref).ignore("rollback failed owned effects allocation"); return eve::script::projectStatusResult(vm, status, false, false); } ssq::Object owned = std::move(object).takeValue(); - auto result = eve::script::projectStatusResult( - vm, eve::Status::success(eve::StatusCode::Applied), true, false); + auto result = eve::script::projectStatusResult(vm, eve::Status::success(eve::StatusCode::Applied), true, false); result.set("value", owned); result.set("ownership", std::string("owned")); result.set("ownerEpoch", static_cast(ref.ownerEpoch)); @@ -92,8 +85,7 @@ eve::Result Effects::newContainer() { return module->containers_.emplace(std::make_unique()); } -eve::script::Borrowed Effects::resolve( - EffectContainerHandleRef reference) noexcept { +eve::script::Borrowed Effects::resolve(EffectContainerHandleRef reference) noexcept { Effects* module = ModuleManager::getInstance("Effects"); if (!module) return {}; return module->containers_.resolve(reference); @@ -102,8 +94,8 @@ eve::script::Borrowed Effects::resolve( eve::Result Effects::release(EffectContainerHandleRef reference) { Effects* module = ModuleManager::getInstance("Effects"); if (!module) - return effectsBindingFailure(eve::DiagnosticCode::StaleHandle, - "Effects module is no longer loaded", "container"); + return effectsBindingFailure(eve::DiagnosticCode::StaleHandle, "Effects module is no longer loaded", + "container"); return module->containers_.erase(reference); } @@ -116,7 +108,7 @@ bool Effects::isStale(EffectContainerHandleRef reference) noexcept { Module_IMPL(Effects, new Effects()); void Effects::expose(ssq::Table& table) { - const HSQUIRRELVM vm = table.getHandle(); + const HSQUIRRELVM vm = table.getHandle(); auto payload = table.addClass( "EffectPayload", std::function([]() -> EffectPayload* { return nullptr; }), false); payload.addFunc("setString", &EffectPayload::setString); @@ -128,16 +120,16 @@ void Effects::expose(ssq::Table& table) { payload.addFunc("setJson", [vm](EffectPayload* value, const std::string& key, const std::string& json) { if (!value) return eve::script::projectResult( - vm, effectsBindingFailure(eve::DiagnosticCode::InvalidArgument, - "effect payload must not be null", "payload")); + vm, effectsBindingFailure(eve::DiagnosticCode::InvalidArgument, "effect payload must not be null", + "payload")); return eve::script::projectResult(vm, value->setJson(key, json)); }); payload.addFunc("has", &EffectPayload::has); payload.addFunc("erase", [vm](EffectPayload* value, const std::string& key) { if (!value) return eve::script::projectResult( - vm, effectsBindingFailure(eve::DiagnosticCode::InvalidArgument, - "effect payload must not be null", "payload")); + vm, effectsBindingFailure(eve::DiagnosticCode::InvalidArgument, "effect payload must not be null", + "payload")); return eve::script::projectResult(vm, value->erase(key)); }); payload.addFunc("getJson", &EffectPayload::getJson); @@ -159,21 +151,17 @@ void Effects::expose(ssq::Table& table) { effect.addFunc("getPayload", [](Effect* value) -> EffectPayload* { return value ? &value->payload : nullptr; }); effect.addFunc("addTag", [vm](Effect* value, const std::string& tag) { if (!value) - return eve::script::projectResult( - vm, effectsBindingFailure(eve::DiagnosticCode::InvalidArgument, - "effect must not be null", "effect")); + return eve::script::projectResult(vm, effectsBindingFailure(eve::DiagnosticCode::InvalidArgument, + "effect must not be null", "effect")); return eve::script::projectResult(vm, value->addTag(tag)); }); effect.addFunc("removeTag", [vm](Effect* value, const std::string& tag) { if (!value) - return eve::script::projectResult( - vm, effectsBindingFailure(eve::DiagnosticCode::InvalidArgument, - "effect must not be null", "effect")); + return eve::script::projectResult(vm, effectsBindingFailure(eve::DiagnosticCode::InvalidArgument, + "effect must not be null", "effect")); return eve::script::projectResult(vm, value->removeTag(tag)); }); - effect.addFunc("hasTag", [](Effect* value, const std::string& tag) { - return value && value->hasTag(tag); - }); + effect.addFunc("hasTag", [](Effect* value, const std::string& tag) { return value && value->hasTag(tag); }); effect.addFunc("tagCount", &Effect::tagCount); effect.addFunc("tagAt", &Effect::tagAt); @@ -191,84 +179,68 @@ void Effects::expose(ssq::Table& table) { auto container = table.addClass( "EffectContainer", std::function([]() -> EffectContainer* { return nullptr; }), false); - container.addFunc("apply", [vm](EffectContainer* value, const std::string& subject, - const std::string& type, const std::string& source, - int priority, float duration, const std::string& stackKey, - const std::string& policyNameValue) { + container.addFunc("apply", [vm](EffectContainer* value, const std::string& subject, const std::string& type, + const std::string& source, int priority, float duration, + const std::string& stackKey, const std::string& policyNameValue) { if (!value) return eve::script::projectResult( - vm, effectsBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "effect container must not be null", "container"), + vm, + effectsBindingFailure(eve::DiagnosticCode::InvalidArgument, + "effect container must not be null", "container"), [](std::string&& id) { return eve::Value(std::move(id)); }); - auto definition = effectDefinitionFromBinding( - type, priority, duration, stackKey, policyNameValue); + auto definition = effectDefinitionFromBinding(type, priority, duration, stackKey, policyNameValue); if (!definition.ok()) return eve::script::projectResult( - vm, effectsBindingFailure( - definition.code() == eve::StatusCode::Rejected - ? eve::DiagnosticCode::InvalidArgument - : eve::DiagnosticCode::Failed, - definition.status().describe(), "definition"), + vm, + effectsBindingFailure(definition.code() == eve::StatusCode::Rejected + ? eve::DiagnosticCode::InvalidArgument + : eve::DiagnosticCode::Failed, + definition.status().describe(), "definition"), [](std::string&& id) { return eve::Value(std::move(id)); }); EffectDefinition input = std::move(definition).takeValue(); - return eve::script::projectResult( - vm, value->apply(input, subject, source), - [](std::string&& id) { return eve::Value(std::move(id)); }); + return eve::script::projectResult(vm, value->apply(input, subject, source), + [](std::string&& id) { return eve::Value(std::move(id)); }); }); - container.addFunc("applyCanonical", [vm](EffectContainer* value, - const std::string& subject, - const std::string& type, - const std::string& source, - int priority, float duration, - const std::string& stackKey, - const std::string& policyNameValue) { - if (!value) - return eve::script::projectResult( - vm, effectsBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "effect container must not be null", "container"), - [](eve::EffectId id) { - return eve::Value(id.isNil() ? std::string{} : id.format()); - }); - auto definition = effectDefinitionFromBinding( - type, priority, duration, stackKey, policyNameValue); - if (!definition.ok()) - return eve::script::projectResult( - vm, effectsBindingFailure( - definition.code() == eve::StatusCode::Rejected - ? eve::DiagnosticCode::InvalidArgument - : eve::DiagnosticCode::Failed, - definition.status().describe(), "definition"), - [](eve::EffectId id) { - return eve::Value(id.isNil() ? std::string{} : id.format()); - }); - EffectDefinition input = std::move(definition).takeValue(); - return eve::script::projectResult( - vm, value->applyCanonical(input, subject, source), - [](eve::EffectId id) { + container.addFunc( + "applyCanonical", + [vm](EffectContainer* value, const std::string& subject, const std::string& type, const std::string& source, + int priority, float duration, const std::string& stackKey, const std::string& policyNameValue) { + if (!value) + return eve::script::projectResult( + vm, + effectsBindingFailure(eve::DiagnosticCode::InvalidArgument, + "effect container must not be null", "container"), + [](eve::EffectId id) { return eve::Value(id.isNil() ? std::string{} : id.format()); }); + auto definition = effectDefinitionFromBinding(type, priority, duration, stackKey, policyNameValue); + if (!definition.ok()) + return eve::script::projectResult( + vm, + effectsBindingFailure(definition.code() == eve::StatusCode::Rejected + ? eve::DiagnosticCode::InvalidArgument + : eve::DiagnosticCode::Failed, + definition.status().describe(), "definition"), + [](eve::EffectId id) { return eve::Value(id.isNil() ? std::string{} : id.format()); }); + EffectDefinition input = std::move(definition).takeValue(); + return eve::script::projectResult(vm, value->applyCanonical(input, subject, source), [](eve::EffectId id) { return eve::Value(id.isNil() ? std::string{} : id.format()); }); - }); - container.addFunc("remove", [vm](EffectContainer* value, const std::string& id, - const std::string& reason) { + }); + container.addFunc("remove", [vm](EffectContainer* value, const std::string& id, const std::string& reason) { if (!value) return eve::script::projectResult( - vm, effectsBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "effect container must not be null", "container")); + vm, effectsBindingFailure(eve::DiagnosticCode::InvalidArgument, + "effect container must not be null", "container")); return eve::script::projectResult(vm, value->remove(id, reason)); }); container.addFunc("update", [vm](EffectContainer* value, float dtSeconds) { if (!value) return eve::script::projectResult( - vm, effectsBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "effect container must not be null", "container"), + vm, + effectsBindingFailure(eve::DiagnosticCode::InvalidArgument, + "effect container must not be null", "container"), [](EffectUpdateSummary summary) { return effectUpdateProjection(summary); }); - return eve::script::projectResult( - vm, value->update(dtSeconds), - [](EffectUpdateSummary summary) { return effectUpdateProjection(summary); }); + return eve::script::projectResult(vm, value->update(dtSeconds), + [](EffectUpdateSummary summary) { return effectUpdateProjection(summary); }); }); container.addFunc("clear", &EffectContainer::clear); container.addFunc("find", [](EffectContainer* value, const std::string& id) -> Effect* { @@ -283,10 +255,9 @@ void Effects::expose(ssq::Table& table) { return value ? value->subjectAt(subject, index) : nullptr; }); container.addFunc("taggedCount", &EffectContainer::taggedCount); - container.addFunc("taggedAt", [](EffectContainer* value, const std::string& subject, - const std::string& tag, int index) -> Effect* { - return value ? value->taggedAt(subject, tag, index) : nullptr; - }); + container.addFunc("taggedAt", + [](EffectContainer* value, const std::string& subject, const std::string& tag, + int index) -> Effect* { return value ? value->taggedAt(subject, tag, index) : nullptr; }); container.addFunc("eventCount", &EffectContainer::eventCount); container.addFunc("eventAt", [](EffectContainer* value, int index) -> EffectEvent* { return value ? value->eventAt(index) : nullptr; @@ -302,12 +273,10 @@ void Effects::expose(ssq::Table& table) { ownedContainer.addFunc("ownership", [](ScriptEffectContainer*) { return std::string(eve::script::objectSemanticName(eve::script::ObjectSemantic::Owned)); }); - ownedContainer.addFunc("handle", [](ScriptEffectContainer* value) { - return value ? value->reference.packed() : std::uint64_t{0}; - }); - ownedContainer.addFunc("isStale", [](ScriptEffectContainer* value) { - return !value || Effects::isStale(value->reference); - }); + ownedContainer.addFunc( + "handle", [](ScriptEffectContainer* value) { return value ? value->reference.packed() : std::uint64_t{0}; }); + ownedContainer.addFunc("isStale", + [](ScriptEffectContainer* value) { return !value || Effects::isStale(value->reference); }); ownedContainer.addFunc("release", [vm](ScriptEffectContainer* value) { if (!value) return eve::script::projectResult( @@ -318,55 +287,50 @@ void Effects::expose(ssq::Table& table) { // report a stale handle and a second release returns StaleHandle. return eve::script::projectResult(vm, std::move(result)); }); - ownedContainer.addFunc("apply", [vm](ScriptEffectContainer* value, - const std::string& subject, - const std::string& type, - const std::string& source, - int priority, float duration, - const std::string& stackKey, - const std::string& policyNameValue) { - if (!value) - return eve::script::projectResult( - vm, effectsBindingFailure(eve::DiagnosticCode::InvalidArgument, - "owned effect container proxy must not be null", - "container"), - [](std::string&& id) { return eve::Value(std::move(id)); }); - auto containerView = Effects::resolve(value->reference); - if (!containerView.isBound()) - return eve::script::projectResult( - vm, effectsBindingFailure(eve::DiagnosticCode::StaleHandle, - "owned effect container handle is stale", - "container"), - [](std::string&& id) { return eve::Value(std::move(id)); }); - auto definition = effectDefinitionFromBinding(type, priority, duration, stackKey, - policyNameValue); - if (!definition) - return eve::script::projectResult( - vm, effectsBindingFailure(eve::DiagnosticCode::InvalidArgument, - definition.status().describe(), "definition"), - [](std::string&& id) { return eve::Value(std::move(id)); }); - auto input = std::move(definition).takeValue(); - return eve::script::projectResult(vm, containerView->apply(input, subject, source), - [](std::string&& id) { return eve::Value(std::move(id)); }); - }); + ownedContainer.addFunc( + "apply", [vm](ScriptEffectContainer* value, const std::string& subject, const std::string& type, + const std::string& source, int priority, float duration, const std::string& stackKey, + const std::string& policyNameValue) { + if (!value) + return eve::script::projectResult( + vm, + effectsBindingFailure(eve::DiagnosticCode::InvalidArgument, + "owned effect container proxy must not be null", "container"), + [](std::string&& id) { return eve::Value(std::move(id)); }); + auto containerView = Effects::resolve(value->reference); + if (!containerView.isBound()) + return eve::script::projectResult( + vm, + effectsBindingFailure(eve::DiagnosticCode::StaleHandle, + "owned effect container handle is stale", "container"), + [](std::string&& id) { return eve::Value(std::move(id)); }); + auto definition = effectDefinitionFromBinding(type, priority, duration, stackKey, policyNameValue); + if (!definition) + return eve::script::projectResult( + vm, + effectsBindingFailure(eve::DiagnosticCode::InvalidArgument, + definition.status().describe(), "definition"), + [](std::string&& id) { return eve::Value(std::move(id)); }); + auto input = std::move(definition).takeValue(); + return eve::script::projectResult(vm, containerView->apply(input, subject, source), + [](std::string&& id) { return eve::Value(std::move(id)); }); + }); ownedContainer.addFunc("update", [vm](ScriptEffectContainer* value, float dt) { if (!value) return eve::script::projectResult( - vm, effectsBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "owned effect container proxy must not be null", "container"), + vm, + effectsBindingFailure( + eve::DiagnosticCode::InvalidArgument, "owned effect container proxy must not be null", "container"), [](EffectUpdateSummary summary) { return effectUpdateProjection(summary); }); auto containerView = Effects::resolve(value->reference); if (!containerView.isBound()) return eve::script::projectResult( - vm, effectsBindingFailure( - eve::DiagnosticCode::StaleHandle, - "owned effect container handle is stale", "container"), + vm, + effectsBindingFailure(eve::DiagnosticCode::StaleHandle, + "owned effect container handle is stale", "container"), [](EffectUpdateSummary summary) { return effectUpdateProjection(summary); }); return eve::script::projectResult(vm, containerView->update(dt), - [](EffectUpdateSummary summary) { - return effectUpdateProjection(summary); - }); + [](EffectUpdateSummary summary) { return effectUpdateProjection(summary); }); }); ownedContainer.addFunc("clear", [](ScriptEffectContainer* value) { if (!value) return; @@ -374,110 +338,91 @@ void Effects::expose(ssq::Table& table) { if (containerView.isBound()) containerView->clear(); }); ownedContainer.addFunc("find", [](ScriptEffectContainer* value, const std::string& id) -> Effect* { - auto containerView = value ? Effects::resolve(value->reference) - : eve::script::Borrowed(); + auto containerView = value ? Effects::resolve(value->reference) : eve::script::Borrowed(); return containerView.isBound() ? containerView->find(id) : nullptr; }); ownedContainer.addFunc("effectCount", [](ScriptEffectContainer* value) { - auto containerView = value ? Effects::resolve(value->reference) - : eve::script::Borrowed(); + auto containerView = value ? Effects::resolve(value->reference) : eve::script::Borrowed(); return containerView.isBound() ? containerView->effectCount() : 0; }); ownedContainer.addFunc("effectAt", [](ScriptEffectContainer* value, int index) -> Effect* { - auto containerView = value ? Effects::resolve(value->reference) - : eve::script::Borrowed(); + auto containerView = value ? Effects::resolve(value->reference) : eve::script::Borrowed(); return containerView.isBound() ? containerView->effectAt(index) : nullptr; }); ownedContainer.addFunc("subjectCount", [](ScriptEffectContainer* value, const std::string& subject) { - auto containerView = value ? Effects::resolve(value->reference) - : eve::script::Borrowed(); + auto containerView = value ? Effects::resolve(value->reference) : eve::script::Borrowed(); return containerView.isBound() ? containerView->subjectCount(subject) : 0; }); - ownedContainer.addFunc("subjectAt", [](ScriptEffectContainer* value, const std::string& subject, - int index) -> Effect* { - auto containerView = value ? Effects::resolve(value->reference) - : eve::script::Borrowed(); - return containerView.isBound() ? containerView->subjectAt(subject, index) : nullptr; - }); - ownedContainer.addFunc("taggedCount", [](ScriptEffectContainer* value, const std::string& subject, - const std::string& tag) { - auto containerView = value ? Effects::resolve(value->reference) - : eve::script::Borrowed(); - return containerView.isBound() ? containerView->taggedCount(subject, tag) : 0; - }); - ownedContainer.addFunc("taggedAt", [](ScriptEffectContainer* value, const std::string& subject, - const std::string& tag, int index) -> Effect* { - auto containerView = value ? Effects::resolve(value->reference) - : eve::script::Borrowed(); - return containerView.isBound() ? containerView->taggedAt(subject, tag, index) : nullptr; - }); + ownedContainer.addFunc( + "subjectAt", [](ScriptEffectContainer* value, const std::string& subject, int index) -> Effect* { + auto containerView = value ? Effects::resolve(value->reference) : eve::script::Borrowed(); + return containerView.isBound() ? containerView->subjectAt(subject, index) : nullptr; + }); + ownedContainer.addFunc( + "taggedCount", [](ScriptEffectContainer* value, const std::string& subject, const std::string& tag) { + auto containerView = value ? Effects::resolve(value->reference) : eve::script::Borrowed(); + return containerView.isBound() ? containerView->taggedCount(subject, tag) : 0; + }); + ownedContainer.addFunc( + "taggedAt", + [](ScriptEffectContainer* value, const std::string& subject, const std::string& tag, int index) -> Effect* { + auto containerView = value ? Effects::resolve(value->reference) : eve::script::Borrowed(); + return containerView.isBound() ? containerView->taggedAt(subject, tag, index) : nullptr; + }); ownedContainer.addFunc("eventCount", [](ScriptEffectContainer* value) { - auto containerView = value ? Effects::resolve(value->reference) - : eve::script::Borrowed(); + auto containerView = value ? Effects::resolve(value->reference) : eve::script::Borrowed(); return containerView.isBound() ? containerView->eventCount() : 0; }); ownedContainer.addFunc("eventAt", [](ScriptEffectContainer* value, int index) -> EffectEvent* { - auto containerView = value ? Effects::resolve(value->reference) - : eve::script::Borrowed(); + auto containerView = value ? Effects::resolve(value->reference) : eve::script::Borrowed(); return containerView.isBound() ? containerView->eventAt(index) : nullptr; }); ownedContainer.addFunc("clearEvents", [](ScriptEffectContainer* value) { - auto containerView = value ? Effects::resolve(value->reference) - : eve::script::Borrowed(); + auto containerView = value ? Effects::resolve(value->reference) : eve::script::Borrowed(); if (containerView.isBound()) containerView->clearEvents(); }); - ownedContainer.addFunc("remove", [vm](ScriptEffectContainer* value, const std::string& id, - const std::string& reason) { - if (!value) + ownedContainer.addFunc( + "remove", [vm](ScriptEffectContainer* value, const std::string& id, const std::string& reason) { + if (!value) + return eve::script::projectResult( + vm, effectsBindingFailure(eve::DiagnosticCode::InvalidArgument, + "owned effect container proxy must not be null", "container")); + auto containerView = Effects::resolve(value->reference); + if (!containerView.isBound()) + return eve::script::projectResult( + vm, effectsBindingFailure(eve::DiagnosticCode::StaleHandle, + "owned effect container handle is stale", "container")); + return eve::script::projectResult(vm, containerView->remove(id, reason)); + }); + ownedContainer.addFunc( + "applyCanonical", [vm](ScriptEffectContainer* value, const std::string& subject, const std::string& type, + const std::string& source, int priority, float duration, const std::string& stackKey, + const std::string& policyNameValue) { + if (!value) + return eve::script::projectResult( + vm, + effectsBindingFailure(eve::DiagnosticCode::InvalidArgument, + "owned effect container proxy must not be null", "container"), + [](eve::EffectId id) { return eve::Value(id.isNil() ? std::string{} : id.format()); }); + auto containerView = Effects::resolve(value->reference); + if (!containerView.isBound()) + return eve::script::projectResult( + vm, + effectsBindingFailure(eve::DiagnosticCode::StaleHandle, + "owned effect container handle is stale", "container"), + [](eve::EffectId id) { return eve::Value(id.isNil() ? std::string{} : id.format()); }); + auto definition = effectDefinitionFromBinding(type, priority, duration, stackKey, policyNameValue); + if (!definition.ok()) + return eve::script::projectResult( + vm, + effectsBindingFailure(eve::DiagnosticCode::InvalidArgument, + definition.status().describe(), "definition"), + [](eve::EffectId id) { return eve::Value(id.isNil() ? std::string{} : id.format()); }); + auto input = std::move(definition).takeValue(); return eve::script::projectResult( - vm, effectsBindingFailure(eve::DiagnosticCode::InvalidArgument, - "owned effect container proxy must not be null", "container")); - auto containerView = Effects::resolve(value->reference); - if (!containerView.isBound()) - return eve::script::projectResult( - vm, effectsBindingFailure(eve::DiagnosticCode::StaleHandle, - "owned effect container handle is stale", "container")); - return eve::script::projectResult(vm, containerView->remove(id, reason)); - }); - ownedContainer.addFunc("applyCanonical", [vm](ScriptEffectContainer* value, - const std::string& subject, - const std::string& type, - const std::string& source, - int priority, float duration, - const std::string& stackKey, - const std::string& policyNameValue) { - if (!value) - return eve::script::projectResult( - vm, effectsBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "owned effect container proxy must not be null", "container"), - [](eve::EffectId id) { - return eve::Value(id.isNil() ? std::string{} : id.format()); - }); - auto containerView = Effects::resolve(value->reference); - if (!containerView.isBound()) - return eve::script::projectResult( - vm, effectsBindingFailure(eve::DiagnosticCode::StaleHandle, - "owned effect container handle is stale", - "container"), - [](eve::EffectId id) { - return eve::Value(id.isNil() ? std::string{} : id.format()); - }); - auto definition = effectDefinitionFromBinding(type, priority, duration, stackKey, - policyNameValue); - if (!definition.ok()) - return eve::script::projectResult( - vm, effectsBindingFailure(eve::DiagnosticCode::InvalidArgument, - definition.status().describe(), "definition"), - [](eve::EffectId id) { - return eve::Value(id.isNil() ? std::string{} : id.format()); - }); - auto input = std::move(definition).takeValue(); - return eve::script::projectResult(vm, containerView->applyCanonical(input, subject, source), - [](eve::EffectId id) { - return eve::Value(id.isNil() ? std::string{} : id.format()); - }); - }); + vm, containerView->applyCanonical(input, subject, source), + [](eve::EffectId id) { return eve::Value(id.isNil() ? std::string{} : id.format()); }); + }); auto cls = table.addClass(name, Effects::create, false); expose(cls); @@ -487,8 +432,7 @@ void Effects::expose(ssq::Class& cls) { cls.addFunc("getName", &Effects::getName); cls.addFunc("newContainer", [vm = cls.getHandle()](Effects*) -> ssq::Table { return makeOwnedProxy( - vm, Effects::newContainer(), - [](EffectContainerHandleRef ref) { return Effects::release(ref); }); + vm, Effects::newContainer(), [](EffectContainerHandleRef ref) { return Effects::release(ref); }); }); } diff --git a/src/modules/effects/Effects.h b/src/modules/effects/Effects.h index fc988e37..4dbadd32 100644 --- a/src/modules/effects/Effects.h +++ b/src/modules/effects/Effects.h @@ -24,7 +24,7 @@ using EffectContainerHandleRef = eve::script::RuntimeHandleRef newContainer(); /** @brief Resolves a live container as a non-owning observation. */ - [[nodiscard]] static eve::script::Borrowed resolve( - EffectContainerHandleRef reference) noexcept; + [[nodiscard]] static eve::script::Borrowed resolve(EffectContainerHandleRef reference) noexcept; /** @brief Releases a container owned by the Effects module. */ [[nodiscard]] static eve::Result release(EffectContainerHandleRef reference); diff --git a/src/modules/filesystem/ResourceReader.cpp b/src/modules/filesystem/ResourceReader.cpp index b83070ef..7267e803 100644 --- a/src/modules/filesystem/ResourceReader.cpp +++ b/src/modules/filesystem/ResourceReader.cpp @@ -7,15 +7,14 @@ namespace eve::filesystem { namespace { [[nodiscard]] Result> unsupported(const ResourceUri& uri) { - return Result>::failure(Diagnostic::error( - DiagnosticCode::Unsupported, - "filesystem ResourceReader has no provider for this resource namespace", uri.format())); + return Result>::failure( + Diagnostic::error(DiagnosticCode::Unsupported, + "filesystem ResourceReader has no provider for this resource namespace", uri.format())); } [[nodiscard]] Result> readFailure(const ResourceUri& uri) { return Result>::failure( - Diagnostic::error(DiagnosticCode::Failed, "filesystem provider could not read resource", - uri.format())); + Diagnostic::error(DiagnosticCode::Failed, "filesystem provider could not read resource", uri.format())); } template diff --git a/src/modules/fluids/FluidSimulation.cpp b/src/modules/fluids/FluidSimulation.cpp index 8c54e55c..f71a1a41 100644 --- a/src/modules/fluids/FluidSimulation.cpp +++ b/src/modules/fluids/FluidSimulation.cpp @@ -77,9 +77,7 @@ int FluidSimulation::spawnDrop(const glm::vec3& center, float radius, int count) return added; } -void FluidSimulation::step(float dt) { - step(dt, std::max(1, params_.iterations)); -} +void FluidSimulation::step(float dt) { step(dt, std::max(1, params_.iterations)); } void FluidSimulation::step(float dt, int substeps) { if (count_ <= 0 || sdf_.voxelCount() <= 0) return; diff --git a/src/modules/fluids/Fluids.cpp b/src/modules/fluids/Fluids.cpp index ebeb5e3c..387a177d 100644 --- a/src/modules/fluids/Fluids.cpp +++ b/src/modules/fluids/Fluids.cpp @@ -114,9 +114,7 @@ int FluidSimulator::spawnDrop(const glm::vec3& center, float radius, int count) return sim_.spawnDrop(center, radius, count); } -void FluidSimulator::step(float dt) { - stepSolver(dt, std::max(1, sim_.params().iterations)); -} +void FluidSimulator::step(float dt) { stepSolver(dt, std::max(1, sim_.params().iterations)); } void FluidSimulator::stepSolver(float dt, int substeps) { if (sim_.particleCount() <= 0) return; @@ -172,11 +170,9 @@ void FluidSimulator::stepSolver(float dt, int substeps) { for (int i = 0; i < sim_.particleCount(); ++i) dens[size_t(i)] = densF[size_t(i)]; } -eve::Result FluidSimulator::step( - const eve::SimulationStep& stepValue, - const eve::physics::SimulationSettings& settings) { - auto valid = eve::physics::detail::validateSimulationStep( - stepValue, settings, observation_); +eve::Result FluidSimulator::step(const eve::SimulationStep& stepValue, + const eve::physics::SimulationSettings& settings) { + auto valid = eve::physics::detail::validateSimulationStep(stepValue, settings, observation_); if (!valid) return valid; auto next = eve::physics::detail::advanceSimulationObservation(observation_, stepValue); if (!next) return eve::Result::failure(next.status()); @@ -190,36 +186,31 @@ eve::Result FluidSimulator::step( available = false; } if (!available) { - backendFallback_ = true; + backendFallback_ = true; backendSelectionStatus_ = eve::Status( eve::StatusCode::Applied, {eve::Diagnostic::warning( - eve::DiagnosticCode::Unsupported, - "Fluid GPGPU accelerator unavailable; CPU reference selected", - "fluids.simulationBackend", - {{"selected", "cpu"}, {"fallback", "explicit"}})}); + eve::DiagnosticCode::Unsupported, "Fluid GPGPU accelerator unavailable; CPU reference selected", + "fluids.simulationBackend", {{"selected", "cpu"}, {"fallback", "explicit"}})}); } } stepSolver(static_cast(stepValue.delta.seconds()), settings.subStepCount); } catch (const std::exception& error) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, - std::string("Fluid simulation step failed: ") + error.what(), + eve::DiagnosticCode::Failed, std::string("Fluid simulation step failed: ") + error.what(), "fluids.simulationBackend.step")); } catch (...) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, - "Fluid simulation step failed with an unknown exception", + eve::DiagnosticCode::Failed, "Fluid simulation step failed with an unknown exception", "fluids.simulationBackend.step")); } observation_ = std::move(next).takeValue(); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result FluidSimulator::restoreObservation( - const eve::physics::SimulationObservation& observation) { - auto valid = eve::physics::detail::validateSimulationObservation( - observation, "fluids.simulationBackend.restoreObservation"); +eve::Result FluidSimulator::restoreObservation(const eve::physics::SimulationObservation& observation) { + auto valid = + eve::physics::detail::validateSimulationObservation(observation, "fluids.simulationBackend.restoreObservation"); if (!valid) return valid; observation_ = observation; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); diff --git a/src/modules/fluids/Fluids.h b/src/modules/fluids/Fluids.h index 9519e3f8..91c923f0 100644 --- a/src/modules/fluids/Fluids.h +++ b/src/modules/fluids/Fluids.h @@ -14,11 +14,11 @@ #include "fluids/FluidMath.h" #include "fluids/FluidSdf.h" #include "fluids/FluidSimulation.h" -#include "fluids/FluidSurfaceRenderer.h" #include "fluids/FluidSurfaceBinding.h" +#include "fluids/FluidSurfaceRenderer.h" #include "fluids/SurfaceDropletSimulation.h" -#include "fluids/SurfaceFluidRenderData.h" #include "fluids/SurfaceFluidContracts.h" +#include "fluids/SurfaceFluidRenderData.h" #include "fluids/SurfaceFluidSceneRenderer.h" #include "fluids/SurfaceWetnessField.h" #include "physics/SimulationBackend.h" @@ -71,16 +71,12 @@ public: /** @brief Advances the production CPU/GPGPU solver with the shared contract. */ [[nodiscard("check the fluid step outcome")]] - eve::Result step(const eve::SimulationStep& step, - const eve::physics::SimulationSettings& settings) override; + eve::Result step(const eve::SimulationStep& step, const eve::physics::SimulationSettings& settings) override; /** @brief Returns completed tick/time observables. */ - [[nodiscard]] eve::physics::SimulationObservation observation() const noexcept override { - return observation_; - } + [[nodiscard]] eve::physics::SimulationObservation observation() const noexcept override { return observation_; } /** @brief Reports the actual CPU or production GPU path selected. */ [[nodiscard]] eve::physics::SimulationBackendKind kind() const noexcept override { - return gpuOk_ ? eve::physics::SimulationBackendKind::Gpu - : eve::physics::SimulationBackendKind::Cpu; + return gpuOk_ ? eve::physics::SimulationBackendKind::Gpu : eve::physics::SimulationBackendKind::Cpu; } /** @brief CPU/GPGPU particle results are equivalent within a numeric tolerance. */ [[nodiscard]] eve::physics::SimulationDeterminism determinism() const noexcept override { @@ -88,14 +84,15 @@ public: } /** @brief Restores tick/progress metadata after an owner-level restore. */ [[nodiscard("check fluid observation restore")]] - eve::Result restoreObservation( - const eve::physics::SimulationObservation& observation) override; + eve::Result restoreObservation(const eve::physics::SimulationObservation& observation) override; /** @brief Whether optional accelerator selection fell back to CPU. */ [[nodiscard]] bool usedBackendFallback() const noexcept { return backendFallback_; } /** @brief Selection status, including observable provider/degraded-path diagnostics. */ [[nodiscard("inspect fluid backend selection diagnostics")]] - eve::Status backendSelectionStatus() const { return backendSelectionStatus_; } + eve::Status backendSelectionStatus() const { + return backendSelectionStatus_; + } /** @return live particle count. */ int getParticleCount() const { return sim_.particleCount(); } @@ -163,10 +160,10 @@ private: FluidSimulation sim_; bool preferGpu_ = false; bool gpuOk_ = false; - bool backendFallback_ = false; + bool backendFallback_ = false; bool sdfDirty_ = true; eve::physics::SimulationObservation observation_; - eve::Status backendSelectionStatus_ = eve::Status::success(); + eve::Status backendSelectionStatus_ = eve::Status::success(); eve::gpgpu::Gpgpu* gpgpu_ = nullptr; eve::gpgpu::ComputeShader* shClear_ = nullptr; diff --git a/src/modules/fluids/SurfaceFluidContracts.cpp b/src/modules/fluids/SurfaceFluidContracts.cpp index f761e27f..f57620c7 100644 --- a/src/modules/fluids/SurfaceFluidContracts.cpp +++ b/src/modules/fluids/SurfaceFluidContracts.cpp @@ -12,28 +12,22 @@ namespace { eve::Result staleSolver(const char* path) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, - "Surface-fluid adapter has no live borrowed solver", path)); + eve::DiagnosticCode::PreconditionViolation, "Surface-fluid adapter has no live borrowed solver", path)); } eve::Result invalidInput(const char* path, const char* message) { - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, message, path)); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, message, path)); } -eve::Result applied() { - return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); -} +eve::Result applied() { return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result validateTick(const eve::SimulationStep& step, - eve::SimulationTick lastTick, const char* path) { +eve::Result validateTick(const eve::SimulationStep& step, eve::SimulationTick lastTick, const char* path) { const double seconds = step.delta.seconds(); if (!std::isfinite(seconds) || seconds < 0.0) return invalidInput(path, "Surface-fluid duration must be finite and non-negative"); if (!(step.tick > lastTick)) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, - "Surface-fluid simulation tick must increase strictly", path)); + eve::DiagnosticCode::PreconditionViolation, "Surface-fluid simulation tick must increase strictly", path)); return applied(); } @@ -55,20 +49,18 @@ eve::Result FluidSimulationAdapter::step(const eve::SimulationStep& stepVa return applied(); } -int FluidSimulationAdapter::particleCount() const noexcept { - return solver_ ? solver_->particleCount() : 0; -} +int FluidSimulationAdapter::particleCount() const noexcept { return solver_ ? solver_->particleCount() : 0; } std::span FluidSimulationAdapter::particles() const noexcept { if (!solver_) return {}; const auto& values = solver_->particles(); - const auto count = static_cast(std::max(0, solver_->particleCount())); + const auto count = static_cast(std::max(0, solver_->particleCount())); return {values.data(), std::min(count, values.size())}; } -eve::Result FluidSurfaceConstraintAdapter::build( - const std::vector& positions, const std::vector& indices, - const std::vector& uvs) { +eve::Result FluidSurfaceConstraintAdapter::build(const std::vector& positions, + const std::vector& indices, + const std::vector& uvs) { if (!binding_) return staleSolver("fluids.surfaceConstraint.binding"); if (!binding_->build(positions, indices, uvs)) return invalidInput("fluids.surfaceConstraint.topology", @@ -82,90 +74,79 @@ eve::Result FluidSurfaceConstraintAdapter::setTransform(const glm::mat4& t return applied(); } -eve::Result FluidSurfaceConstraintAdapter::setDeformedPositions( - const std::vector& worldPositions) { +eve::Result FluidSurfaceConstraintAdapter::setDeformedPositions(const std::vector& worldPositions) { if (!binding_) return staleSolver("fluids.surfaceConstraint.binding"); if (!binding_->setDeformedPositions(worldPositions)) - return invalidInput("fluids.surfaceConstraint.pose", - "Deformed surface vertex count does not match topology"); + return invalidInput("fluids.surfaceConstraint.pose", "Deformed surface vertex count does not match topology"); return applied(); } -bool FluidSurfaceConstraintAdapter::isValid() const noexcept { - return binding_ && binding_->isValid(); -} +bool FluidSurfaceConstraintAdapter::isValid() const noexcept { return binding_ && binding_->isValid(); } -eve::Result FluidSurfaceConstraintAdapter::evaluate( - const SurfaceLocation& location, eve::Duration poseDelta) const { +eve::Result FluidSurfaceConstraintAdapter::evaluate(const SurfaceLocation& location, + eve::Duration poseDelta) const { auto validDuration = validateDuration(poseDelta, "fluids.surfaceConstraint.evaluate.dt"); if (!validDuration) return eve::Result::failure(validDuration.status()); if (!binding_ || !binding_->isValid()) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, - "Cannot evaluate an invalid surface constraint", "fluids.surfaceConstraint.evaluate")); + eve::DiagnosticCode::PreconditionViolation, "Cannot evaluate an invalid surface constraint", + "fluids.surfaceConstraint.evaluate")); if (location.triangle >= static_cast(binding_->triangleCount())) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::NotFound, - "Surface location triangle is outside the bound topology", + eve::DiagnosticCode::NotFound, "Surface location triangle is outside the bound topology", "fluids.surfaceConstraint.evaluate.triangle")); - return eve::Result::success( - binding_->evaluate(location, static_cast(poseDelta.seconds()))); + return eve::Result::success(binding_->evaluate(location, static_cast(poseDelta.seconds()))); } -eve::Result FluidSurfaceConstraintAdapter::project( - const glm::vec3& worldPosition, float maxDistance) const { +eve::Result FluidSurfaceConstraintAdapter::project(const glm::vec3& worldPosition, + float maxDistance) const { if (!binding_ || !binding_->isValid()) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, - "Cannot project against an invalid surface constraint", + eve::DiagnosticCode::PreconditionViolation, "Cannot project against an invalid surface constraint", "fluids.surfaceConstraint.project")); SurfaceLocation location; if (!binding_->project(worldPosition, maxDistance, location)) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::NotFound, - "No surface location satisfies the projection distance", + eve::DiagnosticCode::NotFound, "No surface location satisfies the projection distance", "fluids.surfaceConstraint.project")); return eve::Result::success(location); } -eve::Result FluidSurfaceConstraintAdapter::walk( - const SurfaceLocation& start, const glm::vec3& displacement, int maxCrossings) const { +eve::Result FluidSurfaceConstraintAdapter::walk(const SurfaceLocation& start, + const glm::vec3& displacement, + int maxCrossings) const { if (!binding_ || !binding_->isValid()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, - "Cannot walk an invalid surface constraint", "fluids.surfaceConstraint.walk")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, + "Cannot walk an invalid surface constraint", "fluids.surfaceConstraint.walk")); if (maxCrossings < 0) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "Surface traversal maxCrossings must be non-negative", + eve::DiagnosticCode::InvalidArgument, "Surface traversal maxCrossings must be non-negative", "fluids.surfaceConstraint.walk.maxCrossings")); const auto result = binding_->walkAcrossSurface(start, displacement, maxCrossings); if (!result.valid) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::NotFound, - "Surface traversal start is outside the bound topology", + eve::DiagnosticCode::NotFound, "Surface traversal start is outside the bound topology", "fluids.surfaceConstraint.walk.start")); return eve::Result::success(result); } -ScreenSpaceSurfaceReconstructionAdapter::ScreenSpaceSurfaceReconstructionAdapter( - const FluidSurfaceParams& params, bool preferGpu) { - if (params.width <= 0 || params.height <= 0) - throw eve::Exception("Surface SSF output dimensions must be positive"); +ScreenSpaceSurfaceReconstructionAdapter::ScreenSpaceSurfaceReconstructionAdapter(const FluidSurfaceParams& params, + bool preferGpu) { + if (params.width <= 0 || params.height <= 0) throw eve::Exception("Surface SSF output dimensions must be positive"); renderer_ = std::make_unique(params, preferGpu); } ScreenSpaceSurfaceReconstructionAdapter::~ScreenSpaceSurfaceReconstructionAdapter() = default; -eve::Result ScreenSpaceSurfaceReconstructionAdapter::reconstruct( - std::span positions, float particleRadius) { +eve::Result ScreenSpaceSurfaceReconstructionAdapter::reconstruct(std::span positions, + float particleRadius) { if (!renderer_) return staleSolver("fluids.screenSpaceReconstruction.renderer"); if (!std::isfinite(particleRadius) || particleRadius <= 0.f) return invalidInput("fluids.screenSpaceReconstruction.particleRadius", "Screen-space particle radius must be finite and positive"); for (const glm::vec3& position : positions) { - if (!std::isfinite(position.x) || !std::isfinite(position.y) || - !std::isfinite(position.z)) + if (!std::isfinite(position.x) || !std::isfinite(position.y) || !std::isfinite(position.z)) return invalidInput("fluids.screenSpaceReconstruction.positions", "Screen-space particle positions must be finite"); } @@ -174,35 +155,26 @@ eve::Result ScreenSpaceSurfaceReconstructionAdapter::reconstruct( return applied(); } -int ScreenSpaceSurfaceReconstructionAdapter::width() const noexcept { - return renderer_ ? renderer_->getWidth() : 0; -} +int ScreenSpaceSurfaceReconstructionAdapter::width() const noexcept { return renderer_ ? renderer_->getWidth() : 0; } -int ScreenSpaceSurfaceReconstructionAdapter::height() const noexcept { - return renderer_ ? renderer_->getHeight() : 0; -} +int ScreenSpaceSurfaceReconstructionAdapter::height() const noexcept { return renderer_ ? renderer_->getHeight() : 0; } std::span ScreenSpaceSurfaceReconstructionAdapter::depth() const noexcept { return renderer_ ? std::span(renderer_->depth()) : std::span{}; } std::span ScreenSpaceSurfaceReconstructionAdapter::normals() const noexcept { - return renderer_ ? std::span(renderer_->normals()) - : std::span{}; + return renderer_ ? std::span(renderer_->normals()) : std::span{}; } std::span ScreenSpaceSurfaceReconstructionAdapter::thickness() const noexcept { - return renderer_ ? std::span(renderer_->thickness()) - : std::span{}; + return renderer_ ? std::span(renderer_->thickness()) : std::span{}; } std::span ScreenSpaceSurfaceReconstructionAdapter::color() const noexcept { - return renderer_ ? std::span(renderer_->color()) - : std::span{}; + return renderer_ ? std::span(renderer_->color()) : std::span{}; } -bool ScreenSpaceSurfaceReconstructionAdapter::usingGpu() const noexcept { - return renderer_ && renderer_->usingGpu(); -} +bool ScreenSpaceSurfaceReconstructionAdapter::usingGpu() const noexcept { return renderer_ && renderer_->usingGpu(); } } // namespace eve::fluids diff --git a/src/modules/fluids/SurfaceFluidContracts.h b/src/modules/fluids/SurfaceFluidContracts.h index f915a88a..22d52e2b 100644 --- a/src/modules/fluids/SurfaceFluidContracts.h +++ b/src/modules/fluids/SurfaceFluidContracts.h @@ -54,8 +54,7 @@ public: /** @brief Builds topology and pose state from owning caller data. */ [[nodiscard("check surface constraint construction")]] - virtual eve::Result build(const std::vector& positions, - const std::vector& indices, + virtual eve::Result build(const std::vector& positions, const std::vector& indices, const std::vector& uvs = {}) = 0; /** @brief Applies a rigid pose while preserving the previous pose. */ @@ -64,26 +63,22 @@ public: /** @brief Applies a deformed pose with the existing topology. */ [[nodiscard("check surface constraint pose update")]] - virtual eve::Result setDeformedPositions( - const std::vector& worldPositions) = 0; + virtual eve::Result setDeformedPositions(const std::vector& worldPositions) = 0; /** @brief Returns whether topology and poses are currently valid. */ [[nodiscard]] virtual bool isValid() const noexcept = 0; /** @brief Evaluates a material-space location in the current pose. */ [[nodiscard("check surface location evaluation")]] - virtual eve::Result evaluate(const SurfaceLocation& location, - eve::Duration poseDelta) const = 0; + virtual eve::Result evaluate(const SurfaceLocation& location, eve::Duration poseDelta) const = 0; /** @brief Projects a world point to the nearest material-space location. */ [[nodiscard("check surface projection")]] - virtual eve::Result project(const glm::vec3& worldPosition, - float maxDistance) const = 0; + virtual eve::Result project(const glm::vec3& worldPosition, float maxDistance) const = 0; /** @brief Walks a material-space location over triangle adjacency. */ [[nodiscard("check surface traversal")]] - virtual eve::Result walk(const SurfaceLocation& start, - const glm::vec3& displacement, + virtual eve::Result walk(const SurfaceLocation& start, const glm::vec3& displacement, int maxCrossings = 16) const = 0; }; @@ -98,8 +93,7 @@ public: * @return Applied, or a structured input/device failure. */ [[nodiscard("check screen-space reconstruction")]] - virtual eve::Result reconstruct(std::span positions, - float particleRadius) = 0; + virtual eve::Result reconstruct(std::span positions, float particleRadius) = 0; /** @brief Output width in pixels. */ [[nodiscard]] virtual int width() const noexcept = 0; @@ -123,12 +117,12 @@ public: /** @param solver Borrowed CPU solver; it must outlive this adapter. */ explicit FluidSimulationAdapter(FluidSimulation& solver) noexcept : solver_(&solver) {} - [[nodiscard]] eve::Result step(const eve::SimulationStep& step) override; - [[nodiscard]] int particleCount() const noexcept override; + [[nodiscard]] eve::Result step(const eve::SimulationStep& step) override; + [[nodiscard]] int particleCount() const noexcept override; [[nodiscard]] std::span particles() const noexcept override; private: - FluidSimulation* solver_ = nullptr; + FluidSimulation* solver_ = nullptr; eve::SimulationTick lastTick_ = eve::SimulationTick::zero(); }; @@ -136,22 +130,19 @@ private: class FluidSurfaceConstraintAdapter final : public ISurfaceConstraint { public: /** @param binding Borrowed surface binding; it must outlive this adapter. */ - explicit FluidSurfaceConstraintAdapter(FluidSurfaceBinding& binding) noexcept - : binding_(&binding) {} + explicit FluidSurfaceConstraintAdapter(FluidSurfaceBinding& binding) noexcept : binding_(&binding) {} - [[nodiscard]] eve::Result build(const std::vector& positions, + [[nodiscard]] eve::Result build(const std::vector& positions, const std::vector& indices, - const std::vector& uvs = {}) override; + const std::vector& uvs = {}) override; [[nodiscard]] eve::Result setTransform(const glm::mat4& transform) override; - [[nodiscard]] eve::Result setDeformedPositions( - const std::vector& worldPositions) override; - [[nodiscard]] bool isValid() const noexcept override; - [[nodiscard]] eve::Result evaluate(const SurfaceLocation& location, - eve::Duration poseDelta) const override; - [[nodiscard]] eve::Result project(const glm::vec3& worldPosition, - float maxDistance) const override; - [[nodiscard]] eve::Result walk(const SurfaceLocation& start, - const glm::vec3& displacement, + [[nodiscard]] eve::Result setDeformedPositions(const std::vector& worldPositions) override; + [[nodiscard]] bool isValid() const noexcept override; + [[nodiscard]] eve::Result evaluate(const SurfaceLocation& location, + eve::Duration poseDelta) const override; + [[nodiscard]] eve::Result project(const glm::vec3& worldPosition, + float maxDistance) const override; + [[nodiscard]] eve::Result walk(const SurfaceLocation& start, const glm::vec3& displacement, int maxCrossings = 16) const override; private: @@ -164,8 +155,7 @@ private: * The renderer remains a presentation implementation; callers depend only on * this interface and may request the GPU path without making it mandatory. */ -class ScreenSpaceSurfaceReconstructionAdapter final - : public IScreenSpaceSurfaceReconstruction { +class ScreenSpaceSurfaceReconstructionAdapter final : public IScreenSpaceSurfaceReconstruction { public: /** * @param params Camera and output configuration. @@ -175,19 +165,17 @@ public: ScreenSpaceSurfaceReconstructionAdapter(const FluidSurfaceParams& params, bool preferGpu); ~ScreenSpaceSurfaceReconstructionAdapter() override; - ScreenSpaceSurfaceReconstructionAdapter(const ScreenSpaceSurfaceReconstructionAdapter&) = delete; - ScreenSpaceSurfaceReconstructionAdapter& operator=( - const ScreenSpaceSurfaceReconstructionAdapter&) = delete; + ScreenSpaceSurfaceReconstructionAdapter(const ScreenSpaceSurfaceReconstructionAdapter&) = delete; + ScreenSpaceSurfaceReconstructionAdapter& operator=(const ScreenSpaceSurfaceReconstructionAdapter&) = delete; - [[nodiscard]] eve::Result reconstruct(std::span positions, - float particleRadius) override; - [[nodiscard]] int width() const noexcept override; - [[nodiscard]] int height() const noexcept override; - [[nodiscard]] std::span depth() const noexcept override; - [[nodiscard]] std::span normals() const noexcept override; - [[nodiscard]] std::span thickness() const noexcept override; + [[nodiscard]] eve::Result reconstruct(std::span positions, float particleRadius) override; + [[nodiscard]] int width() const noexcept override; + [[nodiscard]] int height() const noexcept override; + [[nodiscard]] std::span depth() const noexcept override; + [[nodiscard]] std::span normals() const noexcept override; + [[nodiscard]] std::span thickness() const noexcept override; [[nodiscard]] std::span color() const noexcept override; - [[nodiscard]] bool usingGpu() const noexcept override; + [[nodiscard]] bool usingGpu() const noexcept override; private: std::unique_ptr renderer_; diff --git a/src/modules/font/FontLoader.cpp b/src/modules/font/FontLoader.cpp index 318621f0..7bfda98e 100644 --- a/src/modules/font/FontLoader.cpp +++ b/src/modules/font/FontLoader.cpp @@ -67,9 +67,7 @@ public: * @return `false`; ResourceManager is the sole CPU cache owner and performs * the reload before any consumer is notified. */ - eve::Result reload(const std::string &) override { - return eve::Result::success(false); - } + eve::Result reload(const std::string &) override { return eve::Result::success(false); } eve::Resource *load(const std::string &key) override { const std::string path = eve::ResourceManager::pathOfKey(key); diff --git a/src/modules/game_event/GameEvent.cpp b/src/modules/game_event/GameEvent.cpp index 906b8c9f..20da1efe 100644 --- a/src/modules/game_event/GameEvent.cpp +++ b/src/modules/game_event/GameEvent.cpp @@ -3,6 +3,7 @@ #include "common/SquirrelBinding.h" #include "common/Json.h" +#include "schema/SchemaRegistry.h" #include @@ -16,6 +17,69 @@ namespace eve::game_event { namespace { +eve::schema::FieldDefinition schemaField(std::string name, eve::schema::ValueType type, bool required = true) { + eve::schema::FieldDefinition result; + result.name = std::move(name); + result.type = type; + result.required = required; + return result; +} + +eve::schema::SchemaDefinition eventStreamSchemaDefinition() { + eve::schema::SchemaDefinition event; + event.additionalProperties = false; + event.fields = { + schemaField("eventId", eve::schema::ValueType::String), + schemaField("sequence", eve::schema::ValueType::String), + schemaField("type", eve::schema::ValueType::String), + schemaField("source", eve::schema::ValueType::String), + schemaField("subject", eve::schema::ValueType::String), + schemaField("causationKind", eve::schema::ValueType::String), + schemaField("causation", eve::schema::ValueType::String), + schemaField("correlationKind", eve::schema::ValueType::String), + schemaField("correlation", eve::schema::ValueType::String), + schemaField("schemaId", eve::schema::ValueType::String), + schemaField("schemaVersion", eve::schema::ValueType::String), + schemaField("tick", eve::schema::ValueType::String), + schemaField("flags", eve::schema::ValueType::Integer), + schemaField("payload", eve::schema::ValueType::Any), + }; + eve::schema::SchemaNode eventNode; + eventNode.type = eve::schema::ValueType::Object; + eventNode.objectSchema = std::make_shared(std::move(event)); + auto events = schemaField("events", eve::schema::ValueType::Array); + events.itemSchema = std::make_shared(std::move(eventNode)); + + eve::schema::SchemaDefinition schema; + schema.id = "game_event:stream"; + schema.version = 1; + schema.title = "Game Event Stream Snapshot"; + schema.description = "Current persistent payload owned by GameEventLog."; + schema.additionalProperties = false; + schema.fields = { + schemaField("version", eve::schema::ValueType::Integer), + schemaField("revision", eve::schema::ValueType::String), + schemaField("nextSequence", eve::schema::ValueType::String), + std::move(events), + }; + return schema; +} + +eve::Result validateCurrentEventStream(std::string_view json) { + constexpr auto id = "game_event:stream"; + if (!eve::schema::SchemaRegistry::resolve(id, 1)) { + auto registration = eve::schema::SchemaRegistry::registerVersioned(eventStreamSchemaDefinition()); + if (!registration.ok()) return eve::Result::failure(registration.status()); + } + const auto errors = eve::schema::SchemaRegistry::validate(id, 1, std::string(json)); + if (errors.empty()) return eve::Result::success(); + const auto& error = errors.front(); + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::ParseError, error.message, error.path, + eve::DiagnosticDetails{{"schemaId", id}, {"schemaVersion", "1"}, {"validationCode", error.code}}, + "game_event.snapshot.schema")); +} + std::string quote(const std::string& value) { std::ostringstream out; out << '"'; @@ -89,19 +153,19 @@ std::string canonicalJson(const eve::json::Value& value) { const char* correlationKindName(CorrelationId::Kind kind) { switch (kind) { - case CorrelationId::Kind::None: return "none"; - case CorrelationId::Kind::Id: return "id"; - case CorrelationId::Kind::Legacy: return "legacy"; + case CorrelationId::Kind::None: return "none"; + case CorrelationId::Kind::Id: return "id"; + case CorrelationId::Kind::Legacy: return "legacy"; } return "none"; } const char* causationKindName(CausationRef::Kind kind) { switch (kind) { - case CausationRef::Kind::None: return "none"; - case CausationRef::Kind::Event: return "event"; - case CausationRef::Kind::Command: return "command"; - case CausationRef::Kind::Legacy: return "legacy"; + case CausationRef::Kind::None: return "none"; + case CausationRef::Kind::Event: return "event"; + case CausationRef::Kind::Command: return "command"; + case CausationRef::Kind::Legacy: return "legacy"; } return "none"; } @@ -137,15 +201,15 @@ eve::LogicalId eventStreamSchema() { const eve::SnapshotMigrationChain& eventStreamMigrations() { static const eve::SnapshotMigrationChain chain = [] { eve::SnapshotMigrationChain result; - const auto registration = result.add( - eventStreamSchema(), eve::SchemaVersion(0), eve::SchemaVersion(1), - [](const eve::Value& payload) -> eve::Result { - const auto* object = payload.getIf(); - if (!object) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "event stream payload must be an object")); - return eve::Result::success(payload); - }); + const auto registration = + result.add(eventStreamSchema(), eve::SchemaVersion(0), eve::SchemaVersion(1), + [](const eve::Value& payload) -> eve::Result { + const auto* object = payload.getIf(); + if (!object) + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::ParseError, "event stream payload must be an object")); + return eve::Result::success(payload); + }); if (!registration.ok()) std::terminate(); return result; }(); @@ -165,8 +229,7 @@ EventConsumer::EventConsumer(GameEventLog* stream, EventSequence sequence) int EventConsumer::read(int maxCount) { batch_.clear(); if (!stream_ || maxCount <= 0) return 0; - if (nextSequence_.value() < stream_->oldestSequence().value()) - nextSequence_ = stream_->oldestSequence(); + if (nextSequence_.value() < stream_->oldestSequence().value()) nextSequence_ = stream_->oldestSequence(); for (const auto& event : stream_->events_) { if (event.sequence.value() < nextSequence_.value()) continue; if (static_cast(batch_.size()) >= maxCount) break; @@ -230,16 +293,15 @@ eve::Result GameEventLog::append(GameEvent envelope) { envelope.payload = canonicalJson(payloadDocument.root()); events_.push_back(std::move(envelope)); nextSequence_ = *next; - revision_ = *revision_.incremented(); + revision_ = *revision_.incremented(); if (events_.back().tick > snapshotTick_) snapshotTick_ = events_.back().tick; return eve::Result::success(events_.back().sequence); } const GameEvent* GameEventLog::find(EventSequence sequence) const { - const auto it = std::lower_bound(events_.begin(), events_.end(), sequence.value(), - [](const GameEvent& event, uint64_t value) { - return event.sequence.value() < value; - }); + const auto it = + std::lower_bound(events_.begin(), events_.end(), sequence.value(), + [](const GameEvent& event, uint64_t value) { return event.sequence.value() < value; }); return it != events_.end() && it->sequence.value() == sequence.value() ? &*it : nullptr; } @@ -309,7 +371,7 @@ void GameEventLog::clear() { void GameEventLog::reset() { clear(); nextSequence_ = EventSequence(1); - revision_ = eve::Revision::zero(); + revision_ = eve::Revision::zero(); snapshotTick_ = eve::SimulationTick::zero(); for (auto& consumer : consumers_) consumer->seek(1); } @@ -341,13 +403,16 @@ eve::Result GameEventLog::restore(std::string_view json) { std::string error; auto document = eve::json::Document::parse(std::string(json), &error); const auto root = document.root(); - const auto invalid = [&](eve::DiagnosticCode code, std::string message) { + const auto invalid = [&](eve::DiagnosticCode code, std::string message) { return eve::Result::failure(eve::Diagnostic::error(code, std::move(message))); }; const int snapshotVersion = root.getInt("version"); if (!document.valid() || !root.isObject() || (snapshotVersion != 1 && snapshotVersion != 2)) { - return invalid(eve::DiagnosticCode::ParseError, - error.empty() ? "invalid event stream snapshot" : error); + return invalid(eve::DiagnosticCode::ParseError, error.empty() ? "invalid event stream snapshot" : error); + } + if (snapshotVersion == 2) { + auto schemaValidation = validateCurrentEventStream(json); + if (!schemaValidation.ok()) return schemaValidation; } uint64_t restoredNext = 0; if (!parseU64(root.get("nextSequence"), restoredNext) || restoredNext == 0) { @@ -360,14 +425,14 @@ eve::Result GameEventLog::restore(std::string_view json) { if (!values.isArray()) { return invalid(eve::DiagnosticCode::ParseError, "events must be an array"); } - std::deque restored; - uint64_t previous = 0; - std::vector restoredEventIds; + std::deque restored; + uint64_t previous = 0; + std::vector restoredEventIds; for (size_t i = 0; i < values.size(); ++i) { - const auto value = values.at(i); - GameEvent event; - int64_t restoredTick = 0; - uint64_t restoredSequence = 0; + const auto value = values.at(i); + GameEvent event; + int64_t restoredTick = 0; + uint64_t restoredSequence = 0; if (!value.isObject() || !parseU64(value.get("sequence"), restoredSequence) || restoredSequence <= previous || !parseI64(value.get("tick"), restoredTick) || !value.get("flags").isNumber() || !value.get("payload")) { return invalid(eve::DiagnosticCode::ParseError, "invalid event at index " + std::to_string(i)); @@ -386,7 +451,8 @@ eve::Result GameEventLog::restore(std::string_view json) { event.eventId = EventId::nil(); } else { const auto parsedId = EventId::parse(eventIdText); - if (!parsedId) return invalid(eve::DiagnosticCode::ParseError, "invalid eventId at index " + std::to_string(i)); + if (!parsedId) + return invalid(eve::DiagnosticCode::ParseError, "invalid eventId at index " + std::to_string(i)); event.eventId = *parsedId; if (!event.eventId.isNil()) { const auto formattedId = event.eventId.format(); @@ -396,27 +462,28 @@ eve::Result GameEventLog::restore(std::string_view json) { restoredEventIds.push_back(formattedId); } } - event.source = value.getString("source"); - event.subject = value.getString("subject"); - const std::string causationText = value.getString("causation"); + event.source = value.getString("source"); + event.subject = value.getString("subject"); + const std::string causationText = value.getString("causation"); const std::string correlationText = value.getString("correlation"); - const std::string causationKind = snapshotVersion == 1 ? - (causationText.empty() ? "none" : "legacy") : value.getString("causationKind"); - const std::string correlationKind = snapshotVersion == 1 ? - (correlationText.empty() ? "none" : "legacy") : value.getString("correlationKind"); + const std::string causationKind = + snapshotVersion == 1 ? (causationText.empty() ? "none" : "legacy") : value.getString("causationKind"); + const std::string correlationKind = + snapshotVersion == 1 ? (correlationText.empty() ? "none" : "legacy") : value.getString("correlationKind"); if (causationKind != "none" && causationKind != "event" && causationKind != "command" && causationKind != "legacy") return invalid(eve::DiagnosticCode::ParseError, "invalid causation kind at index " + std::to_string(i)); if (correlationKind != "none" && correlationKind != "id" && correlationKind != "legacy") return invalid(eve::DiagnosticCode::ParseError, "invalid correlation kind at index " + std::to_string(i)); - if ((causationKind == "none" && !causationText.empty()) || - (causationKind != "none" && causationText.empty()) || + if ((causationKind == "none" && !causationText.empty()) || (causationKind != "none" && causationText.empty()) || (correlationKind == "none" && !correlationText.empty()) || (correlationKind != "none" && correlationText.empty())) - return invalid(eve::DiagnosticCode::ParseError, "causal reference value/kind mismatch at index " + std::to_string(i)); + return invalid(eve::DiagnosticCode::ParseError, + "causal reference value/kind mismatch at index " + std::to_string(i)); event.causation = causationFromSnapshot(causationKind, causationText); event.correlation = correlationFromSnapshot(correlationKind, correlationText); - if ((causationKind == "event" || causationKind == "command") && event.causation.kind() == CausationRef::Kind::None) + if ((causationKind == "event" || causationKind == "command") && + event.causation.kind() == CausationRef::Kind::None) return invalid(eve::DiagnosticCode::ParseError, "invalid causation ID at index " + std::to_string(i)); if (correlationKind == "id" && event.correlation.kind() == CorrelationId::Kind::None) return invalid(eve::DiagnosticCode::ParseError, "invalid correlation ID at index " + std::to_string(i)); @@ -425,7 +492,8 @@ eve::Result GameEventLog::restore(std::string_view json) { event.schemaId = eve::LogicalId::fromParts("game_event", event.type).value_or(eve::LogicalId()); } else { const auto parsedSchema = eve::LogicalId::parse(schemaIdText); - if (!parsedSchema) return invalid(eve::DiagnosticCode::ParseError, "invalid schemaId at index " + std::to_string(i)); + if (!parsedSchema) + return invalid(eve::DiagnosticCode::ParseError, "invalid schemaId at index " + std::to_string(i)); event.schemaId = *parsedSchema; } uint64_t restoredSchemaVersion = snapshotVersion == 1 ? 1 : 0; @@ -434,7 +502,7 @@ eve::Result GameEventLog::restore(std::string_view json) { return invalid(eve::DiagnosticCode::ParseError, "invalid schemaVersion at index " + std::to_string(i)); } event.schemaVersion = SchemaVersion(restoredSchemaVersion); - const double flags = value.getDouble("flags", -1.0); + const double flags = value.getDouble("flags", -1.0); if (flags < 0 || flags > std::numeric_limits::max() || std::floor(flags) != flags) { return invalid(eve::DiagnosticCode::ParseError, "invalid flags at index " + std::to_string(i)); } @@ -457,16 +525,15 @@ eve::Result GameEventLog::restore(std::string_view json) { return eve::Result::success(); } -eve::Result GameEventLog::snapshot( - const eve::SnapshotHashProvider& hashProvider) const { +eve::Result GameEventLog::snapshot(const eve::SnapshotHashProvider& hashProvider) const { auto payload = eve::Value::fromJson(snapshotJson()); if (!payload.ok()) return eve::Result::failure(payload.status()); return eve::makeSnapshotEnvelope("game_event.stream", eventStreamSchema(), eve::SchemaVersion(1), instanceId_, revision_, snapshotTick_, std::move(payload).takeValue(), hashProvider); } -eve::Result GameEventLog::restoreSnapshot( - const eve::SnapshotEnvelope& source, const eve::SnapshotHashProvider& hashProvider) { +eve::Result GameEventLog::restoreSnapshot(const eve::SnapshotEnvelope& source, + const eve::SnapshotHashProvider& hashProvider) { if (source.type != "game_event.stream" || source.schema != eventStreamSchema()) return snapshotFailure(eve::DiagnosticCode::InvalidArgument, "snapshot does not belong to game_event::GameEventLog"); @@ -475,8 +542,7 @@ eve::Result GameEventLog::restoreSnapshot( "snapshot instanceId does not match game_event::GameEventLog"); auto migrated = eventStreamMigrations().migrate(source, eve::SchemaVersion(1), hashProvider); if (!migrated.ok()) return eve::Result::failure(migrated.status()); - auto metadata = eve::validateSnapshotPayloadMetadata(migrated.value().payload, - migrated.value().revision, + auto metadata = eve::validateSnapshotPayloadMetadata(migrated.value().payload, migrated.value().revision, migrated.value().tick); if (!metadata.ok()) return eve::Result::failure(metadata.status()); auto payload = migrated.value().payload.toJson(); @@ -484,7 +550,7 @@ eve::Result GameEventLog::restoreSnapshot( GameEventLog candidate(instanceId_); candidate.eventIdGenerator_ = eventIdGenerator_; - auto restored = candidate.restore(std::move(payload).takeValue()); + auto restored = candidate.restore(std::move(payload).takeValue()); if (!restored.ok()) return eve::Result::failure(restored.status()); if (candidate.snapshotTick_ != migrated.value().tick) return snapshotFailure(eve::DiagnosticCode::Conflict, @@ -505,22 +571,23 @@ eve::Result GameEventLog::restoreSnapshot( return eve::Result::success(); } -eve::Result GameEventLog::snapshotEnvelopeJson( - const eve::SnapshotHashProvider& hashProvider) const { +eve::Result GameEventLog::snapshotEnvelopeJson(const eve::SnapshotHashProvider& hashProvider) const { auto value = snapshot(hashProvider); if (!value.ok()) return eve::Result::failure(value.status()); return std::move(value).andThen( [](eve::SnapshotEnvelope&& envelope) { return eve::serializeSnapshotEnvelope(envelope); }); } -eve::Result GameEventLog::restoreSnapshotJson( - std::string_view json, const eve::SnapshotHashProvider& hashProvider) { +eve::Result GameEventLog::restoreSnapshotJson(std::string_view json, + const eve::SnapshotHashProvider& hashProvider) { auto source = eve::parseSnapshotEnvelope(json, hashProvider); if (!source.ok()) return eve::Result::failure(source.status()); return restoreSnapshot(std::move(source).takeValue(), hashProvider); } -EventSequence GameEventLog::oldestSequence() const { return events_.empty() ? nextSequence_ : events_.front().sequence; } +EventSequence GameEventLog::oldestSequence() const { + return events_.empty() ? nextSequence_ : events_.front().sequence; +} GameEventLog* GameEventModule::newLog() { auto* module = GameEventModule::create(); @@ -528,8 +595,8 @@ GameEventLog* GameEventModule::newLog() { return module->streams_.back().get(); } -ModuleRegister GameEventModule_register( - "GameEvent", (ModuleManager::creator_t)(GameEventModule::create), GameEventModule::expose); +ModuleRegister GameEventModule_register("GameEvent", (ModuleManager::creator_t)(GameEventModule::create), + GameEventModule::expose); GameEventModule* GameEventModule::create() { auto* existing = ModuleManager::find(name); if (existing) return static_cast(existing); @@ -544,33 +611,22 @@ void GameEventModule::expose(ssq::Table& table) { "GameEventRecord", std::function([]() -> GameEvent* { return nullptr; }), false); envelope.addFunc("getEventId", [](GameEvent* e) { return e ? e->eventId.format() : std::string{}; }); envelope.addFunc("getSequence", - [](GameEvent* e) { - return e ? static_cast(e->sequence.value()) : int64_t{0}; - }); + [](GameEvent* e) { return e ? static_cast(e->sequence.value()) : int64_t{0}; }); envelope.addFunc("getType", [](GameEvent* e) { return e ? e->type : std::string{}; }); envelope.addFunc("getSource", [](GameEvent* e) { return e ? e->source : std::string{}; }); envelope.addFunc("getSubject", [](GameEvent* e) { return e ? e->subject : std::string{}; }); - envelope.addFunc("getCausation", [](GameEvent* e) { - return e ? e->causation.format() : std::string{}; - }); + envelope.addFunc("getCausation", [](GameEvent* e) { return e ? e->causation.format() : std::string{}; }); envelope.addFunc("getCausationKind", [](GameEvent* e) { return e ? std::string(causationKindName(e->causation.kind())) : std::string{}; }); - envelope.addFunc("getCorrelation", [](GameEvent* e) { - return e ? e->correlation.format() : std::string{}; - }); + envelope.addFunc("getCorrelation", [](GameEvent* e) { return e ? e->correlation.format() : std::string{}; }); envelope.addFunc("getCorrelationKind", [](GameEvent* e) { return e ? std::string(correlationKindName(e->correlation.kind())) : std::string{}; }); - envelope.addFunc("getSchemaId", [](GameEvent* e) { - return e ? e->schemaId.format() : std::string{}; - }); - envelope.addFunc("getSchemaVersion", [](GameEvent* e) { - return e ? static_cast(e->schemaVersion.value()) : int64_t{0}; - }); - envelope.addFunc("getTick", [](GameEvent* e) { - return e ? static_cast(e->tick.value()) : int64_t{0}; - }); + envelope.addFunc("getSchemaId", [](GameEvent* e) { return e ? e->schemaId.format() : std::string{}; }); + envelope.addFunc("getSchemaVersion", + [](GameEvent* e) { return e ? static_cast(e->schemaVersion.value()) : int64_t{0}; }); + envelope.addFunc("getTick", [](GameEvent* e) { return e ? static_cast(e->tick.value()) : int64_t{0}; }); envelope.addFunc("getFlags", [](GameEvent* e) { return e ? static_cast(e->flags) : int64_t{0}; }); envelope.addFunc("getPayload", [](GameEvent* e) { return e ? e->payload : std::string{}; }); @@ -581,67 +637,67 @@ void GameEventModule::expose(ssq::Table& table) { consumer.addFunc("batchAt", [](EventConsumer* c, int index) -> GameEvent* { return c ? const_cast(c->batchAt(index)) : nullptr; }); - consumer.addFunc("position", [](EventConsumer* c) { - return c ? static_cast(c->position().value()) : int64_t{0}; - }); + consumer.addFunc("position", + [](EventConsumer* c) { return c ? static_cast(c->position().value()) : int64_t{0}; }); consumer.addFunc("seek", [](EventConsumer* c, int64_t sequence) { if (c) c->seek(sequence > 0 ? uint64_t(sequence) : 1); }); - auto stream = - table.addClass("GameEventLog", std::function([]() -> GameEventLog* { return nullptr; }), false); + auto stream = table.addClass( + "GameEventLog", std::function([]() -> GameEventLog* { return nullptr; }), false); const HSQUIRRELVM vm = table.getHandle(); - stream.addFunc("append", [vm](GameEventLog* s, const std::string& eventId, - const std::string& type, const std::string& source, - const std::string& subject, const std::string& causation, - const std::string& correlation, int64_t tick, int64_t flags, - const std::string& payload) { + stream.addFunc("append", [vm](GameEventLog* s, const std::string& eventId, const std::string& type, + const std::string& source, const std::string& subject, const std::string& causation, + const std::string& correlation, int64_t tick, int64_t flags, + const std::string& payload) { if (!s) { - return eve::script::projectResult(vm, eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, - "event stream must not be null", "stream")), + return eve::script::projectResult( + vm, + eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "event stream must not be null", "stream")), [](EventSequence) { return eve::Value::null(); }); } if (tick < 0 || flags < 0 || static_cast(flags) > std::numeric_limits::max()) { - return eve::script::projectResult(vm, eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, - "event tick and flags must be non-negative and in range", "envelope")), + return eve::script::projectResult( + vm, + eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "event tick and flags must be non-negative and in range", "envelope")), [](EventSequence) { return eve::Value::null(); }); } GameEvent envelope; if (!eventId.empty()) { const auto parsed = EventId::parse(eventId); if (!parsed) { - return eve::script::projectResult(vm, eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, - "eventId must be a UUID", "eventId")), + return eve::script::projectResult( + vm, + eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "eventId must be a UUID", "eventId")), [](EventSequence) { return eve::Value::null(); }); } envelope.eventId = *parsed; } - envelope.type = type; - envelope.source = source; + envelope.type = type; + envelope.source = source; envelope.subject = subject; if (!causation.empty()) { - const auto parsed = EventId::parse(causation); - envelope.causation = parsed ? CausationRef::fromEventId(*parsed) - : CausationRef::fromLegacy(causation); + const auto parsed = EventId::parse(causation); + envelope.causation = parsed ? CausationRef::fromEventId(*parsed) : CausationRef::fromLegacy(causation); } if (!correlation.empty()) { const auto parsed = EventId::parse(correlation); - envelope.correlation = parsed ? CorrelationId::fromEventId(*parsed) - : CorrelationId::fromLegacy(correlation); + envelope.correlation = + parsed ? CorrelationId::fromEventId(*parsed) : CorrelationId::fromLegacy(correlation); } const auto schema = eve::LogicalId::fromParts("game_event", type); if (schema) envelope.schemaId = *schema; envelope.schemaVersion = SchemaVersion(1); - envelope.tick = SimulationTick(static_cast(tick)); - envelope.flags = static_cast(flags); - envelope.payload = payload; - return eve::script::projectResult(vm, s->append(std::move(envelope)), - [](EventSequence sequence) { - return eve::Value::integer(static_cast(sequence.value())); - }); + envelope.tick = SimulationTick(static_cast(tick)); + envelope.flags = static_cast(flags); + envelope.payload = payload; + return eve::script::projectResult(vm, s->append(std::move(envelope)), [](EventSequence sequence) { + return eve::Value::integer(static_cast(sequence.value())); + }); }); stream.addFunc("find", [](GameEventLog* s, int64_t sequence) -> GameEvent* { return s && sequence > 0 ? const_cast(s->find(uint64_t(sequence))) : nullptr; @@ -668,12 +724,11 @@ void GameEventModule::expose(ssq::Table& table) { stream.addFunc("clear", &GameEventLog::clear); stream.addFunc("reset", &GameEventLog::reset); stream.addFunc("snapshotJson", &GameEventLog::snapshotJson); - stream.addFunc("restore", [vm = table.getHandle()](GameEventLog* stream, - const std::string& json) { + stream.addFunc("restore", [vm = table.getHandle()](GameEventLog* stream, const std::string& json) { if (!stream) return eve::script::projectResult( - vm, eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "game event log must not be null"))); + vm, eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "game event log must not be null"))); return eve::script::projectResult(vm, stream->restore(json)); }); diff --git a/src/modules/game_event/GameEvent.h b/src/modules/game_event/GameEvent.h index b9412173..a7c9b8ec 100644 --- a/src/modules/game_event/GameEvent.h +++ b/src/modules/game_event/GameEvent.h @@ -1,12 +1,12 @@ #pragma once +#include "common/EventSequence.h" #include "common/Identity.h" #include "common/Module.h" #include "common/Result.h" -#include "common/Snapshot.h" -#include "common/EventSequence.h" #include "common/Revision.h" #include "common/SchemaVersion.h" +#include "common/Snapshot.h" #include "common/Time.h" #include @@ -62,7 +62,7 @@ public: /** @brief Creates a compatibility-only arbitrary string correlation. */ [[nodiscard]] static CorrelationId fromLegacy(std::string value) { CorrelationId result; - result.kind_ = value.empty() ? Kind::None : Kind::Legacy; + result.kind_ = value.empty() ? Kind::None : Kind::Legacy; result.legacy_ = std::move(value); return result; } @@ -74,16 +74,14 @@ public: [[nodiscard]] bool isCanonical() const noexcept { return kind_ != Kind::Legacy; } /** @brief Returns the stable ID or compatibility text projection. */ - [[nodiscard]] std::string format() const { - return kind_ == Kind::Id ? id_.format() : legacy_; - } + [[nodiscard]] std::string format() const { return kind_ == Kind::Id ? id_.format() : legacy_; } friend bool operator==(const CorrelationId&, const CorrelationId&) noexcept = default; private: - Kind kind_ = Kind::None; - EventId id_; - std::string legacy_; + Kind kind_ = Kind::None; + EventId id_; + std::string legacy_; }; /** @@ -103,7 +101,7 @@ public: [[nodiscard]] static CausationRef fromEventId(EventId value) { CausationRef result; if (!value.isNil()) { - result.kind_ = Kind::Event; + result.kind_ = Kind::Event; result.value_ = std::move(value); } return result; @@ -113,7 +111,7 @@ public: [[nodiscard]] static CausationRef fromCommandId(CommandId value) { CausationRef result; if (!value.isNil()) { - result.kind_ = Kind::Command; + result.kind_ = Kind::Command; result.value_ = std::move(value); } return result; @@ -122,7 +120,7 @@ public: /** @brief Creates a compatibility-only arbitrary string causation. */ [[nodiscard]] static CausationRef fromLegacy(std::string value) { CausationRef result; - result.kind_ = value.empty() ? Kind::None : Kind::Legacy; + result.kind_ = value.empty() ? Kind::None : Kind::Legacy; result.value_ = std::move(value); return result; } @@ -144,16 +142,16 @@ public: friend bool operator==(const CausationRef&, const CausationRef&) noexcept = default; private: - Kind kind_ = Kind::None; + Kind kind_ = Kind::None; std::variant value_; }; /** @brief Writes a correlation representation tag for diagnostics. */ inline std::ostream& operator<<(std::ostream& stream, CorrelationId::Kind kind) { switch (kind) { - case CorrelationId::Kind::None: return stream << "none"; - case CorrelationId::Kind::Id: return stream << "id"; - case CorrelationId::Kind::Legacy: return stream << "legacy"; + case CorrelationId::Kind::None: return stream << "none"; + case CorrelationId::Kind::Id: return stream << "id"; + case CorrelationId::Kind::Legacy: return stream << "legacy"; } return stream << "unknown"; } @@ -161,10 +159,10 @@ inline std::ostream& operator<<(std::ostream& stream, CorrelationId::Kind kind) /** @brief Writes a causation representation tag for diagnostics. */ inline std::ostream& operator<<(std::ostream& stream, CausationRef::Kind kind) { switch (kind) { - case CausationRef::Kind::None: return stream << "none"; - case CausationRef::Kind::Event: return stream << "event"; - case CausationRef::Kind::Command: return stream << "command"; - case CausationRef::Kind::Legacy: return stream << "legacy"; + case CausationRef::Kind::None: return stream << "none"; + case CausationRef::Kind::Event: return stream << "event"; + case CausationRef::Kind::Command: return stream << "command"; + case CausationRef::Kind::Legacy: return stream << "legacy"; } return stream << "unknown"; } @@ -212,7 +210,7 @@ struct GameEvent { template struct TypedEventEnvelope { GameEvent metadata; - Payload payload; + Payload payload; }; class GameEventLog; @@ -243,8 +241,8 @@ private: friend class GameEventLog; EventConsumer(GameEventLog* stream, EventSequence sequence); - GameEventLog* stream_ = nullptr; - EventSequence nextSequence_ = EventSequence(1); + GameEventLog* stream_ = nullptr; + EventSequence nextSequence_ = EventSequence(1); std::vector batch_; }; @@ -359,34 +357,32 @@ public: [[nodiscard]] eve::Result restore(std::string_view json); /** @brief Captures the stream payload in the common snapshot envelope. */ - [[nodiscard]] eve::Result snapshot( - const eve::SnapshotHashProvider& hashProvider) const; + [[nodiscard]] eve::Result snapshot(const eve::SnapshotHashProvider& hashProvider) const; /** * @brief Restores a verified or migrated stream envelope atomically. * @param snapshot Source envelope with schema `game_event:stream`. * @param hashProvider Explicit content-digest provider. * @return Success, or a failure leaving retained events and cursors intact. */ - [[nodiscard]] eve::Result restoreSnapshot( - const eve::SnapshotEnvelope& snapshot, const eve::SnapshotHashProvider& hashProvider); + [[nodiscard]] eve::Result restoreSnapshot(const eve::SnapshotEnvelope& snapshot, + const eve::SnapshotHashProvider& hashProvider); /** @brief Serializes the common event-stream snapshot envelope. */ - [[nodiscard]] eve::Result snapshotEnvelopeJson( - const eve::SnapshotHashProvider& hashProvider) const; + [[nodiscard]] eve::Result snapshotEnvelopeJson(const eve::SnapshotHashProvider& hashProvider) const; /** @brief Parses and transactionally restores a common event-stream envelope. */ - [[nodiscard]] eve::Result restoreSnapshotJson( - std::string_view json, const eve::SnapshotHashProvider& hashProvider); + [[nodiscard]] eve::Result restoreSnapshotJson(std::string_view json, + const eve::SnapshotHashProvider& hashProvider); private: friend class EventConsumer; [[nodiscard]] EventSequence oldestSequence() const; - std::optional eventIdGenerator_; - eve::PersistentId instanceId_; - eve::Revision revision_ = eve::Revision::zero(); - eve::SimulationTick snapshotTick_ = eve::SimulationTick::zero(); - EventSequence nextSequence_ = EventSequence(1); - std::deque events_; - std::vector query_; + std::optional eventIdGenerator_; + eve::PersistentId instanceId_; + eve::Revision revision_ = eve::Revision::zero(); + eve::SimulationTick snapshotTick_ = eve::SimulationTick::zero(); + EventSequence nextSequence_ = EventSequence(1); + std::deque events_; + std::vector query_; std::vector> consumers_; }; @@ -394,7 +390,7 @@ private: class GameEventModule : public Module { public: Module_REG(GameEventModule); - GameEventModule() = default; + GameEventModule() = default; ~GameEventModule() override = default; /** diff --git a/src/modules/graphics/ArtifactProvider.cpp b/src/modules/graphics/ArtifactProvider.cpp index 6f79f933..ee584e8f 100644 --- a/src/modules/graphics/ArtifactProvider.cpp +++ b/src/modules/graphics/ArtifactProvider.cpp @@ -20,10 +20,9 @@ const eve::Value* member(const eve::Value::Object& object, const char* name) noe return found == object.end() ? nullptr : &found->second; } -bool readString(const eve::Value::Object& object, const char* name, std::string& output, - bool allowEmpty = false) { +bool readString(const eve::Value::Object& object, const char* name, std::string& output, bool allowEmpty = false) { const auto* value = member(object, name); - const auto* text = value ? value->getIf() : nullptr; + const auto* text = value ? value->getIf() : nullptr; if (!text || (!allowEmpty && text->empty())) return false; output = *text; return true; @@ -37,7 +36,7 @@ bool readU64(const eve::Value::Object& object, const char* name, std::uint64_t& } bool readBool(const eve::Value::Object& object, const char* name, bool& output) { - const auto* value = member(object, name); + const auto* value = member(object, name); const auto* boolean = value ? value->getIf() : nullptr; if (!boolean) return false; output = *boolean; @@ -92,8 +91,7 @@ bool readNumericArray(const eve::Value* encoded, std::vector& output) { } } else { const auto* integer = value.getIf(); - if (!integer || *integer < 0 || - static_cast(*integer) > std::numeric_limits::max()) + if (!integer || *integer < 0 || static_cast(*integer) > std::numeric_limits::max()) return false; output.push_back(static_cast(*integer)); } @@ -101,10 +99,10 @@ bool readNumericArray(const eve::Value* encoded, std::vector& output) { return true; } -std::uint64_t hashStreams(std::span positions, std::span normals, - std::span uvs, std::span indices) noexcept { +std::uint64_t hashStreams(std::span positions, std::span normals, std::span uvs, + std::span indices) noexcept { std::uint64_t hash = 14695981039346656037ull; - const auto mix = [&hash](const auto* data, std::size_t count) { + const auto mix = [&hash](const auto* data, std::size_t count) { const auto* bytes = reinterpret_cast(data); for (std::size_t i = 0; i < count * sizeof(*data); ++i) { hash ^= bytes[i]; @@ -125,16 +123,14 @@ std::uint64_t hashDescriptor(const GraphicsArtifactResource& resource) noexcept const eve::artifact::PartView* selectedMesh(const eve::artifact::PublicationView& publication) noexcept { const eve::artifact::PartView* mesh = nullptr; for (const auto& part : publication.parts) { - if (part.kind == eve::artifact::PartKind::MeshData && - (part.role == "mesh" || mesh == nullptr)) - mesh = ∂ + if (part.kind == eve::artifact::PartKind::MeshData && (part.role == "mesh" || mesh == nullptr)) mesh = ∂ } return mesh; } bool validMeshView(const eve::artifact::PartView& mesh) noexcept { - if (mesh.positions.empty() || mesh.positions.size() % 3u != 0u || - mesh.indices.empty() || mesh.indices.size() % 3u != 0u) + if (mesh.positions.empty() || mesh.positions.size() % 3u != 0u || mesh.indices.empty() || + mesh.indices.size() % 3u != 0u) return false; const std::size_t vertices = mesh.positions.size() / 3u; if ((!mesh.normals.empty() && mesh.normals.size() != mesh.positions.size()) || @@ -154,7 +150,7 @@ bool validMeshView(const eve::artifact::PartView& mesh) noexcept { } // namespace void GraphicsArtifactProvider::uploadIfAvailable(Graphics* graphics, RuntimeResource& runtime) noexcept { - auto& descriptor = runtime.descriptor; + auto& descriptor = runtime.descriptor; runtime.uploadedMesh = nullptr; if (!graphics) { descriptor.backendName = "cpu"; @@ -166,7 +162,7 @@ void GraphicsArtifactProvider::uploadIfAvailable(Graphics* graphics, RuntimeReso descriptor.uploadState = "cpu-fallback"; try { descriptor.backendName = graphics->getBackendName(); - runtime.uploadedMesh = graphics->newMeshFromArrays( + runtime.uploadedMesh = graphics->newMeshFromArrays( descriptor.positions.data(), descriptor.normals.empty() ? nullptr : descriptor.normals.data(), descriptor.uvs.empty() ? nullptr : descriptor.uvs.data(), static_cast(descriptor.positions.size() / 3u), descriptor.indices.data(), @@ -177,15 +173,15 @@ void GraphicsArtifactProvider::uploadIfAvailable(Graphics* graphics, RuntimeReso backend->indexCount != descriptor.indices.size() || backend->vertexStride == 0u || (backend->indexElementSize != 2u && backend->indexElementSize != 4u)) { (void)graphics->releaseMesh(runtime.uploadedMesh); - runtime.uploadedMesh = nullptr; + runtime.uploadedMesh = nullptr; descriptor.uploadState = "cpu-fallback"; descriptor.gpuResident = false; return; } - descriptor.backendVertexStride = backend->vertexStride; + descriptor.backendVertexStride = backend->vertexStride; descriptor.backendIndexElementSize = backend->indexElementSize; - descriptor.uploadState = "uploaded"; - descriptor.gpuResident = true; + descriptor.uploadState = "uploaded"; + descriptor.gpuResident = true; } } catch (...) { if (runtime.uploadedMesh) { @@ -194,15 +190,14 @@ void GraphicsArtifactProvider::uploadIfAvailable(Graphics* graphics, RuntimeReso } catch (...) { } } - runtime.uploadedMesh = nullptr; + runtime.uploadedMesh = nullptr; descriptor.uploadState = "cpu-fallback"; } } class GraphicsArtifactStage final : public eve::artifact::PreparedPublication { public: - GraphicsArtifactStage(GraphicsArtifactProvider& owner, GraphicsArtifactResource resource, - Mesh* uploadedMesh) + GraphicsArtifactStage(GraphicsArtifactProvider& owner, GraphicsArtifactResource resource, Mesh* uploadedMesh) : owner_(&owner), resource_{std::move(resource), uploadedMesh} {} ~GraphicsArtifactStage() override { rollback(); } void commit() noexcept override { @@ -212,12 +207,12 @@ public: } void rollback() noexcept override { if (owner_) owner_->release(resource_); - owner_ = nullptr; + owner_ = nullptr; resource_ = {}; } private: - GraphicsArtifactProvider* owner_ = nullptr; + GraphicsArtifactProvider* owner_ = nullptr; GraphicsArtifactProvider::RuntimeResource resource_; }; @@ -226,12 +221,12 @@ std::optional WebGpuArtifactDescriptorAdapter::descr const auto* mesh = selectedMesh(publication); if (!mesh || !validMeshView(*mesh)) return std::nullopt; GraphicsArtifactDescriptor result; - result.id = mesh->id; - result.buildKey = std::string(mesh->buildKey); - result.role = std::string(mesh->role); + result.id = mesh->id; + result.buildKey = std::string(mesh->buildKey); + result.role = std::string(mesh->role); result.vertexCount = mesh->positions.size() / 3u; - result.indexCount = mesh->indices.size(); - result.checksum = hashStreams(mesh->positions, mesh->normals, mesh->uvs, mesh->indices); + result.indexCount = mesh->indices.size(); + result.checksum = hashStreams(mesh->positions, mesh->normals, mesh->uvs, mesh->indices); return result; } @@ -239,36 +234,30 @@ eve::Result> GraphicsArtifac const eve::artifact::PublicationView& publication) { if (failPrepare_) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Failed, - "graphics artifact prepare failure was injected")); + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "graphics artifact prepare failure was injected")); if (publication.id.isNil() || publication.buildKey.empty()) - return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, - "graphics publication requires identity and build key")); + return eve::Result>::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvalidArgument, "graphics publication requires identity and build key")); if (find(publication.id) != nullptr) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "graphics artifact identity is already published")); + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "graphics artifact identity is already published")); const auto* mesh = selectedMesh(publication); if (!mesh || !validMeshView(*mesh)) - return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, - "graphics publication requires a complete mesh leaf")); + return eve::Result>::failure(eve::Diagnostic::error( + eve::DiagnosticCode::PreconditionViolation, "graphics publication requires a complete mesh leaf")); if (mesh->positions.size() / 3u > static_cast(std::numeric_limits::max()) || mesh->indices.size() > static_cast(std::numeric_limits::max())) - return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, - "graphics mesh exceeds backend integer limits")); + return eve::Result>::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvalidArgument, "graphics mesh exceeds backend integer limits")); if (nextIndex_ == GraphicsArtifactHandle::invalidIndex) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Failed, - "graphics artifact handle index exhausted")); + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "graphics artifact handle index exhausted")); GraphicsArtifactResource resource; - resource.id = publication.id; + resource.id = publication.id; resource.buildKey = std::string(publication.buildKey); - resource.handle = GraphicsArtifactHandle(nextIndex_, 1); - resource.role = std::string(mesh->role); + resource.handle = GraphicsArtifactHandle(nextIndex_, 1); + resource.role = std::string(mesh->role); RuntimeResource runtime; runtime.descriptor = std::move(resource); try { @@ -279,8 +268,7 @@ eve::Result> GraphicsArtifac uploadIfAvailable(graphics_, runtime); resources_.reserve(resources_.size() + 1); return eve::Result>::success( - std::make_unique(*this, std::move(runtime.descriptor), - runtime.uploadedMesh)); + std::make_unique(*this, std::move(runtime.descriptor), runtime.uploadedMesh)); } catch (...) { if (runtime.uploadedMesh && graphics_) { try { @@ -289,8 +277,7 @@ eve::Result> GraphicsArtifac } } return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Failed, - "graphics artifact prepare allocation failed")); + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "graphics artifact prepare allocation failed")); } } @@ -307,11 +294,11 @@ void GraphicsArtifactProvider::release(RuntimeResource& resource) noexcept { } } if (resource.uploadedMesh) { - resource.descriptor.gpuResident = false; - resource.descriptor.uploadState = "detached"; - resource.descriptor.backendVertexStride = 0; + resource.descriptor.gpuResident = false; + resource.descriptor.uploadState = "detached"; + resource.descriptor.backendVertexStride = 0; resource.descriptor.backendIndexElementSize = 0; - resource.uploadedMesh = nullptr; + resource.uploadedMesh = nullptr; } } @@ -326,14 +313,17 @@ std::uint64_t GraphicsArtifactProvider::checksum(eve::PersistentId id) const noe return resource ? hashDescriptor(*resource) : 0; } -std::optional GraphicsArtifactProvider::descriptor( - eve::PersistentId id) const { +std::optional GraphicsArtifactProvider::descriptor(eve::PersistentId id) const { const auto* resource = find(id); if (!resource) return std::nullopt; - return GraphicsArtifactDescriptor{id, resource->buildKey, resource->role, - resource->positions.size() / 3u, resource->indices.size(), - hashDescriptor(*resource), resource->backendVertexStride, - resource->backendIndexElementSize}; + return GraphicsArtifactDescriptor{id, + resource->buildKey, + resource->role, + resource->positions.size() / 3u, + resource->indices.size(), + hashDescriptor(*resource), + resource->backendVertexStride, + resource->backendIndexElementSize}; } bool GraphicsArtifactProvider::isGpuResident(eve::PersistentId id) const noexcept { @@ -344,7 +334,7 @@ bool GraphicsArtifactProvider::isGpuResident(eve::PersistentId id) const noexcep void GraphicsArtifactProvider::clear() noexcept { for (auto& resource : resources_) release(resource); resources_.clear(); - nextIndex_ = 0; + nextIndex_ = 0; failPrepare_ = false; } @@ -367,7 +357,7 @@ eve::Result GraphicsArtifactProvider::snapshotState() const { eve::Value::Array resources; resources.reserve(resources_.size()); for (const auto& runtime : resources_) { - const auto& resource = runtime.descriptor; + const auto& resource = runtime.descriptor; eve::Value::Object item; item.emplace("artifactId", eve::Value(resource.id.format())); item.emplace("buildKey", eve::Value(resource.buildKey)); @@ -389,8 +379,8 @@ eve::Result GraphicsArtifactProvider::snapshotState() const { state.emplace("resources", eve::Value(std::move(resources))); return eve::Result::success(eve::Value(std::move(state))); } catch (...) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "graphics artifact snapshot allocation failed")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "graphics artifact snapshot allocation failed")); } } @@ -398,22 +388,22 @@ eve::Result GraphicsArtifactProvider::restoreState(const eve::Value& state if (!resources_.empty()) return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::Conflict, "graphics artifact restore requires an empty registry")); - const auto* object = state.getIf(); - const auto* provider = object ? member(*object, "provider") : nullptr; - const auto* providerName = provider ? provider->getIf() : nullptr; - const auto* versionValue = object ? member(*object, "version") : nullptr; - const auto* version = versionValue ? versionValue->getIf() : nullptr; + const auto* object = state.getIf(); + const auto* provider = object ? member(*object, "provider") : nullptr; + const auto* providerName = provider ? provider->getIf() : nullptr; + const auto* versionValue = object ? member(*object, "version") : nullptr; + const auto* version = versionValue ? versionValue->getIf() : nullptr; const auto* encodedResources = object ? member(*object, "resources") : nullptr; - const auto* resources = encodedResources ? encodedResources->getIf() : nullptr; - if (!object || !providerName || *providerName != "graphics.resource-provider" || - !hasSupportedVersion(*object) || !version || !resources) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::UnknownVersion, "invalid graphics artifact provider state")); + const auto* resources = encodedResources ? encodedResources->getIf() : nullptr; + if (!object || !providerName || *providerName != "graphics.resource-provider" || !hasSupportedVersion(*object) || + !version || !resources) + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::UnknownVersion, "invalid graphics artifact provider state")); - std::vector candidate; + std::vector candidate; std::unordered_set identities; - std::uint32_t next = 0; - const auto cleanup = [&]() noexcept { + std::uint32_t next = 0; + const auto cleanup = [&]() noexcept { for (auto& resource : candidate) release(resource); candidate.clear(); }; @@ -424,49 +414,47 @@ eve::Result GraphicsArtifactProvider::restoreState(const eve::Value& state try { candidate.reserve(resources->size()); for (const eve::Value& encoded : *resources) { - const auto* item = encoded.getIf(); - std::string idText; - std::string buildKey; - std::string role; - std::string backend; - std::string uploadState; - std::uint64_t packed = 0; - bool gpuResident = false; + const auto* item = encoded.getIf(); + std::string idText; + std::string buildKey; + std::string role; + std::string backend; + std::string uploadState; + std::uint64_t packed = 0; + bool gpuResident = false; RuntimeResource runtime; - auto& resource = runtime.descriptor; - if (!item || !readString(*item, "artifactId", idText) || - !readString(*item, "buildKey", buildKey) || !readString(*item, "role", role) || - !readU64(*item, "handle", packed) || + auto& resource = runtime.descriptor; + if (!item || !readString(*item, "artifactId", idText) || !readString(*item, "buildKey", buildKey) || + !readString(*item, "role", role) || !readU64(*item, "handle", packed) || !readNumericArray(member(*item, "positions"), resource.positions) || !readNumericArray(member(*item, "normals"), resource.normals) || !readNumericArray(member(*item, "uvs"), resource.uvs) || !readNumericArray(member(*item, "indices"), resource.indices)) - return fail(eve::Diagnostic::error(eve::DiagnosticCode::ParseError, - "invalid graphics artifact resource")); + return fail( + eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "invalid graphics artifact resource")); const auto parsedId = eve::PersistentId::parse(idText); - resource.handle = GraphicsArtifactHandle::fromPacked(packed); + resource.handle = GraphicsArtifactHandle::fromPacked(packed); if (!parsedId || parsedId->isNil() || resource.handle.isInvalid() || role.empty() || !identities.emplace(*parsedId).second || resource.positions.empty() || resource.positions.size() % 3u != 0u || resource.indices.empty() || resource.indices.size() % 3u != 0u || (!resource.normals.empty() && resource.normals.size() != resource.positions.size()) || (!resource.uvs.empty() && resource.uvs.size() != resource.positions.size() / 3u * 2u)) - return fail(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "invalid graphics artifact streams or identity")); + return fail(eve::Diagnostic::error(eve::DiagnosticCode::ParseError, + "invalid graphics artifact streams or identity")); for (const auto index : resource.indices) if (index >= resource.positions.size() / 3u) - return fail(eve::Diagnostic::error(eve::DiagnosticCode::ParseError, - "invalid graphics artifact index")); + return fail( + eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "invalid graphics artifact index")); if (*version >= 2) { - if (!readString(*item, "backend", backend) || - !readString(*item, "uploadState", uploadState) || + if (!readString(*item, "backend", backend) || !readString(*item, "uploadState", uploadState) || !readBool(*item, "gpuResident", gpuResident)) - return fail(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "invalid graphics upload state")); + return fail( + eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "invalid graphics upload state")); } - resource.id = *parsedId; + resource.id = *parsedId; resource.buildKey = std::move(buildKey); - resource.role = std::move(role); + resource.role = std::move(role); if (*version >= 2) { resource.backendName = std::move(backend); resource.uploadState = std::move(uploadState); @@ -478,8 +466,8 @@ eve::Result GraphicsArtifactProvider::restoreState(const eve::Value& state } } catch (...) { cleanup(); - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "graphics artifact restore allocation failed")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "graphics artifact restore allocation failed")); } resources_.swap(candidate); nextIndex_ = next; diff --git a/src/modules/graphics/ArtifactProvider.h b/src/modules/graphics/ArtifactProvider.h index 866d7d8a..3af756c7 100644 --- a/src/modules/graphics/ArtifactProvider.h +++ b/src/modules/graphics/ArtifactProvider.h @@ -27,29 +27,28 @@ using GraphicsArtifactHandle = eve::RuntimeHandle; /** @brief Backend-neutral mesh descriptor shared by Vulkan and WebGPU adapters. */ struct GraphicsArtifactDescriptor { eve::PersistentId id; - std::string buildKey; - std::string role; - std::size_t vertexCount = 0; - std::size_t indexCount = 0; - std::uint64_t checksum = 0; + std::string buildKey; + std::string role; + std::size_t vertexCount = 0; + std::size_t indexCount = 0; + std::uint64_t checksum = 0; /** @brief Actual backend vertex stride when a live upload exists. */ std::uint32_t backendVertexStride = 0; /** @brief Actual backend index element size when a live upload exists. */ std::uint32_t backendIndexElementSize = 0; - friend bool operator==(const GraphicsArtifactDescriptor&, - const GraphicsArtifactDescriptor&) = default; + friend bool operator==(const GraphicsArtifactDescriptor&, const GraphicsArtifactDescriptor&) = default; }; /** @brief Queryable backend-neutral mesh resource owned by the graphics module. */ struct GraphicsArtifactResource { - eve::PersistentId id; - std::string buildKey; - GraphicsArtifactHandle handle; - std::string role; - std::vector positions; - std::vector normals; - std::vector uvs; + eve::PersistentId id; + std::string buildKey; + GraphicsArtifactHandle handle; + std::string role; + std::vector positions; + std::vector normals; + std::vector uvs; std::vector indices; /** @brief Backend name observed when upload was attempted, or `cpu`. */ std::string backendName = "cpu"; @@ -113,8 +112,7 @@ public: /** @brief Return a deterministic checksum of one resource's CPU descriptor. */ [[nodiscard]] std::uint64_t checksum(eve::PersistentId id) const noexcept; /** @brief Return the backend-neutral descriptor for one resource. */ - [[nodiscard]] std::optional descriptor( - eve::PersistentId id) const; + [[nodiscard]] std::optional descriptor(eve::PersistentId id) const; /** @brief Return whether a resource was backed by the live Graphics upload boundary. */ [[nodiscard]] bool isGpuResident(eve::PersistentId id) const noexcept; /** @brief Remove all resources and reset the local handle allocator. */ @@ -131,15 +129,15 @@ private: friend class GraphicsArtifactStage; struct RuntimeResource { GraphicsArtifactResource descriptor; - Mesh* uploadedMesh = nullptr; + Mesh* uploadedMesh = nullptr; }; - static void uploadIfAvailable(Graphics* graphics, RuntimeResource& resource) noexcept; - void commit(RuntimeResource resource) noexcept; - void release(RuntimeResource& resource) noexcept; + static void uploadIfAvailable(Graphics* graphics, RuntimeResource& resource) noexcept; + void commit(RuntimeResource resource) noexcept; + void release(RuntimeResource& resource) noexcept; std::vector resources_; - Graphics* graphics_ = nullptr; - std::uint32_t nextIndex_ = 0; - bool failPrepare_ = false; + Graphics* graphics_ = nullptr; + std::uint32_t nextIndex_ = 0; + bool failPrepare_ = false; }; /** @brief Return the process-owned graphics artifact provider singleton. */ diff --git a/src/modules/graphics/Graphics.cpp b/src/modules/graphics/Graphics.cpp index fd0b6adf..68f151ae 100644 --- a/src/modules/graphics/Graphics.cpp +++ b/src/modules/graphics/Graphics.cpp @@ -1,11 +1,11 @@ #include "graphics/Graphics.h" -#include "graphics/ArtifactProvider.h" #include "common/Capability.h" #include "common/config.h" +#include "font/FontData.h" +#include "graphics/ArtifactProvider.h" #include "graphics/GraphicsCapabilities.h" #include "graphics/Grass.h" #include "graphics/HairShader.h" -#include "font/FontData.h" #ifdef EVENGINE_WEBGPU #include "graphics/webgpu/Graphics.h" diff --git a/src/modules/graphics/Graphics.h b/src/modules/graphics/Graphics.h index 70d0f305..7513b820 100644 --- a/src/modules/graphics/Graphics.h +++ b/src/modules/graphics/Graphics.h @@ -43,9 +43,9 @@ class Mesh; * without depending on Vulkan or WebGPU headers. */ struct MeshBackendDescriptor { - std::uint32_t vertexCount = 0; - std::uint32_t indexCount = 0; - std::uint32_t vertexStride = 0; + std::uint32_t vertexCount = 0; + std::uint32_t indexCount = 0; + std::uint32_t vertexStride = 0; std::uint32_t indexElementSize = 0; }; class Outline; diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index d527d892..0fce923c 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -30,8 +30,8 @@ #include #endif -#include "common/Exception.h" #include "common/CrashLog.h" +#include "common/Exception.h" #include "common/StartupTiming.h" #include "common/config.h" #include "filesystem/Filesystem.h" diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index fd608b94..0eccd02d 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -411,8 +411,7 @@ public: Mesh *newMeshFromArrays(const float *posXYZ, const float *nrmXYZ, const float *uvST, int vertexCount, const uint32_t *indices, int indexCount) override; /** @brief Return layout facts from the owned Vulkan mesh upload. */ - [[nodiscard]] std::optional describeMesh( - Mesh *mesh) const override; + [[nodiscard]] std::optional describeMesh(Mesh *mesh) const override; bool bakeMeshMorph(Mesh *mesh) override; bool updateMeshVertices(Mesh *mesh, const float *posXYZ, const float *nrmXYZ, const float *uvST, int vertexCount, const uint32_t *indices, int indexCount) override; diff --git a/src/modules/graphics/vulkan/GraphicsMesh.cpp b/src/modules/graphics/vulkan/GraphicsMesh.cpp index 8dbb73de..b5d980eb 100644 --- a/src/modules/graphics/vulkan/GraphicsMesh.cpp +++ b/src/modules/graphics/vulkan/GraphicsMesh.cpp @@ -247,14 +247,13 @@ Mesh *Graphics::newMeshFromArrays(const float *posXYZ, const float *nrmXYZ, cons std::optional Graphics::describeMesh(Mesh *mesh) const { if (!mesh || !mesh->gpuHandle) return std::nullopt; const auto *gpu = static_cast(mesh->gpuHandle); - const auto owned = std::find_if(ownedGpuMeshes.begin(), ownedGpuMeshes.end(), - [gpu](const std::unique_ptr &candidate) { - return candidate.get() == gpu; - }); + const auto owned = + std::find_if(ownedGpuMeshes.begin(), ownedGpuMeshes.end(), + [gpu](const std::unique_ptr &candidate) { return candidate.get() == gpu; }); if (owned == ownedGpuMeshes.end()) return std::nullopt; - return eve::graphics::MeshBackendDescriptor{ - gpu->vertexCount, gpu->indexCount, static_cast(sizeof(MeshVertex)), - gpu->indexType == vk::IndexType::eUint16 ? 2u : 4u}; + return eve::graphics::MeshBackendDescriptor{gpu->vertexCount, gpu->indexCount, + static_cast(sizeof(MeshVertex)), + gpu->indexType == vk::IndexType::eUint16 ? 2u : 4u}; } bool Graphics::bakeMeshMorph(Mesh *mesh) { diff --git a/src/modules/graphics/webgpu/Graphics.cpp b/src/modules/graphics/webgpu/Graphics.cpp index 9bee240d..6617e360 100644 --- a/src/modules/graphics/webgpu/Graphics.cpp +++ b/src/modules/graphics/webgpu/Graphics.cpp @@ -1,7 +1,7 @@ #include "graphics/webgpu/Graphics.h" -#include "graphics/GraphicsCapabilities.h" #include "graphics/Batcher.h" #include "graphics/ClusteredLight.h" +#include "graphics/GraphicsCapabilities.h" #include "graphics/Light.h" #include "graphics/Mesh.h" #include "graphics/RenderControl.h" @@ -9,9 +9,9 @@ #include "graphics/Shadow.h" #include "graphics/Texture.h" #include "graphics/TextureSampler.h" +#include "graphics/webgpu/BindGroupLayoutBuilder.h" #include "graphics/webgpu/Canvas.h" #include "graphics/webgpu/InitFlow.h" -#include "graphics/webgpu/BindGroupLayoutBuilder.h" #include "graphics/webgpu/PipelineBuilder.h" #include "graphics/webgpu/wgsl_shaders.h" @@ -2083,14 +2083,12 @@ Mesh *Graphics::newMeshFromArrays(const float *posXYZ, const float *nrmXYZ, cons std::optional Graphics::describeMesh(Mesh *mesh) const { if (!mesh || !mesh->gpuHandle) return std::nullopt; const auto *gpu = static_cast(mesh->gpuHandle); - const auto owned = std::find_if(ownedGpuMeshes.begin(), ownedGpuMeshes.end(), - [gpu](const std::unique_ptr &candidate) { - return candidate.get() == gpu; - }); + const auto owned = + std::find_if(ownedGpuMeshes.begin(), ownedGpuMeshes.end(), + [gpu](const std::unique_ptr &candidate) { return candidate.get() == gpu; }); if (owned == ownedGpuMeshes.end()) return std::nullopt; - return eve::graphics::MeshBackendDescriptor{ - gpu->vertexCount, gpu->indexCount, gpu->vertexStride, - gpu->indexFormat == wgpu::IndexFormat::Uint16 ? 2u : 4u}; + return eve::graphics::MeshBackendDescriptor{gpu->vertexCount, gpu->indexCount, gpu->vertexStride, + gpu->indexFormat == wgpu::IndexFormat::Uint16 ? 2u : 4u}; } Mesh *Graphics::newMeshFromAssimp(const ::aiMesh &mesh) { diff --git a/src/modules/graphics/webgpu/Graphics.h b/src/modules/graphics/webgpu/Graphics.h index 0ce0b8fc..fd564908 100644 --- a/src/modules/graphics/webgpu/Graphics.h +++ b/src/modules/graphics/webgpu/Graphics.h @@ -284,8 +284,7 @@ public: Mesh *newMeshFromArrays(const float *posXYZ, const float *nrmXYZ, const float *uvST, int vertexCount, const uint32_t *indices, int indexCount) override; /** @brief Return layout facts from the owned WebGPU mesh upload. */ - [[nodiscard]] std::optional describeMesh( - Mesh *mesh) const override; + [[nodiscard]] std::optional describeMesh(Mesh *mesh) const override; bool bakeMeshMorph(Mesh *mesh) override; bool updateMeshVertices(Mesh *mesh, const float *posXYZ, const float *nrmXYZ, const float *uvST, int vertexCount, const uint32_t *indices, int indexCount) override; diff --git a/src/modules/housegen/HouseComponentLibrary.cpp b/src/modules/housegen/HouseComponentLibrary.cpp index f508d731..c353ddf0 100644 --- a/src/modules/housegen/HouseComponentLibrary.cpp +++ b/src/modules/housegen/HouseComponentLibrary.cpp @@ -14,10 +14,9 @@ namespace { using eve::json::Value; template -eve::Result failure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - code, std::move(message), std::move(path), {}, "housegen.components")); +eve::Result failure(eve::DiagnosticCode code, std::string message, std::string path = {}) { + return eve::Result::failure( + eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "housegen.components")); } eve::Result parseDirection(std::string_view value) { @@ -27,40 +26,36 @@ eve::Result parseDirection(std::string_view value) { if (value == "west") return eve::Result::success(SocketDirection::West); if (value == "up") return eve::Result::success(SocketDirection::Up); if (value == "down") return eve::Result::success(SocketDirection::Down); - return failure(eve::DiagnosticCode::InvalidArgument, - "socket direction is invalid", "direction"); + return failure(eve::DiagnosticCode::InvalidArgument, "socket direction is invalid", "direction"); } eve::Result parseComponent(Value object) { if (!object.isObject()) - return failure(eve::DiagnosticCode::ParseError, - "component must be an object"); + return failure(eve::DiagnosticCode::ParseError, "component must be an object"); HouseComponent out; - out.id = object.getString("id"); + out.id = object.getString("id"); out.modelPath = object.getString("model"); - out.category = object.getString("category"); - out.width = object.getInt("width", 1); - out.depth = object.getInt("depth", 1); - out.height = object.getInt("height", 1); - out.weight = object.getInt("weight", 1); - out.tags = object.getStringArray("tags"); + out.category = object.getString("category"); + out.width = object.getInt("width", 1); + out.depth = object.getInt("depth", 1); + out.height = object.getInt("height", 1); + out.weight = object.getInt("weight", 1); + out.tags = object.getStringArray("tags"); if (object.get("rotations").isArray()) out.rotations = object.getIntArray("rotations"); const Value sockets = object.get("sockets"); for (size_t i = 0; i < sockets.size(); ++i) { const Value socketObject = sockets.at(i); if (!socketObject.isObject()) - return failure(eve::DiagnosticCode::ParseError, - "socket must be an object", "sockets"); + return failure(eve::DiagnosticCode::ParseError, "socket must be an object", "sockets"); auto direction = parseDirection(socketObject.getString("direction")); - if (!direction.ok()) - return eve::Result::failure(direction.status()); + if (!direction.ok()) return eve::Result::failure(direction.status()); HouseSocket socket; socket.direction = std::move(direction).takeValue(); - socket.type = socketObject.getString("type"); - socket.accepts = socketObject.getStringArray("accepts"); + socket.type = socketObject.getString("type"); + socket.accepts = socketObject.getStringArray("accepts"); if (socket.type.empty()) return failure(eve::DiagnosticCode::InvalidArgument, "socket needs a valid direction and type", "sockets"); @@ -73,8 +68,7 @@ eve::Result parseComponent(Value object) { if (color.isArray()) { if (color.size() != 3 && color.size() != 4) return failure(eve::DiagnosticCode::InvalidArgument, - "material baseColor needs 3 or 4 values", - "material.baseColor"); + "material baseColor needs 3 or 4 values", "material.baseColor"); out.material.hasBaseColor = true; out.material.baseColorR = color.at(0).asFloat(); out.material.baseColorG = color.at(1).asFloat(); @@ -106,56 +100,48 @@ eve::Result parseComponent(Value object) { eve::Result HouseComponentLibrary::registerComponent(const HouseComponent &component) { if (component.id.empty() || component.modelPath.empty() || component.category.empty()) - return failure(eve::DiagnosticCode::InvalidArgument, - "component needs id, model and category"); + return failure(eve::DiagnosticCode::InvalidArgument, "component needs id, model and category"); if (component.width < 1 || component.depth < 1 || component.height < 1 || component.weight < 1) - return failure(eve::DiagnosticCode::InvalidArgument, - "component dimensions and weight must be positive"); + return failure(eve::DiagnosticCode::InvalidArgument, "component dimensions and weight must be positive"); for (const int rotation : component.rotations) { if (rotation < 0 || rotation >= 360 || rotation % 90 != 0) - return failure(eve::DiagnosticCode::InvalidArgument, - "rotations must be cardinal degrees", "rotations"); + return failure(eve::DiagnosticCode::InvalidArgument, "rotations must be cardinal degrees", + "rotations"); } const auto inUnitRange = [](float value) { return value >= 0.f && value <= 1.f; }; if ((component.material.hasBaseColor && - (!inUnitRange(component.material.baseColorR) || - !inUnitRange(component.material.baseColorG) || - !inUnitRange(component.material.baseColorB) || - !inUnitRange(component.material.baseColorA))) || + (!inUnitRange(component.material.baseColorR) || !inUnitRange(component.material.baseColorG) || + !inUnitRange(component.material.baseColorB) || !inUnitRange(component.material.baseColorA))) || (component.material.hasMetallic && !inUnitRange(component.material.metallic)) || (component.material.hasRoughness && !inUnitRange(component.material.roughness)) || - !inUnitRange(component.material.cellBombStrength) || - !inUnitRange(component.material.cellBombRotation)) - return failure(eve::DiagnosticCode::InvalidArgument, - "material values must be between 0 and 1", "material"); + !inUnitRange(component.material.cellBombStrength) || !inUnitRange(component.material.cellBombRotation)) + return failure(eve::DiagnosticCode::InvalidArgument, "material values must be between 0 and 1", + "material"); if (component.material.parallaxScale < 0.f || component.material.parallaxScale > 0.2f || component.material.parallaxMinLayers < 1.f || component.material.parallaxMaxLayers < component.material.parallaxMinLayers || component.material.cellBombScale <= 0.f) - return failure(eve::DiagnosticCode::InvalidArgument, - "material sampling parameters are invalid", "material"); + return failure(eve::DiagnosticCode::InvalidArgument, "material sampling parameters are invalid", + "material"); if (components_.contains(component.id)) - return failure(eve::DiagnosticCode::Conflict, - "duplicate component id: " + component.id, "id"); + return failure(eve::DiagnosticCode::Conflict, "duplicate component id: " + component.id, "id"); components_.emplace(component.id, component); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } eve::Result HouseComponentLibrary::loadFromJson(std::string_view json) { - std::string parseError; - const eve::json::Document document = - eve::json::Document::parse(std::string(json), &parseError); + std::string parseError; + const eve::json::Document document = eve::json::Document::parse(std::string(json), &parseError); if (!document.valid()) return failure(eve::DiagnosticCode::ParseError, parseError.empty() ? "invalid component manifest" : parseError); - const Value root = document.root(); + const Value root = document.root(); const Value values = root.isArray() ? root : root.get("components"); if (!values.isArray()) - return failure(eve::DiagnosticCode::ParseError, - "expected components array", "components"); + return failure(eve::DiagnosticCode::ParseError, "expected components array", "components"); HouseComponentLibrary candidate; for (size_t i = 0; i < values.size(); ++i) { @@ -170,23 +156,19 @@ eve::Result HouseComponentLibrary::loadFromJson(std::string_view json) { eve::Result HouseComponentLibrary::loadFromFile(std::string_view filename) { const std::string path(filename); - std::ifstream input(path, std::ios::binary); - if (!input) - return failure(eve::DiagnosticCode::NotFound, - "cannot open component manifest: " + path, path); + std::ifstream input(path, std::ios::binary); + if (!input) return failure(eve::DiagnosticCode::NotFound, "cannot open component manifest: " + path, path); std::ostringstream json; json << input.rdbuf(); HouseComponentLibrary candidate; - auto loaded = candidate.loadFromJson(json.str()); + auto loaded = candidate.loadFromJson(json.str()); if (!loaded.ok()) return loaded; - const std::filesystem::path assetRoot = - std::filesystem::absolute(std::filesystem::path(path)).parent_path(); + const std::filesystem::path assetRoot = std::filesystem::absolute(std::filesystem::path(path)).parent_path(); for (auto &[_, component] : candidate.components_) { std::filesystem::path model(component.modelPath); - if (model.is_relative()) - component.modelPath = (assetRoot / model).lexically_normal().string(); + if (model.is_relative()) component.modelPath = (assetRoot / model).lexically_normal().string(); } components_ = std::move(candidate.components_); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); @@ -215,14 +197,12 @@ std::vector> HouseComponentLibrary: std::vector> result; for (const auto &[_, component] : components_) { if (component.category == category && - (style.empty() || std::find(component.tags.begin(), component.tags.end(), style) != - component.tags.end())) + (style.empty() || std::find(component.tags.begin(), component.tags.end(), style) != component.tags.end())) result.push_back(std::cref(component)); } if (result.empty() && !style.empty()) return byCategory(category, {}); - std::sort(result.begin(), result.end(), [](const auto &left, const auto &right) { - return left.get().id < right.get().id; - }); + std::sort(result.begin(), result.end(), + [](const auto &left, const auto &right) { return left.get().id < right.get().id; }); return result; } diff --git a/src/modules/housegen/HouseGen.cpp b/src/modules/housegen/HouseGen.cpp index acd71fba..dd288df5 100644 --- a/src/modules/housegen/HouseGen.cpp +++ b/src/modules/housegen/HouseGen.cpp @@ -11,23 +11,21 @@ namespace eve::housegen { Module_IMPL(HouseGen, new HouseGen()); -eve::Result HouseGen::loadComponentsFromJson(const std::string &json) { - return library_.loadFromJson(json); -} +eve::Result HouseGen::loadComponentsFromJson(const std::string &json) { return library_.loadFromJson(json); } eve::Result HouseGen::loadComponentsFromFile(const std::string &filename) { return library_.loadFromFile(filename); } -void HouseGen::clearComponents() { library_.clear(); } +void HouseGen::clearComponents() { library_.clear(); } int HouseGen::getComponentCount() const { return library_.count(); } -HouseRequest HouseGen::newRequest() const { return {}; } -HouseLayout HouseGen::newLayout() const { return {}; } +HouseRequest HouseGen::newRequest() const { return {}; } +HouseLayout HouseGen::newLayout() const { return {}; } eve::Result HouseGen::generate(const HouseRequest &request, HouseLayout &layout) { HouseGenerator generator(library_); return generator.generate(request, layout); } void HouseGen::expose(ssq::Table &table) { - const HSQUIRRELVM vm = table.getHandle(); + const HSQUIRRELVM vm = table.getHandle(); auto cls = table.addClass(name, HouseGen::create, false); expose(cls); auto request = table.addClass("HouseRequest", std::function([] { return new HouseRequest(); }), true); request.addFunc("setSeed", [](HouseRequest *r, int v) { r->seed = uint32_t(v); }); @@ -42,9 +40,9 @@ void HouseGen::expose(ssq::Table &table) { layout.addFunc("fromJson", [vm](HouseLayout *value, const std::string &json) { if (!value) return eve::script::projectResult( - vm, eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "layout receiver must not be null", "layout", {}, "housegen.squirrel"))); + vm, eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "layout receiver must not be null", "layout", {}, + "housegen.squirrel"))); return eve::script::projectResult(vm, value->fromJson(json)); }); layout.addFunc("getInstanceCount", [](HouseLayout *v) { return int(v->instances.size()); }); @@ -56,17 +54,17 @@ void HouseGen::expose(ssq::Class &cls) { cls.addFunc("loadComponentsFromJson", [vm](HouseGen *value, const std::string &json) { if (!value) return eve::script::projectResult( - vm, eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "HouseGen receiver must not be null", "housegen", {}, "housegen.squirrel"))); + vm, eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "HouseGen receiver must not be null", "housegen", + {}, "housegen.squirrel"))); return eve::script::projectResult(vm, value->loadComponentsFromJson(json)); }); cls.addFunc("loadComponentsFromFile", [vm](HouseGen *value, const std::string &filename) { if (!value) return eve::script::projectResult( - vm, eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "HouseGen receiver must not be null", "housegen", {}, "housegen.squirrel"))); + vm, eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "HouseGen receiver must not be null", "housegen", + {}, "housegen.squirrel"))); return eve::script::projectResult(vm, value->loadComponentsFromFile(filename)); }); cls.addFunc("clearComponents", &HouseGen::clearComponents); @@ -76,10 +74,9 @@ void HouseGen::expose(ssq::Class &cls) { cls.addFunc("generate", [vm](HouseGen *value, HouseRequest *request, HouseLayout *layout) { if (!value || !request || !layout) return eve::script::projectResult( - vm, eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "HouseGen, request and layout are required", "generate", {}, - "housegen.squirrel"))); + vm, eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "HouseGen, request and layout are required", + "generate", {}, "housegen.squirrel"))); return eve::script::projectResult(vm, value->generate(*request, *layout)); }); } diff --git a/src/modules/housegen/HouseGen.h b/src/modules/housegen/HouseGen.h index 06e0b76d..aabdb66e 100644 --- a/src/modules/housegen/HouseGen.h +++ b/src/modules/housegen/HouseGen.h @@ -21,7 +21,7 @@ public: int getComponentCount() const; /** @brief 工厂:生成请求 / 布局。 */ [[nodiscard]] HouseRequest newRequest() const; - [[nodiscard]] HouseLayout newLayout() const; + [[nodiscard]] HouseLayout newLayout() const; /** @brief 按请求生成布局;失败返回结构化诊断。 */ [[nodiscard]] eve::Result generate(const HouseRequest &request, HouseLayout &layout); /** @brief 组件库(可直接访问)。 */ diff --git a/src/modules/housegen/HouseGenerator.cpp b/src/modules/housegen/HouseGenerator.cpp index f96e1842..6344aebe 100644 --- a/src/modules/housegen/HouseGenerator.cpp +++ b/src/modules/housegen/HouseGenerator.cpp @@ -12,15 +12,13 @@ namespace eve::housegen { namespace { template -eve::Result failure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - code, std::move(message), std::move(path), {}, "housegen.generate")); +eve::Result failure(eve::DiagnosticCode code, std::string message, std::string path = {}) { + return eve::Result::failure( + eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "housegen.generate")); } -const HouseComponent *pick( - const std::vector> &choices, - std::mt19937 &rng) { +const HouseComponent *pick(const std::vector> &choices, + std::mt19937 &rng) { if (choices.empty()) return nullptr; int total = 0; for (const auto &choice : choices) total += choice.get().weight; @@ -33,9 +31,7 @@ const HouseComponent *pick( return &choices.back().get(); } -bool has(const std::vector> &values) { - return !values.empty(); -} +bool has(const std::vector> &values) { return !values.empty(); } bool isWindowComponent(const HouseComponent *component) { if (!component) return false; @@ -44,11 +40,11 @@ bool isWindowComponent(const HouseComponent *component) { } std::vector> facadeVariant( - const std::vector> &choices, - bool wantWindow) { + const std::vector> &choices, bool wantWindow) { bool hasWindow = false, hasSolid = false; for (const auto &choice : choices) { - if (isWindowComponent(&choice.get())) hasWindow = true; + if (isWindowComponent(&choice.get())) + hasWindow = true; else hasSolid = true; } if (!hasWindow || !hasSolid) return choices; @@ -115,14 +111,16 @@ SocketDirection rotate(SocketDirection direction, int degrees) { } std::vector> compatibleOnFace( - const std::vector> &choices, - SocketDirection face, int rotation) { + const std::vector> &choices, SocketDirection face, int rotation) { std::vector> out; for (const auto &choice : choices) { const auto &c = choice.get(); // Components without sockets are intentionally wildcard-compatible. This preserves // compatibility with small legacy kits while new player kits opt into strict sockets. - if (c.sockets.empty()) { out.push_back(choice); continue; } + if (c.sockets.empty()) { + out.push_back(choice); + continue; + } for (const auto &socket : c.sockets) { if (rotate(socket.direction, rotation) == face && !socket.type.empty()) { out.push_back(choice); @@ -144,29 +142,28 @@ eve::Result HouseGenerator::generate(const HouseRequest &r, HouseLayout &o if (!oneOf(r.footprint, {"auto", "rectangle", "l_shape", "t_shape"}) || !oneOf(r.roof, {"auto", "gable", "flat", "shed"}) || !oneOf(r.entrance, {"auto", "north", "east", "south", "west"})) { - return failure(eve::DiagnosticCode::Unsupported, - "unsupported footprint, roof or entrance mode"); + return failure(eve::DiagnosticCode::Unsupported, "unsupported footprint, roof or entrance mode"); } const auto foundation = library_.byCategory("foundation", r.style); - const auto floor = library_.byCategory("floor", r.style); - const auto wall = library_.byCategory("wall", r.style); - const auto door = library_.byCategory("door", r.style); - const auto roof = library_.byCategory("roof", r.style); + const auto floor = library_.byCategory("floor", r.style); + const auto wall = library_.byCategory("wall", r.style); + const auto door = library_.byCategory("door", r.style); + const auto roof = library_.byCategory("roof", r.style); if (!has(foundation) || !has(floor) || !has(wall) || !has(door) || !has(roof)) { return failure(eve::DiagnosticCode::NotFound, "library needs foundation, floor, wall, door and roof categories"); } std::mt19937 rng(r.seed); - generated.seed = r.seed; - generated.moduleSize = r.moduleSize; - generated.floorHeight = r.floorHeight; + generated.seed = r.seed; + generated.moduleSize = r.moduleSize; + generated.floorHeight = r.floorHeight; static constexpr const char *shapes[] = {"rectangle", "l_shape", "t_shape"}; static constexpr const char *roofs[] = {"gable", "flat", "shed"}; static constexpr SocketDirection sides[] = {SocketDirection::North, SocketDirection::East, SocketDirection::South, SocketDirection::West}; - generated.footprintStyle = r.footprint == "auto" ? shapes[rng() % 3] : r.footprint; - generated.roofStyle = r.roof == "auto" ? roofs[rng() % 3] : r.roof; + generated.footprintStyle = r.footprint == "auto" ? shapes[rng() % 3] : r.footprint; + generated.roofStyle = r.roof == "auto" ? roofs[rng() % 3] : r.roof; const SocketDirection entranceDirection = r.entrance == "auto" ? sides[rng() % 4] : directionFromName(r.entrance); generated.entranceSide = directionName(entranceDirection); @@ -198,10 +195,8 @@ eve::Result HouseGenerator::generate(const HouseRequest &r, HouseLayout &o generated.instances.push_back({pick(floor, rng)->id, x, y, z, 0}); } if (maxX < minX || maxY < minY) - return failure(eve::DiagnosticCode::Failed, - "footprint collapsed after inset"); - generated.rooms.push_back({z == 0 ? "living" : "upper", minX, minY, - maxX - minX + 1, maxY - minY + 1}); + return failure(eve::DiagnosticCode::Failed, "footprint collapsed after inset"); + generated.rooms.push_back({z == 0 ? "living" : "upper", minX, minY, maxX - minX + 1, maxY - minY + 1}); using Face = std::tuple; std::vector faces; @@ -240,15 +235,16 @@ eve::Result HouseGenerator::generate(const HouseRequest &r, HouseLayout &o } const auto *selected = pick(choices, rng); if (!selected) { - return failure(eve::DiagnosticCode::NotFound, - "no socket-compatible " + - std::string(isEntrance ? "door" : "wall") + " component"); + return failure( + eve::DiagnosticCode::NotFound, + "no socket-compatible " + std::string(isEntrance ? "door" : "wall") + " component"); } generated.instances.push_back({selected->id, x, y, z, rotation}); } if (z + 1 == r.floors) { for (int y = 0; y < r.depth; ++y) for (int x = 0; x < r.width; ++x) - if (active(mask, r.width, r.depth, x, y)) generated.instances.push_back({pick(roof, rng)->id, x, y, z + 1, 0}); + if (active(mask, r.width, r.depth, x, y)) + generated.instances.push_back({pick(roof, rng)->id, x, y, z + 1, 0}); } previousMask = mask; } diff --git a/src/modules/housegen/HouseGenerator.h b/src/modules/housegen/HouseGenerator.h index 5578927a..2c47bbf0 100644 --- a/src/modules/housegen/HouseGenerator.h +++ b/src/modules/housegen/HouseGenerator.h @@ -17,8 +17,7 @@ public: /** @brief 当前组件库。 */ [[nodiscard]] const HouseComponentLibrary &library() const noexcept { return library_; } /** @brief 生成布局;失败时不修改输出布局并返回结构化诊断。 */ - [[nodiscard]] eve::Result generate(const HouseRequest &request, - HouseLayout &out) const; + [[nodiscard]] eve::Result generate(const HouseRequest &request, HouseLayout &out) const; private: const HouseComponentLibrary &library_; diff --git a/src/modules/housegen/HouseLayout.cpp b/src/modules/housegen/HouseLayout.cpp index a50bd64e..ada1d02f 100644 --- a/src/modules/housegen/HouseLayout.cpp +++ b/src/modules/housegen/HouseLayout.cpp @@ -31,10 +31,9 @@ namespace eve::housegen { namespace { template -eve::Result failure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - code, std::move(message), std::move(path), {}, "housegen.layout")); +eve::Result failure(eve::DiagnosticCode code, std::string message, std::string path = {}) { + return eve::Result::failure( + eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "housegen.layout")); } std::string esc(const std::string &v) { std::string o; for (char c : v) { if (c == '\\' || c == '"') o += '\\'; o += c; } return o; } @@ -137,16 +136,13 @@ std::string HouseLayout::toJson() const { eve::Result HouseLayout::fromJson(std::string_view json) { using eve::json::Value; - std::string parseError; - const eve::json::Document doc = - eve::json::Document::parse(std::string(json), &parseError); + std::string parseError; + const eve::json::Document doc = eve::json::Document::parse(std::string(json), &parseError); if (!doc.valid()) return failure(eve::DiagnosticCode::ParseError, parseError.empty() ? "invalid house layout JSON" : parseError); const Value o = doc.root(); - if (!o.isObject()) - return failure(eve::DiagnosticCode::ParseError, - "layout must be an object"); + if (!o.isObject()) return failure(eve::DiagnosticCode::ParseError, "layout must be an object"); HouseLayout parsed; parsed.seed = static_cast(o.getInt("seed", 1)); @@ -158,14 +154,13 @@ eve::Result HouseLayout::fromJson(std::string_view json) { const Value instances = o.get("instances"); if (!instances.isArray()) - return failure(eve::DiagnosticCode::ParseError, - "layout has no instances", "instances"); + return failure(eve::DiagnosticCode::ParseError, "layout has no instances", "instances"); for (size_t i = 0; i < instances.size(); ++i) { const Value v = instances.at(i); // componentId and the cell coordinates are required, not defaulted. if (!v.has("componentId") || !v.has("x") || !v.has("y") || !v.has("z")) - return failure(eve::DiagnosticCode::ParseError, - "instance needs componentId, x, y and z", "instances"); + return failure(eve::DiagnosticCode::ParseError, "instance needs componentId, x, y and z", + "instances"); parsed.instances.push_back({v.getString("componentId"), v.getInt("x"), v.getInt("y"), v.getInt("z"), v.getInt("rotationDeg", 0)}); } @@ -173,10 +168,8 @@ eve::Result HouseLayout::fromJson(std::string_view json) { const Value rooms = o.get("rooms"); for (size_t i = 0; i < rooms.size(); ++i) { const Value v = rooms.at(i); - if (!v.has("type") || !v.has("x") || !v.has("y") || !v.has("width") || - !v.has("depth")) - return failure(eve::DiagnosticCode::ParseError, - "room needs type, x, y, width and depth", "rooms"); + if (!v.has("type") || !v.has("x") || !v.has("y") || !v.has("width") || !v.has("depth")) + return failure(eve::DiagnosticCode::ParseError, "room needs type, x, y, width and depth", "rooms"); parsed.rooms.push_back({v.getString("type"), v.getInt("x"), v.getInt("y"), v.getInt("width"), v.getInt("depth")}); } @@ -197,23 +190,21 @@ eve::Result HouseLayout::validate(const HouseComponentLibrary &library) co for (const auto &i : instances) { const auto component = library.find(i.componentId); if (!component) - return failure(eve::DiagnosticCode::NotFound, - "unknown component: " + i.componentId, "componentId"); + return failure(eve::DiagnosticCode::NotFound, "unknown component: " + i.componentId, "componentId"); const HouseComponent &c = component->get(); if (i.rotationDeg % 90 != 0) - return failure(eve::DiagnosticCode::InvalidArgument, - "non-cardinal rotation", "rotationDeg"); + return failure(eve::DiagnosticCode::InvalidArgument, "non-cardinal rotation", "rotationDeg"); const bool quarter = (i.rotationDeg / 90) % 2 != 0; - const int w = quarter ? c.depth : c.width, d = quarter ? c.width : c.depth; + const int w = quarter ? c.depth : c.width, d = quarter ? c.width : c.depth; for (int y = 0; y < d; ++y) for (int x = 0; x < w; ++x) { // Boundary cells legitimately carry two perpendicular wall modules at corners. const std::string orientation = (c.category == "wall" || c.category == "door") ? ":" + std::to_string((i.rotationDeg % 360 + 360) % 360) : ""; - const std::string key = std::to_string(i.x + x) + ":" + std::to_string(i.y + y) + ":" + std::to_string(i.z) + ":" + c.category + orientation; + const std::string key = std::to_string(i.x + x) + ":" + std::to_string(i.y + y) + ":" + + std::to_string(i.z) + ":" + c.category + orientation; if (!occupied.insert(key).second) - return failure(eve::DiagnosticCode::Conflict, - "overlapping " + c.category + " components"); + return failure(eve::DiagnosticCode::Conflict, "overlapping " + c.category + " components"); if (c.category == "floor") { floorCells.insert(cellKey(i.x + x, i.y + y, i.z)); floors.emplace_back(i.x + x, i.y + y, i.z); @@ -222,33 +213,26 @@ eve::Result HouseLayout::validate(const HouseComponentLibrary &library) co } } entrance = entrance || (c.category == "door" && i.z == 0); - roof = roof || c.category == "roof"; + roof = roof || c.category == "roof"; } - if (!entrance) - return failure(eve::DiagnosticCode::InvalidArgument, - "house has no entrance"); - if (!roof) - return failure(eve::DiagnosticCode::InvalidArgument, - "house has no roof"); + if (!entrance) return failure(eve::DiagnosticCode::InvalidArgument, "house has no entrance"); + if (!roof) return failure(eve::DiagnosticCode::InvalidArgument, "house has no roof"); for (const auto &[x, y, z] : floors) { if (z > 0 && !floorCells.contains(cellKey(x, y, z - 1))) { - return failure(eve::DiagnosticCode::InvalidArgument, - "upper floor has no structural support"); + return failure(eve::DiagnosticCode::InvalidArgument, "upper floor has no structural support"); } if (!floorCells.contains(cellKey(x, y, z + 1)) && !roofCells.contains(cellKey(x, y, z + 1))) { - return failure(eve::DiagnosticCode::InvalidArgument, - "floor cell has no roof coverage"); + return failure(eve::DiagnosticCode::InvalidArgument, "floor cell has no roof coverage"); } } return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result> HouseLayout::instantiate( - graphics::Graphics &gfx, model3d::Model3D &models, - const HouseComponentLibrary &library) const { +eve::Result> HouseLayout::instantiate(graphics::Graphics &gfx, model3d::Model3D &models, + const HouseComponentLibrary &library) const { std::vector entities; - const auto destroyCreated = [&entities]() noexcept { + const auto destroyCreated = [&entities]() noexcept { for (const auto &handle : entities) { if (auto *entity = ecs::try_get(handle)) ecs::DestroyEntity(entity); } @@ -271,23 +255,20 @@ eve::Result> HouseLayout::instantiate( const auto component = library.find(i.componentId); if (!component) { destroyCreated(); - return failure>( - eve::DiagnosticCode::NotFound, - "unknown component: " + i.componentId, "componentId"); + return failure>(eve::DiagnosticCode::NotFound, + "unknown component: " + i.componentId, "componentId"); } const HouseComponent &c = component->get(); // eve::ref cannot represent null, so look up before default-inserting. auto modelIt = data.find(c.modelPath); - if (modelIt == data.end()) - modelIt = data.emplace(c.modelPath, loadModel(&models, c.modelPath)).first; + if (modelIt == data.end()) modelIt = data.emplace(c.modelPath, loadModel(&models, c.modelPath)).first; model3d::ModelData *model = modelIt->second.get(); // Material overrides belong to a component, so two components may safely reuse the // same GLB with different architectural finishes. auto &cached = parts[c.id]; if (cached.empty()) { const aiScene *scene = model->getScene(); - if (!scene || !scene->mRootNode) - throw std::runtime_error("model has no scene root: " + c.modelPath); + if (!scene || !scene->mRootNode) throw std::runtime_error("model has no scene root: " + c.modelPath); std::function walk = [&](const aiNode *node, const aiMatrix4x4 &parent) { const aiMatrix4x4 world = parent * node->mTransformation; @@ -305,37 +286,33 @@ eve::Result> HouseLayout::instantiate( part.r = color.r; part.g = color.g; part.b = color.b; part.a = color.a; material->Get(AI_MATKEY_METALLIC_FACTOR, part.metallic); material->Get(AI_MATKEY_ROUGHNESS_FACTOR, part.roughness); - part.texture = assimpTexture(&gfx, scene, material, c.modelPath, - aiTextureType_BASE_COLOR, - aiTextureType_DIFFUSE); + part.texture = assimpTexture(&gfx, scene, material, c.modelPath, + aiTextureType_BASE_COLOR, aiTextureType_DIFFUSE); part.normalTexture = assimpTexture(&gfx, scene, material, c.modelPath, - aiTextureType_NORMALS, - aiTextureType_NORMAL_CAMERA); + aiTextureType_NORMALS, aiTextureType_NORMAL_CAMERA); part.heightTexture = assimpTexture(&gfx, scene, material, c.modelPath, - aiTextureType_HEIGHT, - aiTextureType_DISPLACEMENT); + aiTextureType_HEIGHT, aiTextureType_DISPLACEMENT); } if (c.material.hasBaseColor) { - part.r = c.material.baseColorR; part.g = c.material.baseColorG; - part.b = c.material.baseColorB; part.a = c.material.baseColorA; + part.r = c.material.baseColorR; + part.g = c.material.baseColorG; + part.b = c.material.baseColorB; + part.a = c.material.baseColorA; } if (!c.material.baseColorTexture.empty()) - part.texture = textureFromFile(&gfx, c.modelPath, - c.material.baseColorTexture); + part.texture = textureFromFile(&gfx, c.modelPath, c.material.baseColorTexture); if (!c.material.normalTexture.empty()) - part.normalTexture = textureFromFile(&gfx, c.modelPath, - c.material.normalTexture); + part.normalTexture = textureFromFile(&gfx, c.modelPath, c.material.normalTexture); if (!c.material.heightTexture.empty()) - part.heightTexture = textureFromFile(&gfx, c.modelPath, - c.material.heightTexture); + part.heightTexture = textureFromFile(&gfx, c.modelPath, c.material.heightTexture); if (c.material.hasMetallic) part.metallic = c.material.metallic; if (c.material.hasRoughness) part.roughness = c.material.roughness; - part.parallaxScale = c.material.parallaxScale; + part.parallaxScale = c.material.parallaxScale; part.parallaxMinLayers = c.material.parallaxMinLayers; part.parallaxMaxLayers = c.material.parallaxMaxLayers; - part.cellBombScale = c.material.cellBombScale; - part.cellBombStrength = c.material.cellBombStrength; - part.cellBombRotation = c.material.cellBombRotation; + part.cellBombScale = c.material.cellBombScale; + part.cellBombStrength = c.material.cellBombStrength; + part.cellBombRotation = c.material.cellBombRotation; cached.push_back(part); } for (unsigned child = 0; child < node->mNumChildren; ++child) @@ -347,9 +324,8 @@ eve::Result> HouseLayout::instantiate( auto *e = graphics::Renderable3D::create(); if (!e) { destroyCreated(); - return failure>( - eve::DiagnosticCode::Failed, - "failed to create Renderable3D entity"); + return failure>(eve::DiagnosticCode::Failed, + "failed to create Renderable3D entity"); } e->setMesh(part.mesh); e->setPosition(i.x * moduleSize, i.z * floorHeight, i.y * moduleSize); @@ -370,15 +346,14 @@ eve::Result> HouseLayout::instantiate( } } catch (const std::exception &e) { destroyCreated(); - return failure>( - eve::DiagnosticCode::Failed, e.what()); + return failure>(eve::DiagnosticCode::Failed, e.what()); } catch (...) { destroyCreated(); - return failure>( - eve::DiagnosticCode::Failed, "house layout instantiation failed"); + return failure>(eve::DiagnosticCode::Failed, + "house layout instantiation failed"); } - return eve::Result>::success( - std::move(entities), eve::Status::success(eve::StatusCode::Applied)); + return eve::Result>::success(std::move(entities), + eve::Status::success(eve::StatusCode::Applied)); } } // namespace eve::housegen diff --git a/src/modules/housegen/HouseLayout.h b/src/modules/housegen/HouseLayout.h index 9fe1bbe3..09d224c7 100644 --- a/src/modules/housegen/HouseLayout.h +++ b/src/modules/housegen/HouseLayout.h @@ -44,9 +44,9 @@ public: * @remarks Callers must resolve handles before use and must not retain resolved pointers * across world mutation. Failure leaves no partially-created entities. */ - [[nodiscard]] eve::Result> instantiate( - graphics::Graphics &gfx, model3d::Model3D &models, - const HouseComponentLibrary &library) const; + [[nodiscard]] eve::Result> instantiate(graphics::Graphics &gfx, + model3d::Model3D &models, + const HouseComponentLibrary &library) const; }; } // namespace housegen diff --git a/src/modules/image/ImageLoader.cpp b/src/modules/image/ImageLoader.cpp index 71093a07..26e2e1d7 100644 --- a/src/modules/image/ImageLoader.cpp +++ b/src/modules/image/ImageLoader.cpp @@ -52,9 +52,7 @@ public: * graphics TextureReloader performs the later GPU refresh. * @remarks This provider must not decode a second copy during dispatch. */ - eve::Result reload(const std::string &) override { - return eve::Result::success(false); - } + eve::Result reload(const std::string &) override { return eve::Result::success(false); } eve::Resource *load(const std::string &key) override { const std::string path = eve::ResourceManager::pathOfKey(key); diff --git a/src/modules/inventory/ContainerAdapters.cpp b/src/modules/inventory/ContainerAdapters.cpp index febdc266..3c4b98bf 100644 --- a/src/modules/inventory/ContainerAdapters.cpp +++ b/src/modules/inventory/ContainerAdapters.cpp @@ -18,8 +18,7 @@ namespace { [[nodiscard]] bool hasId(const Bag& bag, const eve::container::MembershipId& id) { return std::any_of(bag.slots().begin(), bag.slots().end(), [&](const ItemStack& stack) { - return !stack.empty() && - eve::container::MembershipId("inventory:" + std::to_string(stack.instanceId)) == id; + return !stack.empty() && eve::container::MembershipId("inventory:" + std::to_string(stack.instanceId)) == id; }); } @@ -35,16 +34,13 @@ void swapStacks(ItemStack& lhs, ItemStack& rhs) noexcept { [[nodiscard]] bool sameLayout(const eve::container::ContainerSnapshot& lhs, const eve::container::ContainerSnapshot& rhs) { - if (lhs.id != rhs.id || lhs.revision != rhs.revision || lhs.entries.size() != rhs.entries.size()) - return false; + if (lhs.id != rhs.id || lhs.revision != rhs.revision || lhs.entries.size() != rhs.entries.size()) return false; for (std::size_t index = 0; index < lhs.entries.size(); ++index) { - const auto& left = lhs.entries[index]; + const auto& left = lhs.entries[index]; const auto& right = rhs.entries[index]; - if (left.membership.object != right.membership.object || - left.membership.slot != right.membership.slot || - left.membership.generation != right.membership.generation || - left.object.id != right.object.id || left.object.type != right.object.type || - left.object.quantity != right.object.quantity) + if (left.membership.object != right.membership.object || left.membership.slot != right.membership.slot || + left.membership.generation != right.membership.generation || left.object.id != right.object.id || + left.object.type != right.object.type || left.object.quantity != right.object.quantity) return false; } return true; @@ -52,30 +48,26 @@ void swapStacks(ItemStack& lhs, ItemStack& rhs) noexcept { } // namespace -class InventoryContainerAdapter::PreparedState final - : public eve::container::IContainer::PreparedState { +class InventoryContainerAdapter::PreparedState final : public eve::container::IContainer::PreparedState { public: - PreparedState(InventoryContainerAdapter& owner, std::vector staged, - eve::Revision revision) + PreparedState(InventoryContainerAdapter& owner, std::vector staged, eve::Revision revision) : owner_(owner), staged_(std::move(staged)), revision_(revision) {} void commit() noexcept override { if (committed_) return; if (owner_.kind_ == InventoryContainerKind::Bag) { - if (owner_.bag_ == nullptr || owner_.bag_->slots().size() != staged_.size()) - std::terminate(); + if (owner_.bag_ == nullptr || owner_.bag_->slots().size() != staged_.size()) std::terminate(); owner_.bag_->slots().swap(staged_); } else { if (owner_.equipment_ == nullptr) std::terminate(); for (std::size_t index = 0; index < staged_.size(); ++index) { - auto* live = owner_.stackAt(eve::container::SlotIndex( - static_cast(index))); + auto* live = owner_.stackAt(eve::container::SlotIndex(static_cast(index))); if (live == nullptr) std::terminate(); swapStacks(*live, staged_[index]); } } owner_.revision_ = revision_; - committed_ = true; + committed_ = true; } void rollback() noexcept override { @@ -84,9 +76,9 @@ public: private: InventoryContainerAdapter& owner_; - std::vector staged_; - eve::Revision revision_; - bool committed_ = false; + std::vector staged_; + eve::Revision revision_; + bool committed_ = false; }; InventoryContainerAdapter::InventoryContainerAdapter(eve::container::ContainerId id, Bag* bag, @@ -96,36 +88,34 @@ InventoryContainerAdapter::InventoryContainerAdapter(eve::container::ContainerId ? eve::container::Capacity::fixed(static_cast(bag->getSlotCount())) : (bag != nullptr ? capacity : eve::container::Capacity::fixed(0)), eve::container::Ordering::ExplicitSlots, eve::container::Filter{}}, - kind_(InventoryContainerKind::Bag), bag_(bag) {} + kind_(InventoryContainerKind::Bag), + bag_(bag) {} -InventoryContainerAdapter::InventoryContainerAdapter(eve::container::ContainerId id, - EquipmentSet* equipment) +InventoryContainerAdapter::InventoryContainerAdapter(eve::container::ContainerId id, EquipmentSet* equipment) : descriptor_{std::move(id), - eve::container::Capacity::fixed( - equipment ? static_cast(equipment->getSlotCount()) : 0u), + eve::container::Capacity::fixed(equipment ? static_cast(equipment->getSlotCount()) : 0u), eve::container::Ordering::ExplicitSlots, eve::container::Filter{}}, - kind_(InventoryContainerKind::Equipment), equipment_(equipment) {} + kind_(InventoryContainerKind::Equipment), + equipment_(equipment) {} eve::container::MembershipId InventoryContainerAdapter::objectId(const ItemStack& stack) { return eve::container::MembershipId("inventory:" + std::to_string(stack.instanceId)); } -eve::container::ContainerObject InventoryContainerAdapter::describe(const ItemStack& stack, - std::string equipmentSlot) { +eve::container::ContainerObject InventoryContainerAdapter::describe(const ItemStack& stack, std::string equipmentSlot) { eve::container::ContainerObject object; - object.id = objectId(stack); - object.type = stack.itemId; + object.id = objectId(stack); + object.type = stack.itemId; object.quantity = stack.quantity > 0 ? static_cast(stack.quantity) : 0u; - object.tags = stack.tags; + object.tags = stack.tags; if (const auto* definition = ItemRegistry::find(stack.itemId)) { for (const auto& tag : definition->tags) - if (std::find(object.tags.begin(), object.tags.end(), tag) == object.tags.end()) - object.tags.push_back(tag); + if (std::find(object.tags.begin(), object.tags.end(), tag) == object.tags.end()) object.tags.push_back(tag); } - auto payload = std::make_shared(); - payload->stack = stack; + auto payload = std::make_shared(); + payload->stack = stack; payload->equipmentSlot = std::move(equipmentSlot); - object.payload = std::move(payload); + object.payload = std::move(payload); return object; } @@ -152,54 +142,42 @@ std::string InventoryContainerAdapter::slotName(eve::container::SlotIndex slot) } eve::Result InventoryContainerAdapter::snapshot() const { - if (!descriptor_.id.isValid() || - (kind_ == InventoryContainerKind::Bag ? bag_ == nullptr : equipment_ == nullptr)) + if (!descriptor_.id.isValid() || (kind_ == InventoryContainerKind::Bag ? bag_ == nullptr : equipment_ == nullptr)) return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, - "inventory container adapter is not bound")); - eve::container::ContainerSnapshot snapshot{descriptor_.id, revision_, {}}; + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "inventory container adapter is not bound")); + eve::container::ContainerSnapshot snapshot{descriptor_.id, revision_, {}}; std::unordered_set ids; - const int count = kind_ == InventoryContainerKind::Bag ? bag_->getSlotCount() - : equipment_->getSlotCount(); + const int count = kind_ == InventoryContainerKind::Bag ? bag_->getSlotCount() : equipment_->getSlotCount(); for (int index = 0; index < count; ++index) { - const auto slot = eve::container::SlotIndex(index); + const auto slot = eve::container::SlotIndex(index); const auto* stack = stackAt(slot); if (stack == nullptr || stack->empty()) continue; - if (!descriptor_.capacity.isUnlimited() && - static_cast(index) >= descriptor_.capacity.value()) - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, - "inventory membership exceeds configured capacity")); + if (!descriptor_.capacity.isUnlimited() && static_cast(index) >= descriptor_.capacity.value()) + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvariantViolation, "inventory membership exceeds configured capacity")); if (stack->instanceId <= 0) - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, - "inventory stack has no instance identity")); + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvariantViolation, "inventory stack has no instance identity")); const auto id = objectId(*stack); if (!ids.insert(id).second) - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "inventory container contains duplicate instance identity")); - snapshot.entries.push_back({{id, slot, eve::Generation(1)}, - describe(*stack, slotName(slot))}); + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::Conflict, "inventory container contains duplicate instance identity")); + snapshot.entries.push_back({{id, slot, eve::Generation(1)}, describe(*stack, slotName(slot))}); } - if (!descriptor_.capacity.isUnlimited() && - snapshot.entries.size() > descriptor_.capacity.value()) - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, - "inventory container exceeds configured capacity")); + if (!descriptor_.capacity.isUnlimited() && snapshot.entries.size() > descriptor_.capacity.value()) + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvariantViolation, "inventory container exceeds configured capacity")); return eve::Result::success(std::move(snapshot)); } -eve::Result InventoryContainerAdapter::validateObject( - const InventoryContainerObject& object) const { +eve::Result InventoryContainerAdapter::validateObject(const InventoryContainerObject& object) const { if (object.stack.empty() || object.stack.instanceId <= 0 || object.stack.itemId.empty()) return error(eve::DiagnosticCode::InvalidArgument, "inventory transfer stack is incomplete"); return eve::Result::success(); } eve::Result InventoryContainerAdapter::validateInsert( - const eve::container::ContainerObject& object, - std::optional destination, + const eve::container::ContainerObject& object, std::optional destination, std::optional ignoredObject) const { const auto* payload = dynamic_cast(object.payload.get()); if (payload == nullptr) return error(eve::DiagnosticCode::StaleHandle, "inventory payload is stale"); @@ -207,35 +185,30 @@ eve::Result InventoryContainerAdapter::validateInsert( if (!validObject) return validObject; if (object.id != objectId(payload->stack)) return error(eve::DiagnosticCode::StaleHandle, "inventory membership identity is stale"); - if (object.type != payload->stack.itemId || - object.quantity != static_cast(payload->stack.quantity)) + if (object.type != payload->stack.itemId || object.quantity != static_cast(payload->stack.quantity)) return error(eve::DiagnosticCode::StaleHandle, "inventory object facts are stale"); const auto* definition = ItemRegistry::find(payload->stack.itemId); - if (definition == nullptr) - return error(eve::DiagnosticCode::NotFound, "inventory item definition was not found"); + if (definition == nullptr) return error(eve::DiagnosticCode::NotFound, "inventory item definition was not found"); if (definition->maxStack <= 0 || payload->stack.quantity > definition->maxStack) - return error(eve::DiagnosticCode::PreconditionViolation, - "inventory stack exceeds item definition maxStack"); + return error(eve::DiagnosticCode::PreconditionViolation, "inventory stack exceeds item definition maxStack"); if (kind_ == InventoryContainerKind::Bag) { if (bag_ == nullptr) return error(eve::DiagnosticCode::InvalidArgument, "bag adapter is unbound"); const std::size_t slotCount = static_cast(bag_->getSlotCount()); - const std::size_t slotLimit = descriptor_.capacity.isUnlimited() - ? slotCount - : std::min(slotCount, descriptor_.capacity.value()); + const std::size_t slotLimit = + descriptor_.capacity.isUnlimited() ? slotCount : std::min(slotCount, descriptor_.capacity.value()); if (hasId(*bag_, object.id) && (!ignoredObject || *ignoredObject != object.id)) return error(eve::DiagnosticCode::Conflict, "bag already contains inventory object"); if (destination) { - if (!destination->isValid() || - static_cast(destination->value()) >= slotLimit) + if (!destination->isValid() || static_cast(destination->value()) >= slotLimit) return error(eve::DiagnosticCode::InvalidArgument, "bag destination slot is out of range"); if (!bag_->slots()[static_cast(destination->value())].empty() && (!ignoredObject || *ignoredObject != object.id)) return error(eve::DiagnosticCode::Conflict, "bag destination slot is occupied"); } else { - const bool hasEmptySlot = std::any_of( - bag_->slots().begin(), bag_->slots().begin() + static_cast(slotLimit), - [](const ItemStack& stack) { return stack.empty(); }); + const bool hasEmptySlot = + std::any_of(bag_->slots().begin(), bag_->slots().begin() + static_cast(slotLimit), + [](const ItemStack& stack) { return stack.empty(); }); if (!hasEmptySlot && (!ignoredObject || *ignoredObject != object.id)) return error(eve::DiagnosticCode::Conflict, "bag has no empty membership slot"); } @@ -250,83 +223,68 @@ eve::Result InventoryContainerAdapter::validateInsert( if (equipment_ == nullptr) return error(eve::DiagnosticCode::InvalidArgument, "equipment is unbound"); if (!destination || !destination->isValid() || - static_cast(destination->value()) >= - static_cast(equipment_->getSlotCount())) + static_cast(destination->value()) >= static_cast(equipment_->getSlotCount())) return error(eve::DiagnosticCode::PreconditionViolation, "equipment transfer requires a valid destination slot"); const std::string target = slotName(*destination); if (target.empty()) return error(eve::DiagnosticCode::NotFound, "equipment slot was not found"); const auto* current = equipment_->stackAt(target); - if (current != nullptr && !current->empty() && - (!ignoredObject || object.id != objectId(*current))) + if (current != nullptr && !current->empty() && (!ignoredObject || object.id != objectId(*current))) return error(eve::DiagnosticCode::Conflict, "equipment destination slot is occupied"); if (!definition->equipSlot.empty() && definition->equipSlot != target) - return error(eve::DiagnosticCode::PreconditionViolation, - "inventory item does not fit equipment slot"); + return error(eve::DiagnosticCode::PreconditionViolation, "inventory item does not fit equipment slot"); const auto* allowed = equipment_->allowedTags(target); if (allowed != nullptr && !allowed->empty()) { const bool accepted = std::any_of(allowed->begin(), allowed->end(), [&](const std::string& tag) { return definition->hasTag(tag) || payload->stack.hasTag(tag); }); if (!accepted) - return error(eve::DiagnosticCode::PreconditionViolation, - "equipment slot rejected inventory tags"); + return error(eve::DiagnosticCode::PreconditionViolation, "equipment slot rejected inventory tags"); } return eve::Result::success(); } -eve::Result> -InventoryContainerAdapter::prepare(const eve::container::ContainerSnapshot& expected, - const eve::container::ContainerSnapshot& candidate) { +eve::Result> InventoryContainerAdapter::prepare( + const eve::container::ContainerSnapshot& expected, const eve::container::ContainerSnapshot& candidate) { const bool bound = kind_ == InventoryContainerKind::Bag ? bag_ != nullptr : equipment_ != nullptr; if (!bound || !descriptor_.id.isValid()) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, - "inventory adapter is not bound")); + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "inventory adapter is not bound")); if (expected.id != descriptor_.id || candidate.id != descriptor_.id) - return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, - "inventory prepare targets another container")); + return eve::Result>::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvalidArgument, "inventory prepare targets another container")); auto currentResult = snapshot(); if (!currentResult) - return eve::Result>::failure( - currentResult.status()); + return eve::Result>::failure(currentResult.status()); if (!sameLayout(currentResult.value(), expected)) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, - "inventory prepare expected snapshot is stale")); + eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, "inventory prepare expected snapshot is stale")); const auto nextRevision = expected.revision.incremented(); if (!nextRevision || candidate.revision != *nextRevision) + return eve::Result>::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvariantViolation, "inventory candidate revision is not the next revision")); + const int slotCount = kind_ == InventoryContainerKind::Bag ? bag_->getSlotCount() : equipment_->getSlotCount(); + if (!descriptor_.capacity.isUnlimited() && candidate.entries.size() > descriptor_.capacity.value()) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, - "inventory candidate revision is not the next revision")); - const int slotCount = kind_ == InventoryContainerKind::Bag ? bag_->getSlotCount() - : equipment_->getSlotCount(); - if (!descriptor_.capacity.isUnlimited() && - candidate.entries.size() > descriptor_.capacity.value()) - return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "inventory candidate exceeds container capacity")); + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "inventory candidate exceeds container capacity")); - std::vector staged(static_cast(slotCount)); + std::vector staged(static_cast(slotCount)); std::unordered_set ids; - std::unordered_set slots; + std::unordered_set slots; for (const auto& entry : candidate.entries) { if (!entry.membership.object.isValid() || !entry.membership.slot.isValid() || entry.membership.slot.value() >= slotCount || (!descriptor_.capacity.isUnlimited() && static_cast(entry.membership.slot.value()) >= descriptor_.capacity.value()) || entry.membership.generation.isZero() || entry.object.id != entry.membership.object || - !ids.insert(entry.membership.object).second || - !slots.insert(entry.membership.slot.value()).second) + !ids.insert(entry.membership.object).second || !slots.insert(entry.membership.slot.value()).second) return eve::Result>::failure( eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "inventory candidate contains invalid membership")); const auto* payload = dynamic_cast(entry.object.payload.get()); if (payload == nullptr) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, - "inventory candidate payload is stale")); + eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, "inventory candidate payload is stale")); auto validObject = validateObject(*payload); if (!validObject) return eve::Result>::failure( @@ -336,15 +294,13 @@ InventoryContainerAdapter::prepare(const eve::container::ContainerSnapshot& expe objectId(payload->stack) != entry.membership.object || (kind_ == InventoryContainerKind::Equipment && slotName(entry.membership.slot).empty())) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, - "inventory candidate object facts are stale")); + eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, "inventory candidate object facts are stale")); staged[static_cast(entry.membership.slot.value())] = payload->stack; } std::unique_ptr prepared = std::make_unique(*this, std::move(staged), *nextRevision); - return eve::Result>::success( - std::move(prepared)); + return eve::Result>::success(std::move(prepared)); } } // namespace eve::inventory diff --git a/src/modules/inventory/ContainerAdapters.h b/src/modules/inventory/ContainerAdapters.h index 4eddfc4f..564178b4 100644 --- a/src/modules/inventory/ContainerAdapters.h +++ b/src/modules/inventory/ContainerAdapters.h @@ -15,7 +15,7 @@ enum class InventoryContainerKind : std::uint8_t { Bag, Equipment }; /** @brief Owning transfer payload for one inventory stack. */ struct InventoryContainerObject final : eve::container::ContainerObjectPayload { - ItemStack stack; + ItemStack stack; std::string equipmentSlot; }; @@ -50,14 +50,12 @@ public: return descriptor_; } [[nodiscard]] eve::Result snapshot() const override; - [[nodiscard]] eve::Result validateInsert( - const eve::container::ContainerObject& object, - std::optional destination, - std::optional ignoredObject = std::nullopt) const override; + [[nodiscard]] eve::Result validateInsert( + const eve::container::ContainerObject& object, std::optional destination, + std::optional ignoredObject = std::nullopt) const override; /** @copydoc eve::container::IContainer::prepare */ [[nodiscard]] eve::Result> prepare( - const eve::container::ContainerSnapshot& expected, - const eve::container::ContainerSnapshot& candidate) override; + const eve::container::ContainerSnapshot& expected, const eve::container::ContainerSnapshot& candidate) override; /** @brief Return the adapter revision for optimistic transfer requests. */ [[nodiscard]] eve::Revision revision() const noexcept { return revision_; } @@ -65,9 +63,9 @@ public: private: class PreparedState; - [[nodiscard]] static eve::container::MembershipId objectId(const ItemStack& stack); + [[nodiscard]] static eve::container::MembershipId objectId(const ItemStack& stack); [[nodiscard]] static eve::container::ContainerObject describe(const ItemStack& stack, - std::string equipmentSlot = {}); + std::string equipmentSlot = {}); /** * @brief Resolve one authoritative inventory slot for a synchronous operation. * @ownership Borrowed from the bound Bag or EquipmentSet; the adapter never deletes it. @@ -79,15 +77,15 @@ private: */ [[nodiscard]] const ItemStack* stackAt(eve::container::SlotIndex slot) const; /** @copydoc InventoryContainerAdapter::stackAt(eve::container::SlotIndex) */ - [[nodiscard]] ItemStack* stackAt(eve::container::SlotIndex slot); - [[nodiscard]] std::string slotName(eve::container::SlotIndex slot) const; + [[nodiscard]] ItemStack* stackAt(eve::container::SlotIndex slot); + [[nodiscard]] std::string slotName(eve::container::SlotIndex slot) const; [[nodiscard]] eve::Result validateObject(const InventoryContainerObject& object) const; eve::container::ContainerDescriptor descriptor_; - InventoryContainerKind kind_; - Bag* bag_ = nullptr; - EquipmentSet* equipment_ = nullptr; - eve::Revision revision_ = eve::Revision(0); + InventoryContainerKind kind_; + Bag* bag_ = nullptr; + EquipmentSet* equipment_ = nullptr; + eve::Revision revision_ = eve::Revision(0); }; } // namespace eve::inventory diff --git a/src/modules/inventory/InventoryResourceAccount.cpp b/src/modules/inventory/InventoryResourceAccount.cpp index 41fa7c8e..aeb50a93 100644 --- a/src/modules/inventory/InventoryResourceAccount.cpp +++ b/src/modules/inventory/InventoryResourceAccount.cpp @@ -23,10 +23,8 @@ using eve::resource::ReservationId; using eve::resource::ResourceId; template -eve::Result failure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { - return eve::Result::failure( - eve::Diagnostic::error(code, std::move(message), std::move(path))); +eve::Result failure(eve::DiagnosticCode code, std::string message, std::string path = {}) { + return eve::Result::failure(eve::Diagnostic::error(code, std::move(message), std::move(path))); } eve::Result validateCost(const CostSpec& cost) { @@ -46,23 +44,20 @@ eve::Status insufficient(const Affordability& affordability) { return eve::Status::failure( eve::StatusCode::Rejected, eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, - "inventory item cost exceeds available quantity", "cost", - std::move(details))); + "inventory item cost exceeds available quantity", "cost", std::move(details))); } eve::Status foreignReservation() { return eve::Status::failure( eve::StatusCode::Conflict, eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "resource reservation belongs to a different inventory account", - "reservation.account")); + "resource reservation belongs to a different inventory account", "reservation.account")); } eve::Status terminalReservation() { return eve::Status::failure( eve::StatusCode::Conflict, - eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "inventory resource reservation is no longer active", + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "inventory resource reservation is no longer active", "reservation")); } @@ -70,15 +65,13 @@ eve::Status unknownReservation() { return eve::Status::failure( eve::StatusCode::NotFound, eve::Diagnostic::error(eve::DiagnosticCode::NotFound, - "inventory resource reservation was not issued by this account", - "reservation")); + "inventory resource reservation was not issued by this account", "reservation")); } eve::Status receiptExhausted() { - return eve::Status::failure( - eve::StatusCode::Failed, - eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, - "inventory resource receipt id exhausted", "receipt")); + return eve::Status::failure(eve::StatusCode::Failed, + eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, + "inventory resource receipt id exhausted", "receipt")); } eve::Status identityUnavailable() { @@ -91,16 +84,14 @@ eve::Status identityUnavailable() { template eve::Result exceptionFailure(std::string_view operation, const std::exception& exception) { return failure(eve::DiagnosticCode::Failed, - std::string(operation) + " failed while staging an inventory candidate: " + - exception.what(), + std::string(operation) + " failed while staging an inventory candidate: " + exception.what(), "inventory.account"); } template eve::Result unknownExceptionFailure(std::string_view operation) { return failure(eve::DiagnosticCode::Failed, - std::string(operation) + " failed while staging an inventory candidate", - "inventory.account"); + std::string(operation) + " failed while staging an inventory candidate", "inventory.account"); } } // namespace @@ -117,13 +108,11 @@ eve::Result InventoryResourceAccount::balanceOf(const ResourceId& const int quantity = bag_.countItem(resource.value()); if (quantity < 0) return failure(eve::DiagnosticCode::InvariantViolation, - "inventory item quantity must not be negative", - resource.value()); + "inventory item quantity must not be negative", resource.value()); return eve::Result::success(static_cast(quantity)); } -eve::Result InventoryResourceAccount::activeReservationsFor( - const ResourceId& resource) const { +eve::Result InventoryResourceAccount::activeReservationsFor(const ResourceId& resource) const { std::int64_t total = 0; for (const auto& [id, record] : reservations_) { (void)id; @@ -132,8 +121,7 @@ eve::Result InventoryResourceAccount::activeReservationsFor( if (item.resource != resource) continue; if (item.amount.value() > std::numeric_limits::max() - total) return failure(eve::DiagnosticCode::InvariantViolation, - "active inventory reservations overflowed", - resource.value()); + "active inventory reservations overflowed", resource.value()); total += item.amount.value(); } } @@ -152,9 +140,7 @@ eve::Result InventoryResourceAccount::canAfford(const CostSpec& c if (!balance) return eve::Result::failure(balance.status()); auto reserved = activeReservationsFor(item.resource); if (!reserved) return eve::Result::failure(reserved.status()); - const std::int64_t available = balance.value() >= reserved.value() - ? balance.value() - reserved.value() - : 0; + const std::int64_t available = balance.value() >= reserved.value() ? balance.value() - reserved.value() : 0; if (available < item.amount.value()) { result.affordable = false; result.shortfalls.push_back({item.resource, item.amount, Amount(available)}); @@ -168,23 +154,19 @@ eve::Result InventoryResourceAccount::reserve(const CostSpec& cost) auto affordability = canAfford(cost); if (!affordability) return eve::Result::failure(affordability.status()); const auto& checked = affordability.value(); - if (!checked.affordable) - return eve::Result::failure(insufficient(checked)); + if (!checked.affordable) return eve::Result::failure(insufficient(checked)); if (nextReservation_.value() == std::numeric_limits::max()) return failure(eve::DiagnosticCode::InvariantViolation, "inventory resource reservation id exhausted", "reservation"); - const ReservationId id = nextReservation_; - const auto [it, inserted] = reservations_.emplace( - id, ReservationRecord{cost, ReservationState::Active}); + const ReservationId id = nextReservation_; + const auto [it, inserted] = reservations_.emplace(id, ReservationRecord{cost, ReservationState::Active}); if (!inserted) - return failure(eve::DiagnosticCode::Conflict, - "inventory resource reservation id was already used", + return failure(eve::DiagnosticCode::Conflict, "inventory resource reservation id was already used", "reservation"); nextReservation_ = ReservationId(id.value() + 1); - return eve::Result::success( - Reservation{accountNonce_, id, it->second.cost}, - eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(Reservation{accountNonce_, id, it->second.cost}, + eve::Status::success(eve::StatusCode::Applied)); } eve::Result InventoryResourceAccount::applyDelta(const CostSpec& cost, bool debit) { @@ -193,18 +175,17 @@ eve::Result InventoryResourceAccount::applyDelta(const CostSpec& cost, boo for (const auto& item : cost.items()) { if (item.amount.value() > std::numeric_limits::max()) return failure(eve::DiagnosticCode::Unsupported, - "inventory item quantity exceeds the Bag integer range", - item.resource.value()); + "inventory item quantity exceeds the Bag integer range", item.resource.value()); } - const auto originalSlots = bag_.slots(); - const int originalInstanceCounter = InventorySystem::instanceCounter(); - const std::size_t originalEventCount = InventorySystem::eventQueue().size(); - const bool oldHookSuppression = InventorySystem::changeHooksSuppressed(); - InventorySystem::changeHooksSuppressed() = true; + const auto originalSlots = bag_.slots(); + const int originalInstanceCounter = InventorySystem::instanceCounter(); + const std::size_t originalEventCount = InventorySystem::eventQueue().size(); + const bool oldHookSuppression = InventorySystem::changeHooksSuppressed(); + InventorySystem::changeHooksSuppressed() = true; auto restore = [&] { - bag_.slots() = originalSlots; + bag_.slots() = originalSlots; InventorySystem::instanceCounter() = originalInstanceCounter; InventorySystem::eventQueue().resize(originalEventCount); InventorySystem::changeHooksSuppressed() = oldHookSuppression; @@ -218,13 +199,11 @@ eve::Result InventoryResourceAccount::applyDelta(const CostSpec& cost, boo if (!balance || balance.value() < amount) { restore(); return failure(eve::DiagnosticCode::PreconditionViolation, - "inventory debit would make item quantity negative", - item.resource.value()); + "inventory debit would make item quantity negative", item.resource.value()); } if (InventorySystem::removeItem(&bag_, item.resource.value(), amount) != amount) { restore(); - return failure(eve::DiagnosticCode::Failed, - "inventory debit was not applied in full", + return failure(eve::DiagnosticCode::Failed, "inventory debit was not applied in full", item.resource.value()); } } else { @@ -238,8 +217,7 @@ eve::Result InventoryResourceAccount::applyDelta(const CostSpec& cost, boo if (InventorySystem::addItem(&bag_, item.resource.value(), amount) != amount) { restore(); return failure(eve::DiagnosticCode::PreconditionViolation, - "inventory credit was not applied in full", - item.resource.value()); + "inventory credit was not applied in full", item.resource.value()); } } } @@ -284,11 +262,10 @@ eve::Result InventoryResourceAccount::debit(const CostSpec& cost) { return eve::Result::failure(receiptExhausted()); Receipt receipt{accountNonce_, nextReceipt_, ReservationId{}, ReceiptOperation::Debit, cost}; - auto applied = applyDelta(cost, true); + auto applied = applyDelta(cost, true); if (!applied) return eve::Result::failure(applied.status()); nextReceipt_ = eve::resource::ReceiptId(nextReceipt_.value() + 1); - return eve::Result::success(std::move(receipt), - eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(std::move(receipt), eve::Status::success(eve::StatusCode::Applied)); } eve::Result InventoryResourceAccount::credit(const CostSpec& cost) { @@ -296,11 +273,10 @@ eve::Result InventoryResourceAccount::credit(const CostSpec& cost) { if (nextReceipt_.value() == std::numeric_limits::max()) return eve::Result::failure(receiptExhausted()); Receipt receipt{accountNonce_, nextReceipt_, ReservationId{}, ReceiptOperation::Credit, cost}; - auto applied = applyDelta(cost, false); + auto applied = applyDelta(cost, false); if (!applied) return eve::Result::failure(applied.status()); nextReceipt_ = eve::resource::ReceiptId(nextReceipt_.value() + 1); - return eve::Result::success(std::move(receipt), - eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(std::move(receipt), eve::Status::success(eve::StatusCode::Applied)); } eve::Result InventoryResourceAccount::activeReservationsAreCovered() const { @@ -324,57 +300,49 @@ eve::Result InventoryResourceAccount::activeReservationsAreCovered() const eve::Result InventoryResourceAccount::commit(const Reservation& reservation) { if (accountNonce_.isZero()) return eve::Result::failure(identityUnavailable()); if (!reservation.isValid()) - return failure(eve::DiagnosticCode::InvalidArgument, - "inventory resource reservation is invalid", "reservation"); + return failure(eve::DiagnosticCode::InvalidArgument, "inventory resource reservation is invalid", + "reservation"); if (reservation.account != accountNonce_) return eve::Result::failure(foreignReservation()); const auto it = reservations_.find(reservation.id); if (it == reservations_.end()) return eve::Result::failure(unknownReservation()); if (it->second.cost != reservation.cost) return failure(eve::DiagnosticCode::Conflict, - "inventory reservation cost does not match its account record", - "reservation.cost"); - if (it->second.state != ReservationState::Active) - return eve::Result::failure(terminalReservation()); + "inventory reservation cost does not match its account record", "reservation.cost"); + if (it->second.state != ReservationState::Active) return eve::Result::failure(terminalReservation()); auto covered = activeReservationsAreCovered(); if (!covered) return eve::Result::failure(covered.status()); if (nextReceipt_.value() == std::numeric_limits::max()) return eve::Result::failure(receiptExhausted()); - Receipt receipt{accountNonce_, nextReceipt_, reservation.id, ReceiptOperation::Debit, - it->second.cost}; - auto applied = applyDelta(it->second.cost, true); + Receipt receipt{accountNonce_, nextReceipt_, reservation.id, ReceiptOperation::Debit, it->second.cost}; + auto applied = applyDelta(it->second.cost, true); if (!applied) return eve::Result::failure(applied.status()); it->second.state = ReservationState::Committed; - nextReceipt_ = eve::resource::ReceiptId(nextReceipt_.value() + 1); - return eve::Result::success(std::move(receipt), - eve::Status::success(eve::StatusCode::Applied)); + nextReceipt_ = eve::resource::ReceiptId(nextReceipt_.value() + 1); + return eve::Result::success(std::move(receipt), eve::Status::success(eve::StatusCode::Applied)); } eve::Result InventoryResourceAccount::rollback(const Reservation& reservation) { if (accountNonce_.isZero()) return eve::Result::failure(identityUnavailable()); if (!reservation.isValid()) - return failure(eve::DiagnosticCode::InvalidArgument, - "inventory resource reservation is invalid", "reservation"); + return failure(eve::DiagnosticCode::InvalidArgument, "inventory resource reservation is invalid", + "reservation"); if (reservation.account != accountNonce_) return eve::Result::failure(foreignReservation()); const auto it = reservations_.find(reservation.id); if (it == reservations_.end()) return eve::Result::failure(unknownReservation()); if (it->second.cost != reservation.cost) return failure(eve::DiagnosticCode::Conflict, - "inventory reservation cost does not match its account record", - "reservation.cost"); - if (it->second.state != ReservationState::Active) - return eve::Result::failure(terminalReservation()); + "inventory reservation cost does not match its account record", "reservation.cost"); + if (it->second.state != ReservationState::Active) return eve::Result::failure(terminalReservation()); it->second.state = ReservationState::RolledBack; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result ItemCostAdapter::itemCost(std::string_view itemId, - std::int64_t quantity) { +eve::Result ItemCostAdapter::itemCost(std::string_view itemId, std::int64_t quantity) { return CostSpec::single(itemId, quantity); } -eve::Result ItemCostAdapter::ammoCost(std::string_view ammoItemId, - std::int64_t quantity) { +eve::Result ItemCostAdapter::ammoCost(std::string_view ammoItemId, std::int64_t quantity) { return CostSpec::single(ammoItemId, quantity); } diff --git a/src/modules/inventory/InventoryResourceAccount.h b/src/modules/inventory/InventoryResourceAccount.h index e3a9fba1..313dfbb1 100644 --- a/src/modules/inventory/InventoryResourceAccount.h +++ b/src/modules/inventory/InventoryResourceAccount.h @@ -30,51 +30,43 @@ public: /** @brief Bind the adapter to one caller-owned inventory bag. */ explicit InventoryResourceAccount(Bag& bag); - InventoryResourceAccount(const InventoryResourceAccount&) = delete; + InventoryResourceAccount(const InventoryResourceAccount&) = delete; InventoryResourceAccount& operator=(const InventoryResourceAccount&) = delete; - InventoryResourceAccount(InventoryResourceAccount&&) = delete; - InventoryResourceAccount& operator=(InventoryResourceAccount&&) = delete; - ~InventoryResourceAccount() override = default; + InventoryResourceAccount(InventoryResourceAccount&&) = delete; + InventoryResourceAccount& operator=(InventoryResourceAccount&&) = delete; + ~InventoryResourceAccount() override = default; /** @copydoc eve::resource::IResourceAccount::canAfford */ [[nodiscard]] eve::Result canAfford( const eve::resource::CostSpec& cost) const override; /** @copydoc eve::resource::IResourceAccount::reserve */ - [[nodiscard]] eve::Result reserve( - const eve::resource::CostSpec& cost) override; + [[nodiscard]] eve::Result reserve(const eve::resource::CostSpec& cost) override; /** @copydoc eve::resource::IResourceAccount::debit */ - [[nodiscard]] eve::Result debit( - const eve::resource::CostSpec& cost) override; + [[nodiscard]] eve::Result debit(const eve::resource::CostSpec& cost) override; /** @copydoc eve::resource::IResourceAccount::credit */ - [[nodiscard]] eve::Result credit( - const eve::resource::CostSpec& cost) override; + [[nodiscard]] eve::Result credit(const eve::resource::CostSpec& cost) override; /** @copydoc eve::resource::IResourceAccount::commit */ - [[nodiscard]] eve::Result commit( - const eve::resource::Reservation& reservation) override; + [[nodiscard]] eve::Result commit(const eve::resource::Reservation& reservation) override; /** @copydoc eve::resource::IResourceAccount::rollback */ - [[nodiscard]] eve::Result rollback( - const eve::resource::Reservation& reservation) override; + [[nodiscard]] eve::Result rollback(const eve::resource::Reservation& reservation) override; private: enum class ReservationState : std::uint8_t { Active, Committed, RolledBack }; struct ReservationRecord { eve::resource::CostSpec cost; - ReservationState state = ReservationState::Active; + ReservationState state = ReservationState::Active; }; - [[nodiscard]] eve::Result balanceOf( - const eve::resource::ResourceId& resource) const; - [[nodiscard]] eve::Result activeReservationsFor( - const eve::resource::ResourceId& resource) const; - [[nodiscard]] eve::Result activeReservationsAreCovered() const; - [[nodiscard]] eve::Result applyDelta(const eve::resource::CostSpec& cost, - bool debit); + [[nodiscard]] eve::Result balanceOf(const eve::resource::ResourceId& resource) const; + [[nodiscard]] eve::Result activeReservationsFor(const eve::resource::ResourceId& resource) const; + [[nodiscard]] eve::Result activeReservationsAreCovered() const; + [[nodiscard]] eve::Result applyDelta(const eve::resource::CostSpec& cost, bool debit); - Bag& bag_; + Bag& bag_; std::unordered_map reservations_; - eve::resource::AccountNonce accountNonce_; - eve::resource::ReservationId nextReservation_{1}; - eve::resource::ReceiptId nextReceipt_{1}; + eve::resource::AccountNonce accountNonce_; + eve::resource::ReservationId nextReservation_{1}; + eve::resource::ReceiptId nextReceipt_{1}; }; /** @@ -92,8 +84,7 @@ public: * @param quantity Positive quantity. * @return Canonical item cost or a structured validation failure. */ - [[nodiscard]] static eve::Result itemCost( - std::string_view itemId, std::int64_t quantity); + [[nodiscard]] static eve::Result itemCost(std::string_view itemId, std::int64_t quantity); /** * @brief Build a positive cost for ammunition stored as an item stack. @@ -101,8 +92,8 @@ public: * @param quantity Positive number of rounds/charges. * @return Canonical ammunition cost or a structured validation failure. */ - [[nodiscard]] static eve::Result ammoCost( - std::string_view ammoItemId, std::int64_t quantity); + [[nodiscard]] static eve::Result ammoCost(std::string_view ammoItemId, + std::int64_t quantity); }; } // namespace eve::inventory diff --git a/src/modules/inventory/InventorySystem.h b/src/modules/inventory/InventorySystem.h index 452c3a3a..124ccc54 100644 --- a/src/modules/inventory/InventorySystem.h +++ b/src/modules/inventory/InventorySystem.h @@ -94,7 +94,7 @@ private: static std::vector &eventQueue(); static int &instanceCounter(); static bool &builtinsReady(); - static bool &changeHooksSuppressed(); + static bool &changeHooksSuppressed(); }; } // namespace eve::inventory diff --git a/src/modules/joystick/sdl/EventSink.cpp b/src/modules/joystick/sdl/EventSink.cpp index c85c5100..a3642747 100644 --- a/src/modules/joystick/sdl/EventSink.cpp +++ b/src/modules/joystick/sdl/EventSink.cpp @@ -6,10 +6,10 @@ #include "common/Capability.h" #include "common/Module.h" -#include "platform_event/PlatformEvent.h" -#include "platform_event/PlatformEventSink.h" #include "joystick/Joystick.h" #include "joystick/Pad.h" +#include "platform_event/PlatformEvent.h" +#include "platform_event/PlatformEventSink.h" #include #include @@ -102,8 +102,8 @@ public: struct Register { Register() { static JoystickEventSink sink; - eve::cap::addListener( - &sink, eve::platform_event::IPlatformEventSink::kInput); + eve::cap::addListener(&sink, + eve::platform_event::IPlatformEventSink::kInput); } } g_register; diff --git a/src/modules/keyboard/sdl/EventSink.cpp b/src/modules/keyboard/sdl/EventSink.cpp index 979deb9c..03632106 100644 --- a/src/modules/keyboard/sdl/EventSink.cpp +++ b/src/modules/keyboard/sdl/EventSink.cpp @@ -5,9 +5,9 @@ #include "common/Capability.h" #include "common/Module.h" +#include "keyboard/Keyboard.h" #include "platform_event/PlatformEvent.h" #include "platform_event/PlatformEventSink.h" -#include "keyboard/Keyboard.h" #include #include @@ -58,8 +58,8 @@ public: struct Register { Register() { static KeyboardEventSink sink; - eve::cap::addListener( - &sink, eve::platform_event::IPlatformEventSink::kInput); + eve::cap::addListener(&sink, + eve::platform_event::IPlatformEventSink::kInput); } } g_register; diff --git a/src/modules/map/ArtifactProvider.cpp b/src/modules/map/ArtifactProvider.cpp index dd3ec2a5..a86ed0c2 100644 --- a/src/modules/map/ArtifactProvider.cpp +++ b/src/modules/map/ArtifactProvider.cpp @@ -19,7 +19,7 @@ const eve::Value* member(const eve::Value::Object& object, const char* name) noe bool readString(const eve::Value::Object& object, const char* name, std::string& output) { const auto* value = member(object, name); - const auto* text = value ? value->getIf() : nullptr; + const auto* text = value ? value->getIf() : nullptr; if (!text || text->empty()) return false; output = *text; return true; @@ -58,52 +58,46 @@ public: owner_ = nullptr; } void rollback() noexcept override { - owner_ = nullptr; + owner_ = nullptr; record_ = {}; } private: MapArtifactProvider* owner_ = nullptr; - MapArtifactRecord record_; + MapArtifactRecord record_; }; eve::Result> MapArtifactProvider::prepare( const eve::artifact::PublicationView& publication) { if (failPrepare_) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Failed, - "map artifact prepare failure was injected")); + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "map artifact prepare failure was injected")); if (publication.id.isNil() || publication.buildKey.empty()) - return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, - "map publication requires identity and build key")); + return eve::Result>::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvalidArgument, "map publication requires identity and build key")); if (find(publication.id) != nullptr) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "map artifact identity is already published")); + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "map artifact identity is already published")); const eve::artifact::PartView* topology = nullptr; for (const auto& part : publication.parts) { - if (part.kind == eve::artifact::PartKind::Grid && - (part.role == "topology" || topology == nullptr)) + if (part.kind == eve::artifact::PartKind::Grid && (part.role == "topology" || topology == nullptr)) topology = ∂ } if (!topology || topology->width <= 0 || topology->height <= 0 || - topology->cells.size() != static_cast(topology->width) * - static_cast(topology->height)) - return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, - "map publication requires a complete grid topology")); + topology->cells.size() != + static_cast(topology->width) * static_cast(topology->height)) + return eve::Result>::failure(eve::Diagnostic::error( + eve::DiagnosticCode::PreconditionViolation, "map publication requires a complete grid topology")); if (nextIndex_ == MapArtifactHandle::invalidIndex) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Failed, - "map artifact handle index exhausted")); + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "map artifact handle index exhausted")); MapArtifactRecord record; - record.id = publication.id; + record.id = publication.id; record.buildKey = std::string(publication.buildKey); - record.handle = MapArtifactHandle(nextIndex_, 1); - record.width = topology->width; - record.height = topology->height; + record.handle = MapArtifactHandle(nextIndex_, 1); + record.width = topology->width; + record.height = topology->height; try { record.cells.assign(topology->cells.begin(), topology->cells.end()); records_.reserve(records_.size() + 1); @@ -111,8 +105,7 @@ eve::Result> MapArtifactProv std::make_unique(*this, std::move(record))); } catch (...) { return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Failed, - "map artifact prepare allocation failed")); + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "map artifact prepare allocation failed")); } } @@ -122,22 +115,22 @@ void MapArtifactProvider::commit(MapArtifactRecord record) noexcept { } const MapArtifactRecord* MapArtifactProvider::find(eve::PersistentId id) const noexcept { - const auto found = std::find_if(records_.begin(), records_.end(), - [id](const auto& record) { return record.id == id; }); + const auto found = + std::find_if(records_.begin(), records_.end(), [id](const auto& record) { return record.id == id; }); return found == records_.end() ? nullptr : &*found; } std::optional MapArtifactProvider::cell(eve::PersistentId id, std::int32_t x, - std::int32_t y) const noexcept { + std::int32_t y) const noexcept { const auto* record = find(id); if (!record || x < 0 || y < 0 || x >= record->width || y >= record->height) return std::nullopt; - return record->cells[static_cast(y) * static_cast(record->width) + - static_cast(x)]; + return record + ->cells[static_cast(y) * static_cast(record->width) + static_cast(x)]; } void MapArtifactProvider::clear() noexcept { records_.clear(); - nextIndex_ = 0; + nextIndex_ = 0; failPrepare_ = false; } @@ -164,74 +157,71 @@ eve::Result MapArtifactProvider::snapshotState() const { state.emplace("records", eve::Value(std::move(records))); return eve::Result::success(eve::Value(std::move(state))); } catch (...) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "map artifact snapshot allocation failed")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "map artifact snapshot allocation failed")); } } eve::Result MapArtifactProvider::restoreState(const eve::Value& state) { if (!records_.empty()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, "map artifact restore requires an empty registry")); - const auto* object = state.getIf(); - const auto* provider = object ? member(*object, "provider") : nullptr; - const auto* providerName = provider ? provider->getIf() : nullptr; + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "map artifact restore requires an empty registry")); + const auto* object = state.getIf(); + const auto* provider = object ? member(*object, "provider") : nullptr; + const auto* providerName = provider ? provider->getIf() : nullptr; const auto* encodedRecords = object ? member(*object, "records") : nullptr; - const auto* records = encodedRecords ? encodedRecords->getIf() : nullptr; - if (!object || !providerName || *providerName != "map.cpu-registry" || - !hasSupportedVersion(*object) || !records) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "invalid map artifact provider state")); + const auto* records = encodedRecords ? encodedRecords->getIf() : nullptr; + if (!object || !providerName || *providerName != "map.cpu-registry" || !hasSupportedVersion(*object) || !records) + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "invalid map artifact provider state")); - std::vector candidate; + std::vector candidate; std::unordered_set identities; - std::uint32_t next = 0; + std::uint32_t next = 0; try { candidate.reserve(records->size()); for (const eve::Value& encoded : *records) { - const auto* item = encoded.getIf(); - std::string idText; - std::string buildKey; - std::uint64_t packed = 0; - std::int64_t width = 0; - std::int64_t height = 0; - const auto* encodedCells = item ? member(*item, "cells") : nullptr; - const auto* cells = encodedCells ? encodedCells->getIf() : nullptr; - if (!item || !readString(*item, "artifactId", idText) || - !readString(*item, "buildKey", buildKey) || !readU64(*item, "handle", packed) || - !readI64(*item, "width", width) || !readI64(*item, "height", height) || - width <= 0 || height <= 0 || !cells) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "invalid map artifact record")); + const auto* item = encoded.getIf(); + std::string idText; + std::string buildKey; + std::uint64_t packed = 0; + std::int64_t width = 0; + std::int64_t height = 0; + const auto* encodedCells = item ? member(*item, "cells") : nullptr; + const auto* cells = encodedCells ? encodedCells->getIf() : nullptr; + if (!item || !readString(*item, "artifactId", idText) || !readString(*item, "buildKey", buildKey) || + !readU64(*item, "handle", packed) || !readI64(*item, "width", width) || + !readI64(*item, "height", height) || width <= 0 || height <= 0 || !cells) + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "invalid map artifact record")); const auto parsedId = eve::PersistentId::parse(idText); - const auto handle = MapArtifactHandle::fromPacked(packed); + const auto handle = MapArtifactHandle::fromPacked(packed); if (!parsedId || parsedId->isNil() || handle.isInvalid() || - width > std::numeric_limits::max() || - height > std::numeric_limits::max() || + width > std::numeric_limits::max() || height > std::numeric_limits::max() || !identities.emplace(*parsedId).second || static_cast(width) * static_cast(height) != cells->size()) return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::ParseError, "invalid map artifact dimensions or identity")); MapArtifactRecord record; - record.id = *parsedId; + record.id = *parsedId; record.buildKey = std::move(buildKey); - record.handle = handle; - record.width = static_cast(width); - record.height = static_cast(height); + record.handle = handle; + record.width = static_cast(width); + record.height = static_cast(height); record.cells.reserve(cells->size()); for (const eve::Value& value : *cells) { const auto* cell = value.getIf(); if (!cell || *cell < 0 || *cell > std::numeric_limits::max()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "invalid map topology cell")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "invalid map topology cell")); record.cells.push_back(static_cast(*cell)); } next = std::max(next, handle.index() + 1u); candidate.push_back(std::move(record)); } } catch (...) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "map artifact restore allocation failed")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "map artifact restore allocation failed")); } records_.swap(candidate); nextIndex_ = next; @@ -243,8 +233,6 @@ MapArtifactProvider& mapArtifactProvider() noexcept { return provider; } -void registerMapArtifactProvider() { - eve::cap::provide(&mapArtifactProvider()); -} +void registerMapArtifactProvider() { eve::cap::provide(&mapArtifactProvider()); } } // namespace eve::map diff --git a/src/modules/map/ArtifactProvider.h b/src/modules/map/ArtifactProvider.h index d2c774b1..fcadf62f 100644 --- a/src/modules/map/ArtifactProvider.h +++ b/src/modules/map/ArtifactProvider.h @@ -23,11 +23,11 @@ using MapArtifactHandle = eve::RuntimeHandle; /** @brief Queryable map record containing the copied semantic topology. */ struct MapArtifactRecord { - eve::PersistentId id; - std::string buildKey; - MapArtifactHandle handle; - std::int32_t width = 0; - std::int32_t height = 0; + eve::PersistentId id; + std::string buildKey; + MapArtifactHandle handle; + std::int32_t width = 0; + std::int32_t height = 0; std::vector cells; }; @@ -64,7 +64,7 @@ public: [[nodiscard]] const MapArtifactRecord* find(eve::PersistentId id) const noexcept; /** @brief Return one semantic cell, or empty for an invalid coordinate. */ [[nodiscard]] std::optional cell(eve::PersistentId id, std::int32_t x, - std::int32_t y) const noexcept; + std::int32_t y) const noexcept; /** @brief Return the number of committed map artifact records. */ [[nodiscard]] std::size_t size() const noexcept { return records_.size(); } /** @brief Remove all records and reset the local handle allocator. */ @@ -74,10 +74,10 @@ public: private: friend class MapArtifactStage; - void commit(MapArtifactRecord record) noexcept; + void commit(MapArtifactRecord record) noexcept; std::vector records_; - std::uint32_t nextIndex_ = 0; - bool failPrepare_ = false; + std::uint32_t nextIndex_ = 0; + bool failPrepare_ = false; }; /** @brief Return the process-owned map artifact provider singleton. */ diff --git a/src/modules/map/Map.cpp b/src/modules/map/Map.cpp index 8ef1afad..64fedc5e 100644 --- a/src/modules/map/Map.cpp +++ b/src/modules/map/Map.cpp @@ -1,17 +1,17 @@ #include "map/Map.h" -#include "map/ArtifactProvider.h" -#include "map/DualGrid.h" -#include "map/TileSystem.h" -#include "map/TileConfig.h" -#include "map/Pathfinder.h" -#include "map/Path.h" -#include "map/FlowField.h" -#include "map/Fov.h" +#include "common/Capability.h" +#include "common/Module.h" #include "graphics/Graphics.h" #include "graphics/RenderSystem.h" -#include "common/Module.h" -#include "common/Capability.h" +#include "map/ArtifactProvider.h" +#include "map/DualGrid.h" +#include "map/FlowField.h" +#include "map/Fov.h" +#include "map/Path.h" +#include "map/Pathfinder.h" +#include "map/TileConfig.h" +#include "map/TileSystem.h" #include diff --git a/src/modules/model3d/ModelLoader.cpp b/src/modules/model3d/ModelLoader.cpp index 64a48572..9ba9690e 100644 --- a/src/modules/model3d/ModelLoader.cpp +++ b/src/modules/model3d/ModelLoader.cpp @@ -102,9 +102,7 @@ public: * @return `false`; ResourceManager reloads the owned CPU ModelData and * keeps its identity stable for existing holders. */ - eve::Result reload(const std::string &) override { - return eve::Result::success(false); - } + eve::Result reload(const std::string &) override { return eve::Result::success(false); } eve::Resource *load(const std::string &key) override { const std::string path = eve::ResourceManager::pathOfKey(key); diff --git a/src/modules/network/Channel.cpp b/src/modules/network/Channel.cpp index 88e5bd72..80d82432 100644 --- a/src/modules/network/Channel.cpp +++ b/src/modules/network/Channel.cpp @@ -1,11 +1,11 @@ #include "network/Channel.h" -#include "network/TcpSocket.h" -#include "network/Network.h" -#include "network/NetTypes.h" -#include "data/ByteData.h" -#include "platform_event/PlatformEvent.h" #include "common/Assert.h" #include "common/Module.h" +#include "data/ByteData.h" +#include "network/NetTypes.h" +#include "network/Network.h" +#include "network/TcpSocket.h" +#include "platform_event/PlatformEvent.h" #include @@ -104,8 +104,8 @@ void Channel::feed(const std::vector& bytes) { c.handle = this; c.bytes = std::move(payload); // Emit directly via event on main thread — feed is called from pump - using eve::platform_event::Variant; using eve::platform_event::Message; + using eve::platform_event::Variant; auto* ev = eve::ModuleManager::getInstance("PlatformEvent"); if (ev) { eve::data::ByteData* bd = nullptr; diff --git a/src/modules/network/NetHost.cpp b/src/modules/network/NetHost.cpp index 63e095c9..5585ee0c 100644 --- a/src/modules/network/NetHost.cpp +++ b/src/modules/network/NetHost.cpp @@ -1,7 +1,7 @@ #include "network/NetHost.h" +#include "data/ByteData.h" #include "network/Network.h" #include "network/UdpSocket.h" -#include "data/ByteData.h" #include "platform_event/PlatformEvent.h" namespace eve::network { diff --git a/src/modules/network/Network.cpp b/src/modules/network/Network.cpp index 82866c3f..62e7a89d 100644 --- a/src/modules/network/Network.cpp +++ b/src/modules/network/Network.cpp @@ -1,15 +1,15 @@ #include "network/Network.h" -#include "network/NetWorker.h" -#include "network/TcpSocket.h" -#include "network/UdpSocket.h" -#include "network/HttpRequest.h" +#include "data/ByteData.h" #include "network/Channel.h" -#include "network/Session.h" -#include "network/NetStream.h" -#include "network/UdpLink.h" +#include "network/HttpRequest.h" #include "network/NetHost.h" #include "network/NetRpc.h" -#include "data/ByteData.h" +#include "network/NetStream.h" +#include "network/NetWorker.h" +#include "network/Session.h" +#include "network/TcpSocket.h" +#include "network/UdpLink.h" +#include "network/UdpSocket.h" #include "platform_event/PlatformEvent.h" #include @@ -314,8 +314,8 @@ void Network::emitCompletion(const NetCompletion& c) { auto* ev = eve::ModuleManager::getInstance("PlatformEvent"); if (!ev) return; - using eve::platform_event::Variant; using eve::platform_event::Message; + using eve::platform_event::Variant; std::vector args; switch (c.type) { diff --git a/src/modules/orders/CommandQueue.cpp b/src/modules/orders/CommandQueue.cpp index f82101b2..a688d446 100644 --- a/src/modules/orders/CommandQueue.cpp +++ b/src/modules/orders/CommandQueue.cpp @@ -17,9 +17,9 @@ namespace eve::orders { namespace { -constexpr size_t noIndex = static_cast(-1); -constexpr std::string_view snapshotSchema = "orders.command_queue"; -constexpr std::int64_t snapshotVersion = 1; +constexpr size_t noIndex = static_cast(-1); +constexpr std::string_view snapshotSchema = "orders.command_queue"; +constexpr std::int64_t snapshotVersion = 1; /** @brief Script-owned proxy; the queue itself remains module-owned. */ struct ScriptCommandQueue { @@ -28,10 +28,8 @@ struct ScriptCommandQueue { }; template -eve::Result orderFailure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { - return eve::Result::failure( - eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "orders")); +eve::Result orderFailure(eve::DiagnosticCode code, std::string message, std::string path = {}) { + return eve::Result::failure(eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "orders")); } template @@ -48,8 +46,7 @@ bool readObjectMember(const eve::Value::Object& object, std::string_view key, T& return true; } else if constexpr (std::is_same_v) { if (!it->second.isDouble() && !it->second.isInt64()) return false; - result = it->second.isDouble() ? it->second.asDouble() - : static_cast(it->second.asInt()); + result = it->second.isDouble() ? it->second.asDouble() : static_cast(it->second.asInt()); return std::isfinite(result); } else { static_assert(std::is_same_v, "unsupported snapshot member type"); @@ -59,10 +56,10 @@ bool readObjectMember(const eve::Value::Object& object, std::string_view key, T& bool readCounter(const eve::Value::Object& object, std::string_view key, std::uint64_t& result) { std::string encoded; if (!readObjectMember(object, key, encoded) || encoded.empty()) return false; - const auto* first = encoded.data(); - const auto* last = first + encoded.size(); - std::uint64_t value = 0; - const auto parsed = std::from_chars(first, last, value); + const auto* first = encoded.data(); + const auto* last = first + encoded.size(); + std::uint64_t value = 0; + const auto parsed = std::from_chars(first, last, value); if (parsed.ec != std::errc{} || parsed.ptr != last) return false; result = value; return true; @@ -99,8 +96,8 @@ bool readIndex(const eve::Value& value, size_t limit, size_t& result) { template eve::Result snapshotFailure(std::string message, std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, std::move(message), std::move(path), {}, "orders.snapshot")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::ParseError, std::move(message), + std::move(path), {}, "orders.snapshot")); } bool generatedOrderId(std::string_view id, std::uint64_t& number) { @@ -140,8 +137,7 @@ eve::Result OrderPayload::setJson(const std::string& key, std::string_view eve::Result OrderPayload::set(const std::string& key, eve::Value value) { if (key.empty()) - return orderFailure(eve::DiagnosticCode::InvalidArgument, - "order payload key must not be empty", "key"); + return orderFailure(eve::DiagnosticCode::InvalidArgument, "order payload key must not be empty", "key"); values_[key] = std::move(value); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } @@ -150,8 +146,7 @@ bool OrderPayload::has(const std::string& key) const { return values_.contains(k eve::Result OrderPayload::erase(const std::string& key) { if (key.empty()) - return orderFailure(eve::DiagnosticCode::InvalidArgument, - "order payload key must not be empty", "key"); + return orderFailure(eve::DiagnosticCode::InvalidArgument, "order payload key must not be empty", "key"); return eve::Result::success(values_.erase(key) != 0); } @@ -191,8 +186,8 @@ std::string CommandQueue::create(const std::string& kind, int priority, double t order.timeoutSeconds = std::max(0.0, timeoutSeconds); orders_.push_back(std::move(order)); const size_t index = orders_.size() - 1; - events_.push_back({{nextSequence_++, {}}, orders_[index].id, orders_[index].kind, - OrderState::Queued, OrderState::Queued}); + events_.push_back( + {{nextSequence_++, {}}, orders_[index].id, orders_[index].kind, OrderState::Queued, OrderState::Queued}); if (activateNow) { active_ = index; transition(orders_[index], OrderState::Active, {}); @@ -282,84 +277,63 @@ void CommandQueue::updateUnchecked(double dtSeconds) { } } -eve::Result CommandQueue::append(const std::string& kind, int priority, - double timeoutSeconds) { +eve::Result CommandQueue::append(const std::string& kind, int priority, double timeoutSeconds) { if (kind.empty()) - return orderFailure(eve::DiagnosticCode::InvalidArgument, - "order kind must not be empty", "kind"); + return orderFailure(eve::DiagnosticCode::InvalidArgument, "order kind must not be empty", "kind"); if (!std::isfinite(timeoutSeconds) || timeoutSeconds < 0.0) return orderFailure(eve::DiagnosticCode::InvalidArgument, - "order timeout must be finite and non-negative", "timeoutSeconds"); + "order timeout must be finite and non-negative", "timeoutSeconds"); const std::string id = appendUnchecked(kind, priority, timeoutSeconds); if (id.empty()) - return orderFailure(eve::DiagnosticCode::Failed, - "order append did not produce an identity"); - return eve::Result::success(std::string(id), - eve::Status::success(eve::StatusCode::Applied)); + return orderFailure(eve::DiagnosticCode::Failed, "order append did not produce an identity"); + return eve::Result::success(std::string(id), eve::Status::success(eve::StatusCode::Applied)); } -eve::Result CommandQueue::replace(const std::string& kind, int priority, - double timeoutSeconds) { +eve::Result CommandQueue::replace(const std::string& kind, int priority, double timeoutSeconds) { if (kind.empty()) - return orderFailure(eve::DiagnosticCode::InvalidArgument, - "order kind must not be empty", "kind"); + return orderFailure(eve::DiagnosticCode::InvalidArgument, "order kind must not be empty", "kind"); if (!std::isfinite(timeoutSeconds) || timeoutSeconds < 0.0) return orderFailure(eve::DiagnosticCode::InvalidArgument, - "order timeout must be finite and non-negative", "timeoutSeconds"); + "order timeout must be finite and non-negative", "timeoutSeconds"); const std::string id = replaceUnchecked(kind, priority, timeoutSeconds); if (id.empty()) - return orderFailure(eve::DiagnosticCode::Failed, - "order replacement did not produce an identity"); - return eve::Result::success(std::string(id), - eve::Status::success(eve::StatusCode::Applied)); + return orderFailure(eve::DiagnosticCode::Failed, "order replacement did not produce an identity"); + return eve::Result::success(std::string(id), eve::Status::success(eve::StatusCode::Applied)); } -eve::Result CommandQueue::interrupt(const std::string& kind, int priority, - double timeoutSeconds) { +eve::Result CommandQueue::interrupt(const std::string& kind, int priority, double timeoutSeconds) { if (kind.empty()) - return orderFailure(eve::DiagnosticCode::InvalidArgument, - "order kind must not be empty", "kind"); + return orderFailure(eve::DiagnosticCode::InvalidArgument, "order kind must not be empty", "kind"); if (!std::isfinite(timeoutSeconds) || timeoutSeconds < 0.0) return orderFailure(eve::DiagnosticCode::InvalidArgument, - "order timeout must be finite and non-negative", "timeoutSeconds"); + "order timeout must be finite and non-negative", "timeoutSeconds"); if (active_ != noIndex && priority < orders_[active_].priority) return orderFailure(eve::DiagnosticCode::Conflict, - "order priority cannot preempt the active order", "priority"); + "order priority cannot preempt the active order", "priority"); const std::string id = interruptUnchecked(kind, priority, timeoutSeconds); if (id.empty()) - return orderFailure(eve::DiagnosticCode::Failed, - "order interrupt did not produce an identity"); - return eve::Result::success(std::string(id), - eve::Status::success(eve::StatusCode::Applied)); + return orderFailure(eve::DiagnosticCode::Failed, "order interrupt did not produce an identity"); + return eve::Result::success(std::string(id), eve::Status::success(eve::StatusCode::Applied)); } eve::Result CommandQueue::complete(const std::string& id) { - if (id.empty()) - return orderFailure(eve::DiagnosticCode::InvalidArgument, - "order id must not be empty", "id"); + if (id.empty()) return orderFailure(eve::DiagnosticCode::InvalidArgument, "order id must not be empty", "id"); if (!completeUnchecked(id)) - return orderFailure(eve::DiagnosticCode::Conflict, - "order is not the active unfinished order", "id"); + return orderFailure(eve::DiagnosticCode::Conflict, "order is not the active unfinished order", "id"); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } eve::Result CommandQueue::fail(const std::string& id, const std::string& reason) { - if (id.empty()) - return orderFailure(eve::DiagnosticCode::InvalidArgument, - "order id must not be empty", "id"); + if (id.empty()) return orderFailure(eve::DiagnosticCode::InvalidArgument, "order id must not be empty", "id"); if (!failUnchecked(id, reason)) - return orderFailure(eve::DiagnosticCode::Conflict, - "order is not the active unfinished order", "id"); + return orderFailure(eve::DiagnosticCode::Conflict, "order is not the active unfinished order", "id"); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } eve::Result CommandQueue::cancel(const std::string& id, const std::string& reason) { - if (id.empty()) - return orderFailure(eve::DiagnosticCode::InvalidArgument, - "order id must not be empty", "id"); + if (id.empty()) return orderFailure(eve::DiagnosticCode::InvalidArgument, "order id must not be empty", "id"); if (!cancelUnchecked(id, reason)) - return orderFailure(eve::DiagnosticCode::NotFound, - "order id is not active or queued", "id"); + return orderFailure(eve::DiagnosticCode::NotFound, "order id is not active or queued", "id"); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } @@ -369,8 +343,8 @@ eve::Result CommandQueue::update(double dtSeconds) { "order update delta must be finite and non-negative", "dtSeconds"); const bool hadActiveOrder = active_ != noIndex; updateUnchecked(dtSeconds); - return eve::Result::success(eve::Status::success( - dtSeconds == 0.0 || !hadActiveOrder ? eve::StatusCode::NoOp : eve::StatusCode::Applied)); + return eve::Result::success( + eve::Status::success(dtSeconds == 0.0 || !hadActiveOrder ? eve::StatusCode::NoOp : eve::StatusCode::Applied)); } void CommandQueue::cancelUnfinished(const std::string& reason) { @@ -432,9 +406,7 @@ void CommandQueue::clearEvents() { events_.clear(); } eve::Result CommandQueue::snapshot() const { eve::Value::Object root; - root.emplace("active", active_ == noIndex - ? eve::Value() - : eve::Value(static_cast(active_))); + root.emplace("active", active_ == noIndex ? eve::Value() : eve::Value(static_cast(active_))); root.emplace("nextId", eve::Value(std::to_string(nextId_))); root.emplace("nextSequence", eve::Value(std::to_string(nextSequence_))); root.emplace("schema", eve::Value(std::string(snapshotSchema))); @@ -487,11 +459,11 @@ eve::Result CommandQueue::snapshot() const { eve::Result CommandQueue::restore(std::string_view json) { auto parsed = eve::Value::fromJson(json); if (!parsed.ok()) return eve::Result::failure(parsed.status()); - eve::Value snapshotValue = std::move(parsed).takeValue(); - const auto* root = snapshotValue.getIf(); + eve::Value snapshotValue = std::move(parsed).takeValue(); + const auto* root = snapshotValue.getIf(); if (!root) return snapshotFailure("command queue snapshot must be an object"); - std::string schema; + std::string schema; std::int64_t version = 0; if (!readObjectMember(*root, "schema", schema) || schema != snapshotSchema || !readObjectMember(*root, "version", version) || version != snapshotVersion) @@ -501,9 +473,8 @@ eve::Result CommandQueue::restore(std::string_view json) { const auto queuedIt = root->find("queued"); const auto activeIt = root->find("active"); const auto eventsIt = root->find("events"); - if (ordersIt == root->end() || queuedIt == root->end() || activeIt == root->end() || - eventsIt == root->end() || !ordersIt->second.isArray() || !queuedIt->second.isArray() || - !eventsIt->second.isArray()) + if (ordersIt == root->end() || queuedIt == root->end() || activeIt == root->end() || eventsIt == root->end() || + !ordersIt->second.isArray() || !queuedIt->second.isArray() || !eventsIt->second.isArray()) return snapshotFailure("command queue snapshot is missing required arrays"); CommandQueue candidate; @@ -512,18 +483,17 @@ eve::Result CommandQueue::restore(std::string_view json) { candidate.nextSequence_ == 0) return snapshotFailure("command queue snapshot has invalid counters", "counters"); - const auto& encodedOrders = *ordersIt->second.getIf(); + const auto& encodedOrders = *ordersIt->second.getIf(); std::set ids; - std::uint64_t largestId = 0; + std::uint64_t largestId = 0; for (const auto& encoded : encodedOrders) { const auto* object = encoded.getIf(); if (!object) return snapshotFailure("order entry must be an object", "orders"); - Order order; + Order order; std::int64_t priority = 0; if (!readObjectMember(*object, "id", order.id) || !readObjectMember(*object, "kind", order.kind) || - !readObjectMember(*object, "priority", priority) || - !readOrderState(*object, "state", order.state) || + !readObjectMember(*object, "priority", priority) || !readOrderState(*object, "state", order.state) || !readObjectMember(*object, "timeoutSeconds", order.timeoutSeconds) || !readObjectMember(*object, "elapsedSeconds", order.elapsedSeconds) || !readObjectMember(*object, "reason", order.reason)) @@ -532,15 +502,15 @@ eve::Result CommandQueue::restore(std::string_view json) { if (payloadIt == object->end() || !payloadIt->second.isObject()) return snapshotFailure("order payload must be an object", "orders.payload"); if (priority < std::numeric_limits::min() || priority > std::numeric_limits::max() || - order.id.empty() || order.kind.empty() || order.timeoutSeconds < 0.0 || - order.elapsedSeconds < 0.0 || !ids.insert(order.id).second) + order.id.empty() || order.kind.empty() || order.timeoutSeconds < 0.0 || order.elapsedSeconds < 0.0 || + !ids.insert(order.id).second) return snapshotFailure("order entry violates an invariant", "orders"); std::uint64_t idNumber = 0; if (!generatedOrderId(order.id, idNumber) || idNumber >= candidate.nextId_) return snapshotFailure("order id does not match the queue counter", "orders.id"); - largestId = std::max(largestId, idNumber); - order.priority = static_cast(priority); + largestId = std::max(largestId, idNumber); + order.priority = static_cast(priority); order.payload.values_ = *payloadIt->second.getIf(); candidate.orders_.push_back(std::move(order)); } @@ -548,7 +518,7 @@ eve::Result CommandQueue::restore(std::string_view json) { return snapshotFailure("nextId must follow every restored order", "nextId"); std::vector queuedMembership(candidate.orders_.size(), false); - const auto& encodedQueued = *queuedIt->second.getIf(); + const auto& encodedQueued = *queuedIt->second.getIf(); for (const auto& encodedIndex : encodedQueued) { size_t index = 0; if (!readIndex(encodedIndex, candidate.orders_.size(), index) || queuedMembership[index] || @@ -577,21 +547,20 @@ eve::Result CommandQueue::restore(std::string_view json) { return snapshotFailure("order state and queue membership disagree", "orders"); } - const auto& encodedEvents = *eventsIt->second.getIf(); + const auto& encodedEvents = *eventsIt->second.getIf(); std::uint64_t previousSequence = 0; for (const auto& encoded : encodedEvents) { const auto* object = encoded.getIf(); if (!object) return snapshotFailure("event entry must be an object", "events"); - OrderEvent event; + OrderEvent event; std::string from; std::string to; - if (!readCounter(*object, "sequence", event.sequence) || - !readObjectMember(*object, "orderId", event.orderId) || + if (!readCounter(*object, "sequence", event.sequence) || !readObjectMember(*object, "orderId", event.orderId) || !readObjectMember(*object, "kind", event.kind) || !readObjectMember(*object, "from", from) || !readObjectMember(*object, "to", to) || !readObjectMember(*object, "reason", event.reason) || !parseOrderState(from, event.from) || !parseOrderState(to, event.to) || event.sequence == 0 || - event.sequence <= previousSequence || event.sequence >= candidate.nextSequence_ || - event.kind.empty() || !ids.contains(event.orderId)) + event.sequence <= previousSequence || event.sequence >= candidate.nextSequence_ || event.kind.empty() || + !ids.contains(event.orderId)) return snapshotFailure("event entry violates an invariant", "events"); previousSequence = event.sequence; candidate.events_.push_back(std::move(event)); @@ -615,8 +584,7 @@ eve::script::Borrowed Orders::resolve(CommandQueueHandleRef refere eve::Result Orders::release(CommandQueueHandleRef reference) { Orders* module = ModuleManager::getInstance("Orders"); if (!module) - return orderFailure(eve::DiagnosticCode::StaleHandle, - "Orders module is no longer loaded", "queue"); + return orderFailure(eve::DiagnosticCode::StaleHandle, "Orders module is no longer loaded", "queue"); return module->queues_.erase(reference); } @@ -629,30 +597,27 @@ bool Orders::isStale(CommandQueueHandleRef reference) noexcept { Module_IMPL(Orders, new Orders()); void Orders::expose(ssq::Table& table) { - const HSQUIRRELVM vm = table.getHandle(); - auto payload = table.addClass( + const HSQUIRRELVM vm = table.getHandle(); + auto payload = table.addClass( "OrderPayload", std::function([]() -> OrderPayload* { return nullptr; }), false); payload.addFunc("setString", &OrderPayload::setString); payload.addFunc("setNumber", &OrderPayload::setNumber); payload.addFunc("setBool", &OrderPayload::setBool); payload.addFunc("setNull", &OrderPayload::setNull); - payload.addFunc("setJson", [vm](OrderPayload* value, const std::string& key, - const std::string& json) { + payload.addFunc("setJson", [vm](OrderPayload* value, const std::string& key, const std::string& json) { if (!value) - return eve::script::projectResult( - vm, orderFailure(eve::DiagnosticCode::InvalidArgument, - "order payload must not be null", "payload")); + return eve::script::projectResult(vm, orderFailure(eve::DiagnosticCode::InvalidArgument, + "order payload must not be null", "payload")); return eve::script::projectResult(vm, value->setJson(key, json)); }); payload.addFunc("has", &OrderPayload::has); payload.addFunc("erase", [vm](OrderPayload* value, const std::string& key) { if (!value) return eve::script::projectResult( - vm, orderFailure(eve::DiagnosticCode::InvalidArgument, - "order payload must not be null", "payload"), + vm, + orderFailure(eve::DiagnosticCode::InvalidArgument, "order payload must not be null", "payload"), [](bool removed) { return eve::Value(removed); }); - return eve::script::projectResult(vm, value->erase(key), - [](bool removed) { return eve::Value(removed); }); + return eve::script::projectResult(vm, value->erase(key), [](bool removed) { return eve::Value(removed); }); }); payload.addFunc("clear", &OrderPayload::clear); payload.addFunc("getJson", &OrderPayload::getJson); @@ -680,64 +645,55 @@ void Orders::expose(ssq::Table& table) { auto queue = table.addClass( "CommandQueue", std::function([]() -> CommandQueue* { return nullptr; }), false); - queue.addFunc("append", [vm](CommandQueue* value, const std::string& kind, - int priority, float timeout) { + queue.addFunc("append", [vm](CommandQueue* value, const std::string& kind, int priority, float timeout) { if (!value) - return eve::script::projectResult( - vm, orderFailure(eve::DiagnosticCode::InvalidArgument, - "order queue must not be null", "queue"), - [](std::string&& id) { return eve::Value(std::move(id)); }); + return eve::script::projectResult(vm, + orderFailure(eve::DiagnosticCode::InvalidArgument, + "order queue must not be null", "queue"), + [](std::string&& id) { return eve::Value(std::move(id)); }); return eve::script::projectResult(vm, value->append(kind, priority, timeout), [](std::string&& id) { return eve::Value(std::move(id)); }); }); - queue.addFunc("replace", [vm](CommandQueue* value, const std::string& kind, - int priority, float timeout) { + queue.addFunc("replace", [vm](CommandQueue* value, const std::string& kind, int priority, float timeout) { if (!value) - return eve::script::projectResult( - vm, orderFailure(eve::DiagnosticCode::InvalidArgument, - "order queue must not be null", "queue"), - [](std::string&& id) { return eve::Value(std::move(id)); }); + return eve::script::projectResult(vm, + orderFailure(eve::DiagnosticCode::InvalidArgument, + "order queue must not be null", "queue"), + [](std::string&& id) { return eve::Value(std::move(id)); }); return eve::script::projectResult(vm, value->replace(kind, priority, timeout), [](std::string&& id) { return eve::Value(std::move(id)); }); }); - queue.addFunc("interrupt", [vm](CommandQueue* value, const std::string& kind, - int priority, float timeout) { + queue.addFunc("interrupt", [vm](CommandQueue* value, const std::string& kind, int priority, float timeout) { if (!value) - return eve::script::projectResult( - vm, orderFailure(eve::DiagnosticCode::InvalidArgument, - "order queue must not be null", "queue"), - [](std::string&& id) { return eve::Value(std::move(id)); }); + return eve::script::projectResult(vm, + orderFailure(eve::DiagnosticCode::InvalidArgument, + "order queue must not be null", "queue"), + [](std::string&& id) { return eve::Value(std::move(id)); }); return eve::script::projectResult(vm, value->interrupt(kind, priority, timeout), [](std::string&& id) { return eve::Value(std::move(id)); }); }); queue.addFunc("complete", [vm](CommandQueue* value, const std::string& id) { if (!value) return eve::script::projectResult( - vm, orderFailure(eve::DiagnosticCode::InvalidArgument, - "order queue must not be null", "queue")); + vm, orderFailure(eve::DiagnosticCode::InvalidArgument, "order queue must not be null", "queue")); return eve::script::projectResult(vm, value->complete(id)); }); - queue.addFunc("fail", [vm](CommandQueue* value, const std::string& id, - const std::string& reason) { + queue.addFunc("fail", [vm](CommandQueue* value, const std::string& id, const std::string& reason) { if (!value) return eve::script::projectResult( - vm, orderFailure(eve::DiagnosticCode::InvalidArgument, - "order queue must not be null", "queue")); + vm, orderFailure(eve::DiagnosticCode::InvalidArgument, "order queue must not be null", "queue")); return eve::script::projectResult(vm, value->fail(id, reason)); }); - queue.addFunc("cancel", [vm](CommandQueue* value, const std::string& id, - const std::string& reason) { + queue.addFunc("cancel", [vm](CommandQueue* value, const std::string& id, const std::string& reason) { if (!value) return eve::script::projectResult( - vm, orderFailure(eve::DiagnosticCode::InvalidArgument, - "order queue must not be null", "queue")); + vm, orderFailure(eve::DiagnosticCode::InvalidArgument, "order queue must not be null", "queue")); return eve::script::projectResult(vm, value->cancel(id, reason)); }); queue.addFunc("update", [vm](CommandQueue* value, float dt) { if (!value) return eve::script::projectResult( - vm, orderFailure(eve::DiagnosticCode::InvalidArgument, - "order queue must not be null", "queue")); + vm, orderFailure(eve::DiagnosticCode::InvalidArgument, "order queue must not be null", "queue")); return eve::script::projectResult(vm, value->update(dt)); }); queue.addFunc("clear", &CommandQueue::clear); @@ -770,39 +726,38 @@ void Orders::expose(ssq::Table& table) { // object (Squirrel release hook); its queue is addressed only by a // generation-qualified RuntimeHandleRef and resolves as Borrowed per call. auto ownedQueue = table.addClass( - "OwnedOrderQueue", std::function([]() -> ScriptCommandQueue* { return nullptr; }), false); + "OwnedOrderQueue", std::function([]() -> ScriptCommandQueue* { return nullptr; }), + false); ownedQueue.addFunc("ownership", [](ScriptCommandQueue*) { return std::string(eve::script::objectSemanticName(eve::script::ObjectSemantic::Owned)); }); - ownedQueue.addFunc("handle", [](ScriptCommandQueue* value) { - return value ? value->reference.packed() : std::uint64_t{0}; - }); - ownedQueue.addFunc("isStale", [](ScriptCommandQueue* value) { - return !value || Orders::isStale(value->reference); - }); + ownedQueue.addFunc("handle", + [](ScriptCommandQueue* value) { return value ? value->reference.packed() : std::uint64_t{0}; }); + ownedQueue.addFunc("isStale", + [](ScriptCommandQueue* value) { return !value || Orders::isStale(value->reference); }); ownedQueue.addFunc("release", [vm](ScriptCommandQueue* value) { if (!value) return eve::script::projectResult( - vm, orderFailure(eve::DiagnosticCode::InvalidArgument, - "owned order queue proxy must not be null", "queue")); + vm, orderFailure(eve::DiagnosticCode::InvalidArgument, "owned order queue proxy must not be null", + "queue")); auto result = Orders::release(value->reference); // Retain the coordinates after release so the same script object can // report a stale handle and a second release returns StaleHandle. return eve::script::projectResult(vm, std::move(result)); }); - ownedQueue.addFunc("append", [vm](ScriptCommandQueue* value, - const std::string& kind, int priority, float timeout) { + ownedQueue.addFunc("append", [vm](ScriptCommandQueue* value, const std::string& kind, int priority, float timeout) { if (!value) return eve::script::projectResult( - vm, orderFailure(eve::DiagnosticCode::InvalidArgument, - "owned order queue proxy must not be null", "queue"), + vm, + orderFailure(eve::DiagnosticCode::InvalidArgument, + "owned order queue proxy must not be null", "queue"), [](std::string&& id) { return eve::Value(std::move(id)); }); auto queueView = Orders::resolve(value->reference); if (!queueView.isBound()) - return eve::script::projectResult( - vm, orderFailure(eve::DiagnosticCode::StaleHandle, - "owned order queue handle is stale", "queue"), - [](std::string&& id) { return eve::Value(std::move(id)); }); + return eve::script::projectResult(vm, + orderFailure(eve::DiagnosticCode::StaleHandle, + "owned order queue handle is stale", "queue"), + [](std::string&& id) { return eve::Value(std::move(id)); }); return eve::script::projectResult(vm, queueView->append(kind, priority, timeout), [](std::string&& id) { return eve::Value(std::move(id)); }); }); @@ -823,52 +778,50 @@ void Orders::expose(ssq::Table& table) { ownedQueue.addFunc("complete", [vm](ScriptCommandQueue* value, const std::string& id) { if (!value) return eve::script::projectResult( - vm, orderFailure(eve::DiagnosticCode::InvalidArgument, - "owned order queue proxy must not be null", "queue")); + vm, orderFailure(eve::DiagnosticCode::InvalidArgument, "owned order queue proxy must not be null", + "queue")); auto queueView = Orders::resolve(value->reference); if (!queueView.isBound()) return eve::script::projectResult( - vm, orderFailure(eve::DiagnosticCode::StaleHandle, - "owned order queue handle is stale", "queue")); + vm, orderFailure(eve::DiagnosticCode::StaleHandle, "owned order queue handle is stale", "queue")); return eve::script::projectResult(vm, queueView->complete(id)); }); ownedQueue.addFunc("snapshot", [vm](ScriptCommandQueue* value) { if (!value) return eve::script::projectResult( - vm, orderFailure(eve::DiagnosticCode::InvalidArgument, - "owned order queue proxy must not be null", "queue"), + vm, + orderFailure(eve::DiagnosticCode::InvalidArgument, + "owned order queue proxy must not be null", "queue"), [](std::string&& text) { return eve::Value(std::move(text)); }); auto queueView = Orders::resolve(value->reference); if (!queueView.isBound()) - return eve::script::projectResult( - vm, orderFailure(eve::DiagnosticCode::StaleHandle, - "owned order queue handle is stale", "queue"), - [](std::string&& text) { return eve::Value(std::move(text)); }); + return eve::script::projectResult(vm, + orderFailure(eve::DiagnosticCode::StaleHandle, + "owned order queue handle is stale", "queue"), + [](std::string&& text) { return eve::Value(std::move(text)); }); return eve::script::projectResult(vm, queueView->snapshot(), [](std::string&& text) { return eve::Value(std::move(text)); }); }); ownedQueue.addFunc("restore", [vm](ScriptCommandQueue* value, const std::string& json) { if (!value) return eve::script::projectResult( - vm, orderFailure(eve::DiagnosticCode::InvalidArgument, - "owned order queue proxy must not be null", "queue")); + vm, orderFailure(eve::DiagnosticCode::InvalidArgument, "owned order queue proxy must not be null", + "queue")); auto queueView = Orders::resolve(value->reference); if (!queueView.isBound()) return eve::script::projectResult( - vm, orderFailure(eve::DiagnosticCode::StaleHandle, - "owned order queue handle is stale", "queue")); + vm, orderFailure(eve::DiagnosticCode::StaleHandle, "owned order queue handle is stale", "queue")); return eve::script::projectResult(vm, queueView->restore(json)); }); ownedQueue.addFunc("update", [vm](ScriptCommandQueue* value, float dt) { if (!value) return eve::script::projectResult( - vm, orderFailure(eve::DiagnosticCode::InvalidArgument, - "owned order queue proxy must not be null", "queue")); + vm, orderFailure(eve::DiagnosticCode::InvalidArgument, "owned order queue proxy must not be null", + "queue")); auto queueView = Orders::resolve(value->reference); if (!queueView.isBound()) return eve::script::projectResult( - vm, orderFailure(eve::DiagnosticCode::StaleHandle, - "owned order queue handle is stale", "queue")); + vm, orderFailure(eve::DiagnosticCode::StaleHandle, "owned order queue handle is stale", "queue")); return eve::script::projectResult(vm, queueView->update(dt)); }); ownedQueue.addFunc("clear", [](ScriptCommandQueue* value) { @@ -889,16 +842,15 @@ void Orders::expose(ssq::Class& cls) { return eve::script::projectStatusResult(vm, reference.status(), false, false); } const auto ref = std::move(reference).takeValue(); - auto object = eve::script::makeOwnedSquirrelInstance( - vm, std::make_unique(ref)); + auto object = + eve::script::makeOwnedSquirrelInstance(vm, std::make_unique(ref)); if (!object) { object.ignore("failed to create owned order queue proxy"); Orders::release(ref).ignore("rollback failed owned order queue allocation"); return eve::script::projectStatusResult(vm, object.status(), false, false); } ssq::Object owned = std::move(object).takeValue(); - auto result = eve::script::projectStatusResult( - vm, eve::Status::success(eve::StatusCode::Applied), true, false); + auto result = eve::script::projectStatusResult(vm, eve::Status::success(eve::StatusCode::Applied), true, false); result.set("value", owned); return result; }); diff --git a/src/modules/orders/CommandQueue.h b/src/modules/orders/CommandQueue.h index ad65999b..a1e52289 100644 --- a/src/modules/orders/CommandQueue.h +++ b/src/modules/orders/CommandQueue.h @@ -1,7 +1,7 @@ #pragma once -#include "common/Module.h" #include "common/BorrowedRef.h" +#include "common/Module.h" #include "common/Result.h" #include "common/Scheduling.h" #include "common/SquirrelOwnership.h" @@ -87,21 +87,20 @@ struct OrderEvent : eve::scheduling::EventMetadata { class CommandQueue { public: /** @brief Appends a command and returns its stable identity or validation failure. */ - [[nodiscard]] eve::Result append( - const std::string& kind, int priority = 0, double timeoutSeconds = 0.0); + [[nodiscard]] eve::Result append(const std::string& kind, int priority = 0, + double timeoutSeconds = 0.0); /** @brief Cancels unfinished commands and starts a replacement. */ - [[nodiscard]] eve::Result replace( - const std::string& kind, int priority = 0, double timeoutSeconds = 0.0); + [[nodiscard]] eve::Result replace(const std::string& kind, int priority = 0, + double timeoutSeconds = 0.0); /** @brief Preempts the active command when priority permits. */ - [[nodiscard]] eve::Result interrupt( - const std::string& kind, int priority = 0, double timeoutSeconds = 0.0); + [[nodiscard]] eve::Result interrupt(const std::string& kind, int priority = 0, + double timeoutSeconds = 0.0); /** @brief Completes the active command. */ [[nodiscard]] eve::Result complete(const std::string& id); /** @brief Fails the active command. */ [[nodiscard]] eve::Result fail(const std::string& id, const std::string& reason); /** @brief Cancels an active or queued command. */ - [[nodiscard]] eve::Result cancel( - const std::string& id, const std::string& reason = "cancelled"); + [[nodiscard]] eve::Result cancel(const std::string& id, const std::string& reason = "cancelled"); /** * @brief Checked simulation update. * @param dtSeconds Non-negative simulation delta; zero is a successful no-op. @@ -203,10 +202,10 @@ private: std::string appendUnchecked(const std::string& kind, int priority, double timeoutSeconds); std::string replaceUnchecked(const std::string& kind, int priority, double timeoutSeconds); std::string interruptUnchecked(const std::string& kind, int priority, double timeoutSeconds); - bool completeUnchecked(const std::string& id); - bool failUnchecked(const std::string& id, const std::string& reason); - bool cancelUnchecked(const std::string& id, const std::string& reason); - void updateUnchecked(double dtSeconds); + bool completeUnchecked(const std::string& id); + bool failUnchecked(const std::string& id, const std::string& reason); + bool cancelUnchecked(const std::string& id, const std::string& reason); + void updateUnchecked(double dtSeconds); std::string create(const std::string& kind, int priority, double timeoutSeconds, bool activateNow); void activateNext(); bool transition(Order& order, OrderState state, const std::string& reason); @@ -239,8 +238,7 @@ public: [[nodiscard]] static eve::Result newQueueHandle(); /** @brief Resolves a live queue as a non-owning observation. */ - [[nodiscard]] static eve::script::Borrowed resolve( - CommandQueueHandleRef reference) noexcept; + [[nodiscard]] static eve::script::Borrowed resolve(CommandQueueHandleRef reference) noexcept; /** @brief Releases a queue owned by the Orders module. */ [[nodiscard]] static eve::Result release(CommandQueueHandleRef reference); diff --git a/src/modules/particles/ParticleEmitter.h b/src/modules/particles/ParticleEmitter.h index 4965dce7..c082e702 100644 --- a/src/modules/particles/ParticleEmitter.h +++ b/src/modules/particles/ParticleEmitter.h @@ -257,8 +257,8 @@ public: bool overflowWarned = false; int activeSeed = 0; /** @brief Last scheduler tick observed by the checked simulation API. */ - eve::SimulationTick simulationTick = eve::SimulationTick::zero(); - bool hasSimulationTick = false; + eve::SimulationTick simulationTick = eve::SimulationTick::zero(); + bool hasSimulationTick = false; /** @brief Temporary automatic-spawn quota set by ParticleSimSystem (-1 = unlimited). */ int spawnQuota = -1; int spawnedThisFrame = 0; @@ -596,7 +596,7 @@ public: * residency and global budgets are enabled. This method is the * CPU emitter-domain adapter. */ - [[nodiscard]] eve::Result advance(const eve::SimulationStep& step); + [[nodiscard]] eve::Result advance(const eve::SimulationStep &step); /** @brief Last scheduler tick consumed by the checked emitter API. */ [[nodiscard]] eve::SimulationTick currentSimulationTick() const noexcept { @@ -668,9 +668,8 @@ void stepEmitterSim(ParticleEmitter::Config &cfg, ParticleEmitter::Sim &sim, flo /** @brief Apply playback speed and optional bounded fixed stepping before simulation. */ float advanceEmitterSim(ParticleEmitter::Config& cfg, ParticleEmitter::Sim& sim, float dt); /** @brief Advance with scheduler-provided Duration/Tick; no local playback scaling. */ - [[nodiscard]] eve::Result advanceEmitterSim(ParticleEmitter::Config& cfg, - ParticleEmitter::Sim& sim, - const eve::SimulationStep& step); +[[nodiscard]] eve::Result advanceEmitterSim(ParticleEmitter::Config &cfg, ParticleEmitter::Sim &sim, + const eve::SimulationStep &step); /** @brief World collision query used by emitters with worldCollision enabled. */ using WorldCollisionFn = bool (*)(float x, float y, float radius, float &nx, float &ny); void setWorldCollisionResolver(WorldCollisionFn fn); diff --git a/src/modules/particles/ParticlePlayback.cpp b/src/modules/particles/ParticlePlayback.cpp index b4b0836b..f1265258 100644 --- a/src/modules/particles/ParticlePlayback.cpp +++ b/src/modules/particles/ParticlePlayback.cpp @@ -37,29 +37,28 @@ float advanceEmitterSim(ParticleEmitter::Config& cfg, ParticleEmitter::Sim& sim, return float(steps) * step; } -eve::Result advanceEmitterSim(ParticleEmitter::Config& cfg, - ParticleEmitter::Sim& sim, +eve::Result advanceEmitterSim(ParticleEmitter::Config& cfg, ParticleEmitter::Sim& sim, const eve::SimulationStep& step) { if (step.delta.nanoseconds() < 0) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "particle simulation delta must be non-negative")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "particle simulation delta must be non-negative")); if (sim.hasSimulationTick && step.tick <= sim.simulationTick) return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::Conflict, "particle simulation tick must advance monotonically")); const float dt = static_cast(step.delta.seconds()); if (!std::isfinite(dt)) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "particle simulation delta is outside float range")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "particle simulation delta is outside float range")); // Scheduler-owned pause, rate and fixed-step policy are already reflected // in the supplied SimulationStep. The legacy emitter playback controls are // intentionally not consulted here. if (!sim.paused && dt > 0.f) stepEmitterSim(cfg, sim, dt); - sim.simulationTick = step.tick; + sim.simulationTick = step.tick; sim.hasSimulationTick = true; - return eve::Result::success(eve::Status::success( - (sim.paused || dt == 0.f) ? eve::StatusCode::NoOp : eve::StatusCode::Applied)); + return eve::Result::success( + eve::Status::success((sim.paused || dt == 0.f) ? eve::StatusCode::NoOp : eve::StatusCode::Applied)); } eve::Result ParticleEmitter::advance(const eve::SimulationStep& step) { diff --git a/src/modules/particles/ParticleRuntime.h b/src/modules/particles/ParticleRuntime.h index ccfa9d72..b3dfcba2 100644 --- a/src/modules/particles/ParticleRuntime.h +++ b/src/modules/particles/ParticleRuntime.h @@ -20,7 +20,7 @@ struct ParticleBudgetConfig { struct ParticleFrameStats { uint64_t frameIndex = 0; /** @brief Simulation tick that produced the latest simulation counters. */ - eve::SimulationTick simulationTick = eve::SimulationTick::zero(); + eve::SimulationTick simulationTick = eve::SimulationTick::zero(); int emittersTotal = 0; int emittersSimulated = 0; int emittersCulled = 0; diff --git a/src/modules/particles/ParticleSystem.cpp b/src/modules/particles/ParticleSystem.cpp index 7b09c880..a328b675 100644 --- a/src/modules/particles/ParticleSystem.cpp +++ b/src/modules/particles/ParticleSystem.cpp @@ -243,8 +243,8 @@ void deactivateResidentGpu(graphics::Graphics* gfx, ParticleEmitter::GpuSim& gpu gpu.deathTimes.clear(); } -eve::Result advanceResidentGpu(graphics::Graphics* gfx, ParticleEmitter* emitter, - const eve::SimulationStep& step, bool legacyControls) { +eve::Result advanceResidentGpu(graphics::Graphics *gfx, ParticleEmitter *emitter, const eve::SimulationStep &step, + bool legacyControls) { auto cfg = emitter->config(); auto sim = emitter->sim(); auto gpu = emitter->gpuSim(); @@ -252,8 +252,7 @@ eve::Result advanceResidentGpu(graphics::Graphics* gfx, ParticleEmitter* e if (gpu->residentHandle == graphics::kInvalidGpuParticleHandle) { gpu->residentHandle = gfx->createGpuParticleEmitter(std::uint32_t(sim->particles.size())); - if (gpu->residentHandle == graphics::kInvalidGpuParticleHandle) - return eve::Result::success(false); + if (gpu->residentHandle == graphics::kInvalidGpuParticleHandle) return eve::Result::success(false); } const bool hadLastPosition = sim->hasLastPos; @@ -330,24 +329,24 @@ eve::Result advanceResidentGpu(graphics::Graphics* gfx, ParticleEmitter* e } // namespace -eve::Result runParticleSimulation(const eve::SimulationStep& step, bool legacyControls) { +eve::Result runParticleSimulation(const eve::SimulationStep &step, bool legacyControls) { if (step.delta.nanoseconds() < 0) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "particle simulation delta must be non-negative")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "particle simulation delta must be non-negative")); const float dt = static_cast(step.delta.seconds()); if (!std::isfinite(dt)) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "particle simulation delta is outside float range")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "particle simulation delta is outside float range")); // Build the complete frame record off to the side. Checked failures must // not publish counters that describe only the prefix of a frame; the // previous published record remains the observable snapshot until the // simulation reaches its commit point below. ParticleFrameStats nextStats; - const uint64_t nextFrame = particleFrameStats().frameIndex + 1; - nextStats.frameIndex = nextFrame; - nextStats.simulationTick = step.tick; - auto &stats = nextStats; + const uint64_t nextFrame = particleFrameStats().frameIndex + 1; + nextStats.frameIndex = nextFrame; + nextStats.simulationTick = step.tick; + auto &stats = nextStats; const auto begin = std::chrono::steady_clock::now(); if (ecs::current()->getManager() == nullptr) { @@ -365,9 +364,9 @@ eve::Result runParticleSimulation(const eve::SimulationStep& step, bool le allEmitters.push_back(cfg->entity); auto sim = cfg->entity->sim(); if (!legacyControls && sim->hasSimulationTick && step.tick <= sim->simulationTick) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, - "particle simulation tick must advance monotonically for every emitter")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, + "particle simulation tick must advance monotonically for every emitter")); if (sim->active || liveParticleCount(cfg->entity) > 0) emitters.push_back(cfg->entity); } } @@ -435,7 +434,7 @@ eve::Result runParticleSimulation(const eve::SimulationStep& step, bool le const bool gpuEligible = emitter->isGpuFeatureSetSupported(); if (gpuSim->residentActive && (!cfg->gpuSimulation || !gpuEligible)) deactivateResidentGpu(gfx, *gpuSim); const bool wantsResident = cfg->gpuSimulation && gpuEligible; - bool handled = false; + bool handled = false; if (wantsResident) { auto gpuAdvance = advanceResidentGpu(gfx, emitter, step, legacyControls); if (!gpuAdvance) return eve::Result::failure(gpuAdvance.status()); @@ -477,8 +476,8 @@ eve::Result runParticleSimulation(const eve::SimulationStep& step, bool le if (!legacyControls) { for (auto *emitter : allEmitters) { - auto sim = emitter->sim(); - sim->simulationTick = step.tick; + auto sim = emitter->sim(); + sim->simulationTick = step.tick; sim->hasSimulationTick = true; } } @@ -486,7 +485,7 @@ eve::Result runParticleSimulation(const eve::SimulationStep& step, bool le return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result ParticleSimSystem::advance(const eve::SimulationStep& step) { +eve::Result ParticleSimSystem::advance(const eve::SimulationStep &step) { return runParticleSimulation(step, false); } diff --git a/src/modules/particles/Particles.cpp b/src/modules/particles/Particles.cpp index 7f2bc507..4ac21cb6 100644 --- a/src/modules/particles/Particles.cpp +++ b/src/modules/particles/Particles.cpp @@ -87,7 +87,7 @@ void Particles::update(float dt) { ParticleLightSystem::update(); } -eve::Result Particles::advance(const eve::SimulationStep& step) { +eve::Result Particles::advance(const eve::SimulationStep &step) { // Config polling is an asset-side concern. Simulation itself is driven // exclusively by the injected scheduler step below. ParticleConfigSystem::poll(); diff --git a/src/modules/physics/ArtifactProvider.cpp b/src/modules/physics/ArtifactProvider.cpp index 7abd2327..b04c94ac 100644 --- a/src/modules/physics/ArtifactProvider.cpp +++ b/src/modules/physics/ArtifactProvider.cpp @@ -28,14 +28,14 @@ namespace eve::physics { */ struct PhysicsArtifactProvider::State { struct RuntimeCollider { - PhysicsArtifactCollider descriptor; + PhysicsArtifactCollider descriptor; std::unique_ptr world; - std::unique_ptr body; + std::unique_ptr body; std::unique_ptr shape; }; std::vector records; - std::size_t pendingStages = 0; + std::size_t pendingStages = 0; }; namespace { @@ -47,7 +47,7 @@ const eve::Value* member(const eve::Value::Object& object, const char* name) noe bool readString(const eve::Value::Object& object, const char* name, std::string& output) { const auto* value = member(object, name); - const auto* text = value ? value->getIf() : nullptr; + const auto* text = value ? value->getIf() : nullptr; if (!text || text->empty()) return false; output = *text; return true; @@ -82,20 +82,17 @@ bool hasSupportedVersion(const eve::Value::Object& object) noexcept { } bool validBounds(const eve::artifact::Bounds& bounds) noexcept { - return bounds.valid && std::isfinite(bounds.minX) && std::isfinite(bounds.minY) && - std::isfinite(bounds.minZ) && std::isfinite(bounds.maxX) && - std::isfinite(bounds.maxY) && std::isfinite(bounds.maxZ) && - bounds.minX <= bounds.maxX && bounds.minY <= bounds.maxY && - bounds.minZ <= bounds.maxZ; + return bounds.valid && std::isfinite(bounds.minX) && std::isfinite(bounds.minY) && std::isfinite(bounds.minZ) && + std::isfinite(bounds.maxX) && std::isfinite(bounds.maxY) && std::isfinite(bounds.maxZ) && + bounds.minX <= bounds.maxX && bounds.minY <= bounds.maxY && bounds.minZ <= bounds.maxZ; } bool readBounds(const eve::Value::Object& object, eve::artifact::Bounds& output) { const auto* encoded = member(object, "bounds"); - const auto* bounds = encoded ? encoded->getIf() : nullptr; - if (!bounds || !readFloat(*bounds, "minX", output.minX) || - !readFloat(*bounds, "minY", output.minY) || !readFloat(*bounds, "minZ", output.minZ) || - !readFloat(*bounds, "maxX", output.maxX) || !readFloat(*bounds, "maxY", output.maxY) || - !readFloat(*bounds, "maxZ", output.maxZ)) + const auto* bounds = encoded ? encoded->getIf() : nullptr; + if (!bounds || !readFloat(*bounds, "minX", output.minX) || !readFloat(*bounds, "minY", output.minY) || + !readFloat(*bounds, "minZ", output.minZ) || !readFloat(*bounds, "maxX", output.maxX) || + !readFloat(*bounds, "maxY", output.maxY) || !readFloat(*bounds, "maxZ", output.maxZ)) return false; output.valid = true; return validBounds(output); @@ -136,8 +133,7 @@ bool readArray(const eve::Value* encoded, std::vector& output) { } } else { const auto* integer = value.getIf(); - if (!integer || *integer < 0 || - static_cast(*integer) > std::numeric_limits::max()) + if (!integer || *integer < 0 || static_cast(*integer) > std::numeric_limits::max()) return false; output.push_back(static_cast(*integer)); } @@ -156,17 +152,15 @@ eve::Value boundsValue(const eve::artifact::Bounds& bounds) { return eve::Value(std::move(value)); } -bool validGeometry(const std::vector& vertices, - const std::vector& indices) { - if (vertices.empty() || vertices.size() % 3u != 0u || indices.empty() || - indices.size() % 3u != 0u || vertices.size() / 3u > - static_cast(std::numeric_limits::max())) +bool validGeometry(const std::vector& vertices, const std::vector& indices) { + if (vertices.empty() || vertices.size() % 3u != 0u || indices.empty() || indices.size() % 3u != 0u || + vertices.size() / 3u > static_cast(std::numeric_limits::max())) return false; for (const float value : vertices) if (!std::isfinite(value)) return false; for (const auto index : indices) - if (index >= vertices.size() / 3u || index > - static_cast(std::numeric_limits::max())) + if (index >= vertices.size() / 3u || + index > static_cast(std::numeric_limits::max())) return false; return true; } @@ -178,8 +172,8 @@ std::vector signedIndices(const std::vector& indice return result; } -eve::Result> prepareFailure( - eve::DiagnosticCode code, std::string message) { +eve::Result> prepareFailure(eve::DiagnosticCode code, + std::string message) { return eve::Result>::failure( eve::Diagnostic::error(code, std::move(message))); } @@ -194,32 +188,33 @@ eve::Result restoreFailure(eve::DiagnosticCode code, std::string message) class PhysicsArtifactStage final : public eve::artifact::PreparedPublication { public: PhysicsArtifactStage(PhysicsArtifactProvider& owner, PhysicsArtifactCollider collider, - std::unique_ptr world, std::unique_ptr body, - std::unique_ptr shape) - : owner_(&owner), collider_(std::move(collider)), world_(std::move(world)), - body_(std::move(body)), shape_(std::move(shape)) {} + std::unique_ptr world, std::unique_ptr body, std::unique_ptr shape) + : owner_(&owner), + collider_(std::move(collider)), + world_(std::move(world)), + body_(std::move(body)), + shape_(std::move(shape)) {} ~PhysicsArtifactStage() override { rollback(); } void commit() noexcept override { if (!owner_) return; - owner_->commit(std::move(collider_), std::move(world_), std::move(body_), - std::move(shape_)); + owner_->commit(std::move(collider_), std::move(world_), std::move(body_), std::move(shape_)); owner_ = nullptr; } void rollback() noexcept override { if (!owner_) return; owner_->release(collider_, world_, body_, shape_); - owner_ = nullptr; + owner_ = nullptr; collider_ = {}; } private: PhysicsArtifactProvider* owner_ = nullptr; - PhysicsArtifactCollider collider_; + PhysicsArtifactCollider collider_; std::unique_ptr world_; - std::unique_ptr body_; + std::unique_ptr body_; std::unique_ptr shape_; }; @@ -230,35 +225,31 @@ PhysicsArtifactProvider::~PhysicsArtifactProvider() { clear(); } eve::Result> PhysicsArtifactProvider::prepare( const eve::artifact::PublicationView& publication) { if (failPrepare_) - return prepareFailure(eve::DiagnosticCode::Failed, - "physics artifact prepare failure was injected"); + return prepareFailure(eve::DiagnosticCode::Failed, "physics artifact prepare failure was injected"); if (publication.schemaVersion != 1 || publication.id.isNil() || publication.buildKey.empty()) - return prepareFailure(publication.schemaVersion != 1 ? eve::DiagnosticCode::UnknownVersion - : eve::DiagnosticCode::InvalidArgument, - "physics publication requires schema version 1, identity and build key"); + return prepareFailure( + publication.schemaVersion != 1 ? eve::DiagnosticCode::UnknownVersion : eve::DiagnosticCode::InvalidArgument, + "physics publication requires schema version 1, identity and build key"); if (find(publication.id) != nullptr) - return prepareFailure(eve::DiagnosticCode::Conflict, - "physics artifact identity is already published"); + return prepareFailure(eve::DiagnosticCode::Conflict, "physics artifact identity is already published"); const eve::artifact::PartView* colliderPart = nullptr; for (const auto& part : publication.parts) { - if (part.kind == eve::artifact::PartKind::Collider && - (part.role == "collider" || colliderPart == nullptr)) + if (part.kind == eve::artifact::PartKind::Collider && (part.role == "collider" || colliderPart == nullptr)) colliderPart = ∂ } if (!colliderPart) return prepareFailure(eve::DiagnosticCode::PreconditionViolation, "physics publication requires a collider part"); if (colliderPart->schemaVersion != 1) - return prepareFailure(eve::DiagnosticCode::UnknownVersion, - "physics collider schema version is unsupported"); + return prepareFailure(eve::DiagnosticCode::UnknownVersion, "physics collider schema version is unsupported"); PhysicsArtifactCollider collider; - collider.id = publication.id; + collider.id = publication.id; collider.buildKey = std::string(publication.buildKey); - collider.shape = "triangle_mesh"; - collider.backend = "box3d"; - collider.bounds = colliderPart->bounds; + collider.shape = "triangle_mesh"; + collider.backend = "box3d"; + collider.bounds = colliderPart->bounds; try { collider.vertices.assign(colliderPart->positions.begin(), colliderPart->positions.end()); collider.indices.assign(colliderPart->indices.begin(), colliderPart->indices.end()); @@ -266,8 +257,7 @@ eve::Result> PhysicsArtifact return prepareFailure(eve::DiagnosticCode::PreconditionViolation, "physics publication requires finite valid triangle geometry"); if (nextIndex_ == PhysicsArtifactHandle::invalidIndex) - return prepareFailure(eve::DiagnosticCode::Failed, - "physics artifact handle index exhausted"); + return prepareFailure(eve::DiagnosticCode::Failed, "physics artifact handle index exhausted"); // Allocate the artifact handle during prepare. A rolled-back stage // burns this slot intentionally; reusing it would make a stale handle @@ -277,47 +267,38 @@ eve::Result> PhysicsArtifact state_->records.reserve(state_->records.size() + state_->pendingStages + 1u); auto world = std::make_unique(0.f, 0.f, 0.f, true); - auto body = std::unique_ptr(world->newBody("static", 0.f, 0.f, 0.f)); + auto body = std::unique_ptr(world->newBody("static", 0.f, 0.f, 0.f)); if (!body) - return prepareFailure(eve::DiagnosticCode::Failed, - "Box3D did not create the generated collider body"); - auto mesh = signedIndices(collider.indices); + return prepareFailure(eve::DiagnosticCode::Failed, "Box3D did not create the generated collider body"); + auto mesh = signedIndices(collider.indices); auto shape = std::unique_ptr(body->newTriangleMeshShape(collider.vertices, mesh)); if (!body || !shape) - return prepareFailure(eve::DiagnosticCode::Failed, - "Box3D did not create the generated collider"); - auto stage = std::make_unique(*this, std::move(collider), - std::move(world), std::move(body), - std::move(shape)); + return prepareFailure(eve::DiagnosticCode::Failed, "Box3D did not create the generated collider"); + auto stage = std::make_unique(*this, std::move(collider), std::move(world), + std::move(body), std::move(shape)); ++state_->pendingStages; - return eve::Result>::success( - std::move(stage)); + return eve::Result>::success(std::move(stage)); } catch (const std::exception& error) { return prepareFailure(eve::DiagnosticCode::Failed, std::string("Box3D collider construction failed: ") + error.what()); } catch (...) { - return prepareFailure(eve::DiagnosticCode::Failed, - "Box3D collider construction failed"); + return prepareFailure(eve::DiagnosticCode::Failed, "Box3D collider construction failed"); } } -void PhysicsArtifactProvider::commit(PhysicsArtifactCollider collider, - std::unique_ptr world, - std::unique_ptr body, - std::unique_ptr shape) noexcept { +void PhysicsArtifactProvider::commit(PhysicsArtifactCollider collider, std::unique_ptr world, + std::unique_ptr body, std::unique_ptr shape) noexcept { State::RuntimeCollider runtime; if (state_->pendingStages > 0) --state_->pendingStages; runtime.descriptor = std::move(collider); - runtime.world = std::move(world); - runtime.body = std::move(body); - runtime.shape = std::move(shape); + runtime.world = std::move(world); + runtime.body = std::move(body); + runtime.shape = std::move(shape); state_->records.push_back(std::move(runtime)); } -void PhysicsArtifactProvider::release(PhysicsArtifactCollider& collider, - std::unique_ptr& world, - std::unique_ptr& body, - std::unique_ptr& shape) noexcept { +void PhysicsArtifactProvider::release(PhysicsArtifactCollider& collider, std::unique_ptr& world, + std::unique_ptr& body, std::unique_ptr& shape) noexcept { // Explicitly tear down the backend before wrapper ownership is released. // This also makes rollback behavior independent of Box3D's wrapper order. if (state_->pendingStages > 0) --state_->pendingStages; @@ -331,17 +312,15 @@ void PhysicsArtifactProvider::release(PhysicsArtifactCollider& collider, } const PhysicsArtifactCollider* PhysicsArtifactProvider::find(eve::PersistentId id) const noexcept { - const auto found = std::find_if( - state_->records.begin(), state_->records.end(), - [id](const auto& record) { return record.descriptor.id == id; }); + const auto found = std::find_if(state_->records.begin(), state_->records.end(), + [id](const auto& record) { return record.descriptor.id == id; }); return found == state_->records.end() ? nullptr : &found->descriptor; } const PhysicsArtifactCollider* PhysicsArtifactProvider::find(PhysicsArtifactHandle handle) const noexcept { if (handle.isInvalid()) return nullptr; - const auto found = std::find_if( - state_->records.begin(), state_->records.end(), - [handle](const auto& record) { return record.descriptor.handle == handle; }); + const auto found = std::find_if(state_->records.begin(), state_->records.end(), + [handle](const auto& record) { return record.descriptor.handle == handle; }); return found == state_->records.end() ? nullptr : &found->descriptor; } @@ -349,20 +328,18 @@ bool PhysicsArtifactProvider::isHandleLive(PhysicsArtifactHandle handle) const n return find(handle) != nullptr; } -PhysicsArtifactRayHit PhysicsArtifactProvider::rayCast(eve::PersistentId id, float ox, float oy, - float oz, float dx, float dy, float dz) const noexcept { +PhysicsArtifactRayHit PhysicsArtifactProvider::rayCast(eve::PersistentId id, float ox, float oy, float oz, float dx, + float dy, float dz) const noexcept { PhysicsArtifactRayHit result; - const auto found = std::find_if( - state_->records.begin(), state_->records.end(), - [id](const auto& record) { return record.descriptor.id == id; }); - if (found == state_->records.end()) - return result; + const auto found = std::find_if(state_->records.begin(), state_->records.end(), + [id](const auto& record) { return record.descriptor.id == id; }); + if (found == state_->records.end()) return result; const auto& runtime = *found; - if (!runtime.world || !runtime.body || !runtime.shape || !runtime.world->isValid() || - !runtime.body->isValid() || !runtime.shape->isValid()) + if (!runtime.world || !runtime.body || !runtime.shape || !runtime.world->isValid() || !runtime.body->isValid() || + !runtime.shape->isValid()) return result; - if (!std::isfinite(ox) || !std::isfinite(oy) || !std::isfinite(oz) || !std::isfinite(dx) || - !std::isfinite(dy) || !std::isfinite(dz)) + if (!std::isfinite(ox) || !std::isfinite(oy) || !std::isfinite(oz) || !std::isfinite(dx) || !std::isfinite(dy) || + !std::isfinite(dz)) return result; const float ex = ox + dx; const float ey = oy + dy; @@ -371,14 +348,14 @@ PhysicsArtifactRayHit PhysicsArtifactProvider::rayCast(eve::PersistentId id, flo try { const int bodyId = runtime.world->rayCast(ox, oy, oz, ex, ey, ez); if (bodyId < 0 || runtime.world->getRayResultCount() <= 0) return result; - result.hit = true; + result.hit = true; result.fraction = runtime.world->getRayResultFraction(0); - result.x = runtime.world->getRayResultX(0); - result.y = runtime.world->getRayResultY(0); - result.z = runtime.world->getRayResultZ(0); - result.normalX = runtime.world->getRayResultNormalX(0); - result.normalY = runtime.world->getRayResultNormalY(0); - result.normalZ = runtime.world->getRayResultNormalZ(0); + result.x = runtime.world->getRayResultX(0); + result.y = runtime.world->getRayResultY(0); + result.z = runtime.world->getRayResultZ(0); + result.normalX = runtime.world->getRayResultNormalX(0); + result.normalY = runtime.world->getRayResultNormalY(0); + result.normalZ = runtime.world->getRayResultNormalZ(0); } catch (...) { // The public query is a noexcept probe. Invalid input or a backend // query failure is represented as a miss; the collider remains intact. @@ -395,14 +372,12 @@ std::string PhysicsArtifactProvider::backendName(eve::PersistentId id) const { } bool PhysicsArtifactProvider::isBox3DBacked(eve::PersistentId id) const noexcept { - const auto found = std::find_if( - state_->records.begin(), state_->records.end(), - [id](const auto& record) { return record.descriptor.id == id; }); + const auto found = std::find_if(state_->records.begin(), state_->records.end(), + [id](const auto& record) { return record.descriptor.id == id; }); if (found == state_->records.end()) return false; const auto& runtime = *found; - return runtime.world && runtime.body && runtime.shape && runtime.world->isValid() && - runtime.body->isValid() && runtime.shape->isValid() && - runtime.shape->getKind() == "triangleMesh"; + return runtime.world && runtime.body && runtime.shape && runtime.world->isValid() && runtime.body->isValid() && + runtime.shape->isValid() && runtime.shape->getKind() == "triangleMesh"; } bool PhysicsArtifactProvider::emptyState() const noexcept { return state_->records.empty(); } @@ -419,7 +394,7 @@ eve::Result PhysicsArtifactProvider::snapshotState() const { eve::Value::Array colliders; colliders.reserve(state_->records.size()); for (const auto& runtime : state_->records) { - const auto& collider = runtime.descriptor; + const auto& collider = runtime.descriptor; eve::Value::Object item; item.emplace("artifactId", eve::Value(collider.id.format())); item.emplace("buildKey", eve::Value(collider.buildKey)); @@ -439,8 +414,8 @@ eve::Result PhysicsArtifactProvider::snapshotState() const { state.emplace("colliders", eve::Value(std::move(colliders))); return eve::Result::success(eve::Value(std::move(state))); } catch (...) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "physics artifact snapshot allocation failed")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "physics artifact snapshot allocation failed")); } } @@ -448,42 +423,38 @@ eve::Result PhysicsArtifactProvider::restoreState(const eve::Value& state) if (state_->pendingStages != 0) return restoreFailure(eve::DiagnosticCode::Conflict, "cannot restore physics artifacts while a publication is staged"); - const auto* object = state.getIf(); - const auto* provider = object ? member(*object, "provider") : nullptr; - const auto* providerName = provider ? provider->getIf() : nullptr; + const auto* object = state.getIf(); + const auto* provider = object ? member(*object, "provider") : nullptr; + const auto* providerName = provider ? provider->getIf() : nullptr; const auto* encodedColliders = object ? member(*object, "colliders") : nullptr; - const auto* colliders = encodedColliders ? encodedColliders->getIf() : nullptr; - if (!object || !providerName || *providerName != "physics.box3d-collider" || - !hasSupportedVersion(*object) || !colliders) - return restoreFailure(eve::DiagnosticCode::ParseError, - "invalid physics artifact provider state"); + const auto* colliders = encodedColliders ? encodedColliders->getIf() : nullptr; + if (!object || !providerName || *providerName != "physics.box3d-collider" || !hasSupportedVersion(*object) || + !colliders) + return restoreFailure(eve::DiagnosticCode::ParseError, "invalid physics artifact provider state"); - std::vector candidate; + std::vector candidate; std::unordered_set identities; - std::uint32_t next = nextIndex_; + std::uint32_t next = nextIndex_; try { candidate.reserve(colliders->size()); for (const eve::Value& encoded : *colliders) { - const auto* item = encoded.getIf(); - std::string idText; - std::string buildKey; - std::string shape; - std::string backend; - std::uint64_t packed = 0; + const auto* item = encoded.getIf(); + std::string idText; + std::string buildKey; + std::string shape; + std::string backend; + std::uint64_t packed = 0; PhysicsArtifactCollider collider; - if (!item || !readString(*item, "artifactId", idText) || - !readString(*item, "buildKey", buildKey) || !readString(*item, "shape", shape) || - !readString(*item, "backend", backend) || !readU64(*item, "handle", packed) || - !readBounds(*item, collider.bounds) || + if (!item || !readString(*item, "artifactId", idText) || !readString(*item, "buildKey", buildKey) || + !readString(*item, "shape", shape) || !readString(*item, "backend", backend) || + !readU64(*item, "handle", packed) || !readBounds(*item, collider.bounds) || !readArray(member(*item, "vertices"), collider.vertices) || !readArray(member(*item, "indices"), collider.indices)) - return restoreFailure(eve::DiagnosticCode::ParseError, - "invalid physics artifact collider state"); - const auto parsedId = eve::PersistentId::parse(idText); + return restoreFailure(eve::DiagnosticCode::ParseError, "invalid physics artifact collider state"); + const auto parsedId = eve::PersistentId::parse(idText); const auto persistedHandle = PhysicsArtifactHandle::fromPacked(packed); - if (!parsedId || parsedId->isNil() || persistedHandle.isInvalid() || - backend != "box3d" || shape != "triangle_mesh" || - !identities.emplace(*parsedId).second || + if (!parsedId || parsedId->isNil() || persistedHandle.isInvalid() || backend != "box3d" || + shape != "triangle_mesh" || !identities.emplace(*parsedId).second || !validGeometry(collider.vertices, collider.indices)) return restoreFailure(eve::DiagnosticCode::Unsupported, "physics artifact state has unsupported backend or geometry"); @@ -491,36 +462,32 @@ eve::Result PhysicsArtifactProvider::restoreState(const eve::Value& state) if (next == PhysicsArtifactHandle::invalidIndex) return restoreFailure(eve::DiagnosticCode::Failed, "physics artifact handle index exhausted during restore"); - collider.id = *parsedId; + collider.id = *parsedId; collider.buildKey = std::move(buildKey); - collider.shape = std::move(shape); - collider.backend = std::move(backend); + collider.shape = std::move(shape); + collider.backend = std::move(backend); // Runtime handles are deliberately regenerated. This preserves // stale detection across clear/restore and keeps process-local // coordinates out of the persistent contract. collider.handle = PhysicsArtifactHandle(next++, 1u); auto world = std::make_unique(0.f, 0.f, 0.f, true); - auto body = std::unique_ptr(world->newBody("static", 0.f, 0.f, 0.f)); + auto body = std::unique_ptr(world->newBody("static", 0.f, 0.f, 0.f)); if (!body) - return restoreFailure(eve::DiagnosticCode::Failed, - "Box3D did not recreate the restored collider body"); - const auto mesh = signedIndices(collider.indices); - auto shapeObject = - std::unique_ptr(body->newTriangleMeshShape(collider.vertices, mesh)); + return restoreFailure(eve::DiagnosticCode::Failed, "Box3D did not recreate the restored collider body"); + const auto mesh = signedIndices(collider.indices); + auto shapeObject = std::unique_ptr(body->newTriangleMeshShape(collider.vertices, mesh)); if (!body || !shapeObject) - return restoreFailure(eve::DiagnosticCode::Failed, - "Box3D did not recreate a restored collider"); + return restoreFailure(eve::DiagnosticCode::Failed, "Box3D did not recreate a restored collider"); State::RuntimeCollider runtime; runtime.descriptor = std::move(collider); - runtime.world = std::move(world); - runtime.body = std::move(body); - runtime.shape = std::move(shapeObject); + runtime.world = std::move(world); + runtime.body = std::move(body); + runtime.shape = std::move(shapeObject); candidate.push_back(std::move(runtime)); } } catch (const std::exception& error) { - return restoreFailure(eve::DiagnosticCode::Failed, - std::string("Box3D restore failed: ") + error.what()); + return restoreFailure(eve::DiagnosticCode::Failed, std::string("Box3D restore failed: ") + error.what()); } catch (...) { return restoreFailure(eve::DiagnosticCode::Failed, "Box3D restore failed"); } diff --git a/src/modules/physics/ArtifactProvider.h b/src/modules/physics/ArtifactProvider.h index 0bc14c30..e23a087f 100644 --- a/src/modules/physics/ArtifactProvider.h +++ b/src/modules/physics/ArtifactProvider.h @@ -27,26 +27,26 @@ using PhysicsArtifactHandle = eve::RuntimeHandle; /** @brief Backend-neutral ray-query result used for parity tests. */ struct PhysicsArtifactRayHit { - bool hit = false; + bool hit = false; float fraction = 0.f; - float x = 0.f; - float y = 0.f; - float z = 0.f; - float normalX = 0.f; - float normalY = 0.f; - float normalZ = 0.f; + float x = 0.f; + float y = 0.f; + float z = 0.f; + float normalX = 0.f; + float normalY = 0.f; + float normalZ = 0.f; }; /** @brief Queryable CPU collider resource owned by the physics module. */ struct PhysicsArtifactCollider { - eve::PersistentId id; - std::string buildKey; + eve::PersistentId id; + std::string buildKey; PhysicsArtifactHandle handle; - std::string shape; + std::string shape; /** @brief Backend family that owns the live shape, normally `box3d`. */ - std::string backend = "box3d"; - eve::artifact::Bounds bounds; - std::vector vertices; + std::string backend = "box3d"; + eve::artifact::Bounds bounds; + std::vector vertices; std::vector indices; }; @@ -80,7 +80,8 @@ public: * @brief Find a committed collider by persistent identity. * @return Borrowed nullable collider owned by this provider. * @ownership PhysicsArtifactProvider owns committed collider records; callers must not delete or mutate the result. - * @lifetime Valid until clear(), restoreState(), or provider destruction; retain PhysicsArtifactHandle across frames. + * @lifetime Valid until clear(), restoreState(), or provider destruction; retain PhysicsArtifactHandle across + * frames. * @thread Call on the physics provider's owning thread. * @reentrancy The lookup invokes no callbacks and is invalid across provider mutation. */ @@ -97,8 +98,8 @@ public: /** @brief Return whether a generation-qualified artifact handle is live. */ [[nodiscard]] bool isHandleLive(PhysicsArtifactHandle handle) const noexcept; /** @brief Ray query one collider through its live Box3D world. */ - [[nodiscard]] PhysicsArtifactRayHit rayCast(eve::PersistentId id, float ox, float oy, float oz, - float dx, float dy, float dz) const noexcept; + [[nodiscard]] PhysicsArtifactRayHit rayCast(eve::PersistentId id, float ox, float oy, float oz, float dx, float dy, + float dz) const noexcept; /** @brief Return the number of committed physics colliders. */ [[nodiscard]] std::size_t size() const noexcept; /** @brief Return the backend family for a committed collider, or empty. */ @@ -113,13 +114,13 @@ public: private: struct State; friend class PhysicsArtifactStage; - void commit(PhysicsArtifactCollider collider, std::unique_ptr world, - std::unique_ptr body, std::unique_ptr shape) noexcept; - void release(PhysicsArtifactCollider& collider, std::unique_ptr& world, - std::unique_ptr& body, std::unique_ptr& shape) noexcept; + void commit(PhysicsArtifactCollider collider, std::unique_ptr world, std::unique_ptr body, + std::unique_ptr shape) noexcept; + void release(PhysicsArtifactCollider& collider, std::unique_ptr& world, std::unique_ptr& body, + std::unique_ptr& shape) noexcept; std::unique_ptr state_; - std::uint32_t nextIndex_ = 0; - bool failPrepare_ = false; + std::uint32_t nextIndex_ = 0; + bool failPrepare_ = false; }; /** @brief Return the process-owned physics artifact provider singleton. */ diff --git a/src/modules/physics/Body3D.cpp b/src/modules/physics/Body3D.cpp index a68f99e1..e4709dae 100644 --- a/src/modules/physics/Body3D.cpp +++ b/src/modules/physics/Body3D.cpp @@ -687,8 +687,7 @@ Shape3D *Body3D::newBoxShape(float width, float height, float depth, float densi b3ShapeDef def = makeShapeDef(density, friction, restitution); b3ShapeId id = b3CreateHullShape(bodyId_, &def, &box.base); - auto *shape = new Shape3D(world_, this, id, world_->nextShapeRuntimeHandle(), - Shape3D::Kind::Box, hx, hy, hz); + auto *shape = new Shape3D(world_, this, id, world_->nextShapeRuntimeHandle(), Shape3D::Kind::Box, hx, hy, hz); b3Shape_SetUserData(id, shape); world_->shapes_.insert(shape); return shape; @@ -705,8 +704,8 @@ Shape3D *Body3D::newSphereShape(float radius, float density, float friction, flo b3ShapeDef def = makeShapeDef(density, friction, restitution); b3ShapeId id = b3CreateSphereShape(bodyId_, &def, &sphere); - auto *shape = new Shape3D(world_, this, id, world_->nextShapeRuntimeHandle(), - Shape3D::Kind::Sphere, radius, 0.f, 0.f); + auto *shape = + new Shape3D(world_, this, id, world_->nextShapeRuntimeHandle(), Shape3D::Kind::Sphere, radius, 0.f, 0.f); b3Shape_SetUserData(id, shape); world_->shapes_.insert(shape); return shape; @@ -727,8 +726,8 @@ Shape3D *Body3D::newCapsuleShape(float height, float radius, float density, floa b3ShapeDef def = makeShapeDef(density, friction, restitution); b3ShapeId id = b3CreateCapsuleShape(bodyId_, &def, &capsule); - auto *shape = new Shape3D(world_, this, id, world_->nextShapeRuntimeHandle(), - Shape3D::Kind::Capsule, half, radius, 0.f); + auto *shape = + new Shape3D(world_, this, id, world_->nextShapeRuntimeHandle(), Shape3D::Kind::Capsule, half, radius, 0.f); b3Shape_SetUserData(id, shape); world_->shapes_.insert(shape); return shape; @@ -743,9 +742,8 @@ Shape3D *Body3D::newConvexHullShape(const std::vector &vertices, int maxV b3ShapeId id = b3CreateHullShape(bodyId_, &def, hull); b3DestroyHull(hull); - auto *shape = new Shape3D(world_, this, id, world_->nextShapeRuntimeHandle(), - Shape3D::Kind::ConvexHull, 0.f, 0.f, 0.f, - vertices, maxVertices); + auto *shape = new Shape3D(world_, this, id, world_->nextShapeRuntimeHandle(), Shape3D::Kind::ConvexHull, 0.f, 0.f, + 0.f, vertices, maxVertices); b3Shape_SetUserData(id, shape); world_->shapes_.insert(shape); return shape; @@ -768,10 +766,9 @@ Shape3D *Body3D::newTriangleMeshShape(const std::vector &vertices, b3DestroyMesh(mesh); throw eve::Exception("Body3D.newTriangleMeshShape: Box3D rejected the mesh shape"); } - auto *shape = new Shape3D(world_, this, id, world_->nextShapeRuntimeHandle(), - Shape3D::Kind::TriangleMesh, 0.f, 0.f, 0.f, - {}, 64, vertices, indices, mesh, weldVertices, weldTolerance, - identifyEdges, useMedianSplit); + auto *shape = + new Shape3D(world_, this, id, world_->nextShapeRuntimeHandle(), Shape3D::Kind::TriangleMesh, 0.f, 0.f, 0.f, {}, + 64, vertices, indices, mesh, weldVertices, weldTolerance, identifyEdges, useMedianSplit); b3Shape_SetUserData(id, shape); world_->shapes_.insert(shape); return shape; @@ -794,11 +791,9 @@ Shape3D *Body3D::newHeightFieldShape(int countX, int countZ, float cellSizeX, b3DestroyHeightField(heightData); throw eve::Exception("Body3D.newHeightFieldShape: Box3D rejected the height field"); } - auto *shape = new Shape3D( - world_, this, id, world_->nextShapeRuntimeHandle(), Shape3D::Kind::HeightField, - 0.f, 0.f, 0.f, {}, 64, {}, {}, nullptr, - true, 0.001f, true, false, heights, countX, countZ, cellSizeX, cellSizeZ, globalMin, - globalMax, clockwiseWinding, heightData); + auto *shape = new Shape3D(world_, this, id, world_->nextShapeRuntimeHandle(), Shape3D::Kind::HeightField, 0.f, 0.f, + 0.f, {}, 64, {}, {}, nullptr, true, 0.001f, true, false, heights, countX, countZ, + cellSizeX, cellSizeZ, globalMin, globalMax, clockwiseWinding, heightData); b3Shape_SetUserData(id, shape); world_->shapes_.insert(shape); return shape; diff --git a/src/modules/physics/Cloth.cpp b/src/modules/physics/Cloth.cpp index ff0a9fd4..f9451926 100644 --- a/src/modules/physics/Cloth.cpp +++ b/src/modules/physics/Cloth.cpp @@ -570,39 +570,34 @@ void Cloth::updateSubsteps(float dt, int substeps) { interactStrength_ = 0.f; } -eve::Result Cloth::step(const eve::SimulationStep& stepValue, - const SimulationSettings& settings) { +eve::Result Cloth::step(const eve::SimulationStep &stepValue, const SimulationSettings &settings) { if (destroyed_) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, - "Cannot step a destroyed cloth", "physics.cloth.step")); + eve::DiagnosticCode::PreconditionViolation, "Cannot step a destroyed cloth", "physics.cloth.step")); auto valid = detail::validateSimulationStep(stepValue, settings, observation_); if (!valid) return valid; auto next = detail::advanceSimulationObservation(observation_, stepValue); if (!next) return eve::Result::failure(next.status()); try { updateSubsteps(static_cast(stepValue.delta.seconds()), settings.subStepCount); - } catch (const std::exception& error) { + } catch (const std::exception &error) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, - std::string("Cloth step failed: ") + error.what(), "physics.cloth.step")); + eve::DiagnosticCode::Failed, std::string("Cloth step failed: ") + error.what(), "physics.cloth.step")); } catch (...) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "Cloth step failed with an unknown exception", - "physics.cloth.step")); + eve::DiagnosticCode::Failed, "Cloth step failed with an unknown exception", "physics.cloth.step")); } observation_ = std::move(next).takeValue(); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result Cloth::restoreObservation(const SimulationObservation& observation) { - auto valid = detail::validateSimulationObservation( - observation, "physics.cloth.restoreObservation"); +eve::Result Cloth::restoreObservation(const SimulationObservation &observation) { + auto valid = detail::validateSimulationObservation(observation, "physics.cloth.restoreObservation"); if (!valid) return valid; if (destroyed_) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, - "Cannot restore a destroyed cloth", "physics.cloth.restoreObservation")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, + "Cannot restore a destroyed cloth", + "physics.cloth.restoreObservation")); observation_ = observation; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } diff --git a/src/modules/physics/Cloth.h b/src/modules/physics/Cloth.h index d9228c3b..d5742e9c 100644 --- a/src/modules/physics/Cloth.h +++ b/src/modules/physics/Cloth.h @@ -43,24 +43,18 @@ public: /** @brief Advances cloth with the shared ticked backend contract. */ [[nodiscard("check the cloth step outcome")]] - eve::Result step(const eve::SimulationStep& step, - const SimulationSettings& settings) override; + eve::Result step(const eve::SimulationStep &step, const SimulationSettings &settings) override; /** @brief Returns completed tick/time observables. */ - [[nodiscard]] SimulationObservation observation() const noexcept override { - return observation_; - } + [[nodiscard]] SimulationObservation observation() const noexcept override { return observation_; } /** @brief Identifies this production CPU cloth backend. */ - [[nodiscard]] SimulationBackendKind kind() const noexcept override { - return SimulationBackendKind::Cpu; - } + [[nodiscard]] SimulationBackendKind kind() const noexcept override { return SimulationBackendKind::Cpu; } /** @brief CPU cloth uses bounded floating-point determinism. */ [[nodiscard]] SimulationDeterminism determinism() const noexcept override { return SimulationDeterminism::ToleranceBounded; } /** @brief Restores tick/progress metadata after an owner-level restore. */ [[nodiscard("check cloth observation restore")]] - eve::Result restoreObservation( - const SimulationObservation& observation) override; + eve::Result restoreObservation(const SimulationObservation &observation) override; void setGravity(float gx, float gy); float getGravityX() const { return gravityX_; } @@ -200,7 +194,7 @@ private: }; void rebuildLinks(); - void updateSubsteps(float dt, int substeps); + void updateSubsteps(float dt, int substeps); void integrate(float dt); void solveConstraints(); void solveFoldConstraint(); diff --git a/src/modules/physics/Cloth3D.cpp b/src/modules/physics/Cloth3D.cpp index 7f433873..5864180f 100644 --- a/src/modules/physics/Cloth3D.cpp +++ b/src/modules/physics/Cloth3D.cpp @@ -1068,39 +1068,34 @@ void Cloth3D::updateSubsteps(float dt, int substeps) { interactStrength_ = 0.f; } -eve::Result Cloth3D::step(const eve::SimulationStep& stepValue, - const SimulationSettings& settings) { +eve::Result Cloth3D::step(const eve::SimulationStep &stepValue, const SimulationSettings &settings) { if (destroyed_) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, - "Cannot step a destroyed cloth", "physics.cloth3d.step")); + eve::DiagnosticCode::PreconditionViolation, "Cannot step a destroyed cloth", "physics.cloth3d.step")); auto valid = detail::validateSimulationStep(stepValue, settings, observation_); if (!valid) return valid; auto next = detail::advanceSimulationObservation(observation_, stepValue); if (!next) return eve::Result::failure(next.status()); try { updateSubsteps(static_cast(stepValue.delta.seconds()), settings.subStepCount); - } catch (const std::exception& error) { + } catch (const std::exception &error) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, - std::string("Cloth3D step failed: ") + error.what(), "physics.cloth3d.step")); + eve::DiagnosticCode::Failed, std::string("Cloth3D step failed: ") + error.what(), "physics.cloth3d.step")); } catch (...) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "Cloth3D step failed with an unknown exception", - "physics.cloth3d.step")); + eve::DiagnosticCode::Failed, "Cloth3D step failed with an unknown exception", "physics.cloth3d.step")); } observation_ = std::move(next).takeValue(); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result Cloth3D::restoreObservation(const SimulationObservation& observation) { - auto valid = detail::validateSimulationObservation( - observation, "physics.cloth3d.restoreObservation"); +eve::Result Cloth3D::restoreObservation(const SimulationObservation &observation) { + auto valid = detail::validateSimulationObservation(observation, "physics.cloth3d.restoreObservation"); if (!valid) return valid; if (destroyed_) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, - "Cannot restore a destroyed cloth", "physics.cloth3d.restoreObservation")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, + "Cannot restore a destroyed cloth", + "physics.cloth3d.restoreObservation")); observation_ = observation; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } diff --git a/src/modules/physics/Cloth3D.h b/src/modules/physics/Cloth3D.h index 500a7290..922f96f4 100644 --- a/src/modules/physics/Cloth3D.h +++ b/src/modules/physics/Cloth3D.h @@ -45,24 +45,18 @@ public: /** @brief Advances cloth with the shared ticked backend contract. */ [[nodiscard("check the cloth step outcome")]] - eve::Result step(const eve::SimulationStep& step, - const SimulationSettings& settings) override; + eve::Result step(const eve::SimulationStep &step, const SimulationSettings &settings) override; /** @brief Returns completed tick/time observables. */ - [[nodiscard]] SimulationObservation observation() const noexcept override { - return observation_; - } + [[nodiscard]] SimulationObservation observation() const noexcept override { return observation_; } /** @brief Identifies this production CPU cloth backend. */ - [[nodiscard]] SimulationBackendKind kind() const noexcept override { - return SimulationBackendKind::Cpu; - } + [[nodiscard]] SimulationBackendKind kind() const noexcept override { return SimulationBackendKind::Cpu; } /** @brief CPU cloth uses bounded floating-point determinism. */ [[nodiscard]] SimulationDeterminism determinism() const noexcept override { return SimulationDeterminism::ToleranceBounded; } /** @brief Restores tick/progress metadata after an owner-level restore. */ [[nodiscard("check cloth observation restore")]] - eve::Result restoreObservation( - const SimulationObservation& observation) override; + eve::Result restoreObservation(const SimulationObservation &observation) override; void setGravity(float gx, float gy, float gz); float getGravityX() const { return gravityX_; } @@ -214,7 +208,7 @@ private: }; void rebuildLinks(); - void updateSubsteps(float dt, int substeps); + void updateSubsteps(float dt, int substeps); void rebuildTriangles(); void buildLinkKeys(); bool areLinked(int a, int b) const; diff --git a/src/modules/physics/ClothGPU.cpp b/src/modules/physics/ClothGPU.cpp index 479b665c..e9a8f3f0 100644 --- a/src/modules/physics/ClothGPU.cpp +++ b/src/modules/physics/ClothGPU.cpp @@ -536,13 +536,13 @@ void ClothGPU::update(float dt) { eve::Result ClothGPU::stepGpu(float dt, int substeps) { if (destroyed_ || !shader_ || !gpgpu_ || !seq_) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, - "GPU cloth resources are not available", "physics.clothGpu.step")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, + "GPU cloth resources are not available", + "physics.clothGpu.step")); if (substeps < 1 || substeps > 1024) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "GPU cloth substep count must be in [1, 1024]", "physics.clothGpu.step.substeps")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "GPU cloth substep count must be in [1, 1024]", + "physics.clothGpu.step.substeps")); const int count = getParticleCount(); ensureHashBuffers(); @@ -622,42 +622,36 @@ eve::Result ClothGPU::stepGpu(float dt, int substeps) { return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result ClothGPU::step(const eve::SimulationStep& stepValue, - const SimulationSettings& settings) { +eve::Result ClothGPU::step(const eve::SimulationStep &stepValue, const SimulationSettings &settings) { if (destroyed_) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, - "Cannot step a destroyed GPU cloth", "physics.clothGpu.step")); + eve::DiagnosticCode::PreconditionViolation, "Cannot step a destroyed GPU cloth", "physics.clothGpu.step")); auto valid = detail::validateSimulationStep(stepValue, settings, observation_); if (!valid) return valid; auto next = detail::advanceSimulationObservation(observation_, stepValue); if (!next) return eve::Result::failure(next.status()); try { - auto applied = stepGpu(static_cast(stepValue.delta.seconds()), - settings.subStepCount); + auto applied = stepGpu(static_cast(stepValue.delta.seconds()), settings.subStepCount); if (!applied) return applied; - } catch (const std::exception& error) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, - std::string("GPU cloth step failed: ") + error.what(), - "physics.clothGpu.step")); + } catch (const std::exception &error) { + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::Failed, + std::string("GPU cloth step failed: ") + error.what(), + "physics.clothGpu.step")); } catch (...) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, - "GPU cloth step failed with an unknown exception", "physics.clothGpu.step")); + eve::DiagnosticCode::Failed, "GPU cloth step failed with an unknown exception", "physics.clothGpu.step")); } observation_ = std::move(next).takeValue(); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result ClothGPU::restoreObservation(const SimulationObservation& observation) { - auto valid = detail::validateSimulationObservation( - observation, "physics.clothGpu.restoreObservation"); +eve::Result ClothGPU::restoreObservation(const SimulationObservation &observation) { + auto valid = detail::validateSimulationObservation(observation, "physics.clothGpu.restoreObservation"); if (!valid) return valid; if (destroyed_) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, - "Cannot restore a destroyed GPU cloth", "physics.clothGpu.restoreObservation")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, + "Cannot restore a destroyed GPU cloth", + "physics.clothGpu.restoreObservation")); observation_ = observation; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } diff --git a/src/modules/physics/ClothGPU.h b/src/modules/physics/ClothGPU.h index e0b57ea4..f32449c8 100644 --- a/src/modules/physics/ClothGPU.h +++ b/src/modules/physics/ClothGPU.h @@ -51,24 +51,18 @@ public: /** @brief Advances the production GPU cloth with the shared ticked contract. */ [[nodiscard("check the GPU cloth step outcome")]] - eve::Result step(const eve::SimulationStep& step, - const SimulationSettings& settings) override; + eve::Result step(const eve::SimulationStep &step, const SimulationSettings &settings) override; /** @brief Returns completed tick/time observables. */ - [[nodiscard]] SimulationObservation observation() const noexcept override { - return observation_; - } + [[nodiscard]] SimulationObservation observation() const noexcept override { return observation_; } /** @brief Identifies this real accelerator backend. */ - [[nodiscard]] SimulationBackendKind kind() const noexcept override { - return SimulationBackendKind::Gpu; - } + [[nodiscard]] SimulationBackendKind kind() const noexcept override { return SimulationBackendKind::Gpu; } /** @brief GPU/CPU parity is numerically bounded, not bit exact. */ [[nodiscard]] SimulationDeterminism determinism() const noexcept override { return SimulationDeterminism::ToleranceBounded; } /** @brief Restores tick/progress metadata after an owner-level restore. */ [[nodiscard("check GPU cloth observation restore")]] - eve::Result restoreObservation( - const SimulationObservation& observation) override; + eve::Result restoreObservation(const SimulationObservation &observation) override; void setGravity(float gx, float gy); float getGravityX() const { return gravityX_; } diff --git a/src/modules/physics/Joint3D.cpp b/src/modules/physics/Joint3D.cpp index 1aab263e..2c7f3e84 100644 --- a/src/modules/physics/Joint3D.cpp +++ b/src/modules/physics/Joint3D.cpp @@ -22,10 +22,15 @@ void finite(float value, const char *operation, const char *name) { } // namespace -Joint3D::Joint3D(World3D *world, Body3D *bodyA, Body3D *bodyB, b3JointId jointId, - PhysicsJointHandle runtimeHandle, Kind kind, int id) - : world_(world), bodyA_(bodyA), bodyB_(bodyB), jointId_(jointId), - runtimeHandle_(runtimeHandle), kind_(kind), id_(id) {} +Joint3D::Joint3D(World3D *world, Body3D *bodyA, Body3D *bodyB, b3JointId jointId, PhysicsJointHandle runtimeHandle, + Kind kind, int id) + : world_(world), + bodyA_(bodyA), + bodyB_(bodyB), + jointId_(jointId), + runtimeHandle_(runtimeHandle), + kind_(kind), + id_(id) {} Joint3D::~Joint3D() { destroy(); } diff --git a/src/modules/physics/Joint3D.h b/src/modules/physics/Joint3D.h index 6ece48ee..07d4df29 100644 --- a/src/modules/physics/Joint3D.h +++ b/src/modules/physics/Joint3D.h @@ -18,8 +18,8 @@ public: enum class Kind { Distance, Revolute, Prismatic, Spherical, Wheel }; /** @brief Internal wrapper constructor; use World3D::new*Joint. */ - Joint3D(World3D *world, Body3D *bodyA, Body3D *bodyB, b3JointId jointId, - PhysicsJointHandle runtimeHandle, Kind kind, int id); + Joint3D(World3D *world, Body3D *bodyA, Body3D *bodyB, b3JointId jointId, PhysicsJointHandle runtimeHandle, + Kind kind, int id); ~Joint3D(); Joint3D(const Joint3D &) = delete; diff --git a/src/modules/physics/PhysicsCapabilities.cpp b/src/modules/physics/PhysicsCapabilities.cpp index 088e165b..01c24b0f 100644 --- a/src/modules/physics/PhysicsCapabilities.cpp +++ b/src/modules/physics/PhysicsCapabilities.cpp @@ -1,8 +1,8 @@ -#include "common/Capability.h" #include "common/CameraObstruction.h" +#include "common/Capability.h" #include "common/PhysicsQuery.h" -#include "physics/Physics.h" #include "physics/ArtifactProvider.h" +#include "physics/Physics.h" #include "physics/TargetingLineOfSightAdapter.h" #include "physics/World.h" #include "physics/World3D.h" @@ -75,11 +75,9 @@ private: class CameraObstructionQueryImpl final : public eve::ICameraObstructionQuery { public: void add(World3D* world, std::weak_ptr lifetime) { - std::erase_if(worlds_, - [](const RegisteredWorld& entry) { return entry.lifetime.expired(); }); - const auto found = std::find_if( - worlds_.begin(), worlds_.end(), - [world](const RegisteredWorld& entry) { return entry.world == world; }); + std::erase_if(worlds_, [](const RegisteredWorld& entry) { return entry.lifetime.expired(); }); + const auto found = std::find_if(worlds_.begin(), worlds_.end(), + [world](const RegisteredWorld& entry) { return entry.world == world; }); if (world && found == worlds_.end()) worlds_.push_back({world, std::move(lifetime)}); } @@ -89,8 +87,8 @@ public: if (out) *out = eve::CameraObstructionHit{}; if (!out) return false; for (const RegisteredWorld& entry : worlds_) { - auto lifetime = entry.lifetime.lock(); - World3D* world = entry.world; + auto lifetime = entry.lifetime.lock(); + World3D* world = entry.world; if (!lifetime || !world || !world->isValid()) continue; CameraSphereHit3D hit; if (!world->sphereCast(fromX, fromY, fromZ, toX, toY, toZ, radius, maskBits, @@ -113,7 +111,7 @@ public: private: struct RegisteredWorld { - World3D* world = nullptr; + World3D* world = nullptr; std::weak_ptr lifetime; }; diff --git a/src/modules/physics/PhysicsHandles.cpp b/src/modules/physics/PhysicsHandles.cpp index 28813302..266d1973 100644 --- a/src/modules/physics/PhysicsHandles.cpp +++ b/src/modules/physics/PhysicsHandles.cpp @@ -8,16 +8,15 @@ namespace eve::physics::detail { PhysicsWorldHandle allocatePhysicsWorldHandle() { using Handle = PhysicsWorldHandle; - using Index = Handle::index_type; + using Index = Handle::index_type; static std::atomic nextIndex{1u}; - Index current = nextIndex.load(std::memory_order_relaxed); + Index current = nextIndex.load(std::memory_order_relaxed); for (;;) { if (current == Handle::invalidIndex) throw eve::Exception("Physics: process-local world handle space exhausted"); const Index next = static_cast(current + 1u); - if (nextIndex.compare_exchange_weak(current, next, std::memory_order_relaxed, - std::memory_order_relaxed)) { + if (nextIndex.compare_exchange_weak(current, next, std::memory_order_relaxed, std::memory_order_relaxed)) { return Handle(current, 1u); } } diff --git a/src/modules/physics/PhysicsLink.cpp b/src/modules/physics/PhysicsLink.cpp index e5deb061..6632f6b5 100644 --- a/src/modules/physics/PhysicsLink.cpp +++ b/src/modules/physics/PhysicsLink.cpp @@ -15,14 +15,12 @@ eve::Diagnostic invalidLinkDiagnostic(const char* path, const char* message) { } eve::Diagnostic staleLinkDiagnostic(const char* path) { - return eve::Diagnostic::error( - eve::DiagnosticCode::StaleHandle, - "Physics link does not resolve to a live object owned by this world", path); + return eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, + "Physics link does not resolve to a live object owned by this world", path); } template -eve::Result attachLink(PhysicsWorldHandle world, Handle handle, - const char* path, const char* message) { +eve::Result attachLink(PhysicsWorldHandle world, Handle handle, const char* path, const char* message) { if (world.isInvalid() || handle.isInvalid()) return eve::Result::failure(invalidLinkDiagnostic(path, message)); return eve::Result::success(Link{world, handle}); @@ -30,11 +28,10 @@ eve::Result attachLink(PhysicsWorldHandle world, Handle handle, } // namespace -eve::Result PhysicsLink::attach(PhysicsWorldHandle world, - PhysicsBodyHandle body) { +eve::Result PhysicsLink::attach(PhysicsWorldHandle world, PhysicsBodyHandle body) { if (world.isInvalid() || body.isInvalid()) { - return eve::Result::failure(invalidLinkDiagnostic( - "physics.link", "PhysicsLink requires valid world and body handles")); + return eve::Result::failure( + invalidLinkDiagnostic("physics.link", "PhysicsLink requires valid world and body handles")); } return eve::Result::success({world, body}); } @@ -77,19 +74,16 @@ eve::Result PhysicsLink::resolve(World3D& world) const { return eve::Result::success(resolved); } -eve::Result PhysicsShapeLink::attach(PhysicsWorldHandle world, - PhysicsShapeHandle shape) { - return attachLink( - world, shape, "physics.shapeLink", - "PhysicsShapeLink requires valid world and shape handles"); +eve::Result PhysicsShapeLink::attach(PhysicsWorldHandle world, PhysicsShapeHandle shape) { + return attachLink(world, shape, "physics.shapeLink", + "PhysicsShapeLink requires valid world and shape handles"); } eve::Result PhysicsShapeLink::fromShape(const Shape3D& shape) { - const Body3D* body = shape.getBody(); + const Body3D* body = shape.getBody(); const World3D* world = body ? body->getWorld() : nullptr; if (!shape.isValid() || !body || !world || !world->isValid()) - return eve::Result::failure( - staleLinkDiagnostic("physics.shapeLink.shape")); + return eve::Result::failure(staleLinkDiagnostic("physics.shapeLink.shape")); return attach(world->runtimeHandle(), shape.runtimeHandle()); } @@ -97,23 +91,19 @@ eve::Result PhysicsShapeLink::resolve(World3D& world) const { if (!isValid() || world.runtimeHandle() != this->world) return eve::Result::failure(staleLinkDiagnostic("physics.shapeLink.resolve")); Shape3D* resolved = world.findShape(shape); - if (!resolved) - return eve::Result::failure(staleLinkDiagnostic("physics.shapeLink.resolve")); + if (!resolved) return eve::Result::failure(staleLinkDiagnostic("physics.shapeLink.resolve")); return eve::Result::success(resolved); } -eve::Result PhysicsJointLink::attach(PhysicsWorldHandle world, - PhysicsJointHandle joint) { - return attachLink( - world, joint, "physics.jointLink", - "PhysicsJointLink requires valid world and joint handles"); +eve::Result PhysicsJointLink::attach(PhysicsWorldHandle world, PhysicsJointHandle joint) { + return attachLink(world, joint, "physics.jointLink", + "PhysicsJointLink requires valid world and joint handles"); } eve::Result PhysicsJointLink::fromJoint(const Joint3D& joint) { World3D* world = joint.getWorld(); if (!joint.isValid() || !world || !world->isValid()) - return eve::Result::failure( - staleLinkDiagnostic("physics.jointLink.joint")); + return eve::Result::failure(staleLinkDiagnostic("physics.jointLink.joint")); return attach(world->runtimeHandle(), joint.runtimeHandle()); } @@ -121,8 +111,7 @@ eve::Result PhysicsJointLink::resolve(World3D& world) const { if (!isValid() || world.runtimeHandle() != this->world) return eve::Result::failure(staleLinkDiagnostic("physics.jointLink.resolve")); Joint3D* resolved = world.findJoint(joint); - if (!resolved) - return eve::Result::failure(staleLinkDiagnostic("physics.jointLink.resolve")); + if (!resolved) return eve::Result::failure(staleLinkDiagnostic("physics.jointLink.resolve")); return eve::Result::success(resolved); } diff --git a/src/modules/physics/PhysicsLink.h b/src/modules/physics/PhysicsLink.h index 5585e1a7..d64b8e59 100644 --- a/src/modules/physics/PhysicsLink.h +++ b/src/modules/physics/PhysicsLink.h @@ -35,17 +35,14 @@ struct PhysicsLink { PhysicsBodyHandle body = PhysicsBodyHandle::invalid(); /** @brief Whether both encoded handle values are non-invalid. */ - [[nodiscard]] bool isValid() const noexcept { - return world.isValid() && body.isValid(); - } + [[nodiscard]] bool isValid() const noexcept { return world.isValid() && body.isValid(); } /** * @brief Validates and constructs a link from explicit runtime handles. * @return A link candidate; ownership is verified by resolve(). */ [[nodiscard("check the link construction outcome")]] - static eve::Result attach(PhysicsWorldHandle world, - PhysicsBodyHandle body); + static eve::Result attach(PhysicsWorldHandle world, PhysicsBodyHandle body); /** @brief Creates a link from a live 2D body. */ [[nodiscard("check the link construction outcome")]] @@ -73,13 +70,10 @@ struct PhysicsShapeLink { PhysicsShapeHandle shape = PhysicsShapeHandle::invalid(); /** @brief Whether both encoded handle values are non-invalid. */ - [[nodiscard]] bool isValid() const noexcept { - return world.isValid() && shape.isValid(); - } + [[nodiscard]] bool isValid() const noexcept { return world.isValid() && shape.isValid(); } /** @brief Constructs a shape link candidate; ownership is checked by resolve(). */ [[nodiscard("check the shape-link construction outcome")]] - static eve::Result attach(PhysicsWorldHandle world, - PhysicsShapeHandle shape); + static eve::Result attach(PhysicsWorldHandle world, PhysicsShapeHandle shape); /** @brief Creates a link from a live shape. */ [[nodiscard("check the shape-link construction outcome")]] static eve::Result fromShape(const Shape3D& shape); @@ -99,13 +93,10 @@ struct PhysicsJointLink { PhysicsJointHandle joint = PhysicsJointHandle::invalid(); /** @brief Whether both encoded handle values are non-invalid. */ - [[nodiscard]] bool isValid() const noexcept { - return world.isValid() && joint.isValid(); - } + [[nodiscard]] bool isValid() const noexcept { return world.isValid() && joint.isValid(); } /** @brief Constructs a joint link candidate; ownership is checked by resolve(). */ [[nodiscard("check the joint-link construction outcome")]] - static eve::Result attach(PhysicsWorldHandle world, - PhysicsJointHandle joint); + static eve::Result attach(PhysicsWorldHandle world, PhysicsJointHandle joint); /** @brief Creates a link from a live joint. */ [[nodiscard("check the joint-link construction outcome")]] static eve::Result fromJoint(const Joint3D& joint); diff --git a/src/modules/physics/Shape3D.cpp b/src/modules/physics/Shape3D.cpp index 1ce1eb92..b60a08cc 100644 --- a/src/modules/physics/Shape3D.cpp +++ b/src/modules/physics/Shape3D.cpp @@ -163,26 +163,38 @@ b3HeightFieldData *createCheckedHeightField(int countX, int countZ, float cellSi } // namespace -Shape3D::Shape3D(World3D *world, Body3D *body, b3ShapeId shapeId, - PhysicsShapeHandle runtimeHandle, Kind kind, float a, float b, float c, - std::vector hullVertices, int hullMaxVertices, - std::vector meshVertices, std::vector meshIndices, - b3MeshData *meshData, bool meshWeldVertices, float meshWeldTolerance, - bool meshIdentifyEdges, bool meshUseMedianSplit, - std::vector heightValues, int heightCountX, int heightCountZ, - float heightCellSizeX, float heightCellSizeZ, float heightGlobalMin, - float heightGlobalMax, bool heightClockwise, b3HeightFieldData *heightData) - : world_(world), body_(body), shapeId_(shapeId), runtimeHandle_(runtimeHandle), kind_(kind), - a_(a), b_(b), c_(c), - hullVertices_(std::move(hullVertices)), hullMaxVertices_(hullMaxVertices), - meshVertices_(std::move(meshVertices)), meshIndices_(std::move(meshIndices)), - meshData_(meshData), meshWeldVertices_(meshWeldVertices), - meshWeldTolerance_(meshWeldTolerance), meshIdentifyEdges_(meshIdentifyEdges), +Shape3D::Shape3D(World3D *world, Body3D *body, b3ShapeId shapeId, PhysicsShapeHandle runtimeHandle, Kind kind, float a, + float b, float c, std::vector hullVertices, int hullMaxVertices, + std::vector meshVertices, std::vector meshIndices, b3MeshData *meshData, + bool meshWeldVertices, float meshWeldTolerance, bool meshIdentifyEdges, bool meshUseMedianSplit, + std::vector heightValues, int heightCountX, int heightCountZ, float heightCellSizeX, + float heightCellSizeZ, float heightGlobalMin, float heightGlobalMax, bool heightClockwise, + b3HeightFieldData *heightData) + : world_(world), + body_(body), + shapeId_(shapeId), + runtimeHandle_(runtimeHandle), + kind_(kind), + a_(a), + b_(b), + c_(c), + hullVertices_(std::move(hullVertices)), + hullMaxVertices_(hullMaxVertices), + meshVertices_(std::move(meshVertices)), + meshIndices_(std::move(meshIndices)), + meshData_(meshData), + meshWeldVertices_(meshWeldVertices), + meshWeldTolerance_(meshWeldTolerance), + meshIdentifyEdges_(meshIdentifyEdges), meshUseMedianSplit_(meshUseMedianSplit), - heightValues_(std::move(heightValues)), heightCountX_(heightCountX), - heightCountZ_(heightCountZ), heightCellSizeX_(heightCellSizeX), - heightCellSizeZ_(heightCellSizeZ), heightGlobalMin_(heightGlobalMin), - heightGlobalMax_(heightGlobalMax), heightClockwise_(heightClockwise), + heightValues_(std::move(heightValues)), + heightCountX_(heightCountX), + heightCountZ_(heightCountZ), + heightCellSizeX_(heightCellSizeX), + heightCellSizeZ_(heightCellSizeZ), + heightGlobalMin_(heightGlobalMin), + heightGlobalMax_(heightGlobalMax), + heightClockwise_(heightClockwise), heightData_(heightData), id_(world ? world->nextShapeId() : 0) { if (kind_ == Kind::TriangleMesh && isValid() && !meshMaterialsDirty_) { diff --git a/src/modules/physics/Shape3D.h b/src/modules/physics/Shape3D.h index 3106cd09..d6fe39bc 100644 --- a/src/modules/physics/Shape3D.h +++ b/src/modules/physics/Shape3D.h @@ -25,17 +25,13 @@ public: enum class Kind { Box, Sphere, Capsule, ConvexHull, TriangleMesh, HeightField }; /** @brief Internal: wraps a Box3D shape (use Body3D::new*Shape). */ - Shape3D(World3D *world, Body3D *body, b3ShapeId shapeId, - PhysicsShapeHandle runtimeHandle, Kind kind, float a, float b, float c, - std::vector hullVertices = {}, int hullMaxVertices = 64, - std::vector meshVertices = {}, std::vector meshIndices = {}, - b3MeshData *meshData = nullptr, bool meshWeldVertices = true, - float meshWeldTolerance = 0.001f, bool meshIdentifyEdges = true, - bool meshUseMedianSplit = false, std::vector heightValues = {}, - int heightCountX = 0, int heightCountZ = 0, float heightCellSizeX = 1.f, - float heightCellSizeZ = 1.f, float heightGlobalMin = 0.f, - float heightGlobalMax = 0.f, bool heightClockwise = false, - b3HeightFieldData *heightData = nullptr); + Shape3D(World3D *world, Body3D *body, b3ShapeId shapeId, PhysicsShapeHandle runtimeHandle, Kind kind, float a, + float b, float c, std::vector hullVertices = {}, int hullMaxVertices = 64, + std::vector meshVertices = {}, std::vector meshIndices = {}, b3MeshData *meshData = nullptr, + bool meshWeldVertices = true, float meshWeldTolerance = 0.001f, bool meshIdentifyEdges = true, + bool meshUseMedianSplit = false, std::vector heightValues = {}, int heightCountX = 0, + int heightCountZ = 0, float heightCellSizeX = 1.f, float heightCellSizeZ = 1.f, float heightGlobalMin = 0.f, + float heightGlobalMax = 0.f, bool heightClockwise = false, b3HeightFieldData *heightData = nullptr); ~Shape3D(); Shape3D(const Shape3D &) = delete; @@ -319,7 +315,7 @@ private: World3D *world_ = nullptr; Body3D *body_ = nullptr; b3ShapeId shapeId_{}; - PhysicsShapeHandle runtimeHandle_ = PhysicsShapeHandle::invalid(); + PhysicsShapeHandle runtimeHandle_ = PhysicsShapeHandle::invalid(); Kind kind_ = Kind::Box; float a_ = 0.f; // box hx | sphere r | capsule half-height float b_ = 0.f; // box hy | unused | capsule radius diff --git a/src/modules/physics/SimulationBackend.cpp b/src/modules/physics/SimulationBackend.cpp index defdd6e3..dabe6374 100644 --- a/src/modules/physics/SimulationBackend.cpp +++ b/src/modules/physics/SimulationBackend.cpp @@ -16,44 +16,35 @@ namespace { constexpr int kMinimumIterations = 1; constexpr int kMaximumIterations = 1024; -eve::Result validateStep(const eve::SimulationStep& step, - const SimulationSettings& settings, +eve::Result validateStep(const eve::SimulationStep& step, const SimulationSettings& settings, const SimulationObservation& observation) { const double seconds = step.delta.seconds(); if (!std::isfinite(seconds) || seconds < 0.0) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "Physics simulation step duration must be finite and non-negative", + eve::DiagnosticCode::InvalidArgument, "Physics simulation step duration must be finite and non-negative", "physics.simulationStep.delta")); } if (seconds > 0.05) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "Physics simulation step duration must be in [0, 0.05] seconds", + eve::DiagnosticCode::InvalidArgument, "Physics simulation step duration must be in [0, 0.05] seconds", "physics.simulationStep.delta")); } if (!(step.tick > observation.lastTick)) { return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::PreconditionViolation, - "Physics simulation tick must increase strictly for every backend step", - "physics.simulationStep.tick")); + "Physics simulation tick must increase strictly for every backend step", "physics.simulationStep.tick")); } - if (settings.velocityIterations < kMinimumIterations || - settings.velocityIterations > kMaximumIterations || - settings.positionIterations < kMinimumIterations || - settings.positionIterations > kMaximumIterations || - settings.subStepCount < kMinimumIterations || - settings.subStepCount > kMaximumIterations) { + if (settings.velocityIterations < kMinimumIterations || settings.velocityIterations > kMaximumIterations || + settings.positionIterations < kMinimumIterations || settings.positionIterations > kMaximumIterations || + settings.subStepCount < kMinimumIterations || settings.subStepCount > kMaximumIterations) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "Physics simulation iteration counts must be in [1, 1024]", + eve::DiagnosticCode::InvalidArgument, "Physics simulation iteration counts must be in [1, 1024]", "physics.simulationStep.settings")); } return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result accumulatedDuration(const eve::Duration& current, - const eve::Duration& delta) { +eve::Result accumulatedDuration(const eve::Duration& current, const eve::Duration& delta) { auto total = current.tryAdd(delta); if (!total) { const eve::Status status = total.status(); @@ -64,13 +55,11 @@ eve::Result accumulatedDuration(const eve::Duration& current, class CallbackSimulationBackend final : public ISimulationBackend { public: - CallbackSimulationBackend(void* context, SimulationStepCallback callback, - SimulationBackendKind kind, + CallbackSimulationBackend(void* context, SimulationStepCallback callback, SimulationBackendKind kind, SimulationDeterminism determinism) : context_(context), callback_(callback), kind_(kind), determinism_(determinism) {} - [[nodiscard]] eve::Result step(const eve::SimulationStep& step, - const SimulationSettings& settings) override { + [[nodiscard]] eve::Result step(const eve::SimulationStep& step, const SimulationSettings& settings) override { auto valid = detail::validateSimulationStep(step, settings, observation_); if (!valid) return valid; @@ -83,56 +72,46 @@ public: return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } - [[nodiscard]] SimulationObservation observation() const noexcept override { - return observation_; - } + [[nodiscard]] SimulationObservation observation() const noexcept override { return observation_; } [[nodiscard]] SimulationBackendKind kind() const noexcept override { return kind_; } - [[nodiscard]] SimulationDeterminism determinism() const noexcept override { - return determinism_; - } + [[nodiscard]] SimulationDeterminism determinism() const noexcept override { return determinism_; } - [[nodiscard]] eve::Result restoreObservation( - const SimulationObservation& observation) override { - auto valid = detail::validateSimulationObservation( - observation, "physics.simulationBackend.restoreObservation"); + [[nodiscard]] eve::Result restoreObservation(const SimulationObservation& observation) override { + auto valid = detail::validateSimulationObservation(observation, "physics.simulationBackend.restoreObservation"); if (!valid) return valid; observation_ = observation; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } private: - void* context_ = nullptr; // borrowed by the owner that created this backend - SimulationStepCallback callback_ = nullptr; - SimulationBackendKind kind_ = SimulationBackendKind::Cpu; - SimulationDeterminism determinism_ = SimulationDeterminism::ToleranceBounded; - SimulationObservation observation_; + void* context_ = nullptr; // borrowed by the owner that created this backend + SimulationStepCallback callback_ = nullptr; + SimulationBackendKind kind_ = SimulationBackendKind::Cpu; + SimulationDeterminism determinism_ = SimulationDeterminism::ToleranceBounded; + SimulationObservation observation_; }; -void stepBox2D(void* context, const eve::SimulationStep& step, - const SimulationSettings& settings) noexcept { +void stepBox2D(void* context, const eve::SimulationStep& step, const SimulationSettings& settings) noexcept { auto* world = static_cast(context); - world->Step(static_cast(step.delta.seconds()), settings.velocityIterations, - settings.positionIterations); + world->Step(static_cast(step.delta.seconds()), settings.velocityIterations, settings.positionIterations); } eve::Diagnostic fallbackDiagnostic(const char* reason) { - return eve::Diagnostic::warning( - eve::DiagnosticCode::Unsupported, - "Physics accelerator capability is unavailable; CPU backend selected", - "physics.simulationBackend", - {{"provider", IAcceleratorBackendProvider::capabilityName}, - {"selected", "cpu"}, - {"fallback", "structured-capability-fallback"}, - {"reason", reason}}); + return eve::Diagnostic::warning(eve::DiagnosticCode::Unsupported, + "Physics accelerator capability is unavailable; CPU backend selected", + "physics.simulationBackend", + {{"provider", IAcceleratorBackendProvider::capabilityName}, + {"selected", "cpu"}, + {"fallback", "structured-capability-fallback"}, + {"reason", reason}}); } -eve::Result cpuFallback( - std::unique_ptr cpuBackend, const char* reason) { +eve::Result cpuFallback(std::unique_ptr cpuBackend, + const char* reason) { return eve::Result::success( - {std::move(cpuBackend), SimulationBackendKind::Gpu, - SimulationBackendKind::Cpu, true}, + {std::move(cpuBackend), SimulationBackendKind::Gpu, SimulationBackendKind::Cpu, true}, eve::Status(eve::StatusCode::Applied, {fallbackDiagnostic(reason)})); } @@ -140,97 +119,83 @@ eve::Result cpuFallback( namespace detail { -eve::Result validateSimulationStep(const eve::SimulationStep& step, - const SimulationSettings& settings, +eve::Result validateSimulationStep(const eve::SimulationStep& step, const SimulationSettings& settings, const SimulationObservation& observation) { return validateStep(step, settings, observation); } -eve::Result advanceSimulationObservation( - const SimulationObservation& current, const eve::SimulationStep& step) { +eve::Result advanceSimulationObservation(const SimulationObservation& current, + const eve::SimulationStep& step) { if (current.stepCount == std::numeric_limits::max()) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, - "Physics simulation observation step count cannot overflow", + eve::DiagnosticCode::InvariantViolation, "Physics simulation observation step count cannot overflow", "physics.simulationBackend.stepCount")); } auto total = accumulatedDuration(current.simulatedDuration, step.delta); if (!total) return eve::Result::failure(total.status()); SimulationObservation next = current; - next.simulatedDuration = std::move(total).takeValue(); - next.lastTick = step.tick; - next.simulatedSeconds = next.simulatedDuration.seconds(); - next.lastDeltaSeconds = static_cast(step.delta.seconds()); + next.simulatedDuration = std::move(total).takeValue(); + next.lastTick = step.tick; + next.simulatedSeconds = next.simulatedDuration.seconds(); + next.lastDeltaSeconds = static_cast(step.delta.seconds()); ++next.stepCount; - if (!std::isfinite(next.simulatedSeconds) || - !std::isfinite(next.lastDeltaSeconds)) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, - "Physics simulation observation exceeded representable floating-point range", - "physics.simulationBackend.observation")); + if (!std::isfinite(next.simulatedSeconds) || !std::isfinite(next.lastDeltaSeconds)) { + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, + "Physics simulation observation exceeded representable floating-point range", + "physics.simulationBackend.observation")); } return eve::Result::success(std::move(next)); } -eve::Result validateSimulationObservation(const SimulationObservation& observation, - const char* path) { - if (!std::isfinite(observation.simulatedSeconds) || - observation.simulatedSeconds < 0.0 || - !std::isfinite(observation.lastDeltaSeconds) || - observation.lastDeltaSeconds < 0.f || +eve::Result validateSimulationObservation(const SimulationObservation& observation, const char* path) { + if (!std::isfinite(observation.simulatedSeconds) || observation.simulatedSeconds < 0.0 || + !std::isfinite(observation.lastDeltaSeconds) || observation.lastDeltaSeconds < 0.f || observation.simulatedDuration.nanoseconds() < 0 || (observation.stepCount == 0 && !observation.lastTick.isZero()) || (observation.stepCount == 0 && observation.lastDeltaSeconds != 0.f)) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "Simulation observation contains invalid progress values", path)); + eve::DiagnosticCode::InvalidArgument, "Simulation observation contains invalid progress values", path)); } const double durationSeconds = observation.simulatedDuration.seconds(); - if (!std::isfinite(durationSeconds) || - std::fabs(durationSeconds - observation.simulatedSeconds) > 1e-6) { + if (!std::isfinite(durationSeconds) || std::fabs(durationSeconds - observation.simulatedSeconds) > 1e-6) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, - "Simulation observation seconds disagree with exact duration", path)); + eve::DiagnosticCode::Conflict, "Simulation observation seconds disagree with exact duration", path)); } return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } std::unique_ptr makeBox2DSimulationBackend(b2World* world) { - if (!world) - throw eve::Exception("Physics: cannot create a simulation backend for a null world"); + if (!world) throw eve::Exception("Physics: cannot create a simulation backend for a null world"); return makeCallbackSimulationBackend(world, &stepBox2D, SimulationBackendKind::Cpu, SimulationDeterminism::ToleranceBounded); } -std::unique_ptr makeCallbackSimulationBackend( - void* context, SimulationStepCallback callback, SimulationBackendKind kind, - SimulationDeterminism determinism) { - if (!callback) - throw eve::Exception("Physics: callback simulation backend requires a callback"); +std::unique_ptr makeCallbackSimulationBackend(void* context, SimulationStepCallback callback, + SimulationBackendKind kind, + SimulationDeterminism determinism) { + if (!callback) throw eve::Exception("Physics: callback simulation backend requires a callback"); return std::make_unique(context, callback, kind, determinism); } std::unique_ptr makeMockAcceleratorBackend() { - return std::make_unique( - nullptr, nullptr, SimulationBackendKind::MockAccelerator, - SimulationDeterminism::ToleranceBounded); + return std::make_unique(nullptr, nullptr, SimulationBackendKind::MockAccelerator, + SimulationDeterminism::ToleranceBounded); } -eve::Result selectSimulationBackend( - SimulationBackendDomain domain, std::unique_ptr cpuBackend, - void* state, bool preferAccelerator) { +eve::Result selectSimulationBackend(SimulationBackendDomain domain, + std::unique_ptr cpuBackend, + void* state, bool preferAccelerator) { if (!cpuBackend) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "Physics backend selection requires a CPU fallback backend", + eve::DiagnosticCode::InvalidArgument, "Physics backend selection requires a CPU fallback backend", "physics.simulationBackend.cpu")); } if (!preferAccelerator) { return eve::Result::success( - {std::move(cpuBackend), SimulationBackendKind::Cpu, - SimulationBackendKind::Cpu, false}, + {std::move(cpuBackend), SimulationBackendKind::Cpu, SimulationBackendKind::Cpu, false}, eve::Status::success(eve::StatusCode::Applied)); } @@ -238,8 +203,7 @@ eve::Result selectSimulationBackend( if (!provider) return cpuFallback(std::move(cpuBackend), "capability-absent"); try { - if (!provider->supports(domain)) - return cpuFallback(std::move(cpuBackend), "provider-does-not-support-domain"); + if (!provider->supports(domain)) return cpuFallback(std::move(cpuBackend), "provider-does-not-support-domain"); auto candidate = provider->create(domain, state); if (!candidate) { @@ -253,12 +217,12 @@ eve::Result selectSimulationBackend( if (!backend) return cpuFallback(std::move(cpuBackend), "provider-returned-null-backend"); SimulationBackendSelection selection; - selection.backend = std::move(backend); + selection.backend = std::move(backend); selection.requestedKind = SimulationBackendKind::Gpu; - selection.actualKind = selection.backend->kind(); - selection.usedFallback = false; - return eve::Result::success( - std::move(selection), eve::Status::success(eve::StatusCode::Applied)); + selection.actualKind = selection.backend->kind(); + selection.usedFallback = false; + return eve::Result::success(std::move(selection), + eve::Status::success(eve::StatusCode::Applied)); } catch (const std::exception&) { return cpuFallback(std::move(cpuBackend), "provider-create-threw"); } catch (...) { diff --git a/src/modules/physics/SimulationBackend.h b/src/modules/physics/SimulationBackend.h index 93793072..6d0c9014 100644 --- a/src/modules/physics/SimulationBackend.h +++ b/src/modules/physics/SimulationBackend.h @@ -54,24 +54,21 @@ enum class SimulationDeterminism { }; /** @brief Returns the stable backend-kind spelling. */ -[[nodiscard]] constexpr std::string_view simulationBackendKindName( - SimulationBackendKind kind) noexcept { +[[nodiscard]] constexpr std::string_view simulationBackendKindName(SimulationBackendKind kind) noexcept { switch (kind) { - case SimulationBackendKind::Cpu: return "cpu"; - case SimulationBackendKind::Gpu: return "gpu"; - case SimulationBackendKind::MockAccelerator: return "mock_accelerator"; + case SimulationBackendKind::Cpu: return "cpu"; + case SimulationBackendKind::Gpu: return "gpu"; + case SimulationBackendKind::MockAccelerator: return "mock_accelerator"; } return "unknown"; } /** @brief Returns the stable determinism-contract spelling. */ -[[nodiscard]] constexpr std::string_view simulationDeterminismName( - SimulationDeterminism determinism) noexcept { +[[nodiscard]] constexpr std::string_view simulationDeterminismName(SimulationDeterminism determinism) noexcept { switch (determinism) { - case SimulationDeterminism::BitExact: return "bit_exact"; - case SimulationDeterminism::ToleranceBounded: return "tolerance_bounded"; - case SimulationDeterminism::ExplicitlyNondeterministic: - return "explicitly_nondeterministic"; + case SimulationDeterminism::BitExact: return "bit_exact"; + case SimulationDeterminism::ToleranceBounded: return "tolerance_bounded"; + case SimulationDeterminism::ExplicitlyNondeterministic: return "explicitly_nondeterministic"; } return "unknown"; } @@ -151,8 +148,7 @@ public: * @return Applied with updated state, or a structured rejection/failure. */ [[nodiscard("check the step outcome or explicitly ignore it")]] - virtual eve::Result step(const eve::SimulationStep& step, - const SimulationSettings& settings) = 0; + virtual eve::Result step(const eve::SimulationStep& step, const SimulationSettings& settings) = 0; /** * @brief Returns completed-step observables by value. @@ -182,11 +178,9 @@ public: * callers must restore the domain state atomically as one unit. */ [[nodiscard("check backend observation restore")]] - virtual eve::Result restoreObservation( - const SimulationObservation& /*observation*/) { + virtual eve::Result restoreObservation(const SimulationObservation& /*observation*/) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Unsupported, - "This simulation backend does not support observation restore", + eve::DiagnosticCode::Unsupported, "This simulation backend does not support observation restore", "physics.simulationBackend.restoreObservation")); } }; @@ -220,8 +214,7 @@ public: * @return A backend or a structured Unsupported/Failed status. */ [[nodiscard("inspect provider creation or explicitly fall back")]] - virtual eve::Result> create( - SimulationBackendDomain domain, void* state) = 0; + virtual eve::Result> create(SimulationBackendDomain domain, void* state) = 0; }; /** @@ -242,8 +235,7 @@ struct SimulationBackendSelection { }; /** @brief Callback used by a domain-owned CPU or test backend adapter. */ -using SimulationStepCallback = void (*)(void* context, - const eve::SimulationStep& step, +using SimulationStepCallback = void (*)(void* context, const eve::SimulationStep& step, const SimulationSettings& settings) noexcept; namespace detail { @@ -256,8 +248,7 @@ namespace detail { * @return Applied when all shared backend invariants hold. */ [[nodiscard("check simulation-step validation")]] -eve::Result validateSimulationStep(const eve::SimulationStep& step, - const SimulationSettings& settings, +eve::Result validateSimulationStep(const eve::SimulationStep& step, const SimulationSettings& settings, const SimulationObservation& observation); /** @@ -267,8 +258,8 @@ eve::Result validateSimulationStep(const eve::SimulationStep& step, * @return The next observation, or an overflow/invalid-input failure. */ [[nodiscard("check observation advancement")]] -eve::Result advanceSimulationObservation( - const SimulationObservation& current, const eve::SimulationStep& step); +eve::Result advanceSimulationObservation(const SimulationObservation& current, + const eve::SimulationStep& step); /** * @brief Validates restored backend progress metadata. @@ -277,8 +268,7 @@ eve::Result advanceSimulationObservation( * @return Applied when all fields are finite, non-negative and coherent. */ [[nodiscard("check observation validation")]] -eve::Result validateSimulationObservation(const SimulationObservation& observation, - const char* path); +eve::Result validateSimulationObservation(const SimulationObservation& observation, const char* path); /** * @brief Creates the built-in Box2D CPU backend adapter. @@ -300,9 +290,9 @@ std::unique_ptr makeBox2DSimulationBackend(b2World* world); * @return An owning backend adapter. */ [[nodiscard("retain the backend while its callback state is live")]] -std::unique_ptr makeCallbackSimulationBackend( - void* context, SimulationStepCallback callback, SimulationBackendKind kind, - SimulationDeterminism determinism); +std::unique_ptr makeCallbackSimulationBackend(void* context, SimulationStepCallback callback, + SimulationBackendKind kind, + SimulationDeterminism determinism); /** * @brief Creates a no-op mock accelerator used for headless contract tests. @@ -321,9 +311,9 @@ std::unique_ptr makeMockAcceleratorBackend(); * Warning diagnostic and `usedFallback=true`. */ [[nodiscard("inspect the selected backend and fallback diagnostics")]] -eve::Result selectSimulationBackend( - SimulationBackendDomain domain, std::unique_ptr cpuBackend, - void* state, bool preferAccelerator = true); +eve::Result selectSimulationBackend(SimulationBackendDomain domain, + std::unique_ptr cpuBackend, + void* state, bool preferAccelerator = true); } // namespace detail } // namespace eve::physics diff --git a/src/modules/physics/TargetingLineOfSightAdapter.cpp b/src/modules/physics/TargetingLineOfSightAdapter.cpp index abe860a0..bf1b2668 100644 --- a/src/modules/physics/TargetingLineOfSightAdapter.cpp +++ b/src/modules/physics/TargetingLineOfSightAdapter.cpp @@ -19,11 +19,9 @@ eve::Result failure(eve::DiagnosticCode code, std::string message) { eve::Result TargetingLineOfSightAdapter::addWorld(World3D* world) { if (!world) - return failure(eve::DiagnosticCode::InvalidArgument, - "Physics LOS cannot register a null World3D"); + return failure(eve::DiagnosticCode::InvalidArgument, "Physics LOS cannot register a null World3D"); if (!world->isValid()) - return failure(eve::DiagnosticCode::NotFound, - "Physics LOS cannot register an invalid World3D"); + return failure(eve::DiagnosticCode::NotFound, "Physics LOS cannot register an invalid World3D"); for (auto it = worlds_.begin(); it != worlds_.end();) { auto lifetime = it->lifetime.lock(); @@ -31,67 +29,56 @@ eve::Result TargetingLineOfSightAdapter::addWorld(World3D* world) { it = worlds_.erase(it); continue; } - if (it->world == world) - return eve::Result::success(eve::Status::success(eve::StatusCode::NoOp)); - return failure(eve::DiagnosticCode::Conflict, - "Physics LOS already has a different active World3D"); + if (it->world == world) return eve::Result::success(eve::Status::success(eve::StatusCode::NoOp)); + return failure(eve::DiagnosticCode::Conflict, "Physics LOS already has a different active World3D"); } worlds_.push_back({world, world->queryLifetime_}); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } eve::Result TargetingLineOfSightAdapter::removeWorld(World3D* world) { - if (!world) - return failure(eve::DiagnosticCode::InvalidArgument, - "Physics LOS cannot remove a null World3D"); + if (!world) return failure(eve::DiagnosticCode::InvalidArgument, "Physics LOS cannot remove a null World3D"); const auto before = worlds_.size(); std::erase_if(worlds_, [world](const RegisteredWorld& entry) { return entry.world == world; }); const auto code = before == worlds_.size() ? eve::StatusCode::NoOp : eve::StatusCode::Applied; return eve::Result::success(eve::Status::success(code)); } -eve::Result TargetingLineOfSightAdapter::query( - const sensing::TargetLocation& from, const sensing::TargetLocation& to) const { +eve::Result TargetingLineOfSightAdapter::query(const sensing::TargetLocation& from, + const sensing::TargetLocation& to) const { const auto* fromPoint = std::get_if(&from); - const auto* toPoint = std::get_if(&to); + const auto* toPoint = std::get_if(&to); if (!fromPoint || !toPoint || !fromPoint->isValid() || !toPoint->isValid() || fromPoint->space() != sensing::CoordinateSpace::World3D || toPoint->space() != sensing::CoordinateSpace::World3D) return failure( - eve::DiagnosticCode::Unsupported, - "Physics LOS supports only two valid World3D points in the same space"); + eve::DiagnosticCode::Unsupported, "Physics LOS supports only two valid World3D points in the same space"); - if (!std::isfinite(fromPoint->x()) || !std::isfinite(fromPoint->y()) || - !std::isfinite(fromPoint->z()) || !std::isfinite(toPoint->x()) || - !std::isfinite(toPoint->y()) || !std::isfinite(toPoint->z())) + if (!std::isfinite(fromPoint->x()) || !std::isfinite(fromPoint->y()) || !std::isfinite(fromPoint->z()) || + !std::isfinite(toPoint->x()) || !std::isfinite(toPoint->y()) || !std::isfinite(toPoint->z())) return failure(eve::DiagnosticCode::InvalidArgument, "Physics LOS points must be finite"); - World3D* activeWorld = nullptr; + World3D* activeWorld = nullptr; std::shared_ptr activeLifetime; for (const RegisteredWorld& entry : worlds_) { - auto lifetime = entry.lifetime.lock(); - World3D* world = entry.world; + auto lifetime = entry.lifetime.lock(); + World3D* world = entry.world; if (!lifetime || !world || !world->isValid()) continue; if (activeWorld) - return failure( - eve::DiagnosticCode::Conflict, - "Physics LOS has more than one valid active World3D"); - activeWorld = world; + return failure(eve::DiagnosticCode::Conflict, + "Physics LOS has more than one valid active World3D"); + activeWorld = world; activeLifetime = std::move(lifetime); } if (!activeWorld) - return failure( - eve::DiagnosticCode::Unsupported, - "Physics LOS has no valid active World3D"); + return failure(eve::DiagnosticCode::Unsupported, + "Physics LOS has no valid active World3D"); - activeWorld->rayCast(fromPoint->x(), fromPoint->y(), fromPoint->z(), toPoint->x(), toPoint->y(), - toPoint->z()); + activeWorld->rayCast(fromPoint->x(), fromPoint->y(), fromPoint->z(), toPoint->x(), toPoint->y(), toPoint->z()); if (activeWorld->hasRayHit()) - return eve::Result::success( - sensing::LineOfSightResult{false, std::nullopt}); - return eve::Result::success( - sensing::LineOfSightResult{true, std::nullopt}); + return eve::Result::success(sensing::LineOfSightResult{false, std::nullopt}); + return eve::Result::success(sensing::LineOfSightResult{true, std::nullopt}); } } // namespace eve::physics diff --git a/src/modules/physics/TargetingLineOfSightAdapter.h b/src/modules/physics/TargetingLineOfSightAdapter.h index 0f7043a1..a078df1e 100644 --- a/src/modules/physics/TargetingLineOfSightAdapter.h +++ b/src/modules/physics/TargetingLineOfSightAdapter.h @@ -49,7 +49,7 @@ public: private: struct RegisteredWorld { - World3D* world = nullptr; + World3D* world = nullptr; std::weak_ptr lifetime; }; diff --git a/src/modules/physics/World.cpp b/src/modules/physics/World.cpp index 7ec73e42..fa205137 100644 --- a/src/modules/physics/World.cpp +++ b/src/modules/physics/World.cpp @@ -4,8 +4,8 @@ #include "physics/SimulationBackend.h" #include "common/Exception.h" -#include "platform_event/PlatformEvent.h" #include "common/Profile.h" +#include "platform_event/PlatformEvent.h" #include @@ -176,23 +176,20 @@ World::ContactEvent contactEventFrom(b2Contact *contact) { eve::Result makeLegacyStep(float dt, eve::SimulationTick currentTick) { if (!std::isfinite(dt) || dt < 0.f) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "World update dt must be finite and non-negative", - "physics.world.update.dt")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "World update dt must be finite and non-negative", "physics.world.update.dt")); } const float normalized = std::min(dt, 0.05f); - const auto nextTick = currentTick.incremented(); + const auto nextTick = currentTick.incremented(); if (!nextTick) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, - "World simulation tick cannot be incremented", - "physics.world.simulationTick")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, + "World simulation tick cannot be incremented", "physics.world.simulationTick")); } auto duration = eve::Duration::fromSeconds(static_cast(normalized)); if (!duration) return eve::Result::failure(duration.status()); - return eve::Result::success( - {*nextTick, std::move(duration).takeValue()}); + return eve::Result::success({*nextTick, std::move(duration).takeValue()}); } } // namespace @@ -221,18 +218,16 @@ World::World(float gravityX, float gravityY, bool sleep, float meter) : meter_(m world_->SetAllowSleeping(sleep); relay_ = new ContactRelay(this); world_->SetContactListener(relay_); - auto selection = detail::selectSimulationBackend( - SimulationBackendDomain::World2D, - detail::makeBox2DSimulationBackend(world_), world_, true); + auto selection = detail::selectSimulationBackend(SimulationBackendDomain::World2D, + detail::makeBox2DSimulationBackend(world_), world_, true); if (!selection) { const eve::Status status = selection.status(); - throw eve::Exception("World: cannot select a simulation backend: %s", - status.describe().c_str()); + throw eve::Exception("World: cannot select a simulation backend: %s", status.describe().c_str()); } backendSelectionStatus_ = selection.status(); - auto selected = std::move(selection).takeValue(); - backendFallback_ = selected.usedFallback; - simulation_ = std::move(selected.backend); + auto selected = std::move(selection).takeValue(); + backendFallback_ = selected.usedFallback; + simulation_ = std::move(selected.backend); } World::~World() { destroy(); } @@ -327,21 +322,18 @@ void World::updateFull(float dt, int velocityIterations, int positionIterations) legacyStep.ignore("legacy World::updateFull cannot return a structured error"); return; } - auto result = step(std::move(legacyStep).takeValue(), - SimulationSettings{velocityIterations, positionIterations, 4}); + auto result = + step(std::move(legacyStep).takeValue(), SimulationSettings{velocityIterations, positionIterations, 4}); result.ignore("legacy World::updateFull cannot return a structured result"); } -eve::Result World::step(const eve::SimulationStep& stepValue, - const SimulationSettings& settings) { +eve::Result World::step(const eve::SimulationStep &stepValue, const SimulationSettings &settings) { if (!isValid() || !simulation_) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, - "Cannot step a destroyed or uninitialized physics world", - "physics.world.step")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, + "Cannot step a destroyed or uninitialized physics world", "physics.world.step")); } - auto valid = detail::validateSimulationStep( - stepValue, settings, simulation_->observation()); + auto valid = detail::validateSimulationStep(stepValue, settings, simulation_->observation()); if (!valid) return valid; auto result = simulation_->step(stepValue, settings); if (!result) return result; @@ -404,7 +396,7 @@ Body *World::newBody(const std::string &bodyType, float x, float y) { const PhysicsBodyHandle runtimeHandle = nextBodyRuntimeHandle(); b2Body *raw = world_->CreateBody(&def); - Body *body = new Body(this, raw, nextBodyId(), runtimeHandle); + Body *body = new Body(this, raw, nextBodyId(), runtimeHandle); raw->SetUserData(body); bodies_.insert(body); return body; @@ -470,7 +462,7 @@ void World::onBeginContact(b2Contact *contact) { auto *ev = eve::ModuleManager::getInstance("PlatformEvent"); if (!ev) return; std::vector args = {eve::platform_event::Variant::makeInt(a->getId()), - eve::platform_event::Variant::makeInt(b->getId())}; + eve::platform_event::Variant::makeInt(b->getId())}; ev->push(new eve::platform_event::Message("begincontact", args)); } @@ -487,7 +479,7 @@ void World::onEndContact(b2Contact *contact) { auto *ev = eve::ModuleManager::getInstance("PlatformEvent"); if (!ev) return; std::vector args = {eve::platform_event::Variant::makeInt(a->getId()), - eve::platform_event::Variant::makeInt(b->getId())}; + eve::platform_event::Variant::makeInt(b->getId())}; ev->push(new eve::platform_event::Message("endcontact", args)); } diff --git a/src/modules/physics/World.h b/src/modules/physics/World.h index d6a0f120..4bfeeff2 100644 --- a/src/modules/physics/World.h +++ b/src/modules/physics/World.h @@ -7,8 +7,8 @@ #include #include #include -#include #include +#include #include class b2World; @@ -88,8 +88,7 @@ public: * @remarks A rejected step leaves solver state and the current tick unchanged. */ [[nodiscard("check the physics step outcome or explicitly ignore it")]] - eve::Result step(const eve::SimulationStep& step, - const SimulationSettings& settings = {}); + eve::Result step(const eve::SimulationStep &step, const SimulationSettings &settings = {}); /** @brief Snapshot of completed backend steps and logical simulation time. */ [[nodiscard]] SimulationObservation simulationObservation() const noexcept; @@ -117,8 +116,7 @@ public: * @remarks The provider is not retained; this call is owner-thread-only. */ [[nodiscard("check or persist the physics snapshot")]] - eve::Result snapshot( - const eve::SnapshotHashProvider& hashProvider) const; + eve::Result snapshot(const eve::SnapshotHashProvider &hashProvider) const; /** * @brief Restores a verified snapshot without exposing partial state. @@ -129,8 +127,7 @@ public: * never persisted or reused from its payload. */ [[nodiscard("check the physics snapshot restore outcome")]] - eve::Result restore(const eve::SnapshotEnvelope& snapshot, - const eve::SnapshotHashProvider& hashProvider); + eve::Result restore(const eve::SnapshotEnvelope &snapshot, const eve::SnapshotHashProvider &hashProvider); /** @brief Sets the world gravity vector in pixels/s^2. */ void setGravity(float gx, float gy); @@ -260,14 +257,14 @@ private: b2World *world_ = nullptr; ContactRelay *relay_ = nullptr; std::unique_ptr simulation_; - PhysicsWorldHandle runtimeHandle_ = PhysicsWorldHandle::invalid(); + PhysicsWorldHandle runtimeHandle_ = PhysicsWorldHandle::invalid(); float meter_ = 30.f; int nextId_ = 1; - std::uint32_t nextBodyHandleIndex_ = 1u; + std::uint32_t nextBodyHandleIndex_ = 1u; bool destroyed_ = false; - eve::SimulationTick simulationTick_ = eve::SimulationTick::zero(); - eve::Status backendSelectionStatus_ = eve::Status::success(); - bool backendFallback_ = false; + eve::SimulationTick simulationTick_ = eve::SimulationTick::zero(); + eve::Status backendSelectionStatus_ = eve::Status::success(); + bool backendFallback_ = false; std::unordered_set bodies_; std::unordered_set fixtures_; diff --git a/src/modules/physics/World3D.cpp b/src/modules/physics/World3D.cpp index 780e6a0e..1d46c869 100644 --- a/src/modules/physics/World3D.cpp +++ b/src/modules/physics/World3D.cpp @@ -5,8 +5,8 @@ #include "physics/Joint3D.h" #include "common/Exception.h" -#include "platform_event/PlatformEvent.h" #include "common/Profile.h" +#include "platform_event/PlatformEvent.h" #include @@ -100,30 +100,26 @@ void makeBoxProxyPoints(b3Vec3 (&points)[8], float width, float height, float de static_cast(z) * hz}); } -void stepBox3D(void* context, const eve::SimulationStep& step, - const SimulationSettings& settings) noexcept { - auto* worldId = static_cast(context); +void stepBox3D(void *context, const eve::SimulationStep &step, const SimulationSettings &settings) noexcept { + auto *worldId = static_cast(context); b3World_Step(*worldId, static_cast(step.delta.seconds()), settings.subStepCount); } eve::Result makeLegacyStep(float dt, eve::SimulationTick currentTick) { if (!std::isfinite(dt) || dt < 0.f) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "World3D update dt must be finite and non-negative", - "physics.world3d.update.dt")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "World3D update dt must be finite and non-negative", "physics.world3d.update.dt")); } const auto nextTick = currentTick.incremented(); if (!nextTick) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, - "World3D simulation tick cannot be incremented", - "physics.world3d.simulationTick")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, + "World3D simulation tick cannot be incremented", "physics.world3d.simulationTick")); } auto duration = eve::Duration::fromSeconds(static_cast(std::min(dt, 0.05f))); if (!duration) return eve::Result::failure(duration.status()); - return eve::Result::success( - {*nextTick, std::move(duration).takeValue()}); + return eve::Result::success({*nextTick, std::move(duration).takeValue()}); } } // namespace @@ -135,23 +131,21 @@ World3D::World3D(float gravityX, float gravityY, float gravityZ, bool sleep) { contactHertz_ = def.contactHertz; contactDampingRatio_ = def.contactDampingRatio; contactPushOutSpeed_ = def.contactSpeed; - runtimeHandle_ = detail::allocatePhysicsWorldHandle(); + runtimeHandle_ = detail::allocatePhysicsWorldHandle(); worldId_ = b3CreateWorld(&def); - auto selection = detail::selectSimulationBackend( + auto selection = detail::selectSimulationBackend( SimulationBackendDomain::World3D, - detail::makeCallbackSimulationBackend( - &worldId_, &stepBox3D, SimulationBackendKind::Cpu, - SimulationDeterminism::ToleranceBounded), + detail::makeCallbackSimulationBackend(&worldId_, &stepBox3D, SimulationBackendKind::Cpu, + SimulationDeterminism::ToleranceBounded), &worldId_, true); if (!selection) { const eve::Status status = selection.status(); - throw eve::Exception("World3D: cannot select a simulation backend: %s", - status.describe().c_str()); + throw eve::Exception("World3D: cannot select a simulation backend: %s", status.describe().c_str()); } backendSelectionStatus_ = selection.status(); - auto selected = std::move(selection).takeValue(); - backendFallback_ = selected.usedFallback; - simulation_ = std::move(selected.backend); + auto selected = std::move(selection).takeValue(); + backendFallback_ = selected.usedFallback; + simulation_ = std::move(selected.backend); registerCameraObstructionWorld(this); auto targetingRegistration = registerTargetingLineOfSightWorld(this); if (!targetingRegistration) { @@ -454,8 +448,7 @@ Joint3D *World3D::newDistanceJoint(Body3D *bodyA, Body3D *bodyB, float anchorAX, def.length = length; const PhysicsJointHandle runtimeHandle = nextJointRuntimeHandle(); b3JointId id = b3CreateDistanceJoint(worldId_, &def); - auto *joint = new Joint3D(this, bodyA, bodyB, id, runtimeHandle, - Joint3D::Kind::Distance, nextJointId()); + auto *joint = new Joint3D(this, bodyA, bodyB, id, runtimeHandle, Joint3D::Kind::Distance, nextJointId()); b3Joint_SetUserData(id, joint); joints_.insert(joint); jointHandles_[runtimeHandle] = joint; @@ -490,8 +483,7 @@ Joint3D *World3D::newRevoluteJoint(Body3D *bodyA, Body3D *bodyB, float anchorX, def.base.collideConnected = collideConnected; const PhysicsJointHandle runtimeHandle = nextJointRuntimeHandle(); b3JointId id = b3CreateRevoluteJoint(worldId_, &def); - auto *joint = new Joint3D(this, bodyA, bodyB, id, runtimeHandle, - Joint3D::Kind::Revolute, nextJointId()); + auto *joint = new Joint3D(this, bodyA, bodyB, id, runtimeHandle, Joint3D::Kind::Revolute, nextJointId()); b3Joint_SetUserData(id, joint); joints_.insert(joint); jointHandles_[runtimeHandle] = joint; @@ -526,8 +518,7 @@ Joint3D *World3D::newPrismaticJoint(Body3D *bodyA, Body3D *bodyB, float anchorX, def.base.collideConnected = collideConnected; const PhysicsJointHandle runtimeHandle = nextJointRuntimeHandle(); const b3JointId id = b3CreatePrismaticJoint(worldId_, &def); - auto *joint = new Joint3D(this, bodyA, bodyB, id, runtimeHandle, Joint3D::Kind::Prismatic, - nextJointId()); + auto *joint = new Joint3D(this, bodyA, bodyB, id, runtimeHandle, Joint3D::Kind::Prismatic, nextJointId()); b3Joint_SetUserData(id, joint); joints_.insert(joint); jointHandles_[runtimeHandle] = joint; @@ -562,8 +553,7 @@ Joint3D *World3D::newSphericalJoint(Body3D *bodyA, Body3D *bodyB, float anchorX, def.base.collideConnected = collideConnected; const PhysicsJointHandle runtimeHandle = nextJointRuntimeHandle(); const b3JointId id = b3CreateSphericalJoint(worldId_, &def); - auto *joint = new Joint3D(this, bodyA, bodyB, id, runtimeHandle, Joint3D::Kind::Spherical, - nextJointId()); + auto *joint = new Joint3D(this, bodyA, bodyB, id, runtimeHandle, Joint3D::Kind::Spherical, nextJointId()); b3Joint_SetUserData(id, joint); joints_.insert(joint); jointHandles_[runtimeHandle] = joint; @@ -610,8 +600,7 @@ Joint3D *World3D::newWheelJoint(Body3D *bodyA, Body3D *bodyB, float anchorX, def.base.collideConnected = collideConnected; const PhysicsJointHandle runtimeHandle = nextJointRuntimeHandle(); const b3JointId id = b3CreateWheelJoint(worldId_, &def); - auto *joint = - new Joint3D(this, bodyA, bodyB, id, runtimeHandle, Joint3D::Kind::Wheel, nextJointId()); + auto *joint = new Joint3D(this, bodyA, bodyB, id, runtimeHandle, Joint3D::Kind::Wheel, nextJointId()); b3Joint_SetUserData(id, joint); joints_.insert(joint); jointHandles_[runtimeHandle] = joint; @@ -639,21 +628,17 @@ void World3D::updateFull(float dt, int subStepCount) { legacyStep.ignore("legacy World3D::updateFull cannot return a structured error"); return; } - auto result = step(std::move(legacyStep).takeValue(), - SimulationSettings{8, 3, subStepCount}); + auto result = step(std::move(legacyStep).takeValue(), SimulationSettings{8, 3, subStepCount}); result.ignore("legacy World3D::updateFull cannot return a structured result"); } -eve::Result World3D::step(const eve::SimulationStep& stepValue, - const SimulationSettings& settings) { +eve::Result World3D::step(const eve::SimulationStep &stepValue, const SimulationSettings &settings) { if (!isValid() || !simulation_) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, - "Cannot step a destroyed or uninitialized 3D physics world", + eve::DiagnosticCode::PreconditionViolation, "Cannot step a destroyed or uninitialized 3D physics world", "physics.world3d.step")); } - auto valid = detail::validateSimulationStep( - stepValue, settings, simulation_->observation()); + auto valid = detail::validateSimulationStep(stepValue, settings, simulation_->observation()); if (!valid) return valid; clearContactEvents(); for (Shape3D *shape : shapes_) { @@ -817,7 +802,7 @@ Body3D *World3D::newBody(const std::string &bodyType, float x, float y, float z) const PhysicsBodyHandle runtimeHandle = nextBodyRuntimeHandle(); b3BodyId raw = b3CreateBody(worldId_, &def); - Body3D *body = new Body3D(this, raw, nextBodyId(), runtimeHandle); + Body3D *body = new Body3D(this, raw, nextBodyId(), runtimeHandle); b3Body_SetUserData(raw, body); bodies_.insert(body); return body; @@ -902,16 +887,21 @@ void World3D::forgetShape(Shape3D *shape) { const PhysicsShapeHandle handle = shape->runtimeHandle(); if (handle.isValid()) shapeHandles_.erase(handle); for (auto it = shapeHandles_.begin(); it != shapeHandles_.end();) { - if (it->second == shape) it = shapeHandles_.erase(it); else ++it; + if (it->second == shape) + it = shapeHandles_.erase(it); + else + ++it; } for (auto it = shapeRawHandles_.begin(); it != shapeRawHandles_.end();) { - if (it->second == handle) it = shapeRawHandles_.erase(it); else ++it; + if (it->second == handle) + it = shapeRawHandles_.erase(it); + else + ++it; } } void World3D::registerShapeHandle(Shape3D *shape) { - if (!shape || !shape->getBody() || B3_IS_NULL(shape->raw()) || - shape->runtimeHandle().isInvalid()) return; + if (!shape || !shape->getBody() || B3_IS_NULL(shape->raw()) || shape->runtimeHandle().isInvalid()) return; const PhysicsShapeHandle handle = shape->runtimeHandle(); for (auto it = shapeHandles_.begin(); it != shapeHandles_.end();) { if (it->second == shape || it->first == handle) @@ -920,13 +910,15 @@ void World3D::registerShapeHandle(Shape3D *shape) { ++it; } for (auto it = shapeRawHandles_.begin(); it != shapeRawHandles_.end();) { - if (it->second == handle) it = shapeRawHandles_.erase(it); else ++it; + if (it->second == handle) + it = shapeRawHandles_.erase(it); + else + ++it; } - const uint64_t rawKey = b3StoreShapeId(shape->raw()); - shapeHandles_[handle] = shape; + const uint64_t rawKey = b3StoreShapeId(shape->raw()); + shapeHandles_[handle] = shape; shapeRawHandles_[rawKey] = handle; - shapeRecords_[rawKey] = - EventShape{shape->getBody()->getId(), shape->getId(), shape->getTag()}; + shapeRecords_[rawKey] = EventShape{shape->getBody()->getId(), shape->getId(), shape->getTag()}; } bool World3D::preSolveCallback(b3ShapeId shapeIdA, b3ShapeId shapeIdB, b3Pos point, @@ -1074,8 +1066,8 @@ void World3D::emitContactEvents() { beginContacts_.push_back({a, b}); if (ev) { std::vector args = { - eve::platform_event::Variant::makeInt(a.bodyId), eve::platform_event::Variant::makeInt(b.bodyId), - eve::platform_event::Variant::makeInt(a.shapeId), eve::platform_event::Variant::makeInt(b.shapeId), + eve::platform_event::Variant::makeInt(a.bodyId), eve::platform_event::Variant::makeInt(b.bodyId), + eve::platform_event::Variant::makeInt(a.shapeId), eve::platform_event::Variant::makeInt(b.shapeId), eve::platform_event::Variant::makeInt(a.shapeTag), eve::platform_event::Variant::makeInt(b.shapeTag)}; ev->push(new eve::platform_event::Message("begincontact3d", args)); } @@ -1088,8 +1080,8 @@ void World3D::emitContactEvents() { endContacts_.push_back({a, b}); if (ev) { std::vector args = { - eve::platform_event::Variant::makeInt(a.bodyId), eve::platform_event::Variant::makeInt(b.bodyId), - eve::platform_event::Variant::makeInt(a.shapeId), eve::platform_event::Variant::makeInt(b.shapeId), + eve::platform_event::Variant::makeInt(a.bodyId), eve::platform_event::Variant::makeInt(b.bodyId), + eve::platform_event::Variant::makeInt(a.shapeId), eve::platform_event::Variant::makeInt(b.shapeId), eve::platform_event::Variant::makeInt(a.shapeTag), eve::platform_event::Variant::makeInt(b.shapeTag)}; ev->push(new eve::platform_event::Message("endcontact3d", args)); } @@ -1128,8 +1120,8 @@ void World3D::emitContactEvents() { normalImpulse}); if (ev) { std::vector args = { - eve::platform_event::Variant::makeInt(a.bodyId), eve::platform_event::Variant::makeInt(b.bodyId), - eve::platform_event::Variant::makeInt(a.shapeId), eve::platform_event::Variant::makeInt(b.shapeId), + eve::platform_event::Variant::makeInt(a.bodyId), eve::platform_event::Variant::makeInt(b.bodyId), + eve::platform_event::Variant::makeInt(a.shapeId), eve::platform_event::Variant::makeInt(b.shapeId), eve::platform_event::Variant::makeInt(a.shapeTag), eve::platform_event::Variant::makeInt(b.shapeTag)}; ev->push(new eve::platform_event::Message("hit3d", args)); } @@ -1142,13 +1134,12 @@ void World3D::emitContactEvents() { if (sensor.shapeId == 0 || visitor.shapeId == 0) continue; beginTriggers_.push_back({sensor, visitor}); if (ev) { - std::vector args = { - eve::platform_event::Variant::makeInt(sensor.bodyId), - eve::platform_event::Variant::makeInt(visitor.bodyId), - eve::platform_event::Variant::makeInt(sensor.shapeId), - eve::platform_event::Variant::makeInt(visitor.shapeId), - eve::platform_event::Variant::makeInt(sensor.shapeTag), - eve::platform_event::Variant::makeInt(visitor.shapeTag)}; + std::vector args = {eve::platform_event::Variant::makeInt(sensor.bodyId), + eve::platform_event::Variant::makeInt(visitor.bodyId), + eve::platform_event::Variant::makeInt(sensor.shapeId), + eve::platform_event::Variant::makeInt(visitor.shapeId), + eve::platform_event::Variant::makeInt(sensor.shapeTag), + eve::platform_event::Variant::makeInt(visitor.shapeTag)}; ev->push(new eve::platform_event::Message("begintrigger3d", args)); } } @@ -1158,13 +1149,12 @@ void World3D::emitContactEvents() { if (sensor.shapeId == 0 || visitor.shapeId == 0) continue; endTriggers_.push_back({sensor, visitor}); if (ev) { - std::vector args = { - eve::platform_event::Variant::makeInt(sensor.bodyId), - eve::platform_event::Variant::makeInt(visitor.bodyId), - eve::platform_event::Variant::makeInt(sensor.shapeId), - eve::platform_event::Variant::makeInt(visitor.shapeId), - eve::platform_event::Variant::makeInt(sensor.shapeTag), - eve::platform_event::Variant::makeInt(visitor.shapeTag)}; + std::vector args = {eve::platform_event::Variant::makeInt(sensor.bodyId), + eve::platform_event::Variant::makeInt(visitor.bodyId), + eve::platform_event::Variant::makeInt(sensor.shapeId), + eve::platform_event::Variant::makeInt(visitor.shapeId), + eve::platform_event::Variant::makeInt(sensor.shapeTag), + eve::platform_event::Variant::makeInt(visitor.shapeTag)}; ev->push(new eve::platform_event::Message("endtrigger3d", args)); } } diff --git a/src/modules/physics/World3D.h b/src/modules/physics/World3D.h index c4ddd7a1..14b14935 100644 --- a/src/modules/physics/World3D.h +++ b/src/modules/physics/World3D.h @@ -4,11 +4,11 @@ #include "physics/PhysicsHandles.h" #include "physics/SimulationBackend.h" -#include #include #include -#include +#include #include +#include #include #include @@ -103,8 +103,7 @@ public: * @remarks A rejected step leaves solver state and the current tick unchanged. */ [[nodiscard("check the physics step outcome or explicitly ignore it")]] - eve::Result step(const eve::SimulationStep& step, - const SimulationSettings& settings = {}); + eve::Result step(const eve::SimulationStep &step, const SimulationSettings &settings = {}); /** @brief Snapshot of completed backend steps and logical simulation time. */ [[nodiscard]] SimulationObservation simulationObservation() const noexcept; @@ -129,8 +128,7 @@ public: * @remarks The provider is not retained; this call is owner-thread-only. */ [[nodiscard("check or persist the physics snapshot")]] - eve::Result snapshot( - const eve::SnapshotHashProvider& hashProvider) const; + eve::Result snapshot(const eve::SnapshotHashProvider &hashProvider) const; /** * @brief Restores a verified snapshot without exposing partial state. @@ -141,8 +139,7 @@ public: * never persisted or reused from its payload. */ [[nodiscard("check the physics snapshot restore outcome")]] - eve::Result restore(const eve::SnapshotEnvelope& snapshot, - const eve::SnapshotHashProvider& hashProvider); + eve::Result restore(const eve::SnapshotEnvelope &snapshot, const eve::SnapshotHashProvider &hashProvider); /** @brief Gravity in m/s². */ void setGravity(float gx, float gy, float gz); @@ -274,7 +271,8 @@ public: * @brief Creates a body in meter-space units. * @return Borrowed nullable body owned by this world; null means creation failed. * @ownership World3D owns the body and its shapes; callers must destroy it through this API. - * @lifetime Valid until Body3D::destroy(), World3D::destroy(), or world teardown; use PhysicsBodyHandle across frames. + * @lifetime Valid until Body3D::destroy(), World3D::destroy(), or world teardown; use PhysicsBodyHandle across + * frames. * @thread Call on the owning physics thread. * @reentrancy Creation does not invoke callbacks; do not re-enter structural world mutation while using the result. */ @@ -289,7 +287,8 @@ public: * @brief Connects two world-space anchor points with a rigid distance constraint. * @return Borrowed nullable joint owned by this world; null means creation failed. * @ownership World3D owns the joint; body pointers are borrowed inputs and are never retained after validation. - * @lifetime Valid until Joint3D::destroy(), World3D::destroy(), or dependent body destruction; use PhysicsJointHandle across frames. + * @lifetime Valid until Joint3D::destroy(), World3D::destroy(), or dependent body destruction; use + * PhysicsJointHandle across frames. * @thread Call on the owning physics thread. * @reentrancy Creation does not invoke callbacks; do not mutate either body re-entrantly. * @throws eve::Exception for invalid bodies, anchors, length, or cross-world bodies. @@ -301,7 +300,8 @@ public: * @brief Creates a hinge around a normalized world-space axis at a shared anchor. * @return Borrowed nullable joint owned by this world; null means creation failed. * @ownership World3D owns the joint; body pointers are borrowed inputs and are not retained as caller ownership. - * @lifetime Valid until Joint3D::destroy(), World3D::destroy(), or dependent body destruction; use PhysicsJointHandle across frames. + * @lifetime Valid until Joint3D::destroy(), World3D::destroy(), or dependent body destruction; use + * PhysicsJointHandle across frames. * @thread Call on the owning physics thread. * @reentrancy Creation does not invoke callbacks; do not mutate either body re-entrantly. * @throws eve::Exception for invalid/cross-world bodies or a zero/non-finite axis. @@ -313,7 +313,8 @@ public: * @brief Creates a slider whose permitted world-space translation follows axis. * @return Borrowed nullable joint owned by this world; null means creation failed. * @ownership World3D owns the joint; body pointers are borrowed inputs and are not retained as caller ownership. - * @lifetime Valid until Joint3D::destroy(), World3D::destroy(), or dependent body destruction; use PhysicsJointHandle across frames. + * @lifetime Valid until Joint3D::destroy(), World3D::destroy(), or dependent body destruction; use + * PhysicsJointHandle across frames. * @thread Call on the owning physics thread. * @reentrancy Creation does not invoke callbacks; do not mutate either body re-entrantly. */ @@ -324,7 +325,8 @@ public: * @brief Creates a ball-and-socket joint with a world-space twist/cone axis. * @return Borrowed nullable joint owned by this world; null means creation failed. * @ownership World3D owns the joint; body pointers are borrowed inputs and are not retained as caller ownership. - * @lifetime Valid until Joint3D::destroy(), World3D::destroy(), or dependent body destruction; use PhysicsJointHandle across frames. + * @lifetime Valid until Joint3D::destroy(), World3D::destroy(), or dependent body destruction; use + * PhysicsJointHandle across frames. * @thread Call on the owning physics thread. * @reentrancy Creation does not invoke callbacks; do not mutate either body re-entrantly. */ @@ -335,7 +337,8 @@ public: * @brief Creates a vehicle wheel joint with independent world suspension and spin axes. * @return Borrowed nullable joint owned by this world; null means creation failed. * @ownership World3D owns the joint; body pointers are borrowed inputs and are not retained as caller ownership. - * @lifetime Valid until Joint3D::destroy(), World3D::destroy(), or dependent body destruction; use PhysicsJointHandle across frames. + * @lifetime Valid until Joint3D::destroy(), World3D::destroy(), or dependent body destruction; use + * PhysicsJointHandle across frames. * @thread Call on the owning physics thread. * @reentrancy Creation does not invoke callbacks; do not mutate either body re-entrantly. */ @@ -820,22 +823,22 @@ private: friend class Joint3D; friend class Shape3D; friend class TargetingLineOfSightAdapter; - friend void registerCameraObstructionWorld(World3D* world); + friend void registerCameraObstructionWorld(World3D *world); b3WorldId worldId_{}; std::unique_ptr simulation_; - PhysicsWorldHandle runtimeHandle_ = PhysicsWorldHandle::invalid(); - std::shared_ptr queryLifetime_ = std::make_shared(0); + PhysicsWorldHandle runtimeHandle_ = PhysicsWorldHandle::invalid(); + std::shared_ptr queryLifetime_ = std::make_shared(0); bool destroyed_ = false; int nextId_ = 1; int nextShapeId_ = 1; int nextJointId_ = 1; - std::uint32_t nextBodyHandleIndex_ = 1u; - std::uint32_t nextShapeHandleIndex_ = 1u; - std::uint32_t nextJointHandleIndex_ = 1u; - eve::SimulationTick simulationTick_ = eve::SimulationTick::zero(); - eve::Status backendSelectionStatus_ = eve::Status::success(); - bool backendFallback_ = false; + std::uint32_t nextBodyHandleIndex_ = 1u; + std::uint32_t nextShapeHandleIndex_ = 1u; + std::uint32_t nextJointHandleIndex_ = 1u; + eve::SimulationTick simulationTick_ = eve::SimulationTick::zero(); + eve::Status backendSelectionStatus_ = eve::Status::success(); + bool backendFallback_ = false; std::unordered_set bodies_; std::unordered_set shapes_; @@ -844,7 +847,7 @@ private: std::unordered_set disabledShapePairs_; std::unordered_map shapeRecords_; std::unordered_map shapeHandles_; - std::unordered_map shapeRawHandles_; + std::unordered_map shapeRawHandles_; std::unordered_map jointHandles_; std::vector beginContacts_; std::vector endContacts_; diff --git a/src/modules/physics/WorldDebugDraw.cpp b/src/modules/physics/WorldDebugDraw.cpp index 717ff47e..cd4b179e 100644 --- a/src/modules/physics/WorldDebugDraw.cpp +++ b/src/modules/physics/WorldDebugDraw.cpp @@ -24,19 +24,17 @@ public: DebugDraw() { SetFlags(e_shapeBit | e_jointBit | e_aabbBit); } void begin(graphics::Graphics *gfx, float meter) { - gfx_ = gfx; + gfx_ = gfx; meter_ = meter; } void end() { gfx_ = nullptr; } - void DrawPolygon(const b2Vec2 *vertices, int32 vertexCount, - const b2Color &color) override { + void DrawPolygon(const b2Vec2 *vertices, int32 vertexCount, const b2Color &color) override { drawPoly(vertices, vertexCount, color, false); } - void DrawSolidPolygon(const b2Vec2 *vertices, int32 vertexCount, - const b2Color &color) override { + void DrawSolidPolygon(const b2Vec2 *vertices, int32 vertexCount, const b2Color &color) override { drawPoly(vertices, vertexCount, color, true); } @@ -44,22 +42,20 @@ public: drawCircle(center, radius, color, false); } - void DrawSolidCircle(const b2Vec2 ¢er, float32 radius, const b2Vec2 & /*axis*/, - const b2Color &color) override { + void DrawSolidCircle(const b2Vec2 ¢er, float32 radius, const b2Vec2 & /*axis*/, const b2Color &color) override { drawCircle(center, radius, color, true); } void DrawSegment(const b2Vec2 &p1, const b2Vec2 &p2, const b2Color &color) override { - if (!gfx_) - return; - float x1 = p1.x * meter_; - float y1 = p1.y * meter_; - float x2 = p2.x * meter_; - float y2 = p2.y * meter_; + if (!gfx_) return; + float x1 = p1.x * meter_; + float y1 = p1.y * meter_; + float x2 = p2.x * meter_; + float y2 = p2.y * meter_; float minx = std::min(x1, x2); float miny = std::min(y1, y2); - float w = std::max(1.f, std::fabs(x2 - x1)); - float h = std::max(1.f, std::fabs(y2 - y1)); + float w = std::max(1.f, std::fabs(x2 - x1)); + float h = std::max(1.f, std::fabs(y2 - y1)); gfx_->drawSolidRect(minx, miny, w, h, Color(color.r, color.g, color.b, color.a * 0.8f)); } @@ -70,8 +66,7 @@ public: private: void drawPoly(const b2Vec2 *vertices, int32 vertexCount, const b2Color &color, bool solid) { - if (!gfx_ || vertexCount < 2) - return; + if (!gfx_ || vertexCount < 2) return; for (int32 i = 0; i < vertexCount; ++i) { const b2Vec2 &a = vertices[i]; const b2Vec2 &b = vertices[(i + 1) % vertexCount]; @@ -88,24 +83,22 @@ private: miny = std::min(miny, vertices[i].y); maxy = std::max(maxy, vertices[i].y); } - gfx_->drawSolidRect(minx * meter_, miny * meter_, (maxx - minx) * meter_, - (maxy - miny) * meter_, + gfx_->drawSolidRect(minx * meter_, miny * meter_, (maxx - minx) * meter_, (maxy - miny) * meter_, Color(color.r, color.g, color.b, color.a * 0.25f)); } } void drawCircle(const b2Vec2 ¢er, float32 radius, const b2Color &color, bool solid) { - if (!gfx_) - return; + if (!gfx_) return; float px = (center.x - radius) * meter_; float py = (center.y - radius) * meter_; - float d = radius * 2.f * meter_; - float a = solid ? color.a * 0.35f : color.a * 0.7f; + float d = radius * 2.f * meter_; + float a = solid ? color.a * 0.35f : color.a * 0.7f; gfx_->drawSolidRect(px, py, d, d, Color(color.r, color.g, color.b, a)); } - graphics::Graphics *gfx_ = nullptr; - float meter_ = 30.f; + graphics::Graphics *gfx_ = nullptr; + float meter_ = 30.f; }; class DebugDrawBinding final { @@ -113,7 +106,7 @@ public: DebugDrawBinding(b2World *world, b2Draw *draw) : world_(world) { world_->SetDebugDraw(draw); } ~DebugDrawBinding() { world_->SetDebugDraw(nullptr); } - DebugDrawBinding(const DebugDrawBinding &) = delete; + DebugDrawBinding(const DebugDrawBinding &) = delete; DebugDrawBinding &operator=(const DebugDrawBinding &) = delete; private: @@ -124,8 +117,7 @@ private: void World::drawDebug(graphics::Graphics *gfx) { b2World *world = raw(); - if (!world || !isValid() || !gfx) - return; + if (!world || !isValid() || !gfx) return; DebugDraw draw; draw.begin(gfx, getMeter()); diff --git a/src/modules/physics/WorldSnapshot.cpp b/src/modules/physics/WorldSnapshot.cpp index 52cf05aa..41d78c7e 100644 --- a/src/modules/physics/WorldSnapshot.cpp +++ b/src/modules/physics/WorldSnapshot.cpp @@ -21,21 +21,18 @@ namespace eve::physics { namespace { -constexpr std::string_view kWorld2DType = "physics.world2d"; -constexpr std::string_view kWorld2DSchema = "physics:world2d"; -constexpr std::string_view kWorld3DType = "physics.world3d"; -constexpr std::string_view kWorld3DSchema = "physics:world3d"; -constexpr std::uint64_t kSnapshotVersion = 1; +constexpr std::string_view kWorld2DType = "physics.world2d"; +constexpr std::string_view kWorld2DSchema = "physics:world2d"; +constexpr std::string_view kWorld3DType = "physics.world3d"; +constexpr std::string_view kWorld3DSchema = "physics:world3d"; +constexpr std::uint64_t kSnapshotVersion = 1; template -eve::Result failure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { - return eve::Result::failure( - eve::Diagnostic::error(code, std::move(message), std::move(path))); +eve::Result failure(eve::DiagnosticCode code, std::string message, std::string path = {}) { + return eve::Result::failure(eve::Diagnostic::error(code, std::move(message), std::move(path))); } -bool hasExactFields(const eve::Value::Object& object, - std::initializer_list expected) { +bool hasExactFields(const eve::Value::Object& object, std::initializer_list expected) { if (object.size() != expected.size()) return false; for (const std::string_view field : expected) { if (!object.contains(std::string(field))) return false; @@ -48,79 +45,62 @@ const eve::Value* field(const eve::Value::Object& object, std::string_view name) return found == object.end() ? nullptr : &found->second; } -eve::Result readString(const eve::Value::Object& object, - std::string_view name) { +eve::Result readString(const eve::Value::Object& object, std::string_view name) { const eve::Value* value = field(object, name); if (!value || !value->isString()) - return failure(eve::DiagnosticCode::ParseError, - "snapshot field must be a string", + return failure(eve::DiagnosticCode::ParseError, "snapshot field must be a string", std::string(name)); return eve::Result::success(value->asString()); } -eve::Result readUint64(const eve::Value::Object& object, - std::string_view name) { +eve::Result readUint64(const eve::Value::Object& object, std::string_view name) { auto text = readString(object, name); if (!text) return eve::Result::failure(text.status()); - std::uint64_t output = 0; - const std::string& value = text.value(); - const auto [end, error] = - std::from_chars(value.data(), value.data() + value.size(), output); + std::uint64_t output = 0; + const std::string& value = text.value(); + const auto [end, error] = std::from_chars(value.data(), value.data() + value.size(), output); if (value.empty() || error != std::errc{} || end != value.data() + value.size()) return failure(eve::DiagnosticCode::ParseError, - "snapshot integer is not a uint64 decimal string", - std::string(name)); + "snapshot integer is not a uint64 decimal string", std::string(name)); return eve::Result::success(output); } -eve::Result readInt64(const eve::Value::Object& object, - std::string_view name) { +eve::Result readInt64(const eve::Value::Object& object, std::string_view name) { auto text = readString(object, name); if (!text) return eve::Result::failure(text.status()); - std::int64_t output = 0; - const std::string& value = text.value(); - const auto [end, error] = - std::from_chars(value.data(), value.data() + value.size(), output); + std::int64_t output = 0; + const std::string& value = text.value(); + const auto [end, error] = std::from_chars(value.data(), value.data() + value.size(), output); if (value.empty() || error != std::errc{} || end != value.data() + value.size()) - return failure(eve::DiagnosticCode::ParseError, - "snapshot integer is not an int64 decimal string", + return failure(eve::DiagnosticCode::ParseError, "snapshot integer is not an int64 decimal string", std::string(name)); return eve::Result::success(output); } -eve::Result readNumber(const eve::Value::Object& object, - std::string_view name) { +eve::Result readNumber(const eve::Value::Object& object, std::string_view name) { const eve::Value* value = field(object, name); if (!value || !value->isNumeric()) - return failure(eve::DiagnosticCode::ParseError, - "snapshot field must be numeric", std::string(name)); - const double result = value->isDouble() ? value->asDouble() - : static_cast(value->asInt()); + return failure(eve::DiagnosticCode::ParseError, "snapshot field must be numeric", std::string(name)); + const double result = value->isDouble() ? value->asDouble() : static_cast(value->asInt()); if (!std::isfinite(result)) - return failure(eve::DiagnosticCode::InvalidArgument, - "snapshot numeric field must be finite", + return failure(eve::DiagnosticCode::InvalidArgument, "snapshot numeric field must be finite", std::string(name)); return eve::Result::success(result); } -eve::Result readFloat(const eve::Value::Object& object, - std::string_view name) { +eve::Result readFloat(const eve::Value::Object& object, std::string_view name) { auto number = readNumber(object, name); if (!number) return eve::Result::failure(number.status()); - if (number.value() < -std::numeric_limits::max() || - number.value() > std::numeric_limits::max()) - return failure(eve::DiagnosticCode::InvalidArgument, - "snapshot numeric field is outside float range", + if (number.value() < -std::numeric_limits::max() || number.value() > std::numeric_limits::max()) + return failure(eve::DiagnosticCode::InvalidArgument, "snapshot numeric field is outside float range", std::string(name)); return eve::Result::success(static_cast(number.value())); } -eve::Result readBool(const eve::Value::Object& object, - std::string_view name) { +eve::Result readBool(const eve::Value::Object& object, std::string_view name) { const eve::Value* value = field(object, name); if (!value || !value->isBool()) - return failure(eve::DiagnosticCode::ParseError, - "snapshot field must be boolean", std::string(name)); + return failure(eve::DiagnosticCode::ParseError, "snapshot field must be boolean", std::string(name)); return eve::Result::success(value->asBool()); } @@ -128,23 +108,19 @@ eve::Result snapshotSchema(std::string_view text) { const auto parsed = eve::LogicalId::parse(text); if (!parsed) return failure(eve::DiagnosticCode::InvariantViolation, - "physics snapshot schema constant is invalid", - "schema"); + "physics snapshot schema constant is invalid", "schema"); return eve::Result::success(*parsed); } -eve::Result checkEnvelope(const eve::SnapshotEnvelope& snapshot, - std::string_view type, std::string_view schema) { +eve::Result checkEnvelope(const eve::SnapshotEnvelope& snapshot, std::string_view type, std::string_view schema) { if (snapshot.type != type || snapshot.schema.format() != schema) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, - "snapshot type or schema does not belong to this physics world", - "snapshot.type")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, + "snapshot type or schema does not belong to this physics world", "snapshot.type")); if (snapshot.schemaVersion.value() != kSnapshotVersion) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::UnknownVersion, - "physics world snapshot schema version is not supported", - "snapshot.schemaVersion")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::UnknownVersion, + "physics world snapshot schema version is not supported", "snapshot.schemaVersion")); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } @@ -152,7 +128,7 @@ struct ObservationState { SimulationObservation value; }; -eve::Result parseObservation(const eve::Value::Object& object, +eve::Result parseObservation(const eve::Value::Object& object, const eve::SnapshotEnvelope& snapshot) { auto tick = readUint64(object, "tick"); if (!tick) return eve::Result::failure(tick.status()); @@ -164,110 +140,129 @@ eve::Result parseObservation(const eve::Value::Object& object, if (!duration) return eve::Result::failure(duration.status()); auto lastDelta = readFloat(object, "lastDeltaSeconds"); if (!lastDelta) return eve::Result::failure(lastDelta.status()); - if (duration.value() < 0 || revision.value() != stepCount.value() || - tick.value() != snapshot.tick.value() || + if (duration.value() < 0 || revision.value() != stepCount.value() || tick.value() != snapshot.tick.value() || stepCount.value() != snapshot.revision.value()) { - return failure( - eve::DiagnosticCode::Conflict, - "physics snapshot payload progress disagrees with its envelope", - "payload.observation"); + return failure(eve::DiagnosticCode::Conflict, + "physics snapshot payload progress disagrees with its envelope", + "payload.observation"); } ObservationState result; - result.value.stepCount = stepCount.value(); - result.value.lastTick = eve::SimulationTick(tick.value()); + result.value.stepCount = stepCount.value(); + result.value.lastTick = eve::SimulationTick(tick.value()); result.value.simulatedDuration = eve::Duration::fromNanoseconds(duration.value()); - result.value.simulatedSeconds = result.value.simulatedDuration.seconds(); - result.value.lastDeltaSeconds = lastDelta.value(); - auto valid = detail::validateSimulationObservation( - result.value, "physics.snapshot.observation"); + result.value.simulatedSeconds = result.value.simulatedDuration.seconds(); + result.value.lastDeltaSeconds = lastDelta.value(); + auto valid = detail::validateSimulationObservation(result.value, "physics.snapshot.observation"); if (!valid) return eve::Result::failure(valid.status()); return eve::Result::success(std::move(result)); } struct Body2DState { - int id = 0; + int id = 0; std::string type; - float x = 0.f, y = 0.f, angle = 0.f; - float vx = 0.f, vy = 0.f, angularVelocity = 0.f; - bool active = false, bullet = false, awake = false, fixedRotation = false; + float x = 0.f, y = 0.f, angle = 0.f; + float vx = 0.f, vy = 0.f, angularVelocity = 0.f; + bool active = false, bullet = false, awake = false, fixedRotation = false; }; struct Body3DState { - int id = 0; + int id = 0; std::string type; - float x = 0.f, y = 0.f, z = 0.f; - float qx = 0.f, qy = 0.f, qz = 0.f, qw = 1.f; - float vx = 0.f, vy = 0.f, vz = 0.f; - float wx = 0.f, wy = 0.f, wz = 0.f; - bool active = false, bullet = false, awake = false, fixedRotation = false; + float x = 0.f, y = 0.f, z = 0.f; + float qx = 0.f, qy = 0.f, qz = 0.f, qw = 1.f; + float vx = 0.f, vy = 0.f, vz = 0.f; + float wx = 0.f, wy = 0.f, wz = 0.f; + bool active = false, bullet = false, awake = false, fixedRotation = false; }; -bool validBodyType(const std::string& type) { - return type == "static" || type == "kinematic" || type == "dynamic"; -} +bool validBodyType(const std::string& type) { return type == "static" || type == "kinematic" || type == "dynamic"; } eve::Result parseBody2D(const eve::Value& value) { const auto* object = value.getIf(); - if (!object || !hasExactFields(*object, - {"active", "angle", "awake", "bullet", - "fixedRotation", "id", "type", "vx", "vy", - "angularVelocity", "x", "y"})) + if (!object || !hasExactFields(*object, {"active", "angle", "awake", "bullet", "fixedRotation", "id", "type", "vx", + "vy", "angularVelocity", "x", "y"})) return failure(eve::DiagnosticCode::ParseError, - "2D physics snapshot body has unknown or missing fields", - "payload.bodies"); + "2D physics snapshot body has unknown or missing fields", "payload.bodies"); Body2DState state; - auto id = readUint64(*object, "id"); + auto id = readUint64(*object, "id"); if (!id || id.value() > static_cast(std::numeric_limits::max())) - return failure(eve::DiagnosticCode::ParseError, - "2D physics snapshot body id is invalid", "payload.bodies.id"); - state.id = static_cast(id.value()); + return failure(eve::DiagnosticCode::ParseError, "2D physics snapshot body id is invalid", + "payload.bodies.id"); + state.id = static_cast(id.value()); auto type = readString(*object, "type"); if (!type || !validBodyType(type.value())) - return failure(eve::DiagnosticCode::InvalidArgument, - "2D physics snapshot body type is invalid", "payload.bodies.type"); + return failure(eve::DiagnosticCode::InvalidArgument, "2D physics snapshot body type is invalid", + "payload.bodies.type"); state.type = std::move(type).takeValue(); - auto x = readFloat(*object, "x"); if (!x) return eve::Result::failure(x.status()); state.x = x.value(); - auto y = readFloat(*object, "y"); if (!y) return eve::Result::failure(y.status()); state.y = y.value(); - auto angle = readFloat(*object, "angle"); if (!angle) return eve::Result::failure(angle.status()); state.angle = angle.value(); - auto vx = readFloat(*object, "vx"); if (!vx) return eve::Result::failure(vx.status()); state.vx = vx.value(); - auto vy = readFloat(*object, "vy"); if (!vy) return eve::Result::failure(vy.status()); state.vy = vy.value(); - auto angular = readFloat(*object, "angularVelocity"); if (!angular) return eve::Result::failure(angular.status()); state.angularVelocity = angular.value(); - auto active = readBool(*object, "active"); if (!active) return eve::Result::failure(active.status()); state.active = active.value(); - auto bullet = readBool(*object, "bullet"); if (!bullet) return eve::Result::failure(bullet.status()); state.bullet = bullet.value(); - auto awake = readBool(*object, "awake"); if (!awake) return eve::Result::failure(awake.status()); state.awake = awake.value(); - auto fixed = readBool(*object, "fixedRotation"); if (!fixed) return eve::Result::failure(fixed.status()); state.fixedRotation = fixed.value(); + auto x = readFloat(*object, "x"); + if (!x) return eve::Result::failure(x.status()); + state.x = x.value(); + auto y = readFloat(*object, "y"); + if (!y) return eve::Result::failure(y.status()); + state.y = y.value(); + auto angle = readFloat(*object, "angle"); + if (!angle) return eve::Result::failure(angle.status()); + state.angle = angle.value(); + auto vx = readFloat(*object, "vx"); + if (!vx) return eve::Result::failure(vx.status()); + state.vx = vx.value(); + auto vy = readFloat(*object, "vy"); + if (!vy) return eve::Result::failure(vy.status()); + state.vy = vy.value(); + auto angular = readFloat(*object, "angularVelocity"); + if (!angular) return eve::Result::failure(angular.status()); + state.angularVelocity = angular.value(); + auto active = readBool(*object, "active"); + if (!active) return eve::Result::failure(active.status()); + state.active = active.value(); + auto bullet = readBool(*object, "bullet"); + if (!bullet) return eve::Result::failure(bullet.status()); + state.bullet = bullet.value(); + auto awake = readBool(*object, "awake"); + if (!awake) return eve::Result::failure(awake.status()); + state.awake = awake.value(); + auto fixed = readBool(*object, "fixedRotation"); + if (!fixed) return eve::Result::failure(fixed.status()); + state.fixedRotation = fixed.value(); return eve::Result::success(std::move(state)); } eve::Result parseBody3D(const eve::Value& value) { const auto* object = value.getIf(); - if (!object || !hasExactFields(*object, - {"active", "awake", "bullet", "fixedRotation", "id", - "type", "x", "y", "z", "qx", "qy", "qz", "qw", - "vx", "vy", "vz", "wx", "wy", "wz"})) + if (!object || !hasExactFields(*object, {"active", "awake", "bullet", "fixedRotation", "id", "type", "x", "y", "z", + "qx", "qy", "qz", "qw", "vx", "vy", "vz", "wx", "wy", "wz"})) return failure(eve::DiagnosticCode::ParseError, - "3D physics snapshot body has unknown or missing fields", - "payload.bodies"); + "3D physics snapshot body has unknown or missing fields", "payload.bodies"); Body3DState state; - auto id = readUint64(*object, "id"); + auto id = readUint64(*object, "id"); if (!id || id.value() > static_cast(std::numeric_limits::max())) - return failure(eve::DiagnosticCode::ParseError, - "3D physics snapshot body id is invalid", "payload.bodies.id"); - state.id = static_cast(id.value()); + return failure(eve::DiagnosticCode::ParseError, "3D physics snapshot body id is invalid", + "payload.bodies.id"); + state.id = static_cast(id.value()); auto type = readString(*object, "type"); if (!type || !validBodyType(type.value())) - return failure(eve::DiagnosticCode::InvalidArgument, - "3D physics snapshot body type is invalid", "payload.bodies.type"); + return failure(eve::DiagnosticCode::InvalidArgument, "3D physics snapshot body type is invalid", + "payload.bodies.type"); state.type = std::move(type).takeValue(); -#define EV_READ_BODY3D_FLOAT(name) \ - do { auto value = readFloat(*object, #name); \ - if (!value) return eve::Result::failure(value.status()); \ - state.name = value.value(); } while (false) - EV_READ_BODY3D_FLOAT(x); EV_READ_BODY3D_FLOAT(y); EV_READ_BODY3D_FLOAT(z); - EV_READ_BODY3D_FLOAT(qx); EV_READ_BODY3D_FLOAT(qy); EV_READ_BODY3D_FLOAT(qz); - EV_READ_BODY3D_FLOAT(qw); EV_READ_BODY3D_FLOAT(vx); EV_READ_BODY3D_FLOAT(vy); - EV_READ_BODY3D_FLOAT(vz); EV_READ_BODY3D_FLOAT(wx); EV_READ_BODY3D_FLOAT(wy); +#define EV_READ_BODY3D_FLOAT(name) \ + do { \ + auto value = readFloat(*object, #name); \ + if (!value) return eve::Result::failure(value.status()); \ + state.name = value.value(); \ + } while (false) + EV_READ_BODY3D_FLOAT(x); + EV_READ_BODY3D_FLOAT(y); + EV_READ_BODY3D_FLOAT(z); + EV_READ_BODY3D_FLOAT(qx); + EV_READ_BODY3D_FLOAT(qy); + EV_READ_BODY3D_FLOAT(qz); + EV_READ_BODY3D_FLOAT(qw); + EV_READ_BODY3D_FLOAT(vx); + EV_READ_BODY3D_FLOAT(vy); + EV_READ_BODY3D_FLOAT(vz); + EV_READ_BODY3D_FLOAT(wx); + EV_READ_BODY3D_FLOAT(wy); EV_READ_BODY3D_FLOAT(wz); #undef EV_READ_BODY3D_FLOAT const double quaternionLengthSquared = @@ -275,14 +270,17 @@ eve::Result parseBody3D(const eve::Value& value) { static_cast(state.qz) * state.qz + static_cast(state.qw) * state.qw; if (!(quaternionLengthSquared > 1e-16)) return failure(eve::DiagnosticCode::InvalidArgument, - "3D physics snapshot rotation must be non-zero", - "payload.bodies.rotation"); -#define EV_READ_BODY3D_BOOL(name) \ - do { auto value = readBool(*object, #name); \ - if (!value) return eve::Result::failure(value.status()); \ - state.name = value.value(); } while (false) - EV_READ_BODY3D_BOOL(active); EV_READ_BODY3D_BOOL(bullet); - EV_READ_BODY3D_BOOL(awake); EV_READ_BODY3D_BOOL(fixedRotation); + "3D physics snapshot rotation must be non-zero", "payload.bodies.rotation"); +#define EV_READ_BODY3D_BOOL(name) \ + do { \ + auto value = readBool(*object, #name); \ + if (!value) return eve::Result::failure(value.status()); \ + state.name = value.value(); \ + } while (false) + EV_READ_BODY3D_BOOL(active); + EV_READ_BODY3D_BOOL(bullet); + EV_READ_BODY3D_BOOL(awake); + EV_READ_BODY3D_BOOL(fixedRotation); #undef EV_READ_BODY3D_BOOL return eve::Result::success(std::move(state)); } @@ -312,38 +310,41 @@ eve::Value bodyValue(const Body3D& body) { object.emplace("fixedRotation", eve::Value(body.isFixedRotation())); object.emplace("id", eve::Value(std::to_string(body.getId()))); object.emplace("type", eve::Value(body.getType())); - object.emplace("x", eve::Value(body.getX())); object.emplace("y", eve::Value(body.getY())); - object.emplace("z", eve::Value(body.getZ())); object.emplace("qx", eve::Value(body.getRotX())); - object.emplace("qy", eve::Value(body.getRotY())); object.emplace("qz", eve::Value(body.getRotZ())); + object.emplace("x", eve::Value(body.getX())); + object.emplace("y", eve::Value(body.getY())); + object.emplace("z", eve::Value(body.getZ())); + object.emplace("qx", eve::Value(body.getRotX())); + object.emplace("qy", eve::Value(body.getRotY())); + object.emplace("qz", eve::Value(body.getRotZ())); object.emplace("qw", eve::Value(body.getRotW())); - object.emplace("vx", eve::Value(body.getLinearVelocityX())); object.emplace("vy", eve::Value(body.getLinearVelocityY())); - object.emplace("vz", eve::Value(body.getLinearVelocityZ())); object.emplace("wx", eve::Value(body.getAngularVelocityX())); - object.emplace("wy", eve::Value(body.getAngularVelocityY())); object.emplace("wz", eve::Value(body.getAngularVelocityZ())); + object.emplace("vx", eve::Value(body.getLinearVelocityX())); + object.emplace("vy", eve::Value(body.getLinearVelocityY())); + object.emplace("vz", eve::Value(body.getLinearVelocityZ())); + object.emplace("wx", eve::Value(body.getAngularVelocityX())); + object.emplace("wy", eve::Value(body.getAngularVelocityY())); + object.emplace("wz", eve::Value(body.getAngularVelocityZ())); return eve::Value(std::move(object)); } -eve::Value::Object observationFields(const SimulationObservation& observation, - eve::SimulationTick tick) { +eve::Value::Object observationFields(const SimulationObservation& observation, eve::SimulationTick tick) { eve::Value::Object object; object.emplace("tick", eve::Value(std::to_string(tick.value()))); object.emplace("revision", eve::Value(std::to_string(observation.stepCount))); object.emplace("stepCount", eve::Value(std::to_string(observation.stepCount))); - object.emplace("simulatedDurationNs", - eve::Value(std::to_string(observation.simulatedDuration.nanoseconds()))); + object.emplace("simulatedDurationNs", eve::Value(std::to_string(observation.simulatedDuration.nanoseconds()))); object.emplace("lastDeltaSeconds", eve::Value(observation.lastDeltaSeconds)); return object; } eve::Value make2DPayload(const World& world, const std::vector& bodies) { - const auto observation = world.simulationObservation(); - eve::Value::Object object = observationFields(observation, world.simulationTick()); + const auto observation = world.simulationObservation(); + eve::Value::Object object = observationFields(observation, world.simulationTick()); object.emplace("gravityX", eve::Value(world.getGravityX())); object.emplace("gravityY", eve::Value(world.getGravityY())); object.emplace("meter", eve::Value(world.getMeter())); std::vector sortedBodies = bodies; - std::sort(sortedBodies.begin(), sortedBodies.end(), [](const Body* left, const Body* right) { - return left->getId() < right->getId(); - }); + std::sort(sortedBodies.begin(), sortedBodies.end(), + [](const Body* left, const Body* right) { return left->getId() < right->getId(); }); eve::Value::Array values; values.reserve(sortedBodies.size()); for (const Body* body : sortedBodies) @@ -353,15 +354,14 @@ eve::Value make2DPayload(const World& world, const std::vector& bodies) { } eve::Value make3DPayload(const World3D& world, const std::vector& bodies) { - const auto observation = world.simulationObservation(); - eve::Value::Object object = observationFields(observation, world.simulationTick()); + const auto observation = world.simulationObservation(); + eve::Value::Object object = observationFields(observation, world.simulationTick()); object.emplace("gravityX", eve::Value(world.getGravityX())); object.emplace("gravityY", eve::Value(world.getGravityY())); object.emplace("gravityZ", eve::Value(world.getGravityZ())); std::vector sortedBodies = bodies; - std::sort(sortedBodies.begin(), sortedBodies.end(), [](const Body3D* left, const Body3D* right) { - return left->getId() < right->getId(); - }); + std::sort(sortedBodies.begin(), sortedBodies.end(), + [](const Body3D* left, const Body3D* right) { return left->getId() < right->getId(); }); eve::Value::Array values; values.reserve(sortedBodies.size()); for (const Body3D* body : sortedBodies) @@ -371,23 +371,21 @@ eve::Value make3DPayload(const World3D& world, const std::vector& bodie } template -eve::Result matchBodyIds(const std::vector& states, - std::set& ids) { +eve::Result matchBodyIds(const std::vector& states, std::set& ids) { for (const BodyState& state : states) { if (!ids.insert(state.id).second) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, - "physics snapshot contains a duplicate body id", "payload.bodies.id")); + eve::DiagnosticCode::Conflict, "physics snapshot contains a duplicate body id", "payload.bodies.id")); } return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } eve::Result> parseBodies2D(const eve::Value::Object& object) { const eve::Value* value = field(object, "bodies"); - const auto* array = value ? value->getIf() : nullptr; - if (!array) return failure>(eve::DiagnosticCode::ParseError, - "physics snapshot bodies must be an array", - "payload.bodies"); + const auto* array = value ? value->getIf() : nullptr; + if (!array) + return failure>(eve::DiagnosticCode::ParseError, + "physics snapshot bodies must be an array", "payload.bodies"); std::vector result; result.reserve(array->size()); for (const eve::Value& entry : *array) { @@ -400,10 +398,10 @@ eve::Result> parseBodies2D(const eve::Value::Object& ob eve::Result> parseBodies3D(const eve::Value::Object& object) { const eve::Value* value = field(object, "bodies"); - const auto* array = value ? value->getIf() : nullptr; - if (!array) return failure>(eve::DiagnosticCode::ParseError, - "physics snapshot bodies must be an array", - "payload.bodies"); + const auto* array = value ? value->getIf() : nullptr; + if (!array) + return failure>(eve::DiagnosticCode::ParseError, + "physics snapshot bodies must be an array", "payload.bodies"); std::vector result; result.reserve(array->size()); for (const eve::Value& entry : *array) { @@ -416,45 +414,36 @@ eve::Result> parseBodies3D(const eve::Value::Object& ob } // namespace -eve::Result World::snapshot( - const eve::SnapshotHashProvider& hashProvider) const { +eve::Result World::snapshot(const eve::SnapshotHashProvider& hashProvider) const { if (!isValid() || !simulation_) - return failure( - eve::DiagnosticCode::PreconditionViolation, - "Cannot snapshot a destroyed or uninitialized physics world", - "physics.world.snapshot"); + return failure(eve::DiagnosticCode::PreconditionViolation, + "Cannot snapshot a destroyed or uninitialized physics world", + "physics.world.snapshot"); auto schema = snapshotSchema(kWorld2DSchema); if (!schema) return eve::Result::failure(schema.status()); - const auto observation = simulationObservation(); + const auto observation = simulationObservation(); std::vector bodies(bodies_.begin(), bodies_.end()); - return eve::makeSnapshotEnvelope( - std::string(kWorld2DType), std::move(schema).takeValue(), - eve::SchemaVersion(kSnapshotVersion), eve::PersistentId::nil(), - eve::Revision(observation.stepCount), simulationTick(), - make2DPayload(*this, bodies), - hashProvider); + return eve::makeSnapshotEnvelope(std::string(kWorld2DType), std::move(schema).takeValue(), + eve::SchemaVersion(kSnapshotVersion), eve::PersistentId::nil(), + eve::Revision(observation.stepCount), simulationTick(), + make2DPayload(*this, bodies), hashProvider); } -eve::Result World::restore(const eve::SnapshotEnvelope& snapshotValue, - const eve::SnapshotHashProvider& hashProvider) { +eve::Result World::restore(const eve::SnapshotEnvelope& snapshotValue, + const eve::SnapshotHashProvider& hashProvider) { if (!isValid() || !simulation_) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, - "Cannot restore a destroyed or uninitialized physics world", + eve::DiagnosticCode::PreconditionViolation, "Cannot restore a destroyed or uninitialized physics world", "physics.world.restore")); auto verified = eve::verifySnapshotEnvelope(snapshotValue, hashProvider); if (!verified) return verified; auto envelope = checkEnvelope(snapshotValue, kWorld2DType, kWorld2DSchema); if (!envelope) return envelope; const auto* object = snapshotValue.payload.getIf(); - if (!object || !hasExactFields(*object, - {"bodies", "gravityX", "gravityY", "meter", "tick", - "revision", "stepCount", "simulatedDurationNs", - "lastDeltaSeconds"})) + if (!object || !hasExactFields(*object, {"bodies", "gravityX", "gravityY", "meter", "tick", "revision", "stepCount", + "simulatedDurationNs", "lastDeltaSeconds"})) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, - "2D physics snapshot payload has unknown or missing fields", - "payload")); + eve::DiagnosticCode::ParseError, "2D physics snapshot payload has unknown or missing fields", "payload")); auto observation = parseObservation(*object, snapshotValue); if (!observation) return eve::Result::failure(observation.status()); auto bodies = parseBodies2D(*object); @@ -465,41 +454,42 @@ eve::Result World::restore(const eve::SnapshotEnvelope& snapshotValue, if (!gravityY) return eve::Result::failure(gravityY.status()); auto meter = readFloat(*object, "meter"); if (!meter) return eve::Result::failure(meter.status()); - if (meter.value() <= 0.f) return failure(eve::DiagnosticCode::InvalidArgument, - "physics snapshot meter must be positive", - "payload.meter"); + if (meter.value() <= 0.f) + return failure(eve::DiagnosticCode::InvalidArgument, "physics snapshot meter must be positive", + "payload.meter"); std::set ids; - for (const Body* body : bodies_) if (body && body->isValid()) ids.insert(body->getId()); + for (const Body* body : bodies_) + if (body && body->isValid()) ids.insert(body->getId()); std::set snapshotIds; - auto matched = matchBodyIds(bodies.value(), snapshotIds); + auto matched = matchBodyIds(bodies.value(), snapshotIds); if (!matched) return matched; - if (ids != snapshotIds) return failure(eve::DiagnosticCode::Conflict, - "physics snapshot body ids do not match the live world", - "payload.bodies"); + if (ids != snapshotIds) + return failure(eve::DiagnosticCode::Conflict, "physics snapshot body ids do not match the live world", + "payload.bodies"); std::vector> targets; targets.reserve(bodies.value().size()); for (const Body2DState& state : bodies.value()) { Body* target = nullptr; - for (Body* candidate : bodies_) if (candidate && candidate->getId() == state.id) target = candidate; - if (!target) return failure(eve::DiagnosticCode::Conflict, - "physics snapshot body target disappeared during validation", - "payload.bodies"); + for (Body* candidate : bodies_) + if (candidate && candidate->getId() == state.id) target = candidate; + if (!target) + return failure(eve::DiagnosticCode::Conflict, + "physics snapshot body target disappeared during validation", "payload.bodies"); targets.emplace_back(target, state); } - const auto previousObservation = simulationObservation(); - const auto previousTick = simulationTick_; - const float previousGravityX = getGravityX(); - const float previousGravityY = getGravityY(); - const float previousMeter = getMeter(); + const auto previousObservation = simulationObservation(); + const auto previousTick = simulationTick_; + const float previousGravityX = getGravityX(); + const float previousGravityY = getGravityY(); + const float previousMeter = getMeter(); std::vector backup; backup.reserve(targets.size()); for (const auto& target : targets) { const Body& body = *target.first; backup.push_back({body.getId(), body.getType(), body.getX(), body.getY(), body.getAngle(), - body.getLinearVelocityX(), body.getLinearVelocityY(), - body.getAngularVelocity(), body.isActive(), body.isBullet(), - body.isAwake(), body.isFixedRotation()}); + body.getLinearVelocityX(), body.getLinearVelocityY(), body.getAngularVelocity(), + body.isActive(), body.isBullet(), body.isAwake(), body.isFixedRotation()}); } auto backendRestore = simulation_->restoreObservation(observation.value().value); if (!backendRestore) return backendRestore; @@ -507,24 +497,35 @@ eve::Result World::restore(const eve::SnapshotEnvelope& snapshotValue, setMeter(meter.value()); setGravity(gravityX.value(), gravityY.value()); for (const auto& target : targets) { - Body& body = *target.first; const Body2DState& state = target.second; - body.setType(state.type); body.setFixedRotation(state.fixedRotation); - body.setBullet(state.bullet); body.setActive(state.active); - body.setPosition(state.x, state.y); body.setAngle(state.angle); - body.setLinearVelocity(state.vx, state.vy); body.setAngularVelocity(state.angularVelocity); + Body& body = *target.first; + const Body2DState& state = target.second; + body.setType(state.type); + body.setFixedRotation(state.fixedRotation); + body.setBullet(state.bullet); + body.setActive(state.active); + body.setPosition(state.x, state.y); + body.setAngle(state.angle); + body.setLinearVelocity(state.vx, state.vy); + body.setAngularVelocity(state.angularVelocity); body.setAwake(state.awake); } } catch (const std::exception& error) { try { - setMeter(previousMeter); setGravity(previousGravityX, previousGravityY); + setMeter(previousMeter); + setGravity(previousGravityX, previousGravityY); for (const Body2DState& state : backup) { Body* body = nullptr; - for (Body* candidate : bodies_) if (candidate && candidate->getId() == state.id) body = candidate; + for (Body* candidate : bodies_) + if (candidate && candidate->getId() == state.id) body = candidate; if (!body) continue; - body->setType(state.type); body->setFixedRotation(state.fixedRotation); - body->setBullet(state.bullet); body->setActive(state.active); - body->setPosition(state.x, state.y); body->setAngle(state.angle); - body->setLinearVelocity(state.vx, state.vy); body->setAngularVelocity(state.angularVelocity); + body->setType(state.type); + body->setFixedRotation(state.fixedRotation); + body->setBullet(state.bullet); + body->setActive(state.active); + body->setPosition(state.x, state.y); + body->setAngle(state.angle); + body->setLinearVelocity(state.vx, state.vy); + body->setAngularVelocity(state.angularVelocity); body->setAwake(state.awake); } auto rollback = simulation_->restoreObservation(previousObservation); @@ -542,44 +543,36 @@ eve::Result World::restore(const eve::SnapshotEnvelope& snapshotValue, return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result World3D::snapshot( - const eve::SnapshotHashProvider& hashProvider) const { +eve::Result World3D::snapshot(const eve::SnapshotHashProvider& hashProvider) const { if (!isValid() || !simulation_) - return failure( - eve::DiagnosticCode::PreconditionViolation, - "Cannot snapshot a destroyed or uninitialized 3D physics world", - "physics.world3d.snapshot"); + return failure(eve::DiagnosticCode::PreconditionViolation, + "Cannot snapshot a destroyed or uninitialized 3D physics world", + "physics.world3d.snapshot"); auto schema = snapshotSchema(kWorld3DSchema); if (!schema) return eve::Result::failure(schema.status()); - const auto observation = simulationObservation(); + const auto observation = simulationObservation(); std::vector bodies(bodies_.begin(), bodies_.end()); - return eve::makeSnapshotEnvelope( - std::string(kWorld3DType), std::move(schema).takeValue(), - eve::SchemaVersion(kSnapshotVersion), eve::PersistentId::nil(), - eve::Revision(observation.stepCount), simulationTick(), - make3DPayload(*this, bodies), - hashProvider); + return eve::makeSnapshotEnvelope(std::string(kWorld3DType), std::move(schema).takeValue(), + eve::SchemaVersion(kSnapshotVersion), eve::PersistentId::nil(), + eve::Revision(observation.stepCount), simulationTick(), + make3DPayload(*this, bodies), hashProvider); } -eve::Result World3D::restore(const eve::SnapshotEnvelope& snapshotValue, +eve::Result World3D::restore(const eve::SnapshotEnvelope& snapshotValue, const eve::SnapshotHashProvider& hashProvider) { if (!isValid() || !simulation_) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, - "Cannot restore a destroyed or uninitialized 3D physics world", + eve::DiagnosticCode::PreconditionViolation, "Cannot restore a destroyed or uninitialized 3D physics world", "physics.world3d.restore")); auto verified = eve::verifySnapshotEnvelope(snapshotValue, hashProvider); if (!verified) return verified; auto envelope = checkEnvelope(snapshotValue, kWorld3DType, kWorld3DSchema); if (!envelope) return envelope; const auto* object = snapshotValue.payload.getIf(); - if (!object || !hasExactFields(*object, - {"bodies", "gravityX", "gravityY", "gravityZ", "tick", - "revision", "stepCount", "simulatedDurationNs", - "lastDeltaSeconds"})) + if (!object || !hasExactFields(*object, {"bodies", "gravityX", "gravityY", "gravityZ", "tick", "revision", + "stepCount", "simulatedDurationNs", "lastDeltaSeconds"})) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, - "3D physics snapshot payload has unknown or missing fields", "payload")); + eve::DiagnosticCode::ParseError, "3D physics snapshot payload has unknown or missing fields", "payload")); auto observation = parseObservation(*object, snapshotValue); if (!observation) return eve::Result::failure(observation.status()); auto bodies = parseBodies3D(*object); @@ -591,64 +584,74 @@ eve::Result World3D::restore(const eve::SnapshotEnvelope& snapshotValue, auto gravityZ = readFloat(*object, "gravityZ"); if (!gravityZ) return eve::Result::failure(gravityZ.status()); std::set ids; - for (const Body3D* body : bodies_) if (body && body->isValid()) ids.insert(body->getId()); + for (const Body3D* body : bodies_) + if (body && body->isValid()) ids.insert(body->getId()); std::set snapshotIds; - auto matched = matchBodyIds(bodies.value(), snapshotIds); + auto matched = matchBodyIds(bodies.value(), snapshotIds); if (!matched) return matched; - if (ids != snapshotIds) return failure(eve::DiagnosticCode::Conflict, - "physics snapshot body ids do not match the live world", - "payload.bodies"); + if (ids != snapshotIds) + return failure(eve::DiagnosticCode::Conflict, "physics snapshot body ids do not match the live world", + "payload.bodies"); std::vector> targets; targets.reserve(bodies.value().size()); for (const Body3DState& state : bodies.value()) { Body3D* target = nullptr; - for (Body3D* candidate : bodies_) if (candidate && candidate->getId() == state.id) target = candidate; - if (!target) return failure(eve::DiagnosticCode::Conflict, - "physics snapshot body target disappeared during validation", - "payload.bodies"); + for (Body3D* candidate : bodies_) + if (candidate && candidate->getId() == state.id) target = candidate; + if (!target) + return failure(eve::DiagnosticCode::Conflict, + "physics snapshot body target disappeared during validation", "payload.bodies"); targets.emplace_back(target, state); } - const auto previousObservation = simulationObservation(); - const auto previousTick = simulationTick_; - const float previousGravityX = getGravityX(); - const float previousGravityY = getGravityY(); - const float previousGravityZ = getGravityZ(); + const auto previousObservation = simulationObservation(); + const auto previousTick = simulationTick_; + const float previousGravityX = getGravityX(); + const float previousGravityY = getGravityY(); + const float previousGravityZ = getGravityZ(); std::vector backup; backup.reserve(targets.size()); for (const auto& target : targets) { const Body3D& body = *target.first; - backup.push_back({body.getId(), body.getType(), body.getX(), body.getY(), body.getZ(), - body.getRotX(), body.getRotY(), body.getRotZ(), body.getRotW(), - body.getLinearVelocityX(), body.getLinearVelocityY(), body.getLinearVelocityZ(), - body.getAngularVelocityX(), body.getAngularVelocityY(), body.getAngularVelocityZ(), - body.isActive(), body.isBullet(), body.isAwake(), body.isFixedRotation()}); + backup.push_back({body.getId(), body.getType(), body.getX(), body.getY(), body.getZ(), body.getRotX(), + body.getRotY(), body.getRotZ(), body.getRotW(), body.getLinearVelocityX(), + body.getLinearVelocityY(), body.getLinearVelocityZ(), body.getAngularVelocityX(), + body.getAngularVelocityY(), body.getAngularVelocityZ(), body.isActive(), body.isBullet(), + body.isAwake(), body.isFixedRotation()}); } auto backendRestore = simulation_->restoreObservation(observation.value().value); if (!backendRestore) return backendRestore; try { setGravity(gravityX.value(), gravityY.value(), gravityZ.value()); for (const auto& target : targets) { - Body3D& body = *target.first; const Body3DState& state = target.second; - body.setType(state.type); body.setFixedRotation(state.fixedRotation); - body.setBullet(state.bullet); body.setActive(state.active); + Body3D& body = *target.first; + const Body3DState& state = target.second; + body.setType(state.type); + body.setFixedRotation(state.fixedRotation); + body.setBullet(state.bullet); + body.setActive(state.active); body.setPosition(state.x, state.y, state.z); body.setRotation(state.qx, state.qy, state.qz, state.qw); body.setLinearVelocity(state.vx, state.vy, state.vz); - body.setAngularVelocity(state.wx, state.wy, state.wz); body.setAwake(state.awake); + body.setAngularVelocity(state.wx, state.wy, state.wz); + body.setAwake(state.awake); } } catch (const std::exception& error) { try { setGravity(previousGravityX, previousGravityY, previousGravityZ); for (const Body3DState& state : backup) { Body3D* body = nullptr; - for (Body3D* candidate : bodies_) if (candidate && candidate->getId() == state.id) body = candidate; + for (Body3D* candidate : bodies_) + if (candidate && candidate->getId() == state.id) body = candidate; if (!body) continue; - body->setType(state.type); body->setFixedRotation(state.fixedRotation); - body->setBullet(state.bullet); body->setActive(state.active); + body->setType(state.type); + body->setFixedRotation(state.fixedRotation); + body->setBullet(state.bullet); + body->setActive(state.active); body->setPosition(state.x, state.y, state.z); body->setRotation(state.qx, state.qy, state.qz, state.qw); body->setLinearVelocity(state.vx, state.vy, state.vz); - body->setAngularVelocity(state.wx, state.wy, state.wz); body->setAwake(state.awake); + body->setAngularVelocity(state.wx, state.wy, state.wz); + body->setAwake(state.awake); } auto rollback = simulation_->restoreObservation(previousObservation); rollback.ignore("physics 3D snapshot rollback after failed body restore"); diff --git a/src/modules/platform_event/MainThreadPost.cpp b/src/modules/platform_event/MainThreadPost.cpp index e9ee8e27..c057ae1f 100644 --- a/src/modules/platform_event/MainThreadPost.cpp +++ b/src/modules/platform_event/MainThreadPost.cpp @@ -1,8 +1,8 @@ // Publishes the event queue as the IMainThreadPost capability, so the thread // module can hand results back to the main thread without depending on event. -#include "common/Capability.h" #include "common/MainThreadPost.h" +#include "common/Capability.h" #include "common/Module.h" #include "platform_event/PlatformEvent.h" diff --git a/src/modules/platform_event/PlatformEvent.cpp b/src/modules/platform_event/PlatformEvent.cpp index 77e19b8e..53b5248b 100644 --- a/src/modules/platform_event/PlatformEvent.cpp +++ b/src/modules/platform_event/PlatformEvent.cpp @@ -32,14 +32,11 @@ eve::Subscription PlatformEvent::subscribePoll(std::function(msg)); -} +void PlatformEvent::push(Message* msg) { push(std::unique_ptr(msg)); } void PlatformEvent::push(std::unique_ptr msg) { EV_PARAM_CHECK(msg.get() != nullptr, "event message must not be null"); - if (!msg) - return; + if (!msg) return; { std::lock_guard lock(queueMu_); queue.push(std::move(msg)); @@ -49,21 +46,17 @@ void PlatformEvent::push(std::unique_ptr msg) { void PlatformEvent::pushData(std::string eventName, std::string data) { std::vector args; - if (!data.empty()) - args.push_back(Variant::makeString(std::move(data))); + if (!data.empty()) args.push_back(Variant::makeString(std::move(data))); push(std::make_unique(std::move(eventName), args)); } -Message* PlatformEvent::poll() { - return pollOwned().release(); -} +Message* PlatformEvent::poll() { return pollOwned().release(); } std::unique_ptr PlatformEvent::pollOwned() { std::unique_ptr msg; { std::lock_guard lock(queueMu_); - if (queue.empty()) - return nullptr; + if (queue.empty()) return nullptr; msg = std::move(queue.front()); queue.pop(); } @@ -71,8 +64,7 @@ std::unique_ptr PlatformEvent::pollOwned() { // The queue mutation is already committed; callback exceptions are // contained and counted instead of making a successful poll look failed. if (msg) { - static_cast(pollObservers_.notifyChecked( - [this]() noexcept { ++pollObserverFailures_; }, *msg)); + static_cast(pollObservers_.notifyChecked([this]() noexcept { ++pollObserverFailures_; }, *msg)); } return msg; } @@ -80,8 +72,7 @@ std::unique_ptr PlatformEvent::pollOwned() { std::string PlatformEvent::pollName() { auto msg = pollOwned(); lastData_.clear(); - if (!msg) - return {}; + if (!msg) return {}; for (const auto& a : msg->args) { if (a.type == Variant::Type::String) { lastData_ = a.s; @@ -97,8 +88,7 @@ std::string PlatformEvent::getLastData() const { return lastData_; } std::string PlatformEvent::pollData() { auto msg = pollOwned(); lastData_.clear(); - if (!msg) - return {}; + if (!msg) return {}; std::string data; for (const auto& a : msg->args) { if (a.type == Variant::Type::String) { diff --git a/src/modules/platform_event/PlatformEvent.h b/src/modules/platform_event/PlatformEvent.h index 7d076cce..d0177b25 100644 --- a/src/modules/platform_event/PlatformEvent.h +++ b/src/modules/platform_event/PlatformEvent.h @@ -22,10 +22,10 @@ namespace platform_event { struct Variant { enum class Type { Nil, Int, String, Ptr }; - Type type = Type::Nil; - int64_t i = 0; - std::string s; - void* p = nullptr; + Type type = Type::Nil; + int64_t i = 0; + std::string s; + void* p = nullptr; std::shared_ptr owner; /** @brief Constructs a nil variant. */ @@ -79,7 +79,7 @@ public: Message(const std::string& name, const std::vector& vargs = {}); ~Message(); - const std::string name; + const std::string name; const std::vector args; }; @@ -92,15 +92,15 @@ public: * @brief Queues a message. Thread-safe: workers may push completions for the main loop to poll. * @param msg Message to queue (must be non-null; ownership transfers to the queue). */ - void push(Message* msg); + void push(Message* msg); /** @brief Queues an owning message handle; ownership transfers to the queue. */ - void push(std::unique_ptr msg); + void push(std::unique_ptr msg); /** * @brief Script helper: pushes an event with an optional string payload. * @param name Event name. * @param data Optional string payload stored as the first argument. */ - void pushData(std::string name, std::string data = ""); + void pushData(std::string name, std::string data = ""); /** * @brief Pops the oldest message, or nullptr if the queue is empty. * @ownership Ownership transfers to the caller, which must delete the message. @@ -108,18 +108,18 @@ public: * @thread Thread-safe queue operation; observer callbacks run after unlocking. * @reentrancy Poll observers may re-enter the queue. */ - Message* poll(); + Message* poll(); /** @brief Pops the oldest message into an RAII handle, or returns null. */ std::unique_ptr pollOwned(); /** @brief Script helper: pops one message and returns its name, or "" if none. */ - std::string pollName(); + std::string pollName(); /** @brief First string arg of the message most recently returned by pollName/pollData. */ - std::string getLastData() const; + std::string getLastData() const; /** * @brief Pops one message and returns its first string arg (or ""). * Name is discarded — use pollName when you need the type. */ - std::string pollData(); + std::string pollData(); /** @brief Drops all queued messages and frees them. */ virtual void clear(); @@ -134,8 +134,7 @@ public: void setPollObserver(std::function observer) { legacyPollSubscription_.dispose(); pollObserver_ = std::move(observer); - if (pollObserver_) - legacyPollSubscription_ = pollObservers_.subscribe(pollObserver_); + if (pollObserver_) legacyPollSubscription_ = pollObservers_.subscribe(pollObserver_); } /** @brief Current poll observer, or empty if none. */ @@ -152,12 +151,10 @@ public: std::function callback); /** @brief Number of contained poll-listener exceptions since construction. */ - [[nodiscard]] std::uint64_t pollObserverFailureCount() const noexcept { - return pollObserverFailures_; - } + [[nodiscard]] std::uint64_t pollObserverFailureCount() const noexcept { return pollObserverFailures_; } /** @brief Platform hook: pumps the native event queue into this module. */ - virtual void pump() = 0; + virtual void pump() = 0; /** * @brief Platform hook: blocks until a message is available, then polls it. * @ownership Ownership transfers to the caller, which must delete the message. @@ -173,13 +170,13 @@ protected: /** @brief Wake a platform-specific blocking wait after a message is queued. */ virtual void wakeWaiters() {} - std::mutex queueMu_; + std::mutex queueMu_; std::queue> queue; - std::string lastData_; - std::function pollObserver_; - eve::Observer pollObservers_; - eve::Subscription legacyPollSubscription_; - std::uint64_t pollObserverFailures_ = 0; + std::string lastData_; + std::function pollObserver_; + eve::Observer pollObservers_; + eve::Subscription legacyPollSubscription_; + std::uint64_t pollObserverFailures_ = 0; }; } // namespace platform_event diff --git a/src/modules/platform_event/PlatformEventSink.h b/src/modules/platform_event/PlatformEventSink.h index afa66c38..2e207284 100644 --- a/src/modules/platform_event/PlatformEventSink.h +++ b/src/modules/platform_event/PlatformEventSink.h @@ -31,13 +31,13 @@ class EVENGINE_API IPlatformEventSink { public: /** Dispatch order; lower runs first. */ enum Priority { - kObserver = 0, // sees everything before anyone claims it (UI) - kSurface = 10, // window / swapchain lifecycle - kInput = 20, // keyboard, joystick, touch + kObserver = 0, // sees everything before anyone claims it (UI) + kSurface = 10, // window / swapchain lifecycle + kInput = 20, // keyboard, joystick, touch }; static constexpr const char* capabilityName = "eve.event.IPlatformEventSink"; - virtual ~IPlatformEventSink() = default; + virtual ~IPlatformEventSink() = default; /** * Inspect an event before translation. Return true to consume it, so no diff --git a/src/modules/platform_event/sdl/Event.cpp b/src/modules/platform_event/sdl/Event.cpp index b1637c5b..42e4afa5 100644 --- a/src/modules/platform_event/sdl/Event.cpp +++ b/src/modules/platform_event/sdl/Event.cpp @@ -14,108 +14,85 @@ #include #endif -namespace eve::platform_event::sdl -{ +namespace eve::platform_event::sdl { -Event::Event() -{ - if (SDL_InitSubSystem(SDL_INIT_EVENTS) < 0) - throw eve::Exception("Could not initialize SDL events subsystem (%s)", SDL_GetError()); - wakeEventType_ = SDL_RegisterEvents(1); - if (wakeEventType_ == static_cast(-1)) { - SDL_QuitSubSystem(SDL_INIT_EVENTS); - throw eve::Exception("Could not reserve SDL wake event (%s)", SDL_GetError()); - } +Event::Event() { + if (SDL_InitSubSystem(SDL_INIT_EVENTS) < 0) + throw eve::Exception("Could not initialize SDL events subsystem (%s)", SDL_GetError()); + wakeEventType_ = SDL_RegisterEvents(1); + if (wakeEventType_ == static_cast(-1)) { + SDL_QuitSubSystem(SDL_INIT_EVENTS); + throw eve::Exception("Could not reserve SDL wake event (%s)", SDL_GetError()); + } } -Event::~Event() -{ - SDL_QuitSubSystem(SDL_INIT_EVENTS); +Event::~Event() { SDL_QuitSubSystem(SDL_INIT_EVENTS); } + +void Event::pump() { + EV_PROFILE_MODULE("event", "Event::pump"); + SDL_Event e; + + while (SDL_PollEvent(&e)) { + Message *msg = convert(e); + if (msg) push(msg); + // Ownership stays in the queue until poll()/clear() — do not delete here. + } + + // Per-frame work that modules hang off the pump (audio streaming, ...). + cap::forEach([](IPlatformEventSink *sink) { sink->onPumpFinished(); }); } -void Event::pump() -{ - EV_PROFILE_MODULE("event", "Event::pump"); - SDL_Event e; +Message *Event::wait() { + for (;;) { + if (Message *queued = poll()) return queued; - while (SDL_PollEvent(&e)) - { - Message *msg = convert(e); - if (msg) - push(msg); - // Ownership stays in the queue until poll()/clear() — do not delete here. - } + SDL_Event e; + if (SDL_WaitEvent(&e) != 1) return nullptr; + if (e.type == wakeEventType_) continue; - // Per-frame work that modules hang off the pump (audio streaming, ...). - cap::forEach([](IPlatformEventSink *sink) { sink->onPumpFinished(); }); + if (Message *message = convert(e)) return message; + } } -Message *Event::wait() -{ - for (;;) { - if (Message *queued = poll()) - return queued; - - SDL_Event e; - if (SDL_WaitEvent(&e) != 1) - return nullptr; - if (e.type == wakeEventType_) - continue; - - if (Message *message = convert(e)) - return message; - } +void Event::wakeWaiters() { + SDL_Event e{}; + e.type = wakeEventType_; + e.user.type = wakeEventType_; + e.user.data1 = this; + SDL_PushEvent(&e); } -void Event::wakeWaiters() -{ - SDL_Event e{}; - e.type = wakeEventType_; - e.user.type = wakeEventType_; - e.user.data1 = this; - SDL_PushEvent(&e); +void Event::clear() { + SDL_Event e; + + while (SDL_PollEvent(&e)) { + // Do nothing with 'e' ... + } + + eve::platform_event::PlatformEvent::clear(); } -void Event::clear() -{ - SDL_Event e; +Message *Event::convert(const SDL_Event &e) { + // Modules that own an event family translate it themselves; see + // event/PlatformEventSink.h. Only window-close and quit are handled here, + // because they are about the event loop rather than any one module. + Message *translated = nullptr; + const bool consumed = cap::forEachUntil([&](IPlatformEventSink *sink) { + if (sink->shouldConsumePlatformEvent(&e)) return true; + translated = sink->translatePlatformEvent(&e); + return translated != nullptr; + }); + if (translated) return translated; + if (consumed) return nullptr; - while (SDL_PollEvent(&e)) - { - // Do nothing with 'e' ... - } - - eve::platform_event::PlatformEvent::clear(); + switch (e.type) { + case SDL_QUIT: return new Message("quit"); + case SDL_WINDOWEVENT: + if (e.window.event == SDL_WINDOWEVENT_CLOSE) return new Message("quit"); + break; + default: break; + } + return nullptr; } -Message *Event::convert(const SDL_Event &e) -{ - // Modules that own an event family translate it themselves; see - // event/PlatformEventSink.h. Only window-close and quit are handled here, - // because they are about the event loop rather than any one module. - Message *translated = nullptr; - const bool consumed = cap::forEachUntil([&](IPlatformEventSink *sink) { - if (sink->shouldConsumePlatformEvent(&e)) - return true; - translated = sink->translatePlatformEvent(&e); - return translated != nullptr; - }); - if (translated) - return translated; - if (consumed) - return nullptr; - - switch (e.type) { - case SDL_QUIT: - return new Message("quit"); - case SDL_WINDOWEVENT: - if (e.window.event == SDL_WINDOWEVENT_CLOSE) - return new Message("quit"); - break; - default: - break; - } - return nullptr; -} - -} +} // namespace eve::platform_event::sdl diff --git a/src/modules/platform_event/sdl/Event.h b/src/modules/platform_event/sdl/Event.h index 7513a6ab..40bca955 100644 --- a/src/modules/platform_event/sdl/Event.h +++ b/src/modules/platform_event/sdl/Event.h @@ -9,48 +9,46 @@ #endif #include -namespace eve::platform_event::sdl -{ +namespace eve::platform_event::sdl { -class Event final : public eve::platform_event::PlatformEvent -{ +class Event final : public eve::platform_event::PlatformEvent { public: - Event(); - virtual ~Event(); + Event(); + virtual ~Event(); - /** - * @brief Pumps the event queue. This function gathers all the pending input information - * from devices and places it on the event queue. Normally not needed if you poll - * for events. - **/ - void pump() override; + /** + * @brief Pumps the event queue. This function gathers all the pending input information + * from devices and places it on the event queue. Normally not needed if you poll + * for events. + **/ + void pump() override; - /** - * @brief Waits for the next event (indefinitely). Useful for creating games where - * the screen and game state only needs updating when the user interacts with - * the window. - * @ownership Ownership of the returned Message transfers to the caller. - * @lifetime Valid until caller deletion; independent of the native event. - * @thread Platform event thread only. - * @reentrancy Registered sinks run synchronously during conversion. - **/ - Message *wait() override; + /** + * @brief Waits for the next event (indefinitely). Useful for creating games where + * the screen and game state only needs updating when the user interacts with + * the window. + * @ownership Ownership of the returned Message transfers to the caller. + * @lifetime Valid until caller deletion; independent of the native event. + * @thread Platform event thread only. + * @reentrancy Registered sinks run synchronously during conversion. + **/ + Message *wait() override; - /** - * @brief Clears the event queue. - */ - void clear() override; + /** + * @brief Clears the event queue. + */ + void clear() override; private: - void wakeWaiters() override; - /** - * @brief Offers the event to registered sinks, then handles quit itself. - * @ownership The SDL event is borrowed for this call; returned Message ownership transfers to the caller. - * @lifetime The input and any sink borrow are valid only during this call. - */ - Message *convert(const SDL_Event &e); - Uint32 wakeEventType_ = static_cast(-1); + void wakeWaiters() override; + /** + * @brief Offers the event to registered sinks, then handles quit itself. + * @ownership The SDL event is borrowed for this call; returned Message ownership transfers to the caller. + * @lifetime The input and any sink borrow are valid only during this call. + */ + Message *convert(const SDL_Event &e); + Uint32 wakeEventType_ = static_cast(-1); -}; // Event +}; // Event -} // eve::platform_event::sdl +} // namespace eve::platform_event::sdl diff --git a/src/modules/plugins/Plugins.cpp b/src/modules/plugins/Plugins.cpp index 9f758bd2..27cbd696 100644 --- a/src/modules/plugins/Plugins.cpp +++ b/src/modules/plugins/Plugins.cpp @@ -42,8 +42,7 @@ bool Plugins::load(const std::string& path) { if (loaded_.count(path)) return true; auto registration = ModuleManager::beginPluginRegistration(); - if (!registration.ok()) - throw Exception("plugins.load: nested plugin loading is not supported"); + if (!registration.ok()) throw Exception("plugins.load: nested plugin loading is not supported"); #if defined(_WIN32) HMODULE mod = LoadLibraryA(path.c_str()); diff --git a/src/modules/policyregistry/PolicyRegistry.cpp b/src/modules/policyregistry/PolicyRegistry.cpp index 86bf80f9..621d22c2 100644 --- a/src/modules/policyregistry/PolicyRegistry.cpp +++ b/src/modules/policyregistry/PolicyRegistry.cpp @@ -26,7 +26,7 @@ const eve::Value* field(const eve::Value::Object& object, std::string_view name) bool readString(const eve::Value::Object& object, std::string_view name, std::string& output) { const auto* value = field(object, name); - const auto* text = value ? value->getIf() : nullptr; + const auto* text = value ? value->getIf() : nullptr; if (!text) return false; output = *text; return true; @@ -68,17 +68,17 @@ eve::LogicalId policySchema() { const eve::SnapshotMigrationChain& policyMigrations() { static const eve::SnapshotMigrationChain chain = [] { eve::SnapshotMigrationChain result; - const auto registration = result.add( - policySchema(), eve::SchemaVersion(0), eve::SchemaVersion(1), - [](const eve::Value& payload) -> eve::Result { - const auto* object = payload.getIf(); - if (!object) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "policy snapshot payload must be an object")); - eve::Value::Object migrated = *object; - migrated["version"] = eve::Value(std::int64_t(1)); - return eve::Result::success(eve::Value(std::move(migrated))); - }); + const auto registration = + result.add(policySchema(), eve::SchemaVersion(0), eve::SchemaVersion(1), + [](const eve::Value& payload) -> eve::Result { + const auto* object = payload.getIf(); + if (!object) + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::ParseError, "policy snapshot payload must be an object")); + eve::Value::Object migrated = *object; + migrated["version"] = eve::Value(std::int64_t(1)); + return eve::Result::success(eve::Value(std::move(migrated))); + }); if (!registration.ok()) std::terminate(); return result; }(); @@ -103,8 +103,7 @@ eve::Result makeDescriptor(const std::string& domain, const st if (!metadata.ok()) return eve::Result::failure(metadata.status()); auto metadataValue = std::move(metadata).takeValue(); if (!metadataValue.isObject()) - return failure(eve::DiagnosticCode::InvalidArgument, - "policy metadata must be a JSON object"); + return failure(eve::DiagnosticCode::InvalidArgument, "policy metadata must be a JSON object"); auto canonical = metadataValue.toJson(); if (!canonical.ok()) return eve::Result::failure(canonical.status()); @@ -114,21 +113,20 @@ eve::Result makeDescriptor(const std::string& domain, const st const auto& error = errors.front(); return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::InvalidArgument, error.message, error.path, - eve::DiagnosticDetails{{"schemaId", schemaId}, - {"schemaVersion", std::to_string(version)}, - {"validationCode", error.code}}, + eve::DiagnosticDetails{ + {"schemaId", schemaId}, {"schemaVersion", std::to_string(version)}, {"validationCode", error.code}}, "policyregistry.schema")); } } PolicyDescriptor descriptor; - descriptor.domain = domain; - descriptor.name = name; - descriptor.version = eve::SchemaVersion(static_cast(version)); - descriptor.priority = priority; - descriptor.enabled = enabled; - descriptor.kind = kind; - descriptor.schemaId = schemaId; + descriptor.domain = domain; + descriptor.name = name; + descriptor.version = eve::SchemaVersion(static_cast(version)); + descriptor.priority = priority; + descriptor.enabled = enabled; + descriptor.kind = kind; + descriptor.schemaId = schemaId; descriptor.metadataJson = std::move(canonical).takeValue(); return eve::Result::success(std::move(descriptor)); } @@ -156,8 +154,7 @@ eve::Value policyDescriptorValue(const PolicyDescriptor& descriptor) { } template -eve::Result registryFailure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { +eve::Result registryFailure(eve::DiagnosticCode code, std::string message, std::string path = {}) { return eve::Result::failure( eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "policyregistry")); } @@ -166,9 +163,9 @@ eve::Result registryFailure(eve::DiagnosticCode code, std::string message, std::string implementationKindName(ImplementationKind kind) { switch (kind) { - case ImplementationKind::Builtin: return "builtin"; - case ImplementationKind::Script: return "script"; - case ImplementationKind::Batch: return "batch"; + case ImplementationKind::Builtin: return "builtin"; + case ImplementationKind::Script: return "script"; + case ImplementationKind::Batch: return "batch"; } return {}; } @@ -200,11 +197,11 @@ PolicyEvent PolicyRegistry::projectEvent(const Storage::Event& event) { result.name = "policy_replaced"; else result.name = "policy_removed"; - result.domain = event.key.first; + result.domain = event.key.first; result.policyName = event.key.second; - result.version = event.data.version; + result.version = event.data.version; result.generation = event.generation; - result.enabled = event.data.enabled; + result.enabled = event.data.enabled; return result; } @@ -218,41 +215,41 @@ PolicyRegistry::Storage::Handle PolicyRegistry::storageHandle(const PolicyHandle void PolicyRegistry::clearEventProjection() const { eventProjection_.clear(); } -eve::Result PolicyRegistry::insert( - const std::string& domain, const std::string& name, int version, int priority, bool enabled, - const std::string& kind, const std::string& schemaId, const std::string& metadataJson) { +eve::Result PolicyRegistry::insert(const std::string& domain, const std::string& name, int version, + int priority, bool enabled, const std::string& kind, + const std::string& schemaId, const std::string& metadataJson) { auto descriptor = makeDescriptor(domain, name, version, priority, enabled, kind, schemaId, metadataJson); if (!descriptor.ok()) { return eve::Result::failure(descriptor.status()); } PolicyDescriptor value = std::move(descriptor).takeValue(); - const EventData data{value.version, value.enabled}; - auto result = storage_.insert(Key{domain, name}, std::move(value), data); + const EventData data{value.version, value.enabled}; + auto result = storage_.insert(Key{domain, name}, std::move(value), data); if (!result.ok()) { return eve::Result::failure(result.status()); } const eve::Status mutationStatus = result.status(); - const auto handle = std::move(result).takeValue(); + const auto handle = std::move(result).takeValue(); clearEventProjection(); if (const auto next = revision_.incremented()) revision_ = *next; return eve::Result::success(projectHandle(handle), mutationStatus); } -eve::Result PolicyRegistry::replace( - const std::string& domain, const std::string& name, int version, int priority, bool enabled, - const std::string& kind, const std::string& schemaId, const std::string& metadataJson) { +eve::Result PolicyRegistry::replace(const std::string& domain, const std::string& name, int version, + int priority, bool enabled, const std::string& kind, + const std::string& schemaId, const std::string& metadataJson) { auto descriptor = makeDescriptor(domain, name, version, priority, enabled, kind, schemaId, metadataJson); if (!descriptor.ok()) { return eve::Result::failure(descriptor.status()); } PolicyDescriptor value = std::move(descriptor).takeValue(); - const EventData data{value.version, value.enabled}; - auto result = storage_.replace(Key{domain, name}, std::move(value), data); + const EventData data{value.version, value.enabled}; + auto result = storage_.replace(Key{domain, name}, std::move(value), data); if (!result.ok()) { return eve::Result::failure(result.status()); } const eve::Status mutationStatus = result.status(); - const auto handle = std::move(result).takeValue(); + const auto handle = std::move(result).takeValue(); clearEventProjection(); if (const auto next = revision_.incremented()) revision_ = *next; return eve::Result::success(projectHandle(handle), mutationStatus); @@ -260,18 +257,18 @@ eve::Result PolicyRegistry::replace( eve::Result PolicyRegistry::remove(const std::string& domain, const std::string& name) { const Key key{domain, name}; - auto current = storage_.resolve(key); + auto current = storage_.resolve(key); if (!current.ok()) { return eve::Result::failure(current.status()); } - const auto& descriptor = current.value().get(); + const auto& descriptor = current.value().get(); const EventData data{descriptor.version, descriptor.enabled}; - auto result = storage_.remove(key, data); + auto result = storage_.remove(key, data); if (!result.ok()) { return eve::Result::failure(result.status()); } const eve::Status mutationStatus = result.status(); - const auto handle = std::move(result).takeValue(); + const auto handle = std::move(result).takeValue(); clearEventProjection(); if (const auto next = revision_.incremented()) revision_ = *next; return eve::Result::success(projectHandle(handle), mutationStatus); @@ -279,7 +276,7 @@ eve::Result PolicyRegistry::remove(const std::string& domain, cons eve::Result PolicyRegistry::enable(const std::string& domain, const std::string& name, bool enabled) { const Key key{domain, name}; - auto current = storage_.resolve(key); + auto current = storage_.resolve(key); if (!current.ok()) { return eve::Result::failure(current.status()); } @@ -290,44 +287,41 @@ eve::Result PolicyRegistry::enable(const std::string& domain, cons return eve::Result::failure(handle.status()); } return eve::Result::success(projectHandle(handle.value()), - eve::Status::success(eve::StatusCode::NoOp)); + eve::Status::success(eve::StatusCode::NoOp)); } value.enabled = enabled; const EventData data{value.version, value.enabled}; - auto result = storage_.replace(key, std::move(value), data, "enabled"); + auto result = storage_.replace(key, std::move(value), data, "enabled"); if (!result.ok()) { return eve::Result::failure(result.status()); } const eve::Status mutationStatus = result.status(); - const auto handle = std::move(result).takeValue(); + const auto handle = std::move(result).takeValue(); clearEventProjection(); if (const auto next = revision_.incremented()) revision_ = *next; return eve::Result::success(projectHandle(handle), mutationStatus); } -eve::ResultRef PolicyRegistry::resolve( - const std::string& domain, const std::string& name) const { +eve::ResultRef PolicyRegistry::resolve(const std::string& domain, + const std::string& name) const { auto result = storage_.resolve(Key{domain, name}); if (!result.ok()) return eve::ResultRef::failure(result.status()); return eve::ResultRef::success(std::cref(result.value().get())); } -eve::ResultRef PolicyRegistry::resolveHandle( - const PolicyHandle& handle) const { +eve::ResultRef PolicyRegistry::resolveHandle(const PolicyHandle& handle) const { auto result = storage_.resolve(storageHandle(handle)); if (!result.ok()) return eve::ResultRef::failure(result.status()); return eve::ResultRef::success(std::cref(result.value().get())); } -eve::Result PolicyRegistry::handle( - const std::string& domain, const std::string& name) const { +eve::Result PolicyRegistry::handle(const std::string& domain, const std::string& name) const { auto result = storage_.handle(Key{domain, name}); if (!result.ok()) return eve::Result::failure(result.status()); return eve::Result::success(projectHandle(result.value())); } -eve::Result PolicyRegistry::generationOf( - const std::string& domain, const std::string& name) const { +eve::Result PolicyRegistry::generationOf(const std::string& domain, const std::string& name) const { return storage_.generationOf(Key{domain, name}); } @@ -403,7 +397,7 @@ void PolicyRegistry::clearEvents() { } std::string PolicyRegistry::snapshotJson() const { - const auto state = storage_.snapshotState(); + const auto state = storage_.snapshotState(); eve::Value::Array descriptors; eve::Value::Array events; eve::Value::Array generations; @@ -419,7 +413,7 @@ std::string PolicyRegistry::snapshotJson() const { generations.emplace_back(std::move(generation)); if (!entry.value.has_value()) continue; - const auto& descriptor = *entry.value; + const auto& descriptor = *entry.value; eve::Value::Object item; item.emplace("domain", eve::Value(descriptor.domain)); item.emplace("enabled", eve::Value(descriptor.enabled)); @@ -434,7 +428,7 @@ std::string PolicyRegistry::snapshotJson() const { } for (const auto& raw : state.events) { - const auto event = projectEvent(raw); + const auto event = projectEvent(raw); eve::Value::Object item; item.emplace("domain", eve::Value(event.domain)); item.emplace("enabled", eve::Value(event.enabled)); @@ -463,20 +457,20 @@ eve::Result PolicyRegistry::restoreJson(const std::string& json) { return eve::Result::failure(parsed.status()); } const eve::Value value = std::move(parsed).takeValue(); - const auto* root = value.getIf(); + const auto* root = value.getIf(); if (!root) { return failure(eve::DiagnosticCode::ParseError, "policy snapshot must be an object"); } - const auto* snapshotVersion = field(*root, "version"); - const auto* versionNumber = snapshotVersion ? snapshotVersion->getIf() : nullptr; - const auto* descriptorValue = field(*root, "descriptors"); - const auto* generationValue = field(*root, "generations"); - const auto* eventValue = field(*root, "events"); - const auto* nextSequenceValue = field(*root, "nextEventSequence"); - const auto* descriptorArray = descriptorValue ? descriptorValue->getIf() : nullptr; - const auto* generationArray = generationValue ? generationValue->getIf() : nullptr; - const auto* eventArray = eventValue ? eventValue->getIf() : nullptr; - std::uint64_t nextSequence = 0; + const auto* snapshotVersion = field(*root, "version"); + const auto* versionNumber = snapshotVersion ? snapshotVersion->getIf() : nullptr; + const auto* descriptorValue = field(*root, "descriptors"); + const auto* generationValue = field(*root, "generations"); + const auto* eventValue = field(*root, "events"); + const auto* nextSequenceValue = field(*root, "nextEventSequence"); + const auto* descriptorArray = descriptorValue ? descriptorValue->getIf() : nullptr; + const auto* generationArray = generationValue ? generationValue->getIf() : nullptr; + const auto* eventArray = eventValue ? eventValue->getIf() : nullptr; + std::uint64_t nextSequence = 0; if (!versionNumber || *versionNumber != 1 || !descriptorArray || !generationArray || !eventArray || !nextSequenceValue || !readUint64String(*nextSequenceValue, nextSequence) || nextSequence == 0) { return failure(eve::DiagnosticCode::ParseError, "invalid policy snapshot fields"); @@ -484,47 +478,46 @@ eve::Result PolicyRegistry::restoreJson(const std::string& json) { Storage::State candidate; for (std::size_t index = 0; index < generationArray->size(); ++index) { - const auto* item = (*generationArray)[index].getIf(); - std::string domain; - std::string name; - std::uint64_t generation = 0; - const auto* generationField = item ? field(*item, "generation") : nullptr; + const auto* item = (*generationArray)[index].getIf(); + std::string domain; + std::string name; + std::uint64_t generation = 0; + const auto* generationField = item ? field(*item, "generation") : nullptr; if (!item || !readString(*item, "domain", domain) || !readString(*item, "name", name) || domain.empty() || name.empty() || !generationField || !readUint64String(*generationField, generation) || generation == 0 || - !candidate.entries.emplace(Key{domain, name}, Storage::Entry{ - eve::Generation(generation), std::nullopt}) - .second) { + !candidate.entries.emplace(Key{domain, name}, Storage::Entry{eve::Generation(generation), std::nullopt}) + .second) { return failure(eve::DiagnosticCode::ParseError, "invalid policy generation at index " + std::to_string(index)); } } for (std::size_t index = 0; index < descriptorArray->size(); ++index) { - const auto* item = (*descriptorArray)[index].getIf(); - std::string domain; - std::string name; - std::string kindName; - std::string schemaId; + const auto* item = (*descriptorArray)[index].getIf(); + std::string domain; + std::string name; + std::string kindName; + std::string schemaId; eve::SchemaVersion version; - int priority = 0; - std::uint64_t generation = 0; - bool enabled = false; - const auto* versionField = item ? field(*item, "version") : nullptr; - const auto* priorityField = item ? field(*item, "priority") : nullptr; - const auto* enabledField = item ? field(*item, "enabled") : nullptr; - const auto* generationField = item ? field(*item, "generation") : nullptr; - const auto* metadata = item ? field(*item, "metadata") : nullptr; - ImplementationKind kind = ImplementationKind::Builtin; + int priority = 0; + std::uint64_t generation = 0; + bool enabled = false; + const auto* versionField = item ? field(*item, "version") : nullptr; + const auto* priorityField = item ? field(*item, "priority") : nullptr; + const auto* enabledField = item ? field(*item, "enabled") : nullptr; + const auto* generationField = item ? field(*item, "generation") : nullptr; + const auto* metadata = item ? field(*item, "metadata") : nullptr; + ImplementationKind kind = ImplementationKind::Builtin; if (!item || !readString(*item, "domain", domain) || !readString(*item, "name", name) || domain.empty() || name.empty() || !versionField || !readPositiveSchemaVersion(*versionField, version) || !priorityField || !readInt(*priorityField, priority) || !enabledField || !enabledField->isBool() || !generationField || - !readUint64String(*generationField, generation) || generation == 0 || !readString(*item, "kind", kindName) || - !parseImplementationKind(kindName, kind) || !metadata || !metadata->isObject() || - !readString(*item, "schemaId", schemaId)) { + !readUint64String(*generationField, generation) || generation == 0 || + !readString(*item, "kind", kindName) || !parseImplementationKind(kindName, kind) || !metadata || + !metadata->isObject() || !readString(*item, "schemaId", schemaId)) { return failure(eve::DiagnosticCode::ParseError, "invalid policy descriptor at index " + std::to_string(index)); } - enabled = *enabledField->getIf(); + enabled = *enabledField->getIf(); auto entry = candidate.entries.find(Key{domain, name}); if (entry == candidate.entries.end() || entry->second.generation != eve::Generation(generation) || entry->second.value.has_value()) { @@ -536,35 +529,36 @@ eve::Result PolicyRegistry::restoreJson(const std::string& json) { return eve::Result::failure(metadataJson.status()); } PolicyDescriptor descriptor; - descriptor.domain = domain; - descriptor.name = name; - descriptor.version = version; - descriptor.priority = priority; - descriptor.enabled = enabled; - descriptor.kind = kind; - descriptor.schemaId = schemaId; - descriptor.generation = eve::Generation(generation); + descriptor.domain = domain; + descriptor.name = name; + descriptor.version = version; + descriptor.priority = priority; + descriptor.enabled = enabled; + descriptor.kind = kind; + descriptor.schemaId = schemaId; + descriptor.generation = eve::Generation(generation); descriptor.metadataJson = std::move(metadataJson).takeValue(); - entry->second.value = std::move(descriptor); + entry->second.value = std::move(descriptor); } eve::EventSequence previousSequence; for (std::size_t index = 0; index < eventArray->size(); ++index) { - const auto* item = (*eventArray)[index].getIf(); - std::string name; - std::string domain; - std::string policyName; - std::uint64_t sequence = 0; - std::uint64_t generation = 0; - const auto* sequenceField = item ? field(*item, "sequence") : nullptr; - const auto* generationField = item ? field(*item, "generation") : nullptr; - const auto* enabledField = item ? field(*item, "enabled") : nullptr; - const auto* versionField = item ? field(*item, "version") : nullptr; + const auto* item = (*eventArray)[index].getIf(); + std::string name; + std::string domain; + std::string policyName; + std::uint64_t sequence = 0; + std::uint64_t generation = 0; + const auto* sequenceField = item ? field(*item, "sequence") : nullptr; + const auto* generationField = item ? field(*item, "generation") : nullptr; + const auto* enabledField = item ? field(*item, "enabled") : nullptr; + const auto* versionField = item ? field(*item, "version") : nullptr; eve::SchemaVersion version{1}; if (!item || !readString(*item, "name", name) || !readString(*item, "domain", domain) || !readString(*item, "policyName", policyName) || domain.empty() || policyName.empty() || (name != "policy_registered" && name != "policy_replaced" && name != "policy_removed" && - name != "policy_enabled") || !sequenceField || !readUint64String(*sequenceField, sequence) || sequence == 0 || + name != "policy_enabled") || + !sequenceField || !readUint64String(*sequenceField, sequence) || sequence == 0 || (!previousSequence.isZero() && eve::EventSequence(sequence) <= previousSequence) || !generationField || !readUint64String(*generationField, generation) || generation == 0 || !enabledField || !enabledField->isBool()) { @@ -576,14 +570,14 @@ eve::Result PolicyRegistry::restoreJson(const std::string& json) { "invalid policy event version at index " + std::to_string(index)); } Storage::Event event; - event.sequence = eve::EventSequence(sequence); - event.operation = name == "policy_removed" - ? eve::RegistryOperation::Remove - : (generation == 1 ? eve::RegistryOperation::Insert : eve::RegistryOperation::Replace); - event.key = Key{domain, policyName}; - event.generation = eve::Generation(generation); - event.tombstone = name == "policy_removed"; - event.label = name == "policy_enabled" ? "enabled" : std::string{}; + event.sequence = eve::EventSequence(sequence); + event.operation = name == "policy_removed" + ? eve::RegistryOperation::Remove + : (generation == 1 ? eve::RegistryOperation::Insert : eve::RegistryOperation::Replace); + event.key = Key{domain, policyName}; + event.generation = eve::Generation(generation); + event.tombstone = name == "policy_removed"; + event.label = name == "policy_enabled" ? "enabled" : std::string{}; event.data.version = version; event.data.enabled = *enabledField->getIf(); candidate.events.push_back(std::move(event)); @@ -601,20 +595,19 @@ eve::Result PolicyRegistry::restoreJson(const std::string& json) { } clearEventProjection(); revision_ = eve::Revision(nextSequence - 1); - tick_ = eve::SimulationTick{}; + tick_ = eve::SimulationTick{}; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result PolicyRegistry::snapshot( - const eve::SnapshotHashProvider& hashProvider) const { +eve::Result PolicyRegistry::snapshot(const eve::SnapshotHashProvider& hashProvider) const { auto payload = eve::Value::fromJson(snapshotJson()); if (!payload.ok()) return eve::Result::failure(payload.status()); return eve::makeSnapshotEnvelope("policyregistry.registry", policySchema(), eve::SchemaVersion(1), instanceId_, revision_, tick_, std::move(payload).takeValue(), hashProvider); } -eve::Result PolicyRegistry::restoreSnapshot( - const eve::SnapshotEnvelope& source, const eve::SnapshotHashProvider& hashProvider) { +eve::Result PolicyRegistry::restoreSnapshot(const eve::SnapshotEnvelope& source, + const eve::SnapshotHashProvider& hashProvider) { if (source.type != "policyregistry.registry" || source.schema != policySchema()) return failure(eve::DiagnosticCode::InvalidArgument, "snapshot does not belong to policyregistry::PolicyRegistry"); @@ -628,22 +621,21 @@ eve::Result PolicyRegistry::restoreSnapshot( auto restored = restoreJson(std::move(payload).takeValue()); if (!restored.ok()) return eve::Result::failure(restored.status()); const auto& candidate = migrated.value(); - instanceId_ = candidate.instanceId; - revision_ = candidate.revision; - tick_ = candidate.tick; + instanceId_ = candidate.instanceId; + revision_ = candidate.revision; + tick_ = candidate.tick; return eve::Result::success(); } -eve::Result PolicyRegistry::snapshotEnvelopeJson( - const eve::SnapshotHashProvider& hashProvider) const { +eve::Result PolicyRegistry::snapshotEnvelopeJson(const eve::SnapshotHashProvider& hashProvider) const { auto value = snapshot(hashProvider); if (!value.ok()) return eve::Result::failure(value.status()); return std::move(value).andThen( [](eve::SnapshotEnvelope&& envelope) { return eve::serializeSnapshotEnvelope(envelope); }); } -eve::Result PolicyRegistry::restoreSnapshotJson( - std::string_view json, const eve::SnapshotHashProvider& hashProvider) { +eve::Result PolicyRegistry::restoreSnapshotJson(std::string_view json, + const eve::SnapshotHashProvider& hashProvider) { auto source = eve::parseSnapshotEnvelope(json, hashProvider); if (!source.ok()) return eve::Result::failure(source.status()); return restoreSnapshot(std::move(source).takeValue(), hashProvider); @@ -665,7 +657,7 @@ PolicyRegistry* PolicyRegistryModule::newRegistry() { Module_IMPL(PolicyRegistryModule, new PolicyRegistryModule()); void PolicyRegistryModule::expose(ssq::Table& table) { - const HSQUIRRELVM vm = table.getHandle(); + const HSQUIRRELVM vm = table.getHandle(); auto descriptor = table.addClass( "PolicyDescriptor", std::function([]() -> PolicyDescriptor* { return nullptr; }), false); descriptor.addFunc("getDomain", [](PolicyDescriptor* v) { return v ? v->domain : std::string{}; }); @@ -685,15 +677,13 @@ void PolicyRegistryModule::expose(ssq::Table& table) { auto event = table.addClass( "PolicyEvent", std::function([]() -> PolicyEvent* { return nullptr; }), false); - event.addFunc("getSequence", [](PolicyEvent* v) { - return v ? static_cast(v->sequence.value()) : std::int64_t{0}; - }); + event.addFunc("getSequence", + [](PolicyEvent* v) { return v ? static_cast(v->sequence.value()) : std::int64_t{0}; }); event.addFunc("getName", [](PolicyEvent* v) { return v ? v->name : std::string{}; }); event.addFunc("getDomain", [](PolicyEvent* v) { return v ? v->domain : std::string{}; }); event.addFunc("getPolicyName", [](PolicyEvent* v) { return v ? v->policyName : std::string{}; }); - event.addFunc("getVersion", [](PolicyEvent* v) { - return v ? static_cast(v->version.value()) : std::int64_t{0}; - }); + event.addFunc("getVersion", + [](PolicyEvent* v) { return v ? static_cast(v->version.value()) : std::int64_t{0}; }); event.addFunc("getGeneration", [](PolicyEvent* v) { return v ? static_cast(v->generation.value()) : std::int64_t{0}; }); @@ -701,58 +691,59 @@ void PolicyRegistryModule::expose(ssq::Table& table) { auto registry = table.addClass( "PolicyRegistry", std::function([]() -> PolicyRegistry* { return nullptr; }), false); - registry.addFunc("insert", [vm](PolicyRegistry* value, const std::string& domain, - const std::string& policyName, int version, int priority, - bool enabled, const std::string& kind, - const std::string& schemaId, const std::string& metadataJson) { + registry.addFunc("insert", [vm](PolicyRegistry* value, const std::string& domain, const std::string& policyName, + int version, int priority, bool enabled, const std::string& kind, + const std::string& schemaId, const std::string& metadataJson) { if (!value) return eve::script::projectResult( - vm, registryFailure(eve::DiagnosticCode::InvalidArgument, - "policy registry must not be null", "registry"), + vm, + registryFailure(eve::DiagnosticCode::InvalidArgument, "policy registry must not be null", + "registry"), [](PolicyHandle&& handle) { return policyHandleValue(handle); }); return eve::script::projectResult( vm, value->insert(domain, policyName, version, priority, enabled, kind, schemaId, metadataJson), [](PolicyHandle&& handle) { return policyHandleValue(handle); }); }); - registry.addFunc("replace", [vm](PolicyRegistry* value, const std::string& domain, - const std::string& policyName, int version, int priority, - bool enabled, const std::string& kind, - const std::string& schemaId, const std::string& metadataJson) { + registry.addFunc("replace", [vm](PolicyRegistry* value, const std::string& domain, const std::string& policyName, + int version, int priority, bool enabled, const std::string& kind, + const std::string& schemaId, const std::string& metadataJson) { if (!value) return eve::script::projectResult( - vm, registryFailure(eve::DiagnosticCode::InvalidArgument, - "policy registry must not be null", "registry"), + vm, + registryFailure(eve::DiagnosticCode::InvalidArgument, "policy registry must not be null", + "registry"), [](PolicyHandle&& handle) { return policyHandleValue(handle); }); return eve::script::projectResult( vm, value->replace(domain, policyName, version, priority, enabled, kind, schemaId, metadataJson), [](PolicyHandle&& handle) { return policyHandleValue(handle); }); }); - registry.addFunc("remove", [vm](PolicyRegistry* value, const std::string& domain, - const std::string& policyName) { + registry.addFunc("remove", [vm](PolicyRegistry* value, const std::string& domain, const std::string& policyName) { if (!value) return eve::script::projectResult( - vm, registryFailure(eve::DiagnosticCode::InvalidArgument, - "policy registry must not be null", "registry"), + vm, + registryFailure(eve::DiagnosticCode::InvalidArgument, "policy registry must not be null", + "registry"), [](PolicyHandle&& handle) { return policyHandleValue(handle); }); return eve::script::projectResult(vm, value->remove(domain, policyName), [](PolicyHandle&& handle) { return policyHandleValue(handle); }); }); - registry.addFunc("enable", [vm](PolicyRegistry* value, const std::string& domain, - const std::string& policyName, bool enabled) { + registry.addFunc( + "enable", [vm](PolicyRegistry* value, const std::string& domain, const std::string& policyName, bool enabled) { + if (!value) + return eve::script::projectResult( + vm, + registryFailure(eve::DiagnosticCode::InvalidArgument, + "policy registry must not be null", "registry"), + [](PolicyHandle&& handle) { return policyHandleValue(handle); }); + return eve::script::projectResult(vm, value->enable(domain, policyName, enabled), + [](PolicyHandle&& handle) { return policyHandleValue(handle); }); + }); + registry.addFunc("resolve", [vm](PolicyRegistry* value, const std::string& domain, const std::string& policyName) { if (!value) return eve::script::projectResult( - vm, registryFailure(eve::DiagnosticCode::InvalidArgument, - "policy registry must not be null", "registry"), - [](PolicyHandle&& handle) { return policyHandleValue(handle); }); - return eve::script::projectResult(vm, value->enable(domain, policyName, enabled), - [](PolicyHandle&& handle) { return policyHandleValue(handle); }); - }); - registry.addFunc("resolve", [vm](PolicyRegistry* value, const std::string& domain, - const std::string& policyName) { - if (!value) - return eve::script::projectResult( - vm, registryFailure>( - eve::DiagnosticCode::InvalidArgument, "policy registry must not be null", "registry"), + vm, + registryFailure>( + eve::DiagnosticCode::InvalidArgument, "policy registry must not be null", "registry"), [](std::reference_wrapper&& descriptor) { return policyDescriptorValue(descriptor.get()); }); @@ -762,29 +753,32 @@ void PolicyRegistryModule::expose(ssq::Table& table) { }); }); registry.addFunc("resolveHandle", [vm](PolicyRegistry* value, const std::string& domain, - const std::string& policyName, std::int64_t generation) { + const std::string& policyName, std::int64_t generation) { using DescriptorRef = std::reference_wrapper; if (!value) return eve::script::projectResult( - vm, registryFailure(eve::DiagnosticCode::InvalidArgument, - "policy registry must not be null", "registry"), + vm, + registryFailure(eve::DiagnosticCode::InvalidArgument, "policy registry must not be null", + "registry"), [](DescriptorRef&& descriptor) { return policyDescriptorValue(descriptor.get()); }); if (generation <= 0) return eve::script::projectResult( - vm, registryFailure(eve::DiagnosticCode::InvalidArgument, - "policy generation must be positive", "generation"), + vm, + registryFailure(eve::DiagnosticCode::InvalidArgument, + "policy generation must be positive", "generation"), [](DescriptorRef&& descriptor) { return policyDescriptorValue(descriptor.get()); }); return eve::script::projectResult( - vm, value->resolveHandle(PolicyHandle{domain, policyName, - eve::Generation(static_cast(generation))}), + vm, + value->resolveHandle( + PolicyHandle{domain, policyName, eve::Generation(static_cast(generation))}), [](DescriptorRef&& descriptor) { return policyDescriptorValue(descriptor.get()); }); }); - registry.addFunc("handle", [vm](PolicyRegistry* value, const std::string& domain, - const std::string& policyName) { + registry.addFunc("handle", [vm](PolicyRegistry* value, const std::string& domain, const std::string& policyName) { if (!value) return eve::script::projectResult( - vm, registryFailure(eve::DiagnosticCode::InvalidArgument, - "policy registry must not be null", "registry"), + vm, + registryFailure(eve::DiagnosticCode::InvalidArgument, "policy registry must not be null", + "registry"), [](PolicyHandle&& handle) { return policyHandleValue(handle); }); return eve::script::projectResult(vm, value->handle(domain, policyName), [](PolicyHandle&& handle) { return policyHandleValue(handle); }); @@ -809,23 +803,21 @@ void PolicyRegistryModule::expose(ssq::Table& table) { registry.addFunc("restoreJson", [vm](PolicyRegistry* value, const std::string& json) { if (!value) return eve::script::projectResult( - vm, registryFailure(eve::DiagnosticCode::InvalidArgument, - "policy registry must not be null", "registry")); + vm, registryFailure(eve::DiagnosticCode::InvalidArgument, "policy registry must not be null", + "registry")); return eve::script::projectResult(vm, value->restoreJson(json)); }); registry.addFunc("generationOf", [vm](PolicyRegistry* value, const std::string& domain, - const std::string& policyName) { + const std::string& policyName) { if (!value) return eve::script::projectResult( - vm, registryFailure(eve::DiagnosticCode::InvalidArgument, - "policy registry must not be null", "registry"), - [](eve::Generation generation) { - return eve::Value(static_cast(generation.value())); - }); - return eve::script::projectResult(vm, value->generationOf(domain, policyName), - [](eve::Generation generation) { - return eve::Value(static_cast(generation.value())); - }); + vm, + registryFailure(eve::DiagnosticCode::InvalidArgument, + "policy registry must not be null", "registry"), + [](eve::Generation generation) { return eve::Value(static_cast(generation.value())); }); + return eve::script::projectResult(vm, value->generationOf(domain, policyName), [](eve::Generation generation) { + return eve::Value(static_cast(generation.value())); + }); }); auto cls = table.addClass(name, PolicyRegistryModule::create, false); diff --git a/src/modules/policyregistry/PolicyRegistry.h b/src/modules/policyregistry/PolicyRegistry.h index 8377dafc..196c556d 100644 --- a/src/modules/policyregistry/PolicyRegistry.h +++ b/src/modules/policyregistry/PolicyRegistry.h @@ -19,8 +19,8 @@ enum class ImplementationKind { Builtin, Script, Batch }; /** @brief Generation-qualified policy handle used to detect replacement or removal. */ struct PolicyHandle { - std::string domain; - std::string name; + std::string domain; + std::string name; eve::Generation generation; }; @@ -35,18 +35,18 @@ struct PolicyDescriptor { std::string schemaId; std::string metadataJson = "{}"; // Compatibility projection; the VersionedRegistry slot owns generation. - eve::Generation generation; + eve::Generation generation; }; /** @brief Deterministically sequenced policy registry mutation event. */ struct PolicyEvent { eve::EventSequence sequence; - std::string name; - std::string domain; - std::string policyName; - eve::SchemaVersion version; - eve::Generation generation; - bool enabled = false; + std::string name; + std::string domain; + std::string policyName; + eve::SchemaVersion version; + eve::Generation generation; + bool enabled = false; }; /** @brief Deterministic registry for discoverable, non-executable policy descriptions. */ @@ -59,31 +59,26 @@ public: explicit PolicyRegistry(eve::PersistentId instanceId = {}); /** @brief Inserts a descriptor through the common checked registry. */ - [[nodiscard]] eve::Result insert( - const std::string& domain, const std::string& name, int version, int priority, bool enabled, - const std::string& kind, const std::string& schemaId, const std::string& metadataJson); + [[nodiscard]] eve::Result insert(const std::string& domain, const std::string& name, int version, + int priority, bool enabled, const std::string& kind, + const std::string& schemaId, const std::string& metadataJson); /** @brief Replaces a descriptor and invalidates the prior generation handle. */ - [[nodiscard]] eve::Result replace( - const std::string& domain, const std::string& name, int version, int priority, bool enabled, - const std::string& kind, const std::string& schemaId, const std::string& metadataJson); + [[nodiscard]] eve::Result replace(const std::string& domain, const std::string& name, int version, + int priority, bool enabled, const std::string& kind, + const std::string& schemaId, const std::string& metadataJson); /** @brief Removes a descriptor and returns its retained tombstone handle. */ - [[nodiscard]] eve::Result remove( - const std::string& domain, const std::string& name); + [[nodiscard]] eve::Result remove(const std::string& domain, const std::string& name); /** @brief Changes enabled state through the same replacement generation path. */ - [[nodiscard]] eve::Result enable( - const std::string& domain, const std::string& name, bool enabled); + [[nodiscard]] eve::Result enable(const std::string& domain, const std::string& name, bool enabled); /** @brief Resolves a descriptor through the common checked-registry API. */ - [[nodiscard]] eve::ResultRef resolve( - const std::string& domain, const std::string& name) const; + [[nodiscard]] eve::ResultRef resolve(const std::string& domain, + const std::string& name) const; /** @brief Resolves a generation-qualified policy handle through Result. */ - [[nodiscard]] eve::ResultRef resolveHandle( - const PolicyHandle& handle) const; + [[nodiscard]] eve::ResultRef resolveHandle(const PolicyHandle& handle) const; /** @brief Returns the current generation-qualified handle through Result. */ - [[nodiscard]] eve::Result handle( - const std::string& domain, const std::string& name) const; + [[nodiscard]] eve::Result handle(const std::string& domain, const std::string& name) const; /** @brief Returns the latest generation, including a retained tombstone. */ - [[nodiscard]] eve::Result generationOf( - const std::string& domain, const std::string& name) const; + [[nodiscard]] eve::Result generationOf(const std::string& domain, const std::string& name) const; /** @brief Returns whether a key is retained as a removal tombstone. */ [[nodiscard]] bool isTombstone(const std::string& domain, const std::string& name) const noexcept; /** @brief Returns whether a generation-qualified policy handle is stale. */ @@ -111,17 +106,15 @@ public: [[nodiscard]] eve::Result restoreJson(const std::string& json); /** @brief Captures this registry in the common snapshot envelope. */ - [[nodiscard]] eve::Result snapshot( - const eve::SnapshotHashProvider& hashProvider) const; + [[nodiscard]] eve::Result snapshot(const eve::SnapshotHashProvider& hashProvider) const; /** @brief Restores a verified snapshot transactionally without per-item events. */ - [[nodiscard]] eve::Result restoreSnapshot( - const eve::SnapshotEnvelope& snapshot, const eve::SnapshotHashProvider& hashProvider); + [[nodiscard]] eve::Result restoreSnapshot(const eve::SnapshotEnvelope& snapshot, + const eve::SnapshotHashProvider& hashProvider); /** @brief Serializes the common snapshot envelope as canonical JSON. */ - [[nodiscard]] eve::Result snapshotEnvelopeJson( - const eve::SnapshotHashProvider& hashProvider) const; + [[nodiscard]] eve::Result snapshotEnvelopeJson(const eve::SnapshotHashProvider& hashProvider) const; /** @brief Parses and transactionally restores a common snapshot envelope. */ - [[nodiscard]] eve::Result restoreSnapshotJson( - std::string_view json, const eve::SnapshotHashProvider& hashProvider); + [[nodiscard]] eve::Result restoreSnapshotJson(std::string_view json, + const eve::SnapshotHashProvider& hashProvider); /** * @brief Subscribes to successful registry mutations. @@ -138,10 +131,10 @@ private: }; using Storage = eve::VersionedRegistry; - static PolicyEvent projectEvent(const Storage::Event& event); - static PolicyHandle projectHandle(const Storage::Handle& handle); + static PolicyEvent projectEvent(const Storage::Event& event); + static PolicyHandle projectHandle(const Storage::Handle& handle); static Storage::Handle storageHandle(const PolicyHandle& handle); - void clearEventProjection() const; + void clearEventProjection() const; Storage storage_; eve::PersistentId instanceId_; diff --git a/src/modules/procgen/ArtifactPublish.cpp b/src/modules/procgen/ArtifactPublish.cpp index 0e3aa0c1..02a3de7e 100644 --- a/src/modules/procgen/ArtifactPublish.cpp +++ b/src/modules/procgen/ArtifactPublish.cpp @@ -13,91 +13,94 @@ namespace eve::procgen { namespace { struct PublicationBuffer { - std::vector parts; + std::vector parts; std::vector> pointBuffers; - eve::artifact::PublicationView view; + eve::artifact::PublicationView view; }; eve::artifact::PartKind partKind(ArtifactType type) { switch (type) { - case ArtifactType::Grid: return eve::artifact::PartKind::Grid; - case ArtifactType::PointSet: return eve::artifact::PartKind::PointSet; - case ArtifactType::MeshData: return eve::artifact::PartKind::MeshData; - case ArtifactType::ImageData: return eve::artifact::PartKind::ImageData; - case ArtifactType::Collider: return eve::artifact::PartKind::Collider; - case ArtifactType::Composite: break; + case ArtifactType::Grid: return eve::artifact::PartKind::Grid; + case ArtifactType::PointSet: return eve::artifact::PartKind::PointSet; + case ArtifactType::MeshData: return eve::artifact::PartKind::MeshData; + case ArtifactType::ImageData: return eve::artifact::PartKind::ImageData; + case ArtifactType::Collider: return eve::artifact::PartKind::Collider; + case ArtifactType::Composite: break; } return eve::artifact::PartKind::Grid; } eve::artifact::Bounds boundsView(const Bounds& bounds) { - return {bounds.minX, bounds.minY, bounds.minZ, bounds.maxX, bounds.maxY, bounds.maxZ, - bounds.valid}; + return {bounds.minX, bounds.minY, bounds.minZ, bounds.maxX, bounds.maxY, bounds.maxZ, bounds.valid}; } void appendPartView(const ArtifactPart& source, PublicationBuffer& buffer) { eve::artifact::PartView view; - view.id = eve::PersistentId::fromUuid(source.id); - view.role = source.role; - view.kind = partKind(source.type); + view.id = eve::PersistentId::fromUuid(source.id); + view.role = source.role; + view.kind = partKind(source.type); view.schemaVersion = source.schemaVersion.value(); - view.buildKey = source.buildKey.format(); - view.bounds = boundsView(source.bounds); - std::visit([&view, &buffer](const auto& payload) { - using T = std::decay_t; - if constexpr (std::is_same_v) { - view.positions = payload.positions(); - view.normals = payload.normals(); - view.uvs = payload.uvs(); - view.indices = payload.indices(); - } else if constexpr (std::is_same_v) { - view.positions = payload.vertices; - view.indices = payload.indices; - } else if constexpr (std::is_same_v) { - view.width = payload.getWidth(); - view.height = payload.getHeight(); - view.cells = payload.cells(); - } else if constexpr (std::is_same_v) { - buffer.pointBuffers.emplace_back(); - auto& points = buffer.pointBuffers.back(); - points.reserve(payload.points().size()); - for (const auto& point : payload.points()) points.push_back({point.x, point.y, point.z}); - view.points = points; - } - }, source.payload); + view.buildKey = source.buildKey.format(); + view.bounds = boundsView(source.bounds); + std::visit( + [&view, &buffer](const auto& payload) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + view.positions = payload.positions(); + view.normals = payload.normals(); + view.uvs = payload.uvs(); + view.indices = payload.indices(); + } else if constexpr (std::is_same_v) { + view.positions = payload.vertices; + view.indices = payload.indices; + } else if constexpr (std::is_same_v) { + view.width = payload.getWidth(); + view.height = payload.getHeight(); + view.cells = payload.cells(); + } else if constexpr (std::is_same_v) { + buffer.pointBuffers.emplace_back(); + auto& points = buffer.pointBuffers.back(); + points.reserve(payload.points().size()); + for (const auto& point : payload.points()) points.push_back({point.x, point.y, point.z}); + view.points = points; + } + }, + source.payload); buffer.parts.emplace_back(view); } void appendLeafView(const GeneratedArtifact& source, PublicationBuffer& buffer) { eve::artifact::PartView view; - view.id = eve::PersistentId::fromUuid(source.id); - view.role = artifactTypeName(source.type); - view.kind = partKind(source.type); + view.id = eve::PersistentId::fromUuid(source.id); + view.role = artifactTypeName(source.type); + view.kind = partKind(source.type); view.schemaVersion = source.schemaVersion.value(); - view.buildKey = source.buildKey.format(); - view.bounds = boundsView(source.bounds); - std::visit([&view, &buffer](const auto& payload) { - using T = std::decay_t; - if constexpr (std::is_same_v) { - view.positions = payload.positions(); - view.normals = payload.normals(); - view.uvs = payload.uvs(); - view.indices = payload.indices(); - } else if constexpr (std::is_same_v) { - view.positions = payload.vertices; - view.indices = payload.indices; - } else if constexpr (std::is_same_v) { - view.width = payload.getWidth(); - view.height = payload.getHeight(); - view.cells = payload.cells(); - } else if constexpr (std::is_same_v) { - buffer.pointBuffers.emplace_back(); - auto& points = buffer.pointBuffers.back(); - points.reserve(payload.points().size()); - for (const auto& point : payload.points()) points.push_back({point.x, point.y, point.z}); - view.points = points; - } - }, source.payload); + view.buildKey = source.buildKey.format(); + view.bounds = boundsView(source.bounds); + std::visit( + [&view, &buffer](const auto& payload) { + using T = std::decay_t; + if constexpr (std::is_same_v) { + view.positions = payload.positions(); + view.normals = payload.normals(); + view.uvs = payload.uvs(); + view.indices = payload.indices(); + } else if constexpr (std::is_same_v) { + view.positions = payload.vertices; + view.indices = payload.indices; + } else if constexpr (std::is_same_v) { + view.width = payload.getWidth(); + view.height = payload.getHeight(); + view.cells = payload.cells(); + } else if constexpr (std::is_same_v) { + buffer.pointBuffers.emplace_back(); + auto& points = buffer.pointBuffers.back(); + points.reserve(payload.points().size()); + for (const auto& point : payload.points()) points.push_back({point.x, point.y, point.z}); + view.points = points; + } + }, + source.payload); buffer.parts.emplace_back(view); } @@ -113,19 +116,16 @@ PublicationBuffer makePublicationBuffer(const GeneratedArtifact& artifact) { buffer.pointBuffers.reserve(1); appendLeafView(artifact, buffer); } - buffer.view.id = eve::PersistentId::fromUuid(artifact.id); + buffer.view.id = eve::PersistentId::fromUuid(artifact.id); buffer.view.schemaVersion = artifact.schemaVersion.value(); - buffer.view.buildKey = artifact.buildKey.format(); - buffer.view.bounds = boundsView(artifact.bounds); - buffer.view.parts = std::span(buffer.parts.data(), - buffer.parts.size()); + buffer.view.buildKey = artifact.buildKey.format(); + buffer.view.bounds = boundsView(artifact.bounds); + buffer.view.parts = std::span(buffer.parts.data(), buffer.parts.size()); return buffer; } -eve::Result participantFailure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { - return eve::Result::failure( - eve::Diagnostic::error(code, std::move(message), std::move(path))); +eve::Result participantFailure(eve::DiagnosticCode code, std::string message, std::string path = {}) { + return eve::Result::failure(eve::Diagnostic::error(code, std::move(message), std::move(path))); } /** @brief Adapts one PreparedPublication to the common transaction protocol. */ @@ -141,11 +141,10 @@ public: std::string_view name() const noexcept override { return name_; } - [[nodiscard]] eve::Result prepare( - const eve::transaction::TransactionContext&) override { + [[nodiscard]] eve::Result prepare(const eve::transaction::TransactionContext&) override { if (phase_ != Phase::Idle) return participantFailure(eve::DiagnosticCode::PreconditionViolation, - "artifact provider participant is not idle", name_ + ".prepare"); + "artifact provider participant is not idle", name_ + ".prepare"); // Do not initialize a placeholder Result and then move-assign over it: // in assertion builds move assignment intentionally rejects overwriting // an unobserved Result. Returning from this lambda constructs the one @@ -155,68 +154,59 @@ public: try { return provider_->prepare(view_); } catch (const std::exception& error) { - return eve::Result>::failure( - eve::Diagnostic::error( - eve::DiagnosticCode::Failed, - std::string("artifact provider prepare threw: ") + error.what(), - name_ + ".prepare")); + return eve::Result>::failure(eve::Diagnostic::error( + eve::DiagnosticCode::Failed, std::string("artifact provider prepare threw: ") + error.what(), + name_ + ".prepare")); } catch (...) { - return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Failed, - "artifact provider prepare threw", name_ + ".prepare")); + return eve::Result>::failure(eve::Diagnostic::error( + eve::DiagnosticCode::Failed, "artifact provider prepare threw", name_ + ".prepare")); } }(); if (!prepared.ok()) return eve::Result::failure(prepared.status()); stage_ = std::move(prepared).takeValue(); if (!stage_) return participantFailure(eve::DiagnosticCode::InvariantViolation, - "artifact provider returned a null prepared stage", - name_ + ".prepare"); + "artifact provider returned a null prepared stage", name_ + ".prepare"); phase_ = Phase::Prepared; return eve::Result::success(); } - [[nodiscard]] eve::Result commit( - const eve::transaction::TransactionContext&) override { + [[nodiscard]] eve::Result commit(const eve::transaction::TransactionContext&) override { if (phase_ != Phase::Prepared || !stage_) return participantFailure(eve::DiagnosticCode::PreconditionViolation, - "artifact provider participant is not prepared", name_ + ".commit"); + "artifact provider participant is not prepared", name_ + ".commit"); stage_->commit(); stage_.reset(); phase_ = Phase::Committed; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } - [[nodiscard]] eve::Result rollback( - const eve::transaction::TransactionContext&) override { + [[nodiscard]] eve::Result rollback(const eve::transaction::TransactionContext&) override { if (phase_ != Phase::Prepared) return participantFailure(eve::DiagnosticCode::PreconditionViolation, - "artifact provider participant is not prepared", name_ + ".rollback"); + "artifact provider participant is not prepared", name_ + ".rollback"); stage_->rollback(); stage_.reset(); phase_ = Phase::RolledBack; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } - [[nodiscard]] eve::Result compensate( - const eve::transaction::TransactionContext&) override { + [[nodiscard]] eve::Result compensate(const eve::transaction::TransactionContext&) override { if (phase_ != Phase::Committed) return participantFailure(eve::DiagnosticCode::PreconditionViolation, - "artifact provider participant is not committed", - name_ + ".compensate"); + "artifact provider participant is not committed", name_ + ".compensate"); return participantFailure(eve::DiagnosticCode::Unsupported, - "artifact provider publication has no inverse compensation", - name_ + ".compensate"); + "artifact provider publication has no inverse compensation", name_ + ".compensate"); } private: enum class Phase { Idle, Prepared, Committed, RolledBack }; - std::string name_; - eve::artifact::ProviderContract* provider_ = nullptr; - const eve::artifact::PublicationView& view_; + std::string name_; + eve::artifact::ProviderContract* provider_ = nullptr; + const eve::artifact::PublicationView& view_; std::unique_ptr stage_; - Phase phase_ = Phase::Idle; + Phase phase_ = Phase::Idle; }; /** @brief Adapts the store's PreparedPublication to the common coordinator. */ @@ -231,11 +221,10 @@ public: std::string_view name() const noexcept override { return "procgen.artifact-store"; } - [[nodiscard]] eve::Result prepare( - const eve::transaction::TransactionContext&) override { + [[nodiscard]] eve::Result prepare(const eve::transaction::TransactionContext&) override { if (phase_ != Phase::Idle) return participantFailure(eve::DiagnosticCode::PreconditionViolation, - "artifact store participant is not idle", "procgen.artifact-store.prepare"); + "artifact store participant is not idle", "procgen.artifact-store.prepare"); auto staged = store_->stagePublish(std::move(artifact_)); if (!staged.ok()) return eve::Result::failure(staged.status()); stage_ = std::move(staged).takeValue(); @@ -247,94 +236,83 @@ public: return eve::Result::success(); } - [[nodiscard]] eve::Result commit( - const eve::transaction::TransactionContext&) override { + [[nodiscard]] eve::Result commit(const eve::transaction::TransactionContext&) override { if (phase_ != Phase::Prepared || !stage_) return participantFailure(eve::DiagnosticCode::PreconditionViolation, - "artifact store participant is not prepared", - "procgen.artifact-store.commit"); + "artifact store participant is not prepared", "procgen.artifact-store.commit"); stage_->commit(); stage_.reset(); phase_ = Phase::Committed; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } - [[nodiscard]] eve::Result rollback( - const eve::transaction::TransactionContext&) override { + [[nodiscard]] eve::Result rollback(const eve::transaction::TransactionContext&) override { if (phase_ != Phase::Prepared) return participantFailure(eve::DiagnosticCode::PreconditionViolation, - "artifact store participant is not prepared", - "procgen.artifact-store.rollback"); + "artifact store participant is not prepared", "procgen.artifact-store.rollback"); stage_->rollback(); stage_.reset(); phase_ = Phase::RolledBack; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } - [[nodiscard]] eve::Result compensate( - const eve::transaction::TransactionContext&) override { + [[nodiscard]] eve::Result compensate(const eve::transaction::TransactionContext&) override { if (phase_ != Phase::Committed) return participantFailure(eve::DiagnosticCode::PreconditionViolation, - "artifact store participant is not committed", - "procgen.artifact-store.compensate"); + "artifact store participant is not committed", + "procgen.artifact-store.compensate"); return participantFailure(eve::DiagnosticCode::Unsupported, - "artifact store publication has no inverse compensation", - "procgen.artifact-store.compensate"); + "artifact store publication has no inverse compensation", + "procgen.artifact-store.compensate"); } private: enum class Phase { Idle, Prepared, Committed, RolledBack }; - ArtifactStore* store_ = nullptr; - GeneratedArtifact artifact_; + ArtifactStore* store_ = nullptr; + GeneratedArtifact artifact_; std::unique_ptr stage_; - Phase phase_ = Phase::Idle; + Phase phase_ = Phase::Idle; }; /** @brief Restores one provider only during the coordinator commit phase. */ class ProviderRestoreParticipant final : public eve::transaction::ITransactionParticipant { public: - ProviderRestoreParticipant(std::string name, eve::artifact::ProviderContract& provider, - const eve::Value& state) + ProviderRestoreParticipant(std::string name, eve::artifact::ProviderContract& provider, const eve::Value& state) : name_(std::move(name)), provider_(&provider), state_(&state) {} std::string_view name() const noexcept override { return name_; } - [[nodiscard]] eve::Result prepare( - const eve::transaction::TransactionContext&) override { + [[nodiscard]] eve::Result prepare(const eve::transaction::TransactionContext&) override { if (phase_ != Phase::Idle) return participantFailure(eve::DiagnosticCode::PreconditionViolation, - "artifact restore participant is not idle", name_ + ".prepare"); + "artifact restore participant is not idle", name_ + ".prepare"); phase_ = Phase::Prepared; return eve::Result::success(); } - [[nodiscard]] eve::Result commit( - const eve::transaction::TransactionContext&) override { + [[nodiscard]] eve::Result commit(const eve::transaction::TransactionContext&) override { if (phase_ != Phase::Prepared) return participantFailure(eve::DiagnosticCode::PreconditionViolation, - "artifact restore participant is not prepared", name_ + ".commit"); + "artifact restore participant is not prepared", name_ + ".commit"); auto restored = provider_->restoreState(*state_); if (!restored.ok()) return eve::Result::failure(restored.status()); phase_ = Phase::Committed; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } - [[nodiscard]] eve::Result rollback( - const eve::transaction::TransactionContext&) override { + [[nodiscard]] eve::Result rollback(const eve::transaction::TransactionContext&) override { if (phase_ != Phase::Prepared) return participantFailure(eve::DiagnosticCode::PreconditionViolation, - "artifact restore participant is not prepared", name_ + ".rollback"); + "artifact restore participant is not prepared", name_ + ".rollback"); phase_ = Phase::RolledBack; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } - [[nodiscard]] eve::Result compensate( - const eve::transaction::TransactionContext&) override { + [[nodiscard]] eve::Result compensate(const eve::transaction::TransactionContext&) override { if (phase_ != Phase::Committed) return participantFailure(eve::DiagnosticCode::PreconditionViolation, - "artifact restore participant is not committed", - name_ + ".compensate"); + "artifact restore participant is not committed", name_ + ".compensate"); provider_->clearState(); phase_ = Phase::Compensated; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); @@ -343,58 +321,53 @@ public: private: enum class Phase { Idle, Prepared, Committed, RolledBack, Compensated }; - std::string name_; + std::string name_; eve::artifact::ProviderContract* provider_ = nullptr; - const eve::Value* state_ = nullptr; - Phase phase_ = Phase::Idle; + const eve::Value* state_ = nullptr; + Phase phase_ = Phase::Idle; }; /** @brief Restores the store through the same common coordinator protocol. */ class StoreRestoreParticipant final : public eve::transaction::ITransactionParticipant { public: - StoreRestoreParticipant(ArtifactStore& store, const eve::Value& state) - : store_(&store), state_(&state) {} + StoreRestoreParticipant(ArtifactStore& store, const eve::Value& state) : store_(&store), state_(&state) {} std::string_view name() const noexcept override { return "procgen.artifact-store.restore"; } - [[nodiscard]] eve::Result prepare( - const eve::transaction::TransactionContext&) override { + [[nodiscard]] eve::Result prepare(const eve::transaction::TransactionContext&) override { if (phase_ != Phase::Idle) return participantFailure(eve::DiagnosticCode::PreconditionViolation, - "artifact store restore participant is not idle", - "procgen.artifact-store.restore.prepare"); + "artifact store restore participant is not idle", + "procgen.artifact-store.restore.prepare"); phase_ = Phase::Prepared; return eve::Result::success(); } - [[nodiscard]] eve::Result commit( - const eve::transaction::TransactionContext&) override { + [[nodiscard]] eve::Result commit(const eve::transaction::TransactionContext&) override { if (phase_ != Phase::Prepared) return participantFailure(eve::DiagnosticCode::PreconditionViolation, - "artifact store restore participant is not prepared", - "procgen.artifact-store.restore.commit"); + "artifact store restore participant is not prepared", + "procgen.artifact-store.restore.commit"); auto restored = store_->restoreState(*state_); if (!restored.ok()) return eve::Result::failure(restored.status()); phase_ = Phase::Committed; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } - [[nodiscard]] eve::Result rollback( - const eve::transaction::TransactionContext&) override { + [[nodiscard]] eve::Result rollback(const eve::transaction::TransactionContext&) override { if (phase_ != Phase::Prepared) return participantFailure(eve::DiagnosticCode::PreconditionViolation, - "artifact store restore participant is not prepared", - "procgen.artifact-store.restore.rollback"); + "artifact store restore participant is not prepared", + "procgen.artifact-store.restore.rollback"); phase_ = Phase::RolledBack; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } - [[nodiscard]] eve::Result compensate( - const eve::transaction::TransactionContext&) override { + [[nodiscard]] eve::Result compensate(const eve::transaction::TransactionContext&) override { if (phase_ != Phase::Committed) return participantFailure(eve::DiagnosticCode::PreconditionViolation, - "artifact store restore participant is not committed", - "procgen.artifact-store.restore.compensate"); + "artifact store restore participant is not committed", + "procgen.artifact-store.restore.compensate"); store_->clear(); phase_ = Phase::Compensated; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); @@ -403,16 +376,16 @@ public: private: enum class Phase { Idle, Prepared, Committed, RolledBack, Compensated }; - ArtifactStore* store_ = nullptr; + ArtifactStore* store_ = nullptr; const eve::Value* state_ = nullptr; - Phase phase_ = Phase::Idle; + Phase phase_ = Phase::Idle; }; template eve::Result addProviderParticipant( bool required, const char* name, const eve::artifact::PublicationView& publication, std::vector>& owners, - std::vector& participants) { + std::vector& participants) { if (!required) return eve::Result::success(eve::Status::success(eve::StatusCode::NoOp)); Adapter* adapter = eve::cap::query(); if (!adapter) @@ -435,89 +408,84 @@ const eve::Value* objectMember(const eve::Value::Object& object, const char* nam return found == object.end() ? nullptr : &found->second; } -eve::transaction::TransactionContext publicationContext(const eve::PersistentId& id, - std::string_view operation) { +eve::transaction::TransactionContext publicationContext(const eve::PersistentId& id, std::string_view operation) { if (!id.isNil()) return eve::transaction::TransactionContext(eve::TransactionId::fromUuid(id)); return eve::transaction::TransactionContext(std::string(operation) + ".invalid-identity"); } } // namespace -eve::Result ArtifactPublisher::publish( - GeneratedArtifact artifact, ArtifactPublishOptions options) { +eve::Result ArtifactPublisher::publish(GeneratedArtifact artifact, + ArtifactPublishOptions options) { PublicationBuffer publication; try { publication = makePublicationBuffer(artifact); } catch (const std::exception& error) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, - std::string("failed to materialize artifact publication view: ") + error.what())); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, + std::string("failed to materialize artifact publication view: ") + error.what())); } catch (...) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "failed to materialize artifact publication view")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "failed to materialize artifact publication view")); } std::vector> owners; - std::vector participants; + std::vector participants; owners.reserve(5); participants.reserve(5); - auto scene = addProviderParticipant( - options.scene, "scene", publication.view, owners, participants); + auto scene = + addProviderParticipant(options.scene, "scene", publication.view, owners, participants); if (!scene.ok()) return eve::Result::failure(scene.status()); - auto map = addProviderParticipant( - options.map, "map", publication.view, owners, participants); + auto map = addProviderParticipant(options.map, "map", publication.view, owners, participants); if (!map.ok()) return eve::Result::failure(map.status()); - auto graphics = addProviderParticipant( - options.graphics, "graphics", publication.view, owners, participants); + auto graphics = addProviderParticipant(options.graphics, "graphics", publication.view, + owners, participants); if (!graphics.ok()) return eve::Result::failure(graphics.status()); - auto physics = addProviderParticipant( - options.physics, "physics", publication.view, owners, participants); + auto physics = addProviderParticipant(options.physics, "physics", publication.view, owners, + participants); if (!physics.ok()) return eve::Result::failure(physics.status()); try { owners.emplace_back(std::make_unique(store_, std::move(artifact))); participants.push_back(owners.back().get()); } catch (...) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "failed to allocate artifact store participant")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "failed to allocate artifact store participant")); } - const auto context = publicationContext(publication.view.id, "artifact.publish"); + const auto context = publicationContext(publication.view.id, "artifact.publish"); eve::transaction::Coordinator coordinator; - auto coordinated = coordinator.execute(context, participants); - if (!coordinated.ok()) - return eve::Result::failure(coordinated.status()); + auto coordinated = coordinator.execute(context, participants); + if (!coordinated.ok()) return eve::Result::failure(coordinated.status()); auto transactionReceipt = std::move(coordinated).takeValue(); if (transactionReceipt.state != eve::transaction::CoordinatorState::Committed) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, - "artifact publication coordinator returned a non-committed receipt")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, + "artifact publication coordinator returned a non-committed receipt")); ArtifactPublishReceipt receipt; - receipt.id = ArtifactId::fromUuid(publication.view.id); - receipt.scenePublished = options.scene; + receipt.id = ArtifactId::fromUuid(publication.view.id); + receipt.scenePublished = options.scene; receipt.graphicsPublished = options.graphics; - receipt.physicsPublished = options.physics; - receipt.mapPublished = options.map; + receipt.physicsPublished = options.physics; + receipt.mapPublished = options.map; return eve::Result::success(std::move(receipt)); } -eve::Result ArtifactPublisher::snapshot( - const ArtifactSnapshotContext& context) const { +eve::Result ArtifactPublisher::snapshot(const ArtifactSnapshotContext& context) const { if (!context.hashProvider) return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::Unsupported, "artifact snapshot requires an injected hash provider")); const auto schema = eve::LogicalId::fromParts("eve", "artifact-publication"); if (!schema) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, "artifact snapshot schema id is invalid")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "artifact snapshot schema id is invalid")); auto store = store_.snapshotState(); if (!store.ok()) return eve::Result::failure(store.status()); eve::Value::Object providers; - const auto appendProvider = [&providers](const char* name, - eve::artifact::ProviderContract* provider) - -> eve::Result { + const auto appendProvider = [&providers](const char* name, + eve::artifact::ProviderContract* provider) -> eve::Result { if (!provider) return eve::Result::success(); auto state = provider->snapshotState(); if (!state.ok()) return eve::Result::failure(state.status()); @@ -536,54 +504,49 @@ eve::Result ArtifactPublisher::snapshot( eve::Value::Object payload; payload.emplace("store", std::move(store).takeValue()); payload.emplace("providers", eve::Value(std::move(providers))); - return eve::makeSnapshotEnvelope("eve.artifact.publication", *schema, eve::SchemaVersion(1), - context.instanceId, context.revision, context.tick, - eve::Value(std::move(payload)), context.hashProvider); + return eve::makeSnapshotEnvelope("eve.artifact.publication", *schema, eve::SchemaVersion(1), context.instanceId, + context.revision, context.tick, eve::Value(std::move(payload)), + context.hashProvider); } -eve::Result ArtifactPublisher::restore( - const eve::SnapshotEnvelope& snapshot, const eve::SnapshotHashProvider& hashProvider) { +eve::Result ArtifactPublisher::restore(const eve::SnapshotEnvelope& snapshot, + const eve::SnapshotHashProvider& hashProvider) { auto verified = eve::verifySnapshotEnvelope(snapshot, hashProvider); if (!verified.ok()) return verified; const auto schema = eve::LogicalId::fromParts("eve", "artifact-publication"); if (!schema || snapshot.type != "eve.artifact.publication" || snapshot.schema != *schema || snapshot.schemaVersion.value() != 1) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::UnknownVersion, "unsupported artifact publication snapshot")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::UnknownVersion, "unsupported artifact publication snapshot")); if (store_.size() != 0) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, "artifact snapshot restore requires an empty store")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "artifact snapshot restore requires an empty store")); - const auto* payload = snapshot.payload.getIf(); - const auto* storeState = payload ? objectMember(*payload, "store") : nullptr; + const auto* payload = snapshot.payload.getIf(); + const auto* storeState = payload ? objectMember(*payload, "store") : nullptr; const auto* providerStateValue = payload ? objectMember(*payload, "providers") : nullptr; - const auto* providerStates = providerStateValue - ? providerStateValue->getIf() - : nullptr; + const auto* providerStates = providerStateValue ? providerStateValue->getIf() : nullptr; if (!payload || !storeState || !providerStates) return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::ParseError, "artifact publication snapshot payload is incomplete")); - eve::artifact::ISceneArtifactAdapter* scene = eve::cap::query(); - eve::artifact::IMapArtifactAdapter* map = eve::cap::query(); - eve::artifact::IGraphicsArtifactAdapter* graphics = - eve::cap::query(); - eve::artifact::IPhysicsArtifactAdapter* physics = - eve::cap::query(); - if ((scene && !scene->emptyState()) || (map && !map->emptyState()) || - (graphics && !graphics->emptyState()) || (physics && !physics->emptyState())) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, "artifact snapshot restore requires empty providers")); + eve::artifact::ISceneArtifactAdapter* scene = eve::cap::query(); + eve::artifact::IMapArtifactAdapter* map = eve::cap::query(); + eve::artifact::IGraphicsArtifactAdapter* graphics = eve::cap::query(); + eve::artifact::IPhysicsArtifactAdapter* physics = eve::cap::query(); + if ((scene && !scene->emptyState()) || (map && !map->emptyState()) || (graphics && !graphics->emptyState()) || + (physics && !physics->emptyState())) + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::Conflict, + "artifact snapshot restore requires empty providers")); const auto lookupProvider = [providerStates](const char* name) -> const eve::Value* { const auto found = providerStates->find(name); return found == providerStates->end() ? nullptr : &found->second; }; for (const auto& entry : *providerStates) { - if (entry.first != "scene" && entry.first != "map" && entry.first != "graphics" && - entry.first != "physics") - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "unknown artifact publication provider state")); + if (entry.first != "scene" && entry.first != "map" && entry.first != "graphics" && entry.first != "physics") + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "unknown artifact publication provider state")); } if ((lookupProvider("scene") && !scene) || (lookupProvider("map") && !map) || (lookupProvider("graphics") && !graphics) || (lookupProvider("physics") && !physics)) @@ -591,14 +554,13 @@ eve::Result ArtifactPublisher::restore( eve::DiagnosticCode::Unsupported, "snapshot contains state for an unavailable provider")); std::vector> owners; - std::vector participants; + std::vector participants; owners.reserve(5); participants.reserve(5); try { owners.emplace_back(std::make_unique(store_, *storeState)); participants.push_back(owners.back().get()); - const auto addRestore = [&owners, &participants](const char* name, - eve::artifact::ProviderContract* provider, + const auto addRestore = [&owners, &participants](const char* name, eve::artifact::ProviderContract* provider, const eve::Value* state) { if (!provider || !state) return; owners.emplace_back(std::make_unique(name, *provider, *state)); @@ -609,19 +571,18 @@ eve::Result ArtifactPublisher::restore( addRestore("graphics", graphics, lookupProvider("graphics")); addRestore("physics", physics, lookupProvider("physics")); } catch (...) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "failed to allocate artifact restore participants")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "failed to allocate artifact restore participants")); } - const auto context = publicationContext(snapshot.instanceId, "artifact.restore"); + const auto context = publicationContext(snapshot.instanceId, "artifact.restore"); eve::transaction::Coordinator coordinator; - auto coordinated = coordinator.execute(context, participants); + auto coordinated = coordinator.execute(context, participants); if (!coordinated.ok()) return eve::Result::failure(coordinated.status()); auto transactionReceipt = std::move(coordinated).takeValue(); if (transactionReceipt.state != eve::transaction::CoordinatorState::Committed) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, - "artifact restore coordinator returned a non-committed receipt")); + eve::DiagnosticCode::InvariantViolation, "artifact restore coordinator returned a non-committed receipt")); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } diff --git a/src/modules/procgen/ArtifactPublish.h b/src/modules/procgen/ArtifactPublish.h index 64467580..beee6a61 100644 --- a/src/modules/procgen/ArtifactPublish.h +++ b/src/modules/procgen/ArtifactPublish.h @@ -16,27 +16,27 @@ namespace eve::procgen { // The low-level contracts live in common so scene/map/graphics/physics can // implement them without a reverse include of procgen. These aliases preserve // the established procgen-facing source API. -using PreparedArtifactPublish = eve::artifact::PreparedPublication; -using ISceneArtifactAdapter = eve::artifact::ISceneArtifactAdapter; -using IMapArtifactAdapter = eve::artifact::IMapArtifactAdapter; +using PreparedArtifactPublish = eve::artifact::PreparedPublication; +using ISceneArtifactAdapter = eve::artifact::ISceneArtifactAdapter; +using IMapArtifactAdapter = eve::artifact::IMapArtifactAdapter; using IGraphicsArtifactAdapter = eve::artifact::IGraphicsArtifactAdapter; -using IPhysicsArtifactAdapter = eve::artifact::IPhysicsArtifactAdapter; -using ArtifactPublishOptions = eve::artifact::PublishOptions; +using IPhysicsArtifactAdapter = eve::artifact::IPhysicsArtifactAdapter; +using ArtifactPublishOptions = eve::artifact::PublishOptions; /** @brief Compatibility receipt retaining procgen ArtifactId at this boundary. */ struct ArtifactPublishReceipt { ArtifactId id; - bool scenePublished = false; - bool graphicsPublished = false; - bool physicsPublished = false; - bool mapPublished = false; + bool scenePublished = false; + bool graphicsPublished = false; + bool physicsPublished = false; + bool mapPublished = false; }; /** @brief Snapshot context supplied by the caller; time and hashing are injected. */ struct ArtifactSnapshotContext { - eve::PersistentId instanceId; - eve::Revision revision; - eve::SimulationTick tick; + eve::PersistentId instanceId; + eve::Revision revision; + eve::SimulationTick tick; eve::SnapshotHashProvider hashProvider; }; @@ -58,16 +58,15 @@ public: * @remarks Any Result failure rolls back every prepared adapter and leaves * the ArtifactStore unchanged. */ - [[nodiscard]] eve::Result publish( - GeneratedArtifact artifact, ArtifactPublishOptions options); + [[nodiscard]] eve::Result publish(GeneratedArtifact artifact, + ArtifactPublishOptions options); /** * @brief Capture store and registered provider state in a sealed snapshot. * @param context Stable instance/revision/tick and injected content hash. * @return Versioned snapshot, or Unsupported when a requested provider is absent. */ - [[nodiscard]] eve::Result snapshot( - const ArtifactSnapshotContext& context) const; + [[nodiscard]] eve::Result snapshot(const ArtifactSnapshotContext& context) const; /** * @brief Restore store and provider state transactionally from a sealed snapshot. @@ -75,8 +74,8 @@ public: * @param hashProvider Hash provider used to verify the envelope. * @return Applied or a structured validation/provider error. */ - [[nodiscard]] eve::Result restore(const eve::SnapshotEnvelope& snapshot, - const eve::SnapshotHashProvider& hashProvider); + [[nodiscard]] eve::Result restore(const eve::SnapshotEnvelope& snapshot, + const eve::SnapshotHashProvider& hashProvider); private: ArtifactStore& store_; diff --git a/src/modules/procgen/GeneratedArtifact.cpp b/src/modules/procgen/GeneratedArtifact.cpp index 0ce6f59f..61763fe1 100644 --- a/src/modules/procgen/GeneratedArtifact.cpp +++ b/src/modules/procgen/GeneratedArtifact.cpp @@ -6,8 +6,8 @@ #include #include -#include #include +#include #include #include #include @@ -20,8 +20,7 @@ namespace eve::procgen { namespace { -eve::Result rejectedArtifact(eve::DiagnosticCode code, - std::string message) { +eve::Result rejectedArtifact(eve::DiagnosticCode code, std::string message) { return eve::Result::failure( eve::Diagnostic::error(code, std::move(message), "generatedArtifact")); } @@ -47,33 +46,30 @@ bool hasControl(std::string_view text) noexcept { return false; } -bool typeMatches(ArtifactType type, const ArtifactLeafPayload& payload) noexcept { +bool typeMatches(ArtifactType type, const ArtifactLeafPayload &payload) noexcept { switch (type) { - case ArtifactType::Grid: return std::holds_alternative(payload); - case ArtifactType::PointSet: return std::holds_alternative(payload); - case ArtifactType::MeshData: return std::holds_alternative(payload); - case ArtifactType::ImageData: return std::holds_alternative(payload); - case ArtifactType::Collider: return std::holds_alternative(payload); - case ArtifactType::Composite: return false; + case ArtifactType::Grid: return std::holds_alternative(payload); + case ArtifactType::PointSet: return std::holds_alternative(payload); + case ArtifactType::MeshData: return std::holds_alternative(payload); + case ArtifactType::ImageData: return std::holds_alternative(payload); + case ArtifactType::Collider: return std::holds_alternative(payload); + case ArtifactType::Composite: return false; } return false; } -bool typeMatches(ArtifactType type, const GeneratedArtifact::Payload& payload) noexcept { +bool typeMatches(ArtifactType type, const GeneratedArtifact::Payload &payload) noexcept { return type == artifactType(payload); } -bool finiteBounds(const Bounds& bounds) noexcept { - return bounds.valid && std::isfinite(bounds.minX) && std::isfinite(bounds.minY) && - std::isfinite(bounds.minZ) && std::isfinite(bounds.maxX) && - std::isfinite(bounds.maxY) && std::isfinite(bounds.maxZ) && - bounds.minX <= bounds.maxX && bounds.minY <= bounds.maxY && - bounds.minZ <= bounds.maxZ; +bool finiteBounds(const Bounds &bounds) noexcept { + return bounds.valid && std::isfinite(bounds.minX) && std::isfinite(bounds.minY) && std::isfinite(bounds.minZ) && + std::isfinite(bounds.maxX) && std::isfinite(bounds.maxY) && std::isfinite(bounds.maxZ) && + bounds.minX <= bounds.maxX && bounds.minY <= bounds.maxY && bounds.minZ <= bounds.maxZ; } -bool validMesh(const MeshData& mesh) noexcept { - if (mesh.empty() || mesh.positions().size() % 3u != 0u || mesh.indices().size() % 3u != 0u) - return false; +bool validMesh(const MeshData &mesh) noexcept { + if (mesh.empty() || mesh.positions().size() % 3u != 0u || mesh.indices().size() % 3u != 0u) return false; const std::size_t vertices = mesh.positions().size() / 3u; if ((!mesh.normals().empty() && mesh.normals().size() != mesh.positions().size()) || (!mesh.uvs().empty() && mesh.uvs().size() != vertices * 2u)) @@ -82,95 +78,93 @@ bool validMesh(const MeshData& mesh) noexcept { [](float value) { return std::isfinite(value); }) && std::all_of(mesh.normals().begin(), mesh.normals().end(), [](float value) { return std::isfinite(value); }) && - std::all_of(mesh.uvs().begin(), mesh.uvs().end(), - [](float value) { return std::isfinite(value); }) && + std::all_of(mesh.uvs().begin(), mesh.uvs().end(), [](float value) { return std::isfinite(value); }) && std::all_of(mesh.indices().begin(), mesh.indices().end(), [vertices](std::uint32_t index) { return index < vertices; }); } -bool validLeaf(ArtifactType type, const ArtifactLeafPayload& payload) noexcept { +bool validLeaf(ArtifactType type, const ArtifactLeafPayload &payload) noexcept { if (!typeMatches(type, payload)) return false; - return std::visit([](const auto& value) { - using T = std::decay_t; - if constexpr (std::is_same_v) - return value.getWidth() > 0 && value.getHeight() > 0; - else if constexpr (std::is_same_v) - return !value.points().empty() && - std::all_of(value.points().begin(), value.points().end(), [](const auto& point) { - return std::isfinite(point.x) && std::isfinite(point.y) && - std::isfinite(point.z); - }); - else if constexpr (std::is_same_v) - return validMesh(value); - else - return value.isValid(); - }, payload); + return std::visit( + [](const auto &value) { + using T = std::decay_t; + if constexpr (std::is_same_v) + return value.getWidth() > 0 && value.getHeight() > 0; + else if constexpr (std::is_same_v) + return !value.points().empty() && + std::all_of(value.points().begin(), value.points().end(), [](const auto &point) { + return std::isfinite(point.x) && std::isfinite(point.y) && std::isfinite(point.z); + }); + else if constexpr (std::is_same_v) + return validMesh(value); + else + return value.isValid(); + }, + payload); } -bool validDependencies(ArtifactId self, const std::vector& dependencies) { +bool validDependencies(ArtifactId self, const std::vector &dependencies) { std::unordered_set seen; for (ArtifactId dependency : dependencies) { - if (dependency.isNil() || dependency == self || !seen.emplace(dependency).second) - return false; + if (dependency.isNil() || dependency == self || !seen.emplace(dependency).second) return false; } return true; } -bool validPart(const ArtifactPart& part) { - return !part.role.empty() && !part.id.isNil() && !part.schemaVersion.isZero() && - !part.buildKey.empty() && finiteBounds(part.bounds) && - validDependencies(part.id, part.dependencies) && validLeaf(part.type, part.payload); +bool validPart(const ArtifactPart &part) { + return !part.role.empty() && !part.id.isNil() && !part.schemaVersion.isZero() && !part.buildKey.empty() && + finiteBounds(part.bounds) && validDependencies(part.id, part.dependencies) && + validLeaf(part.type, part.payload); } -bool validArtifact(const GeneratedArtifact& artifact) { +bool validArtifact(const GeneratedArtifact &artifact) { if (artifact.id.isNil() || artifact.schemaVersion.isZero() || artifact.buildKey.empty() || !finiteBounds(artifact.bounds) || !validDependencies(artifact.id, artifact.dependencies) || !typeMatches(artifact.type, artifact.payload)) return false; if (artifact.type != ArtifactType::Composite) { - return std::visit([&](const auto& value) { - using T = std::decay_t; - if constexpr (std::is_same_v) return false; - else return validLeaf(artifact.type, ArtifactLeafPayload(value)); - }, artifact.payload); + return std::visit( + [&](const auto &value) { + using T = std::decay_t; + if constexpr (std::is_same_v) + return false; + else + return validLeaf(artifact.type, ArtifactLeafPayload(value)); + }, + artifact.payload); } - const auto& composite = std::get(artifact.payload); + const auto &composite = std::get(artifact.payload); if (composite.children.empty()) return false; - std::unordered_set identities; + std::unordered_set identities; std::unordered_set roles; - for (const ArtifactPart& child : composite.children) { - if (child.id == artifact.id || !validPart(child) || - !identities.emplace(child.id).second || !roles.emplace(child.role).second) + for (const ArtifactPart &child : composite.children) { + if (child.id == artifact.id || !validPart(child) || !identities.emplace(child.id).second || + !roles.emplace(child.role).second) return false; } return true; } -eve::Result validateBatchAgainstStore(const ArtifactStore& store, - const std::vector& artifacts) { +eve::Result validateBatchAgainstStore(const ArtifactStore &store, + const std::vector &artifacts) { if (artifacts.empty()) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "artifact publish batch must not be empty", - "artifacts")); + eve::DiagnosticCode::InvalidArgument, "artifact publish batch must not be empty", "artifacts")); std::unordered_map batch; batch.reserve(artifacts.size() * 5u); - for (const GeneratedArtifact& artifact : artifacts) { + for (const GeneratedArtifact &artifact : artifacts) { if (!validArtifact(artifact)) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "artifact structure or typed payload is invalid", "artifact")); - if (store.typeOf(artifact.id).has_value() || - !batch.emplace(artifact.id, artifact.type).second) + eve::DiagnosticCode::InvalidArgument, "artifact structure or typed payload is invalid", "artifact")); + if (store.typeOf(artifact.id).has_value() || !batch.emplace(artifact.id, artifact.type).second) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, "artifact identity is already published", - "artifact.id")); + eve::DiagnosticCode::Conflict, "artifact identity is already published", "artifact.id")); if (artifact.type == ArtifactType::Composite) { - for (const ArtifactPart& part : std::get(artifact.payload).children) { + for (const ArtifactPart &part : std::get(artifact.payload).children) { if (store.typeOf(part.id).has_value() || !batch.emplace(part.id, part.type).second) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, "composite part identity conflicts", - "artifact.children.id")); + eve::DiagnosticCode::Conflict, "composite part identity conflicts", "artifact.children.id")); } } } @@ -180,30 +174,27 @@ eve::Result validateBatchAgainstStore(const ArtifactStore& store, if (inBatch != batch.end()) return inBatch->second; return store.typeOf(id); }; - const auto checkDependencies = [&typeFor](ArtifactType type, - const std::vector& dependencies) - -> std::optional { + const auto checkDependencies = + [&typeFor](ArtifactType type, const std::vector &dependencies) -> std::optional { bool meshDependency = false; for (ArtifactId dependency : dependencies) { const auto found = typeFor(dependency); if (!found) - return eve::Diagnostic::error( - eve::DiagnosticCode::NotFound, - "artifact dependency is not published or in batch", - "artifact.dependencies"); + return eve::Diagnostic::error(eve::DiagnosticCode::NotFound, + "artifact dependency is not published or in batch", + "artifact.dependencies"); meshDependency = meshDependency || *found == ArtifactType::MeshData; } if (type == ArtifactType::Collider && !meshDependency) - return eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, - "collider must depend on a mesh artifact", "artifact.dependencies"); + return eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, + "collider must depend on a mesh artifact", "artifact.dependencies"); return std::nullopt; }; - for (const GeneratedArtifact& artifact : artifacts) { + for (const GeneratedArtifact &artifact : artifacts) { if (auto error = checkDependencies(artifact.type, artifact.dependencies)) return eve::Result::failure(std::move(*error)); if (artifact.type == ArtifactType::Composite) { - for (const ArtifactPart& part : std::get(artifact.payload).children) { + for (const ArtifactPart &part : std::get(artifact.payload).children) { if (auto error = checkDependencies(part.type, part.dependencies)) return eve::Result::failure(std::move(*error)); } @@ -257,9 +248,7 @@ eve::Value encodeBounds(const Bounds &bounds) { return eve::Value(std::move(result)); } -eve::Value encodeMetadata(const eve::Value::Object &metadata) { - return eve::Value(metadata); -} +eve::Value encodeMetadata(const eve::Value::Object &metadata) { return eve::Value(metadata); } eve::Value encodeDependencies(const std::vector &dependencies) { eve::Value::Array result; @@ -306,7 +295,7 @@ eve::Value encodeGrid(const Grid2D &grid) { eve::Value encodePointSet(const PointSet &points) { eve::Value::Object result; - eve::Value::Array encodedPoints; + eve::Value::Array encodedPoints; encodedPoints.reserve(points.points().size()); for (const ProcgenPoint &point : points.points()) { eve::Value::Object encoded; @@ -370,18 +359,25 @@ eve::Value encodeCollider(const Collider &collider) { eve::Value encodeLeafPayload(ArtifactType type, const ArtifactLeafPayload &payload) { eve::Value::Object result; result.emplace("kind", eve::Value(artifactTypeName(type))); - result.emplace("data", std::visit([](const auto &value) -> eve::Value { - using T = std::decay_t; - if constexpr (std::is_same_v) return encodeGrid(value); - else if constexpr (std::is_same_v) return encodePointSet(value); - else if constexpr (std::is_same_v) return encodeMesh(value); - else if constexpr (std::is_same_v) return encodeImage(value); - else if constexpr (std::is_same_v) return encodeCollider(value); - else { - static_assert(std::is_same_v, "unhandled artifact leaf payload"); - return eve::Value{}; - } - }, payload)); + result.emplace("data", std::visit( + [](const auto &value) -> eve::Value { + using T = std::decay_t; + if constexpr (std::is_same_v) + return encodeGrid(value); + else if constexpr (std::is_same_v) + return encodePointSet(value); + else if constexpr (std::is_same_v) + return encodeMesh(value); + else if constexpr (std::is_same_v) + return encodeImage(value); + else if constexpr (std::is_same_v) + return encodeCollider(value); + else { + static_assert(std::is_same_v, "unhandled artifact leaf payload"); + return eve::Value{}; + } + }, + payload)); return eve::Value(std::move(result)); } @@ -410,7 +406,7 @@ eve::Value encodeArtifact(const GeneratedArtifact &artifact) { result.emplace("metadata", encodeMetadata(artifact.metadata)); if (artifact.type == ArtifactType::Composite) { eve::Value::Array children; - const auto &composite = std::get(artifact.payload); + const auto &composite = std::get(artifact.payload); children.reserve(composite.children.size()); for (const ArtifactPart &part : composite.children) children.emplace_back(encodePart(part)); eve::Value::Object payload; @@ -418,27 +414,30 @@ eve::Value encodeArtifact(const GeneratedArtifact &artifact) { payload.emplace("children", eve::Value(std::move(children))); result.emplace("payload", eve::Value(std::move(payload))); } else { - result.emplace("payload", std::visit([&artifact](const auto &value) -> eve::Value { - using T = std::decay_t; - if constexpr (std::is_same_v) return eve::Value{}; - else return encodeLeafPayload(artifact.type, ArtifactLeafPayload(value)); - }, artifact.payload)); + result.emplace("payload", std::visit( + [&artifact](const auto &value) -> eve::Value { + using T = std::decay_t; + if constexpr (std::is_same_v) + return eve::Value{}; + else + return encodeLeafPayload(artifact.type, ArtifactLeafPayload(value)); + }, + artifact.payload)); } return eve::Value(std::move(result)); } -bool readStringValue(const eve::Value::Object &object, const char *name, std::string &output, - bool allowEmpty = false) { +bool readStringValue(const eve::Value::Object &object, const char *name, std::string &output, bool allowEmpty = false) { const eve::Value *value = stateMember(object, name); - const auto *text = value ? value->getIf() : nullptr; + const auto *text = value ? value->getIf() : nullptr; if (!text || (!allowEmpty && text->empty())) return false; output = *text; return true; } bool readIntValue(const eve::Value::Object &object, const char *name, std::int64_t &output) { - const eve::Value *value = stateMember(object, name); - const auto *integer = value ? value->getIf() : nullptr; + const eve::Value *value = stateMember(object, name); + const auto *integer = value ? value->getIf() : nullptr; if (!integer) return false; output = *integer; return true; @@ -462,8 +461,8 @@ bool readFloatValue(const eve::Value::Object &object, const char *name, float &o } bool readBoolValue(const eve::Value::Object &object, const char *name, bool &output) { - const eve::Value *value = stateMember(object, name); - const auto *boolean = value ? value->getIf() : nullptr; + const eve::Value *value = stateMember(object, name); + const auto *boolean = value ? value->getIf() : nullptr; if (!boolean) return false; output = *boolean; return true; @@ -489,17 +488,14 @@ bool decodeNumericArray(const eve::Value *encoded, std::vector &output) { } else { return false; } - } else if constexpr (std::is_same_v || - std::is_same_v) { + } else if constexpr (std::is_same_v || std::is_same_v) { const auto *integer = value.getIf(); - if (!integer || *integer < 0 || - static_cast(*integer) > std::numeric_limits::max()) + if (!integer || *integer < 0 || static_cast(*integer) > std::numeric_limits::max()) return false; output.push_back(static_cast(*integer)); } else { const auto *integer = value.getIf(); - if (!integer || *integer < std::numeric_limits::min() || - *integer > std::numeric_limits::max()) + if (!integer || *integer < std::numeric_limits::min() || *integer > std::numeric_limits::max()) return false; output.push_back(static_cast(*integer)); } @@ -509,16 +505,15 @@ bool decodeNumericArray(const eve::Value *encoded, std::vector &output) { bool decodeBounds(const eve::Value *encoded, Bounds &bounds) { const auto *object = encoded ? encoded->getIf() : nullptr; - if (!object || !readFloatValue(*object, "minX", bounds.minX) || - !readFloatValue(*object, "minY", bounds.minY) || !readFloatValue(*object, "minZ", bounds.minZ) || - !readFloatValue(*object, "maxX", bounds.maxX) || !readFloatValue(*object, "maxY", bounds.maxY) || - !readFloatValue(*object, "maxZ", bounds.maxZ) || !readBoolValue(*object, "valid", bounds.valid)) + if (!object || !readFloatValue(*object, "minX", bounds.minX) || !readFloatValue(*object, "minY", bounds.minY) || + !readFloatValue(*object, "minZ", bounds.minZ) || !readFloatValue(*object, "maxX", bounds.maxX) || + !readFloatValue(*object, "maxY", bounds.maxY) || !readFloatValue(*object, "maxZ", bounds.maxZ) || + !readBoolValue(*object, "valid", bounds.valid)) return false; return finiteBounds(bounds); } -bool decodeStringMap(const eve::Value *encoded, - std::unordered_map &output) { +bool decodeStringMap(const eve::Value *encoded, std::unordered_map &output) { const auto *object = encoded ? encoded->getIf() : nullptr; if (!object) return false; output.clear(); @@ -564,21 +559,19 @@ bool decodeDependencies(const eve::Value *encoded, std::vector &outp output.clear(); output.reserve(array->size()); for (const eve::Value &value : *array) { - const auto *text = value.getIf(); - const auto parsed = text ? ArtifactId::parse(*text) : std::nullopt; + const auto *text = value.getIf(); + const auto parsed = text ? ArtifactId::parse(*text) : std::nullopt; if (!parsed || parsed->isNil()) return false; output.push_back(*parsed); } return true; } -std::optional decodeArtifactType(const eve::Value::Object &object, - const char *name = "type") { +std::optional decodeArtifactType(const eve::Value::Object &object, const char *name = "type") { std::string text; if (!readStringValue(object, name, text)) return std::nullopt; - for (const ArtifactType type : {ArtifactType::Grid, ArtifactType::PointSet, - ArtifactType::MeshData, ArtifactType::ImageData, - ArtifactType::Collider, ArtifactType::Composite}) + for (const ArtifactType type : {ArtifactType::Grid, ArtifactType::PointSet, ArtifactType::MeshData, + ArtifactType::ImageData, ArtifactType::Collider, ArtifactType::Composite}) if (text == artifactTypeName(type)) return type; return std::nullopt; } @@ -586,7 +579,7 @@ std::optional decodeArtifactType(const eve::Value::Object &object, bool decodeSchemaVersion(const eve::Value::Object &object, eve::SchemaVersion &version) { std::string text; if (!readStringValue(object, "schemaVersion", text)) return false; - std::uint64_t value = 0; + std::uint64_t value = 0; const auto [end, error] = std::from_chars(text.data(), text.data() + text.size(), value); if (error != std::errc{} || end != text.data() + text.size() || value == 0) return false; version = eve::SchemaVersion(value); @@ -594,23 +587,21 @@ bool decodeSchemaVersion(const eve::Value::Object &object, eve::SchemaVersion &v } bool decodePayload(ArtifactType type, const eve::Value *encoded, ArtifactLeafPayload &output) { - const auto *wrapper = encoded ? encoded->getIf() : nullptr; - std::string kind; + const auto *wrapper = encoded ? encoded->getIf() : nullptr; + std::string kind; const eve::Value *data = wrapper ? stateMember(*wrapper, "data") : nullptr; - if (!wrapper || !readStringValue(*wrapper, "kind", kind) || kind != artifactTypeName(type) || !data) - return false; + if (!wrapper || !readStringValue(*wrapper, "kind", kind) || kind != artifactTypeName(type) || !data) return false; const auto *object = data->getIf(); if (!object) return false; if (type == ArtifactType::Grid) { - std::int64_t width = 0, height = 0; - std::vector cells; - std::vector detail; + std::int64_t width = 0, height = 0; + std::vector cells; + std::vector detail; std::unordered_map metadata; - const auto *objectsValue = stateMember(*object, "objects"); + const auto *objectsValue = stateMember(*object, "objects"); const auto *objects = objectsValue ? objectsValue->getIf() : nullptr; - if (!readIntValue(*object, "width", width) || !readIntValue(*object, "height", height) || - width <= 0 || height <= 0 || width > std::numeric_limits::max() || - height > std::numeric_limits::max() || + if (!readIntValue(*object, "width", width) || !readIntValue(*object, "height", height) || width <= 0 || + height <= 0 || width > std::numeric_limits::max() || height > std::numeric_limits::max() || !decodeNumericArray(stateMember(*object, "cells"), cells) || !decodeNumericArray(stateMember(*object, "detail"), detail) || !objects || !decodeStringMap(stateMember(*object, "metadata"), metadata)) @@ -619,20 +610,19 @@ bool decodePayload(ArtifactType type, const eve::Value *encoded, ArtifactLeafPay if (expected != cells.size() || expected != detail.size()) return false; Grid2D grid; grid.resize(static_cast(width), static_cast(height)); - grid.cells() = std::move(cells); + grid.cells() = std::move(cells); grid.detail() = std::move(detail); for (const auto &[key, value] : metadata) grid.setMeta(key, value); for (const eve::Value &encodedObject : *objects) { - const auto *item = encodedObject.getIf(); - std::string name, objectType; - float x = 0.f, y = 0.f, objectWidth = 0.f, objectHeight = 0.f; + const auto *item = encodedObject.getIf(); + std::string name, objectType; + float x = 0.f, y = 0.f, objectWidth = 0.f, objectHeight = 0.f; std::int64_t gid = 0; if (!item || !readStringValue(*item, "name", name, true) || - !readStringValue(*item, "type", objectType, true) || - !readFloatValue(*item, "x", x) || !readFloatValue(*item, "y", y) || - !readFloatValue(*item, "width", objectWidth) || - !readFloatValue(*item, "height", objectHeight) || !readIntValue(*item, "gid", gid) || - gid < 0 || gid > std::numeric_limits::max()) + !readStringValue(*item, "type", objectType, true) || !readFloatValue(*item, "x", x) || + !readFloatValue(*item, "y", y) || !readFloatValue(*item, "width", objectWidth) || + !readFloatValue(*item, "height", objectHeight) || !readIntValue(*item, "gid", gid) || gid < 0 || + gid > std::numeric_limits::max()) return false; grid.addObject(name, objectType, x, y, objectWidth, objectHeight, static_cast(gid)); } @@ -641,26 +631,20 @@ bool decodePayload(ArtifactType type, const eve::Value *encoded, ArtifactLeafPay } if (type == ArtifactType::PointSet) { const auto *encodedPointsValue = stateMember(*object, "points"); - const auto *encodedPoints = encodedPointsValue - ? encodedPointsValue->getIf() - : nullptr; + const auto *encodedPoints = encodedPointsValue ? encodedPointsValue->getIf() : nullptr; if (!encodedPoints) return false; PointSet points; for (const eve::Value &encodedPoint : *encodedPoints) { - const auto *item = encodedPoint.getIf(); + const auto *item = encodedPoint.getIf(); ProcgenPoint point; std::int64_t seed = 0; if (!item || !readFloatValue(*item, "x", point.x) || !readFloatValue(*item, "y", point.y) || - !readFloatValue(*item, "z", point.z) || - !readFloatValue(*item, "normalX", point.normalX) || - !readFloatValue(*item, "normalY", point.normalY) || - !readFloatValue(*item, "normalZ", point.normalZ) || - !readFloatValue(*item, "yaw", point.yaw) || - !readFloatValue(*item, "scaleX", point.scaleX) || - !readFloatValue(*item, "scaleY", point.scaleY) || - !readFloatValue(*item, "scaleZ", point.scaleZ) || - !readFloatValue(*item, "density", point.density) || !readIntValue(*item, "seed", seed) || - seed < 0 || seed > std::numeric_limits::max() || + !readFloatValue(*item, "z", point.z) || !readFloatValue(*item, "normalX", point.normalX) || + !readFloatValue(*item, "normalY", point.normalY) || !readFloatValue(*item, "normalZ", point.normalZ) || + !readFloatValue(*item, "yaw", point.yaw) || !readFloatValue(*item, "scaleX", point.scaleX) || + !readFloatValue(*item, "scaleY", point.scaleY) || !readFloatValue(*item, "scaleZ", point.scaleZ) || + !readFloatValue(*item, "density", point.density) || !readIntValue(*item, "seed", seed) || seed < 0 || + seed > std::numeric_limits::max() || !decodeFloatMap(stateMember(*item, "floatAttributes"), point.floatAttributes) || !decodeStringMap(stateMember(*item, "stringAttributes"), point.stringAttributes)) return false; @@ -671,14 +655,12 @@ bool decodePayload(ArtifactType type, const eve::Value *encoded, ArtifactLeafPay return true; } if (type == ArtifactType::MeshData) { - MeshData mesh; + MeshData mesh; std::vector names; - std::vector assignments; - std::int64_t activeGroup = -1; - const auto *encodedNamesValue = stateMember(*object, "groupNames"); - const auto *encodedNames = encodedNamesValue - ? encodedNamesValue->getIf() - : nullptr; + std::vector assignments; + std::int64_t activeGroup = -1; + const auto *encodedNamesValue = stateMember(*object, "groupNames"); + const auto *encodedNames = encodedNamesValue ? encodedNamesValue->getIf() : nullptr; const auto *encodedMetadata = stateMember(*object, "metadata"); std::unordered_map metadata; if (!decodeNumericArray(stateMember(*object, "positions"), mesh.positions()) || @@ -686,25 +668,23 @@ bool decodePayload(ArtifactType type, const eve::Value *encoded, ArtifactLeafPay !decodeNumericArray(stateMember(*object, "uvs"), mesh.uvs()) || !decodeNumericArray(stateMember(*object, "indices"), mesh.indices()) || !encodedNames || !decodeNumericArray(stateMember(*object, "triangleGroups"), assignments) || - !readIntValue(*object, "activeGroup", activeGroup) || - activeGroup < std::numeric_limits::min() || activeGroup > std::numeric_limits::max() || - !decodeStringMap(encodedMetadata, metadata)) + !readIntValue(*object, "activeGroup", activeGroup) || activeGroup < std::numeric_limits::min() || + activeGroup > std::numeric_limits::max() || !decodeStringMap(encodedMetadata, metadata)) return false; for (const eve::Value &encodedName : *encodedNames) { const auto *name = encodedName.getIf(); if (!name) return false; names.push_back(*name); } - auto restoredGroups = mesh.restoreGroupData( - std::move(names), std::move(assignments), static_cast(activeGroup)); - if (!restoredGroups.ok()) - return false; + auto restoredGroups = + mesh.restoreGroupData(std::move(names), std::move(assignments), static_cast(activeGroup)); + if (!restoredGroups.ok()) return false; for (const auto &[key, value] : metadata) mesh.setMeta(key, value); output = std::move(mesh); return true; } if (type == ArtifactType::ImageData) { - ImageData image; + ImageData image; std::int64_t width = 0, height = 0, channels = 0; if (!readIntValue(*object, "width", width) || !readIntValue(*object, "height", height) || !readIntValue(*object, "channels", channels) || width <= 0 || height <= 0 || channels <= 0 || @@ -712,8 +692,8 @@ bool decodePayload(ArtifactType type, const eve::Value *encoded, ArtifactLeafPay channels > std::numeric_limits::max() || !readStringValue(*object, "format", image.format) || !decodeNumericArray(stateMember(*object, "pixels"), image.pixels)) return false; - image.width = static_cast(width); - image.height = static_cast(height); + image.width = static_cast(width); + image.height = static_cast(height); image.channels = static_cast(channels); if (!image.isValid()) return false; output = std::move(image); @@ -733,16 +713,15 @@ bool decodePayload(ArtifactType type, const eve::Value *encoded, ArtifactLeafPay } bool decodePart(const eve::Value &encoded, ArtifactPart &output) { - const auto *object = encoded.getIf(); - std::string role, idText, buildKeyText; - eve::SchemaVersion schemaVersion; - Bounds bounds; + const auto *object = encoded.getIf(); + std::string role, idText, buildKeyText; + eve::SchemaVersion schemaVersion; + Bounds bounds; std::vector dependencies; - eve::Value::Object metadata; - const auto type = object ? decodeArtifactType(*object) : std::nullopt; - const auto parsedId = object && readStringValue(*object, "artifactId", idText) - ? ArtifactId::parse(idText) - : std::nullopt; + eve::Value::Object metadata; + const auto type = object ? decodeArtifactType(*object) : std::nullopt; + const auto parsedId = + object && readStringValue(*object, "artifactId", idText) ? ArtifactId::parse(idText) : std::nullopt; if (!object || !readStringValue(*object, "role", role) || !parsedId || parsedId->isNil() || !type || *type == ArtifactType::Composite || !decodeSchemaVersion(*object, schemaVersion) || !readStringValue(*object, "buildKey", buildKeyText) || !decodeBounds(stateMember(*object, "bounds"), bounds) || @@ -761,16 +740,15 @@ bool decodePart(const eve::Value &encoded, ArtifactPart &output) { } bool decodeArtifact(const eve::Value &encoded, GeneratedArtifact &output) { - const auto *object = encoded.getIf(); - std::string idText, buildKeyText; - eve::SchemaVersion schemaVersion; - Bounds bounds; + const auto *object = encoded.getIf(); + std::string idText, buildKeyText; + eve::SchemaVersion schemaVersion; + Bounds bounds; std::vector dependencies; - eve::Value::Object metadata; - const auto type = object ? decodeArtifactType(*object) : std::nullopt; - const auto parsedId = object && readStringValue(*object, "artifactId", idText) - ? ArtifactId::parse(idText) - : std::nullopt; + eve::Value::Object metadata; + const auto type = object ? decodeArtifactType(*object) : std::nullopt; + const auto parsedId = + object && readStringValue(*object, "artifactId", idText) ? ArtifactId::parse(idText) : std::nullopt; if (!object || !parsedId || parsedId->isNil() || !type || !decodeSchemaVersion(*object, schemaVersion) || !readStringValue(*object, "buildKey", buildKeyText) || !decodeBounds(stateMember(*object, "bounds"), bounds) || !decodeDependencies(stateMember(*object, "dependencies"), dependencies) || @@ -781,15 +759,12 @@ bool decodeArtifact(const eve::Value &encoded, GeneratedArtifact &output) { GeneratedArtifact::Payload payload; if (*type == ArtifactType::Composite) { const auto *payloadObjectValue = stateMember(*object, "payload"); - const auto *payloadObject = payloadObjectValue - ? payloadObjectValue->getIf() - : nullptr; + const auto *payloadObject = payloadObjectValue ? payloadObjectValue->getIf() : nullptr; std::string kind; const auto *encodedChildren = payloadObject ? stateMember(*payloadObject, "children") : nullptr; - const auto *children = encodedChildren ? encodedChildren->getIf() : nullptr; + const auto *children = encodedChildren ? encodedChildren->getIf() : nullptr; CompositeArtifact composite; - if (!payloadObject || !readStringValue(*payloadObject, "kind", kind) || kind != "composite" || - !children) + if (!payloadObject || !readStringValue(*payloadObject, "kind", kind) || kind != "composite" || !children) return false; composite.children.reserve(children->size()); for (const eve::Value &encodedPart : *children) { @@ -801,18 +776,20 @@ bool decodeArtifact(const eve::Value &encoded, GeneratedArtifact &output) { } else { ArtifactLeafPayload leaf; if (!decodePayload(*type, stateMember(*object, "payload"), leaf)) return false; - payload = std::visit([](auto &&value) -> GeneratedArtifact::Payload { - return GeneratedArtifact::Payload(std::forward(value)); - }, std::move(leaf)); + payload = std::visit( + [](auto &&value) -> GeneratedArtifact::Payload { + return GeneratedArtifact::Payload(std::forward(value)); + }, + std::move(leaf)); } - auto result = makeArtifact(*parsedId, *type, schemaVersion, *key, bounds, - std::move(dependencies), std::move(metadata), std::move(payload)); + auto result = makeArtifact(*parsedId, *type, schemaVersion, *key, bounds, std::move(dependencies), + std::move(metadata), std::move(payload)); if (!result.ok()) return false; output = std::move(result).takeValue(); return true; } -Bounds boundsForMesh(const MeshBuild& mesh) noexcept { +Bounds boundsForMesh(const MeshBuild &mesh) noexcept { Bounds bounds; for (int i = 0; i < mesh.getVertexCount(); ++i) { bounds.include(mesh.getPositionX(i), mesh.getPositionY(i), mesh.getPositionZ(i)); @@ -820,7 +797,7 @@ Bounds boundsForMesh(const MeshBuild& mesh) noexcept { return bounds; } -Bounds boundsForGrid(const Grid2D& grid) noexcept { +Bounds boundsForGrid(const Grid2D &grid) noexcept { Bounds bounds; if (grid.getWidth() <= 0 || grid.getHeight() <= 0) return bounds; bounds.include(0.f, 0.f, 0.f); @@ -828,24 +805,24 @@ Bounds boundsForGrid(const Grid2D& grid) noexcept { return bounds; } -Bounds boundsForPoints(const PointSet& points) noexcept { +Bounds boundsForPoints(const PointSet &points) noexcept { Bounds bounds; - for (const ProcgenPoint& point : points.points()) bounds.include(point.x, point.y, point.z); + for (const ProcgenPoint &point : points.points()) bounds.include(point.x, point.y, point.z); return bounds; } -Collider colliderForMesh(const MeshBuild& mesh) { +Collider colliderForMesh(const MeshBuild &mesh) { Collider collider; collider.vertices = mesh.positions(); - collider.indices = mesh.indices(); - collider.bounds = boundsForMesh(mesh); + collider.indices = mesh.indices(); + collider.bounds = boundsForMesh(mesh); return collider; } -Grid2D makeHexTopology(const Params& params) { - const int width = std::clamp(params.getInt("width", 32), 1, 256); +Grid2D makeHexTopology(const Params ¶ms) { + const int width = std::clamp(params.getInt("width", 32), 1, 256); const int height = std::clamp(params.getInt("height", 24), 1, 256); - Grid2D topology; + Grid2D topology; topology.resize(width, height); const std::uint32_t seed = params.getSeed(); for (int y = 0; y < height; ++y) { @@ -853,8 +830,7 @@ Grid2D makeHexTopology(const Params& params) { // This is a compact cell-topology projection. Elevation and dense // geometry remain in the mesh recipe; no per-cell ECS objects are // created merely to publish topology. - const std::uint32_t value = seed ^ (std::uint32_t(x) * 0x9e3779b9u) ^ - (std::uint32_t(y) * 0x85ebca6bu); + const std::uint32_t value = seed ^ (std::uint32_t(x) * 0x9e3779b9u) ^ (std::uint32_t(y) * 0x85ebca6bu); topology.setCell(x, y, int((value ^ (value >> 16u)) % 10u)); } } @@ -864,18 +840,17 @@ Grid2D makeHexTopology(const Params& params) { return topology; } -PointSet makeHexAnchors(const Params& params) { +PointSet makeHexAnchors(const Params ¶ms) { const float radius = params.getFloat("radius", 1.f); - const int width = std::max(2, params.getInt("width", 32)); - const int height = std::max(2, params.getInt("height", 24)); - const float xStep = radius * 1.5f; - const float zStep = radius * 1.7320508076f; - PointSet anchors; + const int width = std::max(2, params.getInt("width", 32)); + const int height = std::max(2, params.getInt("height", 24)); + const float xStep = radius * 1.5f; + const float zStep = radius * 1.7320508076f; + PointSet anchors; anchors.add(0.f, 0.f, 0.f); anchors.add(float(width - 1) * xStep, 0.f, 0.f); anchors.add(0.f, 0.f, float(height - 1) * zStep); - anchors.add(float(width - 1) * xStep, 0.f, - (float(height - 1) + float((width - 1) & 1) * .5f) * zStep); + anchors.add(float(width - 1) * xStep, 0.f, (float(height - 1) + float((width - 1) & 1) * .5f) * zStep); anchors.setStringAttribute(0, "role", "spawn"); anchors.setStringAttribute(1, "role", "edge"); anchors.setStringAttribute(2, "role", "edge"); @@ -883,9 +858,9 @@ PointSet makeHexAnchors(const Params& params) { return anchors; } -Grid2D makeCastleTopology(const Params& params) { +Grid2D makeCastleTopology(const Params ¶ms) { const int rings = std::clamp(params.getInt("rings", 2), 1, 4); - Grid2D topology; + Grid2D topology; topology.resize(rings, 1); for (int ring = 0; ring < rings; ++ring) topology.setCell(ring, 0, ring + 1); topology.setMeta("role", "topology"); @@ -894,15 +869,13 @@ Grid2D makeCastleTopology(const Params& params) { return topology; } -PointSet makeCastleAnchors(const Params& params) { - const float width = std::max(16.f, params.getFloat("width", 42.f)); - const float depth = std::max(16.f, params.getFloat("depth", 36.f)); +PointSet makeCastleAnchors(const Params ¶ms) { + const float width = std::max(16.f, params.getFloat("width", 42.f)); + const float depth = std::max(16.f, params.getFloat("depth", 36.f)); const float gateWidth = std::clamp(params.getFloat("gateWidth", 5.f), 2.f, width * .3f); - const float keepWidth = std::clamp(params.getFloat("keepWidth", width * .25f), 6.f, - width * .55f); - const float keepDepth = std::clamp(params.getFloat("keepDepth", depth * .24f), 6.f, - depth * .55f); - PointSet anchors; + const float keepWidth = std::clamp(params.getFloat("keepWidth", width * .25f), 6.f, width * .55f); + const float keepDepth = std::clamp(params.getFloat("keepDepth", depth * .24f), 6.f, depth * .55f); + PointSet anchors; anchors.add(0.f, 0.f, -depth * .5f - 1.f); anchors.add(-width * .5f + gateWidth, 0.f, -depth * .5f); anchors.add(width * .5f - gateWidth, 0.f, -depth * .5f); @@ -916,24 +889,22 @@ PointSet makeCastleAnchors(const Params& params) { return anchors; } -eve::Result partFromMesh(std::string role, ArtifactId parent, - const BuildKey& parentKey, MeshBuild mesh, +eve::Result partFromMesh(std::string role, ArtifactId parent, const BuildKey &parentKey, MeshBuild mesh, eve::Value::Object metadata) { auto childKey = BuildKey::fromCanonical(parentKey.format() + "/" + role); - if (!childKey) return rejectedPart(eve::DiagnosticCode::InvalidArgument, - "cannot derive child build key for " + role); + if (!childKey) + return rejectedPart(eve::DiagnosticCode::InvalidArgument, "cannot derive child build key for " + role); const ArtifactId childId = parent.child(role); const Bounds bounds = boundsForMesh(mesh); return makeArtifactPart(std::move(role), childId, ArtifactType::MeshData, eve::SchemaVersion(1), std::move(*childKey), bounds, {}, std::move(metadata), std::move(mesh)); } -eve::Result partFromCollider(std::string role, ArtifactId parent, - const BuildKey& parentKey, Collider collider, - eve::Value::Object metadata) { +eve::Result partFromCollider(std::string role, ArtifactId parent, const BuildKey &parentKey, + Collider collider, eve::Value::Object metadata) { auto childKey = BuildKey::fromCanonical(parentKey.format() + "/" + role); - if (!childKey) return rejectedPart(eve::DiagnosticCode::InvalidArgument, - "cannot derive child build key for " + role); + if (!childKey) + return rejectedPart(eve::DiagnosticCode::InvalidArgument, "cannot derive child build key for " + role); const ArtifactId childId = parent.child(role); const Bounds bounds = collider.bounds; return makeArtifactPart(std::move(role), childId, ArtifactType::Collider, eve::SchemaVersion(1), @@ -941,24 +912,22 @@ eve::Result partFromCollider(std::string role, ArtifactId parent, std::move(collider)); } -eve::Result partFromGrid(std::string role, ArtifactId parent, - const BuildKey& parentKey, Grid2D grid, +eve::Result partFromGrid(std::string role, ArtifactId parent, const BuildKey &parentKey, Grid2D grid, eve::Value::Object metadata) { auto childKey = BuildKey::fromCanonical(parentKey.format() + "/" + role); - if (!childKey) return rejectedPart(eve::DiagnosticCode::InvalidArgument, - "cannot derive child build key for " + role); + if (!childKey) + return rejectedPart(eve::DiagnosticCode::InvalidArgument, "cannot derive child build key for " + role); const ArtifactId childId = parent.child(role); const Bounds bounds = boundsForGrid(grid); return makeArtifactPart(std::move(role), childId, ArtifactType::Grid, eve::SchemaVersion(1), std::move(*childKey), bounds, {}, std::move(metadata), std::move(grid)); } -eve::Result partFromPoints(std::string role, ArtifactId parent, - const BuildKey& parentKey, PointSet points, - eve::Value::Object metadata) { +eve::Result partFromPoints(std::string role, ArtifactId parent, const BuildKey &parentKey, + PointSet points, eve::Value::Object metadata) { auto childKey = BuildKey::fromCanonical(parentKey.format() + "/" + role); - if (!childKey) return rejectedPart(eve::DiagnosticCode::InvalidArgument, - "cannot derive child build key for " + role); + if (!childKey) + return rejectedPart(eve::DiagnosticCode::InvalidArgument, "cannot derive child build key for " + role); const ArtifactId childId = parent.child(role); const Bounds bounds = boundsForPoints(points); return makeArtifactPart(std::move(role), childId, ArtifactType::PointSet, eve::SchemaVersion(1), @@ -966,8 +935,7 @@ eve::Result partFromPoints(std::string role, ArtifactId parent, } template -bool appendPart(eve::Result&& result, std::vector& parts, - eve::Status& failure) { +bool appendPart(eve::Result &&result, std::vector &parts, eve::Status &failure) { if (!result.ok()) { failure = result.status(); return false; @@ -976,27 +944,25 @@ bool appendPart(eve::Result&& result, std::vector& p return true; } -eve::Result generateComposite(const Params& params, ArtifactId id, - std::string_view recipe, - bool (*generateMesh)(const Params&, MeshBuild&, - std::string&), +eve::Result generateComposite(const Params ¶ms, ArtifactId id, std::string_view recipe, + bool (*generateMesh)(const Params &, MeshBuild &, std::string &), Grid2D topology, PointSet anchors) { - if (id.isNil()) return rejectedArtifact(eve::DiagnosticCode::InvalidArgument, - "top-level artifact identity must not be nil"); + if (id.isNil()) + return rejectedArtifact(eve::DiagnosticCode::InvalidArgument, "top-level artifact identity must not be nil"); const auto key = BuildKey::forRecipe(recipe, params); - if (!key) return rejectedArtifact(eve::DiagnosticCode::InvalidArgument, - "recipe or parameters cannot form a build key"); + if (!key) + return rejectedArtifact(eve::DiagnosticCode::InvalidArgument, "recipe or parameters cannot form a build key"); - MeshBuild mesh; + MeshBuild mesh; std::string error; if (!generateMesh(params, mesh, error)) { return rejectedArtifact(eve::DiagnosticCode::Failed, error.empty() ? "procedural mesh generation failed" : error); } - const Collider collider = colliderForMesh(mesh); + const Collider collider = colliderForMesh(mesh); std::vector parts; parts.reserve(4); - eve::Status failure; + eve::Status failure; eve::Value::Object meshMeta; meshMeta.emplace("role", eve::Value("mesh")); meshMeta.emplace("densePayload", eve::Value(true)); @@ -1013,16 +979,14 @@ eve::Result generateComposite(const Params& params, ArtifactI eve::Value::Object topologyMeta; topologyMeta.emplace("role", eve::Value("topology")); - auto topologyPart = partFromGrid("topology", id, *key, std::move(topology), - std::move(topologyMeta)); + auto topologyPart = partFromGrid("topology", id, *key, std::move(topology), std::move(topologyMeta)); if (!appendPart(std::move(topologyPart), parts, failure)) return eve::Result::failure(std::move(failure)); eve::Value::Object anchorsMeta; anchorsMeta.emplace("role", eve::Value("anchors")); anchorsMeta.emplace("coordinateSpace", eve::Value("world")); - auto anchorsPart = partFromPoints("anchors", id, *key, std::move(anchors), - std::move(anchorsMeta)); + auto anchorsPart = partFromPoints("anchors", id, *key, std::move(anchors), std::move(anchorsMeta)); if (!appendPart(std::move(anchorsPart), parts, failure)) return eve::Result::failure(std::move(failure)); @@ -1042,7 +1006,7 @@ eve::Result generateComposite(const Params& params, ArtifactI class ArtifactStoreStage final : public eve::artifact::PreparedPublication { public: - ArtifactStoreStage(ArtifactStore& owner, std::vector artifacts) + ArtifactStoreStage(ArtifactStore &owner, std::vector artifacts) : owner_(&owner), artifacts_(std::move(artifacts)) {} ~ArtifactStoreStage() override { rollback(); } @@ -1061,7 +1025,7 @@ public: } private: - ArtifactStore* owner_ = nullptr; + ArtifactStore *owner_ = nullptr; std::vector artifacts_; }; @@ -1070,11 +1034,11 @@ std::optional BuildKey::fromCanonical(std::string_view canonical) { return BuildKey(std::string(canonical)); } -std::optional BuildKey::forRecipe(std::string_view recipeId, const Params& params) { +std::optional BuildKey::forRecipe(std::string_view recipeId, const Params ¶ms) { if (recipeId.empty() || hasControl(recipeId)) return std::nullopt; std::ostringstream value; - value << "procgen/v1/" << recipeId << '/' << std::hex << std::setw(16) - << std::setfill('0') << fnv1a(std::string(recipeId) + '\0' + params.canonicalString()); + value << "procgen/v1/" << recipeId << '/' << std::hex << std::setw(16) << std::setfill('0') + << fnv1a(std::string(recipeId) + '\0' + params.canonicalString()); return BuildKey(value.str()); } @@ -1083,95 +1047,95 @@ void Bounds::include(float x, float y, float z) noexcept { minX = maxX = x; minY = maxY = y; minZ = maxZ = z; - valid = true; + valid = true; return; } - minX = std::min(minX, x); minY = std::min(minY, y); minZ = std::min(minZ, z); - maxX = std::max(maxX, x); maxY = std::max(maxY, y); maxZ = std::max(maxZ, z); + minX = std::min(minX, x); + minY = std::min(minY, y); + minZ = std::min(minZ, z); + maxX = std::max(maxX, x); + maxY = std::max(maxY, y); + maxZ = std::max(maxZ, z); } bool ImageData::isValid() const noexcept { if (width <= 0 || height <= 0 || channels <= 0) return false; - const auto expected = static_cast(width) * static_cast(height) * - static_cast(channels); + const auto expected = + static_cast(width) * static_cast(height) * static_cast(channels); return expected == pixels.size() && !format.empty(); } bool Collider::isValid() const noexcept { - return finiteBounds(bounds) && !shape.empty() && vertices.size() % 3u == 0u && - indices.size() % 3u == 0u && + return finiteBounds(bounds) && !shape.empty() && vertices.size() % 3u == 0u && indices.size() % 3u == 0u && !vertices.empty() && !indices.empty() && - std::all_of(vertices.begin(), vertices.end(), - [](float value) { return std::isfinite(value); }) && - std::all_of(indices.begin(), indices.end(), [&](std::uint32_t index) { - return index < vertices.size() / 3u; - }); + std::all_of(vertices.begin(), vertices.end(), [](float value) { return std::isfinite(value); }) && + std::all_of(indices.begin(), indices.end(), + [&](std::uint32_t index) { return index < vertices.size() / 3u; }); } -const ArtifactPart* CompositeArtifact::find(std::string_view role) const noexcept { - const auto found = std::find_if(children.begin(), children.end(), [&](const ArtifactPart& part) { - return part.role == role; - }); +const ArtifactPart *CompositeArtifact::find(std::string_view role) const noexcept { + const auto found = + std::find_if(children.begin(), children.end(), [&](const ArtifactPart &part) { return part.role == role; }); return found == children.end() ? nullptr : &*found; } -ArtifactType artifactType(const GeneratedArtifact::Payload& payload) noexcept { - return std::visit([](const auto& value) -> ArtifactType { - using T = std::decay_t; - if constexpr (std::is_same_v) return ArtifactType::Grid; - if constexpr (std::is_same_v) return ArtifactType::PointSet; - if constexpr (std::is_same_v) return ArtifactType::MeshData; - if constexpr (std::is_same_v) return ArtifactType::ImageData; - if constexpr (std::is_same_v) return ArtifactType::Collider; - return ArtifactType::Composite; - }, payload); +ArtifactType artifactType(const GeneratedArtifact::Payload &payload) noexcept { + return std::visit( + [](const auto &value) -> ArtifactType { + using T = std::decay_t; + if constexpr (std::is_same_v) return ArtifactType::Grid; + if constexpr (std::is_same_v) return ArtifactType::PointSet; + if constexpr (std::is_same_v) return ArtifactType::MeshData; + if constexpr (std::is_same_v) return ArtifactType::ImageData; + if constexpr (std::is_same_v) return ArtifactType::Collider; + return ArtifactType::Composite; + }, + payload); } -const char* artifactTypeName(ArtifactType type) noexcept { +const char *artifactTypeName(ArtifactType type) noexcept { switch (type) { - case ArtifactType::Grid: return "grid"; - case ArtifactType::PointSet: return "point_set"; - case ArtifactType::MeshData: return "mesh_data"; - case ArtifactType::ImageData: return "image_data"; - case ArtifactType::Collider: return "collider"; - case ArtifactType::Composite: return "composite"; + case ArtifactType::Grid: return "grid"; + case ArtifactType::PointSet: return "point_set"; + case ArtifactType::MeshData: return "mesh_data"; + case ArtifactType::ImageData: return "image_data"; + case ArtifactType::Collider: return "collider"; + case ArtifactType::Composite: return "composite"; } return "unknown"; } -eve::Result makeArtifact( - ArtifactId id, ArtifactType type, eve::SchemaVersion schemaVersion, BuildKey buildKey, - Bounds bounds, std::vector dependencies, eve::Value::Object metadata, - GeneratedArtifact::Payload payload) { +eve::Result makeArtifact(ArtifactId id, ArtifactType type, eve::SchemaVersion schemaVersion, + BuildKey buildKey, Bounds bounds, std::vector dependencies, + eve::Value::Object metadata, GeneratedArtifact::Payload payload) { GeneratedArtifact result; - result.id = id; - result.type = type; + result.id = id; + result.type = type; result.schemaVersion = schemaVersion; - result.buildKey = std::move(buildKey); - result.bounds = bounds; - result.dependencies = std::move(dependencies); - result.metadata = std::move(metadata); - result.payload = std::move(payload); + result.buildKey = std::move(buildKey); + result.bounds = bounds; + result.dependencies = std::move(dependencies); + result.metadata = std::move(metadata); + result.payload = std::move(payload); if (!validArtifact(result)) - return rejectedArtifact(eve::DiagnosticCode::InvalidArgument, - "artifact structure or typed payload is invalid"); + return rejectedArtifact(eve::DiagnosticCode::InvalidArgument, "artifact structure or typed payload is invalid"); return eve::Result::success(std::move(result)); } -eve::Result makeArtifactPart( - std::string role, ArtifactId id, ArtifactType type, eve::SchemaVersion schemaVersion, - BuildKey buildKey, Bounds bounds, std::vector dependencies, - eve::Value::Object metadata, ArtifactLeafPayload payload) { +eve::Result makeArtifactPart(std::string role, ArtifactId id, ArtifactType type, + eve::SchemaVersion schemaVersion, BuildKey buildKey, Bounds bounds, + std::vector dependencies, eve::Value::Object metadata, + ArtifactLeafPayload payload) { ArtifactPart result; - result.role = std::move(role); - result.id = id; - result.type = type; + result.role = std::move(role); + result.id = id; + result.type = type; result.schemaVersion = schemaVersion; - result.buildKey = std::move(buildKey); - result.bounds = bounds; - result.dependencies = std::move(dependencies); - result.metadata = std::move(metadata); - result.payload = std::move(payload); + result.buildKey = std::move(buildKey); + result.bounds = bounds; + result.dependencies = std::move(dependencies); + result.metadata = std::move(metadata); + result.payload = std::move(payload); if (!validPart(result)) return rejectedPart(eve::DiagnosticCode::InvalidArgument, "artifact part '" + result.role + "' structure or typed payload is invalid"); @@ -1185,72 +1149,66 @@ eve::Result ArtifactStore::publish(GeneratedArtifact artifact) { return eve::Result::success(ids.front()); } -eve::Result> ArtifactStore::publishBatch( - std::vector artifacts) { +eve::Result> ArtifactStore::publishBatch(std::vector artifacts) { if (stageActive_) + return eve::Result>::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "artifact store already has a pending stage")); + if (artifacts.empty()) return eve::Result>::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, "artifact store already has a pending stage")); - if (artifacts.empty()) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, - "artifact publish batch must not be empty", "artifacts")); + eve::DiagnosticCode::InvalidArgument, "artifact publish batch must not be empty", "artifacts")); std::unordered_map available; available.reserve(artifacts_.size() + partOwners_.size() + artifacts.size() * 5u); - for (const auto& [id, artifact] : artifacts_) available.emplace(id, artifact.type); - for (const auto& [partId, ownerId] : partOwners_) { - const GeneratedArtifact* owner = find(ownerId); + for (const auto &[id, artifact] : artifacts_) available.emplace(id, artifact.type); + for (const auto &[partId, ownerId] : partOwners_) { + const GeneratedArtifact *owner = find(ownerId); if (!owner || owner->type != ArtifactType::Composite) continue; - const auto& children = std::get(owner->payload).children; - const auto found = std::find_if(children.begin(), children.end(), - [partId](const ArtifactPart& part) { - return part.id == partId; - }); + const auto &children = std::get(owner->payload).children; + const auto found = std::find_if(children.begin(), children.end(), + [partId](const ArtifactPart &part) { return part.id == partId; }); if (found != children.end()) available.emplace(partId, found->type); } std::vector ids; ids.reserve(artifacts.size()); - for (const GeneratedArtifact& artifact : artifacts) { - if (!validArtifact(artifact)) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, - "artifact structure or typed payload is invalid", "artifact")); + for (const GeneratedArtifact &artifact : artifacts) { + if (!validArtifact(artifact)) + return eve::Result>::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvalidArgument, "artifact structure or typed payload is invalid", "artifact")); if (!available.emplace(artifact.id, artifact.type).second) return eve::Result>::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, "artifact or part identity is already published", - "artifact.id")); + eve::DiagnosticCode::Conflict, "artifact or part identity is already published", "artifact.id")); ids.push_back(artifact.id); if (artifact.type == ArtifactType::Composite) { - for (const ArtifactPart& part : std::get(artifact.payload).children) { + for (const ArtifactPart &part : std::get(artifact.payload).children) { if (!available.emplace(part.id, part.type).second) return eve::Result>::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, "composite part identity conflicts", - "artifact.children.id")); + eve::DiagnosticCode::Conflict, "composite part identity conflicts", "artifact.children.id")); } } } - const auto checkDependencies = [&](ArtifactType type, - const std::vector& dependencies) - -> std::optional { + const auto checkDependencies = [&](ArtifactType type, + const std::vector &dependencies) -> std::optional { bool meshDependency = false; for (ArtifactId dependency : dependencies) { const auto found = available.find(dependency); - if (found == available.end()) return eve::Diagnostic::error( - eve::DiagnosticCode::NotFound, "artifact dependency is not published or in batch", - "artifact.dependencies"); + if (found == available.end()) + return eve::Diagnostic::error(eve::DiagnosticCode::NotFound, + "artifact dependency is not published or in batch", + "artifact.dependencies"); meshDependency = meshDependency || found->second == ArtifactType::MeshData; } if (type == ArtifactType::Collider && !meshDependency) return eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, - "collider must depend on a mesh artifact", - "artifact.dependencies"); + "collider must depend on a mesh artifact", "artifact.dependencies"); return std::nullopt; }; - for (const GeneratedArtifact& artifact : artifacts) { + for (const GeneratedArtifact &artifact : artifacts) { if (auto error = checkDependencies(artifact.type, artifact.dependencies)) return eve::Result>::failure(std::move(*error)); if (artifact.type == ArtifactType::Composite) - for (const ArtifactPart& part : std::get(artifact.payload).children) + for (const ArtifactPart &part : std::get(artifact.payload).children) if (auto error = checkDependencies(part.type, part.dependencies)) return eve::Result>::failure(std::move(*error)); } @@ -1258,15 +1216,14 @@ eve::Result> ArtifactStore::publishBatch( try { artifacts_.reserve(artifacts_.size() + artifacts.size()); std::size_t newParts = 0; - for (const GeneratedArtifact& artifact : artifacts) + for (const GeneratedArtifact &artifact : artifacts) if (artifact.type == ArtifactType::Composite) newParts += std::get(artifact.payload).children.size(); partOwners_.reserve(partOwners_.size() + newParts); - for (GeneratedArtifact& artifact : artifacts) { + for (GeneratedArtifact &artifact : artifacts) { const ArtifactId owner = artifact.id; if (artifact.type == ArtifactType::Composite) - for (const ArtifactPart& part : - std::get(artifact.payload).children) + for (const ArtifactPart &part : std::get(artifact.payload).children) partOwners_.emplace(part.id, owner); artifacts_.emplace(owner, std::move(artifact)); } @@ -1279,8 +1236,7 @@ eve::Result> ArtifactStore::publishBatch( ++owner; } return eve::Result>::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "artifact store allocation failed; batch rolled back", - "artifacts")); + eve::DiagnosticCode::Failed, "artifact store allocation failed; batch rolled back", "artifacts")); } return eve::Result>::success(std::move(ids)); } @@ -1289,17 +1245,15 @@ eve::Result> ArtifactStore:: GeneratedArtifact artifact) { if (stageActive_) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "artifact store already has a pending stage")); + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "artifact store already has a pending stage")); std::vector artifacts; try { artifacts.emplace_back(std::move(artifact)); auto valid = validateBatchAgainstStore(*this, artifacts); if (!valid.ok()) - return eve::Result>::failure( - valid.status()); + return eve::Result>::failure(valid.status()); std::size_t newParts = 0; - for (const auto& entry : artifacts) + for (const auto &entry : artifacts) if (entry.type == ArtifactType::Composite) newParts += std::get(entry.payload).children.size(); // Reserve while the store is still hidden. The no-throw commit only @@ -1312,16 +1266,15 @@ eve::Result> ArtifactStore:: } catch (...) { stageActive_ = false; return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Failed, - "artifact store stage allocation failed")); + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "artifact store stage allocation failed")); } } void ArtifactStore::commitStaged(std::vector artifacts) noexcept { - for (GeneratedArtifact& artifact : artifacts) { + for (GeneratedArtifact &artifact : artifacts) { const ArtifactId owner = artifact.id; if (artifact.type == ArtifactType::Composite) - for (const ArtifactPart& part : std::get(artifact.payload).children) + for (const ArtifactPart &part : std::get(artifact.payload).children) partOwners_.emplace(part.id, owner); artifacts_.emplace(owner, std::move(artifact)); } @@ -1330,19 +1283,19 @@ void ArtifactStore::commitStaged(std::vector artifacts) noexc void ArtifactStore::rollbackStaged() noexcept { stageActive_ = false; } -const GeneratedArtifact* ArtifactStore::find(ArtifactId id) const noexcept { +const GeneratedArtifact *ArtifactStore::find(ArtifactId id) const noexcept { const auto found = artifacts_.find(id); return found == artifacts_.end() ? nullptr : &found->second; } -const ArtifactPart* ArtifactStore::findPart(ArtifactId id) const noexcept { +const ArtifactPart *ArtifactStore::findPart(ArtifactId id) const noexcept { const auto owner = partOwners_.find(id); if (owner == partOwners_.end()) return nullptr; - const GeneratedArtifact* artifact = find(owner->second); + const GeneratedArtifact *artifact = find(owner->second); if (!artifact || artifact->type != ArtifactType::Composite) return nullptr; - const auto& children = std::get(artifact->payload).children; - const auto found = std::find_if(children.begin(), children.end(), - [id](const ArtifactPart& part) { return part.id == id; }); + const auto &children = std::get(artifact->payload).children; + const auto found = + std::find_if(children.begin(), children.end(), [id](const ArtifactPart &part) { return part.id == id; }); return found == children.end() ? nullptr : &*found; } @@ -1353,34 +1306,33 @@ std::optional ArtifactStore::typeOf(ArtifactId id) const noexcept if (part != partOwners_.end()) { const auto owner = artifacts_.find(part->second); if (owner != artifacts_.end() && owner->second.type == ArtifactType::Composite) { - const auto& children = std::get(owner->second.payload).children; - const auto found = std::find_if(children.begin(), children.end(), - [id](const ArtifactPart& value) { return value.id == id; }); + const auto &children = std::get(owner->second.payload).children; + const auto found = std::find_if(children.begin(), children.end(), + [id](const ArtifactPart &value) { return value.id == id; }); if (found != children.end()) return found->type; } } return std::nullopt; } -std::vector ArtifactStore::findByBuildKey(const BuildKey& key) const { +std::vector ArtifactStore::findByBuildKey(const BuildKey &key) const { std::vector result; - for (const auto& entry : artifacts_) { + for (const auto &entry : artifacts_) { if (entry.second.buildKey == key) result.push_back(entry.first); } - std::sort(result.begin(), result.end(), [](const ArtifactId& lhs, const ArtifactId& rhs) { - return lhs.format() < rhs.format(); - }); + std::sort(result.begin(), result.end(), + [](const ArtifactId &lhs, const ArtifactId &rhs) { return lhs.format() < rhs.format(); }); return result; } eve::Result ArtifactStore::remove(ArtifactId id) { const auto found = artifacts_.find(id); if (found == artifacts_.end()) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::NotFound, "artifact identity is not published", "artifact.id")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "artifact identity is not published", "artifact.id")); } if (found->second.type == ArtifactType::Composite) - for (const ArtifactPart& part : std::get(found->second.payload).children) + for (const ArtifactPart &part : std::get(found->second.payload).children) partOwners_.erase(part.id); artifacts_.erase(found); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); @@ -1392,18 +1344,14 @@ void ArtifactStore::clear() noexcept { stageActive_ = false; } -std::size_t ArtifactStore::partCount() const noexcept { - return partOwners_.size(); -} +std::size_t ArtifactStore::partCount() const noexcept { return partOwners_.size(); } eve::Result ArtifactStore::snapshotState() const { try { std::vector ids; ids.reserve(artifacts_.size()); - for (const auto& entry : artifacts_) ids.push_back(entry.first); - std::sort(ids.begin(), ids.end(), [](ArtifactId lhs, ArtifactId rhs) { - return lhs.format() < rhs.format(); - }); + for (const auto &entry : artifacts_) ids.push_back(entry.first); + std::sort(ids.begin(), ids.end(), [](ArtifactId lhs, ArtifactId rhs) { return lhs.format() < rhs.format(); }); eve::Value::Array records; records.reserve(ids.size()); @@ -1415,45 +1363,45 @@ eve::Result ArtifactStore::snapshotState() const { state.emplace("artifacts", eve::Value(std::move(records))); return eve::Result::success(eve::Value(std::move(state))); } catch (...) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "artifact store snapshot allocation failed")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "artifact store snapshot allocation failed")); } } -eve::Result ArtifactStore::restoreState(const eve::Value& state) { +eve::Result ArtifactStore::restoreState(const eve::Value &state) { if (stageActive_ || !artifacts_.empty()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, "artifact store restore requires an empty store")); - const auto* object = state.getIf(); - const auto* provider = object ? stateMember(*object, "provider") : nullptr; - const auto* providerName = provider ? provider->getIf() : nullptr; - const auto* encodedVersion = object ? stateMember(*object, "version") : nullptr; - const auto* version = encodedVersion ? encodedVersion->getIf() : nullptr; - const auto* encodedArtifacts = object ? stateMember(*object, "artifacts") : nullptr; - const auto* encodedList = encodedArtifacts ? encodedArtifacts->getIf() : nullptr; - if (!object || !providerName || *providerName != "procgen.cpu-artifact-store" || - !version || *version != 2 || !encodedList) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::UnknownVersion, "artifact store requires dense snapshot version 2")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "artifact store restore requires an empty store")); + const auto *object = state.getIf(); + const auto *provider = object ? stateMember(*object, "provider") : nullptr; + const auto *providerName = provider ? provider->getIf() : nullptr; + const auto *encodedVersion = object ? stateMember(*object, "version") : nullptr; + const auto *version = encodedVersion ? encodedVersion->getIf() : nullptr; + const auto *encodedArtifacts = object ? stateMember(*object, "artifacts") : nullptr; + const auto *encodedList = encodedArtifacts ? encodedArtifacts->getIf() : nullptr; + if (!object || !providerName || *providerName != "procgen.cpu-artifact-store" || !version || *version != 2 || + !encodedList) + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::UnknownVersion, + "artifact store requires dense snapshot version 2")); std::vector candidate; try { candidate.reserve(encodedList->size()); - for (const eve::Value& encoded : *encodedList) { + for (const eve::Value &encoded : *encodedList) { GeneratedArtifact artifact; if (!decodeArtifact(encoded, artifact)) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "invalid dense artifact store record")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "invalid dense artifact store record")); candidate.push_back(std::move(artifact)); } } catch (...) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "artifact store restore allocation failed")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "artifact store restore allocation failed")); } auto valid = validateBatchAgainstStore(*this, candidate); if (!valid.ok()) return valid; std::unordered_map restoredArtifacts; - std::unordered_map restoredParts; + std::unordered_map restoredParts; try { restoredArtifacts.reserve(candidate.size()); std::size_t partCount = 0; @@ -1469,35 +1417,34 @@ eve::Result ArtifactStore::restoreState(const eve::Value& state) { restoredArtifacts.emplace(owner, std::move(artifact)); } } catch (...) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "artifact store restore indexing allocation failed")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "artifact store restore indexing allocation failed")); } artifacts_.swap(restoredArtifacts); partOwners_.swap(restoredParts); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result generateHexTerrainArtifact(const Params& params, ArtifactId id) { - return generateComposite(params, id, "mesh.hexterrain", generateHexTerrainMesh, - makeHexTopology(params), makeHexAnchors(params)); +eve::Result generateHexTerrainArtifact(const Params ¶ms, ArtifactId id) { + return generateComposite(params, id, "mesh.hexterrain", generateHexTerrainMesh, makeHexTopology(params), + makeHexAnchors(params)); } -eve::Result generateCastleArtifact(const Params& params, ArtifactId id) { - return generateComposite(params, id, "mesh.castle", generateCastleMesh, - makeCastleTopology(params), makeCastleAnchors(params)); +eve::Result generateCastleArtifact(const Params ¶ms, ArtifactId id) { + return generateComposite(params, id, "mesh.castle", generateCastleMesh, makeCastleTopology(params), + makeCastleAnchors(params)); } -eve::Result generateMeshArtifact(std::string_view recipeId, - const Params& params, ArtifactId id) { +eve::Result generateMeshArtifact(std::string_view recipeId, const Params ¶ms, ArtifactId id) { if (recipeId == "mesh.hexterrain") return generateHexTerrainArtifact(params, id); if (recipeId == "mesh.castle") return generateCastleArtifact(params, id); - if (id.isNil()) return rejectedArtifact(eve::DiagnosticCode::InvalidArgument, - "top-level artifact identity must not be nil"); + if (id.isNil()) + return rejectedArtifact(eve::DiagnosticCode::InvalidArgument, "top-level artifact identity must not be nil"); const auto key = BuildKey::forRecipe(recipeId, params); - if (!key) return rejectedArtifact(eve::DiagnosticCode::InvalidArgument, - "recipe or parameters cannot form a build key"); + if (!key) + return rejectedArtifact(eve::DiagnosticCode::InvalidArgument, "recipe or parameters cannot form a build key"); MeshRecipeRegistry::instance().registerBuiltins(); - MeshBuild mesh; + MeshBuild mesh; std::string error; if (!MeshRecipeRegistry::instance().generate(std::string(recipeId), params, mesh, error)) return rejectedArtifact(eve::DiagnosticCode::Failed, diff --git a/src/modules/procgen/GeneratedArtifact.h b/src/modules/procgen/GeneratedArtifact.h index 85c12543..0b83be66 100644 --- a/src/modules/procgen/GeneratedArtifact.h +++ b/src/modules/procgen/GeneratedArtifact.h @@ -9,13 +9,13 @@ * dense data in typed values; metadata is deliberately small and dynamic. */ -#include "common/Identity.h" #include "common/ArtifactPublication.h" +#include "common/Generation.h" +#include "common/Identity.h" #include "common/Result.h" +#include "common/SchemaVersion.h" #include "common/Snapshot.h" #include "common/Value.h" -#include "common/Generation.h" -#include "common/SchemaVersion.h" #include "procgen/Grid2D.h" #include "procgen/MeshBuild.h" #include "procgen/Params.h" @@ -28,8 +28,8 @@ #include #include #include -#include #include +#include #include namespace eve::procgen { @@ -61,14 +61,13 @@ public: * @param params Generation parameters. * @return A key containing a versioned hash of canonical recipe inputs. */ - [[nodiscard]] static std::optional forRecipe(std::string_view recipeId, - const Params& params); + [[nodiscard]] static std::optional forRecipe(std::string_view recipeId, const Params& params); /** @brief Return whether this key is non-empty. */ [[nodiscard]] bool empty() const noexcept { return value_.empty(); } /** @brief Return canonical key text. */ [[nodiscard]] const std::string& format() const noexcept { return value_; } - friend bool operator==(const BuildKey&, const BuildKey&) noexcept = default; + friend bool operator==(const BuildKey&, const BuildKey&) noexcept = default; private: explicit BuildKey(std::string value) : value_(std::move(value)) {} @@ -78,7 +77,8 @@ private: } // namespace eve::procgen namespace std { -template <> struct hash { +template <> +struct hash { size_t operator()(const eve::procgen::BuildKey& key) const noexcept { return std::hash{}(key.format()); } @@ -89,12 +89,12 @@ namespace eve::procgen { /** @brief Axis-aligned bounds in the artifact's declared world coordinate space. */ struct Bounds { - float minX = 0.f; - float minY = 0.f; - float minZ = 0.f; - float maxX = 0.f; - float maxY = 0.f; - float maxZ = 0.f; + float minX = 0.f; + float minY = 0.f; + float minZ = 0.f; + float maxX = 0.f; + float maxY = 0.f; + float maxZ = 0.f; bool valid = false; /** @brief Expand this box to include one point. */ @@ -115,10 +115,10 @@ enum class ArtifactType : std::uint8_t { /** @brief Small owning image product; raw pixels stay out of JSON metadata. */ struct ImageData { - int width = 0; - int height = 0; - int channels = 0; - std::string format; + int width = 0; + int height = 0; + int channels = 0; + std::string format; std::vector pixels; /** @brief Return whether dimensions and pixel storage describe an image. */ @@ -127,10 +127,10 @@ struct ImageData { /** @brief Backend-neutral collider product, normally a triangle mesh or hull. */ struct Collider { - std::vector vertices; + std::vector vertices; std::vector indices; - Bounds bounds; - std::string shape = "triangle_mesh"; + Bounds bounds; + std::string shape = "triangle_mesh"; /** @brief Return whether the collider has complete triangle data. */ [[nodiscard]] bool isValid() const noexcept; @@ -144,15 +144,15 @@ using ArtifactLeafPayload = std::variant dependencies; - eve::Value::Object metadata; - ArtifactLeafPayload payload; - std::string role; + eve::Value::Object metadata; + ArtifactLeafPayload payload; + std::string role; }; /** @brief A coherent set of products published from one deterministic build. */ @@ -177,15 +177,14 @@ struct CompositeArtifact { /** @brief Complete immutable-once-published artifact record. */ struct GeneratedArtifact { - ArtifactId id; - ArtifactType type = ArtifactType::Grid; - eve::SchemaVersion schemaVersion{1}; - BuildKey buildKey; - Bounds bounds; + ArtifactId id; + ArtifactType type = ArtifactType::Grid; + eve::SchemaVersion schemaVersion{1}; + BuildKey buildKey; + Bounds bounds; std::vector dependencies; - eve::Value::Object metadata; - using Payload = std::variant; + eve::Value::Object metadata; + using Payload = std::variant; Payload payload; }; @@ -206,16 +205,17 @@ struct GeneratedArtifact { * @brief Validate and construct one artifact record. * @return A complete owning artifact or a structured rejection diagnostic. */ -[[nodiscard]] eve::Result makeArtifact( - ArtifactId id, ArtifactType type, eve::SchemaVersion schemaVersion, BuildKey buildKey, - Bounds bounds, std::vector dependencies, eve::Value::Object metadata, - GeneratedArtifact::Payload payload); +[[nodiscard]] eve::Result makeArtifact(ArtifactId id, ArtifactType type, + eve::SchemaVersion schemaVersion, BuildKey buildKey, + Bounds bounds, std::vector dependencies, + eve::Value::Object metadata, + GeneratedArtifact::Payload payload); /** @brief Validate and construct a leaf part for a composite artifact. */ -[[nodiscard]] eve::Result makeArtifactPart( - std::string role, ArtifactId id, ArtifactType type, eve::SchemaVersion schemaVersion, - BuildKey buildKey, Bounds bounds, std::vector dependencies, - eve::Value::Object metadata, ArtifactLeafPayload payload); +[[nodiscard]] eve::Result makeArtifactPart(std::string role, ArtifactId id, ArtifactType type, + eve::SchemaVersion schemaVersion, BuildKey buildKey, + Bounds bounds, std::vector dependencies, + eve::Value::Object metadata, ArtifactLeafPayload payload); /** * @brief In-memory owner and publisher for generated artifacts. @@ -242,8 +242,7 @@ public: * @return Published top-level identities in input order. * @remarks A failed Result leaves the store unchanged. */ - [[nodiscard]] eve::Result> publishBatch( - std::vector artifacts); + [[nodiscard]] eve::Result> publishBatch(std::vector artifacts); /** * @brief Prepare one store mutation for a larger provider transaction. @@ -314,7 +313,7 @@ private: std::unordered_map artifacts_; std::unordered_map partOwners_; - bool stageActive_ = false; + bool stageActive_ = false; }; /** @@ -324,8 +323,7 @@ private: * @param id Non-nil identity for the top-level artifact instance. * @return An owning composite artifact, or a structured generation failure. */ -[[nodiscard]] eve::Result generateHexTerrainArtifact( - const Params& params, ArtifactId id); +[[nodiscard]] eve::Result generateHexTerrainArtifact(const Params& params, ArtifactId id); /** * @brief Generate a castle composite containing mesh, collider, ring topology @@ -334,8 +332,7 @@ private: * @param id Non-nil identity for the top-level artifact instance. * @return An owning composite artifact, or a structured generation failure. */ -[[nodiscard]] eve::Result generateCastleArtifact( - const Params& params, ArtifactId id); +[[nodiscard]] eve::Result generateCastleArtifact(const Params& params, ArtifactId id); /** * @brief Generate any registered mesh recipe as a CPU artifact. @@ -344,7 +341,7 @@ private: * @param id Non-nil artifact instance identity. * @return Hex terrain and castle become composites; other recipes become MeshData artifacts. */ -[[nodiscard]] eve::Result generateMeshArtifact( - std::string_view recipeId, const Params& params, ArtifactId id); +[[nodiscard]] eve::Result generateMeshArtifact(std::string_view recipeId, const Params& params, + ArtifactId id); } // namespace eve::procgen diff --git a/src/modules/procgen/MeshBuild.cpp b/src/modules/procgen/MeshBuild.cpp index 3e511730..1d800459 100644 --- a/src/modules/procgen/MeshBuild.cpp +++ b/src/modules/procgen/MeshBuild.cpp @@ -69,24 +69,21 @@ int MeshBuild::getTriangleGroup(int triangleIndex) const { return triangleGroups_[size_t(triangleIndex)]; } -eve::Result MeshBuild::restoreGroupData(std::vector names, - std::vector assignments, +eve::Result MeshBuild::restoreGroupData(std::vector names, std::vector assignments, int activeGroup) { const auto invalid = [&](std::string message, std::string path) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ProcgenGroupDataInvalid, std::move(message), std::move(path), - {}, "procgen.mesh")); + eve::DiagnosticCode::ProcgenGroupDataInvalid, std::move(message), std::move(path), {}, "procgen.mesh")); }; if (assignments.size() != indices_.size() / 3u || activeGroup < -1 || activeGroup >= static_cast(names.size()) || std::any_of(names.begin(), names.end(), [](const std::string &name) { return name.empty(); }) || - std::any_of(assignments.begin(), assignments.end(), [&](int group) { - return group < -1 || group >= static_cast(names.size()); - })) + std::any_of(assignments.begin(), assignments.end(), + [&](int group) { return group < -1 || group >= static_cast(names.size()); })) return invalid("mesh group sidecar is inconsistent with dense mesh streams", "groups"); - groupNames_ = std::move(names); + groupNames_ = std::move(names); triangleGroups_ = std::move(assignments); - activeGroup_ = activeGroup; + activeGroup_ = activeGroup; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } diff --git a/src/modules/procgen/MeshBuild.h b/src/modules/procgen/MeshBuild.h index 7b9656ed..c903ecf0 100644 --- a/src/modules/procgen/MeshBuild.h +++ b/src/modules/procgen/MeshBuild.h @@ -65,8 +65,8 @@ public: * otherwise a stable `ProcgenGroupDataInvalid` diagnostic. Failure * leaves all existing group state unchanged. */ - [[nodiscard]] eve::Result restoreGroupData( - std::vector names, std::vector assignments, int activeGroup); + [[nodiscard]] eve::Result restoreGroupData(std::vector names, std::vector assignments, + int activeGroup); /** * @brief Copies one named group into a standalone compact mesh. * @param groupIndex Triangle-group index to copy. diff --git a/src/modules/procgen/ParamSchema.cpp b/src/modules/procgen/ParamSchema.cpp index 1bf5053d..874afc72 100644 --- a/src/modules/procgen/ParamSchema.cpp +++ b/src/modules/procgen/ParamSchema.cpp @@ -93,9 +93,7 @@ void RecipeDescriptor::applyDefaults(Params& values) const { if (values.has(param.key)) continue; switch (param.kind) { case ParamKind::Integer: values.setInt(param.key, std::stoi(param.defaultValue)); break; - case ParamKind::Boolean: - values.setBool(param.key, std::stoi(param.defaultValue) != 0); - break; + case ParamKind::Boolean: values.setBool(param.key, std::stoi(param.defaultValue) != 0); break; case ParamKind::Float: values.setFloat(param.key, std::stof(param.defaultValue)); break; case ParamKind::String: case ParamKind::Choice: values.setString(param.key, param.defaultValue); break; diff --git a/src/modules/procgen/Params.cpp b/src/modules/procgen/Params.cpp index 9a46401a..7bee72d2 100644 --- a/src/modules/procgen/Params.cpp +++ b/src/modules/procgen/Params.cpp @@ -34,47 +34,43 @@ std::string doubleToken(double value) { std::string canonicalValue(const eve::Value& value) { switch (value.type()) { - case eve::Value::Type::Null: - return "null"; - case eve::Value::Type::Bool: - return std::string("bool:") + (value.asBool() ? "1" : "0"); - case eve::Value::Type::Int64: - return "int:" + std::to_string(value.asInt()); - case eve::Value::Type::Double: - return "double:" + doubleToken(value.asDouble()); - case eve::Value::Type::String: { - const std::string& text = value.asString(); - std::string result = "string:"; - appendLength(result, text); - return result; - } - case eve::Value::Type::Array: { - std::string result = "array:"; - const auto* array = value.getIf(); - result += std::to_string(array ? array->size() : 0); - result.push_back(':'); - if (array) { - for (const eve::Value& element : *array) { - const std::string encoded = canonicalValue(element); - appendLength(result, encoded); - } + case eve::Value::Type::Null: return "null"; + case eve::Value::Type::Bool: return std::string("bool:") + (value.asBool() ? "1" : "0"); + case eve::Value::Type::Int64: return "int:" + std::to_string(value.asInt()); + case eve::Value::Type::Double: return "double:" + doubleToken(value.asDouble()); + case eve::Value::Type::String: { + const std::string& text = value.asString(); + std::string result = "string:"; + appendLength(result, text); + return result; } - return result; - } - case eve::Value::Type::Object: { - std::string result = "object:"; - const auto* object = value.getIf(); - result += std::to_string(object ? object->size() : 0); - result.push_back(':'); - if (object) { - for (const auto& [key, member] : *object) { - appendLength(result, key); - const std::string encoded = canonicalValue(member); - appendLength(result, encoded); + case eve::Value::Type::Array: { + std::string result = "array:"; + const auto* array = value.getIf(); + result += std::to_string(array ? array->size() : 0); + result.push_back(':'); + if (array) { + for (const eve::Value& element : *array) { + const std::string encoded = canonicalValue(element); + appendLength(result, encoded); + } } + return result; + } + case eve::Value::Type::Object: { + std::string result = "object:"; + const auto* object = value.getIf(); + result += std::to_string(object ? object->size() : 0); + result.push_back(':'); + if (object) { + for (const auto& [key, member] : *object) { + appendLength(result, key); + const std::string encoded = canonicalValue(member); + appendLength(result, encoded); + } + } + return result; } - return result; - } } return "null"; } @@ -86,8 +82,7 @@ const eve::Value* findValue(const eve::Value::Object& values, const std::string& std::optional asInt(const eve::Value& value) { if (const auto* integer = value.getIf()) { - if (*integer < std::numeric_limits::lowest() || - *integer > std::numeric_limits::max()) + if (*integer < std::numeric_limits::lowest() || *integer > std::numeric_limits::max()) return std::nullopt; return static_cast(*integer); } @@ -131,8 +126,7 @@ std::optional asBool(const eve::Value& value) { return std::nullopt; } -void appendField(std::string& output, std::string_view domain, std::string_view key, - const eve::Value& value) { +void appendField(std::string& output, std::string_view domain, std::string_view key, const eve::Value& value) { appendLength(output, domain); appendLength(output, key); const std::string encoded = canonicalValue(value); @@ -168,15 +162,11 @@ void Params::setInt(const std::string& key, int value) { values_[key] = eve::Value(static_cast(value)); } -void Params::setFloat(const std::string& key, float value) { - values_[key] = eve::Value(static_cast(value)); -} +void Params::setFloat(const std::string& key, float value) { values_[key] = eve::Value(static_cast(value)); } void Params::setBool(const std::string& key, bool value) { values_[key] = eve::Value(value); } -void Params::setString(const std::string& key, const std::string& value) { - values_[key] = eve::Value(value); -} +void Params::setString(const std::string& key, const std::string& value) { values_[key] = eve::Value(value); } bool Params::has(const std::string& key) const { if (key == kSeedKey || key == kWidthKey || key == kHeightKey) return true; @@ -185,9 +175,7 @@ bool Params::has(const std::string& key) const { int Params::getInt(const std::string& key, int defaultValue) const { if (key == kSeedKey) { - return seed_ <= static_cast(std::numeric_limits::max()) - ? static_cast(seed_) - : defaultValue; + return seed_ <= static_cast(std::numeric_limits::max()) ? static_cast(seed_) : defaultValue; } if (key == kWidthKey) return width_; if (key == kHeightKey) return height_; diff --git a/src/modules/procgen/Params.h b/src/modules/procgen/Params.h index fc9ea682..df29df50 100644 --- a/src/modules/procgen/Params.h +++ b/src/modules/procgen/Params.h @@ -99,9 +99,9 @@ public: [[nodiscard]] std::string canonicalString() const; private: - uint32_t seed_ = 1; - int width_ = 32; - int height_ = 32; + uint32_t seed_ = 1; + int width_ = 32; + int height_ = 32; eve::Value::Object values_; }; diff --git a/src/modules/procgen/Procgen.cpp b/src/modules/procgen/Procgen.cpp index 2e7c15cf..e1baef5c 100644 --- a/src/modules/procgen/Procgen.cpp +++ b/src/modules/procgen/Procgen.cpp @@ -1,12 +1,12 @@ #include "procgen/Procgen.h" -#include "common/SquirrelBinding.h" #include "common/Capability.h" #include "common/ProcgenSceneSink.h" +#include "common/SquirrelBinding.h" #include "procgen/BiomeScript.h" #include "procgen/PointGraphScript.h" -#include "procgen/ShapeGrammarScript.h" #include "procgen/ProcgenCapabilities.h" +#include "procgen/ShapeGrammarScript.h" #include "image/ImageData.h" @@ -25,6 +25,7 @@ #include +#include #include #include #include @@ -33,8 +34,8 @@ #include #include #include -#include #include +#include #include namespace eve::procgen { @@ -59,98 +60,93 @@ struct ScriptProcgenContext { ProcgenContextHandleRef reference; }; -struct ProcgenAnyHandle { - std::uint64_t ownerEpoch = 0; - std::uint64_t packedValue = 0; - std::function()> release; - std::function stale; - [[nodiscard]] std::uint64_t packed() const noexcept { return packedValue; } -}; - -/** @brief Script-owned opaque handle for procgen objects with typed C++ refs. */ -struct ScriptProcgenHandle { - explicit ScriptProcgenHandle(ProcgenAnyHandle value) - : ownerEpoch(value.ownerEpoch), packed(value.packedValue), release(std::move(value.release)), - stale(std::move(value.stale)) {} - std::uint64_t ownerEpoch = 0; - std::uint64_t packed = 0; - std::function()> release; - std::function stale; - bool released = false; - - ~ScriptProcgenHandle() { - if (!released && release) release().ignore("script procgen proxy destructor release"); - } -}; - template eve::Result procgenBindingFailure(eve::DiagnosticCode code, std::string message, std::string path = {}); -std::mutex heightmapProxyMutex; -std::unordered_map heightmapProxyRefs; +template +struct NativeProxyReleases { + struct Record { + std::any reference; + std::function()> release; + }; + static inline std::mutex mutex; + static inline std::unordered_map records; +}; -SQInteger releaseHeightmapProxy(SQUserPointer pointer, SQInteger) { - auto* heightmap = static_cast(pointer); - if (!heightmap) return 0; +template +std::optional nativeProxyReference(T* native) { + if (!native) return std::nullopt; + std::lock_guard lock(NativeProxyReleases::mutex); + const auto found = NativeProxyReleases::records.find(native); + if (found == NativeProxyReleases::records.end()) return std::nullopt; + if (const auto* reference = std::any_cast(&found->second.reference)) return *reference; + return std::nullopt; +} - std::optional reference; +template +SQInteger releaseNativeProxy(SQUserPointer pointer, SQInteger) { + auto* native = static_cast(pointer); + if (!native) return 0; + + std::function()> release; { - std::lock_guard lock(heightmapProxyMutex); - const auto found = heightmapProxyRefs.find(heightmap); - if (found == heightmapProxyRefs.end()) return 0; - reference = found->second; - heightmapProxyRefs.erase(found); + std::lock_guard lock(NativeProxyReleases::mutex); + const auto found = NativeProxyReleases::records.find(native); + if (found == NativeProxyReleases::records.end()) return 0; + release = std::move(found->second.release); + NativeProxyReleases::records.erase(found); } - - if (auto* owner = ModuleManager::getInstance("Procgen"); owner && reference) - owner->releaseHeightmap(*reference).ignore("release Squirrel heightmap proxy"); + release().ignore("release Squirrel procgen native proxy"); return 0; } -ssq::Table makeOwnedHeightmapProxy(HSQUIRRELVM vm, eve::Result&& reference) { - if (!reference) - return eve::script::projectStatusResult(vm, reference.status(), false, false); +template +ssq::Table makeOwnedNativeProxy(HSQUIRRELVM vm, eve::Result&& reference, Resolve&& resolve, Release&& release) { + if (!reference) return eve::script::projectStatusResult(vm, reference.status(), false, false); - const auto ref = std::move(reference).takeValue(); - auto* module = ModuleManager::getInstance("Procgen"); - const auto view = module ? module->resolveHeightmap(ref) : eve::script::Borrowed(); + const auto ref = std::move(reference).takeValue(); + const auto view = std::invoke(std::forward(resolve), ref); if (!view.isBound()) { - if (module) module->releaseHeightmap(ref).ignore("rollback unbound Squirrel heightmap proxy"); + std::invoke(release, ref).ignore("rollback unbound Squirrel procgen proxy"); return eve::script::projectStatusResult( - vm, procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "heightmap proxy could not resolve its owned object", "heightmap") - .status(), + vm, + procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "procgen proxy could not resolve its owned object", "procgen") + .status(), false, false); } const SQInteger top = sq_gettop(vm); - sq_pushobject(vm, ssq::detail::getClassObj(vm, typeid(Heightmap*).hash_code())); + sq_pushobject(vm, ssq::detail::getClassObj(vm, typeid(T*).hash_code())); if (SQ_FAILED(sq_createinstance(vm, -1))) { sq_settop(vm, top); - module->releaseHeightmap(ref).ignore("rollback failed Squirrel heightmap instance"); + std::invoke(release, ref).ignore("rollback failed Squirrel procgen instance"); return eve::script::projectStatusResult( - vm, procgenBindingFailure(eve::DiagnosticCode::Failed, - "failed to create Squirrel heightmap proxy", "heightmap") - .status(), + vm, + procgenBindingFailure(eve::DiagnosticCode::Failed, "failed to create Squirrel procgen proxy", + "procgen") + .status(), false, false); } sq_remove(vm, -2); auto* native = view.get(); { - std::lock_guard lock(heightmapProxyMutex); - heightmapProxyRefs.emplace(native, ref); + std::lock_guard lock(NativeProxyReleases::mutex); + NativeProxyReleases::records.emplace( + native, + typename NativeProxyReleases::Record{ + ref, [ref, release = std::forward(release)]() mutable { return std::invoke(release, ref); }}); } sq_setinstanceup(vm, -1, native); - sq_settypetag(vm, -1, reinterpret_cast(typeid(Heightmap*).hash_code())); - sq_setreleasehook(vm, -1, &releaseHeightmapProxy); + sq_settypetag(vm, -1, reinterpret_cast(typeid(T*).hash_code())); + sq_setreleasehook(vm, -1, &releaseNativeProxy); ssq::Instance value(vm); sq_getstackobj(vm, -1, &value.getRaw()); sq_addref(vm, &value.getRaw()); sq_settop(vm, top); - auto result = eve::script::projectStatusResult( - vm, eve::Status::success(eve::StatusCode::Applied), true, false); + auto result = eve::script::projectStatusResult(vm, eve::Status::success(eve::StatusCode::Applied), true, false); result.set("value", value); result.set("ownership", std::string("owned")); result.set("ownerEpoch", static_cast(ref.ownerEpoch)); @@ -159,10 +155,48 @@ ssq::Table makeOwnedHeightmapProxy(HSQUIRRELVM vm, eve::Result -eve::Result procgenBindingFailure(eve::DiagnosticCode code, std::string message, - std::string path) { - return eve::Result::failure(eve::Diagnostic::error( - code, std::move(message), std::move(path), {}, "procgen.squirrel")); +ssq::Table projectBorrowedResult(HSQUIRRELVM vm, eve::script::Borrowed borrowed, const char* objectName) { + if (!borrowed.isBound()) + return eve::script::projectStatusResult( + vm, + procgenBindingFailure(eve::DiagnosticCode::Failed, std::string(objectName) + " could not be produced", + objectName) + .status(), + false, false); + auto result = eve::script::projectStatusResult(vm, eve::Status::success(eve::StatusCode::Applied), true, false); + result.set("value", borrowed.get()); + result.set("ownership", std::string("borrowed")); + return result; +} + +ssq::Table makeOwnedPointSetProxy(HSQUIRRELVM vm, eve::Result&& reference) { + auto* module = Procgen::create(); + return makeOwnedNativeProxy( + vm, std::move(reference), [module](ProcgenPointSetHandleRef ref) { return module->resolvePointSet(ref); }, + [](ProcgenPointSetHandleRef ref) { + auto* owner = ModuleManager::getInstance("Procgen"); + return owner ? owner->releasePointSet(ref) + : procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "Procgen module is no longer loaded", "pointSet"); + }); +} + +ssq::Table makeOwnedSpatialProxy(HSQUIRRELVM vm, eve::Result&& reference) { + auto* module = Procgen::create(); + return makeOwnedNativeProxy( + vm, std::move(reference), [module](ProcgenSpatialDataHandleRef ref) { return module->resolveSpatialData(ref); }, + [](ProcgenSpatialDataHandleRef ref) { + auto* owner = ModuleManager::getInstance("Procgen"); + return owner ? owner->release(ref) + : procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "Procgen module is no longer loaded", "spatialData"); + }); +} + +template +eve::Result procgenBindingFailure(eve::DiagnosticCode code, std::string message, std::string path) { + return eve::Result::failure( + eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "procgen.squirrel")); } eve::Value boundsValue(const Bounds& bounds) { @@ -180,8 +214,7 @@ eve::Value boundsValue(const Bounds& bounds) { eve::Value artifactProjection(GeneratedArtifact&& artifact) { eve::Value::Array dependencies; dependencies.reserve(artifact.dependencies.size()); - for (const ArtifactId& dependency : artifact.dependencies) - dependencies.emplace_back(dependency.format()); + for (const ArtifactId& dependency : artifact.dependencies) dependencies.emplace_back(dependency.format()); eve::Value::Object result; result.emplace("id", eve::Value(artifact.id.format())); @@ -193,8 +226,7 @@ eve::Value artifactProjection(GeneratedArtifact&& artifact) { result.emplace("metadata", eve::Value(std::move(artifact.metadata))); if (artifact.type == ArtifactType::Composite) { const auto* composite = std::get_if(&artifact.payload); - result.emplace("partCount", eve::Value(static_cast( - composite ? composite->children.size() : 0))); + result.emplace("partCount", eve::Value(static_cast(composite ? composite->children.size() : 0))); } else { result.emplace("partCount", eve::Value(std::int64_t(1))); } @@ -213,21 +245,17 @@ eve::Value publishReceiptProjection(ArtifactPublishReceipt&& receipt) { template ssq::Table makeOwnedProxy(HSQUIRRELVM vm, eve::Result&& reference, Release&& release) { - if (!reference) - return eve::script::projectStatusResult(vm, reference.status(), false, false); - const Ref ref = std::move(reference).takeValue(); - auto object = eve::script::makeOwnedSquirrelInstance( - vm, std::make_unique(ref)); + if (!reference) return eve::script::projectStatusResult(vm, reference.status(), false, false); + const Ref ref = std::move(reference).takeValue(); + auto object = eve::script::makeOwnedSquirrelInstance(vm, std::make_unique(ref)); if (!object) { const eve::Status status = object.status(); object.ignore("failed to create owned procgen proxy"); - std::invoke(std::forward(release), ref).ignore( - "rollback failed owned procgen allocation"); + std::invoke(std::forward(release), ref).ignore("rollback failed owned procgen allocation"); return eve::script::projectStatusResult(vm, status, false, false); } ssq::Object owned = std::move(object).takeValue(); - auto result = eve::script::projectStatusResult( - vm, eve::Status::success(eve::StatusCode::Applied), true, false); + auto result = eve::script::projectStatusResult(vm, eve::Status::success(eve::StatusCode::Applied), true, false); result.set("value", owned); result.set("ownership", std::string("owned")); result.set("ownerEpoch", static_cast(ref.ownerEpoch)); @@ -239,28 +267,7 @@ template ssq::Table staleProcgenResult(HSQUIRRELVM vm, const char* objectName) { return eve::script::projectResult( vm, procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - std::string("owned procgen ") + objectName + - " handle is stale", objectName)); -} - -template -ssq::Table makeAnyOwnedProxy(HSQUIRRELVM vm, eve::Result&& reference, - Release&& release, Stale&& stale) { - if (!reference) - return eve::script::projectStatusResult(vm, reference.status(), false, false); - const Ref ref = std::move(reference).takeValue(); - ProcgenAnyHandle any; - any.ownerEpoch = ref.ownerEpoch; - any.packedValue = ref.packed(); - any.release = [ref, release = std::forward(release)]() mutable { - return std::invoke(release, ref); - }; - any.stale = [ref, stale = std::forward(stale)]() mutable { - return std::invoke(stale, ref); - }; - return makeOwnedProxy( - vm, eve::Result::success(std::move(any)), - [](ProcgenAnyHandle) { return eve::Result::success(); }); + std::string("owned procgen ") + objectName + " handle is stale", objectName)); } /** @brief Projects one native recipe schema through the canonical Result table. @@ -269,14 +276,13 @@ ssq::Table makeAnyOwnedProxy(HSQUIRRELVM vm, eve::Result&& reference, * returned `value` field. A failed lookup never returns an empty schema * object, so callers cannot mistake a missing recipe for valid metadata. */ -ssq::Table projectRecipeDescriptorResult(HSQUIRRELVM vm, - eve::Result&& result) { - const bool ok = result.ok(); +ssq::Table projectRecipeDescriptorResult(HSQUIRRELVM vm, eve::Result&& result) { + const bool ok = result.ok(); const eve::Status status = result.status(); if (!ok) return eve::script::projectStatusResult(vm, status, false, false); auto descriptor = std::move(result).takeValue(); - auto object = eve::script::makeOwnedSquirrelInstance( + auto object = eve::script::makeOwnedSquirrelInstance( vm, std::make_unique(std::move(descriptor))); if (!object) { const eve::Status failure = object.status(); @@ -290,24 +296,24 @@ ssq::Table projectRecipeDescriptorResult(HSQUIRRELVM vm, } template -eve::Result> ownProcgenObject( - eve::script::RuntimeObjectRegistry& registry, eve::script::Owned object) { +eve::Result> ownProcgenObject(eve::script::RuntimeObjectRegistry& registry, + eve::script::Owned object) { return registry.emplace(std::move(object)); } } // namespace struct Procgen::OwnershipState { - eve::script::RuntimeObjectRegistry outputs; - eve::script::RuntimeObjectRegistry points; + eve::script::RuntimeObjectRegistry outputs; + eve::script::RuntimeObjectRegistry points; eve::script::RuntimeObjectRegistry samplers; - eve::script::RuntimeObjectRegistry heightmaps; - eve::script::RuntimeObjectRegistry clouds; - eve::script::RuntimeObjectRegistry shadows; - eve::script::RuntimeObjectRegistry pbr; - eve::script::RuntimeObjectRegistry meshes; - eve::script::RuntimeObjectRegistry images; - eve::script::RuntimeObjectRegistry normalImages; + eve::script::RuntimeObjectRegistry heightmaps; + eve::script::RuntimeObjectRegistry clouds; + eve::script::RuntimeObjectRegistry shadows; + eve::script::RuntimeObjectRegistry pbr; + eve::script::RuntimeObjectRegistry meshes; + eve::script::RuntimeObjectRegistry images; + eve::script::RuntimeObjectRegistry normalImages; }; Module_IMPL(Procgen, new Procgen()); @@ -346,8 +352,7 @@ eve::Result Procgen::newParamsHandle() { return module->params_.emplace(std::make_unique()); } -eve::script::Borrowed Procgen::resolve( - ProcgenParamsHandleRef reference) noexcept { +eve::script::Borrowed Procgen::resolve(ProcgenParamsHandleRef reference) noexcept { Procgen* module = ModuleManager::getInstance("Procgen"); if (!module) return {}; return module->params_.resolve(reference); @@ -356,8 +361,8 @@ eve::script::Borrowed Procgen::resolve( eve::Result Procgen::release(ProcgenParamsHandleRef reference) { Procgen* module = ModuleManager::getInstance("Procgen"); if (!module) - return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "Procgen module is no longer loaded", "params"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "Procgen module is no longer loaded", + "params"); return module->params_.erase(reference); } @@ -372,18 +377,16 @@ eve::Result Procgen::newOutputHandle() { return ownProcgenObject(module->ownership_->outputs, std::make_unique()); } -eve::script::Borrowed Procgen::resolveOutput( - ProcgenOutputHandleRef reference) noexcept { +eve::script::Borrowed Procgen::resolveOutput(ProcgenOutputHandleRef reference) noexcept { Procgen* module = ModuleManager::getInstance("Procgen"); - return module ? module->ownership_->outputs.resolve(reference) - : eve::script::Borrowed(); + return module ? module->ownership_->outputs.resolve(reference) : eve::script::Borrowed(); } eve::Result Procgen::releaseOutput(ProcgenOutputHandleRef reference) { Procgen* module = ModuleManager::getInstance("Procgen"); if (!module) - return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "Procgen module is no longer loaded", "output"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "Procgen module is no longer loaded", + "output"); return module->ownership_->outputs.erase(reference); } @@ -393,17 +396,15 @@ bool Procgen::isOutputStale(ProcgenOutputHandleRef reference) const noexcept { eve::Result Procgen::newGridHandle(int width, int height) { if (width <= 0 || height <= 0) - return procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "procedural grid dimensions must be positive", "grid"); + return procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "procedural grid dimensions must be positive", "grid"); auto grid = std::make_unique(); grid->resize(width, height); Procgen* module = Procgen::create(); return module->grids_.emplace(std::move(grid)); } -eve::script::Borrowed Procgen::resolve( - ProcgenGridHandleRef reference) noexcept { +eve::script::Borrowed Procgen::resolve(ProcgenGridHandleRef reference) noexcept { Procgen* module = ModuleManager::getInstance("Procgen"); if (!module) return {}; return module->grids_.resolve(reference); @@ -412,8 +413,8 @@ eve::script::Borrowed Procgen::resolve( eve::Result Procgen::release(ProcgenGridHandleRef reference) { Procgen* module = ModuleManager::getInstance("Procgen"); if (!module) - return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "Procgen module is no longer loaded", "grid"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "Procgen module is no longer loaded", + "grid"); return module->grids_.erase(reference); } @@ -427,8 +428,7 @@ eve::Result Procgen::newPointSetHandle() { return ownProcgenObject(ownership_->points, std::make_unique()); } -eve::script::Borrowed Procgen::resolvePointSet( - ProcgenPointSetHandleRef reference) noexcept { +eve::script::Borrowed Procgen::resolvePointSet(ProcgenPointSetHandleRef reference) noexcept { return ownership_->points.resolve(reference); } @@ -440,28 +440,27 @@ bool Procgen::isPointSetStale(ProcgenPointSetHandleRef reference) const noexcept return reference.isValid() && ownership_->points.isStale(reference); } -eve::Result Procgen::sampleGridHandle( - int width, int depth, float spacing, uint32_t seed, float jitter) { +eve::Result Procgen::sampleGridHandle(int width, int depth, float spacing, uint32_t seed, + float jitter) { if (width <= 0 || depth <= 0 || spacing <= 0.f) return procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "sampleGrid requires positive dimensions and spacing", "pointSet"); + eve::DiagnosticCode::InvalidArgument, "sampleGrid requires positive dimensions and spacing", "pointSet"); return ownProcgenObject(ownership_->points, std::make_unique(sampleGridPoints(width, depth, spacing, seed, jitter))); } -eve::Result Procgen::filterHeightHandle( - ProcgenPointSetHandleRef input, float minHeight, float maxHeight) { +eve::Result Procgen::filterHeightHandle(ProcgenPointSetHandleRef input, float minHeight, + float maxHeight) { auto view = resolvePointSet(input); if (!view.isBound()) - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "filterHeight input point-set handle is stale", "input"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "filterHeight input point-set handle is stale", "input"); return ownProcgenObject(ownership_->points, std::make_unique(filterPointHeight(*view, minHeight, maxHeight))); } -eve::Result Procgen::filterDensityHandle( - ProcgenPointSetHandleRef input, float minDensity, float maxDensity) { +eve::Result Procgen::filterDensityHandle(ProcgenPointSetHandleRef input, float minDensity, + float maxDensity) { auto view = resolvePointSet(input); if (!view.isBound()) return procgenBindingFailure( @@ -470,52 +469,52 @@ eve::Result Procgen::filterDensityHandle( std::make_unique(filterPointDensity(*view, minDensity, maxDensity))); } -eve::Result Procgen::filterBoxHandle( - ProcgenPointSetHandleRef input, float minX, float minY, float minZ, float maxX, - float maxY, float maxZ, bool invert) { +eve::Result Procgen::filterBoxHandle(ProcgenPointSetHandleRef input, float minX, float minY, + float minZ, float maxX, float maxY, float maxZ, + bool invert) { auto view = resolvePointSet(input); if (!view.isBound()) - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "filterBox input point-set handle is stale", "input"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "filterBox input point-set handle is stale", "input"); return ownProcgenObject(ownership_->points, std::make_unique( filterPointBox(*view, minX, minY, minZ, maxX, maxY, maxZ, invert))); } -eve::Result Procgen::filterSlopeHandle( - ProcgenPointSetHandleRef input, float minDegrees, float maxDegrees) { +eve::Result Procgen::filterSlopeHandle(ProcgenPointSetHandleRef input, float minDegrees, + float maxDegrees) { auto view = resolvePointSet(input); if (!view.isBound()) - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "filterSlope input point-set handle is stale", "input"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "filterSlope input point-set handle is stale", "input"); return ownProcgenObject(ownership_->points, std::make_unique(filterPointSlope(*view, minDegrees, maxDegrees))); } -eve::Result Procgen::filterPolygonHandle( - ProcgenPointSetHandleRef input, ProcgenPointSetHandleRef polygon, bool invert) { +eve::Result Procgen::filterPolygonHandle(ProcgenPointSetHandleRef input, + ProcgenPointSetHandleRef polygon, bool invert) { auto source = resolvePointSet(input); - auto shape = resolvePointSet(polygon); + auto shape = resolvePointSet(polygon); if (!source.isBound() || !shape.isBound()) - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "filterPolygon input handle is stale", "input"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "filterPolygon input handle is stale", "input"); return ownProcgenObject(ownership_->points, std::make_unique(filterPointsByPolygon(*source, *shape, invert))); } -eve::Result Procgen::filterSplineDistanceHandle( - ProcgenPointSetHandleRef input, ProcgenPointSetHandleRef controlPoints, - float minDistance, float maxDistance) { - auto source = resolvePointSet(input); +eve::Result Procgen::filterSplineDistanceHandle(ProcgenPointSetHandleRef input, + ProcgenPointSetHandleRef controlPoints, + float minDistance, float maxDistance) { + auto source = resolvePointSet(input); auto control = resolvePointSet(controlPoints); if (!source.isBound() || !control.isBound()) - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "filterSplineDistance input handle is stale", "input"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "filterSplineDistance input handle is stale", "input"); return ownProcgenObject(ownership_->points, std::make_unique(filterPointsBySplineDistance( *source, *control, minDistance, maxDistance))); } -eve::Result Procgen::excludeRadiusHandle( - ProcgenPointSetHandleRef input, float x, float z, float radius) { +eve::Result Procgen::excludeRadiusHandle(ProcgenPointSetHandleRef input, float x, float z, + float radius) { auto view = resolvePointSet(input); if (!view.isBound()) return procgenBindingFailure( @@ -523,33 +522,33 @@ eve::Result Procgen::excludeRadiusHandle( return ownProcgenObject(ownership_->points, std::make_unique(excludePointRadius(*view, x, z, radius))); } -eve::Result Procgen::jitterPointsHandle( - ProcgenPointSetHandleRef input, uint32_t seed, float amountX, float amountZ) { +eve::Result Procgen::jitterPointsHandle(ProcgenPointSetHandleRef input, uint32_t seed, + float amountX, float amountZ) { auto view = resolvePointSet(input); if (!view.isBound()) - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "jitterPoints input point-set handle is stale", "input"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "jitterPoints input point-set handle is stale", "input"); return ownProcgenObject(ownership_->points, std::make_unique(jitterPointPositions(*view, seed, amountX, amountZ))); } -eve::Result Procgen::selfPruneHandle( - ProcgenPointSetHandleRef input, float radius) { +eve::Result Procgen::selfPruneHandle(ProcgenPointSetHandleRef input, float radius) { auto view = resolvePointSet(input); if (!view.isBound()) - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "selfPrune input point-set handle is stale", "input"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "selfPrune input point-set handle is stale", "input"); return ownProcgenObject(ownership_->points, std::make_unique(selfPrunePoints(*view, radius))); } -eve::Result Procgen::projectToHeightmapHandle( - ProcgenPointSetHandleRef input, ProcgenHeightmapHandleRef heightmap, float originX, - float originZ, float cellSize, float heightScale) { +eve::Result Procgen::projectToHeightmapHandle(ProcgenPointSetHandleRef input, + ProcgenHeightmapHandleRef heightmap, + float originX, float originZ, float cellSize, + float heightScale) { auto points = resolvePointSet(input); - auto map = resolveHeightmap(heightmap); + auto map = resolveHeightmap(heightmap); if (!points.isBound() || !map.isBound()) - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "projectToHeightmap input handle is stale", "input"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "projectToHeightmap input handle is stale", "input"); if (cellSize <= 0.f) return procgenBindingFailure( eve::DiagnosticCode::InvalidArgument, "projectToHeightmap cellSize must be positive", "cellSize"); @@ -557,44 +556,45 @@ eve::Result Procgen::projectToHeightmapHandle( *points, *map, originX, originZ, cellSize, heightScale))); } -eve::Result Procgen::sampleSplineHandle( - ProcgenPointSetHandleRef controlPoints, float spacing, uint32_t seed, float lateralJitter) { +eve::Result Procgen::sampleSplineHandle(ProcgenPointSetHandleRef controlPoints, float spacing, + uint32_t seed, float lateralJitter) { auto control = resolvePointSet(controlPoints); if (!control.isBound()) return procgenBindingFailure( eve::DiagnosticCode::StaleHandle, "sampleSpline control point-set handle is stale", "controlPoints"); if (spacing <= 0.f) - return procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, "sampleSpline spacing must be positive", "spacing"); + return procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "sampleSpline spacing must be positive", "spacing"); return ownProcgenObject(ownership_->points, std::make_unique(samplePolylinePoints(*control, spacing, seed, lateralJitter))); } -eve::Result Procgen::mergePointsHandle( - ProcgenPointSetHandleRef first, ProcgenPointSetHandleRef second) { +eve::Result Procgen::mergePointsHandle(ProcgenPointSetHandleRef first, + ProcgenPointSetHandleRef second) { auto a = resolvePointSet(first); auto b = resolvePointSet(second); if (!a.isBound() || !b.isBound()) - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "mergePoints input handle is stale", "points"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "mergePoints input handle is stale", "points"); return ownProcgenObject(ownership_->points, std::make_unique(mergePointSets(*a, *b))); } -eve::Result Procgen::transformPointsHandle( - ProcgenPointSetHandleRef input, float translateX, float translateY, float translateZ, - float yawDegrees, float scaleX, float scaleY, float scaleZ) { +eve::Result Procgen::transformPointsHandle(ProcgenPointSetHandleRef input, float translateX, + float translateY, float translateZ, + float yawDegrees, float scaleX, float scaleY, + float scaleZ) { auto view = resolvePointSet(input); if (!view.isBound()) - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "transformPoints input handle is stale", "input"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "transformPoints input handle is stale", "input"); return ownProcgenObject(ownership_->points, std::make_unique(transformPointSet(*view, translateX, translateY, translateZ, yawDegrees, scaleX, scaleY, scaleZ))); } -eve::Result Procgen::filterFloatAttributeHandle( - ProcgenPointSetHandleRef input, const std::string& name, float minValue, float maxValue, - bool invert) { +eve::Result Procgen::filterFloatAttributeHandle(ProcgenPointSetHandleRef input, + const std::string& name, float minValue, + float maxValue, bool invert) { auto view = resolvePointSet(input); if (!view.isBound() || name.empty()) return procgenBindingFailure( @@ -604,9 +604,9 @@ eve::Result Procgen::filterFloatAttributeHandle( *view, name, minValue, maxValue, invert))); } -eve::Result Procgen::filterStringAttributeHandle( - ProcgenPointSetHandleRef input, const std::string& name, const std::string& value, - bool invert) { +eve::Result Procgen::filterStringAttributeHandle(ProcgenPointSetHandleRef input, + const std::string& name, + const std::string& value, bool invert) { auto view = resolvePointSet(input); if (!view.isBound() || name.empty()) return procgenBindingFailure( @@ -616,17 +616,17 @@ eve::Result Procgen::filterStringAttributeHandle( std::make_unique(filterPointStringAttribute(*view, name, value, invert))); } -eve::Result Procgen::densityCullHandle( - ProcgenPointSetHandleRef input, uint32_t seed, float multiplier) { +eve::Result Procgen::densityCullHandle(ProcgenPointSetHandleRef input, uint32_t seed, + float multiplier) { auto view = resolvePointSet(input); if (!view.isBound()) - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "densityCull input handle is stale", "input"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "densityCull input handle is stale", "input"); return ownProcgenObject(ownership_->points, std::make_unique(densityCullPoints(*view, seed, multiplier))); } -eve::Result Procgen::poissonDiskHandle( - int width, int depth, float radius, uint32_t seed, int maxPoints) { +eve::Result Procgen::poissonDiskHandle(int width, int depth, float radius, uint32_t seed, + int maxPoints) { if (width < 0 || depth < 0 || radius <= 0.f || maxPoints < 0) return procgenBindingFailure( eve::DiagnosticCode::InvalidArgument, @@ -635,9 +635,8 @@ eve::Result Procgen::poissonDiskHandle( std::make_unique(poissonDiskPoints(width, depth, radius, seed, maxPoints))); } -eve::Result Procgen::publishInstances( - const std::string& batchId, ProcgenPointSetHandleRef points, - const std::string& assetAttribute, const std::string& defaultAsset) { +eve::Result Procgen::publishInstances(const std::string& batchId, ProcgenPointSetHandleRef points, + const std::string& assetAttribute, const std::string& defaultAsset) { const auto view = resolvePointSet(points); if (batchId.empty() || !view.isBound()) return procgenBindingFailure( @@ -645,8 +644,7 @@ eve::Result Procgen::publishInstances( "publishInstances requires a batch id and a live point-set handle"); auto* sink = eve::cap::query(); if (!sink) - return procgenBindingFailure(eve::DiagnosticCode::Failed, - "publishInstances scene sink is unavailable"); + return procgenBindingFailure(eve::DiagnosticCode::Failed, "publishInstances scene sink is unavailable"); std::vector instances; instances.reserve(view->points().size()); @@ -668,10 +666,10 @@ eve::Result Procgen::publishInstances( const auto found = point.stringAttributes.find(assetAttribute); if (found != point.stringAttributes.end()) instance.asset = found->second; } - instance.x = point.x; - instance.y = point.y; - instance.z = point.z; - instance.yaw = point.yaw; + instance.x = point.x; + instance.y = point.y; + instance.z = point.z; + instance.yaw = point.yaw; instance.scaleX = point.scaleX; instance.scaleY = point.scaleY; instance.scaleZ = point.scaleZ; @@ -679,29 +677,24 @@ eve::Result Procgen::publishInstances( instances.push_back(std::move(instance)); } if (!sink->applyBatch(batchId, instances)) - return procgenBindingFailure(eve::DiagnosticCode::Failed, - "publishInstances scene sink rejected batch"); + return procgenBindingFailure(eve::DiagnosticCode::Failed, "publishInstances scene sink rejected batch"); return eve::Result::success(); } eve::Result Procgen::removeInstances(const std::string& batchId) { auto* sink = eve::cap::query(); if (batchId.empty()) - return procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, - "removeInstances requires a batch id"); + return procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, "removeInstances requires a batch id"); if (!sink) - return procgenBindingFailure(eve::DiagnosticCode::Failed, - "removeInstances scene sink is unavailable"); + return procgenBindingFailure(eve::DiagnosticCode::Failed, "removeInstances scene sink is unavailable"); if (!sink->removeBatch(batchId)) - return procgenBindingFailure(eve::DiagnosticCode::Failed, - "removeInstances scene sink rejected batch"); + return procgenBindingFailure(eve::DiagnosticCode::Failed, "removeInstances scene sink rejected batch"); return eve::Result::success(); } -eve::Result Procgen::publishCellInstances( - const std::string& prefix, const ProcgenCellRequest& request, - ProcgenPointSetHandleRef points, const std::string& assetAttribute, - const std::string& defaultAsset) { +eve::Result Procgen::publishCellInstances(const std::string& prefix, const ProcgenCellRequest& request, + ProcgenPointSetHandleRef points, const std::string& assetAttribute, + const std::string& defaultAsset) { if (prefix.empty()) return procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, "publishCellInstances requires a prefix"); @@ -710,8 +703,7 @@ eve::Result Procgen::publishCellInstances( return publishInstances(batchId, points, assetAttribute, defaultAsset); } -eve::Result Procgen::removeCellInstances( - const std::string& prefix, const ProcgenCellRequest& request) { +eve::Result Procgen::removeCellInstances(const std::string& prefix, const ProcgenCellRequest& request) { if (prefix.empty()) return procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, "removeCellInstances requires a prefix"); @@ -740,125 +732,117 @@ int Procgen::getPublishedRemovedCount(const std::string& batchId) const { return sink && !batchId.empty() ? sink->lastRemovedCount(batchId) : 0; } -eve::Result Procgen::pointDataHandle( - ProcgenPointSetHandleRef points) { +eve::Result Procgen::pointDataHandle(ProcgenPointSetHandleRef points) { auto view = resolvePointSet(points); if (!view.isBound()) - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "pointData point-set handle is stale", "points"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "pointData point-set handle is stale", "points"); return spatialData_.emplace(std::make_unique(SpatialData::fromPoints(*view))); } -eve::Result Procgen::boxVolumeHandle( - float minX, float minY, float minZ, float maxX, float maxY, float maxZ) { - return spatialData_.emplace(std::make_unique( - SpatialData::box(minX, minY, minZ, maxX, maxY, maxZ))); +eve::Result Procgen::boxVolumeHandle(float minX, float minY, float minZ, float maxX, + float maxY, float maxZ) { + return spatialData_.emplace(std::make_unique(SpatialData::box(minX, minY, minZ, maxX, maxY, maxZ))); } -eve::Result Procgen::sphereVolumeHandle( - float x, float y, float z, float radius) { +eve::Result Procgen::sphereVolumeHandle(float x, float y, float z, float radius) { if (radius <= 0.f) - return procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, "sphereVolume radius must be positive", "radius"); - return spatialData_.emplace( - std::make_unique(SpatialData::sphere(x, y, z, radius))); + return procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "sphereVolume radius must be positive", "radius"); + return spatialData_.emplace(std::make_unique(SpatialData::sphere(x, y, z, radius))); } -eve::Result Procgen::splineDataHandle( - ProcgenPointSetHandleRef controlPoints, float radius) { +eve::Result Procgen::splineDataHandle(ProcgenPointSetHandleRef controlPoints, + float radius) { auto view = resolvePointSet(controlPoints); if (!view.isBound() || view->getCount() < 2 || radius < 0.f) return procgenBindingFailure( !view.isBound() ? eve::DiagnosticCode::StaleHandle : eve::DiagnosticCode::InvalidArgument, - "splineData requires a live set with at least two points and non-negative radius", - "controlPoints"); - return spatialData_.emplace( - std::make_unique(SpatialData::spline(*view, radius))); + "splineData requires a live set with at least two points and non-negative radius", "controlPoints"); + return spatialData_.emplace(std::make_unique(SpatialData::spline(*view, radius))); } -eve::Result Procgen::heightfieldDataHandle( - ProcgenHeightmapHandleRef heightmap, float originX, float originZ, float cellSize, - float heightScale) { +eve::Result Procgen::heightfieldDataHandle(ProcgenHeightmapHandleRef heightmap, + float originX, float originZ, float cellSize, + float heightScale) { auto view = resolveHeightmap(heightmap); if (!view.isBound() || view->getWidth() <= 0 || view->getHeight() <= 0 || cellSize <= 0.f) return procgenBindingFailure( !view.isBound() ? eve::DiagnosticCode::StaleHandle : eve::DiagnosticCode::InvalidArgument, "heightfieldData requires a live non-empty heightmap and positive cell size", "heightmap"); - return spatialData_.emplace(std::make_unique( - SpatialData::heightfield(*view, originX, originZ, cellSize, heightScale))); + return spatialData_.emplace( + std::make_unique(SpatialData::heightfield(*view, originX, originZ, cellSize, heightScale))); } -eve::Result Procgen::unionSpatialHandle( - ProcgenSpatialDataHandleRef left, ProcgenSpatialDataHandleRef right) { - auto a = resolveSpatialData(left); auto b = resolveSpatialData(right); +eve::Result Procgen::unionSpatialHandle(ProcgenSpatialDataHandleRef left, + ProcgenSpatialDataHandleRef right) { + auto a = resolveSpatialData(left); + auto b = resolveSpatialData(right); if (!a.isBound() || !b.isBound()) - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "unionSpatial input handle is stale", "spatial"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "unionSpatial input handle is stale", "spatial"); return spatialData_.emplace(std::make_unique(SpatialData::unite(*a, *b))); } -eve::Result Procgen::intersectSpatialHandle( - ProcgenSpatialDataHandleRef left, ProcgenSpatialDataHandleRef right) { - auto a = resolveSpatialData(left); auto b = resolveSpatialData(right); +eve::Result Procgen::intersectSpatialHandle(ProcgenSpatialDataHandleRef left, + ProcgenSpatialDataHandleRef right) { + auto a = resolveSpatialData(left); + auto b = resolveSpatialData(right); if (!a.isBound() || !b.isBound()) - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "intersectSpatial input handle is stale", "spatial"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "intersectSpatial input handle is stale", "spatial"); return spatialData_.emplace(std::make_unique(SpatialData::intersect(*a, *b))); } -eve::Result Procgen::differenceSpatialHandle( - ProcgenSpatialDataHandleRef left, ProcgenSpatialDataHandleRef right) { - auto a = resolveSpatialData(left); auto b = resolveSpatialData(right); +eve::Result Procgen::differenceSpatialHandle(ProcgenSpatialDataHandleRef left, + ProcgenSpatialDataHandleRef right) { + auto a = resolveSpatialData(left); + auto b = resolveSpatialData(right); if (!a.isBound() || !b.isBound()) - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "differenceSpatial input handle is stale", "spatial"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "differenceSpatial input handle is stale", "spatial"); return spatialData_.emplace(std::make_unique(SpatialData::subtract(*a, *b))); } -eve::Result Procgen::sampleSpatialHandle( - ProcgenSpatialDataHandleRef spatial, float spacing, uint32_t seed, float jitter) { +eve::Result Procgen::sampleSpatialHandle(ProcgenSpatialDataHandleRef spatial, float spacing, + uint32_t seed, float jitter) { auto view = resolveSpatialData(spatial); if (!view.isBound() || spacing <= 0.f) return procgenBindingFailure( !view.isBound() ? eve::DiagnosticCode::StaleHandle : eve::DiagnosticCode::InvalidArgument, "sampleSpatial requires live spatial data and positive spacing", "spatial"); - return ownership_->points.emplace( - std::make_unique(view->sample(spacing, seed, jitter))); + return ownership_->points.emplace(std::make_unique(view->sample(spacing, seed, jitter))); } -eve::Result Procgen::filterSpatialHandle( - ProcgenPointSetHandleRef input, ProcgenSpatialDataHandleRef spatial, bool invert) { - auto points = resolvePointSet(input); auto domain = resolveSpatialData(spatial); +eve::Result Procgen::filterSpatialHandle(ProcgenPointSetHandleRef input, + ProcgenSpatialDataHandleRef spatial, bool invert) { + auto points = resolvePointSet(input); + auto domain = resolveSpatialData(spatial); if (!points.isBound() || !domain.isBound()) - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "filterSpatial input handle is stale", "input"); - return ownership_->points.emplace( - std::make_unique(domain->filter(*points, invert))); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "filterSpatial input handle is stale", "input"); + return ownership_->points.emplace(std::make_unique(domain->filter(*points, invert))); } -eve::Result Procgen::projectToSpatialHandle( - ProcgenPointSetHandleRef input, ProcgenSpatialDataHandleRef spatial) { - auto points = resolvePointSet(input); auto domain = resolveSpatialData(spatial); +eve::Result Procgen::projectToSpatialHandle(ProcgenPointSetHandleRef input, + ProcgenSpatialDataHandleRef spatial) { + auto points = resolvePointSet(input); + auto domain = resolveSpatialData(spatial); if (!points.isBound() || !domain.isBound()) - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "projectToSpatial input handle is stale", "input"); - return ownership_->points.emplace( - std::make_unique(domain->project(*points))); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "projectToSpatial input handle is stale", "input"); + return ownership_->points.emplace(std::make_unique(domain->project(*points))); } -eve::script::Borrowed Procgen::resolveSpatialData( - ProcgenSpatialDataHandleRef reference) noexcept { return spatialData_.resolve(reference); } - -eve::Result Procgen::release(ProcgenSpatialDataHandleRef reference) { - return spatialData_.erase(reference); +eve::script::Borrowed Procgen::resolveSpatialData(ProcgenSpatialDataHandleRef reference) noexcept { + return spatialData_.resolve(reference); } -bool Procgen::isStale(ProcgenSpatialDataHandleRef reference) const noexcept { - return spatialData_.isStale(reference); -} +eve::Result Procgen::release(ProcgenSpatialDataHandleRef reference) { return spatialData_.erase(reference); } -eve::Result Procgen::newRuntimeGenerationHandle( - uint32_t worldSeed) { +bool Procgen::isStale(ProcgenSpatialDataHandleRef reference) const noexcept { return spatialData_.isStale(reference); } + +eve::Result Procgen::newRuntimeGenerationHandle(uint32_t worldSeed) { return runtimeGenerations_.emplace(std::make_unique(worldSeed)); } @@ -871,83 +855,72 @@ eve::script::Borrowed Procgen::resolveRuntimeGeneration( return runtimeGenerations_.resolve(reference); } -eve::script::Borrowed Procgen::resolvePointGraph( - ProcgenPointGraphHandleRef reference) noexcept { return pointGraphs_.resolve(reference); } +eve::script::Borrowed Procgen::resolvePointGraph(ProcgenPointGraphHandleRef reference) noexcept { + return pointGraphs_.resolve(reference); +} -eve::script::Borrowed Procgen::resolveBiomeRules( - ProcgenBiomeRulesHandleRef reference) noexcept { return biomeRules_.resolve(reference); } +eve::script::Borrowed Procgen::resolveBiomeRules(ProcgenBiomeRulesHandleRef reference) noexcept { + return biomeRules_.resolve(reference); +} -eve::script::Borrowed Procgen::resolveShapeGrammar( - ProcgenShapeGrammarHandleRef reference) noexcept { return shapeGrammars_.resolve(reference); } +eve::script::Borrowed Procgen::resolveShapeGrammar(ProcgenShapeGrammarHandleRef reference) noexcept { + return shapeGrammars_.resolve(reference); +} -eve::script::Borrowed Procgen::resolveLSystem( - ProcgenLSystemHandleRef reference) noexcept { return lsystems_.resolve(reference); } +eve::script::Borrowed Procgen::resolveLSystem(ProcgenLSystemHandleRef reference) noexcept { + return lsystems_.resolve(reference); +} eve::Result Procgen::release(ProcgenRuntimeGenerationHandleRef reference) { return runtimeGenerations_.erase(reference); } -eve::Result Procgen::release(ProcgenPointGraphHandleRef reference) { - return pointGraphs_.erase(reference); -} -eve::Result Procgen::release(ProcgenBiomeRulesHandleRef reference) { - return biomeRules_.erase(reference); -} -eve::Result Procgen::release(ProcgenShapeGrammarHandleRef reference) { - return shapeGrammars_.erase(reference); -} -eve::Result Procgen::release(ProcgenLSystemHandleRef reference) { - return lsystems_.erase(reference); -} +eve::Result Procgen::release(ProcgenPointGraphHandleRef reference) { return pointGraphs_.erase(reference); } +eve::Result Procgen::release(ProcgenBiomeRulesHandleRef reference) { return biomeRules_.erase(reference); } +eve::Result Procgen::release(ProcgenShapeGrammarHandleRef reference) { return shapeGrammars_.erase(reference); } +eve::Result Procgen::release(ProcgenLSystemHandleRef reference) { return lsystems_.erase(reference); } bool Procgen::isStale(ProcgenRuntimeGenerationHandleRef reference) const noexcept { return runtimeGenerations_.isStale(reference); } -bool Procgen::isStale(ProcgenPointGraphHandleRef reference) const noexcept { - return pointGraphs_.isStale(reference); -} -bool Procgen::isStale(ProcgenBiomeRulesHandleRef reference) const noexcept { - return biomeRules_.isStale(reference); -} +bool Procgen::isStale(ProcgenPointGraphHandleRef reference) const noexcept { return pointGraphs_.isStale(reference); } +bool Procgen::isStale(ProcgenBiomeRulesHandleRef reference) const noexcept { return biomeRules_.isStale(reference); } bool Procgen::isStale(ProcgenShapeGrammarHandleRef reference) const noexcept { return shapeGrammars_.isStale(reference); } -bool Procgen::isStale(ProcgenLSystemHandleRef reference) const noexcept { - return lsystems_.isStale(reference); -} +bool Procgen::isStale(ProcgenLSystemHandleRef reference) const noexcept { return lsystems_.isStale(reference); } uint32_t Procgen::deriveSeed(uint32_t parent, const std::string& scope) const { return eve::procgen::deriveSeed(parent, scope); } -eve::Result Procgen::beginSystemHandle( - const std::string& name, uint32_t seed) { +eve::Result Procgen::beginSystemHandle(const std::string& name, uint32_t seed) { Procgen* module = Procgen::create(); module->lastError_.clear(); if (name.empty()) { module->lastError_ = "beginSystem: name is empty"; - return procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, module->lastError_, "context"); + return procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, module->lastError_, + "context"); } - auto context = std::make_unique(name, seed); + auto context = std::make_unique(name, seed); const auto previous = module->systems_.find(name); if (previous != module->systems_.end()) context->stageCache_ = previous->second.stageCache; return module->contexts_.emplace(std::move(context)); } -eve::Result Procgen::beginCachedSystemHandle( - const std::string& name, uint32_t seed, const std::string& buildKey) { +eve::Result Procgen::beginCachedSystemHandle(const std::string& name, uint32_t seed, + const std::string& buildKey) { Procgen* module = Procgen::create(); module->lastError_.clear(); if (name.empty()) { module->lastError_ = "beginCachedSystem: name is empty"; - return procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, module->lastError_, "context"); + return procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, module->lastError_, + "context"); } if (buildKey.empty()) { module->lastError_ = "beginCachedSystem: build key is empty"; - return procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, module->lastError_, "buildKey"); + return procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, module->lastError_, + "buildKey"); } const uint32_t normalizedSeed = seed ? seed : 1u; const auto found = module->systems_.find(name); @@ -958,8 +931,7 @@ eve::Result Procgen::beginCachedSystemHandle( return module->contexts_.emplace(std::move(context)); } -eve::script::Borrowed Procgen::resolve( - ProcgenContextHandleRef reference) noexcept { +eve::script::Borrowed Procgen::resolve(ProcgenContextHandleRef reference) noexcept { Procgen* module = ModuleManager::getInstance("Procgen"); if (!module) return {}; return module->contexts_.resolve(reference); @@ -968,8 +940,8 @@ eve::script::Borrowed Procgen::resolve( eve::Result Procgen::release(ProcgenContextHandleRef reference) { Procgen* module = ModuleManager::getInstance("Procgen"); if (!module) - return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "Procgen module is no longer loaded", "context"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "Procgen module is no longer loaded", + "context"); return module->contexts_.erase(reference); } @@ -982,8 +954,8 @@ bool Procgen::isStale(ProcgenContextHandleRef reference) noexcept { eve::Result Procgen::commitSystem(ProcgenContextHandleRef reference) { auto view = Procgen::resolve(reference); if (!view.isBound()) - return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "commitSystem context handle is stale", "context"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "commitSystem context handle is stale", + "context"); ProcgenContext* context = view.get(); if (!context->isActive()) return procgenBindingFailure(eve::DiagnosticCode::PreconditionViolation, @@ -991,8 +963,7 @@ eve::Result Procgen::commitSystem(ProcgenContextHandleRef reference) { if (context->hasFailed()) { const std::string error = context->getError(); context->close(); - return procgenBindingFailure(eve::DiagnosticCode::Failed, - "commitSystem: " + error, "context"); + return procgenBindingFailure(eve::DiagnosticCode::Failed, "commitSystem: " + error, "context"); } if (!context->openTraces_.empty()) { const std::string trace = context->openTraces_.back().name; @@ -1022,8 +993,8 @@ eve::Result Procgen::commitSystem(ProcgenContextHandleRef reference) { eve::Result Procgen::abortSystem(ProcgenContextHandleRef reference) { auto view = Procgen::resolve(reference); if (!view.isBound()) - return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "abortSystem context handle is stale", "context"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "abortSystem context handle is stale", + "context"); view->abort(); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } @@ -1031,8 +1002,7 @@ eve::Result Procgen::abortSystem(ProcgenContextHandleRef reference) { eve::Result Procgen::removeSystem(const std::string& name) { previousSystems_.erase(name); if (systems_.erase(name) == 0) - return procgenBindingFailure(eve::DiagnosticCode::NotFound, - "procgen system was not committed", "system"); + return procgenBindingFailure(eve::DiagnosticCode::NotFound, "procgen system was not committed", "system"); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } @@ -1079,42 +1049,42 @@ std::string Procgen::getSystemDebugStageName(const std::string& name, int index) return found->second.debugStageOrder[size_t(index)]; } -eve::Result Procgen::getSystemOutputHandle( - const std::string& name, const std::string& outputName) const { +eve::Result Procgen::getSystemOutputHandle(const std::string& name, + const std::string& outputName) const { const auto system = systems_.find(name); if (system == systems_.end()) - return procgenBindingFailure( - eve::DiagnosticCode::NotFound, "procgen system is not committed", "system"); + return procgenBindingFailure(eve::DiagnosticCode::NotFound, + "procgen system is not committed", "system"); const auto output = system->second.outputs.find(outputName); if (output == system->second.outputs.end()) - return procgenBindingFailure( - eve::DiagnosticCode::NotFound, "procgen system output was not found", "output"); + return procgenBindingFailure(eve::DiagnosticCode::NotFound, + "procgen system output was not found", "output"); return ownProcgenObject(ownership_->points, std::make_unique(output->second)); } -eve::Result Procgen::getSystemDebugStageHandle( - const std::string& name, const std::string& stageName) const { +eve::Result Procgen::getSystemDebugStageHandle(const std::string& name, + const std::string& stageName) const { const auto system = systems_.find(name); if (system == systems_.end()) - return procgenBindingFailure( - eve::DiagnosticCode::NotFound, "procgen system is not committed", "system"); + return procgenBindingFailure(eve::DiagnosticCode::NotFound, + "procgen system is not committed", "system"); const auto stage = system->second.debugStages.find(stageName); if (stage == system->second.debugStages.end()) - return procgenBindingFailure( - eve::DiagnosticCode::NotFound, "procgen debug stage was not found", "stage"); + return procgenBindingFailure(eve::DiagnosticCode::NotFound, + "procgen debug stage was not found", "stage"); return ownProcgenObject(ownership_->points, std::make_unique(stage->second)); } -eve::Result Procgen::getPreviousSystemDebugStageHandle( - const std::string& name, const std::string& stageName) const { +eve::Result Procgen::getPreviousSystemDebugStageHandle(const std::string& name, + const std::string& stageName) const { const auto system = previousSystems_.find(name); if (system == previousSystems_.end()) - return procgenBindingFailure( - eve::DiagnosticCode::NotFound, "procgen system has no previous revision", "system"); + return procgenBindingFailure(eve::DiagnosticCode::NotFound, + "procgen system has no previous revision", "system"); const auto stage = system->second.debugStages.find(stageName); if (stage == system->second.debugStages.end()) - return procgenBindingFailure( - eve::DiagnosticCode::NotFound, "previous procgen debug stage was not found", "stage"); + return procgenBindingFailure(eve::DiagnosticCode::NotFound, + "previous procgen debug stage was not found", "stage"); return ownProcgenObject(ownership_->points, std::make_unique(stage->second)); } @@ -1180,12 +1150,12 @@ bool Procgen::runGenerate(const std::string &algorithmId, const Params ¶ms, return true; } -eve::Result Procgen::generateHandle( - const std::string& algorithmId, ProcgenParamsHandleRef params) { +eve::Result Procgen::generateHandle(const std::string& algorithmId, + ProcgenParamsHandleRef params) { auto input = Procgen::resolve(params); if (!input.isBound()) - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "generate parameters handle is stale", "params"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "generate parameters handle is stale", "params"); auto grid = std::make_unique(); if (!runGenerate(algorithmId, *input, *grid)) return procgenBindingFailure( @@ -1193,22 +1163,20 @@ eve::Result Procgen::generateHandle( return ownProcgenObject(grids_, std::move(grid)); } -eve::Result Procgen::generateTo(const std::string &algorithmId, - ProcgenParamsHandleRef params, +eve::Result Procgen::generateTo(const std::string& algorithmId, ProcgenParamsHandleRef params, ProcgenOutputHandleRef output) { auto paramsView = Procgen::resolve(params); auto outputView = resolveOutput(output); if (!paramsView.isBound()) - return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "generateTo parameter handle is stale", "params"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "generateTo parameter handle is stale", + "params"); if (!outputView.isBound()) - return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "generateTo output handle is stale", "output"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "generateTo output handle is stale", + "output"); Grid2D grid; if (!runGenerate(algorithmId, *paramsView, grid)) return procgenBindingFailure(eve::DiagnosticCode::Failed, - lastError_.empty() ? "generateTo failed" : lastError_, - "algorithm"); + lastError_.empty() ? "generateTo failed" : lastError_, "algorithm"); const std::string target = outputView->getTarget(); if (target == "grid") { @@ -1235,13 +1203,11 @@ eve::Result Procgen::generateTo(const std::string &algorithmId, "output.target"); } -eve::Result Procgen::applyToLayer(ProcgenGridHandleRef grid, - const std::string &palette, - map::TileLayer& layer) { +eve::Result Procgen::applyToLayer(ProcgenGridHandleRef grid, const std::string& palette, map::TileLayer& layer) { auto view = Procgen::resolve(grid); if (!view.isBound()) - return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "applyToLayer grid handle is stale", "grid"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "applyToLayer grid handle is stale", + "grid"); if (!palettes_.applyToLayer(*view, palette, &layer, &lastError_)) return procgenBindingFailure(eve::DiagnosticCode::Failed, lastError_, "palette"); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); @@ -1280,11 +1246,11 @@ bool Procgen::hasAlgorithm(const std::string &algorithmId) const { return GeneratorRegistry::instance().has(algorithmId); } -eve::Result Procgen::getAlgorithmSchema(const std::string &algorithmId) const { +eve::Result Procgen::getAlgorithmSchema(const std::string& algorithmId) const { const RecipeDescriptor *schema = GeneratorRegistry::instance().descriptor(algorithmId); if (!schema) - return procgenBindingFailure(eve::DiagnosticCode::NotFound, - "algorithm schema was not found", "algorithm"); + return procgenBindingFailure(eve::DiagnosticCode::NotFound, "algorithm schema was not found", + "algorithm"); return eve::Result::success(*schema); } @@ -1383,26 +1349,24 @@ std::string Procgen::getAlgorithmParamChoice(const std::string &algorithmId, int return param->choices[size_t(choiceIndex)]; } -eve::Result Procgen::applyAlgorithmDefaults(const std::string &algorithmId, - ProcgenParamsHandleRef params) const { +eve::Result Procgen::applyAlgorithmDefaults(const std::string& algorithmId, ProcgenParamsHandleRef params) const { auto view = Procgen::resolve(params); if (!view.isBound()) return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "algorithm default parameters handle is stale", "params"); if (!GeneratorRegistry::instance().applyDefaults(algorithmId, *view)) - return procgenBindingFailure(eve::DiagnosticCode::NotFound, - "algorithm schema was not found", "algorithm"); + return procgenBindingFailure(eve::DiagnosticCode::NotFound, "algorithm schema was not found", + "algorithm"); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } eve::Result Procgen::autotileGrid(ProcgenGridHandleRef grid) { auto view = Procgen::resolve(grid); if (!view.isBound()) - return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "autotileGrid grid handle is stale", "grid"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "autotileGrid grid handle is stale", + "grid"); if (!eve::procgen::autotileGridInPlace(*view)) - return procgenBindingFailure(eve::DiagnosticCode::Failed, - "autotileGrid failed", "grid"); + return procgenBindingFailure(eve::DiagnosticCode::Failed, "autotileGrid failed", "grid"); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } @@ -1411,17 +1375,17 @@ uint32_t Procgen::randomSeed() { return eve::procgen::randomSeedValue(); } eve::Result Procgen::gridToJson(ProcgenGridHandleRef grid) const { auto view = Procgen::resolve(grid); if (!view.isBound()) - return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "gridToJson grid handle is stale", "grid"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "gridToJson grid handle is stale", + "grid"); return eve::Result::success(eve::procgen::gridToJson(*view)); } -eve::Result Procgen::generateImageHandle( - const std::string& recipeId, ProcgenParamsHandleRef params) { +eve::Result Procgen::generateImageHandle(const std::string& recipeId, + ProcgenParamsHandleRef params) { auto input = Procgen::resolve(params); if (!input.isBound()) - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "generateImage parameters handle is stale", "params"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "generateImage parameters handle is stale", "params"); lastError_.clear(); TextureRecipeRegistry::instance().registerBuiltins(); auto image = TextureRecipeRegistry::instance().generate(recipeId, *input, lastError_); @@ -1431,18 +1395,16 @@ eve::Result Procgen::generateImageHandle( return ownProcgenObject(ownership_->images, std::move(image)); } -eve::script::Borrowed Procgen::resolve( - ProcgenImageHandleRef reference) noexcept { +eve::script::Borrowed Procgen::resolve(ProcgenImageHandleRef reference) noexcept { Procgen* module = ModuleManager::getInstance("Procgen"); - return module ? module->ownership_->images.resolve(reference) - : eve::script::Borrowed(); + return module ? module->ownership_->images.resolve(reference) : eve::script::Borrowed(); } eve::Result Procgen::release(ProcgenImageHandleRef reference) { Procgen* module = ModuleManager::getInstance("Procgen"); if (!module) - return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "Procgen module is no longer loaded", "image"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "Procgen module is no longer loaded", + "image"); return module->ownership_->images.erase(reference); } @@ -1452,55 +1414,51 @@ bool Procgen::isStale(ProcgenImageHandleRef reference) noexcept { return !module || module->ownership_->images.isStale(reference); } -eve::Result Procgen::generateNormalImageHandle( - const std::string& recipeId, ProcgenParamsHandleRef params) { +eve::Result Procgen::generateNormalImageHandle(const std::string& recipeId, + ProcgenParamsHandleRef params) { auto input = Procgen::resolve(params); if (!input.isBound()) return procgenBindingFailure( eve::DiagnosticCode::StaleHandle, "generateNormalImage parameters handle is stale", "params"); auto image = generateImageHandle(recipeId, params); if (!image) - return procgenBindingFailure( - eve::DiagnosticCode::Failed, image.status().describe(), "recipe"); + return procgenBindingFailure(eve::DiagnosticCode::Failed, + image.status().describe(), "recipe"); const auto imageRef = std::move(image).takeValue(); - auto albedo = ownership_->images.resolve(imageRef); + auto albedo = ownership_->images.resolve(imageRef); if (!albedo.isBound()) { ownership_->images.erase(imageRef).ignore("release unresolvable temporary albedo image"); - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "generated albedo image handle is stale", "image"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "generated albedo image handle is stale", "image"); } const int w = albedo->getWidth(); const int h = albedo->getHeight(); - auto* px = static_cast(albedo->getData()); + auto* px = static_cast(albedo->getData()); std::vector height(size_t(w * h)); for (int i = 0; i < w * h; ++i) { const size_t o = size_t(i) * 4u; - height[size_t(i)] = (float(px[o]) * 0.299f + float(px[o + 1]) * 0.587f + - float(px[o + 2]) * 0.114f) / 255.f; + height[size_t(i)] = (float(px[o]) * 0.299f + float(px[o + 1]) * 0.587f + float(px[o + 2]) * 0.114f) / 255.f; } - const bool seamless = input->getInt("seamless", 1) != 0; + const bool seamless = input->getInt("seamless", 1) != 0; const float strength = input->getFloat("normalStrength", 4.f); - auto normal = heightToNormalImage(height, w, h, strength, seamless); + auto normal = heightToNormalImage(height, w, h, strength, seamless); ownership_->images.erase(imageRef).ignore("release temporary albedo image"); if (!normal) - return procgenBindingFailure( - eve::DiagnosticCode::Failed, "generateNormalImage failed", "recipe"); - return ownProcgenObject(ownership_->normalImages, - std::move(normal)); + return procgenBindingFailure(eve::DiagnosticCode::Failed, + "generateNormalImage failed", "recipe"); + return ownProcgenObject(ownership_->normalImages, std::move(normal)); } -eve::script::Borrowed Procgen::resolve( - ProcgenNormalImageHandleRef reference) noexcept { +eve::script::Borrowed Procgen::resolve(ProcgenNormalImageHandleRef reference) noexcept { Procgen* module = ModuleManager::getInstance("Procgen"); - return module ? module->ownership_->normalImages.resolve(reference) - : eve::script::Borrowed(); + return module ? module->ownership_->normalImages.resolve(reference) : eve::script::Borrowed(); } eve::Result Procgen::release(ProcgenNormalImageHandleRef reference) { Procgen* module = ModuleManager::getInstance("Procgen"); if (!module) - return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "Procgen module is no longer loaded", "normalImage"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "Procgen module is no longer loaded", + "normalImage"); return module->ownership_->normalImages.erase(reference); } @@ -1510,25 +1468,25 @@ bool Procgen::isStale(ProcgenNormalImageHandleRef reference) noexcept { return !module || module->ownership_->normalImages.isStale(reference); } -eve::script::Borrowed Procgen::generateTextureBorrowed( - const std::string& recipeId, ProcgenParamsHandleRef params, graphics::Graphics* gfx) { +eve::script::Borrowed Procgen::generateTextureBorrowed(const std::string& recipeId, + ProcgenParamsHandleRef params, + graphics::Graphics* gfx) { auto input = Procgen::resolve(params); if (!input.isBound() || !gfx) return {}; auto generated = generateImageHandle(recipeId, params); if (!generated) return {}; const auto imageRef = std::move(generated).takeValue(); - auto image = ownership_->images.resolve(imageRef); + auto image = ownership_->images.resolve(imageRef); if (!image.isBound()) { ownership_->images.erase(imageRef).ignore("release unresolvable temporary image"); return {}; } - const bool seamless = input->getInt("seamless", 1) != 0; - graphics::Texture* texture = gfx->newTexture( - image->getWidth(), image->getHeight(), static_cast(image->getData()), - seamless, seamless); + const bool seamless = input->getInt("seamless", 1) != 0; + graphics::Texture* texture = gfx->newTexture(image->getWidth(), image->getHeight(), + static_cast(image->getData()), seamless, seamless); ownership_->images.erase(imageRef).ignore("release temporary generated image"); - return eve::script::Borrowed( - texture, static_cast(reinterpret_cast(gfx))); + return eve::script::Borrowed(texture, + static_cast(reinterpret_cast(gfx))); } int Procgen::getTextureRecipeCount() const { @@ -1551,7 +1509,7 @@ bool Procgen::hasTextureRecipe(const std::string &recipeId) const { return TextureRecipeRegistry::instance().has(recipeId); } -eve::Result Procgen::getTextureRecipeSchema(const std::string &recipeId) const { +eve::Result Procgen::getTextureRecipeSchema(const std::string& recipeId) const { TextureRecipeRegistry::instance().registerBuiltins(); const RecipeDescriptor *schema = TextureRecipeRegistry::instance().descriptor(recipeId); if (!schema) @@ -1560,16 +1518,16 @@ eve::Result Procgen::getTextureRecipeSchema(const std::string return eve::Result::success(*schema); } -eve::Result Procgen::applyTextureRecipeDefaults( - const std::string &recipeId, ProcgenParamsHandleRef params) const { +eve::Result Procgen::applyTextureRecipeDefaults(const std::string& recipeId, + ProcgenParamsHandleRef params) const { auto view = Procgen::resolve(params); if (!view.isBound()) return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "texture default parameters handle is stale", "params"); TextureRecipeRegistry::instance().registerBuiltins(); if (!TextureRecipeRegistry::instance().applyDefaults(recipeId, *view)) - return procgenBindingFailure(eve::DiagnosticCode::NotFound, - "texture recipe schema was not found", "recipe"); + return procgenBindingFailure(eve::DiagnosticCode::NotFound, "texture recipe schema was not found", + "recipe"); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } @@ -1577,8 +1535,7 @@ eve::Result Procgen::newCloudFieldHandle() { return ownProcgenObject(ownership_->clouds, std::make_unique()); } -eve::script::Borrowed Procgen::resolveCloudField( - ProcgenCloudFieldHandleRef reference) noexcept { +eve::script::Borrowed Procgen::resolveCloudField(ProcgenCloudFieldHandleRef reference) noexcept { return ownership_->clouds.resolve(reference); } @@ -1594,8 +1551,7 @@ eve::Result Procgen::newCloudShadowHandle() { return ownProcgenObject(ownership_->shadows, std::make_unique()); } -eve::script::Borrowed Procgen::resolveCloudShadow( - ProcgenCloudShadowHandleRef reference) noexcept { +eve::script::Borrowed Procgen::resolveCloudShadow(ProcgenCloudShadowHandleRef reference) noexcept { return ownership_->shadows.resolve(reference); } @@ -1607,31 +1563,25 @@ bool Procgen::isCloudShadowStale(ProcgenCloudShadowHandleRef reference) const no return reference.isValid() && ownership_->shadows.isStale(reference); } -eve::Result Procgen::cloudCoverageAt( - ProcgenCloudFieldHandleRef field, float x, float z, float time) { +eve::Result Procgen::cloudCoverageAt(ProcgenCloudFieldHandleRef field, float x, float z, float time) { auto view = resolveCloudField(field); if (!view.isBound()) - return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "cloud field handle is stale", "field"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "cloud field handle is stale", "field"); return eve::Result::success(view->coverageAt(x, z, time)); } -eve::Result Procgen::cloudShadowFactor( - ProcgenCloudShadowHandleRef shadow, float x, float z, float time) { +eve::Result Procgen::cloudShadowFactor(ProcgenCloudShadowHandleRef shadow, float x, float z, float time) { auto view = resolveCloudShadow(shadow); if (!view.isBound()) - return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "cloud shadow handle is stale", "shadow"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "cloud shadow handle is stale", "shadow"); return eve::Result::success(view->shadowFactorAt(x, z, time)); } -eve::Result Procgen::sampleCloud( - ProcgenCloudFieldHandleRef field, std::span out, int w, int h, float time, - float x0, float z0, float extent) { +eve::Result Procgen::sampleCloud(ProcgenCloudFieldHandleRef field, std::span out, int w, int h, float time, + float x0, float z0, float extent) { auto view = resolveCloudField(field); if (!view.isBound()) - return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "cloud field handle is stale", "field"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "cloud field handle is stale", "field"); if (w <= 0 || h <= 0 || out.size() < static_cast(w) * static_cast(h)) return procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, "cloud output buffer is smaller than width*height", "out"); @@ -1639,13 +1589,11 @@ eve::Result Procgen::sampleCloud( return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result Procgen::sampleCloudShadow( - ProcgenCloudShadowHandleRef shadow, std::span out, int w, int h, float time, - float x0, float z0, float extent) { +eve::Result Procgen::sampleCloudShadow(ProcgenCloudShadowHandleRef shadow, std::span out, int w, int h, + float time, float x0, float z0, float extent) { auto view = resolveCloudShadow(shadow); if (!view.isBound()) - return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "cloud shadow handle is stale", "shadow"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "cloud shadow handle is stale", "shadow"); if (w <= 0 || h <= 0 || out.size() < static_cast(w) * static_cast(h)) return procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, "cloud output buffer is smaller than width*height", "out"); @@ -1653,8 +1601,8 @@ eve::Result Procgen::sampleCloudShadow( return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result Procgen::generatePbrMaterialHandle( - const std::string& recipeId, ProcgenParamsHandleRef params) { +eve::Result Procgen::generatePbrMaterialHandle(const std::string& recipeId, + ProcgenParamsHandleRef params) { auto input = Procgen::resolve(params); if (!input.isBound()) return procgenBindingFailure( @@ -1664,14 +1612,11 @@ eve::Result Procgen::generatePbrMaterialHandle( auto set = PbrRecipeRegistry::instance().generate(recipeId, *input, lastError_); if (!set) return procgenBindingFailure( - eve::DiagnosticCode::Failed, - lastError_.empty() ? "generatePbrMaterial failed" : lastError_, "recipe"); - return ownProcgenObject(ownership_->pbr, - std::move(set)); + eve::DiagnosticCode::Failed, lastError_.empty() ? "generatePbrMaterial failed" : lastError_, "recipe"); + return ownProcgenObject(ownership_->pbr, std::move(set)); } -eve::script::Borrowed Procgen::resolvePbrMaterial( - ProcgenPbrMaterialHandleRef reference) noexcept { +eve::script::Borrowed Procgen::resolvePbrMaterial(ProcgenPbrMaterialHandleRef reference) noexcept { return ownership_->pbr.resolve(reference); } @@ -1703,58 +1648,54 @@ bool Procgen::hasPbrRecipe(const std::string &recipeId) const { return PbrRecipeRegistry::instance().has(recipeId); } -eve::Result Procgen::getPbrRecipeSchema(const std::string &recipeId) const { +eve::Result Procgen::getPbrRecipeSchema(const std::string& recipeId) const { PbrRecipeRegistry::instance().registerPbrBuiltins(); const RecipeDescriptor *schema = PbrRecipeRegistry::instance().descriptor(recipeId); if (!schema) - return procgenBindingFailure(eve::DiagnosticCode::NotFound, - "PBR recipe schema was not found", "recipe"); + return procgenBindingFailure(eve::DiagnosticCode::NotFound, "PBR recipe schema was not found", + "recipe"); return eve::Result::success(*schema); } -eve::Result Procgen::applyPbrRecipeDefaults( - const std::string &recipeId, ProcgenParamsHandleRef params) const { +eve::Result Procgen::applyPbrRecipeDefaults(const std::string& recipeId, ProcgenParamsHandleRef params) const { auto view = Procgen::resolve(params); if (!view.isBound()) - return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "PBR default parameters handle is stale", "params"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "PBR default parameters handle is stale", + "params"); PbrRecipeRegistry::instance().registerPbrBuiltins(); if (!PbrRecipeRegistry::instance().applyDefaults(recipeId, *view)) - return procgenBindingFailure(eve::DiagnosticCode::NotFound, - "PBR recipe schema was not found", "recipe"); + return procgenBindingFailure(eve::DiagnosticCode::NotFound, "PBR recipe schema was not found", "recipe"); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result Procgen::buildMeshHandle( - const std::string& recipeId, ProcgenParamsHandleRef params) { +eve::Result Procgen::buildMeshHandle(const std::string& recipeId, + ProcgenParamsHandleRef params) { auto built = buildArtifact(recipeId, params, nextCompatibilityArtifactId()); if (!built) - return procgenBindingFailure( - eve::DiagnosticCode::Failed, built.status().describe(), "recipe"); + return procgenBindingFailure(eve::DiagnosticCode::Failed, built.status().describe(), + "recipe"); GeneratedArtifact artifact = std::move(built).takeValue(); - const MeshBuild* source = nullptr; + const MeshBuild* source = nullptr; if (artifact.type == ArtifactType::MeshData) { source = &std::get(artifact.payload); } else if (artifact.type == ArtifactType::Composite) { const ArtifactPart* part = std::get(artifact.payload).find("mesh"); - if (part && part->type == ArtifactType::MeshData) - source = &std::get(part->payload); + if (part && part->type == ArtifactType::MeshData) source = &std::get(part->payload); } if (!source) - return procgenBindingFailure( - eve::DiagnosticCode::Failed, "generated artifact has no mesh payload", "recipe"); - auto mesh = std::make_unique(*source); + return procgenBindingFailure(eve::DiagnosticCode::Failed, + "generated artifact has no mesh payload", "recipe"); + auto mesh = std::make_unique(*source); ArtifactPublisher publisher(artifactStore_); - auto published = publisher.publish(std::move(artifact), {}); + auto published = publisher.publish(std::move(artifact), {}); if (!published) - return procgenBindingFailure( - eve::DiagnosticCode::Failed, published.status().describe(), "artifact"); + return procgenBindingFailure(eve::DiagnosticCode::Failed, + published.status().describe(), "artifact"); std::move(published).takeValue(); return ownProcgenObject(ownership_->meshes, std::move(mesh)); } -eve::script::Borrowed Procgen::resolveMeshBuild( - ProcgenMeshBuildHandleRef reference) noexcept { +eve::script::Borrowed Procgen::resolveMeshBuild(ProcgenMeshBuildHandleRef reference) noexcept { return ownership_->meshes.resolve(reference); } @@ -1766,21 +1707,19 @@ bool Procgen::isMeshBuildStale(ProcgenMeshBuildHandleRef reference) const noexce return reference.isValid() && ownership_->meshes.isStale(reference); } -eve::Result Procgen::buildArtifact( - const std::string &recipeId, ProcgenParamsHandleRef params, ArtifactId id) { +eve::Result Procgen::buildArtifact(const std::string& recipeId, ProcgenParamsHandleRef params, + ArtifactId id) { auto view = Procgen::resolve(params); if (!view.isBound()) - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "buildArtifact parameters handle is stale", "params"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "buildArtifact parameters handle is stale", "params"); return generateMeshArtifact(recipeId, *view, id); } -eve::Result Procgen::publishArtifact( - const std::string &recipeId, ProcgenParamsHandleRef params, ArtifactId id, - ArtifactPublishOptions options) { +eve::Result Procgen::publishArtifact(const std::string& recipeId, ProcgenParamsHandleRef params, + ArtifactId id, ArtifactPublishOptions options) { auto artifact = buildArtifact(recipeId, params, id); - if (!artifact.ok()) - return eve::Result::failure(artifact.status()); + if (!artifact.ok()) return eve::Result::failure(artifact.status()); ArtifactPublisher publisher(artifactStore_); return publisher.publish(std::move(artifact).takeValue(), options); } @@ -1793,8 +1732,9 @@ ArtifactId Procgen::nextCompatibilityArtifactId() noexcept { return root.child("legacy-facade-" + std::to_string(nextArtifactSequence_++)); } -eve::script::Borrowed Procgen::generateMeshBorrowed( - const std::string& recipeId, ProcgenParamsHandleRef params, graphics::Graphics* gfx) { +eve::script::Borrowed Procgen::generateMeshBorrowed(const std::string& recipeId, + ProcgenParamsHandleRef params, + graphics::Graphics* gfx) { auto input = Procgen::resolve(params); if (!input.isBound() || !gfx) return {}; auto built = buildMeshHandle(recipeId, params); @@ -1804,14 +1744,13 @@ eve::script::Borrowed Procgen::generateMeshBorrowed( return uploadMeshBorrowed(*cpu, *gfx); } -eve::script::Borrowed Procgen::uploadMeshBorrowed( - const MeshBuild& mesh, graphics::Graphics& gfx) { +eve::script::Borrowed Procgen::uploadMeshBorrowed(const MeshBuild& mesh, graphics::Graphics& gfx) { if (mesh.empty()) return {}; - graphics::Mesh* uploaded = gfx.newMeshFromArrays( - mesh.positions().data(), mesh.normals().data(), mesh.uvs().data(), - mesh.getVertexCount(), mesh.indices().data(), mesh.getIndexCount()); - return eve::script::Borrowed( - uploaded, static_cast(reinterpret_cast(&gfx))); + graphics::Mesh* uploaded = + gfx.newMeshFromArrays(mesh.positions().data(), mesh.normals().data(), mesh.uvs().data(), mesh.getVertexCount(), + mesh.indices().data(), mesh.getIndexCount()); + return eve::script::Borrowed(uploaded, + static_cast(reinterpret_cast(&gfx))); } int Procgen::getMeshRecipeCount() const { @@ -1834,7 +1773,7 @@ bool Procgen::hasMeshRecipe(const std::string &recipeId) const { return MeshRecipeRegistry::instance().has(recipeId); } -eve::Result Procgen::getMeshRecipeSchema(const std::string &recipeId) const { +eve::Result Procgen::getMeshRecipeSchema(const std::string& recipeId) const { MeshRecipeRegistry::instance().registerBuiltins(); const RecipeDescriptor *schema = MeshRecipeRegistry::instance().descriptor(recipeId); if (!schema) @@ -1843,16 +1782,14 @@ eve::Result Procgen::getMeshRecipeSchema(const std::string &re return eve::Result::success(*schema); } -eve::Result Procgen::applyMeshRecipeDefaults( - const std::string &recipeId, ProcgenParamsHandleRef params) const { +eve::Result Procgen::applyMeshRecipeDefaults(const std::string& recipeId, ProcgenParamsHandleRef params) const { auto view = Procgen::resolve(params); if (!view.isBound()) - return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "mesh default parameters handle is stale", "params"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "mesh default parameters handle is stale", + "params"); MeshRecipeRegistry::instance().registerBuiltins(); if (!MeshRecipeRegistry::instance().applyDefaults(recipeId, *view)) - return procgenBindingFailure(eve::DiagnosticCode::NotFound, - "mesh recipe schema was not found", "recipe"); + return procgenBindingFailure(eve::DiagnosticCode::NotFound, "mesh recipe schema was not found", "recipe"); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } @@ -1875,14 +1812,12 @@ bool Procgen::isTerrainSamplerStale(ProcgenTerrainSamplerHandleRef reference) co eve::Result Procgen::newHeightmapHandle(int width, int height) { if (width <= 0 || height <= 0) - return procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, "heightmap dimensions must be positive", "heightmap"); - return ownProcgenObject(ownership_->heightmaps, - std::make_unique(width, height)); + return procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "heightmap dimensions must be positive", "heightmap"); + return ownProcgenObject(ownership_->heightmaps, std::make_unique(width, height)); } -eve::script::Borrowed Procgen::resolveHeightmap( - ProcgenHeightmapHandleRef reference) noexcept { +eve::script::Borrowed Procgen::resolveHeightmap(ProcgenHeightmapHandleRef reference) noexcept { return ownership_->heightmaps.resolve(reference); } @@ -1894,38 +1829,37 @@ bool Procgen::isHeightmapStale(ProcgenHeightmapHandleRef reference) const noexce return reference.isValid() && ownership_->heightmaps.isStale(reference); } -eve::Result Procgen::generateHeightmapHandle( - ProcgenParamsHandleRef params) { +eve::Result Procgen::generateHeightmapHandle(ProcgenParamsHandleRef params) { auto input = Procgen::resolve(params); if (!input.isBound()) return procgenBindingFailure( eve::DiagnosticCode::StaleHandle, "generateHeightmap parameters handle is stale", "params"); const TerrainSampler sampler = TerrainSampler::fromParams(*input); - return ownProcgenObject(ownership_->heightmaps, std::make_unique( - Heightmap::generate(sampler, input->getWidth(), input->getHeight()))); + return ownProcgenObject(ownership_->heightmaps, std::make_unique(Heightmap::generate( + sampler, input->getWidth(), input->getHeight()))); } -eve::Result Procgen::heightmapToGrid( - ProcgenHeightmapHandleRef heightmap, ProcgenParamsHandleRef params) { - auto map = resolveHeightmap(heightmap); +eve::Result Procgen::heightmapToGrid(ProcgenHeightmapHandleRef heightmap, + ProcgenParamsHandleRef params) { + auto map = resolveHeightmap(heightmap); auto input = Procgen::resolve(params); if (!map.isBound()) - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "heightmap handle is stale", "heightmap"); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "heightmap handle is stale", "heightmap"); if (!input.isBound()) - return procgenBindingFailure( - eve::DiagnosticCode::StaleHandle, "heightmap parameters handle is stale", "params"); - auto grid = std::make_unique(); + return procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "heightmap parameters handle is stale", "params"); + auto grid = std::make_unique(); const TerrainBands bands = TerrainBands::fromParams(*input); if (!map->toGrid(*grid, bands)) - return procgenBindingFailure( - eve::DiagnosticCode::Failed, "heightmap is empty", "heightmap"); + return procgenBindingFailure(eve::DiagnosticCode::Failed, "heightmap is empty", + "heightmap"); Procgen* module = Procgen::create(); return module->grids_.emplace(std::move(grid)); } void Procgen::expose(ssq::Table &table) { - const HSQUIRRELVM vm = table.getHandle(); + const HSQUIRRELVM vm = table.getHandle(); auto cls = table.addClass(name, Procgen::create, false); expose(cls); exposeBiomeRules(table); @@ -2201,8 +2135,7 @@ auto lsystem = table.addClass( context.addFunc("abort", &ProcgenContext::abort); auto ownedParams = table.addClass( - "ProcgenOwnedParams", - std::function([] { return nullptr; }), true); + "ProcgenOwnedParams", std::function([] { return nullptr; }), true); ownedParams.addFunc("ownership", [](ScriptProcgenParams*) { return std::string("owned"); }); ownedParams.addFunc("ownerEpoch", [](ScriptProcgenParams* value) { return value ? static_cast(value->reference.ownerEpoch) : int64_t{0}; @@ -2210,14 +2143,13 @@ auto lsystem = table.addClass( ownedParams.addFunc("handle", [](ScriptProcgenParams* value) { return value ? static_cast(value->reference.packed()) : int64_t{0}; }); - ownedParams.addFunc("isStale", [](ScriptProcgenParams* value) { - return !value || Procgen::isStale(value->reference); - }); + ownedParams.addFunc("isStale", + [](ScriptProcgenParams* value) { return !value || Procgen::isStale(value->reference); }); ownedParams.addFunc("release", [vm](ScriptProcgenParams* value) { if (!value) return eve::script::projectResult( vm, procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, - "owned procgen params proxy must not be null", "params")); + "owned procgen params proxy must not be null", "params")); return eve::script::projectResult(vm, Procgen::release(value->reference)); }); ownedParams.addFunc("setSeed", [vm](ScriptProcgenParams* value, uint32_t seed) { @@ -2225,49 +2157,48 @@ auto lsystem = table.addClass( auto view = Procgen::resolve(value->reference); if (!view.isBound()) return staleProcgenResult(vm, "params"); view->setSeed(seed); - return eve::script::projectResult( - vm, eve::Result::success(eve::Status::success(eve::StatusCode::Applied))); + return eve::script::projectResult(vm, + eve::Result::success(eve::Status::success(eve::StatusCode::Applied))); }); ownedParams.addFunc("setSize", [vm](ScriptProcgenParams* value, int width, int height) { if (!value) return staleProcgenResult(vm, "params"); auto view = Procgen::resolve(value->reference); if (!view.isBound()) return staleProcgenResult(vm, "params"); view->setSize(width, height); - return eve::script::projectResult( - vm, eve::Result::success(eve::Status::success(eve::StatusCode::Applied))); + return eve::script::projectResult(vm, + eve::Result::success(eve::Status::success(eve::StatusCode::Applied))); }); ownedParams.addFunc("setInt", [vm](ScriptProcgenParams* value, const std::string& key, int number) { if (!value) return staleProcgenResult(vm, "params"); auto view = Procgen::resolve(value->reference); if (!view.isBound()) return staleProcgenResult(vm, "params"); view->setInt(key, number); - return eve::script::projectResult( - vm, eve::Result::success(eve::Status::success(eve::StatusCode::Applied))); + return eve::script::projectResult(vm, + eve::Result::success(eve::Status::success(eve::StatusCode::Applied))); }); ownedParams.addFunc("setFloat", [vm](ScriptProcgenParams* value, const std::string& key, float number) { if (!value) return staleProcgenResult(vm, "params"); auto view = Procgen::resolve(value->reference); if (!view.isBound()) return staleProcgenResult(vm, "params"); view->setFloat(key, number); - return eve::script::projectResult( - vm, eve::Result::success(eve::Status::success(eve::StatusCode::Applied))); + return eve::script::projectResult(vm, + eve::Result::success(eve::Status::success(eve::StatusCode::Applied))); }); ownedParams.addFunc("setBool", [vm](ScriptProcgenParams* value, const std::string& key, bool flag) { if (!value) return staleProcgenResult(vm, "params"); auto view = Procgen::resolve(value->reference); if (!view.isBound()) return staleProcgenResult(vm, "params"); view->setBool(key, flag); - return eve::script::projectResult( - vm, eve::Result::success(eve::Status::success(eve::StatusCode::Applied))); + return eve::script::projectResult(vm, + eve::Result::success(eve::Status::success(eve::StatusCode::Applied))); }); - ownedParams.addFunc("setString", [vm](ScriptProcgenParams* value, const std::string& key, - const std::string& text) { + ownedParams.addFunc("setString", [vm](ScriptProcgenParams* value, const std::string& key, const std::string& text) { if (!value) return staleProcgenResult(vm, "params"); auto view = Procgen::resolve(value->reference); if (!view.isBound()) return staleProcgenResult(vm, "params"); view->setString(key, text); - return eve::script::projectResult( - vm, eve::Result::success(eve::Status::success(eve::StatusCode::Applied))); + return eve::script::projectResult(vm, + eve::Result::success(eve::Status::success(eve::StatusCode::Applied))); }); ownedParams.addFunc("getSeed", [](ScriptProcgenParams* value) { auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); @@ -2281,40 +2212,34 @@ auto lsystem = table.addClass( auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); return view.isBound() ? view->getHeight() : 0; }); + ownedParams.addFunc("has", [](ScriptProcgenParams* value, const std::string& key) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() && view->has(key); + }); + ownedParams.addFunc("getInt", [](ScriptProcgenParams* value, const std::string& key, int fallback) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() ? view->getInt(key, fallback) : fallback; + }); + ownedParams.addFunc("getFloat", [](ScriptProcgenParams* value, const std::string& key, float fallback) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() ? view->getFloat(key, fallback) : fallback; + }); + ownedParams.addFunc("getBool", [](ScriptProcgenParams* value, const std::string& key, bool fallback) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() ? view->getBool(key, fallback) : fallback; + }); + ownedParams.addFunc("getString", + [](ScriptProcgenParams* value, const std::string& key, const std::string& fallback) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() ? view->getString(key, fallback) : fallback; + }); ownedParams.addFunc("canonicalString", [](ScriptProcgenParams* value) { auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); return view.isBound() ? view->canonicalString() : std::string{}; }); - auto ownedHandle = table.addClass( - "ProcgenOwnedHandle", std::function([] { return nullptr; }), true); - ownedHandle.addFunc("ownership", [](ScriptProcgenHandle*) { return std::string("owned"); }); - ownedHandle.addFunc("ownerEpoch", [](ScriptProcgenHandle* value) { - return value ? static_cast(value->ownerEpoch) : int64_t{0}; - }); - ownedHandle.addFunc("handle", [](ScriptProcgenHandle* value) { - return value ? static_cast(value->packed) : int64_t{0}; - }); - ownedHandle.addFunc("isStale", [](ScriptProcgenHandle* value) { - return !value || (value->stale && value->stale()); - }); - ownedHandle.addFunc("release", [vm](ScriptProcgenHandle* value) { - if (!value) - return eve::script::projectResult( - vm, procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, - "owned procgen handle must not be null", "handle")); - if (value->released) - return eve::script::projectResult( - vm, procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "owned procgen handle was already released", "handle")); - value->released = true; - return eve::script::projectResult( - vm, value->release ? value->release() : eve::Result::success()); - }); - auto ownedGrid = table.addClass( - "ProcgenOwnedGrid2D", - std::function([] { return nullptr; }), true); + "ProcgenOwnedGrid2D", std::function([] { return nullptr; }), true); ownedGrid.addFunc("ownership", [](ScriptProcgenGrid*) { return std::string("owned"); }); ownedGrid.addFunc("ownerEpoch", [](ScriptProcgenGrid* value) { return value ? static_cast(value->reference.ownerEpoch) : int64_t{0}; @@ -2322,14 +2247,12 @@ auto lsystem = table.addClass( ownedGrid.addFunc("handle", [](ScriptProcgenGrid* value) { return value ? static_cast(value->reference.packed()) : int64_t{0}; }); - ownedGrid.addFunc("isStale", [](ScriptProcgenGrid* value) { - return !value || Procgen::isStale(value->reference); - }); + ownedGrid.addFunc("isStale", [](ScriptProcgenGrid* value) { return !value || Procgen::isStale(value->reference); }); ownedGrid.addFunc("release", [vm](ScriptProcgenGrid* value) { if (!value) return eve::script::projectResult( vm, procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, - "owned procgen grid proxy must not be null", "grid")); + "owned procgen grid proxy must not be null", "grid")); return eve::script::projectResult(vm, Procgen::release(value->reference)); }); ownedGrid.addFunc("resize", [vm](ScriptProcgenGrid* value, int width, int height) { @@ -2337,24 +2260,24 @@ auto lsystem = table.addClass( auto view = Procgen::resolve(value->reference); if (!view.isBound()) return staleProcgenResult(vm, "grid"); view->resize(width, height); - return eve::script::projectResult( - vm, eve::Result::success(eve::Status::success(eve::StatusCode::Applied))); + return eve::script::projectResult(vm, + eve::Result::success(eve::Status::success(eve::StatusCode::Applied))); }); ownedGrid.addFunc("fill", [vm](ScriptProcgenGrid* value, int semantic) { if (!value) return staleProcgenResult(vm, "grid"); auto view = Procgen::resolve(value->reference); if (!view.isBound()) return staleProcgenResult(vm, "grid"); view->fill(semantic); - return eve::script::projectResult( - vm, eve::Result::success(eve::Status::success(eve::StatusCode::Applied))); + return eve::script::projectResult(vm, + eve::Result::success(eve::Status::success(eve::StatusCode::Applied))); }); ownedGrid.addFunc("setCell", [vm](ScriptProcgenGrid* value, int x, int y, int semantic) { if (!value) return staleProcgenResult(vm, "grid"); auto view = Procgen::resolve(value->reference); if (!view.isBound()) return staleProcgenResult(vm, "grid"); view->setCell(x, y, semantic); - return eve::script::projectResult( - vm, eve::Result::success(eve::Status::success(eve::StatusCode::Applied))); + return eve::script::projectResult(vm, + eve::Result::success(eve::Status::success(eve::StatusCode::Applied))); }); ownedGrid.addFunc("getWidth", [](ScriptProcgenGrid* value) { auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); @@ -2368,10 +2291,63 @@ auto lsystem = table.addClass( auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); return view.isBound() ? view->getCell(x, y) : 0; }); + ownedGrid.addFunc("setDetail", [vm](ScriptProcgenGrid* value, int x, int y, int detail) { + if (!value) return staleProcgenResult(vm, "grid"); + auto view = Procgen::resolve(value->reference); + if (!view.isBound()) return staleProcgenResult(vm, "grid"); + view->setDetail(x, y, detail); + return eve::script::projectResult(vm, eve::Result::success()); + }); + ownedGrid.addFunc("getDetail", [](ScriptProcgenGrid* value, int x, int y) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() ? view->getDetail(x, y) : 0; + }); + ownedGrid.addFunc("getObjectCount", [](ScriptProcgenGrid* value) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() ? view->getObjectCount() : 0; + }); + ownedGrid.addFunc("getObjectName", [](ScriptProcgenGrid* value, int index) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() ? view->getObjectName(index) : std::string{}; + }); + ownedGrid.addFunc("getObjectType", [](ScriptProcgenGrid* value, int index) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() ? view->getObjectType(index) : std::string{}; + }); + ownedGrid.addFunc("getObjectX", [](ScriptProcgenGrid* value, int index) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() ? view->getObjectX(index) : 0; + }); + ownedGrid.addFunc("getObjectY", [](ScriptProcgenGrid* value, int index) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() ? view->getObjectY(index) : 0; + }); + ownedGrid.addFunc("getObjectWidth", [](ScriptProcgenGrid* value, int index) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() ? view->getObjectWidth(index) : 0; + }); + ownedGrid.addFunc("getObjectHeight", [](ScriptProcgenGrid* value, int index) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() ? view->getObjectHeight(index) : 0; + }); + ownedGrid.addFunc("getObjectGid", [](ScriptProcgenGrid* value, int index) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() ? view->getObjectGid(index) : 0; + }); + ownedGrid.addFunc("getMeta", [](ScriptProcgenGrid* value, const std::string& key, const std::string& fallback) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() ? view->getMeta(key, fallback) : fallback; + }); + ownedGrid.addFunc("setMeta", [vm](ScriptProcgenGrid* value, const std::string& key, const std::string& data) { + if (!value) return staleProcgenResult(vm, "grid"); + auto view = Procgen::resolve(value->reference); + if (!view.isBound()) return staleProcgenResult(vm, "grid"); + view->setMeta(key, data); + return eve::script::projectResult(vm, eve::Result::success()); + }); auto ownedContext = table.addClass( - "ProcgenOwnedContext", - std::function([] { return nullptr; }), true); + "ProcgenOwnedContext", std::function([] { return nullptr; }), true); ownedContext.addFunc("ownership", [](ScriptProcgenContext*) { return std::string("owned"); }); ownedContext.addFunc("ownerEpoch", [](ScriptProcgenContext* value) { return value ? static_cast(value->reference.ownerEpoch) : int64_t{0}; @@ -2379,14 +2355,13 @@ auto lsystem = table.addClass( ownedContext.addFunc("handle", [](ScriptProcgenContext* value) { return value ? static_cast(value->reference.packed()) : int64_t{0}; }); - ownedContext.addFunc("isStale", [](ScriptProcgenContext* value) { - return !value || Procgen::isStale(value->reference); - }); + ownedContext.addFunc("isStale", + [](ScriptProcgenContext* value) { return !value || Procgen::isStale(value->reference); }); ownedContext.addFunc("release", [vm](ScriptProcgenContext* value) { if (!value) return eve::script::projectResult( vm, procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, - "owned procgen context proxy must not be null", "context")); + "owned procgen context proxy must not be null", "context")); return eve::script::projectResult(vm, Procgen::release(value->reference)); }); ownedContext.addFunc("getName", [](ScriptProcgenContext* value) { @@ -2401,13 +2376,52 @@ auto lsystem = table.addClass( auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); return view.isBound() && view->hasFailed(); }); + ownedContext.addFunc("seedFor", [](ScriptProcgenContext* value, const std::string& scope) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() ? view->seedFor(scope) : uint32_t{0}; + }); + ownedContext.addFunc("getError", [](ScriptProcgenContext* value) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() ? view->getError() : std::string("stale procgen context"); + }); + ownedContext.addFunc("beginTrace", [](ScriptProcgenContext* value, const std::string& stage, int inputCount) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() && view->beginTrace(stage, inputCount); + }); + ownedContext.addFunc("endTrace", [](ScriptProcgenContext* value, int outputCount) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() && view->endTrace(outputCount); + }); + ownedContext.addFunc("publish", [](ScriptProcgenContext* value, const std::string& name, PointSet* points) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() && view->publish(name, points); + }); + ownedContext.addFunc("captureDebug", [](ScriptProcgenContext* value, const std::string& name, PointSet* points) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() && view->captureDebug(name, points); + }); + ownedContext.addFunc( + "reuseStage", + [](ScriptProcgenContext* value, const std::string& name, const std::string& cacheKey) -> PointSet* { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() ? view->reuseStage(name, cacheKey) : nullptr; + }); + ownedContext.addFunc("cacheStage", [](ScriptProcgenContext* value, const std::string& name, + const std::string& cacheKey, PointSet* points) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() && view->cacheStage(name, cacheKey, points); + }); + ownedContext.addFunc("fail", [](ScriptProcgenContext* value, const std::string& error) { + auto view = value ? Procgen::resolve(value->reference) : eve::script::Borrowed(); + if (view.isBound()) view->fail(error); + }); ownedContext.addFunc("commit", [vm](ScriptProcgenContext* value) { if (!value) return staleProcgenResult(vm, "context"); auto* module = ModuleManager::getInstance("Procgen"); if (!module) return eve::script::projectResult( - vm, procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "Procgen module is no longer loaded", "context")); + vm, procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "Procgen module is no longer loaded", + "context")); return eve::script::projectResult(vm, module->commitSystem(value->reference)); }); ownedContext.addFunc("abort", [vm](ScriptProcgenContext* value) { @@ -2415,8 +2429,8 @@ auto lsystem = table.addClass( auto* module = ModuleManager::getInstance("Procgen"); if (!module) return eve::script::projectResult( - vm, procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "Procgen module is no longer loaded", "context")); + vm, procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "Procgen module is no longer loaded", + "context")); return eve::script::projectResult(vm, module->abortSystem(value->reference)); }); @@ -2429,10 +2443,9 @@ auto lsystem = table.addClass( mesh.addFunc("getGroupCount", &MeshBuild::getGroupCount); mesh.addFunc("getGroupName", &MeshBuild::getGroupName); mesh.addFunc("getTriangleGroup", &MeshBuild::getTriangleGroup); - mesh.addFunc("copyGroup", [vm](MeshBuild *self, int groupIndex) -> ssq::Object { + mesh.addFunc("copyGroup", [vm](MeshBuild* self, int groupIndex) -> ssq::Object { if (!self) return ssq::Object(vm); - auto object = eve::script::makeOwnedSquirrelInstance(vm, - self->copyGroup(groupIndex)); + auto object = eve::script::makeOwnedSquirrelInstance(vm, self->copyGroup(groupIndex)); if (!object) { object.ignore("failed to create copied mesh Squirrel instance"); return ssq::Object(vm); @@ -2556,247 +2569,432 @@ void Procgen::expose(ssq::Class &cls) { cls.addFunc("getName", &Procgen::getName); cls.addFunc("newParams", [vm = cls.getHandle()](Procgen*) -> ssq::Table { return makeOwnedProxy( - vm, Procgen::newParamsHandle(), - [](ProcgenParamsHandleRef ref) { return Procgen::release(ref); }); + vm, Procgen::newParamsHandle(), [](ProcgenParamsHandleRef ref) { return Procgen::release(ref); }); }); cls.addFunc("newGrid", [vm = cls.getHandle()](Procgen*, int width, int height) -> ssq::Table { return makeOwnedProxy( - vm, Procgen::newGridHandle(width, height), - [](ProcgenGridHandleRef ref) { return Procgen::release(ref); }); + vm, Procgen::newGridHandle(width, height), [](ProcgenGridHandleRef ref) { return Procgen::release(ref); }); }); cls.addFunc("newOutput", [vm = cls.getHandle()](Procgen*) -> ssq::Table { auto* module = Procgen::create(); - return makeAnyOwnedProxy( - vm, module->newOutputHandle(), + return makeOwnedNativeProxy( + vm, module->newOutputHandle(), [module](ProcgenOutputHandleRef ref) { return module->resolveOutput(ref); }, [](ProcgenOutputHandleRef ref) { auto* owner = ModuleManager::getInstance("Procgen"); return owner ? owner->releaseOutput(ref) : procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "Procgen module is no longer loaded", "output"); - }, - [](ProcgenOutputHandleRef ref) { - auto* owner = ModuleManager::getInstance("Procgen"); - return !owner || owner->isOutputStale(ref); }); }); cls.addFunc("newPointSet", [vm = cls.getHandle()](Procgen*) -> ssq::Table { auto* module = Procgen::create(); - return makeAnyOwnedProxy( + return makeOwnedNativeProxy( vm, module->newPointSetHandle(), + [module](ProcgenPointSetHandleRef ref) { return module->resolvePointSet(ref); }, [](ProcgenPointSetHandleRef ref) { auto* owner = ModuleManager::getInstance("Procgen"); return owner ? owner->releasePointSet(ref) : procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "Procgen module is no longer loaded", "pointSet"); - }, - [](ProcgenPointSetHandleRef ref) { - auto* owner = ModuleManager::getInstance("Procgen"); - return !owner || owner->isPointSetStale(ref); - }); - }); - cls.addFunc("sampleGrid", [vm = cls.getHandle()](Procgen*, int width, int depth, - float spacing, uint32_t seed, - float jitter) -> ssq::Table { - auto* module = Procgen::create(); - return makeAnyOwnedProxy( - vm, module->sampleGridHandle(width, depth, spacing, seed, jitter), - [](ProcgenPointSetHandleRef ref) { - auto* owner = ModuleManager::getInstance("Procgen"); - return owner ? owner->releasePointSet(ref) - : procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "Procgen module is no longer loaded", "pointSet"); - }, - [](ProcgenPointSetHandleRef ref) { - auto* owner = ModuleManager::getInstance("Procgen"); - return !owner || owner->isPointSetStale(ref); }); }); + cls.addFunc("sampleGrid", + [vm = cls.getHandle()](Procgen*, int width, int depth, float spacing, uint32_t seed, + float jitter) -> ssq::Table { + auto* module = Procgen::create(); + return makeOwnedNativeProxy( + vm, module->sampleGridHandle(width, depth, spacing, seed, jitter), + [module](ProcgenPointSetHandleRef ref) { return module->resolvePointSet(ref); }, + [](ProcgenPointSetHandleRef ref) { + auto* owner = ModuleManager::getInstance("Procgen"); + return owner + ? owner->releasePointSet(ref) + : procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "Procgen module is no longer loaded", "pointSet"); + }); + }); cls.addFunc("newTerrainSampler", [vm = cls.getHandle()](Procgen*) -> ssq::Table { auto* module = Procgen::create(); - return makeAnyOwnedProxy( + return makeOwnedNativeProxy( vm, module->newTerrainSamplerHandle(), + [module](ProcgenTerrainSamplerHandleRef ref) { return module->resolveTerrainSampler(ref); }, [](ProcgenTerrainSamplerHandleRef ref) { auto* owner = ModuleManager::getInstance("Procgen"); return owner ? owner->releaseTerrainSampler(ref) : procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "Procgen module is no longer loaded", "sampler"); - }, - [](ProcgenTerrainSamplerHandleRef ref) { - auto* owner = ModuleManager::getInstance("Procgen"); - return !owner || owner->isTerrainSamplerStale(ref); }); }); cls.addFunc("newHeightmap", [vm = cls.getHandle()](Procgen*, int width, int height) -> ssq::Table { auto* module = Procgen::create(); - return makeOwnedHeightmapProxy(vm, module->newHeightmapHandle(width, height)); + return makeOwnedNativeProxy( + vm, module->newHeightmapHandle(width, height), + [module](ProcgenHeightmapHandleRef ref) { return module->resolveHeightmap(ref); }, + [module](ProcgenHeightmapHandleRef ref) { return module->releaseHeightmap(ref); }); }); cls.addFunc("newCloudField", [vm = cls.getHandle()](Procgen*) -> ssq::Table { auto* module = Procgen::create(); - return makeAnyOwnedProxy( + return makeOwnedNativeProxy( vm, module->newCloudFieldHandle(), + [module](ProcgenCloudFieldHandleRef ref) { return module->resolveCloudField(ref); }, [](ProcgenCloudFieldHandleRef ref) { auto* owner = ModuleManager::getInstance("Procgen"); return owner ? owner->releaseCloudField(ref) : procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "Procgen module is no longer loaded", "cloudField"); - }, - [](ProcgenCloudFieldHandleRef ref) { - auto* owner = ModuleManager::getInstance("Procgen"); - return !owner || owner->isCloudFieldStale(ref); }); }); cls.addFunc("newCloudShadow", [vm = cls.getHandle()](Procgen*) -> ssq::Table { auto* module = Procgen::create(); - return makeAnyOwnedProxy( + return makeOwnedNativeProxy( vm, module->newCloudShadowHandle(), + [module](ProcgenCloudShadowHandleRef ref) { return module->resolveCloudShadow(ref); }, [](ProcgenCloudShadowHandleRef ref) { auto* owner = ModuleManager::getInstance("Procgen"); return owner ? owner->releaseCloudShadow(ref) : procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "Procgen module is no longer loaded", "cloudShadow"); - }, - [](ProcgenCloudShadowHandleRef ref) { - auto* owner = ModuleManager::getInstance("Procgen"); - return !owner || owner->isCloudShadowStale(ref); }); }); - cls.addFunc("generate", [vm = cls.getHandle()](Procgen*, const std::string& algorithm, - ScriptProcgenParams* params) -> ssq::Table { - auto* module = Procgen::create(); - if (!params) - return eve::script::projectStatusResult( - vm, procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, "generate params proxy must not be null", "params") - .status(), - false, false); - return makeAnyOwnedProxy( - vm, module->generateHandle(algorithm, params->reference), - [](ProcgenGridHandleRef ref) { return Procgen::release(ref); }, - [](ProcgenGridHandleRef ref) { return Procgen::isStale(ref); }); - }); - cls.addFunc("generateImage", [vm = cls.getHandle()](Procgen*, const std::string& recipe, - ScriptProcgenParams* params) -> ssq::Table { - auto* module = Procgen::create(); - if (!params) - return eve::script::projectStatusResult( - vm, procgenBindingFailure( + cls.addFunc( + "generate", + [vm = cls.getHandle()](Procgen*, const std::string& algorithm, ScriptProcgenParams* params) -> ssq::Table { + auto* module = Procgen::create(); + if (!params) + return eve::script::projectStatusResult( + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "generate params proxy must not be null", "params") + .status(), + false, false); + return makeOwnedProxy( + vm, module->generateHandle(algorithm, params->reference), + [](ProcgenGridHandleRef ref) { return Procgen::release(ref); }); + }); + cls.addFunc( + "generateImage", + [vm = cls.getHandle()](Procgen*, const std::string& recipe, ScriptProcgenParams* params) -> ssq::Table { + auto* module = Procgen::create(); + if (!params) + return eve::script::projectStatusResult( + vm, + procgenBindingFailure( eve::DiagnosticCode::InvalidArgument, "generateImage params proxy must not be null", "params") - .status(), - false, false); - return makeAnyOwnedProxy( - vm, module->generateImageHandle(recipe, params->reference), - [](ProcgenImageHandleRef ref) { return Procgen::release(ref); }, - [](ProcgenImageHandleRef ref) { return Procgen::isStale(ref); }); - }); - cls.addFunc("generateNormalImage", [vm = cls.getHandle()](Procgen*, const std::string& recipe, - ScriptProcgenParams* params) -> ssq::Table { - auto* module = Procgen::create(); - if (!params) - return eve::script::projectStatusResult( - vm, procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, "generateNormalImage params proxy must not be null", "params") - .status(), - false, false); - return makeAnyOwnedProxy( - vm, module->generateNormalImageHandle(recipe, params->reference), - [](ProcgenNormalImageHandleRef ref) { return Procgen::release(ref); }, - [](ProcgenNormalImageHandleRef ref) { return Procgen::isStale(ref); }); - }); - cls.addFunc("generatePbrMaterial", [vm = cls.getHandle()](Procgen*, const std::string& recipe, - ScriptProcgenParams* params) -> ssq::Table { - auto* module = Procgen::create(); - if (!params) - return eve::script::projectStatusResult( - vm, procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, "generatePbrMaterial params proxy must not be null", "params") - .status(), - false, false); - return makeAnyOwnedProxy( - vm, module->generatePbrMaterialHandle(recipe, params->reference), - [](ProcgenPbrMaterialHandleRef ref) { - auto* owner = ModuleManager::getInstance("Procgen"); - return owner ? owner->ownership_->pbr.erase(ref) - : procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "Procgen module is no longer loaded", "pbr"); - }, - [](ProcgenPbrMaterialHandleRef ref) { - auto* owner = ModuleManager::getInstance("Procgen"); - return !owner || owner->ownership_->pbr.isStale(ref); - }); - }); - cls.addFunc("buildMesh", [vm = cls.getHandle()](Procgen*, const std::string& recipe, - ScriptProcgenParams* params) -> ssq::Table { - auto* module = Procgen::create(); - if (!params) - return eve::script::projectStatusResult( - vm, procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, "buildMesh params proxy must not be null", "params") - .status(), - false, false); - return makeAnyOwnedProxy( - vm, module->buildMeshHandle(recipe, params->reference), - [](ProcgenMeshBuildHandleRef ref) { - auto* owner = ModuleManager::getInstance("Procgen"); - return owner ? owner->ownership_->meshes.erase(ref) - : procgenBindingFailure(eve::DiagnosticCode::StaleHandle, - "Procgen module is no longer loaded", "mesh"); - }, - [](ProcgenMeshBuildHandleRef ref) { - auto* owner = ModuleManager::getInstance("Procgen"); - return !owner || owner->ownership_->meshes.isStale(ref); - }); - }); + .status(), + false, false); + return makeOwnedNativeProxy( + vm, module->generateImageHandle(recipe, params->reference), + [](ProcgenImageHandleRef ref) { return Procgen::resolve(ref); }, + [](ProcgenImageHandleRef ref) { return Procgen::release(ref); }); + }); + cls.addFunc("generateNormalImage", + [vm = cls.getHandle()](Procgen*, const std::string& recipe, ScriptProcgenParams* params) -> ssq::Table { + auto* module = Procgen::create(); + if (!params) + return eve::script::projectStatusResult( + vm, + procgenBindingFailure( + eve::DiagnosticCode::InvalidArgument, + "generateNormalImage params proxy must not be null", "params") + .status(), + false, false); + return makeOwnedNativeProxy( + vm, module->generateNormalImageHandle(recipe, params->reference), + [](ProcgenNormalImageHandleRef ref) { return Procgen::resolve(ref); }, + [](ProcgenNormalImageHandleRef ref) { return Procgen::release(ref); }); + }); + cls.addFunc("generatePbrMaterial", + [vm = cls.getHandle()](Procgen*, const std::string& recipe, ScriptProcgenParams* params) -> ssq::Table { + auto* module = Procgen::create(); + if (!params) + return eve::script::projectStatusResult( + vm, + procgenBindingFailure( + eve::DiagnosticCode::InvalidArgument, + "generatePbrMaterial params proxy must not be null", "params") + .status(), + false, false); + return makeOwnedNativeProxy( + vm, module->generatePbrMaterialHandle(recipe, params->reference), + [module](ProcgenPbrMaterialHandleRef ref) { return module->resolvePbrMaterial(ref); }, + [](ProcgenPbrMaterialHandleRef ref) { + auto* owner = ModuleManager::getInstance("Procgen"); + return owner ? owner->ownership_->pbr.erase(ref) + : procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "Procgen module is no longer loaded", "pbr"); + }); + }); + cls.addFunc("buildMesh", + [vm = cls.getHandle()](Procgen*, const std::string& recipe, ScriptProcgenParams* params) -> ssq::Table { + auto* module = Procgen::create(); + if (!params) + return eve::script::projectStatusResult(vm, + procgenBindingFailure( + eve::DiagnosticCode::InvalidArgument, + "buildMesh params proxy must not be null", "params") + .status(), + false, false); + return makeOwnedNativeProxy( + vm, module->buildMeshHandle(recipe, params->reference), + [module](ProcgenMeshBuildHandleRef ref) { return module->resolveMeshBuild(ref); }, + [](ProcgenMeshBuildHandleRef ref) { + auto* owner = ModuleManager::getInstance("Procgen"); + return owner ? owner->ownership_->meshes.erase(ref) + : procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "Procgen module is no longer loaded", "mesh"); + }); + }); cls.addFunc("generateHeightmap", [vm = cls.getHandle()](Procgen*, ScriptProcgenParams* params) -> ssq::Table { auto* module = Procgen::create(); if (!params) return eve::script::projectStatusResult( - vm, procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, "generateHeightmap params proxy must not be null", "params") - .status(), + vm, + procgenBindingFailure( + eve::DiagnosticCode::InvalidArgument, "generateHeightmap params proxy must not be null", "params") + .status(), false, false); - return makeAnyOwnedProxy( + return makeOwnedNativeProxy( vm, module->generateHeightmapHandle(params->reference), + [module](ProcgenHeightmapHandleRef ref) { return module->resolveHeightmap(ref); }, [](ProcgenHeightmapHandleRef ref) { auto* owner = ModuleManager::getInstance("Procgen"); return owner ? owner->ownership_->heightmaps.erase(ref) : procgenBindingFailure(eve::DiagnosticCode::StaleHandle, "Procgen module is no longer loaded", "heightmap"); - }, - [](ProcgenHeightmapHandleRef ref) { - auto* owner = ModuleManager::getInstance("Procgen"); - return !owner || owner->ownership_->heightmaps.isStale(ref); }); }); + cls.addFunc("newRuntimeGeneration", [vm = cls.getHandle()](Procgen* value, uint32_t worldSeed) -> ssq::Table { + if (!value) + return eve::script::projectStatusResult( + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "runtime generation requires a Procgen module", "procgen") + .status(), + false, false); + return makeOwnedNativeProxy( + vm, value->newRuntimeGenerationHandle(worldSeed), + [value](ProcgenRuntimeGenerationHandleRef ref) { return value->resolveRuntimeGeneration(ref); }, + [](ProcgenRuntimeGenerationHandleRef ref) { + auto* owner = ModuleManager::getInstance("Procgen"); + return owner ? owner->release(ref) + : procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "Procgen module is no longer loaded", "runtimeGeneration"); + }); + }); + cls.addFunc("newPointGraph", [vm = cls.getHandle()](Procgen* value) -> ssq::Table { + if (!value) + return eve::script::projectStatusResult( + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, "Procgen module must not be null", + "procgen") + .status(), + false, false); + return makeOwnedNativeProxy( + vm, value->newPointGraphHandle(), + [value](ProcgenPointGraphHandleRef ref) { return value->resolvePointGraph(ref); }, + [](ProcgenPointGraphHandleRef ref) { + auto* owner = ModuleManager::getInstance("Procgen"); + return owner ? owner->release(ref) + : procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "Procgen module is no longer loaded", "pointGraph"); + }); + }); + cls.addFunc("newBiomeRules", [vm = cls.getHandle()](Procgen* value) -> ssq::Table { + if (!value) + return eve::script::projectStatusResult( + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, "Procgen module must not be null", + "procgen") + .status(), + false, false); + return makeOwnedNativeProxy( + vm, value->newBiomeRulesHandle(), + [value](ProcgenBiomeRulesHandleRef ref) { return value->resolveBiomeRules(ref); }, + [](ProcgenBiomeRulesHandleRef ref) { + auto* owner = ModuleManager::getInstance("Procgen"); + return owner ? owner->release(ref) + : procgenBindingFailure(eve::DiagnosticCode::StaleHandle, + "Procgen module is no longer loaded", "biomeRules"); + }); + }); + cls.addFunc("boxVolume", + [vm = cls.getHandle()](Procgen* value, float minX, float minY, float minZ, float maxX, float maxY, + float maxZ) -> ssq::Table { + if (!value) + return eve::script::projectStatusResult( + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "Procgen module must not be null", "procgen") + .status(), + false, false); + return makeOwnedSpatialProxy(vm, value->boxVolumeHandle(minX, minY, minZ, maxX, maxY, maxZ)); + }); + cls.addFunc("splineData", [vm = cls.getHandle()](Procgen* value, PointSet* points, float radius) -> ssq::Table { + const auto reference = nativeProxyReference(points); + if (!value || !reference) + return eve::script::projectStatusResult( + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "splineData requires an owned point set", "points") + .status(), + false, false); + return makeOwnedSpatialProxy(vm, value->splineDataHandle(*reference, radius)); + }); + cls.addFunc("sampleSpline", + [vm = cls.getHandle()](Procgen* value, PointSet* points, float spacing, uint32_t seed, + float jitter) -> ssq::Table { + const auto reference = nativeProxyReference(points); + if (!value || !reference) + return eve::script::projectStatusResult( + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "sampleSpline requires an owned point set", "points") + .status(), + false, false); + return makeOwnedPointSetProxy(vm, value->sampleSplineHandle(*reference, spacing, seed, jitter)); + }); + cls.addFunc("filterSplineDistance", + [vm = cls.getHandle()](Procgen* value, PointSet* input, PointSet* spline, float minDistance, + float maxDistance) -> ssq::Table { + const auto inputRef = nativeProxyReference(input); + const auto splineRef = nativeProxyReference(spline); + if (!value || !inputRef || !splineRef) + return eve::script::projectStatusResult( + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "filterSplineDistance requires owned point sets", "points") + .status(), + false, false); + return makeOwnedPointSetProxy( + vm, value->filterSplineDistanceHandle(*inputRef, *splineRef, minDistance, maxDistance)); + }); + cls.addFunc("selfPrune", [vm = cls.getHandle()](Procgen* value, PointSet* input, float radius) -> ssq::Table { + const auto inputRef = nativeProxyReference(input); + if (!value || !inputRef) + return eve::script::projectStatusResult( + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, "selfPrune requires owned points", + "points") + .status(), + false, false); + return makeOwnedPointSetProxy(vm, value->selfPruneHandle(*inputRef, radius)); + }); + cls.addFunc( + "publishCellInstances", + [vm = cls.getHandle()](Procgen* value, const std::string& prefix, ProcgenCellRequest* request, PointSet* points, + const std::string& assetAttribute, const std::string& defaultAsset) { + const auto pointsRef = nativeProxyReference(points); + if (!value || !request || !pointsRef) + return eve::script::projectResult( + vm, procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "publishCellInstances requires request and owned points", + "publishCellInstances")); + return eve::script::projectResult( + vm, value->publishCellInstances(prefix, *request, *pointsRef, assetAttribute, defaultAsset)); + }); + cls.addFunc("removeCellInstances", + [vm = cls.getHandle()](Procgen* value, const std::string& prefix, ProcgenCellRequest* request) { + if (!value || !request) + return eve::script::projectResult( + vm, procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "removeCellInstances requires a request", "request")); + return eve::script::projectResult(vm, value->removeCellInstances(prefix, *request)); + }); + cls.addFunc("removeInstances", [vm = cls.getHandle()](Procgen* value, const std::string& batchId) { + if (!value) + return eve::script::projectResult( + vm, procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "removeInstances requires Procgen", "procgen")); + return eve::script::projectResult(vm, value->removeInstances(batchId)); + }); + cls.addFunc( + "generateTexture", + [vm = cls.getHandle()](Procgen* value, const std::string& recipe, ScriptProcgenParams* params, + graphics::Graphics* gfx) -> ssq::Table { + if (!value || !params || !gfx) + return eve::script::projectStatusResult( + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "generateTexture requires params and Graphics", "generateTexture") + .status(), + false, false); + return projectBorrowedResult(vm, value->generateTextureBorrowed(recipe, params->reference, gfx), "texture"); + }); + cls.addFunc("generateMesh", + [vm = cls.getHandle()](Procgen* value, const std::string& recipe, ScriptProcgenParams* params, + graphics::Graphics* gfx) -> ssq::Table { + if (!value || !params || !gfx) + return eve::script::projectStatusResult( + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "generateMesh requires params and Graphics", "generateMesh") + .status(), + false, false); + return projectBorrowedResult(vm, value->generateMeshBorrowed(recipe, params->reference, gfx), + "mesh"); + }); + cls.addFunc("uploadMesh", + [vm = cls.getHandle()](Procgen* value, MeshBuild* mesh, graphics::Graphics* gfx) -> ssq::Table { + if (!value || !mesh || !gfx) + return eve::script::projectStatusResult( + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "uploadMesh requires a mesh and Graphics", "uploadMesh") + .status(), + false, false); + return projectBorrowedResult(vm, value->uploadMeshBorrowed(*mesh, *gfx), "mesh"); + }); cls.addFunc("deriveSeed", &Procgen::deriveSeed); - cls.addFunc("beginSystem", [vm = cls.getHandle()](Procgen*, const std::string& name, - uint32_t seed) -> ssq::Table { + cls.addFunc("beginSystem", [vm = cls.getHandle()](Procgen*, const std::string& name, uint32_t seed) -> ssq::Table { return makeOwnedProxy( vm, Procgen::beginSystemHandle(name, seed), [](ProcgenContextHandleRef ref) { return Procgen::release(ref); }); }); - cls.addFunc("beginCachedSystem", [vm = cls.getHandle()](Procgen*, const std::string& name, - uint32_t seed, - const std::string& buildKey) -> ssq::Table { - return makeOwnedProxy( - vm, Procgen::beginCachedSystemHandle(name, seed, buildKey), - [](ProcgenContextHandleRef ref) { return Procgen::release(ref); }); - }); - cls.addFunc("commitSystem", [vm = cls.getHandle()](Procgen* value, - ScriptProcgenContext* context) { + cls.addFunc("beginCachedSystem", + [vm = cls.getHandle()](Procgen*, const std::string& name, uint32_t seed, + const std::string& buildKey) -> ssq::Table { + return makeOwnedProxy( + vm, Procgen::beginCachedSystemHandle(name, seed, buildKey), + [](ProcgenContextHandleRef ref) { return Procgen::release(ref); }); + }); + cls.addFunc("commitSystem", [vm = cls.getHandle()](Procgen* value, ScriptProcgenContext* context) { if (!value || !context) return eve::script::projectResult( vm, procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, "commitSystem requires a context proxy", "context")); return eve::script::projectResult(vm, value->commitSystem(context->reference)); }); - cls.addFunc("abortSystem", [vm = cls.getHandle()](Procgen* value, - ScriptProcgenContext* context) { + cls.addFunc("abortSystem", [vm = cls.getHandle()](Procgen* value, ScriptProcgenContext* context) { if (!value || !context) return eve::script::projectResult( vm, procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, "abortSystem requires a context proxy", "context")); return eve::script::projectResult(vm, value->abortSystem(context->reference)); }); + cls.addFunc("getSystemOutput", + [vm = cls.getHandle()](Procgen* value, const std::string& system, const std::string& output) { + if (!value) + return makeOwnedPointSetProxy(vm, procgenBindingFailure( + eve::DiagnosticCode::InvalidArgument, + "Procgen module must not be null", "procgen")); + return makeOwnedPointSetProxy(vm, value->getSystemOutputHandle(system, output)); + }); + cls.addFunc("getSystemDebugStage", + [vm = cls.getHandle()](Procgen* value, const std::string& system, const std::string& stage) { + if (!value) + return makeOwnedPointSetProxy(vm, procgenBindingFailure( + eve::DiagnosticCode::InvalidArgument, + "Procgen module must not be null", "procgen")); + return makeOwnedPointSetProxy(vm, value->getSystemDebugStageHandle(system, stage)); + }); + cls.addFunc("getPreviousSystemDebugStage", + [vm = cls.getHandle()](Procgen* value, const std::string& system, const std::string& stage) { + if (!value) + return makeOwnedPointSetProxy(vm, procgenBindingFailure( + eve::DiagnosticCode::InvalidArgument, + "Procgen module must not be null", "procgen")); + return makeOwnedPointSetProxy(vm, value->getPreviousSystemDebugStageHandle(system, stage)); + }); cls.addFunc("removeSystem", [vm = cls.getHandle()](Procgen* value, const std::string& name) { if (!value) return eve::script::projectResult( @@ -2820,14 +3018,13 @@ void Procgen::expose(ssq::Class &cls) { cls.addFunc("getAlgorithmCount", &Procgen::getAlgorithmCount); cls.addFunc("getAlgorithmId", &Procgen::getAlgorithmId); cls.addFunc("hasAlgorithm", &Procgen::hasAlgorithm); - cls.addFunc("getAlgorithmSchema", [vm = cls.getHandle()](Procgen* value, - const std::string& algorithm) { + cls.addFunc("getAlgorithmSchema", [vm = cls.getHandle()](Procgen* value, const std::string& algorithm) { if (!value) return eve::script::projectStatusResult( - vm, procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "algorithm schema requires a Procgen module", "procgen") - .status(), + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "algorithm schema requires a Procgen module", "procgen") + .status(), false, false); return projectRecipeDescriptorResult(vm, value->getAlgorithmSchema(algorithm)); }); @@ -2848,14 +3045,22 @@ void Procgen::expose(ssq::Class &cls) { cls.addFunc("isAlgorithmParamAdvanced", &Procgen::isAlgorithmParamAdvanced); cls.addFunc("getAlgorithmParamChoiceCount", &Procgen::getAlgorithmParamChoiceCount); cls.addFunc("getAlgorithmParamChoice", &Procgen::getAlgorithmParamChoice); - cls.addFunc("applyAlgorithmDefaults", [vm = cls.getHandle()](Procgen* value, - const std::string& algorithm, - ScriptProcgenParams* params) { - if (!value || !params) + cls.addFunc("applyAlgorithmDefaults", + [vm = cls.getHandle()](Procgen* value, const std::string& algorithm, ScriptProcgenParams* params) { + if (!value || !params) + return eve::script::projectResult( + vm, procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "algorithm defaults require a params proxy", "params")); + return eve::script::projectResult(vm, value->applyAlgorithmDefaults(algorithm, params->reference)); + }); + cls.addFunc("generateTo", [vm = cls.getHandle()](Procgen* value, const std::string& algorithm, + ScriptProcgenParams* params, OutputSpec* output) { + const auto outputRef = nativeProxyReference(output); + if (!value || !params || !outputRef) return eve::script::projectResult( vm, procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, - "algorithm defaults require a params proxy", "params")); - return eve::script::projectResult(vm, value->applyAlgorithmDefaults(algorithm, params->reference)); + "generateTo requires params and owned output", "generateTo")); + return eve::script::projectResult(vm, value->generateTo(algorithm, params->reference, *outputRef)); }); cls.addFunc("autotileGrid", [vm = cls.getHandle()](Procgen* value, ScriptProcgenGrid* grid) { if (!value || !grid) @@ -2868,153 +3073,136 @@ void Procgen::expose(ssq::Class &cls) { cls.addFunc("gridToJson", [vm = cls.getHandle()](Procgen* value, ScriptProcgenGrid* grid) { if (!value || !grid) return eve::script::projectResult( - vm, procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, - "gridToJson requires a grid proxy", "grid"), + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "gridToJson requires a grid proxy", "grid"), [](std::string&& json) { return eve::Value(std::move(json)); }); - return eve::script::projectResult( - vm, value->gridToJson(grid->reference), - [](std::string&& json) { return eve::Value(std::move(json)); }); + return eve::script::projectResult(vm, value->gridToJson(grid->reference), + [](std::string&& json) { return eve::Value(std::move(json)); }); }); cls.addFunc("getTextureRecipeCount", &Procgen::getTextureRecipeCount); cls.addFunc("getTextureRecipeId", &Procgen::getTextureRecipeId); cls.addFunc("hasTextureRecipe", &Procgen::hasTextureRecipe); - cls.addFunc("getTextureRecipeSchema", [vm = cls.getHandle()](Procgen* value, - const std::string& recipe) { + cls.addFunc("getTextureRecipeSchema", [vm = cls.getHandle()](Procgen* value, const std::string& recipe) { if (!value) return eve::script::projectStatusResult( - vm, procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "texture schema requires a Procgen module", "procgen") - .status(), + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "texture schema requires a Procgen module", "procgen") + .status(), false, false); return projectRecipeDescriptorResult(vm, value->getTextureRecipeSchema(recipe)); }); - cls.addFunc("applyTextureRecipeDefaults", [vm = cls.getHandle()](Procgen* value, - const std::string& recipe, - ScriptProcgenParams* params) { - if (!value || !params) - return eve::script::projectResult( - vm, procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, - "texture defaults require a params proxy", "params")); - return eve::script::projectResult(vm, value->applyTextureRecipeDefaults(recipe, params->reference)); - }); + cls.addFunc("applyTextureRecipeDefaults", + [vm = cls.getHandle()](Procgen* value, const std::string& recipe, ScriptProcgenParams* params) { + if (!value || !params) + return eve::script::projectResult( + vm, procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "texture defaults require a params proxy", "params")); + return eve::script::projectResult(vm, value->applyTextureRecipeDefaults(recipe, params->reference)); + }); cls.addFunc("getPbrRecipeCount", &Procgen::getPbrRecipeCount); cls.addFunc("getPbrRecipeId", &Procgen::getPbrRecipeId); cls.addFunc("hasPbrRecipe", &Procgen::hasPbrRecipe); - cls.addFunc("getPbrRecipeSchema", [vm = cls.getHandle()](Procgen* value, - const std::string& recipe) { + cls.addFunc("getPbrRecipeSchema", [vm = cls.getHandle()](Procgen* value, const std::string& recipe) { if (!value) return eve::script::projectStatusResult( - vm, procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "PBR schema requires a Procgen module", "procgen") - .status(), + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "PBR schema requires a Procgen module", "procgen") + .status(), false, false); return projectRecipeDescriptorResult(vm, value->getPbrRecipeSchema(recipe)); }); - cls.addFunc("applyPbrRecipeDefaults", [vm = cls.getHandle()](Procgen* value, - const std::string& recipe, - ScriptProcgenParams* params) { - if (!value || !params) - return eve::script::projectResult( - vm, procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, - "PBR defaults require a params proxy", "params")); - return eve::script::projectResult(vm, value->applyPbrRecipeDefaults(recipe, params->reference)); - }); - cls.addFunc("buildArtifact", [vm = cls.getHandle()](Procgen* value, - const std::string& recipeId, - ScriptProcgenParams* params, - const std::string& artifactIdentity) { - if (!value) - return eve::script::projectResult( - vm, procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "procgen module must not be null", "procgen"), - [](GeneratedArtifact&& artifact) { return artifactProjection(std::move(artifact)); }); - const auto parsed = ArtifactId::parse(artifactIdentity); - if (!parsed || parsed->isNil()) - return eve::script::projectResult( - vm, procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "artifact identity must be a non-nil canonical UUID", "artifactIdentity"), - [](GeneratedArtifact&& artifact) { return artifactProjection(std::move(artifact)); }); - if (!params) - return eve::script::projectResult( - vm, procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "buildArtifact requires a params proxy", "params"), - [](GeneratedArtifact&& artifact) { return artifactProjection(std::move(artifact)); }); - return eve::script::projectResult( - vm, value->buildArtifact(recipeId, params->reference, *parsed), - [](GeneratedArtifact&& artifact) { return artifactProjection(std::move(artifact)); }); - }); - cls.addFunc("publishArtifact", [vm = cls.getHandle()](Procgen* value, - const std::string& recipeId, - ScriptProcgenParams* params, - const std::string& artifactIdentity, - bool scene, - bool graphics, - bool physics, - bool map) { - if (!value) - return eve::script::projectResult( - vm, procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "procgen module must not be null", "procgen"), - [](ArtifactPublishReceipt&& receipt) { - return publishReceiptProjection(std::move(receipt)); + cls.addFunc("applyPbrRecipeDefaults", + [vm = cls.getHandle()](Procgen* value, const std::string& recipe, ScriptProcgenParams* params) { + if (!value || !params) + return eve::script::projectResult( + vm, procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "PBR defaults require a params proxy", "params")); + return eve::script::projectResult(vm, value->applyPbrRecipeDefaults(recipe, params->reference)); }); - const auto parsed = ArtifactId::parse(artifactIdentity); - if (!parsed || parsed->isNil()) + cls.addFunc( + "buildArtifact", [vm = cls.getHandle()](Procgen* value, const std::string& recipeId, + ScriptProcgenParams* params, const std::string& artifactIdentity) { + if (!value) + return eve::script::projectResult( + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "procgen module must not be null", "procgen"), + [](GeneratedArtifact&& artifact) { return artifactProjection(std::move(artifact)); }); + const auto parsed = ArtifactId::parse(artifactIdentity); + if (!parsed || parsed->isNil()) + return eve::script::projectResult( + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "artifact identity must be a non-nil canonical UUID", + "artifactIdentity"), + [](GeneratedArtifact&& artifact) { return artifactProjection(std::move(artifact)); }); + if (!params) + return eve::script::projectResult( + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "buildArtifact requires a params proxy", "params"), + [](GeneratedArtifact&& artifact) { return artifactProjection(std::move(artifact)); }); return eve::script::projectResult( - vm, procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "artifact identity must be a non-nil canonical UUID", "artifactIdentity"), - [](ArtifactPublishReceipt&& receipt) { - return publishReceiptProjection(std::move(receipt)); - }); - ArtifactPublishOptions options; - options.scene = scene; - options.graphics = graphics; - options.physics = physics; - options.map = map; - if (!params) + vm, value->buildArtifact(recipeId, params->reference, *parsed), + [](GeneratedArtifact&& artifact) { return artifactProjection(std::move(artifact)); }); + }); + cls.addFunc( + "publishArtifact", + [vm = cls.getHandle()](Procgen* value, const std::string& recipeId, ScriptProcgenParams* params, + const std::string& artifactIdentity, bool scene, bool graphics, bool physics, bool map) { + if (!value) + return eve::script::projectResult( + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "procgen module must not be null", "procgen"), + [](ArtifactPublishReceipt&& receipt) { return publishReceiptProjection(std::move(receipt)); }); + const auto parsed = ArtifactId::parse(artifactIdentity); + if (!parsed || parsed->isNil()) + return eve::script::projectResult( + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "artifact identity must be a non-nil canonical UUID", + "artifactIdentity"), + [](ArtifactPublishReceipt&& receipt) { return publishReceiptProjection(std::move(receipt)); }); + ArtifactPublishOptions options; + options.scene = scene; + options.graphics = graphics; + options.physics = physics; + options.map = map; + if (!params) + return eve::script::projectResult( + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "publishArtifact requires a params proxy", "params"), + [](ArtifactPublishReceipt&& receipt) { return publishReceiptProjection(std::move(receipt)); }); return eve::script::projectResult( - vm, procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "publishArtifact requires a params proxy", "params"), - [](ArtifactPublishReceipt&& receipt) { - return publishReceiptProjection(std::move(receipt)); - }); - return eve::script::projectResult( - vm, value->publishArtifact(recipeId, params->reference, *parsed, options), - [](ArtifactPublishReceipt&& receipt) { - return publishReceiptProjection(std::move(receipt)); - }); - }); + vm, value->publishArtifact(recipeId, params->reference, *parsed, options), + [](ArtifactPublishReceipt&& receipt) { return publishReceiptProjection(std::move(receipt)); }); + }); cls.addFunc("getMeshRecipeCount", &Procgen::getMeshRecipeCount); cls.addFunc("getMeshRecipeId", &Procgen::getMeshRecipeId); cls.addFunc("hasMeshRecipe", &Procgen::hasMeshRecipe); - cls.addFunc("getMeshRecipeSchema", [vm = cls.getHandle()](Procgen* value, - const std::string& recipe) { + cls.addFunc("getMeshRecipeSchema", [vm = cls.getHandle()](Procgen* value, const std::string& recipe) { if (!value) return eve::script::projectStatusResult( - vm, procgenBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "mesh schema requires a Procgen module", "procgen") - .status(), + vm, + procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "mesh schema requires a Procgen module", "procgen") + .status(), false, false); return projectRecipeDescriptorResult(vm, value->getMeshRecipeSchema(recipe)); }); - cls.addFunc("applyMeshRecipeDefaults", [vm = cls.getHandle()](Procgen* value, - const std::string& recipe, - ScriptProcgenParams* params) { - if (!value || !params) - return eve::script::projectResult( - vm, procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, - "mesh defaults require a params proxy", "params")); - return eve::script::projectResult(vm, value->applyMeshRecipeDefaults(recipe, params->reference)); - }); + cls.addFunc("applyMeshRecipeDefaults", + [vm = cls.getHandle()](Procgen* value, const std::string& recipe, ScriptProcgenParams* params) { + if (!value || !params) + return eve::script::projectResult( + vm, procgenBindingFailure(eve::DiagnosticCode::InvalidArgument, + "mesh defaults require a params proxy", "params")); + return eve::script::projectResult(vm, value->applyMeshRecipeDefaults(recipe, params->reference)); + }); } } // namespace eve::procgen diff --git a/src/modules/procgen/Procgen.h b/src/modules/procgen/Procgen.h index 46ddcc24..1680e677 100644 --- a/src/modules/procgen/Procgen.h +++ b/src/modules/procgen/Procgen.h @@ -2,7 +2,6 @@ #include "common/Module.h" #include "common/SquirrelOwnership.h" -#include "procgen/core/ProcgenCore.h" #include "procgen/Biome.h" #include "procgen/Grid2D.h" #include "procgen/MeshBuild.h" @@ -10,21 +9,22 @@ #include "procgen/Palette.h" #include "procgen/ParamSchema.h" #include "procgen/Params.h" -#include "procgen/PointSet.h" #include "procgen/PointGraph.h" +#include "procgen/PointSet.h" #include "procgen/ProcgenSystem.h" #include "procgen/RuntimeGeneration.h" #include "procgen/ShapeGrammar.h" #include "procgen/SpatialData.h" #include "procgen/algorithms/LSystem.h" +#include "procgen/core/ProcgenCore.h" #include "procgen/heightmap/Heightmap.h" #include "procgen/heightmap/TerrainSampler.h" #include "procgen/texture/CloudField.h" #include "procgen/texture/CloudShadow.h" -#include #include #include +#include #include #include @@ -79,25 +79,25 @@ struct ProcgenBiomeRulesHandleTag {}; struct ProcgenShapeGrammarHandleTag {}; /** @brief Handle domain for module-owned L-system engines. */ struct ProcgenLSystemHandleTag {}; -using ProcgenParamsHandleRef = eve::script::RuntimeHandleRef; -using ProcgenGridHandleRef = eve::script::RuntimeHandleRef; -using ProcgenContextHandleRef = eve::script::RuntimeHandleRef; -using ProcgenOutputHandleRef = eve::script::RuntimeHandleRef; -using ProcgenPointSetHandleRef = eve::script::RuntimeHandleRef; -using ProcgenTerrainSamplerHandleRef = eve::script::RuntimeHandleRef; -using ProcgenHeightmapHandleRef = eve::script::RuntimeHandleRef; -using ProcgenCloudFieldHandleRef = eve::script::RuntimeHandleRef; -using ProcgenCloudShadowHandleRef = eve::script::RuntimeHandleRef; -using ProcgenPbrMaterialHandleRef = eve::script::RuntimeHandleRef; -using ProcgenMeshBuildHandleRef = eve::script::RuntimeHandleRef; -using ProcgenImageHandleRef = eve::script::RuntimeHandleRef; -using ProcgenNormalImageHandleRef = eve::script::RuntimeHandleRef; -using ProcgenSpatialDataHandleRef = eve::script::RuntimeHandleRef; +using ProcgenParamsHandleRef = eve::script::RuntimeHandleRef; +using ProcgenGridHandleRef = eve::script::RuntimeHandleRef; +using ProcgenContextHandleRef = eve::script::RuntimeHandleRef; +using ProcgenOutputHandleRef = eve::script::RuntimeHandleRef; +using ProcgenPointSetHandleRef = eve::script::RuntimeHandleRef; +using ProcgenTerrainSamplerHandleRef = eve::script::RuntimeHandleRef; +using ProcgenHeightmapHandleRef = eve::script::RuntimeHandleRef; +using ProcgenCloudFieldHandleRef = eve::script::RuntimeHandleRef; +using ProcgenCloudShadowHandleRef = eve::script::RuntimeHandleRef; +using ProcgenPbrMaterialHandleRef = eve::script::RuntimeHandleRef; +using ProcgenMeshBuildHandleRef = eve::script::RuntimeHandleRef; +using ProcgenImageHandleRef = eve::script::RuntimeHandleRef; +using ProcgenNormalImageHandleRef = eve::script::RuntimeHandleRef; +using ProcgenSpatialDataHandleRef = eve::script::RuntimeHandleRef; using ProcgenRuntimeGenerationHandleRef = eve::script::RuntimeHandleRef; -using ProcgenPointGraphHandleRef = eve::script::RuntimeHandleRef; -using ProcgenBiomeRulesHandleRef = eve::script::RuntimeHandleRef; -using ProcgenShapeGrammarHandleRef = eve::script::RuntimeHandleRef; -using ProcgenLSystemHandleRef = eve::script::RuntimeHandleRef; +using ProcgenPointGraphHandleRef = eve::script::RuntimeHandleRef; +using ProcgenBiomeRulesHandleRef = eve::script::RuntimeHandleRef; +using ProcgenShapeGrammarHandleRef = eve::script::RuntimeHandleRef; +using ProcgenLSystemHandleRef = eve::script::RuntimeHandleRef; /** * @brief Procedural generation module. @@ -118,8 +118,7 @@ public: */ [[nodiscard]] static eve::Result newParamsHandle(); /** @brief Resolves parameters as a non-owning observation. */ - [[nodiscard]] static eve::script::Borrowed resolve( - ProcgenParamsHandleRef reference) noexcept; + [[nodiscard]] static eve::script::Borrowed resolve(ProcgenParamsHandleRef reference) noexcept; /** @brief Releases module-owned parameters. */ [[nodiscard]] static eve::Result release(ProcgenParamsHandleRef reference); /** @brief Reports whether a parameter reference is stale. */ @@ -127,18 +126,15 @@ public: /** @brief Allocates an owning output specification. */ [[nodiscard]] eve::Result newOutputHandle(); /** @brief Resolves an output specification as a borrowed view. */ - [[nodiscard]] eve::script::Borrowed resolveOutput( - ProcgenOutputHandleRef reference) noexcept; + [[nodiscard]] eve::script::Borrowed resolveOutput(ProcgenOutputHandleRef reference) noexcept; /** @brief Releases an output specification. */ [[nodiscard]] eve::Result releaseOutput(ProcgenOutputHandleRef reference); /** @brief Reports whether an output specification reference is stale. */ [[nodiscard]] bool isOutputStale(ProcgenOutputHandleRef reference) const noexcept; /** @brief Allocates module-owned grid storage and returns its handle. */ - [[nodiscard]] static eve::Result newGridHandle( - int width, int height); + [[nodiscard]] static eve::Result newGridHandle(int width, int height); /** @brief Resolves a grid as a non-owning observation. */ - [[nodiscard]] static eve::script::Borrowed resolve( - ProcgenGridHandleRef reference) noexcept; + [[nodiscard]] static eve::script::Borrowed resolve(ProcgenGridHandleRef reference) noexcept; /** @brief Releases module-owned grid storage. */ [[nodiscard]] static eve::Result release(ProcgenGridHandleRef reference); /** @brief Reports whether a grid reference is stale. */ @@ -148,159 +144,153 @@ public: /** @brief Allocates a point-set intermediate and returns its owner handle. */ [[nodiscard]] eve::Result newPointSetHandle(); /** @brief Resolves a point set as a borrowed view. */ - [[nodiscard]] eve::script::Borrowed resolvePointSet( - ProcgenPointSetHandleRef reference) noexcept; + [[nodiscard]] eve::script::Borrowed resolvePointSet(ProcgenPointSetHandleRef reference) noexcept; /** @brief Releases a point-set intermediate. */ [[nodiscard]] eve::Result releasePointSet(ProcgenPointSetHandleRef reference); /** @brief Reports whether a point-set handle is stale. */ [[nodiscard]] bool isPointSetStale(ProcgenPointSetHandleRef reference) const noexcept; - [[nodiscard]] eve::Result sampleGridHandle( - int width, int depth, float spacing, uint32_t seed, float jitter); - [[nodiscard]] eve::Result filterHeightHandle( - ProcgenPointSetHandleRef input, float minHeight, float maxHeight); - [[nodiscard]] eve::Result filterDensityHandle( - ProcgenPointSetHandleRef input, float minDensity, float maxDensity); - [[nodiscard]] eve::Result filterBoxHandle( - ProcgenPointSetHandleRef input, float minX, float minY, float minZ, float maxX, - float maxY, float maxZ, bool invert = false); - [[nodiscard]] eve::Result filterSlopeHandle( - ProcgenPointSetHandleRef input, float minDegrees, float maxDegrees); - [[nodiscard]] eve::Result filterPolygonHandle( - ProcgenPointSetHandleRef input, ProcgenPointSetHandleRef polygon, bool invert = false); + [[nodiscard]] eve::Result sampleGridHandle(int width, int depth, float spacing, + uint32_t seed, float jitter); + [[nodiscard]] eve::Result filterHeightHandle(ProcgenPointSetHandleRef input, + float minHeight, float maxHeight); + [[nodiscard]] eve::Result filterDensityHandle(ProcgenPointSetHandleRef input, + float minDensity, float maxDensity); + [[nodiscard]] eve::Result filterBoxHandle(ProcgenPointSetHandleRef input, float minX, + float minY, float minZ, float maxX, float maxY, + float maxZ, bool invert = false); + [[nodiscard]] eve::Result filterSlopeHandle(ProcgenPointSetHandleRef input, + float minDegrees, float maxDegrees); + [[nodiscard]] eve::Result filterPolygonHandle(ProcgenPointSetHandleRef input, + ProcgenPointSetHandleRef polygon, + bool invert = false); [[nodiscard]] eve::Result filterSplineDistanceHandle( - ProcgenPointSetHandleRef input, ProcgenPointSetHandleRef controlPoints, - float minDistance, float maxDistance); - [[nodiscard]] eve::Result excludeRadiusHandle( - ProcgenPointSetHandleRef input, float x, float z, float radius); - [[nodiscard]] eve::Result jitterPointsHandle( - ProcgenPointSetHandleRef input, uint32_t seed, float amountX, float amountZ); - [[nodiscard]] eve::Result selfPruneHandle( - ProcgenPointSetHandleRef input, float radius); - [[nodiscard]] eve::Result projectToHeightmapHandle( - ProcgenPointSetHandleRef input, ProcgenHeightmapHandleRef heightmap, float originX, - float originZ, float cellSize, float heightScale); - [[nodiscard]] eve::Result sampleSplineHandle( - ProcgenPointSetHandleRef controlPoints, float spacing, uint32_t seed, - float lateralJitter); - [[nodiscard]] eve::Result poissonDiskHandle( - int width, int depth, float radius, uint32_t seed, int maxPoints); - [[nodiscard]] eve::Result mergePointsHandle( - ProcgenPointSetHandleRef first, ProcgenPointSetHandleRef second); - [[nodiscard]] eve::Result transformPointsHandle( - ProcgenPointSetHandleRef input, float translateX, float translateY, float translateZ, - float yawDegrees, float scaleX, float scaleY, float scaleZ); - [[nodiscard]] eve::Result filterFloatAttributeHandle( - ProcgenPointSetHandleRef input, const std::string& name, float minValue, float maxValue, - bool invert); - [[nodiscard]] eve::Result filterStringAttributeHandle( - ProcgenPointSetHandleRef input, const std::string& name, const std::string& value, - bool invert); - [[nodiscard]] eve::Result densityCullHandle( - ProcgenPointSetHandleRef input, uint32_t seed, float multiplier); + ProcgenPointSetHandleRef input, ProcgenPointSetHandleRef controlPoints, float minDistance, float maxDistance); + [[nodiscard]] eve::Result excludeRadiusHandle(ProcgenPointSetHandleRef input, float x, + float z, float radius); + [[nodiscard]] eve::Result jitterPointsHandle(ProcgenPointSetHandleRef input, + uint32_t seed, float amountX, float amountZ); + [[nodiscard]] eve::Result selfPruneHandle(ProcgenPointSetHandleRef input, float radius); + [[nodiscard]] eve::Result projectToHeightmapHandle(ProcgenPointSetHandleRef input, + ProcgenHeightmapHandleRef heightmap, + float originX, float originZ, + float cellSize, float heightScale); + [[nodiscard]] eve::Result sampleSplineHandle(ProcgenPointSetHandleRef controlPoints, + float spacing, uint32_t seed, + float lateralJitter); + [[nodiscard]] eve::Result poissonDiskHandle(int width, int depth, float radius, + uint32_t seed, int maxPoints); + [[nodiscard]] eve::Result mergePointsHandle(ProcgenPointSetHandleRef first, + ProcgenPointSetHandleRef second); + [[nodiscard]] eve::Result transformPointsHandle(ProcgenPointSetHandleRef input, + float translateX, float translateY, + float translateZ, float yawDegrees, + float scaleX, float scaleY, float scaleZ); + [[nodiscard]] eve::Result filterFloatAttributeHandle(ProcgenPointSetHandleRef input, + const std::string &name, + float minValue, float maxValue, + bool invert); + [[nodiscard]] eve::Result filterStringAttributeHandle(ProcgenPointSetHandleRef input, + const std::string &name, + const std::string &value, + bool invert); + [[nodiscard]] eve::Result densityCullHandle(ProcgenPointSetHandleRef input, uint32_t seed, + float multiplier); // --- Spatial data and composable PCG domains --- - [[nodiscard]] eve::Result pointDataHandle( - ProcgenPointSetHandleRef points); - [[nodiscard]] eve::Result boxVolumeHandle( - float minX, float minY, float minZ, float maxX, float maxY, float maxZ); - [[nodiscard]] eve::Result sphereVolumeHandle( - float x, float y, float z, float radius); - [[nodiscard]] eve::Result splineDataHandle( - ProcgenPointSetHandleRef controlPoints, float radius); - [[nodiscard]] eve::Result heightfieldDataHandle( - ProcgenHeightmapHandleRef heightmap, float originX, float originZ, float cellSize, - float heightScale); - [[nodiscard]] eve::Result unionSpatialHandle( - ProcgenSpatialDataHandleRef left, ProcgenSpatialDataHandleRef right); - [[nodiscard]] eve::Result intersectSpatialHandle( - ProcgenSpatialDataHandleRef left, ProcgenSpatialDataHandleRef right); - [[nodiscard]] eve::Result differenceSpatialHandle( - ProcgenSpatialDataHandleRef left, ProcgenSpatialDataHandleRef right); - [[nodiscard]] eve::Result sampleSpatialHandle( - ProcgenSpatialDataHandleRef spatial, float spacing, uint32_t seed, float jitter); - [[nodiscard]] eve::Result filterSpatialHandle( - ProcgenPointSetHandleRef input, ProcgenSpatialDataHandleRef spatial, bool invert); - [[nodiscard]] eve::Result projectToSpatialHandle( - ProcgenPointSetHandleRef input, ProcgenSpatialDataHandleRef spatial); + [[nodiscard]] eve::Result pointDataHandle(ProcgenPointSetHandleRef points); + [[nodiscard]] eve::Result boxVolumeHandle(float minX, float minY, float minZ, + float maxX, float maxY, float maxZ); + [[nodiscard]] eve::Result sphereVolumeHandle(float x, float y, float z, float radius); + [[nodiscard]] eve::Result splineDataHandle(ProcgenPointSetHandleRef controlPoints, + float radius); + [[nodiscard]] eve::Result heightfieldDataHandle(ProcgenHeightmapHandleRef heightmap, + float originX, float originZ, + float cellSize, float heightScale); + [[nodiscard]] eve::Result unionSpatialHandle(ProcgenSpatialDataHandleRef left, + ProcgenSpatialDataHandleRef right); + [[nodiscard]] eve::Result intersectSpatialHandle(ProcgenSpatialDataHandleRef left, + ProcgenSpatialDataHandleRef right); + [[nodiscard]] eve::Result differenceSpatialHandle(ProcgenSpatialDataHandleRef left, + ProcgenSpatialDataHandleRef right); + [[nodiscard]] eve::Result sampleSpatialHandle(ProcgenSpatialDataHandleRef spatial, + float spacing, uint32_t seed, float jitter); + [[nodiscard]] eve::Result filterSpatialHandle(ProcgenPointSetHandleRef input, + ProcgenSpatialDataHandleRef spatial, + bool invert); + [[nodiscard]] eve::Result projectToSpatialHandle(ProcgenPointSetHandleRef input, + ProcgenSpatialDataHandleRef spatial); - [[nodiscard]] eve::Result newRuntimeGenerationHandle( - uint32_t worldSeed); - [[nodiscard]] eve::Result newPointGraphHandle(); - [[nodiscard]] eve::Result newBiomeRulesHandle(); - [[nodiscard]] eve::Result newShapeGrammarHandle(); - [[nodiscard]] eve::Result newLSystemHandle(); - [[nodiscard]] eve::script::Borrowed resolveSpatialData( - ProcgenSpatialDataHandleRef reference) noexcept; + [[nodiscard]] eve::Result newRuntimeGenerationHandle(uint32_t worldSeed); + [[nodiscard]] eve::Result newPointGraphHandle(); + [[nodiscard]] eve::Result newBiomeRulesHandle(); + [[nodiscard]] eve::Result newShapeGrammarHandle(); + [[nodiscard]] eve::Result newLSystemHandle(); + [[nodiscard]] eve::script::Borrowed resolveSpatialData(ProcgenSpatialDataHandleRef reference) noexcept; [[nodiscard]] eve::script::Borrowed resolveRuntimeGeneration( ProcgenRuntimeGenerationHandleRef reference) noexcept; - [[nodiscard]] eve::script::Borrowed resolvePointGraph( - ProcgenPointGraphHandleRef reference) noexcept; - [[nodiscard]] eve::script::Borrowed resolveBiomeRules( - ProcgenBiomeRulesHandleRef reference) noexcept; + [[nodiscard]] eve::script::Borrowed resolvePointGraph(ProcgenPointGraphHandleRef reference) noexcept; + [[nodiscard]] eve::script::Borrowed resolveBiomeRules(ProcgenBiomeRulesHandleRef reference) noexcept; [[nodiscard]] eve::script::Borrowed resolveShapeGrammar( ProcgenShapeGrammarHandleRef reference) noexcept; - [[nodiscard]] eve::script::Borrowed resolveLSystem( - ProcgenLSystemHandleRef reference) noexcept; - [[nodiscard]] eve::Result release(ProcgenSpatialDataHandleRef reference); - [[nodiscard]] eve::Result release(ProcgenRuntimeGenerationHandleRef reference); - [[nodiscard]] eve::Result release(ProcgenPointGraphHandleRef reference); - [[nodiscard]] eve::Result release(ProcgenBiomeRulesHandleRef reference); - [[nodiscard]] eve::Result release(ProcgenShapeGrammarHandleRef reference); - [[nodiscard]] eve::Result release(ProcgenLSystemHandleRef reference); - [[nodiscard]] bool isStale(ProcgenSpatialDataHandleRef reference) const noexcept; - [[nodiscard]] bool isStale(ProcgenRuntimeGenerationHandleRef reference) const noexcept; - [[nodiscard]] bool isStale(ProcgenPointGraphHandleRef reference) const noexcept; - [[nodiscard]] bool isStale(ProcgenBiomeRulesHandleRef reference) const noexcept; - [[nodiscard]] bool isStale(ProcgenShapeGrammarHandleRef reference) const noexcept; - [[nodiscard]] bool isStale(ProcgenLSystemHandleRef reference) const noexcept; + [[nodiscard]] eve::script::Borrowed resolveLSystem(ProcgenLSystemHandleRef reference) noexcept; + [[nodiscard]] eve::Result release(ProcgenSpatialDataHandleRef reference); + [[nodiscard]] eve::Result release(ProcgenRuntimeGenerationHandleRef reference); + [[nodiscard]] eve::Result release(ProcgenPointGraphHandleRef reference); + [[nodiscard]] eve::Result release(ProcgenBiomeRulesHandleRef reference); + [[nodiscard]] eve::Result release(ProcgenShapeGrammarHandleRef reference); + [[nodiscard]] eve::Result release(ProcgenLSystemHandleRef reference); + [[nodiscard]] bool isStale(ProcgenSpatialDataHandleRef reference) const noexcept; + [[nodiscard]] bool isStale(ProcgenRuntimeGenerationHandleRef reference) const noexcept; + [[nodiscard]] bool isStale(ProcgenPointGraphHandleRef reference) const noexcept; + [[nodiscard]] bool isStale(ProcgenBiomeRulesHandleRef reference) const noexcept; + [[nodiscard]] bool isStale(ProcgenShapeGrammarHandleRef reference) const noexcept; + [[nodiscard]] bool isStale(ProcgenLSystemHandleRef reference) const noexcept; - [[nodiscard]] eve::Result publishInstances( - const std::string& batchId, ProcgenPointSetHandleRef points, - const std::string& assetAttribute, const std::string& defaultAsset); - [[nodiscard]] eve::Result removeInstances(const std::string& batchId); - [[nodiscard]] eve::Result publishCellInstances( - const std::string& prefix, const ProcgenCellRequest& request, - ProcgenPointSetHandleRef points, const std::string& assetAttribute, - const std::string& defaultAsset); - [[nodiscard]] eve::Result removeCellInstances( - const std::string& prefix, const ProcgenCellRequest& request); - int getPublishedInstanceCount(const std::string& batchId) const; - int getPublishedCreatedCount(const std::string& batchId) const; - int getPublishedReusedCount(const std::string& batchId) const; - int getPublishedRemovedCount(const std::string& batchId) const; - uint32_t deriveSeed(uint32_t parent, const std::string& scope) const; + [[nodiscard]] eve::Result publishInstances(const std::string &batchId, ProcgenPointSetHandleRef points, + const std::string &assetAttribute, + const std::string &defaultAsset); + [[nodiscard]] eve::Result removeInstances(const std::string &batchId); + [[nodiscard]] eve::Result publishCellInstances(const std::string &prefix, const ProcgenCellRequest &request, + ProcgenPointSetHandleRef points, + const std::string &assetAttribute, + const std::string &defaultAsset); + [[nodiscard]] eve::Result removeCellInstances(const std::string &prefix, const ProcgenCellRequest &request); + int getPublishedInstanceCount(const std::string &batchId) const; + int getPublishedCreatedCount(const std::string &batchId) const; + int getPublishedReusedCount(const std::string &batchId) const; + int getPublishedRemovedCount(const std::string &batchId) const; + uint32_t deriveSeed(uint32_t parent, const std::string &scope) const; // --- Atomic script rebuilds --- /** @brief Starts a module-owned rebuild context and returns its handle. */ - [[nodiscard]] static eve::Result beginSystemHandle( - const std::string& name, uint32_t seed); + [[nodiscard]] static eve::Result beginSystemHandle(const std::string &name, uint32_t seed); /** @brief Starts a cached module-owned rebuild context and returns its handle. */ - [[nodiscard]] static eve::Result beginCachedSystemHandle( - const std::string& name, uint32_t seed, const std::string& buildKey); + [[nodiscard]] static eve::Result beginCachedSystemHandle(const std::string &name, + uint32_t seed, + const std::string &buildKey); /** @brief Resolves a rebuild context as a non-owning observation. */ - [[nodiscard]] static eve::script::Borrowed resolve( - ProcgenContextHandleRef reference) noexcept; + [[nodiscard]] static eve::script::Borrowed resolve(ProcgenContextHandleRef reference) noexcept; /** @brief Releases a module-owned rebuild context. */ [[nodiscard]] static eve::Result release(ProcgenContextHandleRef reference); /** @brief Reports whether a rebuild context reference is stale. */ - [[nodiscard]] static bool isStale(ProcgenContextHandleRef reference) noexcept; - [[nodiscard]] eve::Result commitSystem(ProcgenContextHandleRef context); - [[nodiscard]] eve::Result abortSystem(ProcgenContextHandleRef context); - [[nodiscard]] eve::Result removeSystem(const std::string& name); + [[nodiscard]] static bool isStale(ProcgenContextHandleRef reference) noexcept; + [[nodiscard]] eve::Result commitSystem(ProcgenContextHandleRef context); + [[nodiscard]] eve::Result abortSystem(ProcgenContextHandleRef context); + [[nodiscard]] eve::Result removeSystem(const std::string &name); bool hasSystem(const std::string& name) const; uint64_t getSystemRevision(const std::string& name) const; uint32_t getSystemSeed(const std::string& name) const; std::string getSystemBuildKey(const std::string& name) const; int getSystemOutputCount(const std::string& name) const; - std::string getSystemOutputName(const std::string& name, int index) const; + std::string getSystemOutputName(const std::string &name, int index) const; int getSystemDebugStageCount(const std::string& name) const; std::string getSystemDebugStageName(const std::string& name, int index) const; - [[nodiscard]] eve::Result getSystemOutputHandle( - const std::string& name, const std::string& outputName) const; - [[nodiscard]] eve::Result getSystemDebugStageHandle( - const std::string& name, const std::string& stageName) const; + [[nodiscard]] eve::Result getSystemOutputHandle(const std::string &name, + const std::string &outputName) const; + [[nodiscard]] eve::Result getSystemDebugStageHandle(const std::string &name, + const std::string &stageName) const; [[nodiscard]] eve::Result getPreviousSystemDebugStageHandle( - const std::string& name, const std::string& stageName) const; + const std::string &name, const std::string &stageName) const; /** @brief Revision number of the snapshot retained for hot-reload comparison. */ uint64_t getPreviousSystemRevision(const std::string& name) const; std::string getSystemDebugReport(const std::string& name) const; @@ -308,13 +298,12 @@ public: std::string getSystemDebugDiffReport(const std::string& name) const; // --- Phase A: maps --- - [[nodiscard]] eve::Result generateHandle( - const std::string &algorithmId, ProcgenParamsHandleRef params); - [[nodiscard]] eve::Result generateTo( - const std::string &algorithmId, ProcgenParamsHandleRef params, - ProcgenOutputHandleRef output); - [[nodiscard]] eve::Result applyToLayer( - ProcgenGridHandleRef grid, const std::string &palette, map::TileLayer& layer); + [[nodiscard]] eve::Result generateHandle(const std::string &algorithmId, + ProcgenParamsHandleRef params); + [[nodiscard]] eve::Result generateTo(const std::string &algorithmId, ProcgenParamsHandleRef params, + ProcgenOutputHandleRef output); + [[nodiscard]] eve::Result applyToLayer(ProcgenGridHandleRef grid, const std::string &palette, + map::TileLayer &layer); void setPaletteGid(const std::string &palette, const std::string &semantic, int gid); int getPaletteGid(const std::string &palette, const std::string &semantic) const; @@ -323,8 +312,7 @@ public: std::string getAlgorithmId(int index) const; bool hasAlgorithm(const std::string &algorithmId) const; /** @brief Returns an owning copy of an algorithm schema or a diagnostic. */ - [[nodiscard]] eve::Result getAlgorithmSchema( - const std::string &algorithmId) const; + [[nodiscard]] eve::Result getAlgorithmSchema(const std::string &algorithmId) const; /** @brief Human-readable algorithm label from its schema. * @param algorithmId Algorithm id. @return Display name or empty text. */ std::string getAlgorithmDisplayName(const std::string &algorithmId) const; @@ -381,8 +369,8 @@ public: /** @brief Fill missing algorithm-specific values from schema defaults. * @param algorithmId Algorithm id. @param params Parameters to update. * @return Whether the schema exists. */ - [[nodiscard]] eve::Result applyAlgorithmDefaults( - const std::string &algorithmId, ProcgenParamsHandleRef params) const; + [[nodiscard]] eve::Result applyAlgorithmDefaults(const std::string &algorithmId, + ProcgenParamsHandleRef params) const; /** * @brief Post-process a generated grid: fill each wall cell's detail with an @@ -396,8 +384,8 @@ public: [[nodiscard]] eve::Result gridToJson(ProcgenGridHandleRef grid) const; // --- Phase B: textures --- - [[nodiscard]] eve::Result generateImageHandle( - const std::string &recipeId, ProcgenParamsHandleRef params); + [[nodiscard]] eve::Result generateImageHandle(const std::string &recipeId, + ProcgenParamsHandleRef params); /** * @brief Resolves a generated image as a non-owning observation. * @param reference Generation- and module-epoch-qualified image reference. @@ -410,8 +398,7 @@ public: * @thread Procgen owner thread; no synchronization is provided. * @reentrancy Side-effect free and does not invoke callbacks. */ - [[nodiscard]] static eve::script::Borrowed resolve( - ProcgenImageHandleRef reference) noexcept; + [[nodiscard]] static eve::script::Borrowed resolve(ProcgenImageHandleRef reference) noexcept; /** * @brief Releases a module-owned generated image. * @param reference Generation- and module-epoch-qualified image reference. @@ -426,8 +413,8 @@ public: */ [[nodiscard]] static bool isStale(ProcgenImageHandleRef reference) noexcept; - [[nodiscard]] eve::Result generateNormalImageHandle( - const std::string &recipeId, ProcgenParamsHandleRef params); + [[nodiscard]] eve::Result generateNormalImageHandle(const std::string &recipeId, + ProcgenParamsHandleRef params); /** * @brief Resolves a generated normal image as a non-owning observation. * @param reference Generation- and module-epoch-qualified normal-image reference. @@ -468,66 +455,61 @@ public: * @thread Graphics/Procgen owner thread; no synchronization is provided. * @reentrancy Does not invoke user callbacks. */ - [[nodiscard]] eve::script::Borrowed generateTextureBorrowed( - const std::string &recipeId, ProcgenParamsHandleRef params, - graphics::Graphics *gfx); + [[nodiscard]] eve::script::Borrowed generateTextureBorrowed(const std::string &recipeId, + ProcgenParamsHandleRef params, + graphics::Graphics *gfx); int getTextureRecipeCount() const; std::string getTextureRecipeId(int index) const; bool hasTextureRecipe(const std::string &recipeId) const; /** @brief Returns an owning copy of a texture schema or a diagnostic. */ - [[nodiscard]] eve::Result getTextureRecipeSchema( - const std::string &recipeId) const; + [[nodiscard]] eve::Result getTextureRecipeSchema(const std::string &recipeId) const; /** @brief Fill missing texture parameters in a module-owned parameter object. */ - [[nodiscard]] eve::Result applyTextureRecipeDefaults( - const std::string &recipeId, ProcgenParamsHandleRef params) const; + [[nodiscard]] eve::Result applyTextureRecipeDefaults(const std::string &recipeId, + ProcgenParamsHandleRef params) const; // --- Phase E: dynamic clouds + cloud shadows --- /** @brief New deterministic, tiling, time-animated cloud field (caller owns). */ [[nodiscard]] eve::Result newCloudFieldHandle(); - [[nodiscard]] eve::script::Borrowed resolveCloudField(ProcgenCloudFieldHandleRef) noexcept; - [[nodiscard]] eve::Result releaseCloudField(ProcgenCloudFieldHandleRef); - [[nodiscard]] bool isCloudFieldStale(ProcgenCloudFieldHandleRef) const noexcept; + [[nodiscard]] eve::script::Borrowed resolveCloudField(ProcgenCloudFieldHandleRef) noexcept; + [[nodiscard]] eve::Result releaseCloudField(ProcgenCloudFieldHandleRef); + [[nodiscard]] bool isCloudFieldStale(ProcgenCloudFieldHandleRef) const noexcept; /** @brief New sun projection over a cloud field → ground shadows (caller owns). */ [[nodiscard]] eve::Result newCloudShadowHandle(); - [[nodiscard]] eve::script::Borrowed resolveCloudShadow(ProcgenCloudShadowHandleRef) noexcept; - [[nodiscard]] eve::Result releaseCloudShadow(ProcgenCloudShadowHandleRef); + [[nodiscard]] eve::script::Borrowed resolveCloudShadow(ProcgenCloudShadowHandleRef) noexcept; + [[nodiscard]] eve::Result releaseCloudShadow(ProcgenCloudShadowHandleRef); [[nodiscard]] bool isCloudShadowStale(ProcgenCloudShadowHandleRef) const noexcept; /** @brief Cloud coverage at world (x, z) and time using a live field handle. */ - [[nodiscard]] eve::Result cloudCoverageAt( - ProcgenCloudFieldHandleRef field, float x, float z, float time); + [[nodiscard]] eve::Result cloudCoverageAt(ProcgenCloudFieldHandleRef field, float x, float z, float time); /** @brief Light multiplier in [0,1] using a live shadow handle. */ - [[nodiscard]] eve::Result cloudShadowFactor( - ProcgenCloudShadowHandleRef shadow, float x, float z, float time); + [[nodiscard]] eve::Result cloudShadowFactor(ProcgenCloudShadowHandleRef shadow, float x, float z, + float time); /** @brief Samples coverage into a caller-owned buffer after validating its size. */ - [[nodiscard]] eve::Result sampleCloud( - ProcgenCloudFieldHandleRef field, std::span out, int w, int h, - float time, float x0, float z0, float extent); + [[nodiscard]] eve::Result sampleCloud(ProcgenCloudFieldHandleRef field, std::span out, int w, int h, + float time, float x0, float z0, float extent); /** @brief Samples shadow coverage into a caller-owned buffer after validating its size. */ - [[nodiscard]] eve::Result sampleCloudShadow( - ProcgenCloudShadowHandleRef shadow, std::span out, int w, int h, - float time, float x0, float z0, float extent); + [[nodiscard]] eve::Result sampleCloudShadow(ProcgenCloudShadowHandleRef shadow, std::span out, int w, + int h, float time, float x0, float z0, float extent); /** * @brief Full metallic-roughness PBR set (albedo/normal/roughness/metallic/height/ao) * derived from a single displacement field. Caller owns the returned set * (call PbrTextureSet::destroy()). Recipes: pbr.soil/stone/rock/marble/water/ * ripple/wood/cloth/ornament/spot/zebra/wall/cement/mud/sky_cloud. */ - [[nodiscard]] eve::Result generatePbrMaterialHandle( - const std::string &recipeId, ProcgenParamsHandleRef params); - [[nodiscard]] eve::script::Borrowed resolvePbrMaterial(ProcgenPbrMaterialHandleRef) noexcept; - [[nodiscard]] eve::Result releasePbrMaterial(ProcgenPbrMaterialHandleRef); + [[nodiscard]] eve::Result generatePbrMaterialHandle(const std::string &recipeId, + ProcgenParamsHandleRef params); + [[nodiscard]] eve::script::Borrowed resolvePbrMaterial(ProcgenPbrMaterialHandleRef) noexcept; + [[nodiscard]] eve::Result releasePbrMaterial(ProcgenPbrMaterialHandleRef); [[nodiscard]] bool isPbrMaterialStale(ProcgenPbrMaterialHandleRef) const noexcept; int getPbrRecipeCount() const; std::string getPbrRecipeId(int index) const; bool hasPbrRecipe(const std::string &recipeId) const; /** @brief Returns an owning copy of a PBR schema or a diagnostic. */ - [[nodiscard]] eve::Result getPbrRecipeSchema( - const std::string &recipeId) const; + [[nodiscard]] eve::Result getPbrRecipeSchema(const std::string &recipeId) const; /** @brief Fill missing PBR parameters in a module-owned parameter object. */ - [[nodiscard]] eve::Result applyPbrRecipeDefaults( - const std::string &recipeId, ProcgenParamsHandleRef params) const; + [[nodiscard]] eve::Result applyPbrRecipeDefaults(const std::string &recipeId, + ProcgenParamsHandleRef params) const; // --- Mesh recipes (Marching Cubes, …) --- /** @@ -535,11 +517,11 @@ public: * Recipes include mesh.marchingcubes, mesh.hexplanet, mesh.castle and the * registered vegetation, building and linear-structure recipes. */ - [[nodiscard]] eve::Result buildMeshHandle( - const std::string &recipeId, ProcgenParamsHandleRef params); - [[nodiscard]] eve::script::Borrowed resolveMeshBuild(ProcgenMeshBuildHandleRef) noexcept; - [[nodiscard]] eve::Result releaseMeshBuild(ProcgenMeshBuildHandleRef); - [[nodiscard]] bool isMeshBuildStale(ProcgenMeshBuildHandleRef) const noexcept; + [[nodiscard]] eve::Result buildMeshHandle(const std::string &recipeId, + ProcgenParamsHandleRef params); + [[nodiscard]] eve::script::Borrowed resolveMeshBuild(ProcgenMeshBuildHandleRef) noexcept; + [[nodiscard]] eve::Result releaseMeshBuild(ProcgenMeshBuildHandleRef); + [[nodiscard]] bool isMeshBuildStale(ProcgenMeshBuildHandleRef) const noexcept; /** * @brief Build an owning backend-neutral CPU artifact for a mesh recipe. * @param recipeId Registered mesh recipe id. @@ -547,8 +529,8 @@ public: * @param id Non-nil identity for this artifact instance. * @return Generated CPU artifact or a structured diagnostic. */ - [[nodiscard]] eve::Result buildArtifact( - const std::string &recipeId, ProcgenParamsHandleRef params, ArtifactId id); + [[nodiscard]] eve::Result buildArtifact(const std::string &recipeId, + ProcgenParamsHandleRef params, ArtifactId id); /** * @brief Build then transactionally publish through requested optional adapters. * @param recipeId Registered mesh recipe id. @@ -558,47 +540,45 @@ public: * @return Publication receipt or an Unsupported/validation/adapter diagnostic. * @remarks Must run on the main/simulation thread. A failure leaves the store unchanged. */ - [[nodiscard]] eve::Result publishArtifact( - const std::string &recipeId, ProcgenParamsHandleRef params, ArtifactId id, - ArtifactPublishOptions options = {}); + [[nodiscard]] eve::Result publishArtifact(const std::string &recipeId, + ProcgenParamsHandleRef params, ArtifactId id, + ArtifactPublishOptions options = {}); /** @brief Borrow the compatibility facade's published CPU artifact store. */ - [[nodiscard]] const ArtifactStore& artifactStore() const noexcept { return artifactStore_; } + [[nodiscard]] const ArtifactStore &artifactStore() const noexcept { return artifactStore_; } /** @brief Upload an existing/composed CPU mesh to Graphics. */ /** @brief Upload a CPU mesh and return a Graphics-owned borrowed mesh. */ - [[nodiscard]] eve::script::Borrowed uploadMeshBorrowed( - const MeshBuild& mesh, graphics::Graphics& gfx); + [[nodiscard]] eve::script::Borrowed uploadMeshBorrowed(const MeshBuild &mesh, + graphics::Graphics &gfx); /** @brief Build + upload to GPU Mesh (owned by Graphics). */ - [[nodiscard]] eve::script::Borrowed generateMeshBorrowed( - const std::string &recipeId, ProcgenParamsHandleRef params, - graphics::Graphics *gfx); + [[nodiscard]] eve::script::Borrowed generateMeshBorrowed(const std::string &recipeId, + ProcgenParamsHandleRef params, + graphics::Graphics *gfx); int getMeshRecipeCount() const; std::string getMeshRecipeId(int index) const; bool hasMeshRecipe(const std::string &recipeId) const; /** @brief Returns an owning copy of a mesh schema or a diagnostic. */ - [[nodiscard]] eve::Result getMeshRecipeSchema( - const std::string &recipeId) const; + [[nodiscard]] eve::Result getMeshRecipeSchema(const std::string &recipeId) const; /** @brief Fill missing mesh parameters in a module-owned parameter object. */ - [[nodiscard]] eve::Result applyMeshRecipeDefaults( - const std::string &recipeId, ProcgenParamsHandleRef params) const; + [[nodiscard]] eve::Result applyMeshRecipeDefaults(const std::string &recipeId, + ProcgenParamsHandleRef params) const; // --- Phase D: terrain height sampling --- /** @brief Sampling function over continuous map coordinates (caller owns). */ [[nodiscard]] eve::Result newTerrainSamplerHandle(); [[nodiscard]] eve::script::Borrowed resolveTerrainSampler(ProcgenTerrainSamplerHandleRef) noexcept; - [[nodiscard]] eve::Result releaseTerrainSampler(ProcgenTerrainSamplerHandleRef); + [[nodiscard]] eve::Result releaseTerrainSampler(ProcgenTerrainSamplerHandleRef); [[nodiscard]] bool isTerrainSamplerStale(ProcgenTerrainSamplerHandleRef) const noexcept; /** @brief Empty in-memory heightmap (caller owns). */ [[nodiscard]] eve::Result newHeightmapHandle(int width, int height); - [[nodiscard]] eve::script::Borrowed resolveHeightmap(ProcgenHeightmapHandleRef) noexcept; - [[nodiscard]] eve::Result releaseHeightmap(ProcgenHeightmapHandleRef); - [[nodiscard]] bool isHeightmapStale(ProcgenHeightmapHandleRef) const noexcept; + [[nodiscard]] eve::script::Borrowed resolveHeightmap(ProcgenHeightmapHandleRef) noexcept; + [[nodiscard]] eve::Result releaseHeightmap(ProcgenHeightmapHandleRef); + [[nodiscard]] bool isHeightmapStale(ProcgenHeightmapHandleRef) const noexcept; /** @brief Build a sampler from params (seed/scale/octaves/…) and materialize it (caller owns). */ - [[nodiscard]] eve::Result generateHeightmapHandle( - ProcgenParamsHandleRef params); + [[nodiscard]] eve::Result generateHeightmapHandle(ProcgenParamsHandleRef params); /** @brief Classify a heightmap into a module-owned grid using params bands. */ - [[nodiscard]] eve::Result heightmapToGrid( - ProcgenHeightmapHandleRef heightmap, ProcgenParamsHandleRef params); + [[nodiscard]] eve::Result heightmapToGrid(ProcgenHeightmapHandleRef heightmap, + ProcgenParamsHandleRef params); PaletteTable &palettes() { return palettes_; } @@ -617,17 +597,17 @@ private: mutable std::vector meshRecipeIdsCache_; std::unordered_map systems_; std::unordered_map previousSystems_; - ArtifactStore artifactStore_; - std::uint64_t nextArtifactSequence_ = 1; - eve::script::RuntimeObjectRegistry params_; - eve::script::RuntimeObjectRegistry grids_; - eve::script::RuntimeObjectRegistry contexts_; - eve::script::RuntimeObjectRegistry spatialData_; + ArtifactStore artifactStore_; + std::uint64_t nextArtifactSequence_ = 1; + eve::script::RuntimeObjectRegistry params_; + eve::script::RuntimeObjectRegistry grids_; + eve::script::RuntimeObjectRegistry contexts_; + eve::script::RuntimeObjectRegistry spatialData_; eve::script::RuntimeObjectRegistry runtimeGenerations_; - eve::script::RuntimeObjectRegistry pointGraphs_; - eve::script::RuntimeObjectRegistry biomeRules_; - eve::script::RuntimeObjectRegistry shapeGrammars_; - eve::script::RuntimeObjectRegistry lsystems_; + eve::script::RuntimeObjectRegistry pointGraphs_; + eve::script::RuntimeObjectRegistry biomeRules_; + eve::script::RuntimeObjectRegistry shapeGrammars_; + eve::script::RuntimeObjectRegistry lsystems_; }; } // namespace eve::procgen diff --git a/src/modules/procgen/ProcgenCapabilities.cpp b/src/modules/procgen/ProcgenCapabilities.cpp index 8709fe00..1af1e944 100644 --- a/src/modules/procgen/ProcgenCapabilities.cpp +++ b/src/modules/procgen/ProcgenCapabilities.cpp @@ -58,8 +58,8 @@ public: if (err) *err = "parameter allocation failed"; return {}; } - const auto parRef = std::move(parResult).takeValue(); - auto parView = Procgen::resolve(parRef); + const auto parRef = std::move(parResult).takeValue(); + auto parView = Procgen::resolve(parRef); if (!parView.isBound()) { Procgen::release(parRef).ignore("release failed after stale parameter lookup"); if (err) *err = "parameter handle became stale"; @@ -103,8 +103,8 @@ public: if (err) *err = "parameter allocation failed"; return {}; } - const auto parRef = std::move(parResult).takeValue(); - auto parView = Procgen::resolve(parRef); + const auto parRef = std::move(parResult).takeValue(); + auto parView = Procgen::resolve(parRef); if (!parView.isBound()) { Procgen::release(parRef).ignore("release failed after stale parameter lookup"); if (err) *err = "parameter handle became stale"; @@ -122,7 +122,7 @@ public: return {}; } const auto meshRef = std::move(meshResult).takeValue(); - auto mesh = p->resolveMeshBuild(meshRef); + auto mesh = p->resolveMeshBuild(meshRef); if (!mesh.isBound()) { p->releaseMeshBuild(meshRef).ignore("release failed after stale mesh lookup"); Procgen::release(parRef).ignore("release failed after stale mesh lookup"); diff --git a/src/modules/procgen/adapters/heightmap/PointSetHeightmapAdapter.cpp b/src/modules/procgen/adapters/heightmap/PointSetHeightmapAdapter.cpp index 048e62b0..8bfaadff 100644 --- a/src/modules/procgen/adapters/heightmap/PointSetHeightmapAdapter.cpp +++ b/src/modules/procgen/adapters/heightmap/PointSetHeightmapAdapter.cpp @@ -5,9 +5,8 @@ namespace eve::procgen { -PointSet projectPointsToHeightmap(const PointSet& input, const Heightmap& heightmap, - float originX, float originZ, float cellSize, - float heightScale) { +PointSet projectPointsToHeightmap(const PointSet& input, const Heightmap& heightmap, float originX, float originZ, + float cellSize, float heightScale) { if (cellSize <= 0.f || heightmap.getWidth() <= 0 || heightmap.getHeight() <= 0) return {}; PointSet output = input; for (auto& point : output.points()) { @@ -15,13 +14,13 @@ PointSet projectPointsToHeightmap(const PointSet& input, const Heightmap& height const float hz = (point.z - originZ) / cellSize; point.y = heightmap.sampleBilinear(hx, hz) * heightScale; - const float left = heightmap.sampleBilinear(hx - 0.5f, hz) * heightScale; - const float right = heightmap.sampleBilinear(hx + 0.5f, hz) * heightScale; - const float down = heightmap.sampleBilinear(hx, hz - 0.5f) * heightScale; - const float up = heightmap.sampleBilinear(hx, hz + 0.5f) * heightScale; - float nx = -(right - left) / cellSize; - float ny = 1.f; - float nz = -(up - down) / cellSize; + const float left = heightmap.sampleBilinear(hx - 0.5f, hz) * heightScale; + const float right = heightmap.sampleBilinear(hx + 0.5f, hz) * heightScale; + const float down = heightmap.sampleBilinear(hx, hz - 0.5f) * heightScale; + const float up = heightmap.sampleBilinear(hx, hz + 0.5f) * heightScale; + float nx = -(right - left) / cellSize; + float ny = 1.f; + float nz = -(up - down) / cellSize; const float length = std::sqrt(nx * nx + ny * ny + nz * nz); if (length > 0.f) { nx /= length; diff --git a/src/modules/procgen/adapters/map/PaletteMapAdapter.cpp b/src/modules/procgen/adapters/map/PaletteMapAdapter.cpp index c88c9121..a11ccd06 100644 --- a/src/modules/procgen/adapters/map/PaletteMapAdapter.cpp +++ b/src/modules/procgen/adapters/map/PaletteMapAdapter.cpp @@ -1,12 +1,12 @@ -#include "procgen/Palette.h" #include "map/TileLayer.h" +#include "procgen/Palette.h" #include "procgen/Semantic.h" namespace eve::procgen { -bool PaletteTable::applyToLayer(const Grid2D& grid, const std::string& palette, - map::TileLayer* layer, std::string* error) const { +bool PaletteTable::applyToLayer(const Grid2D& grid, const std::string& palette, map::TileLayer* layer, + std::string* error) const { if (!layer) { if (error) *error = "applyToLayer: null TileLayer"; return false; diff --git a/src/modules/procgen/core/ProcgenCore.h b/src/modules/procgen/core/ProcgenCore.h index e5fdbe0b..f4203228 100644 --- a/src/modules/procgen/core/ProcgenCore.h +++ b/src/modules/procgen/core/ProcgenCore.h @@ -7,9 +7,9 @@ * build state. Map, scene, graphics and image adapters belong to the public * Procgen facade or to their adapter translation units, not to this include set. */ -#include "procgen/GeneratorRegistry.h" -#include "procgen/GeneratedArtifact.h" #include "procgen/ArtifactPublish.h" +#include "procgen/GeneratedArtifact.h" +#include "procgen/GeneratorRegistry.h" #include "procgen/Grid2D.h" #include "procgen/JsonExport.h" #include "procgen/MeshBuild.h" diff --git a/src/modules/procgen/texture/BuiltinTextures.cpp b/src/modules/procgen/texture/BuiltinTextures.cpp index f5f2c83f..e887538c 100644 --- a/src/modules/procgen/texture/BuiltinTextures.cpp +++ b/src/modules/procgen/texture/BuiltinTextures.cpp @@ -48,7 +48,7 @@ std::unique_ptr makeFromHeightFn(const Params ¶ms, std::st error = "texture size too large (max 4096)"; return nullptr; } - auto img = std::make_unique(ctx.width, ctx.height, "RGBA8"); + auto img = std::make_unique(ctx.width, ctx.height, "RGBA8"); std::vector height; fillHeightField(ctx, def.height, height); paintHeightToImage(*img, height, ctx.width, ctx.height, def.albedo, ctx.colors, ctx.pixelSize); @@ -427,7 +427,7 @@ std::unique_ptr genCloud(const Params ¶ms, std::string &er error = "texture size too large (max 4096)"; return nullptr; } - auto img = std::make_unique(ctx.width, ctx.height, "RGBA8"); + auto img = std::make_unique(ctx.width, ctx.height, "RGBA8"); ColorRamp ramp; ramp.add(0.00f, 90, 140, 205); ramp.add(0.30f, 168, 205, 238); @@ -450,7 +450,7 @@ std::unique_ptr genCloudShadow(const Params ¶ms, std::stri error = "texture size too large (max 4096)"; return nullptr; } - auto img = std::make_unique(ctx.width, ctx.height, "RGBA8"); + auto img = std::make_unique(ctx.width, ctx.height, "RGBA8"); ColorRamp ramp; ramp.add(0.00f, 18, 20, 26); // clear (lit) ramp.add(0.30f, 60, 62, 70); diff --git a/src/modules/procgen/texture/PbrMaterial.cpp b/src/modules/procgen/texture/PbrMaterial.cpp index 13f0ab06..84691592 100644 --- a/src/modules/procgen/texture/PbrMaterial.cpp +++ b/src/modules/procgen/texture/PbrMaterial.cpp @@ -43,8 +43,8 @@ bool PbrRecipeRegistry::has(const std::string &id) const { return recipes_.find(id) != recipes_.end(); } -std::unique_ptr PbrRecipeRegistry::generate( - const std::string &id, const Params ¶ms, std::string &error) const { +std::unique_ptr PbrRecipeRegistry::generate(const std::string &id, const Params ¶ms, + std::string &error) const { auto it = recipes_.find(id); if (it == recipes_.end()) { error = "unknown pbr recipe: " + id; @@ -94,7 +94,7 @@ void PbrRecipeRegistry::registerPbrBuiltins() { } std::unique_ptr grayscaleImage(const std::vector &values, int w, int h) { - auto img = std::make_unique(w, h, "RGBA8"); + auto img = std::make_unique(w, h, "RGBA8"); auto *pixels = static_cast(img->getData()); for (int i = 0; i < w * h && i < int(values.size()); ++i) { const uint8_t v = uint8_t(std::lround(std::clamp(values[size_t(i)], 0.f, 1.f) * 255.f)); @@ -105,8 +105,7 @@ std::unique_ptr grayscaleImage(const std::vector &value return img; } -std::unique_ptr generatePbrSet(const TextureRecipeDef &def, const Params ¶ms, - std::string &error) { +std::unique_ptr generatePbrSet(const TextureRecipeDef &def, const Params ¶ms, std::string &error) { const auto ctx = TextureGenContext::fromParams(params); if (ctx.width > 4096 || ctx.height > 4096) { error = "texture size too large (max 4096)"; @@ -127,7 +126,7 @@ std::unique_ptr generatePbrSet(const TextureRecipeDef &def, const const int w = ctx.width; const int h = ctx.height; - auto set = std::make_unique(); + auto set = std::make_unique(); set->albedo = new image::ImageData(w, h, "RGBA8"); paintHeightToImage(*set->albedo, height, w, h, def.albedo, ctx.colors, ctx.pixelSize); set->normal = heightToNormalImage(height, w, h, pbr.normalStrength, ctx.seamless).release(); diff --git a/src/modules/procgen/texture/PbrMaterial.h b/src/modules/procgen/texture/PbrMaterial.h index 6f3387de..8d058108 100644 --- a/src/modules/procgen/texture/PbrMaterial.h +++ b/src/modules/procgen/texture/PbrMaterial.h @@ -49,8 +49,7 @@ struct PbrTextureSet { }; /** @brief Recipe returns a newly owned full PBR set, or null on failure. */ -using PbrRecipeFn = - std::function(const Params ¶ms, std::string &error)>; +using PbrRecipeFn = std::function(const Params ¶ms, std::string &error)>; class PbrRecipeRegistry { public: @@ -71,8 +70,8 @@ public: * @return The newly owned set, or null when the recipe is unknown or generation fails. * @ownership The returned set and all maps it owns belong to the caller. */ - [[nodiscard]] std::unique_ptr generate( - const std::string &id, const Params ¶ms, std::string &error) const; + [[nodiscard]] std::unique_ptr generate(const std::string &id, const Params ¶ms, + std::string &error) const; /** @brief List recipe ids. @return Sorted ids. */ std::vector list() const; /** @brief Look up recipe metadata. @param id Recipe id. @return Registry-owned schema or nullptr. */ @@ -94,11 +93,10 @@ private: }; /** @brief Builds a full PBR set from a texture definition and returns unique ownership. */ -[[nodiscard]] std::unique_ptr generatePbrSet( - const TextureRecipeDef &def, const Params ¶ms, std::string &error); +[[nodiscard]] std::unique_ptr generatePbrSet(const TextureRecipeDef &def, const Params ¶ms, + std::string &error); /** @brief Builds a grayscale RGBA8 image and returns unique ownership to the caller. */ -[[nodiscard]] std::unique_ptr grayscaleImage( - const std::vector &values, int w, int h); +[[nodiscard]] std::unique_ptr grayscaleImage(const std::vector &values, int w, int h); } // namespace eve::procgen diff --git a/src/modules/procgen/texture/TextureRecipe.cpp b/src/modules/procgen/texture/TextureRecipe.cpp index 2858304c..73cbe1cf 100644 --- a/src/modules/procgen/texture/TextureRecipe.cpp +++ b/src/modules/procgen/texture/TextureRecipe.cpp @@ -30,8 +30,8 @@ bool TextureRecipeRegistry::has(const std::string &id) const { return recipes_.find(id) != recipes_.end(); } -std::unique_ptr TextureRecipeRegistry::generate( - const std::string &id, const Params ¶ms, std::string &error) const { +std::unique_ptr TextureRecipeRegistry::generate(const std::string &id, const Params ¶ms, + std::string &error) const { auto it = recipes_.find(id); if (it == recipes_.end()) { error = "unknown texture recipe: " + id; @@ -145,9 +145,9 @@ void paintHeightToImage(image::ImageData &img, const std::vector &height, } } -std::unique_ptr heightToNormalImage( - const std::vector &height, int w, int h, float strength, bool seamless) { - auto img = std::make_unique(w, h, "RGBA8"); +std::unique_ptr heightToNormalImage(const std::vector &height, int w, int h, float strength, + bool seamless) { + auto img = std::make_unique(w, h, "RGBA8"); auto *pixels = static_cast(img->getData()); auto sample = [&](int x, int y) -> float { if (seamless) { diff --git a/src/modules/procgen/texture/TextureRecipe.h b/src/modules/procgen/texture/TextureRecipe.h index 1e5e3533..7cf33acb 100644 --- a/src/modules/procgen/texture/TextureRecipe.h +++ b/src/modules/procgen/texture/TextureRecipe.h @@ -19,8 +19,7 @@ class ImageData; namespace eve::procgen { /** @brief Recipe returns a newly owned RGBA8 image, or null on failure. */ -using TextureRecipeFn = - std::function(const Params ¶ms, std::string &error)>; +using TextureRecipeFn = std::function(const Params ¶ms, std::string &error)>; class TextureRecipeRegistry { public: @@ -41,8 +40,8 @@ public: * @return The newly owned image, or null when the recipe is unknown or generation fails. * @ownership The returned image is owned by the caller. */ - [[nodiscard]] std::unique_ptr generate( - const std::string &id, const Params ¶ms, std::string &error) const; + [[nodiscard]] std::unique_ptr generate(const std::string &id, const Params ¶ms, + std::string &error) const; /** @brief List registered recipe ids. @return Sorted ids. */ std::vector list() const; /** @brief Look up recipe metadata. @param id Recipe id. @return Registry-owned schema or nullptr. */ @@ -89,8 +88,8 @@ void fillHeightField(const TextureGenContext &ctx, * @brief Builds a normal-map image and transfers its unique ownership to the caller. * @return The newly owned image, or null only when allocation fails. */ -[[nodiscard]] std::unique_ptr heightToNormalImage( - const std::vector &height, int w, int h, float strength, bool seamless); +[[nodiscard]] std::unique_ptr heightToNormalImage(const std::vector &height, int w, int h, + float strength, bool seamless); /** * @brief PBR surface defaults for a generated material. Every map in a PBR set is diff --git a/src/modules/production/Production.cpp b/src/modules/production/Production.cpp index 1388b0e1..6beb059a 100644 --- a/src/modules/production/Production.cpp +++ b/src/modules/production/Production.cpp @@ -25,45 +25,37 @@ struct ScriptWorkQueue { /** @brief Squirrel-owned task view that re-resolves its queue handle per call. */ struct ScriptWorkTask { - ScriptWorkTask(WorkQueueHandleRef queue, std::string id) - : reference(queue), taskId(std::move(id)) {} + ScriptWorkTask(WorkQueueHandleRef queue, std::string id) : reference(queue), taskId(std::move(id)) {} WorkQueueHandleRef reference; - std::string taskId; + std::string taskId; }; /** @brief Squirrel-owned event view that re-resolves its queue handle per call. */ struct ScriptWorkEvent { - ScriptWorkEvent(WorkQueueHandleRef queue, int eventIndex) - : reference(queue), index(eventIndex) {} + ScriptWorkEvent(WorkQueueHandleRef queue, int eventIndex) : reference(queue), index(eventIndex) {} WorkQueueHandleRef reference; - int index = -1; + int index = -1; }; template -eve::Result productionBindingFailure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - code, std::move(message), std::move(path), {}, "production.squirrel")); +eve::Result productionBindingFailure(eve::DiagnosticCode code, std::string message, std::string path = {}) { + return eve::Result::failure( + eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "production.squirrel")); } template -ssq::Table makeOwnedProxy(HSQUIRRELVM vm, eve::Result&& reference, Release&& release, - const char* errorPath) { - if (!reference) - return eve::script::projectStatusResult(vm, reference.status(), false, false); - const Ref ref = std::move(reference).takeValue(); - auto object = eve::script::makeOwnedSquirrelInstance( - vm, std::make_unique(ref)); +ssq::Table makeOwnedProxy(HSQUIRRELVM vm, eve::Result&& reference, Release&& release, const char* errorPath) { + if (!reference) return eve::script::projectStatusResult(vm, reference.status(), false, false); + const Ref ref = std::move(reference).takeValue(); + auto object = eve::script::makeOwnedSquirrelInstance(vm, std::make_unique(ref)); if (!object) { const eve::Status status = object.status(); object.ignore("failed to create owned production proxy"); - std::invoke(std::forward(release), ref).ignore( - "rollback failed owned production allocation"); + std::invoke(std::forward(release), ref).ignore("rollback failed owned production allocation"); return eve::script::projectStatusResult(vm, status, false, false); } ssq::Object owned = std::move(object).takeValue(); - auto result = eve::script::projectStatusResult( - vm, eve::Status::success(eve::StatusCode::Applied), true, false); + auto result = eve::script::projectStatusResult(vm, eve::Status::success(eve::StatusCode::Applied), true, false); result.set("value", owned); result.set("ownership", std::string("owned")); result.set("ownerEpoch", static_cast(ref.ownerEpoch)); @@ -117,8 +109,8 @@ std::string canonicalJson(const eve::json::Value& value) { ssq::Object projectValueObject(HSQUIRRELVM vm, const eve::Value& value) { if (!vm) return ssq::Object(); - const SQInteger top = sq_gettop(vm); - auto pushed = eve::script::pushValue(vm, value); + const SQInteger top = sq_gettop(vm); + auto pushed = eve::script::pushValue(vm, value); if (!pushed.ok()) { pushed.ignore("failed to project a work-task Value payload"); sq_settop(vm, top); @@ -152,8 +144,7 @@ bool parseI64(const eve::json::Value& value, std::int64_t& result) { } } -bool parseDuration(const eve::json::Value& nanoseconds, const eve::json::Value& legacySeconds, - eve::Duration& result) { +bool parseDuration(const eve::json::Value& nanoseconds, const eve::json::Value& legacySeconds, eve::Duration& result) { if (!nanoseconds.isNull()) { std::int64_t value = 0; if (!parseI64(nanoseconds, value)) return false; @@ -214,17 +205,17 @@ eve::LogicalId productionSchema() { const eve::SnapshotMigrationChain& productionMigrations() { static const eve::SnapshotMigrationChain chain = [] { eve::SnapshotMigrationChain result; - const auto registration = result.add( - productionSchema(), eve::SchemaVersion(0), eve::SchemaVersion(1), - [](const eve::Value& payload) -> eve::Result { - const auto* object = payload.getIf(); - if (!object) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "production queue payload must be an object")); - eve::Value::Object migrated = *object; - if (!migrated.contains("version")) migrated.emplace("version", eve::Value(std::int64_t(1))); - return eve::Result::success(eve::Value(std::move(migrated))); - }); + const auto registration = + result.add(productionSchema(), eve::SchemaVersion(0), eve::SchemaVersion(1), + [](const eve::Value& payload) -> eve::Result { + const auto* object = payload.getIf(); + if (!object) + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::ParseError, "production queue payload must be an object")); + eve::Value::Object migrated = *object; + if (!migrated.contains("version")) migrated.emplace("version", eve::Value(std::int64_t(1))); + return eve::Result::success(eve::Value(std::move(migrated))); + }); if (!registration.ok()) std::terminate(); return result; }(); @@ -270,8 +261,8 @@ std::string_view eventKindName(ProductionEventKind kind) { } void WorkQueue::emit(ProductionEventKind kind, const ProductionTask& task, std::string_view reason) { - events_.push_back({{nextEventSequence_++, std::string(reason)}, tick_, kind, task.id, - task.owner, task.kind, task.product}); + events_.push_back( + {{nextEventSequence_++, std::string(reason)}, tick_, kind, task.id, task.owner, task.kind, task.product}); if (const auto next = revision_.incremented()) revision_ = *next; } @@ -303,23 +294,22 @@ void WorkQueue::schedule(std::string_view owner) { } } -eve::Result WorkQueue::enqueue(std::string_view owner, std::string_view kind, - std::string_view product, eve::Value context, - double duration, int priority) { +eve::Result WorkQueue::enqueue(std::string_view owner, std::string_view kind, std::string_view product, + eve::Value context, double duration, int priority) { if (owner.empty() || kind.empty() || product.empty()) - return productionBindingFailure( - eve::DiagnosticCode::InvalidArgument, "work task owner, kind and product are required"); + return productionBindingFailure(eve::DiagnosticCode::InvalidArgument, + "work task owner, kind and product are required"); if (!context.isObject()) - return productionBindingFailure( - eve::DiagnosticCode::InvalidArgument, "work task context must be a Value object", "context"); + return productionBindingFailure(eve::DiagnosticCode::InvalidArgument, + "work task context must be a Value object", "context"); if (!std::isfinite(duration) || duration <= 0.0) - return productionBindingFailure( - eve::DiagnosticCode::InvalidArgument, "work task duration must be finite and positive", "duration"); + return productionBindingFailure(eve::DiagnosticCode::InvalidArgument, + "work task duration must be finite and positive", "duration"); auto durationValue = eve::Duration::fromSeconds(duration); if (!durationValue.ok()) return eve::Result::failure(durationValue.status()); if (durationValue.value().nanoseconds() <= 0) - return productionBindingFailure( - eve::DiagnosticCode::InvalidArgument, "work task duration must be positive", "duration"); + return productionBindingFailure(eve::DiagnosticCode::InvalidArgument, + "work task duration must be positive", "duration"); auto task = std::make_unique(); std::ostringstream id; id << "task-" << std::setw(16) << std::setfill('0') << nextTaskId_; @@ -356,8 +346,7 @@ eve::OptionalRef WorkQueue::find(std::string_view taskId) eve::Result WorkQueue::pause(std::string_view taskId) { auto taskRef = find(taskId); if (!taskRef) - return productionBindingFailure(eve::DiagnosticCode::NotFound, - "work task was not found", "taskId"); + return productionBindingFailure(eve::DiagnosticCode::NotFound, "work task was not found", "taskId"); auto& task = taskRef->get(); if (task.state != TaskState::Queued && task.state != TaskState::Running) return productionBindingFailure(eve::DiagnosticCode::Conflict, @@ -371,12 +360,11 @@ eve::Result WorkQueue::pause(std::string_view taskId) { eve::Result WorkQueue::resume(std::string_view taskId) { auto taskRef = find(taskId); if (!taskRef) - return productionBindingFailure(eve::DiagnosticCode::NotFound, - "work task was not found", "taskId"); + return productionBindingFailure(eve::DiagnosticCode::NotFound, "work task was not found", "taskId"); auto& task = taskRef->get(); if (task.state != TaskState::Paused) - return productionBindingFailure(eve::DiagnosticCode::Conflict, - "only paused tasks can be resumed", "taskId"); + return productionBindingFailure(eve::DiagnosticCode::Conflict, "only paused tasks can be resumed", + "taskId"); task.state = TaskState::Queued; emit(ProductionEventKind::Resumed, task); schedule(task.owner); @@ -386,12 +374,11 @@ eve::Result WorkQueue::resume(std::string_view taskId) { eve::Result WorkQueue::cancel(std::string_view taskId, std::string_view reason) { auto taskRef = find(taskId); if (!taskRef) - return productionBindingFailure(eve::DiagnosticCode::NotFound, - "work task was not found", "taskId"); + return productionBindingFailure(eve::DiagnosticCode::NotFound, "work task was not found", "taskId"); auto& task = taskRef->get(); if (terminal(task.state)) - return productionBindingFailure(eve::DiagnosticCode::Conflict, - "terminal work tasks cannot be cancelled", "taskId"); + return productionBindingFailure(eve::DiagnosticCode::Conflict, "terminal work tasks cannot be cancelled", + "taskId"); task.state = TaskState::Cancelled; task.reason = std::string(reason); emit(ProductionEventKind::Cancelled, task, reason); @@ -402,12 +389,11 @@ eve::Result WorkQueue::cancel(std::string_view taskId, std::string_view re eve::Result WorkQueue::fail(std::string_view taskId, std::string_view reason) { auto taskRef = find(taskId); if (!taskRef) - return productionBindingFailure(eve::DiagnosticCode::NotFound, - "work task was not found", "taskId"); + return productionBindingFailure(eve::DiagnosticCode::NotFound, "work task was not found", "taskId"); auto& task = taskRef->get(); if (terminal(task.state)) - return productionBindingFailure(eve::DiagnosticCode::Conflict, - "terminal work tasks cannot fail", "taskId"); + return productionBindingFailure(eve::DiagnosticCode::Conflict, "terminal work tasks cannot fail", + "taskId"); task.state = TaskState::Failed; task.reason = std::string(reason); emit(ProductionEventKind::Failed, task, reason); @@ -417,20 +403,20 @@ eve::Result WorkQueue::fail(std::string_view taskId, std::string_view reas eve::Result WorkQueue::advance(const eve::SimulationStep& step) { if (step.tick <= tick_) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, "production step tick must be strictly newer")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, + "production step tick must be strictly newer")); if (step.delta.nanoseconds() < 0) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "production step duration must be non-negative")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "production step duration must be non-negative")); - tick_ = step.tick; - const std::int64_t delta = step.delta.nanoseconds(); + tick_ = step.tick; + const std::int64_t delta = step.delta.nanoseconds(); std::set owners; for (auto& task : tasks_) { if (task->state != TaskState::Running) continue; const std::int64_t remaining = task->duration.nanoseconds() - task->progress.nanoseconds(); - const std::int64_t applied = delta >= remaining ? remaining : delta; - task->progress = eve::Duration::fromNanoseconds(task->progress.nanoseconds() + applied); + const std::int64_t applied = delta >= remaining ? remaining : delta; + task->progress = eve::Duration::fromNanoseconds(task->progress.nanoseconds() + applied); if (task->progress >= task->duration) { task->state = TaskState::Completed; emit(ProductionEventKind::Completed, *task); @@ -443,8 +429,8 @@ eve::Result WorkQueue::advance(const eve::SimulationStep& step) { eve::Result WorkQueue::setSlotCount(std::string_view owner, int slots) { if (owner.empty() || slots < 0) - return productionBindingFailure( - eve::DiagnosticCode::InvalidArgument, "owner must be non-empty and slots must be non-negative"); + return productionBindingFailure(eve::DiagnosticCode::InvalidArgument, + "owner must be non-empty and slots must be non-negative"); auto it = std::lower_bound(slots_.begin(), slots_.end(), owner, [](const auto& entry, std::string_view key) { return entry.first < key; }); if (it != slots_.end() && it->first == owner) @@ -479,8 +465,7 @@ eve::OptionalRef WorkQueue::ownerTaskAt(std::string_view owner, return {}; } -eve::OptionalRef WorkQueue::ownerTaskAt( - std::string_view owner, int index) const { +eve::OptionalRef WorkQueue::ownerTaskAt(std::string_view owner, int index) const { if (index < 0) return {}; for (const auto& task : tasks_) if (task->owner == owner && index-- == 0) return std::cref(*task); @@ -519,14 +504,14 @@ eve::Result WorkQueue::snapshot() const { out << "{\"kind\":" << quote(eventKindName(event.kind)) << ",\"owner\":" << quote(event.owner) << ",\"product\":" << quote(event.product) << ",\"reason\":" << quote(event.reason) << ",\"sequence\":" << quote(std::to_string(event.sequence)) << ",\"taskId\":" << quote(event.taskId) - << ",\"taskKind\":" << quote(event.taskKind) - << ",\"tick\":" << quote(std::to_string(event.tick.value())) << '}'; + << ",\"taskKind\":" << quote(event.taskKind) << ",\"tick\":" << quote(std::to_string(event.tick.value())) + << '}'; } out << "],\"tasks\":["; for (size_t i = 0; i < tasks_.size(); ++i) { if (i) out << ','; const auto& t = *tasks_[i]; - auto context = t.context.toJson(); + auto context = t.context.toJson(); if (!context.ok()) return eve::Result::failure(context.status()); out << "{\"context\":" << std::move(context).takeValue() << ",\"durationNs\":" << quote(std::to_string(t.duration.nanoseconds())) @@ -546,30 +531,27 @@ eve::Result WorkQueue::restore(std::string_view json) { return productionBindingFailure(eve::DiagnosticCode::ParseError, error.empty() ? "snapshot must be an object" : error); } - WorkQueue candidate(instanceId_); + WorkQueue candidate(instanceId_); const auto root = doc.root(); if (root.getInt("version") != 1 || !parseU64(root.get("nextTaskId"), candidate.nextTaskId_) || !parseU64(root.get("nextEnqueueSequence"), candidate.nextEnqueueSequence_) || - !parseU64(root.get("nextEventSequence"), candidate.nextEventSequence_) || - candidate.nextTaskId_ == 0 || candidate.nextEnqueueSequence_ == 0 || candidate.nextEventSequence_ == 0 || - !root.get("slots").isArray() || + !parseU64(root.get("nextEventSequence"), candidate.nextEventSequence_) || candidate.nextTaskId_ == 0 || + candidate.nextEnqueueSequence_ == 0 || candidate.nextEventSequence_ == 0 || !root.get("slots").isArray() || !root.get("events").isArray() || !root.get("tasks").isArray()) { - return productionBindingFailure(eve::DiagnosticCode::ParseError, - "invalid snapshot counters or arrays"); + return productionBindingFailure(eve::DiagnosticCode::ParseError, "invalid snapshot counters or arrays"); } if (!root.get("tick").isNull()) { uint64_t tick = 0; if (!parseU64(root.get("tick"), tick)) { - return productionBindingFailure(eve::DiagnosticCode::ParseError, - "invalid snapshot tick", "tick"); + return productionBindingFailure(eve::DiagnosticCode::ParseError, "invalid snapshot tick", "tick"); } candidate.tick_ = eve::SimulationTick(tick); } if (!root.get("revision").isNull()) { uint64_t revision = 0; if (!parseU64(root.get("revision"), revision)) { - return productionBindingFailure(eve::DiagnosticCode::ParseError, - "invalid snapshot revision", "revision"); + return productionBindingFailure(eve::DiagnosticCode::ParseError, "invalid snapshot revision", + "revision"); } candidate.revision_ = eve::Revision(revision); } else { @@ -578,8 +560,7 @@ eve::Result WorkQueue::restore(std::string_view json) { for (size_t i = 0; i < root.get("slots").size(); ++i) { auto value = root.get("slots").at(i); if (!value.isObject() || !value.get("owner").isString() || !value.get("value").isNumber()) { - return productionBindingFailure(eve::DiagnosticCode::ParseError, - "invalid slot entry", "slots"); + return productionBindingFailure(eve::DiagnosticCode::ParseError, "invalid slot entry", "slots"); } int slots = value.get("value").asInt(); auto setSlots = candidate.setSlotCount(value.get("owner").asString(), slots); @@ -595,37 +576,35 @@ eve::Result WorkQueue::restore(std::string_view json) { !value.get("kind").isString() || !value.get("product").isString() || (!value.get("progressNs").isString() && !value.get("progress").isNumber()) || (!value.get("durationNs").isString() && !value.get("duration").isNumber()) || - !value.get("priority").isNumber() || !value.get("state").isString() || - !value.get("reason").isString() || !parseU64(value.get("enqueueSequence"), sequence) || - !parseState(value.get("state").asString(), state) || value.get("id").asString().empty() || - value.get("owner").asString().empty() || value.get("kind").asString().empty() || - value.get("product").asString().empty() || !ids.insert(value.get("id").asString()).second) { - return productionBindingFailure(eve::DiagnosticCode::ParseError, - "invalid task entry", "tasks"); + !value.get("priority").isNumber() || !value.get("state").isString() || !value.get("reason").isString() || + !parseU64(value.get("enqueueSequence"), sequence) || !parseState(value.get("state").asString(), state) || + value.get("id").asString().empty() || value.get("owner").asString().empty() || + value.get("kind").asString().empty() || value.get("product").asString().empty() || + !ids.insert(value.get("id").asString()).second) { + return productionBindingFailure(eve::DiagnosticCode::ParseError, "invalid task entry", "tasks"); } task->id = value.get("id").asString(); task->owner = value.get("owner").asString(); task->kind = value.get("kind").asString(); task->product = value.get("product").asString(); - auto context = eve::Value::fromJson(canonicalJson(value.get("context"))); + auto context = eve::Value::fromJson(canonicalJson(value.get("context"))); if (!context.ok()) return eve::Result::failure(context.status()); if (!context.value().isObject()) return productionBindingFailure(eve::DiagnosticCode::ParseError, "work task context must be an object", "tasks.context"); - task->context = std::move(context).takeValue(); + task->context = std::move(context).takeValue(); if (!parseDuration(value.get("durationNs"), value.get("duration"), task->duration) || !parseDuration(value.get("progressNs"), value.get("progress"), task->progress)) { - return productionBindingFailure(eve::DiagnosticCode::ParseError, - "invalid task duration", "tasks.duration"); + return productionBindingFailure(eve::DiagnosticCode::ParseError, "invalid task duration", + "tasks.duration"); } task->priority = value.get("priority").asInt(); task->state = state; task->enqueueSequence = sequence; task->reason = value.get("reason").asString(); - if (task->duration.nanoseconds() <= 0 || task->progress.nanoseconds() < 0 || - task->progress > task->duration) { - return productionBindingFailure(eve::DiagnosticCode::ParseError, - "invalid task progress", "tasks.progress"); + if (task->duration.nanoseconds() <= 0 || task->progress.nanoseconds() < 0 || task->progress > task->duration) { + return productionBindingFailure(eve::DiagnosticCode::ParseError, "invalid task progress", + "tasks.progress"); } candidate.tasks_.push_back(std::move(task)); } @@ -638,8 +617,7 @@ eve::Result WorkQueue::restore(std::string_view json) { !value.get("taskKind").isString() || !parseU64(value.get("sequence"), event.sequence) || !parseEventKind(value.get("kind").asString(), event.kind) || event.sequence <= previousEventSequence || event.sequence >= candidate.nextEventSequence_) { - return productionBindingFailure(eve::DiagnosticCode::ParseError, - "invalid event entry", "events"); + return productionBindingFailure(eve::DiagnosticCode::ParseError, "invalid event entry", "events"); } previousEventSequence = event.sequence; event.owner = value.get("owner").asString(); @@ -650,8 +628,8 @@ eve::Result WorkQueue::restore(std::string_view json) { if (!value.get("tick").isNull()) { uint64_t tick = 0; if (!parseU64(value.get("tick"), tick)) { - return productionBindingFailure(eve::DiagnosticCode::ParseError, - "invalid event tick", "events.tick"); + return productionBindingFailure(eve::DiagnosticCode::ParseError, "invalid event tick", + "events.tick"); } event.tick = eve::SimulationTick(tick); } @@ -666,12 +644,11 @@ void WorkQueue::clear() { events_.clear(); slots_.clear(); nextTaskId_ = nextEnqueueSequence_ = nextEventSequence_ = 1; - revision_ = eve::Revision::zero(); - tick_ = eve::SimulationTick::zero(); + revision_ = eve::Revision::zero(); + tick_ = eve::SimulationTick::zero(); } -eve::Result WorkQueue::snapshot( - const eve::SnapshotHashProvider& hashProvider) const { +eve::Result WorkQueue::snapshot(const eve::SnapshotHashProvider& hashProvider) const { auto serialized = snapshot(); if (!serialized.ok()) return eve::Result::failure(serialized.status()); auto payload = eve::Value::fromJson(std::move(serialized).takeValue()); @@ -680,8 +657,8 @@ eve::Result WorkQueue::snapshot( revision_, tick_, std::move(payload).takeValue(), hashProvider); } -eve::Result WorkQueue::restoreSnapshot( - const eve::SnapshotEnvelope& source, const eve::SnapshotHashProvider& hashProvider) { +eve::Result WorkQueue::restoreSnapshot(const eve::SnapshotEnvelope& source, + const eve::SnapshotHashProvider& hashProvider) { if (source.type != "production.queue" || source.schema != productionSchema()) return snapshotFailure(eve::DiagnosticCode::InvalidArgument, "snapshot does not belong to production::WorkQueue"); @@ -691,33 +668,30 @@ eve::Result WorkQueue::restoreSnapshot( auto migrated = productionMigrations().migrate(source, eve::SchemaVersion(1), hashProvider); if (!migrated.ok()) return eve::Result::failure(migrated.status()); const auto& candidateEnvelope = migrated.value(); - auto metadata = eve::validateSnapshotPayloadMetadata(candidateEnvelope.payload, - candidateEnvelope.revision, - candidateEnvelope.tick); + auto metadata = eve::validateSnapshotPayloadMetadata(candidateEnvelope.payload, candidateEnvelope.revision, + candidateEnvelope.tick); if (!metadata.ok()) return eve::Result::failure(metadata.status()); auto payload = candidateEnvelope.payload.toJson(); if (!payload.ok()) return eve::Result::failure(payload.status()); WorkQueue candidate(instanceId_); - auto restored = candidate.restore(std::move(payload).takeValue()); + auto restored = candidate.restore(std::move(payload).takeValue()); if (!restored.ok()) return eve::Result::failure(restored.status()); candidate.instanceId_ = candidateEnvelope.instanceId; candidate.revision_ = candidateEnvelope.revision; candidate.tick_ = candidateEnvelope.tick; - *this = std::move(candidate); + *this = std::move(candidate); return eve::Result::success(); } -eve::Result WorkQueue::snapshotEnvelopeJson( - const eve::SnapshotHashProvider& hashProvider) const { +eve::Result WorkQueue::snapshotEnvelopeJson(const eve::SnapshotHashProvider& hashProvider) const { auto value = snapshot(hashProvider); if (!value.ok()) return eve::Result::failure(value.status()); return std::move(value).andThen( [](eve::SnapshotEnvelope&& envelope) { return eve::serializeSnapshotEnvelope(envelope); }); } -eve::Result WorkQueue::restoreSnapshotJson( - std::string_view json, const eve::SnapshotHashProvider& hashProvider) { +eve::Result WorkQueue::restoreSnapshotJson(std::string_view json, const eve::SnapshotHashProvider& hashProvider) { auto source = eve::parseSnapshotEnvelope(json, hashProvider); if (!source.ok()) return eve::Result::failure(source.status()); return restoreSnapshot(std::move(source).takeValue(), hashProvider); @@ -735,16 +709,16 @@ eve::ResultRef Production::resolve(WorkQueueHandleRef reference) { eve::DiagnosticCode::StaleHandle, "Production module is no longer loaded", "queue"); auto view = module->queues_.resolve(reference); if (!view.isBound()) - return productionBindingFailure>( - eve::DiagnosticCode::StaleHandle, "work queue handle is stale", "queue"); + return productionBindingFailure>(eve::DiagnosticCode::StaleHandle, + "work queue handle is stale", "queue"); return eve::ResultRef::success(std::ref(*view)); } eve::Result Production::release(WorkQueueHandleRef reference) { Production* module = ModuleManager::getInstance("Production"); if (!module) - return productionBindingFailure(eve::DiagnosticCode::StaleHandle, - "Production module is no longer loaded", "queue"); + return productionBindingFailure(eve::DiagnosticCode::StaleHandle, "Production module is no longer loaded", + "queue"); return module->queues_.erase(reference); } @@ -758,8 +732,8 @@ Module_IMPL(Production, new Production()); void Production::expose(ssq::Table& table) { const HSQUIRRELVM vm = table.getHandle(); - auto task = table.addClass( - "WorkTask", std::function([] { return nullptr; }), true); + auto task = + table.addClass("WorkTask", std::function([] { return nullptr; }), true); auto resolveTask = [](ScriptWorkTask* proxy) -> eve::OptionalRef { if (!proxy) return {}; auto queue = Production::resolve(proxy->reference); @@ -810,8 +784,8 @@ void Production::expose(ssq::Table& table) { return value ? value->get().reason : std::string{}; }); - auto event = table.addClass( - "WorkEvent", std::function([] { return nullptr; }), true); + auto event = + table.addClass("WorkEvent", std::function([] { return nullptr; }), true); auto resolveEvent = [](ScriptWorkEvent* proxy) -> eve::OptionalRef { if (!proxy) return {}; auto queue = Production::resolve(proxy->reference); @@ -854,8 +828,8 @@ void Production::expose(ssq::Table& table) { return value ? value->get().reason : std::string{}; }); - auto queue = table.addClass( - "WorkQueue", std::function([] { return nullptr; }), true); + auto queue = + table.addClass("WorkQueue", std::function([] { return nullptr; }), true); queue.addFunc("ownership", [](ScriptWorkQueue*) { return std::string("owned"); }); queue.addFunc("ownerEpoch", [](ScriptWorkQueue* value) { return value ? static_cast(value->reference.ownerEpoch) : int64_t{0}; @@ -863,90 +837,95 @@ void Production::expose(ssq::Table& table) { queue.addFunc("handle", [](ScriptWorkQueue* value) { return value ? static_cast(value->reference.packed()) : int64_t{0}; }); - queue.addFunc("isStale", [](ScriptWorkQueue* value) { - return !value || Production::isStale(value->reference); - }); + queue.addFunc("isStale", [](ScriptWorkQueue* value) { return !value || Production::isStale(value->reference); }); queue.addFunc("release", [vm](ScriptWorkQueue* value) { if (!value) return eve::script::projectResult( vm, productionBindingFailure(eve::DiagnosticCode::InvalidArgument, - "work queue proxy must not be null", "queue")); + "work queue proxy must not be null", "queue")); return eve::script::projectResult(vm, Production::release(value->reference)); }); - queue.addFunc("enqueue", [vm](ScriptWorkQueue* value, const std::string& owner, - const std::string& kind, const std::string& product, - const std::string& contextJson, float duration, int priority) { + queue.addFunc("enqueue", [vm](ScriptWorkQueue* value, const std::string& owner, const std::string& kind, + const std::string& product, const std::string& contextJson, float duration, + int priority) { if (!value) return eve::script::projectStatusResult( - vm, productionBindingFailure(eve::DiagnosticCode::InvalidArgument, - "work queue proxy must not be null", "queue") - .status(), false, false); + vm, + productionBindingFailure(eve::DiagnosticCode::InvalidArgument, + "work queue proxy must not be null", "queue") + .status(), + false, false); auto payload = eve::Value::fromJson(contextJson); if (!payload.ok()) return eve::script::projectStatusResult(vm, payload.status(), false, false); auto queueView = Production::resolve(value->reference); if (!queueView.ok()) return eve::script::projectStatusResult(vm, queueView.status(), false, false); return eve::script::projectResult( - vm, queueView.value().get().enqueue(owner, kind, product, - std::move(payload).takeValue(), duration, priority), + vm, + queueView.value().get().enqueue(owner, kind, product, std::move(payload).takeValue(), duration, priority), [](std::string id) { return eve::Value(std::move(id)); }); }); queue.addFunc("pause", [vm](ScriptWorkQueue* value, const std::string& id) { - if (!value) return eve::script::projectResult( - vm, productionBindingFailure(eve::DiagnosticCode::InvalidArgument, - "work queue proxy must not be null", "queue")); + if (!value) + return eve::script::projectResult( + vm, productionBindingFailure(eve::DiagnosticCode::InvalidArgument, + "work queue proxy must not be null", "queue")); auto queueView = Production::resolve(value->reference); if (!queueView.ok()) return eve::script::projectStatusResult(vm, queueView.status(), false, false); return eve::script::projectResult(vm, queueView.value().get().pause(id)); }); queue.addFunc("resume", [vm](ScriptWorkQueue* value, const std::string& id) { - if (!value) return eve::script::projectResult( - vm, productionBindingFailure(eve::DiagnosticCode::InvalidArgument, - "work queue proxy must not be null", "queue")); + if (!value) + return eve::script::projectResult( + vm, productionBindingFailure(eve::DiagnosticCode::InvalidArgument, + "work queue proxy must not be null", "queue")); auto queueView = Production::resolve(value->reference); if (!queueView.ok()) return eve::script::projectStatusResult(vm, queueView.status(), false, false); return eve::script::projectResult(vm, queueView.value().get().resume(id)); }); - queue.addFunc("cancel", [vm](ScriptWorkQueue* value, const std::string& id, - const std::string& reason) { - if (!value) return eve::script::projectResult( - vm, productionBindingFailure(eve::DiagnosticCode::InvalidArgument, - "work queue proxy must not be null", "queue")); + queue.addFunc("cancel", [vm](ScriptWorkQueue* value, const std::string& id, const std::string& reason) { + if (!value) + return eve::script::projectResult( + vm, productionBindingFailure(eve::DiagnosticCode::InvalidArgument, + "work queue proxy must not be null", "queue")); auto queueView = Production::resolve(value->reference); if (!queueView.ok()) return eve::script::projectStatusResult(vm, queueView.status(), false, false); return eve::script::projectResult(vm, queueView.value().get().cancel(id, reason)); }); - queue.addFunc("fail", [vm](ScriptWorkQueue* value, const std::string& id, - const std::string& reason) { - if (!value) return eve::script::projectResult( - vm, productionBindingFailure(eve::DiagnosticCode::InvalidArgument, - "work queue proxy must not be null", "queue")); + queue.addFunc("fail", [vm](ScriptWorkQueue* value, const std::string& id, const std::string& reason) { + if (!value) + return eve::script::projectResult( + vm, productionBindingFailure(eve::DiagnosticCode::InvalidArgument, + "work queue proxy must not be null", "queue")); auto queueView = Production::resolve(value->reference); if (!queueView.ok()) return eve::script::projectStatusResult(vm, queueView.status(), false, false); return eve::script::projectResult(vm, queueView.value().get().fail(id, reason)); }); queue.addFunc("advance", [vm](ScriptWorkQueue* value, std::int64_t tick, float seconds) { - if (!value) return eve::script::projectResult( - vm, productionBindingFailure(eve::DiagnosticCode::InvalidArgument, - "work queue proxy must not be null", "queue")); + if (!value) + return eve::script::projectResult( + vm, productionBindingFailure(eve::DiagnosticCode::InvalidArgument, + "work queue proxy must not be null", "queue")); auto delta = eve::Duration::fromSeconds(seconds); if (!delta.ok()) return eve::script::projectStatusResult(vm, delta.status(), false, false); auto queueView = Production::resolve(value->reference); if (!queueView.ok()) return eve::script::projectStatusResult(vm, queueView.status(), false, false); return eve::script::projectResult( - vm, queueView.value().get().advance({eve::SimulationTick(static_cast(tick)), - std::move(delta).takeValue()})); + vm, queueView.value().get().advance( + {eve::SimulationTick(static_cast(tick)), std::move(delta).takeValue()})); }); queue.addFunc("setSlotCount", [vm](ScriptWorkQueue* value, const std::string& owner, int slots) { - if (!value) return eve::script::projectResult( - vm, productionBindingFailure(eve::DiagnosticCode::InvalidArgument, - "work queue proxy must not be null", "queue")); + if (!value) + return eve::script::projectResult( + vm, productionBindingFailure(eve::DiagnosticCode::InvalidArgument, + "work queue proxy must not be null", "queue")); auto queueView = Production::resolve(value->reference); if (!queueView.ok()) return eve::script::projectStatusResult(vm, queueView.status(), false, false); return eve::script::projectResult(vm, queueView.value().get().setSlotCount(owner, slots)); }); queue.addFunc("slotCount", [](ScriptWorkQueue* value, const std::string& owner) { - auto queueView = value ? Production::resolve(value->reference) : eve::ResultRef::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "work queue proxy must not be null")); + auto queueView = value ? Production::resolve(value->reference) + : eve::ResultRef::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvalidArgument, "work queue proxy must not be null")); if (!queueView.ok()) { queueView.ignore("stale work queue while reading slot count"); return 0; @@ -954,8 +933,9 @@ void Production::expose(ssq::Table& table) { return queueView.value().get().slotCount(owner); }); queue.addFunc("runningCount", [](ScriptWorkQueue* value, const std::string& owner) { - auto queueView = value ? Production::resolve(value->reference) : eve::ResultRef::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "work queue proxy must not be null")); + auto queueView = value ? Production::resolve(value->reference) + : eve::ResultRef::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvalidArgument, "work queue proxy must not be null")); if (!queueView.ok()) { queueView.ignore("stale work queue while reading running count"); return 0; @@ -979,8 +959,9 @@ void Production::expose(ssq::Table& table) { return std::move(object).takeValue(); }); queue.addFunc("taskCount", [](ScriptWorkQueue* value) { - auto queueView = value ? Production::resolve(value->reference) : eve::ResultRef::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "work queue proxy must not be null")); + auto queueView = value ? Production::resolve(value->reference) + : eve::ResultRef::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvalidArgument, "work queue proxy must not be null")); if (!queueView.ok()) { queueView.ignore("stale work queue while reading task count"); return 0; @@ -995,8 +976,7 @@ void Production::expose(ssq::Table& table) { return ssq::Object(vm); } auto object = eve::script::makeOwnedSquirrelInstance( - vm, std::make_unique(value->reference, - queueView.value().get().taskAt(index)->get().id)); + vm, std::make_unique(value->reference, queueView.value().get().taskAt(index)->get().id)); if (!object.ok()) { object.ignore("failed to create work task proxy"); return ssq::Object(vm); @@ -1004,8 +984,9 @@ void Production::expose(ssq::Table& table) { return std::move(object).takeValue(); }); queue.addFunc("ownerTaskCount", [](ScriptWorkQueue* value, const std::string& owner) { - auto queueView = value ? Production::resolve(value->reference) : eve::ResultRef::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "work queue proxy must not be null")); + auto queueView = value ? Production::resolve(value->reference) + : eve::ResultRef::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvalidArgument, "work queue proxy must not be null")); if (!queueView.ok()) { queueView.ignore("stale work queue while reading owner task count"); return 0; @@ -1030,8 +1011,9 @@ void Production::expose(ssq::Table& table) { return std::move(object).takeValue(); }); queue.addFunc("eventCount", [](ScriptWorkQueue* value) { - auto queueView = value ? Production::resolve(value->reference) : eve::ResultRef::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "work queue proxy must not be null")); + auto queueView = value ? Production::resolve(value->reference) + : eve::ResultRef::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvalidArgument, "work queue proxy must not be null")); if (!queueView.ok()) { queueView.ignore("stale work queue while reading event count"); return 0; @@ -1054,41 +1036,48 @@ void Production::expose(ssq::Table& table) { return std::move(object).takeValue(); }); queue.addFunc("clearEvents", [vm](ScriptWorkQueue* value) { - if (!value) return eve::script::projectResult( - vm, productionBindingFailure(eve::DiagnosticCode::InvalidArgument, - "work queue proxy must not be null", "queue")); + if (!value) + return eve::script::projectResult( + vm, productionBindingFailure(eve::DiagnosticCode::InvalidArgument, + "work queue proxy must not be null", "queue")); auto queueView = Production::resolve(value->reference); if (!queueView.ok()) return eve::script::projectStatusResult(vm, queueView.status(), false, false); queueView.value().get().clearEvents(); return eve::script::projectResult(vm, eve::Result::success()); }); queue.addFunc("snapshot", [vm](ScriptWorkQueue* value) { - if (!value) return eve::script::projectResult( - vm, productionBindingFailure(eve::DiagnosticCode::InvalidArgument, - "work queue proxy must not be null", "queue"), - [](std::string text) { return eve::Value(std::move(text)); }); + if (!value) + return eve::script::projectResult( + vm, + productionBindingFailure(eve::DiagnosticCode::InvalidArgument, + "work queue proxy must not be null", "queue"), + [](std::string text) { return eve::Value(std::move(text)); }); auto queueView = Production::resolve(value->reference); - if (!queueView.ok()) return eve::script::projectResult( - vm, productionBindingFailure(queueView.status().code() == eve::StatusCode::NotFound - ? eve::DiagnosticCode::NotFound - : eve::DiagnosticCode::StaleHandle, - "work queue handle is stale", "queue"), - [](std::string text) { return eve::Value(std::move(text)); }); + if (!queueView.ok()) + return eve::script::projectResult( + vm, + productionBindingFailure(queueView.status().code() == eve::StatusCode::NotFound + ? eve::DiagnosticCode::NotFound + : eve::DiagnosticCode::StaleHandle, + "work queue handle is stale", "queue"), + [](std::string text) { return eve::Value(std::move(text)); }); return eve::script::projectResult(vm, queueView.value().get().snapshot(), [](std::string text) { return eve::Value(std::move(text)); }); }); queue.addFunc("restore", [vm](ScriptWorkQueue* value, const std::string& json) { - if (!value) return eve::script::projectResult( - vm, productionBindingFailure(eve::DiagnosticCode::InvalidArgument, - "work queue proxy must not be null", "queue")); + if (!value) + return eve::script::projectResult( + vm, productionBindingFailure(eve::DiagnosticCode::InvalidArgument, + "work queue proxy must not be null", "queue")); auto queueView = Production::resolve(value->reference); if (!queueView.ok()) return eve::script::projectStatusResult(vm, queueView.status(), false, false); return eve::script::projectResult(vm, queueView.value().get().restore(json)); }); queue.addFunc("clear", [vm](ScriptWorkQueue* value) { - if (!value) return eve::script::projectResult( - vm, productionBindingFailure(eve::DiagnosticCode::InvalidArgument, - "work queue proxy must not be null", "queue")); + if (!value) + return eve::script::projectResult( + vm, productionBindingFailure(eve::DiagnosticCode::InvalidArgument, + "work queue proxy must not be null", "queue")); auto queueView = Production::resolve(value->reference); if (!queueView.ok()) return eve::script::projectStatusResult(vm, queueView.status(), false, false); queueView.value().get().clear(); @@ -1102,8 +1091,7 @@ void Production::expose(ssq::Class& cls) { cls.addFunc("getName", &Production::getName); cls.addFunc("newWorkQueue", [vm = cls.getHandle()](Production*) -> ssq::Table { return makeOwnedProxy( - vm, Production::newQueueHandle(), - [](WorkQueueHandleRef ref) { return Production::release(ref); }, "queue"); + vm, Production::newQueueHandle(), [](WorkQueueHandleRef ref) { return Production::release(ref); }, "queue"); }); } diff --git a/src/modules/production/Production.h b/src/modules/production/Production.h index 173a3cc6..1d715b58 100644 --- a/src/modules/production/Production.h +++ b/src/modules/production/Production.h @@ -1,9 +1,9 @@ #pragma once -#include "common/Module.h" #include "common/BorrowedRef.h" -#include "common/Snapshot.h" +#include "common/Module.h" #include "common/Scheduling.h" +#include "common/Snapshot.h" #include "common/SquirrelOwnership.h" #include "common/Time.h" @@ -33,11 +33,11 @@ struct ProductionTask : eve::scheduling::ItemMetadata { std::string owner; std::string kind; std::string product; - eve::Value context = eve::Value(eve::Value::Object{}); - eve::Duration duration = eve::Duration::zero(); - eve::Duration progress = eve::Duration::zero(); + eve::Value context = eve::Value(eve::Value::Object{}); + eve::Duration duration = eve::Duration::zero(); + eve::Duration progress = eve::Duration::zero(); TaskState state = TaskState::Queued; - uint64_t enqueueSequence = 0; + uint64_t enqueueSequence = 0; }; /** @brief Deterministically sequenced production lifecycle event. */ @@ -65,19 +65,17 @@ public: * @param priority Higher priorities run first; enqueue order breaks ties. * @return A stable task ID, or a structured validation/allocation failure. */ - [[nodiscard]] eve::Result enqueue( - std::string_view owner, std::string_view kind, std::string_view product, - eve::Value context, double duration, int priority = 0); + [[nodiscard]] eve::Result enqueue(std::string_view owner, std::string_view kind, + std::string_view product, eve::Value context, double duration, + int priority = 0); /** @brief Pauses a queued or running task, or returns NotFound/Conflict. */ [[nodiscard]] eve::Result pause(std::string_view taskId); /** @brief Returns a paused task to deterministic scheduling. */ [[nodiscard]] eve::Result resume(std::string_view taskId); /** @brief Cancels a non-terminal task with a structured outcome. */ - [[nodiscard]] eve::Result cancel(std::string_view taskId, - std::string_view reason = "cancelled"); + [[nodiscard]] eve::Result cancel(std::string_view taskId, std::string_view reason = "cancelled"); /** @brief Marks a non-terminal task failed with a structured outcome. */ - [[nodiscard]] eve::Result fail(std::string_view taskId, - std::string_view reason = "failed"); + [[nodiscard]] eve::Result fail(std::string_view taskId, std::string_view reason = "failed"); /** * @brief Applies one injected deterministic simulation step. @@ -119,8 +117,7 @@ public: /** @brief Returns an owner's retained task by enqueue index, or an empty borrowed reference. */ [[nodiscard]] eve::OptionalRef ownerTaskAt(std::string_view owner, int index); /** @brief Const overload of ownerTaskAt with the same immediate-borrow lifetime. */ - [[nodiscard]] eve::OptionalRef ownerTaskAt( - std::string_view owner, int index) const; + [[nodiscard]] eve::OptionalRef ownerTaskAt(std::string_view owner, int index) const; /** @brief Returns retained event count. */ int eventCount() const; @@ -138,22 +135,20 @@ public: void clear(); /** @brief Captures the production payload in the common snapshot envelope. */ - [[nodiscard]] eve::Result snapshot( - const eve::SnapshotHashProvider& hashProvider) const; + [[nodiscard]] eve::Result snapshot(const eve::SnapshotHashProvider& hashProvider) const; /** * @brief Restores a verified or migrated production envelope atomically. * @param snapshot Source envelope with schema `production:queue`. * @param hashProvider Explicit content-digest provider. * @return Success, or a failure leaving queue state unchanged. */ - [[nodiscard]] eve::Result restoreSnapshot( - const eve::SnapshotEnvelope& snapshot, const eve::SnapshotHashProvider& hashProvider); + [[nodiscard]] eve::Result restoreSnapshot(const eve::SnapshotEnvelope& snapshot, + const eve::SnapshotHashProvider& hashProvider); /** @brief Serializes the common production snapshot envelope. */ - [[nodiscard]] eve::Result snapshotEnvelopeJson( - const eve::SnapshotHashProvider& hashProvider) const; + [[nodiscard]] eve::Result snapshotEnvelopeJson(const eve::SnapshotHashProvider& hashProvider) const; /** @brief Parses and transactionally restores a common production envelope. */ - [[nodiscard]] eve::Result restoreSnapshotJson( - std::string_view json, const eve::SnapshotHashProvider& hashProvider); + [[nodiscard]] eve::Result restoreSnapshotJson(std::string_view json, + const eve::SnapshotHashProvider& hashProvider); private: void schedule(std::string_view owner); @@ -162,9 +157,9 @@ private: uint64_t nextTaskId_ = 1; uint64_t nextEnqueueSequence_ = 1; uint64_t nextEventSequence_ = 1; - eve::PersistentId instanceId_; - eve::Revision revision_ = eve::Revision::zero(); - eve::SimulationTick tick_ = eve::SimulationTick::zero(); + eve::PersistentId instanceId_; + eve::Revision revision_ = eve::Revision::zero(); + eve::SimulationTick tick_ = eve::SimulationTick::zero(); std::deque> tasks_; std::deque events_; std::vector> slots_; @@ -173,9 +168,7 @@ private: /** @brief Returns the stable lowercase name of a task state. */ std::string_view taskStateName(TaskState state); /** @brief Writes the stable task-state spelling to a stream. */ -inline std::ostream& operator<<(std::ostream& stream, TaskState state) { - return stream << taskStateName(state); -} +inline std::ostream& operator<<(std::ostream& stream, TaskState state) { return stream << taskStateName(state); } /** @brief Returns the stable lowercase name of an event kind. */ std::string_view eventKindName(ProductionEventKind kind); @@ -198,8 +191,7 @@ public: * @ownership Production owns the queue; the returned reference does not. * @lifetime Valid only until queue mutation, release, module unload, or reload. */ - [[nodiscard]] static eve::ResultRef resolve( - WorkQueueHandleRef reference); + [[nodiscard]] static eve::ResultRef resolve(WorkQueueHandleRef reference); /** @brief Releases a module-owned queue. */ [[nodiscard]] static eve::Result release(WorkQueueHandleRef reference); /** @brief Reports whether a queue reference is stale for the current module. */ diff --git a/src/modules/property_access/PropertyAccess.cpp b/src/modules/property_access/PropertyAccess.cpp index 54f8eb9d..dd1dc3c6 100644 --- a/src/modules/property_access/PropertyAccess.cpp +++ b/src/modules/property_access/PropertyAccess.cpp @@ -8,9 +8,7 @@ namespace eve::property_access { eve::OptionalRef PropertySchema::find(const std::string &path) const { const auto found = std::find_if(properties.begin(), properties.end(), - [&path](const PropertyDescriptor &property) { - return property.path == path; - }); + [&path](const PropertyDescriptor &property) { return property.path == path; }); return found == properties.end() ? eve::OptionalRef{} : eve::OptionalRef{std::cref(*found)}; } @@ -19,15 +17,13 @@ WriteResult validatePropertyValue(const PropertyDescriptor &property, const Valu if (hasFlag(property.flags, PropertyFlag::ReadOnly)) return WriteResult::reject("property_access.property.read-only", "Property is read-only"); - const Value::Type type = value.type(); - bool compatible = false; + const Value::Type type = value.type(); + bool compatible = false; switch (property.kind) { case PropertyKind::Auto: compatible = true; break; case PropertyKind::Bool: compatible = type == Value::Type::Bool; break; case PropertyKind::Integer: compatible = type == Value::Type::Integer; break; - case PropertyKind::Number: - compatible = type == Value::Type::Number || type == Value::Type::Integer; - break; + case PropertyKind::Number: compatible = type == Value::Type::Number || type == Value::Type::Integer; break; case PropertyKind::String: case PropertyKind::Enum: case PropertyKind::AssetRef: @@ -47,21 +43,20 @@ WriteResult validatePropertyValue(const PropertyDescriptor &property, const Valu if (property.kind == PropertyKind::Enum) { const auto *selected = value.getIf(); - if (!selected || std::find(property.choices.begin(), property.choices.end(), *selected) == - property.choices.end()) + if (!selected || + std::find(property.choices.begin(), property.choices.end(), *selected) == property.choices.end()) return WriteResult::reject("property_access.property.choice", "Value is not an allowed choice"); } - double number = 0.0; - bool hasNumber = false; + double number = 0.0; + bool hasNumber = false; if (const auto *integer = value.getIf()) { - number = static_cast(*integer); + number = static_cast(*integer); hasNumber = true; } else if (const auto *floating = value.getIf()) { number = *floating; if (!std::isfinite(number)) - return WriteResult::reject("property_access.property.finite", - "Numeric values must be finite"); + return WriteResult::reject("property_access.property.finite", "Numeric values must be finite"); hasNumber = true; } if (hasNumber && property.numeric.minimum && number < *property.numeric.minimum) diff --git a/src/modules/property_access/PropertyAccess.h b/src/modules/property_access/PropertyAccess.h index b7da9fd8..0115ce94 100644 --- a/src/modules/property_access/PropertyAccess.h +++ b/src/modules/property_access/PropertyAccess.h @@ -38,23 +38,21 @@ enum class PropertyKind { /** @brief Cross-host property visibility and editing flags. */ enum class PropertyFlag : std::uint64_t { - None = 0, - ReadOnly = 1ull << 0, - Advanced = 1ull << 1, + None = 0, + ReadOnly = 1ull << 0, + Advanced = 1ull << 1, EditorOnly = 1ull << 2, - Runtime = 1ull << 3, - Transient = 1ull << 4, - Dangerous = 1ull << 5, - MultiEdit = 1ull << 6 + Runtime = 1ull << 3, + Transient = 1ull << 4, + Dangerous = 1ull << 5, + MultiEdit = 1ull << 6 }; constexpr PropertyFlag operator|(PropertyFlag left, PropertyFlag right) { - return static_cast(static_cast(left) | - static_cast(right)); + return static_cast(static_cast(left) | static_cast(right)); } constexpr PropertyFlag operator&(PropertyFlag left, PropertyFlag right) { - return static_cast(static_cast(left) & - static_cast(right)); + return static_cast(static_cast(left) & static_cast(right)); } constexpr bool hasFlag(PropertyFlag value, PropertyFlag flag) { return (value & flag) == flag; } @@ -63,28 +61,28 @@ struct NumericMetadata { std::optional minimum; std::optional maximum; std::optional step; - std::string units; - int precision = 3; + std::string units; + int precision = 3; }; /** @brief One stable property description consumed by any UI shell. */ struct PropertyDescriptor { - std::string path; - std::string displayName; - std::string description; - std::string category; - PropertyKind kind = PropertyKind::Auto; - PropertyFlag flags = PropertyFlag::None; - Value defaultValue; - NumericMetadata numeric; + std::string path; + std::string displayName; + std::string description; + std::string category; + PropertyKind kind = PropertyKind::Auto; + PropertyFlag flags = PropertyFlag::None; + Value defaultValue; + NumericMetadata numeric; std::vector choices; - std::string presenterHint; + std::string presenterHint; }; /** @brief Versioned schema for one view model or editable target. */ struct PropertySchema { - std::string typeId; - std::uint32_t version = 1; + std::string typeId; + std::uint32_t version = 1; std::vector properties; /** @brief Find a property by stable path, or an empty immediate borrow. */ @@ -93,7 +91,7 @@ struct PropertySchema { /** @brief Result of a model write, including a stable diagnostic code. */ struct WriteResult { - bool accepted = false; + bool accepted = false; std::string code; std::string message; @@ -113,13 +111,12 @@ struct WriteResult { * adapters. Host-specific adapters may translate its diagnostics, but must not * reimplement kind, enum, finite or numeric-range validation. */ -EVENGINE_API WriteResult validatePropertyValue(const PropertyDescriptor &property, - const Value &value); +EVENGINE_API WriteResult validatePropertyValue(const PropertyDescriptor &property, const Value &value); /** @brief Immutable notification emitted after a model property changes. */ struct PropertyChange { - std::string path; - Value value; + std::string path; + Value value; std::uint64_t revision = 0; }; diff --git a/src/modules/rpg/AttributeSystem.cpp b/src/modules/rpg/AttributeSystem.cpp index 6c2deca3..791d06e2 100644 --- a/src/modules/rpg/AttributeSystem.cpp +++ b/src/modules/rpg/AttributeSystem.cpp @@ -8,23 +8,19 @@ namespace eve::rpg { namespace { eve::Diagnostic invalidArgument(std::string message, std::string path = {}) { - return eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, std::move(message), - std::move(path)); + return eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, std::move(message), std::move(path)); } eve::Result actorRequired(RPGActor*) { - return eve::Result::failure( - invalidArgument("attribute operation requires an actor", "actor")); + return eve::Result::failure(invalidArgument("attribute operation requires an actor", "actor")); } eve::Result actorRequiredVoid(RPGActor*) { - return eve::Result::failure( - invalidArgument("attribute operation requires an actor", "actor")); + return eve::Result::failure(invalidArgument("attribute operation requires an actor", "actor")); } eve::Result actorRequiredCount(RPGActor*) { - return eve::Result::failure( - invalidArgument("attribute operation requires an actor", "actor")); + return eve::Result::failure(invalidArgument("attribute operation requires an actor", "actor")); } } // namespace @@ -55,24 +51,23 @@ eve::Result AttributeSystem::addModifier(RPGActor* actor, AttributeM return actor->attributes()->values.addModifier(std::move(modifier)); } -std::string AttributeSystem::addModifier(RPGActor* actor, const std::string& attribute, - const std::string& source, const std::string& operation, - double value, int priority) { +std::string AttributeSystem::addModifier(RPGActor* actor, const std::string& attribute, const std::string& source, + const std::string& operation, double value, int priority) { if (!actor || attribute.empty()) return {}; - auto parsed = ::eve::attributes::parseAttributeOperation(operation, value); + auto parsed = ::eve::attributes::parseAttributeOperation(operation, value); AttributeModifier modifier; modifier.attribute = attribute; - modifier.source = source; - modifier.priority = priority; + modifier.source = source; + modifier.priority = priority; if (parsed.ok()) { modifier.operation = parsed.value().operation; - modifier.value = parsed.value().value; + modifier.value = parsed.value().value; } else if (customOps().has(operation)) { parsed.ignore("RPG custom operation compatibility fallback"); modifier.operation = AttributeOperation::Custom; - modifier.policyId = operation; - modifier.value = value; + modifier.policyId = operation; + modifier.value = value; } else { parsed.ignore("unknown RPG attribute operation"); return {}; @@ -88,22 +83,20 @@ eve::Result AttributeSystem::removeModifier(RPGActor* actor, const Modifie return actor->attributes()->values.removeModifier(modifierId); } -bool AttributeSystem::removeModifier(RPGActor* actor, const std::string& attribute, - const std::string& modifierId) { +bool AttributeSystem::removeModifier(RPGActor* actor, const std::string& attribute, const std::string& modifierId) { if (!actor) return false; auto result = actor->attributes()->values.removeModifier(attribute, modifierId); return result.ok(); } -eve::Result AttributeSystem::removeModifiersBySource( - RPGActor* actor, const std::string& attribute, const std::string& source) { +eve::Result AttributeSystem::removeModifiersBySource(RPGActor* actor, const std::string& attribute, + const std::string& source) { if (!actor) return actorRequiredCount(actor); return actor->attributes()->values.removeBySource(source, attribute); } int AttributeSystem::removeAllModifiersBySource(RPGActor* actor, const std::string& source) { - auto result = actor ? actor->attributes()->values.removeBySource(source) - : actorRequiredCount(actor); + auto result = actor ? actor->attributes()->values.removeBySource(source) : actorRequiredCount(actor); return result.ok() ? result.value() : 0; } diff --git a/src/modules/rpg/AttributeSystem.h b/src/modules/rpg/AttributeSystem.h index 450cb4e6..03426ed2 100644 --- a/src/modules/rpg/AttributeSystem.h +++ b/src/modules/rpg/AttributeSystem.h @@ -42,26 +42,22 @@ public: * @brief Add one canonical modifier to an actor. * @return The generated or supplied modifier id, or structured rejection. */ - [[nodiscard]] static eve::Result addModifier(RPGActor* actor, - AttributeModifier modifier); + [[nodiscard]] static eve::Result addModifier(RPGActor* actor, AttributeModifier modifier); /** * @brief Compatibility string facade for existing RPG/effects callers. * @return Empty string when the request is rejected. */ - static std::string addModifier(RPGActor* actor, const std::string& attribute, - const std::string& source, const std::string& operation, - double value, int priority = 0); + static std::string addModifier(RPGActor* actor, const std::string& attribute, const std::string& source, + const std::string& operation, double value, int priority = 0); /** @brief Remove by canonical modifier id with a structured result. */ - [[nodiscard]] static eve::Result removeModifier(RPGActor* actor, - const ModifierId& modifierId); + [[nodiscard]] static eve::Result removeModifier(RPGActor* actor, const ModifierId& modifierId); /** @brief Compatibility boolean removal facade. */ - static bool removeModifier(RPGActor* actor, const std::string& attribute, - const std::string& modifierId); + static bool removeModifier(RPGActor* actor, const std::string& attribute, const std::string& modifierId); /** @brief Remove source modifiers with a structured count result. */ - [[nodiscard]] static eve::Result removeModifiersBySource( - RPGActor* actor, const std::string& attribute, const std::string& source); + [[nodiscard]] static eve::Result removeModifiersBySource(RPGActor* actor, const std::string& attribute, + const std::string& source); /** @brief Compatibility all-attribute source removal facade. */ static int removeAllModifiersBySource(RPGActor* actor, const std::string& source); diff --git a/src/modules/rpg/AttributeTypes.h b/src/modules/rpg/AttributeTypes.h index 743f38a9..3f6a92b2 100644 --- a/src/modules/rpg/AttributeTypes.h +++ b/src/modules/rpg/AttributeTypes.h @@ -14,13 +14,13 @@ namespace eve::rpg { -using AttributeId = ::eve::attributes::AttributeId; -using ModifierId = ::eve::attributes::ModifierId; -using SourceId = ::eve::attributes::SourceId; +using AttributeId = ::eve::attributes::AttributeId; +using ModifierId = ::eve::attributes::ModifierId; +using SourceId = ::eve::attributes::SourceId; using AttributeOperation = ::eve::attributes::AttributeOperation; -using AttributeModifier = ::eve::attributes::AttributeModifier; -using AttributeValue = ::eve::attributes::AttributeValue; -using AttributeOpTable = ::eve::attributes::AttributeOperationRegistry; +using AttributeModifier = ::eve::attributes::AttributeModifier; +using AttributeValue = ::eve::attributes::AttributeValue; +using AttributeOpTable = ::eve::attributes::AttributeOperationRegistry; /** @brief RPG exposes the canonical attributes computation without a duplicate overload. */ using ::eve::attributes::computeAttributeValue; diff --git a/src/modules/rpg/RPG.h b/src/modules/rpg/RPG.h index 99267065..bd4e542c 100644 --- a/src/modules/rpg/RPG.h +++ b/src/modules/rpg/RPG.h @@ -37,7 +37,8 @@ public: * @ownership The ECS world owns the actor; callers must release it through ECS and never delete it. * @lifetime Valid until actor/world destruction; retain the generation-qualified EntityHandle across frames. * @thread Call on the RPG module's owning ECS thread. - * @reentrancy The factory invokes no user callbacks; do not re-enter structural ECS mutation while using the result. + * @reentrancy The factory invokes no user callbacks; do not re-enter structural ECS mutation while using the + * result. */ RPGActor *newActor(); diff --git a/src/modules/rpg/RPGActor.h b/src/modules/rpg/RPGActor.h index dc293029..a1348cc4 100644 --- a/src/modules/rpg/RPGActor.h +++ b/src/modules/rpg/RPGActor.h @@ -47,10 +47,10 @@ public: * the ECS component for staging/copy operations. */ struct Statuses { - ::eve::effects::EffectContainer container; + ::eve::effects::EffectContainer container; std::unordered_map metadata; - std::unordered_map legacyIdByEffect; - std::unordered_map effectByLegacyId; + std::unordered_map legacyIdByEffect; + std::unordered_map effectByLegacyId; int nextInstanceId = 1; }; @@ -70,7 +70,8 @@ public: * @ownership The ECS world owns the actor; callers must release it through ECS and never delete it. * @lifetime Valid until actor/world destruction; retain the generation-qualified EntityHandle across frames. * @thread Call on the RPG ECS thread. - * @reentrancy The factory invokes no user callbacks; do not re-enter structural ECS mutation while using the result. + * @reentrancy The factory invokes no user callbacks; do not re-enter structural ECS mutation while using the + * result. */ static RPGActor *createActor(); @@ -89,8 +90,8 @@ public: /** @brief Canonical modifier insertion backed by AttributeSet. */ [[nodiscard]] eve::Result addAttributeModifier(AttributeModifier modifier); /** @brief Legacy string modifier insertion facade. */ - std::string addAttributeModifier(const std::string &attribute, const std::string &source, - const std::string &op, double value, int priority = 0); + std::string addAttributeModifier(const std::string &attribute, const std::string &source, const std::string &op, + double value, int priority = 0); /** @brief Canonical modifier removal backed by AttributeSet. */ [[nodiscard]] eve::Result removeAttributeModifier(const ModifierId &modifierId); bool removeAttributeModifier(const std::string &attribute, const std::string &modifierId); diff --git a/src/modules/rpg/SettlementAdapter.cpp b/src/modules/rpg/SettlementAdapter.cpp index 17d49aae..5bf0e76b 100644 --- a/src/modules/rpg/SettlementAdapter.cpp +++ b/src/modules/rpg/SettlementAdapter.cpp @@ -12,8 +12,7 @@ namespace { template eve::Result failure(eve::DiagnosticCode code, std::string message, std::string path = {}) { - return eve::Result::failure( - eve::Diagnostic::error(code, std::move(message), std::move(path))); + return eve::Result::failure(eve::Diagnostic::error(code, std::move(message), std::move(path))); } template @@ -27,14 +26,12 @@ eve::Result success(eve::StatusCode code = eve::StatusCode::Ok) { bool isDamageKind(std::string_view kind) noexcept { return kind == "damage"; } -bool isHealingKind(std::string_view kind) noexcept { - return kind == "heal" || kind == "healing"; -} +bool isHealingKind(std::string_view kind) noexcept { return kind == "heal" || kind == "healing"; } eve::Result checkedNumber(double value, std::string_view name) { if (!std::isfinite(value)) - return failure(eve::DiagnosticCode::InvariantViolation, - std::string(name) + " must be finite", std::string(name)); + return failure(eve::DiagnosticCode::InvariantViolation, std::string(name) + " must be finite", + std::string(name)); return eve::Result::success(value); } @@ -46,14 +43,13 @@ RPGSettlementAdapter::RPGSettlementAdapter(RPGActor& target, SubjectRef targetRe RPGSettlementAdapter::RPGSettlementAdapter(RPGActor& target, SubjectRef targetRef, Config config) : target_(target), targetRef_(targetRef), config_(std::move(config)) {} -RPGSettlementAdapter::RPGSettlementAdapter(RPGActor& target, SubjectRef targetRef, - RPGActor* source, SubjectRef sourceRef) +RPGSettlementAdapter::RPGSettlementAdapter(RPGActor& target, SubjectRef targetRef, RPGActor* source, + SubjectRef sourceRef) : RPGSettlementAdapter(target, std::move(targetRef), source, std::move(sourceRef), Config{}) {} -RPGSettlementAdapter::RPGSettlementAdapter(RPGActor& target, SubjectRef targetRef, - RPGActor* source, SubjectRef sourceRef, Config config) - : target_(target), targetRef_(targetRef), source_(source), sourceRef_(sourceRef), - config_(std::move(config)) {} +RPGSettlementAdapter::RPGSettlementAdapter(RPGActor& target, SubjectRef targetRef, RPGActor* source, + SubjectRef sourceRef, Config config) + : target_(target), targetRef_(targetRef), source_(source), sourceRef_(sourceRef), config_(std::move(config)) {} bool RPGSettlementAdapter::isDamage(const settlement::SettlementContext& context) const noexcept { return isDamageKind(context.request().kind); @@ -64,7 +60,7 @@ bool RPGSettlementAdapter::isHealing(const settlement::SettlementContext& contex } const eve::Value* RPGSettlementAdapter::contextValue(const settlement::SettlementContext& context, - std::string_view key) noexcept { + std::string_view key) noexcept { const auto* object = context.request().context.getIf(); if (object == nullptr) return nullptr; const auto it = object->find(std::string(key)); @@ -92,12 +88,12 @@ std::optional RPGSettlementAdapter::stringValue(const eve::Value* v eve::Result RPGSettlementAdapter::readBase(std::string_view name) const { if (name.empty()) - return failure(eve::DiagnosticCode::InvalidArgument, - "RPG settlement attribute name must not be empty", "attribute"); + return failure(eve::DiagnosticCode::InvalidArgument, "RPG settlement attribute name must not be empty", + "attribute"); const auto& attributes = target_.attributes()->values; if (!attributes.has(std::string(name))) - return failure(eve::DiagnosticCode::NotFound, - "RPG settlement attribute is not present", std::string(name)); + return failure(eve::DiagnosticCode::NotFound, "RPG settlement attribute is not present", + std::string(name)); return checkedNumber(attributes.getBase(std::string(name)), name); } @@ -108,8 +104,7 @@ eve::Result RPGSettlementAdapter::readFinal(std::string_view name, doubl return checkedNumber(attributes.getFinal(std::string(name), fallback), name); } -eve::Result RPGSettlementAdapter::readSourceFinal(std::string_view name, - double fallback) const { +eve::Result RPGSettlementAdapter::readSourceFinal(std::string_view name, double fallback) const { if (source_ == nullptr || !sourceRef_.isValid()) return eve::Result::success(fallback); const auto& attributes = source_->attributes()->values; if (!attributes.has(std::string(name))) return eve::Result::success(fallback); @@ -119,30 +114,27 @@ eve::Result RPGSettlementAdapter::readSourceFinal(std::string_view name, eve::Result RPGSettlementAdapter::validate(settlement::SettlementContext& context) { if (!(context.request().target == targetRef_)) return failure(eve::DiagnosticCode::Conflict, - "RPG settlement request target does not match adapter target", - "target"); - const bool damage = isDamage(context); + "RPG settlement request target does not match adapter target", "target"); + const bool damage = isDamage(context); const bool healing = isHealing(context); if (!damage && !healing) return failure(eve::DiagnosticCode::Unsupported, - "RPG settlement adapter supports damage and healing kinds only", - "kind"); + "RPG settlement adapter supports damage and healing kinds only", "kind"); - auto health = readBase(config_.healthAttribute); + auto health = readBase(config_.healthAttribute); const bool healthOk = health.ok(); if (!healthOk) { const auto status = health.status(); return failure(status); } - auto maximum = readBase(config_.maxHealthAttribute); + auto maximum = readBase(config_.maxHealthAttribute); const bool maximumOk = maximum.ok(); if (!maximumOk) { const auto status = maximum.status(); return failure(status); } if (health.value() > maximum.value()) - return failure(eve::DiagnosticCode::InvariantViolation, - "RPG health exceeds max health", "health"); + return failure(eve::DiagnosticCode::InvariantViolation, "RPG health exceeds max health", "health"); context.setStageDetail("policy", "rpg"); context.setStageDetail("health_attribute", config_.healthAttribute); context.setStageDetail("kind", context.request().kind); @@ -155,7 +147,7 @@ eve::Result RPGSettlementAdapter::sourceModifiers(settlement::SettlementCo if (const auto value = numberValue(contextValue(context, "source_multiplier"))) { multiplier = *value; } else { - auto sourceMultiplier = readSourceFinal(config_.sourceMultiplierAttribute, 1.0); + auto sourceMultiplier = readSourceFinal(config_.sourceMultiplierAttribute, 1.0); const bool sourceMultiplierOk = sourceMultiplier.ok(); if (!sourceMultiplierOk) { const auto status = sourceMultiplier.status(); @@ -165,10 +157,9 @@ eve::Result RPGSettlementAdapter::sourceModifiers(settlement::SettlementCo } if (!std::isfinite(multiplier) || multiplier < 0.0) return failure(eve::DiagnosticCode::InvalidArgument, - "RPG source multiplier must be finite and non-negative", - "source_multiplier"); + "RPG source multiplier must be finite and non-negative", "source_multiplier"); - auto scaled = context.setMagnitude(context.magnitude() * multiplier); + auto scaled = context.setMagnitude(context.magnitude() * multiplier); const bool scaledOk = scaled.ok(); if (!scaledOk) { const auto status = scaled.status(); @@ -177,8 +168,8 @@ eve::Result RPGSettlementAdapter::sourceModifiers(settlement::SettlementCo bool critical = boolValue(contextValue(context, "critical")).value_or(false); if (!critical) { - const auto roll = numberValue(contextValue(context, "critical_roll")); - auto chance = readSourceFinal(config_.criticalChanceAttribute, 0.0); + const auto roll = numberValue(contextValue(context, "critical_roll")); + auto chance = readSourceFinal(config_.criticalChanceAttribute, 0.0); const bool chanceOk = chance.ok(); if (!chanceOk) { const auto status = chance.status(); @@ -186,7 +177,7 @@ eve::Result RPGSettlementAdapter::sourceModifiers(settlement::SettlementCo } if (roll && std::isfinite(*roll)) { const double probability = std::clamp(chance.value(), 0.0, 1.0); - critical = *roll >= 0.0 && *roll < probability; + critical = *roll >= 0.0 && *roll < probability; } } @@ -195,7 +186,7 @@ eve::Result RPGSettlementAdapter::sourceModifiers(settlement::SettlementCo if (const auto value = numberValue(contextValue(context, "critical_multiplier"))) { criticalMultiplier = *value; } else { - auto configured = readSourceFinal(config_.criticalMultiplierAttribute, 2.0); + auto configured = readSourceFinal(config_.criticalMultiplierAttribute, 2.0); const bool configuredOk = configured.ok(); if (!configuredOk) { const auto status = configured.status(); @@ -205,9 +196,8 @@ eve::Result RPGSettlementAdapter::sourceModifiers(settlement::SettlementCo } if (!std::isfinite(criticalMultiplier) || criticalMultiplier < 1.0) return failure(eve::DiagnosticCode::InvalidArgument, - "RPG critical multiplier must be finite and at least one", - "critical_multiplier"); - auto critScaled = context.setMagnitude(context.magnitude() * criticalMultiplier); + "RPG critical multiplier must be finite and at least one", "critical_multiplier"); + auto critScaled = context.setMagnitude(context.magnitude() * criticalMultiplier); const bool critScaledOk = critScaled.ok(); if (!critScaledOk) { const auto status = critScaled.status(); @@ -224,23 +214,23 @@ eve::Result RPGSettlementAdapter::sourceModifiers(settlement::SettlementCo eve::Result RPGSettlementAdapter::targetMitigation(settlement::SettlementContext& context) { if (!isDamage(context)) return eve::Result::success(); - std::string element = stringValue(contextValue(context, "element")).value_or("damage"); + std::string element = stringValue(contextValue(context, "element")).value_or("damage"); const std::string resistanceAttribute = config_.resistancePrefix + element; - auto resistance = readFinal(resistanceAttribute, 0.0); - const bool resistanceOk = resistance.ok(); + auto resistance = readFinal(resistanceAttribute, 0.0); + const bool resistanceOk = resistance.ok(); if (!resistanceOk) { const auto status = resistance.status(); return failure(status); } - const double factor = 1.0 - std::clamp(resistance.value(), 0.0, 1.0); - const double before = context.magnitude(); - auto changed = context.setMagnitude(before * factor); - const bool changedOk = changed.ok(); + const double factor = 1.0 - std::clamp(resistance.value(), 0.0, 1.0); + const double before = context.magnitude(); + auto changed = context.setMagnitude(before * factor); + const bool changedOk = changed.ok(); if (!changedOk) { const auto status = changed.status(); return failure(status); } - auto recorded = context.addResisted(before - context.magnitude()); + auto recorded = context.addResisted(before - context.magnitude()); const bool recordedOk = recorded.ok(); if (!recordedOk) { const auto status = recorded.status(); @@ -254,31 +244,30 @@ eve::Result RPGSettlementAdapter::targetMitigation(settlement::SettlementC eve::Result RPGSettlementAdapter::armorShield(settlement::SettlementContext& context) { if (!isDamage(context)) return eve::Result::success(); - auto armor = readFinal(config_.armorAttribute, 0.0); + auto armor = readFinal(config_.armorAttribute, 0.0); const bool armorOk = armor.ok(); if (!armorOk) { const auto status = armor.status(); return failure(status); } - const double penetration = std::max(0.0, numberValue(contextValue(context, "penetration")) - .value_or(0.0)); + const double penetration = std::max(0.0, numberValue(contextValue(context, "penetration")).value_or(0.0)); const double effectiveArmor = std::max(0.0, armor.value() - penetration); - const double armorFactor = 100.0 / (100.0 + effectiveArmor); - const double beforeArmor = context.magnitude(); - auto armorChanged = context.setMagnitude(beforeArmor * armorFactor); - const bool armorChangedOk = armorChanged.ok(); + const double armorFactor = 100.0 / (100.0 + effectiveArmor); + const double beforeArmor = context.magnitude(); + auto armorChanged = context.setMagnitude(beforeArmor * armorFactor); + const bool armorChangedOk = armorChanged.ok(); if (!armorChangedOk) { const auto status = armorChanged.status(); return failure(status); } - auto armorLoss = context.addResisted(beforeArmor - context.magnitude()); + auto armorLoss = context.addResisted(beforeArmor - context.magnitude()); const bool armorLossOk = armorLoss.ok(); if (!armorLossOk) { const auto status = armorLoss.status(); return failure(status); } - auto shield = readBase(config_.shieldAttribute); + auto shield = readBase(config_.shieldAttribute); double shieldValue = 0.0; if (shield.ok()) { shieldValue = shield.value(); @@ -286,14 +275,14 @@ eve::Result RPGSettlementAdapter::armorShield(settlement::SettlementContex const auto status = shield.status(); return failure(status); } - const double absorbed = std::min(shieldValue, context.magnitude()); - auto shieldChanged = context.setMagnitude(context.magnitude() - absorbed); - const bool shieldChangedOk = shieldChanged.ok(); + const double absorbed = std::min(shieldValue, context.magnitude()); + auto shieldChanged = context.setMagnitude(context.magnitude() - absorbed); + const bool shieldChangedOk = shieldChanged.ok(); if (!shieldChangedOk) { const auto status = shieldChanged.status(); return failure(status); } - auto absorbedRecorded = context.addAbsorbed(absorbed); + auto absorbedRecorded = context.addAbsorbed(absorbed); const bool absorbedRecordedOk = absorbedRecorded.ok(); if (!absorbedRecordedOk) { const auto status = absorbedRecorded.status(); @@ -307,21 +296,21 @@ eve::Result RPGSettlementAdapter::armorShield(settlement::SettlementContex } eve::Result RPGSettlementAdapter::clamp(settlement::SettlementContext& context) { - auto health = readBase(config_.healthAttribute); + auto health = readBase(config_.healthAttribute); const bool healthOk = health.ok(); if (!healthOk) { const auto status = health.status(); return failure(status); } - auto maximum = readBase(config_.maxHealthAttribute); + auto maximum = readBase(config_.maxHealthAttribute); const bool maximumOk = maximum.ok(); if (!maximumOk) { const auto status = maximum.status(); return failure(status); } - const double bound = isDamage(context) ? health.value() : maximum.value() - health.value(); - auto configured = context.setClampMax(std::max(0.0, bound)); - const bool configuredOk = configured.ok(); + const double bound = isDamage(context) ? health.value() : maximum.value() - health.value(); + auto configured = context.setClampMax(std::max(0.0, bound)); + const bool configuredOk = configured.ok(); if (!configuredOk) { const auto status = configured.status(); return failure(status); @@ -332,35 +321,34 @@ eve::Result RPGSettlementAdapter::clamp(settlement::SettlementContext& con eve::Result RPGSettlementAdapter::prepareApply( const settlement::SettlementContext& context) { - auto health = readBase(config_.healthAttribute); + auto health = readBase(config_.healthAttribute); const bool healthOk = health.ok(); if (!healthOk) { const auto status = health.status(); return failure(status); } - auto maximum = readBase(config_.maxHealthAttribute); + auto maximum = readBase(config_.maxHealthAttribute); const bool maximumOk = maximum.ok(); if (!maximumOk) { const auto status = maximum.status(); return failure(status); } - auto candidate = target_.attributes()->values; - const double nextHealth = isDamage(context) ? health.value() - context.magnitude() - : health.value() + context.magnitude(); + auto candidate = target_.attributes()->values; + const double nextHealth = + isDamage(context) ? health.value() - context.magnitude() : health.value() + context.magnitude(); if (!std::isfinite(nextHealth) || nextHealth < 0.0 || nextHealth > maximum.value()) - return failure( - eve::DiagnosticCode::InvariantViolation, - "RPG settlement candidate health is outside its validated bounds", "health"); + return failure(eve::DiagnosticCode::InvariantViolation, + "RPG settlement candidate health is outside its validated bounds", + "health"); candidate.setBase(config_.healthAttribute, nextHealth); if (isDamage(context) && !config_.shieldAttribute.empty() && context.absorbed() > 0.0) { - auto shield = readBase(config_.shieldAttribute); + auto shield = readBase(config_.shieldAttribute); const bool shieldOk = shield.ok(); if (!shieldOk) { const auto status = shield.status(); - if (status.code() != eve::StatusCode::NotFound) - return failure(status); + if (status.code() != eve::StatusCode::NotFound) return failure(status); } else { if (context.absorbed() > shield.value()) return failure( @@ -371,24 +359,22 @@ eve::Result RPGSettlementAdapter::prepareApply( } struct Pending { - RPGActor* actor = nullptr; + RPGActor* actor = nullptr; eve::attributes::AttributeSet before; eve::attributes::AttributeSet candidate; - bool published = false; + bool published = false; }; - auto pending = std::make_shared(Pending{&target_, target_.attributes()->values, - std::move(candidate), false}); - return eve::Result::success( - settlement::PreparedApply( - [pending]() -> eve::Result { - pending->actor->attributes()->values = std::move(pending->candidate); - pending->published = true; - return success(eve::StatusCode::Applied); - }, - [pending]() { - if (pending->published) - pending->actor->attributes()->values = pending->before; - })); + auto pending = + std::make_shared(Pending{&target_, target_.attributes()->values, std::move(candidate), false}); + return eve::Result::success(settlement::PreparedApply( + [pending]() -> eve::Result { + pending->actor->attributes()->values = std::move(pending->candidate); + pending->published = true; + return success(eve::StatusCode::Applied); + }, + [pending]() { + if (pending->published) pending->actor->attributes()->values = pending->before; + })); } } // namespace eve::rpg diff --git a/src/modules/rpg/SettlementAdapter.h b/src/modules/rpg/SettlementAdapter.h index 25a798d5..78f48b2f 100644 --- a/src/modules/rpg/SettlementAdapter.h +++ b/src/modules/rpg/SettlementAdapter.h @@ -29,14 +29,14 @@ class RPGSettlementAdapter final : public settlement::ISettlementPolicy { public: /** @brief Attribute names used by the RPG policy. */ struct Config { - std::string healthAttribute = "health"; - std::string maxHealthAttribute = "max_health"; - std::string shieldAttribute = "shield"; - std::string armorAttribute = "armor"; - std::string resistancePrefix = "resistance."; - std::string criticalChanceAttribute = "critical_chance"; + std::string healthAttribute = "health"; + std::string maxHealthAttribute = "max_health"; + std::string shieldAttribute = "shield"; + std::string armorAttribute = "armor"; + std::string resistancePrefix = "resistance."; + std::string criticalChanceAttribute = "critical_chance"; std::string criticalMultiplierAttribute = "critical_multiplier"; - std::string sourceMultiplierAttribute = "damage_multiplier"; + std::string sourceMultiplierAttribute = "damage_multiplier"; }; /** @brief Bind a target actor with default RPG attribute names. */ @@ -44,12 +44,10 @@ public: RPGSettlementAdapter(RPGActor& target, SubjectRef targetRef, Config config); /** @brief Bind target and optional source actors with explicit identities. */ - RPGSettlementAdapter(RPGActor& target, SubjectRef targetRef, RPGActor* source, - SubjectRef sourceRef); - RPGSettlementAdapter(RPGActor& target, SubjectRef targetRef, RPGActor* source, - SubjectRef sourceRef, Config config); + RPGSettlementAdapter(RPGActor& target, SubjectRef targetRef, RPGActor* source, SubjectRef sourceRef); + RPGSettlementAdapter(RPGActor& target, SubjectRef targetRef, RPGActor* source, SubjectRef sourceRef, Config config); - RPGSettlementAdapter(const RPGSettlementAdapter&) = delete; + RPGSettlementAdapter(const RPGSettlementAdapter&) = delete; RPGSettlementAdapter& operator=(const RPGSettlementAdapter&) = delete; /** @brief Stable identity checked for the target side of the request. */ @@ -80,21 +78,20 @@ private: * @thread Call on the settlement pipeline's owning simulation thread. * @reentrancy Does not invoke callbacks and is invalid across re-entrant context mutation. */ - [[nodiscard]] static const eve::Value* contextValue( - const settlement::SettlementContext& context, std::string_view key) noexcept; - [[nodiscard]] static std::optional numberValue(const eve::Value* value) noexcept; - [[nodiscard]] static std::optional boolValue(const eve::Value* value) noexcept; + [[nodiscard]] static const eve::Value* contextValue(const settlement::SettlementContext& context, + std::string_view key) noexcept; + [[nodiscard]] static std::optional numberValue(const eve::Value* value) noexcept; + [[nodiscard]] static std::optional boolValue(const eve::Value* value) noexcept; [[nodiscard]] static std::optional stringValue(const eve::Value* value); - [[nodiscard]] eve::Result readBase(std::string_view name) const; - [[nodiscard]] eve::Result readFinal(std::string_view name, double fallback = 0.0) const; - [[nodiscard]] eve::Result readSourceFinal(std::string_view name, - double fallback = 0.0) const; + [[nodiscard]] eve::Result readBase(std::string_view name) const; + [[nodiscard]] eve::Result readFinal(std::string_view name, double fallback = 0.0) const; + [[nodiscard]] eve::Result readSourceFinal(std::string_view name, double fallback = 0.0) const; - RPGActor& target_; + RPGActor& target_; SubjectRef targetRef_; - RPGActor* source_ = nullptr; + RPGActor* source_ = nullptr; SubjectRef sourceRef_; - Config config_; + Config config_; }; } // namespace eve::rpg diff --git a/src/modules/rpg/Skill.cpp b/src/modules/rpg/Skill.cpp index 924682e5..0b2c6c3f 100644 --- a/src/modules/rpg/Skill.cpp +++ b/src/modules/rpg/Skill.cpp @@ -67,9 +67,7 @@ std::unordered_map &table() { return t; } -eve::definitions::DefinitionRegistry& SkillRegistry::definitionRegistry() { - return canonicalSkillRegistry(); -} +eve::definitions::DefinitionRegistry& SkillRegistry::definitionRegistry() { return canonicalSkillRegistry(); } void SkillRegistry::registerSkill(const SkillDefinition &def) { if (def.id.empty()) return; @@ -78,13 +76,13 @@ void SkillRegistry::registerSkill(const SkillDefinition &def) { json.ignore("legacy skill registration cannot serialize invalid definition"); return; } - auto& registry = canonicalSkillRegistry(); - auto existing = registry.resolve("rpg.skill", def.id); - eve::Result result = existing.ok() - ? registry.replace("rpg.skill", def.id, 1, std::move(json).takeValue()) + auto& registry = canonicalSkillRegistry(); + auto existing = registry.resolve("rpg.skill", def.id); + eve::Result result = + existing.ok() ? registry.replace("rpg.skill", def.id, 1, std::move(json).takeValue()) : existing.status().code() == eve::StatusCode::NotFound - ? registry.insert("rpg.skill", def.id, 1, std::move(json).takeValue()) - : eve::Result::failure(existing.status()); + ? registry.insert("rpg.skill", def.id, 1, std::move(json).takeValue()) + : eve::Result::failure(existing.status()); if (!result) return; table()[def.id] = def; } @@ -95,15 +93,15 @@ const SkillDefinition *SkillRegistry::find(const std::string &id) { return it == t.end() ? nullptr : &it->second; } -bool SkillRegistry::remove(const std::string &id) { +bool SkillRegistry::remove(const std::string& id) { auto& registry = canonicalSkillRegistry(); - auto result = registry.remove("rpg.skill", id); + auto result = registry.remove("rpg.skill", id); if (!result) return false; return table().erase(id) > 0; } void SkillRegistry::clear() { - auto& skills = table(); + auto& skills = table(); auto& registry = canonicalSkillRegistry(); for (const auto& [id, unused] : skills) { (void)unused; @@ -117,7 +115,7 @@ int SkillRegistry::count() { return int(table().size()); } namespace { -SkillDefinition parseSkillObject(Value o, std::string *error) { +SkillDefinition parseSkillObject(Value o, std::string* error) { SkillDefinition def; if (!o.isObject()) return def; def.id = o.getString("id"); @@ -132,17 +130,15 @@ SkillDefinition parseSkillObject(Value o, std::string *error) { for (size_t i = 0; i < costs.size(); ++i) { const Value co = costs.at(i); const std::string resource = co.getString("attribute"); - const double amount = co.getDouble("amount", 0.0); + const double amount = co.getDouble("amount", 0.0); // Keep the legacy JSON shape, but do not silently truncate a value // into the canonical integral resource Amount type. if (resource.empty()) continue; - if (!std::isfinite(amount) || amount <= 0.0 || std::floor(amount) != amount || - amount >= std::ldexp(1.0, 63)) { + if (!std::isfinite(amount) || amount <= 0.0 || std::floor(amount) != amount || amount >= std::ldexp(1.0, 63)) { if (error) *error = "skill cost must be a finite positive integer: " + resource; return {}; } - auto item = eve::resource::ResourceCost::create( - resource, static_cast(amount)); + auto item = eve::resource::ResourceCost::create(resource, static_cast(amount)); if (!item) { if (error) *error = item.status().describe(); return {}; diff --git a/src/modules/rpg/Skill.h b/src/modules/rpg/Skill.h index 396f7e3f..e252b377 100644 --- a/src/modules/rpg/Skill.h +++ b/src/modules/rpg/Skill.h @@ -12,8 +12,8 @@ // 任何自定义资源;RPG 不再维护一份私有 Cost 类型。 #include "common/ResourceAccount.h" -#include "definitions/Definitions.h" #include "decision/Condition.h" +#include "definitions/Definitions.h" #include #include @@ -32,7 +32,7 @@ struct SkillDefinition { std::vector grantedEffects; ///< 命中/释放后施加给目标的效果 id 列表 std::vector tags; /** @brief Side-effect-free condition tree required before this skill can cast. */ - eve::decision::Condition castCondition; + eve::decision::Condition castCondition; std::unordered_map extra; ///< 游戏自定义附加数据 bool hasTag(const std::string &tag) const; @@ -46,7 +46,7 @@ public: * legacy static SkillRegistry contract. Typed runtime adapters bind to it * through a generation-qualified DefinitionRef. */ - [[nodiscard]] static eve::definitions::DefinitionRegistry& definitionRegistry(); + [[nodiscard]] static eve::definitions::DefinitionRegistry &definitionRegistry(); static void registerSkill(const SkillDefinition &def); /** diff --git a/src/modules/rpg/SkillAction.cpp b/src/modules/rpg/SkillAction.cpp index 884fd025..197f1e5a 100644 --- a/src/modules/rpg/SkillAction.cpp +++ b/src/modules/rpg/SkillAction.cpp @@ -7,18 +7,16 @@ namespace { template eve::Result failure(eve::DiagnosticCode code, std::string message, std::string path = {}) { - return eve::Result::failure( - eve::Diagnostic::error(code, std::move(message), std::move(path))); + return eve::Result::failure(eve::Diagnostic::error(code, std::move(message), std::move(path))); } eve::Result skillActionId(const SkillDefinition& skill) { if (skill.id.empty()) - return failure(eve::DiagnosticCode::InvalidArgument, - "skill id must not be empty", "skill.id"); + return failure(eve::DiagnosticCode::InvalidArgument, "skill id must not be empty", "skill.id"); const auto id = eve::LogicalId::fromParts("rpg", "skill." + skill.id); if (!id) return failure(eve::DiagnosticCode::InvalidArgument, - "skill id cannot form a valid action logical id", "skill.id"); + "skill id cannot form a valid action logical id", "skill.id"); return eve::Result::success(*id); } @@ -37,8 +35,7 @@ eve::Result seconds(float value, const char* path) { } // namespace -eve::Result SkillActionAdapter::makeDefinition( - const SkillDefinition& skill) { +eve::Result SkillActionAdapter::makeDefinition(const SkillDefinition& skill) { auto id = skillActionId(skill); if (!id) return eve::Result::failure(id.status()); auto windup = seconds(skill.castTime, "skill.castTime"); @@ -47,12 +44,12 @@ eve::Result SkillActionAdapter::makeDefinition( if (!cooldown) return eve::Result::failure(cooldown.status()); action::ActionDefinition definition; - definition.id = std::move(id).takeValue(); + definition.id = std::move(id).takeValue(); definition.timing.windup = std::move(windup).takeValue(); - definition.condition = skill.castCondition; - definition.targetingMode = skill.targetType == "self" ? action::TargetingMode::None - : action::TargetingMode::Explicit; - definition.cost = skill.cost; + definition.condition = skill.castCondition; + definition.targetingMode = + skill.targetType == "self" ? action::TargetingMode::None : action::TargetingMode::Explicit; + definition.cost = skill.cost; definition.effectIds = skill.grantedEffects; // A skill is an Active operation even when it currently has no effect id; // the injected executor may emit a settlement/event for that operation. @@ -66,25 +63,24 @@ eve::Result SkillActionAdapter::makeDefinition( return eve::Result::success(std::move(definition)); } -eve::Result SkillActionAdapter::makeRequest( - const SkillDefinition& skill, std::optional source, - std::optional target, eve::SimulationTick requestedTick) { +eve::Result SkillActionAdapter::makeRequest(const SkillDefinition& skill, + std::optional source, + std::optional target, + eve::SimulationTick requestedTick) { auto definition = makeDefinition(skill); if (!definition) return eve::Result::failure(definition.status()); auto checkedDefinition = std::move(definition).takeValue(); if (checkedDefinition.targetingMode == action::TargetingMode::None && target) - return failure( - eve::DiagnosticCode::InvalidArgument, - "self skill request cannot carry an explicit target", "target"); + return failure(eve::DiagnosticCode::InvalidArgument, + "self skill request cannot carry an explicit target", "target"); if (checkedDefinition.targetingMode == action::TargetingMode::Explicit && !target) - return failure( - eve::DiagnosticCode::PreconditionViolation, - "non-self skill request requires an explicit target", "target"); + return failure(eve::DiagnosticCode::PreconditionViolation, + "non-self skill request requires an explicit target", "target"); action::ActionRequest request; - request.actionId = checkedDefinition.id; - request.source = std::move(source); + request.actionId = checkedDefinition.id; + request.source = std::move(source); request.requestedTick = requestedTick; if (target) request.targetEntities.push_back(*target); auto valid = request.validate(checkedDefinition); @@ -92,10 +88,11 @@ eve::Result SkillActionAdapter::makeRequest( return eve::Result::success(std::move(request)); } -eve::Result SkillActionAdapter::submit( - action::ActionRuntime& runtime, const SkillDefinition& skill, - std::optional source, std::optional target, - eve::SimulationTick requestedTick) { +eve::Result SkillActionAdapter::submit(action::ActionRuntime& runtime, + const SkillDefinition& skill, + std::optional source, + std::optional target, + eve::SimulationTick requestedTick) { auto definition = makeDefinition(skill); if (!definition) return eve::Result::failure(definition.status()); auto request = makeRequest(skill, std::move(source), std::move(target), requestedTick); diff --git a/src/modules/rpg/SkillAction.h b/src/modules/rpg/SkillAction.h index f2001260..7bb88b90 100644 --- a/src/modules/rpg/SkillAction.h +++ b/src/modules/rpg/SkillAction.h @@ -28,8 +28,7 @@ public: * @param skill Borrowed definition observed during this call only. * @return An action definition or a structured mapping failure. */ - [[nodiscard]] static eve::Result makeDefinition( - const SkillDefinition& skill); + [[nodiscard]] static eve::Result makeDefinition(const SkillDefinition& skill); /** * @brief Build a request for one skill invocation. @@ -41,8 +40,8 @@ public: */ [[nodiscard]] static eve::Result makeRequest( const SkillDefinition& skill, std::optional source = std::nullopt, - std::optional target = std::nullopt, - eve::SimulationTick requestedTick = eve::SimulationTick::zero()); + std::optional target = std::nullopt, + eve::SimulationTick requestedTick = eve::SimulationTick::zero()); /** * @brief Translate and submit one skill to an existing lifecycle owner. @@ -51,8 +50,7 @@ public: */ [[nodiscard]] static eve::Result submit( action::ActionRuntime& runtime, const SkillDefinition& skill, - std::optional source = std::nullopt, - std::optional target = std::nullopt, + std::optional source = std::nullopt, std::optional target = std::nullopt, eve::SimulationTick requestedTick = eve::SimulationTick::zero()); }; diff --git a/src/modules/rpg/SkillCondition.cpp b/src/modules/rpg/SkillCondition.cpp index 4eaca57d..68438310 100644 --- a/src/modules/rpg/SkillCondition.cpp +++ b/src/modules/rpg/SkillCondition.cpp @@ -9,11 +9,10 @@ namespace eve::rpg { namespace { -std::optional skillValue(const RPGActor* actor, const SkillDefinition& definition, - std::string_view key) { +std::optional skillValue(const RPGActor* actor, const SkillDefinition& definition, std::string_view key) { if (!actor) return std::nullopt; - auto* mutableActor = const_cast(actor); - const auto* runtime = [&]() -> const SkillRuntime* { + auto* mutableActor = const_cast(actor); + const auto* runtime = [&]() -> const SkillRuntime* { const auto it = mutableActor->skills()->known.find(definition.id); return it == mutableActor->skills()->known.end() ? nullptr : &it->second; }(); @@ -55,25 +54,22 @@ std::optional SkillConditionContext::resource(std::string_view key) return attribute(key); } -std::optional SkillConditionContext::state(std::string_view key) const { - return value(key); -} +std::optional SkillConditionContext::state(std::string_view key) const { return value(key); } std::optional SkillConditionContext::authority(std::string_view scope) const { if (!queries_.authority) return std::nullopt; return queries_.authority(scope); } -std::optional SkillConditionContext::policy(std::string_view name, - const eve::Value& arguments) const { +std::optional SkillConditionContext::policy(std::string_view name, + const eve::Value& arguments) const { if (!queries_.policy) return std::nullopt; return queries_.policy(name, arguments); } -decision::ConditionResult SkillConditionAdapter::evaluate(const RPGActor* actor, - const SkillDefinition& definition, +decision::ConditionResult SkillConditionAdapter::evaluate(const RPGActor* actor, const SkillDefinition& definition, const decision::Condition& condition, - SkillConditionQueries queries) { + SkillConditionQueries queries) { SkillConditionContext context(actor, definition, std::move(queries)); return condition.evaluate(context); } diff --git a/src/modules/rpg/SkillCondition.h b/src/modules/rpg/SkillCondition.h index e5f5a3a3..e4325a38 100644 --- a/src/modules/rpg/SkillCondition.h +++ b/src/modules/rpg/SkillCondition.h @@ -25,11 +25,11 @@ struct SkillDefinition; */ struct SkillConditionQueries { using Authority = std::function(std::string_view scope)>; - using Policy = std::function(std::string_view name, - const eve::Value& arguments)>; + using Policy = + std::function(std::string_view name, const eve::Value& arguments)>; Authority authority; - Policy policy; + Policy policy; }; /** @@ -49,8 +49,7 @@ public: * @param definition Borrowed skill definition; it must outlive the call. * @param queries Optional read-only authority and policy providers. */ - SkillConditionContext(const RPGActor* actor, const SkillDefinition& definition, - SkillConditionQueries queries = {}); + SkillConditionContext(const RPGActor* actor, const SkillDefinition& definition, SkillConditionQueries queries = {}); /** @copydoc decision::EvaluationContext::value */ [[nodiscard]] std::optional value(std::string_view key) const override; @@ -65,13 +64,13 @@ public: /** @copydoc decision::EvaluationContext::authority */ [[nodiscard]] std::optional authority(std::string_view scope) const override; /** @copydoc decision::EvaluationContext::policy */ - [[nodiscard]] std::optional policy(std::string_view name, - const eve::Value& arguments) const override; + [[nodiscard]] std::optional policy(std::string_view name, + const eve::Value& arguments) const override; private: - const RPGActor* actor_ = nullptr; + const RPGActor* actor_ = nullptr; const SkillDefinition* definition_ = nullptr; - SkillConditionQueries queries_; + SkillConditionQueries queries_; }; /** @brief Evaluates a condition using RPG Skill state without mutating it. */ @@ -85,10 +84,9 @@ public: * @param queries Optional read-only authority and policy providers. * @return A stable, UI-ready condition explanation. */ - [[nodiscard]] static decision::ConditionResult evaluate(const RPGActor* actor, - const SkillDefinition& definition, - const decision::Condition& condition, - SkillConditionQueries queries = {}); + [[nodiscard]] static decision::ConditionResult evaluate(const RPGActor* actor, const SkillDefinition& definition, + const decision::Condition& condition, + SkillConditionQueries queries = {}); }; } // namespace eve::rpg diff --git a/src/modules/rpg/SkillConditionCodec.cpp b/src/modules/rpg/SkillConditionCodec.cpp index b5833b61..d2602c70 100644 --- a/src/modules/rpg/SkillConditionCodec.cpp +++ b/src/modules/rpg/SkillConditionCodec.cpp @@ -9,9 +9,8 @@ namespace { template eve::Result invalid(std::string message, std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, std::move(message), std::move(path), {}, - "rpg.skill.condition_codec")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, std::move(message), + std::move(path), {}, "rpg.skill.condition_codec")); } const eve::Value* field(const eve::Value::Object& object, std::string_view name) { @@ -30,7 +29,7 @@ bool exactFields(const eve::Value::Object& object, std::initializer_list requiredString(const eve::Value::Object& object, std::string_view name) { const auto* value = field(object, name); - const auto* text = value == nullptr ? nullptr : value->getIf(); + const auto* text = value == nullptr ? nullptr : value->getIf(); if (text == nullptr || text->empty()) return invalid("condition field must be a non-empty string", std::string(name)); return eve::Result::success(*text); @@ -39,12 +38,18 @@ eve::Result requiredString(const eve::Value::Object& object, std::s eve::Result parseKind(const eve::Value& value) { const auto* text = value.getIf(); if (text == nullptr) return invalid("condition kind must be a string", "kind"); - using K = eve::decision::ConditionKind; + using K = eve::decision::ConditionKind; static const std::pair names[] = { - {"all", K::All}, {"any", K::Any}, {"not", K::Not}, {"compare", K::Compare}, - {"has_tag", K::HasTag}, {"has_attribute", K::HasAttribute}, - {"has_resource", K::HasResource}, {"state_equals", K::StateEquals}, - {"authority_check", K::AuthorityCheck}, {"policy_call", K::PolicyCall}, + {"all", K::All}, + {"any", K::Any}, + {"not", K::Not}, + {"compare", K::Compare}, + {"has_tag", K::HasTag}, + {"has_attribute", K::HasAttribute}, + {"has_resource", K::HasResource}, + {"state_equals", K::StateEquals}, + {"authority_check", K::AuthorityCheck}, + {"policy_call", K::PolicyCall}, }; for (const auto& [name, kind] : names) if (*text == name) return eve::Result::success(kind); @@ -53,11 +58,12 @@ eve::Result parseKind(const eve::Value& value) { eve::Result parseOperator(const eve::Value& value) { const auto* text = value.getIf(); - if (text == nullptr) return invalid("condition operator must be a string", "operator"); - using O = eve::decision::CompareOperator; + if (text == nullptr) + return invalid("condition operator must be a string", "operator"); + using O = eve::decision::CompareOperator; static const std::pair names[] = { - {"eq", O::Equal}, {"ne", O::NotEqual}, {"lt", O::Less}, - {"le", O::LessEqual}, {"gt", O::Greater}, {"ge", O::GreaterEqual}, + {"eq", O::Equal}, {"ne", O::NotEqual}, {"lt", O::Less}, + {"le", O::LessEqual}, {"gt", O::Greater}, {"ge", O::GreaterEqual}, }; for (const auto& [name, op] : names) if (*text == name) return eve::Result::success(op); @@ -66,10 +72,13 @@ eve::Result parseOperator(const eve::Value& valu eve::Result parseDeterminism(const eve::Value& value) { const auto* text = value.getIf(); - if (text == nullptr) return invalid("determinism must be a string", "scriptDeclaration.determinism"); - using D = eve::decision::DeterminismLevel; + if (text == nullptr) + return invalid("determinism must be a string", + "scriptDeclaration.determinism"); + using D = eve::decision::DeterminismLevel; static const std::pair names[] = { - {"bit_exact", D::BitExact}, {"tick_deterministic", D::TickDeterministic}, + {"bit_exact", D::BitExact}, + {"tick_deterministic", D::TickDeterministic}, {"tolerance_bounded", D::ToleranceBounded}, {"explicitly_nondeterministic", D::ExplicitlyNondeterministic}, }; @@ -83,7 +92,8 @@ eve::Result decodeNode(const eve::Value& value); eve::Result> decodeChildren(const eve::Value::Object& object) { const auto* value = field(object, "children"); const auto* array = value == nullptr ? nullptr : value->getIf(); - if (array == nullptr) return invalid>("condition children must be an array", "children"); + if (array == nullptr) + return invalid>("condition children must be an array", "children"); std::vector result; result.reserve(array->size()); for (const auto& child : *array) { @@ -102,36 +112,47 @@ eve::Result decodeNode(const eve::Value& value) { auto kind = parseKind(*kindValue); if (!kind) return eve::Result::failure(kind.status()); const auto parsedKind = kind.value(); - using K = eve::decision::ConditionKind; + using K = eve::decision::ConditionKind; if (parsedKind == K::All || parsedKind == K::Any || parsedKind == K::Not) { - if (!exactFields(*object, {"kind", "children"})) return invalid("logical condition contains an unknown field"); + if (!exactFields(*object, {"kind", "children"})) + return invalid("logical condition contains an unknown field"); auto children = decodeChildren(*object); if (!children) return eve::Result::failure(children.status()); auto values = std::move(children).takeValue(); - if (parsedKind == K::Not && values.size() != 1) return invalid("not condition requires one child", "children"); - if (parsedKind == K::All) return eve::Result::success(eve::decision::Condition::all(std::move(values))); - if (parsedKind == K::Any) return eve::Result::success(eve::decision::Condition::any(std::move(values))); - return eve::Result::success(eve::decision::Condition::not_(std::move(values.front()))); + if (parsedKind == K::Not && values.size() != 1) + return invalid("not condition requires one child", "children"); + if (parsedKind == K::All) + return eve::Result::success(eve::decision::Condition::all(std::move(values))); + if (parsedKind == K::Any) + return eve::Result::success(eve::decision::Condition::any(std::move(values))); + return eve::Result::success( + eve::decision::Condition::not_(std::move(values.front()))); } if (parsedKind == K::Compare) { - if (!exactFields(*object, {"kind", "key", "operator", "expected"})) return invalid("compare condition contains an unknown field"); - auto key = requiredString(*object, "key"); - const auto* op = field(*object, "operator"); + if (!exactFields(*object, {"kind", "key", "operator", "expected"})) + return invalid("compare condition contains an unknown field"); + auto key = requiredString(*object, "key"); + const auto* op = field(*object, "operator"); const auto* expected = field(*object, "expected"); - if (!key || op == nullptr || expected == nullptr) return invalid("compare condition is incomplete"); + if (!key || op == nullptr || expected == nullptr) + return invalid("compare condition is incomplete"); auto parsedOp = parseOperator(*op); if (!parsedOp) return eve::Result::failure(parsedOp.status()); - return eve::Result::success(eve::decision::Condition::compare(std::move(key).takeValue(), parsedOp.value(), *expected)); + return eve::Result::success( + eve::decision::Condition::compare(std::move(key).takeValue(), parsedOp.value(), *expected)); } if (parsedKind == K::StateEquals) { - if (!exactFields(*object, {"kind", "key", "expected"})) return invalid("state condition contains an unknown field"); - auto key = requiredString(*object, "key"); + if (!exactFields(*object, {"kind", "key", "expected"})) + return invalid("state condition contains an unknown field"); + auto key = requiredString(*object, "key"); const auto* expected = field(*object, "expected"); if (!key || expected == nullptr) return invalid("state condition is incomplete"); - return eve::Result::success(eve::decision::Condition::stateEquals(std::move(key).takeValue(), *expected)); + return eve::Result::success( + eve::decision::Condition::stateEquals(std::move(key).takeValue(), *expected)); } if (parsedKind == K::PolicyCall) { - if (!exactFields(*object, {"kind", "key", "arguments", "scriptDeclaration"})) return invalid("policy condition contains an unknown field"); + if (!exactFields(*object, {"kind", "key", "arguments", "scriptDeclaration"})) + return invalid("policy condition contains an unknown field"); auto key = requiredString(*object, "key"); if (!key) return eve::Result::failure(key.status()); eve::Value arguments = eve::Value::Object{}; @@ -139,33 +160,49 @@ eve::Result decodeNode(const eve::Value& value) { std::optional declaration; if (const auto* value = field(*object, "scriptDeclaration")) { const auto* declarationObject = value->getIf(); - if (declarationObject == nullptr || !exactFields(*declarationObject, {"name", "dependencies", "determinism"})) return invalid("script declaration is invalid", "scriptDeclaration"); - auto name = requiredString(*declarationObject, "name"); + if (declarationObject == nullptr || + !exactFields(*declarationObject, {"name", "dependencies", "determinism"})) + return invalid("script declaration is invalid", "scriptDeclaration"); + auto name = requiredString(*declarationObject, "name"); const auto* dependencyValue = field(*declarationObject, "dependencies"); - const auto* dependencies = dependencyValue == nullptr ? nullptr : dependencyValue->getIf(); + const auto* dependencies = + dependencyValue == nullptr ? nullptr : dependencyValue->getIf(); const auto* determinism = field(*declarationObject, "determinism"); - if (!name || dependencies == nullptr || determinism == nullptr) return invalid("script declaration is incomplete", "scriptDeclaration"); + if (!name || dependencies == nullptr || determinism == nullptr) + return invalid("script declaration is incomplete", "scriptDeclaration"); auto level = parseDeterminism(*determinism); if (!level) return eve::Result::failure(level.status()); eve::decision::ScriptConditionDeclaration parsed{std::move(name).takeValue(), {}, level.value()}; for (const auto& dependency : *dependencies) { const auto* text = dependency.getIf(); - if (text == nullptr || text->empty()) return invalid("script dependency is invalid", "scriptDeclaration.dependencies"); + if (text == nullptr || text->empty()) + return invalid("script dependency is invalid", + "scriptDeclaration.dependencies"); parsed.dependencies.push_back(*text); } declaration = std::move(parsed); } - return eve::Result::success(eve::decision::Condition::policyCall(std::move(key).takeValue(), std::move(arguments), std::move(declaration))); + return eve::Result::success(eve::decision::Condition::policyCall( + std::move(key).takeValue(), std::move(arguments), std::move(declaration))); } - if (!exactFields(*object, {"kind", "key"})) return invalid("leaf condition contains an unknown field"); + if (!exactFields(*object, {"kind", "key"})) + return invalid("leaf condition contains an unknown field"); auto key = requiredString(*object, "key"); if (!key) return eve::Result::failure(key.status()); switch (parsedKind) { - case K::HasTag: return eve::Result::success(eve::decision::Condition::hasTag(std::move(key).takeValue())); - case K::HasAttribute: return eve::Result::success(eve::decision::Condition::hasAttribute(std::move(key).takeValue())); - case K::HasResource: return eve::Result::success(eve::decision::Condition::hasResource(std::move(key).takeValue())); - case K::AuthorityCheck: return eve::Result::success(eve::decision::Condition::authorityCheck(std::move(key).takeValue())); - default: return invalid("unsupported condition node kind"); + case K::HasTag: + return eve::Result::success( + eve::decision::Condition::hasTag(std::move(key).takeValue())); + case K::HasAttribute: + return eve::Result::success( + eve::decision::Condition::hasAttribute(std::move(key).takeValue())); + case K::HasResource: + return eve::Result::success( + eve::decision::Condition::hasResource(std::move(key).takeValue())); + case K::AuthorityCheck: + return eve::Result::success( + eve::decision::Condition::authorityCheck(std::move(key).takeValue())); + default: return invalid("unsupported condition node kind"); } } @@ -195,27 +232,28 @@ eve::Result encodeSkillCondition(const eve::decision::Condition& con if (condition.kind() == K::PolicyCall) { object.emplace("arguments", condition.arguments()); if (condition.scriptDeclaration()) { - const auto& declaration = *condition.scriptDeclaration(); + const auto& declaration = *condition.scriptDeclaration(); eve::Value::Array dependencies; for (const auto& dependency : declaration.dependencies) dependencies.emplace_back(dependency); std::string determinism; switch (declaration.determinism) { - case eve::decision::DeterminismLevel::BitExact: determinism = "bit_exact"; break; - case eve::decision::DeterminismLevel::TickDeterministic: determinism = "tick_deterministic"; break; - case eve::decision::DeterminismLevel::ToleranceBounded: determinism = "tolerance_bounded"; break; - case eve::decision::DeterminismLevel::ExplicitlyNondeterministic: determinism = "explicitly_nondeterministic"; break; + case eve::decision::DeterminismLevel::BitExact: determinism = "bit_exact"; break; + case eve::decision::DeterminismLevel::TickDeterministic: determinism = "tick_deterministic"; break; + case eve::decision::DeterminismLevel::ToleranceBounded: determinism = "tolerance_bounded"; break; + case eve::decision::DeterminismLevel::ExplicitlyNondeterministic: + determinism = "explicitly_nondeterministic"; + break; } - object.emplace("scriptDeclaration", eve::Value(eve::Value::Object{ - {"name", eve::Value(declaration.name)}, {"dependencies", eve::Value(std::move(dependencies))}, - {"determinism", eve::Value(std::move(determinism))}})); + object.emplace("scriptDeclaration", + eve::Value(eve::Value::Object{{"name", eve::Value(declaration.name)}, + {"dependencies", eve::Value(std::move(dependencies))}, + {"determinism", eve::Value(std::move(determinism))}})); } } } return eve::Result::success(eve::Value(std::move(object))); } -eve::Result decodeSkillCondition(const eve::Value& value) { - return decodeNode(value); -} +eve::Result decodeSkillCondition(const eve::Value& value) { return decodeNode(value); } } // namespace eve::rpg diff --git a/src/modules/rpg/SkillConditionCodec.h b/src/modules/rpg/SkillConditionCodec.h index becbd365..79c3ec0c 100644 --- a/src/modules/rpg/SkillConditionCodec.h +++ b/src/modules/rpg/SkillConditionCodec.h @@ -16,15 +16,13 @@ namespace eve::rpg { * @param condition Immutable condition tree to encode. * @return Node-shaped Value, or InvalidArgument when the tree is invalid. */ -[[nodiscard]] eve::Result encodeSkillCondition( - const eve::decision::Condition& condition); +[[nodiscard]] eve::Result encodeSkillCondition(const eve::decision::Condition& condition); /** * @brief Decode one Skill condition tree from an owning Value. * @param value Node-shaped Value from a canonical Skill definition. * @return Validated condition tree, or a structured parse failure. */ -[[nodiscard]] eve::Result decodeSkillCondition( - const eve::Value& value); +[[nodiscard]] eve::Result decodeSkillCondition(const eve::Value& value); } // namespace eve::rpg diff --git a/src/modules/rpg/SkillDefinitionRuntime.cpp b/src/modules/rpg/SkillDefinitionRuntime.cpp index 3f577759..1c0407d4 100644 --- a/src/modules/rpg/SkillDefinitionRuntime.cpp +++ b/src/modules/rpg/SkillDefinitionRuntime.cpp @@ -17,9 +17,8 @@ namespace { template eve::Result invalid(std::string message, std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, std::move(message), std::move(path), {}, - "rpg.skill.definition_runtime")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, std::move(message), + std::move(path), {}, "rpg.skill.definition_runtime")); } eve::LogicalId skillSchema() { @@ -31,11 +30,10 @@ eve::LogicalId skillSchema() { return value; } -eve::Result currentHandle( - eve::definitions::DefinitionRegistry& registry, const eve::DefinitionRef& reference) { +eve::Result currentHandle(eve::definitions::DefinitionRegistry& registry, + const eve::DefinitionRef& reference) { const auto& logical = reference.id(); - return registry.handle(std::string(logical.namespaceName()), - std::string(logical.name())); + return registry.handle(std::string(logical.namespaceName()), std::string(logical.name())); } const eve::Value* field(const eve::Value::Object& object, std::string_view name) { @@ -56,13 +54,11 @@ bool readNumber(const eve::Value::Object& object, std::string_view name, double& return false; } -eve::Result> readStrings(const eve::Value::Object& object, - std::string_view name) { +eve::Result> readStrings(const eve::Value::Object& object, std::string_view name) { const auto* value = field(object, name); if (value == nullptr) return eve::Result>::success({}); const auto* array = value->getIf(); - if (array == nullptr) - return invalid>("skill field must be an array", std::string(name)); + if (array == nullptr) return invalid>("skill field must be an array", std::string(name)); std::vector result; result.reserve(array->size()); for (std::size_t index = 0; index < array->size(); ++index) { @@ -79,11 +75,10 @@ eve::Result parseDefinition(const eve::definitions::Definition& auto owned = eve::Value::fromJson(source.json); if (!owned) return eve::Result::failure(owned.status()); const auto* payload = owned.value().getIf(); - if (payload == nullptr) - return invalid("skill definition payload must be an object", "json"); + if (payload == nullptr) return invalid("skill definition payload must be an object", "json"); SkillDefinition result; - result.id = source.id; + result.id = source.id; const auto* target = field(*payload, "targetType"); if (target != nullptr) { const auto* text = target->getIf(); @@ -105,7 +100,7 @@ eve::Result parseDefinition(const eve::definitions::Definition& auto effects = readStrings(*payload, "grantedEffects"); if (!effects) return eve::Result::failure(effects.status()); result.grantedEffects = std::move(effects).takeValue(); - auto tags = readStrings(*payload, "tags"); + auto tags = readStrings(*payload, "tags"); if (!tags) return eve::Result::failure(tags.status()); result.tags = std::move(tags).takeValue(); if (const auto* condition = field(*payload, "castCondition")) { @@ -116,32 +111,29 @@ eve::Result parseDefinition(const eve::definitions::Definition& auto extras = field(*payload, "extra"); if (extras != nullptr) { const auto* extraObject = extras->getIf(); - if (extraObject == nullptr) - return invalid("skill extra must be an object", "extra"); + if (extraObject == nullptr) return invalid("skill extra must be an object", "extra"); for (const auto& [key, value] : *extraObject) { const auto* text = value.getIf(); - if (text == nullptr) - return invalid("skill extra values must be strings", "extra." + key); + if (text == nullptr) return invalid("skill extra values must be strings", "extra." + key); result.extra.emplace(key, *text); } } const auto* costs = field(*payload, "costs"); if (costs != nullptr) { const auto* array = costs->getIf(); - if (array == nullptr) - return invalid("skill costs must be an array", "costs"); + if (array == nullptr) return invalid("skill costs must be an array", "costs"); std::vector items; for (std::size_t index = 0; index < array->size(); ++index) { const auto* item = (*array)[index].getIf(); if (item == nullptr) return invalid("skill cost must be an object", "costs[" + std::to_string(index) + "]"); const auto* resource = field(*item, "attribute"); - const auto* amount = field(*item, "amount"); - const auto* name = resource ? resource->getIf() : nullptr; - const auto* integer = amount ? amount->getIf() : nullptr; + const auto* amount = field(*item, "amount"); + const auto* name = resource ? resource->getIf() : nullptr; + const auto* integer = amount ? amount->getIf() : nullptr; if (name == nullptr || name->empty() || integer == nullptr || *integer <= 0) return invalid("skill cost requires a positive attribute amount", - "costs[" + std::to_string(index) + "]"); + "costs[" + std::to_string(index) + "]"); auto cost = eve::resource::ResourceCost::create(*name, *integer); if (!cost) return eve::Result::failure(cost.status()); items.push_back(std::move(cost).takeValue()); @@ -168,27 +160,27 @@ eve::Result decodeState(const eve::Value& value) { static const std::set fields = {"cooldownRemaining", "learned"}; for (const auto& [name, unused] : *object) { (void)unused; - if (!fields.contains(name)) return invalid("unknown skill runtime state field", "state." + name); + if (!fields.contains(name)) + return invalid("unknown skill runtime state field", "state." + name); } if (!object->contains("cooldownRemaining") || !object->contains("learned")) return invalid("skill runtime state is missing a required field", "state"); const auto* cooldown = object->at("cooldownRemaining").getIf(); - const auto* learned = object->at("learned").getIf(); + const auto* learned = object->at("learned").getIf(); if (cooldown == nullptr || learned == nullptr || !std::isfinite(*cooldown) || *cooldown < 0.0 || *cooldown > std::numeric_limits::max()) return invalid("skill cooldown state is invalid", "state.cooldownRemaining"); - return eve::Result::success( - SkillRuntimeState{static_cast(*cooldown), *learned}); + return eve::Result::success(SkillRuntimeState{static_cast(*cooldown), *learned}); } } // namespace -eve::Result SkillDefinitionRuntime::create( - eve::definitions::DefinitionRegistry& registry, eve::DefinitionRef definition, - eve::PersistentId instanceId, eve::definition::ReloadPolicy policy) { +eve::Result SkillDefinitionRuntime::create(eve::definitions::DefinitionRegistry& registry, + eve::DefinitionRef definition, + eve::PersistentId instanceId, + eve::definition::ReloadPolicy policy) { if (!definition.id().isValid() || definition.id().namespaceName() != "rpg.skill") - return invalid("skill definition reference must use rpg.skill namespace", - "definition"); + return invalid("skill definition reference must use rpg.skill namespace", "definition"); auto handle = currentHandle(registry, definition); if (!handle) return eve::Result::failure(handle.status()); auto resolved = registry.resolveHandle(handle.value()); @@ -202,16 +194,14 @@ eve::Result SkillDefinitionRuntime::create( SkillDefinitionRuntime(registry, std::move(runtime).takeValue(), policy)); } -eve::Result SkillDefinitionRuntime::create( - eve::PersistentId instanceId, std::string_view skillId, - eve::definition::ReloadPolicy policy) { +eve::Result SkillDefinitionRuntime::create(eve::PersistentId instanceId, + std::string_view skillId, + eve::definition::ReloadPolicy policy) { auto logical = eve::LogicalId::fromParts("rpg.skill", skillId); - if (!logical) - return invalid("skill id is not a valid logical name", "skillId"); + if (!logical) return invalid("skill id is not a valid logical name", "skillId"); auto reference = eve::DefinitionRef::fromId(*logical); if (!reference) return eve::Result::failure(reference.status()); - return create(eve::rpg::SkillRegistry::definitionRegistry(), - std::move(reference).takeValue(), instanceId, policy); + return create(eve::rpg::SkillRegistry::definitionRegistry(), std::move(reference).takeValue(), instanceId, policy); } const eve::definition::InstanceIdentity& SkillDefinitionRuntime::identity() const noexcept { @@ -227,8 +217,7 @@ eve::definition::DefinitionHandle SkillDefinitionRuntime::definitionHandle() con } eve::Result SkillDefinitionRuntime::definition() const { - if (registry_ == nullptr) - return invalid("skill definition registry is not bound", "registry"); + if (registry_ == nullptr) return invalid("skill definition registry is not bound", "registry"); auto resolved = registry_->resolveHandle(definitionHandle()); if (!resolved) return eve::Result::failure(resolved.status()); return parseDefinition(resolved.value().get()); @@ -240,10 +229,10 @@ bool SkillDefinitionRuntime::isActive() const noexcept { return runtime_.isActiv eve::Result SkillDefinitionRuntime::applyTo(RPGActor* actor) const { if (actor == nullptr) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "skill runtime cannot project to a null RPGActor", - "actor", {}, "rpg.skill.definition_runtime")); - auto candidate = *actor->skills().operator->(); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "skill runtime cannot project to a null RPGActor", + "actor", {}, "rpg.skill.definition_runtime")); + auto candidate = *actor->skills().operator->(); const std::string id(identity().definition.id().name()); if (state().learned) candidate.known[id] = SkillRuntime{state().cooldownRemaining}; @@ -254,8 +243,7 @@ eve::Result SkillDefinitionRuntime::applyTo(RPGActor* actor) const { return eve::Result::success(); } -eve::Result SkillDefinitionRuntime::reload( - eve::definition::ReloadPolicy policy) { +eve::Result SkillDefinitionRuntime::reload(eve::definition::ReloadPolicy policy) { if (registry_ == nullptr) return invalid("skill definition registry is not bound", "registry"); auto next = currentHandle(*registry_, identity().definition); @@ -264,10 +252,9 @@ eve::Result SkillDefinitionRuntime::reload( if (!resolved) return eve::Result::failure(resolved.status()); auto typed = parseDefinition(resolved.value().get()); if (!typed) return eve::Result::failure(typed.status()); - const float nextCooldown = typed.value().cooldown; + const float nextCooldown = typed.value().cooldown; SkillRuntimeState defaults{}; - auto rebuild = [nextCooldown](const SkillRuntimeState& oldState, - const eve::definition::InstanceIdentity&, + auto rebuild = [nextCooldown](const SkillRuntimeState& oldState, const eve::definition::InstanceIdentity&, const eve::definition::DefinitionHandle&) { SkillRuntimeState rebuilt = oldState; rebuilt.cooldownRemaining = std::clamp(oldState.cooldownRemaining, 0.f, nextCooldown); @@ -279,41 +266,34 @@ eve::Result SkillDefinitionRuntime::reload( } eve::Result SkillDefinitionRuntime::snapshot( - eve::Revision revision, eve::SimulationTick tick, - const eve::SnapshotHashProvider& hashProvider) const { - const eve::definition::RuntimeStateEncoder encoder = - [](const SkillRuntimeState& value) { - return eve::Result::success(encodeState(value)); - }; - return eve::definition::snapshotRuntimeInstance( - runtime_, "rpg.skill.runtime", skillSchema(), eve::SchemaVersion(1), revision, tick, - hashProvider, encoder); + eve::Revision revision, eve::SimulationTick tick, const eve::SnapshotHashProvider& hashProvider) const { + const eve::definition::RuntimeStateEncoder encoder = [](const SkillRuntimeState& value) { + return eve::Result::success(encodeState(value)); + }; + return eve::definition::snapshotRuntimeInstance(runtime_, "rpg.skill.runtime", skillSchema(), eve::SchemaVersion(1), + revision, tick, hashProvider, encoder); } -eve::Result SkillDefinitionRuntime::snapshotJson( - eve::Revision revision, eve::SimulationTick tick, - const eve::SnapshotHashProvider& hashProvider) const { +eve::Result SkillDefinitionRuntime::snapshotJson(eve::Revision revision, eve::SimulationTick tick, + const eve::SnapshotHashProvider& hashProvider) const { auto result = snapshot(revision, tick, hashProvider); if (!result) return eve::Result::failure(result.status()); return std::move(result).andThen( [](eve::SnapshotEnvelope&& value) { return eve::serializeSnapshotEnvelope(value); }); } -eve::Result SkillDefinitionRuntime::restore( - const eve::SnapshotEnvelope& snapshotValue, const eve::SnapshotHashProvider& hashProvider) { - if (registry_ == nullptr) - return invalid("skill definition registry is not bound", "registry"); +eve::Result SkillDefinitionRuntime::restore(const eve::SnapshotEnvelope& snapshotValue, + const eve::SnapshotHashProvider& hashProvider) { + if (registry_ == nullptr) return invalid("skill definition registry is not bound", "registry"); auto current = currentHandle(*registry_, identity().definition); if (!current) return eve::Result::failure(current.status()); if (current.value() != definitionHandle()) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::StaleHandle, - "skill snapshot cannot restore against a replaced definition", "definitionGeneration", {}, - "rpg.skill.definition_runtime")); + eve::DiagnosticCode::StaleHandle, "skill snapshot cannot restore against a replaced definition", + "definitionGeneration", {}, "rpg.skill.definition_runtime")); const eve::definition::RuntimeStateDecoder decoder = decodeState; - return eve::definition::restoreRuntimeInstance( - runtime_, snapshotValue, "rpg.skill.runtime", skillSchema(), eve::SchemaVersion(1), - hashProvider, decoder); + return eve::definition::restoreRuntimeInstance(runtime_, snapshotValue, "rpg.skill.runtime", skillSchema(), + eve::SchemaVersion(1), hashProvider, decoder); } } // namespace eve::rpg diff --git a/src/modules/rpg/SkillDefinitionRuntime.h b/src/modules/rpg/SkillDefinitionRuntime.h index 239693dc..20d1e3c7 100644 --- a/src/modules/rpg/SkillDefinitionRuntime.h +++ b/src/modules/rpg/SkillDefinitionRuntime.h @@ -25,7 +25,7 @@ class RPGActor; /** @brief Mutable state belonging to one learned skill instance. */ struct SkillRuntimeState { float cooldownRemaining = 0.f; - bool learned = true; + bool learned = true; }; /** @@ -48,8 +48,7 @@ public: * @return A typed adapter or a structured resolution/validation failure. */ [[nodiscard]] static eve::Result create( - eve::definitions::DefinitionRegistry& registry, eve::DefinitionRef definition, - eve::PersistentId instanceId, + eve::definitions::DefinitionRegistry& registry, eve::DefinitionRef definition, eve::PersistentId instanceId, eve::definition::ReloadPolicy policy = eve::definition::ReloadPolicy::RebuildInstance); /** @brief Create from the canonical legacy SkillRegistry facade. */ @@ -57,11 +56,11 @@ public: eve::PersistentId instanceId, std::string_view skillId, eve::definition::ReloadPolicy policy = eve::definition::ReloadPolicy::RebuildInstance); - SkillDefinitionRuntime(SkillDefinitionRuntime&&) noexcept = default; + SkillDefinitionRuntime(SkillDefinitionRuntime&&) noexcept = default; SkillDefinitionRuntime& operator=(SkillDefinitionRuntime&&) noexcept = default; - SkillDefinitionRuntime(const SkillDefinitionRuntime&) = delete; - SkillDefinitionRuntime& operator=(const SkillDefinitionRuntime&) = delete; - ~SkillDefinitionRuntime() = default; + SkillDefinitionRuntime(const SkillDefinitionRuntime&) = delete; + SkillDefinitionRuntime& operator=(const SkillDefinitionRuntime&) = delete; + ~SkillDefinitionRuntime() = default; /** @brief Borrow the common identity, including exact definition generation. */ [[nodiscard]] const eve::definition::InstanceIdentity& identity() const noexcept; @@ -89,8 +88,7 @@ public: [[nodiscard]] eve::Result applyTo(RPGActor* actor) const; /** @brief Reload the current registry generation using a typed policy. */ - [[nodiscard]] eve::Result reload( - eve::definition::ReloadPolicy policy); + [[nodiscard]] eve::Result reload(eve::definition::ReloadPolicy policy); /** * @brief Capture this skill's instance state in a common SnapshotEnvelope. @@ -99,32 +97,30 @@ public: * @param hashProvider Explicit content digest provider. * @return A sealed snapshot or a structured failure. */ - [[nodiscard]] eve::Result snapshot( - eve::Revision revision, eve::SimulationTick tick, - const eve::SnapshotHashProvider& hashProvider) const; + [[nodiscard]] eve::Result snapshot(eve::Revision revision, eve::SimulationTick tick, + const eve::SnapshotHashProvider& hashProvider) const; /** @brief Serialize snapshot() as canonical JSON. */ - [[nodiscard]] eve::Result snapshotJson( - eve::Revision revision, eve::SimulationTick tick, - const eve::SnapshotHashProvider& hashProvider) const; + [[nodiscard]] eve::Result snapshotJson(eve::Revision revision, eve::SimulationTick tick, + const eve::SnapshotHashProvider& hashProvider) const; /** * @brief Verify and restore a same-generation skill snapshot atomically. * @return Success, or schema/hash/definition/stale/decode failure; state * remains unchanged on every failure. */ - [[nodiscard]] eve::Result restore( - const eve::SnapshotEnvelope& snapshot, const eve::SnapshotHashProvider& hashProvider); + [[nodiscard]] eve::Result restore(const eve::SnapshotEnvelope& snapshot, + const eve::SnapshotHashProvider& hashProvider); private: - SkillDefinitionRuntime(eve::definitions::DefinitionRegistry& registry, + SkillDefinitionRuntime(eve::definitions::DefinitionRegistry& registry, eve::definition::RuntimeInstance runtime, - eve::definition::ReloadPolicy policy) + eve::definition::ReloadPolicy policy) : registry_(®istry), runtime_(std::move(runtime)), policy_(policy) {} - eve::definitions::DefinitionRegistry* registry_ = nullptr; // borrowed + eve::definitions::DefinitionRegistry* registry_ = nullptr; // borrowed eve::definition::RuntimeInstance runtime_; - eve::definition::ReloadPolicy policy_ = eve::definition::ReloadPolicy::RebuildInstance; + eve::definition::ReloadPolicy policy_ = eve::definition::ReloadPolicy::RebuildInstance; }; } // namespace eve::rpg diff --git a/src/modules/rpg/SkillSystem.cpp b/src/modules/rpg/SkillSystem.cpp index e2cf55f3..1ffa8114 100644 --- a/src/modules/rpg/SkillSystem.cpp +++ b/src/modules/rpg/SkillSystem.cpp @@ -1,8 +1,8 @@ #include "rpg/SkillSystem.h" -#include "rpg/SkillCondition.h" +#include "rpg/AttributeSystem.h" #include "rpg/RPGActor.h" #include "rpg/Skill.h" -#include "rpg/AttributeSystem.h" +#include "rpg/SkillCondition.h" #include "rpg/StatusSystem.h" #include @@ -24,8 +24,7 @@ std::vector &castQueue() { bool checkCostsAffordable(RPGActor *actor, const SkillDefinition &def) { if (!def.cost) return true; for (const auto &cost : def.cost->items()) - if (AttributeSystem::getFinal(actor, cost.resource.value()) < - static_cast(cost.amount.value())) + if (AttributeSystem::getFinal(actor, cost.resource.value()) < static_cast(cost.amount.value())) return false; return true; } @@ -33,8 +32,7 @@ bool checkCostsAffordable(RPGActor *actor, const SkillDefinition &def) { void deductCosts(RPGActor *actor, const SkillDefinition &def) { if (!def.cost) return; for (const auto &cost : def.cost->items()) - AttributeSystem::modifyBase(actor, cost.resource.value(), - -static_cast(cost.amount.value())); + AttributeSystem::modifyBase(actor, cost.resource.value(), -static_cast(cost.amount.value())); } void resolveCast(RPGActor *actor, const SkillDefinition &def, RPGActor *target) { diff --git a/src/modules/rpg/StatusSystem.cpp b/src/modules/rpg/StatusSystem.cpp index 6d66d27f..ff9816cf 100644 --- a/src/modules/rpg/StatusSystem.cpp +++ b/src/modules/rpg/StatusSystem.cpp @@ -573,7 +573,7 @@ eve::Result StatusSystem::apply(RPGActor *actor, const std::string &effectI eve::Result StatusSystem::remove(RPGActor *actor, int instanceId) { if (!actor) return rejected(eve::DiagnosticCode::InvalidArgument, "status removal requires an actor"); auto statuses = actor->statuses(); - auto mapping = statuses->effectByLegacyId.find(instanceId); + auto mapping = statuses->effectByLegacyId.find(instanceId); if (mapping == statuses->effectByLegacyId.end()) return rejected(eve::DiagnosticCode::NotFound, "status legacy instance id was not found", eve::StatusCode::NotFound); @@ -627,8 +627,8 @@ int removeMatching(RPGActor *actor, const std::vector &canonicalIds int removed = 0; for (const auto &canonicalId : canonicalIds) { if (!actor) continue; - auto statuses = actor->statuses(); - const auto mapping = statuses->legacyIdByEffect.find(canonicalId); + auto statuses = actor->statuses(); + const auto mapping = statuses->legacyIdByEffect.find(canonicalId); if (mapping == statuses->legacyIdByEffect.end()) continue; auto didRemove = StatusSystem::remove(actor, mapping->second); if (didRemove.ok()) ++removed; diff --git a/src/modules/rts/RTS.cpp b/src/modules/rts/RTS.cpp index 62cd2477..cedf1281 100644 --- a/src/modules/rts/RTS.cpp +++ b/src/modules/rts/RTS.cpp @@ -33,16 +33,15 @@ void destroyHandles(std::vector& handles) { template std::size_t countLive(const std::vector& handles) { - return static_cast(std::count_if( - handles.begin(), handles.end(), [](const ecs::EntityHandle& handle) { - return dynamic_cast(ecs::try_get(handle)) != nullptr; - })); + return static_cast(std::count_if(handles.begin(), handles.end(), [](const ecs::EntityHandle& handle) { + return dynamic_cast(ecs::try_get(handle)) != nullptr; + })); } template -bool owns(const std::vector &handles, const T &entity) { - const auto live = ecs::handle_of(const_cast(&entity)); - return std::any_of(handles.begin(), handles.end(), [&live](const auto &handle) { +bool owns(const std::vector& handles, const T& entity) { + const auto live = ecs::handle_of(const_cast(&entity)); + return std::any_of(handles.begin(), handles.end(), [&live](const auto& handle) { return handle.table == live.table && handle.type == live.type && handle.id == live.id && handle.generation == live.generation; }); @@ -61,11 +60,9 @@ RTS::~RTS() { Result RTS::newUnit(SubjectRef subject, LogicalId definition) { if (!validSubject(subject)) - return failure(DiagnosticCode::InvalidArgument, - "RTS Unit requires a valid SubjectRef", "subject"); + return failure(DiagnosticCode::InvalidArgument, "RTS Unit requires a valid SubjectRef", "subject"); Unit* unit = Unit::createUnit(subject, std::move(definition)); - if (unit == nullptr) - return failure(DiagnosticCode::Failed, "ECS failed to create an RTS Unit", "unit"); + if (unit == nullptr) return failure(DiagnosticCode::Failed, "ECS failed to create an RTS Unit", "unit"); auto attributes = RTSUnitAttributeAdapter::ensure(*unit); if (!attributes) { const auto status = attributes.status(); @@ -84,8 +81,8 @@ Result RTS::newUnit(SubjectRef subject, LogicalId definition) { Result RTS::newBuilding(SubjectRef subject, LogicalId definition) { if (!validSubject(subject)) - return failure(DiagnosticCode::InvalidArgument, - "RTS Building requires a valid SubjectRef", "subject"); + return failure(DiagnosticCode::InvalidArgument, "RTS Building requires a valid SubjectRef", + "subject"); Building* building = Building::createBuilding(subject, std::move(definition)); if (building == nullptr) return failure(DiagnosticCode::Failed, "ECS failed to create an RTS Building", "building"); @@ -101,8 +98,7 @@ Result RTS::newBuilding(SubjectRef subject, LogicalId definition) { Result RTS::newPlayer(SubjectRef subject) { if (!validSubject(subject)) - return failure(DiagnosticCode::InvalidArgument, - "RTS Player requires a valid SubjectRef", "subject"); + return failure(DiagnosticCode::InvalidArgument, "RTS Player requires a valid SubjectRef", "subject"); Player* player = Player::createPlayer(subject); if (player == nullptr) return failure(DiagnosticCode::Failed, "ECS failed to create an RTS Player", "player"); @@ -112,8 +108,7 @@ Result RTS::newPlayer(SubjectRef subject) { Result RTS::newFaction(SubjectRef subject) { if (!validSubject(subject)) - return failure(DiagnosticCode::InvalidArgument, - "RTS Faction requires a valid SubjectRef", "subject"); + return failure(DiagnosticCode::InvalidArgument, "RTS Faction requires a valid SubjectRef", "subject"); Faction* faction = Faction::createFaction(subject); if (faction == nullptr) return failure(DiagnosticCode::Failed, "ECS failed to create an RTS Faction", "faction"); @@ -121,58 +116,52 @@ Result RTS::newFaction(SubjectRef subject) { return Result::success(faction, Status::success(StatusCode::Applied)); } -Result RTS::fanOut(const Player::Selection& selection, - const CommandSpec& command, +Result RTS::fanOut(const Player::Selection& selection, const CommandSpec& command, const FormationSpec& formation) const { return CommandFanOutSystem::fanOut(selection.units, command, formation); } -Result RTS::readUnitAttribute(Unit &unit, std::string_view attribute) const { +Result RTS::readUnitAttribute(Unit& unit, std::string_view attribute) const { if (!owns(units_, unit)) - return Result::failure(Diagnostic::error( - DiagnosticCode::StaleHandle, "RTS Unit does not belong to this facade", "unit")); + return Result::failure( + Diagnostic::error(DiagnosticCode::StaleHandle, "RTS Unit does not belong to this facade", "unit")); return RTSUnitAttributeAdapter::read(unit, attribute); } -Result RTS::setUnitAttribute(Unit &unit, std::string_view attribute, double value) const { +Result RTS::setUnitAttribute(Unit& unit, std::string_view attribute, double value) const { if (!owns(units_, unit)) - return Result::failure(Diagnostic::error( - DiagnosticCode::StaleHandle, "RTS Unit does not belong to this facade", "unit")); + return Result::failure( + Diagnostic::error(DiagnosticCode::StaleHandle, "RTS Unit does not belong to this facade", "unit")); return RTSUnitAttributeAdapter::setBase(unit, attribute, value); } -Result RTS::applyEffect( - Unit &unit, const RTSEffectDefinition &definition) const { +Result RTS::applyEffect(Unit& unit, const RTSEffectDefinition& definition) const { if (!owns(units_, unit)) - return Result::failure(Diagnostic::error( - DiagnosticCode::StaleHandle, "RTS Unit does not belong to this facade", "unit")); + return Result::failure( + Diagnostic::error(DiagnosticCode::StaleHandle, "RTS Unit does not belong to this facade", "unit")); return unit.effects()->values.apply(definition); } -Result RTS::applyEffect( - Building &building, const RTSEffectDefinition &definition) const { +Result RTS::applyEffect(Building& building, const RTSEffectDefinition& definition) const { if (!owns(buildings_, building)) - return Result::failure(Diagnostic::error( - DiagnosticCode::StaleHandle, "RTS Building does not belong to this facade", "building")); + return Result::failure( + Diagnostic::error(DiagnosticCode::StaleHandle, "RTS Building does not belong to this facade", "building")); return building.effects()->values.apply(definition); } -Result RTS::build( - Building &building, action::ActionRuntime &action, - resource::IResourceAccount &account, resource::CostSpec cost, - std::string product, Duration duration, std::string productionKind, - int priority, std::string transactionId) { +Result RTS::build(Building& building, action::ActionRuntime& action, + resource::IResourceAccount& account, resource::CostSpec cost, std::string product, + Duration duration, std::string productionKind, int priority, + std::string transactionId) { if (!owns(buildings_, building)) - return Result::failure(Diagnostic::error( - DiagnosticCode::StaleHandle, "RTS Building does not belong to this facade", "building")); - return RTSProductionActionAdapter::build( - building, action, account, std::move(cost), std::move(product), - std::move(duration), std::move(productionKind), priority, - std::move(transactionId)); + return Result::failure( + Diagnostic::error(DiagnosticCode::StaleHandle, "RTS Building does not belong to this facade", "building")); + return RTSProductionActionAdapter::build(building, action, account, std::move(cost), std::move(product), + std::move(duration), std::move(productionKind), priority, + std::move(transactionId)); } -Result RTS::step(const SimulationStep& simulationStep, - IRTSActionExecutor& executor) { +Result RTS::step(const SimulationStep& simulationStep, IRTSActionExecutor& executor) { auto motion = MotionSystem::step(simulationStep); if (!motion) return failureFrom(motion.status()); std::size_t processed = std::move(motion).takeValue(); diff --git a/src/modules/rts/RTS.h b/src/modules/rts/RTS.h index 18a29665..c630610c 100644 --- a/src/modules/rts/RTS.h +++ b/src/modules/rts/RTS.h @@ -61,22 +61,18 @@ public: * @param formation Deterministic formation layout. * @return Accepted order ids in selection order. */ - [[nodiscard]] Result fanOut(const Player::Selection& selection, - const CommandSpec& command, + [[nodiscard]] Result fanOut(const Player::Selection& selection, const CommandSpec& command, const FormationSpec& formation) const; /** @brief Read a canonical selected combat attribute through the RTS facade. */ - [[nodiscard]] Result readUnitAttribute(Unit& unit, - std::string_view attribute) const; + [[nodiscard]] Result readUnitAttribute(Unit& unit, std::string_view attribute) const; /** @brief Set a canonical selected combat attribute through the RTS facade. */ - [[nodiscard]] Result setUnitAttribute(Unit& unit, std::string_view attribute, - double value) const; + [[nodiscard]] Result setUnitAttribute(Unit& unit, std::string_view attribute, double value) const; /** @brief Apply a typed effect to a module-owned Unit component. */ - [[nodiscard]] Result applyEffect( - Unit& unit, const RTSEffectDefinition& definition) const; + [[nodiscard]] Result applyEffect(Unit& unit, const RTSEffectDefinition& definition) const; /** @brief Apply a typed effect to a module-owned Building component. */ - [[nodiscard]] Result applyEffect( - Building& building, const RTSEffectDefinition& definition) const; + [[nodiscard]] Result applyEffect(Building& building, + const RTSEffectDefinition& definition) const; /** * @brief Submit a building production action through the canonical transaction facade. @@ -91,11 +87,11 @@ public: * @param transactionId Optional transaction correlation id. * @return Committed build receipt or a checked failure with no partial state. */ - [[nodiscard]] Result build( - Building& building, action::ActionRuntime& action, - resource::IResourceAccount& account, resource::CostSpec cost, - std::string product, Duration duration, std::string productionKind = "unit", - int priority = 0, std::string transactionId = {}); + [[nodiscard]] Result build(Building& building, action::ActionRuntime& action, + resource::IResourceAccount& account, resource::CostSpec cost, + std::string product, Duration duration, + std::string productionKind = "unit", int priority = 0, + std::string transactionId = {}); /** * @brief Run phase-one simulation systems in deterministic order. @@ -103,8 +99,7 @@ public: * @param executor Borrowed adapter over the shared ActionRuntime. * @return Number of processed records across the systems. */ - [[nodiscard]] Result step(const SimulationStep& step, - IRTSActionExecutor& executor); + [[nodiscard]] Result step(const SimulationStep& step, IRTSActionExecutor& executor); /** @brief Return the number of live Unit roots owned by this module. */ [[nodiscard]] std::size_t unitCount() const noexcept; diff --git a/src/modules/rts/RTSAction.cpp b/src/modules/rts/RTSAction.cpp index 7ca09ca2..77723a8d 100644 --- a/src/modules/rts/RTSAction.cpp +++ b/src/modules/rts/RTSAction.cpp @@ -40,24 +40,22 @@ std::optional actionIdFor(const OrderRecord& order) { struct ActionAdapter::Impl { struct Pending { - ecs::EntityHandle unit; - std::string orderId; + ecs::EntityHandle unit; + std::string orderId; action::ActionExecutionId execution; }; explicit Impl(action::ActionRuntime& runtimeValue) : runtime(runtimeValue) {} action::ActionRuntime& runtime; - std::vector pending; + std::vector pending; }; -ActionAdapter::ActionAdapter(action::ActionRuntime& runtime) - : impl_(std::make_unique(runtime)) {} +ActionAdapter::ActionAdapter(action::ActionRuntime& runtime) : impl_(std::make_unique(runtime)) {} ActionAdapter::~ActionAdapter() = default; -Result ActionAdapter::execute(Unit& unit, const OrderRecord& order, - const SimulationStep& step) { +Result ActionAdapter::execute(Unit& unit, const OrderRecord& order, const SimulationStep& step) { if (step.delta.nanoseconds() < 0) return failure(DiagnosticCode::InvalidArgument, "RTS action step delta must be non-negative", "step.delta"); @@ -66,10 +64,9 @@ Result ActionAdapter::execute(Unit& unit, const OrderReco "RTS action adapter is not initialized", "adapter"); const ecs::EntityHandle unitHandle = ecs::handle_of(&unit); - auto pendingForUnit = std::find_if( - impl_->pending.begin(), impl_->pending.end(), [&unitHandle](const Impl::Pending& pending) { - return sameHandle(pending.unit, unitHandle); - }); + auto pendingForUnit = + std::find_if(impl_->pending.begin(), impl_->pending.end(), + [&unitHandle](const Impl::Pending& pending) { return sameHandle(pending.unit, unitHandle); }); if (pendingForUnit != impl_->pending.end() && pendingForUnit->orderId != order.id) { const auto* oldExecution = impl_->runtime.find(pendingForUnit->execution); @@ -85,16 +82,15 @@ Result ActionAdapter::execute(Unit& unit, const OrderReco if (pendingForUnit == impl_->pending.end()) { const auto logicalId = actionIdFor(order); if (!logicalId) - return failure(DiagnosticCode::InvalidArgument, - "RTS order definition is not a valid LogicalId", - "order.definitionId"); + return failure( + DiagnosticCode::InvalidArgument, "RTS order definition is not a valid LogicalId", "order.definitionId"); action::ActionDefinition definition; definition.id = *logicalId; action::ActionRequest request; - request.actionId = definition.id; - request.source = unitHandle; + request.actionId = definition.id; + request.source = unitHandle; request.requestedTick = step.tick; auto submitted = impl_->runtime.submit(std::move(definition), std::move(request)); @@ -109,27 +105,22 @@ Result ActionAdapter::execute(Unit& unit, const OrderReco if (!advanced) { impl_->pending.erase( std::remove_if(impl_->pending.begin(), impl_->pending.end(), - [&executionId](const Impl::Pending& pending) { - return pending.execution == executionId; - }), + [&executionId](const Impl::Pending& pending) { return pending.execution == executionId; }), impl_->pending.end()); return failureFrom(advanced.status()); } - const auto summary = std::move(advanced).takeValue(); - bool completed = summary.phase == action::ActionPhase::Completed; + const auto summary = std::move(advanced).takeValue(); + bool completed = summary.phase == action::ActionPhase::Completed; if (order.kind == OrderKind::Move && !unit.motion()->arrived) completed = false; if (completed) { impl_->pending.erase( std::remove_if(impl_->pending.begin(), impl_->pending.end(), - [&executionId](const Impl::Pending& pending) { - return pending.execution == executionId; - }), + [&executionId](const Impl::Pending& pending) { return pending.execution == executionId; }), impl_->pending.end()); } - const ActionExecutionResult result{ - completed ? ActionDisposition::Completed : ActionDisposition::Pending}; + const ActionExecutionResult result{completed ? ActionDisposition::Completed : ActionDisposition::Pending}; return Result::success( result, Status::success(completed ? StatusCode::Applied : StatusCode::Pending)); } diff --git a/src/modules/rts/RTSAction.h b/src/modules/rts/RTSAction.h index e6decd29..5333b7dc 100644 --- a/src/modules/rts/RTSAction.h +++ b/src/modules/rts/RTSAction.h @@ -44,8 +44,8 @@ public: * @param step Deterministic simulation step supplied by the caller. * @return Pending or Completed, or a structured provider failure. */ - [[nodiscard]] virtual Result execute( - Unit& unit, const OrderRecord& order, const SimulationStep& step) = 0; + [[nodiscard]] virtual Result execute(Unit& unit, const OrderRecord& order, + const SimulationStep& step) = 0; }; /** @@ -61,12 +61,12 @@ public: explicit ActionAdapter(action::ActionRuntime& runtime); ~ActionAdapter() override; - ActionAdapter(const ActionAdapter&) = delete; + ActionAdapter(const ActionAdapter&) = delete; ActionAdapter& operator=(const ActionAdapter&) = delete; /** @copydoc IRTSActionExecutor::execute */ - [[nodiscard]] Result execute( - Unit& unit, const OrderRecord& order, const SimulationStep& step) override; + [[nodiscard]] Result execute(Unit& unit, const OrderRecord& order, + const SimulationStep& step) override; private: struct Impl; diff --git a/src/modules/rts/RTSAttributes.cpp b/src/modules/rts/RTSAttributes.cpp index 25e73452..4173baad 100644 --- a/src/modules/rts/RTSAttributes.cpp +++ b/src/modules/rts/RTSAttributes.cpp @@ -11,8 +11,8 @@ namespace { template eve::Result failure(eve::DiagnosticCode code, std::string message, std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - code, std::move(message), std::move(path), {}, "rts.attributes")); + return eve::Result::failure( + eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "rts.attributes")); } template @@ -22,33 +22,29 @@ eve::Result failure(eve::Status status, std::string, std::string = {}) { template eve::Result failure(eve::StatusCode code, std::string message, std::string path = {}) { - return eve::Result::failure(eve::Status::failure( - code, eve::Diagnostic::error(eve::DiagnosticCode::Failed, std::move(message), - std::move(path), {}, "rts.attributes"))); + return eve::Result::failure( + eve::Status::failure(code, eve::Diagnostic::error(eve::DiagnosticCode::Failed, std::move(message), + std::move(path), {}, "rts.attributes"))); } -eve::Result failureVoid(eve::DiagnosticCode code, std::string message, - std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - code, std::move(message), std::move(path), {}, "rts.attributes")); +eve::Result failureVoid(eve::DiagnosticCode code, std::string message, std::string path = {}) { + return eve::Result::failure( + eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "rts.attributes")); } eve::Result failureVoid(eve::Status status, std::string, std::string = {}) { return eve::Result::failure(std::move(status)); } -eve::Result failureVoid(eve::StatusCode code, std::string message, - std::string path = {}) { - return eve::Result::failure(eve::Status::failure( - code, eve::Diagnostic::error(eve::DiagnosticCode::Failed, std::move(message), - std::move(path), {}, "rts.attributes"))); +eve::Result failureVoid(eve::StatusCode code, std::string message, std::string path = {}) { + return eve::Result::failure( + eve::Status::failure(code, eve::Diagnostic::error(eve::DiagnosticCode::Failed, std::move(message), + std::move(path), {}, "rts.attributes"))); } bool selected(std::string_view name) noexcept { - return name == RTSUnitAttributeAdapter::attackAttribute || - name == RTSUnitAttributeAdapter::healthAttribute || - name == RTSUnitAttributeAdapter::maxHealthAttribute || - name == RTSUnitAttributeAdapter::armorAttribute; + return name == RTSUnitAttributeAdapter::attackAttribute || name == RTSUnitAttributeAdapter::healthAttribute || + name == RTSUnitAttributeAdapter::maxHealthAttribute || name == RTSUnitAttributeAdapter::armorAttribute; } std::array selectedAttributes() { @@ -60,8 +56,7 @@ std::array selectedAttributes() { eve::Result RTSUnitAttributeAdapter::ensure(Unit& unit) { auto bound = unit.attributes()->values.bindOwner(ecs::handle_of(&unit)); - if (!bound) - return failureVoid(bound.code(), "RTS unit attribute owner could not be bound", "owner"); + if (!bound) return failureVoid(bound.code(), "RTS unit attribute owner could not be bound", "owner"); if (!unit.attributes()->values.initialized()) { const std::array bases = { eve::attributes::AttributeSnapshotBase{std::string(attackAttribute), 0.0}, @@ -70,8 +65,7 @@ eve::Result RTSUnitAttributeAdapter::ensure(Unit& unit) { eve::attributes::AttributeSnapshotBase{std::string(armorAttribute), 0.0}}; auto initialized = unit.attributes()->values.initialize(bases); if (!initialized) - return failureVoid(initialized.code(), "RTS unit attribute state could not be initialized", - "attributes"); + return failureVoid(initialized.code(), "RTS unit attribute state could not be initialized", "attributes"); } return eve::Result::success(eve::Status::success(eve::StatusCode::NoOp)); } @@ -79,15 +73,13 @@ eve::Result RTSUnitAttributeAdapter::ensure(Unit& unit) { eve::Result RTSUnitAttributeAdapter::read(Unit& unit, std::string_view attribute) { if (!selected(attribute)) return failure(eve::DiagnosticCode::Unsupported, - "RTS unit attribute is outside the selective combat projection", - "attribute"); + "RTS unit attribute is outside the selective combat projection", "attribute"); auto ready = ensure(unit); if (!ready) return failure(ready.code(), "RTS unit attributes are not available", "attributes"); return unit.attributes()->values.getFinal(attribute); } -eve::Result RTSUnitAttributeAdapter::setBase(Unit& unit, std::string_view attribute, - double value) { +eve::Result RTSUnitAttributeAdapter::setBase(Unit& unit, std::string_view attribute, double value) { if (!selected(attribute)) return failureVoid(eve::DiagnosticCode::Unsupported, "RTS unit attribute is outside the selective combat projection", "attribute"); @@ -98,8 +90,7 @@ eve::Result RTSUnitAttributeAdapter::setBase(Unit& unit, std::string_view eve::Result RTSUnitAttributeAdapter::addModifier( Unit& unit, std::string id, std::string_view attribute, std::string source, - eve::attributes::AttributeOperation operation, double value, - eve::attributes::ModifierPriority priority) { + eve::attributes::AttributeOperation operation, double value, eve::attributes::ModifierPriority priority) { if (!selected(attribute) || source.empty()) return failure( eve::DiagnosticCode::InvalidArgument, @@ -108,13 +99,11 @@ eve::Result RTSUnitAttributeAdapter::addModifier( if (!ready) return failure(ready.code(), "RTS unit attributes are not available", "attributes"); - return unit.attributes()->values.addModifier( - eve::attributes::AttributeModifier(std::move(id), std::string(attribute), std::move(source), - operation, value, priority)); + return unit.attributes()->values.addModifier(eve::attributes::AttributeModifier( + std::move(id), std::string(attribute), std::move(source), operation, value, priority)); } -eve::Result RTSUnitAttributeAdapter::snapshot( - Unit& unit) { +eve::Result RTSUnitAttributeAdapter::snapshot(Unit& unit) { auto ready = ensure(unit); if (!ready) return failure( @@ -123,9 +112,9 @@ eve::Result RTSUnitAttributeAdapte return unit.attributes()->values.snapshot(names); } -eve::Result RTSUnitAttributeAdapter::restore( - Unit& unit, const eve::attributes::AttributeProjectionSnapshot& snapshotValue, - eve::Revision expectedRevision) { +eve::Result RTSUnitAttributeAdapter::restore(Unit& unit, + const eve::attributes::AttributeProjectionSnapshot& snapshotValue, + eve::Revision expectedRevision) { auto ready = ensure(unit); if (!ready) return failureVoid(ready.code(), "RTS unit attributes are not available", "attributes"); return unit.attributes()->values.restore(snapshotValue, expectedRevision); diff --git a/src/modules/rts/RTSAttributes.h b/src/modules/rts/RTSAttributes.h index 62671919..ea76c783 100644 --- a/src/modules/rts/RTSAttributes.h +++ b/src/modules/rts/RTSAttributes.h @@ -23,36 +23,33 @@ class Unit; */ class RTSUnitAttributeAdapter final { public: - static constexpr std::string_view attackAttribute = "attack"; - static constexpr std::string_view healthAttribute = "health"; + static constexpr std::string_view attackAttribute = "attack"; + static constexpr std::string_view healthAttribute = "health"; static constexpr std::string_view maxHealthAttribute = "max_health"; - static constexpr std::string_view armorAttribute = "armor"; + static constexpr std::string_view armorAttribute = "armor"; /** @brief Bind and seed default optional combat stats exactly once. */ [[nodiscard]] static eve::Result ensure(Unit& unit); /** @brief Read one selected combat stat. */ - [[nodiscard]] static eve::Result read(Unit& unit, - std::string_view attribute); + [[nodiscard]] static eve::Result read(Unit& unit, std::string_view attribute); /** @brief Set one selected canonical base stat. */ - [[nodiscard]] static eve::Result setBase(Unit& unit, - std::string_view attribute, double value); + [[nodiscard]] static eve::Result setBase(Unit& unit, std::string_view attribute, double value); /** @brief Add a modifier using the shared operation/priority/sequence contract. */ [[nodiscard]] static eve::Result addModifier( - Unit& unit, std::string id, std::string_view attribute, - std::string source, eve::attributes::AttributeOperation operation, double value, + Unit& unit, std::string id, std::string_view attribute, std::string source, + eve::attributes::AttributeOperation operation, double value, eve::attributes::ModifierPriority priority = eve::attributes::priority::runtime); /** @brief Capture selected unit combat stats with owner generation and revision. */ - [[nodiscard]] static eve::Result snapshot( - Unit& unit); + [[nodiscard]] static eve::Result snapshot(Unit& unit); /** @brief Restore selected unit combat stats after owner/revision checks. */ - [[nodiscard]] static eve::Result restore( - Unit& unit, const eve::attributes::AttributeProjectionSnapshot& snapshot, - eve::Revision expectedRevision); + [[nodiscard]] static eve::Result restore(Unit& unit, + const eve::attributes::AttributeProjectionSnapshot& snapshot, + eve::Revision expectedRevision); }; } // namespace eve::rts diff --git a/src/modules/rts/RTSEconomy.cpp b/src/modules/rts/RTSEconomy.cpp index 89202b9a..5be79f50 100644 --- a/src/modules/rts/RTSEconomy.cpp +++ b/src/modules/rts/RTSEconomy.cpp @@ -4,23 +4,18 @@ namespace eve::rts { -RTSEconomyAdapter::RTSEconomyAdapter(eve::economy::EconomyLedger& ledger) - : account_(ledger) {} +RTSEconomyAdapter::RTSEconomyAdapter(eve::economy::EconomyLedger& ledger) : account_(ledger) {} -eve::resource::IResourceAccount& RTSEconomyAdapter::account() noexcept { - return account_; -} +eve::resource::IResourceAccount& RTSEconomyAdapter::account() noexcept { return account_; } eve::Result RTSEconomyAdapter::pay( - const eve::resource::CostSpec& cost, - eve::transaction::ITransactionParticipant& effect, std::string transactionId) { + const eve::resource::CostSpec& cost, eve::transaction::ITransactionParticipant& effect, std::string transactionId) { if (transactionId.empty()) transactionId = "rts.economy.payment"; return eve::transaction::AtomicResourcePayment::execute( eve::transaction::TransactionContext(std::move(transactionId)), account_, cost, effect); } -eve::Result RTSEconomyAdapter::credit( - const eve::resource::CostSpec& cost) { +eve::Result RTSEconomyAdapter::credit(const eve::resource::CostSpec& cost) { return account_.credit(cost); } diff --git a/src/modules/rts/RTSEconomy.h b/src/modules/rts/RTSEconomy.h index 66bb8c72..582422fc 100644 --- a/src/modules/rts/RTSEconomy.h +++ b/src/modules/rts/RTSEconomy.h @@ -26,11 +26,11 @@ public: /** @brief Bind this adapter to a caller-owned RTS economy ledger. */ explicit RTSEconomyAdapter(eve::economy::EconomyLedger& ledger); - RTSEconomyAdapter(const RTSEconomyAdapter&) = delete; + RTSEconomyAdapter(const RTSEconomyAdapter&) = delete; RTSEconomyAdapter& operator=(const RTSEconomyAdapter&) = delete; - RTSEconomyAdapter(RTSEconomyAdapter&&) = delete; - RTSEconomyAdapter& operator=(RTSEconomyAdapter&&) = delete; - ~RTSEconomyAdapter() = default; + RTSEconomyAdapter(RTSEconomyAdapter&&) = delete; + RTSEconomyAdapter& operator=(RTSEconomyAdapter&&) = delete; + ~RTSEconomyAdapter() = default; /** * @brief Return the account view for direct reservation/credit operations. @@ -46,8 +46,7 @@ public: * @return Committed transaction receipt or rollback/compensation diagnostics. */ [[nodiscard]] eve::Result pay( - const eve::resource::CostSpec& cost, - eve::transaction::ITransactionParticipant& effect, + const eve::resource::CostSpec& cost, eve::transaction::ITransactionParticipant& effect, std::string transactionId = {}); /** @@ -55,8 +54,7 @@ public: * @param cost Canonical positive resource quantity to add. * @return A checked credit receipt or a structured capacity/range failure. */ - [[nodiscard]] eve::Result credit( - const eve::resource::CostSpec& cost); + [[nodiscard]] eve::Result credit(const eve::resource::CostSpec& cost); private: eve::economy::EconomyLedgerResourceAccount account_; diff --git a/src/modules/rts/RTSEffects.cpp b/src/modules/rts/RTSEffects.cpp index 8155616e..8b28e6e9 100644 --- a/src/modules/rts/RTSEffects.cpp +++ b/src/modules/rts/RTSEffects.cpp @@ -10,9 +10,9 @@ namespace { const char* kindTag(RTSEffectKind kind) { switch (kind) { - case RTSEffectKind::Morale: return "rts:morale"; - case RTSEffectKind::Suppression: return "rts:suppression"; - case RTSEffectKind::ProductionLock: return "rts:production-lock"; + case RTSEffectKind::Morale: return "rts:morale"; + case RTSEffectKind::Suppression: return "rts:suppression"; + case RTSEffectKind::ProductionLock: return "rts:production-lock"; } return "rts:unknown"; } @@ -21,12 +21,12 @@ const char* kindTag(RTSEffectKind kind) { eve::Result RTSEffectExecutor::validate(const RTSEffectTarget& target) const { if (!std::isfinite(target.morale) || target.morale < 0.0 || target.morale > 100.0) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "RTS morale must be in [0,100]", "target.morale")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "RTS morale must be in [0,100]", "target.morale")); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result RTSEffectExecutor::applyImmediate(RTSEffectTarget& target, +eve::Result RTSEffectExecutor::applyImmediate(RTSEffectTarget& target, const effects::EffectInstance& effect) const { auto valid = validate(target); if (!valid) return valid; @@ -34,11 +34,10 @@ eve::Result RTSEffectExecutor::applyImmediate(RTSEffectTarget& target, return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result RTSEffectExecutor::settle(RTSEffectTarget& target, +eve::Result RTSEffectExecutor::settle(RTSEffectTarget& target, effects::EffectUpdateSummary lifecycle) const { auto valid = validate(target); - if (!valid) - return eve::Result::failure(valid.status()); + if (!valid) return eve::Result::failure(valid.status()); RTSEffectUpdate result; result.lifecycle = std::move(lifecycle); for (const auto& tick : result.lifecycle.periodicTicks) { @@ -66,42 +65,42 @@ eve::Result RTSEffectExecutor::settle(RTSEffectTarget& target, } ++result.settled; } - return eve::Result::success(std::move(result), - eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(std::move(result), eve::Status::success(eve::StatusCode::Applied)); } eve::Result RTSEffectAdapter::apply(const RTSEffectDefinition& definition, - eve::SubjectRef subject) { + eve::SubjectRef subject) { if (definition.id.empty() || !subject.isValid()) return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::InvalidArgument, "RTS effect requires an id and valid subject", "effect")); effects::EffectDefinition common; - common.id = definition.id; - common.stackKey = definition.id; - common.duration = definition.duration; - common.period = definition.period; + common.id = definition.id; + common.stackKey = definition.id; + common.duration = definition.duration; + common.period = definition.period; common.magnitude = definition.magnitude; - common.policy = definition.policy; - common.tags = definition.tags; + common.policy = definition.policy; + common.tags = definition.tags; common.tags.push_back(kindTag(definition.kind)); auto valid = common.validate(); if (!valid) return eve::Result::failure(valid.status()); auto candidate = container_.clone(); - auto applied = candidate.apply(common, subject.format(), definition.source); + auto applied = candidate.apply(common, subject.format(), definition.source); if (!applied) return eve::Result::failure(applied.status()); - const std::string id = std::move(applied).takeValue(); - const auto* instance = candidate.find(id); - if (!instance) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, "RTS effect candidate disappeared", "effect")); + const std::string id = std::move(applied).takeValue(); + const auto* instance = candidate.find(id); + if (!instance) + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvariantViolation, "RTS effect candidate disappeared", "effect")); auto targetCandidate = target_; - auto targetValid = executor_.applyImmediate(targetCandidate, *instance); + auto targetValid = executor_.applyImmediate(targetCandidate, *instance); if (!targetValid) return eve::Result::failure(targetValid.status()); auto handle = candidate.handleFor(id); if (!handle) return eve::Result::failure(handle.status()); - target_ = std::move(targetCandidate); + target_ = std::move(targetCandidate); container_ = std::move(candidate); return eve::Result::success(std::move(handle).takeValue(), - eve::Status::success(eve::StatusCode::Applied)); + eve::Status::success(eve::StatusCode::Applied)); } eve::Result RTSEffectAdapter::remove(effects::EffectHandle handle) { @@ -115,13 +114,12 @@ eve::Result RTSEffectAdapter::advance(const eve::SimulationStep auto lifecycle = effects::EffectExecutor{}.advance(candidate, step); if (!lifecycle) return eve::Result::failure(lifecycle.status()); auto targetCandidate = target_; - auto settled = executor_.settle(targetCandidate, std::move(lifecycle).takeValue()); + auto settled = executor_.settle(targetCandidate, std::move(lifecycle).takeValue()); if (!settled) return settled; auto result = std::move(settled).takeValue(); - target_ = std::move(targetCandidate); - container_ = std::move(candidate); - return eve::Result::success(std::move(result), - eve::Status::success(eve::StatusCode::Applied)); + target_ = std::move(targetCandidate); + container_ = std::move(candidate); + return eve::Result::success(std::move(result), eve::Status::success(eve::StatusCode::Applied)); } RTSEffectSnapshot RTSEffectAdapter::snapshot() const { return {container_.snapshot(), target_}; } @@ -135,9 +133,7 @@ eve::Result RTSEffectAdapter::restore(const RTSEffectSnapshot& snapshotVal return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -std::size_t RTSEffectAdapter::count() const noexcept { - return static_cast(container_.effectCount()); -} +std::size_t RTSEffectAdapter::count() const noexcept { return static_cast(container_.effectCount()); } eve::Result RTSEffectAdapter::resolve(effects::EffectHandle handle) const { return container_.resolve(handle); @@ -146,31 +142,25 @@ eve::Result RTSEffectAdapter::resolve(effects::E eve::Result RTSEffectComponent::bindSubject(eve::SubjectRef subject) { if (!subject.isValid()) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "RTS effect component requires a valid subject", "subject")); + eve::DiagnosticCode::InvalidArgument, "RTS effect component requires a valid subject", "subject")); if (subject_.isValid() && subject_ != subject && adapter_.count() != 0) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, - "RTS effect component cannot change subject while effects are active", "subject")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, + "RTS effect component cannot change subject while effects are active", "subject")); subject_ = subject; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result RTSEffectComponent::apply( - const RTSEffectDefinition& definition) { +eve::Result RTSEffectComponent::apply(const RTSEffectDefinition& definition) { if (!subject_.isValid()) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "RTS effect component has no bound subject", "subject")); + eve::DiagnosticCode::InvalidArgument, "RTS effect component has no bound subject", "subject")); return adapter_.apply(definition, subject_); } -eve::Result RTSEffectComponent::remove(effects::EffectHandle handle) { - return adapter_.remove(handle); -} +eve::Result RTSEffectComponent::remove(effects::EffectHandle handle) { return adapter_.remove(handle); } -eve::Result RTSEffectComponent::advance( - const eve::SimulationStep& step) { +eve::Result RTSEffectComponent::advance(const eve::SimulationStep& step) { return adapter_.advance(step); } diff --git a/src/modules/rts/RTSEffects.h b/src/modules/rts/RTSEffects.h index a51a4bf6..4109a8d1 100644 --- a/src/modules/rts/RTSEffects.h +++ b/src/modules/rts/RTSEffects.h @@ -22,35 +22,35 @@ enum class RTSEffectKind : std::uint8_t { Morale, Suppression, ProductionLock }; /** @brief Mutable RTS state affected by the domain executor. */ struct RTSEffectTarget { - double morale = 100.0; - std::uint32_t suppression = 0; - bool productionLocked = false; + double morale = 100.0; + std::uint32_t suppression = 0; + bool productionLocked = false; std::uint32_t commandInterrupts = 0; }; /** @brief Typed RTS definition projected to the common lifecycle schema. */ struct RTSEffectDefinition { - std::string id; - std::string source; - double duration = 0.0; - double period = 0.0; - double magnitude = 0.0; - effects::EffectPolicy policy; - RTSEffectKind kind = RTSEffectKind::Morale; + std::string id; + std::string source; + double duration = 0.0; + double period = 0.0; + double magnitude = 0.0; + effects::EffectPolicy policy; + RTSEffectKind kind = RTSEffectKind::Morale; std::vector tags; }; /** @brief Result of one RTS lifecycle step and domain settlement. */ struct RTSEffectUpdate { effects::EffectUpdateSummary lifecycle; - std::uint32_t settled = 0; - std::uint32_t commandInterrupts = 0; + std::uint32_t settled = 0; + std::uint32_t commandInterrupts = 0; }; /** @brief Serializable in-memory RTS effect snapshot. */ struct RTSEffectSnapshot { effects::EffectContainer effects; - RTSEffectTarget target; + RTSEffectTarget target; }; /** @brief RTS executor for morale, suppression and production-lock semantics. */ @@ -59,10 +59,10 @@ public: /** @brief Validate the target state before a staged application. */ [[nodiscard]] eve::Result validate(const RTSEffectTarget& target) const; /** @brief Apply immediate production-lock semantics to a staged target. */ - [[nodiscard]] eve::Result applyImmediate(RTSEffectTarget& target, + [[nodiscard]] eve::Result applyImmediate(RTSEffectTarget& target, const effects::EffectInstance& effect) const; /** @brief Settle common periodic triggers using RTS-specific policies. */ - [[nodiscard]] eve::Result settle(RTSEffectTarget& target, + [[nodiscard]] eve::Result settle(RTSEffectTarget& target, effects::EffectUpdateSummary lifecycle) const; }; @@ -71,7 +71,7 @@ class RTSEffectAdapter { public: /** @brief Apply one typed effect to a stable subject. */ [[nodiscard]] eve::Result apply(const RTSEffectDefinition& definition, - eve::SubjectRef subject); + eve::SubjectRef subject); /** @brief Remove one generation-qualified effect handle. */ [[nodiscard]] eve::Result remove(effects::EffectHandle handle); /** @brief Advance and settle RTS effects atomically. */ @@ -91,8 +91,8 @@ public: private: effects::EffectContainer container_; - RTSEffectExecutor executor_; - RTSEffectTarget target_; + RTSEffectExecutor executor_; + RTSEffectTarget target_; }; /** @@ -107,8 +107,7 @@ public: /** @brief Bind the owning entity subject before applying effects. */ [[nodiscard]] eve::Result bindSubject(eve::SubjectRef subject); /** @brief Apply a typed RTS effect to this entity's canonical lifecycle. */ - [[nodiscard]] eve::Result apply( - const RTSEffectDefinition& definition); + [[nodiscard]] eve::Result apply(const RTSEffectDefinition& definition); /** @brief Remove one generation-qualified effect handle. */ [[nodiscard]] eve::Result remove(effects::EffectHandle handle); /** @brief Advance this entity's effect lifecycle and settle domain state. */ @@ -122,7 +121,7 @@ public: private: RTSEffectAdapter adapter_; - eve::SubjectRef subject_; + eve::SubjectRef subject_; }; } // namespace eve::rts diff --git a/src/modules/rts/RTSProductionAction.cpp b/src/modules/rts/RTSProductionAction.cpp index 10bb2078..a80d0613 100644 --- a/src/modules/rts/RTSProductionAction.cpp +++ b/src/modules/rts/RTSProductionAction.cpp @@ -11,47 +11,42 @@ namespace eve::rts { namespace { template -eve::Result failure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { - return eve::Result::failure( - eve::Diagnostic::error(code, std::move(message), std::move(path))); +eve::Result failure(eve::DiagnosticCode code, std::string message, std::string path = {}) { + return eve::Result::failure(eve::Diagnostic::error(code, std::move(message), std::move(path))); } -eve::Result failure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { - return eve::Result::failure( - eve::Diagnostic::error(code, std::move(message), std::move(path))); +eve::Result failure(eve::DiagnosticCode code, std::string message, std::string path = {}) { + return eve::Result::failure(eve::Diagnostic::error(code, std::move(message), std::move(path))); } class OrderParticipant final : public transaction::ITransactionParticipant { public: - OrderParticipant(orders::CommandQueue& queue, std::string kind, int priority, - double timeout, std::string product, std::string owner) - : queue_(queue), kind_(std::move(kind)), priority_(priority), timeout_(timeout), - product_(std::move(product)), owner_(std::move(owner)) {} + OrderParticipant(orders::CommandQueue& queue, std::string kind, int priority, double timeout, std::string product, + std::string owner) + : queue_(queue), + kind_(std::move(kind)), + priority_(priority), + timeout_(timeout), + product_(std::move(product)), + owner_(std::move(owner)) {} - [[nodiscard]] std::string_view name() const noexcept override { - return "rts-build-order"; - } + [[nodiscard]] std::string_view name() const noexcept override { return "rts-build-order"; } [[nodiscard]] eve::Result prepare(const transaction::TransactionContext&) override { if (prepared_ || committed_) - return failure(eve::DiagnosticCode::Conflict, - "RTS order participant is already in flight", "orders"); + return failure(eve::DiagnosticCode::Conflict, "RTS order participant is already in flight", "orders"); try { before_ = queue_; staged_ = queue_; } catch (const std::exception& exception) { - return failure(eve::DiagnosticCode::Failed, - std::string("failed to stage RTS order: ") + exception.what(), "orders"); + return failure(eve::DiagnosticCode::Failed, std::string("failed to stage RTS order: ") + exception.what(), + "orders"); } auto id = staged_->append(kind_, priority_, timeout_); if (!id) return eve::Result::failure(id.status()); - orderId_ = std::move(id).takeValue(); + orderId_ = std::move(id).takeValue(); auto order = staged_->find(orderId_); - if (!order) - return failure(eve::DiagnosticCode::InvariantViolation, - "staged RTS order disappeared", "orders"); + if (!order) return failure(eve::DiagnosticCode::InvariantViolation, "staged RTS order disappeared", "orders"); order->get().payload.setString("owner", owner_); order->get().payload.setString("product", product_); prepared_ = true; @@ -60,13 +55,12 @@ public: [[nodiscard]] eve::Result commit(const transaction::TransactionContext&) override { if (!prepared_ || committed_) - return failure(eve::DiagnosticCode::Conflict, - "RTS order participant has no prepared stage", "orders"); + return failure(eve::DiagnosticCode::Conflict, "RTS order participant has no prepared stage", "orders"); try { queue_ = *staged_; } catch (const std::exception& exception) { - return failure(eve::DiagnosticCode::Failed, - std::string("failed to publish RTS order: ") + exception.what(), "orders"); + return failure(eve::DiagnosticCode::Failed, std::string("failed to publish RTS order: ") + exception.what(), + "orders"); } committed_ = true; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); @@ -74,8 +68,7 @@ public: [[nodiscard]] eve::Result rollback(const transaction::TransactionContext&) override { if (committed_) - return failure(eve::DiagnosticCode::Conflict, - "committed RTS order requires compensation", "orders"); + return failure(eve::DiagnosticCode::Conflict, "committed RTS order requires compensation", "orders"); staged_.reset(); prepared_ = false; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); @@ -83,8 +76,7 @@ public: [[nodiscard]] eve::Result compensate(const transaction::TransactionContext&) override { if (!committed_) - return failure(eve::DiagnosticCode::Conflict, - "RTS order has no committed state to compensate", "orders"); + return failure(eve::DiagnosticCode::Conflict, "RTS order has no committed state to compensate", "orders"); try { queue_ = *before_; } catch (const std::exception& exception) { @@ -93,7 +85,7 @@ public: } staged_.reset(); before_.reset(); - prepared_ = false; + prepared_ = false; committed_ = false; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } @@ -101,56 +93,57 @@ public: [[nodiscard]] const std::string& orderId() const noexcept { return orderId_; } private: - orders::CommandQueue& queue_; - std::string kind_; - int priority_ = 0; - double timeout_ = 0.0; - std::string product_; - std::string owner_; + orders::CommandQueue& queue_; + std::string kind_; + int priority_ = 0; + double timeout_ = 0.0; + std::string product_; + std::string owner_; std::optional before_; std::optional staged_; - std::string orderId_; - bool prepared_ = false; - bool committed_ = false; + std::string orderId_; + bool prepared_ = false; + bool committed_ = false; }; class ProductionParticipant final : public transaction::ITransactionParticipant { public: - ProductionParticipant(production::WorkQueue& queue, std::string owner, - std::string kind, std::string product, eve::Value context, - Duration duration, int priority) - : queue_(queue), owner_(std::move(owner)), kind_(std::move(kind)), - product_(std::move(product)), context_(std::move(context)), duration_(duration), + ProductionParticipant(production::WorkQueue& queue, std::string owner, std::string kind, std::string product, + eve::Value context, Duration duration, int priority) + : queue_(queue), + owner_(std::move(owner)), + kind_(std::move(kind)), + product_(std::move(product)), + context_(std::move(context)), + duration_(duration), priority_(priority) {} - [[nodiscard]] std::string_view name() const noexcept override { - return "rts-production-queue"; - } + [[nodiscard]] std::string_view name() const noexcept override { return "rts-production-queue"; } [[nodiscard]] eve::Result prepare(const transaction::TransactionContext&) override { if (prepared_ || committed_) - return failure(eve::DiagnosticCode::Conflict, - "RTS production participant is already in flight", "production"); + return failure(eve::DiagnosticCode::Conflict, "RTS production participant is already in flight", + "production"); auto before = queue_.snapshot(); if (!before) return eve::Result::failure(before.status()); - beforeJson_ = std::move(before).takeValue(); - staged_ = std::make_unique(); + beforeJson_ = std::move(before).takeValue(); + staged_ = std::make_unique(); auto restored = staged_->restore(beforeJson_); if (!restored) return restored; auto task = staged_->enqueue(owner_, kind_, product_, context_, duration_.seconds(), priority_); if (!task) return eve::Result::failure(task.status()); - taskId_ = std::move(task).takeValue(); + taskId_ = std::move(task).takeValue(); auto after = staged_->snapshot(); if (!after) return eve::Result::failure(after.status()); afterJson_ = std::move(after).takeValue(); - prepared_ = true; + prepared_ = true; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } [[nodiscard]] eve::Result commit(const transaction::TransactionContext&) override { if (!prepared_ || committed_) - return failure(eve::DiagnosticCode::Conflict, - "RTS production participant has no prepared stage", "production"); + return failure(eve::DiagnosticCode::Conflict, "RTS production participant has no prepared stage", + "production"); auto current = queue_.snapshot(); if (!current) return eve::Result::failure(current.status()); if (current.value() != beforeJson_) @@ -164,8 +157,8 @@ public: [[nodiscard]] eve::Result rollback(const transaction::TransactionContext&) override { if (committed_) - return failure(eve::DiagnosticCode::Conflict, - "committed production task requires compensation", "production"); + return failure(eve::DiagnosticCode::Conflict, "committed production task requires compensation", + "production"); staged_.reset(); prepared_ = false; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); @@ -173,19 +166,19 @@ public: [[nodiscard]] eve::Result compensate(const transaction::TransactionContext&) override { if (!committed_) - return failure(eve::DiagnosticCode::Conflict, - "production queue has no committed task to compensate", "production"); + return failure(eve::DiagnosticCode::Conflict, "production queue has no committed task to compensate", + "production"); auto current = queue_.snapshot(); if (!current) return eve::Result::failure(current.status()); if (current.value() != afterJson_) - return failure(eve::DiagnosticCode::StaleHandle, - "RTS production queue changed before compensation", "production"); + return failure(eve::DiagnosticCode::StaleHandle, "RTS production queue changed before compensation", + "production"); auto restored = queue_.restore(beforeJson_); if (!restored) return restored; staged_.reset(); beforeJson_.clear(); afterJson_.clear(); - prepared_ = false; + prepared_ = false; committed_ = false; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } @@ -193,46 +186,46 @@ public: [[nodiscard]] const std::string& taskId() const noexcept { return taskId_; } private: - production::WorkQueue& queue_; - std::string owner_; - std::string kind_; - std::string product_; - eve::Value context_; - Duration duration_; - int priority_ = 0; + production::WorkQueue& queue_; + std::string owner_; + std::string kind_; + std::string product_; + eve::Value context_; + Duration duration_; + int priority_ = 0; std::unique_ptr staged_; - std::string beforeJson_; - std::string afterJson_; - std::string taskId_; - bool prepared_ = false; - bool committed_ = false; + std::string beforeJson_; + std::string afterJson_; + std::string taskId_; + bool prepared_ = false; + bool committed_ = false; }; class ActionParticipant final : public transaction::ITransactionParticipant { public: ActionParticipant(action::ActionRuntime& runtime, action::ActionDefinition definition, action::ActionRequest request, SimulationTick tick, Duration delta) - : runtime_(runtime), definition_(std::move(definition)), request_(std::move(request)), - tick_(tick), delta_(delta) {} + : runtime_(runtime), + definition_(std::move(definition)), + request_(std::move(request)), + tick_(tick), + delta_(delta) {} - [[nodiscard]] std::string_view name() const noexcept override { - return "rts-build-action"; - } + [[nodiscard]] std::string_view name() const noexcept override { return "rts-build-action"; } [[nodiscard]] eve::Result prepare(const transaction::TransactionContext&) override { - if (execution_) return failure(eve::DiagnosticCode::Conflict, - "RTS Action participant is already in flight", "action"); + if (execution_) + return failure(eve::DiagnosticCode::Conflict, "RTS Action participant is already in flight", "action"); auto submitted = runtime_.submit(std::move(definition_), std::move(request_)); if (!submitted) return eve::Result::failure(submitted.status()); execution_ = std::move(submitted).takeValue(); - prepared_ = true; + prepared_ = true; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } [[nodiscard]] eve::Result commit(const transaction::TransactionContext&) override { if (!prepared_ || committed_ || !execution_) - return failure(eve::DiagnosticCode::Conflict, - "RTS Action participant has no prepared execution", "action"); + return failure(eve::DiagnosticCode::Conflict, "RTS Action participant has no prepared execution", "action"); auto advanced = runtime_.advance(*execution_, tick_, delta_); if (!advanced) return eve::Result::failure(advanced.status()); std::move(advanced).takeValue(); @@ -242,13 +235,11 @@ public: [[nodiscard]] eve::Result rollback(const transaction::TransactionContext&) override { if (committed_) - return failure(eve::DiagnosticCode::Conflict, - "committed RTS Action requires compensation", "action"); + return failure(eve::DiagnosticCode::Conflict, "committed RTS Action requires compensation", "action"); if (!execution_) return eve::Result::success(eve::Status::success(eve::StatusCode::NoOp)); const auto* current = runtime_.find(*execution_); if (current != nullptr && current->phase() != action::ActionPhase::Failed && - current->phase() != action::ActionPhase::Cancelled && - current->phase() != action::ActionPhase::Completed) { + current->phase() != action::ActionPhase::Cancelled && current->phase() != action::ActionPhase::Completed) { auto cancelled = runtime_.cancel(*execution_, tick_); if (!cancelled) return cancelled; } @@ -260,7 +251,7 @@ public: // ActionRuntime's terminal record is an audit record. It has no // mutable business state once the other participants are compensated. committed_ = false; - prepared_ = false; + prepared_ = false; return eve::Result::success(eve::Status::success(eve::StatusCode::NoOp)); } @@ -269,47 +260,45 @@ public: } private: - action::ActionRuntime& runtime_; - action::ActionDefinition definition_; - action::ActionRequest request_; - SimulationTick tick_; - Duration delta_; + action::ActionRuntime& runtime_; + action::ActionDefinition definition_; + action::ActionRequest request_; + SimulationTick tick_; + Duration delta_; std::optional execution_; - bool prepared_ = false; - bool committed_ = false; + bool prepared_ = false; + bool committed_ = false; }; } // namespace -eve::Result RTSProductionActionAdapter::build( - Building &building, action::ActionRuntime &action, - resource::IResourceAccount &account, resource::CostSpec cost, - std::string product, Duration duration, std::string productionKind, - int priority, std::string transactionId) { +eve::Result RTSProductionActionAdapter::build(Building& building, action::ActionRuntime& action, + resource::IResourceAccount& account, + resource::CostSpec cost, std::string product, + Duration duration, std::string productionKind, + int priority, std::string transactionId) { const auto subject = building.identity()->subject; if (!subject.isValid()) - return failure( - eve::DiagnosticCode::InvalidArgument, - "RTS building build requires a valid building subject", "building.subject"); - auto *production = building.production()->values.queueForComposition(); - auto *orders = building.orders()->values.queueForComposition(); + return failure(eve::DiagnosticCode::InvalidArgument, + "RTS building build requires a valid building subject", "building.subject"); + auto* production = building.production()->values.queueForComposition(); + auto* orders = building.orders()->values.queueForComposition(); if (production == nullptr || orders == nullptr) - return failure( - eve::DiagnosticCode::InvariantViolation, - "RTS building build components are not initialized", "building"); + return failure(eve::DiagnosticCode::InvariantViolation, + "RTS building build components are not initialized", "building"); RTSBuildRequest request; - request.production = production; - request.orders = orders; - request.action = &action; - request.account = &account; - request.cost = std::move(cost); - request.owner = subject.format(); - request.productionKind = std::move(productionKind); - request.product = std::move(product); - request.duration = std::move(duration); - request.priority = priority; - request.transactionId = std::move(transactionId); + request.production = production; + request.orders = orders; + request.action = &action; + request.account = &account; + request.cost = std::move(cost); + request.owner = subject.format(); + request.productionKind = std::move(productionKind); + request.product = std::move(product); + request.duration = std::move(duration); + request.priority = priority; + request.transactionId = std::move(transactionId); request.actionRequest.source = ecs::handle_of(&building); return build(std::move(request)); } @@ -318,8 +307,7 @@ eve::Result RTSProductionActionAdapter::build(RTSBuildRequest r if (request.production == nullptr || request.orders == nullptr || request.action == nullptr || request.account == nullptr) return failure(eve::DiagnosticCode::InvalidArgument, - "RTS build requires production, orders, Action and account ports", - "request"); + "RTS build requires production, orders, Action and account ports", "request"); if (request.owner.empty() || request.productionKind.empty() || request.product.empty()) return failure(eve::DiagnosticCode::InvalidArgument, "RTS build requires owner, kind and product", "request"); @@ -327,13 +315,14 @@ eve::Result RTSProductionActionAdapter::build(RTSBuildRequest r return failure(eve::DiagnosticCode::InvalidArgument, "RTS build requires a validated resource cost", "cost"); if (request.duration.nanoseconds() <= 0 || request.actionDelta.nanoseconds() < 0) - return failure(eve::DiagnosticCode::InvalidArgument, - "RTS build durations are invalid", "duration"); + return failure(eve::DiagnosticCode::InvalidArgument, "RTS build durations are invalid", + "duration"); if (!request.actionDefinition.id.isValid()) { auto id = eve::LogicalId::fromParts("rts", "build." + request.product); - if (!id) return failure(eve::DiagnosticCode::InvalidArgument, - "RTS product cannot form an Action id", "product"); + if (!id) + return failure(eve::DiagnosticCode::InvalidArgument, + "RTS product cannot form an Action id", "product"); request.actionDefinition.id = *id; } if (request.actionDefinition.cost || request.actionDefinition.activeExecutionRequired || @@ -342,8 +331,7 @@ eve::Result RTSProductionActionAdapter::build(RTSBuildRequest r "RTS build Action must be effect-free; effect is a transaction port", "actionDefinition"); request.actionDefinition.timing = {}; - if (!request.actionRequest.actionId.isValid()) - request.actionRequest.actionId = request.actionDefinition.id; + if (!request.actionRequest.actionId.isValid()) request.actionRequest.actionId = request.actionDefinition.id; if (request.actionRequest.actionId != request.actionDefinition.id) return failure(eve::DiagnosticCode::InvalidArgument, "RTS build Action request does not match definition", "actionRequest"); @@ -351,14 +339,12 @@ eve::Result RTSProductionActionAdapter::build(RTSBuildRequest r if (request.transactionId.empty()) request.transactionId = "rts.build." + request.product; transaction::TransactionContext context(std::move(request.transactionId)); - OrderParticipant order(*request.orders, request.orderKind, request.orderPriority, - request.orderTimeoutSeconds, request.product, request.owner); - ProductionParticipant production(*request.production, request.owner, request.productionKind, - request.product, std::move(request.context), request.duration, - request.priority); - ActionParticipant action(*request.action, std::move(request.actionDefinition), - std::move(request.actionRequest), request.tick, - request.actionDelta); + OrderParticipant order(*request.orders, request.orderKind, request.orderPriority, request.orderTimeoutSeconds, + request.product, request.owner); + ProductionParticipant production(*request.production, request.owner, request.productionKind, request.product, + std::move(request.context), request.duration, request.priority); + ActionParticipant action(*request.action, std::move(request.actionDefinition), std::move(request.actionRequest), + request.tick, request.actionDelta); std::vector participants; participants.reserve(request.effect ? 4u : 3u); participants.push_back(&order); @@ -367,14 +353,11 @@ eve::Result RTSProductionActionAdapter::build(RTSBuildRequest r participants.push_back(&action); auto committed = transaction::AtomicResourcePayment::execute( - context, *request.account, request.cost, - std::span(participants)); - if (!committed) - return eve::Result::failure(committed.status()); - auto receipt = std::move(committed).takeValue(); + context, *request.account, request.cost, std::span(participants)); + if (!committed) return eve::Result::failure(committed.status()); + auto receipt = std::move(committed).takeValue(); RTSBuildReceipt result{std::move(receipt), order.orderId(), production.taskId(), action.executionId()}; - return eve::Result::success(std::move(result), - eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(std::move(result), eve::Status::success(eve::StatusCode::Applied)); } } // namespace eve::rts diff --git a/src/modules/rts/RTSProductionAction.h b/src/modules/rts/RTSProductionAction.h index 5947e0c0..70b05390 100644 --- a/src/modules/rts/RTSProductionAction.h +++ b/src/modules/rts/RTSProductionAction.h @@ -30,22 +30,22 @@ class Building; * call and must remain valid until the returned Result is consumed. */ struct RTSBuildRequest { - production::WorkQueue* production = nullptr; - orders::CommandQueue* orders = nullptr; - action::ActionRuntime* action = nullptr; - resource::IResourceAccount* account = nullptr; + production::WorkQueue* production = nullptr; + orders::CommandQueue* orders = nullptr; + action::ActionRuntime* action = nullptr; + resource::IResourceAccount* account = nullptr; resource::CostSpec cost; - std::string owner; - std::string productionKind = "build"; - std::string product; - eve::Value context = eve::Value(eve::Value::Object{}); - Duration duration = Duration::zero(); - int priority = 0; + std::string owner; + std::string productionKind = "build"; + std::string product; + eve::Value context = eve::Value(eve::Value::Object{}); + Duration duration = Duration::zero(); + int priority = 0; - std::string orderKind = "build"; - int orderPriority = 0; - double orderTimeoutSeconds = 0.0; + std::string orderKind = "build"; + int orderPriority = 0; + double orderTimeoutSeconds = 0.0; /** @brief Optional custom action definition; an invalid id gets a build id. */ action::ActionDefinition actionDefinition; @@ -64,9 +64,9 @@ struct RTSBuildRequest { /** @brief Result of an accepted atomic RTS build/production command. */ struct RTSBuildReceipt { transaction::TransactionReceipt transaction; - std::string orderId; - std::string productionTaskId; - action::ActionExecutionId actionExecution; + std::string orderId; + std::string productionTaskId; + action::ActionExecutionId actionExecution; }; /** @@ -94,11 +94,11 @@ public: * @param transactionId Optional transaction correlation id. * @return Complete build receipt, or a failure without partial queue/payment state. */ - [[nodiscard]] static eve::Result build( - Building& building, action::ActionRuntime& action, - resource::IResourceAccount& account, resource::CostSpec cost, - std::string product, Duration duration, std::string productionKind = "unit", - int priority = 0, std::string transactionId = {}); + [[nodiscard]] static eve::Result build(Building& building, action::ActionRuntime& action, + resource::IResourceAccount& account, + resource::CostSpec cost, std::string product, + Duration duration, std::string productionKind = "unit", + int priority = 0, std::string transactionId = {}); /** * @brief Atomically enqueue an RTS build/production action and charge cost. diff --git a/src/modules/rts/RTSSystems.cpp b/src/modules/rts/RTSSystems.cpp index bf76f4e7..51f28e14 100644 --- a/src/modules/rts/RTSSystems.cpp +++ b/src/modules/rts/RTSSystems.cpp @@ -21,9 +21,7 @@ Result failureFrom(const Status& status) { return Result::failure(status); } -bool finitePosition(WorldPosition position) { - return std::isfinite(position.x) && std::isfinite(position.y); -} +bool finitePosition(WorldPosition position) { return std::isfinite(position.x) && std::isfinite(position.y); } bool sameHandle(const ecs::EntityHandle& left, const ecs::EntityHandle& right) { return left.table == right.table && left.type == right.type && left.id == right.id && @@ -35,8 +33,7 @@ Result> readCurrent(OrderComponent& orders) { if (current) return Result>::success(std::move(current).takeValue()); if (current.code() == StatusCode::NotFound) { current.ignore("RTS entity has no active order"); - return Result>::success(std::nullopt, - Status::success(StatusCode::NoOp)); + return Result>::success(std::nullopt, Status::success(StatusCode::NoOp)); } return failureFrom>(current.status()); } @@ -45,56 +42,30 @@ Result advanceEffects(RTSEffectComponent& effects, const Simulation auto advanced = effects.advance(step); if (!advanced) return failureFrom(advanced.status()); const auto result = std::move(advanced).takeValue(); - return Result::success( - result.settled, Status::success(result.settled == 0 ? StatusCode::NoOp - : StatusCode::Applied)); + return Result::success(result.settled, + Status::success(result.settled == 0 ? StatusCode::NoOp : StatusCode::Applied)); } } // namespace std::span systemContracts() noexcept { static constexpr SystemContract contracts[] = { - {"rts.command_fan_out", - "ecs::View", - "Unit::Identity; selection handles; formation input", - "Unit::Orders", + {"rts.command_fan_out", "ecs::View", + "Unit::Identity; selection handles; formation input", "Unit::Orders", "none; use ecs::ScopedDefer if future code creates/removes entities/components", - "none; caller owns command receipt/event publication", - "input.command"}, - {"rts.motion", - "ecs::View", - "Unit::Identity; Unit::Orders", - "Unit::Motion", - "none", - "none", - "simulation.movement"}, - {"rts.order_action", - "ecs::View", - "Unit::Identity; Unit::Action; active OrderRecord", - "Unit::Orders; generic order lifecycle state", - "none", - "delegated to IRTSActionExecutor/ActionRuntime", - "simulation.action"}, - {"rts.building_production", - "ecs::View", - "Building::Identity; SimulationStep", - "Building::Production", - "none", - "delegated to production::WorkQueue", + "none; caller owns command receipt/event publication", "input.command"}, + {"rts.motion", "ecs::View", "Unit::Identity; Unit::Orders", + "Unit::Motion", "none", "none", "simulation.movement"}, + {"rts.order_action", "ecs::View", + "Unit::Identity; Unit::Action; active OrderRecord", "Unit::Orders; generic order lifecycle state", "none", + "delegated to IRTSActionExecutor/ActionRuntime", "simulation.action"}, + {"rts.building_production", "ecs::View", + "Building::Identity; SimulationStep", "Building::Production", "none", "delegated to production::WorkQueue", "simulation.production"}, - {"rts.effects.unit", - "ecs::View", - "Unit::Identity; SimulationStep", - "Unit::Effects", - "none", - "delegated to effects::EffectContainer", - "simulation.effects"}, - {"rts.effects.building", - "ecs::View", - "Building::Identity; SimulationStep", - "Building::Effects", - "none", - "delegated to effects::EffectContainer", + {"rts.effects.unit", "ecs::View", "Unit::Identity; SimulationStep", + "Unit::Effects", "none", "delegated to effects::EffectContainer", "simulation.effects"}, + {"rts.effects.building", "ecs::View", + "Building::Identity; SimulationStep", "Building::Effects", "none", "delegated to effects::EffectContainer", "simulation.effects"}, }; return {contracts, sizeof(contracts) / sizeof(contracts[0])}; @@ -102,89 +73,82 @@ std::span systemContracts() noexcept { Result FormationSpec::validate() const { if (!std::isfinite(spacing) || spacing <= 0.0f) - return failure(DiagnosticCode::InvalidArgument, - "formation spacing must be finite and positive", "spacing"); + return failure(DiagnosticCode::InvalidArgument, "formation spacing must be finite and positive", + "spacing"); if (columns < 0) - return failure(DiagnosticCode::InvalidArgument, - "formation columns must be non-negative", "columns"); + return failure(DiagnosticCode::InvalidArgument, "formation columns must be non-negative", "columns"); switch (kind) { - case FormationKind::Line: - case FormationKind::Grid: - case FormationKind::Wedge: - return Result::success(Status::success(StatusCode::Applied)); + case FormationKind::Line: + case FormationKind::Grid: + case FormationKind::Wedge: return Result::success(Status::success(StatusCode::Applied)); } return failure(DiagnosticCode::InvalidArgument, "formation kind is invalid", "kind"); } -Result> FormationPlanner::plan(std::size_t count, - WorldPosition anchor, +Result> FormationPlanner::plan(std::size_t count, WorldPosition anchor, const FormationSpec& spec) { auto valid = spec.validate(); if (!valid) return failureFrom>(valid.status()); if (!finitePosition(anchor)) - return failure>(DiagnosticCode::InvalidArgument, - "formation anchor must be finite", "anchor"); + return failure>(DiagnosticCode::InvalidArgument, "formation anchor must be finite", + "anchor"); std::vector result; result.reserve(count); if (count == 0) - return Result>::success(std::move(result), - Status::success(StatusCode::NoOp)); + return Result>::success(std::move(result), Status::success(StatusCode::NoOp)); const float spacing = spec.spacing; switch (spec.kind) { - case FormationKind::Line: { - const float center = static_cast(count - 1) * 0.5f; - for (std::size_t index = 0; index < count; ++index) { - result.push_back({anchor.x + (static_cast(index) - center) * spacing, anchor.y}); - } - break; - } - case FormationKind::Grid: { - const std::size_t columns = spec.columns > 0 - ? static_cast(spec.columns) - : static_cast(std::ceil(std::sqrt( - static_cast(count)))); - if (columns == 0) - return failure>(DiagnosticCode::InvariantViolation, - "grid formation computed zero columns", "columns"); - const std::size_t rows = (count + columns - 1) / columns; - const float columnCenter = static_cast(columns - 1) * 0.5f; - const float rowCenter = static_cast(rows - 1) * 0.5f; - for (std::size_t index = 0; index < count; ++index) { - const std::size_t row = index / columns; - const std::size_t column = index % columns; - result.push_back({anchor.x + (static_cast(column) - columnCenter) * spacing, - anchor.y + (static_cast(row) - rowCenter) * spacing}); - } - break; - } - case FormationKind::Wedge: { - std::size_t row = 0; - std::size_t rowStart = 0; - for (std::size_t index = 0; index < count; ++index) { - while (index >= rowStart + row + 1) { - rowStart += row + 1; - ++row; + case FormationKind::Line: { + const float center = static_cast(count - 1) * 0.5f; + for (std::size_t index = 0; index < count; ++index) { + result.push_back({anchor.x + (static_cast(index) - center) * spacing, anchor.y}); } - const std::size_t slot = index - rowStart; - const float rowCenter = static_cast(row) * 0.5f; - result.push_back({anchor.x + (static_cast(slot) - rowCenter) * spacing, - anchor.y + static_cast(row) * spacing}); + break; + } + case FormationKind::Grid: { + const std::size_t columns = + spec.columns > 0 ? static_cast(spec.columns) + : static_cast(std::ceil(std::sqrt(static_cast(count)))); + if (columns == 0) + return failure>(DiagnosticCode::InvariantViolation, + "grid formation computed zero columns", "columns"); + const std::size_t rows = (count + columns - 1) / columns; + const float columnCenter = static_cast(columns - 1) * 0.5f; + const float rowCenter = static_cast(rows - 1) * 0.5f; + for (std::size_t index = 0; index < count; ++index) { + const std::size_t row = index / columns; + const std::size_t column = index % columns; + result.push_back({anchor.x + (static_cast(column) - columnCenter) * spacing, + anchor.y + (static_cast(row) - rowCenter) * spacing}); + } + break; + } + case FormationKind::Wedge: { + std::size_t row = 0; + std::size_t rowStart = 0; + for (std::size_t index = 0; index < count; ++index) { + while (index >= rowStart + row + 1) { + rowStart += row + 1; + ++row; + } + const std::size_t slot = index - rowStart; + const float rowCenter = static_cast(row) * 0.5f; + result.push_back({anchor.x + (static_cast(slot) - rowCenter) * spacing, + anchor.y + static_cast(row) * spacing}); + } + break; } - break; } - } - return Result>::success(std::move(result), - Status::success(StatusCode::Applied)); + return Result>::success(std::move(result), Status::success(StatusCode::Applied)); } -Result CommandFanOutSystem::fanOut( - std::span unitHandles, const CommandSpec& command, - const FormationSpec& formation) { +Result CommandFanOutSystem::fanOut(std::span unitHandles, + const CommandSpec& command, const FormationSpec& formation) { if (unitHandles.empty()) - return failure(DiagnosticCode::InvalidArgument, - "RTS command fan-out requires at least one Unit", "selection.units"); + return failure(DiagnosticCode::InvalidArgument, "RTS command fan-out requires at least one Unit", + "selection.units"); auto commandValid = command.validate(); if (!commandValid) return failureFrom(commandValid.status()); @@ -203,9 +167,7 @@ Result CommandFanOutSystem::fanOut( for (auto it = view.begin(); it != view.end(); ++it) { auto [identity, orders] = *it; (void)orders; - Unit* unit = identity == nullptr - ? nullptr - : dynamic_cast(ecs::try_get(identity->self)); + Unit* unit = identity == nullptr ? nullptr : dynamic_cast(ecs::try_get(identity->self)); if (unit != nullptr) visibleUnits.push_back(ecs::handle_of(unit)); } } @@ -214,20 +176,18 @@ Result CommandFanOutSystem::fanOut( selected.reserve(unitHandles.size()); for (const auto& handle : unitHandles) { auto* entity = ecs::try_get(handle); - auto* unit = entity == nullptr ? nullptr : dynamic_cast(entity); + auto* unit = entity == nullptr ? nullptr : dynamic_cast(entity); if (unit == nullptr) return failure(DiagnosticCode::StaleHandle, "RTS fan-out selection contains a stale or non-Unit handle", "selection.units"); const auto liveHandle = ecs::handle_of(unit); - const bool inView = std::any_of( - visibleUnits.begin(), visibleUnits.end(), [&liveHandle](const auto& candidate) { - return sameHandle(candidate, liveHandle); - }); + const bool inView = std::any_of(visibleUnits.begin(), visibleUnits.end(), [&liveHandle](const auto& candidate) { + return sameHandle(candidate, liveHandle); + }); if (!inView) return failure(DiagnosticCode::InvariantViolation, - "RTS Unit selection is outside the declared View closure", - "selection.units"); + "RTS Unit selection is outside the declared View closure", "selection.units"); selected.push_back(unit); } @@ -239,8 +199,8 @@ Result CommandFanOutSystem::fanOut( if (!order) { const Status status = order.status(); for (std::size_t rollback = 0; rollback < receipt.orderIds.size(); ++rollback) { - auto cancelled = selected[rollback]->orders()->values.cancel( - receipt.orderIds[rollback], "command fan-out rollback"); + auto cancelled = + selected[rollback]->orders()->values.cancel(receipt.orderIds[rollback], "command fan-out rollback"); cancelled.ignore("best-effort fan-out rollback"); } return Result::failure(status); @@ -253,23 +213,21 @@ Result CommandFanOutSystem::fanOut( Result MotionSystem::step(const SimulationStep& step) { if (step.delta.nanoseconds() < 0) - return failure(DiagnosticCode::InvalidArgument, - "RTS motion step delta must be non-negative", "step.delta"); + return failure(DiagnosticCode::InvalidArgument, "RTS motion step delta must be non-negative", + "step.delta"); const double deltaSeconds = step.delta.seconds(); - std::size_t processed = 0; - auto view = ecs::View(); + std::size_t processed = 0; + auto view = ecs::View(); for (auto it = view.begin(); it != view.end(); ++it) { auto [identity, motion, orders] = *it; - Unit* unit = identity == nullptr - ? nullptr - : dynamic_cast(ecs::try_get(identity->self)); + Unit* unit = identity == nullptr ? nullptr : dynamic_cast(ecs::try_get(identity->self)); if (unit == nullptr) - return failure(DiagnosticCode::StaleHandle, - "RTS motion identity link is stale", "unit.identity.self"); + return failure(DiagnosticCode::StaleHandle, "RTS motion identity link is stale", + "unit.identity.self"); if (!std::isfinite(motion->x) || !std::isfinite(motion->y) || !std::isfinite(motion->speed) || !std::isfinite(motion->arrivalRadius) || motion->speed < 0.0f || motion->arrivalRadius < 0.0f) return failure(DiagnosticCode::InvalidArgument, - "RTS motion state must contain finite non-negative values", "unit.motion"); + "RTS motion state must contain finite non-negative values", "unit.motion"); auto current = readCurrent(orders->values); if (!current) return failureFrom(current.status()); @@ -281,21 +239,21 @@ Result MotionSystem::step(const SimulationStep& step) { continue; } - const float dx = record->target.x - motion->x; - const float dy = record->target.y - motion->y; + const float dx = record->target.x - motion->x; + const float dy = record->target.y - motion->y; const float distance = std::hypot(dx, dy); if (!std::isfinite(distance)) - return failure(DiagnosticCode::InvariantViolation, - "RTS motion target distance is non-finite", "order.target"); + return failure(DiagnosticCode::InvariantViolation, "RTS motion target distance is non-finite", + "order.target"); if (distance <= motion->arrivalRadius) { - motion->x = record->target.x; - motion->y = record->target.y; + motion->x = record->target.x; + motion->y = record->target.y; motion->arrived = true; } else if (deltaSeconds > 0.0 && motion->speed > 0.0f) { const double travel = static_cast(motion->speed) * deltaSeconds; if (!std::isfinite(travel)) - return failure(DiagnosticCode::InvalidArgument, - "RTS motion travel distance is non-finite", "unit.motion.speed"); + return failure(DiagnosticCode::InvalidArgument, "RTS motion travel distance is non-finite", + "unit.motion.speed"); const float amount = static_cast(std::min(travel, distance)); motion->x += dx / distance * amount; motion->y += dy / distance * amount; @@ -312,22 +270,19 @@ Result MotionSystem::step(const SimulationStep& step) { return Result::success(processed, Status::success(StatusCode::Applied)); } -Result OrderActionSystem::step(const SimulationStep& step, - IRTSActionExecutor& executor) { +Result OrderActionSystem::step(const SimulationStep& step, IRTSActionExecutor& executor) { if (step.delta.nanoseconds() < 0) - return failure(DiagnosticCode::InvalidArgument, - "RTS action step delta must be non-negative", "step.delta"); + return failure(DiagnosticCode::InvalidArgument, "RTS action step delta must be non-negative", + "step.delta"); std::size_t processed = 0; - auto view = ecs::View(); + auto view = ecs::View(); for (auto it = view.begin(); it != view.end(); ++it) { auto [identity, orders, action] = *it; (void)action; - Unit* unit = identity == nullptr - ? nullptr - : dynamic_cast(ecs::try_get(identity->self)); + Unit* unit = identity == nullptr ? nullptr : dynamic_cast(ecs::try_get(identity->self)); if (unit == nullptr) - return failure(DiagnosticCode::StaleHandle, - "RTS action identity link is stale", "unit.identity.self"); + return failure(DiagnosticCode::StaleHandle, "RTS action identity link is stale", + "unit.identity.self"); auto current = readCurrent(orders->values); if (!current) return failureFrom(current.status()); auto record = std::move(current).takeValue(); @@ -347,18 +302,16 @@ Result OrderActionSystem::step(const SimulationStep& step, Result BuildingProductionSystem::step(const SimulationStep& step) { if (step.delta.nanoseconds() < 0) - return failure(DiagnosticCode::InvalidArgument, - "RTS production step delta must be non-negative", "step.delta"); + return failure(DiagnosticCode::InvalidArgument, "RTS production step delta must be non-negative", + "step.delta"); std::size_t processed = 0; - auto view = ecs::View(); + auto view = ecs::View(); for (auto it = view.begin(); it != view.end(); ++it) { auto [identity, production] = *it; - Building* building = identity == nullptr - ? nullptr - : dynamic_cast(ecs::try_get(identity->self)); + Building* building = identity == nullptr ? nullptr : dynamic_cast(ecs::try_get(identity->self)); if (building == nullptr) - return failure(DiagnosticCode::StaleHandle, - "RTS production identity link is stale", "building.identity.self"); + return failure(DiagnosticCode::StaleHandle, "RTS production identity link is stale", + "building.identity.self"); auto advanced = production->values.advance(step); if (!advanced) return failureFrom(advanced.status()); advanced.value(); @@ -369,19 +322,17 @@ Result BuildingProductionSystem::step(const SimulationStep& step) { Result EffectSystem::step(const SimulationStep& step) { if (step.delta.nanoseconds() < 0) - return failure(DiagnosticCode::InvalidArgument, - "RTS effects step delta must be non-negative", "step.delta"); + return failure(DiagnosticCode::InvalidArgument, "RTS effects step delta must be non-negative", + "step.delta"); std::size_t processed = 0; { auto view = ecs::View(); for (auto it = view.begin(); it != view.end(); ++it) { auto [identity, effects] = *it; - Unit* unit = identity == nullptr - ? nullptr - : dynamic_cast(ecs::try_get(identity->self)); + Unit* unit = identity == nullptr ? nullptr : dynamic_cast(ecs::try_get(identity->self)); if (unit == nullptr) - return failure(DiagnosticCode::StaleHandle, - "RTS unit effects identity link is stale", "unit.identity.self"); + return failure(DiagnosticCode::StaleHandle, "RTS unit effects identity link is stale", + "unit.identity.self"); auto advanced = advanceEffects(effects->values, step); if (!advanced) return failureFrom(advanced.status()); processed += std::move(advanced).takeValue(); @@ -391,13 +342,10 @@ Result EffectSystem::step(const SimulationStep& step) { auto view = ecs::View(); for (auto it = view.begin(); it != view.end(); ++it) { auto [identity, effects] = *it; - Building* building = identity == nullptr - ? nullptr - : dynamic_cast(ecs::try_get(identity->self)); + Building* building = identity == nullptr ? nullptr : dynamic_cast(ecs::try_get(identity->self)); if (building == nullptr) - return failure(DiagnosticCode::StaleHandle, - "RTS building effects identity link is stale", - "building.identity.self"); + return failure(DiagnosticCode::StaleHandle, "RTS building effects identity link is stale", + "building.identity.self"); auto advanced = advanceEffects(effects->values, step); if (!advanced) return failureFrom(advanced.status()); processed += std::move(advanced).takeValue(); diff --git a/src/modules/rts/RTSSystems.h b/src/modules/rts/RTSSystems.h index c5c600bd..923dbb72 100644 --- a/src/modules/rts/RTSSystems.h +++ b/src/modules/rts/RTSSystems.h @@ -22,9 +22,9 @@ enum class FormationKind : std::uint8_t { /** @brief Input to the pure formation planner. */ struct FormationSpec { - FormationKind kind = FormationKind::Line; - float spacing = 32.0f; - int columns = 0; + FormationKind kind = FormationKind::Line; + float spacing = 32.0f; + int columns = 0; /** @brief Validate spacing and grid column constraints. */ [[nodiscard]] Result validate() const; @@ -32,13 +32,13 @@ struct FormationSpec { /** @brief Static metadata for one ECS system's access and phase contract. */ struct SystemContract { - const char* name = ""; - const char* view = ""; - const char* readSet = ""; - const char* writeSet = ""; + const char* name = ""; + const char* view = ""; + const char* readSet = ""; + const char* writeSet = ""; const char* structuralChanges = ""; - const char* events = ""; - const char* phase = ""; + const char* events = ""; + const char* phase = ""; }; /** @brief Return the phase-one RTS ECS contracts for tooling and review. */ @@ -59,14 +59,14 @@ public: * @param spec Layout strategy and spacing. * @return Exactly `count` positions or a structured validation failure. */ - [[nodiscard]] static Result> plan( - std::size_t count, WorldPosition anchor, const FormationSpec& spec); + [[nodiscard]] static Result> plan(std::size_t count, WorldPosition anchor, + const FormationSpec& spec); }; /** @brief Receipt containing all order ids accepted by command fan-out. */ struct FanOutReceipt { - std::size_t requested = 0; - std::size_t accepted = 0; + std::size_t requested = 0; + std::size_t accepted = 0; std::vector orderIds; }; @@ -87,9 +87,8 @@ public: * @return Accepted order ids in selection order, or a failure with no * partially accepted command exposed to the caller. */ - [[nodiscard]] static Result fanOut( - std::span unitHandles, const CommandSpec& command, - const FormationSpec& formation); + [[nodiscard]] static Result fanOut(std::span unitHandles, + const CommandSpec& command, const FormationSpec& formation); }; /** @brief Moves units toward their active generic order targets. */ @@ -112,8 +111,7 @@ public: * @param executor Borrowed action adapter; it must outlive this call. * @return Number of action-bearing units inspected, or a structured failure. */ - [[nodiscard]] static Result step(const SimulationStep& step, - IRTSActionExecutor& executor); + [[nodiscard]] static Result step(const SimulationStep& step, IRTSActionExecutor& executor); }; /** @brief Advances Building production queues without duplicating task state. */ diff --git a/src/modules/rts/RTSTypes.cpp b/src/modules/rts/RTSTypes.cpp index 2ced14b0..d72bac8c 100644 --- a/src/modules/rts/RTSTypes.cpp +++ b/src/modules/rts/RTSTypes.cpp @@ -27,9 +27,7 @@ Result failureFrom(const Status& status) { return Result::failure(status); } -bool finitePosition(WorldPosition position) { - return std::isfinite(position.x) && std::isfinite(position.y); -} +bool finitePosition(WorldPosition position) { return std::isfinite(position.x) && std::isfinite(position.y); } bool handleEqual(const ecs::EntityHandle& left, const ecs::EntityHandle& right) { return left.table == right.table && left.type == right.type && left.id == right.id && @@ -47,25 +45,25 @@ std::optional parseOrderKind(std::string_view kind) { Result projectOrder(const orders::Order& order) { const auto kind = parseOrderKind(order.kind); if (!kind) { - return failure(DiagnosticCode::InvariantViolation, - "generic order contains an unknown RTS kind", "order.kind"); + return failure(DiagnosticCode::InvariantViolation, "generic order contains an unknown RTS kind", + "order.kind"); } const std::string json = order.payload.toJson(); - std::string errorText; - auto document = json::Document::parse(json, &errorText); + std::string errorText; + auto document = json::Document::parse(json, &errorText); if (!document.valid() || !document.root().isObject()) { - return failure(DiagnosticCode::ParseError, - "generic order payload is not a JSON object", "order.payload"); + return failure(DiagnosticCode::ParseError, "generic order payload is not a JSON object", + "order.payload"); } - const auto root = document.root(); + const auto root = document.root(); OrderRecord result; - result.id = order.id; - result.kind = *kind; - result.target.x = root.get("x").asFloat(0.0f); - result.target.y = root.get("y").asFloat(0.0f); - result.definitionId = root.get("definition").asString(); + result.id = order.id; + result.kind = *kind; + result.target.x = root.get("x").asFloat(0.0f); + result.target.y = root.get("y").asFloat(0.0f); + result.definitionId = root.get("definition").asString(); result.formationSlot = root.get("formationSlot").asInt(-1); if (!finitePosition(result.target)) { return failure(DiagnosticCode::InvariantViolation, @@ -126,51 +124,45 @@ void touchFactionComponents(Faction& faction) { const char* orderKindName(OrderKind kind) noexcept { switch (kind) { - case OrderKind::Move: return "move"; - case OrderKind::Attack: return "attack"; - case OrderKind::Build: return "build"; - case OrderKind::Gather: return "gather"; + case OrderKind::Move: return "move"; + case OrderKind::Attack: return "attack"; + case OrderKind::Build: return "build"; + case OrderKind::Gather: return "gather"; } return "unknown"; } Result CommandSpec::validate() const { if (!finitePosition(target)) { - return failure(DiagnosticCode::InvalidArgument, - "RTS command target must contain finite coordinates", "target"); + return failure(DiagnosticCode::InvalidArgument, "RTS command target must contain finite coordinates", "target"); } if (!std::isfinite(timeoutSeconds) || timeoutSeconds < 0.0) { - return failure(DiagnosticCode::InvalidArgument, - "RTS command timeout must be finite and non-negative", "timeoutSeconds"); + return failure(DiagnosticCode::InvalidArgument, "RTS command timeout must be finite and non-negative", + "timeoutSeconds"); } switch (kind) { - case OrderKind::Move: - case OrderKind::Attack: - case OrderKind::Build: - case OrderKind::Gather: - return Result::success(Status::success(StatusCode::Applied)); + case OrderKind::Move: + case OrderKind::Attack: + case OrderKind::Build: + case OrderKind::Gather: return Result::success(Status::success(StatusCode::Applied)); } return failure(DiagnosticCode::InvalidArgument, "RTS command kind is invalid", "kind"); } Result TagSet::add(std::string_view tag) { - if (tag.empty()) - return failure(DiagnosticCode::InvalidArgument, "RTS tag must not be empty", "tag"); + if (tag.empty()) return failure(DiagnosticCode::InvalidArgument, "RTS tag must not be empty", "tag"); auto it = std::lower_bound(values_.begin(), values_.end(), tag, [](const std::string& value, std::string_view key) { return value < key; }); - if (it != values_.end() && *it == tag) - return Result::success(Status::success(StatusCode::NoOp)); + if (it != values_.end() && *it == tag) return Result::success(Status::success(StatusCode::NoOp)); values_.insert(it, std::string(tag)); return Result::success(Status::success(StatusCode::Applied)); } Result TagSet::remove(std::string_view tag) { - if (tag.empty()) - return failure(DiagnosticCode::InvalidArgument, "RTS tag must not be empty", "tag"); + if (tag.empty()) return failure(DiagnosticCode::InvalidArgument, "RTS tag must not be empty", "tag"); auto it = std::lower_bound(values_.begin(), values_.end(), tag, [](const std::string& value, std::string_view key) { return value < key; }); - if (it == values_.end() || *it != tag) - return failure(DiagnosticCode::NotFound, "RTS tag is not present", "tag"); + if (it == values_.end() || *it != tag) return failure(DiagnosticCode::NotFound, "RTS tag is not present", "tag"); values_.erase(it); return Result::success(Status::success(StatusCode::Applied)); } @@ -194,13 +186,13 @@ AttributeComponent& AttributeComponent::operator=(const AttributeComponent& othe impl_ = other.impl_ ? std::make_unique(*other.impl_) : nullptr; return *this; } -AttributeComponent::AttributeComponent(AttributeComponent&& other) noexcept = default; +AttributeComponent::AttributeComponent(AttributeComponent&& other) noexcept = default; AttributeComponent& AttributeComponent::operator=(AttributeComponent&& other) noexcept = default; Result AttributeComponent::setBase(std::string_view attribute, double value) { if (attribute.empty() || !std::isfinite(value)) { - return failure(DiagnosticCode::InvalidArgument, - "RTS attribute base requires a non-empty finite value", "attribute"); + return failure(DiagnosticCode::InvalidArgument, "RTS attribute base requires a non-empty finite value", + "attribute"); } if (!impl_) impl_ = std::make_unique(); return impl_->values.setBase(attribute, value); @@ -208,8 +200,8 @@ Result AttributeComponent::setBase(std::string_view attribute, double valu Result AttributeComponent::modifyBase(std::string_view attribute, double delta) { if (attribute.empty() || !std::isfinite(delta)) { - return failure(DiagnosticCode::InvalidArgument, - "RTS attribute delta requires a non-empty finite value", "attribute"); + return failure(DiagnosticCode::InvalidArgument, "RTS attribute delta requires a non-empty finite value", + "attribute"); } if (!impl_) impl_ = std::make_unique(); return impl_->values.modifyBase(attribute, delta); @@ -241,22 +233,16 @@ Result AttributeComponent::bindOwner(ecs::EntityHandle owner) { return impl_->values.bindOwner(owner); } -bool AttributeComponent::initialized() const noexcept { - return impl_ != nullptr && impl_->values.initialized(); -} +bool AttributeComponent::initialized() const noexcept { return impl_ != nullptr && impl_->values.initialized(); } -Result AttributeComponent::initialize( - std::span values) { +Result AttributeComponent::initialize(std::span values) { if (!impl_) impl_ = std::make_unique(); return impl_->values.initialize(values); } -bool AttributeComponent::isStale() const noexcept { - return impl_ != nullptr && impl_->values.isStale(); -} +bool AttributeComponent::isStale() const noexcept { return impl_ != nullptr && impl_->values.isStale(); } -Result AttributeComponent::addModifier( - eve::attributes::AttributeModifier modifier) { +Result AttributeComponent::addModifier(eve::attributes::AttributeModifier modifier) { if (!impl_) impl_ = std::make_unique(); return impl_->values.addModifier(std::move(modifier)); } @@ -264,15 +250,13 @@ Result AttributeComponent::addModifier( Result AttributeComponent::snapshot( std::span attributes) const { if (!impl_) - return Result::failure( - Diagnostic::error(DiagnosticCode::InvariantViolation, - "RTS attribute component is not initialized", "attributes")); + return Result::failure(Diagnostic::error( + DiagnosticCode::InvariantViolation, "RTS attribute component is not initialized", "attributes")); return impl_->values.snapshot(attributes); } -Result AttributeComponent::restore( - const eve::attributes::AttributeProjectionSnapshot& snapshot, - Revision expectedRevision) { +Result AttributeComponent::restore(const eve::attributes::AttributeProjectionSnapshot& snapshot, + Revision expectedRevision) { if (!impl_) impl_ = std::make_unique(); return impl_->values.restore(snapshot, expectedRevision); } @@ -290,18 +274,17 @@ OrderComponent& OrderComponent::operator=(const OrderComponent& other) { impl_ = other.impl_ ? std::make_unique(*other.impl_) : nullptr; return *this; } -OrderComponent::OrderComponent(OrderComponent&& other) noexcept = default; +OrderComponent::OrderComponent(OrderComponent&& other) noexcept = default; OrderComponent& OrderComponent::operator=(OrderComponent&& other) noexcept = default; Result OrderComponent::enqueue(const CommandSpec& command, int formationSlot) { auto valid = command.validate(); if (!valid) return failureFrom(valid.status()); if (!impl_) impl_ = std::make_unique(); - auto queued = impl_->queue.append(orderKindName(command.kind), command.priority, - command.timeoutSeconds); + auto queued = impl_->queue.append(orderKindName(command.kind), command.priority, command.timeoutSeconds); if (!queued) return failureFrom(queued.status()); - const std::string id = std::move(queued).takeValue(); - auto order = impl_->queue.find(id); + const std::string id = std::move(queued).takeValue(); + auto order = impl_->queue.find(id); if (!order) return failure(DiagnosticCode::InvariantViolation, "generic order queue lost its newly appended command", "order"); @@ -316,11 +299,10 @@ Result OrderComponent::replace(const CommandSpec& command, int form auto valid = command.validate(); if (!valid) return failureFrom(valid.status()); if (!impl_) impl_ = std::make_unique(); - auto queued = impl_->queue.replace(orderKindName(command.kind), command.priority, - command.timeoutSeconds); + auto queued = impl_->queue.replace(orderKindName(command.kind), command.priority, command.timeoutSeconds); if (!queued) return failureFrom(queued.status()); - const std::string id = std::move(queued).takeValue(); - auto order = impl_->queue.find(id); + const std::string id = std::move(queued).takeValue(); + auto order = impl_->queue.find(id); if (!order) return failure(DiagnosticCode::InvariantViolation, "generic order queue lost its replacement command", "order"); @@ -338,8 +320,7 @@ Result OrderComponent::current() const { } Result OrderComponent::complete(std::string_view orderId) { - if (orderId.empty()) - return failure(DiagnosticCode::InvalidArgument, "RTS order id must not be empty", "orderId"); + if (orderId.empty()) return failure(DiagnosticCode::InvalidArgument, "RTS order id must not be empty", "orderId"); if (!impl_ || !impl_->queue.find(std::string(orderId))) return failure(DiagnosticCode::NotFound, "RTS order id is not present", "orderId"); if (!impl_->queue.complete(std::string(orderId)).ok()) @@ -349,8 +330,7 @@ Result OrderComponent::complete(std::string_view orderId) { Result OrderComponent::fail(std::string_view orderId, std::string_view reason) { if (orderId.empty() || reason.empty()) - return failure(DiagnosticCode::InvalidArgument, - "RTS order failure requires an id and reason", "order"); + return failure(DiagnosticCode::InvalidArgument, "RTS order failure requires an id and reason", "order"); if (!impl_ || !impl_->queue.find(std::string(orderId))) return failure(DiagnosticCode::NotFound, "RTS order id is not present", "orderId"); if (!impl_->queue.fail(std::string(orderId), std::string(reason)).ok()) @@ -360,8 +340,7 @@ Result OrderComponent::fail(std::string_view orderId, std::string_view rea Result OrderComponent::cancel(std::string_view orderId, std::string_view reason) { if (orderId.empty() || reason.empty()) - return failure(DiagnosticCode::InvalidArgument, - "RTS order cancellation requires an id and reason", "order"); + return failure(DiagnosticCode::InvalidArgument, "RTS order cancellation requires an id and reason", "order"); if (!impl_ || !impl_->queue.find(std::string(orderId))) return failure(DiagnosticCode::NotFound, "RTS order id is not present", "orderId"); if (!impl_->queue.cancel(std::string(orderId), std::string(reason)).ok()) @@ -369,15 +348,13 @@ Result OrderComponent::cancel(std::string_view orderId, std::string_view r return Result::success(Status::success(StatusCode::Applied)); } -bool OrderComponent::empty() const noexcept { - return !impl_ || !impl_->queue.current(); -} +bool OrderComponent::empty() const noexcept { return !impl_ || !impl_->queue.current(); } std::size_t OrderComponent::orderCount() const noexcept { return impl_ ? static_cast(impl_->queue.orderCount()) : 0u; } -orders::CommandQueue *OrderComponent::queueForComposition() noexcept { +orders::CommandQueue* OrderComponent::queueForComposition() noexcept { if (!impl_) impl_ = std::make_unique(); return &impl_->queue; } @@ -407,18 +384,16 @@ ProductionComponent& ProductionComponent::operator=(const ProductionComponent& o impl_ = other.impl_ ? std::make_unique(*other.impl_) : nullptr; return *this; } -ProductionComponent::ProductionComponent(ProductionComponent&& other) noexcept = default; +ProductionComponent::ProductionComponent(ProductionComponent&& other) noexcept = default; ProductionComponent& ProductionComponent::operator=(ProductionComponent&& other) noexcept = default; Result ProductionComponent::enqueue(std::string_view owner, std::string_view kind, - std::string_view product, Duration duration, - int priority) { + std::string_view product, Duration duration, int priority) { if (owner.empty() || kind.empty() || product.empty() || duration.nanoseconds() <= 0) return failure(DiagnosticCode::InvalidArgument, "RTS production requires non-empty ids and a positive duration", "production"); if (!impl_) impl_ = std::make_unique(); - return impl_->queue.enqueue(owner, kind, product, eve::Value(eve::Value::Object{}), - duration.seconds(), priority); + return impl_->queue.enqueue(owner, kind, product, eve::Value(eve::Value::Object{}), duration.seconds(), priority); } Result ProductionComponent::advance(const SimulationStep& step) { @@ -430,40 +405,40 @@ std::size_t ProductionComponent::taskCount() const noexcept { return impl_ ? static_cast(impl_->queue.taskCount()) : 0u; } -production::WorkQueue *ProductionComponent::queueForComposition() noexcept { +production::WorkQueue* ProductionComponent::queueForComposition() noexcept { if (!impl_) impl_ = std::make_unique(); return &impl_->queue; } Unit* Unit::createUnit(SubjectRef subject, LogicalId definition) { - Unit* unit = Unit::create(); - unit->identity()->self = ecs::handle_of(unit); + Unit* unit = Unit::create(); + unit->identity()->self = ecs::handle_of(unit); unit->identity()->subject = subject; - unit->definition()->id = std::move(definition); + unit->definition()->id = std::move(definition); touchUnitComponents(*unit); return unit; } Building* Building::createBuilding(SubjectRef subject, LogicalId definition) { - Building* building = Building::create(); - building->identity()->self = ecs::handle_of(building); + Building* building = Building::create(); + building->identity()->self = ecs::handle_of(building); building->identity()->subject = subject; - building->definition()->id = std::move(definition); + building->definition()->id = std::move(definition); touchBuildingComponents(*building); return building; } Player* Player::createPlayer(SubjectRef subject) { - Player* player = Player::create(); - player->identity()->self = ecs::handle_of(player); + Player* player = Player::create(); + player->identity()->self = ecs::handle_of(player); player->identity()->subject = subject; touchPlayerComponents(*player); return player; } Faction* Faction::createFaction(SubjectRef subject) { - Faction* faction = Faction::create(); - faction->identity()->self = ecs::handle_of(faction); + Faction* faction = Faction::create(); + faction->identity()->self = ecs::handle_of(faction); faction->identity()->subject = subject; touchFactionComponents(*faction); return faction; diff --git a/src/modules/rts/RTSTypes.h b/src/modules/rts/RTSTypes.h index 4d957e26..c64f6220 100644 --- a/src/modules/rts/RTSTypes.h +++ b/src/modules/rts/RTSTypes.h @@ -10,12 +10,12 @@ * cross-domain relationships are represented by typed links. */ +#include "attributes/AttributeProjection.h" #include "common/ECS.h" #include "common/Identity.h" #include "common/Result.h" #include "common/SubjectRef.h" #include "common/Time.h" -#include "attributes/AttributeProjection.h" #include "rts/RTSEffects.h" #include @@ -70,11 +70,11 @@ enum class OrderKind : std::uint8_t { * and is intentionally copied into the order payload. */ struct CommandSpec { - OrderKind kind = OrderKind::Move; + OrderKind kind = OrderKind::Move; WorldPosition target; - std::string definitionId; - int priority = 0; - double timeoutSeconds = 0.0; + std::string definitionId; + int priority = 0; + double timeoutSeconds = 0.0; /** @brief Validate coordinates and command metadata without mutating state. */ [[nodiscard]] Result validate() const; @@ -82,11 +82,11 @@ struct CommandSpec { /** @brief Owning projection of the active generic order used by RTS systems. */ struct OrderRecord { - std::string id; - OrderKind kind = OrderKind::Move; + std::string id; + OrderKind kind = OrderKind::Move; WorldPosition target; - std::string definitionId; - int formationSlot = -1; + std::string definitionId; + int formationSlot = -1; }; /** @brief Entity-local sorted tags; this is the RTS entity's tag authority. */ @@ -122,12 +122,12 @@ public: /** @brief Bind a live ECS handle, rejecting a null or stale handle. */ [[nodiscard]] static Result bind(ecs::EntityHandle handle) { if (ecs::try_get(handle) == nullptr) { - return Result::failure(Diagnostic::error( - DiagnosticCode::StaleHandle, "RTS entity link target is not live", "link.handle")); + return Result::failure( + Diagnostic::error(DiagnosticCode::StaleHandle, "RTS entity link target is not live", "link.handle")); } EntityLink result; result.handle_ = handle; - result.bound_ = true; + result.bound_ = true; return Result::success(std::move(result)); } @@ -138,31 +138,27 @@ public: [[nodiscard]] static EntityLink fromHandleForRestore(ecs::EntityHandle handle) noexcept { EntityLink result; result.handle_ = handle; - result.bound_ = true; + result.bound_ = true; return result; } /** @brief Whether a handle has been assigned, including a stale one. */ [[nodiscard]] bool isBound() const noexcept { return bound_; } /** @brief Whether the assigned target no longer resolves at its generation. */ - [[nodiscard]] bool isStale() const noexcept { - return bound_ && ecs::try_get(handle_) == nullptr; - } + [[nodiscard]] bool isStale() const noexcept { return bound_ && ecs::try_get(handle_) == nullptr; } /** @brief Resolve the target for this call, or return null when stale. */ - [[nodiscard]] ecs::Entity* resolve() const noexcept { - return bound_ ? ecs::try_get(handle_) : nullptr; - } + [[nodiscard]] ecs::Entity* resolve() const noexcept { return bound_ ? ecs::try_get(handle_) : nullptr; } /** @brief Return the local runtime handle retained by this link. */ [[nodiscard]] const ecs::EntityHandle& handle() const noexcept { return handle_; } /** @brief Clear the relationship after source-side destruction. */ void reset() noexcept { - bound_ = false; + bound_ = false; handle_ = {}; } private: ecs::EntityHandle handle_{}; - bool bound_ = false; + bool bound_ = false; }; /** @brief Typed link tag for a provider-owned capability or registry entry. */ @@ -212,7 +208,7 @@ private: struct FactionLinkTag {}; struct WeaponLinkTag {}; using FactionLink = EntityLink; -using WeaponLink = EntityLink; +using WeaponLink = EntityLink; struct SensingLinkTag {}; struct SteeringLinkTag {}; @@ -224,16 +220,16 @@ struct EconomyLinkTag {}; struct AuthorityLinkTag {}; struct SocialLinkTag {}; struct GameEventLinkTag {}; -using SensingLink = ServiceLink; -using SteeringLink = ServiceLink; -using CrowdLink = ServiceLink; -using ActionLink = ServiceLink; +using SensingLink = ServiceLink; +using SteeringLink = ServiceLink; +using CrowdLink = ServiceLink; +using ActionLink = ServiceLink; using SettlementLink = ServiceLink; -using PlacementLink = ServiceLink; -using EconomyLink = ServiceLink; -using AuthorityLink = ServiceLink; -using SocialLink = ServiceLink; -using GameEventLink = ServiceLink; +using PlacementLink = ServiceLink; +using EconomyLink = ServiceLink; +using AuthorityLink = ServiceLink; +using SocialLink = ServiceLink; +using GameEventLink = ServiceLink; /** @brief Canonical attributes component adapter, backed by attributes::AttributeSet. */ class AttributeComponent { @@ -274,19 +270,17 @@ public: /** @brief Return whether initial unit stat bases have been installed. */ [[nodiscard]] bool initialized() const noexcept; /** @brief Seed unit stat bases once without replacing existing values. */ - [[nodiscard]] Result initialize( - std::span values); + [[nodiscard]] Result initialize(std::span values); /** @brief Return whether the canonical attribute owner is stale. */ [[nodiscard]] bool isStale() const noexcept; /** @brief Add a modifier with the common source/priority/sequence contract. */ - [[nodiscard]] Result addModifier( - eve::attributes::AttributeModifier modifier); + [[nodiscard]] Result addModifier(eve::attributes::AttributeModifier modifier); /** @brief Capture selected values with owner and revision metadata. */ [[nodiscard]] Result snapshot( std::span attributes) const; /** @brief Restore selected values after generation and revision checks. */ - [[nodiscard]] Result restore( - const eve::attributes::AttributeProjectionSnapshot& snapshot, Revision expectedRevision); + [[nodiscard]] Result restore(const eve::attributes::AttributeProjectionSnapshot& snapshot, + Revision expectedRevision); private: struct Impl; @@ -339,9 +333,8 @@ public: ProductionComponent& operator=(ProductionComponent&& other) noexcept; /** @brief Enqueue a production task with an injected simulation duration. */ - [[nodiscard]] Result enqueue(std::string_view owner, std::string_view kind, - std::string_view product, Duration duration, - int priority = 0); + [[nodiscard]] Result enqueue(std::string_view owner, std::string_view kind, std::string_view product, + Duration duration, int priority = 0); /** @brief Advance the queue using a caller-owned deterministic step. */ [[nodiscard]] Result advance(const SimulationStep& step); /** @brief Return the number of retained tasks. */ @@ -371,8 +364,8 @@ public: /** @brief Runtime identity and logical unit definition. */ struct Identity { ecs::EntityHandle self{}; - SubjectRef subject; - std::string displayName; + SubjectRef subject; + std::string displayName; }; /** @brief Logical unit definition identity. */ struct Definition { @@ -424,11 +417,11 @@ public: }; /** @brief Authoritative kinematic state used by the phase-one move slice. */ struct Motion { - float x = 0.0f; - float y = 0.0f; - float speed = 1.0f; + float x = 0.0f; + float y = 0.0f; + float speed = 1.0f; float arrivalRadius = 0.01f; - bool arrived = true; + bool arrived = true; }; COMPONENT(Identity, identity) @@ -454,7 +447,8 @@ public: * @ownership The current ECS world owns the entity; callers must release it through ECS and must not delete it. * @lifetime Valid until ECS destruction of the entity or its world; retain an EntityHandle across frames instead. * @thread Call on the owning ECS/simulation thread. - * @reentrancy Creation does not invoke user callbacks; do not re-enter ECS mutation while using the returned pointer. + * @reentrancy Creation does not invoke user callbacks; do not re-enter ECS mutation while using the returned + * pointer. */ [[nodiscard]] static Unit* createUnit(SubjectRef subject = {}, LogicalId definition = {}); }; @@ -470,8 +464,8 @@ public: /** @brief Runtime identity. */ struct Identity { ecs::EntityHandle self{}; - SubjectRef subject; - std::string displayName; + SubjectRef subject; + std::string displayName; }; /** @brief Logical building definition identity. */ struct Definition { @@ -480,10 +474,10 @@ public: /** @brief Placement world key and authoritative cell state. */ struct Placement { PlacementLink link; - int cellX = 0; - int cellY = 0; - float rotation = 0.0f; - bool placed = false; + int cellX = 0; + int cellY = 0; + float rotation = 0.0f; + bool placed = false; }; /** @brief Canonical production queue. */ struct Production { @@ -533,7 +527,8 @@ public: * @ownership The current ECS world owns the entity; callers must release it through ECS and must not delete it. * @lifetime Valid until ECS destruction of the entity or its world; retain an EntityHandle across frames instead. * @thread Call on the owning ECS/simulation thread. - * @reentrancy Creation does not invoke user callbacks; do not re-enter ECS mutation while using the returned pointer. + * @reentrancy Creation does not invoke user callbacks; do not re-enter ECS mutation while using the returned + * pointer. */ [[nodiscard]] static Building* createBuilding(SubjectRef subject = {}, LogicalId definition = {}); }; @@ -549,8 +544,8 @@ public: /** @brief Runtime identity. */ struct Identity { ecs::EntityHandle self{}; - SubjectRef subject; - std::string displayName; + SubjectRef subject; + std::string displayName; }; /** @brief Authority provider link. */ struct Authority { @@ -587,7 +582,8 @@ public: * @ownership The current ECS world owns the entity; callers must release it through ECS and must not delete it. * @lifetime Valid until ECS destruction of the entity or its world; retain an EntityHandle across frames instead. * @thread Call on the owning ECS/simulation thread. - * @reentrancy Creation does not invoke user callbacks; do not re-enter ECS mutation while using the returned pointer. + * @reentrancy Creation does not invoke user callbacks; do not re-enter ECS mutation while using the returned + * pointer. */ [[nodiscard]] static Player* createPlayer(SubjectRef subject = {}); }; @@ -603,8 +599,8 @@ public: /** @brief Runtime identity. */ struct Identity { ecs::EntityHandle self{}; - SubjectRef subject; - std::string displayName; + SubjectRef subject; + std::string displayName; }; /** @brief Authority provider link. */ struct Authority { @@ -641,7 +637,8 @@ public: * @ownership The current ECS world owns the entity; callers must release it through ECS and must not delete it. * @lifetime Valid until ECS destruction of the entity or its world; retain an EntityHandle across frames instead. * @thread Call on the owning ECS/simulation thread. - * @reentrancy Creation does not invoke user callbacks; do not re-enter ECS mutation while using the returned pointer. + * @reentrancy Creation does not invoke user callbacks; do not re-enter ECS mutation while using the returned + * pointer. */ [[nodiscard]] static Faction* createFaction(SubjectRef subject = {}); }; diff --git a/src/modules/scene/ArtifactProvider.cpp b/src/modules/scene/ArtifactProvider.cpp index c6462daf..a0e7d0bd 100644 --- a/src/modules/scene/ArtifactProvider.cpp +++ b/src/modules/scene/ArtifactProvider.cpp @@ -7,8 +7,8 @@ #include #include #include -#include #include +#include #include #include @@ -25,11 +25,9 @@ eve::Result failureValue(eve::DiagnosticCode code, std::string messa } bool validBounds(const eve::artifact::Bounds& bounds) noexcept { - return bounds.valid && std::isfinite(bounds.minX) && std::isfinite(bounds.minY) && - std::isfinite(bounds.minZ) && std::isfinite(bounds.maxX) && - std::isfinite(bounds.maxY) && std::isfinite(bounds.maxZ) && - bounds.minX <= bounds.maxX && bounds.minY <= bounds.maxY && - bounds.minZ <= bounds.maxZ; + return bounds.valid && std::isfinite(bounds.minX) && std::isfinite(bounds.minY) && std::isfinite(bounds.minZ) && + std::isfinite(bounds.maxX) && std::isfinite(bounds.maxY) && std::isfinite(bounds.maxZ) && + bounds.minX <= bounds.maxX && bounds.minY <= bounds.maxY && bounds.minZ <= bounds.maxZ; } const eve::Value* member(const eve::Value::Object& object, const char* name) noexcept { @@ -39,7 +37,7 @@ const eve::Value* member(const eve::Value::Object& object, const char* name) noe bool readString(const eve::Value::Object& object, const char* name, std::string& output) { const auto* value = member(object, name); - const auto* text = value ? value->getIf() : nullptr; + const auto* text = value ? value->getIf() : nullptr; if (!text || text->empty()) return false; output = *text; return true; @@ -75,11 +73,10 @@ bool readFloat(const eve::Value::Object& object, const char* name, float& output bool readBounds(const eve::Value::Object& object, eve::artifact::Bounds& bounds) { const auto* encoded = member(object, "bounds"); - const auto* value = encoded ? encoded->getIf() : nullptr; - return value && readFloat(*value, "minX", bounds.minX) && - readFloat(*value, "minY", bounds.minY) && readFloat(*value, "minZ", bounds.minZ) && - readFloat(*value, "maxX", bounds.maxX) && readFloat(*value, "maxY", bounds.maxY) && - readFloat(*value, "maxZ", bounds.maxZ) && + const auto* value = encoded ? encoded->getIf() : nullptr; + return value && readFloat(*value, "minX", bounds.minX) && readFloat(*value, "minY", bounds.minY) && + readFloat(*value, "minZ", bounds.minZ) && readFloat(*value, "maxX", bounds.maxX) && + readFloat(*value, "maxY", bounds.maxY) && readFloat(*value, "maxZ", bounds.maxZ) && (bounds.valid = true, validBounds(bounds)); } @@ -106,44 +103,39 @@ public: } void rollback() noexcept override { - owner_ = nullptr; + owner_ = nullptr; record_ = {}; } private: SceneArtifactProvider* owner_ = nullptr; - SceneArtifactRecord record_; + SceneArtifactRecord record_; }; eve::Result> SceneArtifactProvider::prepare( const eve::artifact::PublicationView& publication) { if (failPrepare_) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Failed, - "scene artifact prepare failure was injected")); + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "scene artifact prepare failure was injected")); if (publication.id.isNil() || publication.buildKey.empty() || publication.parts.empty()) - return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, - "scene publication requires identity, build key and parts")); + return eve::Result>::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvalidArgument, "scene publication requires identity, build key and parts")); if (find(publication.id) != nullptr) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "scene artifact identity is already published")); + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "scene artifact identity is already published")); if (nextIndex_ == SceneArtifactHandle::invalidIndex) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Failed, - "scene artifact handle index exhausted")); + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "scene artifact handle index exhausted")); SceneArtifactRecord record; - record.id = publication.id; + record.id = publication.id; record.buildKey = std::string(publication.buildKey); - record.handle = SceneArtifactHandle(nextIndex_, 1); + record.handle = SceneArtifactHandle(nextIndex_, 1); record.nodes.reserve(publication.parts.size()); for (const auto& part : publication.parts) { if (part.id.isNil() || part.role.empty() || !validBounds(part.bounds)) - return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, - "scene artifact part has invalid identity, role or bounds")); + return eve::Result>::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvalidArgument, "scene artifact part has invalid identity, role or bounds")); record.nodes.push_back({std::string(part.role), part.bounds}); } try { @@ -152,8 +144,7 @@ eve::Result> SceneArtifactPr std::make_unique(*this, std::move(record))); } catch (...) { return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Failed, - "scene artifact prepare allocation failed")); + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "scene artifact prepare allocation failed")); } } @@ -163,14 +154,14 @@ void SceneArtifactProvider::commit(SceneArtifactRecord record) noexcept { } const SceneArtifactRecord* SceneArtifactProvider::find(eve::PersistentId id) const noexcept { - const auto found = std::find_if(records_.begin(), records_.end(), - [id](const auto& record) { return record.id == id; }); + const auto found = + std::find_if(records_.begin(), records_.end(), [id](const auto& record) { return record.id == id; }); return found == records_.end() ? nullptr : &*found; } void SceneArtifactProvider::clear() noexcept { records_.clear(); - nextIndex_ = 0; + nextIndex_ = 0; failPrepare_ = false; } @@ -206,65 +197,62 @@ eve::Result SceneArtifactProvider::snapshotState() const { eve::Result SceneArtifactProvider::restoreState(const eve::Value& state) { if (!records_.empty()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, "scene artifact restore requires an empty registry")); - const auto* object = state.getIf(); - const auto* provider = object ? member(*object, "provider") : nullptr; + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "scene artifact restore requires an empty registry")); + const auto* object = state.getIf(); + const auto* provider = object ? member(*object, "provider") : nullptr; const auto* providerName = provider ? provider->getIf() : nullptr; - const auto* records = object ? member(*object, "records") : nullptr; - const auto* array = records ? records->getIf() : nullptr; - if (!object || !providerName || *providerName != "scene.cpu-registry" || - !hasSupportedVersion(*object) || !array) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "invalid scene artifact provider state")); + const auto* records = object ? member(*object, "records") : nullptr; + const auto* array = records ? records->getIf() : nullptr; + if (!object || !providerName || *providerName != "scene.cpu-registry" || !hasSupportedVersion(*object) || !array) + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "invalid scene artifact provider state")); - std::vector candidate; + std::vector candidate; std::unordered_set identities; - std::uint32_t next = 0; + std::uint32_t next = 0; try { candidate.reserve(array->size()); for (const eve::Value& encoded : *array) { - const auto* item = encoded.getIf(); - std::string idText; - std::string buildKey; + const auto* item = encoded.getIf(); + std::string idText; + std::string buildKey; std::uint64_t packed = 0; - if (!item || !readString(*item, "artifactId", idText) || - !readString(*item, "buildKey", buildKey) || + if (!item || !readString(*item, "artifactId", idText) || !readString(*item, "buildKey", buildKey) || !readUint64String(*item, "handle", packed)) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "invalid scene artifact record")); - const auto parsedId = eve::PersistentId::parse(idText); - const auto handle = SceneArtifactHandle::fromPacked(packed); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "invalid scene artifact record")); + const auto parsedId = eve::PersistentId::parse(idText); + const auto handle = SceneArtifactHandle::fromPacked(packed); const auto* encodedNodes = member(*item, "nodes"); - const auto* nodes = encodedNodes ? encodedNodes->getIf() : nullptr; + const auto* nodes = encodedNodes ? encodedNodes->getIf() : nullptr; if (!parsedId || parsedId->isNil() || handle.isInvalid() || !nodes || !identities.emplace(*parsedId).second || handle.index() == SceneArtifactHandle::invalidIndex) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "invalid scene artifact identity or handle")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::ParseError, + "invalid scene artifact identity or handle")); SceneArtifactRecord record{*parsedId, std::move(buildKey), handle, {}}; record.nodes.reserve(nodes->size()); for (const eve::Value& encodedNode : *nodes) { const auto* node = encodedNode.getIf(); std::string role; const auto* boundsEncoded = node ? member(*node, "bounds") : nullptr; - const auto* boundsObject = boundsEncoded ? boundsEncoded->getIf() : nullptr; + const auto* boundsObject = boundsEncoded ? boundsEncoded->getIf() : nullptr; if (!node || !readString(*node, "role", role) || !boundsObject) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "invalid scene artifact node")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "invalid scene artifact node")); eve::artifact::Bounds bounds; if (!readBounds(*node, bounds)) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "invalid scene artifact node bounds")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "invalid scene artifact node bounds")); record.nodes.push_back({std::move(role), bounds}); } - next = std::max(next, handle.index() == std::numeric_limits::max() - ? handle.index() - : handle.index() + 1u); + next = std::max(next, handle.index() == std::numeric_limits::max() ? handle.index() + : handle.index() + 1u); candidate.push_back(std::move(record)); } } catch (...) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "scene artifact restore allocation failed")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "scene artifact restore allocation failed")); } records_.swap(candidate); nextIndex_ = next; diff --git a/src/modules/scene/ArtifactProvider.h b/src/modules/scene/ArtifactProvider.h index dae70900..5eb924d5 100644 --- a/src/modules/scene/ArtifactProvider.h +++ b/src/modules/scene/ArtifactProvider.h @@ -22,15 +22,15 @@ using SceneArtifactHandle = eve::RuntimeHandle; /** @brief Backend-neutral node observable emitted for one artifact part. */ struct SceneArtifactNode { - std::string role; + std::string role; eve::artifact::Bounds bounds; }; /** @brief Queryable scene registry record for one published artifact. */ struct SceneArtifactRecord { - eve::PersistentId id; - std::string buildKey; - SceneArtifactHandle handle; + eve::PersistentId id; + std::string buildKey; + SceneArtifactHandle handle; std::vector nodes; }; @@ -73,10 +73,10 @@ public: private: friend class SceneArtifactStage; - void commit(SceneArtifactRecord record) noexcept; + void commit(SceneArtifactRecord record) noexcept; std::vector records_; - std::uint32_t nextIndex_ = 0; - bool failPrepare_ = false; + std::uint32_t nextIndex_ = 0; + bool failPrepare_ = false; }; /** @brief Return the process-owned scene artifact provider singleton. */ diff --git a/src/modules/scene/Scene.cpp b/src/modules/scene/Scene.cpp index 9e97c6a3..2cae3629 100644 --- a/src/modules/scene/Scene.cpp +++ b/src/modules/scene/Scene.cpp @@ -40,8 +40,7 @@ namespace { template eve::Result sceneFailure(eve::DiagnosticCode code, std::string message) { - return eve::Result::failure( - eve::Diagnostic::error(code, std::move(message), "scene")); + return eve::Result::failure(eve::Diagnostic::error(code, std::move(message), "scene")); } template @@ -626,11 +625,11 @@ SceneHost *Scene::ensureSelected(const std::string &preferredName) { return selected_; } -eve::Result Scene::mountAs(const std::string &name, NodeDesc root) { +eve::Result Scene::mountAs(const std::string &name, NodeDesc root) { SceneHost *h = findHostByName(name); if (!h) { auto created = SceneHost::createHost(name); - if (!created.ok()) return eve::Result::failure(created.status()); + if (!created.ok()) return eve::Result::failure(created.status()); h = std::move(created).takeValue(); } try { @@ -638,22 +637,20 @@ eve::Result Scene::mountAs(const std::string &name, NodeDesc root) { TransformSystem::updateHost(h); pruneOrphanObjects(); } catch (const std::exception &error) { - return sceneFailure(eve::DiagnosticCode::PreconditionViolation, + return sceneFailure(eve::DiagnosticCode::PreconditionViolation, std::string("scene mount rejected: ") + error.what()); } selected_ = h; - return eve::Result::success(h, eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(h, eve::Status::success(eve::StatusCode::Applied)); } -eve::Result Scene::mount(NodeDesc root) { - return mountAs("default", std::move(root)); -} +eve::Result Scene::mount(NodeDesc root) { return mountAs("default", std::move(root)); } -eve::Result Scene::remount(NodeDesc root) { +eve::Result Scene::remount(NodeDesc root) { SceneHost *h = selected_ ? selected_ : findHostByName("default"); if (!h) { auto created = SceneHost::createHost("default"); - if (!created.ok()) return eve::Result::failure(created.status()); + if (!created.ok()) return eve::Result::failure(created.status()); h = std::move(created).takeValue(); } try { @@ -661,18 +658,18 @@ eve::Result Scene::remount(NodeDesc root) { TransformSystem::updateHost(h); pruneOrphanObjects(); } catch (const std::exception &error) { - return sceneFailure(eve::DiagnosticCode::PreconditionViolation, + return sceneFailure(eve::DiagnosticCode::PreconditionViolation, std::string("scene remount rejected: ") + error.what()); } selected_ = h; - return eve::Result::success(h, eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(h, eve::Status::success(eve::StatusCode::Applied)); } -eve::Result Scene::remountReconcile(NodeDesc root) { +eve::Result Scene::remountReconcile(NodeDesc root) { SceneHost *h = selected_ ? selected_ : findHostByName("default"); if (!h) { auto created = SceneHost::createHost("default"); - if (!created.ok()) return eve::Result::failure(created.status()); + if (!created.ok()) return eve::Result::failure(created.status()); h = std::move(created).takeValue(); } try { @@ -680,15 +677,15 @@ eve::Result Scene::remountReconcile(NodeDesc root) { TransformSystem::updateHost(h); pruneOrphanObjects(); selected_ = h; - return eve::Result::success( + return eve::Result::success( h, eve::Status::success(rebuilt ? eve::StatusCode::Applied : eve::StatusCode::NoOp)); } catch (const std::exception &error) { - return sceneFailure(eve::DiagnosticCode::PreconditionViolation, + return sceneFailure(eve::DiagnosticCode::PreconditionViolation, std::string("scene reconcile rejected: ") + error.what()); } } -eve::Result Scene::remountAs(const std::string &name, NodeDesc root) { +eve::Result Scene::remountAs(const std::string &name, NodeDesc root) { return mountAs(name, std::move(root)); } @@ -699,24 +696,20 @@ bool Scene::select(const std::string &name) { return true; } -eve::Result Scene::findHost(const std::string &name) const { +eve::Result Scene::findHost(const std::string &name) const { if (name.empty()) - return sceneFailure(eve::DiagnosticCode::InvalidArgument, - "scene host name must not be empty"); - if (SceneHost *host = findHostByName(name)) - return eve::Result::success(host, eve::Status::success()); - return sceneFailure(eve::DiagnosticCode::NotFound, - "scene host was not found: " + name); + return sceneFailure(eve::DiagnosticCode::InvalidArgument, "scene host name must not be empty"); + if (SceneHost *host = findHostByName(name)) return eve::Result::success(host, eve::Status::success()); + return sceneFailure(eve::DiagnosticCode::NotFound, "scene host was not found: " + name); } -eve::Result Scene::findHostByOwner(uint32_t ownerId) const { +eve::Result Scene::findHostByOwner(uint32_t ownerId) const { if (ownerId == 0) - return sceneFailure(eve::DiagnosticCode::InvalidArgument, - "scene host owner id must not be zero"); + return sceneFailure(eve::DiagnosticCode::InvalidArgument, "scene host owner id must not be zero"); if (SceneHost *host = findHostByOwnerId(ownerId)) - return eve::Result::success(host, eve::Status::success()); - return sceneFailure(eve::DiagnosticCode::NotFound, - "scene host owner id was not found: " + std::to_string(ownerId)); + return eve::Result::success(host, eve::Status::success()); + return sceneFailure(eve::DiagnosticCode::NotFound, + "scene host owner id was not found: " + std::to_string(ownerId)); } void Scene::bindOwner(uint32_t ownerId) { @@ -761,7 +754,7 @@ bool Scene::deserializeHostAt(const std::string &hostName, const std::string &js Poco::JSON::Object::Ptr tree = root->getObject("root"); if (!tree) return false; NodeDesc desc = nodeFromJson(tree); - auto mounted = mountAs(hostName.empty() ? "default" : hostName, std::move(desc)); + auto mounted = mountAs(hostName.empty() ? "default" : hostName, std::move(desc)); return mounted.ok(); } catch (...) { return false; @@ -922,9 +915,9 @@ SceneObject *Scene::ensureSceneObject(SceneHost *host, SceneNode *node, } if (!node->persistentId.isNil()) { if (SceneObject *o = findSceneObjectByPersistentId(node->persistentId)) { - node->objectId = uint32_t(o->id); + node->objectId = uint32_t(o->id); o->meta()->hostName = hostName; - o->meta()->nodeId = node->id; + o->meta()->nodeId = node->id; return o; } } @@ -1166,9 +1159,8 @@ void Scene::expose(ssq::Table &table) { std::function([]() -> SceneNodeRef * { return nullptr; }), true); refCls.addFunc("getNodeId", &SceneNodeRef::getNodeId); refCls.addFunc("getHostName", &SceneNodeRef::getHostName); - refCls.addFunc("getPersistentId", [](SceneNodeRef *ref) { - return ref ? ref->persistentId().format() : std::string{}; - }); + refCls.addFunc("getPersistentId", + [](SceneNodeRef *ref) { return ref ? ref->persistentId().format() : std::string{}; }); refCls.addFunc("isValid", &SceneNodeRef::isValid); refCls.addFunc("getScene", &SceneNodeRef::getScene); refCls.addFunc("setPosition", &SceneNodeRef::setPosition); diff --git a/src/modules/scene/Scene.h b/src/modules/scene/Scene.h index cc97b2c4..3ad843e0 100644 --- a/src/modules/scene/Scene.h +++ b/src/modules/scene/Scene.h @@ -58,27 +58,27 @@ public: * @remarks The returned pointer remains valid until that host is destroyed; * nodes are invalidated by a subsequent tree replacement/reconcile. */ - [[nodiscard]] eve::Result mountAs(const std::string &name, NodeDesc root); + [[nodiscard]] eve::Result mountAs(const std::string &name, NodeDesc root); /** * @brief Mounts the tree as the default host and selects it. * @return Borrowed ECS-owned host, or a structured failure. */ - [[nodiscard]] eve::Result mount(NodeDesc root); + [[nodiscard]] eve::Result mount(NodeDesc root); /** * @brief Replaces the selected host's tree. * @return Borrowed ECS-owned host, or a structured failure. */ - [[nodiscard]] eve::Result remount(NodeDesc root); + [[nodiscard]] eve::Result remount(NodeDesc root); /** * @brief Reconciles the selected host, reporting whether replacement failed. * @return Borrowed ECS-owned host, or a structured failure. */ - [[nodiscard]] eve::Result remountReconcile(NodeDesc root); + [[nodiscard]] eve::Result remountReconcile(NodeDesc root); /** * @brief Creates/replaces a named host through the canonical mount path. * @return Borrowed ECS-owned host, or a structured failure. */ - [[nodiscard]] eve::Result remountAs(const std::string &name, NodeDesc root); + [[nodiscard]] eve::Result remountAs(const std::string &name, NodeDesc root); /** @brief Selects a named host; false when it does not exist. */ bool select(const std::string &name); @@ -86,12 +86,12 @@ public: * @brief Finds a host by name with an explicit not-found status. * @return Borrowed ECS-owned host, or a structured not-found/argument failure. */ - [[nodiscard]] eve::Result findHost(const std::string &name) const; + [[nodiscard]] eve::Result findHost(const std::string &name) const; /** * @brief Finds the host bound to an owner id with an explicit status. * @return Borrowed ECS-owned host, or a structured not-found/argument failure. */ - [[nodiscard]] eve::Result findHostByOwner(uint32_t ownerId) const; + [[nodiscard]] eve::Result findHostByOwner(uint32_t ownerId) const; /** @brief Currently selected borrowed host, or nullptr. */ [[nodiscard]] SceneHost *current() const noexcept { return selected_; } /** @brief Binds the selected host to a UI/scene owner id. */ @@ -294,10 +294,8 @@ public: void updateScripts(float dt); std::string currentHostName() const; - [[nodiscard]] SceneNodeRef *getNodeRefAt(const std::string &hostName, - const std::string &nodeId) const; - [[nodiscard]] SceneNodeRef *getNodeRefByPathAt(const std::string &hostName, - const std::string &path) const; + [[nodiscard]] SceneNodeRef *getNodeRefAt(const std::string &hostName, const std::string &nodeId) const; + [[nodiscard]] SceneNodeRef *getNodeRefByPathAt(const std::string &hostName, const std::string &path) const; private: [[nodiscard]] SceneHost *ensureSelected(const std::string &preferredName = ""); @@ -307,8 +305,7 @@ private: [[nodiscard]] SceneObject *findSceneObjectById(uint32_t id) const; [[nodiscard]] SceneObject *findSceneObjectByPersistentId(eve::SceneObjectId id) const; - [[nodiscard]] SceneObject *ensureSceneObject(SceneHost *host, SceneNode *node, - const std::string &hostName); + [[nodiscard]] SceneObject *ensureSceneObject(SceneHost *host, SceneNode *node, const std::string &hostName); /** @brief Destroy bindings whose host/node no longer resolves; self-heal meta. */ void pruneOrphanObjects(); /** @brief Fire onDetach + destroy() and release every rooted instance. */ diff --git a/src/modules/scene/SceneCapabilities.cpp b/src/modules/scene/SceneCapabilities.cpp index db406c6c..33343586 100644 --- a/src/modules/scene/SceneCapabilities.cpp +++ b/src/modules/scene/SceneCapabilities.cpp @@ -7,9 +7,9 @@ #include #include -#include #include #include +#include #include namespace eve::scene { diff --git a/src/modules/scene/SceneComponent.cpp b/src/modules/scene/SceneComponent.cpp index 082a0661..cf891521 100644 --- a/src/modules/scene/SceneComponent.cpp +++ b/src/modules/scene/SceneComponent.cpp @@ -16,8 +16,8 @@ void SceneComponent::attach(SceneHost *host) { } void SceneComponent::mountAs(const std::string &hostName) { - SceneHost *h = nullptr; - auto found = Scene::create()->findHost(hostName); + SceneHost *h = nullptr; + auto found = Scene::create()->findHost(hostName); if (found.ok()) { h = std::move(found).takeValue(); } else { diff --git a/src/modules/scene/SceneHost.cpp b/src/modules/scene/SceneHost.cpp index ae68057e..eb35de5a 100644 --- a/src/modules/scene/SceneHost.cpp +++ b/src/modules/scene/SceneHost.cpp @@ -2,8 +2,8 @@ #include "scene/NodeDesc.h" -#include #include +#include namespace eve::scene { namespace { @@ -12,8 +12,7 @@ uint32_t g_anonHostSeq = 0; template eve::Result hostFailure(eve::DiagnosticCode code, std::string message) { - return eve::Result::failure( - eve::Diagnostic::error(code, std::move(message), "scene.host")); + return eve::Result::failure(eve::Diagnostic::error(code, std::move(message), "scene.host")); } template @@ -132,17 +131,16 @@ std::vector splitPath(const std::string &path) { } // namespace -eve::Result SceneHost::createHost(const std::string &name) { +eve::Result SceneHost::createHost(const std::string &name) { SceneHost *h = SceneHost::create(); - if (!h) return hostFailure(eve::DiagnosticCode::Failed, - "scene host creation returned null"); + if (!h) return hostFailure(eve::DiagnosticCode::Failed, "scene host creation returned null"); h->meta()->entity = h; if (name.empty()) { h->meta()->name = "host" + std::to_string(++g_anonHostSeq); } else { h->meta()->name = name; } - return eve::Result::success(h, eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(h, eve::Status::success(eve::StatusCode::Applied)); } void SceneHost::setName(const std::string &name) { meta()->name = name; } @@ -153,52 +151,42 @@ void SceneHost::setTree(NodeDesc root) { applyTree(this, std::move(root)); } bool SceneHost::setTreeReconcile(NodeDesc root) { return applyTreeReconcile(this, std::move(root)); } -eve::Result SceneHost::findById(const std::string &id) { +eve::Result SceneHost::findById(const std::string &id) { if (id.empty()) - return hostFailure(eve::DiagnosticCode::InvalidArgument, - "scene node id must not be empty"); + return hostFailure(eve::DiagnosticCode::InvalidArgument, "scene node id must not be empty"); auto t = tree(); for (auto &n : t->nodes) { - if (n.id == id) - return eve::Result::success(&n, eve::Status::success()); + if (n.id == id) return eve::Result::success(&n, eve::Status::success()); } - return hostFailure(eve::DiagnosticCode::NotFound, - "scene node id was not found: " + id); + return hostFailure(eve::DiagnosticCode::NotFound, "scene node id was not found: " + id); } -eve::Result SceneHost::findByKey(const std::string &key) { +eve::Result SceneHost::findByKey(const std::string &key) { if (key.empty()) - return hostFailure(eve::DiagnosticCode::InvalidArgument, - "scene node key must not be empty"); + return hostFailure(eve::DiagnosticCode::InvalidArgument, "scene node key must not be empty"); auto t = tree(); for (auto &n : t->nodes) { - if (n.key == key) - return eve::Result::success(&n, eve::Status::success()); + if (n.key == key) return eve::Result::success(&n, eve::Status::success()); } - return hostFailure(eve::DiagnosticCode::NotFound, - "scene node key was not found: " + key); + return hostFailure(eve::DiagnosticCode::NotFound, "scene node key was not found: " + key); } -eve::Result SceneHost::findByName(const std::string &name) { +eve::Result SceneHost::findByName(const std::string &name) { if (name.empty()) - return hostFailure(eve::DiagnosticCode::InvalidArgument, - "scene node name must not be empty"); + return hostFailure(eve::DiagnosticCode::InvalidArgument, "scene node name must not be empty"); const int idx = findIndexByName(name); if (idx < 0) - return hostFailure(eve::DiagnosticCode::NotFound, - "scene node name was not found: " + name); - return eve::Result::success(&tree()->nodes[size_t(idx)], eve::Status::success()); + return hostFailure(eve::DiagnosticCode::NotFound, "scene node name was not found: " + name); + return eve::Result::success(&tree()->nodes[size_t(idx)], eve::Status::success()); } -eve::Result SceneHost::findByPath(const std::string &path) { +eve::Result SceneHost::findByPath(const std::string &path) { if (path.empty()) - return hostFailure(eve::DiagnosticCode::InvalidArgument, - "scene node path must not be empty"); + return hostFailure(eve::DiagnosticCode::InvalidArgument, "scene node path must not be empty"); const int idx = findIndexByPath(path); if (idx < 0) - return hostFailure(eve::DiagnosticCode::NotFound, - "scene node path was not found: " + path); - return eve::Result::success(&tree()->nodes[size_t(idx)], eve::Status::success()); + return hostFailure(eve::DiagnosticCode::NotFound, "scene node path was not found: " + path); + return eve::Result::success(&tree()->nodes[size_t(idx)], eve::Status::success()); } int SceneHost::findIndexById(const std::string &id) { diff --git a/src/modules/scene/SceneHost.h b/src/modules/scene/SceneHost.h index 2f4fa5bf..a2b0790e 100644 --- a/src/modules/scene/SceneHost.h +++ b/src/modules/scene/SceneHost.h @@ -114,7 +114,7 @@ public: * @remarks The ECS table owns the host. The pointer becomes stale after * entity destruction; retain a runtime handle across deferred work. */ - [[nodiscard]] static eve::Result createHost(const std::string &name = ""); + [[nodiscard]] static eve::Result createHost(const std::string &name = ""); void setName(const std::string &name); const std::string &getName(); @@ -130,22 +130,22 @@ public: * @brief Finds a node by stable id with an explicit status. * @return Borrowed node, invalidated by the next tree replacement/reconcile, or a failure. */ - [[nodiscard]] eve::Result findById(const std::string &id); + [[nodiscard]] eve::Result findById(const std::string &id); /** * @brief Finds a node by reconcile key with an explicit status. * @return Borrowed node, invalidated by the next tree replacement/reconcile, or a failure. */ - [[nodiscard]] eve::Result findByKey(const std::string &key); + [[nodiscard]] eve::Result findByKey(const std::string &key); /** * @brief Finds the first node name match with an explicit status. * @return Borrowed node, invalidated by the next tree replacement/reconcile, or a failure. */ - [[nodiscard]] eve::Result findByName(const std::string &name); + [[nodiscard]] eve::Result findByName(const std::string &name); /** * @brief Finds a node path with an explicit status. * @return Borrowed node, invalidated by the next tree replacement/reconcile, or a failure. */ - [[nodiscard]] eve::Result findByPath(const std::string &path); + [[nodiscard]] eve::Result findByPath(const std::string &path); int findIndexById(const std::string &id); int findIndexByKey(const std::string &key); int findIndexByName(const std::string &name); diff --git a/src/modules/scene/SceneObject.cpp b/src/modules/scene/SceneObject.cpp index b02b9fcc..cd5648cd 100644 --- a/src/modules/scene/SceneObject.cpp +++ b/src/modules/scene/SceneObject.cpp @@ -4,17 +4,17 @@ namespace eve::scene { -eve::Result SceneObject::createObject( - const std::string &hostName, const std::string &nodeId, eve::SceneObjectId persistentId) { +eve::Result SceneObject::createObject(const std::string &hostName, const std::string &nodeId, + eve::SceneObjectId persistentId) { SceneObject *o = SceneObject::create(); if (!o) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "scene object creation returned null", "scene.object")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "scene object creation returned null", "scene.object")); o->meta()->entity = o; o->meta()->hostName = hostName; o->meta()->nodeId = nodeId; o->meta()->persistentId = persistentId; - return eve::Result::success(o, eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(o, eve::Status::success(eve::StatusCode::Applied)); } } // namespace eve::scene diff --git a/src/modules/scene/SceneObject.h b/src/modules/scene/SceneObject.h index 1f388cda..e06d66c9 100644 --- a/src/modules/scene/SceneObject.h +++ b/src/modules/scene/SceneObject.h @@ -50,13 +50,13 @@ public: * @ownership The scene ECS table owns the object; callers must not delete it. * @lifetime Valid until deferred ECS destruction or table teardown; use the entity handle across frames. * @thread Call on the scene ECS thread. - * @reentrancy Creation does not invoke user callbacks; do not re-enter structural ECS mutation while using the result. + * @reentrancy Creation does not invoke user callbacks; do not re-enter structural ECS mutation while using the + * result. * @remarks The scene/ECS table owns the object; the pointer becomes stale * after destruction or a deferred table commit. */ - [[nodiscard]] static eve::Result createObject( - const std::string &hostName, const std::string &nodeId, - eve::SceneObjectId persistentId = {}); + [[nodiscard]] static eve::Result createObject(const std::string &hostName, const std::string &nodeId, + eve::SceneObjectId persistentId = {}); }; } // namespace eve::scene diff --git a/src/modules/sceneloader/SceneLoader.cpp b/src/modules/sceneloader/SceneLoader.cpp index 09f4e9af..ebc30fb0 100644 --- a/src/modules/sceneloader/SceneLoader.cpp +++ b/src/modules/sceneloader/SceneLoader.cpp @@ -1,27 +1,27 @@ #include "sceneloader/SceneLoader.h" -#include "scene/SceneHost.h" -#include "scene/NodeDesc.h" -#include "scene/TransformSystem.h" -#include "model3d/Model3D.h" -#include "model3d/ModelData.h" +#include "animation/AnimClip.h" +#include "animation/AnimImporter.h" +#include "animation/AnimSkeleton.h" +#include "common/ECS.h" +#include "common/Resource.h" +#include "common/SquirrelBinding.h" +#include "data/ByteData.h" +#include "filesystem/FileData.h" +#include "filesystem/Filesystem.h" #include "graphics/Graphics.h" #include "graphics/Light.h" #include "graphics/Mesh.h" #include "graphics/RenderSystem3D.h" #include "graphics/Texture.h" -#include "filesystem/Filesystem.h" -#include "filesystem/FileData.h" -#include "data/ByteData.h" #include "image/Image.h" #include "image/ImageData.h" -#include "animation/AnimImporter.h" -#include "animation/AnimSkeleton.h" -#include "animation/AnimClip.h" +#include "model3d/Model3D.h" +#include "model3d/ModelData.h" +#include "scene/NodeDesc.h" +#include "scene/SceneHost.h" +#include "scene/TransformSystem.h" #include "thread/ThreadPool.h" -#include "common/ECS.h" -#include "common/Resource.h" -#include "common/SquirrelBinding.h" #include #include @@ -994,17 +994,15 @@ scene::SceneHost *SceneLoader::loadPreset(const std::string &path, const std::st return load(path, presetOptions(preset)); } -eve::Result SceneLoader::reload(const std::string &path, SceneDiff *out, - const LoadOptions &options) { +eve::Result SceneLoader::reload(const std::string &path, SceneDiff *out, const LoadOptions &options) { const std::string key = normPath(path); auto it = scenes_.find(key); if (it == scenes_.end()) { if (!load(path, options)) { const std::string message = decodeErrorFor(path); return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, - message.empty() ? "scene reload failed to load the scene" : message, - key, {}, "sceneloader.reload")); + eve::DiagnosticCode::Failed, message.empty() ? "scene reload failed to load the scene" : message, key, + {}, "sceneloader.reload")); } if (out) *out = SceneDiff{}; return eve::Result::success(true, eve::Status::success(eve::StatusCode::Applied)); @@ -1015,9 +1013,8 @@ eve::Result SceneLoader::reload(const std::string &path, SceneDiff *out, if (!decode(path, options, &d)) { const std::string message = decodeErrorFor(path); return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, - message.empty() ? "scene reload failed to decode the scene" : message, - key, {}, "sceneloader.reload")); + eve::DiagnosticCode::Failed, message.empty() ? "scene reload failed to decode the scene" : message, key, {}, + "sceneloader.reload")); } SceneDiff diff = diffTree(ld.host, *d.root); @@ -1028,8 +1025,7 @@ eve::Result SceneLoader::reload(const std::string &path, SceneDiff *out, linkMeshNodes(ld.host, d.slots, ld.gfx, options, textures_, shared, &d.cpuImages); scene::TransformSystem::updateHost(ld.host); } - if (diff.empty()) - return eve::Result::success(false, eve::Status::success(eve::StatusCode::NoOp)); + if (diff.empty()) return eve::Result::success(false, eve::Status::success(eve::StatusCode::NoOp)); return eve::Result::success(true, eve::Status::success(eve::StatusCode::Applied)); } @@ -1311,8 +1307,9 @@ void SceneLoader::expose(ssq::Class &cls) { cls.addFunc("reload", [vm](SceneLoader *value, const std::string &path) { if (!value) return eve::script::projectResult( - vm, eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "scene loader must not be null", "scene")), + vm, + eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "scene loader must not be null", "scene")), [](bool changed) { return eve::Value::boolean(changed); }); return eve::script::projectResult(vm, value->reload(path), [](bool changed) { return eve::Value::boolean(changed); }); diff --git a/src/modules/sceneloader/SceneLoader.h b/src/modules/sceneloader/SceneLoader.h index d496d692..ac042172 100644 --- a/src/modules/sceneloader/SceneLoader.h +++ b/src/modules/sceneloader/SceneLoader.h @@ -156,9 +156,9 @@ public: * @param out Optional borrowed output written with the applied diff. * @param options Import options used for the fresh decode. */ - [[nodiscard("scene reload outcome must be checked")]] eve::Result reload( - const std::string &path, SceneDiff *out = nullptr, - const LoadOptions &options = {}); + [[nodiscard("scene reload outcome must be checked")]] eve::Result reload(const std::string &path, + SceneDiff *out = nullptr, + const LoadOptions &options = {}); /** @brief Dry-run diff for `path` against the currently mounted tree (no mutation). */ SceneDiff diff(const std::string &path); diff --git a/src/modules/schema/Schema.cpp b/src/modules/schema/Schema.cpp index 32706bd1..f526002c 100644 --- a/src/modules/schema/Schema.cpp +++ b/src/modules/schema/Schema.cpp @@ -1,7 +1,6 @@ #include "schema/Schema.h" #include "common/SquirrelBinding.h" -#include "schema/SchemaBuiltins.h" #include "schema/SchemaRegistry.h" #include @@ -15,10 +14,7 @@ namespace eve::schema { Module_IMPL(Schema, new Schema()); -Schema::Schema() { - auto standardSchemas = registerStandardSchemas(); - standardSchemas.expect("failed to bootstrap standard schemas"); -} +Schema::Schema() = default; eve::Result Schema::registerJson(const std::string& json) { auto result = SchemaRegistry::registerFromJson(json); @@ -200,9 +196,9 @@ void applyFieldAttributes(HSQUIRRELVM vm, SQInteger attrsIdx, FieldDefinition& f eve::Result Schema::registerFromClass(const ssq::Object& classOrInstance) { const ssq::Type type = classOrInstance.getType(); if (type != ssq::Type::CLASS && type != ssq::Type::INSTANCE) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "registerFromClass expects a class or an instance of a class", "class")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "registerFromClass expects a class or an instance of a class", "class")); } HSQUIRRELVM vm = classOrInstance.getHandle(); const SQInteger top = sq_gettop(vm); @@ -210,8 +206,8 @@ eve::Result Schema::registerFromClass(const ssq::Object& classOrInstance) sq_pushobject(vm, classOrInstance.getRaw()); if (type == ssq::Type::INSTANCE && SQ_FAILED(sq_getclass(vm, -1))) { sq_settop(vm, top); - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "could not resolve the instance's class", "class")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "could not resolve the instance's class", "class")); } const SQInteger clsIdx = sq_gettop(vm); @@ -234,9 +230,9 @@ eve::Result Schema::registerFromClass(const ssq::Object& classOrInstance) if (definition.id.empty()) { sq_settop(vm, top); - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "schema id is required (set the class-level attribute 'id')", "schema.id")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "schema id is required (set the class-level attribute 'id')", "schema.id")); } std::unordered_set names; @@ -401,20 +397,17 @@ std::string Schema::getFieldEnumValue(const std::string& id, int fieldIndex, int eve::Result Schema::validateJson(const std::string& schemaId, const std::string& json) { validationErrors_ = SchemaRegistry::validate(schemaId, json); - if (validationErrors_.empty()) - return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); + if (validationErrors_.empty()) return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); const auto& error = validationErrors_.front(); - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, error.message, error.path, {}, "schema")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, error.message, error.path, {}, "schema")); } -eve::Result Schema::validateJsonVersioned(const std::string& schemaId, int version, - const std::string& json) { +eve::Result Schema::validateJsonVersioned(const std::string& schemaId, int version, const std::string& json) { validationErrors_ = SchemaRegistry::validate(schemaId, version, json); - if (validationErrors_.empty()) - return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); + if (validationErrors_.empty()) return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); const auto& error = validationErrors_.front(); - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, error.message, error.path, {}, "schema")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, error.message, error.path, {}, "schema")); } int Schema::getValidationErrorCount() const { return static_cast(validationErrors_.size()); } std::string Schema::getValidationErrorPath(int index) const { @@ -444,16 +437,16 @@ void Schema::expose(ssq::Class& cls) { cls.addFunc("registerJson", [vm](Schema* value, const std::string& json) { if (!value) { return eve::script::projectResult( - vm, eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "schema facade must not be null", "schema"))); + vm, eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "schema facade must not be null", "schema"))); } return eve::script::projectResult(vm, value->registerJson(json)); }); cls.addFunc("registerFromClass", [vm](Schema* value, const ssq::Object& classOrInstance) { if (!value) { return eve::script::projectResult( - vm, eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "schema facade must not be null", "schema"))); + vm, eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "schema facade must not be null", "schema"))); } return eve::script::projectResult(vm, value->registerFromClass(classOrInstance)); }); @@ -486,20 +479,20 @@ void Schema::expose(ssq::Class& cls) { cls.addFunc("validateJson", [vm](Schema* value, const std::string& id, const std::string& json) { if (!value) { return eve::script::projectResult( - vm, eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "schema facade must not be null", "schema"))); + vm, eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "schema facade must not be null", "schema"))); } return eve::script::projectResult(vm, value->validateJson(id, json)); }); - cls.addFunc("validateJsonVersioned", [vm](Schema* value, const std::string& id, int version, - const std::string& json) { - if (!value) { - return eve::script::projectResult( - vm, eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "schema facade must not be null", "schema"))); - } - return eve::script::projectResult(vm, value->validateJsonVersioned(id, version, json)); - }); + cls.addFunc("validateJsonVersioned", + [vm](Schema* value, const std::string& id, int version, const std::string& json) { + if (!value) { + return eve::script::projectResult( + vm, eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvalidArgument, "schema facade must not be null", "schema"))); + } + return eve::script::projectResult(vm, value->validateJsonVersioned(id, version, json)); + }); cls.addFunc("getValidationErrorCount", &Schema::getValidationErrorCount); cls.addFunc("getValidationErrorPath", &Schema::getValidationErrorPath); cls.addFunc("getValidationErrorCode", &Schema::getValidationErrorCode); diff --git a/src/modules/schema/Schema.h b/src/modules/schema/Schema.h index c445156c..7186e670 100644 --- a/src/modules/schema/Schema.h +++ b/src/modules/schema/Schema.h @@ -16,7 +16,7 @@ class EVENGINE_API Schema : public Module { public: Module_REG(Schema); - /** @brief Creates the facade and bootstraps engine-owned standard schemas. */ + /** @brief Creates an empty facade over the process schema registry. */ Schema(); /** @brief Registers or replaces a schema encoded as JSON. diff --git a/src/modules/schema/SchemaBuiltins.cpp b/src/modules/schema/SchemaBuiltins.cpp deleted file mode 100644 index 6844842b..00000000 --- a/src/modules/schema/SchemaBuiltins.cpp +++ /dev/null @@ -1,120 +0,0 @@ -#include "schema/SchemaBuiltins.h" - -#include "schema/SchemaRegistry.h" - -#include -#include -#include -#include - -namespace eve::schema { -namespace { - -FieldDefinition field(std::string name, ValueType type, bool required = true) { - FieldDefinition result; - result.name = std::move(name); - result.type = type; - result.required = required; - return result; -} - -SchemaDefinition snapshotEnvelopeSchema() { - SchemaDefinition result; - result.id = "snapshot:envelope"; - result.version = 1; - result.title = "Snapshot Envelope"; - result.description = "Stable outer header and owning payload for persistence."; - result.additionalProperties = false; - result.fields = { - field("type", ValueType::String), - field("schema", ValueType::String), - field("schemaVersion", ValueType::String), - field("instanceId", ValueType::String), - field("revision", ValueType::String), - field("tick", ValueType::String), - field("contentHash", ValueType::String), - field("payload", ValueType::Any), - }; - return result; -} - -SchemaDefinition eventEnvelopeSchema() { - SchemaDefinition result; - result.id = "game_event:envelope"; - result.version = 1; - result.title = "Event Envelope"; - result.description = "Causal/order metadata around one domain-owned JSON payload."; - result.additionalProperties = false; - result.fields = { - field("eventId", ValueType::String), - field("sequence", ValueType::String), - field("type", ValueType::String), - field("source", ValueType::String), - field("subject", ValueType::String), - field("causationKind", ValueType::String), - field("causation", ValueType::String), - field("correlationKind", ValueType::String), - field("correlation", ValueType::String), - field("schemaId", ValueType::String), - field("schemaVersion", ValueType::String), - field("tick", ValueType::String), - field("flags", ValueType::Integer), - field("payload", ValueType::Any), - }; - return result; -} - -SchemaDefinition definitionMetadataSchema() { - SchemaDefinition result; - result.id = "definitions:metadata"; - result.version = 1; - result.title = "Definition Metadata"; - result.description = "Identity and version metadata for one definition instance."; - result.additionalProperties = false; - result.fields = { - field("type", ValueType::String), - field("id", ValueType::String), - field("version", ValueType::Integer), - field("generation", ValueType::String), - field("json", ValueType::Any), - }; - return result; -} - -} // namespace - -eve::Result registerStandardSchemas() { - const std::array definitions = { - snapshotEnvelopeSchema(), eventEnvelopeSchema(), definitionMetadataSchema()}; - - bool anyRegistered = false; - bool allRegistered = true; - for (const auto& definition : definitions) { - if (SchemaRegistry::resolve(definition.id, definition.version)) anyRegistered = true; - else allRegistered = false; - } - if (allRegistered) return eve::Result::success(); - if (anyRegistered) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, - "standard schema set is partially registered; refusing a partial bootstrap")); - - std::vector registered; - registered.reserve(definitions.size()); - for (const auto& definition : definitions) { - auto status = SchemaRegistry::registerVersioned(definition); - if (status.ok()) { - registered.push_back(definition.id); - continue; - } - const auto diagnostic = status.status().describe(); - for (const auto& id : registered) SchemaRegistry::remove(id, 1).ignore("standard schema teardown"); - return eve::Result::failure(eve::Diagnostic::error( - status.code() == eve::StatusCode::Conflict ? eve::DiagnosticCode::Conflict - : eve::DiagnosticCode::InvalidArgument, - diagnostic.empty() ? "standard schema registration failed" : diagnostic, definition.id)); - } - return eve::Result::success(); -} - -} // namespace eve::schema diff --git a/src/modules/schema/SchemaBuiltins.h b/src/modules/schema/SchemaBuiltins.h deleted file mode 100644 index c699efe6..00000000 --- a/src/modules/schema/SchemaBuiltins.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "common/Result.h" - -namespace eve::schema { - -/** - * @brief Registers the engine-owned envelope and definition metadata schemas. - * - * The helper is owned by the schema module so common data types do not depend - * on Snapshot, GameEvent, or Definitions. Registration is idempotent when - * the complete standard set is already present and never replaces a caller's - * exact version. - * @return Success, or a structured conflict/failure without partial standard - * registrations. - */ -[[nodiscard]] EVENGINE_API eve::Result registerStandardSchemas(); - -} // namespace eve::schema diff --git a/src/modules/schema/SchemaDocumentation.cpp b/src/modules/schema/SchemaDocumentation.cpp index 9853f767..d9a93ffc 100644 --- a/src/modules/schema/SchemaDocumentation.cpp +++ b/src/modules/schema/SchemaDocumentation.cpp @@ -9,8 +9,7 @@ namespace { template eve::Result failure(eve::DiagnosticCode code, std::string message, std::string path = {}) { - return eve::Result::failure( - eve::Diagnostic::error(code, std::move(message), std::move(path))); + return eve::Result::failure(eve::Diagnostic::error(code, std::move(message), std::move(path))); } eve::Value nodeContract(const SchemaNode& node); @@ -40,8 +39,7 @@ eve::Value definitionContract(const SchemaDefinition& definition) { eve::Value nodeContract(const SchemaNode& node) { eve::Value::Object result; result.emplace("type", eve::Value(valueTypeName(node.type))); - if (node.elementType != ValueType::Any) - result.emplace("elementType", eve::Value(valueTypeName(node.elementType))); + if (node.elementType != ValueType::Any) result.emplace("elementType", eve::Value(valueTypeName(node.elementType))); if (!node.ref.empty()) { result.emplace("ref", eve::Value(node.ref)); if (node.refVersion > 0) result.emplace("refVersion", eve::Value(node.refVersion)); @@ -57,8 +55,7 @@ eve::Value nodeContract(const SchemaNode& node) { if (!node.discriminator.empty()) result.emplace("discriminator", eve::Value(node.discriminator)); if (!node.discriminatorMapping.empty()) { eve::Value::Object mapping; - for (const auto& [value, target] : node.discriminatorMapping) - mapping.emplace(value, eve::Value(target)); + for (const auto& [value, target] : node.discriminatorMapping) mapping.emplace(value, eve::Value(target)); result.emplace("discriminatorMapping", eve::Value(std::move(mapping))); } if (node.minimum) result.emplace("minimum", eve::Value(*node.minimum)); @@ -79,15 +76,18 @@ std::string markdownEscape(std::string_view text) { std::string result; result.reserve(text.size()); for (const char character : text) { - if (character == '|') result += "\\|"; - else if (character == '\n') result += ' '; - else result += character; + if (character == '|') + result += "\\|"; + else if (character == '\n') + result += ' '; + else + result += character; } return result; } -void appendFieldDocumentation(std::ostringstream& output, const FieldDefinition& field, - const std::string& parentPath, int depth) { +void appendFieldDocumentation(std::ostringstream& output, const FieldDefinition& field, const std::string& parentPath, + int depth) { const std::string path = parentPath + "/" + field.name; output << std::string(static_cast(depth), ' ') << "- `" << markdownEscape(path) << "` — `" << valueTypeName(field.type) << "`, " << (field.required ? "required" : "optional"); @@ -96,13 +96,11 @@ void appendFieldDocumentation(std::ostringstream& output, const FieldDefinition& if (field.refVersion > 0) output << "@" << field.refVersion; output << '`'; } - if (!field.discriminator.empty()) - output << ", discriminator=`" << markdownEscape(field.discriminator) << '`'; + if (!field.discriminator.empty()) output << ", discriminator=`" << markdownEscape(field.discriminator) << '`'; if (!field.description.empty()) output << " — " << markdownEscape(field.description); output << '\n'; if (field.objectSchema) { - for (const auto& nested : field.objectSchema->fields) - appendFieldDocumentation(output, nested, path, depth + 2); + for (const auto& nested : field.objectSchema->fields) appendFieldDocumentation(output, nested, path, depth + 2); } if (field.itemSchema && field.itemSchema->objectSchema) { output << std::string(static_cast(depth + 2), ' ') << "- array item shape:\n"; @@ -113,18 +111,16 @@ void appendFieldDocumentation(std::ostringstream& output, const FieldDefinition& } // namespace -eve::Result SchemaRegistry::generateDocumentation(const std::string& schemaId, - int schemaVersion) { +eve::Result SchemaRegistry::generateDocumentation(const std::string& schemaId, int schemaVersion) { const auto* schema = resolve(schemaId, schemaVersion); if (!schema) - return failure(eve::DiagnosticCode::UnknownVersion, - "schema version is not registered", "schemaVersion"); + return failure(eve::DiagnosticCode::UnknownVersion, "schema version is not registered", + "schemaVersion"); std::ostringstream output; output << "# " << markdownEscape(schema->id) << " (v" << schema->version << ")\n\n"; output << "Language: Eve Schema v1\n\n"; - output << (schema->additionalProperties ? "Unknown object fields: allow\n\n" - : "Unknown object fields: reject\n\n"); + output << (schema->additionalProperties ? "Unknown object fields: allow\n\n" : "Unknown object fields: reject\n\n"); if (!schema->title.empty()) output << "Title: " << markdownEscape(schema->title) << "\n\n"; if (!schema->description.empty()) output << markdownEscape(schema->description) << "\n\n"; output << "## Fields\n\n"; @@ -134,12 +130,11 @@ eve::Result SchemaRegistry::generateDocumentation(const std::string return eve::Result::success(output.str()); } -eve::Result SchemaRegistry::generateBindingContract(const std::string& schemaId, - int schemaVersion) { +eve::Result SchemaRegistry::generateBindingContract(const std::string& schemaId, int schemaVersion) { const auto* schema = resolve(schemaId, schemaVersion); if (!schema) - return failure(eve::DiagnosticCode::UnknownVersion, - "schema version is not registered", "schemaVersion"); + return failure(eve::DiagnosticCode::UnknownVersion, "schema version is not registered", + "schemaVersion"); eve::Value::Object result; result.emplace("language", eve::Value("eve-schema-v1")); result.emplace("schemaId", eve::Value(schema->id)); diff --git a/src/modules/schema/SchemaRegistry.cpp b/src/modules/schema/SchemaRegistry.cpp index 27a17ae8..f2ac17aa 100644 --- a/src/modules/schema/SchemaRegistry.cpp +++ b/src/modules/schema/SchemaRegistry.cpp @@ -19,7 +19,7 @@ namespace eve::schema { namespace { -using JsonValue = eve::json::Value; +using JsonValue = eve::json::Value; using SchemaVersions = std::map; std::unordered_map& schemas() { @@ -41,8 +41,7 @@ std::map& migrationSteps() { template eve::Result failure(eve::DiagnosticCode code, std::string message, std::string path = {}) { - return eve::Result::failure( - eve::Diagnostic::error(code, std::move(message), std::move(path))); + return eve::Result::failure(eve::Diagnostic::error(code, std::move(message), std::move(path))); } void addError(std::vector& out, std::string path, std::string code, std::string message) { @@ -67,9 +66,12 @@ std::string pointerSegment(std::string_view value) { std::string result; result.reserve(value.size()); for (const char character : value) { - if (character == '~') result += "~0"; - else if (character == '/') result += "~1"; - else result += character; + if (character == '~') + result += "~0"; + else if (character == '/') + result += "~1"; + else + result += character; } return result; } @@ -106,7 +108,7 @@ bool parseField(JsonValue value, FieldDefinition& output, std::string& error, co return false; } - output = FieldDefinition{}; + output = FieldDefinition{}; const JsonValue name = value.get("name"); if (!readStrictString(name, output.name) || output.name.empty()) { error = path + "/name must be a non-empty string"; @@ -170,13 +172,29 @@ bool parseNode(JsonValue value, SchemaNode& output, std::string& error, const st return false; } - static const std::set nodeKeys = { - "additionalProperties", "discriminator", "discriminatorMapping", "elementType", "enum", - "fields", "itemSchema", "items", "maxItems", "maxLength", "maximum", "minItems", - "minLength", "minimum", "oneOf", "ref", "refVersion", "referenceVersion", "schemaRef", - "type", "union"}; - static const std::set fieldMetadataKeys = { - "defaultJson", "description", "name", "reference", "required", "title"}; + static const std::set nodeKeys = {"additionalProperties", + "discriminator", + "discriminatorMapping", + "elementType", + "enum", + "fields", + "itemSchema", + "items", + "maxItems", + "maxLength", + "maximum", + "minItems", + "minLength", + "minimum", + "oneOf", + "ref", + "refVersion", + "referenceVersion", + "schemaRef", + "type", + "union"}; + static const std::set fieldMetadataKeys = {"defaultJson", "description", "name", + "reference", "required", "title"}; for (const auto& key : value.keys()) { if (!nodeKeys.contains(key) && (!allowFieldMetadata || !fieldMetadataKeys.contains(key))) { error = path + "/" + pointerSegment(key) + " is not supported by Eve Schema v1"; @@ -279,10 +297,8 @@ bool parseNode(JsonValue value, SchemaNode& output, std::string& error, const st target = parsed; return true; }; - if (!readLengthConstraint("minLength", output.minLength) || - !readLengthConstraint("maxLength", output.maxLength) || - !readLengthConstraint("minItems", output.minItems) || - !readLengthConstraint("maxItems", output.maxItems)) + if (!readLengthConstraint("minLength", output.minLength) || !readLengthConstraint("maxLength", output.maxLength) || + !readLengthConstraint("minItems", output.minItems) || !readLengthConstraint("maxItems", output.maxItems)) return false; if (output.minLength && output.maxLength && *output.minLength > *output.maxLength) { error = path + "/minLength must not exceed maxLength"; @@ -309,7 +325,7 @@ bool parseNode(JsonValue value, SchemaNode& output, std::string& error, const st } } - const bool hasItems = value.has("items"); + const bool hasItems = value.has("items"); const bool hasItemSchema = value.has("itemSchema"); if (hasItems && hasItemSchema) { error = path + " cannot define both items and itemSchema"; @@ -340,16 +356,14 @@ bool parseNode(JsonValue value, SchemaNode& output, std::string& error, const st output.variants.reserve(alternatives.size()); for (size_t index = 0; index < alternatives.size(); ++index) { SchemaNode alternative; - if (!parseNode(alternatives.at(index), alternative, error, - path + "/union/" + std::to_string(index))) + if (!parseNode(alternatives.at(index), alternative, error, path + "/union/" + std::to_string(index))) return false; output.variants.push_back(std::move(alternative)); } } if (value.has("discriminator")) { - if (!readStrictString(value.get("discriminator"), output.discriminator) || - output.discriminator.empty()) { + if (!readStrictString(value.get("discriminator"), output.discriminator) || output.discriminator.empty()) { error = path + "/discriminator must be a non-empty string"; return false; } @@ -362,7 +376,7 @@ bool parseNode(JsonValue value, SchemaNode& output, std::string& error, const st } for (const std::string& key : mapping.keys()) { const JsonValue mapped = mapping.get(key.c_str()); - std::string target; + std::string target; if (!readStrictString(mapped, target)) { int mappingIndex = 0; if (!readInteger(mapped, mappingIndex) || mappingIndex < 0) { @@ -384,7 +398,7 @@ bool parseNode(JsonValue value, SchemaNode& output, std::string& error, const st error = path + "/fields or additionalProperties requires type object"; return false; } - auto nested = std::make_shared(); + auto nested = std::make_shared(); nested->version = 1; if (value.has("additionalProperties")) { if (!readStrictBool(value.get("additionalProperties"), nested->additionalProperties)) { @@ -392,8 +406,7 @@ bool parseNode(JsonValue value, SchemaNode& output, std::string& error, const st return false; } } - if (value.has("fields") && - !parseFieldArray(value.get("fields"), nested->fields, error, path + "/fields")) + if (value.has("fields") && !parseFieldArray(value.get("fields"), nested->fields, error, path + "/fields")) return false; output.objectSchema = std::move(nested); } @@ -401,16 +414,16 @@ bool parseNode(JsonValue value, SchemaNode& output, std::string& error, const st } bool parseDefinition(const std::string& json, SchemaDefinition& definition, std::string* error) { - std::string parseError; - const auto document = eve::json::Document::parse(json, &parseError); - const JsonValue root = document.root(); + std::string parseError; + const auto document = eve::json::Document::parse(json, &parseError); + const JsonValue root = document.root(); if (!document.valid() || !root.isObject()) { if (error) *error = parseError.empty() ? "schema must be a JSON object" : parseError; return false; } - static const std::set definitionKeys = { - "additionalProperties", "description", "fields", "id", "schemaVersion", "title", "version"}; + static const std::set definitionKeys = {"additionalProperties", "description", "fields", "id", + "schemaVersion", "title", "version"}; for (const auto& key : root.keys()) { if (!definitionKeys.contains(key)) { if (error) *error = "/" + pointerSegment(key) + " is not supported by Eve Schema v1"; @@ -418,10 +431,10 @@ bool parseDefinition(const std::string& json, SchemaDefinition& definition, std: } } - definition = SchemaDefinition{}; + definition = SchemaDefinition{}; const bool hasSchemaVersion = root.has("schemaVersion"); const bool hasLegacyVersion = root.has("version"); - int schemaVersion = 1; + int schemaVersion = 1; if (hasSchemaVersion && !readInteger(root.get("schemaVersion"), schemaVersion)) { if (error) *error = "schemaVersion must be an integer"; return false; @@ -482,8 +495,7 @@ bool validateDefinition(const SchemaDefinition& definition, const std::string& p activeDefinitions.erase(&definition); return false; } - if (!validateNodeDefinition(field, path + "/fields/" + pointerSegment(field.name), error, - activeDefinitions)) { + if (!validateNodeDefinition(field, path + "/fields/" + pointerSegment(field.name), error, activeDefinitions)) { activeDefinitions.erase(&definition); return false; } @@ -502,11 +514,10 @@ bool validateNodeDefinition(const SchemaNode& node, const std::string& path, std error = path + "/refVersion requires ref"; return false; } - if (!node.ref.empty() && - (node.type != ValueType::Any || node.elementType != ValueType::Any || node.objectSchema || - node.itemSchema || !node.variants.empty() || !node.discriminator.empty() || - !node.discriminatorMapping.empty() || !node.enumValues.empty() || node.minimum || node.maximum || - node.minLength || node.maxLength || node.minItems || node.maxItems)) { + if (!node.ref.empty() && (node.type != ValueType::Any || node.elementType != ValueType::Any || node.objectSchema || + node.itemSchema || !node.variants.empty() || !node.discriminator.empty() || + !node.discriminatorMapping.empty() || !node.enumValues.empty() || node.minimum || + node.maximum || node.minLength || node.maxLength || node.minItems || node.maxItems)) { error = path + " cannot combine ref with type, constraints, fields, items, or union"; return false; } @@ -519,8 +530,7 @@ bool validateNodeDefinition(const SchemaNode& node, const std::string& path, std error = path + " string constraints require type string"; return false; } - if (node.type != ValueType::Number && node.type != ValueType::Integer && - (node.minimum || node.maximum)) { + if (node.type != ValueType::Number && node.type != ValueType::Integer && (node.minimum || node.maximum)) { error = path + " numeric constraints require type number or integer"; return false; } @@ -544,9 +554,8 @@ bool validateNodeDefinition(const SchemaNode& node, const std::string& path, std error = path + "/discriminatorMapping keys and values must not be empty"; return false; } - size_t mappingIndex = 0; - const auto [end, parseError] = - std::from_chars(target.data(), target.data() + target.size(), mappingIndex); + size_t mappingIndex = 0; + const auto [end, parseError] = std::from_chars(target.data(), target.data() + target.size(), mappingIndex); if (parseError == std::errc{}) { if (end != target.data() + target.size() || mappingIndex >= node.variants.size()) { error = path + "/discriminatorMapping points outside the union"; @@ -569,11 +578,9 @@ bool validateNodeDefinition(const SchemaNode& node, const std::string& path, std error = path + "/discriminator requires a union"; return false; } - if (node.objectSchema && - !validateDefinition(*node.objectSchema, path + "/fields", error, activeDefinitions)) + if (node.objectSchema && !validateDefinition(*node.objectSchema, path + "/fields", error, activeDefinitions)) return false; - if (node.itemSchema && - !validateNodeDefinition(*node.itemSchema, path + "/items", error, activeDefinitions)) + if (node.itemSchema && !validateNodeDefinition(*node.itemSchema, path + "/items", error, activeDefinitions)) return false; for (size_t index = 0; index < node.variants.size(); ++index) { if (!validateNodeDefinition(node.variants[index], path + "/union/" + std::to_string(index), error, @@ -584,7 +591,7 @@ bool validateNodeDefinition(const SchemaNode& node, const std::string& path, std } SchemaRegistrationStatus registerDefinition(const SchemaDefinition& definition, bool replaceExisting, - std::string* error) { + std::string* error) { if (definition.id.empty()) { if (error) *error = "schema id must not be empty"; return SchemaRegistrationStatus::Invalid; @@ -593,15 +600,15 @@ SchemaRegistrationStatus registerDefinition(const SchemaDefinition& definition, if (error) *error = "schema version must be positive"; return SchemaRegistrationStatus::Invalid; } - std::string validationError; + std::string validationError; std::unordered_set activeDefinitions; if (!validateDefinition(definition, "schema", validationError, activeDefinitions)) { if (error) *error = validationError; return SchemaRegistrationStatus::Invalid; } - auto& versions = schemas()[definition.id]; - const auto it = versions.find(definition.version); + auto& versions = schemas()[definition.id]; + const auto it = versions.find(definition.version); if (it != versions.end()) { if (!replaceExisting) { if (error) { @@ -635,8 +642,8 @@ void validateObject(const SchemaDefinition& schema, JsonValue value, const std:: std::unordered_set known; for (const auto& field : schema.fields) { known.insert(field.name); - const JsonValue fieldValue = value.get(field.name.c_str()); - const std::string fieldPath = path + "/" + pointerSegment(field.name); + const JsonValue fieldValue = value.get(field.name.c_str()); + const std::string fieldPath = path + "/" + pointerSegment(field.name); if (!fieldValue) { if (field.required) addError(errors, fieldPath, "required", "required field is missing"); continue; @@ -681,7 +688,7 @@ void validateUnionValue(const SchemaNode& node, JsonValue value, const std::stri return; } const std::string valueText = discriminatorValue.asString(); - const auto mapping = node.discriminatorMapping.find(valueText); + const auto mapping = node.discriminatorMapping.find(valueText); if (mapping != node.discriminatorMapping.end()) { size_t index = 0; const auto [end, parseError] = @@ -779,7 +786,7 @@ void validateNode(const SchemaNode& node, JsonValue value, const std::string& pa bool migrationReaches(const std::string& schemaId, int start, int target) { std::set visited; - int current = start; + int current = start; while (visited.insert(current).second) { if (current == target) return true; const auto it = migrationSteps().find(MigrationKey{schemaId, current}); @@ -828,7 +835,7 @@ const char* schemaRegistrationStatusName(SchemaRegistrationStatus status) { } static eve::Result registrationResult(SchemaRegistrationStatus status, std::string error, - std::string path = {}) { + std::string path = {}) { if (status == SchemaRegistrationStatus::Conflict) return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::Conflict, error.empty() ? "schema version is already registered" : std::move(error), @@ -837,8 +844,7 @@ static eve::Result registrationResult(SchemaRegistrati return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::InvalidArgument, error.empty() ? "invalid schema definition" : std::move(error), std::move(path), {}, "schema")); - return eve::Result::success( - status, eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(status, eve::Status::success(eve::StatusCode::Applied)); } eve::Result SchemaRegistry::registerSchema(const SchemaDefinition& definition) { @@ -848,7 +854,7 @@ eve::Result SchemaRegistry::registerSchema(const Schem eve::Result SchemaRegistry::registerFromJson(const std::string& json) { SchemaDefinition definition; - std::string error; + std::string error; if (!parseDefinition(json, definition, &error)) return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::ParseError, error.empty() ? "invalid schema JSON" : error, {}, {}, "schema")); @@ -867,10 +873,11 @@ eve::Result SchemaRegistry::registerVersioned(const Sc eve::Result SchemaRegistry::registerFromJsonVersioned(const std::string& json) { SchemaDefinition definition; - std::string error; + std::string error; if (!parseDefinition(json, definition, &error)) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, error.empty() ? "invalid schema JSON" : std::move(error), {}, {}, "schema")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::ParseError, + error.empty() ? "invalid schema JSON" : std::move(error), {}, {}, "schema")); return registerVersioned(definition); } @@ -883,7 +890,7 @@ const SchemaDefinition* SchemaRegistry::resolve(const std::string& schemaId, int std::vector SchemaRegistry::versions(const std::string& schemaId) { std::vector result; - const auto it = schemas().find(schemaId); + const auto it = schemas().find(schemaId); if (it == schemas().end()) return result; result.reserve(it->second.size()); for (const auto& [version, definition] : it->second) { @@ -896,31 +903,33 @@ std::vector SchemaRegistry::versions(const std::string& schemaId) { eve::Result SchemaRegistry::remove(const std::string& id) { const bool removed = schemas().erase(id) != 0; for (auto it = migrationSteps().begin(); it != migrationSteps().end();) { - if (it->first.first == id) it = migrationSteps().erase(it); - else ++it; + if (it->first.first == id) + it = migrationSteps().erase(it); + else + ++it; } if (!removed) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::NotFound, "schema id is not registered", id, {}, "schema")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "schema id is not registered", id, {}, "schema")); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } eve::Result SchemaRegistry::remove(const std::string& id, int schemaVersion) { const auto schemaIt = schemas().find(id); if (schemaIt == schemas().end()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::NotFound, "schema id is not registered", id, {}, "schema")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "schema id is not registered", id, {}, "schema")); const bool removed = schemaIt->second.erase(schemaVersion) != 0; if (schemaIt->second.empty()) schemas().erase(schemaIt); for (auto it = migrationSteps().begin(); it != migrationSteps().end();) { - if (it->first.first == id && - (it->first.second == schemaVersion || it->second.toVersion == schemaVersion)) + if (it->first.first == id && (it->first.second == schemaVersion || it->second.toVersion == schemaVersion)) it = migrationSteps().erase(it); - else ++it; + else + ++it; } if (!removed) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::NotFound, "schema version is not registered", id, {}, "schema")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::NotFound, + "schema version is not registered", id, {}, "schema")); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } @@ -949,7 +958,7 @@ std::vector SchemaRegistry::ids() { } std::vector SchemaRegistry::validate(const std::string& schemaId, const std::string& json) { - const auto* schema = find(schemaId); + const auto* schema = find(schemaId); std::vector errors; if (!schema) { addError(errors, "", "schema_not_found", "schema '" + schemaId + "' is not registered"); @@ -959,7 +968,7 @@ std::vector SchemaRegistry::validate(const std::string& schemaI } std::vector SchemaRegistry::validate(const std::string& schemaId, int schemaVersion, - const std::string& json) { + const std::string& json) { std::vector errors; const auto* schema = resolve(schemaId, schemaVersion); if (!schema) { @@ -968,8 +977,8 @@ std::vector SchemaRegistry::validate(const std::string& schemaI return errors; } std::string parseError; - const auto document = eve::json::Document::parse(json, &parseError); - const JsonValue root = document.root(); + const auto document = eve::json::Document::parse(json, &parseError); + const JsonValue root = document.root(); if (!document.valid()) { addError(errors, "", "invalid_json", parseError); return errors; @@ -981,14 +990,14 @@ std::vector SchemaRegistry::validate(const std::string& schemaI } eve::Result SchemaRegistry::registerMigration(const std::string& schemaId, int fromVersion, int toVersion, - MigrationFunction migration) { + MigrationFunction migration) { if (schemaId.empty() || fromVersion <= 0 || toVersion <= 0 || !migration) return failure(eve::DiagnosticCode::InvalidArgument, "migration id, versions, and function are required"); if (fromVersion == toVersion) return failure(eve::DiagnosticCode::Conflict, "a self migration edge is a cycle", "schemaVersion"); if (fromVersion > toVersion) - return failure(eve::DiagnosticCode::Unsupported, - "schema downgrade migrations are not supported", "schemaVersion"); + return failure(eve::DiagnosticCode::Unsupported, "schema downgrade migrations are not supported", + "schemaVersion"); const MigrationKey key{schemaId, fromVersion}; if (migrationSteps().contains(key)) @@ -1010,21 +1019,21 @@ eve::Result SchemaRegistry::registerMigration(const std::string& schemaId, } eve::Result SchemaRegistry::queryCompatibility(const std::string& schemaId, int fromVersion, - int toVersion) { + int toVersion) { if (schemaId.empty() || fromVersion <= 0 || toVersion <= 0) return failure(eve::DiagnosticCode::InvalidArgument, - "schema id and versions must be positive", "schemaVersion"); + "schema id and versions must be positive", "schemaVersion"); if (!resolve(schemaId, fromVersion) || !resolve(schemaId, toVersion)) return failure(eve::DiagnosticCode::UnknownVersion, - "source or target schema version is not registered", "schemaVersion"); + "source or target schema version is not registered", "schemaVersion"); if (fromVersion > toVersion) return failure(eve::DiagnosticCode::Unsupported, - "schema downgrade compatibility is not supported", "schemaVersion"); + "schema downgrade compatibility is not supported", "schemaVersion"); SchemaCompatibility result; - result.schemaId = schemaId; + result.schemaId = schemaId; result.fromVersion = fromVersion; - result.toVersion = toVersion; + result.toVersion = toVersion; result.versions.push_back(fromVersion); if (fromVersion == toVersion) return eve::Result::success(std::move(result)); @@ -1033,25 +1042,25 @@ eve::Result SchemaRegistry::queryCompatibility(const std::s while (current != toVersion) { if (current > toVersion) return failure(eve::DiagnosticCode::Unsupported, - "migration chain would downgrade the payload", "schemaVersion"); + "migration chain would downgrade the payload", "schemaVersion"); if (!visited.insert(current).second) - return failure(eve::DiagnosticCode::Conflict, - "migration chain contains a cycle", "schemaVersion"); + return failure(eve::DiagnosticCode::Conflict, "migration chain contains a cycle", + "schemaVersion"); const auto it = migrationSteps().find(MigrationKey{schemaId, current}); if (it == migrationSteps().end()) return failure(eve::DiagnosticCode::Unsupported, - "migration chain is missing an explicit edge", "schemaVersion"); + "migration chain is missing an explicit edge", "schemaVersion"); if (it->second.toVersion <= current) return failure(eve::DiagnosticCode::Conflict, - "migration chain contains a non-forward edge", "schemaVersion"); + "migration chain contains a non-forward edge", "schemaVersion"); if (it->second.toVersion > toVersion) return failure(eve::DiagnosticCode::Unsupported, - "migration edge overshoots the requested target", "schemaVersion"); + "migration edge overshoots the requested target", "schemaVersion"); current = it->second.toVersion; if (!resolve(schemaId, current)) return failure(eve::DiagnosticCode::UnknownVersion, - "migration chain reaches an unregistered schema version", - "schemaVersion"); + "migration chain reaches an unregistered schema version", + "schemaVersion"); result.versions.push_back(current); } return eve::Result::success(std::move(result)); @@ -1071,8 +1080,8 @@ eve::Result SchemaRegistry::migrate(const std::string& schemaId, int eve::Value current; try { - current = input; - const auto& path = compatibility.value().versions; + current = input; + const auto& path = compatibility.value().versions; std::vector chain; chain.reserve(path.size() > 0 ? path.size() - 1 : 0); for (size_t index = 1; index < path.size(); ++index) { @@ -1085,8 +1094,8 @@ eve::Result SchemaRegistry::migrate(const std::string& schemaId, int for (size_t index = 1; index < path.size(); ++index) { auto next = chain[index - 1](current); if (!next.ok()) return eve::Result::failure(next.status()); - eve::Value candidate = std::move(next).takeValue(); - auto candidateJson = candidate.toJson(); + eve::Value candidate = std::move(next).takeValue(); + auto candidateJson = candidate.toJson(); if (!candidateJson.ok()) return eve::Result::failure(candidateJson.status()); const auto errors = validate(schemaId, path[index], candidateJson.value()); if (!errors.empty()) @@ -1104,7 +1113,7 @@ eve::Result SchemaRegistry::migrate(const std::string& schemaId, int } eve::Result SchemaRegistry::migrateJson(const std::string& schemaId, int fromVersion, int toVersion, - const std::string& json) { + const std::string& json) { auto input = eve::Value::fromJson(json); if (!input.ok()) return eve::Result::failure(input.status()); auto migrated = migrate(schemaId, fromVersion, toVersion, input.value()); diff --git a/src/modules/schema/SchemaRegistry.h b/src/modules/schema/SchemaRegistry.h index 9f3504ad..7bda9cd0 100644 --- a/src/modules/schema/SchemaRegistry.h +++ b/src/modules/schema/SchemaRegistry.h @@ -21,10 +21,10 @@ using MigrationFunction = std::function(const eve::Value * nearest-version or downgrade behavior is represented by this type. */ struct EVENGINE_API SchemaCompatibility { - std::string schemaId; - int fromVersion = 0; - int toVersion = 0; - std::vector versions; + std::string schemaId; + int fromVersion = 0; + int toVersion = 0; + std::vector versions; /** @brief Whether source and target are the same exact schema version. */ [[nodiscard]] bool exact() const noexcept { return versions.size() == 1; } @@ -37,8 +37,7 @@ public: * @param definition Schema to copy into the registry. * @return Registered or Replaced, or a structured validation failure. */ - [[nodiscard]] static eve::Result registerSchema( - const SchemaDefinition& definition); + [[nodiscard]] static eve::Result registerSchema(const SchemaDefinition& definition); /** @brief Parses and registers or replaces one schema definition from JSON. * @param json JSON schema definition. @@ -50,8 +49,7 @@ public: * @param definition Schema to copy into the registry. * @return Registered, or a structured conflict/validation failure. */ - [[nodiscard]] static eve::Result registerVersioned( - const SchemaDefinition& definition); + [[nodiscard]] static eve::Result registerVersioned(const SchemaDefinition& definition); /** @brief Parses and registers a new exact `(id, schemaVersion)` entry. * @param json JSON schema definition. `schemaVersion` is canonical; @@ -131,7 +129,7 @@ public: /** @brief Validates JSON against one exact schema version. */ [[nodiscard]] static std::vector validate(const std::string& schemaId, int schemaVersion, - const std::string& json); + const std::string& json); /** * @brief Registers one explicit forward migration edge. @@ -145,7 +143,7 @@ public: * migration chain unambiguous and lets missing links be reported. */ [[nodiscard]] static eve::Result registerMigration(const std::string& schemaId, int fromVersion, - int toVersion, MigrationFunction migration); + int toVersion, MigrationFunction migration); /** * @brief Resolves an exact compatibility path without executing migrations. @@ -157,7 +155,7 @@ public: * failures; the registry is never mutated by this query. */ [[nodiscard]] static eve::Result queryCompatibility(const std::string& schemaId, - int fromVersion, int toVersion); + int fromVersion, int toVersion); /** * @brief Applies an explicit migration chain to an owning payload. @@ -170,8 +168,8 @@ public: * value. Callback or validation failure leaves both input and * the registry unchanged. */ - [[nodiscard]] static eve::Result migrate(const std::string& schemaId, int fromVersion, - int toVersion, const eve::Value& input); + [[nodiscard]] static eve::Result migrate(const std::string& schemaId, int fromVersion, int toVersion, + const eve::Value& input); /** * @brief Parses, migrates and serializes a JSON payload through the chain. @@ -182,7 +180,7 @@ public: * @return Canonical migrated JSON, or a structured failure. */ [[nodiscard]] static eve::Result migrateJson(const std::string& schemaId, int fromVersion, - int toVersion, const std::string& json); + int toVersion, const std::string& json); /** * @brief Produce stable Markdown for one exact registered schema. @@ -190,8 +188,7 @@ public: * @param schemaVersion Exact schema version. * @return Minimal generated schema documentation. */ - [[nodiscard]] static eve::Result generateDocumentation(const std::string& schemaId, - int schemaVersion); + [[nodiscard]] static eve::Result generateDocumentation(const std::string& schemaId, int schemaVersion); /** * @brief Produce a stable JSON binding contract for one exact schema. @@ -200,7 +197,7 @@ public: * @return Canonical contract consumed by tooling/binding generators. */ [[nodiscard]] static eve::Result generateBindingContract(const std::string& schemaId, - int schemaVersion); + int schemaVersion); }; } // namespace eve::schema diff --git a/src/modules/scriptmodel/ReflectedPropertyModel.cpp b/src/modules/scriptmodel/ReflectedPropertyModel.cpp index 9c43e53d..bef2a0f1 100644 --- a/src/modules/scriptmodel/ReflectedPropertyModel.cpp +++ b/src/modules/scriptmodel/ReflectedPropertyModel.cpp @@ -7,10 +7,10 @@ namespace eve::scriptmodel { namespace { +using eve::Value; using property_access::PropertyDescriptor; using property_access::PropertyFlag; using property_access::PropertyKind; -using eve::Value; PropertyKind propertyKind(const ReflectedMember &member) { if (member.attrString("editor") == "combo" && !member.attrOptions("options").empty()) @@ -81,18 +81,12 @@ ReflectedValue toReflectedValue(const Value &value) { } const char *scriptValidationCode(const std::string &presentationCode) { - if (presentationCode == "property_access.property.read-only") - return "scriptmodel.property.read-only"; - if (presentationCode == "property_access.property.type") - return "scriptmodel.property.type"; - if (presentationCode == "property_access.property.choice") - return "scriptmodel.property.choice"; - if (presentationCode == "property_access.property.finite") - return "scriptmodel.property.finite"; - if (presentationCode == "property_access.property.minimum") - return "scriptmodel.property.minimum"; - if (presentationCode == "property_access.property.maximum") - return "scriptmodel.property.maximum"; + if (presentationCode == "property_access.property.read-only") return "scriptmodel.property.read-only"; + if (presentationCode == "property_access.property.type") return "scriptmodel.property.type"; + if (presentationCode == "property_access.property.choice") return "scriptmodel.property.choice"; + if (presentationCode == "property_access.property.finite") return "scriptmodel.property.finite"; + if (presentationCode == "property_access.property.minimum") return "scriptmodel.property.minimum"; + if (presentationCode == "property_access.property.maximum") return "scriptmodel.property.maximum"; return "scriptmodel.property.validation"; } @@ -211,23 +205,19 @@ std::optional ReflectedPropertyModel::read(const std::string &path) const return convertValue(path, runtime_->readProperty(instance_, path)); } -property_access::WriteResult ReflectedPropertyModel::write(const std::string &path, - const Value &value) { +property_access::WriteResult ReflectedPropertyModel::write(const std::string &path, const Value &value) { auto descriptor = schema_.find(path); if (!descriptor) - return property_access::WriteResult::reject("scriptmodel.property.missing", - "Property is not reflected"); - const property_access::WriteResult validation = - property_access::validatePropertyValue(descriptor->get(), value); + return property_access::WriteResult::reject("scriptmodel.property.missing", "Property is not reflected"); + const property_access::WriteResult validation = property_access::validatePropertyValue(descriptor->get(), value); if (!validation.accepted) return validationFailure(validation); ReflectedValue reflected = toReflectedValue(value); if (reflected.empty()) - return property_access::WriteResult::reject("scriptmodel.property.type", - "Unsupported property value type"); + return property_access::WriteResult::reject("scriptmodel.property.type", "Unsupported property value type"); if (!runtime_->writeProperty(instance_, path, reflected)) return property_access::WriteResult::reject("scriptmodel.property.write", - "Runtime rejected the property write"); + "Runtime rejected the property write"); const Value applied = convertValue(path, runtime_->readProperty(instance_, path)); const auto found = cachedValues_.find(path); if (found == cachedValues_.end() || found->second != applied) emit(path, applied); diff --git a/src/modules/scriptmodel/ReflectedPropertyModel.h b/src/modules/scriptmodel/ReflectedPropertyModel.h index 848d588c..fb752d07 100644 --- a/src/modules/scriptmodel/ReflectedPropertyModel.h +++ b/src/modules/scriptmodel/ReflectedPropertyModel.h @@ -22,11 +22,10 @@ public: ReflectedPropertyModel &operator=(const ReflectedPropertyModel &) = delete; const property_access::PropertySchema &schema() const override { return schema_; } - std::optional read(const std::string &path) const override; - property_access::WriteResult write(const std::string &path, - const eve::Value &value) override; + std::optional read(const std::string &path) const override; + property_access::WriteResult write(const std::string &path, const eve::Value &value) override; std::uint64_t revision() const override { return revision_; } - property_access::Subscription subscribe(ChangeCallback callback) override; + property_access::Subscription subscribe(ChangeCallback callback) override; /** @brief Rebuild reflection metadata after a script reload. */ void rebuildSchema(); @@ -35,13 +34,12 @@ public: private: struct ObserverState; - eve::Value convertValue(const std::string &path, - const ReflectedValue &value) const; - void emit(const std::string &path, const eve::Value &value); + eve::Value convertValue(const std::string &path, const ReflectedValue &value) const; + void emit(const std::string &path, const eve::Value &value); Runtime *runtime_ = nullptr; ssq::Object instance_; - property_access::PropertySchema schema_; + property_access::PropertySchema schema_; std::map cachedValues_; std::uint64_t revision_ = 0; std::shared_ptr observers_; diff --git a/src/modules/sensing/Sensing.cpp b/src/modules/sensing/Sensing.cpp index b97906b9..d6fc04ee 100644 --- a/src/modules/sensing/Sensing.cpp +++ b/src/modules/sensing/Sensing.cpp @@ -40,31 +40,25 @@ std::string join(const std::set& s) { bool finite(float v) { return std::isfinite(v); } template -eve::Result sensingFailure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - code, std::move(message), std::move(path), {}, "sensing")); +eve::Result sensingFailure(eve::DiagnosticCode code, std::string message, std::string path = {}) { + return eve::Result::failure(eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "sensing")); } template ssq::Table makeOwnedProxy(HSQUIRRELVM vm, eve::Result&& reference, Release&& release) { - if (!reference) - return eve::script::projectStatusResult(vm, reference.status(), false, false); + if (!reference) return eve::script::projectStatusResult(vm, reference.status(), false, false); - const Ref ref = std::move(reference).takeValue(); - auto object = eve::script::makeOwnedSquirrelInstance( - vm, std::make_unique(ref)); + const Ref ref = std::move(reference).takeValue(); + auto object = eve::script::makeOwnedSquirrelInstance(vm, std::make_unique(ref)); if (!object) { const eve::Status status = object.status(); object.ignore("failed to create owned sensing proxy"); - std::invoke(std::forward(release), ref).ignore( - "rollback failed owned sensing allocation"); + std::invoke(std::forward(release), ref).ignore("rollback failed owned sensing allocation"); return eve::script::projectStatusResult(vm, status, false, false); } ssq::Object owned = std::move(object).takeValue(); - auto result = eve::script::projectStatusResult( - vm, eve::Status::success(eve::StatusCode::Applied), true, false); + auto result = eve::script::projectStatusResult(vm, eve::Status::success(eve::StatusCode::Applied), true, false); result.set("value", owned); result.set("ownership", std::string("owned")); result.set("ownerEpoch", static_cast(ref.ownerEpoch)); @@ -72,8 +66,8 @@ ssq::Table makeOwnedProxy(HSQUIRRELVM vm, eve::Result&& reference, Release& return result; } } // namespace -eve::Result SensingWorld::upsert(std::string_view id, float x, float y, std::string_view f, - std::string_view t, std::string_view v) { +eve::Result SensingWorld::upsert(std::string_view id, float x, float y, std::string_view f, std::string_view t, + std::string_view v) { if (id.empty() || !finite(x) || !finite(y)) { return sensingFailure(eve::DiagnosticCode::InvalidArgument, "subject id and finite coordinates are required", "subject"); @@ -84,8 +78,7 @@ eve::Result SensingWorld::upsert(std::string_view id, float x, float y, st eve::Result SensingWorld::remove(std::string_view id) { results_.clear(); if (subjects_.erase(std::string(id)) == 0) - return sensingFailure(eve::DiagnosticCode::NotFound, - "subject is not registered", "subject.id"); + return sensingFailure(eve::DiagnosticCode::NotFound, "subject is not registered", "subject.id"); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } bool SensingWorld::accepts(const Subject& s, const std::set& req, const std::set& exc, @@ -106,14 +99,12 @@ int SensingWorld::finish(float x, float y, int limit) { if (limit >= 0 && size_t(limit) < results_.size()) results_.resize(size_t(limit)); return int(results_.size()); } -eve::Result SensingWorld::circle(float x, float y, float radius, std::string_view r, - std::string_view e, std::string_view i, std::string_view ef, - std::string_view v, int limit) { +eve::Result SensingWorld::circle(float x, float y, float radius, std::string_view r, std::string_view e, + std::string_view i, std::string_view ef, std::string_view v, int limit) { results_.clear(); if (!finite(x) || !finite(y) || !finite(radius) || radius < 0 || limit < 0) { return sensingFailure(eve::DiagnosticCode::InvalidArgument, - "circle query requires finite coordinates, non-negative radius and limit", - "query"); + "circle query requires finite coordinates, non-negative radius and limit", "query"); } auto R = csv(r), E = csv(e), I = csv(i), F = csv(ef); for (auto& [id, s] : subjects_) @@ -121,9 +112,8 @@ eve::Result SensingWorld::circle(float x, float y, float radius, std::strin results_.push_back({id, s.x, s.y, 0}); return eve::Result::success(finish(x, y, limit)); } -eve::Result SensingWorld::box(float a, float b, float c, float d, std::string_view r, - std::string_view e, std::string_view i, std::string_view ef, - std::string_view v, int limit) { +eve::Result SensingWorld::box(float a, float b, float c, float d, std::string_view r, std::string_view e, + std::string_view i, std::string_view ef, std::string_view v, int limit) { results_.clear(); if (!finite(a) || !finite(b) || !finite(c) || !finite(d) || a > c || b > d || limit < 0) { return sensingFailure(eve::DiagnosticCode::InvalidArgument, @@ -137,9 +127,8 @@ eve::Result SensingWorld::box(float a, float b, float c, float d, std::stri return eve::Result::success(finish(x, y, limit)); } eve::OptionalRef SensingWorld::resultAt(int i) const { - return i >= 0 && size_t(i) < results_.size() - ? eve::OptionalRef(std::cref(results_[size_t(i)])) - : eve::OptionalRef{}; + return i >= 0 && size_t(i) < results_.size() ? eve::OptionalRef(std::cref(results_[size_t(i)])) + : eve::OptionalRef{}; } std::string SensingWorld::snapshotJson() const { std::ostringstream o; @@ -192,8 +181,7 @@ eve::script::Borrowed Sensing::resolve(SensingWorldHandleRef refer eve::Result Sensing::release(SensingWorldHandleRef reference) { Sensing* module = ModuleManager::getInstance("Sensing"); if (!module) - return sensingFailure(eve::DiagnosticCode::StaleHandle, - "Sensing module is no longer loaded", "world"); + return sensingFailure(eve::DiagnosticCode::StaleHandle, "Sensing module is no longer loaded", "world"); return module->worlds_.erase(reference); } @@ -205,9 +193,7 @@ bool Sensing::isStale(SensingWorldHandleRef reference) noexcept { struct ScriptSensingWorld { explicit ScriptSensingWorld(SensingWorldHandleRef value) : reference(value) {} - ~ScriptSensingWorld() noexcept { - Sensing::release(reference).ignore("script sensing world proxy destruction"); - } + ~ScriptSensingWorld() noexcept { Sensing::release(reference).ignore("script sensing world proxy destruction"); } SensingWorldHandleRef reference; }; @@ -219,8 +205,8 @@ void Sensing::expose(ssq::Table& t) { c.addFunc("getY", [](Candidate* v) { return v ? v->y : 0.f; }); c.addFunc("getDistance", [](Candidate* v) { return v ? v->distance : 0.f; }); const HSQUIRRELVM vm = t.getHandle(); - auto w = t.addClass("SensingWorld", - std::function([]() { return nullptr; }), false); + auto w = t.addClass("SensingWorld", + std::function([]() { return nullptr; }), false); w.addFunc("ownership", [](ScriptSensingWorld*) { return std::string("owned"); }); w.addFunc("ownerEpoch", [](ScriptSensingWorld* value) { return value ? static_cast(value->reference.ownerEpoch) : std::int64_t{0}; @@ -228,40 +214,35 @@ void Sensing::expose(ssq::Table& t) { w.addFunc("handle", [](ScriptSensingWorld* value) { return value ? static_cast(value->reference.packed()) : std::int64_t{0}; }); - w.addFunc("isStale", [](ScriptSensingWorld* value) { - return !value || Sensing::isStale(value->reference); - }); + w.addFunc("isStale", [](ScriptSensingWorld* value) { return !value || Sensing::isStale(value->reference); }); w.addFunc("release", [vm](ScriptSensingWorld* value) { if (!value) return eve::script::projectResult( - vm, sensingFailure(eve::DiagnosticCode::InvalidArgument, - "sensing world proxy must not be null", "world")); + vm, sensingFailure(eve::DiagnosticCode::InvalidArgument, "sensing world proxy must not be null", + "world")); return eve::script::projectResult(vm, Sensing::release(value->reference)); }); w.addFunc("upsert", [vm](ScriptSensingWorld* value, const std::string& id, float x, float y, - const std::string& faction, const std::string& tags, - const std::string& visibleTo) { + const std::string& faction, const std::string& tags, const std::string& visibleTo) { if (!value) return eve::script::projectResult( - vm, sensingFailure(eve::DiagnosticCode::InvalidArgument, - "sensing world proxy must not be null", "world")); + vm, sensingFailure(eve::DiagnosticCode::InvalidArgument, "sensing world proxy must not be null", + "world")); auto world = Sensing::resolve(value->reference); if (!world.isBound()) return eve::script::projectResult( - vm, sensingFailure(eve::DiagnosticCode::StaleHandle, - "sensing world handle is stale", "world")); + vm, sensingFailure(eve::DiagnosticCode::StaleHandle, "sensing world handle is stale", "world")); return eve::script::projectResult(vm, world->upsert(id, x, y, faction, tags, visibleTo)); }); w.addFunc("remove", [vm](ScriptSensingWorld* value, const std::string& id) { if (!value) return eve::script::projectResult( - vm, sensingFailure(eve::DiagnosticCode::InvalidArgument, - "sensing world proxy must not be null", "world")); + vm, sensingFailure(eve::DiagnosticCode::InvalidArgument, "sensing world proxy must not be null", + "world")); auto world = Sensing::resolve(value->reference); if (!world.isBound()) return eve::script::projectResult( - vm, sensingFailure(eve::DiagnosticCode::StaleHandle, - "sensing world handle is stale", "world")); + vm, sensingFailure(eve::DiagnosticCode::StaleHandle, "sensing world handle is stale", "world")); return eve::script::projectResult(vm, world->remove(id)); }); auto projectCount = [vm](eve::Result&& result) { @@ -269,42 +250,38 @@ void Sensing::expose(ssq::Table& t) { [](int count) { return eve::Value(static_cast(count)); }); }; w.addFunc("circle", [vm, projectCount](ScriptSensingWorld* value, float x, float y, float radius, - const std::string& required, const std::string& excluded, - const std::string& includedFactions, - const std::string& excludedFactions, const std::string& visibleTo, - int limit) { + const std::string& required, const std::string& excluded, + const std::string& includedFactions, const std::string& excludedFactions, + const std::string& visibleTo, int limit) { if (!value) - return eve::script::projectResult( - vm, sensingFailure(eve::DiagnosticCode::InvalidArgument, - "sensing world proxy must not be null", "world"), - [](int count) { return eve::Value(static_cast(count)); }); + return eve::script::projectResult(vm, + sensingFailure(eve::DiagnosticCode::InvalidArgument, + "sensing world proxy must not be null", "world"), + [](int count) { return eve::Value(static_cast(count)); }); auto world = Sensing::resolve(value->reference); if (!world.isBound()) return eve::script::projectResult( - vm, sensingFailure(eve::DiagnosticCode::StaleHandle, - "sensing world handle is stale", "world"), + vm, sensingFailure(eve::DiagnosticCode::StaleHandle, "sensing world handle is stale", "world"), [](int count) { return eve::Value(static_cast(count)); }); - return projectCount(world->circle(x, y, radius, required, excluded, includedFactions, - excludedFactions, visibleTo, limit)); + return projectCount( + world->circle(x, y, radius, required, excluded, includedFactions, excludedFactions, visibleTo, limit)); }); - w.addFunc("box", [vm, projectCount](ScriptSensingWorld* value, float minX, float minY, float maxX, - float maxY, const std::string& required, const std::string& excluded, - const std::string& includedFactions, - const std::string& excludedFactions, const std::string& visibleTo, - int limit) { + w.addFunc("box", [vm, projectCount](ScriptSensingWorld* value, float minX, float minY, float maxX, float maxY, + const std::string& required, const std::string& excluded, + const std::string& includedFactions, const std::string& excludedFactions, + const std::string& visibleTo, int limit) { if (!value) - return eve::script::projectResult( - vm, sensingFailure(eve::DiagnosticCode::InvalidArgument, - "sensing world proxy must not be null", "world"), - [](int count) { return eve::Value(static_cast(count)); }); + return eve::script::projectResult(vm, + sensingFailure(eve::DiagnosticCode::InvalidArgument, + "sensing world proxy must not be null", "world"), + [](int count) { return eve::Value(static_cast(count)); }); auto world = Sensing::resolve(value->reference); if (!world.isBound()) return eve::script::projectResult( - vm, sensingFailure(eve::DiagnosticCode::StaleHandle, - "sensing world handle is stale", "world"), + vm, sensingFailure(eve::DiagnosticCode::StaleHandle, "sensing world handle is stale", "world"), [](int count) { return eve::Value(static_cast(count)); }); - return projectCount(world->box(minX, minY, maxX, maxY, required, excluded, includedFactions, - excludedFactions, visibleTo, limit)); + return projectCount(world->box(minX, minY, maxX, maxY, required, excluded, includedFactions, excludedFactions, + visibleTo, limit)); }); w.addFunc("resultAt", [](ScriptSensingWorld* value, int i) -> Candidate* { if (!value) return nullptr; @@ -316,29 +293,28 @@ void Sensing::expose(ssq::Table& t) { w.addFunc("snapshotJson", [vm](ScriptSensingWorld* value) { if (!value) return eve::script::projectResult( - vm, sensingFailure(eve::DiagnosticCode::InvalidArgument, - "sensing world proxy must not be null", "world"), + vm, + sensingFailure(eve::DiagnosticCode::InvalidArgument, + "sensing world proxy must not be null", "world"), [](std::string text) { return eve::Value(std::move(text)); }); auto world = Sensing::resolve(value->reference); if (!world.isBound()) return eve::script::projectResult( - vm, sensingFailure(eve::DiagnosticCode::StaleHandle, - "sensing world handle is stale", "world"), + vm, + sensingFailure(eve::DiagnosticCode::StaleHandle, "sensing world handle is stale", "world"), [](std::string text) { return eve::Value(std::move(text)); }); - return eve::script::projectResult( - vm, eve::Result::success(world->snapshotJson()), - [](std::string text) { return eve::Value(std::move(text)); }); + return eve::script::projectResult(vm, eve::Result::success(world->snapshotJson()), + [](std::string text) { return eve::Value(std::move(text)); }); }); w.addFunc("restoreJson", [vm](ScriptSensingWorld* value, const std::string& json) { if (!value) return eve::script::projectResult( - vm, sensingFailure(eve::DiagnosticCode::InvalidArgument, - "sensing world proxy must not be null", "world")); + vm, sensingFailure(eve::DiagnosticCode::InvalidArgument, "sensing world proxy must not be null", + "world")); auto world = Sensing::resolve(value->reference); if (!world.isBound()) return eve::script::projectResult( - vm, sensingFailure(eve::DiagnosticCode::StaleHandle, - "sensing world handle is stale", "world")); + vm, sensingFailure(eve::DiagnosticCode::StaleHandle, "sensing world handle is stale", "world")); return eve::script::projectResult(vm, world->restoreJson(json)); }); auto cls = t.addClass(name, Sensing::create, false); @@ -347,9 +323,8 @@ void Sensing::expose(ssq::Table& t) { void Sensing::expose(ssq::Class& c) { c.addFunc("getName", &Sensing::getName); c.addFunc("newWorld", [vm = c.getHandle()](Sensing*) { - return makeOwnedProxy< SensingWorldHandleRef, ScriptSensingWorld >( - vm, Sensing::newWorld(), - [](SensingWorldHandleRef reference) { return Sensing::release(reference); }); + return makeOwnedProxy( + vm, Sensing::newWorld(), [](SensingWorldHandleRef reference) { return Sensing::release(reference); }); }); } } // namespace eve::sensing diff --git a/src/modules/sensing/Sensing.h b/src/modules/sensing/Sensing.h index 83914bd9..1881e9b9 100644 --- a/src/modules/sensing/Sensing.h +++ b/src/modules/sensing/Sensing.h @@ -60,8 +60,8 @@ public: [[nodiscard]] eve::Result restoreJson(const std::string& json); private: - bool accepts(const Subject&, const std::set&, const std::set&, - const std::set&, const std::set&, std::string_view) const; + bool accepts(const Subject&, const std::set&, const std::set&, + const std::set&, const std::set&, std::string_view) const; int finish(float x, float y, int limit); std::map subjects_; std::vector results_; diff --git a/src/modules/sensing/Targeting.cpp b/src/modules/sensing/Targeting.cpp index 23ae4ff0..b66d541e 100644 --- a/src/modules/sensing/Targeting.cpp +++ b/src/modules/sensing/Targeting.cpp @@ -35,34 +35,37 @@ bool isGridSpace(CoordinateSpace space) noexcept { } bool sameSpace(const TargetLocation& a, const TargetLocation& b) noexcept { - return std::visit([](const auto& left, const auto& right) { - return left.isValid() && right.isValid() && left.space() == right.space(); - }, a, b); + return std::visit( + [](const auto& left, const auto& right) { + return left.isValid() && right.isValid() && left.space() == right.space(); + }, + a, b); } bool sameSpace(const TargetLocation& location, CoordinateSpace space) noexcept { - return std::visit([space](const auto& value) { return value.isValid() && value.space() == space; }, - location); + return std::visit([space](const auto& value) { return value.isValid() && value.space() == space; }, location); } double distanceSquared(const TargetLocation& a, const TargetLocation& b) noexcept { - return std::visit([](const auto& left, const auto& right) -> double { - if constexpr (std::is_same_v, WorldPoint> && - std::is_same_v, WorldPoint>) { - const double dx = static_cast(left.x()) - right.x(); - const double dy = static_cast(left.y()) - right.y(); - const double dz = static_cast(left.z()) - right.z(); - return dx * dx + dy * dy + dz * dz; - } else if constexpr (std::is_same_v, GridPoint> && - std::is_same_v, GridPoint>) { - const double dx = static_cast(left.x()) - right.x(); - const double dy = static_cast(left.y()) - right.y(); - const double dz = static_cast(left.z()) - right.z(); - return dx * dx + dy * dy + dz * dz; - } else { - return std::numeric_limits::infinity(); - } - }, a, b); + return std::visit( + [](const auto& left, const auto& right) -> double { + if constexpr (std::is_same_v, WorldPoint> && + std::is_same_v, WorldPoint>) { + const double dx = static_cast(left.x()) - right.x(); + const double dy = static_cast(left.y()) - right.y(); + const double dz = static_cast(left.z()) - right.z(); + return dx * dx + dy * dy + dz * dz; + } else if constexpr (std::is_same_v, GridPoint> && + std::is_same_v, GridPoint>) { + const double dx = static_cast(left.x()) - right.x(); + const double dy = static_cast(left.y()) - right.y(); + const double dz = static_cast(left.z()) - right.z(); + return dx * dx + dy * dy + dz * dz; + } else { + return std::numeric_limits::infinity(); + } + }, + a, b); } bool contains(const TargetingSpec& spec, const TargetLocation& location) noexcept { @@ -99,14 +102,12 @@ bool matches(const TargetingQuery& query, const TargetCandidate& candidate) { if (!inZone(candidate, query.spec.zone) || !contains(query.spec, candidate.location)) return false; const double distance = std::sqrt(distanceSquared(query.originLocation, candidate.location)); - return distance >= static_cast(query.spec.minRange) && - distance <= static_cast(query.spec.maxRange); + return distance >= static_cast(query.spec.minRange) && distance <= static_cast(query.spec.maxRange); } Result validateWorldPair(WorldPoint minimum, WorldPoint maximum, CoordinateSpace expected, const char* operation) { - if (!minimum.isValid() || !maximum.isValid() || minimum.space() != expected || - maximum.space() != expected) + if (!minimum.isValid() || !maximum.isValid() || minimum.space() != expected || maximum.space() != expected) return failure(DiagnosticCode::InvalidArgument, std::string(operation) + " requires two points in the same coordinate space"); if (minimum.x() > maximum.x() || minimum.y() > maximum.y() || @@ -116,10 +117,8 @@ Result validateWorldPair(WorldPoint minimum, WorldPoint maximum, Coordinat return Result::success(); } -Result validateGridPair(GridPoint minimum, GridPoint maximum, CoordinateSpace expected, - const char* operation) { - if (!minimum.isValid() || !maximum.isValid() || minimum.space() != expected || - maximum.space() != expected) +Result validateGridPair(GridPoint minimum, GridPoint maximum, CoordinateSpace expected, const char* operation) { + if (!minimum.isValid() || !maximum.isValid() || minimum.space() != expected || maximum.space() != expected) return failure(DiagnosticCode::InvalidArgument, std::string(operation) + " requires two points in the same grid space"); if (minimum.x() > maximum.x() || minimum.y() > maximum.y() || @@ -185,14 +184,13 @@ Result WorldArea::box3D(WorldPoint minimum, WorldPoint maximum) { bool WorldArea::contains(WorldPoint point) const noexcept { if (!valid_ || !point.isValid() || point.space() != space_) return false; if (shape_ == Shape::Circle2D || shape_ == Shape::Sphere3D) { - const double dx = static_cast(point.x()) - first_.x(); - const double dy = static_cast(point.y()) - first_.y(); - const double dz = static_cast(point.z()) - first_.z(); + const double dx = static_cast(point.x()) - first_.x(); + const double dy = static_cast(point.y()) - first_.y(); + const double dz = static_cast(point.z()) - first_.z(); const double distance = dx * dx + dy * dy + dz * dz; return distance <= static_cast(radius_) * radius_; } - return point.x() >= first_.x() && point.x() <= second_.x() && point.y() >= first_.y() && - point.y() <= second_.y() && + return point.x() >= first_.x() && point.x() <= second_.x() && point.y() >= first_.y() && point.y() <= second_.y() && (shape_ == Shape::Box2D || (point.z() >= first_.z() && point.z() <= second_.z())); } @@ -229,19 +227,17 @@ Result TargetingSpec::validate() const { if (minCount > maxCount) return failure(DiagnosticCode::InvalidArgument, "TargetingSpec minCount exceeds maxCount"); if (!std::isfinite(minRange) || - (!(std::isfinite(maxRange) || maxRange == std::numeric_limits::infinity())) || - minRange < 0.f || maxRange < minRange) + (!(std::isfinite(maxRange) || maxRange == std::numeric_limits::infinity())) || minRange < 0.f || + maxRange < minRange) return failure(DiagnosticCode::InvalidArgument, "TargetingSpec range must be finite and ordered"); if (zone && !zone->isValid()) return failure(DiagnosticCode::InvalidArgument, "TargetingSpec zone must be valid"); for (const auto& tag : requiredTags) if (tag.empty()) - return failure(DiagnosticCode::InvalidArgument, - "requiredTags cannot contain empty keys"); + return failure(DiagnosticCode::InvalidArgument, "requiredTags cannot contain empty keys"); for (const auto& tag : excludedTags) if (tag.empty()) - return failure(DiagnosticCode::InvalidArgument, - "excludedTags cannot contain empty keys"); + return failure(DiagnosticCode::InvalidArgument, "excludedTags cannot contain empty keys"); return Result::success(); } @@ -249,8 +245,7 @@ Result TargetingQuery::validate() const { auto specResult = spec.validate(); if (!specResult) return failure(specResult.status()); if (!origin.isValid()) - return failure(DiagnosticCode::InvalidArgument, - "TargetingQuery origin must be non-nil"); + return failure(DiagnosticCode::InvalidArgument, "TargetingQuery origin must be non-nil"); if (!sameSpace(originLocation, spec.space)) return failure(DiagnosticCode::InvalidArgument, "TargetingQuery origin and spec use different coordinate spaces"); @@ -278,11 +273,9 @@ Result TargetSet::setPoint(TargetLocation point) { return failure(DiagnosticCode::InvalidArgument, "TargetSet point must be valid"); if (point_ && !sameSpace(*point_, point)) return failure(DiagnosticCode::InvalidArgument, "TargetSet points cannot mix coordinate spaces"); - if (worldArea_ && (!std::get_if(&point) || - std::get(point).space() != worldArea_->space())) + if (worldArea_ && (!std::get_if(&point) || std::get(point).space() != worldArea_->space())) return failure(DiagnosticCode::InvalidArgument, "TargetSet point and world area use different spaces"); - if (gridArea_ && (!std::get_if(&point) || - std::get(point).space() != gridArea_->space())) + if (gridArea_ && (!std::get_if(&point) || std::get(point).space() != gridArea_->space())) return failure(DiagnosticCode::InvalidArgument, "TargetSet point and grid area use different spaces"); point_ = std::move(point); return Result::success(Status::success(StatusCode::Applied)); @@ -291,8 +284,7 @@ Result TargetSet::setPoint(TargetLocation point) { Result TargetSet::setArea(WorldArea area) { if (!area.isValid()) return failure(DiagnosticCode::InvalidArgument, "TargetSet world area must be valid"); if (gridArea_) return failure(DiagnosticCode::InvalidArgument, "TargetSet cannot mix world and grid areas"); - if (point_ && (!std::get_if(&*point_) || - std::get(*point_).space() != area.space())) + if (point_ && (!std::get_if(&*point_) || std::get(*point_).space() != area.space())) return failure(DiagnosticCode::InvalidArgument, "TargetSet point and world area use different spaces"); worldArea_ = std::move(area); return Result::success(Status::success(StatusCode::Applied)); @@ -301,8 +293,7 @@ Result TargetSet::setArea(WorldArea area) { Result TargetSet::setArea(GridArea area) { if (!area.isValid()) return failure(DiagnosticCode::InvalidArgument, "TargetSet grid area must be valid"); if (worldArea_) return failure(DiagnosticCode::InvalidArgument, "TargetSet cannot mix world and grid areas"); - if (point_ && (!std::get_if(&*point_) || - std::get(*point_).space() != area.space())) + if (point_ && (!std::get_if(&*point_) || std::get(*point_).space() != area.space())) return failure(DiagnosticCode::InvalidArgument, "TargetSet point and grid area use different spaces"); gridArea_ = std::move(area); return Result::success(Status::success(StatusCode::Applied)); @@ -315,8 +306,7 @@ Result SensingCandidateProvider::upsert(TargetCandidate candidate) { return failure(DiagnosticCode::InvalidArgument, "candidate location must be valid"); for (const auto& tag : candidate.tags) if (tag.empty()) - return failure(DiagnosticCode::InvalidArgument, - "candidate tags cannot contain empty keys"); + return failure(DiagnosticCode::InvalidArgument, "candidate tags cannot contain empty keys"); for (const auto& zone : candidate.zones) if (!zone.isValid()) return failure(DiagnosticCode::InvalidArgument, "candidate zones must be valid"); candidates_[candidate.subject.format()] = std::move(candidate); @@ -385,10 +375,10 @@ Result TargetingResolver::resolve(const TargetingQuery& query) const std::string_view coordinateSpaceName(CoordinateSpace space) noexcept { switch (space) { - case CoordinateSpace::World2D: return "world2d"; - case CoordinateSpace::World3D: return "world3d"; - case CoordinateSpace::Grid2D: return "grid2d"; - case CoordinateSpace::Grid3D: return "grid3d"; + case CoordinateSpace::World2D: return "world2d"; + case CoordinateSpace::World3D: return "world3d"; + case CoordinateSpace::Grid2D: return "grid2d"; + case CoordinateSpace::Grid3D: return "grid3d"; } return "unknown"; } diff --git a/src/modules/sensing/Targeting.h b/src/modules/sensing/Targeting.h index 1cd5aa7e..243ca254 100644 --- a/src/modules/sensing/Targeting.h +++ b/src/modules/sensing/Targeting.h @@ -144,10 +144,10 @@ private: : space_(space), x_(x), y_(y), z_(z), valid_(true) {} CoordinateSpace space_ = CoordinateSpace::World2D; - float x_ = 0.f; - float y_ = 0.f; - float z_ = 0.f; - bool valid_ = false; + float x_ = 0.f; + float y_ = 0.f; + float z_ = 0.f; + bool valid_ = false; }; /** @@ -184,10 +184,10 @@ private: : space_(space), x_(x), y_(y), z_(z), valid_(true) {} CoordinateSpace space_ = CoordinateSpace::Grid2D; - std::int32_t x_ = 0; - std::int32_t y_ = 0; - std::int32_t z_ = 0; - bool valid_ = false; + std::int32_t x_ = 0; + std::int32_t y_ = 0; + std::int32_t z_ = 0; + bool valid_ = false; }; /** @brief Either a world point or a grid point, with the tag retained. */ @@ -226,15 +226,14 @@ public: private: WorldArea(Shape shape, WorldPoint first, WorldPoint second, float radius) noexcept - : shape_(shape), space_(first.space()), first_(first), second_(second), radius_(radius), - valid_(true) {} + : shape_(shape), space_(first.space()), first_(first), second_(second), radius_(radius), valid_(true) {} - Shape shape_ = Shape::Circle2D; + Shape shape_ = Shape::Circle2D; CoordinateSpace space_ = CoordinateSpace::World2D; - WorldPoint first_; - WorldPoint second_; - float radius_ = 0.f; - bool valid_ = false; + WorldPoint first_; + WorldPoint second_; + float radius_ = 0.f; + bool valid_ = false; }; /** @@ -267,11 +266,11 @@ private: GridArea(Shape shape, GridPoint minimum, GridPoint maximum) noexcept : shape_(shape), space_(minimum.space()), minimum_(minimum), maximum_(maximum), valid_(true) {} - Shape shape_ = Shape::Box2D; + Shape shape_ = Shape::Box2D; CoordinateSpace space_ = CoordinateSpace::Grid2D; - GridPoint minimum_; - GridPoint maximum_; - bool valid_ = false; + GridPoint minimum_; + GridPoint maximum_; + bool valid_ = false; }; /** @brief A checked target-count and spatial constraint description. */ @@ -368,11 +367,11 @@ public: [[nodiscard]] const std::optional& gridArea() const noexcept { return gridArea_; } private: - std::vector subjects_; - std::optional primary_; + std::vector subjects_; + std::optional primary_; std::optional point_; - std::optional worldArea_; - std::optional gridArea_; + std::optional worldArea_; + std::optional gridArea_; }; /** @brief Result of one injected line-of-sight query. */ @@ -393,7 +392,7 @@ struct LineOfSightResult { class ILineOfSightQuery { public: static constexpr const char* capabilityName = "eve.sensing.ILineOfSightQuery"; - virtual ~ILineOfSightQuery() = default; + virtual ~ILineOfSightQuery() = default; /** * @brief Tests the segment between two same-space locations. @@ -403,7 +402,7 @@ public: * @remarks Synchronous; callers must invoke on the provider's documented simulation thread. */ [[nodiscard]] virtual Result query(const TargetLocation& from, - const TargetLocation& to) const = 0; + const TargetLocation& to) const = 0; }; /** @@ -415,7 +414,7 @@ public: class ISensingCandidateProvider { public: static constexpr const char* capabilityName = "eve.sensing.ISensingCandidateProvider"; - virtual ~ISensingCandidateProvider() = default; + virtual ~ISensingCandidateProvider() = default; /** * @brief Returns broad-phase candidates satisfying the coordinate and spatial constraints. @@ -436,7 +435,7 @@ public: class SensingCandidateProvider final : public ISensingCandidateProvider { public: /** @brief Creates an empty provider; registration with capability is explicit. */ - SensingCandidateProvider() = default; + SensingCandidateProvider() = default; ~SensingCandidateProvider() override = default; /** @brief Adds or replaces one candidate using its SubjectRef as the key. */ diff --git a/src/modules/settlement/Settlement.cpp b/src/modules/settlement/Settlement.cpp index 81d49f2d..a9b863a1 100644 --- a/src/modules/settlement/Settlement.cpp +++ b/src/modules/settlement/Settlement.cpp @@ -11,8 +11,7 @@ namespace { template eve::Result failure(eve::DiagnosticCode code, std::string message, std::string path = {}) { - return eve::Result::failure( - eve::Diagnostic::error(code, std::move(message), std::move(path))); + return eve::Result::failure(eve::Diagnostic::error(code, std::move(message), std::move(path))); } template @@ -27,18 +26,16 @@ eve::Result success(eve::StatusCode code = eve::StatusCode::Ok) { eve::Result validateFiniteNonNegative(double value, std::string_view name) { if (!std::isfinite(value) || value < 0.0) return failure(eve::DiagnosticCode::InvalidArgument, - std::string(name) + " must be finite and non-negative", - std::string(name)); + std::string(name) + " must be finite and non-negative", std::string(name)); return eve::Result::success(); } eve::Result validateRequest(const SettlementRequest& request) { if (!request.target.isValid()) - return failure(eve::DiagnosticCode::InvalidArgument, - "settlement target must be a valid SubjectRef", "target"); + return failure(eve::DiagnosticCode::InvalidArgument, "settlement target must be a valid SubjectRef", + "target"); if (request.kind.empty()) - return failure(eve::DiagnosticCode::InvalidArgument, - "settlement kind must not be empty", "kind"); + return failure(eve::DiagnosticCode::InvalidArgument, "settlement kind must not be empty", "kind"); auto magnitude = validateFiniteNonNegative(request.magnitude, "magnitude"); if (!magnitude) { @@ -47,11 +44,10 @@ eve::Result validateRequest(const SettlementRequest& request) { } if (!request.causation.isCanonical()) return failure(eve::DiagnosticCode::InvalidArgument, - "settlement causation must use a canonical event or command id", - "causation"); + "settlement causation must use a canonical event or command id", "causation"); if (!request.correlation.isCanonical()) - return failure(eve::DiagnosticCode::InvalidArgument, - "settlement correlation must use a canonical id", "correlation"); + return failure(eve::DiagnosticCode::InvalidArgument, "settlement correlation must use a canonical id", + "correlation"); return eve::Result::success(); } @@ -78,10 +74,9 @@ eve::Diagnostic stageDiagnostic(std::string_view stage, const eve::Status& statu eve::DiagnosticDetails details; details.emplace_back("stage", std::string(stage)); details.emplace_back("status", std::string(eve::statusCodeName(status.code()))); - return eve::Diagnostic::error( - eve::DiagnosticCode::CallbackFailure, - "settlement stage failed: " + std::string(stage), - "settlement.stage." + std::string(stage), std::move(details)); + return eve::Diagnostic::error(eve::DiagnosticCode::CallbackFailure, + "settlement stage failed: " + std::string(stage), + "settlement.stage." + std::string(stage), std::move(details)); } eve::Status stageFailureStatus(std::string_view stage, const eve::Status& status) { @@ -92,24 +87,23 @@ eve::Status stageFailureStatus(std::string_view stage, const eve::Status& status eve::Result makeEvent(const SettlementContext& context) { const auto& request = context.request(); - const auto& result = context.projectedResult(); + const auto& result = context.projectedResult(); auto schema = eve::LogicalId::parse("settlement:result"); if (!schema) - return failure( - eve::DiagnosticCode::InvariantViolation, - "settlement result schema id is not a valid LogicalId", "event.schemaId"); + return failure(eve::DiagnosticCode::InvariantViolation, + "settlement result schema id is not a valid LogicalId", "event.schemaId"); eve::Value::Object payload; - payload["kind"] = request.kind; - payload["requested"] = result.requested; - payload["applied"] = result.applied; - payload["absorbed"] = result.absorbed; - payload["resisted"] = result.resisted; - payload["clamped"] = result.clamped; - payload["critical"] = result.critical; - payload["tick"] = static_cast(result.tick.value()); - payload["context"] = request.context; + payload["kind"] = request.kind; + payload["requested"] = result.requested; + payload["applied"] = result.applied; + payload["absorbed"] = result.absorbed; + payload["resisted"] = result.resisted; + payload["clamped"] = result.clamped; + payload["critical"] = result.critical; + payload["tick"] = static_cast(result.tick.value()); + payload["context"] = request.context; eve::Value::Array tags; tags.reserve(request.tags.size()); @@ -120,11 +114,11 @@ eve::Result makeEvent(const SettlementContext& context) { stages.reserve(result.stages.size()); for (const auto& stage : result.stages) { eve::Value::Object stageValue; - stageValue["name"] = stage.name; - stageValue["kind"] = stageKindName(stage.kind); - stageValue["status"] = std::string(eve::statusCodeName(stage.status)); - stageValue["before"] = stage.before; - stageValue["after"] = stage.after; + stageValue["name"] = stage.name; + stageValue["kind"] = stageKindName(stage.kind); + stageValue["status"] = std::string(eve::statusCodeName(stage.status)); + stageValue["before"] = stage.before; + stageValue["after"] = stage.after; stageValue["details"] = stage.details; stages.emplace_back(eve::Value(std::move(stageValue))); } @@ -154,34 +148,22 @@ eve::Result makeEvent(const SettlementContext& context) { const char* stageKindName(StageKind kind) noexcept { switch (kind) { - case StageKind::Validate: - return "validate"; - case StageKind::SourceModifiers: - return "source_modifiers"; - case StageKind::TargetMitigation: - return "target_mitigation"; - case StageKind::ArmorShield: - return "armor_shield"; - case StageKind::Clamp: - return "clamp"; - case StageKind::Apply: - return "apply"; - case StageKind::Event: - return "event"; - case StageKind::Trigger: - return "trigger"; + case StageKind::Validate: return "validate"; + case StageKind::SourceModifiers: return "source_modifiers"; + case StageKind::TargetMitigation: return "target_mitigation"; + case StageKind::ArmorShield: return "armor_shield"; + case StageKind::Clamp: return "clamp"; + case StageKind::Apply: return "apply"; + case StageKind::Event: return "event"; + case StageKind::Trigger: return "trigger"; } return "unknown"; } -bool SettlementResult::hasStage(std::string_view name) const noexcept { - return stage(name) != nullptr; -} +bool SettlementResult::hasStage(std::string_view name) const noexcept { return stage(name) != nullptr; } const SettlementStageResult* SettlementResult::stage(std::string_view name) const noexcept { - const auto it = std::find_if(stages.begin(), stages.end(), [&](const auto& value) { - return value.name == name; - }); + const auto it = std::find_if(stages.begin(), stages.end(), [&](const auto& value) { return value.name == name; }); return it == stages.end() ? nullptr : &*it; } @@ -189,8 +171,10 @@ PreparedApply::PreparedApply(CommitFunction commit, RollbackFunction rollback) : commit_(std::move(commit)), rollback_(std::move(rollback)) {} PreparedApply::PreparedApply(PreparedApply&& other) noexcept - : commit_(std::move(other.commit_)), rollback_(std::move(other.rollback_)), - committed_(other.committed_), rolledBack_(other.rolledBack_) { + : commit_(std::move(other.commit_)), + rollback_(std::move(other.rollback_)), + committed_(other.committed_), + rolledBack_(other.rolledBack_) { other.commit_ = {}; other.rollback_ = {}; other.committed_ = true; @@ -200,10 +184,10 @@ PreparedApply::PreparedApply(PreparedApply&& other) noexcept PreparedApply& PreparedApply::operator=(PreparedApply&& other) noexcept { if (this == &other) return *this; rollback(); - commit_ = std::move(other.commit_); - rollback_ = std::move(other.rollback_); - committed_ = other.committed_; - rolledBack_ = other.rolledBack_; + commit_ = std::move(other.commit_); + rollback_ = std::move(other.rollback_); + committed_ = other.committed_; + rolledBack_ = other.rolledBack_; other.commit_ = {}; other.rollback_ = {}; other.committed_ = true; @@ -228,8 +212,8 @@ eve::Result PreparedApply::commit() { "settlement mutation is empty or already rolled back", "apply"); try { - auto outcome = commit_(); - const bool passed = outcome.ok(); + auto outcome = commit_(); + const bool passed = outcome.ok(); if (passed) { committed_ = true; return outcome; @@ -238,11 +222,10 @@ eve::Result PreparedApply::commit() { return failure(status); } catch (const std::exception& exception) { return failure(eve::DiagnosticCode::Failed, - std::string("settlement apply commit threw: ") + exception.what(), - "apply"); + std::string("settlement apply commit threw: ") + exception.what(), "apply"); } catch (...) { - return failure(eve::DiagnosticCode::Failed, - "settlement apply commit threw an unknown exception", "apply"); + return failure(eve::DiagnosticCode::Failed, "settlement apply commit threw an unknown exception", + "apply"); } } @@ -266,10 +249,8 @@ SettlementContext::SettlementContext(const SettlementRequest& request, Settlemen eve::Result SettlementContext::setMagnitude(double value) { if (applyPrepared_) { - recordMutationViolation("settlement magnitude cannot change after apply preparation", - "magnitude"); - return failure(eve::DiagnosticCode::Conflict, - "settlement magnitude is frozen after apply preparation", + recordMutationViolation("settlement magnitude cannot change after apply preparation", "magnitude"); + return failure(eve::DiagnosticCode::Conflict, "settlement magnitude is frozen after apply preparation", "magnitude"); } auto valid = validateFiniteNonNegative(value, "working magnitude"); @@ -283,11 +264,9 @@ eve::Result SettlementContext::setMagnitude(double value) { eve::Result SettlementContext::addAbsorbed(double value) { if (applyPrepared_) { - recordMutationViolation("settlement absorbed amount cannot change after apply preparation", - "absorbed"); + recordMutationViolation("settlement absorbed amount cannot change after apply preparation", "absorbed"); return failure(eve::DiagnosticCode::Conflict, - "settlement absorbed amount is frozen after apply preparation", - "absorbed"); + "settlement absorbed amount is frozen after apply preparation", "absorbed"); } auto valid = validateFiniteNonNegative(value, "absorbed amount"); if (!valid) { @@ -295,19 +274,16 @@ eve::Result SettlementContext::addAbsorbed(double value) { return failure(status); } if (value > std::numeric_limits::max() - absorbed_) - return failure(eve::DiagnosticCode::InvariantViolation, - "absorbed amount overflowed", "absorbed"); + return failure(eve::DiagnosticCode::InvariantViolation, "absorbed amount overflowed", "absorbed"); absorbed_ += value; return eve::Result::success(); } eve::Result SettlementContext::addResisted(double value) { if (applyPrepared_) { - recordMutationViolation("settlement resisted amount cannot change after apply preparation", - "resisted"); + recordMutationViolation("settlement resisted amount cannot change after apply preparation", "resisted"); return failure(eve::DiagnosticCode::Conflict, - "settlement resisted amount is frozen after apply preparation", - "resisted"); + "settlement resisted amount is frozen after apply preparation", "resisted"); } auto valid = validateFiniteNonNegative(value, "resisted amount"); if (!valid) { @@ -315,19 +291,16 @@ eve::Result SettlementContext::addResisted(double value) { return failure(status); } if (value > std::numeric_limits::max() - resisted_) - return failure(eve::DiagnosticCode::InvariantViolation, - "resisted amount overflowed", "resisted"); + return failure(eve::DiagnosticCode::InvariantViolation, "resisted amount overflowed", "resisted"); resisted_ += value; return eve::Result::success(); } eve::Result SettlementContext::addClamped(double value) { if (applyPrepared_) { - recordMutationViolation("settlement clamped amount cannot change after apply preparation", - "clamped"); + recordMutationViolation("settlement clamped amount cannot change after apply preparation", "clamped"); return failure(eve::DiagnosticCode::Conflict, - "settlement clamped amount is frozen after apply preparation", - "clamped"); + "settlement clamped amount is frozen after apply preparation", "clamped"); } auto valid = validateFiniteNonNegative(value, "clamped amount"); if (!valid) { @@ -335,8 +308,7 @@ eve::Result SettlementContext::addClamped(double value) { return failure(status); } if (value > std::numeric_limits::max() - clamped_) - return failure(eve::DiagnosticCode::InvariantViolation, - "clamped amount overflowed", "clamped"); + return failure(eve::DiagnosticCode::InvariantViolation, "clamped amount overflowed", "clamped"); clamped_ += value; return eve::Result::success(); } @@ -344,8 +316,8 @@ eve::Result SettlementContext::addClamped(double value) { eve::Result SettlementContext::setClampMax(std::optional value) { if (applyPrepared_) { recordMutationViolation("settlement clamp cannot change after apply preparation", "clamp"); - return failure(eve::DiagnosticCode::Conflict, - "settlement clamp is frozen after apply preparation", "clamp"); + return failure(eve::DiagnosticCode::Conflict, "settlement clamp is frozen after apply preparation", + "clamp"); } if (value) { auto valid = validateFiniteNonNegative(*value, "clamp maximum"); @@ -361,8 +333,7 @@ eve::Result SettlementContext::setClampMax(std::optional value) { void SettlementContext::setStageDetail(std::string key, Value value) { if (key.empty()) return; if (eventPrepared_) { - recordMutationViolation("settlement stage details cannot change after event preparation", - "stage.details"); + recordMutationViolation("settlement stage details cannot change after event preparation", "stage.details"); return; } stageDetails_[std::move(key)] = std::move(value); @@ -385,9 +356,8 @@ eve::Result SettlementContext::applyClamp() { eve::Result SettlementContext::prepareApply() { if (applyPrepared_) - return failure(eve::DiagnosticCode::Conflict, - "settlement apply preparation may run only once", "apply"); - auto prepared = policy().prepareApply(*this); + return failure(eve::DiagnosticCode::Conflict, "settlement apply preparation may run only once", "apply"); + auto prepared = policy().prepareApply(*this); const bool preparedOk = prepared.ok(); if (!preparedOk) { const auto status = prepared.status(); @@ -395,8 +365,8 @@ eve::Result SettlementContext::prepareApply() { } auto mutation = std::move(prepared).takeValue(); if (!mutation.isValid()) - return failure(eve::DiagnosticCode::InvariantViolation, - "settlement policy returned an invalid mutation", "apply"); + return failure(eve::DiagnosticCode::InvariantViolation, "settlement policy returned an invalid mutation", + "apply"); pendingApply_.emplace(std::move(mutation)); applyPrepared_ = true; return eve::Result::success(); @@ -404,10 +374,9 @@ eve::Result SettlementContext::prepareApply() { eve::Result SettlementContext::prepareEvent() { if (eventPrepared_) - return failure(eve::DiagnosticCode::Conflict, - "settlement event preparation may run only once", "event"); + return failure(eve::DiagnosticCode::Conflict, "settlement event preparation may run only once", "event"); synchronizeResult(); - auto event = makeEvent(*this); + auto event = makeEvent(*this); const bool eventOk = event.ok(); if (!eventOk) { const auto status = event.status(); @@ -429,8 +398,8 @@ void SettlementContext::synchronizeResult() noexcept { void SettlementContext::recordMutationViolation(std::string message, std::string path) noexcept { if (mutationViolation_) return; try { - mutationViolation_.emplace(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, std::move(message), std::move(path))); + mutationViolation_.emplace( + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, std::move(message), std::move(path))); } catch (...) { // A late mutation is a contract violation. If recording its // diagnostic itself cannot allocate, fail closed instead of allowing @@ -439,20 +408,17 @@ void SettlementContext::recordMutationViolation(std::string message, std::string } } -eve::Result ISettlementPolicy::prepareTrigger(const SettlementContext&, - const SettlementResult&) { +eve::Result ISettlementPolicy::prepareTrigger(const SettlementContext&, const SettlementResult&) { return eve::Result::success(); } SettlementPipeline::SettlementPipeline() { - auto install = [&](StageKind kind, const char* name, StageFunction function, - bool terminal = false) { - stages_.push_back(StageEntry{kind, name, 0, nextRegistration_++, terminal, - std::move(function)}); + auto install = [&](StageKind kind, const char* name, StageFunction function, bool terminal = false) { + stages_.push_back(StageEntry{kind, name, 0, nextRegistration_++, terminal, std::move(function)}); }; install(StageKind::Validate, "validate", [](SettlementContext& context) { - auto generic = validateRequest(context.request()); + auto generic = validateRequest(context.request()); const bool genericOk = generic.ok(); if (!genericOk) { const auto status = generic.status(); @@ -460,17 +426,14 @@ SettlementPipeline::SettlementPipeline() { } return context.policy().validate(context); }); - install(StageKind::SourceModifiers, - "source_modifiers", + install(StageKind::SourceModifiers, "source_modifiers", [](SettlementContext& context) { return context.policy().sourceModifiers(context); }); - install(StageKind::TargetMitigation, - "target_mitigation", + install(StageKind::TargetMitigation, "target_mitigation", [](SettlementContext& context) { return context.policy().targetMitigation(context); }); - install(StageKind::ArmorShield, - "armor_shield", + install(StageKind::ArmorShield, "armor_shield", [](SettlementContext& context) { return context.policy().armorShield(context); }); install(StageKind::Clamp, "clamp", [](SettlementContext& context) { - auto configured = context.policy().clamp(context); + auto configured = context.policy().clamp(context); const bool configuredOk = configured.ok(); if (!configuredOk) { const auto status = configured.status(); @@ -478,43 +441,43 @@ SettlementPipeline::SettlementPipeline() { } return context.applyClamp(); }); - install(StageKind::Apply, "apply", [](SettlementContext& context) { - context.synchronizeResult(); - return context.prepareApply(); - }, true); - install(StageKind::Event, "event", [](SettlementContext& context) { - return context.prepareEvent(); - }, true); - install(StageKind::Trigger, "trigger", [](SettlementContext& context) { - context.synchronizeResult(); - return context.policy().prepareTrigger(context, context.projectedResult()); - }, true); + install( + StageKind::Apply, "apply", + [](SettlementContext& context) { + context.synchronizeResult(); + return context.prepareApply(); + }, + true); + install(StageKind::Event, "event", [](SettlementContext& context) { return context.prepareEvent(); }, true); + install( + StageKind::Trigger, "trigger", + [](SettlementContext& context) { + context.synchronizeResult(); + return context.policy().prepareTrigger(context, context.projectedResult()); + }, + true); } -eve::Result SettlementPipeline::addStage(StageKind kind, std::string name, int priority, - StageFunction function) { +eve::Result SettlementPipeline::addStage(StageKind kind, std::string name, int priority, StageFunction function) { if (name.empty()) - return failure(eve::DiagnosticCode::InvalidArgument, - "settlement stage name must not be empty", "stage.name"); + return failure(eve::DiagnosticCode::InvalidArgument, "settlement stage name must not be empty", + "stage.name"); if (!function) - return failure(eve::DiagnosticCode::InvalidArgument, - "settlement stage function must not be empty", "stage.function"); - const auto duplicate = std::find_if(stages_.begin(), stages_.end(), [&](const auto& stage) { - return stage.name == name; - }); + return failure(eve::DiagnosticCode::InvalidArgument, "settlement stage function must not be empty", + "stage.function"); + const auto duplicate = + std::find_if(stages_.begin(), stages_.end(), [&](const auto& stage) { return stage.name == name; }); if (duplicate != stages_.end()) - return failure(eve::DiagnosticCode::AlreadyExists, - "settlement stage name is already registered", "stage.name"); + return failure(eve::DiagnosticCode::AlreadyExists, "settlement stage name is already registered", + "stage.name"); if (nextRegistration_ == std::numeric_limits::max()) return failure(eve::DiagnosticCode::InvariantViolation, "settlement stage registration sequence exhausted", "stage"); - stages_.push_back(StageEntry{kind, std::move(name), priority, nextRegistration_++, - false, std::move(function)}); + stages_.push_back(StageEntry{kind, std::move(name), priority, nextRegistration_++, false, std::move(function)}); return eve::Result::success(); } -eve::Result SettlementPipeline::settle(const SettlementRequest& request, - ISettlementPolicy& policy, +eve::Result SettlementPipeline::settle(const SettlementRequest& request, ISettlementPolicy& policy, game_event::GameEventLog* events) const { SettlementResult result; result.requested = request.magnitude; @@ -544,28 +507,27 @@ eve::Result SettlementPipeline::settle(const SettlementRequest for (const StageEntry* entry : ordered) { context.beginStage(); - const double before = context.magnitude(); - auto outcome = entry->function(context); - const bool outcomeOk = outcome.ok(); - const auto outcomeStatus = outcome.status(); + const double before = context.magnitude(); + auto outcome = entry->function(context); + const bool outcomeOk = outcome.ok(); + const auto outcomeStatus = outcome.status(); context.synchronizeResult(); - auto frame = validateFrame(context); - const bool frameOk = frame.ok(); - const auto frameStatus = frame.status(); - const auto* violation = context.mutationViolation(); - const bool violationPresent = violation != nullptr; - const eve::Status violationStatus = violationPresent - ? eve::Status::failure(*violation) - : eve::Status::success(); + auto frame = validateFrame(context); + const bool frameOk = frame.ok(); + const auto frameStatus = frame.status(); + const auto* violation = context.mutationViolation(); + const bool violationPresent = violation != nullptr; + const eve::Status violationStatus = + violationPresent ? eve::Status::failure(*violation) : eve::Status::success(); SettlementStageResult stageResult; stageResult.kind = entry->kind; stageResult.name = entry->name; - stageResult.status = !outcomeOk ? outcomeStatus.code() - : !frameOk ? frameStatus.code() + stageResult.status = !outcomeOk ? outcomeStatus.code() + : !frameOk ? frameStatus.code() : violationPresent ? violationStatus.code() - : outcomeStatus.code(); + : outcomeStatus.code(); stageResult.before = before; stageResult.after = context.magnitude(); stageResult.details = Value(context.stageDetails()); @@ -589,10 +551,9 @@ eve::Result SettlementPipeline::settle(const SettlementRequest auto* pending = context.pendingApply(); if (pending == nullptr) return failure(eve::DiagnosticCode::InvariantViolation, - "settlement pipeline did not prepare an apply mutation", - "apply"); + "settlement pipeline did not prepare an apply mutation", "apply"); - auto committed = pending->commit(); + auto committed = pending->commit(); const bool committedOk = committed.ok(); if (!committedOk) { const auto status = committed.status(); @@ -602,10 +563,10 @@ eve::Result SettlementPipeline::settle(const SettlementRequest if (const auto* preparedEvent = context.pendingEvent()) { game_event::GameEvent envelope = *preparedEvent; - result.event = envelope; + result.event = envelope; if (events != nullptr) { try { - auto appended = events->append(envelope); + auto appended = events->append(envelope); const bool appendedOk = appended.ok(); if (!appendedOk) { const auto status = appended.status(); @@ -623,13 +584,11 @@ eve::Result SettlementPipeline::settle(const SettlementRequest rollback(); return failure( eve::DiagnosticCode::Failed, - std::string("settlement result event append threw: ") + exception.what(), - "event"); + std::string("settlement result event append threw: ") + exception.what(), "event"); } catch (...) { rollback(); return failure(eve::DiagnosticCode::Failed, - "settlement result event append threw an unknown exception", - "event"); + "settlement result event append threw an unknown exception", "event"); } } } diff --git a/src/modules/settlement/Settlement.h b/src/modules/settlement/Settlement.h index 0625df11..0f957c71 100644 --- a/src/modules/settlement/Settlement.h +++ b/src/modules/settlement/Settlement.h @@ -13,8 +13,8 @@ #include "common/Result.h" #include "common/SubjectRef.h" -#include "common/Value.h" #include "common/Time.h" +#include "common/Value.h" #include "game_event/GameEvent.h" #include @@ -58,14 +58,14 @@ enum class StageKind : std::uint8_t { * metadata and are copied to the result event. */ struct SettlementRequest { - SubjectRef source; - SubjectRef target; - std::string kind; - double magnitude = 0.0; - std::vector tags; - Value context = Value(Value::Object{}); - SimulationTick tick = SimulationTick::zero(); - game_event::CausationRef causation; + SubjectRef source; + SubjectRef target; + std::string kind; + double magnitude = 0.0; + std::vector tags; + Value context = Value(Value::Object{}); + SimulationTick tick = SimulationTick::zero(); + game_event::CausationRef causation; game_event::CorrelationId correlation; }; @@ -77,12 +77,12 @@ struct SettlementRequest { * understand a policy-specific pointer or callback. */ struct SettlementStageResult { - StageKind kind = StageKind::Validate; - std::string name; - eve::StatusCode status = eve::StatusCode::Ok; - double before = 0.0; - double after = 0.0; - Value details = Value(Value::Object{}); + StageKind kind = StageKind::Validate; + std::string name; + eve::StatusCode status = eve::StatusCode::Ok; + double before = 0.0; + double after = 0.0; + Value details = Value(Value::Object{}); }; /** @@ -95,14 +95,14 @@ struct SettlementStageResult { * effective amount. */ struct SettlementResult { - double requested = 0.0; - double applied = 0.0; - double absorbed = 0.0; - double resisted = 0.0; - double clamped = 0.0; - bool critical = false; - SimulationTick tick = SimulationTick::zero(); - std::vector stages; + double requested = 0.0; + double applied = 0.0; + double absorbed = 0.0; + double resisted = 0.0; + double clamped = 0.0; + bool critical = false; + SimulationTick tick = SimulationTick::zero(); + std::vector stages; std::optional event; /** @brief Return whether a named stage explanation exists. */ @@ -130,7 +130,7 @@ struct SettlementResult { */ class PreparedApply { public: - using CommitFunction = std::function()>; + using CommitFunction = std::function()>; using RollbackFunction = std::function; /** @brief Construct an empty invalid mutation. */ @@ -139,7 +139,7 @@ public: /** @brief Construct a mutation from commit and rollback callbacks. */ PreparedApply(CommitFunction commit, RollbackFunction rollback); - PreparedApply(const PreparedApply&) = delete; + PreparedApply(const PreparedApply&) = delete; PreparedApply& operator=(const PreparedApply&) = delete; PreparedApply(PreparedApply&& other) noexcept; PreparedApply& operator=(PreparedApply&& other) noexcept; @@ -163,10 +163,10 @@ public: [[nodiscard]] bool isCommitted() const noexcept { return committed_; } private: - CommitFunction commit_; + CommitFunction commit_; RollbackFunction rollback_; - bool committed_ = false; - bool rolledBack_ = false; + bool committed_ = false; + bool rolledBack_ = false; }; class ISettlementPolicy; @@ -217,8 +217,7 @@ public: /** @brief Mark the result as critical; late writes are rejected. */ void setCritical(bool value) noexcept { if (applyPrepared_) { - recordMutationViolation("settlement critical flag cannot change after apply preparation", - "critical"); + recordMutationViolation("settlement critical flag cannot change after apply preparation", "critical"); return; } critical_ = value; @@ -292,8 +291,7 @@ public: private: friend class SettlementPipeline; - SettlementContext(const SettlementRequest& request, SettlementResult& result, - ISettlementPolicy& policy); + SettlementContext(const SettlementRequest& request, SettlementResult& result, ISettlementPolicy& policy); void beginStage() noexcept { stageDetails_.clear(); mutationViolation_.reset(); @@ -302,21 +300,21 @@ private: void clearPendingEvent() noexcept { pendingEvent_.reset(); } void recordMutationViolation(std::string message, std::string path) noexcept; - const SettlementRequest& request_; - SettlementResult& result_; - ISettlementPolicy& policy_; - double magnitude_ = 0.0; - double absorbed_ = 0.0; - double resisted_ = 0.0; - double clamped_ = 0.0; - bool critical_ = false; - std::optional clampMax_; - Value::Object stageDetails_; - std::optional pendingApply_; + const SettlementRequest& request_; + SettlementResult& result_; + ISettlementPolicy& policy_; + double magnitude_ = 0.0; + double absorbed_ = 0.0; + double resisted_ = 0.0; + double clamped_ = 0.0; + bool critical_ = false; + std::optional clampMax_; + Value::Object stageDetails_; + std::optional pendingApply_; std::optional pendingEvent_; - std::optional mutationViolation_; - bool applyPrepared_ = false; - bool eventPrepared_ = false; + std::optional mutationViolation_; + bool applyPrepared_ = false; + bool eventPrepared_ = false; }; /** @@ -347,12 +345,11 @@ public: [[nodiscard]] virtual eve::Result clamp(SettlementContext& context) = 0; /** @brief Build an atomic candidate mutation without publishing it. */ - [[nodiscard]] virtual eve::Result prepareApply( - const SettlementContext& context) = 0; + [[nodiscard]] virtual eve::Result prepareApply(const SettlementContext& context) = 0; /** @brief Validate or prepare trigger semantics without mutating state. */ - [[nodiscard]] virtual eve::Result prepareTrigger( - const SettlementContext& context, const SettlementResult& result); + [[nodiscard]] virtual eve::Result prepareTrigger(const SettlementContext& context, + const SettlementResult& result); }; /** @@ -373,8 +370,7 @@ public: SettlementPipeline(); /** @brief Add one named custom stage; duplicate names are rejected. */ - [[nodiscard]] eve::Result addStage(StageKind kind, std::string name, int priority, - StageFunction function); + [[nodiscard]] eve::Result addStage(StageKind kind, std::string name, int priority, StageFunction function); /** * @brief Execute one settlement transaction against a domain policy. @@ -385,22 +381,21 @@ public: * @return A complete result, or a structured failure. Any failure rolls * back the prepared mutation and leaves policy state unchanged. */ - [[nodiscard]] eve::Result settle( - const SettlementRequest& request, ISettlementPolicy& policy, - game_event::GameEventLog* events = nullptr) const; + [[nodiscard]] eve::Result settle(const SettlementRequest& request, ISettlementPolicy& policy, + game_event::GameEventLog* events = nullptr) const; private: struct StageEntry { - StageKind kind = StageKind::Validate; - std::string name; - int priority = 0; + StageKind kind = StageKind::Validate; + std::string name; + int priority = 0; std::uint64_t registration = 0; - bool terminal = false; + bool terminal = false; StageFunction function; }; std::vector stages_; - std::uint64_t nextRegistration_ = 1; + std::uint64_t nextRegistration_ = 1; }; } // namespace eve::settlement diff --git a/src/modules/sound/SoundLoader.cpp b/src/modules/sound/SoundLoader.cpp index 5af6ab3a..15683219 100644 --- a/src/modules/sound/SoundLoader.cpp +++ b/src/modules/sound/SoundLoader.cpp @@ -47,9 +47,7 @@ public: * @return `false`; ResourceManager is the authoritative CPU SoundData * owner and performs the actual transactional refresh. */ - eve::Result reload(const std::string &) override { - return eve::Result::success(false); - } + eve::Result reload(const std::string &) override { return eve::Result::success(false); } eve::Resource *load(const std::string &key) override { const std::string path = eve::ResourceManager::pathOfKey(key); diff --git a/src/modules/statepatch/StateAccessAdapter.cpp b/src/modules/statepatch/StateAccessAdapter.cpp index edf9711f..e9ede4f3 100644 --- a/src/modules/statepatch/StateAccessAdapter.cpp +++ b/src/modules/statepatch/StateAccessAdapter.cpp @@ -13,17 +13,15 @@ namespace eve::statepatch { namespace { -eve::Result failure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { +eve::Result failure(eve::DiagnosticCode code, std::string message, std::string path = {}) { return eve::Result::failure( eve::Diagnostic::error(code, std::move(message), std::move(path))); } -eve::Result readJsonValue(const Store& store, std::string_view subject, - std::string_view key) { +eve::Result readJsonValue(const Store& store, std::string_view subject, std::string_view key) { if (!store.has(std::string(subject), std::string(key))) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::NotFound, "state value is absent", "state")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "state value is absent", "state")); auto parsed = eve::Value::fromJson(store.get(std::string(subject), std::string(key))); if (!parsed) return eve::Result::failure(parsed.status()); return parsed; @@ -45,15 +43,13 @@ std::string tagKey(std::string_view tag) { return "tag." + std::string(tag); } } // namespace -std::optional StatePatchStateAdapter::value(std::string_view subject, - std::string_view key) const { +std::optional StatePatchStateAdapter::value(std::string_view subject, std::string_view key) const { auto result = readJsonValue(store_, subject, key); if (!result) return std::nullopt; return std::move(result).takeValue(); } -std::optional StatePatchStateAdapter::hasTag(std::string_view subject, - std::string_view tag) const { +std::optional StatePatchStateAdapter::hasTag(std::string_view subject, std::string_view tag) const { if (subject.empty() || tag.empty()) return std::nullopt; const std::string key = tagKey(tag); if (!store_.has(std::string(subject), key)) return false; @@ -64,50 +60,47 @@ std::optional StatePatchStateAdapter::hasTag(std::string_view subject, return parsed.asBool(); } -std::optional StatePatchStateAdapter::state(std::string_view subject, - std::string_view key) const { +std::optional StatePatchStateAdapter::state(std::string_view subject, std::string_view key) const { return value(subject, key); } -eve::Result StatePatchStateAdapter::apply( - std::span mutations, const eve::MutationContext& context) { +eve::Result StatePatchStateAdapter::apply(std::span mutations, + const eve::MutationContext& context) { if (context.transactionId.empty()) - return failure(eve::DiagnosticCode::InvalidArgument, - "persistent dialogue mutations require a transaction id", "transactionId"); + return failure(eve::DiagnosticCode::InvalidArgument, "persistent dialogue mutations require a transaction id", + "transactionId"); using Key = std::pair; std::map> pending; - auto batchReference = store_.newBatch(); - if (!batchReference) - return eve::Result::failure(batchReference.status()); + auto batchReference = store_.newBatch(); + if (!batchReference) return eve::Result::failure(batchReference.status()); const PatchBatchHandleRef batchHandle = std::move(batchReference).takeValue(); - auto batch = store_.resolveBatch(batchHandle); + auto batch = store_.resolveBatch(batchHandle); if (!batch.isBound()) - return failure(eve::DiagnosticCode::StaleHandle, - "StatePatch patch batch became stale during allocation", "batch"); + return failure(eve::DiagnosticCode::StaleHandle, "StatePatch patch batch became stale during allocation", + "batch"); struct BatchLease { - Store& store; + Store& store; PatchBatchHandleRef handle; ~BatchLease() { store.releaseBatch(handle).ignore("release StatePatch adapter batch"); } } batchLease{store_, batchHandle}; for (std::size_t index = 0; index < mutations.size(); ++index) { - const auto& mutation = mutations[index]; - const std::string path = "mutations[" + std::to_string(index) + "]"; + const auto& mutation = mutations[index]; + const std::string path = "mutations[" + std::to_string(index) + "]"; if (!mutation.persistent) - return failure(eve::DiagnosticCode::Unsupported, - "StatePatch adapter accepts persistent mutations only", path); + return failure(eve::DiagnosticCode::Unsupported, "StatePatch adapter accepts persistent mutations only", + path); if (mutation.subject.empty() || mutation.key.empty()) - return failure(eve::DiagnosticCode::InvalidArgument, - "persistent mutation requires subject and key", path); + return failure(eve::DiagnosticCode::InvalidArgument, "persistent mutation requires subject and key", path); const std::string sourceKey = mutation.key; - const std::string key = mutation.kind == eve::MutationKind::AddTag || - mutation.kind == eve::MutationKind::RemoveTag - ? tagKey(sourceKey) - : sourceKey; + const std::string key = + mutation.kind == eve::MutationKind::AddTag || mutation.kind == eve::MutationKind::RemoveTag + ? tagKey(sourceKey) + : sourceKey; const Key pendingKey{mutation.subject, key}; - auto current = [&]() -> std::optional { + auto current = [&]() -> std::optional { const auto found = pending.find(pendingKey); if (found != pending.end()) return found->second; if (!store_.has(mutation.subject, key)) return std::nullopt; @@ -118,67 +111,65 @@ eve::Result StatePatchStateAdapter::apply( bool appended = false; switch (mutation.kind) { - case eve::MutationKind::Set: { - auto json = mutation.value.toJson(); - if (!json) return failure(json.error() ? json.error()->code() : eve::DiagnosticCode::SerializationError, - json.error() ? json.error()->message() : "state value cannot be serialized", - path + ".value"); - appended = batch->set(mutation.subject, key, json.value()); - if (appended) pending[pendingKey] = mutation.value; - break; + case eve::MutationKind::Set: { + auto json = mutation.value.toJson(); + if (!json) + return failure(json.error() ? json.error()->code() : eve::DiagnosticCode::SerializationError, + json.error() ? json.error()->message() : "state value cannot be serialized", + path + ".value"); + appended = batch->set(mutation.subject, key, json.value()); + if (appended) pending[pendingKey] = mutation.value; + break; + } + case eve::MutationKind::Remove: + appended = batch->remove(mutation.subject, key); + if (appended) pending[pendingKey] = std::nullopt; + break; + case eve::MutationKind::AddTag: + appended = batch->set(mutation.subject, key, "true"); + if (appended) pending[pendingKey] = eve::Value(true); + break; + case eve::MutationKind::RemoveTag: + appended = batch->remove(mutation.subject, key); + if (appended) pending[pendingKey] = std::nullopt; + break; + case eve::MutationKind::AddNumber: { + double delta = 0.0; + if (!numeric(mutation.value, delta)) + return failure(eve::DiagnosticCode::InvalidArgument, "AddNumber requires a finite numeric value", + path + ".value"); + const auto existing = current(); + double base = 0.0; + if (existing && !numeric(*existing, base)) + return failure(eve::DiagnosticCode::InvalidArgument, + "AddNumber cannot add to a non-numeric state value", path); + const double total = base + delta; + if (!std::isfinite(total)) + return failure(eve::DiagnosticCode::InvalidArgument, "AddNumber result must be finite", path); + const eve::Value result(total); + auto json = result.toJson(); + if (!json) + return failure(eve::DiagnosticCode::SerializationError, "AddNumber result cannot be serialized", + path); + appended = batch->set(mutation.subject, key, json.value()); + if (appended) pending[pendingKey] = result; + break; + } } - case eve::MutationKind::Remove: - appended = batch->remove(mutation.subject, key); - if (appended) pending[pendingKey] = std::nullopt; - break; - case eve::MutationKind::AddTag: - appended = batch->set(mutation.subject, key, "true"); - if (appended) pending[pendingKey] = eve::Value(true); - break; - case eve::MutationKind::RemoveTag: - appended = batch->remove(mutation.subject, key); - if (appended) pending[pendingKey] = std::nullopt; - break; - case eve::MutationKind::AddNumber: { - double delta = 0.0; - if (!numeric(mutation.value, delta)) - return failure(eve::DiagnosticCode::InvalidArgument, - "AddNumber requires a finite numeric value", path + ".value"); - const auto existing = current(); - double base = 0.0; - if (existing && !numeric(*existing, base)) - return failure(eve::DiagnosticCode::InvalidArgument, - "AddNumber cannot add to a non-numeric state value", path); - const double total = base + delta; - if (!std::isfinite(total)) - return failure(eve::DiagnosticCode::InvalidArgument, - "AddNumber result must be finite", path); - const eve::Value result(total); - auto json = result.toJson(); - if (!json) return failure(eve::DiagnosticCode::SerializationError, - "AddNumber result cannot be serialized", path); - appended = batch->set(mutation.subject, key, json.value()); - if (appended) pending[pendingKey] = result; - break; - } - } - if (!appended) - return failure(eve::DiagnosticCode::Failed, "StatePatch rejected a mutation", path); + if (!appended) return failure(eve::DiagnosticCode::Failed, "StatePatch rejected a mutation", path); } - eve::transaction::TransactionContext transaction(context.transactionId, context.correlationId, - context.causationId); - StoreTransactionParticipant participant(store_, *batch); + eve::transaction::TransactionContext transaction(context.transactionId, context.correlationId, context.causationId); + StoreTransactionParticipant participant(store_, *batch); std::array participants{&participant}; - eve::transaction::Coordinator coordinator; + eve::transaction::Coordinator coordinator; auto committed = coordinator.execute(transaction, participants); if (!committed) return eve::Result::failure(committed.status()); const PatchResult& result = batch->result(); return eve::Result::success( eve::MutationReceipt{context.transactionId, static_cast(result.changedCount)}, - eve::Status::success(result.changedCount == 0 ? eve::StatusCode::NoOp - : eve::StatusCode::Applied)); + eve::Status::success(result.changedCount == 0 ? eve::StatusCode::NoOp : eve::StatusCode::Applied)); } } // namespace eve::statepatch diff --git a/src/modules/statepatch/StateAccessAdapter.h b/src/modules/statepatch/StateAccessAdapter.h index 35eef3f8..24d09f74 100644 --- a/src/modules/statepatch/StateAccessAdapter.h +++ b/src/modules/statepatch/StateAccessAdapter.h @@ -23,33 +23,27 @@ public: explicit StatePatchStateAdapter(Store& store) noexcept : store_(store) {} /** @copydoc eve::IStateQuery::value */ - [[nodiscard]] std::optional value(std::string_view subject, - std::string_view key) const override; + [[nodiscard]] std::optional value(std::string_view subject, std::string_view key) const override; /** @copydoc eve::IStateQuery::hasTag */ - [[nodiscard]] std::optional hasTag(std::string_view subject, - std::string_view tag) const override; + [[nodiscard]] std::optional hasTag(std::string_view subject, std::string_view tag) const override; /** @copydoc eve::IStateQuery::attribute */ - [[nodiscard]] std::optional attribute(std::string_view, - std::string_view) const override { + [[nodiscard]] std::optional attribute(std::string_view, std::string_view) const override { return std::nullopt; } /** @copydoc eve::IStateQuery::resource */ - [[nodiscard]] std::optional resource(std::string_view, - std::string_view) const override { + [[nodiscard]] std::optional resource(std::string_view, std::string_view) const override { return std::nullopt; } /** @copydoc eve::IStateQuery::state */ - [[nodiscard]] std::optional state(std::string_view subject, - std::string_view key) const override; + [[nodiscard]] std::optional state(std::string_view subject, std::string_view key) const override; /** @copydoc eve::IStateQuery::authority */ - [[nodiscard]] std::optional authority(std::string_view, - std::string_view) const override { + [[nodiscard]] std::optional authority(std::string_view, std::string_view) const override { return std::nullopt; } /** @copydoc eve::IStateMutation::apply */ - [[nodiscard]] eve::Result apply( - std::span mutations, const eve::MutationContext& context) override; + [[nodiscard]] eve::Result apply(std::span mutations, + const eve::MutationContext& context) override; private: Store& store_; diff --git a/src/modules/statepatch/StatePatch.cpp b/src/modules/statepatch/StatePatch.cpp index 2f483941..c678e65a 100644 --- a/src/modules/statepatch/StatePatch.cpp +++ b/src/modules/statepatch/StatePatch.cpp @@ -34,38 +34,33 @@ struct ScriptStateBatch { }; template -eve::Result statePatchBindingFailure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - code, std::move(message), std::move(path), {}, "statepatch.squirrel")); +eve::Result statePatchBindingFailure(eve::DiagnosticCode code, std::string message, std::string path = {}) { + return eve::Result::failure( + eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "statepatch.squirrel")); } template ssq::Table staleStateResult(HSQUIRRELVM vm, const char* objectName) { return eve::script::projectResult( - vm, statePatchBindingFailure(eve::DiagnosticCode::StaleHandle, - std::string("owned state-patch ") + objectName + - " handle is stale", objectName), + vm, + statePatchBindingFailure(eve::DiagnosticCode::StaleHandle, + std::string("owned state-patch ") + objectName + " handle is stale", objectName), [](T value) { return eve::Value(value); }); } template ssq::Table makeOwnedProxy(HSQUIRRELVM vm, eve::Result&& reference, Release&& release) { - if (!reference) - return eve::script::projectStatusResult(vm, reference.status(), false, false); - const Ref ref = std::move(reference).takeValue(); - auto object = eve::script::makeOwnedSquirrelInstance( - vm, std::make_unique(ref)); + if (!reference) return eve::script::projectStatusResult(vm, reference.status(), false, false); + const Ref ref = std::move(reference).takeValue(); + auto object = eve::script::makeOwnedSquirrelInstance(vm, std::make_unique(ref)); if (!object) { const eve::Status status = object.status(); object.ignore("failed to create owned state-patch proxy"); - std::invoke(std::forward(release), ref).ignore( - "rollback failed owned state-patch allocation"); + std::invoke(std::forward(release), ref).ignore("rollback failed owned state-patch allocation"); return eve::script::projectStatusResult(vm, status, false, false); } ssq::Object owned = std::move(object).takeValue(); - auto result = eve::script::projectStatusResult( - vm, eve::Status::success(eve::StatusCode::Applied), true, false); + auto result = eve::script::projectStatusResult(vm, eve::Status::success(eve::StatusCode::Applied), true, false); result.set("value", owned); result.set("ownership", std::string("owned")); result.set("ownerEpoch", static_cast(ref.ownerEpoch)); @@ -148,17 +143,17 @@ eve::LogicalId statePatchSchema() { const eve::SnapshotMigrationChain& statePatchMigrations() { static const eve::SnapshotMigrationChain chain = [] { eve::SnapshotMigrationChain result; - const auto registration = result.add( - statePatchSchema(), eve::SchemaVersion(0), eve::SchemaVersion(1), - [](const eve::Value& payload) -> eve::Result { - const auto* object = payload.getIf(); - if (!object) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "state patch payload must be an object")); - eve::Value::Object migrated = *object; - if (!migrated.contains("version")) migrated.emplace("version", eve::Value(std::int64_t(1))); - return eve::Result::success(eve::Value(std::move(migrated))); - }); + const auto registration = + result.add(statePatchSchema(), eve::SchemaVersion(0), eve::SchemaVersion(1), + [](const eve::Value& payload) -> eve::Result { + const auto* object = payload.getIf(); + if (!object) + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::ParseError, "state patch payload must be an object")); + eve::Value::Object migrated = *object; + if (!migrated.contains("version")) migrated.emplace("version", eve::Value(std::int64_t(1))); + return eve::Result::success(eve::Value(std::move(migrated))); + }); if (!registration.ok()) std::terminate(); return result; }(); @@ -171,9 +166,8 @@ eve::Result snapshotFailure(eve::DiagnosticCode code, std::string message) { } eve::Result restoreFailure(std::string message, std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, std::move(message), std::move(path), {}, - "statepatch.store.restoreJson")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::ParseError, std::move(message), + std::move(path), {}, "statepatch.store.restoreJson")); } } // namespace @@ -222,18 +216,13 @@ int PatchBatch::size() const { return static_cast(operations_.size()); } const PatchResult& PatchBatch::result() const { return result_; } -eve::Result Store::newBatch() { - return batches_.emplace(std::make_unique()); -} +eve::Result Store::newBatch() { return batches_.emplace(std::make_unique()); } -eve::script::Borrowed Store::resolveBatch( - PatchBatchHandleRef reference) noexcept { +eve::script::Borrowed Store::resolveBatch(PatchBatchHandleRef reference) noexcept { return batches_.resolve(reference); } -eve::Result Store::releaseBatch(PatchBatchHandleRef reference) { - return batches_.erase(reference); -} +eve::Result Store::releaseBatch(PatchBatchHandleRef reference) { return batches_.erase(reference); } bool Store::isBatchStale(PatchBatchHandleRef reference) const noexcept { if (!reference.isValid()) return false; @@ -423,8 +412,7 @@ eve::Result Store::restoreJson(const std::string& json) { uint64_t restoredRevision = 0; uint64_t restoredSequence = 0; - if (root.getInt("version") != 1) - return restoreFailure("unsupported state patch snapshot version", "$.version"); + if (root.getInt("version") != 1) return restoreFailure("unsupported state patch snapshot version", "$.version"); if (!parseU64(root.get("revision"), restoredRevision)) return restoreFailure("snapshot revision must be a decimal string", "$.revision"); if (!parseU64(root.get("nextSequence"), restoredSequence) || restoredSequence == 0) @@ -442,8 +430,7 @@ eve::Result Store::restoreJson(const std::string& json) { uint64_t valueRevision = 0; if (!item.isObject() || item.getString("subject").empty() || item.getString("key").empty() || !parseU64(item.get("revision"), valueRevision) || valueRevision > restoredRevision || !item.get("value")) { - return restoreFailure("invalid value at index " + std::to_string(i), - "$.values[" + std::to_string(i) + "]"); + return restoreFailure("invalid value at index " + std::to_string(i), "$.values[" + std::to_string(i) + "]"); } const auto subject = item.getString("subject"); const auto key = item.getString("key"); @@ -472,16 +459,15 @@ eve::Result Store::restoreJson(const std::string& json) { return eve::Result::success(); } -eve::Result Store::snapshot( - const eve::SnapshotHashProvider& hashProvider) const { +eve::Result Store::snapshot(const eve::SnapshotHashProvider& hashProvider) const { auto payload = eve::Value::fromJson(snapshotJson()); if (!payload.ok()) return eve::Result::failure(payload.status()); return eve::makeSnapshotEnvelope("statepatch.store", statePatchSchema(), eve::SchemaVersion(1), instanceId_, eve::Revision(revision_), tick_, std::move(payload).takeValue(), hashProvider); } -eve::Result Store::restoreSnapshot( - const eve::SnapshotEnvelope& source, const eve::SnapshotHashProvider& hashProvider) { +eve::Result Store::restoreSnapshot(const eve::SnapshotEnvelope& source, + const eve::SnapshotHashProvider& hashProvider) { if (source.type != "statepatch.store" || source.schema != statePatchSchema()) return snapshotFailure(eve::DiagnosticCode::InvalidArgument, "snapshot does not belong to statepatch::Store"); @@ -491,35 +477,32 @@ eve::Result Store::restoreSnapshot( auto migrated = statePatchMigrations().migrate(source, eve::SchemaVersion(1), hashProvider); if (!migrated.ok()) return eve::Result::failure(migrated.status()); const auto& candidateEnvelope = migrated.value(); - auto metadata = eve::validateSnapshotPayloadMetadata(candidateEnvelope.payload, - candidateEnvelope.revision, - candidateEnvelope.tick); + auto metadata = eve::validateSnapshotPayloadMetadata(candidateEnvelope.payload, candidateEnvelope.revision, + candidateEnvelope.tick); if (!metadata.ok()) return eve::Result::failure(metadata.status()); auto payload = candidateEnvelope.payload.toJson(); if (!payload.ok()) return eve::Result::failure(payload.status()); Store candidate(instanceId_); - auto restored = candidate.restoreJson(std::move(payload).takeValue()); + auto restored = candidate.restoreJson(std::move(payload).takeValue()); if (!restored.ok()) return eve::Result::failure(restored.status()); if (candidate.revision_ != candidateEnvelope.revision.value()) return snapshotFailure(eve::DiagnosticCode::Conflict, "state patch payload revision disagrees with snapshot envelope"); candidate.instanceId_ = candidateEnvelope.instanceId; candidate.tick_ = candidateEnvelope.tick; - *this = std::move(candidate); + *this = std::move(candidate); return eve::Result::success(); } -eve::Result Store::snapshotEnvelopeJson( - const eve::SnapshotHashProvider& hashProvider) const { +eve::Result Store::snapshotEnvelopeJson(const eve::SnapshotHashProvider& hashProvider) const { auto value = snapshot(hashProvider); if (!value.ok()) return eve::Result::failure(value.status()); return std::move(value).andThen( [](eve::SnapshotEnvelope&& envelope) { return eve::serializeSnapshotEnvelope(envelope); }); } -eve::Result Store::restoreSnapshotJson( - std::string_view json, const eve::SnapshotHashProvider& hashProvider) { +eve::Result Store::restoreSnapshotJson(std::string_view json, const eve::SnapshotHashProvider& hashProvider) { auto source = eve::parseSnapshotEnvelope(json, hashProvider); if (!source.ok()) return eve::Result::failure(source.status()); return restoreSnapshot(std::move(source).takeValue(), hashProvider); @@ -564,15 +547,15 @@ bool Store::transactionStateEquals(const Store& other) const { namespace { eve::Result patchParticipantFailure(const PatchResult& result) { - eve::StatusCode statusCode = eve::StatusCode::Failed; - eve::DiagnosticCode diagnosticCode = eve::DiagnosticCode::Failed; + eve::StatusCode statusCode = eve::StatusCode::Failed; + eve::DiagnosticCode diagnosticCode = eve::DiagnosticCode::Failed; std::vector diagnostics; for (const auto& error : result.errors) { if (error.code == "conflict") { - statusCode = eve::StatusCode::Conflict; + statusCode = eve::StatusCode::Conflict; diagnosticCode = eve::DiagnosticCode::Conflict; } else if (statusCode == eve::StatusCode::Failed && error.code.starts_with("invalid_")) { - statusCode = eve::StatusCode::Rejected; + statusCode = eve::StatusCode::Rejected; diagnosticCode = eve::DiagnosticCode::InvalidArgument; } diagnostics.push_back(eve::Diagnostic::error( @@ -580,8 +563,8 @@ eve::Result patchParticipantFailure(const PatchResult& result) { "statepatch[" + std::to_string(error.operationIndex) + "]")); } if (diagnostics.empty()) - diagnostics.push_back(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "state patch batch could not be prepared", "statepatch")); + diagnostics.push_back(eve::Diagnostic::error(eve::DiagnosticCode::Failed, + "state patch batch could not be prepared", "statepatch")); return eve::Result::failure(eve::Status(statusCode, std::move(diagnostics))); } @@ -599,8 +582,7 @@ eve::Result StoreTransactionParticipant::lifecycleFailure(std::string_view "statepatch.lifecycle")); } -eve::Result StoreTransactionParticipant::contextFailure( - const transaction::TransactionContext& context) const { +eve::Result StoreTransactionParticipant::contextFailure(const transaction::TransactionContext& context) const { (void)context; return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::Conflict, "transaction context changed between participant lifecycle calls", @@ -610,8 +592,8 @@ eve::Result StoreTransactionParticipant::contextFailure( eve::Result StoreTransactionParticipant::prepare(const transaction::TransactionContext& context) { if (phase_ != Phase::Idle) return lifecycleFailure("prepare"); if (context.transactionId().empty()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "transaction id must not be empty", "transactionId")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "transaction id must not be empty", "transactionId")); transactionId_ = context.transactionId(); correlationId_ = context.correlationId(); @@ -675,8 +657,7 @@ eve::Result StatePatch::newStore() { return module->stores_.emplace(std::make_unique()); } -eve::script::Borrowed StatePatch::resolveBatch( - StateBatchHandleRef reference) noexcept { +eve::script::Borrowed StatePatch::resolveBatch(StateBatchHandleRef reference) noexcept { StatePatch* module = ModuleManager::getInstance("StatePatch"); if (!module) return {}; auto store = module->stores_.resolve(reference.store); @@ -687,12 +668,12 @@ eve::script::Borrowed StatePatch::resolveBatch( eve::Result StatePatch::releaseBatch(StateBatchHandleRef reference) { StatePatch* module = ModuleManager::getInstance("StatePatch"); if (!module) - return statePatchBindingFailure(eve::DiagnosticCode::StaleHandle, - "StatePatch module is no longer loaded", "batch"); + return statePatchBindingFailure(eve::DiagnosticCode::StaleHandle, "StatePatch module is no longer loaded", + "batch"); auto store = module->stores_.resolve(reference.store); if (!store.isBound()) - return statePatchBindingFailure(eve::DiagnosticCode::StaleHandle, - "owning StatePatch store is stale", "store"); + return statePatchBindingFailure(eve::DiagnosticCode::StaleHandle, "owning StatePatch store is stale", + "store"); return store->releaseBatch(reference.batch); } @@ -704,8 +685,7 @@ bool StatePatch::isBatchStale(StateBatchHandleRef reference) noexcept { return !store.isBound() || store->isBatchStale(reference.batch); } -eve::script::Borrowed StatePatch::resolve( - StateStoreHandleRef reference) noexcept { +eve::script::Borrowed StatePatch::resolve(StateStoreHandleRef reference) noexcept { StatePatch* module = ModuleManager::getInstance("StatePatch"); if (!module) return {}; return module->stores_.resolve(reference); @@ -714,8 +694,8 @@ eve::script::Borrowed StatePatch::resolve( eve::Result StatePatch::release(StateStoreHandleRef reference) { StatePatch* module = ModuleManager::getInstance("StatePatch"); if (!module) - return statePatchBindingFailure(eve::DiagnosticCode::StaleHandle, - "StatePatch module is no longer loaded", "store"); + return statePatchBindingFailure(eve::DiagnosticCode::StaleHandle, "StatePatch module is no longer loaded", + "store"); return module->stores_.erase(reference); } @@ -777,70 +757,54 @@ void StatePatch::expose(ssq::Table& table) { ownedBatch.addFunc("handle", [](ScriptStateBatch* value) { return value ? static_cast(value->reference.packed()) : int64_t{0}; }); - ownedBatch.addFunc("isStale", [](ScriptStateBatch* value) { - return !value || StatePatch::isBatchStale(value->reference); - }); + ownedBatch.addFunc("isStale", + [](ScriptStateBatch* value) { return !value || StatePatch::isBatchStale(value->reference); }); ownedBatch.addFunc("release", [vm](ScriptStateBatch* value) { if (!value) return eve::script::projectResult( vm, statePatchBindingFailure(eve::DiagnosticCode::InvalidArgument, - "owned state batch proxy must not be null", "batch")); + "owned state batch proxy must not be null", "batch")); return eve::script::projectResult(vm, StatePatch::releaseBatch(value->reference)); }); - ownedBatch.addFunc("set", [vm](ScriptStateBatch* value, const std::string& subject, - const std::string& key, const std::string& jsonValue) { - auto view = value ? StatePatch::resolveBatch(value->reference) - : eve::script::Borrowed(); - if (!view.isBound()) - return staleStateResult(vm, "batch"); - return eve::script::projectResult( - vm, eve::Result::success(view->set(subject, key, jsonValue)), - [](bool value) { return eve::Value(value); }); + ownedBatch.addFunc("set", [vm](ScriptStateBatch* value, const std::string& subject, const std::string& key, + const std::string& jsonValue) { + auto view = value ? StatePatch::resolveBatch(value->reference) : eve::script::Borrowed(); + if (!view.isBound()) return staleStateResult(vm, "batch"); + return eve::script::projectResult(vm, eve::Result::success(view->set(subject, key, jsonValue)), + [](bool value) { return eve::Value(value); }); }); - ownedBatch.addFunc("remove", [vm](ScriptStateBatch* value, const std::string& subject, - const std::string& key) { - auto view = value ? StatePatch::resolveBatch(value->reference) - : eve::script::Borrowed(); - if (!view.isBound()) - return staleStateResult(vm, "batch"); - return eve::script::projectResult( - vm, eve::Result::success(view->remove(subject, key)), - [](bool value) { return eve::Value(value); }); + ownedBatch.addFunc("remove", [vm](ScriptStateBatch* value, const std::string& subject, const std::string& key) { + auto view = value ? StatePatch::resolveBatch(value->reference) : eve::script::Borrowed(); + if (!view.isBound()) return staleStateResult(vm, "batch"); + return eve::script::projectResult(vm, eve::Result::success(view->remove(subject, key)), + [](bool value) { return eve::Value(value); }); }); - ownedBatch.addFunc("setExpected", [vm](ScriptStateBatch* value, const std::string& subject, - const std::string& key, const std::string& jsonValue, - const std::string& expectedJson) { - auto view = value ? StatePatch::resolveBatch(value->reference) - : eve::script::Borrowed(); - if (!view.isBound()) - return staleStateResult(vm, "batch"); + ownedBatch.addFunc("setExpected", [vm](ScriptStateBatch* value, const std::string& subject, const std::string& key, + const std::string& jsonValue, const std::string& expectedJson) { + auto view = value ? StatePatch::resolveBatch(value->reference) : eve::script::Borrowed(); + if (!view.isBound()) return staleStateResult(vm, "batch"); return eve::script::projectResult( vm, eve::Result::success(view->setExpected(subject, key, jsonValue, expectedJson)), [](bool result) { return eve::Value(result); }); }); ownedBatch.addFunc("removeExpected", [vm](ScriptStateBatch* value, const std::string& subject, - const std::string& key, const std::string& expectedJson) { - auto view = value ? StatePatch::resolveBatch(value->reference) - : eve::script::Borrowed(); - if (!view.isBound()) - return staleStateResult(vm, "batch"); - return eve::script::projectResult( - vm, eve::Result::success(view->removeExpected(subject, key, expectedJson)), - [](bool result) { return eve::Value(result); }); + const std::string& key, const std::string& expectedJson) { + auto view = value ? StatePatch::resolveBatch(value->reference) : eve::script::Borrowed(); + if (!view.isBound()) return staleStateResult(vm, "batch"); + return eve::script::projectResult(vm, + eve::Result::success(view->removeExpected(subject, key, expectedJson)), + [](bool result) { return eve::Value(result); }); }); ownedBatch.addFunc("clear", [](ScriptStateBatch* value) { - auto view = value ? StatePatch::resolveBatch(value->reference) - : eve::script::Borrowed(); + auto view = value ? StatePatch::resolveBatch(value->reference) : eve::script::Borrowed(); if (view.isBound()) view->clear(); }); ownedBatch.addFunc("size", [](ScriptStateBatch* value) { - auto view = value ? StatePatch::resolveBatch(value->reference) - : eve::script::Borrowed(); + auto view = value ? StatePatch::resolveBatch(value->reference) : eve::script::Borrowed(); return view.isBound() ? view->size() : 0; }); ownedBatch.addFunc("result", [](ScriptStateBatch* value) -> PatchResult* { - auto view = value ? StatePatch::resolveBatch(value->reference) - : eve::script::Borrowed(); + auto view = value ? StatePatch::resolveBatch(value->reference) : eve::script::Borrowed(); return view.isBound() ? const_cast(&view->result()) : nullptr; }); @@ -868,8 +832,8 @@ void StatePatch::expose(ssq::Table& table) { store.addFunc("restoreJson", [vm](Store* value, const std::string& json) { if (!value) return eve::script::projectResult( - vm, statePatchBindingFailure(eve::DiagnosticCode::InvalidArgument, - "state store must not be null", "store")); + vm, statePatchBindingFailure(eve::DiagnosticCode::InvalidArgument, "state store must not be null", + "store")); return eve::script::projectResult(vm, value->restoreJson(json)); }); @@ -882,19 +846,18 @@ void StatePatch::expose(ssq::Table& table) { ownedStore.addFunc("handle", [](ScriptStateStore* value) { return value ? static_cast(value->reference.packed()) : int64_t{0}; }); - ownedStore.addFunc("isStale", [](ScriptStateStore* value) { - return !value || StatePatch::isStale(value->reference); - }); + ownedStore.addFunc("isStale", + [](ScriptStateStore* value) { return !value || StatePatch::isStale(value->reference); }); ownedStore.addFunc("release", [vm](ScriptStateStore* value) { if (!value) return eve::script::projectResult( vm, statePatchBindingFailure(eve::DiagnosticCode::InvalidArgument, - "owned state store proxy must not be null", "store")); + "owned state store proxy must not be null", "store")); return eve::script::projectResult(vm, StatePatch::release(value->reference)); }); ownedStore.addFunc("commit", [](ScriptStateStore* value, ScriptStateBatch* batch) { if (!value || !batch || !(batch->reference.store == value->reference)) return false; - auto store = StatePatch::resolve(value->reference); + auto store = StatePatch::resolve(value->reference); auto staged = StatePatch::resolveBatch(batch->reference); return store.isBound() && staged.isBound() && store->commit(staged.get()); }); @@ -902,21 +865,19 @@ void StatePatch::expose(ssq::Table& table) { auto view = value ? StatePatch::resolve(value->reference) : eve::script::Borrowed(); return view.isBound() ? static_cast(view->revision()) : int64_t{0}; }); - ownedStore.addFunc("has", [](ScriptStateStore* value, const std::string& subject, - const std::string& key) { + ownedStore.addFunc("has", [](ScriptStateStore* value, const std::string& subject, const std::string& key) { auto view = value ? StatePatch::resolve(value->reference) : eve::script::Borrowed(); return view.isBound() && view->has(subject, key); }); - ownedStore.addFunc("get", [](ScriptStateStore* value, const std::string& subject, - const std::string& key) { + ownedStore.addFunc("get", [](ScriptStateStore* value, const std::string& subject, const std::string& key) { auto view = value ? StatePatch::resolve(value->reference) : eve::script::Borrowed(); return view.isBound() ? view->get(subject, key) : std::string{}; }); - ownedStore.addFunc("valueRevision", [](ScriptStateStore* value, const std::string& subject, - const std::string& key) { - auto view = value ? StatePatch::resolve(value->reference) : eve::script::Borrowed(); - return view.isBound() ? static_cast(view->valueRevision(subject, key)) : int64_t{0}; - }); + ownedStore.addFunc("valueRevision", + [](ScriptStateStore* value, const std::string& subject, const std::string& key) { + auto view = value ? StatePatch::resolve(value->reference) : eve::script::Borrowed(); + return view.isBound() ? static_cast(view->valueRevision(subject, key)) : int64_t{0}; + }); ownedStore.addFunc("querySubjects", [](ScriptStateStore* value) { auto view = value ? StatePatch::resolve(value->reference) : eve::script::Borrowed(); return view.isBound() ? view->querySubjects() : 0; @@ -966,26 +927,28 @@ void StatePatch::expose(ssq::Table& table) { if (!view.isBound()) return eve::script::projectResult( vm, statePatchBindingFailure(eve::DiagnosticCode::StaleHandle, - "owned state-patch store handle is stale", "store")); + "owned state-patch store handle is stale", "store")); return eve::script::projectResult(vm, view->restoreJson(json)); }); ownedStore.addFunc("newBatch", [vm](ScriptStateStore* value) -> ssq::Table { if (!value) return eve::script::projectStatusResult( - vm, statePatchBindingFailure(eve::DiagnosticCode::InvalidArgument, - "owned state store proxy must not be null", "store") - .status(), + vm, + statePatchBindingFailure(eve::DiagnosticCode::InvalidArgument, + "owned state store proxy must not be null", "store") + .status(), false, false); auto store = StatePatch::resolve(value->reference); if (!store.isBound()) return eve::script::projectStatusResult( - vm, statePatchBindingFailure(eve::DiagnosticCode::StaleHandle, - "owned state-patch store handle is stale", "store") - .status(), + vm, + statePatchBindingFailure(eve::DiagnosticCode::StaleHandle, + "owned state-patch store handle is stale", "store") + .status(), false, false); auto batch = store->newBatch(); if (!batch) return eve::script::projectStatusResult(vm, batch.status(), false, false); - const auto batchRef = std::move(batch).takeValue(); + const auto batchRef = std::move(batch).takeValue(); StateBatchHandleRef reference{value->reference, batchRef, batchRef.ownerEpoch}; return makeOwnedProxy( vm, eve::Result::success(reference), @@ -1000,8 +963,7 @@ void StatePatch::expose(ssq::Class& cls) { cls.addFunc("getName", &StatePatch::getName); cls.addFunc("newStore", [vm = cls.getHandle()](StatePatch*) -> ssq::Table { return makeOwnedProxy( - vm, StatePatch::newStore(), - [](StateStoreHandleRef ref) { return StatePatch::release(ref); }); + vm, StatePatch::newStore(), [](StateStoreHandleRef ref) { return StatePatch::release(ref); }); }); } diff --git a/src/modules/statepatch/StatePatch.h b/src/modules/statepatch/StatePatch.h index 372dad70..159a3cb9 100644 --- a/src/modules/statepatch/StatePatch.h +++ b/src/modules/statepatch/StatePatch.h @@ -35,8 +35,8 @@ using PatchBatchHandleRef = eve::script::RuntimeHandleRef; */ struct StateBatchHandleRef { StateStoreHandleRef store; - PatchBatchHandleRef batch; - std::uint64_t ownerEpoch = 0; + PatchBatchHandleRef batch; + std::uint64_t ownerEpoch = 0; /** @brief Returns the batch slot/generation projection. */ [[nodiscard]] constexpr std::uint64_t packed() const noexcept { return batch.packed(); } @@ -123,8 +123,7 @@ public: */ [[nodiscard]] eve::Result newBatch(); /** @brief Resolves a live batch as a non-owning observation. */ - [[nodiscard]] eve::script::Borrowed resolveBatch( - PatchBatchHandleRef reference) noexcept; + [[nodiscard]] eve::script::Borrowed resolveBatch(PatchBatchHandleRef reference) noexcept; /** @brief Releases a store-owned patch batch. */ [[nodiscard]] eve::Result releaseBatch(PatchBatchHandleRef reference); /** @brief Reports whether a batch reference is stale for this Store. */ @@ -170,22 +169,20 @@ public: [[nodiscard]] eve::Result restoreJson(const std::string& json); /** @brief Captures the state-patch payload in the common snapshot envelope. */ - [[nodiscard]] eve::Result snapshot( - const eve::SnapshotHashProvider& hashProvider) const; + [[nodiscard]] eve::Result snapshot(const eve::SnapshotHashProvider& hashProvider) const; /** * @brief Restores a verified or migrated state-patch envelope atomically. * @param snapshot Source envelope with schema `statepatch:store`. * @param hashProvider Explicit content-digest provider. * @return Success, or a failure leaving all values and metadata unchanged. */ - [[nodiscard]] eve::Result restoreSnapshot( - const eve::SnapshotEnvelope& snapshot, const eve::SnapshotHashProvider& hashProvider); + [[nodiscard]] eve::Result restoreSnapshot(const eve::SnapshotEnvelope& snapshot, + const eve::SnapshotHashProvider& hashProvider); /** @brief Serializes the common state-patch snapshot envelope. */ - [[nodiscard]] eve::Result snapshotEnvelopeJson( - const eve::SnapshotHashProvider& hashProvider) const; + [[nodiscard]] eve::Result snapshotEnvelopeJson(const eve::SnapshotHashProvider& hashProvider) const; /** @brief Parses and transactionally restores a common state-patch envelope. */ - [[nodiscard]] eve::Result restoreSnapshotJson( - std::string_view json, const eve::SnapshotHashProvider& hashProvider); + [[nodiscard]] eve::Result restoreSnapshotJson(std::string_view json, + const eve::SnapshotHashProvider& hashProvider); private: friend class StoreTransactionParticipant; @@ -199,15 +196,15 @@ private: Values values_; uint64_t revision_ = 0; uint64_t nextSequence_ = 1; - eve::PersistentId instanceId_; - eve::SimulationTick tick_; + eve::PersistentId instanceId_; + eve::SimulationTick tick_; std::set> dirty_; std::vector events_; std::vector query_; std::vector> dirtyQuery_; eve::script::RuntimeObjectRegistry batches_; - void copyTransactionStateFrom(const Store& source); - void swapTransactionState(Store& other) noexcept; + void copyTransactionStateFrom(const Store& source); + void swapTransactionState(Store& other) noexcept; bool transactionStateEquals(const Store& other) const; }; @@ -252,17 +249,17 @@ private: [[nodiscard]] eve::Result lifecycleFailure(std::string_view operation) const; [[nodiscard]] eve::Result contextFailure(const transaction::TransactionContext& context) const; - bool contextMatches(const transaction::TransactionContext& context) const noexcept; + bool contextMatches(const transaction::TransactionContext& context) const noexcept; - Store& store_; - PatchBatch& batch_; - std::unique_ptr before_; - std::unique_ptr prepared_; - std::unique_ptr expectedAfter_; - std::string transactionId_; - std::string correlationId_; - std::string causationId_; - Phase phase_ = Phase::Idle; + Store& store_; + PatchBatch& batch_; + std::unique_ptr before_; + std::unique_ptr prepared_; + std::unique_ptr expectedAfter_; + std::string transactionId_; + std::string correlationId_; + std::string causationId_; + Phase phase_ = Phase::Idle; }; /** @brief Script module factory for generic state patch stores. */ @@ -279,16 +276,14 @@ public: */ [[nodiscard]] static eve::Result newStore(); /** @brief Resolves a live store as a non-owning observation. */ - [[nodiscard]] static eve::script::Borrowed resolve( - StateStoreHandleRef reference) noexcept; + [[nodiscard]] static eve::script::Borrowed resolve(StateStoreHandleRef reference) noexcept; /** @brief Releases a module-owned state store. */ [[nodiscard]] static eve::Result release(StateStoreHandleRef reference); /** @brief Reports whether a store reference is stale for the current module. */ [[nodiscard]] static bool isStale(StateStoreHandleRef reference) noexcept; /** @brief Resolves a store-qualified batch as a non-owning observation. */ - [[nodiscard]] static eve::script::Borrowed resolveBatch( - StateBatchHandleRef reference) noexcept; + [[nodiscard]] static eve::script::Borrowed resolveBatch(StateBatchHandleRef reference) noexcept; /** @brief Releases a store-qualified patch batch. */ [[nodiscard]] static eve::Result releaseBatch(StateBatchHandleRef reference); /** @brief Reports whether a store-qualified batch reference is stale. */ diff --git a/src/modules/tags/StateAccessAdapter.cpp b/src/modules/tags/StateAccessAdapter.cpp index b3c6873a..5df52441 100644 --- a/src/modules/tags/StateAccessAdapter.cpp +++ b/src/modules/tags/StateAccessAdapter.cpp @@ -6,15 +6,12 @@ namespace eve::tags { namespace { -eve::Result failure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { +eve::Result failure(eve::DiagnosticCode code, std::string message, std::string path = {}) { return eve::Result::failure( eve::Diagnostic::error(code, std::move(message), std::move(path))); } -bool isTagKey(std::string_view key) { - return key.starts_with("tag.") || key.starts_with("tag:"); -} +bool isTagKey(std::string_view key) { return key.starts_with("tag.") || key.starts_with("tag:"); } std::string tagName(std::string_view key) { if (key.starts_with("tag.") || key.starts_with("tag:")) key.remove_prefix(4); @@ -23,25 +20,22 @@ std::string tagName(std::string_view key) { } // namespace -std::optional TagStoreStateAdapter::value(std::string_view subject, - std::string_view key) const { +std::optional TagStoreStateAdapter::value(std::string_view subject, std::string_view key) const { if (subject.empty() || key.empty() || !isTagKey(key)) return std::nullopt; return eve::Value(store_.hasTag(std::string(subject), tagName(key))); } -std::optional TagStoreStateAdapter::hasTag(std::string_view subject, - std::string_view tag) const { +std::optional TagStoreStateAdapter::hasTag(std::string_view subject, std::string_view tag) const { if (subject.empty() || tag.empty()) return std::nullopt; return store_.hasTag(std::string(subject), std::string(tag)); } -std::optional TagStoreStateAdapter::state(std::string_view subject, - std::string_view key) const { +std::optional TagStoreStateAdapter::state(std::string_view subject, std::string_view key) const { return value(subject, key); } -eve::Result TagStoreStateAdapter::apply( - std::span mutations, const eve::MutationContext& context) { +eve::Result TagStoreStateAdapter::apply(std::span mutations, + const eve::MutationContext& context) { for (std::size_t index = 0; index < mutations.size(); ++index) { const auto& mutation = mutations[index]; if (mutation.persistent) @@ -49,40 +43,32 @@ eve::Result TagStoreStateAdapter::apply( "TagStore mutations are volatile; use StatePatch for persistence", "mutations[" + std::to_string(index) + "]"); if (mutation.subject.empty() || mutation.key.empty()) - return failure(eve::DiagnosticCode::InvalidArgument, - "tag mutation requires subject and tag", "mutations[" + std::to_string(index) + "]"); - if ((mutation.kind == eve::MutationKind::Set) && - (!isTagKey(mutation.key) || !mutation.value.isBool())) - return failure(eve::DiagnosticCode::InvalidArgument, - "tag Set requires a tag.* key and boolean value", + return failure(eve::DiagnosticCode::InvalidArgument, "tag mutation requires subject and tag", + "mutations[" + std::to_string(index) + "]"); + if ((mutation.kind == eve::MutationKind::Set) && (!isTagKey(mutation.key) || !mutation.value.isBool())) + return failure(eve::DiagnosticCode::InvalidArgument, "tag Set requires a tag.* key and boolean value", "mutations[" + std::to_string(index) + "]"); if (mutation.kind == eve::MutationKind::Set || mutation.kind == eve::MutationKind::AddTag || mutation.kind == eve::MutationKind::RemoveTag) continue; - return failure(eve::DiagnosticCode::Unsupported, - "TagStore supports Set, AddTag, and RemoveTag only", + return failure(eve::DiagnosticCode::Unsupported, "TagStore supports Set, AddTag, and RemoveTag only", "mutations[" + std::to_string(index) + "]"); } - TagStore candidate = store_; - std::size_t changed = 0; + TagStore candidate = store_; + std::size_t changed = 0; for (const auto& mutation : mutations) { - const std::string tag = tagName(mutation.key); - bool didChange = false; + const std::string tag = tagName(mutation.key); + bool didChange = false; switch (mutation.kind) { - case eve::MutationKind::Set: - didChange = mutation.value.asBool() ? candidate.addTag(mutation.subject, tag) - : candidate.removeTag(mutation.subject, tag); - break; - case eve::MutationKind::AddTag: - didChange = candidate.addTag(mutation.subject, tag); - break; - case eve::MutationKind::RemoveTag: - didChange = candidate.removeTag(mutation.subject, tag); - break; - case eve::MutationKind::Remove: - case eve::MutationKind::AddNumber: - break; + case eve::MutationKind::Set: + didChange = mutation.value.asBool() ? candidate.addTag(mutation.subject, tag) + : candidate.removeTag(mutation.subject, tag); + break; + case eve::MutationKind::AddTag: didChange = candidate.addTag(mutation.subject, tag); break; + case eve::MutationKind::RemoveTag: didChange = candidate.removeTag(mutation.subject, tag); break; + case eve::MutationKind::Remove: + case eve::MutationKind::AddNumber: break; } if (didChange) ++changed; } diff --git a/src/modules/tags/StateAccessAdapter.h b/src/modules/tags/StateAccessAdapter.h index 316243ff..b8168c48 100644 --- a/src/modules/tags/StateAccessAdapter.h +++ b/src/modules/tags/StateAccessAdapter.h @@ -23,33 +23,27 @@ public: explicit TagStoreStateAdapter(TagStore& store) noexcept : store_(store) {} /** @copydoc eve::IStateQuery::value */ - [[nodiscard]] std::optional value(std::string_view subject, - std::string_view key) const override; + [[nodiscard]] std::optional value(std::string_view subject, std::string_view key) const override; /** @copydoc eve::IStateQuery::hasTag */ - [[nodiscard]] std::optional hasTag(std::string_view subject, - std::string_view tag) const override; + [[nodiscard]] std::optional hasTag(std::string_view subject, std::string_view tag) const override; /** @copydoc eve::IStateQuery::attribute */ - [[nodiscard]] std::optional attribute(std::string_view, - std::string_view) const override { + [[nodiscard]] std::optional attribute(std::string_view, std::string_view) const override { return std::nullopt; } /** @copydoc eve::IStateQuery::resource */ - [[nodiscard]] std::optional resource(std::string_view, - std::string_view) const override { + [[nodiscard]] std::optional resource(std::string_view, std::string_view) const override { return std::nullopt; } /** @copydoc eve::IStateQuery::state */ - [[nodiscard]] std::optional state(std::string_view subject, - std::string_view key) const override; + [[nodiscard]] std::optional state(std::string_view subject, std::string_view key) const override; /** @copydoc eve::IStateQuery::authority */ - [[nodiscard]] std::optional authority(std::string_view, - std::string_view) const override { + [[nodiscard]] std::optional authority(std::string_view, std::string_view) const override { return std::nullopt; } /** @copydoc eve::IStateMutation::apply */ - [[nodiscard]] eve::Result apply( - std::span mutations, const eve::MutationContext& context) override; + [[nodiscard]] eve::Result apply(std::span mutations, + const eve::MutationContext& context) override; private: TagStore& store_; diff --git a/src/modules/timer/Timer.cpp b/src/modules/timer/Timer.cpp index 90e93d85..f07b0737 100644 --- a/src/modules/timer/Timer.cpp +++ b/src/modules/timer/Timer.cpp @@ -20,9 +20,8 @@ Timer::Timer() { eve::cap::provide(this); eve::cap::provide(this); freq_ = SDL_GetPerformanceFrequency(); - start_ = monotonicNow(); - simulationClock_ = std::make_unique( - *this, eve::Duration::fromNanoseconds(16666667)); + start_ = monotonicNow(); + simulationClock_ = std::make_unique(*this, eve::Duration::fromNanoseconds(16666667)); } Timer::~Timer() { @@ -36,9 +35,7 @@ float Timer::getTime() const { return static_cast(elapsed.value().seconds()); } -float Timer::getDelta() const { - return static_cast(getDeltaDuration().seconds()); -} +float Timer::getDelta() const { return static_cast(getDeltaDuration().seconds()); } float Timer::step() { auto result = stepSimulation(); @@ -53,21 +50,19 @@ float Timer::step() { eve::MonotonicTimestamp Timer::monotonicNow() const { if (freq_ == 0) return eve::MonotonicTimestamp::zero(); const long double counter = static_cast(SDL_GetPerformanceCounter()); - const long double nanos = counter * 1000000000.0L / static_cast(freq_); + const long double nanos = counter * 1000000000.0L / static_cast(freq_); if (nanos >= static_cast(std::numeric_limits::max())) return eve::MonotonicTimestamp(std::numeric_limits::max()); return eve::MonotonicTimestamp(static_cast(nanos)); } eve::WallClockTimestamp Timer::wallClockNow() const { - const auto now = std::chrono::system_clock::now().time_since_epoch(); + const auto now = std::chrono::system_clock::now().time_since_epoch(); const auto nanos = std::chrono::duration_cast(now).count(); return eve::WallClockTimestamp(nanos); } -eve::Result> Timer::stepSimulation() { - return simulationClock_->sample(); -} +eve::Result> Timer::stepSimulation() { return simulationClock_->sample(); } eve::SimulationClock& Timer::simulationClock() { return *simulationClock_; } diff --git a/src/modules/timer/Timer.h b/src/modules/timer/Timer.h index ea61ab09..50898ba2 100644 --- a/src/modules/timer/Timer.h +++ b/src/modules/timer/Timer.h @@ -54,8 +54,8 @@ public: double elapsedSeconds() override { return getTime(); } private: - uint64_t freq_ = 0; - eve::MonotonicTimestamp start_; + uint64_t freq_ = 0; + eve::MonotonicTimestamp start_; std::unique_ptr simulationClock_; }; diff --git a/src/modules/touch/sdl/EventSink.cpp b/src/modules/touch/sdl/EventSink.cpp index 3dccd5c9..99da88dd 100644 --- a/src/modules/touch/sdl/EventSink.cpp +++ b/src/modules/touch/sdl/EventSink.cpp @@ -64,8 +64,8 @@ public: struct Register { Register() { static TouchEventSink sink; - eve::cap::addListener( - &sink, eve::platform_event::IPlatformEventSink::kInput); + eve::cap::addListener(&sink, + eve::platform_event::IPlatformEventSink::kInput); } } g_register; diff --git a/src/modules/transaction/AtomicResourcePayment.cpp b/src/modules/transaction/AtomicResourcePayment.cpp index fc44bc5a..601b3a86 100644 --- a/src/modules/transaction/AtomicResourcePayment.cpp +++ b/src/modules/transaction/AtomicResourcePayment.cpp @@ -10,43 +10,40 @@ namespace { template eve::Result failure(eve::DiagnosticCode code, std::string message, std::string path = {}) { - return eve::Result::failure( - eve::Diagnostic::error(code, std::move(message), std::move(path))); + return eve::Result::failure(eve::Diagnostic::error(code, std::move(message), std::move(path))); } } // namespace -eve::Result AtomicResourcePayment::execute( - const TransactionContext& context, - std::span participants) { +eve::Result AtomicResourcePayment::execute(const TransactionContext& context, + std::span participants) { Coordinator coordinator; return coordinator.execute(context, participants); } -eve::Result AtomicResourcePayment::execute( - const TransactionContext& context, ITransactionParticipant& effect) { +eve::Result AtomicResourcePayment::execute(const TransactionContext& context, + ITransactionParticipant& effect) { std::array participants{&effect}; return execute(context, std::span(participants)); } -eve::Result AtomicResourcePayment::execute( - const TransactionContext& context, eve::resource::IResourceAccount& account, - const eve::resource::CostSpec& cost, ITransactionParticipant& effect) { +eve::Result AtomicResourcePayment::execute(const TransactionContext& context, + eve::resource::IResourceAccount& account, + const eve::resource::CostSpec& cost, + ITransactionParticipant& effect) { std::array participants{&effect}; - return execute(context, account, cost, - std::span(participants)); + return execute(context, account, cost, std::span(participants)); } -eve::Result AtomicResourcePayment::execute( - const TransactionContext& context, eve::resource::IResourceAccount& account, - const eve::resource::CostSpec& cost, - std::span participants) { +eve::Result AtomicResourcePayment::execute(const TransactionContext& context, + eve::resource::IResourceAccount& account, + const eve::resource::CostSpec& cost, + std::span participants) { if (!cost.isValid()) - return failure( - eve::DiagnosticCode::InvalidArgument, - "resource payment requires a validated non-empty CostSpec", "cost"); + return failure(eve::DiagnosticCode::InvalidArgument, + "resource payment requires a validated non-empty CostSpec", "cost"); - ResourceDebitParticipant debit(account, cost, "resource-payment"); + ResourceDebitParticipant debit(account, cost, "resource-payment"); std::vector all; all.reserve(participants.size() + 1u); for (auto* participant : participants) all.push_back(participant); @@ -58,13 +55,13 @@ eve::Result AtomicResourcePayment::execute( return coordinator.execute(context, std::span(all)); } -eve::Result AtomicResourcePayment::execute( - std::string transactionId, eve::resource::IResourceAccount& account, - const eve::resource::CostSpec& cost, ITransactionParticipant& effect) { +eve::Result AtomicResourcePayment::execute(std::string transactionId, + eve::resource::IResourceAccount& account, + const eve::resource::CostSpec& cost, + ITransactionParticipant& effect) { if (transactionId.empty()) - return failure( - eve::DiagnosticCode::InvalidArgument, - "resource payment transaction id must not be empty", "transactionId"); + return failure(eve::DiagnosticCode::InvalidArgument, + "resource payment transaction id must not be empty", "transactionId"); return execute(TransactionContext(std::move(transactionId)), account, cost, effect); } diff --git a/src/modules/transaction/AtomicResourcePayment.h b/src/modules/transaction/AtomicResourcePayment.h index c64f4758..d863b6b6 100644 --- a/src/modules/transaction/AtomicResourcePayment.h +++ b/src/modules/transaction/AtomicResourcePayment.h @@ -36,9 +36,8 @@ public: * card state plus a ruleset effect). The payment helper does not * own the participants. */ - [[nodiscard]] static eve::Result execute( - const TransactionContext& context, - std::span participants); + [[nodiscard]] static eve::Result execute(const TransactionContext& context, + std::span participants); /** * @brief Execute a resource debit and several domain participants atomically. @@ -52,10 +51,10 @@ public: * participants so a failed debit compensates already-visible * domain effects in reverse order. */ - [[nodiscard]] static eve::Result execute( - const TransactionContext& context, eve::resource::IResourceAccount& account, - const eve::resource::CostSpec& cost, - std::span participants); + [[nodiscard]] static eve::Result execute(const TransactionContext& context, + eve::resource::IResourceAccount& account, + const eve::resource::CostSpec& cost, + std::span participants); /** * @brief Execute an effect without a resource cost. @@ -63,8 +62,8 @@ public: * @param effect Borrowed domain participant; it owns its staged state. * @return A committed receipt or structured lifecycle diagnostics. */ - [[nodiscard]] static eve::Result execute( - const TransactionContext& context, ITransactionParticipant& effect); + [[nodiscard]] static eve::Result execute(const TransactionContext& context, + ITransactionParticipant& effect); /** * @brief Execute one canonical cost and one domain effect atomically. @@ -76,9 +75,10 @@ public: * @remarks The account is addressed by the reservation's AccountNonce; * no current/default account lookup is performed. */ - [[nodiscard]] static eve::Result execute( - const TransactionContext& context, eve::resource::IResourceAccount& account, - const eve::resource::CostSpec& cost, ITransactionParticipant& effect); + [[nodiscard]] static eve::Result execute(const TransactionContext& context, + eve::resource::IResourceAccount& account, + const eve::resource::CostSpec& cost, + ITransactionParticipant& effect); /** * @brief Convenience overload using a stable string transaction id. @@ -88,9 +88,10 @@ public: * @param effect Borrowed domain participant. * @return A committed receipt or structured lifecycle diagnostics. */ - [[nodiscard]] static eve::Result execute( - std::string transactionId, eve::resource::IResourceAccount& account, - const eve::resource::CostSpec& cost, ITransactionParticipant& effect); + [[nodiscard]] static eve::Result execute(std::string transactionId, + eve::resource::IResourceAccount& account, + const eve::resource::CostSpec& cost, + ITransactionParticipant& effect); }; } // namespace eve::transaction diff --git a/src/modules/transaction/ResourceAccountParticipant.cpp b/src/modules/transaction/ResourceAccountParticipant.cpp index 83f1f72f..f531381a 100644 --- a/src/modules/transaction/ResourceAccountParticipant.cpp +++ b/src/modules/transaction/ResourceAccountParticipant.cpp @@ -8,13 +8,10 @@ namespace { eve::Status lifecycleConflict(std::string message) { return eve::Status::failure( eve::StatusCode::Conflict, - eve::Diagnostic::error(eve::DiagnosticCode::Conflict, std::move(message), - "resource.transaction")); + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, std::move(message), "resource.transaction")); } -eve::Result forwardFailure(eve::Result& result) { - return eve::Result::failure(result.status()); -} +eve::Result forwardFailure(eve::Result& result) { return eve::Result::failure(result.status()); } template eve::Result forwardFailure(eve::Result& result) { @@ -24,8 +21,7 @@ eve::Result forwardFailure(eve::Result& result) { } // namespace ResourceDebitParticipant::ResourceDebitParticipant(eve::resource::IResourceAccount& account, - eve::resource::CostSpec cost, - std::string name) + eve::resource::CostSpec cost, std::string name) : account_(account), cost_(std::move(cost)), name_(std::move(name)) { if (name_.empty()) name_ = "resource-debit"; } diff --git a/src/modules/transaction/ResourceAccountParticipant.h b/src/modules/transaction/ResourceAccountParticipant.h index 1909be3e..cdd4d731 100644 --- a/src/modules/transaction/ResourceAccountParticipant.h +++ b/src/modules/transaction/ResourceAccountParticipant.h @@ -40,14 +40,13 @@ public: * @param cost Validated or pending-validation canonical resource cost. * @param name Stable diagnostic name used by Coordinator. */ - ResourceDebitParticipant(eve::resource::IResourceAccount& account, - eve::resource::CostSpec cost, + ResourceDebitParticipant(eve::resource::IResourceAccount& account, eve::resource::CostSpec cost, std::string name = "resource-debit"); - ResourceDebitParticipant(const ResourceDebitParticipant&) = delete; + ResourceDebitParticipant(const ResourceDebitParticipant&) = delete; ResourceDebitParticipant& operator=(const ResourceDebitParticipant&) = delete; - ResourceDebitParticipant(ResourceDebitParticipant&&) = delete; - ResourceDebitParticipant& operator=(ResourceDebitParticipant&&) = delete; + ResourceDebitParticipant(ResourceDebitParticipant&&) = delete; + ResourceDebitParticipant& operator=(ResourceDebitParticipant&&) = delete; /** @brief Return the diagnostic name supplied at construction. */ [[nodiscard]] std::string_view name() const noexcept override { return name_; } @@ -69,10 +68,10 @@ public: private: eve::resource::IResourceAccount& account_; - eve::resource::CostSpec cost_; - eve::resource::Reservation reservation_; - std::string name_; - ResourceDebitState state_ = ResourceDebitState::Idle; + eve::resource::CostSpec cost_; + eve::resource::Reservation reservation_; + std::string name_; + ResourceDebitState state_ = ResourceDebitState::Idle; }; } // namespace eve::transaction diff --git a/src/modules/transaction/Transaction.cpp b/src/modules/transaction/Transaction.cpp index 011035b6..f7ddf876 100644 --- a/src/modules/transaction/Transaction.cpp +++ b/src/modules/transaction/Transaction.cpp @@ -64,8 +64,7 @@ std::string canonicalJson(const eve::json::Value& value) { } template -eve::Result transactionBindingFailure(eve::DiagnosticCode code, std::string message, - std::string path = {}) { +eve::Result transactionBindingFailure(eve::DiagnosticCode code, std::string message, std::string path = {}) { return eve::Result::failure( eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "transaction")); } @@ -90,17 +89,17 @@ eve::LogicalId transactionSchema() { const eve::SnapshotMigrationChain& transactionMigrations() { static const eve::SnapshotMigrationChain chain = [] { eve::SnapshotMigrationChain result; - const auto registration = result.add( - transactionSchema(), eve::SchemaVersion(0), eve::SchemaVersion(1), - [](const eve::Value& payload) -> eve::Result { - const auto* object = payload.getIf(); - if (!object) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::ParseError, "transaction ledger payload must be an object")); - eve::Value::Object migrated = *object; - if (!migrated.contains("version")) migrated.emplace("version", eve::Value(std::int64_t(1))); - return eve::Result::success(eve::Value(std::move(migrated))); - }); + const auto registration = + result.add(transactionSchema(), eve::SchemaVersion(0), eve::SchemaVersion(1), + [](const eve::Value& payload) -> eve::Result { + const auto* object = payload.getIf(); + if (!object) + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::ParseError, "transaction ledger payload must be an object")); + eve::Value::Object migrated = *object; + if (!migrated.contains("version")) migrated.emplace("version", eve::Value(std::int64_t(1))); + return eve::Result::success(eve::Value(std::move(migrated))); + }); if (!registration.ok()) std::terminate(); return result; }(); @@ -165,8 +164,7 @@ void appendFailure(std::vector& diagnostics, const eve::Status& const auto& original = status.diagnostics(); diagnostics.insert(diagnostics.end(), original.begin(), original.end()); diagnostics.push_back(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, - std::string(phase) + " failed for participant " + std::string(participant.name()), + eve::DiagnosticCode::Failed, std::string(phase) + " failed for participant " + std::string(participant.name()), "transaction." + std::string(phase) + "[" + std::to_string(index) + "]")); } @@ -179,21 +177,19 @@ eve::Result coordinatorFailure(eve::StatusCode code, std::ve } // namespace -Ledger::Ledger(eve::PersistentId instanceId, eve::UuidEntropySource transactionEntropy, - eve::UuidClock transactionClock) - : instanceId_(instanceId), transactionEntropy_(std::move(transactionEntropy)), +Ledger::Ledger(eve::PersistentId instanceId, eve::UuidEntropySource transactionEntropy, eve::UuidClock transactionClock) + : instanceId_(instanceId), + transactionEntropy_(std::move(transactionEntropy)), transactionClock_(std::move(transactionClock)) { - if (transactionEntropy_) - transactionIdGenerator_.emplace(transactionEntropy_, transactionClock_); + if (transactionEntropy_) transactionIdGenerator_.emplace(transactionEntropy_, transactionClock_); } -eve::Result Coordinator::execute( - const TransactionContext& context, std::span participants) const { +eve::Result Coordinator::execute(const TransactionContext& context, + std::span participants) const { if (context.transactionId().empty()) - return coordinatorFailure( - eve::StatusCode::Rejected, - {eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "transaction id must not be empty", - "transactionId")}); + return coordinatorFailure(eve::StatusCode::Rejected, + {eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "transaction id must not be empty", "transactionId")}); if (participants.empty()) return coordinatorFailure( eve::StatusCode::Rejected, @@ -207,15 +203,14 @@ eve::Result Coordinator::execute( "participants[" + std::to_string(i) + "]")}); for (size_t previous = 0; previous < i; ++previous) if (participants[previous] == participants[i]) - return coordinatorFailure( - eve::StatusCode::Rejected, - {eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "a participant may occur only once in a transaction", - "participants[" + std::to_string(i) + "]")}); + return coordinatorFailure(eve::StatusCode::Rejected, + {eve::Diagnostic::error(eve::DiagnosticCode::Conflict, + "a participant may occur only once in a transaction", + "participants[" + std::to_string(i) + "]")}); } TransactionReceipt receipt; - receipt.identity = context.identity(); + receipt.identity = context.identity(); receipt.transactionId = context.transactionId(); receipt.correlationId = context.correlationId(); receipt.causationId = context.causationId(); @@ -224,22 +219,20 @@ eve::Result Coordinator::execute( std::vector prepared; prepared.reserve(participants.size()); for (size_t i = 0; i < participants.size(); ++i) { - auto result = invokeParticipant( - [&] { return participants[i]->prepare(context); }, "prepare", *participants[i]); + auto result = invokeParticipant([&] { return participants[i]->prepare(context); }, "prepare", *participants[i]); if (!result) { std::vector diagnostics; appendFailure(diagnostics, result.status(), "prepare", i, *participants[i]); bool cleanupFailed = false; for (auto it = prepared.rbegin(); it != prepared.rend(); ++it) { - auto rollback = invokeParticipant( - [&] { return participants[*it]->rollback(context); }, "rollback", *participants[*it]); + auto rollback = invokeParticipant([&] { return participants[*it]->rollback(context); }, "rollback", + *participants[*it]); if (!rollback) { cleanupFailed = true; appendFailure(diagnostics, rollback.status(), "rollback", *it, *participants[*it]); } } - return coordinatorFailure(cleanupFailed ? eve::StatusCode::Failed : result.code(), - std::move(diagnostics)); + return coordinatorFailure(cleanupFailed ? eve::StatusCode::Failed : result.code(), std::move(diagnostics)); } prepared.push_back(i); } @@ -248,8 +241,7 @@ eve::Result Coordinator::execute( std::vector committed; committed.reserve(prepared.size()); for (size_t i : prepared) { - auto result = invokeParticipant( - [&] { return participants[i]->commit(context); }, "commit", *participants[i]); + auto result = invokeParticipant([&] { return participants[i]->commit(context); }, "commit", *participants[i]); if (!result) { std::vector diagnostics; appendFailure(diagnostics, result.status(), "commit", i, *participants[i]); @@ -260,23 +252,22 @@ eve::Result Coordinator::execute( // that were already made observable by earlier commits. for (auto it = prepared.rbegin(); it != prepared.rend(); ++it) { if (std::find(committed.begin(), committed.end(), *it) != committed.end()) continue; - auto rollback = invokeParticipant( - [&] { return participants[*it]->rollback(context); }, "rollback", *participants[*it]); + auto rollback = invokeParticipant([&] { return participants[*it]->rollback(context); }, "rollback", + *participants[*it]); if (!rollback) { cleanupFailed = true; appendFailure(diagnostics, rollback.status(), "rollback", *it, *participants[*it]); } } for (auto it = committed.rbegin(); it != committed.rend(); ++it) { - auto compensation = invokeParticipant( - [&] { return participants[*it]->compensate(context); }, "compensation", *participants[*it]); + auto compensation = invokeParticipant([&] { return participants[*it]->compensate(context); }, + "compensation", *participants[*it]); if (!compensation) { cleanupFailed = true; appendFailure(diagnostics, compensation.status(), "compensation", *it, *participants[*it]); } } - return coordinatorFailure(cleanupFailed ? eve::StatusCode::Failed : result.code(), - std::move(diagnostics)); + return coordinatorFailure(cleanupFailed ? eve::StatusCode::Failed : result.code(), std::move(diagnostics)); } committed.push_back(i); } @@ -286,13 +277,12 @@ eve::Result Coordinator::execute( return eve::Result::success(std::move(receipt), eve::Status::success(eve::StatusCode::Applied)); } -eve::Result Coordinator::compensate( - const TransactionContext& context, std::span participants) const { +eve::Result Coordinator::compensate(const TransactionContext& context, + std::span participants) const { if (context.transactionId().empty()) - return coordinatorFailure( - eve::StatusCode::Rejected, - {eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "transaction id must not be empty", - "transactionId")}); + return coordinatorFailure(eve::StatusCode::Rejected, + {eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "transaction id must not be empty", "transactionId")}); if (participants.empty()) return coordinatorFailure( eve::StatusCode::Rejected, @@ -306,15 +296,14 @@ eve::Result Coordinator::compensate( "participants[" + std::to_string(i) + "]")}); for (size_t previous = 0; previous < i; ++previous) if (participants[previous] == participants[i]) - return coordinatorFailure( - eve::StatusCode::Rejected, - {eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "a participant may occur only once in compensation", - "participants[" + std::to_string(i) + "]")}); + return coordinatorFailure(eve::StatusCode::Rejected, + {eve::Diagnostic::error(eve::DiagnosticCode::Conflict, + "a participant may occur only once in compensation", + "participants[" + std::to_string(i) + "]")}); } TransactionReceipt receipt; - receipt.identity = context.identity(); + receipt.identity = context.identity(); receipt.transactionId = context.transactionId(); receipt.correlationId = context.correlationId(); receipt.causationId = context.causationId(); @@ -322,8 +311,8 @@ eve::Result Coordinator::compensate( std::vector diagnostics; for (size_t offset = 0; offset < participants.size(); ++offset) { const size_t index = participants.size() - 1 - offset; - auto result = invokeParticipant( - [&] { return participants[index]->compensate(context); }, "compensation", *participants[index]); + auto result = invokeParticipant([&] { return participants[index]->compensate(context); }, "compensation", + *participants[index]); if (!result) { appendFailure(diagnostics, result.status(), "compensation", index, *participants[index]); continue; @@ -335,8 +324,7 @@ eve::Result Coordinator::compensate( return coordinatorFailure(eve::StatusCode::Failed, std::move(diagnostics)); } receipt.state = CoordinatorState::Compensated; - return eve::Result::success(std::move(receipt), - eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(std::move(receipt), eve::Status::success(eve::StatusCode::Applied)); } std::string stateName(State state) { @@ -352,10 +340,8 @@ std::string stateName(State state) { Plan::Plan(std::string id, std::string correlation, std::string causation, eve::TransactionId identity, eve::UuidEntropySource operationEntropy, eve::UuidClock operationClock) - : id_(std::move(id)), identity_(identity), correlation_(std::move(correlation)), - causation_(std::move(causation)) { - if (operationEntropy) - operationIdGenerator_.emplace(std::move(operationEntropy), std::move(operationClock)); + : id_(std::move(id)), identity_(identity), correlation_(std::move(correlation)), causation_(std::move(causation)) { + if (operationEntropy) operationIdGenerator_.emplace(std::move(operationEntropy), std::move(operationClock)); emit("opened"); } @@ -367,11 +353,11 @@ const std::string& Plan::causation() const { return causation_; } void Plan::emit(const std::string& type, const std::string& operationId, const std::string& detail) { Event event; - event.sequence = nextEvent_++; - event.transactionId = id_; - event.operationId = operationId; - event.type = type; - event.detail = detail; + event.sequence = nextEvent_++; + event.transactionId = id_; + event.operationId = operationId; + event.type = type; + event.detail = detail; event.transactionIdentity = identity_; if (const auto parsed = eve::OperationId::parse(operationId)) event.operationIdentity = *parsed; events_.push_back(std::move(event)); @@ -395,8 +381,7 @@ eve::Result Plan::stage(const std::string& kind, const std::st return failure(eve::DiagnosticCode::Unsupported, "canonical operation identity requires an injected UUID entropy source"); const auto generated = operationIdGenerator_->generate(); - if (!generated) - return failure(eve::DiagnosticCode::Failed, "canonical operation identity generation failed"); + if (!generated) return failure(eve::DiagnosticCode::Failed, "canonical operation identity generation failed"); operationId = eve::OperationId::fromUuid(*generated); } if (findOperation(operationId)) @@ -411,8 +396,7 @@ eve::Result Plan::stage(const std::string& kind, const std::st operations_.push_back(std::move(operation)); ++nextOperation_; emit("staged", operations_.back().id); - return eve::Result::success( - operationId, eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(operationId, eve::Status::success(eve::StatusCode::Applied)); } Operation* findMutable(std::deque& operations, const std::string& id) { @@ -421,9 +405,8 @@ Operation* findMutable(std::deque& operations, const std::string& id) } Operation* findMutable(std::deque& operations, eve::OperationId id) { - auto it = std::find_if(operations.begin(), operations.end(), [id](const Operation& op) { - return !id.isNil() && op.identity == id; - }); + auto it = std::find_if(operations.begin(), operations.end(), + [id](const Operation& op) { return !id.isNil() && op.identity == id; }); return it == operations.end() ? nullptr : &*it; } @@ -547,18 +530,18 @@ std::string Plan::snapshotJson() const { std::ostringstream out; out << "{\"id\":" << quote(id_); if (!identity_.isNil()) out << ",\"identity\":" << quote(identity_.format()); - out << ",\"state\":" << quote(stateName(state_)) - << ",\"correlation\":" << quote(correlation_) << ",\"causation\":" << quote(causation_) - << ",\"error\":" << quote(error_) << ",\"nextOperation\":" << quote(std::to_string(nextOperation_)) + out << ",\"state\":" << quote(stateName(state_)) << ",\"correlation\":" << quote(correlation_) + << ",\"causation\":" << quote(causation_) << ",\"error\":" << quote(error_) + << ",\"nextOperation\":" << quote(std::to_string(nextOperation_)) << ",\"nextEvent\":" << quote(std::to_string(nextEvent_)) << ",\"operations\":["; for (size_t i = 0; i < operations_.size(); ++i) { if (i) out << ','; const auto& op = operations_[i]; out << "{\"id\":" << quote(op.id); if (!op.identity.isNil()) out << ",\"identity\":" << quote(op.identity.format()); - out << ",\"kind\":" << quote(op.kind) << ",\"target\":" << quote(op.target) - << ",\"payload\":" << op.payload << ",\"checked\":" << (op.checked ? "true" : "false") - << ",\"valid\":" << (op.valid ? "true" : "false") << ",\"error\":" << quote(op.error) << '}'; + out << ",\"kind\":" << quote(op.kind) << ",\"target\":" << quote(op.target) << ",\"payload\":" << op.payload + << ",\"checked\":" << (op.checked ? "true" : "false") << ",\"valid\":" << (op.valid ? "true" : "false") + << ",\"error\":" << quote(op.error) << '}'; } out << "],\"events\":["; for (size_t i = 0; i < events_.size(); ++i) { @@ -581,13 +564,12 @@ eve::Result Ledger::create(const std::string& correlation, const std::str return failure(eve::DiagnosticCode::Unsupported, "canonical transaction identity requires an injected UUID entropy source"); const auto generated = transactionIdGenerator_->generate(); - if (!generated) - return failure(eve::DiagnosticCode::Failed, "canonical transaction identity generation failed"); + if (!generated) return failure(eve::DiagnosticCode::Failed, "canonical transaction identity generation failed"); identity = eve::TransactionId::fromUuid(*generated); } if (find(identity)) return failure(eve::DiagnosticCode::Conflict, "transaction identity already exists"); - plans_.push_back(std::unique_ptr(new Plan( - identity.format(), correlation, causation, identity, transactionEntropy_, transactionClock_))); + plans_.push_back(std::unique_ptr( + new Plan(identity.format(), correlation, causation, identity, transactionEntropy_, transactionClock_))); return eve::Result::success(plans_.back().get(), eve::Status::success(eve::StatusCode::Applied)); } @@ -598,9 +580,8 @@ Plan* Ledger::find(const std::string& transactionId) { } Plan* Ledger::find(eve::TransactionId transactionId) { if (transactionId.isNil()) return nullptr; - auto it = std::find_if(plans_.begin(), plans_.end(), [transactionId](const auto& plan) { - return plan->identity() == transactionId; - }); + auto it = std::find_if(plans_.begin(), plans_.end(), + [transactionId](const auto& plan) { return plan->identity() == transactionId; }); return it == plans_.end() ? nullptr : it->get(); } int Ledger::count() const { return static_cast(plans_.size()); } @@ -621,46 +602,43 @@ std::string Ledger::snapshotJson() const { eve::Result Ledger::restore(std::string_view json) { const auto failure = [](eve::DiagnosticCode code, std::string message) { - return eve::Result::failure( - eve::Diagnostic::error(code, std::move(message), "transaction.ledger")); + return eve::Result::failure(eve::Diagnostic::error(code, std::move(message), "transaction.ledger")); }; auto document = eve::json::Document::parse(std::string(json)); auto root = document.root(); uint64_t restoredNext = 0; if (!document.valid() || !root.isObject() || root.getInt("version") != 1 || - !parseU64(root.get("nextTransaction"), restoredNext) || restoredNext == 0 || - !root.get("plans").isArray()) { + !parseU64(root.get("nextTransaction"), restoredNext) || restoredNext == 0 || !root.get("plans").isArray()) { return failure(eve::DiagnosticCode::ParseError, "invalid transaction ledger snapshot"); } std::vector> restored; - uint64_t previousNumeric = 0; - std::vector restoredPlanIds; - auto plans = root.get("plans"); + uint64_t previousNumeric = 0; + std::vector restoredPlanIds; + auto plans = root.get("plans"); for (size_t i = 0; i < plans.size(); ++i) { auto value = plans.at(i); State state = State::Open; uint64_t nextOperation = 0, nextEvent = 0; if (!value.isObject() || !parseState(value.getString("state"), state) || !parseU64(value.get("nextOperation"), nextOperation) || nextOperation == 0 || - !parseU64(value.get("nextEvent"), nextEvent) || nextEvent == 0 || - !value.get("operations").isArray() || !value.get("events").isArray()) { - return failure(eve::DiagnosticCode::ParseError, - "invalid transaction plan at index " + std::to_string(i)); + !parseU64(value.get("nextEvent"), nextEvent) || nextEvent == 0 || !value.get("operations").isArray() || + !value.get("events").isArray()) { + return failure(eve::DiagnosticCode::ParseError, "invalid transaction plan at index " + std::to_string(i)); } - const std::string id = value.getString("id"); - const std::string identityText = value.getString("identity"); + const std::string id = value.getString("id"); + const std::string identityText = value.getString("identity"); eve::TransactionId identity; - bool canonicalIdentity = false; + bool canonicalIdentity = false; if (!identityText.empty()) { const auto parsed = eve::TransactionId::parse(identityText); if (!parsed || parsed->isNil() || id != parsed->format()) { return failure(eve::DiagnosticCode::ParseError, "invalid canonical transaction id at index " + std::to_string(i)); } - identity = *parsed; + identity = *parsed; canonicalIdentity = true; } else if (const auto parsed = eve::TransactionId::parse(id); parsed && !parsed->isNil()) { - identity = *parsed; + identity = *parsed; canonicalIdentity = true; } @@ -673,19 +651,15 @@ eve::Result Ledger::restore(std::string_view json) { numeric = 0; } if (!id.starts_with(prefix) || numeric <= previousNumeric || numeric >= restoredNext) { - return failure(eve::DiagnosticCode::ParseError, - "invalid transaction id at index " + std::to_string(i)); + return failure(eve::DiagnosticCode::ParseError, "invalid transaction id at index " + std::to_string(i)); } } - if (id.empty() || std::find(restoredPlanIds.begin(), restoredPlanIds.end(), id) != - restoredPlanIds.end()) { - return failure(eve::DiagnosticCode::Conflict, - "duplicate transaction id at index " + std::to_string(i)); + if (id.empty() || std::find(restoredPlanIds.begin(), restoredPlanIds.end(), id) != restoredPlanIds.end()) { + return failure(eve::DiagnosticCode::Conflict, "duplicate transaction id at index " + std::to_string(i)); } restoredPlanIds.push_back(id); - auto plan = std::unique_ptr(new Plan( - id, value.getString("correlation"), value.getString("causation"), identity, - transactionEntropy_, transactionClock_)); + auto plan = std::unique_ptr(new Plan(id, value.getString("correlation"), value.getString("causation"), + identity, transactionEntropy_, transactionClock_)); plan->state_ = state; plan->error_ = value.getString("error"); plan->operations_.clear(); @@ -694,19 +668,17 @@ eve::Result Ledger::restore(std::string_view json) { std::vector restoredOperationIds; for (size_t j = 0; j < operations.size(); ++j) { auto opValue = operations.at(j); - if (!opValue.isObject() || opValue.getString("id").empty() || - opValue.getString("kind").empty() || !opValue.get("payload") || - !opValue.get("checked").isBool() || !opValue.get("valid").isBool()) { + if (!opValue.isObject() || opValue.getString("id").empty() || opValue.getString("kind").empty() || + !opValue.get("payload") || !opValue.get("checked").isBool() || !opValue.get("valid").isBool()) { return failure(eve::DiagnosticCode::ParseError, "invalid operation in plan " + id); } Operation op; - op.id = opValue.getString("id"); + op.id = opValue.getString("id"); const std::string operationIdentityText = opValue.getString("identity"); if (!operationIdentityText.empty()) { const auto parsed = eve::OperationId::parse(operationIdentityText); if (!parsed || parsed->isNil() || op.id != parsed->format()) { - return failure(eve::DiagnosticCode::ParseError, - "invalid canonical operation id in plan " + id); + return failure(eve::DiagnosticCode::ParseError, "invalid canonical operation id in plan " + id); } op.identity = *parsed; } else if (const auto parsed = eve::OperationId::parse(op.id); parsed && !parsed->isNil()) { @@ -714,8 +686,7 @@ eve::Result Ledger::restore(std::string_view json) { } if (std::find(restoredOperationIds.begin(), restoredOperationIds.end(), op.id) != restoredOperationIds.end()) { - return failure(eve::DiagnosticCode::Conflict, - "duplicate operation id in plan " + id); + return failure(eve::DiagnosticCode::Conflict, "duplicate operation id in plan " + id); } restoredOperationIds.push_back(op.id); op.kind = opValue.getString("kind"); @@ -737,39 +708,36 @@ eve::Result Ledger::restore(std::string_view json) { return failure(eve::DiagnosticCode::ParseError, "invalid event in plan " + id); } event.transactionId = id; - event.operationId = eventValue.getString("operationId"); - event.type = eventValue.getString("type"); - event.detail = eventValue.getString("detail"); + event.operationId = eventValue.getString("operationId"); + event.type = eventValue.getString("type"); + event.detail = eventValue.getString("detail"); event.transactionIdentity = identity; - if (const auto parsed = eve::OperationId::parse(event.operationId)) - event.operationIdentity = *parsed; + if (const auto parsed = eve::OperationId::parse(event.operationId)) event.operationIdentity = *parsed; previousEvent = event.sequence; plan->events_.push_back(std::move(event)); } if (!plan->events_.empty() && plan->events_.back().sequence >= nextEvent) { - return failure(eve::DiagnosticCode::ParseError, - "invalid event allocator in plan " + id); + return failure(eve::DiagnosticCode::ParseError, "invalid event allocator in plan " + id); } plan->nextOperation_ = nextOperation; - plan->nextEvent_ = nextEvent; + plan->nextEvent_ = nextEvent; if (!canonicalIdentity) previousNumeric = numeric; restored.push_back(std::move(plan)); } - plans_ = std::move(restored); + plans_ = std::move(restored); nextTransaction_ = restoredNext; return eve::Result::success(); } -eve::Result Ledger::snapshot( - const eve::SnapshotHashProvider& hashProvider) const { +eve::Result Ledger::snapshot(const eve::SnapshotHashProvider& hashProvider) const { auto payload = eve::Value::fromJson(snapshotJson()); if (!payload.ok()) return eve::Result::failure(payload.status()); return eve::makeSnapshotEnvelope("transaction.ledger", transactionSchema(), eve::SchemaVersion(1), instanceId_, revision_, tick_, std::move(payload).takeValue(), hashProvider); } -eve::Result Ledger::restoreSnapshot( - const eve::SnapshotEnvelope& source, const eve::SnapshotHashProvider& hashProvider) { +eve::Result Ledger::restoreSnapshot(const eve::SnapshotEnvelope& source, + const eve::SnapshotHashProvider& hashProvider) { if (source.type != "transaction.ledger" || source.schema != transactionSchema()) return snapshotFailure(eve::DiagnosticCode::InvalidArgument, "snapshot does not belong to transaction::Ledger"); @@ -780,33 +748,30 @@ eve::Result Ledger::restoreSnapshot( auto migrated = transactionMigrations().migrate(source, eve::SchemaVersion(1), hashProvider); if (!migrated.ok()) return eve::Result::failure(migrated.status()); const auto& candidateEnvelope = migrated.value(); - auto metadata = eve::validateSnapshotPayloadMetadata(candidateEnvelope.payload, - candidateEnvelope.revision, - candidateEnvelope.tick); + auto metadata = eve::validateSnapshotPayloadMetadata(candidateEnvelope.payload, candidateEnvelope.revision, + candidateEnvelope.tick); if (!metadata.ok()) return eve::Result::failure(metadata.status()); auto payload = candidateEnvelope.payload.toJson(); if (!payload.ok()) return eve::Result::failure(payload.status()); Ledger candidate(instanceId_, transactionEntropy_, transactionClock_); - auto restored = candidate.restore(std::move(payload).takeValue()); + auto restored = candidate.restore(std::move(payload).takeValue()); if (!restored.ok()) return eve::Result::failure(restored.status()); candidate.instanceId_ = candidateEnvelope.instanceId; candidate.revision_ = candidateEnvelope.revision; candidate.tick_ = candidateEnvelope.tick; - *this = std::move(candidate); + *this = std::move(candidate); return eve::Result::success(); } -eve::Result Ledger::snapshotEnvelopeJson( - const eve::SnapshotHashProvider& hashProvider) const { +eve::Result Ledger::snapshotEnvelopeJson(const eve::SnapshotHashProvider& hashProvider) const { auto value = snapshot(hashProvider); if (!value.ok()) return eve::Result::failure(value.status()); return std::move(value).andThen( [](eve::SnapshotEnvelope&& envelope) { return eve::serializeSnapshotEnvelope(envelope); }); } -eve::Result Ledger::restoreSnapshotJson( - std::string_view json, const eve::SnapshotHashProvider& hashProvider) { +eve::Result Ledger::restoreSnapshotJson(std::string_view json, const eve::SnapshotHashProvider& hashProvider) { auto source = eve::parseSnapshotEnvelope(json, hashProvider); if (!source.ok()) return eve::Result::failure(source.status()); return restoreSnapshot(std::move(source).takeValue(), hashProvider); @@ -851,42 +816,42 @@ void Transaction::expose(ssq::Table& table) { if (!p) return eve::script::projectResult( vm, transactionBindingFailure(eve::DiagnosticCode::InvalidArgument, - "transaction plan must not be null", "plan")); + "transaction plan must not be null", "plan")); return eve::script::projectResult(vm, p->markValid(id)); }); plan.addFunc("markInvalid", [vm](Plan* p, const std::string& id, const std::string& error) { if (!p) return eve::script::projectResult( vm, transactionBindingFailure(eve::DiagnosticCode::InvalidArgument, - "transaction plan must not be null", "plan")); + "transaction plan must not be null", "plan")); return eve::script::projectResult(vm, p->markInvalid(id, error)); }); plan.addFunc("validate", [vm](Plan* p) { if (!p) return eve::script::projectResult( vm, transactionBindingFailure(eve::DiagnosticCode::InvalidArgument, - "transaction plan must not be null", "plan")); + "transaction plan must not be null", "plan")); return eve::script::projectResult(vm, p->validate()); }); plan.addFunc("commit", [vm](Plan* p) { if (!p) return eve::script::projectResult( vm, transactionBindingFailure(eve::DiagnosticCode::InvalidArgument, - "transaction plan must not be null", "plan")); + "transaction plan must not be null", "plan")); return eve::script::projectResult(vm, p->commit()); }); plan.addFunc("rollback", [vm](Plan* p, const std::string& reason) { if (!p) return eve::script::projectResult( vm, transactionBindingFailure(eve::DiagnosticCode::InvalidArgument, - "transaction plan must not be null", "plan")); + "transaction plan must not be null", "plan")); return eve::script::projectResult(vm, p->rollback(reason)); }); plan.addFunc("fail", [vm](Plan* p, const std::string& error) { if (!p) return eve::script::projectResult( vm, transactionBindingFailure(eve::DiagnosticCode::InvalidArgument, - "transaction plan must not be null", "plan")); + "transaction plan must not be null", "plan")); return eve::script::projectResult(vm, p->fail(error)); }); plan.addFunc("getError", &Plan::error); @@ -899,26 +864,24 @@ void Transaction::expose(ssq::Table& table) { plan.addFunc("eventCount", &Plan::eventCount); plan.addFunc("eventAt", [](Plan* p, int i) -> Event* { return p ? const_cast(p->eventAt(i)) : nullptr; }); plan.addFunc("snapshotJson", &Plan::snapshotJson); - plan.addFunc("stage", [vm](Plan* value, const std::string& kind, - const std::string& target, - const std::string& payloadJson, - const std::string& operationIdentity) { + plan.addFunc("stage", [vm](Plan* value, const std::string& kind, const std::string& target, + const std::string& payloadJson, const std::string& operationIdentity) { if (!value) return eve::script::projectResult( - vm, transactionBindingFailure( - eve::DiagnosticCode::InvalidArgument, "transaction plan must not be null", "plan"), + vm, + transactionBindingFailure(eve::DiagnosticCode::InvalidArgument, + "transaction plan must not be null", "plan"), [](eve::OperationId id) { return eve::Value(id.isNil() ? std::string{} : id.format()); }); eve::OperationId identity; if (!operationIdentity.empty()) { const auto parsed = eve::OperationId::parse(operationIdentity); if (!parsed) return eve::script::projectResult( - vm, transactionBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "operation identity must be canonical UUID text", "operationIdentity"), - [](eve::OperationId id) { - return eve::Value(id.isNil() ? std::string{} : id.format()); - }); + vm, + transactionBindingFailure(eve::DiagnosticCode::InvalidArgument, + "operation identity must be canonical UUID text", + "operationIdentity"), + [](eve::OperationId id) { return eve::Value(id.isNil() ? std::string{} : id.format()); }); identity = *parsed; } return eve::script::projectResult( @@ -929,9 +892,8 @@ void Transaction::expose(ssq::Table& table) { auto ledger = table.addClass("TransactionLedger", std::function([] { return nullptr; }), false); // Bind the compatibility spelling through a typed lambda so Ledger's // canonical TransactionId overload is never considered by deduction. - ledger.addFunc("find", [](Ledger* ledger, const std::string& id) -> Plan* { - return ledger ? ledger->find(id) : nullptr; - }); + ledger.addFunc("find", + [](Ledger* ledger, const std::string& id) -> Plan* { return ledger ? ledger->find(id) : nullptr; }); ledger.addFunc("count", &Ledger::count); ledger.addFunc("at", &Ledger::at); ledger.addFunc("snapshotJson", &Ledger::snapshotJson); @@ -939,31 +901,31 @@ void Transaction::expose(ssq::Table& table) { if (!value) return eve::script::projectResult( vm, transactionBindingFailure(eve::DiagnosticCode::InvalidArgument, - "transaction ledger must not be null", "ledger")); + "transaction ledger must not be null", "ledger")); return eve::script::projectResult(vm, value->restore(json)); }); - ledger.addFunc("create", [vm](Ledger* value, const std::string& correlation, - const std::string& causation, - const std::string& transactionIdentity) { + ledger.addFunc("create", [vm](Ledger* value, const std::string& correlation, const std::string& causation, + const std::string& transactionIdentity) { if (!value) return eve::script::projectResult( - vm, transactionBindingFailure(eve::DiagnosticCode::InvalidArgument, - "transaction ledger must not be null", "ledger"), + vm, + transactionBindingFailure(eve::DiagnosticCode::InvalidArgument, + "transaction ledger must not be null", "ledger"), [](Plan* plan) { return planBindingValue(plan); }); eve::TransactionId identity; if (!transactionIdentity.empty()) { const auto parsed = eve::TransactionId::parse(transactionIdentity); if (!parsed) return eve::script::projectResult( - vm, transactionBindingFailure( - eve::DiagnosticCode::InvalidArgument, - "transaction identity must be canonical UUID text", "transactionIdentity"), + vm, + transactionBindingFailure(eve::DiagnosticCode::InvalidArgument, + "transaction identity must be canonical UUID text", + "transactionIdentity"), [](Plan* plan) { return planBindingValue(plan); }); identity = *parsed; } - return eve::script::projectResult( - vm, value->create(correlation, causation, identity), - [](Plan* plan) { return planBindingValue(plan); }); + return eve::script::projectResult(vm, value->create(correlation, causation, identity), + [](Plan* plan) { return planBindingValue(plan); }); }); auto cls = table.addClass(name, Transaction::create, false); diff --git a/src/modules/transaction/Transaction.h b/src/modules/transaction/Transaction.h index 72d9e7c9..9b140e75 100644 --- a/src/modules/transaction/Transaction.h +++ b/src/modules/transaction/Transaction.h @@ -34,7 +34,8 @@ public: * @param causationId Identifier of the command/event that caused it, if any. */ TransactionContext(std::string transactionId, std::string correlationId = {}, std::string causationId = {}) - : transactionId_(std::move(transactionId)), correlationId_(std::move(correlationId)), + : transactionId_(std::move(transactionId)), + correlationId_(std::move(correlationId)), causationId_(std::move(causationId)) {} /** @@ -48,7 +49,8 @@ public: std::string causationId = {}) : identity_(transactionId), transactionId_(transactionId.isNil() ? std::string{} : transactionId.format()), - correlationId_(std::move(correlationId)), causationId_(std::move(causationId)) {} + correlationId_(std::move(correlationId)), + causationId_(std::move(causationId)) {} /** @brief Canonical UUID-backed identity; nil only for the legacy string facade. */ [[nodiscard]] const eve::TransactionId& identity() const noexcept { return identity_; } @@ -61,9 +63,9 @@ public: private: eve::TransactionId identity_; - std::string transactionId_; - std::string correlationId_; - std::string causationId_; + std::string transactionId_; + std::string correlationId_; + std::string causationId_; }; /** @brief Terminal or in-flight outcome of coordinator-managed participants. */ @@ -86,15 +88,15 @@ enum class CoordinatorState { struct TransactionReceipt { /** @brief Canonical transaction identity; nil for legacy string contexts. */ eve::TransactionId identity; - std::string transactionId; - std::string correlationId; - std::string causationId; - CoordinatorState state = CoordinatorState::Committed; - std::size_t participantCount = 0; - std::size_t preparedCount = 0; - std::size_t committedCount = 0; - std::size_t rolledBackCount = 0; - std::size_t compensatedCount = 0; + std::string transactionId; + std::string correlationId; + std::string causationId; + CoordinatorState state = CoordinatorState::Committed; + std::size_t participantCount = 0; + std::size_t preparedCount = 0; + std::size_t committedCount = 0; + std::size_t rolledBackCount = 0; + std::size_t compensatedCount = 0; }; /** @@ -175,7 +177,7 @@ public: * @thread Synchronous; caller and participants must obey their documented * thread affinity and must not re-enter this coordinator call. */ - [[nodiscard]] Result execute(const TransactionContext& context, + [[nodiscard]] Result execute(const TransactionContext& context, std::span participants) const; /** @@ -187,7 +189,7 @@ public: * @remarks Compensation is intentionally separate from rollback: rollback * is only for a prepared participant that was never committed. */ - [[nodiscard]] Result compensate(const TransactionContext& context, + [[nodiscard]] Result compensate(const TransactionContext& context, std::span participants) const; }; @@ -246,9 +248,9 @@ public: * @param operationId Explicit non-nil identity, or nil to use this ledger's injected UUID source. * @return The canonical operation identity, or a structured failure. No incrementing string ID is generated. */ - [[nodiscard]] eve::Result stage( - const std::string& kind, const std::string& target, const std::string& payloadJson = "null", - eve::OperationId operationId = {}); + [[nodiscard]] eve::Result stage(const std::string& kind, const std::string& target, + const std::string& payloadJson = "null", + eve::OperationId operationId = {}); /** @brief Records a successful validation result for an operation. */ [[nodiscard]] eve::Result markValid(const std::string& operationId); /** @brief Records a successful validation result for a canonical operation. */ @@ -312,13 +314,12 @@ public: private: friend class Ledger; - Plan(std::string id, std::string correlation, std::string causation, - eve::TransactionId identity = {}, eve::UuidEntropySource operationEntropy = {}, - eve::UuidClock operationClock = {}); + Plan(std::string id, std::string correlation, std::string causation, eve::TransactionId identity = {}, + eve::UuidEntropySource operationEntropy = {}, eve::UuidClock operationClock = {}); void emit(const std::string& type, const std::string& operationId = {}, const std::string& detail = {}); std::string id_; - eve::TransactionId identity_; + eve::TransactionId identity_; State state_ = State::Open; std::string correlation_; std::string causation_; @@ -343,9 +344,8 @@ public: * @param identity Explicit non-nil identity, or nil to use the injected UUID source. * @return A borrowed plan owned by this ledger, or a structured failure. */ - [[nodiscard]] eve::Result create( - const std::string& correlation = {}, const std::string& causation = {}, - eve::TransactionId identity = {}); + [[nodiscard]] eve::Result create(const std::string& correlation = {}, const std::string& causation = {}, + eve::TransactionId identity = {}); /** @brief Returns a retained plan by legacy projection, or nullptr. */ [[nodiscard]] Plan* find(const std::string& transactionId); /** @brief Returns a retained plan by canonical identity, or nullptr. */ @@ -369,22 +369,20 @@ public: * @param hashProvider Explicit content-digest provider. * @return A sealed transaction-ledger snapshot. */ - [[nodiscard]] eve::Result snapshot( - const eve::SnapshotHashProvider& hashProvider) const; + [[nodiscard]] eve::Result snapshot(const eve::SnapshotHashProvider& hashProvider) const; /** * @brief Restores a verified or migrated transaction-ledger snapshot atomically. * @param snapshot Source envelope with schema `transaction:ledger`. * @param hashProvider Explicit content-digest provider. * @return Success, or a failure leaving all ledger state unchanged. */ - [[nodiscard]] eve::Result restoreSnapshot( - const eve::SnapshotEnvelope& snapshot, const eve::SnapshotHashProvider& hashProvider); + [[nodiscard]] eve::Result restoreSnapshot(const eve::SnapshotEnvelope& snapshot, + const eve::SnapshotHashProvider& hashProvider); /** @brief Serializes the common transaction snapshot envelope. */ - [[nodiscard]] eve::Result snapshotEnvelopeJson( - const eve::SnapshotHashProvider& hashProvider) const; + [[nodiscard]] eve::Result snapshotEnvelopeJson(const eve::SnapshotHashProvider& hashProvider) const; /** @brief Parses and transactionally restores a common transaction snapshot envelope. */ - [[nodiscard]] eve::Result restoreSnapshotJson( - std::string_view json, const eve::SnapshotHashProvider& hashProvider); + [[nodiscard]] eve::Result restoreSnapshotJson(std::string_view json, + const eve::SnapshotHashProvider& hashProvider); private: uint64_t nextTransaction_ = 1; @@ -411,7 +409,8 @@ public: * @brief Allocates a module-owned transaction ledger. * @return Borrowed nullable pointer to the module-owned ledger; null means allocation failed. * @ownership The Transaction module owns the ledger and releases it during module teardown. - * @lifetime Valid until explicit ledger release or Transaction module unload; use the ledger handle in asynchronous code. + * @lifetime Valid until explicit ledger release or Transaction module unload; use the ledger handle in asynchronous + * code. * @thread Call on the module's owning thread. * @reentrancy The factory does not invoke external callbacks; do not use the result across module teardown. */ diff --git a/src/modules/ui/Component.cpp b/src/modules/ui/Component.cpp index 240a37eb..fc4a867c 100644 --- a/src/modules/ui/Component.cpp +++ b/src/modules/ui/Component.cpp @@ -20,8 +20,10 @@ void Component::rebuild(bool forceFull) { auto host = UIHost::resolve(host_); if (!host) return; WidgetDesc tree = build(); - if (forceFull) host->get().setTree(std::move(tree)); - else host->get().setTreeReconcile(std::move(tree)); + if (forceFull) + host->get().setTree(std::move(tree)); + else + host->get().setTreeReconcile(std::move(tree)); dirty_ = false; } diff --git a/src/modules/ui/DatabasePanel.cpp b/src/modules/ui/DatabasePanel.cpp index 1198e1f1..6817ff8f 100644 --- a/src/modules/ui/DatabasePanel.cpp +++ b/src/modules/ui/DatabasePanel.cpp @@ -57,7 +57,7 @@ void DatabasePanel::open() { auto host = UIHost::resolve(host_); if (!host) { host_ = UIHost::createHost(kDatabaseHostName); - host = UIHost::resolve(host_); + host = UIHost::resolve(host_); } if (!host) return; host->get().setVisible(true); @@ -105,8 +105,7 @@ bool DatabasePanel::selectClass(const std::string& name) { eve::Result DatabasePanel::createInstance() { if (selectedClass_.empty()) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "database panel has no selected script class")); + eve::DiagnosticCode::InvalidArgument, "database panel has no selected script class")); } auto result = ObjectRegistry::instance().create(selectedClass_); if (!result) return eve::Result::failure(result.status()); @@ -118,8 +117,7 @@ eve::Result DatabasePanel::createInstance() { return eve::Result::success(handle); } -eve::Result DatabasePanel::registerObject(const ssq::Object& object, - const std::string& label) { +eve::Result DatabasePanel::registerObject(const ssq::Object& object, const std::string& label) { auto result = ObjectRegistry::instance().registerObject(selectedClass_, object, label); if (!result) return eve::Result::failure(result.status()); const ObjectHandle handle = result.value(); @@ -166,23 +164,19 @@ void DatabasePanel::rebuildCache() { WidgetDesc DatabasePanel::cellWidget(const ObjectEntry& entry, const ReflectedMember& member, const ReflectedValue& value) { - const std::string id = - "cell_" + std::to_string(entry.handle.packed()) + "_" + member.name; - const std::string label = cellLabel(member.name, entry.handle); + const std::string id = "cell_" + std::to_string(entry.handle.packed()) + "_" + member.name; + const std::string label = cellLabel(member.name, entry.handle); const std::string editor = member.attrString("editor"); const std::vector options = member.attrOptions("options"); WidgetDesc cell; if (value.kind == ReflectedValueKind::Bool) { - cell = checkbox(label, value.asBool(), id, - [this, entryHandle = entry.handle, name = member.name](bool v) { - ReflectedValue out; - out.kind = ReflectedValueKind::Bool; - out.boolean = v; - if (const ObjectEntry* e = - ObjectRegistry::instance().entry(entryHandle)) - ModuleManager::runtime()->writeProperty(e->object, name, - out); - }); + cell = checkbox(label, value.asBool(), id, [this, entryHandle = entry.handle, name = member.name](bool v) { + ReflectedValue out; + out.kind = ReflectedValueKind::Bool; + out.boolean = v; + if (const ObjectEntry* e = ObjectRegistry::instance().entry(entryHandle)) + ModuleManager::runtime()->writeProperty(e->object, name, out); + }); } else if (editor == "combo" && !options.empty()) { int index = 0; const std::string current = @@ -190,12 +184,10 @@ WidgetDesc DatabasePanel::cellWidget(const ObjectEntry& entry, const auto it = std::find(options.begin(), options.end(), current); if (it != options.end()) index = int(it - options.begin()); cell = combo(label, options, index, id, - [this, entryHandle = entry.handle, name = member.name, - kind = value.kind, options](float i) { + [this, entryHandle = entry.handle, name = member.name, kind = value.kind, options](float i) { const int idx = static_cast(i); if (idx < 0 || idx >= int(options.size())) return; - const ObjectEntry* e = - ObjectRegistry::instance().entry(entryHandle); + const ObjectEntry* e = ObjectRegistry::instance().entry(entryHandle); if (!e) return; ReflectedValue out; if (kind == ReflectedValueKind::Integer) { @@ -226,18 +218,16 @@ WidgetDesc DatabasePanel::cellWidget(const ObjectEntry& entry, } cell = text(member.name + " = " + shown, id); } else { - cell = inputText(label, valueText(value), id, - [this, entryHandle = entry.handle, name = member.name, - kind = value.kind](const std::string& text) { - const ObjectEntry* e = - ObjectRegistry::instance().entry(entryHandle); - if (!e) return; - ReflectedValue out; - out.kind = ReflectedValueKind::String; - out.text = text; - ModuleManager::runtime()->writeProperty(e->object, name, - out); - }); + cell = inputText( + label, valueText(value), id, + [this, entryHandle = entry.handle, name = member.name, kind = value.kind](const std::string& text) { + const ObjectEntry* e = ObjectRegistry::instance().entry(entryHandle); + if (!e) return; + ReflectedValue out; + out.kind = ReflectedValueKind::String; + out.text = text; + ModuleManager::runtime()->writeProperty(e->object, name, out); + }); } cell.withSize(kCellWidth, 0.f); return cell; @@ -264,9 +254,7 @@ WidgetDesc DatabasePanel::build() { if (idx >= 0 && idx < int(classNames_.size())) selectClass(classNames_[size_t(idx)]); }), - button("+", "db_add", [this]() { - createInstance().ignore("create UI database row from button"); - }), + button("+", "db_add", [this]() { createInstance().ignore("create UI database row from button"); }), }, "db_toolbar")); children.push_back(separator("db_sep")); @@ -287,14 +275,10 @@ WidgetDesc DatabasePanel::build() { entry.object, members_[c].name); cells.push_back(cellWidget(entry, members_[c], value)); } - cells.push_back( - button("x##db_" + std::to_string(entry.handle.packed()), - "db_del_" + std::to_string(entry.handle.packed()), - [this, entryHandle = entry.handle]() { - unregister(entryHandle).ignore("delete UI database row"); - })); - rows.push_back(row(std::move(cells), - "db_row_" + std::to_string(entry.handle.packed()))); + cells.push_back(button( + "x##db_" + std::to_string(entry.handle.packed()), "db_del_" + std::to_string(entry.handle.packed()), + [this, entryHandle = entry.handle]() { unregister(entryHandle).ignore("delete UI database row"); })); + rows.push_back(row(std::move(cells), "db_row_" + std::to_string(entry.handle.packed()))); } if (rows.empty()) { children.push_back(text("No instances of " + selectedClass_ + @@ -324,9 +308,7 @@ void DatabasePanel::sync() { rt->readProperty(entries_[r].object, members_[c].name); if (sameValue(live, cached_[r][c])) continue; cached_[r][c] = live; - const std::string id = - "cell_" + std::to_string(entries_[r].handle.packed()) + "_" + - members_[c].name; + const std::string id = "cell_" + std::to_string(entries_[r].handle.packed()) + "_" + members_[c].name; const ReflectedMember& member = members_[c]; const std::string editor = member.attrString("editor"); const std::vector options = member.attrOptions("options"); @@ -337,8 +319,7 @@ void DatabasePanel::sync() { live.kind == ReflectedValueKind::String ? live.text : valueText(live); const auto it = std::find(options.begin(), options.end(), current); - host->get().setValueById(id, it == options.end() ? 0.f - : float(it - options.begin())); + host->get().setValueById(id, it == options.end() ? 0.f : float(it - options.begin())); } else if (live.kind == ReflectedValueKind::Array || live.kind == ReflectedValueKind::Table || live.kind == ReflectedValueKind::Instance || diff --git a/src/modules/ui/DatabasePanel.h b/src/modules/ui/DatabasePanel.h index 335f429f..8cd969f4 100644 --- a/src/modules/ui/DatabasePanel.h +++ b/src/modules/ui/DatabasePanel.h @@ -57,8 +57,7 @@ public: * @brief Registers a live script object (auto-derives class when empty). * @return A generation-qualified UI object handle, or a structured failure. */ - [[nodiscard]] eve::Result registerObject( - const ssq::Object& object, const std::string& label = {}); + [[nodiscard]] eve::Result registerObject(const ssq::Object& object, const std::string& label = {}); /** * @brief Removes an entry from the registry and refreshes the grid. * @param handle The generation-qualified handle to remove. @@ -77,7 +76,7 @@ private: WidgetDesc cellWidget(const ObjectEntry& entry, const ReflectedMember& member, const ReflectedValue& value); - UIHostHandle host_{}; + UIHostHandle host_{}; std::vector classNames_; std::string selectedClass_; std::vector entries_; diff --git a/src/modules/ui/EditorHost.cpp b/src/modules/ui/EditorHost.cpp index b6bb0761..60c42390 100644 --- a/src/modules/ui/EditorHost.cpp +++ b/src/modules/ui/EditorHost.cpp @@ -69,10 +69,10 @@ void EditorHost::exposeScriptApi(ssq::VM&) {} #else // full implementation #include "common/Module.h" -#include "platform_event/PlatformEvent.h" #include "filesystem/FileData.h" #include "graphics/Graphics.h" #include "image/ImageData.h" +#include "platform_event/PlatformEvent.h" #include "timer/Timer.h" #include "ui/UI.h" #include "window/Window.h" @@ -525,7 +525,7 @@ struct EditorHost::Impl { eve::window::Window* win = nullptr; eve::graphics::Graphics* gfx = nullptr; eve::ui::UI* ui = nullptr; - eve::platform_event::PlatformEvent* event = nullptr; + eve::platform_event::PlatformEvent* event = nullptr; eve::timer::Timer* timer = nullptr; bool windowOpen = false; bool inFrame = false; diff --git a/src/modules/ui/EditorShell.cpp b/src/modules/ui/EditorShell.cpp index 51d520a4..b99d823c 100644 --- a/src/modules/ui/EditorShell.cpp +++ b/src/modules/ui/EditorShell.cpp @@ -23,14 +23,14 @@ void EditorShell::open(UIHostHandle inspector, UIHostHandle database, UIHostHand inspector_ = inspector; database_ = database; scene_ = scene; - auto host = UIHost::resolve(host_); + auto host = UIHost::resolve(host_); if (!host) { host_ = UIHost::createHost(kShellHostName); - host = UIHost::resolve(host_); + host = UIHost::resolve(host_); } if (!host) return; UIHost& shell = host->get(); - auto meta = shell.meta(); + auto meta = shell.meta(); meta->overlay = true; // no title bar meta->hasPos = true; meta->lockPos = true; diff --git a/src/modules/ui/Inspector.cpp b/src/modules/ui/Inspector.cpp index c5d5bb43..8f61d2ce 100644 --- a/src/modules/ui/Inspector.cpp +++ b/src/modules/ui/Inspector.cpp @@ -77,7 +77,7 @@ void Inspector::open() { auto host = UIHost::resolve(host_); if (!host) { host_ = UIHost::createHost(kInspectorHostName); - host = UIHost::resolve(host_); + host = UIHost::resolve(host_); } if (!host) return; host->get().setVisible(true); diff --git a/src/modules/ui/Inspector.h b/src/modules/ui/Inspector.h index 064c61d3..486c0a5d 100644 --- a/src/modules/ui/Inspector.h +++ b/src/modules/ui/Inspector.h @@ -30,7 +30,7 @@ class UIHost; * → Slider * → Checkbox (bool) * → Combo - * Other controls → InputText + * Other controls → InputText * * Inherited members are grouped under their owning (base) class header, so * parent properties are editable side-by-side (see docs/dev/界面设计.md). @@ -141,7 +141,7 @@ private: std::vector navStack_; std::function pickScene_; int selectedInstance_ = -1; - UIHostHandle host_{}; + UIHostHandle host_{}; std::unique_ptr propertyModel_; }; diff --git a/src/modules/ui/ObjectRegistry.cpp b/src/modules/ui/ObjectRegistry.cpp index 0b479433..de784988 100644 --- a/src/modules/ui/ObjectRegistry.cpp +++ b/src/modules/ui/ObjectRegistry.cpp @@ -48,25 +48,21 @@ eve::Result ObjectRegistry::create(const std::string& className) { try { return registerObject(className, runtime->createInstance(className)); } catch (const std::exception& error) { - return registryFailure( - eve::DiagnosticCode::Failed, - std::string("UI object construction failed: ") + error.what()); - } catch (...) { return registryFailure(eve::DiagnosticCode::Failed, - "UI object construction failed"); + std::string("UI object construction failed: ") + error.what()); + } catch (...) { + return registryFailure(eve::DiagnosticCode::Failed, "UI object construction failed"); } } -eve::Result ObjectRegistry::registerObject(const std::string& className, - const ssq::Object& object, +eve::Result ObjectRegistry::registerObject(const std::string& className, const ssq::Object& object, const std::string& label) { std::uint32_t allocatedIndex = ObjectHandle::invalidIndex; - bool appendedSlot = false; + bool appendedSlot = false; try { if (object.getType() != ssq::Type::INSTANCE) { - return registryFailure( - eve::DiagnosticCode::InvalidArgument, - "UI object registry accepts only script instances"); + return registryFailure(eve::DiagnosticCode::InvalidArgument, + "UI object registry accepts only script instances"); } std::string group = className; @@ -74,14 +70,12 @@ eve::Result ObjectRegistry::registerObject(const std::string& clas Runtime* runtime = ModuleManager::runtime(); if (!runtime) { return registryFailure( - eve::DiagnosticCode::Failed, - "UI object registry requires an active Runtime to derive the class"); + eve::DiagnosticCode::Failed, "UI object registry requires an active Runtime to derive the class"); } group = runtime->classNameOf(object); if (group.empty()) { - return registryFailure( - eve::DiagnosticCode::InvalidArgument, - "UI object registry could not derive a script class name"); + return registryFailure(eve::DiagnosticCode::InvalidArgument, + "UI object registry could not derive a script class name"); } } @@ -89,9 +83,8 @@ eve::Result ObjectRegistry::registerObject(const std::string& clas allocatedIndex = freeSlots_.back(); } else { if (slots_.size() >= ObjectHandle::invalidIndex) { - return registryFailure( - eve::DiagnosticCode::Failed, - "UI object registry exhausted its slot index space"); + return registryFailure(eve::DiagnosticCode::Failed, + "UI object registry exhausted its slot index space"); } allocatedIndex = static_cast(slots_.size()); slots_.emplace_back(); @@ -101,28 +94,24 @@ eve::Result ObjectRegistry::registerObject(const std::string& clas Slot& target = slots_[allocatedIndex]; if (target.retired || target.entry.has_value()) { if (appendedSlot) slots_.pop_back(); - return registryFailure( - eve::DiagnosticCode::InvariantViolation, - "UI object registry selected an occupied or retired slot"); + return registryFailure(eve::DiagnosticCode::InvariantViolation, + "UI object registry selected an occupied or retired slot"); } const ObjectHandle handle(allocatedIndex, target.generation); - auto ownedObject = eve::script::ownSquirrelObject( - eve::script::borrowSquirrelObject(object)); + auto ownedObject = eve::script::ownSquirrelObject(eve::script::borrowSquirrelObject(object)); if (!ownedObject) return eve::Result::failure(ownedObject.status()); ObjectEntry candidate; - candidate.handle = handle; + candidate.handle = handle; candidate.className = group; - candidate.object = std::move(ownedObject).takeValue().release(); - candidate.label = label.empty() ? group + " #" + - std::to_string(count(group) + 1) - : label; + candidate.object = std::move(ownedObject).takeValue().release(); + candidate.label = label.empty() ? group + " #" + std::to_string(count(group) + 1) : label; target.entry.emplace(std::move(candidate)); bool insertedClass = false; try { auto [classIt, inserted] = byClass_.try_emplace(group); - insertedClass = inserted; + insertedClass = inserted; classIt->second.push_back(handle); } catch (...) { if (insertedClass) byClass_.erase(group); @@ -132,24 +121,21 @@ eve::Result ObjectRegistry::registerObject(const std::string& clas // The free-list entry is consumed only after all potentially-throwing // registration work has completed. - if (!freeSlots_.empty() && freeSlots_.back() == allocatedIndex) - freeSlots_.pop_back(); + if (!freeSlots_.empty() && freeSlots_.back() == allocatedIndex) freeSlots_.pop_back(); return eve::Result::success(handle); } catch (const std::exception& error) { if (appendedSlot && !slots_.empty() && allocatedIndex + 1 == slots_.size() && !slots_.back().entry.has_value()) { slots_.pop_back(); } - return registryFailure( - eve::DiagnosticCode::Failed, - std::string("UI object registration failed: ") + error.what()); + return registryFailure(eve::DiagnosticCode::Failed, + std::string("UI object registration failed: ") + error.what()); } catch (...) { if (appendedSlot && !slots_.empty() && allocatedIndex + 1 == slots_.size() && !slots_.back().entry.has_value()) { slots_.pop_back(); } - return registryFailure(eve::DiagnosticCode::Failed, - "UI object registration failed"); + return registryFailure(eve::DiagnosticCode::Failed, "UI object registration failed"); } } @@ -161,21 +147,18 @@ eve::Result ObjectRegistry::unregister(ObjectHandle handle) { Slot* target = slot(handle); if (!target) { - return registryFailure(eve::DiagnosticCode::NotFound, - "UI object handle index is outside the registry"); + return registryFailure(eve::DiagnosticCode::NotFound, "UI object handle index is outside the registry"); } if (!target->entry.has_value() || target->generation != handle.generation()) { - return registryFailure(eve::DiagnosticCode::StaleHandle, - "UI object handle is stale"); + return registryFailure(eve::DiagnosticCode::StaleHandle, "UI object handle is stale"); } auto classIt = byClass_.find(target->entry->className); if (classIt == byClass_.end()) { - return registryFailure(eve::DiagnosticCode::InvariantViolation, - "UI object class index is missing"); + return registryFailure(eve::DiagnosticCode::InvariantViolation, "UI object class index is missing"); } - auto& classEntries = classIt->second; - const auto handleIt = std::find(classEntries.begin(), classEntries.end(), handle); + auto& classEntries = classIt->second; + const auto handleIt = std::find(classEntries.begin(), classEntries.end(), handle); if (handleIt == classEntries.end()) { return registryFailure(eve::DiagnosticCode::InvariantViolation, "UI object class index does not contain its handle"); @@ -188,12 +171,10 @@ eve::Result ObjectRegistry::unregister(ObjectHandle handle) { // entry unreachable. The remaining operations are noexcept. freeSlots_.push_back(handle.index()); } catch (const std::exception& error) { - return registryFailure( - eve::DiagnosticCode::Failed, - std::string("UI object slot release failed: ") + error.what()); - } catch (...) { return registryFailure(eve::DiagnosticCode::Failed, - "UI object slot release failed"); + std::string("UI object slot release failed: ") + error.what()); + } catch (...) { + return registryFailure(eve::DiagnosticCode::Failed, "UI object slot release failed"); } } @@ -250,8 +231,7 @@ std::vector ObjectRegistry::entries(const std::string& className) c const ObjectEntry* ObjectRegistry::entry(ObjectHandle handle) const noexcept { const Slot* target = slot(handle); - if (!target || !target->entry.has_value() || target->generation != handle.generation()) - return nullptr; + if (!target || !target->entry.has_value() || target->generation != handle.generation()) return nullptr; return &*target->entry; } diff --git a/src/modules/ui/ObjectRegistry.h b/src/modules/ui/ObjectRegistry.h index c38126a3..e524310c 100644 --- a/src/modules/ui/ObjectRegistry.h +++ b/src/modules/ui/ObjectRegistry.h @@ -77,8 +77,7 @@ public: * object, unavailable Runtime, or allocation failure. */ [[nodiscard("retain ObjectHandle or explicitly inspect the failure")]] - eve::Result registerObject(const std::string& className, - const ssq::Object& object, + eve::Result registerObject(const std::string& className, const ssq::Object& object, const std::string& label = {}); /** * @brief Removes a live entry and advances its slot generation. @@ -117,8 +116,8 @@ public: private: struct Slot { - std::uint32_t generation = 1; - bool retired = false; + std::uint32_t generation = 1; + bool retired = false; std::optional entry; }; @@ -142,8 +141,8 @@ private: [[nodiscard]] const Slot* slot(ObjectHandle handle) const noexcept; ObjectRegistry() = default; - std::vector slots_; - std::vector freeSlots_; + std::vector slots_; + std::vector freeSlots_; std::map> byClass_; }; diff --git a/src/modules/ui/PropertyView.cpp b/src/modules/ui/PropertyView.cpp index 19ebb4e5..dfd8c905 100644 --- a/src/modules/ui/PropertyView.cpp +++ b/src/modules/ui/PropertyView.cpp @@ -12,10 +12,10 @@ namespace eve::ui { namespace { +using eve::Value; using property_access::PropertyDescriptor; using property_access::PropertyFlag; using property_access::PropertyKind; -using eve::Value; std::string sanitize(std::string value) { for (char &character : value) @@ -65,13 +65,12 @@ AccessibilityRole accessibilityRole(PropertyKind kind, bool readOnly) { } } -WidgetDesc makePropertyField(property_access::IPropertyAccess &model, - const PropertyViewOptions &options, +WidgetDesc makePropertyField(property_access::IPropertyAccess &model, const PropertyViewOptions &options, const PropertyDescriptor &property, const Value &value) { const std::string id = propertyWidgetId(options, property.path); const std::string label = displayName(property); - const bool readOnly = property_access::hasFlag(property.flags, PropertyFlag::ReadOnly) || - property.kind == PropertyKind::ReadOnlyText; + const bool readOnly = + property_access::hasFlag(property.flags, PropertyFlag::ReadOnly) || property.kind == PropertyKind::ReadOnlyText; WidgetDesc result; if (readOnly) { @@ -177,14 +176,10 @@ WidgetDesc makePropertyField(property_access::IPropertyAccess &model, } bool visible(const PropertyDescriptor &property, const PropertyViewOptions &options) { - if (!options.showAdvanced && property_access::hasFlag(property.flags, PropertyFlag::Advanced)) - return false; - if (!options.showEditorOnly && - property_access::hasFlag(property.flags, PropertyFlag::EditorOnly)) - return false; - if (!options.showReadOnly && - (property_access::hasFlag(property.flags, PropertyFlag::ReadOnly) || - property.kind == PropertyKind::ReadOnlyText)) + if (!options.showAdvanced && property_access::hasFlag(property.flags, PropertyFlag::Advanced)) return false; + if (!options.showEditorOnly && property_access::hasFlag(property.flags, PropertyFlag::EditorOnly)) return false; + if (!options.showReadOnly && (property_access::hasFlag(property.flags, PropertyFlag::ReadOnly) || + property.kind == PropertyKind::ReadOnlyText)) return false; return true; } @@ -197,7 +192,7 @@ std::string propertyWidgetId(const PropertyViewOptions &options, const std::stri WidgetDesc buildPropertyField(property_access::IPropertyAccess &model, const std::string &path, const PropertyViewOptions &options) { - auto property = model.schema().find(path); + auto property = model.schema().find(path); const std::optional value = model.read(path); if (!property || !value) return text(path + ": unavailable", propertyWidgetId(options, path)) @@ -207,8 +202,7 @@ WidgetDesc buildPropertyField(property_access::IPropertyAccess &model, const std return makePropertyField(model, options, property->get(), *value); } -WidgetDesc buildPropertyView(property_access::IPropertyAccess &model, - const PropertyViewOptions &options) { +WidgetDesc buildPropertyView(property_access::IPropertyAccess &model, const PropertyViewOptions &options) { std::vector children; if (!options.title.empty()) children.push_back(sectionHeader(options.title, options.idPrefix + "title")); @@ -226,13 +220,12 @@ WidgetDesc buildPropertyView(property_access::IPropertyAccess &model, return column(std::move(children), options.idPrefix + "root"); } -void syncPropertyView(UIHost &host, const property_access::IPropertyAccess &model, - const PropertyViewOptions &options) { +void syncPropertyView(UIHost &host, const property_access::IPropertyAccess &model, const PropertyViewOptions &options) { for (const PropertyDescriptor &property : model.schema().properties) { const std::optional value = model.read(property.path); if (!value) continue; const std::string id = propertyWidgetId(options, property.path); - const bool readOnly = property_access::hasFlag(property.flags, PropertyFlag::ReadOnly) || + const bool readOnly = property_access::hasFlag(property.flags, PropertyFlag::ReadOnly) || property.kind == PropertyKind::ReadOnlyText; if (readOnly) { host.setTextById(id, displayName(property) + ": " + valueText(*value)); @@ -256,8 +249,7 @@ void syncPropertyView(UIHost &host, const property_access::IPropertyAccess &mode } } -PropertyComponent::PropertyComponent(property_access::IPropertyAccess *model, - PropertyViewOptions options) +PropertyComponent::PropertyComponent(property_access::IPropertyAccess *model, PropertyViewOptions options) : model_(model), options_(std::move(options)) { observe(); } diff --git a/src/modules/ui/PropertyView.h b/src/modules/ui/PropertyView.h index f1aeaaf4..e7fd3323 100644 --- a/src/modules/ui/PropertyView.h +++ b/src/modules/ui/PropertyView.h @@ -31,8 +31,7 @@ WidgetDesc buildPropertyField(property_access::IPropertyAccess &model, const std * IPropertyAccess::write(), so gameplay models and command-backed editor models * use the same UI generation path. */ -WidgetDesc buildPropertyView(property_access::IPropertyAccess &model, - const PropertyViewOptions &options = {}); +WidgetDesc buildPropertyView(property_access::IPropertyAccess &model, const PropertyViewOptions &options = {}); /** @brief Pull current model values into an existing compatible UI tree. */ void syncPropertyView(UIHost &host, const property_access::IPropertyAccess &model, @@ -41,8 +40,7 @@ void syncPropertyView(UIHost &host, const property_access::IPropertyAccess &mode /** @brief Component wrapper that becomes dirty when its bound model changes. */ class PropertyComponent final : public Component { public: - explicit PropertyComponent(property_access::IPropertyAccess *model = nullptr, - PropertyViewOptions options = {}); + explicit PropertyComponent(property_access::IPropertyAccess *model = nullptr, PropertyViewOptions options = {}); ~PropertyComponent() override = default; /** @brief Replace the model and reconnect change observation. */ @@ -51,7 +49,8 @@ public: * @brief Return the currently bound property model, or null when unbound. * @return Borrowed nullable model supplied by the caller. * @ownership PropertyComponent does not own the model; the caller must keep it alive. - * @lifetime Valid until bind() replaces it or the model is destroyed; do not retain across frames without external lifetime control. + * @lifetime Valid until bind() replaces it or the model is destroyed; do not retain across frames without external + * lifetime control. * @thread Call on the UI thread. * @reentrancy The accessor invokes no callbacks and is invalid across bind/rebuild mutation. */ @@ -66,7 +65,7 @@ private: property_access::IPropertyAccess *model_ = nullptr; PropertyViewOptions options_; - property_access::Subscription subscription_; + property_access::Subscription subscription_; }; } // namespace eve::ui diff --git a/src/modules/ui/ScenePanel.cpp b/src/modules/ui/ScenePanel.cpp index 02ed033d..65b29e50 100644 --- a/src/modules/ui/ScenePanel.cpp +++ b/src/modules/ui/ScenePanel.cpp @@ -22,7 +22,7 @@ void ScenePanel::open() { auto host = UIHost::resolve(host_); if (!host) { host_ = UIHost::createHost(kSceneHostName); - host = UIHost::resolve(host_); + host = UIHost::resolve(host_); } if (!host) return; host->get().setVisible(true); diff --git a/src/modules/ui/ScenePanel.h b/src/modules/ui/ScenePanel.h index 0f55d90d..bf6699ce 100644 --- a/src/modules/ui/ScenePanel.h +++ b/src/modules/ui/ScenePanel.h @@ -60,7 +60,7 @@ private: const std::vector& all); void rebuildHost(); - UIHostHandle host_{}; + UIHostHandle host_{}; std::string selectedId_; std::function pickHandler_; }; diff --git a/src/modules/ui/UI.cpp b/src/modules/ui/UI.cpp index 5a9cf4b3..abfc65aa 100644 --- a/src/modules/ui/UI.cpp +++ b/src/modules/ui/UI.cpp @@ -10,9 +10,9 @@ #include "ui/UISystem.h" #include "ui/Widget.h" -#include "common/config.h" #include "common/Module.h" #include "common/SquirrelBinding.h" +#include "common/config.h" #include "graphics/Graphics.h" #include "image/Image.h" #include "image/ImageData.h" @@ -248,13 +248,13 @@ void UI::updateHostTweens() { for (auto &t : hostTweens_) { auto host = UIHost::resolve(t.host); if (!host) continue; - auto m = host->get().meta(); + auto m = host->get().meta(); const double elapsed = now - t.startMs; if (t.durationMs <= 0.0 || elapsed >= t.durationMs) { m->hasPos = true; m->posX = t.toX; m->posY = t.toY; - t.host = {}; // done; removed below + t.host = {}; // done; removed below continue; } const float k = float(elapsed / t.durationMs); @@ -264,9 +264,7 @@ void UI::updateHostTweens() { m->posY = t.fromY + (t.toY - t.fromY) * ease; } hostTweens_.erase(std::remove_if(hostTweens_.begin(), hostTweens_.end(), - [](const HostTween &t) { - return !UIHost::resolve(t.host).has_value(); - }), + [](const HostTween &t) { return !UIHost::resolve(t.host).has_value(); }), hostTweens_.end()); } @@ -297,8 +295,7 @@ void UI::fireScriptHandlers(const UIEvent &ev) { void UI::onClick(const std::string &id, ssq::Function fn) { auto host = resolveSelected(); if (!host || id.empty()) return; - scriptHandlers_.push_back( - ScriptHandler(host->get().getName(), id, "click", std::move(fn))); + scriptHandlers_.push_back(ScriptHandler(host->get().getName(), id, "click", std::move(fn))); } void UI::onChange(const std::string &id, ssq::Function fn) { @@ -319,9 +316,7 @@ bool UI::wantCaptureKeyboard() const { UIHostHandle UI::findHost(const std::string &name) const { return UISystem::findHost(name); } -UIHostHandle UI::findHostByOwner(uint32_t ownerId) const { - return UISystem::findHostByOwner(ownerId); -} +UIHostHandle UI::findHostByOwner(uint32_t ownerId) const { return UISystem::findHostByOwner(ownerId); } bool UI::select(const std::string &name) { const UIHostHandle h = findHost(name); @@ -352,10 +347,10 @@ UIHostHandle UI::ensureSelected(const std::string &preferredName) { UIHostHandle UI::mountAs(const std::string &name, WidgetDesc root) { UIHostHandle handle = findHost(name); - auto host = UIHost::resolve(handle); + auto host = UIHost::resolve(handle); if (!host) { handle = UIHost::createHost(name); - host = UIHost::resolve(handle); + host = UIHost::resolve(handle); } else { host->get().setName(name); } @@ -385,13 +380,9 @@ UIHostHandle UI::remountReconcile(WidgetDesc root) { return handle; } -UIHostHandle UI::remountAs(const std::string &name, WidgetDesc root) { - return mountAs(name, std::move(root)); -} +UIHostHandle UI::remountAs(const std::string &name, WidgetDesc root) { return mountAs(name, std::move(root)); } -eve::OptionalRef UI::resolveSelected() const noexcept { - return UIHost::resolve(selected_); -} +eve::OptionalRef UI::resolveSelected() const noexcept { return UIHost::resolve(selected_); } void UI::beginBuild() { openStack_.clear(); @@ -885,16 +876,14 @@ bool UI::setListItems(const std::string &listId, const std::vector auto host = resolveSelected(); if (!host) return false; WidgetDesc listNode = listButtons(listId, items); - auto existing = host->get().findById(listId); + auto existing = host->get().findById(listId); if (existing && existing->get().type == NodeType::Group) { host->get().setTreeReconcile( - window(host->get().getName().empty() ? "List" : host->get().getName(), - {std::move(listNode)}, "root")); + window(host->get().getName().empty() ? "List" : host->get().getName(), {std::move(listNode)}, "root")); return true; } host->get().setTree( - window(host->get().getName().empty() ? "List" : host->get().getName(), - {std::move(listNode)}, "root")); + window(host->get().getName().empty() ? "List" : host->get().getName(), {std::move(listNode)}, "root")); return true; } @@ -1015,10 +1004,10 @@ bool UI::setImageNinePatchFile(const std::string &id, const std::string &path) { auto *asset = loadNinePatch(path); if (!asset) return false; n->get().textureId = asset->textureId; - n->get().borderL = float(asset->info.borderLeft); - n->get().borderT = float(asset->info.borderTop); - n->get().borderR = float(asset->info.borderRight); - n->get().borderB = float(asset->info.borderBottom); + n->get().borderL = float(asset->info.borderLeft); + n->get().borderT = float(asset->info.borderTop); + n->get().borderR = float(asset->info.borderRight); + n->get().borderB = float(asset->info.borderBottom); if (n->get().type == NodeType::NinePatchPanel) { n->get().paddingL = float(asset->info.paddingLeft); n->get().paddingT = float(asset->info.paddingTop); @@ -1105,8 +1094,7 @@ void UI::setHostOverlay(bool overlay) { } void UI::setHostOverlayAlpha(float alpha) { - if (auto host = resolveSelected()) - host->get().meta()->overlayBgAlpha = std::max(0.f, std::min(1.f, alpha)); + if (auto host = resolveSelected()) host->get().meta()->overlayBgAlpha = std::max(0.f, std::min(1.f, alpha)); } void UI::setHostMovable(bool movable) { @@ -1120,7 +1108,7 @@ void UI::setHostResizable(bool resizable) { void UI::setHostPos(float x, float y, float pivotX, float pivotY) { auto host = resolveSelected(); if (!host) return; - auto m = host->get().meta(); + auto m = host->get().meta(); m->hasPos = true; m->posX = x; m->posY = y; @@ -1131,7 +1119,7 @@ void UI::setHostPos(float x, float y, float pivotX, float pivotY) { void UI::setHostAnchor(float x, float y) { auto host = resolveSelected(); if (!host) return; - auto m = host->get().meta(); + auto m = host->get().meta(); m->anchorX = x; m->anchorY = y; } @@ -1139,7 +1127,7 @@ void UI::setHostAnchor(float x, float y) { void UI::setHostSize(float w, float h) { auto host = resolveSelected(); if (!host) return; - auto m = host->get().meta(); + auto m = host->get().meta(); m->hasSize = true; m->sizeX = w; m->sizeY = h; @@ -1148,7 +1136,7 @@ void UI::setHostSize(float w, float h) { void UI::setHostPercent(float w, float h) { auto host = resolveSelected(); if (!host) return; - auto m = host->get().meta(); + auto m = host->get().meta(); m->percentW = w; m->percentH = h; } @@ -1156,7 +1144,7 @@ void UI::setHostPercent(float w, float h) { void UI::animateHostPos(float x, float y, float durationMs) { auto host = resolveSelected(); if (!host) return; - auto m = host->get().meta(); + auto m = host->get().meta(); HostTween t; t.host = selected_; t.fromX = m->hasPos ? m->posX : 0.f; @@ -1215,7 +1203,7 @@ std::string UI::getStats() const { std::string UI::saveTreeJson() const { auto host = resolveSelected(); if (!host) return "{}"; - auto t = host->get().tree(); + auto t = host->get().tree(); Poco::JSON::Object root; root.set("host", host->get().getName()); if (t->root >= 0) nodeToJson(*t, t->nodes[size_t(t->root)], root); @@ -1249,8 +1237,7 @@ graphics::Canvas *UI::viewportCanvas(const std::string &id) { auto host = resolveSelected(); if (!host || id.empty()) return nullptr; const std::string key = host->get().getName() + "/" + id; - if (auto state = UISystem::viewportState(host->get().getName(), id)) - return state->get().canvas; + if (auto state = UISystem::viewportState(host->get().getName(), id)) return state->get().canvas; auto ensured = UISystem::ensureViewport(key, 320, 240); if (!ensured.ok()) return nullptr; return std::move(ensured).takeValue().get().canvas; @@ -1752,10 +1739,10 @@ uint64_t UI::dbCreateInstance() { eve::Result UI::dbUnregister(uint64_t id) { if (!databasePanel_) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::NotFound, "database panel is not open", {}, {}, "ui")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "database panel is not open", {}, {}, "ui")); const ObjectHandle handle = ObjectHandle::fromPacked(id); - auto result = databasePanel_->unregister(handle); + auto result = databasePanel_->unregister(handle); if (!result.ok()) return eve::Result::failure(result.status()); return eve::Result::success(result.status()); } @@ -2007,12 +1994,11 @@ void UI::expose(ssq::Class &cls) { cls.addFunc("dbRegister", &UI::dbRegister); cls.addFunc("dbCreateInstance", &UI::dbCreateInstance); const HSQUIRRELVM vm = cls.getHandle(); - cls.addFunc("dbUnregister", [vm](UI* value, uint64_t id) { + cls.addFunc("dbUnregister", [vm](UI *value, uint64_t id) { if (!value) return eve::script::projectResult( - vm, eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, - "UI instance must not be null", {}, {}, "ui"))); + vm, eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "UI instance must not be null", {}, {}, "ui"))); return eve::script::projectResult(vm, value->dbUnregister(id)); }); diff --git a/src/modules/ui/UI.h b/src/modules/ui/UI.h index 8733cd40..2e64364c 100644 --- a/src/modules/ui/UI.h +++ b/src/modules/ui/UI.h @@ -396,7 +396,8 @@ public: * @brief Returns the offscreen render target of a selected-host Viewport widget. * @return Borrowed nullable Canvas owned by Graphics; null precedes layout or when the id is absent. * @ownership Graphics owns the canvas; UI only registers and presents it. - * @lifetime Valid until viewport recreation, graphics reset, or the next mutation that replaces the target; copy no pointer across frames. + * @lifetime Valid until viewport recreation, graphics reset, or the next mutation that replaces the target; copy no + * pointer across frames. * @thread Call on the render/UI thread. * @reentrancy The query invokes no callbacks; do not destroy or replace the target re-entrantly. */ @@ -532,7 +533,7 @@ private: std::unique_ptr backend_; std::unordered_map ninePatches_; - UIHostHandle selected_{}; + UIHostHandle selected_{}; std::vector openStack_; WidgetDesc builtRoot_; diff --git a/src/modules/ui/UIAutomationCapabilities.cpp b/src/modules/ui/UIAutomationCapabilities.cpp index 71358d78..4e1c0a24 100644 --- a/src/modules/ui/UIAutomationCapabilities.cpp +++ b/src/modules/ui/UIAutomationCapabilities.cpp @@ -268,15 +268,14 @@ public: if (matches.size() != 1) return matchError(hostName, widgetId, matches.size()); auto host = UIHost::resolve(matches[0].host); if (!host) return "error: UI host became stale"; - UIHost& resolvedHost = host->get(); - const auto tree = resolvedHost.tree(); + UIHost& resolvedHost = host->get(); + const auto tree = resolvedHost.tree(); if (matches[0].nodeIndex < 0 || matches[0].nodeIndex >= static_cast(tree->nodes.size())) return "error: UI widget became stale"; UINode& node = tree->nodes[static_cast(matches[0].nodeIndex)]; if (!resolvedHost.meta()->visible || !node.visible) return "error: widget is not visible: " + resolvedHost.getName() + "/" + widgetId; - if (!isClickable(node)) - return "error: widget is not clickable: " + resolvedHost.getName() + "/" + widgetId; + if (!isClickable(node)) return "error: widget is not clickable: " + resolvedHost.getName() + "/" + widgetId; UIEvent event; event.host = matches[0].host; diff --git a/src/modules/ui/UIBackend.h b/src/modules/ui/UIBackend.h index 24544049..7681c457 100644 --- a/src/modules/ui/UIBackend.h +++ b/src/modules/ui/UIBackend.h @@ -39,7 +39,9 @@ public: * alive while registered. */ [[nodiscard("retain the UI texture registration id or explicitly handle failure")]] - virtual uint64_t registerTexture(graphics::Texture * /*tex*/) { return 0; } + virtual uint64_t registerTexture(graphics::Texture * /*tex*/) { + return 0; + } virtual void unregisterTexture(uint64_t /*id*/) {} /** Texture pixel size for a registered id (used by nine-patch UV math). */ virtual bool textureSize(uint64_t /*id*/, int * /*w*/, int * /*h*/) const { return false; } diff --git a/src/modules/ui/UIHost.cpp b/src/modules/ui/UIHost.cpp index a9283ed4..4f932a9e 100644 --- a/src/modules/ui/UIHost.cpp +++ b/src/modules/ui/UIHost.cpp @@ -48,7 +48,7 @@ UIHostHandle UIHost::createHost(const std::string &name) { UIHost *h = UIHost::create(); if (!h) return {}; const UIHostHandle handle = ecs::handle_of(h); - h->meta()->entity = handle; + h->meta()->entity = handle; if (name.empty()) { h->meta()->name = "host" + std::to_string(++g_anonHostSeq); } else { @@ -59,8 +59,7 @@ UIHostHandle UIHost::createHost(const std::string &name) { eve::OptionalRef UIHost::resolve(UIHostHandle handle) noexcept { auto *entity = ecs::try_get(handle); - if (auto *host = entity ? dynamic_cast(entity) : nullptr) - return std::ref(*host); + if (auto *host = entity ? dynamic_cast(entity) : nullptr) return std::ref(*host); return std::nullopt; } @@ -114,7 +113,7 @@ void UIHost::setEnabledById(const std::string &id, bool enabled) { n->get().enabled = enabled; if (!enabled) { n->get().focusRequested = false; - n->get().focused = false; + n->get().focused = false; } } } @@ -156,8 +155,7 @@ bool UIHost::moveFocus(FocusDirection direction, bool wrap) { const std::string &explicitId = neighbor(*current, direction); if (!explicitId.empty()) { auto explicitTarget = findById(explicitId); - if (explicitTarget && eligible(explicitTarget->get(), true)) - return requestFocusById(explicitId); + if (explicitTarget && eligible(explicitTarget->get(), true)) return requestFocusById(explicitId); } } diff --git a/src/modules/ui/UISystem.cpp b/src/modules/ui/UISystem.cpp index c193b06b..f29fee30 100644 --- a/src/modules/ui/UISystem.cpp +++ b/src/modules/ui/UISystem.cpp @@ -40,7 +40,7 @@ void pushPending(UIHost *host, const UINode &n, const char *kind, uint32_t handl if (!n.enabled || n.mouseFilter == MouseFilter::Ignore) return; UIEvent ev; if (!host) return; - ev.host = host->handle(); + ev.host = host->handle(); ev.hostName = host ? host->meta()->name : ""; ev.nodeId = n.id; if (host) { @@ -1059,7 +1059,7 @@ void walkNode(UIHost *host, UIHost::Tree *tree, int index) { const float h = n.sizeY > 0.f ? n.sizeY : ImGui::GetContentRegionAvail().y; const ImVec2 size(std::max(1.f, w), std::max(1.f, h)); const std::string key = viewportKey(host, n); - auto viewport = UISystem::ensureViewport(key, int(size.x), int(size.y)); + auto viewport = UISystem::ensureViewport(key, int(size.x), int(size.y)); const ImVec2 rectMin = ImGui::GetCursorScreenPos(); const std::string label = "##vp" + (n.id.empty() ? std::string("viewport") : n.id); @@ -1069,10 +1069,10 @@ void walkNode(UIHost *host, UIHost::Tree *tree, int index) { const ImVec2 mouse = ImGui::GetMousePos(); if (viewport.ok()) { ViewportState &vs = viewport.value().get(); - vs.hovered = hovered; - vs.active = active; - vs.mouseX = mouse.x - rectMin.x; - vs.mouseY = mouse.y - rectMin.y; + vs.hovered = hovered; + vs.active = active; + vs.mouseX = mouse.x - rectMin.x; + vs.mouseY = mouse.y - rectMin.y; if (active) { vs.dragDX = ImGui::GetIO().MouseDelta.x; vs.dragDY = ImGui::GetIO().MouseDelta.y; @@ -1083,17 +1083,16 @@ void walkNode(UIHost *host, UIHost::Tree *tree, int index) { vs.wheel = hovered ? ImGui::GetIO().MouseWheel : 0.f; if (vs.textureId && g_backend && g_backend->usesQueuedTextureDraws()) { - g_backend->queueTextureDraw(vs.textureId, rectMin.x, rectMin.y, size.x, size.y, - 0.f, 0.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, true); + g_backend->queueTextureDraw(vs.textureId, rectMin.x, rectMin.y, size.x, size.y, 0.f, 0.f, 1.f, 1.f, 1.f, + 1.f, 1.f, 1.f, true); } else if (vs.textureId && g_backend) { void *handle = g_backend->textureHandle(vs.textureId); if (handle) { ImGui::SetCursorScreenPos(rectMin); ImGui::Image(static_cast(handle), size); } else { - ImGui::GetWindowDrawList()->AddRectFilled( - rectMin, ImVec2(rectMin.x + size.x, rectMin.y + size.y), - IM_COL32(10, 12, 16, 255)); + ImGui::GetWindowDrawList()->AddRectFilled(rectMin, ImVec2(rectMin.x + size.x, rectMin.y + size.y), + IM_COL32(10, 12, 16, 255)); } } else { ImGui::GetWindowDrawList()->AddRectFilled( @@ -1144,43 +1143,39 @@ std::vector &UISystem::pendingEvents() { return g_pending; } std::vector &UISystem::clickQueue() { return g_clicks; } std::vector &UISystem::changeQueue() { return g_changes; } -void UISystem::setBackend(UIBackend& backend) { g_backend = &backend; } -void UISystem::clearBackend() noexcept { g_backend = nullptr; } +void UISystem::setBackend(UIBackend &backend) { g_backend = &backend; } +void UISystem::clearBackend() noexcept { g_backend = nullptr; } std::optional> UISystem::backend() { if (!g_backend) return std::nullopt; return std::ref(*g_backend); } const UIStats &UISystem::stats() { return g_stats; } -eve::Result> UISystem::ensureViewport( - const std::string &key, int w, int h) { +eve::Result> UISystem::ensureViewport(const std::string &key, int w, int h) { using Return = eve::Result>; if (key.empty() || w <= 0 || h <= 0) { return Return::failure(eve::Status::failure( eve::StatusCode::Rejected, - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, - "viewport key and dimensions must be valid"))); + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "viewport key and dimensions must be valid"))); } auto *gfx = eve::ModuleManager::getInstance("Graphics"); if (!gfx) { return Return::failure(eve::Status::failure( eve::StatusCode::Unsupported, - eve::Diagnostic::error(eve::DiagnosticCode::Unsupported, - "viewport requires the Graphics module"))); + eve::Diagnostic::error(eve::DiagnosticCode::Unsupported, "viewport requires the Graphics module"))); } ViewportState &vs = g_viewports[key]; - vs.key = key; + vs.key = key; if (!vs.canvas || vs.width != w || vs.height != h) { graphics::Canvas *canvas = gfx->newCanvas(w, h); if (!canvas) { return Return::failure(eve::Status::failure( eve::StatusCode::Failed, - eve::Diagnostic::error(eve::DiagnosticCode::Failed, - "Graphics could not create the viewport canvas"))); + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "Graphics could not create the viewport canvas"))); } if (vs.textureId && g_backend) g_backend->unregisterTexture(vs.textureId); vs.textureId = 0; - vs.canvas = canvas; + vs.canvas = canvas; vs.width = w; vs.height = h; if (vs.canvas && g_backend) @@ -1189,8 +1184,8 @@ eve::Result> UISystem::ensureViewport( return Return::success(std::ref(vs)); } -std::optional> UISystem::viewportState( - const std::string &hostName, const std::string &nodeId) { +std::optional> UISystem::viewportState(const std::string &hostName, + const std::string &nodeId) { auto it = g_viewports.find(hostName + "/" + nodeId); if (it == g_viewports.end()) return std::nullopt; return std::ref(it->second); @@ -1288,14 +1283,12 @@ void UISystem::dispatchEvents() { } if (ev.kind == "click") { - auto tree = host->tree(); + auto tree = host->tree(); int index = ev.nodeIndex; if (index < 0 || index >= int(tree->nodes.size()) || tree->nodes[size_t(index)].id != ev.nodeId) { auto target = host->findById(ev.nodeId); - index = target && !tree->nodes.empty() - ? int(&target->get() - tree->nodes.data()) - : -1; + index = target && !tree->nodes.empty() ? int(&target->get() - tree->nodes.data()) : -1; } bool first = true; @@ -1317,21 +1310,21 @@ void UISystem::dispatchEvents() { } if (ev.kind == "toggle" && ev.handlerIndex != 0) { - auto t = host->tree(); + auto t = host->tree(); size_t idx = size_t(ev.handlerIndex - 1); if (idx < t->toggleHandlers.size() && t->toggleHandlers[idx]) t->toggleHandlers[idx](ev.toggleValue); continue; } if (ev.kind == "value" && ev.handlerIndex != 0) { - auto t = host->tree(); + auto t = host->tree(); size_t idx = size_t(ev.handlerIndex - 1); if (idx < t->valueHandlers.size() && t->valueHandlers[idx]) t->valueHandlers[idx](ev.floatValue); continue; } if (ev.kind == "text" && ev.handlerIndex != 0) { - auto t = host->tree(); + auto t = host->tree(); size_t idx = size_t(ev.handlerIndex - 1); if (idx < t->textHandlers.size() && t->textHandlers[idx]) t->textHandlers[idx](ev.textValue); diff --git a/src/modules/ui/UISystem.h b/src/modules/ui/UISystem.h index f821664e..8dc2839f 100644 --- a/src/modules/ui/UISystem.h +++ b/src/modules/ui/UISystem.h @@ -74,7 +74,7 @@ struct ViewportState { class UISystem { public: /** @brief Sets the borrowed backend used for the current UI/render lifetime. */ - static void setBackend(UIBackend& backend); + static void setBackend(UIBackend &backend); /** @brief Clears the backend association before the backend is shut down. */ static void clearBackend() noexcept; /** @@ -93,8 +93,8 @@ public: * @lifetime Valid until viewport removal, refresh, or render-state reset; do not retain it. * @thread Call on the UI/render thread. */ - [[nodiscard]] static eve::OptionalRef viewportState( - const std::string &hostName, const std::string &nodeId); + [[nodiscard]] static eve::OptionalRef viewportState(const std::string &hostName, + const std::string &nodeId); /** * @brief Creates or refreshes offscreen state for a viewport key. * @return A borrowed state reference, or a structured failure for invalid dimensions/key. @@ -102,8 +102,7 @@ public: * @lifetime Valid until viewport removal, refresh, or render-state reset; do not retain it. * @thread Call on the UI/render thread. */ - [[nodiscard]] static eve::ResultRef ensureViewport( - const std::string &key, int w, int h); + [[nodiscard]] static eve::ResultRef ensureViewport(const std::string &key, int w, int h); /** @brief Walk all UIHost (+ subclasses) via ECS View. */ static void render(); diff --git a/src/modules/vehicle/ContainerAdapters.cpp b/src/modules/vehicle/ContainerAdapters.cpp index d1c15b79..109cb902 100644 --- a/src/modules/vehicle/ContainerAdapters.cpp +++ b/src/modules/vehicle/ContainerAdapters.cpp @@ -2,8 +2,8 @@ #include "game_event/GameEvent.h" -#include #include +#include #include #include #include @@ -21,8 +21,7 @@ constexpr std::string_view kOccupantPrefix = "vehicle:occupant:"; } [[nodiscard]] const VehicleEntity::Seats* seatsOf(const VehicleEntity* value) noexcept { - return value == nullptr ? nullptr - : &*const_cast(value)->seats(); + return value == nullptr ? nullptr : &*const_cast(value)->seats(); } [[nodiscard]] std::string quote(std::string_view value) { @@ -31,20 +30,20 @@ constexpr std::string_view kOccupantPrefix = "vehicle:occupant:"; result.push_back('"'); for (const unsigned char byte : value) { switch (byte) { - case '"': result += "\\\""; break; - case '\\': result += "\\\\"; break; - case '\n': result += "\\n"; break; - case '\r': result += "\\r"; break; - case '\t': result += "\\t"; break; - default: - if (byte < 0x20u) { - result += "\\u00"; - constexpr char digits[] = "0123456789abcdef"; - result.push_back(digits[(byte >> 4u) & 0x0fu]); - result.push_back(digits[byte & 0x0fu]); - } else { - result.push_back(static_cast(byte)); - } + case '"': result += "\\\""; break; + case '\\': result += "\\\\"; break; + case '\n': result += "\\n"; break; + case '\r': result += "\\r"; break; + case '\t': result += "\\t"; break; + default: + if (byte < 0x20u) { + result += "\\u00"; + constexpr char digits[] = "0123456789abcdef"; + result.push_back(digits[(byte >> 4u) & 0x0fu]); + result.push_back(digits[byte & 0x0fu]); + } else { + result.push_back(static_cast(byte)); + } } } result.push_back('"'); @@ -56,73 +55,65 @@ constexpr std::string_view kOccupantPrefix = "vehicle:occupant:"; if (object.id.value().size() <= kOccupantPrefix.size() || object.id.value().compare(0, kOccupantPrefix.size(), kOccupantPrefix) != 0) return 0; - const auto text = object.id.value().substr(kOccupantPrefix.size()); - int value = 0; + const auto text = object.id.value().substr(kOccupantPrefix.size()); + int value = 0; const auto parsed = std::from_chars(text.data(), text.data() + text.size(), value); - if (parsed.ec != std::errc{} || parsed.ptr != text.data() + text.size() || value <= 0) - return 0; + if (parsed.ec != std::errc{} || parsed.ptr != text.data() + text.size() || value <= 0) return 0; return value; } [[nodiscard]] int payloadOccupant(const eve::container::ContainerObject& object) noexcept { const auto* payload = dynamic_cast(object.payload.get()); if (payload == nullptr) return parseOccupant(object); - const auto expectedId = eve::container::MembershipId( - std::string(kOccupantPrefix) + std::to_string(payload->occupantId)); - if (payload->occupantId <= 0 || object.id != expectedId) - return 0; + const auto expectedId = + eve::container::MembershipId(std::string(kOccupantPrefix) + std::to_string(payload->occupantId)); + if (payload->occupantId <= 0 || object.id != expectedId) return 0; return parseOccupant(object) == payload->occupantId ? payload->occupantId : 0; } [[nodiscard]] bool sameLayout(const eve::container::ContainerSnapshot& lhs, const eve::container::ContainerSnapshot& rhs) { - if (lhs.id != rhs.id || lhs.revision != rhs.revision || - lhs.entries.size() != rhs.entries.size()) - return false; + if (lhs.id != rhs.id || lhs.revision != rhs.revision || lhs.entries.size() != rhs.entries.size()) return false; for (const auto& left : lhs.entries) { - const auto right = std::find_if( - rhs.entries.begin(), rhs.entries.end(), [&](const auto& entry) { - return entry.membership.object == left.membership.object; - }); + const auto right = std::find_if(rhs.entries.begin(), rhs.entries.end(), [&](const auto& entry) { + return entry.membership.object == left.membership.object; + }); if (right == rhs.entries.end() || left.membership.slot != right->membership.slot || - left.membership.generation != right->membership.generation || - left.object.id != right->object.id || left.object.type != right->object.type || - left.object.quantity != right->object.quantity) + left.membership.generation != right->membership.generation || left.object.id != right->object.id || + left.object.type != right->object.type || left.object.quantity != right->object.quantity) return false; } return true; } -[[nodiscard]] game_event::GameEvent makeEvent( - const eve::container::ContainerId& container, const eve::container::MembershipId& object, - eve::container::ContainerEventKind kind, eve::container::SlotIndex slot, - eve::Revision revision, std::uint64_t serial, eve::SimulationTick tick, - std::string_view reason = {}) { +[[nodiscard]] game_event::GameEvent makeEvent(const eve::container::ContainerId& container, + const eve::container::MembershipId& object, + eve::container::ContainerEventKind kind, eve::container::SlotIndex slot, + eve::Revision revision, std::uint64_t serial, eve::SimulationTick tick, + std::string_view reason = {}) { const auto schema = eve::LogicalId::parse(eve::container::containerEventSchemaName); if (!schema) std::terminate(); game_event::GameEvent event; - event.eventId = eve::container::deterministicContainerEventId(container, object, kind, serial); - event.type = eve::container::containerEventTypeName(kind); - event.source = container.value(); - event.subject = object.value(); - event.schemaId = *schema; + event.eventId = eve::container::deterministicContainerEventId(container, object, kind, serial); + event.type = eve::container::containerEventTypeName(kind); + event.source = container.value(); + event.subject = object.value(); + event.schemaId = *schema; event.schemaVersion = eve::SchemaVersion(1); - event.tick = tick; - event.payload = "{\"containerId\":" + quote(container.value()) + - ",\"objectId\":" + quote(object.value()) + - ",\"slot\":" + std::to_string(slot.isValid() ? slot.value() : -1) + - ",\"revision\":\"" + std::to_string(revision.value()) + "\""; + event.tick = tick; + event.payload = "{\"containerId\":" + quote(container.value()) + ",\"objectId\":" + quote(object.value()) + + ",\"slot\":" + std::to_string(slot.isValid() ? slot.value() : -1) + ",\"revision\":\"" + + std::to_string(revision.value()) + "\""; if (!reason.empty()) event.payload += ",\"reason\":" + quote(reason); event.payload += '}'; return event; } -void notify(const eve::container::GameEventSink& sink, - const eve::container::ContainerId& container, +void notify(const eve::container::GameEventSink& sink, const eve::container::ContainerId& container, const eve::container::MembershipId& object, eve::container::ContainerEventKind kind, - eve::container::SlotIndex slot, eve::Revision revision, std::uint64_t serial, - eve::SimulationTick tick, std::string_view reason, bool& callbackFailed) noexcept { + eve::container::SlotIndex slot, eve::Revision revision, std::uint64_t serial, eve::SimulationTick tick, + std::string_view reason, bool& callbackFailed) noexcept { if (!sink) return; try { const auto event = makeEvent(container, object, kind, slot, revision, serial, tick, reason); @@ -137,8 +128,7 @@ void notify(const eve::container::GameEventSink& sink, try { std::vector diagnostics; diagnostics.emplace_back(eve::Diagnostic::warning( - eve::DiagnosticCode::CallbackFailure, - "container membership committed but an event observer threw")); + eve::DiagnosticCode::CallbackFailure, "container membership committed but an event observer threw")); return eve::Status(eve::StatusCode::Applied, std::move(diagnostics)); } catch (...) { return eve::Status::success(eve::StatusCode::Applied); @@ -147,26 +137,23 @@ void notify(const eve::container::GameEventSink& sink, } // namespace -class VehicleSeatContainerAdapter::PreparedState final - : public eve::container::IContainer::PreparedState { +class VehicleSeatContainerAdapter::PreparedState final : public eve::container::IContainer::PreparedState { public: - PreparedState(VehicleSeatContainerAdapter& owner, std::vector> staged, - eve::Revision revision) + PreparedState(VehicleSeatContainerAdapter& owner, std::vector> staged, eve::Revision revision) : owner_(owner), staged_(std::move(staged)), revision_(revision) {} void commit() noexcept override { if (committed_) return; auto* vehicle = owner_.vehicle(); - auto* seats = seatsOf(vehicle); - if (seats == nullptr || seats->list.size() != staged_.size()) - std::terminate(); + auto* seats = seatsOf(vehicle); + if (seats == nullptr || seats->list.size() != staged_.size()) std::terminate(); for (std::size_t index = 0; index < staged_.size(); ++index) { - auto& seat = seats->list[index]; + auto& seat = seats->list[index]; seat.occupied = staged_[index].has_value(); seat.occupant = staged_[index].value_or(0); } seats->revision = revision_; - committed_ = true; + committed_ = true; } void rollback() noexcept override { @@ -174,19 +161,17 @@ public: } private: - VehicleSeatContainerAdapter& owner_; + VehicleSeatContainerAdapter& owner_; std::vector> staged_; - eve::Revision revision_; - bool committed_ = false; + eve::Revision revision_; + bool committed_ = false; }; -VehicleSeatContainerAdapter::VehicleSeatContainerAdapter( - eve::container::ContainerId id, VehicleEntity* vehicle, - eve::container::AcceptedCondition accepted) +VehicleSeatContainerAdapter::VehicleSeatContainerAdapter(eve::container::ContainerId id, VehicleEntity* vehicle, + eve::container::AcceptedCondition accepted) : descriptor_{std::move(id), - vehicle != nullptr - ? eve::container::Capacity::fixed(seatsOf(vehicle)->list.size()) - : eve::container::Capacity::fixed(0), + vehicle != nullptr ? eve::container::Capacity::fixed(seatsOf(vehicle)->list.size()) + : eve::container::Capacity::fixed(0), eve::container::Ordering::ExplicitSlots, std::move(accepted)}, vehicle_(vehicle != nullptr ? ecs::handle_of(vehicle) : ecs::EntityHandle{}) {} @@ -206,20 +191,18 @@ std::uint64_t VehicleSeatContainerAdapter::nextEventSerial() noexcept { } eve::container::MembershipId VehicleSeatContainerAdapter::occupantId(int occupantId) { - return eve::container::MembershipId( - std::string(kOccupantPrefix) + std::to_string(occupantId)); + return eve::container::MembershipId(std::string(kOccupantPrefix) + std::to_string(occupantId)); } -eve::container::ContainerObject VehicleSeatContainerAdapter::describe(int occupantId, - std::string driver) { +eve::container::ContainerObject VehicleSeatContainerAdapter::describe(int occupantId, std::string driver) { eve::container::ContainerObject object; - object.id = VehicleSeatContainerAdapter::occupantId(occupantId); - object.type = "vehicle.occupant"; - object.quantity = 1; - auto payload = std::make_shared(); + object.id = VehicleSeatContainerAdapter::occupantId(occupantId); + object.type = "vehicle.occupant"; + object.quantity = 1; + auto payload = std::make_shared(); payload->occupantId = occupantId; - payload->driver = std::move(driver); - object.payload = std::move(payload); + payload->driver = std::move(driver); + object.payload = std::move(payload); return object; } @@ -228,47 +211,41 @@ eve::Result VehicleSeatContainerAdapter::snap const auto* seats = seatsOf(value); if (!descriptor_.id.isValid() || seats == nullptr) return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, - "vehicle seat adapter is stale or unbound")); - eve::container::ContainerSnapshot result{descriptor_.id, seats->revision, {}}; + eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, "vehicle seat adapter is stale or unbound")); + eve::container::ContainerSnapshot result{descriptor_.id, seats->revision, {}}; std::unordered_set identities; result.entries.reserve(seats->list.size()); for (std::size_t index = 0; index < seats->list.size(); ++index) { const auto& seat = seats->list[index]; if (!seat.occupied) continue; - if (seat.occupant <= 0 || - index > static_cast(std::numeric_limits::max())) - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, - "vehicle seat contains an invalid occupant")); + if (seat.occupant <= 0 || index > static_cast(std::numeric_limits::max())) + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvariantViolation, "vehicle seat contains an invalid occupant")); const auto id = occupantId(seat.occupant); if (!identities.insert(id).second) return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "vehicle occupant appears in multiple seats")); - result.entries.push_back({{id, eve::container::SlotIndex(static_cast(index)), - eve::Generation(1)}, + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "vehicle occupant appears in multiple seats")); + result.entries.push_back({{id, eve::container::SlotIndex(static_cast(index)), eve::Generation(1)}, describe(seat.occupant, seat.driver)}); } return eve::Result::success(std::move(result)); } eve::Result VehicleSeatContainerAdapter::validateInsert( - const eve::container::ContainerObject& object, - std::optional destination, + const eve::container::ContainerObject& object, std::optional destination, std::optional ignoredObject) const { const auto* value = vehicle(); const auto* seats = seatsOf(value); if (!descriptor_.id.isValid() || seats == nullptr) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::StaleHandle, "vehicle seat adapter is stale or unbound")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, "vehicle seat adapter is stale or unbound")); if (!object.id.isValid() || object.type != "vehicle.occupant" || object.quantity != 1u) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "vehicle seat object facts are invalid")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "vehicle seat object facts are invalid")); const int occupant = payloadOccupant(object); if (occupant <= 0) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::StaleHandle, "vehicle occupant identity is stale")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, "vehicle occupant identity is stale")); const auto occupiedBy = [&](std::size_t index) { const auto& seat = seats->list[index]; @@ -276,79 +253,68 @@ eve::Result VehicleSeatContainerAdapter::validateInsert( }; for (std::size_t index = 0; index < seats->list.size(); ++index) { if (occupiedBy(index) && (!ignoredObject || *ignoredObject != object.id)) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, "vehicle occupant is already seated")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "vehicle occupant is already seated")); } if (destination) { - if (!destination->isValid() || - static_cast(destination->value()) >= seats->list.size()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "vehicle seat slot is out of range")); + if (!destination->isValid() || static_cast(destination->value()) >= seats->list.size()) + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "vehicle seat slot is out of range")); const auto& seat = seats->list[static_cast(destination->value())]; - if (seat.occupied && - (!ignoredObject || occupantId(seat.occupant) != *ignoredObject)) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, "vehicle seat slot is occupied")); + if (seat.occupied && (!ignoredObject || occupantId(seat.occupant) != *ignoredObject)) + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "vehicle seat slot is occupied")); } auto accepted = descriptor_.filter.evaluate(object); if (!accepted) return accepted; std::size_t count = 0; for (const auto& seat : seats->list) - if (seat.occupied && (!ignoredObject || occupantId(seat.occupant) != *ignoredObject)) - ++count; + if (seat.occupied && (!ignoredObject || occupantId(seat.occupant) != *ignoredObject)) ++count; if (count >= seats->list.size()) return eve::Result::failure( eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "vehicle has no free seat")); return eve::Result::success(); } -eve::Result> -VehicleSeatContainerAdapter::prepare(const eve::container::ContainerSnapshot& expected, - const eve::container::ContainerSnapshot& candidate) { +eve::Result> VehicleSeatContainerAdapter::prepare( + const eve::container::ContainerSnapshot& expected, const eve::container::ContainerSnapshot& candidate) { const auto* value = vehicle(); const auto* seats = seatsOf(value); if (!descriptor_.id.isValid() || seats == nullptr) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, - "vehicle seat adapter is stale or unbound")); + eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, "vehicle seat adapter is stale or unbound")); if (expected.id != descriptor_.id || candidate.id != descriptor_.id) - return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, - "vehicle seat prepare targets another container")); + return eve::Result>::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvalidArgument, "vehicle seat prepare targets another container")); auto current = snapshot(); if (!current) - return eve::Result>::failure( - current.status()); + return eve::Result>::failure(current.status()); if (!sameLayout(current.value(), expected)) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, - "vehicle seat expected snapshot is stale")); + eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, "vehicle seat expected snapshot is stale")); const auto next = expected.revision.incremented(); if (!next || candidate.revision != *next) - return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, - "vehicle seat candidate revision is not the next revision")); + return eve::Result>::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvariantViolation, "vehicle seat candidate revision is not the next revision")); if (candidate.entries.size() > seats->list.size()) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "vehicle seat candidate exceeds capacity")); + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "vehicle seat candidate exceeds capacity")); - std::vector> staged(seats->list.size()); + std::vector> staged(seats->list.size()); std::unordered_set identities; for (const auto& entry : candidate.entries) { if (!entry.membership.object.isValid() || !entry.membership.slot.isValid() || static_cast(entry.membership.slot.value()) >= staged.size() || - entry.membership.generation.isZero() || - entry.object.id != entry.membership.object || entry.object.type != "vehicle.occupant" || - entry.object.quantity != 1u || !identities.insert(entry.membership.object).second) + entry.membership.generation.isZero() || entry.object.id != entry.membership.object || + entry.object.type != "vehicle.occupant" || entry.object.quantity != 1u || + !identities.insert(entry.membership.object).second) return eve::Result>::failure( eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "vehicle seat candidate contains invalid membership")); const int occupant = payloadOccupant(entry.object); if (occupant <= 0) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, - "vehicle seat candidate payload is stale")); + eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, "vehicle seat candidate payload is stale")); auto& slot = staged[static_cast(entry.membership.slot.value())]; if (slot) return eve::Result>::failure( @@ -358,129 +324,119 @@ VehicleSeatContainerAdapter::prepare(const eve::container::ContainerSnapshot& ex } std::unique_ptr prepared = std::make_unique(*this, std::move(staged), *next); - return eve::Result>::success( - std::move(prepared)); + return eve::Result>::success(std::move(prepared)); } -eve::Result VehicleSeatContainerAdapter::enter( - eve::container::SlotIndex seatIndex, int occupant, eve::container::GameEventSink sink, - eve::SimulationTick tick) { - const auto serial = nextEventSerial(); +eve::Result VehicleSeatContainerAdapter::enter(eve::container::SlotIndex seatIndex, + int occupant, + eve::container::GameEventSink sink, + eve::SimulationTick tick) { + const auto serial = nextEventSerial(); const auto objectIdValue = occupantId(occupant); - auto current = snapshot(); + auto current = snapshot(); if (!current) { - const auto status = current.status(); - bool ignored = false; + const auto status = current.status(); + bool ignored = false; const auto* diagnostic = status.primaryDiagnostic(); - notify(sink, descriptor_.id, objectIdValue, eve::container::ContainerEventKind::Rejected, - seatIndex, revision(), serial, tick, - diagnostic == nullptr ? "stale_container" : diagnostic->message(), ignored); + notify(sink, descriptor_.id, objectIdValue, eve::container::ContainerEventKind::Rejected, seatIndex, revision(), + serial, tick, diagnostic == nullptr ? "stale_container" : diagnostic->message(), ignored); return eve::Result::failure(status); } - auto object = describe(occupant); + auto object = describe(occupant); auto accepted = validateInsert(object, seatIndex, std::nullopt); if (!accepted) { - const auto status = accepted.status(); - bool ignored = false; + const auto status = accepted.status(); + bool ignored = false; const auto* diagnostic = status.primaryDiagnostic(); - notify(sink, descriptor_.id, object.id, eve::container::ContainerEventKind::Rejected, - seatIndex, current.value().revision, serial, tick, - diagnostic == nullptr ? "rejected" : diagnostic->message(), ignored); + notify(sink, descriptor_.id, object.id, eve::container::ContainerEventKind::Rejected, seatIndex, + current.value().revision, serial, tick, diagnostic == nullptr ? "rejected" : diagnostic->message(), + ignored); return eve::Result::failure(status); } - auto candidate = current.value(); - const auto next = candidate.revision.incremented(); + auto candidate = current.value(); + const auto next = candidate.revision.incremented(); if (!next) { const auto status = eve::Status::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, - "vehicle seat revision exhausted")); + eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "vehicle seat revision exhausted")); bool ignored = false; - notify(sink, descriptor_.id, object.id, eve::container::ContainerEventKind::Rejected, - seatIndex, candidate.revision, serial, tick, "revision_exhausted", ignored); + notify(sink, descriptor_.id, object.id, eve::container::ContainerEventKind::Rejected, seatIndex, + candidate.revision, serial, tick, "revision_exhausted", ignored); return eve::Result::failure(status); } candidate.revision = *next; candidate.entries.push_back({{object.id, seatIndex, eve::Generation(1)}, std::move(object)}); auto prepared = prepare(current.value(), candidate); if (!prepared) { - const auto status = prepared.status(); - bool ignored = false; + const auto status = prepared.status(); + bool ignored = false; const auto* diagnostic = status.primaryDiagnostic(); - notify(sink, descriptor_.id, objectIdValue, eve::container::ContainerEventKind::Rejected, - seatIndex, current.value().revision, serial, tick, - diagnostic == nullptr ? "prepare_failed" : diagnostic->message(), ignored); + notify(sink, descriptor_.id, objectIdValue, eve::container::ContainerEventKind::Rejected, seatIndex, + current.value().revision, serial, tick, diagnostic == nullptr ? "prepare_failed" : diagnostic->message(), + ignored); return eve::Result::failure(status); } auto staged = std::move(prepared).takeValue(); staged->commit(); bool callbackFailed = false; - notify(sink, descriptor_.id, objectIdValue, eve::container::ContainerEventKind::Accepted, - seatIndex, *next, serial, tick, {}, callbackFailed); - notify(sink, descriptor_.id, objectIdValue, eve::container::ContainerEventKind::Enter, - seatIndex, *next, serial, tick, {}, callbackFailed); - return eve::Result::success( - objectIdValue, appliedStatus(callbackFailed)); + notify(sink, descriptor_.id, objectIdValue, eve::container::ContainerEventKind::Accepted, seatIndex, *next, serial, + tick, {}, callbackFailed); + notify(sink, descriptor_.id, objectIdValue, eve::container::ContainerEventKind::Enter, seatIndex, *next, serial, + tick, {}, callbackFailed); + return eve::Result::success(objectIdValue, appliedStatus(callbackFailed)); } -eve::Result VehicleSeatContainerAdapter::exit( - eve::container::SlotIndex seatIndex, eve::container::GameEventSink sink, - eve::SimulationTick tick) { - const auto serial = nextEventSerial(); - auto current = snapshot(); +eve::Result VehicleSeatContainerAdapter::exit(eve::container::SlotIndex seatIndex, + eve::container::GameEventSink sink, + eve::SimulationTick tick) { + const auto serial = nextEventSerial(); + auto current = snapshot(); eve::container::MembershipId objectIdValue; if (current) { for (const auto& entry : current.value().entries) if (entry.membership.slot == seatIndex) objectIdValue = entry.membership.object; } if (!current || !objectIdValue.isValid()) { - const auto status = current - ? eve::Status::failure(eve::Diagnostic::error( - eve::DiagnosticCode::NotFound, - "vehicle seat is already empty")) - : current.status(); - bool ignored = false; + const auto status = current ? eve::Status::failure(eve::Diagnostic::error(eve::DiagnosticCode::NotFound, + "vehicle seat is already empty")) + : current.status(); + bool ignored = false; const auto* diagnostic = status.primaryDiagnostic(); - notify(sink, descriptor_.id, objectIdValue, eve::container::ContainerEventKind::Rejected, - seatIndex, revision(), serial, tick, - diagnostic == nullptr ? "rejected" : diagnostic->message(), ignored); + notify(sink, descriptor_.id, objectIdValue, eve::container::ContainerEventKind::Rejected, seatIndex, revision(), + serial, tick, diagnostic == nullptr ? "rejected" : diagnostic->message(), ignored); return eve::Result::failure(status); } auto candidate = current.value(); candidate.entries.erase(std::remove_if(candidate.entries.begin(), candidate.entries.end(), - [&](const auto& entry) { - return entry.membership.slot == seatIndex; - }), - candidate.entries.end()); + [&](const auto& entry) { return entry.membership.slot == seatIndex; }), + candidate.entries.end()); const auto next = candidate.revision.incremented(); if (!next) { const auto status = eve::Status::failure( - eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, - "vehicle seat revision exhausted")); + eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, "vehicle seat revision exhausted")); bool ignored = false; - notify(sink, descriptor_.id, objectIdValue, eve::container::ContainerEventKind::Rejected, - seatIndex, current.value().revision, serial, tick, "revision_exhausted", ignored); + notify(sink, descriptor_.id, objectIdValue, eve::container::ContainerEventKind::Rejected, seatIndex, + current.value().revision, serial, tick, "revision_exhausted", ignored); return eve::Result::failure(status); } candidate.revision = *next; - auto prepared = prepare(current.value(), candidate); + auto prepared = prepare(current.value(), candidate); if (!prepared) { - const auto status = prepared.status(); - bool ignored = false; + const auto status = prepared.status(); + bool ignored = false; const auto* diagnostic = status.primaryDiagnostic(); - notify(sink, descriptor_.id, objectIdValue, eve::container::ContainerEventKind::Rejected, - seatIndex, current.value().revision, serial, tick, - diagnostic == nullptr ? "prepare_failed" : diagnostic->message(), ignored); + notify(sink, descriptor_.id, objectIdValue, eve::container::ContainerEventKind::Rejected, seatIndex, + current.value().revision, serial, tick, diagnostic == nullptr ? "prepare_failed" : diagnostic->message(), + ignored); return eve::Result::failure(status); } auto staged = std::move(prepared).takeValue(); staged->commit(); bool callbackFailed = false; - notify(sink, descriptor_.id, objectIdValue, eve::container::ContainerEventKind::Accepted, - seatIndex, *next, serial, tick, {}, callbackFailed); - notify(sink, descriptor_.id, objectIdValue, eve::container::ContainerEventKind::Exit, - seatIndex, *next, serial, tick, {}, callbackFailed); - return eve::Result::success( - objectIdValue, appliedStatus(callbackFailed)); + notify(sink, descriptor_.id, objectIdValue, eve::container::ContainerEventKind::Accepted, seatIndex, *next, serial, + tick, {}, callbackFailed); + notify(sink, descriptor_.id, objectIdValue, eve::container::ContainerEventKind::Exit, seatIndex, *next, serial, + tick, {}, callbackFailed); + return eve::Result::success(objectIdValue, appliedStatus(callbackFailed)); } } // namespace eve::vehicle diff --git a/src/modules/vehicle/ContainerAdapters.h b/src/modules/vehicle/ContainerAdapters.h index 70440ecc..c1c68652 100644 --- a/src/modules/vehicle/ContainerAdapters.h +++ b/src/modules/vehicle/ContainerAdapters.h @@ -20,7 +20,7 @@ namespace eve::vehicle { /** @brief Owning snapshot payload for a vehicle occupant membership. */ struct VehicleSeatContainerObject final : eve::container::ContainerObjectPayload { - int occupantId = 0; + int occupantId = 0; std::string driver; }; @@ -53,14 +53,12 @@ public: return descriptor_; } [[nodiscard]] eve::Result snapshot() const override; - [[nodiscard]] eve::Result validateInsert( - const eve::container::ContainerObject& object, - std::optional destination, - std::optional ignoredObject = std::nullopt) const override; + [[nodiscard]] eve::Result validateInsert( + const eve::container::ContainerObject& object, std::optional destination, + std::optional ignoredObject = std::nullopt) const override; /** @copydoc eve::container::IContainer::prepare */ [[nodiscard]] eve::Result> prepare( - const eve::container::ContainerSnapshot& expected, - const eve::container::ContainerSnapshot& candidate) override; + const eve::container::ContainerSnapshot& expected, const eve::container::ContainerSnapshot& candidate) override; /** @brief Return the live seat-component revision, or zero when stale. */ [[nodiscard]] eve::Revision revision() const noexcept; @@ -74,8 +72,7 @@ public: * @return Occupant membership identity, or a rejection with no mutation. */ [[nodiscard]] eve::Result enter( - eve::container::SlotIndex seatIndex, int occupantId, - eve::container::GameEventSink sink = {}, + eve::container::SlotIndex seatIndex, int occupantId, eve::container::GameEventSink sink = {}, eve::SimulationTick tick = eve::SimulationTick::zero()); /** @@ -92,15 +89,14 @@ public: private: class PreparedState; - [[nodiscard]] VehicleEntity* vehicle() const noexcept; - [[nodiscard]] std::uint64_t nextEventSerial() noexcept; - [[nodiscard]] static eve::container::MembershipId occupantId(int occupantId); - [[nodiscard]] static eve::container::ContainerObject describe( - int occupantId, std::string driver = {}); + [[nodiscard]] VehicleEntity* vehicle() const noexcept; + [[nodiscard]] std::uint64_t nextEventSerial() noexcept; + [[nodiscard]] static eve::container::MembershipId occupantId(int occupantId); + [[nodiscard]] static eve::container::ContainerObject describe(int occupantId, std::string driver = {}); eve::container::ContainerDescriptor descriptor_; - ecs::EntityHandle vehicle_; - std::uint64_t eventSerial_ = 0; + ecs::EntityHandle vehicle_; + std::uint64_t eventSerial_ = 0; }; } // namespace eve::vehicle diff --git a/src/modules/vehicle/SettlementAdapter.cpp b/src/modules/vehicle/SettlementAdapter.cpp index bd7371f2..a8a5fe70 100644 --- a/src/modules/vehicle/SettlementAdapter.cpp +++ b/src/modules/vehicle/SettlementAdapter.cpp @@ -13,8 +13,7 @@ namespace { template eve::Result failure(eve::DiagnosticCode code, std::string message, std::string path = {}) { - return eve::Result::failure( - eve::Diagnostic::error(code, std::move(message), std::move(path))); + return eve::Result::failure(eve::Diagnostic::error(code, std::move(message), std::move(path))); } template @@ -28,17 +27,15 @@ eve::Result success(eve::StatusCode code = eve::StatusCode::Ok) { bool isDamageKind(std::string_view kind) noexcept { return kind == "damage"; } -bool isHealingKind(std::string_view kind) noexcept { - return kind == "heal" || kind == "healing"; -} +bool isHealingKind(std::string_view kind) noexcept { return kind == "heal" || kind == "healing"; } } // namespace VehicleSettlementAdapter::VehicleSettlementAdapter(VehicleEntity& vehicle, SubjectRef targetRef) : vehicle_(vehicle), targetRef_(targetRef) {} -const eve::Value* VehicleSettlementAdapter::contextValue( - const settlement::SettlementContext& context, std::string_view key) noexcept { +const eve::Value* VehicleSettlementAdapter::contextValue(const settlement::SettlementContext& context, + std::string_view key) noexcept { const auto* object = context.request().context.getIf(); if (object == nullptr) return nullptr; const auto it = object->find(std::string(key)); @@ -75,21 +72,18 @@ bool VehicleSettlementAdapter::isHealing(const settlement::SettlementContext& co eve::Result VehicleSettlementAdapter::validate(settlement::SettlementContext& context) { if (!(context.request().target == targetRef_)) return failure(eve::DiagnosticCode::Conflict, - "Vehicle settlement request target does not match adapter target", - "target"); + "Vehicle settlement request target does not match adapter target", "target"); if (!isDamage(context) && !isHealing(context)) return failure(eve::DiagnosticCode::Unsupported, - "Vehicle settlement adapter supports damage and healing kinds only", - "kind"); + "Vehicle settlement adapter supports damage and healing kinds only", "kind"); if (vehicle_.health().operator->() == nullptr || vehicle_.stateFlags().operator->() == nullptr) return failure(eve::DiagnosticCode::InvariantViolation, "Vehicle settlement requires health and state components", "vehicle"); auto hpResult = VehicleAttributeAdapter::read(vehicle_, VehicleAttributeAdapter::healthAttribute); if (!hpResult) return failure(hpResult.status()); - auto maxHpResult = - VehicleAttributeAdapter::read(vehicle_, VehicleAttributeAdapter::maxHealthAttribute); + auto maxHpResult = VehicleAttributeAdapter::read(vehicle_, VehicleAttributeAdapter::maxHealthAttribute); if (!maxHpResult) return failure(maxHpResult.status()); - const double hp = hpResult.value(); + const double hp = hpResult.value(); const double maxHp = maxHpResult.value(); if (!std::isfinite(hp) || !std::isfinite(maxHp) || hp < 0.0 || maxHp < 0.0 || hp > maxHp) return failure(eve::DiagnosticCode::InvariantViolation, @@ -99,29 +93,25 @@ eve::Result VehicleSettlementAdapter::validate(settlement::SettlementConte return eve::Result::success(); } -eve::Result VehicleSettlementAdapter::sourceModifiers( - settlement::SettlementContext& context) { +eve::Result VehicleSettlementAdapter::sourceModifiers(settlement::SettlementContext& context) { double multiplier = numberValue(contextValue(context, "source_multiplier")).value_or(1.0); if (!std::isfinite(multiplier) || multiplier < 0.0) return failure(eve::DiagnosticCode::InvalidArgument, - "Vehicle source multiplier must be finite and non-negative", - "source_multiplier"); - auto scaled = context.setMagnitude(context.magnitude() * multiplier); + "Vehicle source multiplier must be finite and non-negative", "source_multiplier"); + auto scaled = context.setMagnitude(context.magnitude() * multiplier); const bool scaledOk = scaled.ok(); if (!scaledOk) { const auto status = scaled.status(); return failure(status); } - const bool critical = boolValue(contextValue(context, "critical")).value_or(false); - const double criticalMultiplier = - numberValue(contextValue(context, "critical_multiplier")).value_or(1.0); + const bool critical = boolValue(contextValue(context, "critical")).value_or(false); + const double criticalMultiplier = numberValue(contextValue(context, "critical_multiplier")).value_or(1.0); if (!std::isfinite(criticalMultiplier) || criticalMultiplier < 1.0) return failure(eve::DiagnosticCode::InvalidArgument, - "Vehicle critical multiplier must be finite and at least one", - "critical_multiplier"); + "Vehicle critical multiplier must be finite and at least one", "critical_multiplier"); if (critical) { - auto criticalScaled = context.setMagnitude(context.magnitude() * criticalMultiplier); + auto criticalScaled = context.setMagnitude(context.magnitude() * criticalMultiplier); const bool criticalScaledOk = criticalScaled.ok(); if (!criticalScaledOk) { const auto status = criticalScaled.status(); @@ -135,22 +125,19 @@ eve::Result VehicleSettlementAdapter::sourceModifiers( return eve::Result::success(); } -eve::Result VehicleSettlementAdapter::targetMitigation( - settlement::SettlementContext& context) { +eve::Result VehicleSettlementAdapter::targetMitigation(settlement::SettlementContext& context) { // Vehicle armor is deliberately handled by the armor/shield phase. This // phase remains a real no-op so every policy shares the same contract. context.setStageDetail("resistance", 0.0); return eve::Result::success(); } -eve::Result VehicleSettlementAdapter::armorShield( - settlement::SettlementContext& context) { +eve::Result VehicleSettlementAdapter::armorShield(settlement::SettlementContext& context) { if (!isDamage(context)) return eve::Result::success(); - const std::string zone = stringValue(contextValue(context, "armor_zone")).value_or(""); - double zoneMultiplier = 1.0; - if (!zone.empty() && vehicle_.definition().operator->() != nullptr && - vehicle_.definition()->def != nullptr) { + const std::string zone = stringValue(contextValue(context, "armor_zone")).value_or(""); + double zoneMultiplier = 1.0; + if (!zone.empty() && vehicle_.definition().operator->() != nullptr && vehicle_.definition()->def != nullptr) { for (const auto& armor : vehicle_.definition()->def->armorZones) { if (armor.name != zone) continue; if (!std::isfinite(armor.mult) || armor.mult < 0.0) @@ -161,12 +148,10 @@ eve::Result VehicleSettlementAdapter::armorShield( } } - const double penetration = std::clamp( - numberValue(contextValue(context, "penetration")).value_or(0.0), 0.0, 1.0); + const double penetration = std::clamp(numberValue(contextValue(context, "penetration")).value_or(0.0), 0.0, 1.0); zoneMultiplier += penetration * (1.0 - zoneMultiplier); - const double incidence = std::clamp( - numberValue(contextValue(context, "incidence")).value_or(1.0), 0.0, 1.0); + const double incidence = std::clamp(numberValue(contextValue(context, "incidence")).value_or(1.0), 0.0, 1.0); // Incidence 1 is a square hit. A glancing hit at zero receives half the // zone-adjusted amount; it is deterministic and does not require physics. zoneMultiplier *= 0.5 + 0.5 * incidence; @@ -180,14 +165,14 @@ eve::Result VehicleSettlementAdapter::armorShield( const double armorFactor = armor == 0.0 ? 1.0 : 100.0 / (100.0 + armor); zoneMultiplier *= armorFactor; - const double before = context.magnitude(); - auto changed = context.setMagnitude(before * zoneMultiplier); - const bool changedOk = changed.ok(); + const double before = context.magnitude(); + auto changed = context.setMagnitude(before * zoneMultiplier); + const bool changedOk = changed.ok(); if (!changedOk) { const auto status = changed.status(); return failure(status); } - auto resisted = context.addResisted(before - context.magnitude()); + auto resisted = context.addResisted(before - context.magnitude()); const bool resistedOk = resisted.ok(); if (!resistedOk) { const auto status = resisted.status(); @@ -206,14 +191,13 @@ eve::Result VehicleSettlementAdapter::armorShield( eve::Result VehicleSettlementAdapter::clamp(settlement::SettlementContext& context) { auto hpResult = VehicleAttributeAdapter::read(vehicle_, VehicleAttributeAdapter::healthAttribute); if (!hpResult) return failure(hpResult.status()); - auto maxHpResult = - VehicleAttributeAdapter::read(vehicle_, VehicleAttributeAdapter::maxHealthAttribute); + auto maxHpResult = VehicleAttributeAdapter::read(vehicle_, VehicleAttributeAdapter::maxHealthAttribute); if (!maxHpResult) return failure(maxHpResult.status()); - const double hp = hpResult.value(); - const double maxHp = maxHpResult.value(); - const double bound = isDamage(context) ? hp : maxHp - hp; - auto configured = context.setClampMax(std::max(0.0, bound)); - const bool configuredOk = configured.ok(); + const double hp = hpResult.value(); + const double maxHp = maxHpResult.value(); + const double bound = isDamage(context) ? hp : maxHp - hp; + auto configured = context.setClampMax(std::max(0.0, bound)); + const bool configuredOk = configured.ok(); if (!configuredOk) { const auto status = configured.status(); return failure(status); @@ -226,51 +210,48 @@ eve::Result VehicleSettlementAdapter::prepareApply( const settlement::SettlementContext& context) { auto hpResult = VehicleAttributeAdapter::read(vehicle_, VehicleAttributeAdapter::healthAttribute); if (!hpResult) return failure(hpResult.status()); - auto maxHpResult = - VehicleAttributeAdapter::read(vehicle_, VehicleAttributeAdapter::maxHealthAttribute); + auto maxHpResult = VehicleAttributeAdapter::read(vehicle_, VehicleAttributeAdapter::maxHealthAttribute); if (!maxHpResult) return failure(maxHpResult.status()); - const double hp = hpResult.value(); - const double maxHp = maxHpResult.value(); + const double hp = hpResult.value(); + const double maxHp = maxHpResult.value(); const double nextHp = isDamage(context) ? hp - context.magnitude() : hp + context.magnitude(); if (!std::isfinite(nextHp) || nextHp < 0.0 || nextHp > maxHp) - return failure( - eve::DiagnosticCode::InvariantViolation, - "Vehicle settlement candidate health is outside its validated bounds", "health"); + return failure(eve::DiagnosticCode::InvariantViolation, + "Vehicle settlement candidate health is outside its validated bounds", + "health"); auto beforeSnapshotResult = VehicleAttributeAdapter::snapshot(vehicle_); if (!beforeSnapshotResult) return failure(beforeSnapshotResult.status()); auto beforeSnapshot = std::move(beforeSnapshotResult).takeValue(); struct Pending { - VehicleEntity* vehicle = nullptr; + VehicleEntity* vehicle = nullptr; attributes::AttributeProjectionSnapshot beforeAttributes; - bool beforeDestroyed = false; - float nextHp = 0.0f; - bool nextDestroyed = false; - bool published = false; + bool beforeDestroyed = false; + float nextHp = 0.0f; + bool nextDestroyed = false; + bool published = false; }; const bool nextDestroyed = isDamage(context) && nextHp <= 0.0; - auto pending = std::make_shared(Pending{&vehicle_, std::move(beforeSnapshot), - vehicle_.stateFlags()->destroyed, - static_cast(nextHp), nextDestroyed, false}); - return eve::Result::success( - settlement::PreparedApply( - [pending]() -> eve::Result { - auto applied = VehicleAttributeAdapter::setBase( - *pending->vehicle, VehicleAttributeAdapter::healthAttribute, pending->nextHp); - if (!applied) return applied; - pending->vehicle->stateFlags()->destroyed = pending->nextDestroyed; - pending->published = true; - return success(eve::StatusCode::Applied); - }, - [pending]() { - if (!pending->published) return; - auto restored = VehicleAttributeAdapter::restore( - *pending->vehicle, pending->beforeAttributes, - pending->vehicle->attributes()->values.revision()); - if (!restored) std::terminate(); - pending->vehicle->stateFlags()->destroyed = pending->beforeDestroyed; - })); + auto pending = + std::make_shared(Pending{&vehicle_, std::move(beforeSnapshot), vehicle_.stateFlags()->destroyed, + static_cast(nextHp), nextDestroyed, false}); + return eve::Result::success(settlement::PreparedApply( + [pending]() -> eve::Result { + auto applied = VehicleAttributeAdapter::setBase(*pending->vehicle, VehicleAttributeAdapter::healthAttribute, + pending->nextHp); + if (!applied) return applied; + pending->vehicle->stateFlags()->destroyed = pending->nextDestroyed; + pending->published = true; + return success(eve::StatusCode::Applied); + }, + [pending]() { + if (!pending->published) return; + auto restored = VehicleAttributeAdapter::restore(*pending->vehicle, pending->beforeAttributes, + pending->vehicle->attributes()->values.revision()); + if (!restored) std::terminate(); + pending->vehicle->stateFlags()->destroyed = pending->beforeDestroyed; + })); } } // namespace eve::vehicle diff --git a/src/modules/vehicle/SettlementAdapter.h b/src/modules/vehicle/SettlementAdapter.h index 20906ef1..c86e9570 100644 --- a/src/modules/vehicle/SettlementAdapter.h +++ b/src/modules/vehicle/SettlementAdapter.h @@ -30,7 +30,7 @@ public: /** @brief Bind an existing vehicle health component to a stable subject id. */ VehicleSettlementAdapter(VehicleEntity& vehicle, SubjectRef targetRef); - VehicleSettlementAdapter(const VehicleSettlementAdapter&) = delete; + VehicleSettlementAdapter(const VehicleSettlementAdapter&) = delete; VehicleSettlementAdapter& operator=(const VehicleSettlementAdapter&) = delete; /** @brief Stable identity checked for the target side of the request. */ @@ -59,16 +59,16 @@ private: * @thread Call on the settlement pipeline's owning simulation thread. * @reentrancy Does not invoke callbacks and must not be used across a re-entrant context mutation. */ - [[nodiscard]] static const eve::Value* contextValue( - const settlement::SettlementContext& context, std::string_view key) noexcept; - [[nodiscard]] static std::optional numberValue(const eve::Value* value) noexcept; - [[nodiscard]] static std::optional boolValue(const eve::Value* value) noexcept; + [[nodiscard]] static const eve::Value* contextValue(const settlement::SettlementContext& context, + std::string_view key) noexcept; + [[nodiscard]] static std::optional numberValue(const eve::Value* value) noexcept; + [[nodiscard]] static std::optional boolValue(const eve::Value* value) noexcept; [[nodiscard]] static std::optional stringValue(const eve::Value* value); [[nodiscard]] bool isDamage(const settlement::SettlementContext& context) const noexcept; [[nodiscard]] bool isHealing(const settlement::SettlementContext& context) const noexcept; VehicleEntity& vehicle_; - SubjectRef targetRef_; + SubjectRef targetRef_; }; } // namespace eve::vehicle diff --git a/src/modules/vehicle/Vehicle.cpp b/src/modules/vehicle/Vehicle.cpp index a52f8237..899d0985 100644 --- a/src/modules/vehicle/Vehicle.cpp +++ b/src/modules/vehicle/Vehicle.cpp @@ -144,11 +144,11 @@ int Vehicle::registerVehiclesFromJson(const std::string& json) { return 0; } - int n = 0; + int n = 0; const auto publish = [this, &n](const eve::Value& value) { const auto* object = value.getIf(); if (object == nullptr) return; - const auto id = object->find("id"); + const auto id = object->find("id"); const auto* name = id == object->end() ? nullptr : id->second.getIf(); if (name == nullptr || name->empty()) return; @@ -158,18 +158,18 @@ int Vehicle::registerVehiclesFromJson(const std::string& json) { return; } eve::definitions::Definition source; - source.type = "vehicle"; - source.id = *name; + source.type = "vehicle"; + source.id = *name; source.version = eve::SchemaVersion(1); - source.json = encoded.value(); - auto typed = parseVehicleDefinition(source); + source.json = encoded.value(); + auto typed = parseVehicleDefinition(source); if (!typed) { typed.ignore("vehicle definition failed typed projection validation"); return; } - auto current = definitionRegistry_.resolve("vehicle", *name); - const bool exists = current.ok(); + auto current = definitionRegistry_.resolve("vehicle", *name); + const bool exists = current.ok(); if (!exists && current.status().code() != eve::StatusCode::NotFound) return; auto stored = exists ? definitionRegistry_.replace("vehicle", *name, 1, source.json) : definitionRegistry_.insert("vehicle", *name, 1, source.json); diff --git a/src/modules/vehicle/Vehicle.h b/src/modules/vehicle/Vehicle.h index 9e78e617..b6212cc6 100644 --- a/src/modules/vehicle/Vehicle.h +++ b/src/modules/vehicle/Vehicle.h @@ -5,8 +5,8 @@ * 设计文档:docs/dev/通用载具系统设计.md */ -#include "common/ECS.h" #include "common/BorrowedRef.h" +#include "common/ECS.h" #include "common/Module.h" #include "common/Result.h" #include "definitions/Definitions.h" @@ -50,9 +50,7 @@ public: * @remarks The returned registry is borrowed, owner-thread affine, and is * the binding point for VehicleDefinitionRuntime adapters. */ - [[nodiscard]] eve::definitions::DefinitionRegistry& definitionRegistry() noexcept { - return definitionRegistry_; - } + [[nodiscard]] eve::definitions::DefinitionRegistry& definitionRegistry() noexcept { return definitionRegistry_; } /** @brief 注册移动模型(C++ 插件/游戏侧扩展点;同名替换)。 */ static void registerMobility(IVehicleMobility* mobility); @@ -65,7 +63,8 @@ public: * @ownership The ECS world owns the entity; callers must not delete it. * @lifetime Valid until ECS destroys the entity or world; retain its generation handle across frames. * @thread Call on the Vehicle module's owning ECS thread. - * @reentrancy Creation does not invoke user callbacks; do not mutate the ECS world re-entrantly while using the result. + * @reentrancy Creation does not invoke user callbacks; do not mutate the ECS world re-entrantly while using the + * result. */ VehicleEntity* newVehicle(const std::string& defId, float x, float y, float heading = 0.f, const std::string& faction = ""); @@ -176,7 +175,7 @@ private: */ [[nodiscard]] eve::Result findDef(const std::string& id) const; - eve::definitions::DefinitionRegistry definitionRegistry_; + eve::definitions::DefinitionRegistry definitionRegistry_; std::vector vehicles_; std::vector events_; int nextInstance_ = 1; diff --git a/src/modules/vehicle/VehicleAttributes.cpp b/src/modules/vehicle/VehicleAttributes.cpp index ea5fd96b..6e588056 100644 --- a/src/modules/vehicle/VehicleAttributes.cpp +++ b/src/modules/vehicle/VehicleAttributes.cpp @@ -13,8 +13,8 @@ namespace { template eve::Result failure(eve::DiagnosticCode code, std::string message, std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - code, std::move(message), std::move(path), {}, "vehicle.attributes")); + return eve::Result::failure( + eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "vehicle.attributes")); } template @@ -24,37 +24,33 @@ eve::Result failure(eve::Status status, std::string, std::string = {}) { template eve::Result failure(eve::StatusCode code, std::string message, std::string path = {}) { - return eve::Result::failure(eve::Status::failure( - code, eve::Diagnostic::error(eve::DiagnosticCode::Failed, std::move(message), - std::move(path), {}, "vehicle.attributes"))); + return eve::Result::failure( + eve::Status::failure(code, eve::Diagnostic::error(eve::DiagnosticCode::Failed, std::move(message), + std::move(path), {}, "vehicle.attributes"))); } -eve::Result failureVoid(eve::DiagnosticCode code, std::string message, - std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - code, std::move(message), std::move(path), {}, "vehicle.attributes")); +eve::Result failureVoid(eve::DiagnosticCode code, std::string message, std::string path = {}) { + return eve::Result::failure( + eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "vehicle.attributes")); } eve::Result failureVoid(eve::Status status, std::string, std::string = {}) { return eve::Result::failure(std::move(status)); } -eve::Result failureVoid(eve::StatusCode code, std::string message, - std::string path = {}) { - return eve::Result::failure(eve::Status::failure( - code, eve::Diagnostic::error(eve::DiagnosticCode::Failed, std::move(message), - std::move(path), {}, "vehicle.attributes"))); +eve::Result failureVoid(eve::StatusCode code, std::string message, std::string path = {}) { + return eve::Result::failure( + eve::Status::failure(code, eve::Diagnostic::error(eve::DiagnosticCode::Failed, std::move(message), + std::move(path), {}, "vehicle.attributes"))); } bool selected(std::string_view name) noexcept { - return name == VehicleAttributeAdapter::healthAttribute || - name == VehicleAttributeAdapter::maxHealthAttribute || + return name == VehicleAttributeAdapter::healthAttribute || name == VehicleAttributeAdapter::maxHealthAttribute || name == VehicleAttributeAdapter::armorAttribute; } std::array selectedAttributes() { - return {VehicleAttributeAdapter::healthAttribute, - VehicleAttributeAdapter::maxHealthAttribute, + return {VehicleAttributeAdapter::healthAttribute, VehicleAttributeAdapter::maxHealthAttribute, VehicleAttributeAdapter::armorAttribute}; } @@ -62,19 +58,17 @@ std::array selectedAttributes() { eve::Result VehicleAttributeAdapter::ensure(VehicleEntity& vehicle) { auto bound = vehicle.attributes()->values.bindOwner(ecs::handle_of(&vehicle)); - if (!bound) - return failureVoid(bound.code(), "vehicle attribute owner could not be bound", "owner"); + if (!bound) return failureVoid(bound.code(), "vehicle attribute owner could not be bound", "owner"); if (!vehicle.attributes()->values.initialized()) { const std::array bases = { eve::attributes::AttributeSnapshotBase{std::string(healthAttribute), - static_cast(vehicle.health()->hp)}, + static_cast(vehicle.health()->hp)}, eve::attributes::AttributeSnapshotBase{std::string(maxHealthAttribute), - static_cast(vehicle.health()->maxHp)}, + static_cast(vehicle.health()->maxHp)}, eve::attributes::AttributeSnapshotBase{std::string(armorAttribute), 0.0}}; auto initialized = vehicle.attributes()->values.initialize(bases); if (!initialized) - return failureVoid(initialized.code(), "vehicle attribute state could not be initialized", - "attributes"); + return failureVoid(initialized.code(), "vehicle attribute state could not be initialized", "attributes"); } return eve::Result::success(eve::Status::success(eve::StatusCode::NoOp)); } @@ -85,35 +79,29 @@ eve::Result VehicleAttributeAdapter::project(VehicleEntity& vehicle) { auto hp = vehicle.attributes()->values.getFinal(healthAttribute); if (!hp) return failureVoid(hp.code(), "vehicle health attribute could not be read", "health"); auto maxHp = vehicle.attributes()->values.getFinal(maxHealthAttribute); - if (!maxHp) - return failureVoid(maxHp.code(), "vehicle max health attribute could not be read", "max_health"); - if (!std::isfinite(hp.value()) || !std::isfinite(maxHp.value()) || hp.value() < 0.0 || - maxHp.value() < 0.0 || hp.value() > maxHp.value() || - hp.value() > static_cast(std::numeric_limits::max()) || + if (!maxHp) return failureVoid(maxHp.code(), "vehicle max health attribute could not be read", "max_health"); + if (!std::isfinite(hp.value()) || !std::isfinite(maxHp.value()) || hp.value() < 0.0 || maxHp.value() < 0.0 || + hp.value() > maxHp.value() || hp.value() > static_cast(std::numeric_limits::max()) || maxHp.value() > static_cast(std::numeric_limits::max())) return failureVoid(eve::DiagnosticCode::InvariantViolation, "vehicle health attributes are outside validated bounds", "health"); - vehicle.health()->hp = static_cast(hp.value()); + vehicle.health()->hp = static_cast(hp.value()); vehicle.health()->maxHp = static_cast(maxHp.value()); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result VehicleAttributeAdapter::read(VehicleEntity& vehicle, - std::string_view attribute) { +eve::Result VehicleAttributeAdapter::read(VehicleEntity& vehicle, std::string_view attribute) { if (!selected(attribute)) return failure(eve::DiagnosticCode::Unsupported, - "vehicle attribute is outside the selective combat projection", - "attribute"); + "vehicle attribute is outside the selective combat projection", "attribute"); auto projected = project(vehicle); - if (!projected) - return failure(projected.code(), "vehicle compatibility projection failed", "health"); + if (!projected) return failure(projected.code(), "vehicle compatibility projection failed", "health"); auto value = vehicle.attributes()->values.getFinal(attribute); if (!value) return failure(value.code(), "vehicle attribute read failed", "attribute"); return eve::Result::success(value.value()); } -eve::Result VehicleAttributeAdapter::setBase(VehicleEntity& vehicle, std::string_view attribute, - double value) { +eve::Result VehicleAttributeAdapter::setBase(VehicleEntity& vehicle, std::string_view attribute, double value) { if (!selected(attribute)) return failureVoid(eve::DiagnosticCode::Unsupported, "vehicle attribute is outside the selective combat projection", "attribute"); @@ -126,29 +114,25 @@ eve::Result VehicleAttributeAdapter::setBase(VehicleEntity& vehicle, std:: eve::Result VehicleAttributeAdapter::addModifier( VehicleEntity& vehicle, std::string id, std::string_view attribute, std::string source, - eve::attributes::AttributeOperation operation, double value, - eve::attributes::ModifierPriority priority) { + eve::attributes::AttributeOperation operation, double value, eve::attributes::ModifierPriority priority) { if (!selected(attribute) || source.empty()) return failure( - eve::DiagnosticCode::InvalidArgument, - "vehicle modifier requires a selected attribute and non-empty source", "modifier"); + eve::DiagnosticCode::InvalidArgument, "vehicle modifier requires a selected attribute and non-empty source", + "modifier"); auto ready = ensure(vehicle); if (!ready) - return failure(ready.code(), "vehicle attributes are not available", - "attributes"); - auto added = vehicle.attributes()->values.addModifier( - eve::attributes::AttributeModifier(std::move(id), std::string(attribute), std::move(source), - operation, value, priority)); + return failure(ready.code(), "vehicle attributes are not available", "attributes"); + auto added = vehicle.attributes()->values.addModifier(eve::attributes::AttributeModifier( + std::move(id), std::string(attribute), std::move(source), operation, value, priority)); if (!added) return added; auto projected = project(vehicle); if (!projected) - return failure(projected.code(), - "vehicle compatibility projection failed", "health"); + return failure(projected.code(), "vehicle compatibility projection failed", + "health"); return added; } -eve::Result VehicleAttributeAdapter::snapshot( - VehicleEntity& vehicle) { +eve::Result VehicleAttributeAdapter::snapshot(VehicleEntity& vehicle) { auto ready = ensure(vehicle); if (!ready) return failure( @@ -157,9 +141,9 @@ eve::Result VehicleAttributeAdapte return vehicle.attributes()->values.snapshot(names); } -eve::Result VehicleAttributeAdapter::restore( - VehicleEntity& vehicle, const eve::attributes::AttributeProjectionSnapshot& snapshotValue, - eve::Revision expectedRevision) { +eve::Result VehicleAttributeAdapter::restore(VehicleEntity& vehicle, + const eve::attributes::AttributeProjectionSnapshot& snapshotValue, + eve::Revision expectedRevision) { auto ready = ensure(vehicle); if (!ready) return failureVoid(ready.code(), "vehicle attributes are not available", "attributes"); auto restored = vehicle.attributes()->values.restore(snapshotValue, expectedRevision); diff --git a/src/modules/vehicle/VehicleAttributes.h b/src/modules/vehicle/VehicleAttributes.h index 85a4c09e..dc451e2d 100644 --- a/src/modules/vehicle/VehicleAttributes.h +++ b/src/modules/vehicle/VehicleAttributes.h @@ -23,38 +23,35 @@ class VehicleEntity; */ class VehicleAttributeAdapter final { public: - static constexpr std::string_view healthAttribute = "health"; + static constexpr std::string_view healthAttribute = "health"; static constexpr std::string_view maxHealthAttribute = "max_health"; - static constexpr std::string_view armorAttribute = "armor"; + static constexpr std::string_view armorAttribute = "armor"; /** @brief Bind and seed selected combat attributes exactly once. */ [[nodiscard]] static eve::Result ensure(VehicleEntity& vehicle); /** @brief Read a selected final combat attribute and refresh health projection. */ - [[nodiscard]] static eve::Result read(VehicleEntity& vehicle, - std::string_view attribute); + [[nodiscard]] static eve::Result read(VehicleEntity& vehicle, std::string_view attribute); /** @brief Set a selected canonical base and refresh the compatibility health fields. */ - [[nodiscard]] static eve::Result setBase(VehicleEntity& vehicle, - std::string_view attribute, double value); + [[nodiscard]] static eve::Result setBase(VehicleEntity& vehicle, std::string_view attribute, double value); /** @brief Add a modifier using the shared operation/priority/sequence contract. */ [[nodiscard]] static eve::Result addModifier( - VehicleEntity& vehicle, std::string id, std::string_view attribute, - std::string source, eve::attributes::AttributeOperation operation, double value, + VehicleEntity& vehicle, std::string id, std::string_view attribute, std::string source, + eve::attributes::AttributeOperation operation, double value, eve::attributes::ModifierPriority priority = eve::attributes::priority::runtime); /** @brief Project canonical health values into VehicleEntity::Health. */ [[nodiscard]] static eve::Result project(VehicleEntity& vehicle); /** @brief Capture health/max_health/armor with owner generation and revision. */ - [[nodiscard]] static eve::Result snapshot( - VehicleEntity& vehicle); + [[nodiscard]] static eve::Result snapshot(VehicleEntity& vehicle); /** @brief Restore a vehicle attribute snapshot after owner/revision checks. */ - [[nodiscard]] static eve::Result restore( - VehicleEntity& vehicle, const eve::attributes::AttributeProjectionSnapshot& snapshot, - eve::Revision expectedRevision); + [[nodiscard]] static eve::Result restore(VehicleEntity& vehicle, + const eve::attributes::AttributeProjectionSnapshot& snapshot, + eve::Revision expectedRevision); }; } // namespace eve::vehicle diff --git a/src/modules/vehicle/VehicleDefinitionRuntime.cpp b/src/modules/vehicle/VehicleDefinitionRuntime.cpp index 8ebcb1bb..f41398b4 100644 --- a/src/modules/vehicle/VehicleDefinitionRuntime.cpp +++ b/src/modules/vehicle/VehicleDefinitionRuntime.cpp @@ -16,9 +16,8 @@ namespace { template eve::Result invalid(std::string message, std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, std::move(message), std::move(path), {}, - "vehicle.definition_runtime")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, std::move(message), + std::move(path), {}, "vehicle.definition_runtime")); } eve::LogicalId vehicleSchema() { @@ -30,11 +29,10 @@ eve::LogicalId vehicleSchema() { return value; } -eve::Result currentHandle( - eve::definitions::DefinitionRegistry& registry, const eve::DefinitionRef& reference) { +eve::Result currentHandle(eve::definitions::DefinitionRegistry& registry, + const eve::DefinitionRef& reference) { const auto& logical = reference.id(); - return registry.handle(std::string(logical.namespaceName()), - std::string(logical.name())); + return registry.handle(std::string(logical.namespaceName()), std::string(logical.name())); } const eve::Value* field(const eve::Value::Object& object, std::string_view name) { @@ -42,8 +40,7 @@ const eve::Value* field(const eve::Value::Object& object, std::string_view name) return it == object.end() ? nullptr : &it->second; } -bool readString(const eve::Value::Object& object, std::string_view name, std::string& result, - bool required = false) { +bool readString(const eve::Value::Object& object, std::string_view name, std::string& result, bool required = false) { const auto* value = field(object, name); if (value == nullptr) return !required; const auto* text = value->getIf(); @@ -52,34 +49,37 @@ bool readString(const eve::Value::Object& object, std::string_view name, std::st return true; } -bool readNumber(const eve::Value::Object& object, std::string_view name, float& result, - float fallback) { +bool readNumber(const eve::Value::Object& object, std::string_view name, float& result, float fallback) { const auto* value = field(object, name); if (value == nullptr) { result = fallback; return true; } double number = 0.0; - if (const auto* real = value->getIf()) number = *real; - else if (const auto* integer = value->getIf()) number = static_cast(*integer); - else return false; - if (!std::isfinite(number) || number < 0.0 || number > std::numeric_limits::max()) + if (const auto* real = value->getIf()) + number = *real; + else if (const auto* integer = value->getIf()) + number = static_cast(*integer); + else return false; + if (!std::isfinite(number) || number < 0.0 || number > std::numeric_limits::max()) return false; result = static_cast(number); return true; } -bool readSignedNumber(const eve::Value::Object& object, std::string_view name, float& result, - float fallback) { +bool readSignedNumber(const eve::Value::Object& object, std::string_view name, float& result, float fallback) { const auto* value = field(object, name); if (value == nullptr) { result = fallback; return true; } double number = 0.0; - if (const auto* real = value->getIf()) number = *real; - else if (const auto* integer = value->getIf()) number = static_cast(*integer); - else return false; + if (const auto* real = value->getIf()) + number = *real; + else if (const auto* integer = value->getIf()) + number = static_cast(*integer); + else + return false; if (!std::isfinite(number) || number < -std::numeric_limits::max() || number > std::numeric_limits::max()) return false; @@ -87,8 +87,7 @@ bool readSignedNumber(const eve::Value::Object& object, std::string_view name, f return true; } -bool readBool(const eve::Value::Object& object, std::string_view name, bool& result, - bool fallback) { +bool readBool(const eve::Value::Object& object, std::string_view name, bool& result, bool fallback) { const auto* value = field(object, name); if (value == nullptr) { result = fallback; @@ -110,21 +109,19 @@ eve::Result parseVehicleDefinition(const eve::definitions::De VehicleDefinition result; result.id = source.id; - if (!readString(*object, "category", result.category) || - !readString(*object, "mobility", result.mobility)) + if (!readString(*object, "category", result.category) || !readString(*object, "mobility", result.mobility)) return invalid("vehicle category and mobility must be strings", "definition"); if (!readNumber(*object, "maxSpeed", result.maxSpeed, result.maxSpeed) || !readNumber(*object, "accel", result.accel, result.accel) || !readNumber(*object, "turnRate", result.turnRate, result.turnRate) || !readNumber(*object, "radius", result.radius, result.radius) || - !readNumber(*object, "maxHealth", result.maxHealth, result.maxHealth) || - result.radius <= 0.f || result.maxHealth < 0.f) + !readNumber(*object, "maxHealth", result.maxHealth, result.maxHealth) || result.radius <= 0.f || + result.maxHealth < 0.f) return invalid("vehicle numeric definition field is invalid", "definition"); if (const auto* physics = field(*object, "physics")) { const auto* physicsObject = physics->getIf(); - if (physicsObject == nullptr) - return invalid("vehicle physics must be an object", "physics"); + if (physicsObject == nullptr) return invalid("vehicle physics must be an object", "physics"); if (!readNumber(*physicsObject, "maxSpeed", result.maxSpeed, result.maxSpeed) || !readNumber(*physicsObject, "accel", result.accel, result.accel) || !readNumber(*physicsObject, "turnRate", result.turnRate, result.turnRate) || @@ -169,10 +166,11 @@ eve::Result parseVehicleDefinition(const eve::definitions::De return invalid("mount limits must contain four numbers", "mounts.limits"); float* output[] = {&mount.yawMin, &mount.yawMax, &mount.pitchMin, &mount.pitchMax}; for (std::size_t limit = 0; limit < 4; ++limit) { - const auto* real = (*values)[limit].getIf(); - const auto* integer = (*values)[limit].getIf(); - const double number = real ? *real : integer ? static_cast(*integer) - : std::numeric_limits::quiet_NaN(); + const auto* real = (*values)[limit].getIf(); + const auto* integer = (*values)[limit].getIf(); + const double number = real ? *real + : integer ? static_cast(*integer) + : std::numeric_limits::quiet_NaN(); if (!std::isfinite(number) || number < -std::numeric_limits::max() || number > std::numeric_limits::max()) return invalid("mount limit is not finite", "mounts.limits"); @@ -192,17 +190,21 @@ eve::Result parseVehicleDefinition(const eve::definitions::De return invalid("suspension numeric field is invalid", "suspension"); if (const auto* wheels = field(*value, "wheels")) { const auto* array = wheels->getIf(); - if (array == nullptr) return invalid("suspension wheels must be an array", "suspension.wheels"); + if (array == nullptr) + return invalid("suspension wheels must be an array", "suspension.wheels"); for (std::size_t index = 0; index < array->size(); ++index) { const auto* item = (*array)[index].getIf(); if (item == nullptr) return invalid("wheel must be an object", "suspension.wheels"); SuspensionWheel wheel; - if (!readSignedNumber(*item, "x", wheel.x, wheel.x) || !readSignedNumber(*item, "y", wheel.y, wheel.y) || - !readSignedNumber(*item, "z", wheel.z, wheel.z) || !readNumber(*item, "radius", wheel.radius, wheel.radius) || + if (!readSignedNumber(*item, "x", wheel.x, wheel.x) || + !readSignedNumber(*item, "y", wheel.y, wheel.y) || + !readSignedNumber(*item, "z", wheel.z, wheel.z) || + !readNumber(*item, "radius", wheel.radius, wheel.radius) || !readNumber(*item, "rest", wheel.restLength, wheel.restLength) || !readNumber(*item, "stiffness", wheel.stiffness, wheel.stiffness) || !readNumber(*item, "damping", wheel.damping, wheel.damping) || - !readBool(*item, "drive", wheel.drive, wheel.drive) || !readBool(*item, "steer", wheel.steer, wheel.steer)) + !readBool(*item, "drive", wheel.drive, wheel.drive) || + !readBool(*item, "steer", wheel.steer, wheel.steer)) return invalid("wheel definition field is invalid", "suspension.wheels"); result.suspension.wheels.push_back(wheel); } @@ -262,37 +264,41 @@ eve::Result decodeState(const eve::Value& value) { static const std::set fields = {"destroyed", "faction", "heading", "health", "speed", "x", "y"}; for (const auto& [name, unused] : *object) { (void)unused; - if (!fields.contains(name)) return invalid("unknown vehicle runtime state field", "state." + name); + if (!fields.contains(name)) + return invalid("unknown vehicle runtime state field", "state." + name); } for (const auto& name : fields) - if (!object->contains(name)) return invalid("vehicle runtime state is missing a field", "state." + name); + if (!object->contains(name)) + return invalid("vehicle runtime state is missing a field", "state." + name); auto number = [&](std::string_view name, float& output, bool allowNegative) -> bool { const auto* real = object->at(std::string(name)).getIf(); - if (real == nullptr || !std::isfinite(*real) || - (!allowNegative && *real < 0.0) || *real < -std::numeric_limits::max() || - *real > std::numeric_limits::max()) return false; + if (real == nullptr || !std::isfinite(*real) || (!allowNegative && *real < 0.0) || + *real < -std::numeric_limits::max() || *real > std::numeric_limits::max()) + return false; output = static_cast(*real); return true; }; - const auto* destroyed = object->at("destroyed").getIf(); - const auto* faction = object->at("faction").getIf(); + const auto* destroyed = object->at("destroyed").getIf(); + const auto* faction = object->at("faction").getIf(); VehicleRuntimeState result; if (destroyed == nullptr || faction == nullptr || !number("heading", result.heading, false) || !number("health", result.health, false) || !number("speed", result.speed, false) || !number("x", result.x, true) || !number("y", result.y, true)) return invalid("vehicle runtime state field is invalid", "state"); result.destroyed = *destroyed; - result.faction = *faction; + result.faction = *faction; return eve::Result::success(std::move(result)); } } // namespace -eve::Result VehicleDefinitionRuntime::create( - eve::definitions::DefinitionRegistry& registry, eve::DefinitionRef definition, - eve::PersistentId instanceId, eve::definition::ReloadPolicy policy) { +eve::Result VehicleDefinitionRuntime::create(eve::definitions::DefinitionRegistry& registry, + eve::DefinitionRef definition, + eve::PersistentId instanceId, + eve::definition::ReloadPolicy policy) { if (!definition.id().isValid() || definition.id().namespaceName() != "vehicle") - return invalid("vehicle definition reference must use vehicle namespace", "definition"); + return invalid("vehicle definition reference must use vehicle namespace", + "definition"); auto handle = currentHandle(registry, definition); if (!handle) return eve::Result::failure(handle.status()); auto resolved = registry.resolveHandle(handle.value()); @@ -301,16 +307,17 @@ eve::Result VehicleDefinitionRuntime::create( if (!typed) return eve::Result::failure(typed.status()); VehicleRuntimeState defaults; defaults.health = typed.value().maxHealth; - auto runtime = eve::definition::RuntimeInstance::create( - instanceId, std::move(definition), handle.value().generation, defaults); + auto runtime = eve::definition::RuntimeInstance::create(instanceId, std::move(definition), + handle.value().generation, defaults); if (!runtime) return eve::Result::failure(runtime.status()); return eve::Result::success( VehicleDefinitionRuntime(registry, std::move(runtime).takeValue(), policy)); } -eve::Result VehicleDefinitionRuntime::create( - eve::definitions::DefinitionRegistry& registry, std::string_view definitionId, - eve::PersistentId instanceId, eve::definition::ReloadPolicy policy) { +eve::Result VehicleDefinitionRuntime::create(eve::definitions::DefinitionRegistry& registry, + std::string_view definitionId, + eve::PersistentId instanceId, + eve::definition::ReloadPolicy policy) { auto logical = eve::LogicalId::fromParts("vehicle", definitionId); if (!logical) return invalid("vehicle id is not a valid logical name", "definitionId"); auto reference = eve::DefinitionRef::fromId(*logical); @@ -318,9 +325,9 @@ eve::Result VehicleDefinitionRuntime::create( return create(registry, std::move(reference).takeValue(), instanceId, policy); } -eve::Result VehicleDefinitionRuntime::create( - Vehicle& module, std::string_view definitionId, eve::PersistentId instanceId, - eve::definition::ReloadPolicy policy) { +eve::Result VehicleDefinitionRuntime::create(Vehicle& module, std::string_view definitionId, + eve::PersistentId instanceId, + eve::definition::ReloadPolicy policy) { return create(module.definitionRegistry(), definitionId, instanceId, policy); } @@ -350,34 +357,34 @@ bool VehicleDefinitionRuntime::isActive() const noexcept { return runtime_.isAct eve::Result VehicleDefinitionRuntime::applyTo(VehicleEntity* entity) const { if (entity == nullptr) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "vehicle runtime cannot project to a null VehicleEntity", - "entity", {}, "vehicle.definition_runtime")); + eve::DiagnosticCode::InvalidArgument, "vehicle runtime cannot project to a null VehicleEntity", "entity", + {}, "vehicle.definition_runtime")); auto typed = definition(); if (!typed) return eve::Result::failure(typed.status()); try { auto definitionOwner = std::make_shared(typed.value()); - auto identity = *entity->identity(); - auto definition = *entity->definition(); - auto binding = *entity->definitionBinding(); - auto motion = *entity->motion(); - auto health = *entity->health(); - auto flags = *entity->stateFlags(); + auto identity = *entity->identity(); + auto definition = *entity->definition(); + auto binding = *entity->definitionBinding(); + auto motion = *entity->motion(); + auto health = *entity->health(); + auto flags = *entity->stateFlags(); - identity.id = this->identity().instanceId.format(); - identity.defId = this->identity().definition.id().name(); - definition.owned = std::move(definitionOwner); - definition.def = definition.owned.get(); - binding.identity = this->identity(); + identity.id = this->identity().instanceId.format(); + identity.defId = this->identity().definition.id().name(); + definition.owned = std::move(definitionOwner); + definition.def = definition.owned.get(); + binding.identity = this->identity(); binding.reloadPolicy = policy_; - binding.active = isActive(); - motion.x = state().x; - motion.y = state().y; - motion.heading = state().heading; - motion.speed = state().speed; - motion.arrived = false; - health.maxHp = typed.value().maxHealth; - health.hp = std::clamp(state().health, 0.f, health.maxHp); - flags.destroyed = state().destroyed; + binding.active = isActive(); + motion.x = state().x; + motion.y = state().y; + motion.heading = state().heading; + motion.speed = state().speed; + motion.arrived = false; + health.maxHp = typed.value().maxHealth; + health.hp = std::clamp(state().health, 0.f, health.maxHp); + flags.destroyed = state().destroyed; using std::swap; swap(*entity->identity(), identity); @@ -388,15 +395,15 @@ eve::Result VehicleDefinitionRuntime::applyTo(VehicleEntity* entity) const swap(*entity->stateFlags(), flags); return eve::Result::success(); } catch (const std::exception&) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "vehicle runtime projection allocation failed", "entity", - {}, "vehicle.definition_runtime")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::Failed, + "vehicle runtime projection allocation failed", + "entity", {}, "vehicle.definition_runtime")); } } -eve::Result VehicleDefinitionRuntime::reload( - eve::definition::ReloadPolicy policy) { - if (registry_ == nullptr) return invalid("vehicle definition registry is not bound", "registry"); +eve::Result VehicleDefinitionRuntime::reload(eve::definition::ReloadPolicy policy) { + if (registry_ == nullptr) + return invalid("vehicle definition registry is not bound", "registry"); auto next = currentHandle(*registry_, identity().definition); if (!next) return eve::Result::failure(next.status()); auto resolved = registry_->resolveHandle(next.value()); @@ -404,13 +411,12 @@ eve::Result VehicleDefinitionRuntime::reload( auto typed = parseVehicleDefinition(resolved.value().get()); if (!typed) return eve::Result::failure(typed.status()); VehicleRuntimeState defaults; - defaults.health = typed.value().maxHealth; + defaults.health = typed.value().maxHealth; const float maxHealth = typed.value().maxHealth; - auto rebuild = [maxHealth](const VehicleRuntimeState& oldState, - const eve::definition::InstanceIdentity&, + auto rebuild = [maxHealth](const VehicleRuntimeState& oldState, const eve::definition::InstanceIdentity&, const eve::definition::DefinitionHandle&) { VehicleRuntimeState rebuilt = oldState; - rebuilt.health = std::clamp(oldState.health, 0.f, maxHealth); + rebuilt.health = std::clamp(oldState.health, 0.f, maxHealth); return eve::Result::success(std::move(rebuilt)); }; auto result = runtime_.reload(next.value(), policy, defaults, std::move(rebuild)); @@ -419,40 +425,34 @@ eve::Result VehicleDefinitionRuntime::reload( } eve::Result VehicleDefinitionRuntime::snapshot( - eve::Revision revision, eve::SimulationTick tick, - const eve::SnapshotHashProvider& hashProvider) const { - const eve::definition::RuntimeStateEncoder encoder = - [](const VehicleRuntimeState& value) { - return eve::Result::success(encodeState(value)); - }; - return eve::definition::snapshotRuntimeInstance( - runtime_, "vehicle.runtime", vehicleSchema(), eve::SchemaVersion(1), revision, tick, - hashProvider, encoder); + eve::Revision revision, eve::SimulationTick tick, const eve::SnapshotHashProvider& hashProvider) const { + const eve::definition::RuntimeStateEncoder encoder = [](const VehicleRuntimeState& value) { + return eve::Result::success(encodeState(value)); + }; + return eve::definition::snapshotRuntimeInstance(runtime_, "vehicle.runtime", vehicleSchema(), eve::SchemaVersion(1), + revision, tick, hashProvider, encoder); } -eve::Result VehicleDefinitionRuntime::snapshotJson( - eve::Revision revision, eve::SimulationTick tick, - const eve::SnapshotHashProvider& hashProvider) const { +eve::Result VehicleDefinitionRuntime::snapshotJson(eve::Revision revision, eve::SimulationTick tick, + const eve::SnapshotHashProvider& hashProvider) const { auto result = snapshot(revision, tick, hashProvider); if (!result) return eve::Result::failure(result.status()); return std::move(result).andThen( [](eve::SnapshotEnvelope&& value) { return eve::serializeSnapshotEnvelope(value); }); } -eve::Result VehicleDefinitionRuntime::restore( - const eve::SnapshotEnvelope& snapshotValue, const eve::SnapshotHashProvider& hashProvider) { +eve::Result VehicleDefinitionRuntime::restore(const eve::SnapshotEnvelope& snapshotValue, + const eve::SnapshotHashProvider& hashProvider) { if (registry_ == nullptr) return invalid("vehicle definition registry is not bound", "registry"); auto current = currentHandle(*registry_, identity().definition); if (!current) return eve::Result::failure(current.status()); if (current.value() != definitionHandle()) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::StaleHandle, - "vehicle snapshot cannot restore against a replaced definition", "definitionGeneration", {}, - "vehicle.definition_runtime")); + eve::DiagnosticCode::StaleHandle, "vehicle snapshot cannot restore against a replaced definition", + "definitionGeneration", {}, "vehicle.definition_runtime")); const eve::definition::RuntimeStateDecoder decoder = decodeState; - return eve::definition::restoreRuntimeInstance( - runtime_, snapshotValue, "vehicle.runtime", vehicleSchema(), eve::SchemaVersion(1), - hashProvider, decoder); + return eve::definition::restoreRuntimeInstance(runtime_, snapshotValue, "vehicle.runtime", vehicleSchema(), + eve::SchemaVersion(1), hashProvider, decoder); } } // namespace eve::vehicle diff --git a/src/modules/vehicle/VehicleDefinitionRuntime.h b/src/modules/vehicle/VehicleDefinitionRuntime.h index efd69691..75c648e1 100644 --- a/src/modules/vehicle/VehicleDefinitionRuntime.h +++ b/src/modules/vehicle/VehicleDefinitionRuntime.h @@ -30,18 +30,17 @@ class Vehicle; * @remarks The returned value is not an additional definition store. Callers that need to * retain the projection must own a copy for their runtime instance. */ -[[nodiscard]] eve::Result parseVehicleDefinition( - const eve::definitions::Definition& source); +[[nodiscard]] eve::Result parseVehicleDefinition(const eve::definitions::Definition& source); /** @brief Mutable state owned by one typed vehicle runtime instance. */ struct VehicleRuntimeState { - float x = 0.f; - float y = 0.f; - float heading = 0.f; - float speed = 0.f; - float health = 0.f; + float x = 0.f; + float y = 0.f; + float heading = 0.f; + float speed = 0.f; + float health = 0.f; std::string faction; - bool destroyed = false; + bool destroyed = false; }; /** @@ -59,14 +58,12 @@ public: * @return A typed adapter or a structured parse/resolution failure. */ [[nodiscard]] static eve::Result create( - eve::definitions::DefinitionRegistry& registry, eve::DefinitionRef definition, - eve::PersistentId instanceId, + eve::definitions::DefinitionRegistry& registry, eve::DefinitionRef definition, eve::PersistentId instanceId, eve::definition::ReloadPolicy policy = eve::definition::ReloadPolicy::RebuildInstance); /** @brief Create from a Vehicle module's canonical definition registry. */ [[nodiscard]] static eve::Result create( - eve::definitions::DefinitionRegistry& registry, std::string_view definitionId, - eve::PersistentId instanceId, + eve::definitions::DefinitionRegistry& registry, std::string_view definitionId, eve::PersistentId instanceId, eve::definition::ReloadPolicy policy = eve::definition::ReloadPolicy::RebuildInstance); /** @brief Create through the existing Vehicle module facade. */ @@ -74,11 +71,11 @@ public: Vehicle& module, std::string_view definitionId, eve::PersistentId instanceId, eve::definition::ReloadPolicy policy = eve::definition::ReloadPolicy::RebuildInstance); - VehicleDefinitionRuntime(VehicleDefinitionRuntime&&) noexcept = default; + VehicleDefinitionRuntime(VehicleDefinitionRuntime&&) noexcept = default; VehicleDefinitionRuntime& operator=(VehicleDefinitionRuntime&&) noexcept = default; - VehicleDefinitionRuntime(const VehicleDefinitionRuntime&) = delete; - VehicleDefinitionRuntime& operator=(const VehicleDefinitionRuntime&) = delete; - ~VehicleDefinitionRuntime() = default; + VehicleDefinitionRuntime(const VehicleDefinitionRuntime&) = delete; + VehicleDefinitionRuntime& operator=(const VehicleDefinitionRuntime&) = delete; + ~VehicleDefinitionRuntime() = default; /** @brief Borrow common instance identity and exact definition generation. */ [[nodiscard]] const eve::definition::InstanceIdentity& identity() const noexcept; @@ -105,33 +102,30 @@ public: [[nodiscard]] eve::Result applyTo(VehicleEntity* entity) const; /** @brief Reload the registry's current generation using a typed policy. */ - [[nodiscard]] eve::Result reload( - eve::definition::ReloadPolicy policy); + [[nodiscard]] eve::Result reload(eve::definition::ReloadPolicy policy); /** @brief Capture mutable entity state in a common SnapshotEnvelope. */ - [[nodiscard]] eve::Result snapshot( - eve::Revision revision, eve::SimulationTick tick, - const eve::SnapshotHashProvider& hashProvider) const; + [[nodiscard]] eve::Result snapshot(eve::Revision revision, eve::SimulationTick tick, + const eve::SnapshotHashProvider& hashProvider) const; /** @brief Serialize snapshot() as canonical deterministic JSON. */ - [[nodiscard]] eve::Result snapshotJson( - eve::Revision revision, eve::SimulationTick tick, - const eve::SnapshotHashProvider& hashProvider) const; + [[nodiscard]] eve::Result snapshotJson(eve::Revision revision, eve::SimulationTick tick, + const eve::SnapshotHashProvider& hashProvider) const; /** * @brief Verify and restore a same-generation vehicle snapshot atomically. * @return Failure leaves all mutable runtime state unchanged. */ - [[nodiscard]] eve::Result restore( - const eve::SnapshotEnvelope& snapshot, const eve::SnapshotHashProvider& hashProvider); + [[nodiscard]] eve::Result restore(const eve::SnapshotEnvelope& snapshot, + const eve::SnapshotHashProvider& hashProvider); private: - VehicleDefinitionRuntime(eve::definitions::DefinitionRegistry& registry, + VehicleDefinitionRuntime(eve::definitions::DefinitionRegistry& registry, eve::definition::RuntimeInstance runtime, - eve::definition::ReloadPolicy policy) + eve::definition::ReloadPolicy policy) : registry_(®istry), runtime_(std::move(runtime)), policy_(policy) {} - eve::definitions::DefinitionRegistry* registry_ = nullptr; // borrowed + eve::definitions::DefinitionRegistry* registry_ = nullptr; // borrowed eve::definition::RuntimeInstance runtime_; - eve::definition::ReloadPolicy policy_ = eve::definition::ReloadPolicy::RebuildInstance; + eve::definition::ReloadPolicy policy_ = eve::definition::ReloadPolicy::RebuildInstance; }; } // namespace eve::vehicle diff --git a/src/modules/vehicle/VehicleEffects.cpp b/src/modules/vehicle/VehicleEffects.cpp index 5e97dbc3..edc505b4 100644 --- a/src/modules/vehicle/VehicleEffects.cpp +++ b/src/modules/vehicle/VehicleEffects.cpp @@ -9,17 +9,15 @@ namespace { const char* kindTag(VehicleEffectKind kind) { switch (kind) { - case VehicleEffectKind::Damage: return "vehicle:damage"; - case VehicleEffectKind::Repair: return "vehicle:repair"; - case VehicleEffectKind::Disable: return "vehicle:disable"; + case VehicleEffectKind::Damage: return "vehicle:damage"; + case VehicleEffectKind::Repair: return "vehicle:repair"; + case VehicleEffectKind::Disable: return "vehicle:disable"; } return "vehicle:unknown"; } double zoneMultiplier(const VehicleEffectTarget& target, const std::vector& tags) { - const auto has = [&tags](const std::string& tag) { - return std::find(tags.begin(), tags.end(), tag) != tags.end(); - }; + const auto has = [&tags](const std::string& tag) { return std::find(tags.begin(), tags.end(), tag) != tags.end(); }; if (has("vehicle:zone:front")) return target.frontArmorMultiplier; if (has("vehicle:zone:rear")) return target.rearArmorMultiplier; return target.sideArmorMultiplier; @@ -28,15 +26,15 @@ double zoneMultiplier(const VehicleEffectTarget& target, const std::vector VehicleEffectExecutor::validate(const VehicleEffectTarget& target) const { - if (!std::isfinite(target.hull) || !std::isfinite(target.maxHull) || target.maxHull <= 0.0 || - target.hull < 0.0 || target.hull > target.maxHull || target.frontArmorMultiplier < 0.0 || - target.sideArmorMultiplier < 0.0 || target.rearArmorMultiplier < 0.0) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "vehicle effect target state is invalid", "target")); + if (!std::isfinite(target.hull) || !std::isfinite(target.maxHull) || target.maxHull <= 0.0 || target.hull < 0.0 || + target.hull > target.maxHull || target.frontArmorMultiplier < 0.0 || target.sideArmorMultiplier < 0.0 || + target.rearArmorMultiplier < 0.0) + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "vehicle effect target state is invalid", "target")); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result VehicleEffectExecutor::applyImmediate(VehicleEffectTarget& target, +eve::Result VehicleEffectExecutor::applyImmediate(VehicleEffectTarget& target, const effects::EffectInstance& effect) const { auto valid = validate(target); if (!valid) return valid; @@ -44,8 +42,8 @@ eve::Result VehicleEffectExecutor::applyImmediate(VehicleEffectTarget& tar return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result VehicleEffectExecutor::settle( - VehicleEffectTarget& target, effects::EffectUpdateSummary lifecycle) const { +eve::Result VehicleEffectExecutor::settle(VehicleEffectTarget& target, + effects::EffectUpdateSummary lifecycle) const { auto valid = validate(target); if (!valid) return eve::Result::failure(valid.status()); VehicleEffectUpdate result; @@ -78,43 +76,43 @@ eve::Result VehicleEffectExecutor::settle( } ++result.settled; } - return eve::Result::success(std::move(result), - eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(std::move(result), eve::Status::success(eve::StatusCode::Applied)); } -eve::Result VehicleEffectAdapter::apply( - const VehicleEffectDefinition& definition, eve::SubjectRef subject) { +eve::Result VehicleEffectAdapter::apply(const VehicleEffectDefinition& definition, + eve::SubjectRef subject) { if (definition.id.empty() || !subject.isValid() || definition.armorZone.empty()) return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::InvalidArgument, "vehicle effect requires id, zone and valid subject", "effect")); effects::EffectDefinition common; - common.id = definition.id; - common.stackKey = definition.id; - common.duration = definition.duration; - common.period = definition.period; + common.id = definition.id; + common.stackKey = definition.id; + common.duration = definition.duration; + common.period = definition.period; common.magnitude = definition.magnitude; - common.policy = definition.policy; - common.tags = definition.tags; + common.policy = definition.policy; + common.tags = definition.tags; common.tags.push_back(kindTag(definition.kind)); common.tags.push_back("vehicle:zone:" + definition.armorZone); auto valid = common.validate(); if (!valid) return eve::Result::failure(valid.status()); auto candidate = container_.clone(); - auto applied = candidate.apply(common, subject.format(), definition.source); + auto applied = candidate.apply(common, subject.format(), definition.source); if (!applied) return eve::Result::failure(applied.status()); - const std::string id = std::move(applied).takeValue(); - const auto* instance = candidate.find(id); - if (!instance) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, "vehicle effect candidate disappeared", "effect")); + const std::string id = std::move(applied).takeValue(); + const auto* instance = candidate.find(id); + if (!instance) + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvariantViolation, "vehicle effect candidate disappeared", "effect")); auto targetCandidate = target_; - auto immediate = executor_.applyImmediate(targetCandidate, *instance); + auto immediate = executor_.applyImmediate(targetCandidate, *instance); if (!immediate) return eve::Result::failure(immediate.status()); auto handle = candidate.handleFor(id); if (!handle) return eve::Result::failure(handle.status()); - target_ = std::move(targetCandidate); + target_ = std::move(targetCandidate); container_ = std::move(candidate); return eve::Result::success(std::move(handle).takeValue(), - eve::Status::success(eve::StatusCode::Applied)); + eve::Status::success(eve::StatusCode::Applied)); } eve::Result VehicleEffectAdapter::remove(effects::EffectHandle handle) { @@ -128,13 +126,12 @@ eve::Result VehicleEffectAdapter::advance(const eve::Simula auto lifecycle = effects::EffectExecutor{}.advance(candidate, step); if (!lifecycle) return eve::Result::failure(lifecycle.status()); auto targetCandidate = target_; - auto settled = executor_.settle(targetCandidate, std::move(lifecycle).takeValue()); + auto settled = executor_.settle(targetCandidate, std::move(lifecycle).takeValue()); if (!settled) return settled; auto result = std::move(settled).takeValue(); - target_ = std::move(targetCandidate); - container_ = std::move(candidate); - return eve::Result::success(std::move(result), - eve::Status::success(eve::StatusCode::Applied)); + target_ = std::move(targetCandidate); + container_ = std::move(candidate); + return eve::Result::success(std::move(result), eve::Status::success(eve::StatusCode::Applied)); } VehicleEffectSnapshot VehicleEffectAdapter::snapshot() const { return {container_.snapshot(), target_}; } diff --git a/src/modules/vehicle/VehicleEffects.h b/src/modules/vehicle/VehicleEffects.h index b7f1191f..bd92d2bb 100644 --- a/src/modules/vehicle/VehicleEffects.h +++ b/src/modules/vehicle/VehicleEffects.h @@ -21,39 +21,39 @@ enum class VehicleEffectKind : std::uint8_t { Damage, Repair, Disable }; /** @brief Mutable vehicle state changed by the effect executor. */ struct VehicleEffectTarget { - double hull = 100.0; - double maxHull = 100.0; - bool disabled = false; - std::uint32_t criticalHits = 0; - double frontArmorMultiplier = 0.75; - double sideArmorMultiplier = 1.0; - double rearArmorMultiplier = 1.25; + double hull = 100.0; + double maxHull = 100.0; + bool disabled = false; + std::uint32_t criticalHits = 0; + double frontArmorMultiplier = 0.75; + double sideArmorMultiplier = 1.0; + double rearArmorMultiplier = 1.25; }; /** @brief Strong vehicle definition projected into the common lifecycle. */ struct VehicleEffectDefinition { - std::string id; - std::string source; - std::string armorZone = "side"; - double duration = 0.0; - double period = 0.0; - double magnitude = 0.0; - effects::EffectPolicy policy; - VehicleEffectKind kind = VehicleEffectKind::Damage; + std::string id; + std::string source; + std::string armorZone = "side"; + double duration = 0.0; + double period = 0.0; + double magnitude = 0.0; + effects::EffectPolicy policy; + VehicleEffectKind kind = VehicleEffectKind::Damage; std::vector tags; }; /** @brief Result of one vehicle lifecycle step and armor settlement. */ struct VehicleEffectUpdate { effects::EffectUpdateSummary lifecycle; - std::uint32_t settled = 0; - std::uint32_t criticalHits = 0; + std::uint32_t settled = 0; + std::uint32_t criticalHits = 0; }; /** @brief In-memory vehicle effect snapshot. */ struct VehicleEffectSnapshot { effects::EffectContainer effects; - VehicleEffectTarget target; + VehicleEffectTarget target; }; /** @brief Vehicle executor applying armor-zone and disable strategies. */ @@ -62,10 +62,10 @@ public: /** @brief Validate hull and armor invariants. */ [[nodiscard]] eve::Result validate(const VehicleEffectTarget& target) const; /** @brief Apply an immediate disable strategy to a staged target. */ - [[nodiscard]] eve::Result applyImmediate(VehicleEffectTarget& target, + [[nodiscard]] eve::Result applyImmediate(VehicleEffectTarget& target, const effects::EffectInstance& effect) const; /** @brief Settle common periodic triggers through vehicle armor policy. */ - [[nodiscard]] eve::Result settle(VehicleEffectTarget& target, + [[nodiscard]] eve::Result settle(VehicleEffectTarget& target, effects::EffectUpdateSummary lifecycle) const; }; @@ -74,7 +74,7 @@ class VehicleEffectAdapter { public: /** @brief Apply one typed vehicle effect to a stable subject. */ [[nodiscard]] eve::Result apply(const VehicleEffectDefinition& definition, - eve::SubjectRef subject); + eve::SubjectRef subject); /** @brief Remove one generation-qualified effect handle. */ [[nodiscard]] eve::Result remove(effects::EffectHandle handle); /** @brief Advance and settle vehicle effects atomically. */ @@ -92,8 +92,8 @@ public: private: effects::EffectContainer container_; - VehicleEffectExecutor executor_; - VehicleEffectTarget target_; + VehicleEffectExecutor executor_; + VehicleEffectTarget target_; }; } // namespace eve::vehicle diff --git a/src/modules/vehicle/VehicleOrderQueueAdapter.cpp b/src/modules/vehicle/VehicleOrderQueueAdapter.cpp index 94e859c5..e9e82ce5 100644 --- a/src/modules/vehicle/VehicleOrderQueueAdapter.cpp +++ b/src/modules/vehicle/VehicleOrderQueueAdapter.cpp @@ -18,16 +18,16 @@ eve::Result failure(eve::DiagnosticCode code, std::string message, std::strin } // namespace struct VehicleOrderQueueAdapter::Impl { - orders::CommandQueue queue; + orders::CommandQueue queue; std::unordered_map payloads; }; void VehicleOrderQueueAdapter::pruneTerminalPayloads() { for (auto it = impl_->payloads.begin(); it != impl_->payloads.end();) { - auto recordRef = impl_->queue.find(it->first); - const orders::Order* record = recordRef ? &recordRef->get() : nullptr; - if (record == nullptr || (record->state != orders::OrderState::Queued && - record->state != orders::OrderState::Active)) { + auto recordRef = impl_->queue.find(it->first); + const orders::Order* record = recordRef ? &recordRef->get() : nullptr; + if (record == nullptr || + (record->state != orders::OrderState::Queued && record->state != orders::OrderState::Active)) { it = impl_->payloads.erase(it); } else { ++it; @@ -57,8 +57,7 @@ eve::Result VehicleOrderQueueAdapter::append(const VehicleOrder& or if (!id) return id; const std::string key = std::move(id).takeValue(); impl_->payloads.emplace(key, order); - return eve::Result::success(std::move(key), - eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(std::move(key), eve::Status::success(eve::StatusCode::Applied)); } eve::Result VehicleOrderQueueAdapter::replace(const VehicleOrder& order, int priority, @@ -68,8 +67,7 @@ eve::Result VehicleOrderQueueAdapter::replace(const VehicleOrder& o const std::string key = std::move(id).takeValue(); pruneTerminalPayloads(); impl_->payloads.emplace(key, order); - return eve::Result::success(std::move(key), - eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(std::move(key), eve::Status::success(eve::StatusCode::Applied)); } eve::Result VehicleOrderQueueAdapter::interrupt(const VehicleOrder& order, int priority, @@ -79,27 +77,24 @@ eve::Result VehicleOrderQueueAdapter::interrupt(const VehicleOrder& const std::string key = std::move(id).takeValue(); pruneTerminalPayloads(); impl_->payloads.emplace(key, order); - return eve::Result::success(std::move(key), - eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(std::move(key), eve::Status::success(eve::StatusCode::Applied)); } eve::Result VehicleOrderQueueAdapter::completeCurrent() { - auto currentRef = impl_->queue.current(); - orders::Order* current = currentRef ? ¤tRef->get() : nullptr; + auto currentRef = impl_->queue.current(); + orders::Order* current = currentRef ? ¤tRef->get() : nullptr; if (current == nullptr) - return failure(eve::DiagnosticCode::Conflict, - "vehicle order queue has no active order", "order"); + return failure(eve::DiagnosticCode::Conflict, "vehicle order queue has no active order", "order"); auto completed = impl_->queue.complete(current->id); if (completed) pruneTerminalPayloads(); return completed; } eve::Result VehicleOrderQueueAdapter::failCurrent(const std::string& reason) { - auto currentRef = impl_->queue.current(); - orders::Order* current = currentRef ? ¤tRef->get() : nullptr; + auto currentRef = impl_->queue.current(); + orders::Order* current = currentRef ? ¤tRef->get() : nullptr; if (current == nullptr) - return failure(eve::DiagnosticCode::Conflict, - "vehicle order queue has no active order", "order"); + return failure(eve::DiagnosticCode::Conflict, "vehicle order queue has no active order", "order"); auto failed = impl_->queue.fail(current->id, reason); if (failed) pruneTerminalPayloads(); return failed; @@ -123,8 +118,8 @@ void VehicleOrderQueueAdapter::clear() { } const VehicleOrder* VehicleOrderQueueAdapter::current() const { - auto recordRef = impl_->queue.current(); - const orders::Order* record = recordRef ? &recordRef->get() : nullptr; + auto recordRef = impl_->queue.current(); + const orders::Order* record = recordRef ? &recordRef->get() : nullptr; if (record == nullptr) return nullptr; const auto it = impl_->payloads.find(record->id); return it == impl_->payloads.end() ? nullptr : &it->second; @@ -133,10 +128,10 @@ const VehicleOrder* VehicleOrderQueueAdapter::current() const { int VehicleOrderQueueAdapter::activeOrQueuedCount() const { int count = 0; for (int i = 0; i < impl_->queue.orderCount(); ++i) { - auto recordRef = impl_->queue.orderAt(i); - const orders::Order* record = recordRef ? &recordRef->get() : nullptr; - if (record != nullptr && (record->state == orders::OrderState::Queued || - record->state == orders::OrderState::Active)) { + auto recordRef = impl_->queue.orderAt(i); + const orders::Order* record = recordRef ? &recordRef->get() : nullptr; + if (record != nullptr && + (record->state == orders::OrderState::Queued || record->state == orders::OrderState::Active)) { ++count; } } @@ -147,10 +142,10 @@ void VehicleOrderQueueAdapter::syncCompatibility(VehicleEntity::Orders& legacy) legacy.queue.clear(); legacy.current = -1; for (int i = 0; i < impl_->queue.orderCount(); ++i) { - auto recordRef = impl_->queue.orderAt(i); - const orders::Order* record = recordRef ? &recordRef->get() : nullptr; - if (record == nullptr || (record->state != orders::OrderState::Queued && - record->state != orders::OrderState::Active)) { + auto recordRef = impl_->queue.orderAt(i); + const orders::Order* record = recordRef ? &recordRef->get() : nullptr; + if (record == nullptr || + (record->state != orders::OrderState::Queued && record->state != orders::OrderState::Active)) { continue; } const auto it = impl_->payloads.find(record->id); diff --git a/src/modules/vehicle/VehicleOrderQueueAdapter.h b/src/modules/vehicle/VehicleOrderQueueAdapter.h index 9f0b50ca..b77f5bdc 100644 --- a/src/modules/vehicle/VehicleOrderQueueAdapter.h +++ b/src/modules/vehicle/VehicleOrderQueueAdapter.h @@ -8,8 +8,8 @@ * generic orders module; the adapter only maps their kind and stable id. */ -#include "vehicle/VehicleTypes.h" #include "common/Result.h" +#include "vehicle/VehicleTypes.h" #include #include diff --git a/src/modules/vehicle/VehicleSystem.cpp b/src/modules/vehicle/VehicleSystem.cpp index 1f3e0e45..1eb295ee 100644 --- a/src/modules/vehicle/VehicleSystem.cpp +++ b/src/modules/vehicle/VehicleSystem.cpp @@ -427,16 +427,14 @@ void VehicleSystem::processOrders(VehicleEntity& v, float dt) { eve::Result VehicleSystem::pushOrder(VehicleEntity& v, const VehicleOrder& order) { auto orders = v.orders(); - const bool replaces = order.type == VehicleOrderType::Move || - order.type == VehicleOrderType::AttackMove || + const bool replaces = order.type == VehicleOrderType::Move || order.type == VehicleOrderType::AttackMove || order.type == VehicleOrderType::Stop || order.type == VehicleOrderType::Hold; auto queued = replaces ? orders->adapter->replace(order) : orders->adapter->append(order); if (!queued.ok()) return eve::Result::failure(queued.status()); const std::string id = std::move(queued).takeValue(); if (id.empty()) { return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, "vehicle order adapter returned an empty id", - "orders")); + eve::DiagnosticCode::InvariantViolation, "vehicle order adapter returned an empty id", "orders")); } orders->adapter->syncCompatibility(*orders); v.motion()->arrived = false; @@ -450,9 +448,7 @@ void VehicleSystem::clearOrders(VehicleEntity& v) { v.motion()->arrived = false; } -const VehicleOrder* VehicleSystem::currentOrder(VehicleEntity& v) { - return v.orders()->adapter->current(); -} +const VehicleOrder* VehicleSystem::currentOrder(VehicleEntity& v) { return v.orders()->adapter->current(); } float VehicleSystem::steerToward(VehicleEntity& v, float targetHeadingDeg) { const float delta = angleDelta(v.motion()->heading, targetHeadingDeg); @@ -481,16 +477,14 @@ const PlayerControl* VehicleSystem::playerControls(int playerId) { } bool VehicleSystem::enterSeat(VehicleEntity& v, int seatIndex, int playerId) { - VehicleSeatContainerAdapter adapter( - eve::container::ContainerId("vehicle:seat:" + v.identity()->id), &v); - auto entered = adapter.enter(eve::container::SlotIndex(seatIndex), playerId); + VehicleSeatContainerAdapter adapter(eve::container::ContainerId("vehicle:seat:" + v.identity()->id), &v); + auto entered = adapter.enter(eve::container::SlotIndex(seatIndex), playerId); return entered.ok(); } bool VehicleSystem::exitSeat(VehicleEntity& v, int seatIndex) { - VehicleSeatContainerAdapter adapter( - eve::container::ContainerId("vehicle:seat:" + v.identity()->id), &v); - auto exited = adapter.exit(eve::container::SlotIndex(seatIndex)); + VehicleSeatContainerAdapter adapter(eve::container::ContainerId("vehicle:seat:" + v.identity()->id), &v); + auto exited = adapter.exit(eve::container::SlotIndex(seatIndex)); return exited.ok(); } diff --git a/src/modules/vehicle/VehicleTypes.h b/src/modules/vehicle/VehicleTypes.h index e24ca7be..c2a5098c 100644 --- a/src/modules/vehicle/VehicleTypes.h +++ b/src/modules/vehicle/VehicleTypes.h @@ -10,14 +10,14 @@ * 命令队列 / 驾驶输入 / 座位(Phase 4)这些可选面上。 */ +#include "attributes/AttributeProjection.h" #include "common/ECS.h" #include "common/Revision.h" #include "common/definitions/DefinitionRuntime.h" -#include "attributes/AttributeProjection.h" #include -#include #include +#include #include #include @@ -199,9 +199,8 @@ public: */ struct DefinitionBinding { eve::definition::InstanceIdentity identity; - eve::definition::ReloadPolicy reloadPolicy = - eve::definition::ReloadPolicy::RebuildInstance; - bool active = true; + eve::definition::ReloadPolicy reloadPolicy = eve::definition::ReloadPolicy::RebuildInstance; + bool active = true; }; /** @brief 每帧输入(命令系统 / 驾驶者写入,移动模型消费)。 */ diff --git a/src/modules/weapon/Weapon.cpp b/src/modules/weapon/Weapon.cpp index 0840adb4..07b03713 100644 --- a/src/modules/weapon/Weapon.cpp +++ b/src/modules/weapon/Weapon.cpp @@ -185,11 +185,11 @@ int Weapon::registerWeaponsFromJson(const std::string& json) { return 0; } - int n = 0; + int n = 0; const auto publish = [this, &n](const eve::Value& value) { const auto* object = value.getIf(); if (object == nullptr) return; - const auto id = object->find("id"); + const auto id = object->find("id"); const auto* name = id == object->end() ? nullptr : id->second.getIf(); if (name == nullptr || name->empty()) return; @@ -199,18 +199,18 @@ int Weapon::registerWeaponsFromJson(const std::string& json) { return; } eve::definitions::Definition source; - source.type = "weapon"; - source.id = *name; + source.type = "weapon"; + source.id = *name; source.version = eve::SchemaVersion(1); - source.json = encoded.value(); - auto typed = parseWeaponDefinition(source); + source.json = encoded.value(); + auto typed = parseWeaponDefinition(source); if (!typed) { typed.ignore("weapon definition failed typed projection validation"); return; } - auto current = definitionRegistry_.resolve("weapon", *name); - const bool exists = current.ok(); + auto current = definitionRegistry_.resolve("weapon", *name); + const bool exists = current.ok(); if (!exists && current.status().code() != eve::StatusCode::NotFound) return; auto stored = exists ? definitionRegistry_.replace("weapon", *name, 1, source.json) : definitionRegistry_.insert("weapon", *name, 1, source.json); @@ -240,9 +240,7 @@ void Weapon::clearWeaponDefinitions() { int Weapon::getWeaponDefinitionCount() { return definitionRegistry_.countType("weapon"); } -bool Weapon::hasWeaponDefinition(const std::string& id) { - return definitionRegistry_.resolve("weapon", id).ok(); -} +bool Weapon::hasWeaponDefinition(const std::string& id) { return definitionRegistry_.resolve("weapon", id).ok(); } std::string Weapon::getWeaponDefinitionLogic(const std::string& id) { auto definition = findDef(id); @@ -296,12 +294,12 @@ WeaponEntity* Weapon::newWeapon(const std::string& defId) { WeaponEntity* w = WeaponEntity::createWeapon(); w->identity()->id = defId + "#" + std::to_string(nextInstance_++); w->identity()->defId = defId; - w->definition()->owned = std::make_shared(std::move(d)); - w->definition()->def = w->definition()->owned.get(); - w->state()->stages = &w->definition()->def->stages; + w->definition()->owned = std::make_shared(std::move(d)); + w->definition()->def = w->definition()->owned.get(); + w->state()->stages = &w->definition()->def->stages; const WeaponDefinition& storedDefinition = *w->definition()->def; - w->state()->selector = storedDefinition.fireMode; - w->state()->currentSpread = storedDefinition.spreadMin; + w->state()->selector = storedDefinition.fireMode; + w->state()->currentSpread = storedDefinition.spreadMin; // 运行时资源:热武器 ammo 映射到弹匣/备用;其余形态从模板 resource 拷贝。 Resource& r = w->state()->resource; diff --git a/src/modules/weapon/Weapon.h b/src/modules/weapon/Weapon.h index 39e7d6fb..fa0a032a 100644 --- a/src/modules/weapon/Weapon.h +++ b/src/modules/weapon/Weapon.h @@ -5,8 +5,8 @@ * 设计文档:docs/dev/通用载具系统设计.md */ -#include "common/ECS.h" #include "common/BorrowedRef.h" +#include "common/ECS.h" #include "common/Module.h" #include "definitions/Definitions.h" #include "weapon/WeaponDefinitionRuntime.h" @@ -44,9 +44,7 @@ public: * @ownership Borrowed; this module retains ownership. * @lifetime Valid until this Weapon module is destroyed. */ - [[nodiscard]] eve::definitions::DefinitionRegistry& definitionRegistry() noexcept { - return definitionRegistry_; - } + [[nodiscard]] eve::definitions::DefinitionRegistry& definitionRegistry() noexcept { return definitionRegistry_; } /** @brief 注册武器逻辑(C++ 插件/游戏侧扩展点;同名替换)。 */ static void registerLogic(IWeaponLogic* logic); diff --git a/src/modules/weapon/WeaponAction.cpp b/src/modules/weapon/WeaponAction.cpp index 705d688d..6a31a750 100644 --- a/src/modules/weapon/WeaponAction.cpp +++ b/src/modules/weapon/WeaponAction.cpp @@ -11,18 +11,17 @@ namespace { template eve::Result failure(eve::DiagnosticCode code, std::string message, std::string path = {}) { - return eve::Result::failure( - eve::Diagnostic::error(code, std::move(message), std::move(path))); + return eve::Result::failure(eve::Diagnostic::error(code, std::move(message), std::move(path))); } eve::Result weaponActionId(const WeaponDefinition& weapon) { if (weapon.id.empty()) - return failure(eve::DiagnosticCode::InvalidArgument, - "weapon definition id must not be empty", "weapon.id"); + return failure(eve::DiagnosticCode::InvalidArgument, "weapon definition id must not be empty", + "weapon.id"); const auto id = eve::LogicalId::fromParts("weapon", "attack." + weapon.id); if (!id) return failure(eve::DiagnosticCode::InvalidArgument, - "weapon id cannot form a valid action logical id", "weapon.id"); + "weapon id cannot form a valid action logical id", "weapon.id"); return eve::Result::success(*id); } @@ -37,11 +36,11 @@ eve::Result seconds(float value, const char* path) { const char* resourceName(ResourceKind kind) { switch (kind) { - case ResourceKind::Ammo: return "ammo"; - case ResourceKind::Mana: return "mana"; - case ResourceKind::Charges: return "charges"; - case ResourceKind::Stamina: return "stamina"; - case ResourceKind::None: return ""; + case ResourceKind::Ammo: return "ammo"; + case ResourceKind::Mana: return "mana"; + case ResourceKind::Charges: return "charges"; + case ResourceKind::Stamina: return "stamina"; + case ResourceKind::None: return ""; } return ""; } @@ -54,87 +53,71 @@ eve::Result> weaponCost(const WeaponDefin // Ranged WeaponSystem consumes one magazine unit per trigger even when // the legacy template leaves resource.cost at its zero default. if (weapon.resource.kind == ResourceKind::Ammo && amount <= 0.0) amount = 1.0; - if (!std::isfinite(amount) || amount <= 0.0 || std::floor(amount) != amount || - amount >= std::ldexp(1.0, 63)) - return failure>( - eve::DiagnosticCode::InvalidArgument, - "weapon resource cost must be a finite positive integer", "resource.cost"); + if (!std::isfinite(amount) || amount <= 0.0 || std::floor(amount) != amount || amount >= std::ldexp(1.0, 63)) + return failure>(eve::DiagnosticCode::InvalidArgument, + "weapon resource cost must be a finite positive integer", + "resource.cost"); - auto cost = eve::resource::CostSpec::single(resourceName(weapon.resource.kind), - static_cast(amount)); + auto cost = eve::resource::CostSpec::single(resourceName(weapon.resource.kind), static_cast(amount)); if (!cost) return eve::Result>::failure(cost.status()); - return eve::Result>::success( - std::move(cost).takeValue()); + return eve::Result>::success(std::move(cost).takeValue()); } void put(eve::Value::Object& object, const char* key, float value) { object.emplace(key, eve::Value(static_cast(value))); } -void put(eve::Value::Object& object, const char* key, int value) { - object.emplace(key, eve::Value(value)); -} +void put(eve::Value::Object& object, const char* key, int value) { object.emplace(key, eve::Value(value)); } } // namespace -eve::Result> WeaponActionAdapter::resourceCost( - const WeaponDefinition& weapon) { +eve::Result> WeaponActionAdapter::resourceCost(const WeaponDefinition& weapon) { return weaponCost(weapon); } namespace { eve::Result executeActionFire( - const WeaponDefinition& weapon, const AttackRequest& attack, - eve::resource::IResourceAccount& account, - eve::transaction::ITransactionParticipant& effect, eve::SimulationTick tick, - std::string transactionId) { + const WeaponDefinition& weapon, const AttackRequest& attack, eve::resource::IResourceAccount& account, + eve::transaction::ITransactionParticipant& effect, eve::SimulationTick tick, std::string transactionId) { auto definition = WeaponActionAdapter::makeDefinition(weapon, attack); - if (!definition) - return eve::Result::failure(definition.status()); + if (!definition) return eve::Result::failure(definition.status()); auto request = WeaponActionAdapter::makeRequest(weapon, attack, std::nullopt, tick); - if (!request) - return eve::Result::failure(request.status()); + if (!request) return eve::Result::failure(request.status()); auto actionDefinition = std::move(definition).takeValue(); - auto actionRequest = std::move(request).takeValue(); + auto actionRequest = std::move(request).takeValue(); if (transactionId.empty()) transactionId = "weapon.fire." + weapon.id; actionRequest.transactionId = std::move(transactionId); action::ActionServices services; - services.transactionEffect = &effect; + services.transactionEffect = &effect; services.transactionAccount = &account; action::ActionRuntime runtime(services); - auto submitted = runtime.submit(std::move(actionDefinition), std::move(actionRequest)); - if (!submitted) - return eve::Result::failure(submitted.status()); - const auto executionId = std::move(submitted).takeValue(); + auto submitted = runtime.submit(std::move(actionDefinition), std::move(actionRequest)); + if (!submitted) return eve::Result::failure(submitted.status()); + const auto executionId = std::move(submitted).takeValue(); const auto* submittedExecution = runtime.find(executionId); if (submittedExecution == nullptr) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, - "weapon Action execution disappeared after submit", "weapon.fire")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, + "weapon Action execution disappeared after submit", "weapon.fire")); - auto total = submittedExecution->definition().timing.windup.tryAdd( - submittedExecution->definition().timing.active); - if (!total) - return eve::Result::failure(total.status()); - total = std::move(total).takeValue().tryAdd( - submittedExecution->definition().timing.recover); - if (!total) - return eve::Result::failure(total.status()); + auto total = submittedExecution->definition().timing.windup.tryAdd(submittedExecution->definition().timing.active); + if (!total) return eve::Result::failure(total.status()); + total = std::move(total).takeValue().tryAdd(submittedExecution->definition().timing.recover); + if (!total) return eve::Result::failure(total.status()); auto advanced = runtime.advance(executionId, tick, std::move(total).takeValue()); - if (!advanced) - return eve::Result::failure(advanced.status()); + if (!advanced) return eve::Result::failure(advanced.status()); const auto* execution = runtime.find(executionId); if (execution == nullptr || execution->phase() != action::ActionPhase::Completed || execution->transactionReceipt() == nullptr) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, - "weapon Action execution completed without a transaction receipt", "weapon.fire")); - return eve::Result::success( - *execution->transactionReceipt(), advanced.status()); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::InvariantViolation, + "weapon Action execution completed without a transaction receipt", "weapon.fire")); + return eve::Result::success(*execution->transactionReceipt(), + advanced.status()); } } // namespace @@ -147,14 +130,13 @@ eve::Result WeaponActionAdapter::fire( } eve::Result WeaponActionAdapter::fire( - const WeaponDefinition& weapon, const AttackRequest& attack, - eve::resource::IResourceAccount& account, + const WeaponDefinition& weapon, const AttackRequest& attack, eve::resource::IResourceAccount& account, eve::transaction::ITransactionParticipant& effect, eve::SimulationTick tick) { return executeActionFire(weapon, attack, account, effect, tick, {}); } -eve::Result WeaponActionAdapter::makeDefinition( - const WeaponDefinition& weapon, const AttackRequest& request) { +eve::Result WeaponActionAdapter::makeDefinition(const WeaponDefinition& weapon, + const AttackRequest& request) { auto id = weaponActionId(weapon); if (!id) return eve::Result::failure(id.status()); auto windup = seconds(weapon.stages.windupTime, "stages.windupTime"); @@ -167,13 +149,12 @@ eve::Result WeaponActionAdapter::makeDefinition( if (!cost) return eve::Result::failure(cost.status()); action::ActionDefinition definition; - definition.id = std::move(id).takeValue(); - definition.timing.windup = std::move(windup).takeValue(); - definition.timing.active = std::move(active).takeValue(); + definition.id = std::move(id).takeValue(); + definition.timing.windup = std::move(windup).takeValue(); + definition.timing.active = std::move(active).takeValue(); definition.timing.recover = std::move(recover).takeValue(); - definition.targetingMode = request.hasTarget ? action::TargetingMode::Explicit - : action::TargetingMode::None; - definition.cost = std::move(cost).takeValue(); + definition.targetingMode = request.hasTarget ? action::TargetingMode::Explicit : action::TargetingMode::None; + definition.cost = std::move(cost).takeValue(); // Weapon logic is an Active operation even if its effect ids are resolved // by the weapon logic/event sink rather than the effects registry. definition.activeExecutionRequired = true; @@ -186,16 +167,17 @@ eve::Result WeaponActionAdapter::makeDefinition( return eve::Result::success(std::move(definition)); } -eve::Result WeaponActionAdapter::makeRequest( - const WeaponDefinition& weapon, const AttackRequest& attack, - std::optional source, eve::SimulationTick requestedTick) { +eve::Result WeaponActionAdapter::makeRequest(const WeaponDefinition& weapon, + const AttackRequest& attack, + std::optional source, + eve::SimulationTick requestedTick) { auto definition = makeDefinition(weapon, attack); if (!definition) return eve::Result::failure(definition.status()); auto checkedDefinition = std::move(definition).takeValue(); action::ActionRequest request; - request.actionId = checkedDefinition.id; - request.source = std::move(source); + request.actionId = checkedDefinition.id; + request.source = std::move(source); request.requestedTick = requestedTick; if (attack.hasTarget) request.targetEntities.push_back(attack.targetHandle); diff --git a/src/modules/weapon/WeaponAction.h b/src/modules/weapon/WeaponAction.h index 7349172e..1548d206 100644 --- a/src/modules/weapon/WeaponAction.h +++ b/src/modules/weapon/WeaponAction.h @@ -63,10 +63,8 @@ public: * call submit and then assemble a separate payment transaction. */ [[nodiscard]] static eve::Result fire( - const WeaponDefinition& weapon, const AttackRequest& attack, - eve::resource::IResourceAccount& account, - eve::transaction::ITransactionParticipant& effect, - eve::SimulationTick tick = eve::SimulationTick(1)); + const WeaponDefinition& weapon, const AttackRequest& attack, eve::resource::IResourceAccount& account, + eve::transaction::ITransactionParticipant& effect, eve::SimulationTick tick = eve::SimulationTick(1)); /** * @brief Build an action definition for one attack request. @@ -74,8 +72,8 @@ public: * @param request Borrowed attack request; target selection affects the mode. * @return Common definition or a structured mapping failure. */ - [[nodiscard]] static eve::Result makeDefinition( - const WeaponDefinition& weapon, const AttackRequest& request); + [[nodiscard]] static eve::Result makeDefinition(const WeaponDefinition& weapon, + const AttackRequest& request); /** * @brief Build a request carrying the original weapon attack parameters. @@ -87,9 +85,8 @@ public: */ [[nodiscard]] static eve::Result makeRequest( const WeaponDefinition& weapon, const AttackRequest& attack, - std::optional source = std::nullopt, - eve::SimulationTick requestedTick = eve::SimulationTick::zero()); - + std::optional source = std::nullopt, + eve::SimulationTick requestedTick = eve::SimulationTick::zero()); }; } // namespace eve::weapon diff --git a/src/modules/weapon/WeaponAttributes.cpp b/src/modules/weapon/WeaponAttributes.cpp index 6189f77a..3e86c79f 100644 --- a/src/modules/weapon/WeaponAttributes.cpp +++ b/src/modules/weapon/WeaponAttributes.cpp @@ -2,8 +2,8 @@ #include "weapon/WeaponTypes.h" -#include #include +#include #include #include #include @@ -14,8 +14,8 @@ namespace { template eve::Result failure(eve::DiagnosticCode code, std::string message, std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - code, std::move(message), std::move(path), {}, "weapon.attributes")); + return eve::Result::failure( + eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "weapon.attributes")); } template @@ -25,33 +25,29 @@ eve::Result failure(eve::Status status, std::string, std::string = {}) { template eve::Result failure(eve::StatusCode code, std::string message, std::string path = {}) { - return eve::Result::failure(eve::Status::failure( - code, eve::Diagnostic::error(eve::DiagnosticCode::Failed, std::move(message), - std::move(path), {}, "weapon.attributes"))); + return eve::Result::failure( + eve::Status::failure(code, eve::Diagnostic::error(eve::DiagnosticCode::Failed, std::move(message), + std::move(path), {}, "weapon.attributes"))); } -eve::Result failureVoid(eve::DiagnosticCode code, std::string message, - std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - code, std::move(message), std::move(path), {}, "weapon.attributes")); +eve::Result failureVoid(eve::DiagnosticCode code, std::string message, std::string path = {}) { + return eve::Result::failure( + eve::Diagnostic::error(code, std::move(message), std::move(path), {}, "weapon.attributes")); } eve::Result failureVoid(eve::Status status, std::string, std::string = {}) { return eve::Result::failure(std::move(status)); } -eve::Result failureVoid(eve::StatusCode code, std::string message, - std::string path = {}) { - return eve::Result::failure(eve::Status::failure( - code, eve::Diagnostic::error(eve::DiagnosticCode::Failed, std::move(message), - std::move(path), {}, "weapon.attributes"))); +eve::Result failureVoid(eve::StatusCode code, std::string message, std::string path = {}) { + return eve::Result::failure( + eve::Status::failure(code, eve::Diagnostic::error(eve::DiagnosticCode::Failed, std::move(message), + std::move(path), {}, "weapon.attributes"))); } bool selected(std::string_view name) noexcept { - return name == WeaponAttributeAdapter::manaAttribute || - name == WeaponAttributeAdapter::maxManaAttribute || - name == WeaponAttributeAdapter::staminaAttribute || - name == WeaponAttributeAdapter::maxStaminaAttribute; + return name == WeaponAttributeAdapter::manaAttribute || name == WeaponAttributeAdapter::maxManaAttribute || + name == WeaponAttributeAdapter::staminaAttribute || name == WeaponAttributeAdapter::maxStaminaAttribute; } std::array selectedAttributes() { @@ -59,9 +55,7 @@ std::array selectedAttributes() { WeaponAttributeAdapter::staminaAttribute, WeaponAttributeAdapter::maxStaminaAttribute}; } -bool isManaOrStamina(ResourceKind kind) noexcept { - return kind == ResourceKind::Mana || kind == ResourceKind::Stamina; -} +bool isManaOrStamina(ResourceKind kind) noexcept { return kind == ResourceKind::Mana || kind == ResourceKind::Stamina; } std::string_view currentAttribute(ResourceKind kind) noexcept { return kind == ResourceKind::Mana ? WeaponAttributeAdapter::manaAttribute @@ -77,14 +71,13 @@ std::string_view maxAttribute(ResourceKind kind) noexcept { eve::Result WeaponAttributeAdapter::ensure(WeaponEntity& weapon) { auto bound = weapon.attributes()->values.bindOwner(ecs::handle_of(&weapon)); - if (!bound) - return failureVoid(bound.code(), "weapon attribute owner could not be bound", "owner"); + if (!bound) return failureVoid(bound.code(), "weapon attribute owner could not be bound", "owner"); if (!weapon.attributes()->values.initialized()) { - const Resource& resource = weapon.state()->resource; - const double mana = resource.kind == ResourceKind::Mana ? resource.value : 0.0; - const double maxMana = resource.kind == ResourceKind::Mana ? resource.max : 0.0; - const double stamina = resource.kind == ResourceKind::Stamina ? resource.value : 0.0; - const double maxStamina = resource.kind == ResourceKind::Stamina ? resource.max : 0.0; + const Resource& resource = weapon.state()->resource; + const double mana = resource.kind == ResourceKind::Mana ? resource.value : 0.0; + const double maxMana = resource.kind == ResourceKind::Mana ? resource.max : 0.0; + const double stamina = resource.kind == ResourceKind::Stamina ? resource.value : 0.0; + const double maxStamina = resource.kind == ResourceKind::Stamina ? resource.max : 0.0; const std::array bases = { eve::attributes::AttributeSnapshotBase{std::string(manaAttribute), mana}, eve::attributes::AttributeSnapshotBase{std::string(maxManaAttribute), maxMana}, @@ -92,8 +85,7 @@ eve::Result WeaponAttributeAdapter::ensure(WeaponEntity& weapon) { eve::attributes::AttributeSnapshotBase{std::string(maxStaminaAttribute), maxStamina}}; auto initialized = weapon.attributes()->values.initialize(bases); if (!initialized) - return failureVoid(initialized.code(), "weapon attribute state could not be initialized", - "attributes"); + return failureVoid(initialized.code(), "weapon attribute state could not be initialized", "attributes"); } return eve::Result::success(eve::Status::success(eve::StatusCode::NoOp)); } @@ -102,42 +94,36 @@ eve::Result WeaponAttributeAdapter::project(WeaponEntity& weapon) { auto ready = ensure(weapon); if (!ready) return failureVoid(ready.code(), "weapon attributes are not available", "attributes"); Resource& resource = weapon.state()->resource; - if (!isManaOrStamina(resource.kind)) - return eve::Result::success(eve::Status::success(eve::StatusCode::NoOp)); + if (!isManaOrStamina(resource.kind)) return eve::Result::success(eve::Status::success(eve::StatusCode::NoOp)); auto current = weapon.attributes()->values.getFinal(currentAttribute(resource.kind)); - if (!current) - return failureVoid(current.code(), "weapon resource attribute could not be read", "resource"); + if (!current) return failureVoid(current.code(), "weapon resource attribute could not be read", "resource"); auto maximum = weapon.attributes()->values.getFinal(maxAttribute(resource.kind)); - if (!maximum) - return failureVoid(maximum.code(), "weapon maximum resource attribute could not be read", "resource"); + if (!maximum) return failureVoid(maximum.code(), "weapon maximum resource attribute could not be read", "resource"); if (!std::isfinite(current.value()) || !std::isfinite(maximum.value()) || current.value() < 0.0 || maximum.value() < 0.0 || current.value() > maximum.value() || maximum.value() > static_cast(std::numeric_limits::max())) return failureVoid(eve::DiagnosticCode::InvariantViolation, "weapon mana/stamina attributes are outside validated bounds", "resource"); resource.value = static_cast(current.value()); - resource.max = static_cast(maximum.value()); + resource.max = static_cast(maximum.value()); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result WeaponAttributeAdapter::read(WeaponEntity& weapon, - std::string_view attribute) { +eve::Result WeaponAttributeAdapter::read(WeaponEntity& weapon, std::string_view attribute) { if (!selected(attribute)) return failure(eve::DiagnosticCode::Unsupported, "weapon attribute is outside the mana/stamina projection", "attribute"); auto projected = project(weapon); - if (!projected) - return failure(projected.code(), "weapon compatibility projection failed", "resource"); + if (!projected) return failure(projected.code(), "weapon compatibility projection failed", "resource"); auto value = weapon.attributes()->values.getFinal(attribute); if (!value) return failure(value.code(), "weapon attribute read failed", "attribute"); return eve::Result::success(value.value()); } -eve::Result WeaponAttributeAdapter::setBase(WeaponEntity& weapon, std::string_view attribute, - double value) { +eve::Result WeaponAttributeAdapter::setBase(WeaponEntity& weapon, std::string_view attribute, double value) { if (!selected(attribute)) - return failureVoid(eve::DiagnosticCode::Unsupported, - "weapon attribute is outside the mana/stamina projection", "attribute"); + return failureVoid(eve::DiagnosticCode::Unsupported, "weapon attribute is outside the mana/stamina projection", + "attribute"); auto ready = ensure(weapon); if (!ready) return failureVoid(ready.code(), "weapon attributes are not available", "attributes"); auto changed = weapon.attributes()->values.setBase(attribute, value); @@ -150,14 +136,13 @@ eve::Result WeaponAttributeAdapter::consumeTriggerResource(WeaponEntity& w if (!ready) return failureVoid(ready.code(), "weapon attributes are not available", "attributes"); const Resource& resource = weapon.state()->resource; if (!isManaOrStamina(resource.kind)) - return failureVoid(eve::DiagnosticCode::Unsupported, - "weapon trigger resource is not mana or stamina", "resource.kind"); + return failureVoid(eve::DiagnosticCode::Unsupported, "weapon trigger resource is not mana or stamina", + "resource.kind"); const auto currentName = currentAttribute(resource.kind); - auto current = weapon.attributes()->values.getFinal(currentName); + auto current = weapon.attributes()->values.getFinal(currentName); if (!current) return failureVoid(current.code(), "weapon trigger resource could not be read", "resource"); if (!resource.infinite && current.value() < static_cast(resource.cost)) - return failureVoid(eve::DiagnosticCode::Conflict, - "weapon does not have enough mana or stamina", "resource"); + return failureVoid(eve::DiagnosticCode::Conflict, "weapon does not have enough mana or stamina", "resource"); if (resource.infinite) return eve::Result::success(eve::Status::success(eve::StatusCode::NoOp)); auto changed = weapon.attributes()->values.setBase( currentName, std::max(0.0, current.value() - static_cast(resource.cost))); @@ -167,29 +152,25 @@ eve::Result WeaponAttributeAdapter::consumeTriggerResource(WeaponEntity& w eve::Result WeaponAttributeAdapter::addModifier( WeaponEntity& weapon, std::string id, std::string_view attribute, std::string source, - eve::attributes::AttributeOperation operation, double value, - eve::attributes::ModifierPriority priority) { + eve::attributes::AttributeOperation operation, double value, eve::attributes::ModifierPriority priority) { if (!selected(attribute) || source.empty()) return failure( - eve::DiagnosticCode::InvalidArgument, - "weapon modifier requires a selected attribute and non-empty source", "modifier"); + eve::DiagnosticCode::InvalidArgument, "weapon modifier requires a selected attribute and non-empty source", + "modifier"); auto ready = ensure(weapon); if (!ready) - return failure(ready.code(), "weapon attributes are not available", - "attributes"); - auto added = weapon.attributes()->values.addModifier( - eve::attributes::AttributeModifier(std::move(id), std::string(attribute), std::move(source), - operation, value, priority)); + return failure(ready.code(), "weapon attributes are not available", "attributes"); + auto added = weapon.attributes()->values.addModifier(eve::attributes::AttributeModifier( + std::move(id), std::string(attribute), std::move(source), operation, value, priority)); if (!added) return added; auto projected = project(weapon); if (!projected) - return failure(projected.code(), - "weapon compatibility projection failed", "resource"); + return failure(projected.code(), "weapon compatibility projection failed", + "resource"); return added; } -eve::Result WeaponAttributeAdapter::snapshot( - WeaponEntity& weapon) { +eve::Result WeaponAttributeAdapter::snapshot(WeaponEntity& weapon) { auto ready = ensure(weapon); if (!ready) return failure( @@ -198,9 +179,9 @@ eve::Result WeaponAttributeAdapter return weapon.attributes()->values.snapshot(names); } -eve::Result WeaponAttributeAdapter::restore( - WeaponEntity& weapon, const eve::attributes::AttributeProjectionSnapshot& snapshotValue, - eve::Revision expectedRevision) { +eve::Result WeaponAttributeAdapter::restore(WeaponEntity& weapon, + const eve::attributes::AttributeProjectionSnapshot& snapshotValue, + eve::Revision expectedRevision) { auto ready = ensure(weapon); if (!ready) return failureVoid(ready.code(), "weapon attributes are not available", "attributes"); auto restored = weapon.attributes()->values.restore(snapshotValue, expectedRevision); diff --git a/src/modules/weapon/WeaponAttributes.h b/src/modules/weapon/WeaponAttributes.h index b9ebf3c2..3ac8d93c 100644 --- a/src/modules/weapon/WeaponAttributes.h +++ b/src/modules/weapon/WeaponAttributes.h @@ -23,42 +23,39 @@ class WeaponEntity; */ class WeaponAttributeAdapter final { public: - static constexpr std::string_view manaAttribute = "mana"; - static constexpr std::string_view maxManaAttribute = "max_mana"; - static constexpr std::string_view staminaAttribute = "stamina"; + static constexpr std::string_view manaAttribute = "mana"; + static constexpr std::string_view maxManaAttribute = "max_mana"; + static constexpr std::string_view staminaAttribute = "stamina"; static constexpr std::string_view maxStaminaAttribute = "max_stamina"; /** @brief Bind and seed the selected resource attributes exactly once. */ [[nodiscard]] static eve::Result ensure(WeaponEntity& weapon); /** @brief Read a selected final mana/stamina attribute. */ - [[nodiscard]] static eve::Result read(WeaponEntity& weapon, - std::string_view attribute); + [[nodiscard]] static eve::Result read(WeaponEntity& weapon, std::string_view attribute); /** @brief Set a selected canonical base and refresh Resource compatibility fields. */ - [[nodiscard]] static eve::Result setBase(WeaponEntity& weapon, - std::string_view attribute, double value); + [[nodiscard]] static eve::Result setBase(WeaponEntity& weapon, std::string_view attribute, double value); /** @brief Consume the current weapon's canonical Mana/Stamina trigger cost. */ [[nodiscard]] static eve::Result consumeTriggerResource(WeaponEntity& weapon); /** @brief Add a modifier using the shared operation/priority/sequence contract. */ [[nodiscard]] static eve::Result addModifier( - WeaponEntity& weapon, std::string id, std::string_view attribute, - std::string source, eve::attributes::AttributeOperation operation, double value, + WeaponEntity& weapon, std::string id, std::string_view attribute, std::string source, + eve::attributes::AttributeOperation operation, double value, eve::attributes::ModifierPriority priority = eve::attributes::priority::runtime); /** @brief Project canonical Mana/Stamina values into the legacy Resource component. */ [[nodiscard]] static eve::Result project(WeaponEntity& weapon); /** @brief Capture selected resource attributes with owner generation and revision. */ - [[nodiscard]] static eve::Result snapshot( - WeaponEntity& weapon); + [[nodiscard]] static eve::Result snapshot(WeaponEntity& weapon); /** @brief Restore selected resource attributes after owner/revision checks. */ - [[nodiscard]] static eve::Result restore( - WeaponEntity& weapon, const eve::attributes::AttributeProjectionSnapshot& snapshot, - eve::Revision expectedRevision); + [[nodiscard]] static eve::Result restore(WeaponEntity& weapon, + const eve::attributes::AttributeProjectionSnapshot& snapshot, + eve::Revision expectedRevision); }; } // namespace eve::weapon diff --git a/src/modules/weapon/WeaponDefinitionRuntime.cpp b/src/modules/weapon/WeaponDefinitionRuntime.cpp index 894d2d02..7006c8f1 100644 --- a/src/modules/weapon/WeaponDefinitionRuntime.cpp +++ b/src/modules/weapon/WeaponDefinitionRuntime.cpp @@ -17,9 +17,8 @@ namespace { template eve::Result invalid(std::string message, std::string path = {}) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, std::move(message), std::move(path), {}, - "weapon.definition_runtime")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, std::move(message), + std::move(path), {}, "weapon.definition_runtime")); } const eve::Value* field(const eve::Value::Object& object, std::string_view name) { @@ -55,8 +54,7 @@ bool readInteger(const eve::Value::Object& object, std::string_view name, int& o const auto* value = field(object, name); if (value == nullptr) return true; const auto* integer = value->getIf(); - if (integer == nullptr || *integer < std::numeric_limits::min() || - *integer > std::numeric_limits::max()) + if (integer == nullptr || *integer < std::numeric_limits::min() || *integer > std::numeric_limits::max()) return false; output = static_cast(*integer); return true; @@ -73,11 +71,11 @@ bool readBoolean(const eve::Value::Object& object, std::string_view name, bool& const char* resourceKindName(ResourceKind kind) noexcept { switch (kind) { - case ResourceKind::None: return "none"; - case ResourceKind::Ammo: return "ammo"; - case ResourceKind::Mana: return "mana"; - case ResourceKind::Charges: return "charges"; - case ResourceKind::Stamina: return "stamina"; + case ResourceKind::None: return "none"; + case ResourceKind::Ammo: return "ammo"; + case ResourceKind::Mana: return "mana"; + case ResourceKind::Charges: return "charges"; + case ResourceKind::Stamina: return "stamina"; } return "none"; } @@ -98,41 +96,38 @@ AttackStage attackStageFromName(std::string_view name) noexcept { } eve::Result parseDefinition(const eve::definitions::Definition& source, - const eve::DefinitionRef& reference) { + const eve::DefinitionRef& reference) { auto parsed = eve::Value::fromJson(source.json); if (!parsed) return eve::Result::failure(parsed.status()); - auto value = std::move(parsed).takeValue(); + auto value = std::move(parsed).takeValue(); const auto* object = value.getIf(); if (object == nullptr) return invalid("weapon definition must be an object", "json"); WeaponDefinition result; - result.id = std::string(reference.id().name()); + result.id = std::string(reference.id().name()); std::string id = result.id; if (!readText(*object, "id", id) || id != result.id) return invalid("weapon definition id does not match its logical reference", "id"); - std::string kind = weaponKindName(result.kind); + std::string kind = weaponKindName(result.kind); std::string logic = result.logic; if (!readText(*object, "kind", kind) || !readText(*object, "logic", logic)) return invalid("weapon kind and logic must be strings", "definition"); - result.kind = weaponKindFromName(kind); + result.kind = weaponKindFromName(kind); result.logic = std::move(logic); - if (!readNumber(*object, "damage", result.damage) || - !readNumber(*object, "penetration", result.penetration) || + if (!readNumber(*object, "damage", result.damage) || !readNumber(*object, "penetration", result.penetration) || !readNumber(*object, "range", result.range) || !readNumber(*object, "spread", result.spread) || - !readNumber(*object, "spreadMin", result.spreadMin) || - !readNumber(*object, "spreadMax", result.spreadMax) || + !readNumber(*object, "spreadMin", result.spreadMin) || !readNumber(*object, "spreadMax", result.spreadMax) || !readNumber(*object, "spreadPerShot", result.spreadPerShot) || !readNumber(*object, "spreadRecover", result.spreadRecover) || !readNumber(*object, "recoilPitch", result.recoilPitch) || !readNumber(*object, "recoilYaw", result.recoilYaw) || !readNumber(*object, "recoilRecover", result.recoilRecover) || - !readNumber(*object, "zoomFov", result.zoomFov) || - !readNumber(*object, "cooldown", result.cooldown) || !readNumber(*object, "arc", result.arc)) + !readNumber(*object, "zoomFov", result.zoomFov) || !readNumber(*object, "cooldown", result.cooldown) || + !readNumber(*object, "arc", result.arc)) return invalid("weapon numeric fields must be finite numbers", "definition"); - if (!readText(*object, "damageType", result.damageType) || - !readText(*object, "element", result.element)) + if (!readText(*object, "damageType", result.damageType) || !readText(*object, "element", result.element)) return invalid("weapon damageType and element must be strings", "definition"); if (const auto* modes = field(*object, "fireModes")) { @@ -153,8 +148,7 @@ eve::Result parseDefinition(const eve::definitions::Definition const auto* nested = resource->getIf(); if (nested == nullptr) return invalid("resource must be an object", "resource"); std::string resourceName = resourceKindName(result.resource.kind); - if (!readText(*nested, "kind", resourceName) || - !readNumber(*nested, "max", result.resource.max) || + if (!readText(*nested, "kind", resourceName) || !readNumber(*nested, "max", result.resource.max) || !readNumber(*nested, "regen", result.resource.regen) || !readNumber(*nested, "cost", result.resource.cost) || !readBoolean(*nested, "infinite", result.resource.infinite)) @@ -177,8 +171,7 @@ eve::Result parseDefinition(const eve::definitions::Definition if (const auto* ammo = field(*object, "ammo")) { const auto* nested = ammo->getIf(); if (nested == nullptr || !readInteger(*nested, "mag", result.magSize) || - !readInteger(*nested, "reserve", result.reserveSize) || - !readNumber(*nested, "reload", result.reloadTime)) + !readInteger(*nested, "reserve", result.reserveSize) || !readNumber(*nested, "reload", result.reloadTime)) return invalid("ammo fields are invalid", "ammo"); } if (const auto* projectile = field(*object, "projectile")) { @@ -201,15 +194,15 @@ eve::Result parseDefinition(const eve::definitions::Definition return eve::Result::success(std::move(result)); } -eve::Result currentHandle( - eve::definitions::DefinitionRegistry& registry, const eve::DefinitionRef& reference) { +eve::Result currentHandle(eve::definitions::DefinitionRegistry& registry, + const eve::DefinitionRef& reference) { const auto& logical = reference.id(); return registry.handle(std::string(logical.namespaceName()), std::string(logical.name())); } -eve::Result definitionFor( - eve::definitions::DefinitionRegistry& registry, const eve::definition::DefinitionHandle& handle, - const eve::DefinitionRef& reference) { +eve::Result definitionFor(eve::definitions::DefinitionRegistry& registry, + const eve::definition::DefinitionHandle& handle, + const eve::DefinitionRef& reference) { auto resolved = registry.resolveHandle(handle); if (!resolved) return eve::Result::failure(resolved.status()); return parseDefinition(resolved.value().get(), reference); @@ -226,19 +219,19 @@ eve::LogicalId runtimeSchema() { WeaponRuntimeState initialState(const WeaponDefinition& definition) { WeaponRuntimeState result; - result.selector = definition.fireMode; - result.currentSpread = definition.spreadMin; - result.resource.kind = definition.resource.kind; - result.resource.max = definition.resource.max; - result.resource.regen = definition.resource.regen; - result.resource.cost = definition.resource.cost; + result.selector = definition.fireMode; + result.currentSpread = definition.spreadMin; + result.resource.kind = definition.resource.kind; + result.resource.max = definition.resource.max; + result.resource.regen = definition.resource.regen; + result.resource.cost = definition.resource.cost; result.resource.infinite = definition.resource.infinite; if (definition.kind == WeaponKind::Ranged) { - result.resource.kind = ResourceKind::Ammo; - result.resource.max = static_cast(definition.magSize); - result.resource.value = static_cast(definition.magSize); - result.resource.cost = 1.f; - result.resource.reserve = definition.reserveSize < 0 ? 0 : definition.reserveSize; + result.resource.kind = ResourceKind::Ammo; + result.resource.max = static_cast(definition.magSize); + result.resource.value = static_cast(definition.magSize); + result.resource.cost = 1.f; + result.resource.reserve = definition.reserveSize < 0 ? 0 : definition.reserveSize; result.resource.infinite = definition.reserveSize < 0; } else { result.resource.value = result.resource.infinite ? 0.f : result.resource.max; @@ -247,21 +240,31 @@ WeaponRuntimeState initialState(const WeaponDefinition& definition) { } eve::Value encodeState(const WeaponRuntimeState& state) { - const Resource& resource = state.resource; + const Resource& resource = state.resource; eve::Value::Object resourceObject{ - {"cost", eve::Value(resource.cost)}, {"infinite", eve::Value(resource.infinite)}, - {"kind", eve::Value(resourceKindName(resource.kind))}, {"max", eve::Value(resource.max)}, - {"regen", eve::Value(resource.regen)}, {"reloadProgress", eve::Value(resource.reloadProgress)}, - {"reloading", eve::Value(resource.reloading)}, {"reserve", eve::Value(resource.reserve)}, + {"cost", eve::Value(resource.cost)}, + {"infinite", eve::Value(resource.infinite)}, + {"kind", eve::Value(resourceKindName(resource.kind))}, + {"max", eve::Value(resource.max)}, + {"regen", eve::Value(resource.regen)}, + {"reloadProgress", eve::Value(resource.reloadProgress)}, + {"reloading", eve::Value(resource.reloading)}, + {"reserve", eve::Value(resource.reserve)}, {"value", eve::Value(resource.value)}, }; return eve::Value(eve::Value::Object{ - {"aiming", eve::Value(state.aiming)}, {"burstRemaining", eve::Value(state.burstRemaining)}, - {"burstTimer", eve::Value(state.burstTimer)}, {"cooldown", eve::Value(state.cooldown)}, - {"currentSpread", eve::Value(state.currentSpread)}, {"jammed", eve::Value(state.jammed)}, - {"recoilPitch", eve::Value(state.recoilPitch)}, {"recoilYaw", eve::Value(state.recoilYaw)}, - {"resource", eve::Value(std::move(resourceObject))}, {"selector", eve::Value(fireModeName(state.selector))}, - {"stage", eve::Value(attackStageName(state.stage))}, {"stageTimer", eve::Value(state.stageTimer)}, + {"aiming", eve::Value(state.aiming)}, + {"burstRemaining", eve::Value(state.burstRemaining)}, + {"burstTimer", eve::Value(state.burstTimer)}, + {"cooldown", eve::Value(state.cooldown)}, + {"currentSpread", eve::Value(state.currentSpread)}, + {"jammed", eve::Value(state.jammed)}, + {"recoilPitch", eve::Value(state.recoilPitch)}, + {"recoilYaw", eve::Value(state.recoilYaw)}, + {"resource", eve::Value(std::move(resourceObject))}, + {"selector", eve::Value(fireModeName(state.selector))}, + {"stage", eve::Value(attackStageName(state.stage))}, + {"stageTimer", eve::Value(state.stageTimer)}, }); } @@ -272,35 +275,44 @@ bool readStateNumber(const eve::Value::Object& object, const char* name, float& eve::Result decodeState(const eve::Value& value) { const auto* object = value.getIf(); if (object == nullptr) return invalid("weapon runtime state must be an object", "state"); - static const std::set fields = {"aiming", "burstRemaining", "burstTimer", "cooldown", - "currentSpread", "jammed", "recoilPitch", "recoilYaw", - "resource", "selector", "stage", "stageTimer"}; + static const std::set fields = {"aiming", "burstRemaining", "burstTimer", "cooldown", + "currentSpread", "jammed", "recoilPitch", "recoilYaw", + "resource", "selector", "stage", "stageTimer"}; for (const auto& [name, unused] : *object) { (void)unused; - if (!fields.contains(name)) return invalid("unknown weapon runtime state field", "state." + name); + if (!fields.contains(name)) + return invalid("unknown weapon runtime state field", "state." + name); } for (const auto& name : fields) - if (!object->contains(name)) return invalid("weapon runtime state is missing a field", "state." + name); + if (!object->contains(name)) + return invalid("weapon runtime state is missing a field", "state." + name); WeaponRuntimeState result; - std::string selector; - std::string stage; - if (!readBoolean(*object, "aiming", result.aiming) || !readInteger(*object, "burstRemaining", result.burstRemaining) || - !readStateNumber(*object, "burstTimer", result.burstTimer) || !readStateNumber(*object, "cooldown", result.cooldown) || - !readStateNumber(*object, "currentSpread", result.currentSpread) || !readBoolean(*object, "jammed", result.jammed) || - !readStateNumber(*object, "recoilPitch", result.recoilPitch) || !readStateNumber(*object, "recoilYaw", result.recoilYaw) || - !readText(*object, "selector", selector) || !readText(*object, "stage", stage) || - !readStateNumber(*object, "stageTimer", result.stageTimer)) + std::string selector; + std::string stage; + if (!readBoolean(*object, "aiming", result.aiming) || + !readInteger(*object, "burstRemaining", result.burstRemaining) || + !readStateNumber(*object, "burstTimer", result.burstTimer) || + !readStateNumber(*object, "cooldown", result.cooldown) || + !readStateNumber(*object, "currentSpread", result.currentSpread) || + !readBoolean(*object, "jammed", result.jammed) || + !readStateNumber(*object, "recoilPitch", result.recoilPitch) || + !readStateNumber(*object, "recoilYaw", result.recoilYaw) || !readText(*object, "selector", selector) || + !readText(*object, "stage", stage) || !readStateNumber(*object, "stageTimer", result.stageTimer)) return invalid("weapon runtime scalar field is invalid", "state"); - result.selector = fireModeFromName(selector); - result.stage = attackStageFromName(stage); + result.selector = fireModeFromName(selector); + result.stage = attackStageFromName(stage); const auto* resourceValue = object->at("resource").getIf(); - if (resourceValue == nullptr) return invalid("weapon runtime resource is invalid", "state.resource"); + if (resourceValue == nullptr) + return invalid("weapon runtime resource is invalid", "state.resource"); std::string kind; if (!readText(*resourceValue, "kind", kind) || !readStateNumber(*resourceValue, "value", result.resource.value) || - !readStateNumber(*resourceValue, "max", result.resource.max) || !readStateNumber(*resourceValue, "regen", result.resource.regen) || - !readStateNumber(*resourceValue, "cost", result.resource.cost) || !readBoolean(*resourceValue, "infinite", result.resource.infinite) || - !readInteger(*resourceValue, "reserve", result.resource.reserve) || !readBoolean(*resourceValue, "reloading", result.resource.reloading) || + !readStateNumber(*resourceValue, "max", result.resource.max) || + !readStateNumber(*resourceValue, "regen", result.resource.regen) || + !readStateNumber(*resourceValue, "cost", result.resource.cost) || + !readBoolean(*resourceValue, "infinite", result.resource.infinite) || + !readInteger(*resourceValue, "reserve", result.resource.reserve) || + !readBoolean(*resourceValue, "reloading", result.resource.reloading) || !readStateNumber(*resourceValue, "reloadProgress", result.resource.reloadProgress)) return invalid("weapon runtime resource fields are invalid", "state.resource"); result.resource.kind = resourceKindFromName(kind); @@ -309,20 +321,19 @@ eve::Result decodeState(const eve::Value& value) { } // namespace -eve::Result parseWeaponDefinition( - const eve::definitions::Definition& source) { +eve::Result parseWeaponDefinition(const eve::definitions::Definition& source) { auto logical = eve::LogicalId::fromParts("weapon", source.id); if (!logical) return invalid("weapon definition id is invalid", "id"); auto reference = eve::DefinitionRef::fromId(*logical); if (!reference) return eve::Result::failure(reference.status()); - if (source.type != "weapon") - return invalid("definition type must be weapon", "type"); + if (source.type != "weapon") return invalid("definition type must be weapon", "type"); return parseDefinition(source, reference.value()); } -eve::Result WeaponDefinitionRuntime::create( - eve::definitions::DefinitionRegistry& registry, eve::DefinitionRef definition, - eve::PersistentId instanceId, eve::definition::ReloadPolicy policy) { +eve::Result WeaponDefinitionRuntime::create(eve::definitions::DefinitionRegistry& registry, + eve::DefinitionRef definition, + eve::PersistentId instanceId, + eve::definition::ReloadPolicy policy) { if (!definition.id().isValid() || definition.id().namespaceName() != "weapon") return invalid("weapon definition reference must use weapon namespace", "definition"); auto handle = currentHandle(registry, definition); @@ -336,9 +347,10 @@ eve::Result WeaponDefinitionRuntime::create( WeaponDefinitionRuntime(registry, std::move(runtime).takeValue(), policy)); } -eve::Result WeaponDefinitionRuntime::create( - eve::definitions::DefinitionRegistry& registry, std::string_view definitionId, - eve::PersistentId instanceId, eve::definition::ReloadPolicy policy) { +eve::Result WeaponDefinitionRuntime::create(eve::definitions::DefinitionRegistry& registry, + std::string_view definitionId, + eve::PersistentId instanceId, + eve::definition::ReloadPolicy policy) { auto logical = eve::LogicalId::fromParts("weapon", definitionId); if (!logical) return invalid("weapon id is not a valid logical name", "definitionId"); auto reference = eve::DefinitionRef::fromId(*logical); @@ -346,9 +358,9 @@ eve::Result WeaponDefinitionRuntime::create( return create(registry, std::move(reference).takeValue(), instanceId, policy); } -eve::Result WeaponDefinitionRuntime::create( - Weapon& module, std::string_view definitionId, eve::PersistentId instanceId, - eve::definition::ReloadPolicy policy) { +eve::Result WeaponDefinitionRuntime::create(Weapon& module, std::string_view definitionId, + eve::PersistentId instanceId, + eve::definition::ReloadPolicy policy) { return create(module.definitionRegistry(), definitionId, instanceId, policy); } @@ -375,67 +387,66 @@ bool WeaponDefinitionRuntime::isActive() const noexcept { return runtime_.isActi eve::Result WeaponDefinitionRuntime::applyTo(WeaponEntity* entity) const { if (entity == nullptr) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "weapon runtime cannot project to a null WeaponEntity", "entity", {}, - "weapon.definition_runtime")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "weapon runtime cannot project to a null WeaponEntity", + "entity", {}, "weapon.definition_runtime")); auto typed = definition(); if (!typed) return eve::Result::failure(typed.status()); try { - auto definitionOwner = std::make_shared(typed.value()); - auto definitionComponent = *entity->definition(); - auto binding = *entity->definitionBinding(); - auto targetState = *entity->state(); - definitionComponent.owned = std::move(definitionOwner); - definitionComponent.def = definitionComponent.owned.get(); - binding.identity = identity(); - binding.reloadPolicy = policy_; - binding.active = isActive(); - const auto& source = state(); - targetState.resource = source.resource; - targetState.cooldown = source.cooldown; + auto definitionOwner = std::make_shared(typed.value()); + auto definitionComponent = *entity->definition(); + auto binding = *entity->definitionBinding(); + auto targetState = *entity->state(); + definitionComponent.owned = std::move(definitionOwner); + definitionComponent.def = definitionComponent.owned.get(); + binding.identity = identity(); + binding.reloadPolicy = policy_; + binding.active = isActive(); + const auto& source = state(); + targetState.resource = source.resource; + targetState.cooldown = source.cooldown; targetState.burstRemaining = source.burstRemaining; - targetState.burstTimer = source.burstTimer; - targetState.jammed = source.jammed; - targetState.stage = source.stage; - targetState.stageTimer = source.stageTimer; - targetState.stages = &definitionComponent.def->stages; - targetState.currentSpread = source.currentSpread; - targetState.recoilPitch = source.recoilPitch; - targetState.recoilYaw = source.recoilYaw; - targetState.selector = source.selector; - targetState.aiming = source.aiming; + targetState.burstTimer = source.burstTimer; + targetState.jammed = source.jammed; + targetState.stage = source.stage; + targetState.stageTimer = source.stageTimer; + targetState.stages = &definitionComponent.def->stages; + targetState.currentSpread = source.currentSpread; + targetState.recoilPitch = source.recoilPitch; + targetState.recoilYaw = source.recoilYaw; + targetState.selector = source.selector; + targetState.aiming = source.aiming; using std::swap; swap(*entity->definition(), definitionComponent); swap(*entity->definitionBinding(), binding); swap(*entity->state(), targetState); return eve::Result::success(); } catch (const std::exception&) { - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "weapon runtime projection allocation failed", "entity", {}, - "weapon.definition_runtime")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::Failed, + "weapon runtime projection allocation failed", + "entity", {}, "weapon.definition_runtime")); } } -eve::Result WeaponDefinitionRuntime::reload( - eve::definition::ReloadPolicy policy) { - if (registry_ == nullptr) return invalid("weapon definition registry is not bound", "registry"); +eve::Result WeaponDefinitionRuntime::reload(eve::definition::ReloadPolicy policy) { + if (registry_ == nullptr) + return invalid("weapon definition registry is not bound", "registry"); auto next = currentHandle(*registry_, identity().definition); if (!next) return eve::Result::failure(next.status()); auto typed = definitionFor(*registry_, next.value(), identity().definition); if (!typed) return eve::Result::failure(typed.status()); - const WeaponRuntimeState defaults = initialState(typed.value()); - const float maxSpread = std::max(defaults.currentSpread, typed.value().spreadMax); - auto rebuild = [defaults, maxSpread](const WeaponRuntimeState& oldState, - const eve::definition::InstanceIdentity&, + const WeaponRuntimeState defaults = initialState(typed.value()); + const float maxSpread = std::max(defaults.currentSpread, typed.value().spreadMax); + auto rebuild = [defaults, maxSpread](const WeaponRuntimeState& oldState, const eve::definition::InstanceIdentity&, const eve::definition::DefinitionHandle&) { WeaponRuntimeState rebuilt = oldState; if (oldState.resource.kind != defaults.resource.kind) { rebuilt.resource = defaults.resource; } else { - rebuilt.resource.kind = defaults.resource.kind; - rebuilt.resource.max = defaults.resource.max; - rebuilt.resource.regen = defaults.resource.regen; - rebuilt.resource.cost = defaults.resource.cost; + rebuilt.resource.kind = defaults.resource.kind; + rebuilt.resource.max = defaults.resource.max; + rebuilt.resource.regen = defaults.resource.regen; + rebuilt.resource.cost = defaults.resource.cost; rebuilt.resource.infinite = defaults.resource.infinite; if (rebuilt.resource.infinite) { rebuilt.resource.value = 0.f; @@ -444,7 +455,7 @@ eve::Result WeaponDefinitionRuntime::reload( } rebuilt.resource.reserve = defaults.resource.reserve < 0 ? 0 : rebuilt.resource.reserve; } - rebuilt.selector = defaults.selector; + rebuilt.selector = defaults.selector; rebuilt.currentSpread = std::clamp(oldState.currentSpread, 0.f, maxSpread); return eve::Result::success(std::move(rebuilt)); }; @@ -454,38 +465,34 @@ eve::Result WeaponDefinitionRuntime::reload( } eve::Result WeaponDefinitionRuntime::snapshot( - eve::Revision revision, eve::SimulationTick tick, - const eve::SnapshotHashProvider& hashProvider) const { - const eve::definition::RuntimeStateEncoder encoder = - [](const WeaponRuntimeState& value) { return eve::Result::success(encodeState(value)); }; - return eve::definition::snapshotRuntimeInstance( - runtime_, "weapon.runtime", runtimeSchema(), eve::SchemaVersion(1), revision, tick, - hashProvider, encoder); + eve::Revision revision, eve::SimulationTick tick, const eve::SnapshotHashProvider& hashProvider) const { + const eve::definition::RuntimeStateEncoder encoder = [](const WeaponRuntimeState& value) { + return eve::Result::success(encodeState(value)); + }; + return eve::definition::snapshotRuntimeInstance(runtime_, "weapon.runtime", runtimeSchema(), eve::SchemaVersion(1), + revision, tick, hashProvider, encoder); } -eve::Result WeaponDefinitionRuntime::snapshotJson( - eve::Revision revision, eve::SimulationTick tick, - const eve::SnapshotHashProvider& hashProvider) const { +eve::Result WeaponDefinitionRuntime::snapshotJson(eve::Revision revision, eve::SimulationTick tick, + const eve::SnapshotHashProvider& hashProvider) const { auto result = snapshot(revision, tick, hashProvider); if (!result) return eve::Result::failure(result.status()); return std::move(result).andThen( [](eve::SnapshotEnvelope&& value) { return eve::serializeSnapshotEnvelope(value); }); } -eve::Result WeaponDefinitionRuntime::restore( - const eve::SnapshotEnvelope& snapshotValue, const eve::SnapshotHashProvider& hashProvider) { +eve::Result WeaponDefinitionRuntime::restore(const eve::SnapshotEnvelope& snapshotValue, + const eve::SnapshotHashProvider& hashProvider) { if (registry_ == nullptr) return invalid("weapon definition registry is not bound", "registry"); auto current = currentHandle(*registry_, identity().definition); if (!current) return eve::Result::failure(current.status()); if (current.value() != definitionHandle()) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::StaleHandle, - "weapon snapshot cannot restore against a replaced definition", "definitionGeneration", {}, - "weapon.definition_runtime")); + eve::DiagnosticCode::StaleHandle, "weapon snapshot cannot restore against a replaced definition", + "definitionGeneration", {}, "weapon.definition_runtime")); const eve::definition::RuntimeStateDecoder decoder = decodeState; - return eve::definition::restoreRuntimeInstance( - runtime_, snapshotValue, "weapon.runtime", runtimeSchema(), eve::SchemaVersion(1), - hashProvider, decoder); + return eve::definition::restoreRuntimeInstance(runtime_, snapshotValue, "weapon.runtime", runtimeSchema(), + eve::SchemaVersion(1), hashProvider, decoder); } } // namespace eve::weapon diff --git a/src/modules/weapon/WeaponDefinitionRuntime.h b/src/modules/weapon/WeaponDefinitionRuntime.h index f8247220..fc8bc8c5 100644 --- a/src/modules/weapon/WeaponDefinitionRuntime.h +++ b/src/modules/weapon/WeaponDefinitionRuntime.h @@ -29,8 +29,7 @@ class Weapon; * @return A value-owned projection for one consumer, or a structured parse failure. * @remarks The returned value is independent of the registry and does not form a second store. */ -[[nodiscard]] eve::Result parseWeaponDefinition( - const eve::definitions::Definition& source); +[[nodiscard]] eve::Result parseWeaponDefinition(const eve::definitions::Definition& source); /** * @brief Mutable state owned by one typed weapon runtime instance. @@ -41,18 +40,18 @@ class Weapon; * is the snapshot contract. */ struct WeaponRuntimeState { - Resource resource; - float cooldown = 0.f; - int burstRemaining = 0; - float burstTimer = 0.f; - bool jammed = false; - AttackStage stage = AttackStage::Idle; - float stageTimer = 0.f; - float currentSpread = 0.f; - float recoilPitch = 0.f; - float recoilYaw = 0.f; - FireMode selector = FireMode::Single; - bool aiming = false; + Resource resource; + float cooldown = 0.f; + int burstRemaining = 0; + float burstTimer = 0.f; + bool jammed = false; + AttackStage stage = AttackStage::Idle; + float stageTimer = 0.f; + float currentSpread = 0.f; + float recoilPitch = 0.f; + float recoilYaw = 0.f; + FireMode selector = FireMode::Single; + bool aiming = false; }; /** @@ -75,14 +74,12 @@ public: * @return A typed adapter or a structured resolution/parse failure. */ [[nodiscard]] static eve::Result create( - eve::definitions::DefinitionRegistry& registry, eve::DefinitionRef definition, - eve::PersistentId instanceId, + eve::definitions::DefinitionRegistry& registry, eve::DefinitionRef definition, eve::PersistentId instanceId, eve::definition::ReloadPolicy policy = eve::definition::ReloadPolicy::RebuildInstance); /** @brief Create from a `weapon:` logical name. */ [[nodiscard]] static eve::Result create( - eve::definitions::DefinitionRegistry& registry, std::string_view definitionId, - eve::PersistentId instanceId, + eve::definitions::DefinitionRegistry& registry, std::string_view definitionId, eve::PersistentId instanceId, eve::definition::ReloadPolicy policy = eve::definition::ReloadPolicy::RebuildInstance); /** @brief Create through the existing Weapon module facade. */ @@ -90,11 +87,11 @@ public: Weapon& module, std::string_view definitionId, eve::PersistentId instanceId, eve::definition::ReloadPolicy policy = eve::definition::ReloadPolicy::RebuildInstance); - WeaponDefinitionRuntime(WeaponDefinitionRuntime&&) noexcept = default; + WeaponDefinitionRuntime(WeaponDefinitionRuntime&&) noexcept = default; WeaponDefinitionRuntime& operator=(WeaponDefinitionRuntime&&) noexcept = default; - WeaponDefinitionRuntime(const WeaponDefinitionRuntime&) = delete; - WeaponDefinitionRuntime& operator=(const WeaponDefinitionRuntime&) = delete; - ~WeaponDefinitionRuntime() = default; + WeaponDefinitionRuntime(const WeaponDefinitionRuntime&) = delete; + WeaponDefinitionRuntime& operator=(const WeaponDefinitionRuntime&) = delete; + ~WeaponDefinitionRuntime() = default; /** @brief Borrow the common instance identity and exact definition generation. */ [[nodiscard]] const eve::definition::InstanceIdentity& identity() const noexcept; @@ -125,33 +122,30 @@ public: [[nodiscard]] eve::Result applyTo(WeaponEntity* entity) const; /** @brief Reload the registry's current generation using a typed policy. */ - [[nodiscard]] eve::Result reload( - eve::definition::ReloadPolicy policy); + [[nodiscard]] eve::Result reload(eve::definition::ReloadPolicy policy); /** @brief Capture stable typed runtime state in the common SnapshotEnvelope. */ - [[nodiscard]] eve::Result snapshot( - eve::Revision revision, eve::SimulationTick tick, - const eve::SnapshotHashProvider& hashProvider) const; + [[nodiscard]] eve::Result snapshot(eve::Revision revision, eve::SimulationTick tick, + const eve::SnapshotHashProvider& hashProvider) const; /** @brief Serialize snapshot() as canonical deterministic JSON. */ - [[nodiscard]] eve::Result snapshotJson( - eve::Revision revision, eve::SimulationTick tick, - const eve::SnapshotHashProvider& hashProvider) const; + [[nodiscard]] eve::Result snapshotJson(eve::Revision revision, eve::SimulationTick tick, + const eve::SnapshotHashProvider& hashProvider) const; /** * @brief Verify and restore a same-generation snapshot transactionally. * @return Failure leaves all typed runtime state unchanged. */ - [[nodiscard]] eve::Result restore( - const eve::SnapshotEnvelope& snapshot, const eve::SnapshotHashProvider& hashProvider); + [[nodiscard]] eve::Result restore(const eve::SnapshotEnvelope& snapshot, + const eve::SnapshotHashProvider& hashProvider); private: - WeaponDefinitionRuntime(eve::definitions::DefinitionRegistry& registry, + WeaponDefinitionRuntime(eve::definitions::DefinitionRegistry& registry, eve::definition::RuntimeInstance runtime, - eve::definition::ReloadPolicy policy) + eve::definition::ReloadPolicy policy) : registry_(®istry), runtime_(std::move(runtime)), policy_(policy) {} - eve::definitions::DefinitionRegistry* registry_ = nullptr; // borrowed + eve::definitions::DefinitionRegistry* registry_ = nullptr; // borrowed eve::definition::RuntimeInstance runtime_; - eve::definition::ReloadPolicy policy_ = eve::definition::ReloadPolicy::RebuildInstance; + eve::definition::ReloadPolicy policy_ = eve::definition::ReloadPolicy::RebuildInstance; }; } // namespace eve::weapon diff --git a/src/modules/weapon/WeaponEffects.cpp b/src/modules/weapon/WeaponEffects.cpp index 329bfb41..f34c348a 100644 --- a/src/modules/weapon/WeaponEffects.cpp +++ b/src/modules/weapon/WeaponEffects.cpp @@ -10,9 +10,9 @@ namespace { const char* kindTag(WeaponEffectKind kind) { switch (kind) { - case WeaponEffectKind::Heat: return "weapon:heat"; - case WeaponEffectKind::Jam: return "weapon:jam"; - case WeaponEffectKind::Recoil: return "weapon:recoil"; + case WeaponEffectKind::Heat: return "weapon:heat"; + case WeaponEffectKind::Jam: return "weapon:jam"; + case WeaponEffectKind::Recoil: return "weapon:recoil"; } return "weapon:unknown"; } @@ -26,27 +26,27 @@ bool has(const effects::EffectPeriodicTick& tick, const char* tag) { eve::Result WeaponEffectExecutor::validate(const WeaponEffectTarget& target) const { if (!std::isfinite(target.heat) || !std::isfinite(target.maxHeat) || !std::isfinite(target.recoil) || target.maxHeat <= 0.0 || target.heat < 0.0 || target.heat > target.maxHeat || target.recoil < 0.0) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "weapon effect target state is invalid", "target")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "weapon effect target state is invalid", "target")); return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result WeaponEffectExecutor::applyImmediate(WeaponEffectTarget& target, +eve::Result WeaponEffectExecutor::applyImmediate(WeaponEffectTarget& target, const effects::EffectInstance& effect) const { auto valid = validate(target); if (!valid) return valid; if (effect.hasTag("weapon:jam")) target.jammed = true; if (effect.hasTag("weapon:recoil")) { if (effect.magnitude > std::numeric_limits::max() - target.recoil) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "weapon recoil overflows", "target.recoil")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, + "weapon recoil overflows", "target.recoil")); target.recoil += effect.magnitude; } return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } -eve::Result WeaponEffectExecutor::settle( - WeaponEffectTarget& target, effects::EffectUpdateSummary lifecycle) const { +eve::Result WeaponEffectExecutor::settle(WeaponEffectTarget& target, + effects::EffectUpdateSummary lifecycle) const { auto valid = validate(target); if (!valid) return eve::Result::failure(valid.status()); WeaponEffectUpdate result; @@ -77,42 +77,42 @@ eve::Result WeaponEffectExecutor::settle( ++result.settled; } result.jammed = result.jammed || target.jammed; - return eve::Result::success(std::move(result), - eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(std::move(result), eve::Status::success(eve::StatusCode::Applied)); } -eve::Result WeaponEffectAdapter::apply( - const WeaponEffectDefinition& definition, eve::SubjectRef subject) { +eve::Result WeaponEffectAdapter::apply(const WeaponEffectDefinition& definition, + eve::SubjectRef subject) { if (definition.id.empty() || !subject.isValid()) return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::InvalidArgument, "weapon effect requires an id and valid subject", "effect")); effects::EffectDefinition common; - common.id = definition.id; - common.stackKey = definition.id; - common.duration = definition.duration; - common.period = definition.period; + common.id = definition.id; + common.stackKey = definition.id; + common.duration = definition.duration; + common.period = definition.period; common.magnitude = definition.magnitude; - common.policy = definition.policy; - common.tags = definition.tags; + common.policy = definition.policy; + common.tags = definition.tags; common.tags.push_back(kindTag(definition.kind)); auto valid = common.validate(); if (!valid) return eve::Result::failure(valid.status()); auto candidate = container_.clone(); - auto applied = candidate.apply(common, subject.format(), definition.source); + auto applied = candidate.apply(common, subject.format(), definition.source); if (!applied) return eve::Result::failure(applied.status()); - const std::string id = std::move(applied).takeValue(); - const auto* instance = candidate.find(id); - if (!instance) return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvariantViolation, "weapon effect candidate disappeared", "effect")); + const std::string id = std::move(applied).takeValue(); + const auto* instance = candidate.find(id); + if (!instance) + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvariantViolation, "weapon effect candidate disappeared", "effect")); auto targetCandidate = target_; - auto immediate = executor_.applyImmediate(targetCandidate, *instance); + auto immediate = executor_.applyImmediate(targetCandidate, *instance); if (!immediate) return eve::Result::failure(immediate.status()); auto handle = candidate.handleFor(id); if (!handle) return eve::Result::failure(handle.status()); - target_ = std::move(targetCandidate); + target_ = std::move(targetCandidate); container_ = std::move(candidate); return eve::Result::success(std::move(handle).takeValue(), - eve::Status::success(eve::StatusCode::Applied)); + eve::Status::success(eve::StatusCode::Applied)); } eve::Result WeaponEffectAdapter::remove(effects::EffectHandle handle) { @@ -126,13 +126,12 @@ eve::Result WeaponEffectAdapter::advance(const eve::Simulati auto lifecycle = effects::EffectExecutor{}.advance(candidate, step); if (!lifecycle) return eve::Result::failure(lifecycle.status()); auto targetCandidate = target_; - auto settled = executor_.settle(targetCandidate, std::move(lifecycle).takeValue()); + auto settled = executor_.settle(targetCandidate, std::move(lifecycle).takeValue()); if (!settled) return settled; auto result = std::move(settled).takeValue(); - target_ = std::move(targetCandidate); - container_ = std::move(candidate); - return eve::Result::success(std::move(result), - eve::Status::success(eve::StatusCode::Applied)); + target_ = std::move(targetCandidate); + container_ = std::move(candidate); + return eve::Result::success(std::move(result), eve::Status::success(eve::StatusCode::Applied)); } WeaponEffectSnapshot WeaponEffectAdapter::snapshot() const { return {container_.snapshot(), target_}; } diff --git a/src/modules/weapon/WeaponEffects.h b/src/modules/weapon/WeaponEffects.h index 63ad5467..0ce745a9 100644 --- a/src/modules/weapon/WeaponEffects.h +++ b/src/modules/weapon/WeaponEffects.h @@ -21,36 +21,36 @@ enum class WeaponEffectKind : std::uint8_t { Heat, Jam, Recoil }; /** @brief Mutable weapon state interpreted by the typed executor. */ struct WeaponEffectTarget { - double heat = 0.0; - double maxHeat = 100.0; - bool jammed = false; - double recoil = 0.0; + double heat = 0.0; + double maxHeat = 100.0; + bool jammed = false; + double recoil = 0.0; std::uint32_t blockedShots = 0; }; /** @brief Typed weapon definition projected into the common lifecycle. */ struct WeaponEffectDefinition { - std::string id; - std::string source; - double duration = 0.0; - double period = 0.0; - double magnitude = 0.0; - effects::EffectPolicy policy; - WeaponEffectKind kind = WeaponEffectKind::Heat; + std::string id; + std::string source; + double duration = 0.0; + double period = 0.0; + double magnitude = 0.0; + effects::EffectPolicy policy; + WeaponEffectKind kind = WeaponEffectKind::Heat; std::vector tags; }; /** @brief Result of one weapon lifecycle step and domain settlement. */ struct WeaponEffectUpdate { effects::EffectUpdateSummary lifecycle; - std::uint32_t settled = 0; - bool jammed = false; + std::uint32_t settled = 0; + bool jammed = false; }; /** @brief In-memory weapon effect snapshot. */ struct WeaponEffectSnapshot { effects::EffectContainer effects; - WeaponEffectTarget target; + WeaponEffectTarget target; }; /** @brief Weapon executor applying heat, jam and recoil rules. */ @@ -59,11 +59,11 @@ public: /** @brief Validate heat and recoil invariants. */ [[nodiscard]] eve::Result validate(const WeaponEffectTarget& target) const; /** @brief Apply an immediate jam/recoil strategy to a staged target. */ - [[nodiscard]] eve::Result applyImmediate(WeaponEffectTarget& target, + [[nodiscard]] eve::Result applyImmediate(WeaponEffectTarget& target, const effects::EffectInstance& effect) const; /** @brief Settle common periodic triggers through weapon policy. */ - [[nodiscard]] eve::Result settle(WeaponEffectTarget& target, - effects::EffectUpdateSummary lifecycle) const; + [[nodiscard]] eve::Result settle(WeaponEffectTarget& target, + effects::EffectUpdateSummary lifecycle) const; }; /** @brief Weapon adapter with a single common lifecycle owner. */ @@ -71,7 +71,7 @@ class WeaponEffectAdapter { public: /** @brief Apply one typed weapon effect to a stable subject. */ [[nodiscard]] eve::Result apply(const WeaponEffectDefinition& definition, - eve::SubjectRef subject); + eve::SubjectRef subject); /** @brief Remove one generation-qualified effect handle. */ [[nodiscard]] eve::Result remove(effects::EffectHandle handle); /** @brief Advance and settle weapon effects atomically. */ @@ -89,8 +89,8 @@ public: private: effects::EffectContainer container_; - WeaponEffectExecutor executor_; - WeaponEffectTarget target_; + WeaponEffectExecutor executor_; + WeaponEffectTarget target_; }; } // namespace eve::weapon diff --git a/src/modules/weapon/WeaponSystem.cpp b/src/modules/weapon/WeaponSystem.cpp index a216b16b..f98d0440 100644 --- a/src/modules/weapon/WeaponSystem.cpp +++ b/src/modules/weapon/WeaponSystem.cpp @@ -269,10 +269,9 @@ bool canAttackStage(WeaponEntity& w, const WeaponDefinition* def) { if (state->cooldown > 0.f) return false; const Resource& r = state->resource; if (r.kind == ResourceKind::Mana || r.kind == ResourceKind::Stamina) { - const auto attribute = r.kind == ResourceKind::Mana - ? WeaponAttributeAdapter::manaAttribute - : WeaponAttributeAdapter::staminaAttribute; - auto current = WeaponAttributeAdapter::read(w, attribute); + const auto attribute = r.kind == ResourceKind::Mana ? WeaponAttributeAdapter::manaAttribute + : WeaponAttributeAdapter::staminaAttribute; + auto current = WeaponAttributeAdapter::read(w, attribute); if (!current) { current.ignore("legacy bool canFire cannot expose weapon attribute diagnostics"); return false; diff --git a/src/modules/weapon/WeaponTypes.h b/src/modules/weapon/WeaponTypes.h index 002fd588..53bd4712 100644 --- a/src/modules/weapon/WeaponTypes.h +++ b/src/modules/weapon/WeaponTypes.h @@ -17,8 +17,8 @@ * 手持位(WeaponRigEntity)是轻量持有位,面向 ARPG/RPG 角色手持武器。 */ -#include "common/ECS.h" #include "attributes/AttributeProjection.h" +#include "common/ECS.h" #include "common/definitions/DefinitionRuntime.h" #include @@ -289,9 +289,8 @@ public: */ struct DefinitionBinding { eve::definition::InstanceIdentity identity; - eve::definition::ReloadPolicy reloadPolicy = - eve::definition::ReloadPolicy::RebuildInstance; - bool active = true; + eve::definition::ReloadPolicy reloadPolicy = eve::definition::ReloadPolicy::RebuildInstance; + bool active = true; }; /** @brief 触发资源 / 冷却 / 装填 / 阶段机运行时状态。 */ diff --git a/src/scripts/load.nut b/src/scripts/load.nut index 2f667ec8..9acc0c08 100644 --- a/src/scripts/load.nut +++ b/src/scripts/load.nut @@ -672,11 +672,11 @@ eve_frame <- function() { _log("frame: reached frame " + getroottable()._frame_count); local running = true; dev_scenario_frame(); - event.pump(); + platform_event.pump(); while (true) { - local name = event.poll(); + local name = platform_event.poll(); if (name == "") break; - local data = event.getLastData(); + local data = platform_event.getLastData(); if ("async_dispatch_event" in getroottable()) async_dispatch_event(name, data); if (name == "keypressed") { diff --git a/test/DatabasePanel.cpp b/test/DatabasePanel.cpp index 35ffc6bc..c99b277f 100644 --- a/test/DatabasePanel.cpp +++ b/test/DatabasePanel.cpp @@ -25,9 +25,7 @@ class CharacterData { } )SQ"; -eve::OptionalRef nodeById(UIHost& host, const std::string& id) { - return host.findById(id); -} +eve::OptionalRef nodeById(UIHost& host, const std::string& id) { return host.findById(id); } } // namespace @@ -48,7 +46,7 @@ TEST_CASE("database.registryCreatesRegistersAndLists") { CHECK_EQ(registry.count("CharacterData"), size_t(1)); ssq::Object hero = runtime.createInstance("CharacterData"); - auto secondResult = registry.registerObject("CharacterData", hero); + auto secondResult = registry.registerObject("CharacterData", hero); REQUIRE(secondResult.ok()); const ObjectHandle second = secondResult.value(); CHECK_EQ(registry.count("CharacterData"), size_t(2)); @@ -152,8 +150,6 @@ TEST_CASE("database.registryHandlesRejectStaleAndReuseSlots") { CHECK_EQ(second.index(), first.index()); CHECK_EQ(second.generation(), first.generation() + 1u); CHECK(registry.entry(first) == nullptr); - CHECK_EQ(static_cast(registry.unregister(first).code()), - static_cast(eve::StatusCode::Rejected)); - CHECK_EQ(static_cast(registry.unregister(second).code()), - static_cast(eve::StatusCode::Applied)); + CHECK_EQ(static_cast(registry.unregister(first).code()), static_cast(eve::StatusCode::Rejected)); + CHECK_EQ(static_cast(registry.unregister(second).code()), static_cast(eve::StatusCode::Applied)); } diff --git a/test/EditorShell.cpp b/test/EditorShell.cpp index e16f4407..e87793b5 100644 --- a/test/EditorShell.cpp +++ b/test/EditorShell.cpp @@ -12,9 +12,7 @@ using namespace eve::ui; namespace { -eve::OptionalRef nodeById(UIHost& host, const std::string& id) { - return host.findById(id); -} +eve::OptionalRef nodeById(UIHost& host, const std::string& id) { return host.findById(id); } } // namespace @@ -43,11 +41,11 @@ TEST_CASE("editorShell.menuDocksAndSwitchesPanels") { REQUIRE(nodeById(menu, "menu_close").has_value()); auto resolvedInspector = UIHost::resolve(inspector.host()); - auto resolvedDatabase = UIHost::resolve(database.host()); + auto resolvedDatabase = UIHost::resolve(database.host()); REQUIRE(resolvedInspector.has_value()); REQUIRE(resolvedDatabase.has_value()); UIHost& inspectorHost = resolvedInspector->get(); - UIHost& databaseHost = resolvedDatabase->get(); + UIHost& databaseHost = resolvedDatabase->get(); CHECK(inspectorHost.meta()->visible); CHECK(databaseHost.meta()->visible); CHECK(!inspectorHost.meta()->lockPos); @@ -57,7 +55,7 @@ TEST_CASE("editorShell.menuDocksAndSwitchesPanels") { REQUIRE(toolbar.has_value()); CHECK(int(toolbar->get().type) == int(NodeType::Toolbar)); - auto tree = menu.tree(); + auto tree = menu.tree(); auto databaseButton = nodeById(menu, "menu_database"); REQUIRE(databaseButton.has_value()); REQUIRE_GE(databaseButton->get().handlerClick, 1u); diff --git a/test/GameEventTestSupport.h b/test/GameEventTestSupport.h index 76b67f8b..c0fd9d75 100644 --- a/test/GameEventTestSupport.h +++ b/test/GameEventTestSupport.h @@ -14,38 +14,34 @@ inline EventId eventId(std::uint64_t serial) { bytes[6] = 0x40; bytes[8] = 0x80; for (int index = 0; index < 8; ++index) - bytes[8 + index] = static_cast(bytes[8 + index] ^ - (serial >> ((7 - index) * 8))); + bytes[8 + index] = static_cast(bytes[8 + index] ^ (serial >> ((7 - index) * 8))); bytes[8] = static_cast((bytes[8] & 0x3fu) | 0x80u); return EventId(bytes); } /** @brief Builds a canonical envelope for a test without using the removed emit facade. */ -inline GameEvent envelope(std::uint64_t serial, std::string type, std::string source, - std::string subject, std::string causation, - std::string correlation, std::uint64_t tick, std::uint32_t flags, +inline GameEvent envelope(std::uint64_t serial, std::string type, std::string source, std::string subject, + std::string causation, std::string correlation, std::uint64_t tick, std::uint32_t flags, std::string payload = "null") { GameEvent result; result.eventId = eventId(serial); - result.type = std::move(type); - result.source = std::move(source); + result.type = std::move(type); + result.source = std::move(source); result.subject = std::move(subject); if (!causation.empty()) { const auto parsed = EventId::parse(causation); - result.causation = parsed ? CausationRef::fromEventId(*parsed) - : CausationRef::fromLegacy(std::move(causation)); + result.causation = parsed ? CausationRef::fromEventId(*parsed) : CausationRef::fromLegacy(std::move(causation)); } if (!correlation.empty()) { const auto parsed = EventId::parse(correlation); - result.correlation = parsed ? CorrelationId::fromEventId(*parsed) - : CorrelationId::fromLegacy(std::move(correlation)); + result.correlation = + parsed ? CorrelationId::fromEventId(*parsed) : CorrelationId::fromLegacy(std::move(correlation)); } - if (const auto schema = eve::LogicalId::fromParts("game_event", result.type)) - result.schemaId = *schema; + if (const auto schema = eve::LogicalId::fromParts("game_event", result.type)) result.schemaId = *schema; result.schemaVersion = SchemaVersion(1); - result.tick = SimulationTick(tick); - result.flags = flags; - result.payload = std::move(payload); + result.tick = SimulationTick(tick); + result.flags = flags; + result.payload = std::move(payload); return result; } diff --git a/test/Inspector.cpp b/test/Inspector.cpp index 03244f3f..d64d672d 100644 --- a/test/Inspector.cpp +++ b/test/Inspector.cpp @@ -340,7 +340,7 @@ TEST_CASE("inspector.pickSceneInspectsPickedObject") { inspector.refresh(); inspector.setPickScene([picked]() { return picked; }); inspector.open(); - UIHost *host = resolveHost(inspector.host()); + UIHost* host = resolveHost(inspector.host()); REQUIRE(host != nullptr); UINode *pick = nodeById(host, "inspector_pick"); diff --git a/test/ProcgenGridTestSupport.h b/test/ProcgenGridTestSupport.h index 44f8b292..b0e59e47 100644 --- a/test/ProcgenGridTestSupport.h +++ b/test/ProcgenGridTestSupport.h @@ -28,23 +28,21 @@ namespace eve::test_support { * @remarks Test-only helper. It never returns a borrowed pointer or retains a * grid handle after the synchronous operation. */ -inline void applyGridToLayer(eve::procgen::Procgen& procgen, - const eve::procgen::Grid2D& source, - const std::string& palette, - eve::map::TileLayer& layer) { - auto created = eve::procgen::Procgen::newGridHandle(source.getWidth(), source.getHeight()); +inline void applyGridToLayer(eve::procgen::Procgen& procgen, const eve::procgen::Grid2D& source, + const std::string& palette, eve::map::TileLayer& layer) { + auto created = eve::procgen::Procgen::newGridHandle(source.getWidth(), source.getHeight()); const bool createdOk = created.ok(); REQUIRE(createdOk); if (!createdOk) return; - const auto handle = std::move(created).takeValue(); - auto view = eve::procgen::Procgen::resolve(handle); + const auto handle = std::move(created).takeValue(); + auto view = eve::procgen::Procgen::resolve(handle); const bool resolved = view.isBound(); if (resolved) *view = source; if (!resolved) { - auto releaseResult = eve::procgen::Procgen::release(handle); - const bool released = releaseResult.ok(); + auto releaseResult = eve::procgen::Procgen::release(handle); + const bool released = releaseResult.ok(); REQUIRE(released); REQUIRE(false); return; @@ -52,10 +50,10 @@ inline void applyGridToLayer(eve::procgen::Procgen& procgen, // Keep the operation Result alive until it has been observed before the // handle is released; this also avoids ZeroErr evaluating it twice. - auto applyResult = procgen.applyToLayer(handle, palette, layer); - const bool applied = applyResult.ok(); - auto releaseResult = eve::procgen::Procgen::release(handle); - const bool released = releaseResult.ok(); + auto applyResult = procgen.applyToLayer(handle, palette, layer); + const bool applied = applyResult.ok(); + auto releaseResult = eve::procgen::Procgen::release(handle); + const bool released = releaseResult.ok(); REQUIRE(applied); REQUIRE(released); } diff --git a/test/RenderImageAudit.cpp b/test/RenderImageAudit.cpp index 833183b2..bb872fff 100644 --- a/test/RenderImageAudit.cpp +++ b/test/RenderImageAudit.cpp @@ -2753,7 +2753,7 @@ TEST_CASE("graphics.imageAudit.procgenAssets") { auto paramsResult = eve::procgen::Procgen::newParamsHandle(); REQUIRE(paramsResult.ok()); auto paramsHandle = std::move(paramsResult).takeValue(); - auto paramsView = eve::procgen::Procgen::resolve(paramsHandle); + auto paramsView = eve::procgen::Procgen::resolve(paramsHandle); REQUIRE(paramsView.isBound()); eve::procgen::Params ¶ms = *paramsView; params.setSeed(11); @@ -2913,17 +2913,17 @@ TEST_CASE("graphics.imageAudit.mapFovHex") { maskSp->sprite()->visible = false; auto *pg = eve::procgen::Procgen::create(); - auto paramsResult = eve::procgen::Procgen::newParamsHandle(); + auto paramsResult = eve::procgen::Procgen::newParamsHandle(); REQUIRE(paramsResult.ok()); auto paramsHandle = std::move(paramsResult).takeValue(); - auto paramsView = eve::procgen::Procgen::resolve(paramsHandle); + auto paramsView = eve::procgen::Procgen::resolve(paramsHandle); REQUIRE(paramsView.isBound()); paramsView->setSeed(1); paramsView->setSize(25, 19); auto gridResult = pg->generateHandle("dungeon.bsp", paramsHandle); REQUIRE(gridResult.ok()); auto gridHandle = std::move(gridResult).takeValue(); - auto gridView = eve::procgen::Procgen::resolve(gridHandle); + auto gridView = eve::procgen::Procgen::resolve(gridHandle); REQUIRE(gridView.isBound()); pg->setPaletteGid("audit", "wall", 2); pg->setPaletteGid("audit", "floor", 5); diff --git a/test/StatePatchTestSupport.h b/test/StatePatchTestSupport.h index 470e8c86..b28f07f3 100644 --- a/test/StatePatchTestSupport.h +++ b/test/StatePatchTestSupport.h @@ -17,30 +17,29 @@ namespace eve::test_support { struct StatePatchBatchLease { StatePatchBatchLease() = default; - StatePatchBatchLease(eve::statepatch::Store* owner, - eve::statepatch::PatchBatchHandleRef reference, + StatePatchBatchLease(eve::statepatch::Store* owner, eve::statepatch::PatchBatchHandleRef reference, eve::script::Borrowed view) noexcept : owner(owner), reference(reference), view(view) {} - StatePatchBatchLease(const StatePatchBatchLease&) = delete; + StatePatchBatchLease(const StatePatchBatchLease&) = delete; StatePatchBatchLease& operator=(const StatePatchBatchLease&) = delete; StatePatchBatchLease(StatePatchBatchLease&& other) noexcept : owner(other.owner), reference(other.reference), view(other.view) { - other.owner = nullptr; + other.owner = nullptr; other.reference = {}; - other.view = {}; + other.view = {}; } StatePatchBatchLease& operator=(StatePatchBatchLease&& other) noexcept { if (this == &other) return *this; release(); - owner = other.owner; - reference = other.reference; - view = other.view; - other.owner = nullptr; + owner = other.owner; + reference = other.reference; + view = other.view; + other.owner = nullptr; other.reference = {}; - other.view = {}; + other.view = {}; return *this; } @@ -50,13 +49,13 @@ struct StatePatchBatchLease { void release() noexcept { if (owner != nullptr && reference.isValid()) owner->releaseBatch(reference).ignore("release test StatePatch batch"); - owner = nullptr; + owner = nullptr; reference = {}; - view = {}; + view = {}; } - eve::statepatch::Store* owner = nullptr; - eve::statepatch::PatchBatchHandleRef reference; + eve::statepatch::Store* owner = nullptr; + eve::statepatch::PatchBatchHandleRef reference; eve::script::Borrowed view; }; @@ -70,7 +69,7 @@ inline StatePatchBatchLease openStatePatchBatch(eve::statepatch::Store& store) { if (!created.ok()) return {}; const auto reference = std::move(created).takeValue(); - auto view = store.resolveBatch(reference); + auto view = store.resolveBatch(reference); if (!view.isBound()) { store.releaseBatch(reference).ignore("release unresolved test StatePatch batch"); return {}; diff --git a/test/TestPropertyAccess.h b/test/TestPropertyAccess.h index ee8630ea..ab23b29b 100644 --- a/test/TestPropertyAccess.h +++ b/test/TestPropertyAccess.h @@ -13,49 +13,52 @@ class TestPropertyAccess final : public eve::property_access::IPropertyAccess { public: explicit TestPropertyAccess(eve::property_access::PropertySchema schema = {}) : schema_(std::move(schema)), observers_(std::make_shared()) { - for (const auto& property : schema_.properties) - values_.emplace(property.path, property.defaultValue); + for (const auto& property : schema_.properties) values_.emplace(property.path, property.defaultValue); } const eve::property_access::PropertySchema& schema() const override { return schema_; } - std::optional read(const std::string& path) const override { + std::optional read(const std::string& path) const override { const auto found = values_.find(path); return found == values_.end() ? std::nullopt : std::optional(found->second); } - eve::property_access::WriteResult write(const std::string& path, - const eve::Value& value) override { + eve::property_access::WriteResult write(const std::string& path, const eve::Value& value) override { auto property = schema_.find(path); if (!property) - return eve::property_access::WriteResult::reject( - "property_access.property.missing", "Property is not in the schema"); + return eve::property_access::WriteResult::reject("property_access.property.missing", + "Property is not in the schema"); auto result = eve::property_access::validatePropertyValue(property->get(), value); if (!result.accepted) return result; const auto found = values_.find(path); if (found != values_.end() && found->second == value) return result; values_[path] = value; const eve::property_access::PropertyChange change{path, value, ++revision_}; - const auto snapshot = observers_->entries; + const auto snapshot = observers_->entries; for (const auto& entry : snapshot) if (entry.callback) entry.callback(change); return result; } - std::uint64_t revision() const override { return revision_; } + std::uint64_t revision() const override { return revision_; } eve::Subscription subscribe(ChangeCallback callback) override { const std::uint64_t id = observers_->nextId++; observers_->entries.push_back({id, std::move(callback)}); std::weak_ptr weak = observers_; return eve::Subscription([weak, id]() { if (const auto state = weak.lock()) - std::erase_if(state->entries, - [id](const Entry& entry) { return entry.id == id; }); + std::erase_if(state->entries, [id](const Entry& entry) { return entry.id == id; }); }); } private: - struct Entry { std::uint64_t id; ChangeCallback callback; }; - struct ObserverState { std::uint64_t nextId = 1; std::vector entries; }; + struct Entry { + std::uint64_t id; + ChangeCallback callback; + }; + struct ObserverState { + std::uint64_t nextId = 1; + std::vector entries; + }; eve::property_access::PropertySchema schema_; - std::map values_; - std::uint64_t revision_ = 0; - std::shared_ptr observers_; + std::map values_; + std::uint64_t revision_ = 0; + std::shared_ptr observers_; }; diff --git a/test/UIBuilder.cpp b/test/UIBuilder.cpp index ce1b67f2..51b1b45c 100644 --- a/test/UIBuilder.cpp +++ b/test/UIBuilder.cpp @@ -12,13 +12,13 @@ using namespace eve::ui; namespace { -UIHost* resolveHost(UIHostHandle handle) { +UIHost *resolveHost(UIHostHandle handle) { auto host = UIHost::resolve(handle); return host ? &host->get() : nullptr; } -UINode* findNode(UIHostHandle handle, const std::string& id) { - UIHost* host = resolveHost(handle); +UINode *findNode(UIHostHandle handle, const std::string &id) { + UIHost *host = resolveHost(handle); if (host == nullptr) return nullptr; auto node = host->findById(id); return node ? &node->get() : nullptr; diff --git a/test/UIEditorKit.cpp b/test/UIEditorKit.cpp index 49f2de51..5bcdbabb 100644 --- a/test/UIEditorKit.cpp +++ b/test/UIEditorKit.cpp @@ -20,9 +20,7 @@ UIHost* resolveHost(UIHostHandle handle) { return host ? &host->get() : nullptr; } -UIHost* createHost(const std::string& name) { - return resolveHost(UIHost::createHost(name)); -} +UIHost* createHost(const std::string& name) { return resolveHost(UIHost::createHost(name)); } UINode* findNode(UIHost* host, const std::string& id) { if (host == nullptr) return nullptr; @@ -30,9 +28,7 @@ UINode* findNode(UIHost* host, const std::string& id) { return node ? &node->get() : nullptr; } -UINode* findNode(UI& ui, const std::string& id) { - return findNode(resolveHost(ui.current()), id); -} +UINode* findNode(UI& ui, const std::string& id) { return findNode(resolveHost(ui.current()), id); } } // namespace @@ -113,16 +109,12 @@ TEST_CASE("UI.editorKit.scriptBuilderAndSerialization") { CHECK(!findNode(*ui, "active")->enabled); CHECK(findNode(*ui, "search")->tooltip == "Filter scene nodes"); CHECK(findNode(*ui, "search")->tabIndex == 4); - CHECK(int(findNode(*ui, "search")->mouseFilter) == - int(MouseFilter::Pass)); - CHECK(int(findNode(*ui, "root")->themePreset) == - int(ThemePreset::Light)); - CHECK(int(findNode(*ui, "search")->themePreset) == - int(ThemePreset::Dark)); + CHECK(int(findNode(*ui, "search")->mouseFilter) == int(MouseFilter::Pass)); + CHECK(int(findNode(*ui, "root")->themePreset) == int(ThemePreset::Light)); + CHECK(int(findNode(*ui, "search")->themePreset) == int(ThemePreset::Dark)); CHECK(findNode(*ui, "search")->focusRight == "toolbar_save"); CHECK(findNode(*ui, "search")->accessibilityName == "Search scene"); - CHECK(findNode(*ui, "search")->accessibilityDescription == - "Filters the scene tree"); + CHECK(findNode(*ui, "search")->accessibilityDescription == "Filters the scene tree"); CHECK(int(findNode(*ui, "workspace")->type) == int(NodeType::SplitPane)); CHECK(ui->requestFocus("search")); CHECK(ui->moveFocus("right")); @@ -142,7 +134,7 @@ TEST_CASE("UI.editorKit.scriptBuilderAndSerialization") { } TEST_CASE("UI.editorKit.desktopCompositionRenders") { - UIHost* host = createHost("desktop_components"); + UIHost* host = createHost("desktop_components"); REQUIRE(host != nullptr); host->meta()->hasSize = true; host->meta()->sizeX = 800.f; diff --git a/test/UIOverlaySmoke.cpp b/test/UIOverlaySmoke.cpp index 0ee1c035..6400b0bf 100644 --- a/test/UIOverlaySmoke.cpp +++ b/test/UIOverlaySmoke.cpp @@ -83,13 +83,12 @@ TEST_CASE("UI.smoke.RenderSystemSameFrameVulkanOverlay") { solid->sprite()->b = 0.35f; solid->sprite()->visible = true; - const UIHostHandle smokeHost = ui->mountAs("smoke", - window("Overlay", - { - text("UI+RenderSystem", "label"), - button("Ping", "ping"), - }, - "root")); + const UIHostHandle smokeHost = ui->mountAs("smoke", window("Overlay", + { + text("UI+RenderSystem", "label"), + button("Ping", "ping"), + }, + "root")); REQUIRE(UIHost::resolve(smokeHost).has_value()); int framesOk = 0; @@ -174,18 +173,18 @@ TEST_CASE("UI.smoke.panelGalleryPreview") { bool muted = false; int clicks = 0; - const UIHostHandle galleryHost = ui->mountAs( - "gallery", window("EVEngine Preview", - { - text("UI overlay + world sprites", "title"), - separator("sep0"), - checkbox("Mute", muted, "mute", [&](bool v) { muted = v; }), - slider("Volume", volume, 0.f, 1.f, "vol", [&](float v) { volume = v; }), - progress(volume, "prog", "level"), - button("Ping", "ping", [&]() { ++clicks; }), - text("clicks: 0", "clicks"), - }, - "root")); + const UIHostHandle galleryHost = + ui->mountAs("gallery", window("EVEngine Preview", + { + text("UI overlay + world sprites", "title"), + separator("sep0"), + checkbox("Mute", muted, "mute", [&](bool v) { muted = v; }), + slider("Volume", volume, 0.f, 1.f, "vol", [&](float v) { volume = v; }), + progress(volume, "prog", "level"), + button("Ping", "ping", [&]() { ++clicks; }), + text("clicks: 0", "clicks"), + }, + "root")); REQUIRE(UIHost::resolve(galleryHost).has_value()); for (int frame = 0; frame < 90; ++frame) { @@ -194,20 +193,19 @@ TEST_CASE("UI.smoke.panelGalleryPreview") { volume = 0.35f + 0.3f * (0.5f + 0.5f * std::sin(float(frame) * 0.06f)); const UIHostHandle remounted = ui->remountAs( - "gallery", window("EVEngine Preview", - { - text("UI overlay + world sprites", "title"), - separator("sep0"), - checkbox("Mute", muted, "mute", [&](bool v) { muted = v; }), - slider("Volume", volume, 0.f, 1.f, "vol", - [&](float v) { volume = v; }), - progress(volume, "prog", muted ? "muted" : "level"), - button("Ping", "ping", [&]() { ++clicks; }), - text("clicks: " + std::to_string(clicks) + - " vol: " + std::to_string(int(volume * 100.f)), - "clicks"), - }, - "root")); + "gallery", + window("EVEngine Preview", + { + text("UI overlay + world sprites", "title"), + separator("sep0"), + checkbox("Mute", muted, "mute", [&](bool v) { muted = v; }), + slider("Volume", volume, 0.f, 1.f, "vol", [&](float v) { volume = v; }), + progress(volume, "prog", muted ? "muted" : "level"), + button("Ping", "ping", [&]() { ++clicks; }), + text("clicks: " + std::to_string(clicks) + " vol: " + std::to_string(int(volume * 100.f)), + "clicks"), + }, + "root")); REQUIRE(UIHost::resolve(remounted).has_value()); ui->beginFrameAndRender(); diff --git a/test/UIPhaseAdv.cpp b/test/UIPhaseAdv.cpp index af8dabff..4e011db0 100644 --- a/test/UIPhaseAdv.cpp +++ b/test/UIPhaseAdv.cpp @@ -29,9 +29,7 @@ ui::UINode *findNode(ui::UIHost *host, const std::string &id) { return node ? &node->get() : nullptr; } -ui::UINode *findNode(ui::UIHostHandle handle, const std::string &id) { - return findNode(resolveHost(handle), id); -} +ui::UINode *findNode(ui::UIHostHandle handle, const std::string &id) { return findNode(resolveHost(handle), id); } } // namespace @@ -62,11 +60,11 @@ TEST_CASE("UI.adv.sliderProgressInputCollapseChild") { CHECK(findNode(h, "r1") != nullptr); ui::UIEvent ev; - ev.host = h->handle(); + ev.host = h->handle(); ev.hostName = "adv"; ev.nodeId = "vol"; ev.kind = "value"; - ev.handlerIndex = findNode(h, "vol")->handlerValue; + ev.handlerIndex = findNode(h, "vol")->handlerValue; ev.floatValue = 0.75f; findNode(h, "vol")->value = 0.75f; ui::UISystem::pendingEvents().push_back(ev); @@ -378,7 +376,7 @@ TEST_CASE("UI.layout.measureNestedFlexAndWindowContent") { ui::measureTree(*host->tree()); ui::UINode *outer = findNode(host, "outer"); ui::UINode *inner = findNode(host, "inner"); - ui::UINode *root = findNode(host, "root"); + ui::UINode *root = findNode(host, "root"); REQUIRE(outer != nullptr); REQUIRE(inner != nullptr); // Nested containers previously measured 0; now they carry real sizes. @@ -400,7 +398,7 @@ TEST_CASE("UI.layout.measureNestedFlexAndWindowContent") { TEST_CASE("UI.p0.imageAndImageButton") { int clicks = 0; - ui::UIHost *h = resolveHost(ui::UIHost::createHost("img")); + ui::UIHost *h = resolveHost(ui::UIHost::createHost("img")); REQUIRE(h != nullptr); h->setTree(ui::window( "W", @@ -414,8 +412,8 @@ TEST_CASE("UI.p0.imageAndImageButton") { "root")); ui::UINode *avatar = findNode(h, "avatar"); - ui::UINode *btn = findNode(h, "btn"); - ui::UINode *frame = findNode(h, "frame"); + ui::UINode *btn = findNode(h, "btn"); + ui::UINode *frame = findNode(h, "frame"); REQUIRE(avatar != nullptr); REQUIRE(btn != nullptr); REQUIRE(frame != nullptr); @@ -430,7 +428,7 @@ TEST_CASE("UI.p0.imageAndImageButton") { // Click routing for ImageButton via the pending-event pipeline. ui::UIEvent ev; - ev.host = h->handle(); + ev.host = h->handle(); ev.hostName = "img"; ev.nodeId = "btn"; ev.kind = "click"; @@ -492,7 +490,7 @@ TEST_CASE_FIXTURE(UIScriptCallbackTest, "UI.p0.scriptEventCallbacks") { REQUIRE(sl != nullptr); ui::UIEvent click; - click.host = host->handle(); + click.host = host->handle(); click.hostName = "cb"; click.nodeId = "go"; click.kind = "click"; @@ -500,7 +498,7 @@ TEST_CASE_FIXTURE(UIScriptCallbackTest, "UI.p0.scriptEventCallbacks") { ui::UISystem::pendingEvents().push_back(click); ui::UIEvent change; - change.host = host->handle(); + change.host = host->handle(); change.hostName = "cb"; change.nodeId = "s"; change.kind = "value"; @@ -516,7 +514,7 @@ TEST_CASE_FIXTURE(UIScriptCallbackTest, "UI.p0.scriptEventCallbacks") { TEST_CASE("UI.p1.comboAndTextWrap") { int picked = -1; - ui::UIHost *h = resolveHost(ui::UIHost::createHost("p1")); + ui::UIHost *h = resolveHost(ui::UIHost::createHost("p1")); REQUIRE(h != nullptr); h->setTree(ui::window( "W", @@ -528,7 +526,7 @@ TEST_CASE("UI.p1.comboAndTextWrap") { "root")); ui::UINode *combo = findNode(h, "fruit"); - ui::UINode *tip = findNode(h, "tip"); + ui::UINode *tip = findNode(h, "tip"); REQUIRE(combo != nullptr); REQUIRE(tip != nullptr); CHECK_EQ(int(combo->type), int(ui::NodeType::Combo)); @@ -538,7 +536,7 @@ TEST_CASE("UI.p1.comboAndTextWrap") { // Value change routing: pending event → C++ callback + change queue. ui::UIEvent ev; - ev.host = h->handle(); + ev.host = h->handle(); ev.hostName = "p1"; ev.nodeId = "fruit"; ev.kind = "value"; @@ -594,19 +592,17 @@ TEST_CASE("UI.p1.statsAfterHeadlessRender") { TEST_CASE("UI.p1.jsonRoundTripAndGamepadNav") { ui::UI *uimod = ui::UI::create(); REQUIRE(ui::UIHost::resolve( - uimod->mountAs("jsonhost", ui::window( - "W", - { - ui::text("HP 100", "hp").withWrap(120.f), - ui::row({ui::button("A", "a"), ui::spacer("sp"), - ui::button("B", "b")}, "r") - .withGap(8.f), - ui::image("avatar", 64.f, 64.f) - .withTint(0.2f, 0.3f, 0.4f, 0.5f) - .withCornerRadius(4.f), - ui::combo("C", {"x", "y"}, 1, "combo"), - }, - "root"))) + uimod->mountAs( + "jsonhost", + ui::window( + "W", + { + ui::text("HP 100", "hp").withWrap(120.f), + ui::row({ui::button("A", "a"), ui::spacer("sp"), ui::button("B", "b")}, "r").withGap(8.f), + ui::image("avatar", 64.f, 64.f).withTint(0.2f, 0.3f, 0.4f, 0.5f).withCornerRadius(4.f), + ui::combo("C", {"x", "y"}, 1, "combo"), + }, + "root"))) .has_value()); const std::string json = uimod->saveTreeJson(); CHECK(json.find("\"hp\"") != std::string::npos); @@ -632,9 +628,7 @@ TEST_CASE("UI.p1.jsonRoundTripAndGamepadNav") { TEST_CASE("UI.p1.hostPosTween") { ui::UI *uimod = ui::UI::create(); - REQUIRE(ui::UIHost::resolve( - uimod->mountAs("tween", ui::window("T", {ui::text("x", "x")}, "root"))) - .has_value()); + REQUIRE(ui::UIHost::resolve(uimod->mountAs("tween", ui::window("T", {ui::text("x", "x")}, "root"))).has_value()); ui::UIHost *current = resolveHost(uimod->current()); REQUIRE(current != nullptr); // Zero duration → jump immediately. diff --git a/test/UIPhaseB.cpp b/test/UIPhaseB.cpp index ce89ce12..a79a4763 100644 --- a/test/UIPhaseB.cpp +++ b/test/UIPhaseB.cpp @@ -28,9 +28,7 @@ UINode *findNode(UIHost *host, const std::string &id) { return node ? &node->get() : nullptr; } -UINode *findNode(UIHostHandle handle, const std::string &id) { - return findNode(resolveHost(handle), id); -} +UINode *findNode(UIHostHandle handle, const std::string &id) { return findNode(resolveHost(handle), id); } } // namespace @@ -62,7 +60,7 @@ TEST_CASE("UI.b.listAndWhen") { when(true, text("shown", "s")), listButtons("L", {"a", "b", "c"}), }); - UIHost *h = resolveHost(UIHost::createHost("listwhen")); + UIHost *h = resolveHost(UIHost::createHost("listwhen")); REQUIRE(h != nullptr); h->setTree(std::move(tree)); CHECK(findNode(h, "s") != nullptr); @@ -122,9 +120,7 @@ TEST_CASE("UI.b.themeAndCheckbox") { CHECK(!ui->setTheme("unknown")); CHECK(ui->getTheme() == "light"); // unchanged on failure - REQUIRE(UIHost::resolve( - ui->mountAs("chk", window("C", {checkbox("Mute", false, "mute")}, "root"))) - .has_value()); + REQUIRE(UIHost::resolve(ui->mountAs("chk", window("C", {checkbox("Mute", false, "mute")}, "root"))).has_value()); CHECK(findNode(ui->current(), "mute") != nullptr); CHECK(!findNode(ui->current(), "mute")->checked); ui->setChecked("mute", true); diff --git a/test/UISystem.cpp b/test/UISystem.cpp index 6c2f69d4..feca828d 100644 --- a/test/UISystem.cpp +++ b/test/UISystem.cpp @@ -29,7 +29,7 @@ UINode *findNode(UIHost *host, const std::string &id) { TEST_CASE("WidgetDesc.composeNestedTree") { int clicks = 0; - UIHost *h = resolveHost(UIHost::createHost("nest")); + UIHost *h = resolveHost(UIHost::createHost("nest")); REQUIRE(h != nullptr); h->setTree(window( "Inventory", @@ -53,7 +53,7 @@ TEST_CASE("WidgetDesc.composeNestedTree") { REQUIRE_NE(b->handlerClick, 0u); UIEvent ev; - ev.host = h->handle(); + ev.host = h->handle(); ev.hostName = "nest"; ev.nodeId = "b"; ev.kind = "click"; @@ -88,7 +88,7 @@ TEST_CASE("UIHost.setPropsAndClickHandler") { CHECK(h->setClickHandler("btn", [&]() { ++clicks; })); UIEvent ev; - ev.host = h->handle(); + ev.host = h->handle(); ev.hostName = "props"; ev.nodeId = "btn"; ev.kind = "click"; @@ -100,7 +100,7 @@ TEST_CASE("UIHost.setPropsAndClickHandler") { TEST_CASE("UIAutomation.semanticTreeGetAndClick") { int clicks = 0; - UIHost *host = resolveHost(UIHost::createHost("mcp-ui-test")); + UIHost *host = resolveHost(UIHost::createHost("mcp-ui-test")); REQUIRE(host != nullptr); host->setTree(window("Automation", {text("Ready", "status"), button("Add Tree", "asset-tree", [&]() { ++clicks; })})); diff --git a/test/attribute_domain_adapters.cpp b/test/attribute_domain_adapters.cpp index 4e44d194..1ab3385e 100644 --- a/test/attribute_domain_adapters.cpp +++ b/test/attribute_domain_adapters.cpp @@ -30,14 +30,14 @@ void checkSharedModifierContract(const AttributeModifier* modifier) { } // namespace TEST_CASE("attributes.domainAdapters.projectSelectedStats") { - ecs::Table world; + ecs::Table world; ecs::ScopedTable guard(world); auto* card = eve::card::CardData::createCard(); REQUIRE(card != nullptr); card->stats()->attack = 3; card->stats()->health = 5; - auto cardEnsure = eve::card::CardAttributeAdapter::ensure(*card); + auto cardEnsure = eve::card::CardAttributeAdapter::ensure(*card); REQUIRE(cardEnsure.ok()); auto cardModifier = eve::card::CardAttributeAdapter::addModifier( *card, "card.equipment.attack", eve::card::CardAttributeAdapter::attackAttribute, @@ -53,26 +53,24 @@ TEST_CASE("attributes.domainAdapters.projectSelectedStats") { auto cardSnapshot = eve::card::CardAttributeAdapter::snapshot(*card); REQUIRE(cardSnapshot.ok()); auto snapshot = std::move(cardSnapshot).takeValue(); - auto cardSet = eve::card::CardAttributeAdapter::setBase( - *card, eve::card::CardAttributeAdapter::attackAttribute, 9.0); + auto cardSet = + eve::card::CardAttributeAdapter::setBase(*card, eve::card::CardAttributeAdapter::attackAttribute, 9.0); REQUIRE(cardSet.ok()); CHECK_EQ(card->stats()->attack, 9); - auto cardRestore = eve::card::CardAttributeAdapter::restore( - *card, snapshot, card->attributes()->values.revision()); + auto cardRestore = eve::card::CardAttributeAdapter::restore(*card, snapshot, card->attributes()->values.revision()); REQUIRE(cardRestore.ok()); CHECK_EQ(card->stats()->attack, 5); snapshot.owner.generation += 1; - auto cardStale = eve::card::CardAttributeAdapter::restore( - *card, snapshot, card->attributes()->values.revision()); + auto cardStale = eve::card::CardAttributeAdapter::restore(*card, snapshot, card->attributes()->values.revision()); CHECK(!cardStale.ok()); cardStale.ignore("stale-owner assertion"); card->release(); auto* vehicle = eve::vehicle::VehicleEntity::createVehicle(); REQUIRE(vehicle != nullptr); - vehicle->health()->hp = 80.0f; + vehicle->health()->hp = 80.0f; vehicle->health()->maxHp = 100.0f; - auto vehicleEnsure = eve::vehicle::VehicleAttributeAdapter::ensure(*vehicle); + auto vehicleEnsure = eve::vehicle::VehicleAttributeAdapter::ensure(*vehicle); REQUIRE(vehicleEnsure.ok()); auto vehicleModifier = eve::vehicle::VehicleAttributeAdapter::addModifier( *vehicle, "vehicle.equipment.armor", eve::vehicle::VehicleAttributeAdapter::armorAttribute, @@ -83,19 +81,19 @@ TEST_CASE("attributes.domainAdapters.projectSelectedStats") { auto vehicleProject = eve::vehicle::VehicleAttributeAdapter::project(*vehicle); REQUIRE(vehicleProject.ok()); CHECK_EQ(vehicle->health()->hp, 80.0f); - auto vehicleArmor = eve::vehicle::VehicleAttributeAdapter::read( - *vehicle, eve::vehicle::VehicleAttributeAdapter::armorAttribute); + auto vehicleArmor = + eve::vehicle::VehicleAttributeAdapter::read(*vehicle, eve::vehicle::VehicleAttributeAdapter::armorAttribute); REQUIRE(vehicleArmor.ok()); CHECK_EQ(vehicleArmor.value(), 25.0); vehicle->release(); auto* weapon = eve::weapon::WeaponEntity::createWeapon(); REQUIRE(weapon != nullptr); - weapon->state()->resource.kind = eve::weapon::ResourceKind::Mana; + weapon->state()->resource.kind = eve::weapon::ResourceKind::Mana; weapon->state()->resource.value = 10.0f; - weapon->state()->resource.max = 20.0f; - weapon->state()->resource.cost = 3.0f; - auto weaponEnsure = eve::weapon::WeaponAttributeAdapter::ensure(*weapon); + weapon->state()->resource.max = 20.0f; + weapon->state()->resource.cost = 3.0f; + auto weaponEnsure = eve::weapon::WeaponAttributeAdapter::ensure(*weapon); REQUIRE(weaponEnsure.ok()); auto weaponModifier = eve::weapon::WeaponAttributeAdapter::addModifier( *weapon, "weapon.equipment.mana", eve::weapon::WeaponAttributeAdapter::manaAttribute, @@ -114,7 +112,7 @@ TEST_CASE("attributes.domainAdapters.projectSelectedStats") { auto* unit = eve::rts::Unit::createUnit(); REQUIRE(unit != nullptr); unit->motion()->speed = 7.0f; - auto unitEnsure = eve::rts::RTSUnitAttributeAdapter::ensure(*unit); + auto unitEnsure = eve::rts::RTSUnitAttributeAdapter::ensure(*unit); REQUIRE(unitEnsure.ok()); auto unitModifier = eve::rts::RTSUnitAttributeAdapter::addModifier( *unit, "rts.equipment.attack", eve::rts::RTSUnitAttributeAdapter::attackAttribute, @@ -122,8 +120,8 @@ TEST_CASE("attributes.domainAdapters.projectSelectedStats") { eve::attributes::priority::equipment); REQUIRE(unitModifier.ok()); checkSharedModifierContract(unit->attributes()->values.modifierAt(0)); - auto unitAttack = eve::rts::RTSUnitAttributeAdapter::read( - *unit, eve::rts::RTSUnitAttributeAdapter::attackAttribute); + auto unitAttack = + eve::rts::RTSUnitAttributeAdapter::read(*unit, eve::rts::RTSUnitAttributeAdapter::attackAttribute); REQUIRE(unitAttack.ok()); CHECK_EQ(unitAttack.value(), 4.0); auto unsupported = eve::rts::RTSUnitAttributeAdapter::read(*unit, "speed"); diff --git a/test/attributes_rpg_unification.cpp b/test/attributes_rpg_unification.cpp index 1afb9f71..710d11f5 100644 --- a/test/attributes_rpg_unification.cpp +++ b/test/attributes_rpg_unification.cpp @@ -16,16 +16,14 @@ TEST_CASE("attributes.canonicalOperationOrderAndMetadata") { AttributeSet set("actor:one"); set.setBase("power", 100.0); - auto add = set.addModifier( - AttributeModifier{"", "power", "gear", AttributeOperation::Add, 10.0, 0}); - auto additive = set.addModifier( - AttributeModifier{"", "power", "talent", AttributeOperation::AdditivePercent, 0.2, 0}); - auto multiplicative = set.addModifier(AttributeModifier{ - "", "power", "aura", AttributeOperation::MultiplicativePercent, 0.1, 0}); - auto overrideValue = set.addModifier( - AttributeModifier{"", "power", "scenario", AttributeOperation::Override, 50.0, 100}); - auto clamp = set.addModifier( - AttributeModifier{"", "power", "rules", AttributeOperation::ClampMin, 60.0, 200}); + auto add = set.addModifier(AttributeModifier{"", "power", "gear", AttributeOperation::Add, 10.0, 0}); + auto additive = + set.addModifier(AttributeModifier{"", "power", "talent", AttributeOperation::AdditivePercent, 0.2, 0}); + auto multiplicative = + set.addModifier(AttributeModifier{"", "power", "aura", AttributeOperation::MultiplicativePercent, 0.1, 0}); + auto overrideValue = + set.addModifier(AttributeModifier{"", "power", "scenario", AttributeOperation::Override, 50.0, 100}); + auto clamp = set.addModifier(AttributeModifier{"", "power", "rules", AttributeOperation::ClampMin, 60.0, 200}); REQUIRE(add.ok()); REQUIRE(additive.ok()); @@ -46,10 +44,8 @@ TEST_CASE("attributes.generatedIdsAreSetLocalAndCopyDeterministic") { AttributeSet first("actor:first"); AttributeSet second("actor:second"); - auto firstId = first.addModifier( - AttributeModifier{"", "power", "source", AttributeOperation::Add, 1.0}); - auto secondId = second.addModifier( - AttributeModifier{"", "power", "source", AttributeOperation::Add, 1.0}); + auto firstId = first.addModifier(AttributeModifier{"", "power", "source", AttributeOperation::Add, 1.0}); + auto secondId = second.addModifier(AttributeModifier{"", "power", "source", AttributeOperation::Add, 1.0}); REQUIRE(firstId.ok()); REQUIRE(secondId.ok()); CHECK_EQ(firstId.value(), "attribute:modifier:actor:first:1"); @@ -58,10 +54,8 @@ TEST_CASE("attributes.generatedIdsAreSetLocalAndCopyDeterministic") { CHECK_EQ(second.modifierAt(0)->sequence, 1u); AttributeSet restored = first; - auto restoredNext = restored.addModifier( - AttributeModifier{"", "power", "restore", AttributeOperation::Add, 2.0}); - auto firstNext = first.addModifier( - AttributeModifier{"", "power", "next", AttributeOperation::Add, 2.0}); + auto restoredNext = restored.addModifier(AttributeModifier{"", "power", "restore", AttributeOperation::Add, 2.0}); + auto firstNext = first.addModifier(AttributeModifier{"", "power", "next", AttributeOperation::Add, 2.0}); REQUIRE(restoredNext.ok()); REQUIRE(firstNext.ok()); CHECK_EQ(restoredNext.value(), "attribute:modifier:actor:first:2"); @@ -75,8 +69,8 @@ TEST_CASE("attributes.rpgFacadeUsesTheSameAttributeSetTruth") { AttributeSet direct("actor:parity"); direct.setBase("damage", 100.0); - auto directModifier = direct.addModifier( - AttributeModifier{"direct", "damage", "shared", AttributeOperation::AdditivePercent, 0.2}); + auto directModifier = + direct.addModifier(AttributeModifier{"direct", "damage", "shared", AttributeOperation::AdditivePercent, 0.2}); REQUIRE(directModifier.ok()); RPGActor* actor = RPGActor::createActor(); @@ -86,8 +80,7 @@ TEST_CASE("attributes.rpgFacadeUsesTheSameAttributeSetTruth") { REQUIRE(actorModifier.ok()); CHECK_EQ(direct.getFinal("damage"), actor->getFinalAttribute("damage")); - auto bad = actor->addAttributeModifier(AttributeModifier{ - "bad", "", "source", AttributeOperation::Add, 1.0}); + auto bad = actor->addAttributeModifier(AttributeModifier{"bad", "", "source", AttributeOperation::Add, 1.0}); CHECK(!bad.ok()); CHECK_EQ(bad.code(), eve::StatusCode::Rejected); @@ -102,14 +95,14 @@ TEST_CASE("attributes.customPolicyParityUsesCanonicalOperationRegistry") { AttributeSet direct("actor:custom"); direct.setBase("power", 1.0); - auto directModifier = direct.addModifier(AttributeModifier{ - "square", "power", "policy", AttributeOperation::Custom, 4.0, 0, 0, "square"}); + auto directModifier = direct.addModifier( + AttributeModifier{"square", "power", "policy", AttributeOperation::Custom, 4.0, 0, 0, "square"}); REQUIRE(directModifier.ok()); RPGActor* actor = RPGActor::createActor(); actor->attributes()->values.setBase("power", 1.0); - auto actorModifier = actor->addAttributeModifier(AttributeModifier{ - "square", "power", "policy", AttributeOperation::Custom, 4.0, 0, 0, "square"}); + auto actorModifier = actor->addAttributeModifier( + AttributeModifier{"square", "power", "policy", AttributeOperation::Custom, 4.0, 0, 0, "square"}); REQUIRE(actorModifier.ok()); CHECK_EQ(direct.getFinal("power", 0.0, &policies), actor->getFinalAttribute("power")); diff --git a/test/authority.cpp b/test/authority.cpp index 0a3635ab..f0c20722 100644 --- a/test/authority.cpp +++ b/test/authority.cpp @@ -70,13 +70,13 @@ TEST_CASE("authority.snapshot.restoreIsDeterministicAndTransactional") { const auto snapshot = original.snapshotJson(); Store restored; - auto restoredResult = restored.restoreJson(snapshot); + auto restoredResult = restored.restoreJson(snapshot); REQUIRE(restoredResult.ok()); CHECK_EQ(restored.snapshotJson(), snapshot); CHECK(restored.can("a", "s", "read")); CHECK(!restored.can("a", "s", "write")); const auto before = restored.snapshotJson(); - auto rejected = restored.restoreJson("{\"version\":1}"); + auto rejected = restored.restoreJson("{\"version\":1}"); CHECK(!rejected.ok()); const auto* diagnostic = rejected.error(); REQUIRE(diagnostic != nullptr); diff --git a/test/card_play_condition.cpp b/test/card_play_condition.cpp index 7b2c2a6a..e0b03690 100644 --- a/test/card_play_condition.cpp +++ b/test/card_play_condition.cpp @@ -12,28 +12,26 @@ TEST_CASE("card.playConditionAdapterReadsCardAndExternalCapabilities") { Card mod; REQUIRE_EQ(mod.registerCardsFromJson( - R"JSON([{"id":"firebolt","kind":"spell","cost":3,"attack":0,"health":0,"tags":["fire"]}])JSON"), - 1); + R"JSON([{"id":"firebolt","kind":"spell","cost":3,"attack":0,"health":0,"tags":["fire"]}])JSON"), + 1); CardData* card = mod.newCard("firebolt"); REQUIRE(card != nullptr); card->state()->phase = CardState::Hand; CardDefinition local; - local.id = "firebolt"; - local.kind = "spell"; - local.cost = 3; - local.tags = {"fire"}; - int policyCalls = 0; + local.id = "firebolt"; + local.kind = "spell"; + local.cost = 3; + local.tags = {"fire"}; + int policyCalls = 0; CardPlayConditionQueries queries; queries.resource = [](std::string_view key) -> std::optional { return key == "mana" ? std::optional(eve::Value(5)) : std::nullopt; }; queries.authority = [](std::string_view scope) -> std::optional { return scope == "play"; }; - queries.policy = [&policyCalls](std::string_view name, - const eve::Value&) -> std::optional { + queries.policy = [&policyCalls](std::string_view name, const eve::Value&) -> std::optional { ++policyCalls; - return name == "card.safe" ? std::optional(ConditionResult::success()) - : std::nullopt; + return name == "card.safe" ? std::optional(ConditionResult::success()) : std::nullopt; }; const auto condition = Condition::all({ @@ -44,13 +42,13 @@ TEST_CASE("card.playConditionAdapterReadsCardAndExternalCapabilities") { Condition::authorityCheck("play"), Condition::policyCall("card.safe"), }); - const auto result = CardPlayConditionAdapter::evaluate(card, local, condition, queries); + const auto result = CardPlayConditionAdapter::evaluate(card, local, condition, queries); REQUIRE(result.passed()); CHECK_EQ(policyCalls, 1); card->state()->phase = CardState::Played; - const auto blocked = CardPlayConditionAdapter::evaluate( - card, local, Condition::stateEquals("card.phase", "hand"), queries); + const auto blocked = + CardPlayConditionAdapter::evaluate(card, local, Condition::stateEquals("card.phase", "hand"), queries); REQUIRE(!blocked.passed()); CHECK_EQ(static_cast(blocked.reasonCode()), static_cast(ConditionReasonCode::StateMismatch)); } @@ -60,13 +58,11 @@ TEST_CASE("card.playConditionIsStoredOnDefinitionAndEvaluatedByCardFacade") { using namespace eve::decision; Card mod; - REQUIRE_EQ(mod.registerCardsFromJson( - R"JSON([{"id":"ward","kind":"spell","cost":2,"tags":["ward"]}])JSON"), - 1); + REQUIRE_EQ(mod.registerCardsFromJson(R"JSON([{"id":"ward","kind":"spell","cost":2,"tags":["ward"]}])JSON"), 1); CardData* card = mod.newCard("ward"); REQUIRE(card != nullptr); card->state()->phase = CardState::Hand; - auto stored = mod.setCardPlayCondition("ward", Condition::hasTag("missing")); + auto stored = mod.setCardPlayCondition("ward", Condition::hasTag("missing")); REQUIRE(stored.ok()); const auto rejected = mod.evaluatePlay(card); REQUIRE(!rejected.passed()); diff --git a/test/card_rts_production_integration.cpp b/test/card_rts_production_integration.cpp index 6eed5433..3580f236 100644 --- a/test/card_rts_production_integration.cpp +++ b/test/card_rts_production_integration.cpp @@ -2,8 +2,8 @@ #include "zeroerr/unittest.h" #include "attributes/AttributeSetResourceAccount.h" -#include "card/CardAttributes.h" #include "card/Card.h" +#include "card/CardAttributes.h" #include "card/CardContainers.h" #include "common/ECS.h" #include "economy/EconomyLedger.h" @@ -29,9 +29,9 @@ eve::SimulationStep step(std::uint64_t tick, double seconds) { eve::effects::EffectPolicy oneShotPolicy() { eve::effects::EffectPolicy policy; - policy.stackMode = eve::effects::StackMode::NewInstance; + policy.stackMode = eve::effects::StackMode::NewInstance; policy.stackCount = eve::effects::StackCountPolicy::Keep; - policy.maxStacks = 1; + policy.maxStacks = 1; return policy; } @@ -39,9 +39,9 @@ eve::effects::EffectPolicy oneShotPolicy() { TEST_CASE("production.cardFacadeRoutesAttributesEffectsAndPlay") { eve::card::Card cards; - REQUIRE_EQ(cards.registerCardsFromJson( - R"JSON([{"id":"soldier","kind":"creature","cost":2,"attack":4,"health":10}])JSON"), - 1); + REQUIRE_EQ( + cards.registerCardsFromJson(R"JSON([{"id":"soldier","kind":"creature","cost":2,"attack":4,"health":10}])JSON"), + 1); auto *deck = cards.newDeck(); auto *hand = cards.newHand(cards.newConfig()); auto *card = cards.newCard("soldier"); @@ -50,22 +50,20 @@ TEST_CASE("production.cardFacadeRoutesAttributesEffectsAndPlay") { REQUIRE(card != nullptr); deck->push(card); - auto base = cards.setCardAttribute( - *card, eve::card::CardAttributeAdapter::attackAttribute, 7.0); + auto base = cards.setCardAttribute(*card, eve::card::CardAttributeAdapter::attackAttribute, 7.0); REQUIRE(base.ok()); - auto attack = cards.getCardAttribute( - *card, eve::card::CardAttributeAdapter::attackAttribute); + auto attack = cards.getCardAttribute(*card, eve::card::CardAttributeAdapter::attackAttribute); REQUIRE(attack.ok()); CHECK_EQ(attack.value(), 7.0); eve::card::CardEffectDefinition damage; - damage.id = "poison"; - damage.source = "card.facade.test"; - damage.period = 1.0; - damage.duration = 2.0; + damage.id = "poison"; + damage.source = "card.facade.test"; + damage.period = 1.0; + damage.duration = 2.0; damage.magnitude = 2.0; - damage.policy = oneShotPolicy(); - auto effect = cards.applyEffect(*card, damage, subject("00000000-0000-7000-8000-000000000101")); + damage.policy = oneShotPolicy(); + auto effect = cards.applyEffect(*card, damage, subject("00000000-0000-7000-8000-000000000101")); REQUIRE(effect.ok()); auto effectHandle = std::move(effect).takeValue(); CHECK_EQ(card->effects()->values.count(), 1u); @@ -75,8 +73,7 @@ TEST_CASE("production.cardFacadeRoutesAttributesEffectsAndPlay") { const auto advancedCount = std::move(advanced).takeValue(); CHECK_EQ(advancedCount, 1u); CHECK_EQ(card->effects()->values.target().health, 8); - auto health = cards.getCardAttribute( - *card, eve::card::CardAttributeAdapter::healthAttribute); + auto health = cards.getCardAttribute(*card, eve::card::CardAttributeAdapter::healthAttribute); REQUIRE(health.ok()); CHECK_EQ(health.value(), 8.0); REQUIRE(card->effects()->values.resolve(effectHandle).ok()); @@ -84,16 +81,14 @@ TEST_CASE("production.cardFacadeRoutesAttributesEffectsAndPlay") { eve::attributes::AttributeSet playerAttributes; playerAttributes.setBase("mana", 10.0); eve::attributes::AttributeSetResourceAccount account(playerAttributes); - eve::card::CardContainerAdapter source( - eve::container::ContainerId("card:facade-deck"), - eve::card::CardContainerKind::Deck, deck); - eve::card::CardContainerAdapter destination( - eve::container::ContainerId("card:facade-hand"), - eve::card::CardContainerKind::Hand, nullptr, hand); - eve::card::CardPlayComposition composition; - composition.source = &source; - composition.destination = &destination; - composition.sourceSlot = eve::container::SlotIndex(0); + eve::card::CardContainerAdapter source(eve::container::ContainerId("card:facade-deck"), + eve::card::CardContainerKind::Deck, deck); + eve::card::CardContainerAdapter destination(eve::container::ContainerId("card:facade-hand"), + eve::card::CardContainerKind::Hand, nullptr, hand); + eve::card::CardPlayComposition composition; + composition.source = &source; + composition.destination = &destination; + composition.sourceSlot = eve::container::SlotIndex(0); composition.destinationSlot = eve::container::SlotIndex(0); auto played = cards.play(*card, account, std::move(composition), "card.facade.play"); @@ -102,45 +97,40 @@ TEST_CASE("production.cardFacadeRoutesAttributesEffectsAndPlay") { CHECK_EQ(deck->count(), 0); CHECK_EQ(hand->count(), 1); CHECK_EQ(playerAttributes.getBase("mana"), 8.0); - CHECK_EQ(static_cast(card->state()->phase), - static_cast(eve::card::CardState::Played)); + CHECK_EQ(static_cast(card->state()->phase), static_cast(eve::card::CardState::Played)); } TEST_CASE("production.rtsFacadeRoutesCanonicalAttributesEffectsAndBuild") { eve::rts::RTS rts; - auto unitResult = rts.newUnit( - subject("00000000-0000-7000-8000-000000000102")); + auto unitResult = rts.newUnit(subject("00000000-0000-7000-8000-000000000102")); REQUIRE(unitResult.ok()); - auto *unit = std::move(unitResult).takeValue(); - auto buildingResult = rts.newBuilding( - subject("00000000-0000-7000-8000-000000000103")); + auto *unit = std::move(unitResult).takeValue(); + auto buildingResult = rts.newBuilding(subject("00000000-0000-7000-8000-000000000103")); REQUIRE(buildingResult.ok()); auto *building = std::move(buildingResult).takeValue(); - auto setAttack = rts.setUnitAttribute( - *unit, eve::rts::RTSUnitAttributeAdapter::attackAttribute, 12.0); + auto setAttack = rts.setUnitAttribute(*unit, eve::rts::RTSUnitAttributeAdapter::attackAttribute, 12.0); REQUIRE(setAttack.ok()); - auto attack = rts.readUnitAttribute( - *unit, eve::rts::RTSUnitAttributeAdapter::attackAttribute); + auto attack = rts.readUnitAttribute(*unit, eve::rts::RTSUnitAttributeAdapter::attackAttribute); REQUIRE(attack.ok()); CHECK_EQ(attack.value(), 12.0); eve::rts::RTSEffectDefinition suppression; - suppression.id = "suppression"; - suppression.source = "rts.facade.test"; - suppression.period = 1.0; - suppression.duration = 2.0; + suppression.id = "suppression"; + suppression.source = "rts.facade.test"; + suppression.period = 1.0; + suppression.duration = 2.0; suppression.magnitude = 1.0; - suppression.kind = eve::rts::RTSEffectKind::Suppression; - suppression.policy = oneShotPolicy(); - auto effect = rts.applyEffect(*unit, suppression); + suppression.kind = eve::rts::RTSEffectKind::Suppression; + suppression.policy = oneShotPolicy(); + auto effect = rts.applyEffect(*unit, suppression); REQUIRE(effect.ok()); auto effectHandle = std::move(effect).takeValue(); CHECK_EQ(unit->effects()->values.count(), 1u); eve::action::ActionRuntime action; - eve::rts::ActionAdapter actionAdapter(action); - auto advanced = rts.step(step(1, 1.0), actionAdapter); + eve::rts::ActionAdapter actionAdapter(action); + auto advanced = rts.step(step(1, 1.0), actionAdapter); REQUIRE(advanced.ok()); std::move(advanced).takeValue(); CHECK_EQ(unit->effects()->values.target().suppression, 1u); @@ -149,15 +139,14 @@ TEST_CASE("production.rtsFacadeRoutesCanonicalAttributesEffectsAndBuild") { REQUIRE(unit->effects()->values.remove(effectHandle).ok()); eve::economy::EconomyLedger ledger; - auto credited = ledger.credit("gold", 10); + auto credited = ledger.credit("gold", 10); REQUIRE(credited == 10); eve::rts::RTSEconomyAdapter economy(ledger); - auto cost = eve::resource::CostSpec::single("gold", 3); + auto cost = eve::resource::CostSpec::single("gold", 3); REQUIRE(cost.ok()); auto duration = eve::Duration::fromSeconds(1.0); REQUIRE(duration.ok()); - auto built = rts.build(*building, action, economy.account(), - std::move(cost).takeValue(), "worker", + auto built = rts.build(*building, action, economy.account(), std::move(cost).takeValue(), "worker", std::move(duration).takeValue()); REQUIRE(built.ok()); std::move(built).takeValue(); @@ -168,8 +157,7 @@ TEST_CASE("production.rtsFacadeRoutesCanonicalAttributesEffectsAndBuild") { auto insufficientCost = eve::resource::CostSpec::single("gold", 20); REQUIRE(insufficientCost.ok()); - auto failed = rts.build(*building, action, economy.account(), - std::move(insufficientCost).takeValue(), "tank", + auto failed = rts.build(*building, action, economy.account(), std::move(insufficientCost).takeValue(), "tank", eve::Duration::fromSeconds(1.0).expect("test build duration")); CHECK(!failed.ok()); CHECK_EQ(ledger.get("gold"), 7); diff --git a/test/cmdline.cpp b/test/cmdline.cpp index 63072c0e..9776eb0c 100644 --- a/test/cmdline.cpp +++ b/test/cmdline.cpp @@ -695,8 +695,8 @@ TEST_CASE("cmdline.getAndroidEmptyReleaseResponseReturnsStructuredError") { // A successful HTTP/file transfer may still contain zero bytes (for // example, a truncated mirror response). The installer must reject that // response before ZIP parsing and expose a diagnostic through the C++ API. - const std::string tag = eve::cmd::sdk::sdkVersionTag(); - const auto rel = tempDir("eve_ut_cmdline_release_empty"); + const std::string tag = eve::cmd::sdk::sdkVersionTag(); + const auto rel = tempDir("eve_ut_cmdline_release_empty"); const auto zipPath = rel / ("eve-sdk-android-" + tag + ".zip"); writeFile(zipPath, std::string{}); writeFile(rel / "SHA256SUMS", ""); @@ -708,7 +708,7 @@ TEST_CASE("cmdline.getAndroidEmptyReleaseResponseReturnsStructuredError") { { CaptureStreams cap; - auto result = eve::cmd::sdk::installEveSdk(eve::cmd::sdk::Platform::Android); + auto result = eve::cmd::sdk::installEveSdk(eve::cmd::sdk::Platform::Android); REQUIRE(!result.ok()); CHECK(result.code() == eve::StatusCode::Failed); const auto* diagnostic = result.error(); diff --git a/test/common_identity_version.cpp b/test/common_identity_version.cpp index 47dafe6c..690daea1 100644 --- a/test/common_identity_version.cpp +++ b/test/common_identity_version.cpp @@ -1,9 +1,9 @@ #include "zeroerr/assert.h" #include "zeroerr/unittest.h" -#include "common/Identity.h" #include "common/EventSequence.h" #include "common/Generation.h" +#include "common/Identity.h" #include "common/Revision.h" #include "common/SchemaVersion.h" #include "common/Time.h" @@ -59,9 +59,7 @@ TEST_CASE("common.identity.uuidV7UsesInjectedEntropyAndClock") { CHECK_EQ(bytes[6] & 0xf0u, 0x70u); CHECK_EQ(bytes[8] & 0xc0u, 0x80u); - eve::UuidV7Generator unavailable([](std::span) { return false; }, [timestamp] { - return timestamp; - }); + eve::UuidV7Generator unavailable([](std::span) { return false; }, [timestamp] { return timestamp; }); CHECK(!unavailable.generate().has_value()); const auto negative = generator.generate(std::chrono::system_clock::time_point{} - 1ms); @@ -83,7 +81,7 @@ TEST_CASE("common.identity.logicalAndContentIdsHaveSeparateBoundaries") { CHECK(!eve::LogicalId::parse("rpg/skill:fire").has_value()); const std::array digest{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; - const auto content = eve::ContentId::fromBytes(digest); + const auto content = eve::ContentId::fromBytes(digest); REQUIRE(content.has_value()); CHECK(!content->isNil()); CHECK_EQ(content->format(), "00010203-0405-0607-0809-0a0b0c0d0e0f"); @@ -97,7 +95,7 @@ TEST_CASE("common.identity.logicalAndContentIdsHaveSeparateBoundaries") { TEST_CASE("common.versionTypesDoNotWrapOrMixDomains") { const eve::SchemaVersion schemaVersion(7); - const eve::Generation generation(7); + const eve::Generation generation(7); CHECK_EQ(schemaVersion.value(), 7u); CHECK_EQ(generation.value(), 7u); CHECK(schemaVersion != eve::SchemaVersion(8)); @@ -111,7 +109,7 @@ TEST_CASE("common.versionTypesDoNotWrapOrMixDomains") { CHECK(!maximum.incremented().has_value()); const eve::EventSequence sequence(3); - const eve::FrameIndex frame(4); + const eve::FrameIndex frame(4); CHECK_EQ(sequence.value(), 3u); CHECK_EQ(frame.value(), 4u); diff --git a/test/common_subscription.cpp b/test/common_subscription.cpp index f7b84661..f87014f9 100644 --- a/test/common_subscription.cpp +++ b/test/common_subscription.cpp @@ -13,7 +13,7 @@ TEST_CASE("common.subscriptionIsMoveOnlyAndCancellationIsIdempotent") { static_assert(std::is_move_constructible_v); static_assert(std::is_move_assignable_v); - int cancellations = 0; + int cancellations = 0; eve::Subscription original([&cancellations]() noexcept { ++cancellations; }); eve::Subscription moved(std::move(original)); CHECK(original.disposed()); @@ -32,7 +32,7 @@ TEST_CASE("common.observerAllowsReentrantSubscribeAndDispose") { eve::Subscription second; eve::Subscription added; - first = observer.subscribe([&](const int &value) { + first = observer.subscribe([&](const int &value) { calls.push_back(value * 10 + 1); second.dispose(); first.dispose(); diff --git a/test/condition.cpp b/test/condition.cpp index 49cdac5f..41d224b7 100644 --- a/test/condition.cpp +++ b/test/condition.cpp @@ -11,12 +11,12 @@ namespace { class TestContext final : public eve::decision::EvaluationContext { public: - std::map values; - std::map tags; - std::map attributes; - std::map resources; - std::map states; - std::map authorities; + std::map values; + std::map tags; + std::map attributes; + std::map resources; + std::map states; + std::map authorities; std::map policies; [[nodiscard]] std::optional value(std::string_view key) const override { @@ -43,11 +43,10 @@ public: auto it = authorities.find(std::string(scope)); return it == authorities.end() ? std::nullopt : std::optional(it->second); } - [[nodiscard]] std::optional policy( - std::string_view name, const eve::Value&) const override { + [[nodiscard]] std::optional policy(std::string_view name, + const eve::Value&) const override { auto it = policies.find(std::string(name)); - return it == policies.end() ? std::nullopt - : std::optional(it->second); + return it == policies.end() ? std::nullopt : std::optional(it->second); } }; @@ -73,7 +72,7 @@ TEST_CASE("decision.condition.supportsAllLeafNodes") { Condition::authorityCheck("cast"), Condition::policyCall("safe"), }); - const auto result = condition.evaluate(context); + const auto result = condition.evaluate(context); REQUIRE(result.passed()); CHECK_EQ(static_cast(result.reasonCode()), static_cast(ConditionReasonCode::Passed)); CHECK(result.details().isObject()); @@ -87,14 +86,14 @@ TEST_CASE("decision.condition.compoundsExplainFailureWithoutMutation") { context.states.emplace("phase", "ready"); const auto before = context.values; - const auto any = Condition::any({Condition::compare("level", CompareOperator::Greater, 9), - Condition::hasTag("fire")}); + const auto any = + Condition::any({Condition::compare("level", CompareOperator::Greater, 9), Condition::hasTag("fire")}); const auto anyResult = any.evaluate(context); REQUIRE(!anyResult.passed()); CHECK_EQ(static_cast(anyResult.reasonCode()), static_cast(ConditionReasonCode::NoChildPassed)); CHECK(anyResult.details().isObject()); - const auto negated = Condition::not_(Condition::stateEquals("phase", "ready")); + const auto negated = Condition::not_(Condition::stateEquals("phase", "ready")); const auto notResult = negated.evaluate(context); REQUIRE(!notResult.passed()); CHECK_EQ(static_cast(notResult.reasonCode()), static_cast(ConditionReasonCode::Negated)); @@ -104,11 +103,10 @@ TEST_CASE("decision.condition.compoundsExplainFailureWithoutMutation") { TEST_CASE("decision.condition.scriptDeclarationIsExplicit") { using namespace eve::decision; ScriptConditionDeclaration declaration; - declaration.name = "game.canCast"; + declaration.name = "game.canCast"; declaration.dependencies = {"actor.mana", "world.tick"}; - declaration.determinism = DeterminismLevel::TickDeterministic; - const auto condition = Condition::policyCall("game.canCast", eve::Value::Object{{"mode", "safe"}}, - declaration); + declaration.determinism = DeterminismLevel::TickDeterministic; + const auto condition = Condition::policyCall("game.canCast", eve::Value::Object{{"mode", "safe"}}, declaration); REQUIRE(condition.isValid()); REQUIRE(condition.scriptDeclaration().has_value()); CHECK_EQ(condition.scriptDeclaration()->name, "game.canCast"); @@ -120,8 +118,8 @@ TEST_CASE("decision.condition.scriptDeclarationIsExplicit") { TEST_CASE("decision.conditionUnavailableIsDifferentFromFalse") { using namespace eve::decision; TestContext context; - const auto tag = Condition::hasTag("missing").evaluate(context); - const auto compare = Condition::compare("missing", CompareOperator::Equal, 1).evaluate(context); + const auto tag = Condition::hasTag("missing").evaluate(context); + const auto compare = Condition::compare("missing", CompareOperator::Equal, 1).evaluate(context); REQUIRE(!tag.passed()); REQUIRE(!compare.passed()); CHECK_EQ(static_cast(tag.reasonCode()), static_cast(ConditionReasonCode::TagUnavailable)); diff --git a/test/container_adapters.cpp b/test/container_adapters.cpp index f5cc103b..62447712 100644 --- a/test/container_adapters.cpp +++ b/test/container_adapters.cpp @@ -25,7 +25,7 @@ void registerTestBuilding(const char* id) { eve::building::BuildingRegistry::clear(); eve::building::PlacementSystem::ensureBuiltins(); eve::building::BuildingDefinition definition; - definition.id = id; + definition.id = id; definition.footprintW = 1; definition.footprintH = 1; eve::building::BuildingRegistry::registerBuilding(definition); @@ -40,38 +40,34 @@ TEST_CASE("container.adaptersShareParityAndCanonicalLifecycleEnvelope") { static_assert(!std::is_convertible_v, Coordinate>); static_assert(!std::is_convertible_v, Coordinate>); - auto zone = Zone::create( - ContainerId("zone:world2d"), Rectangle{{0.f, 0.f}, 10.f, 10.f}, - Capacity::fixed(3)); + auto zone = Zone::create(ContainerId("zone:world2d"), Rectangle{{0.f, 0.f}, 10.f, 10.f}, + Capacity::fixed(3)); REQUIRE(zone.ok()); CHECK_EQ(zone.value().id().value(), std::string("zone:world2d")); CHECK(zone.value().contains(Coordinate{5.f, 5.f})); - ecs::Table ecsWorld; + ecs::Table ecsWorld; ecs::ScopedTable guard(ecsWorld); - auto* vehicle = eve::vehicle::VehicleEntity::create(); - vehicle->identity()->id = "vehicle:test"; + auto* vehicle = eve::vehicle::VehicleEntity::create(); + vehicle->identity()->id = "vehicle:test"; vehicle->seats()->list.resize(2); - eve::vehicle::VehicleSeatContainerAdapter seats( - ContainerId("vehicle:seat:test"), vehicle, AcceptedCondition{}); + eve::vehicle::VehicleSeatContainerAdapter seats(ContainerId("vehicle:seat:test"), vehicle, AcceptedCondition{}); registerTestBuilding("garrison.test"); eve::building::PlacementWorld placement(4, 4, 1.f); placement.setId("world:test"); const int buildingId = placement.placeAt("garrison.test", 0, 0); REQUIRE(buildingId > 0); - eve::building::BuildingGarrisonContainerAdapter garrison( - ContainerId("building:garrison:test"), &placement, buildingId, Capacity::fixed(3)); + eve::building::BuildingGarrisonContainerAdapter garrison(ContainerId("building:garrison:test"), &placement, + buildingId, Capacity::fixed(3)); - CHECK_EQ(static_cast(seats.descriptor().ordering), - static_cast(Ordering::ExplicitSlots)); - CHECK_EQ(static_cast(garrison.descriptor().ordering), - static_cast(Ordering::Insertion)); + CHECK_EQ(static_cast(seats.descriptor().ordering), static_cast(Ordering::ExplicitSlots)); + CHECK_EQ(static_cast(garrison.descriptor().ordering), static_cast(Ordering::Insertion)); CHECK_EQ(seats.descriptor().capacity.value(), std::size_t(2)); CHECK_EQ(garrison.descriptor().capacity.value(), std::size_t(3)); std::vector eventTypes; - auto sink = [&](const eve::game_event::GameEvent& event) { + auto sink = [&](const eve::game_event::GameEvent& event) { CHECK(!event.eventId.isNil()); CHECK_EQ(event.schemaId.format(), std::string("container:event")); CHECK_EQ(event.schemaVersion.value(), 1u); @@ -87,7 +83,7 @@ TEST_CASE("container.adaptersShareParityAndCanonicalLifecycleEnvelope") { CHECK_EQ(eventTypes[1], std::string("container.enter")); const auto revisionBeforeReject = seats.revision(); - auto rejected = seats.enter(SlotIndex(0), 8, sink, eve::SimulationTick(17)); + auto rejected = seats.enter(SlotIndex(0), 8, sink, eve::SimulationTick(17)); CHECK(!rejected.ok()); CHECK_EQ(eventTypes.back(), std::string("container.rejected")); CHECK_EQ(seats.revision(), revisionBeforeReject); @@ -95,19 +91,16 @@ TEST_CASE("container.adaptersShareParityAndCanonicalLifecycleEnvelope") { CHECK_EQ(seats.snapshot().value().entries.size(), std::size_t(1)); auto callbackFailure = seats.enter( - SlotIndex(1), 8, - [](const eve::game_event::GameEvent&) { throw std::runtime_error("observer"); }, + SlotIndex(1), 8, [](const eve::game_event::GameEvent&) { throw std::runtime_error("observer"); }, eve::SimulationTick(18)); REQUIRE(callbackFailure.ok()); - CHECK_EQ(static_cast(callbackFailure.code()), - static_cast(eve::StatusCode::Applied)); + CHECK_EQ(static_cast(callbackFailure.code()), static_cast(eve::StatusCode::Applied)); CHECK(callbackFailure.status().hasDiagnostics()); CHECK(vehicle->seats()->list[1].occupied); const auto oldGarrison = garrison.snapshot(); REQUIRE(oldGarrison.ok()); - auto garrisoned = garrison.enter("unit:one", "rts.unit", {"infantry"}, {}, - eve::SimulationTick(19)); + auto garrisoned = garrison.enter("unit:one", "rts.unit", {"infantry"}, {}, eve::SimulationTick(19)); REQUIRE(garrisoned.ok()); CHECK_EQ(garrison.snapshot().value().entries.size(), std::size_t(1)); auto stalePrepare = garrison.prepare(oldGarrison.value(), oldGarrison.value()); @@ -115,15 +108,14 @@ TEST_CASE("container.adaptersShareParityAndCanonicalLifecycleEnvelope") { CHECK_EQ(garrison.snapshot().value().entries.size(), std::size_t(1)); std::vector garrisonEventTypes; - auto garrisonSink = [&](const eve::game_event::GameEvent& event) { + auto garrisonSink = [&](const eve::game_event::GameEvent& event) { CHECK_EQ(event.schemaId.format(), std::string("container:event")); CHECK_EQ(event.source, std::string("building:garrison:test")); CHECK_EQ(event.subject, std::string("unit:one")); CHECK_EQ(event.tick, eve::SimulationTick(20)); garrisonEventTypes.push_back(event.type); }; - auto exited = garrison.exit(MembershipId("unit:one"), garrisonSink, - eve::SimulationTick(20)); + auto exited = garrison.exit(MembershipId("unit:one"), garrisonSink, eve::SimulationTick(20)); REQUIRE(exited.ok()); CHECK_EQ(garrisonEventTypes.size(), std::size_t(2)); CHECK_EQ(garrisonEventTypes[0], std::string("container.accepted")); @@ -132,15 +124,15 @@ TEST_CASE("container.adaptersShareParityAndCanonicalLifecycleEnvelope") { // Cross-adapter transfer uses only common ContainerObject facts; neither // adapter needs to include the other's module or take ownership of it. auto movedToGarrison = eve::container::TransferService::transfer( - TransferRequest{&seats, &garrison, MembershipId("vehicle:occupant:7"), SlotIndex(0), - std::nullopt, seats.revision(), garrison.revision()}); + TransferRequest{&seats, &garrison, MembershipId("vehicle:occupant:7"), SlotIndex(0), std::nullopt, + seats.revision(), garrison.revision()}); REQUIRE(movedToGarrison.ok()); CHECK(!vehicle->seats()->list[0].occupied); CHECK_EQ(garrison.snapshot().value().entries.size(), std::size_t(2)); auto movedBack = eve::container::TransferService::transfer( - TransferRequest{&garrison, &seats, MembershipId("vehicle:occupant:7"), std::nullopt, - SlotIndex(0), garrison.revision(), seats.revision()}); + TransferRequest{&garrison, &seats, MembershipId("vehicle:occupant:7"), std::nullopt, SlotIndex(0), + garrison.revision(), seats.revision()}); REQUIRE(movedBack.ok()); CHECK(vehicle->seats()->list[0].occupied); CHECK_EQ(vehicle->seats()->list[0].occupant, 7); @@ -151,10 +143,10 @@ TEST_CASE("container.adaptersShareParityAndCanonicalLifecycleEnvelope") { TEST_CASE("container.adaptersFailureInjectionLeavesMembershipUnchanged") { registerTestBuilding("garrison.failure"); eve::building::PlacementWorld placement(2, 2, 1.f); - const int buildingId = placement.placeAt("garrison.failure", 0, 0); + const int buildingId = placement.placeAt("garrison.failure", 0, 0); REQUIRE(buildingId > 0); - eve::building::BuildingGarrisonContainerAdapter garrison( - ContainerId("building:garrison:failure"), &placement, buildingId, Capacity::fixed(1)); + eve::building::BuildingGarrisonContainerAdapter garrison(ContainerId("building:garrison:failure"), &placement, + buildingId, Capacity::fixed(1)); REQUIRE(garrison.enter("unit:one").ok()); const auto before = garrison.snapshot(); REQUIRE(before.ok()); diff --git a/test/container_transfer.cpp b/test/container_transfer.cpp index bc29058c..b04b612b 100644 --- a/test/container_transfer.cpp +++ b/test/container_transfer.cpp @@ -16,6 +16,7 @@ namespace { +using eve::Revision; using eve::container::Capacity; using eve::container::ContainerId; using eve::container::ContainerObject; @@ -24,10 +25,9 @@ using eve::container::IContainer; using eve::container::Membership; using eve::container::MembershipEntry; using eve::container::MembershipId; -using eve::Revision; using eve::container::SlotIndex; -ContainerId cid(const char* value) { return ContainerId(value); } +ContainerId cid(const char* value) { return ContainerId(value); } MembershipId mid(const char* value) { return MembershipId(value); } /** @brief Failing second participant used to prove prepare has no half-state. */ @@ -39,29 +39,26 @@ public: return descriptor_; } [[nodiscard]] eve::Result snapshot() const override { - return eve::Result::success( - ContainerSnapshot{descriptor_.id, revision_, entries_}); + return eve::Result::success(ContainerSnapshot{descriptor_.id, revision_, entries_}); } - [[nodiscard]] eve::Result validateInsert( - const ContainerObject&, std::optional, - std::optional) const override { + [[nodiscard]] eve::Result validateInsert(const ContainerObject&, std::optional, + std::optional) const override { return eve::Result::success(); } - [[nodiscard]] eve::Result> prepare( - const ContainerSnapshot&, const ContainerSnapshot&) override { + [[nodiscard]] eve::Result> prepare(const ContainerSnapshot&, + const ContainerSnapshot&) override { ++prepareCalls_; return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Failed, - "injected destination prepare failure")); + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "injected destination prepare failure")); } [[nodiscard]] int prepareCalls() const noexcept { return prepareCalls_; } private: eve::container::ContainerDescriptor descriptor_; - Revision revision_ = Revision(0); - std::vector entries_; - int prepareCalls_ = 0; + Revision revision_ = Revision(0); + std::vector entries_; + int prepareCalls_ = 0; }; } // namespace @@ -71,23 +68,19 @@ TEST_CASE("container.zoneUsesStrongCoordinateSpacesAndValidatesShapes") { static_assert(!std::is_convertible_v, Coordinate>); static_assert(!std::is_convertible_v, Coordinate>); - auto screen = Zone::create( - Rectangle{{10.f, 20.f}, 100.f, 50.f}, Capacity::fixed(2)); + auto screen = Zone::create(Rectangle{{10.f, 20.f}, 100.f, 50.f}, Capacity::fixed(2)); REQUIRE(screen.ok()); CHECK(screen.value().contains({20.f, 30.f})); CHECK(!screen.value().contains({0.f, 0.f})); - auto invalid2d = Zone::create( - Rectangle{{0.f, 0.f}, -1.f, 2.f}); + auto invalid2d = Zone::create(Rectangle{{0.f, 0.f}, -1.f, 2.f}); CHECK(!invalid2d.ok()); CHECK_EQ(invalid2d.code(), eve::StatusCode::Rejected); - auto invalid3d = Zone::create( - Box3D{{0.f, 0.f, 0.f}, 1.f, 2.f, -3.f}); + auto invalid3d = Zone::create(Box3D{{0.f, 0.f, 0.f}, 1.f, 2.f, -3.f}); CHECK(!invalid3d.ok()); - auto sphere = Zone::create( - Sphere3D{{0.f, 0.f, 0.f}, 2.f}); + auto sphere = Zone::create(Sphere3D{{0.f, 0.f, 0.f}, 2.f}); REQUIRE(sphere.ok()); CHECK(sphere.value().contains({1.f, 1.f, 1.f})); CHECK(!sphere.value().contains({2.f, 2.f, 2.f})); @@ -95,9 +88,7 @@ TEST_CASE("container.zoneUsesStrongCoordinateSpacesAndValidatesShapes") { TEST_CASE("container.cardDeckHandDiscardTransferEmitsOneStrongEvent") { eve::card::Card card; - REQUIRE_EQ(card.registerCardsFromJson( - R"([{"id":"creature","kind":"creature"},{"id":"spell","kind":"spell"}])"), - 2); + REQUIRE_EQ(card.registerCardsFromJson(R"([{"id":"creature","kind":"creature"},{"id":"spell","kind":"spell"}])"), 2); auto* deck = card.newDeck(); auto* hand = card.newHand(card.newConfig()); REQUIRE(deck != nullptr); @@ -106,15 +97,13 @@ TEST_CASE("container.cardDeckHandDiscardTransferEmitsOneStrongEvent") { REQUIRE(creature != nullptr); deck->push(creature); - eve::card::CardContainerAdapter source(cid("card:deck"), eve::card::CardContainerKind::Deck, - deck); - eve::card::CardContainerAdapter destination( - cid("card:hand"), eve::card::CardContainerKind::Hand, nullptr, hand, - nullptr, Capacity::fixed(2)); + eve::card::CardContainerAdapter source(cid("card:deck"), eve::card::CardContainerKind::Deck, deck); + eve::card::CardContainerAdapter destination(cid("card:hand"), eve::card::CardContainerKind::Hand, nullptr, hand, + nullptr, Capacity::fixed(2)); std::vector events; - eve::container::TransferRequest request{&source, &destination, - mid(creature->identity()->id.c_str()), SlotIndex(0), - SlotIndex(0), source.revision(), destination.revision()}; + eve::container::TransferRequest request{ + &source, &destination, mid(creature->identity()->id.c_str()), SlotIndex(0), SlotIndex(0), + source.revision(), destination.revision()}; auto transferred = eve::container::TransferService::transfer( request, [&](const eve::container::TransferEvent& event) { events.push_back(event); }); REQUIRE(transferred.ok()); @@ -125,12 +114,11 @@ TEST_CASE("container.cardDeckHandDiscardTransferEmitsOneStrongEvent") { CHECK_EQ(events.front().destination.value(), std::string("card:hand")); std::vector discard; - eve::card::CardContainerAdapter discardAdapter( - cid("card:discard"), eve::card::CardContainerKind::Discard, nullptr, nullptr, &discard); - auto toDiscard = eve::container::TransferService::transfer( - eve::container::TransferRequest{&destination, &discardAdapter, - mid(creature->identity()->id.c_str()), SlotIndex(0), - std::nullopt, destination.revision(), discardAdapter.revision()}, + eve::card::CardContainerAdapter discardAdapter(cid("card:discard"), eve::card::CardContainerKind::Discard, nullptr, + nullptr, &discard); + auto toDiscard = eve::container::TransferService::transfer( + eve::container::TransferRequest{&destination, &discardAdapter, mid(creature->identity()->id.c_str()), + SlotIndex(0), std::nullopt, destination.revision(), discardAdapter.revision()}, [](const eve::container::TransferEvent&) { throw std::runtime_error("observer failure"); }); REQUIRE(toDiscard.ok()); CHECK_EQ(toDiscard.code(), eve::StatusCode::Applied); @@ -143,24 +131,19 @@ TEST_CASE("container.cardDeckHandDiscardTransferEmitsOneStrongEvent") { TEST_CASE("container.cardTransferRejectsFilterAndDuplicateWithoutMutation") { eve::card::Card card; - REQUIRE_EQ(card.registerCardsFromJson( - R"([{"id":"creature","kind":"creature"},{"id":"spell","kind":"spell"}])"), - 2); - auto* deck = card.newDeck(); - auto* hand = card.newHand(card.newConfig()); + REQUIRE_EQ(card.registerCardsFromJson(R"([{"id":"creature","kind":"creature"},{"id":"spell","kind":"spell"}])"), 2); + auto* deck = card.newDeck(); + auto* hand = card.newHand(card.newConfig()); auto* creature = card.newCard("creature"); REQUIRE(deck != nullptr); REQUIRE(hand != nullptr); REQUIRE(creature != nullptr); deck->push(creature); - eve::card::CardContainerAdapter source(cid("card:deck-filter"), eve::card::CardContainerKind::Deck, - deck); - eve::card::CardContainerAdapter destination( - cid("card:spell-hand"), eve::card::CardContainerKind::Hand, nullptr, hand, nullptr, - Capacity::fixed(2), {"spell"}); - auto rejected = eve::container::TransferService::transfer( - eve::container::TransferRequest{&source, &destination, - mid(creature->identity()->id.c_str()), SlotIndex(0), + eve::card::CardContainerAdapter source(cid("card:deck-filter"), eve::card::CardContainerKind::Deck, deck); + eve::card::CardContainerAdapter destination(cid("card:spell-hand"), eve::card::CardContainerKind::Hand, nullptr, + hand, nullptr, Capacity::fixed(2), {"spell"}); + auto rejected = eve::container::TransferService::transfer( + eve::container::TransferRequest{&source, &destination, mid(creature->identity()->id.c_str()), SlotIndex(0), SlotIndex(0), source.revision(), destination.revision()}); CHECK(!rejected.ok()); CHECK_EQ(deck->count(), 1); @@ -168,12 +151,10 @@ TEST_CASE("container.cardTransferRejectsFilterAndDuplicateWithoutMutation") { hand->addCard(creature); eve::card::CardContainerAdapter unrestrictedDestination( - cid("card:duplicate-hand"), eve::card::CardContainerKind::Hand, nullptr, hand, nullptr, - Capacity::fixed(2)); - auto duplicate = eve::container::TransferService::transfer( - eve::container::TransferRequest{&source, &unrestrictedDestination, - mid(creature->identity()->id.c_str()), SlotIndex(0), - SlotIndex(0), source.revision(), unrestrictedDestination.revision()}); + cid("card:duplicate-hand"), eve::card::CardContainerKind::Hand, nullptr, hand, nullptr, Capacity::fixed(2)); + auto duplicate = eve::container::TransferService::transfer(eve::container::TransferRequest{ + &source, &unrestrictedDestination, mid(creature->identity()->id.c_str()), SlotIndex(0), SlotIndex(0), + source.revision(), unrestrictedDestination.revision()}); CHECK(!duplicate.ok()); CHECK_EQ(deck->count(), 1); CHECK_EQ(hand->count(), 1); @@ -182,17 +163,15 @@ TEST_CASE("container.cardTransferRejectsFilterAndDuplicateWithoutMutation") { TEST_CASE("container.transferRestoresSourceAfterApplyFailure") { eve::card::Card card; REQUIRE_EQ(card.registerCardsFromJson(R"([{"id":"creature","kind":"creature"}])"), 1); - auto* deck = card.newDeck(); + auto* deck = card.newDeck(); auto* creature = card.newCard("creature"); REQUIRE(deck != nullptr); REQUIRE(creature != nullptr); deck->push(creature); - eve::card::CardContainerAdapter source(cid("card:rollback"), eve::card::CardContainerKind::Deck, - deck); - FailingPrepareContainer destination(cid("failing:destination")); - auto failed = eve::container::TransferService::transfer( - eve::container::TransferRequest{&source, &destination, - mid(creature->identity()->id.c_str()), SlotIndex(0), + eve::card::CardContainerAdapter source(cid("card:rollback"), eve::card::CardContainerKind::Deck, deck); + FailingPrepareContainer destination(cid("failing:destination")); + auto failed = eve::container::TransferService::transfer( + eve::container::TransferRequest{&source, &destination, mid(creature->identity()->id.c_str()), SlotIndex(0), std::nullopt, source.revision(), destination.snapshot().value().revision}); CHECK(!failed.ok()); CHECK_EQ(destination.prepareCalls(), 1); @@ -208,38 +187,36 @@ TEST_CASE("container.inventoryBagAndEquipmentTransferRejectsStaleAndFits") { ItemRegistry::clear(); InventorySystem::ensureBuiltins(); ItemDefinition sword; - sword.id = "sword"; - sword.maxStack = 1; - sword.tags = {"weapon"}; + sword.id = "sword"; + sword.maxStack = 1; + sword.tags = {"weapon"}; sword.equipSlot = "mainhand"; ItemRegistry::registerItem(sword); - Bag bag(2); + Bag bag(2); const int added = bag.addItem("sword", 1); REQUIRE_EQ(added, 1); EquipmentSet equipment; equipment.defineSlot("mainhand"); InventoryContainerAdapter bagAdapter(cid("inventory:bag"), &bag); InventoryContainerAdapter equipmentAdapter(cid("inventory:equipment"), &equipment); - const auto oldRevision = bagAdapter.revision(); - const auto object = MembershipId("inventory:" + std::to_string(bag.getSlotInstanceId(0))); + const auto oldRevision = bagAdapter.revision(); + const auto object = MembershipId("inventory:" + std::to_string(bag.getSlotInstanceId(0))); - auto equipped = eve::container::TransferService::transfer( - eve::container::TransferRequest{&bagAdapter, &equipmentAdapter, object, SlotIndex(0), - SlotIndex(0), oldRevision, equipmentAdapter.revision()}); + auto equipped = eve::container::TransferService::transfer(eve::container::TransferRequest{ + &bagAdapter, &equipmentAdapter, object, SlotIndex(0), SlotIndex(0), oldRevision, equipmentAdapter.revision()}); REQUIRE(equipped.ok()); CHECK(bag.isSlotEmpty(0)); CHECK_EQ(equipment.getSlotItemId("mainhand"), std::string("sword")); - auto stale = eve::container::TransferService::transfer( - eve::container::TransferRequest{&bagAdapter, &equipmentAdapter, object, SlotIndex(0), - SlotIndex(0), oldRevision, equipmentAdapter.revision()}); + auto stale = eve::container::TransferService::transfer(eve::container::TransferRequest{ + &bagAdapter, &equipmentAdapter, object, SlotIndex(0), SlotIndex(0), oldRevision, equipmentAdapter.revision()}); CHECK(!stale.ok()); CHECK_EQ(stale.code(), eve::StatusCode::Rejected); auto unequipped = eve::container::TransferService::transfer( - eve::container::TransferRequest{&equipmentAdapter, &bagAdapter, object, SlotIndex(0), - SlotIndex(1), equipmentAdapter.revision(), bagAdapter.revision()}); + eve::container::TransferRequest{&equipmentAdapter, &bagAdapter, object, SlotIndex(0), SlotIndex(1), + equipmentAdapter.revision(), bagAdapter.revision()}); REQUIRE(unequipped.ok()); CHECK_EQ(bag.getSlotInstanceId(1), std::stoi(object.value().substr(std::string("inventory:").size()))); CHECK(equipment.isSlotEmpty("mainhand")); @@ -252,28 +229,26 @@ TEST_CASE("container.sameContainerMoveUsesFinalSlotAndRejectsOccupiedSlot") { ItemRegistry::clear(); InventorySystem::ensureBuiltins(); ItemDefinition item; - item.id = "token"; + item.id = "token"; item.maxStack = 1; ItemRegistry::registerItem(item); - Bag bag(3); - const int firstAdded = bag.addItem("token", 1); + Bag bag(3); + const int firstAdded = bag.addItem("token", 1); const int secondAdded = bag.addItem("token", 1); REQUIRE_EQ(firstAdded, 1); REQUIRE_EQ(secondAdded, 1); InventoryContainerAdapter adapter(cid("inventory:same"), &bag); - const auto first = MembershipId("inventory:" + std::to_string(bag.getSlotInstanceId(0))); - const auto revision = adapter.revision(); - auto moved = eve::container::TransferService::transfer( - eve::container::TransferRequest{&adapter, &adapter, first, SlotIndex(0), SlotIndex(2), - revision, revision}); + const auto first = MembershipId("inventory:" + std::to_string(bag.getSlotInstanceId(0))); + const auto revision = adapter.revision(); + auto moved = eve::container::TransferService::transfer( + eve::container::TransferRequest{&adapter, &adapter, first, SlotIndex(0), SlotIndex(2), revision, revision}); REQUIRE(moved.ok()); CHECK_EQ(bag.getSlotInstanceId(2), std::stoi(first.value().substr(10))); CHECK(bag.isSlotEmpty(0)); - const auto second = MembershipId("inventory:" + std::to_string(bag.getSlotInstanceId(1))); + const auto second = MembershipId("inventory:" + std::to_string(bag.getSlotInstanceId(1))); const auto afterMoveRevision = adapter.revision(); - auto occupied = eve::container::TransferService::transfer( - eve::container::TransferRequest{&adapter, &adapter, second, SlotIndex(1), SlotIndex(2), - afterMoveRevision, afterMoveRevision}); + auto occupied = eve::container::TransferService::transfer(eve::container::TransferRequest{ + &adapter, &adapter, second, SlotIndex(1), SlotIndex(2), afterMoveRevision, afterMoveRevision}); CHECK(!occupied.ok()); CHECK_EQ(bag.getSlotInstanceId(2), std::stoi(first.value().substr(10))); ItemRegistry::clear(); diff --git a/test/decision.cpp b/test/decision.cpp index 4b8774a1..b003cc83 100644 --- a/test/decision.cpp +++ b/test/decision.cpp @@ -5,7 +5,7 @@ using namespace eve::decision; TEST_CASE("decision.blackboardsAndFsmAreExplicit") { DecisionContext d; - auto setResult = d.set("ai", "enemy", "\"tank-1\""); + auto setResult = d.set("ai", "enemy", "\"tank-1\""); REQUIRE(setResult.ok()); CHECK_EQ(d.get("ai", "enemy", "null"), std::string("\"tank-1\"")); auto stateResult = d.setState("unit", "idle"); @@ -23,7 +23,7 @@ TEST_CASE("decision.utilityIsDeterministic") { } TEST_CASE("decision.influenceAndSnapshotRoundTrip") { DecisionContext d; - auto gridResult = d.newGrid("threat", 2, 2, 10, 0, 0); + auto gridResult = d.newGrid("threat", 2, 2, 10, 0, 0); REQUIRE(gridResult.ok()); auto setCellResult = d.setCell("threat", 1, 0, 2); REQUIRE(setCellResult.ok()); @@ -32,7 +32,7 @@ TEST_CASE("decision.influenceAndSnapshotRoundTrip") { CHECK_EQ(d.sample("threat", 15, 5, -1), 5.f); auto s = d.snapshotJson(); DecisionContext x; - auto restoreResult = x.restoreJson(s); + auto restoreResult = x.restoreJson(s); REQUIRE(restoreResult.ok()); CHECK_EQ(x.snapshotJson(), s); auto before = x.snapshotJson(); diff --git a/test/definition_runtime.cpp b/test/definition_runtime.cpp index f4d8be35..ec990de4 100644 --- a/test/definition_runtime.cpp +++ b/test/definition_runtime.cpp @@ -14,7 +14,7 @@ namespace { struct TypedState { - int value = 0; + int value = 0; std::string label; }; @@ -32,7 +32,7 @@ eve::DefinitionRef testReference(const char* text) { eve::SnapshotHashProvider testHash() { return [](std::string_view input) -> eve::Result { eve::ContentId::Bytes bytes{}; - std::uint8_t value = 29; + std::uint8_t value = 29; for (std::size_t index = 0; index < bytes.size(); ++index) { for (const unsigned char byte : input) value = static_cast(value * 33u + byte); bytes[index] = static_cast(value + index); @@ -45,74 +45,66 @@ eve::SnapshotHashProvider testHash() { TEST_CASE("definitionRuntime.policiesAndGenerationQualifiedStaleChecks") { const auto reference = testReference("test:unit"); - auto created = eve::definition::RuntimeInstance::create( - testId(), reference, eve::Generation(1), TypedState{7, "original"}); + auto created = eve::definition::RuntimeInstance::create(testId(), reference, eve::Generation(1), + TypedState{7, "original"}); REQUIRE(created.ok()); auto runtime = std::move(created).takeValue(); - auto wrongGeneration = runtime.checkDefinition( - eve::definition::DefinitionHandle{reference, eve::Generation(2)}); + auto wrongGeneration = runtime.checkDefinition(eve::definition::DefinitionHandle{reference, eve::Generation(2)}); CHECK(!wrongGeneration.ok()); - CHECK(static_cast(wrongGeneration.error()->code()) == - static_cast(eve::DiagnosticCode::StaleHandle)); + CHECK(static_cast(wrongGeneration.error()->code()) == static_cast(eve::DiagnosticCode::StaleHandle)); - auto kept = runtime.reload( - eve::definition::DefinitionHandle{reference, eve::Generation(2)}, - eve::definition::ReloadPolicy::KeepInstanceValues, TypedState{99, "new defaults"}); + auto kept = runtime.reload(eve::definition::DefinitionHandle{reference, eve::Generation(2)}, + eve::definition::ReloadPolicy::KeepInstanceValues, TypedState{99, "new defaults"}); REQUIRE(kept.ok()); - CHECK(static_cast(kept.value().disposition) == - static_cast(eve::definition::ReloadDisposition::Kept)); + CHECK(static_cast(kept.value().disposition) == static_cast(eve::definition::ReloadDisposition::Kept)); CHECK_EQ(runtime.state().value, 7); CHECK_EQ(runtime.identity().definitionGeneration.value(), uint64_t{2}); - auto reapplied = runtime.reload( - eve::definition::DefinitionHandle{reference, eve::Generation(3)}, - eve::definition::ReloadPolicy::ReapplyDefaults, TypedState{42, "defaults"}); + auto reapplied = runtime.reload(eve::definition::DefinitionHandle{reference, eve::Generation(3)}, + eve::definition::ReloadPolicy::ReapplyDefaults, TypedState{42, "defaults"}); REQUIRE(reapplied.ok()); CHECK(static_cast(reapplied.value().disposition) == static_cast(eve::definition::ReloadDisposition::DefaultsReapplied)); CHECK_EQ(runtime.state().value, 42); CHECK_EQ(runtime.state().label, std::string("defaults")); - auto rebuilt = runtime.reload( - eve::definition::DefinitionHandle{reference, eve::Generation(4)}, - eve::definition::ReloadPolicy::RebuildInstance, TypedState{100, "ignored"}, - [](const TypedState& oldState, const eve::definition::InstanceIdentity& identity, - const eve::definition::DefinitionHandle& handle) -> eve::Result { - if (!identity.isValid() || !handle.isValid()) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::InvalidArgument, "test rebuild received invalid identity")); - return eve::Result::success(TypedState{oldState.value + 1, "rebuilt"}); - }); + auto rebuilt = + runtime.reload(eve::definition::DefinitionHandle{reference, eve::Generation(4)}, + eve::definition::ReloadPolicy::RebuildInstance, TypedState{100, "ignored"}, + [](const TypedState& oldState, const eve::definition::InstanceIdentity& identity, + const eve::definition::DefinitionHandle& handle) -> eve::Result { + if (!identity.isValid() || !handle.isValid()) + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::InvalidArgument, "test rebuild received invalid identity")); + return eve::Result::success(TypedState{oldState.value + 1, "rebuilt"}); + }); REQUIRE(rebuilt.ok()); CHECK(static_cast(rebuilt.value().disposition) == static_cast(eve::definition::ReloadDisposition::Rebuilt)); CHECK_EQ(runtime.state().value, 43); - auto failedRebuild = runtime.reload( - eve::definition::DefinitionHandle{reference, eve::Generation(5)}, - eve::definition::ReloadPolicy::RebuildInstance, TypedState{0, "must not apply"}, - [](const TypedState&, const eve::definition::InstanceIdentity&, - const eve::definition::DefinitionHandle&) -> eve::Result { - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Failed, "injected rebuild failure")); - }); + auto failedRebuild = runtime.reload(eve::definition::DefinitionHandle{reference, eve::Generation(5)}, + eve::definition::ReloadPolicy::RebuildInstance, TypedState{0, "must not apply"}, + [](const TypedState&, const eve::definition::InstanceIdentity&, + const eve::definition::DefinitionHandle&) -> eve::Result { + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::Failed, "injected rebuild failure")); + }); CHECK(!failedRebuild.ok()); CHECK_EQ(runtime.identity().definitionGeneration.value(), uint64_t{4}); CHECK_EQ(runtime.state().value, 43); - auto rejected = runtime.reload( - eve::definition::DefinitionHandle{reference, eve::Generation(5)}, - eve::definition::ReloadPolicy::RejectWhileActive, TypedState{0, "must not apply"}); + auto rejected = runtime.reload(eve::definition::DefinitionHandle{reference, eve::Generation(5)}, + eve::definition::ReloadPolicy::RejectWhileActive, TypedState{0, "must not apply"}); CHECK(!rejected.ok()); CHECK(static_cast(rejected.error()->code()) == static_cast(eve::DiagnosticCode::Conflict)); CHECK_EQ(runtime.identity().definitionGeneration.value(), uint64_t{4}); CHECK_EQ(runtime.state().value, 43); runtime.setActive(false); - auto inactiveReject = runtime.reload( - eve::definition::DefinitionHandle{reference, eve::Generation(5)}, - eve::definition::ReloadPolicy::RejectWhileActive, TypedState{0, "still kept"}); + auto inactiveReject = runtime.reload(eve::definition::DefinitionHandle{reference, eve::Generation(5)}, + eve::definition::ReloadPolicy::RejectWhileActive, TypedState{0, "still kept"}); REQUIRE(inactiveReject.ok()); CHECK(static_cast(inactiveReject.value().disposition) == static_cast(eve::definition::ReloadDisposition::Kept)); @@ -122,14 +114,12 @@ TEST_CASE("definitionRuntime.policiesAndGenerationQualifiedStaleChecks") { TEST_CASE("definitionRuntime.cardAdapterProjectsTypedIdentityAndReloads") { eve::definitions::DefinitionRegistry registry; - auto inserted = registry.insert( - "card", "scout", 1, - R"({"name":"Scout","kind":"creature","cost":2,"attack":3,"health":5,"tags":["unit"]})"); + auto inserted = registry.insert( + "card", "scout", 1, R"({"name":"Scout","kind":"creature","cost":2,"attack":3,"health":5,"tags":["unit"]})"); REQUIRE(inserted.ok()); - auto runtimeResult = eve::card::CardDefinitionRuntime::create( - registry, testReference("card:scout"), testId(), - eve::definition::ReloadPolicy::RebuildInstance); + auto runtimeResult = eve::card::CardDefinitionRuntime::create(registry, testReference("card:scout"), testId(), + eve::definition::ReloadPolicy::RebuildInstance); REQUIRE(runtimeResult.ok()); auto runtime = std::move(runtimeResult).takeValue(); CHECK_EQ(runtime.state().attack, 3); @@ -139,7 +129,7 @@ TEST_CASE("definitionRuntime.cardAdapterProjectsTypedIdentityAndReloads") { auto snapshot = runtime.snapshot(eve::Revision(1), eve::SimulationTick(2), testHash()); REQUIRE(snapshot.ok()); runtime.state().currentHealth = 1; - auto restored = runtime.restore(snapshot.value(), testHash()); + auto restored = runtime.restore(snapshot.value(), testHash()); REQUIRE(restored.ok()); CHECK_EQ(runtime.state().currentHealth, 2); @@ -152,9 +142,8 @@ TEST_CASE("definitionRuntime.cardAdapterProjectsTypedIdentityAndReloads") { CHECK_EQ(card->definitionBinding()->identity.definitionGeneration.value(), uint64_t{1}); card->release(); - auto replaced = registry.replace( - "card", "scout", 2, - R"({"name":"Veteran Scout","kind":"creature","cost":3,"attack":6,"health":4})"); + auto replaced = registry.replace("card", "scout", 2, + R"({"name":"Veteran Scout","kind":"creature","cost":3,"attack":6,"health":4})"); REQUIRE(replaced.ok()); auto staleSnapshot = runtime.restore(snapshot.value(), testHash()); CHECK(!staleSnapshot.ok()); @@ -170,7 +159,7 @@ TEST_CASE("definitionRuntime.cardAdapterProjectsTypedIdentityAndReloads") { TEST_CASE("definitionRuntime.effectAdapterKeepsTypedPolicyAndRejectsActiveReload") { eve::definitions::DefinitionRegistry registry; - auto inserted = registry.insert( + auto inserted = registry.insert( "effect", "burn", 1, R"({"stackKey":"burn","priority":4,"duration":8.0,"magnitude":3.5,"stackCount":1,"maxStacks":3,"stackMode":"reuse","stackCountPolicy":"increment","payload":{"damageType":"fire"}})"); REQUIRE(inserted.ok()); @@ -180,17 +169,15 @@ TEST_CASE("definitionRuntime.effectAdapterKeepsTypedPolicyAndRejectsActiveReload eve::definition::ReloadPolicy::RejectWhileActive); REQUIRE(runtimeResult.ok()); auto runtime = std::move(runtimeResult).takeValue(); - CHECK(static_cast(runtime.state().policy.stackMode) == - static_cast(eve::effects::StackMode::Reuse)); + CHECK(static_cast(runtime.state().policy.stackMode) == static_cast(eve::effects::StackMode::Reuse)); CHECK(static_cast(runtime.state().policy.stackCount) == static_cast(eve::effects::StackCountPolicy::Increment)); eve::effects::EffectInstance instance; - auto projected = runtime.applyTo(&instance); + auto projected = runtime.applyTo(&instance); REQUIRE(projected.ok()); CHECK(static_cast(instance.definitionIdentity == runtime.identity())); - CHECK(static_cast(instance.policy.stackMode) == - static_cast(eve::effects::StackMode::Reuse)); + CHECK(static_cast(instance.policy.stackMode) == static_cast(eve::effects::StackMode::Reuse)); CHECK_EQ(instance.payload.getJson("damageType"), std::string("\"fire\"")); auto replaced = registry.replace( diff --git a/test/definition_runtime_skill_vehicle.cpp b/test/definition_runtime_skill_vehicle.cpp index 622c7407..e60913e4 100644 --- a/test/definition_runtime_skill_vehicle.cpp +++ b/test/definition_runtime_skill_vehicle.cpp @@ -22,7 +22,7 @@ eve::PersistentId testId() { eve::SnapshotHashProvider testHash() { return [](std::string_view input) -> eve::Result { - std::uint64_t first = 14695981039346656037ull; + std::uint64_t first = 14695981039346656037ull; std::uint64_t second = 1099511628211ull; for (const unsigned char byte : input) { first ^= byte; @@ -32,7 +32,7 @@ eve::SnapshotHashProvider testHash() { } eve::ContentId::Bytes bytes{}; for (std::size_t index = 0; index < sizeof(first); ++index) { - bytes[index] = static_cast((first >> (index * 8u)) & 0xffu); + bytes[index] = static_cast((first >> (index * 8u)) & 0xffu); bytes[8 + index] = static_cast((second >> (index * 8u)) & 0xffu); } return eve::Result::success(eve::ContentId(bytes)); @@ -44,20 +44,20 @@ eve::SnapshotHashProvider testHash() { TEST_CASE("definitionRuntime.skillAdapterUsesCanonicalRegistryAndAtomicSnapshot") { eve::rpg::SkillRegistry::clear(); eve::rpg::SkillDefinition definition; - definition.id = "fireball"; - definition.cooldown = 8.f; - definition.castTime = 1.25f; + definition.id = "fireball"; + definition.cooldown = 8.f; + definition.castTime = 1.25f; definition.targetType = "single"; - definition.tags = {"fire"}; - definition.castCondition = eve::decision::Condition::compare( - "skill.cooldown", eve::decision::CompareOperator::Equal, eve::Value(0)); + definition.tags = {"fire"}; + definition.castCondition = + eve::decision::Condition::compare("skill.cooldown", eve::decision::CompareOperator::Equal, eve::Value(0)); eve::rpg::SkillRegistry::registerSkill(definition); auto runtimeResult = eve::rpg::SkillDefinitionRuntime::create(testId(), "fireball"); REQUIRE(runtimeResult.ok()); - auto runtime = std::move(runtimeResult).takeValue(); + auto runtime = std::move(runtimeResult).takeValue(); const auto originalGeneration = runtime.identity().definitionGeneration; - auto typed = runtime.definition(); + auto typed = runtime.definition(); REQUIRE(typed.ok()); CHECK_EQ(typed.value().targetType, std::string("single")); CHECK_EQ(static_cast(typed.value().castCondition.kind()), @@ -67,17 +67,16 @@ TEST_CASE("definitionRuntime.skillAdapterUsesCanonicalRegistryAndAtomicSnapshot" auto snapshot = runtime.snapshot(eve::Revision(3), eve::SimulationTick(12), testHash()); REQUIRE(snapshot.ok()); runtime.state().cooldownRemaining = 0.f; - auto restored = runtime.restore(snapshot.value(), testHash()); + auto restored = runtime.restore(snapshot.value(), testHash()); REQUIRE(restored.ok()); CHECK_EQ(runtime.state().cooldownRemaining, 4.f); eve::rpg::SkillDefinition replacement = definition; - replacement.cooldown = 3.f; + replacement.cooldown = 3.f; eve::rpg::SkillRegistry::registerSkill(replacement); auto rejectedRestore = runtime.restore(snapshot.value(), testHash()); CHECK(!rejectedRestore.ok()); - CHECK_EQ(static_cast(rejectedRestore.error()->code()), - static_cast(eve::DiagnosticCode::StaleHandle)); + CHECK_EQ(static_cast(rejectedRestore.error()->code()), static_cast(eve::DiagnosticCode::StaleHandle)); CHECK_EQ(runtime.state().cooldownRemaining, 4.f); runtime.setActive(true); @@ -89,9 +88,9 @@ TEST_CASE("definitionRuntime.skillAdapterUsesCanonicalRegistryAndAtomicSnapshot" REQUIRE(reloaded.ok()); CHECK_EQ(runtime.state().cooldownRemaining, 3.f); - ecs::Table world; + ecs::Table world; ecs::ScopedTable guard(world); - auto* actor = eve::rpg::RPGActor::createActor(); + auto* actor = eve::rpg::RPGActor::createActor(); REQUIRE(actor != nullptr); auto projected = runtime.applyTo(actor); REQUIRE(projected.ok()); @@ -102,44 +101,43 @@ TEST_CASE("definitionRuntime.skillAdapterUsesCanonicalRegistryAndAtomicSnapshot" TEST_CASE("definitionRuntime.vehicleAdapterProjectsEntityAndRejectsStaleSnapshot") { eve::definitions::DefinitionRegistry registry; - auto inserted = registry.insert( + auto inserted = registry.insert( "vehicle", "scout", 1, R"({"category":"vehicle","mobility":"kinematic","maxSpeed":80,"radius":6,"maxHealth":100,"tags":["recon"]})"); REQUIRE(inserted.ok()); auto reference = eve::DefinitionRef::parse("vehicle:scout"); REQUIRE(reference.ok()); - auto runtimeResult = eve::vehicle::VehicleDefinitionRuntime::create( - registry, std::move(reference).takeValue(), testId()); + auto runtimeResult = + eve::vehicle::VehicleDefinitionRuntime::create(registry, std::move(reference).takeValue(), testId()); REQUIRE(runtimeResult.ok()); - auto runtime = std::move(runtimeResult).takeValue(); - runtime.state().x = -12.f; - runtime.state().y = 4.f; - runtime.state().health = 75.f; + auto runtime = std::move(runtimeResult).takeValue(); + runtime.state().x = -12.f; + runtime.state().y = 4.f; + runtime.state().health = 75.f; runtime.state().faction = "blue"; - ecs::Table world; + ecs::Table world; ecs::ScopedTable guard(world); - auto* entity = eve::vehicle::VehicleEntity::createVehicle(); + auto* entity = eve::vehicle::VehicleEntity::createVehicle(); REQUIRE(entity != nullptr); auto projected = runtime.applyTo(entity); REQUIRE(projected.ok()); CHECK_EQ(entity->motion()->x, -12.f); CHECK_EQ(entity->health()->hp, 75.f); CHECK_EQ(entity->identity()->faction, std::string("blue")); - CHECK_EQ(entity->definitionBinding()->identity.definitionGeneration, - runtime.identity().definitionGeneration); + CHECK_EQ(entity->definitionBinding()->identity.definitionGeneration, runtime.identity().definitionGeneration); CHECK_EQ(entity->definition()->def->category, std::string("vehicle")); auto snapshot = runtime.snapshot(eve::Revision(4), eve::SimulationTick(20), testHash()); REQUIRE(snapshot.ok()); runtime.state().x = 99.f; - auto restored = runtime.restore(snapshot.value(), testHash()); + auto restored = runtime.restore(snapshot.value(), testHash()); REQUIRE(restored.ok()); CHECK_EQ(runtime.state().x, -12.f); - auto replaced = registry.replace( - "vehicle", "scout", 2, - R"({"category":"vehicle","mobility":"kinematic","maxSpeed":90,"radius":6,"maxHealth":120})"); + auto replaced = + registry.replace("vehicle", "scout", 2, + R"({"category":"vehicle","mobility":"kinematic","maxSpeed":90,"radius":6,"maxHealth":120})"); REQUIRE(replaced.ok()); auto stale = runtime.restore(snapshot.value(), testHash()); CHECK(!stale.ok()); diff --git a/test/definition_runtime_weapon.cpp b/test/definition_runtime_weapon.cpp index 4a6e311a..10a30077 100644 --- a/test/definition_runtime_weapon.cpp +++ b/test/definition_runtime_weapon.cpp @@ -19,10 +19,9 @@ eve::PersistentId testId() { eve::SnapshotHashProvider testHash() { return [](std::string_view input) -> eve::Result { eve::ContentId::Bytes bytes{}; - std::uint8_t value = 41; + std::uint8_t value = 41; for (std::size_t index = 0; index < bytes.size(); ++index) { - for (const unsigned char byte : input) - value = static_cast(value * 33u + byte + index); + for (const unsigned char byte : input) value = static_cast(value * 33u + byte + index); bytes[index] = static_cast(value + index); } return eve::Result::success(eve::ContentId(bytes)); @@ -39,34 +38,34 @@ eve::DefinitionRef weaponRef() { TEST_CASE("definitionRuntime.weaponTypedStateSnapshotReloadAndStale") { eve::definitions::DefinitionRegistry registry; - const std::string original = R"JSON({ + const std::string original = R"JSON({ "id":"rifle", "kind":"ranged", "logic":"hitscan", "damage":25, "range":500, "spreadMin":1, "spreadMax":8, "ammo":{"mag":30,"reserve":90,"reload":2}, "resource":{"kind":"ammo","max":30,"cost":1,"infinite":false}, "projectile":{"type":"bullet","speed":800,"pelletCount":1} })JSON"; - auto inserted = registry.insert("weapon", "rifle", 1, original); + auto inserted = registry.insert("weapon", "rifle", 1, original); REQUIRE(inserted.ok()); - auto created = eve::weapon::WeaponDefinitionRuntime::create( - registry, weaponRef(), testId(), eve::definition::ReloadPolicy::RebuildInstance); + auto created = eve::weapon::WeaponDefinitionRuntime::create(registry, weaponRef(), testId(), + eve::definition::ReloadPolicy::RebuildInstance); REQUIRE(created.ok()); - auto runtime = std::move(created).takeValue(); + auto runtime = std::move(created).takeValue(); const auto oldHandle = runtime.definitionHandle(); - auto typed = runtime.definition(); + auto typed = runtime.definition(); REQUIRE(typed.ok()); CHECK_EQ(typed.value().damage, 25.f); CHECK_EQ(typed.value().projectile.type, std::string("bullet")); CHECK_EQ(runtime.state().resource.value, 30.f); runtime.state().resource.value = 7.f; - runtime.state().currentSpread = 4.f; + runtime.state().currentSpread = 4.f; auto snapshot = runtime.snapshot(eve::Revision(2), eve::SimulationTick(17), testHash()); REQUIRE(snapshot.ok()); runtime.state().resource.value = 1.f; - runtime.state().currentSpread = 0.f; - auto restored = runtime.restore(snapshot.value(), testHash()); + runtime.state().currentSpread = 0.f; + auto restored = runtime.restore(snapshot.value(), testHash()); REQUIRE(restored.ok()); CHECK_EQ(runtime.state().resource.value, 7.f); CHECK_EQ(runtime.state().currentSpread, 4.f); @@ -76,7 +75,7 @@ TEST_CASE("definitionRuntime.weaponTypedStateSnapshotReloadAndStale") { "range":700, "spreadMin":2, "spreadMax":3, "ammo":{"mag":10,"reserve":20,"reload":1} })JSON"; - auto replaced = registry.replace("weapon", "rifle", 1, replacement); + auto replaced = registry.replace("weapon", "rifle", 1, replacement); REQUIRE(replaced.ok()); auto reloaded = runtime.reload(eve::definition::ReloadPolicy::RebuildInstance); REQUIRE(reloaded.ok()); @@ -90,16 +89,16 @@ TEST_CASE("definitionRuntime.weaponTypedStateSnapshotReloadAndStale") { TEST_CASE("definitionRuntime.weaponRejectWhileActiveIsAtomic") { eve::definitions::DefinitionRegistry registry; - const auto first = registry.insert("weapon", "rifle", 1, - R"JSON({"id":"rifle","kind":"ranged","ammo":{"mag":4}})JSON"); + const auto first = + registry.insert("weapon", "rifle", 1, R"JSON({"id":"rifle","kind":"ranged","ammo":{"mag":4}})JSON"); REQUIRE(first.ok()); auto created = eve::weapon::WeaponDefinitionRuntime::create(registry, weaponRef(), testId()); REQUIRE(created.ok()); auto runtime = std::move(created).takeValue(); runtime.setActive(true); const auto before = runtime.definitionHandle(); - const auto replacement = registry.replace( - "weapon", "rifle", 1, R"JSON({"id":"rifle","kind":"ranged","ammo":{"mag":8}})JSON"); + const auto replacement = + registry.replace("weapon", "rifle", 1, R"JSON({"id":"rifle","kind":"ranged","ammo":{"mag":8}})JSON"); REQUIRE(replacement.ok()); auto rejected = runtime.reload(eve::definition::ReloadPolicy::RejectWhileActive); CHECK(!rejected.ok()); diff --git a/test/definitions.cpp b/test/definitions.cpp index 46c32050..c81a8721 100644 --- a/test/definitions.cpp +++ b/test/definitions.cpp @@ -36,25 +36,22 @@ TEST_CASE("definitions.handlesInvalidateButReferencesReload") { REQUIRE(registry.replace("unit", "scout", 2, "{\"speed\":7}").ok()); CHECK(!registry.resolveHandle(oldHandle).ok()); - auto current = registry.resolve(std::string(reference.id().namespaceName()), - std::string(reference.id().name())); + auto current = registry.resolve(std::string(reference.id().namespaceName()), std::string(reference.id().name())); REQUIRE(current.ok()); CHECK_EQ(current.value().get().version.value(), uint64_t{2}); CHECK_EQ(current.value().get().generation.value(), uint64_t{2}); REQUIRE(registry.remove("unit", "scout").ok()); - CHECK(!registry.resolve(std::string(reference.id().namespaceName()), - std::string(reference.id().name())).ok()); + CHECK(!registry.resolve(std::string(reference.id().namespaceName()), std::string(reference.id().name())).ok()); REQUIRE(registry.insert("unit", "scout", 3, "{}").ok()); - auto revived = registry.resolve(std::string(reference.id().namespaceName()), - std::string(reference.id().name())); + auto revived = registry.resolve(std::string(reference.id().namespaceName()), std::string(reference.id().name())); REQUIRE(revived.ok()); CHECK_EQ(revived.value().get().generation.value(), uint64_t{4}); } TEST_CASE("definitions.canonicalRegistryApiKeepsTombstones") { DefinitionRegistry registry; - auto inserted = registry.insert("unit", "scout", 1, "{}"); + auto inserted = registry.insert("unit", "scout", 1, "{}"); REQUIRE(inserted.ok()); const auto first = std::move(inserted).takeValue(); CHECK_EQ(first.generation.value(), uint64_t{1}); @@ -112,17 +109,16 @@ TEST_CASE("definitions.eventsDescribeReloadAndRemoval") { } TEST_CASE("definitions.subscriptionSupportsReentrantReloadObservers") { - DefinitionRegistry registry; + DefinitionRegistry registry; std::vector notifications; - eve::Subscription first; - eve::Subscription replacement; + eve::Subscription first; + eve::Subscription replacement; first = registry.subscribe([&](const DefinitionEvent &event) { notifications.push_back(event.name + ":" + event.id); first.dispose(); - replacement = registry.subscribe([&](const DefinitionEvent &next) { - notifications.push_back("replacement:" + next.name + ":" + next.id); - }); + replacement = registry.subscribe( + [&](const DefinitionEvent &next) { notifications.push_back("replacement:" + next.name + ":" + next.id); }); }); REQUIRE(registry.insert("unit", "scout", 1, "{}").ok()); @@ -130,15 +126,14 @@ TEST_CASE("definitions.subscriptionSupportsReentrantReloadObservers") { CHECK_EQ(registry.eventCount(), 1); REQUIRE(registry.replace("unit", "scout", 2, "{}").ok()); - CHECK_EQ(notifications, std::vector({"definition_reloaded:scout", - "replacement:definition_reloaded:scout"})); + CHECK_EQ(notifications, + std::vector({"definition_reloaded:scout", "replacement:definition_reloaded:scout"})); } TEST_CASE("definitions.mutationResultPreservesObserverWarning") { DefinitionRegistry registry; - auto subscription = registry.subscribe([](const DefinitionEvent&) { - throw std::runtime_error("injected definition observer failure"); - }); + auto subscription = registry.subscribe( + [](const DefinitionEvent &) { throw std::runtime_error("injected definition observer failure"); }); auto result = registry.insert("unit", "scout", 1, "{}"); REQUIRE(result.ok()); @@ -169,6 +164,11 @@ TEST_CASE("definitions.snapshotRoundTripsAndRestoreIsTransactional") { const std::string before = restored.snapshotJson(); CHECK(!restored.restoreJson("{\"version\":1}").ok()); CHECK_EQ(restored.snapshotJson(), before); + + const std::string unknownField = snapshot.substr(0, snapshot.size() - 1) + ",\"unexpected\":true}"; + auto schemaRejected = restored.restoreJson(unknownField); + CHECK(!schemaRejected.ok()); + CHECK_EQ(restored.snapshotJson(), before); } TEST_CASE("definitions.scriptRegisterReplaceEnumerateAndRestore") { diff --git a/test/dialogue_conversation.cpp b/test/dialogue_conversation.cpp index e335e337..696669dd 100644 --- a/test/dialogue_conversation.cpp +++ b/test/dialogue_conversation.cpp @@ -186,24 +186,23 @@ TEST_CASE("dialogueConversation.asyncCommand") { TEST_CASE("dialogueConversation.mutationsExposeStableDiagnostics") { ConversationAsset asset; - asset.id = "scene.mutations"; + asset.id = "scene.mutations"; asset.entry = "choice"; ConversationAsset::Node choice; - choice.id = "choice"; + choice.id = "choice"; choice.kind = ConversationAsset::Node::Kind::Choice; choice.routes.emplace_back("yes", "end"); ConversationAsset::Node end; - end.id = "end"; - end.kind = ConversationAsset::Node::Kind::End; + end.id = "end"; + end.kind = ConversationAsset::Node::Kind::End; asset.nodes = {choice, end}; ConversationRunner runner; - std::string error; + std::string error; REQUIRE(runner.start(&asset, StateValue::object(), &error)); auto missing = runner.selectRouteForTransaction("missing"); REQUIRE(!missing.ok()); - CHECK_EQ(static_cast(missing.error()->code()), - static_cast(eve::DiagnosticCode::DialogueRouteNotFound)); + CHECK_EQ(static_cast(missing.error()->code()), static_cast(eve::DiagnosticCode::DialogueRouteNotFound)); CHECK(runner.currentNodeId() == "choice"); auto selected = runner.selectRouteForTransaction("yes"); diff --git a/test/dialogue_payment.cpp b/test/dialogue_payment.cpp index 710b4a72..15bf2e83 100644 --- a/test/dialogue_payment.cpp +++ b/test/dialogue_payment.cpp @@ -22,10 +22,10 @@ using eve::dialogue::DialogueAccountBindings; using eve::dialogue::DialoguePaymentAdapter; using eve::dialogue::PaymentSpec; -DialoguePaymentAdapter makeAdapter(eve::economy::EconomyLedgerResourceAccount& money, +DialoguePaymentAdapter makeAdapter(eve::economy::EconomyLedgerResourceAccount& money, eve::attributes::AttributeSetResourceAccount& reputation) { DialogueAccountBindings bindings; - bindings.money = &money; + bindings.money = &money; bindings.reputation = &reputation; return DialoguePaymentAdapter(std::move(bindings)); } @@ -37,14 +37,14 @@ TEST_CASE("dialoguePayment.composesMoneyReputationAndStatePatch") { CHECK(ledger.credit("gold", 20) == 20); eve::attributes::AttributeSet attributes("actor"); attributes.setBase("reputation", 10.0); - eve::economy::EconomyLedgerResourceAccount money(ledger); + eve::economy::EconomyLedgerResourceAccount money(ledger); eve::attributes::AttributeSetResourceAccount reputation(attributes); - DialoguePaymentAdapter adapter = makeAdapter(money, reputation); + DialoguePaymentAdapter adapter = makeAdapter(money, reputation); - eve::statepatch::Store store; + eve::statepatch::Store store; eve::statepatch::StatePatchStateAdapter state(store); - PaymentSpec payment{5, 2}; - const std::array mutations{{ + PaymentSpec payment{5, 2}; + const std::array mutations{{ StateMutation{"actor", "accepted", Value(true), eve::MutationKind::Set, true}, }}; @@ -62,27 +62,25 @@ TEST_CASE("dialoguePayment.failureDoesNotDebitOrPublishState") { CHECK(ledger.credit("gold", 20) == 20); eve::attributes::AttributeSet attributes("actor"); attributes.setBase("reputation", 10.0); - eve::economy::EconomyLedgerResourceAccount money(ledger); + eve::economy::EconomyLedgerResourceAccount money(ledger); eve::attributes::AttributeSetResourceAccount reputation(attributes); - DialoguePaymentAdapter adapter = makeAdapter(money, reputation); + DialoguePaymentAdapter adapter = makeAdapter(money, reputation); - eve::statepatch::Store store; + eve::statepatch::Store store; eve::statepatch::StatePatchStateAdapter state(store); - const std::array invalidMutations{{ + const std::array invalidMutations{{ StateMutation{"actor", "must_not_publish", Value(true), eve::MutationKind::Set, true}, StateMutation{"actor", "not_a_number", Value("bad"), eve::MutationKind::AddNumber, true}, }}; - auto result = adapter.executeStateMutation("dialogue-payment-failure", PaymentSpec{5, 2}, state, - invalidMutations); + auto result = adapter.executeStateMutation("dialogue-payment-failure", PaymentSpec{5, 2}, state, invalidMutations); REQUIRE(!result.ok()); (void)result.status(); CHECK(ledger.get("gold") == 20); CHECK(attributes.getBase("reputation") == 10.0); CHECK(!store.has("actor", "must_not_publish")); - auto insufficient = adapter.executeStateMutation( - "dialogue-payment-insufficient", PaymentSpec{100, 1}, state, - std::span{}); + auto insufficient = adapter.executeStateMutation("dialogue-payment-insufficient", PaymentSpec{100, 1}, state, + std::span{}); REQUIRE(!insufficient.ok()); (void)insufficient.status(); CHECK(ledger.get("gold") == 20); @@ -90,8 +88,7 @@ TEST_CASE("dialoguePayment.failureDoesNotDebitOrPublishState") { } TEST_CASE("dialoguePayment.specRejectsUnknownAndNonPositiveFields") { - const auto unknown = PaymentSpec::fromValue( - Value::Object{{"money", Value(1)}, {"tokens", Value(2)}}); + const auto unknown = PaymentSpec::fromValue(Value::Object{{"money", Value(1)}, {"tokens", Value(2)}}); REQUIRE(!unknown.ok()); (void)unknown.status(); @@ -101,7 +98,7 @@ TEST_CASE("dialoguePayment.specRejectsUnknownAndNonPositiveFields") { PaymentSpec payment{3, 4}; const Value serialized = payment.toValue(); - auto roundTrip = PaymentSpec::fromValue(serialized); + auto roundTrip = PaymentSpec::fromValue(serialized); REQUIRE(roundTrip.ok()); const PaymentSpec restored = std::move(roundTrip).takeValue(); CHECK(restored.money == 3); @@ -113,31 +110,31 @@ TEST_CASE("dialoguePayment.choiceUsesTheSameAtomicFacade") { CHECK(ledger.credit("gold", 20) == 20); eve::attributes::AttributeSet attributes("actor"); attributes.setBase("reputation", 10.0); - eve::economy::EconomyLedgerResourceAccount money(ledger); + eve::economy::EconomyLedgerResourceAccount money(ledger); eve::attributes::AttributeSetResourceAccount reputation(attributes); - eve::statepatch::Store store; - eve::statepatch::StatePatchStateAdapter state(store); + eve::statepatch::Store store; + eve::statepatch::StatePatchStateAdapter state(store); eve::dialogue::ConversationAsset asset; - asset.id = "dialogue.payment-choice"; + asset.id = "dialogue.payment-choice"; asset.entry = "choice"; eve::dialogue::ConversationAsset::Node choice; - choice.id = "choice"; + choice.id = "choice"; choice.kind = eve::dialogue::ConversationAsset::Node::Kind::Choice; choice.routes.emplace_back("buy", "end"); choice.routes.front().payment = PaymentSpec{5, 2}; choice.routes.front().stateMutations.push_back( StateMutation{"actor", "accepted", Value(true), eve::MutationKind::Set, true}); eve::dialogue::ConversationAsset::Node end; - end.id = "end"; - end.kind = eve::dialogue::ConversationAsset::Node::Kind::End; + end.id = "end"; + end.kind = eve::dialogue::ConversationAsset::Node::Kind::End; asset.nodes = {choice, end}; eve::dialogue::ConversationDocument document(asset); - eve::dialogue::DialogueFlow flow; + eve::dialogue::DialogueFlow flow; eve::dialogue::DialogueFlow::IntegrationConfig config; - config.stateMutation = &state; - config.accounts.money = &money; + config.stateMutation = &state; + config.accounts.money = &money; config.accounts.reputation = &reputation; flow.configureIntegration(std::move(config)); REQUIRE(flow.applyDocument(&document)); diff --git a/test/dialogue_state_integration.cpp b/test/dialogue_state_integration.cpp index 9867dd41..9b7bc0b7 100644 --- a/test/dialogue_state_integration.cpp +++ b/test/dialogue_state_integration.cpp @@ -1,10 +1,10 @@ +#include "StatePatchTestSupport.h" #include "dialogue/ConversationAuthoring.h" #include "dialogue/Dialogue.h" #include "dialogue/DialogueFlow.h" #include "dialogue/DialogueState.h" #include "statepatch/StateAccessAdapter.h" #include "statepatch/StatePatch.h" -#include "StatePatchTestSupport.h" #include "transaction/Transaction.h" #include "zeroerr/assert.h" #include "zeroerr/unittest.h" @@ -39,86 +39,73 @@ using eve::dialogue::DialogueStateContext; class TestWorld final : public IStateQuery { public: - std::string subject = "actor"; - Value mood = Value("calm"); + std::string subject = "actor"; + Value mood = Value("calm"); bool unlocked = false; - std::optional value(std::string_view queriedSubject, - std::string_view key) const override { + std::optional value(std::string_view queriedSubject, std::string_view key) const override { if (queriedSubject != subject) return std::nullopt; if (key == "mood") return mood; return std::nullopt; } - std::optional hasTag(std::string_view queriedSubject, - std::string_view tag) const override { + std::optional hasTag(std::string_view queriedSubject, std::string_view tag) const override { if (queriedSubject != subject || tag.empty()) return std::nullopt; if (tag == "unlocked") return unlocked; return false; } - std::optional attribute(std::string_view queriedSubject, - std::string_view key) const override { + std::optional attribute(std::string_view queriedSubject, std::string_view key) const override { if (queriedSubject != subject || key != "reputation") return std::nullopt; return Value(12.0); } - std::optional resource(std::string_view, - std::string_view) const override { - return std::nullopt; - } + std::optional resource(std::string_view, std::string_view) const override { return std::nullopt; } - std::optional state(std::string_view queriedSubject, - std::string_view key) const override { + std::optional state(std::string_view queriedSubject, std::string_view key) const override { return value(queriedSubject, key); } - std::optional authority(std::string_view, - std::string_view) const override { - return std::nullopt; - } + std::optional authority(std::string_view, std::string_view) const override { return std::nullopt; } }; class RecordingMutation final : public IStateMutation { public: - int calls = 0; - std::size_t lastCount = 0; + int calls = 0; + std::size_t lastCount = 0; MutationContext lastContext; - Result apply(std::span mutations, - const MutationContext& context) override { + Result apply(std::span mutations, const MutationContext& context) override { ++calls; - lastCount = mutations.size(); + lastCount = mutations.size(); lastContext = context; - return Result::success( - MutationReceipt{context.transactionId, mutations.size()}, - eve::Status::success(eve::StatusCode::Applied)); + return Result::success(MutationReceipt{context.transactionId, mutations.size()}, + eve::Status::success(eve::StatusCode::Applied)); } }; ConversationAsset makeWorldBranchAsset() { ConversationAsset asset; - asset.id = "p1.world-branch"; + asset.id = "p1.world-branch"; asset.entry = "branch"; ConversationAsset::Node branch; - branch.id = "branch"; + branch.id = "branch"; branch.kind = ConversationAsset::Node::Kind::Branch; - branch.routes.emplace_back( - "world", "world-line", - Value::Object{{"state", Value("mood")}, {"equals", Value("calm")}}); + branch.routes.emplace_back("world", "world-line", + Value::Object{{"state", Value("mood")}, {"equals", Value("calm")}}); branch.routes.emplace_back("else", "local-line"); ConversationAsset::Node worldLine; - worldLine.id = "world-line"; + worldLine.id = "world-line"; worldLine.kind = ConversationAsset::Node::Kind::Line; worldLine.next = "end"; ConversationAsset::Node localLine = worldLine; - localLine.id = "local-line"; + localLine.id = "local-line"; ConversationAsset::Node end; - end.id = "end"; + end.id = "end"; end.kind = ConversationAsset::Node::Kind::End; asset.nodes = {branch, worldLine, localLine, end}; @@ -127,33 +114,32 @@ ConversationAsset makeWorldBranchAsset() { ConversationAsset makeFacadeAsset() { ConversationAsset asset; - asset.id = "p1.facade"; + asset.id = "p1.facade"; asset.entry = "branch"; ConversationAsset::Node branch; - branch.id = "branch"; + branch.id = "branch"; branch.kind = ConversationAsset::Node::Kind::Branch; - branch.routes.emplace_back( - "custom", "operation", Value::Object{{"policy", Value("facade.test")}}); + branch.routes.emplace_back("custom", "operation", Value::Object{{"policy", Value("facade.test")}}); branch.routes.emplace_back("else", "end"); ConversationAsset::Node operation; - operation.id = "operation"; - operation.kind = ConversationAsset::Node::Kind::Command; - operation.target = "market.buy"; + operation.id = "operation"; + operation.kind = ConversationAsset::Node::Kind::Command; + operation.target = "market.buy"; operation.expression = "operationResult"; - operation.next = "action"; + operation.next = "action"; ConversationAsset::Node action; - action.id = "action"; - action.kind = ConversationAsset::Node::Kind::Command; - action.target = "combat.attack"; + action.id = "action"; + action.kind = ConversationAsset::Node::Kind::Command; + action.target = "combat.attack"; action.commandKind = CommandRequestKind::GameplayAction; - action.expression = "actionResult"; - action.next = "end"; + action.expression = "actionResult"; + action.next = "end"; ConversationAsset::Node end; - end.id = "end"; + end.id = "end"; end.kind = ConversationAsset::Node::Kind::End; asset.nodes = {branch, operation, action, end}; @@ -169,15 +155,13 @@ std::span participantSpan( } // namespace TEST_CASE("dialogueState.conversationLocalsDoNotBecomeWorldState") { - TestWorld world; + TestWorld world; DialogueStateContext context(world.subject); context.setQueryProvider(&world); ConversationAsset asset = makeWorldBranchAsset(); ConversationRunner runner; - runner.setConditionEvaluator([&context](const Value& specification) { - return context.evaluate(specification); - }); + runner.setConditionEvaluator([&context](const Value& specification) { return context.evaluate(specification); }); eve::StateValue bindings = eve::StateValue::object(); bindings.set("mood", eve::StateValue::string("local-mood")); @@ -204,16 +188,14 @@ TEST_CASE("dialogueState.usesSharedConditionExplanation") { const auto result = context.evaluate(specification); CHECK(!result.passed()); - CHECK(static_cast(result.reasonCode()) == - static_cast(eve::decision::ConditionReasonCode::TagMissing)); + CHECK(static_cast(result.reasonCode()) == static_cast(eve::decision::ConditionReasonCode::TagMissing)); CHECK(std::string(eve::decision::conditionReasonCodeName(result.reasonCode())) == "tag_missing"); const Value* tag = result.details().find("tag"); REQUIRE(tag != nullptr); CHECK(tag->asString() == "unlocked"); - const Value comparison = Value::Object{{"attribute", Value("reputation")}, - {"op", Value("gte")}, - {"value", Value(10.0)}}; + const Value comparison = + Value::Object{{"attribute", Value("reputation")}, {"op", Value("gte")}, {"value", Value(10.0)}}; const auto comparisonResult = context.evaluate(comparison); CHECK(comparisonResult.passed()); CHECK(static_cast(comparisonResult.reasonCode()) == @@ -222,16 +204,16 @@ TEST_CASE("dialogueState.usesSharedConditionExplanation") { TEST_CASE("dialogueState.statePatchTransactionFailureDoesNotPartiallyModify") { eve::statepatch::Store store; - auto seed = eve::test_support::openStatePatchBatch(store); + auto seed = eve::test_support::openStatePatchBatch(store); REQUIRE(seed.view.isBound()); CHECK(seed.view->set("actor", "value", "1")); REQUIRE(store.commit(seed.view.get())); - const std::string before = store.snapshotJson(); - const auto beforeRevision = store.revision(); - const auto beforeEvents = store.eventCount(); + const std::string before = store.snapshotJson(); + const auto beforeRevision = store.revision(); + const auto beforeEvents = store.eventCount(); eve::statepatch::StatePatchStateAdapter adapter(store); - std::array mutations{ + std::array mutations{ StateMutation{"actor", "value", Value::integer(2), eve::MutationKind::Set, true}, StateMutation{"actor", "other", Value("not-a-number"), eve::MutationKind::AddNumber, true}, }; @@ -246,11 +228,11 @@ TEST_CASE("dialogueState.statePatchTransactionFailureDoesNotPartiallyModify") { REQUIRE(batch.view.isBound()); CHECK(batch.view->set("actor", "value", "2")); CHECK(!batch.view->set("actor", "broken", "{")); - eve::statepatch::StoreTransactionParticipant participant(store, *batch.view); + eve::statepatch::StoreTransactionParticipant participant(store, *batch.view); std::array participants{&participant}; - eve::transaction::Coordinator coordinator; - const auto transactionResult = coordinator.execute( - eve::transaction::TransactionContext("tx-dialogue-invalid"), participantSpan(participants)); + eve::transaction::Coordinator coordinator; + const auto transactionResult = + coordinator.execute(eve::transaction::TransactionContext("tx-dialogue-invalid"), participantSpan(participants)); CHECK(!transactionResult.ok()); CHECK(store.snapshotJson() == before); CHECK(store.revision() == beforeRevision); @@ -271,8 +253,8 @@ TEST_CASE("dialogueState.valueRoundtripAndLegacyFacadeParity") { legacy.set("values", std::move(values)); legacy.set("name", eve::StateValue::string("dialogue")); - const Value canonical = eve::dialogue::toCanonicalValue(legacy); - const eve::StateValue restored = eve::dialogue::toDialogueStateValue(canonical); + const Value canonical = eve::dialogue::toCanonicalValue(legacy); + const eve::StateValue restored = eve::dialogue::toDialogueStateValue(canonical); CHECK(restored == legacy); auto encoded = canonical.toJson(); @@ -281,8 +263,7 @@ TEST_CASE("dialogueState.valueRoundtripAndLegacyFacadeParity") { REQUIRE(parsed.ok()); CHECK(parsed.value() == canonical); - eve::dialogue::Dialogue::DataValue oldData = - eve::dialogue::Dialogue::DataValue::object(eve::Value::Object{}); + eve::dialogue::Dialogue::DataValue oldData = eve::dialogue::Dialogue::DataValue::object(eve::Value::Object{}); oldData.set("answer", eve::dialogue::Dialogue::VarValue::integer(42)); auto oldEncoded = oldData.toJson(); REQUIRE(oldEncoded.ok()); @@ -291,16 +272,16 @@ TEST_CASE("dialogueState.valueRoundtripAndLegacyFacadeParity") { } TEST_CASE("dialogueState.dialogueFlowConfiguresAllCrossDomainHooks") { - TestWorld world; - RecordingMutation mutation; - DialogueFlow flow; - bool customConditionCalled = false; + TestWorld world; + RecordingMutation mutation; + DialogueFlow flow; + bool customConditionCalled = false; std::vector requests; DialogueFlow::IntegrationConfig config; - config.subject = world.subject; - config.stateQuery = &world; - config.stateMutation = &mutation; + config.subject = world.subject; + config.stateQuery = &world; + config.stateMutation = &mutation; config.conditionEvaluator = [&customConditionCalled](const Value&) { customConditionCalled = true; return eve::decision::ConditionResult::success(Value(true)); @@ -325,15 +306,15 @@ TEST_CASE("dialogueState.dialogueFlowConfiguresAllCrossDomainHooks") { std::array stateMutation{ StateMutation{"actor", "flag", Value(true), eve::MutationKind::Set, false}}; - const auto mutationResult = flow.applyStateMutations( - stateMutation, MutationContext{"tx-facade", "flow", "dialogue"}); + const auto mutationResult = + flow.applyStateMutations(stateMutation, MutationContext{"tx-facade", "flow", "dialogue"}); REQUIRE(mutationResult.ok()); CHECK(mutation.calls == 1); CHECK(mutation.lastCount == 1); CHECK(mutation.lastContext.transactionId == "tx-facade"); - ConversationAsset asset = makeFacadeAsset(); - auto* document = new eve::dialogue::ConversationDocument(asset); + ConversationAsset asset = makeFacadeAsset(); + auto* document = new eve::dialogue::ConversationDocument(asset); CHECK(flow.applyDocument(document)); delete document; diff --git a/test/editor_property_model.cpp b/test/editor_property_model.cpp index c9271ff3..6c767ae4 100644 --- a/test/editor_property_model.cpp +++ b/test/editor_property_model.cpp @@ -87,8 +87,8 @@ TEST_CASE("editor.property_model_routes_runtime_writes_through_command_intent") }); std::string changedPath; - auto subscription = model.subscribe( - [&](const eve::property_access::PropertyChange &change) { changedPath = change.path; }); + auto subscription = + model.subscribe([&](const eve::property_access::PropertyChange &change) { changedPath = change.path; }); CHECK(model.write("movement.speed", eve::Value(9.5)).accepted); CHECK(sinkCalled); CHECK_EQ(provider.speed, 9.5); diff --git a/test/editor_property_v2.cpp b/test/editor_property_v2.cpp index e65779e8..fa013e17 100644 --- a/test/editor_property_v2.cpp +++ b/test/editor_property_v2.cpp @@ -31,14 +31,13 @@ public: return std::make_unique(*this); } - [[nodiscard]] EditorResult commitDomainState( - std::unique_ptr candidate) override { - auto *typed = dynamic_cast(candidate.get()); + [[nodiscard]] EditorResult commitDomainState(std::unique_ptr candidate) override { + auto* typed = dynamic_cast(candidate.get()); if (!typed) return EditorResult::error(EditorStatus::Rejected, RuleId("scene.property.candidate"), "Compensation candidate has an incompatible type"); - position_ = typed->position_; - revision_ = typed->revision_; + position_ = typed->position_; + revision_ = typed->revision_; return EditorResult::applied(); } @@ -71,7 +70,7 @@ class TransformPropertyProvider final : public IPropertyProvider { public: explicit TransformPropertyProvider(TransformPropertyTarget* target) : target_(target) {} - [[nodiscard]] eve::Result currentRevision(const SelectionSnapshot &) const override { + [[nodiscard]] eve::Result currentRevision(const SelectionSnapshot&) const override { return eve::Result::success(eve::Revision(target_->revision())); } @@ -133,7 +132,7 @@ class LegacyPropertyProvider final : public IPropertyProvider { public: explicit LegacyPropertyProvider(TransformPropertyProvider provider) : provider_(std::move(provider)) {} - PropertySchema schema(const SelectionSnapshot& selection) const override { return provider_.schema(selection); } + PropertySchema schema(const SelectionSnapshot& selection) const override { return provider_.schema(selection); } PropertyReadResult read(const SelectionSnapshot& selection, const PropertyPath& path) const override { return provider_.read(selection, path); } @@ -142,7 +141,7 @@ public: return provider_.makeSet(selection, path, value, mode); } EditorResult makeReset(const SelectionSnapshot& selection, - const PropertyPath& path) const override { + const PropertyPath& path) const override { return provider_.makeReset(selection, path); } @@ -308,17 +307,17 @@ TEST_CASE("editor.v2.property_model_uses_transaction_backend_for_commit_undo_and SelectionSnapshot selection = sceneSelection(target); LocalWorldAuthority authority(&target); LocalTransactionBackend backend(&authority); - EditorPropertyModel model(provider.schema(selection), selection, &provider, - PropertyModelSurface::Developer, HostProfile::developer(), &backend); + EditorPropertyModel model(provider.schema(selection), selection, &provider, PropertyModelSurface::Developer, + HostProfile::developer(), &backend); bool legacySinkCalled = false; - model.setEditSink([&](const PropertyEditIntent &) { + model.setEditSink([&](const PropertyEditIntent&) { legacySinkCalled = true; return EditorResult::applied(); }); const EditorValue desired = EditorValue::Array{10.0, 11.0, 12.0}; - const auto write = model.write("transform.position", toPresentationValue(desired)); + const auto write = model.write("transform.position", toPresentationValue(desired)); CHECK(write.accepted); CHECK(!legacySinkCalled); CHECK(target.position() == desired); @@ -341,9 +340,9 @@ TEST_CASE("editor.v2.property_model_explicit_transaction_previews_without_mutati SelectionSnapshot selection = sceneSelection(target); LocalWorldAuthority authority(&target); LocalTransactionBackend backend(&authority); - EditorPropertyModel model(provider.schema(selection), selection, &provider, - PropertyModelSurface::Developer, HostProfile::developer(), &backend); - const EditorValue desired = EditorValue::Array{13.0, 14.0, 15.0}; + EditorPropertyModel model(provider.schema(selection), selection, &provider, PropertyModelSurface::Developer, + HostProfile::developer(), &backend); + const EditorValue desired = EditorValue::Array{13.0, 14.0, 15.0}; auto begun = model.beginTransaction("Set transform position"); CHECK(begun.accepted()); @@ -369,9 +368,9 @@ TEST_CASE("editor.v2.property_model_failed_commit_keeps_target_unchanged_and_is_ SelectionSnapshot selection = sceneSelection(target); ReadOnlyAuthority authority; LocalTransactionBackend backend(&authority); - EditorPropertyModel model(provider.schema(selection), selection, &provider, - PropertyModelSurface::Developer, HostProfile::developer(), &backend); - const EditorValue before = target.position(); + EditorPropertyModel model(provider.schema(selection), selection, &provider, PropertyModelSurface::Developer, + HostProfile::developer(), &backend); + const EditorValue before = target.position(); const auto failed = model.write("transform.position", toPresentationValue(EditorValue::Array{16.0, 17.0, 18.0})); CHECK(!failed.accepted); @@ -390,8 +389,8 @@ TEST_CASE("editor.v2.property_model_binds_non_zero_provider_revision") { SelectionSnapshot selection = sceneSelection(target); LocalWorldAuthority authority(&target); LocalTransactionBackend backend(&authority); - EditorPropertyModel model(provider.schema(selection), selection, &provider, - PropertyModelSurface::Developer, HostProfile::developer(), &backend); + EditorPropertyModel model(provider.schema(selection), selection, &provider, PropertyModelSurface::Developer, + HostProfile::developer(), &backend); CHECK_EQ(model.targetRevision().value(), static_cast(41)); const auto write = model.write("transform.position", toPresentationValue(EditorValue::Array{1.0, 2.0, 3.0})); @@ -405,8 +404,8 @@ TEST_CASE("editor.v2.property_model_rejects_external_change_until_refresh_rebase SelectionSnapshot selection = sceneSelection(target); LocalWorldAuthority authority(&target); LocalTransactionBackend backend(&authority); - EditorPropertyModel model(provider.schema(selection), selection, &provider, - PropertyModelSurface::Developer, HostProfile::developer(), &backend); + EditorPropertyModel model(provider.schema(selection), selection, &provider, PropertyModelSurface::Developer, + HostProfile::developer(), &backend); const EditorValue external = EditorValue::Array{20.0, 21.0, 22.0}; target.externalSet(external); @@ -420,8 +419,7 @@ TEST_CASE("editor.v2.property_model_rejects_external_change_until_refresh_rebase const auto refreshed = model.refresh(); CHECK(refreshed.accepted()); CHECK_EQ(model.targetRevision().value(), static_cast(8)); - CHECK(model.read("transform.position") == - std::optional(toPresentationValue(external))); + CHECK(model.read("transform.position") == std::optional(toPresentationValue(external))); const auto accepted = model.write("transform.position", toPresentationValue(EditorValue::Array{8.0, 9.0, 10.0})); CHECK(accepted.accepted); @@ -434,8 +432,8 @@ TEST_CASE("editor.v2.property_model_external_change_conflict_preserves_failed_tr SelectionSnapshot selection = sceneSelection(target); LocalWorldAuthority authority(&target); LocalTransactionBackend backend(&authority); - EditorPropertyModel model(provider.schema(selection), selection, &provider, - PropertyModelSurface::Developer, HostProfile::developer(), &backend); + EditorPropertyModel model(provider.schema(selection), selection, &provider, PropertyModelSurface::Developer, + HostProfile::developer(), &backend); const auto begun = model.beginTransaction("stale property edit"); CHECK(begun.accepted()); @@ -463,7 +461,7 @@ TEST_CASE("editor.v2.legacy_property_provider_fails_closed_without_implicit_zero LegacyPropertyProvider legacy(std::move(revisionAware)); SelectionSnapshot selection = sceneSelection(target); EditorPropertyModel model(legacy.schema(selection), selection, &legacy); - model.setEditSink([](const PropertyEditIntent &) { return EditorResult::applied(); }); + model.setEditSink([](const PropertyEditIntent&) { return EditorResult::applied(); }); const auto rejected = model.write("transform.position", toPresentationValue(EditorValue::Array{2.0, 3.0, 4.0})); CHECK(!rejected.accepted); diff --git a/test/editor_transaction_consumer.cpp b/test/editor_transaction_consumer.cpp index 5d7c04d0..763f9c40 100644 --- a/test/editor_transaction_consumer.cpp +++ b/test/editor_transaction_consumer.cpp @@ -21,8 +21,8 @@ using eve::DiagnosticCode; using eve::Result; using eve::StatusCode; using eve::editor::DomainOperation; -using eve::editor::EditorResult; using eve::editor::EditorCommitState; +using eve::editor::EditorResult; using eve::editor::EditorStatus; using eve::editor::EditorTransactionConsumer; using eve::editor::IEditAuthority; @@ -63,11 +63,11 @@ public: return Result::success(); } - int prepareCount = 0; - int commitCount = 0; - int rollbackCount = 0; + int prepareCount = 0; + int commitCount = 0; + int rollbackCount = 0; int compensateCount = 0; - bool prepared = false; + bool prepared = false; bool failCommit = true; }; @@ -77,7 +77,7 @@ public: : name_(std::move(name)), value_(value), after_(after) {} const std::string& name() const override { return name_; } - bool apply() override { + bool apply() override { if (!value_) return false; before_ = *value_; *value_ = after_; @@ -101,35 +101,33 @@ private: class RejectingAuthority final : public IEditAuthority { public: - EditorResult preflight( - const TransactionSpec&, std::span) override { + EditorResult preflight(const TransactionSpec&, + std::span) override { ++preflightCount; return EditorResult::error( - EditorStatus::Conflict, eve::editor::RuleId("test.preflight.rejected"), - "injected preflight rejection"); + EditorStatus::Conflict, eve::editor::RuleId("test.preflight.rejected"), "injected preflight rejection"); } EditorResult commit(const eve::editor::AuthorityPlan&) override { ++commitCount; - return EditorResult::error( - EditorStatus::Failed, eve::editor::RuleId("test.commit.unexpected"), - "commit must not be called after preflight rejection"); + return EditorResult::error(EditorStatus::Failed, + eve::editor::RuleId("test.commit.unexpected"), + "commit must not be called after preflight rejection"); } EditorResult compensate(const TransactionReceipt&) override { ++compensateCount; - return EditorResult::error( - EditorStatus::Failed, eve::editor::RuleId("test.compensate.unexpected"), - "compensation must not be called after preflight rejection"); + return EditorResult::error(EditorStatus::Failed, + eve::editor::RuleId("test.compensate.unexpected"), + "compensation must not be called after preflight rejection"); } - int preflightCount = 0; - int commitCount = 0; - int compensateCount = 0; + int preflightCount = 0; + int commitCount = 0; + int compensateCount = 0; }; -std::unique_ptr makeTileCommand( - eve::editor::TileBufferTarget& target, int value) { +std::unique_ptr makeTileCommand(eve::editor::TileBufferTarget& target, int value) { auto command = std::make_unique("set-tile", &target); if (!command->record(0, 0, value)) return nullptr; return command; @@ -152,9 +150,9 @@ DomainOperation rejectedOperation() { } // namespace TEST_CASE("editor.transactionConsumer.commitUndoRedoAndDryRun") { - eve::editor::TileBuffer buffer(1, 1); + eve::editor::TileBuffer buffer(1, 1); eve::editor::TileBufferTarget target("tiles", &buffer); - EditorTransactionConsumer consumer; + EditorTransactionConsumer consumer; auto begun = consumer.begin(commandSpec("editor.command.1")); REQUIRE(begun.ok()); @@ -191,7 +189,7 @@ TEST_CASE("editor.transactionConsumer.commitUndoRedoAndDryRun") { } TEST_CASE("editor.transactionConsumer.authorityPreflightRejectsWithoutCommit") { - RejectingAuthority authority; + RejectingAuthority authority; EditorTransactionConsumer consumer(&authority); TransactionSpec specification; @@ -214,18 +212,17 @@ TEST_CASE("editor.transactionConsumer.authorityPreflightRejectsWithoutCommit") { } TEST_CASE("editor.transactionConsumer.secondParticipantFailureCompensatesEditor") { - eve::editor::TileBuffer buffer(1, 1); + eve::editor::TileBuffer buffer(1, 1); eve::editor::TileBufferTarget target("tiles", &buffer); - EditorTransactionConsumer consumer; + EditorTransactionConsumer consumer; REQUIRE(consumer.begin(commandSpec("editor.composed.failure")).ok()); auto command = makeTileCommand(target, 9); REQUIRE(static_cast(command)); REQUIRE(consumer.append(std::move(command)).ok()); - FailingParticipant second; + FailingParticipant second; std::array additional{&second}; - auto result = consumer.commit( - std::span(additional.data(), additional.size())); + auto result = consumer.commit(std::span(additional.data(), additional.size())); CHECK(!result.ok()); CHECK_EQ(buffer.getGid(0, 0), 0); CHECK_EQ(second.prepareCount, 1); @@ -238,20 +235,19 @@ TEST_CASE("editor.transactionConsumer.secondParticipantFailureCompensatesEditor" CHECK(!consumer.diagnostics().empty()); second.failCommit = false; - auto retried = consumer.retry(std::span(additional.data(), additional.size())); + auto retried = consumer.retry(std::span(additional.data(), additional.size())); REQUIRE(retried.ok()); CHECK_EQ(buffer.getGid(0, 0), 9); CHECK_EQ(second.commitCount, 2); CHECK_EQ(static_cast(consumer.state()), static_cast(EditorCommitState::Committed)); - auto duplicateCommit = consumer.commit( - std::span(additional.data(), additional.size())); + auto duplicateCommit = consumer.commit(std::span(additional.data(), additional.size())); CHECK(!duplicateCommit.ok()); CHECK_EQ(second.commitCount, 2); } TEST_CASE("editor.transactionConsumer.previewMergeFailureDoesNotMutateTarget") { - int value = 0; + int value = 0; EditorTransactionConsumer consumer; REQUIRE(consumer.begin(commandSpec("editor.preview.merge-failure")).ok()); @@ -272,9 +268,9 @@ TEST_CASE("editor.transactionConsumer.previewMergeFailureDoesNotMutateTarget") { } TEST_CASE("editor.transactionConsumer.explicitDiscardClearsPendingWork") { - eve::editor::TileBuffer buffer(1, 1); + eve::editor::TileBuffer buffer(1, 1); eve::editor::TileBufferTarget target("tiles", &buffer); - EditorTransactionConsumer consumer; + EditorTransactionConsumer consumer; REQUIRE(consumer.begin(commandSpec("editor.explicit-discard")).ok()); auto command = makeTileCommand(target, 4); REQUIRE(static_cast(command)); diff --git a/test/editor_transaction_v2.cpp b/test/editor_transaction_v2.cpp index ef923141..2b4c68ef 100644 --- a/test/editor_transaction_v2.cpp +++ b/test/editor_transaction_v2.cpp @@ -68,8 +68,7 @@ private: EditRegion dirty_; }; -DomainOperation setInteger(const IntegerOperationTarget& target, int before, int after, - std::string inverseType = {}) { +DomainOperation setInteger(const IntegerOperationTarget& target, int before, int after, std::string inverseType = {}) { DomainOperation operation; operation.type = "test.integer.set.v1"; operation.inverseType = std::move(inverseType); @@ -107,10 +106,10 @@ TEST_CASE("editor.v2.authority_preflight_commit_and_compensate") { CHECK_EQ(committed.value->beforeRevision, static_cast(0)); CHECK_EQ(committed.value->afterRevision, static_cast(1)); - target.failCandidateCommit = true; + target.failCandidateCommit = true; const auto valueBeforeFailedCompensation = target.value(); const auto revisionBeforeFailedCompensation = target.revision(); - auto failedCompensation = authority.compensate(*committed.value); + auto failedCompensation = authority.compensate(*committed.value); CHECK(!failedCompensation.accepted()); CHECK_EQ(target.value(), valueBeforeFailedCompensation); CHECK_EQ(target.revision(), revisionBeforeFailedCompensation); @@ -128,7 +127,7 @@ TEST_CASE("editor.v2.authority_preflight_commit_and_compensate") { TEST_CASE("editor.v2.authority_compensation_validates_full_inverse_on_candidate") { IntegerOperationTarget target("integer", 0); LocalWorldAuthority authority(&target); - const DomainOperation operations[] = { + const DomainOperation operations[] = { setInteger(target, 0, 9, "test.fail.v1"), setInteger(target, 9, 5), }; diff --git a/test/effects.cpp b/test/effects.cpp index a65ae233..dee4f79a 100644 --- a/test/effects.cpp +++ b/test/effects.cpp @@ -12,11 +12,11 @@ using namespace eve::effects; TEST_CASE("effects.apply.stableFactsAndDeterministicQueries") { EffectContainer effects; - auto firstResult = effects.apply("unit:7", "weather.wet", "zone:2", 3, 10.0, "surface", StackPolicy::Stack); + auto firstResult = effects.apply("unit:7", "weather.wet", "zone:2", 3, 10.0, "surface", StackPolicy::Stack); auto secondResult = effects.apply("unit:7", "policy.tax", "state:1", 9, 0.0, "policy", StackPolicy::Stack); REQUIRE(firstResult.ok()); REQUIRE(secondResult.ok()); - const auto first = std::move(firstResult).takeValue(); + const auto first = std::move(firstResult).takeValue(); const auto second = std::move(secondResult).takeValue(); CHECK_EQ(first, std::string("effect-0000000000000001")); @@ -31,11 +31,11 @@ TEST_CASE("effects.apply.stableFactsAndDeterministicQueries") { TEST_CASE("effects.stackPolicy.replaceStackRefresh") { EffectContainer effects; - auto oldResult = effects.apply("base:1", "policy.a", "leader:1", 1, 5.0, "administration", StackPolicy::Stack); + auto oldResult = effects.apply("base:1", "policy.a", "leader:1", 1, 5.0, "administration", StackPolicy::Stack); auto stackedResult = effects.apply("base:1", "policy.b", "leader:2", 2, 7.0, "administration", StackPolicy::Stack); REQUIRE(oldResult.ok()); REQUIRE(stackedResult.ok()); - const auto old = std::move(oldResult).takeValue(); + const auto old = std::move(oldResult).takeValue(); const auto stacked = std::move(stackedResult).takeValue(); CHECK_EQ(effects.effectCount(), 2); @@ -66,15 +66,15 @@ TEST_CASE("effects.stackPolicy.replaceStackRefresh") { TEST_CASE("effects.update.expiresFiniteFactsInCreationOrder") { EffectContainer effects; - auto firstResult = effects.apply("army:1", "one", "", 0, 0.5, "", StackPolicy::Stack); - auto permanentResult = effects.apply("army:1", "permanent", "", 0, 0.0, "", StackPolicy::Stack); - auto secondResult = effects.apply("army:1", "two", "", 0, 0.5, "", StackPolicy::Stack); + auto firstResult = effects.apply("army:1", "one", "", 0, 0.5, "", StackPolicy::Stack); + auto permanentResult = effects.apply("army:1", "permanent", "", 0, 0.0, "", StackPolicy::Stack); + auto secondResult = effects.apply("army:1", "two", "", 0, 0.5, "", StackPolicy::Stack); REQUIRE(firstResult.ok()); REQUIRE(permanentResult.ok()); REQUIRE(secondResult.ok()); - const auto first = std::move(firstResult).takeValue(); + const auto first = std::move(firstResult).takeValue(); const auto permanent = std::move(permanentResult).takeValue(); - const auto second = std::move(secondResult).takeValue(); + const auto second = std::move(secondResult).takeValue(); effects.clearEvents(); REQUIRE(effects.update(0.25).ok()); @@ -92,9 +92,9 @@ TEST_CASE("effects.update.expiresFiniteFactsInCreationOrder") { TEST_CASE("effects.tagsAndPayload.areDeterministic") { EffectContainer effects; - auto idResult = effects.apply("unit:2", "condition", "system", 0, 0.0, "", StackPolicy::Stack); + auto idResult = effects.apply("unit:2", "condition", "system", 0, 0.0, "", StackPolicy::Stack); REQUIRE(idResult.ok()); - const auto id = std::move(idResult).takeValue(); + const auto id = std::move(idResult).takeValue(); auto* effect = effects.find(id); REQUIRE(effect != nullptr); CHECK(effect->addTag("visible").ok()); @@ -113,7 +113,7 @@ TEST_CASE("effects.tagsAndPayload.areDeterministic") { TEST_CASE("effects.remove.emitsStableEvent") { EffectContainer effects; - auto idResult = effects.apply("subject", "type", "source", 0, 0.0, "", StackPolicy::Stack); + auto idResult = effects.apply("subject", "type", "source", 0, 0.0, "", StackPolicy::Stack); REQUIRE(idResult.ok()); const auto id = std::move(idResult).takeValue(); CHECK(effects.remove(id, "subject_deleted").ok()); diff --git a/test/effects_domain_adapters.cpp b/test/effects_domain_adapters.cpp index e0388f3b..e67a281b 100644 --- a/test/effects_domain_adapters.cpp +++ b/test/effects_domain_adapters.cpp @@ -15,7 +15,7 @@ namespace { eve::SubjectRef subject() { eve::PersistentId::Bytes bytes{}; - bytes[0] = 0x42; + bytes[0] = 0x42; bytes[15] = 0x17; return eve::SubjectRef::fromPersistentId(eve::PersistentId(bytes)); } @@ -28,59 +28,59 @@ eve::SimulationStep step(std::uint64_t tick, double seconds) { eve::effects::EffectPolicy oncePolicy() { eve::effects::EffectPolicy policy; - policy.stackMode = eve::effects::StackMode::NewInstance; + policy.stackMode = eve::effects::StackMode::NewInstance; policy.stackCount = eve::effects::StackCountPolicy::Keep; - policy.maxStacks = 1; + policy.maxStacks = 1; return policy; } } // namespace TEST_CASE("effects.domainAdapters.shareLifecycleAndPeriodicParity") { - eve::card::CardEffectAdapter card; - eve::rts::RTSEffectAdapter rts; + eve::card::CardEffectAdapter card; + eve::rts::RTSEffectAdapter rts; eve::vehicle::VehicleEffectAdapter vehicle; - eve::weapon::WeaponEffectAdapter weapon; + eve::weapon::WeaponEffectAdapter weapon; eve::card::CardEffectDefinition cardDefinition; - cardDefinition.id = "periodic.damage"; - cardDefinition.source = "spell.card"; - cardDefinition.duration = 3.0; - cardDefinition.period = 1.0; + cardDefinition.id = "periodic.damage"; + cardDefinition.source = "spell.card"; + cardDefinition.duration = 3.0; + cardDefinition.period = 1.0; cardDefinition.magnitude = 5.0; - cardDefinition.policy = oncePolicy(); - auto cardHandle = card.apply(cardDefinition, subject()); + cardDefinition.policy = oncePolicy(); + auto cardHandle = card.apply(cardDefinition, subject()); REQUIRE(cardHandle.ok()); eve::rts::RTSEffectDefinition rtsDefinition; - rtsDefinition.id = cardDefinition.id; - rtsDefinition.source = cardDefinition.source; - rtsDefinition.duration = cardDefinition.duration; - rtsDefinition.period = cardDefinition.period; + rtsDefinition.id = cardDefinition.id; + rtsDefinition.source = cardDefinition.source; + rtsDefinition.duration = cardDefinition.duration; + rtsDefinition.period = cardDefinition.period; rtsDefinition.magnitude = cardDefinition.magnitude; - rtsDefinition.policy = cardDefinition.policy; - auto rtsHandle = rts.apply(rtsDefinition, subject()); + rtsDefinition.policy = cardDefinition.policy; + auto rtsHandle = rts.apply(rtsDefinition, subject()); REQUIRE(rtsHandle.ok()); eve::vehicle::VehicleEffectDefinition vehicleDefinition; - vehicleDefinition.id = cardDefinition.id; - vehicleDefinition.source = cardDefinition.source; - vehicleDefinition.duration = cardDefinition.duration; - vehicleDefinition.period = cardDefinition.period; + vehicleDefinition.id = cardDefinition.id; + vehicleDefinition.source = cardDefinition.source; + vehicleDefinition.duration = cardDefinition.duration; + vehicleDefinition.period = cardDefinition.period; vehicleDefinition.magnitude = cardDefinition.magnitude; - vehicleDefinition.policy = cardDefinition.policy; + vehicleDefinition.policy = cardDefinition.policy; vehicleDefinition.armorZone = "side"; - auto vehicleHandle = vehicle.apply(vehicleDefinition, subject()); + auto vehicleHandle = vehicle.apply(vehicleDefinition, subject()); REQUIRE(vehicleHandle.ok()); eve::weapon::WeaponEffectDefinition weaponDefinition; - weaponDefinition.id = cardDefinition.id; - weaponDefinition.source = cardDefinition.source; - weaponDefinition.duration = cardDefinition.duration; - weaponDefinition.period = cardDefinition.period; + weaponDefinition.id = cardDefinition.id; + weaponDefinition.source = cardDefinition.source; + weaponDefinition.duration = cardDefinition.duration; + weaponDefinition.period = cardDefinition.period; weaponDefinition.magnitude = cardDefinition.magnitude; - weaponDefinition.policy = cardDefinition.policy; - auto weaponHandle = weapon.apply(weaponDefinition, subject()); + weaponDefinition.policy = cardDefinition.policy; + auto weaponHandle = weapon.apply(weaponDefinition, subject()); REQUIRE(weaponHandle.ok()); const auto checkCommon = [&](const auto& adapter, const auto& handle) { @@ -98,10 +98,10 @@ TEST_CASE("effects.domainAdapters.shareLifecycleAndPeriodicParity") { checkCommon(vehicle, std::move(vehicleHandle).takeValue()); checkCommon(weapon, std::move(weaponHandle).takeValue()); - auto cardUpdate = card.advance(step(1, 1.0)); - auto rtsUpdate = rts.advance(step(1, 1.0)); + auto cardUpdate = card.advance(step(1, 1.0)); + auto rtsUpdate = rts.advance(step(1, 1.0)); auto vehicleUpdate = vehicle.advance(step(1, 1.0)); - auto weaponUpdate = weapon.advance(step(1, 1.0)); + auto weaponUpdate = weapon.advance(step(1, 1.0)); REQUIRE(cardUpdate.ok()); REQUIRE(rtsUpdate.ok()); REQUIRE(vehicleUpdate.ok()); @@ -117,115 +117,114 @@ TEST_CASE("effects.domainAdapters.shareLifecycleAndPeriodicParity") { } TEST_CASE("effects.domainAdapters.failureIsAtomicAndRestoreMakesHandlesStale") { - eve::card::CardEffectAdapter card; - eve::rts::RTSEffectAdapter rts; + eve::card::CardEffectAdapter card; + eve::rts::RTSEffectAdapter rts; eve::vehicle::VehicleEffectAdapter vehicle; - eve::weapon::WeaponEffectAdapter weapon; + eve::weapon::WeaponEffectAdapter weapon; eve::card::CardEffectDefinition cardDefinition; - cardDefinition.id = "failure.damage"; - cardDefinition.period = 1.0; + cardDefinition.id = "failure.damage"; + cardDefinition.period = 1.0; cardDefinition.magnitude = -2.0; - cardDefinition.policy = oncePolicy(); - auto cardHandle = card.apply(cardDefinition, subject()); + cardDefinition.policy = oncePolicy(); + auto cardHandle = card.apply(cardDefinition, subject()); REQUIRE(cardHandle.ok()); const auto cardReference = std::move(cardHandle).takeValue(); - const auto cardHealth = card.target().health; - auto cardFailed = card.advance(step(1, 1.0)); + const auto cardHealth = card.target().health; + auto cardFailed = card.advance(step(1, 1.0)); CHECK(!cardFailed.ok()); CHECK_EQ(card.target().health, cardHealth); CHECK(card.resolve(cardReference).ok()); eve::rts::RTSEffectDefinition rtsDefinition; - rtsDefinition.id = "failure.morale"; - rtsDefinition.period = 1.0; + rtsDefinition.id = "failure.morale"; + rtsDefinition.period = 1.0; rtsDefinition.magnitude = -2.0; - rtsDefinition.policy = oncePolicy(); - auto rtsHandle = rts.apply(rtsDefinition, subject()); + rtsDefinition.policy = oncePolicy(); + auto rtsHandle = rts.apply(rtsDefinition, subject()); REQUIRE(rtsHandle.ok()); const auto rtsReference = std::move(rtsHandle).takeValue(); - const auto rtsMorale = rts.target().morale; - auto rtsFailed = rts.advance(step(1, 1.0)); + const auto rtsMorale = rts.target().morale; + auto rtsFailed = rts.advance(step(1, 1.0)); CHECK(!rtsFailed.ok()); CHECK_EQ(rts.target().morale, rtsMorale); CHECK(rts.resolve(rtsReference).ok()); eve::vehicle::VehicleEffectDefinition vehicleDefinition; - vehicleDefinition.id = "failure.hull"; - vehicleDefinition.period = 1.0; + vehicleDefinition.id = "failure.hull"; + vehicleDefinition.period = 1.0; vehicleDefinition.magnitude = -2.0; - vehicleDefinition.policy = oncePolicy(); - auto vehicleHandle = vehicle.apply(vehicleDefinition, subject()); + vehicleDefinition.policy = oncePolicy(); + auto vehicleHandle = vehicle.apply(vehicleDefinition, subject()); REQUIRE(vehicleHandle.ok()); const auto vehicleReference = std::move(vehicleHandle).takeValue(); - const auto vehicleHull = vehicle.target().hull; - auto vehicleFailed = vehicle.advance(step(1, 1.0)); + const auto vehicleHull = vehicle.target().hull; + auto vehicleFailed = vehicle.advance(step(1, 1.0)); CHECK(!vehicleFailed.ok()); CHECK_EQ(vehicle.target().hull, vehicleHull); CHECK(vehicle.resolve(vehicleReference).ok()); eve::weapon::WeaponEffectDefinition weaponDefinition; - weaponDefinition.id = "failure.heat"; - weaponDefinition.period = 1.0; + weaponDefinition.id = "failure.heat"; + weaponDefinition.period = 1.0; weaponDefinition.magnitude = -2.0; - weaponDefinition.policy = oncePolicy(); - auto weaponHandle = weapon.apply(weaponDefinition, subject()); + weaponDefinition.policy = oncePolicy(); + auto weaponHandle = weapon.apply(weaponDefinition, subject()); REQUIRE(weaponHandle.ok()); const auto weaponReference = std::move(weaponHandle).takeValue(); - const auto weaponHeat = weapon.target().heat; - auto weaponFailed = weapon.advance(step(1, 1.0)); + const auto weaponHeat = weapon.target().heat; + auto weaponFailed = weapon.advance(step(1, 1.0)); CHECK(!weaponFailed.ok()); CHECK_EQ(weapon.target().heat, weaponHeat); CHECK(weapon.resolve(weaponReference).ok()); const auto snapshot = weapon.snapshot(); - auto extra = weapon.apply(weaponDefinition, subject()); + auto extra = weapon.apply(weaponDefinition, subject()); REQUIRE(extra.ok()); const auto stale = std::move(extra).takeValue(); REQUIRE(weapon.restore(snapshot).ok()); CHECK(!weapon.resolve(stale).ok()); - CHECK_EQ(static_cast(weapon.resolve(stale).code()), - static_cast(eve::StatusCode::Rejected)); + CHECK_EQ(static_cast(weapon.resolve(stale).code()), static_cast(eve::StatusCode::Rejected)); CHECK(!weapon.resolve(weaponReference).ok()); } TEST_CASE("effects.domainAdapters.keepDomainStrategies") { - eve::card::CardEffectAdapter card; + eve::card::CardEffectAdapter card; eve::card::CardEffectDefinition shield; - shield.id = "card.shield"; + shield.id = "card.shield"; shield.magnitude = 8.0; - shield.kind = eve::card::CardEffectKind::Shield; - shield.policy = oncePolicy(); + shield.kind = eve::card::CardEffectKind::Shield; + shield.policy = oncePolicy(); REQUIRE(card.apply(shield, subject()).ok()); CHECK_EQ(card.target().barrier, 8); - eve::rts::RTSEffectAdapter rts; + eve::rts::RTSEffectAdapter rts; eve::rts::RTSEffectDefinition lock; - lock.id = "rts.production-lock"; - lock.kind = eve::rts::RTSEffectKind::ProductionLock; + lock.id = "rts.production-lock"; + lock.kind = eve::rts::RTSEffectKind::ProductionLock; lock.policy = oncePolicy(); REQUIRE(rts.apply(lock, subject()).ok()); CHECK(rts.target().productionLocked); - eve::vehicle::VehicleEffectAdapter vehicle; + eve::vehicle::VehicleEffectAdapter vehicle; eve::vehicle::VehicleEffectDefinition hit; - hit.id = "vehicle.rear-hit"; + hit.id = "vehicle.rear-hit"; hit.armorZone = "rear"; - hit.period = 1.0; + hit.period = 1.0; hit.magnitude = 10.0; - hit.policy = oncePolicy(); + hit.policy = oncePolicy(); REQUIRE(vehicle.apply(hit, subject()).ok()); REQUIRE(vehicle.advance(step(1, 1.0)).ok()); CHECK_EQ(vehicle.target().hull, 87.5); CHECK_EQ(vehicle.target().criticalHits, 0u); - eve::weapon::WeaponEffectAdapter weapon; + eve::weapon::WeaponEffectAdapter weapon; eve::weapon::WeaponEffectDefinition heat; - heat.id = "weapon.overheat"; - heat.period = 1.0; - heat.duration = 2.0; + heat.id = "weapon.overheat"; + heat.period = 1.0; + heat.duration = 2.0; heat.magnitude = 60.0; - heat.policy = oncePolicy(); + heat.policy = oncePolicy(); REQUIRE(weapon.apply(heat, subject()).ok()); REQUIRE(weapon.advance(step(1, 1.0)).ok()); REQUIRE(weapon.advance(step(2, 1.0)).ok()); diff --git a/test/effects_lifecycle.cpp b/test/effects_lifecycle.cpp index f5daaf5a..283596ad 100644 --- a/test/effects_lifecycle.cpp +++ b/test/effects_lifecycle.cpp @@ -32,24 +32,24 @@ TEST_CASE("effects.definitionPayload.usesCanonicalValueObject") { TEST_CASE("effects.layers.definitionInstanceAndContainer.copyDefinitionData") { EffectDefinition definition; - definition.id = "damage.over.time"; - definition.stackKey = "damage"; - definition.duration = 5.0; - definition.magnitude = 2.0; - definition.policy.stackMode = StackMode::Accumulate; + definition.id = "damage.over.time"; + definition.stackKey = "damage"; + definition.duration = 5.0; + definition.magnitude = 2.0; + definition.policy.stackMode = StackMode::Accumulate; definition.policy.stackCount = StackCountPolicy::Increment; - definition.policy.duration = DurationPolicy::Extend; - definition.policy.magnitude = MagnitudePolicy::Add; - definition.policy.maxStacks = 3; + definition.policy.duration = DurationPolicy::Extend; + definition.policy.magnitude = MagnitudePolicy::Add; + definition.policy.maxStacks = 3; definition.payload.setString("kind", "periodic"); definition.tags = {"debuff", "damage"}; EffectContainer container; - auto firstResult = container.apply(definition, "unit:1", "source:1"); + auto firstResult = container.apply(definition, "unit:1", "source:1"); REQUIRE(firstResult.ok()); const std::string first = std::move(firstResult).takeValue(); - definition.duration = 99.0; - definition.magnitude = 99.0; + definition.duration = 99.0; + definition.magnitude = 99.0; definition.payload.setString("kind", "mutated-after-apply"); auto secondResult = container.apply(definition, "unit:1", "source:2"); @@ -68,25 +68,25 @@ TEST_CASE("effects.layers.definitionInstanceAndContainer.copyDefinitionData") { TEST_CASE("effects.policies.stackDurationMagnitudeAndOverflow.areIndependent") { EffectDefinition definition; - definition.id = "policy.test"; - definition.duration = 4.0; - definition.magnitude = 3.0; - definition.policy.stackMode = StackMode::Accumulate; + definition.id = "policy.test"; + definition.duration = 4.0; + definition.magnitude = 3.0; + definition.policy.stackMode = StackMode::Accumulate; definition.policy.stackCount = StackCountPolicy::Increment; - definition.policy.duration = DurationPolicy::Keep; - definition.policy.magnitude = MagnitudePolicy::Max; - definition.policy.maxStacks = 2; - definition.policy.overflow = OverflowPolicy::Reject; + definition.policy.duration = DurationPolicy::Keep; + definition.policy.magnitude = MagnitudePolicy::Max; + definition.policy.maxStacks = 2; + definition.policy.overflow = OverflowPolicy::Reject; EffectContainer container; - auto firstResult = container.apply(definition, "unit:2", "source"); + auto firstResult = container.apply(definition, "unit:2", "source"); REQUIRE(firstResult.ok()); const std::string id = std::move(firstResult).takeValue(); container.update(1.0).ignore("test elapsed time"); - definition.duration = 100.0; + definition.duration = 100.0; definition.magnitude = 5.0; - auto secondResult = container.apply(definition, "unit:2", "source"); + auto secondResult = container.apply(definition, "unit:2", "source"); REQUIRE(secondResult.ok()); std::move(secondResult).takeValue(); CHECK_EQ(container.find(id)->stackCount, std::uint32_t{2}); @@ -102,16 +102,16 @@ TEST_CASE("effects.policies.stackDurationMagnitudeAndOverflow.areIndependent") { TEST_CASE("effects.executor.advancesOnlyLifecycleAndReportsExpiry") { EffectDefinition definition; - definition.id = "temporary"; + definition.id = "temporary"; definition.duration = 1.0; EffectContainer container; - auto idResult = container.apply(definition, "unit:3", "source"); + auto idResult = container.apply(definition, "unit:3", "source"); REQUIRE(idResult.ok()); const std::string id = std::move(idResult).takeValue(); EffectExecutor executor; - auto invalidResult = executor.advance(container, -1.0); + auto invalidResult = executor.advance(container, -1.0); CHECK(!invalidResult.ok()); CHECK_EQ(invalidResult.code(), eve::StatusCode::Rejected); diff --git a/test/event.cpp b/test/event.cpp index f511b362..654be482 100644 --- a/test/event.cpp +++ b/test/event.cpp @@ -7,8 +7,8 @@ #include #include -using eve::platform_event::PlatformEvent; using eve::platform_event::Message; +using eve::platform_event::PlatformEvent; using eve::platform_event::Variant; namespace { @@ -106,7 +106,7 @@ TEST_CASE("event.pushPollRoundTrip") { } TEST_CASE("event.ownedPushPollDestroysPayloadWithMessage") { - auto* ev = PlatformEvent::create(); + auto* ev = PlatformEvent::create(); int destructions = 0; std::vector args; args.push_back(Variant::makeOwnedPtr(new TrackedPayload(&destructions))); @@ -123,7 +123,7 @@ TEST_CASE("event.ownedPushPollDestroysPayloadWithMessage") { } TEST_CASE("event.clearDestroysOwnedPayloadExactlyOnce") { - auto* ev = PlatformEvent::create(); + auto* ev = PlatformEvent::create(); int destructions = 0; std::vector args; args.push_back(Variant::makeOwnedPtr(new TrackedPayload(&destructions))); @@ -172,7 +172,7 @@ TEST_CASE("event.pumpSmoke") { } TEST_CASE("event.workerPushWakesWait") { - auto *ev = PlatformEvent::create(); + auto* ev = PlatformEvent::create(); ev->clear(); std::thread producer([ev] { std::this_thread::sleep_for(std::chrono::milliseconds(20)); diff --git a/test/event_envelope.cpp b/test/event_envelope.cpp index 3026339d..9ee979b4 100644 --- a/test/event_envelope.cpp +++ b/test/event_envelope.cpp @@ -19,24 +19,24 @@ EventId id(const char* text) { return *parsed; } -GameEvent metadata(const char* eventId, const char* schema, uint64_t tick, - const char* causation = "", const char* correlation = "") { +GameEvent metadata(const char* eventId, const char* schema, uint64_t tick, const char* causation = "", + const char* correlation = "") { GameEvent envelope; - envelope.eventId = id(eventId); - envelope.type = "damage.applied"; - envelope.source = "combat:system"; - envelope.subject = "rpg:target-7"; + envelope.eventId = id(eventId); + envelope.type = "damage.applied"; + envelope.source = "combat:system"; + envelope.subject = "rpg:target-7"; if (causation[0] != '\0') envelope.causation = CausationRef::fromEventId(id(causation)); if (correlation[0] != '\0') envelope.correlation = CorrelationId::fromEventId(id(correlation)); - envelope.schemaId = *SchemaId::parse(schema); + envelope.schemaId = *SchemaId::parse(schema); envelope.schemaVersion = SchemaVersion(2); - envelope.tick = SimulationTick(tick); - envelope.flags = 4; + envelope.tick = SimulationTick(tick); + envelope.flags = 4; return envelope; } struct DamagePayload { - int amount = 0; + int amount = 0; std::string kind; }; @@ -44,19 +44,17 @@ struct DamagePayload { TEST_CASE("event_envelope.typedPayloadAndCausalMetadata") { GameEventLog stream; - const auto causation = id("018f0f4e-6b3c-7abc-8def-0123456789ab"); - const auto correlation = id("018f0f4e-6b3c-7abd-8def-0123456789ab"); - const auto event = id("018f0f4e-6b3c-7abe-8def-0123456789ab"); + const auto causation = id("018f0f4e-6b3c-7abc-8def-0123456789ab"); + const auto correlation = id("018f0f4e-6b3c-7abd-8def-0123456789ab"); + const auto event = id("018f0f4e-6b3c-7abe-8def-0123456789ab"); - auto envelope = metadata(event.format().c_str(), "combat:damage", 77, - causation.format().c_str(), correlation.format().c_str()); + auto envelope = + metadata(event.format().c_str(), "combat:damage", 77, causation.format().c_str(), correlation.format().c_str()); DamagePayload payload{12, "fire"}; - auto appended = stream.appendTyped( - std::move(envelope), payload, [](const DamagePayload& value) { - return eve::Result::success( - "{\"amount\":" + std::to_string(value.amount) + - ",\"kind\":\"" + value.kind + "\"}"); - }); + auto appended = stream.appendTyped(std::move(envelope), payload, [](const DamagePayload& value) { + return eve::Result::success("{\"amount\":" + std::to_string(value.amount) + ",\"kind\":\"" + + value.kind + "\"}"); + }); REQUIRE(appended.ok()); CHECK_EQ(appended.value().value(), uint64_t{1}); @@ -75,7 +73,7 @@ TEST_CASE("event_envelope.typedPayloadAndCausalMetadata") { } TEST_CASE("event_envelope.uuidV7GeneratorFillsMissingEventId") { - const auto timestamp = std::chrono::system_clock::time_point(std::chrono::milliseconds(1710000000123)); + const auto timestamp = std::chrono::system_clock::time_point(std::chrono::milliseconds(1710000000123)); GameEventLog stream( [](std::span bytes) { for (size_t i = 0; i < bytes.size(); ++i) bytes[i] = static_cast(i + 1); @@ -83,9 +81,9 @@ TEST_CASE("event_envelope.uuidV7GeneratorFillsMissingEventId") { }, [timestamp] { return timestamp; }); - auto envelope = metadata("00000000-0000-0000-0000-000000000000", "combat:damage", 3); + auto envelope = metadata("00000000-0000-0000-0000-000000000000", "combat:damage", 3); envelope.eventId = EventId::nil(); - auto appended = stream.append(std::move(envelope)); + auto appended = stream.append(std::move(envelope)); REQUIRE(appended.ok()); CHECK(!stream.find(EventSequence(1))->eventId.isNil()); CHECK_EQ(stream.find(EventSequence(1))->eventId.format().substr(14, 1), std::string("7")); @@ -93,14 +91,13 @@ TEST_CASE("event_envelope.uuidV7GeneratorFillsMissingEventId") { TEST_CASE("event_envelope.restorePreservesStreamOrderAndNextSequence") { GameEventLog original; - auto first = metadata("018f0f4e-6b3c-7ab1-8def-0123456789ab", "combat:damage", 10); - auto second = metadata("018f0f4e-6b3c-7ab2-8def-0123456789ab", "combat:damage", 11, - "018f0f4e-6b3c-7ab1-8def-0123456789ab", - "018f0f4e-6b3c-7ab0-8def-0123456789ab"); - first.payload = "{\"amount\":1}"; - second.payload = "{\"amount\":2}"; - auto firstResult = original.append(std::move(first)); - auto secondResult = original.append(std::move(second)); + auto first = metadata("018f0f4e-6b3c-7ab1-8def-0123456789ab", "combat:damage", 10); + auto second = metadata("018f0f4e-6b3c-7ab2-8def-0123456789ab", "combat:damage", 11, + "018f0f4e-6b3c-7ab1-8def-0123456789ab", "018f0f4e-6b3c-7ab0-8def-0123456789ab"); + first.payload = "{\"amount\":1}"; + second.payload = "{\"amount\":2}"; + auto firstResult = original.append(std::move(first)); + auto secondResult = original.append(std::move(second)); REQUIRE(firstResult.ok()); REQUIRE(secondResult.ok()); CHECK_EQ(firstResult.value().value(), uint64_t{1}); @@ -108,16 +105,14 @@ TEST_CASE("event_envelope.restorePreservesStreamOrderAndNextSequence") { const std::string snapshot = original.snapshotJson(); GameEventLog restored; - auto restoredResult = restored.restore(snapshot); + auto restoredResult = restored.restore(snapshot); REQUIRE(restoredResult.ok()); - CHECK_EQ(restored.find(EventSequence(1))->eventId, - id("018f0f4e-6b3c-7ab1-8def-0123456789ab")); - CHECK_EQ(restored.find(EventSequence(2))->causation.format(), - std::string("018f0f4e-6b3c-7ab1-8def-0123456789ab")); + CHECK_EQ(restored.find(EventSequence(1))->eventId, id("018f0f4e-6b3c-7ab1-8def-0123456789ab")); + CHECK_EQ(restored.find(EventSequence(2))->causation.format(), std::string("018f0f4e-6b3c-7ab1-8def-0123456789ab")); CHECK_EQ(restored.find(EventSequence(2))->correlation.format(), std::string("018f0f4e-6b3c-7ab0-8def-0123456789ab")); - auto third = metadata("018f0f4e-6b3c-7ab3-8def-0123456789ab", "combat:damage", 12); + auto third = metadata("018f0f4e-6b3c-7ab3-8def-0123456789ab", "combat:damage", 12); auto thirdResult = restored.append(std::move(third)); REQUIRE(thirdResult.ok()); CHECK_EQ(thirdResult.value().value(), uint64_t{3}); @@ -127,8 +122,8 @@ TEST_CASE("event_envelope.restorePreservesStreamOrderAndNextSequence") { TEST_CASE("event_envelope.restoreFailureIsTransactional") { GameEventLog stream; - auto event = metadata("018f0f4e-6b3c-7ab4-8def-0123456789ab", "combat:damage", 20); - auto appended = stream.append(std::move(event)); + auto event = metadata("018f0f4e-6b3c-7ab4-8def-0123456789ab", "combat:damage", 20); + auto appended = stream.append(std::move(event)); REQUIRE(appended.ok()); const std::string before = stream.snapshotJson(); @@ -151,7 +146,7 @@ TEST_CASE("event_envelope.restoreRejectsDuplicateNonNilEventId") { "\"schemaId\":\"test:b\",\"schemaVersion\":\"1\",\"tick\":\"2\",\"flags\":0,\"payload\":null}]}"; GameEventLog stream; - auto result = stream.restore(duplicate); + auto result = stream.restore(duplicate); CHECK(!result.ok()); CHECK_EQ(result.code(), eve::StatusCode::Conflict); CHECK_EQ(stream.size(), 0); diff --git a/test/eventstream.cpp b/test/eventstream.cpp index 556909d4..7b05c9de 100644 --- a/test/eventstream.cpp +++ b/test/eventstream.cpp @@ -1,8 +1,8 @@ #include "zeroerr/assert.h" #include "zeroerr/unittest.h" -#include "common/Module.h" #include "GameEventTestSupport.h" +#include "common/Module.h" #include "game_event/GameEvent.h" #include @@ -13,14 +13,12 @@ using namespace eve::game_event; namespace { -std::uint64_t appendSuccess(GameEventLog& stream, std::uint64_t serial, - std::string type, std::string source, std::string subject, - std::string causation, std::string correlation, std::uint64_t tick, +std::uint64_t appendSuccess(GameEventLog& stream, std::uint64_t serial, std::string type, std::string source, + std::string subject, std::string causation, std::string correlation, std::uint64_t tick, std::uint32_t flags, std::string payload = "null") { - auto result = stream.append(test::envelope(serial, std::move(type), std::move(source), - std::move(subject), std::move(causation), - std::move(correlation), tick, flags, - std::move(payload))); + auto result = + stream.append(test::envelope(serial, std::move(type), std::move(source), std::move(subject), + std::move(causation), std::move(correlation), tick, flags, std::move(payload))); REQUIRE(result.ok()); return result.value().value(); } @@ -29,10 +27,8 @@ std::uint64_t appendSuccess(GameEventLog& stream, std::uint64_t serial, TEST_CASE("game_event.emit.monotonicEnvelope") { GameEventLog stream; - CHECK_EQ(appendSuccess(stream, 1, "created", "system", "entity-7", "", "flow-2", 41, 3, - "{\"hp\":9}"), uint64_t{1}); - CHECK_EQ(appendSuccess(stream, 2, "changed", "rule", "entity-7", "1", "flow-2", 42, 0), - uint64_t{2}); + CHECK_EQ(appendSuccess(stream, 1, "created", "system", "entity-7", "", "flow-2", 41, 3, "{\"hp\":9}"), uint64_t{1}); + CHECK_EQ(appendSuccess(stream, 2, "changed", "rule", "entity-7", "1", "flow-2", 42, 0), uint64_t{2}); REQUIRE(stream.find(2) != nullptr); CHECK(!stream.find(1)->eventId.isNil()); CHECK(!stream.find(2)->eventId.isNil()); @@ -77,9 +73,9 @@ TEST_CASE("game_event.consumer.cursorReadsBatches") { TEST_CASE("game_event.retention.clearAndResetSemantics") { GameEventLog stream; - const auto a = appendSuccess(stream, 1, "a", "", "", "", "", 0, 0); - const auto b = appendSuccess(stream, 2, "b", "", "", "", "", 0, 0); - const auto c = appendSuccess(stream, 3, "c", "", "", "", "", 0, 0); + const auto a = appendSuccess(stream, 1, "a", "", "", "", "", 0, 0); + const auto b = appendSuccess(stream, 2, "b", "", "", "", "", 0, 0); + const auto c = appendSuccess(stream, 3, "c", "", "", "", "", 0, 0); CHECK_EQ(a, uint64_t{1}); CHECK_EQ(b, uint64_t{2}); CHECK_EQ(c, uint64_t{3}); @@ -99,9 +95,9 @@ TEST_CASE("game_event.snapshot.roundTripsDeterministically") { CHECK_EQ(appendSuccess(original, 1, "created", "admin", "base-1", "", "campaign", 17, 5, "{ \"rank\": 3, \"general\": \"g-1\" }"), uint64_t{1}); - CHECK_EQ(appendSuccess(original, 2, "assigned", "admin", "general-1", "1", "campaign", 18, 0, - "[\"unit-2\",\"unit-3\"]"), - uint64_t{2}); + CHECK_EQ( + appendSuccess(original, 2, "assigned", "admin", "general-1", "1", "campaign", 18, 0, "[\"unit-2\",\"unit-3\"]"), + uint64_t{2}); const std::string snapshot = original.snapshotJson(); GameEventLog restored; @@ -114,6 +110,11 @@ TEST_CASE("game_event.snapshot.roundTripsDeterministically") { const std::string before = restored.snapshotJson(); CHECK(!restored.restore("{\"version\":1}").ok()); CHECK_EQ(restored.snapshotJson(), before); + + const std::string unknownField = snapshot.substr(0, snapshot.size() - 1) + ",\"unexpected\":true}"; + auto schemaRejected = restored.restore(unknownField); + CHECK(!schemaRejected.ok()); + CHECK_EQ(restored.snapshotJson(), before); } TEST_CASE("game_event.script.emitQueryCursorAndSnapshot") { diff --git a/test/gameplay_action.cpp b/test/gameplay_action.cpp index a5625f34..965a29c6 100644 --- a/test/gameplay_action.cpp +++ b/test/gameplay_action.cpp @@ -41,7 +41,7 @@ using eve::resource::ReservationId; ActionDefinition definition(const char* name) { ActionDefinition result; - const auto id = eve::LogicalId::fromParts("test", name); + const auto id = eve::LogicalId::fromParts("test", name); REQUIRE(id.has_value()); result.id = *id; return result; @@ -55,13 +55,12 @@ ActionRequest requestFor(const ActionDefinition& definition) { class TestConditionEvaluator final : public IActionConditionEvaluator { public: - bool pass = true; + bool pass = true; mutable int calls = 0; eve::Result evaluate(const ActionDefinition&, const ActionRequest&) const override { ++calls; - if (pass) - return eve::Result::success(ConditionResult::success()); + if (pass) return eve::Result::success(ConditionResult::success()); return eve::Result::success( ConditionResult::failed(eve::decision::ConditionReasonCode::TagMissing)); } @@ -69,15 +68,14 @@ public: class TestTargetResolver final : public IActionTargetResolver { public: - bool fail = true; + bool fail = true; mutable int calls = 0; - eve::Result resolve( - const eve::sensing::TargetingQuery&) const override { + eve::Result resolve(const eve::sensing::TargetingQuery&) const override { ++calls; if (fail) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, "test target selection failed")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, "test target selection failed")); return eve::Result::success(eve::sensing::TargetSet{}); } }; @@ -97,20 +95,19 @@ private: class TestEffectExecutor final : public IActionEffectExecutor { public: - bool succeed = true; - int prepareCalls = 0; - int commitCalls = 0; - int rollbackCalls = 0; + bool succeed = true; + int prepareCalls = 0; + int commitCalls = 0; + int rollbackCalls = 0; - eve::Result> prepare( - const ActionDefinition&, const ActionRequest&, const eve::sensing::TargetSet*, - eve::SimulationTick) override { + eve::Result> prepare(const ActionDefinition&, const ActionRequest&, + const eve::sensing::TargetSet*, + eve::SimulationTick) override { ++prepareCalls; if (!succeed) return eve::Result>::failure( eve::Diagnostic::error(eve::DiagnosticCode::Failed, "test active operation failed")); - std::unique_ptr operation( - new TestEffectOperation(commitCalls, rollbackCalls)); + std::unique_ptr operation(new TestEffectOperation(commitCalls, rollbackCalls)); return eve::Result>::success(std::move(operation)); } }; @@ -119,17 +116,17 @@ class TestResourceAccount final : public IResourceAccount { public: TestResourceAccount() { auto nonce = eve::resource::allocateAccountNonce(); - nonce_ = std::move(nonce).expect("test resource account nonce"); + nonce_ = std::move(nonce).expect("test resource account nonce"); } - bool allowReserve = true; - bool reserveMutated = false; - bool failCommit = false; - bool debited = false; - int reserveCalls = 0; + bool allowReserve = true; + bool reserveMutated = false; + bool failCommit = false; + bool debited = false; + int reserveCalls = 0; mutable int canAffordCalls = 0; - int rollbackCalls = 0; - bool active = false; + int rollbackCalls = 0; + bool active = false; Reservation activeReservation; eve::Result canAfford(const CostSpec&) const override { @@ -140,14 +137,13 @@ public: eve::Result reserve(const CostSpec& cost) override { ++reserveCalls; if (!allowReserve) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::PreconditionViolation, "test account cannot reserve cost")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, "test account cannot reserve cost")); activeReservation = Reservation{nonce_, nextReservation_, cost}; - nextReservation_ = ReservationId(nextReservation_.value() + 1); - active = true; - reserveMutated = true; - return eve::Result::success(activeReservation, - eve::Status::success(eve::StatusCode::Applied)); + nextReservation_ = ReservationId(nextReservation_.value() + 1); + active = true; + reserveMutated = true; + return eve::Result::success(activeReservation, eve::Status::success(eve::StatusCode::Applied)); } eve::Result debit(const CostSpec& cost) override { @@ -164,12 +160,12 @@ public: eve::Result commit(const Reservation& reservation) override { if (!active || reservation.id != activeReservation.id) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, "test reservation is not active")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "test reservation is not active")); if (failCommit) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "test account commit failed")); - active = false; + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "test account commit failed")); + active = false; debited = true; return eve::Result::success( Receipt{nonce_, nextReceipt_, reservation.id, ReceiptOperation::Debit, reservation.cost}, @@ -178,17 +174,17 @@ public: eve::Result rollback(const Reservation& reservation) override { if (!active || reservation.id != activeReservation.id) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Conflict, "test reservation is not active")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "test reservation is not active")); active = false; ++rollbackCalls; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } private: - AccountNonce nonce_; + AccountNonce nonce_; ReservationId nextReservation_{1}; - ReceiptId nextReceipt_{1}; + ReceiptId nextReceipt_{1}; }; class TestResourceProvider final : public IActionResourceProvider { @@ -200,69 +196,66 @@ public: eve::Result canAfford(const ActionDefinition&, const ActionRequest&, const CostSpec& cost) const override { if (!available) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Unsupported, "test resource provider is unavailable")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Unsupported, "test resource provider is unavailable")); return currentAccount_->canAfford(cost); } - eve::Result reserve(const ActionDefinition&, const ActionRequest&, - const CostSpec& cost) override { + eve::Result reserve(const ActionDefinition&, const ActionRequest&, const CostSpec& cost) override { if (!available) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Unsupported, "test resource provider is unavailable")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Unsupported, "test resource provider is unavailable")); auto result = currentAccount_->reserve(cost); if (!result) return eve::Result::failure(result.status()); auto credential = std::move(result).takeValue(); if (!routeNonce_) { - routeNonce_ = credential.account; + routeNonce_ = credential.account; routeAccount_ = currentAccount_; } if (switchAfterReserve_ && alternate_) currentAccount_ = alternate_; - return eve::Result::success(std::move(credential), - eve::Status::success(eve::StatusCode::Applied)); + return eve::Result::success(std::move(credential), eve::Status::success(eve::StatusCode::Applied)); } eve::Result commit(const Reservation& reservation) override { if (!available) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Unsupported, "test resource provider is unavailable")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Unsupported, "test resource provider is unavailable")); TestResourceAccount* account = routeFor(reservation); if (!account) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::NotFound, "test provider lost reservation route")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "test provider lost reservation route")); return account->commit(reservation); } eve::Result rollback(const Reservation& reservation) override { if (!available) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Unsupported, "test resource provider is unavailable")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Unsupported, "test resource provider is unavailable")); TestResourceAccount* account = routeFor(reservation); if (!account) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::NotFound, "test provider lost reservation route")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::NotFound, "test provider lost reservation route")); return account->rollback(reservation); } void switchTo(TestResourceAccount& account) { currentAccount_ = &account; } void switchAfterReserveTo(TestResourceAccount& account) { - alternate_ = &account; + alternate_ = &account; switchAfterReserve_ = true; } void unload() { available = false; } private: TestResourceAccount* routeFor(const Reservation& reservation) const { - if (routeAccount_ && routeNonce_ && reservation.account == *routeNonce_) - return routeAccount_; + if (routeAccount_ && routeNonce_ && reservation.account == *routeNonce_) return routeAccount_; return nullptr; } - TestResourceAccount* currentAccount_ = nullptr; - TestResourceAccount* alternate_ = nullptr; - TestResourceAccount* routeAccount_ = nullptr; + TestResourceAccount* currentAccount_ = nullptr; + TestResourceAccount* alternate_ = nullptr; + TestResourceAccount* routeAccount_ = nullptr; std::optional routeNonce_; - bool switchAfterReserve_ = false; + bool switchAfterReserve_ = false; }; eve::sensing::SubjectRef testSubject(const char* value) { @@ -280,11 +273,11 @@ eve::sensing::WorldPoint testPoint() { TEST_CASE("gameplayAction.phasePipelineUsesInjectedSimulationTime") { ActionDefinition def = definition("timed"); - def.timing.windup = eve::Duration::fromNanoseconds(1); - def.timing.active = eve::Duration::fromNanoseconds(2); - def.timing.recover = eve::Duration::fromNanoseconds(3); + def.timing.windup = eve::Duration::fromNanoseconds(1); + def.timing.active = eve::Duration::fromNanoseconds(2); + def.timing.recover = eve::Duration::fromNanoseconds(3); TestEffectExecutor effects; - ActionRuntime runtime(ActionServices{nullptr, nullptr, nullptr, &effects}); + ActionRuntime runtime(ActionServices{nullptr, nullptr, nullptr, &effects}); auto submitted = runtime.submit(def, requestFor(def)); REQUIRE(submitted.ok()); @@ -309,21 +302,20 @@ TEST_CASE("gameplayAction.phasePipelineUsesInjectedSimulationTime") { auto completed = runtime.advance(id, eve::SimulationTick{4}, eve::Duration::fromNanoseconds(3)); REQUIRE(completed.ok()); CHECK_EQ(static_cast(completed.value().phase), static_cast(ActionPhase::Completed)); - CHECK_EQ(static_cast(runtime.find(id)->status().code()), - static_cast(eve::StatusCode::Applied)); + CHECK_EQ(static_cast(runtime.find(id)->status().code()), static_cast(eve::StatusCode::Applied)); } TEST_CASE("gameplayAction.conditionFailureOwnsFailedTransition") { ActionDefinition def = definition("condition"); - def.condition = Condition::hasTag("silenced"); + def.condition = Condition::hasTag("silenced"); TestConditionEvaluator conditions; conditions.pass = false; ActionRuntime runtime(ActionServices{&conditions, nullptr, nullptr, nullptr}); auto submitted = runtime.submit(def, requestFor(def)); REQUIRE(submitted.ok()); - const ActionExecutionId id = std::move(submitted).takeValue(); - auto advanced = runtime.advance(id, eve::SimulationTick{1}, eve::Duration::zero()); + const ActionExecutionId id = std::move(submitted).takeValue(); + auto advanced = runtime.advance(id, eve::SimulationTick{1}, eve::Duration::zero()); CHECK(!advanced.ok()); CHECK_EQ(conditions.calls, 1); const auto* execution = runtime.find(id); @@ -335,19 +327,19 @@ TEST_CASE("gameplayAction.conditionFailureOwnsFailedTransition") { } TEST_CASE("gameplayAction.targetFailureStopsBeforeActive") { - ActionDefinition def = definition("target"); - def.targetingMode = TargetingMode::Query; - def.targetingSpec = eve::sensing::TargetingSpec{}; - ActionRequest request = requestFor(def); - request.targetingQuery = eve::sensing::TargetingQuery{ - testSubject("00000000-0000-7000-8000-000000000001"), testPoint(), *def.targetingSpec}; + ActionDefinition def = definition("target"); + def.targetingMode = TargetingMode::Query; + def.targetingSpec = eve::sensing::TargetingSpec{}; + ActionRequest request = requestFor(def); + request.targetingQuery = eve::sensing::TargetingQuery{testSubject("00000000-0000-7000-8000-000000000001"), + testPoint(), *def.targetingSpec}; TestTargetResolver targeting; - ActionRuntime runtime(ActionServices{nullptr, &targeting, nullptr, nullptr}); + ActionRuntime runtime(ActionServices{nullptr, &targeting, nullptr, nullptr}); auto submitted = runtime.submit(def, std::move(request)); REQUIRE(submitted.ok()); - const ActionExecutionId id = std::move(submitted).takeValue(); - auto advanced = runtime.advance(id, eve::SimulationTick{1}, eve::Duration::zero()); + const ActionExecutionId id = std::move(submitted).takeValue(); + auto advanced = runtime.advance(id, eve::SimulationTick{1}, eve::Duration::zero()); CHECK(!advanced.ok()); CHECK_EQ(targeting.calls, 1); CHECK_EQ(static_cast(runtime.find(id)->phase()), static_cast(ActionPhase::Failed)); @@ -357,20 +349,20 @@ TEST_CASE("gameplayAction.targetFailureStopsBeforeActive") { } TEST_CASE("gameplayAction.costFailureIsAtomicBeforeActive") { - ActionDefinition def = definition("cost"); - auto cost = CostSpec::single("mana", 5); + ActionDefinition def = definition("cost"); + auto cost = CostSpec::single("mana", 5); REQUIRE(cost.ok()); def.cost = std::move(cost).takeValue(); TestResourceAccount account; account.allowReserve = false; TestResourceProvider resources(account); - TestEffectExecutor effects; - ActionRuntime runtime(ActionServices{nullptr, nullptr, &resources, &effects}); + TestEffectExecutor effects; + ActionRuntime runtime(ActionServices{nullptr, nullptr, &resources, &effects}); auto submitted = runtime.submit(def, requestFor(def)); REQUIRE(submitted.ok()); - const ActionExecutionId id = std::move(submitted).takeValue(); - auto advanced = runtime.advance(id, eve::SimulationTick{1}, eve::Duration::zero()); + const ActionExecutionId id = std::move(submitted).takeValue(); + auto advanced = runtime.advance(id, eve::SimulationTick{1}, eve::Duration::zero()); CHECK(!advanced.ok()); CHECK_EQ(account.canAffordCalls, 1); CHECK_EQ(account.reserveCalls, 0); @@ -382,18 +374,18 @@ TEST_CASE("gameplayAction.costFailureIsAtomicBeforeActive") { TEST_CASE("gameplayAction.cancelDuringWindupHasNoCrossFrameReservation") { ActionDefinition def = definition("cancel"); - def.timing.windup = eve::Duration::fromNanoseconds(10); - auto cost = CostSpec::single("mana", 2); + def.timing.windup = eve::Duration::fromNanoseconds(10); + auto cost = CostSpec::single("mana", 2); REQUIRE(cost.ok()); def.cost = std::move(cost).takeValue(); - TestResourceAccount account; + TestResourceAccount account; TestResourceProvider resources(account); - ActionRuntime runtime(ActionServices{nullptr, nullptr, &resources, nullptr}); + ActionRuntime runtime(ActionServices{nullptr, nullptr, &resources, nullptr}); auto submitted = runtime.submit(def, requestFor(def)); REQUIRE(submitted.ok()); - const ActionExecutionId id = std::move(submitted).takeValue(); - auto started = runtime.advance(id, eve::SimulationTick{1}, eve::Duration::zero()); + const ActionExecutionId id = std::move(submitted).takeValue(); + auto started = runtime.advance(id, eve::SimulationTick{1}, eve::Duration::zero()); REQUIRE(started.ok()); CHECK_EQ(static_cast(runtime.find(id)->phase()), static_cast(ActionPhase::Windup)); CHECK_EQ(account.canAffordCalls, 1); @@ -405,26 +397,25 @@ TEST_CASE("gameplayAction.cancelDuringWindupHasNoCrossFrameReservation") { CHECK_EQ(account.rollbackCalls, 0); CHECK(!account.active); CHECK_EQ(static_cast(runtime.find(id)->phase()), static_cast(ActionPhase::Cancelled)); - CHECK_EQ(static_cast(runtime.find(id)->status().code()), - static_cast(eve::StatusCode::Cancelled)); + CHECK_EQ(static_cast(runtime.find(id)->status().code()), static_cast(eve::StatusCode::Cancelled)); } TEST_CASE("gameplayAction.effectPrepareFailureDoesNotReserveCost") { - ActionDefinition def = definition("prepare-failure"); - auto cost = CostSpec::single("mana", 3); + ActionDefinition def = definition("prepare-failure"); + auto cost = CostSpec::single("mana", 3); REQUIRE(cost.ok()); - def.cost = std::move(cost).takeValue(); + def.cost = std::move(cost).takeValue(); def.activeExecutionRequired = true; - TestResourceAccount account; + TestResourceAccount account; TestResourceProvider resources(account); - TestEffectExecutor effects; + TestEffectExecutor effects; effects.succeed = false; ActionRuntime runtime(ActionServices{nullptr, nullptr, &resources, &effects}); auto submitted = runtime.submit(def, requestFor(def)); REQUIRE(submitted.ok()); - const ActionExecutionId id = std::move(submitted).takeValue(); - auto advanced = runtime.advance(id, eve::SimulationTick{1}, eve::Duration::zero()); + const ActionExecutionId id = std::move(submitted).takeValue(); + auto advanced = runtime.advance(id, eve::SimulationTick{1}, eve::Duration::zero()); CHECK(!advanced.ok()); CHECK_EQ(effects.prepareCalls, 1); CHECK_EQ(effects.commitCalls, 0); @@ -436,21 +427,21 @@ TEST_CASE("gameplayAction.effectPrepareFailureDoesNotReserveCost") { } TEST_CASE("gameplayAction.commitFailureCompensatesResourceAndPreparedEffect") { - ActionDefinition def = definition("commit-failure"); - auto cost = CostSpec::single("mana", 4); + ActionDefinition def = definition("commit-failure"); + auto cost = CostSpec::single("mana", 4); REQUIRE(cost.ok()); - def.cost = std::move(cost).takeValue(); + def.cost = std::move(cost).takeValue(); def.activeExecutionRequired = true; TestResourceAccount account; account.failCommit = true; TestResourceProvider resources(account); - TestEffectExecutor effects; - ActionRuntime runtime(ActionServices{nullptr, nullptr, &resources, &effects}); + TestEffectExecutor effects; + ActionRuntime runtime(ActionServices{nullptr, nullptr, &resources, &effects}); auto submitted = runtime.submit(def, requestFor(def)); REQUIRE(submitted.ok()); - const ActionExecutionId id = std::move(submitted).takeValue(); - auto advanced = runtime.advance(id, eve::SimulationTick{1}, eve::Duration::zero()); + const ActionExecutionId id = std::move(submitted).takeValue(); + auto advanced = runtime.advance(id, eve::SimulationTick{1}, eve::Duration::zero()); CHECK(!advanced.ok()); CHECK_EQ(effects.prepareCalls, 1); CHECK_EQ(effects.commitCalls, 0); @@ -463,20 +454,20 @@ TEST_CASE("gameplayAction.commitFailureCompensatesResourceAndPreparedEffect") { } TEST_CASE("gameplayAction.successCommitsCostBeforeNonfailableEffect") { - ActionDefinition def = definition("transaction-success"); - auto cost = CostSpec::single("mana", 6); + ActionDefinition def = definition("transaction-success"); + auto cost = CostSpec::single("mana", 6); REQUIRE(cost.ok()); - def.cost = std::move(cost).takeValue(); + def.cost = std::move(cost).takeValue(); def.activeExecutionRequired = true; - TestResourceAccount account; + TestResourceAccount account; TestResourceProvider resources(account); - TestEffectExecutor effects; - ActionRuntime runtime(ActionServices{nullptr, nullptr, &resources, &effects}); + TestEffectExecutor effects; + ActionRuntime runtime(ActionServices{nullptr, nullptr, &resources, &effects}); auto submitted = runtime.submit(def, requestFor(def)); REQUIRE(submitted.ok()); - const ActionExecutionId id = std::move(submitted).takeValue(); - auto advanced = runtime.advance(id, eve::SimulationTick{1}, eve::Duration::zero()); + const ActionExecutionId id = std::move(submitted).takeValue(); + auto advanced = runtime.advance(id, eve::SimulationTick{1}, eve::Duration::zero()); REQUIRE(advanced.ok()); CHECK_EQ(account.reserveCalls, 1); CHECK(account.debited); @@ -488,20 +479,20 @@ TEST_CASE("gameplayAction.successCommitsCostBeforeNonfailableEffect") { } TEST_CASE("gameplayAction.providerRoutesCommitByReservationNonceAfterAccountSwitch") { - ActionDefinition def = definition("nonce-route"); - auto cost = CostSpec::single("mana", 1); + ActionDefinition def = definition("nonce-route"); + auto cost = CostSpec::single("mana", 1); REQUIRE(cost.ok()); def.cost = std::move(cost).takeValue(); - TestResourceAccount original; - TestResourceAccount replacement; + TestResourceAccount original; + TestResourceAccount replacement; TestResourceProvider resources(original); resources.switchAfterReserveTo(replacement); ActionRuntime runtime(ActionServices{nullptr, nullptr, &resources, nullptr}); auto submitted = runtime.submit(def, requestFor(def)); REQUIRE(submitted.ok()); - const ActionExecutionId id = std::move(submitted).takeValue(); - auto advanced = runtime.advance(id, eve::SimulationTick{1}, eve::Duration::zero()); + const ActionExecutionId id = std::move(submitted).takeValue(); + auto advanced = runtime.advance(id, eve::SimulationTick{1}, eve::Duration::zero()); REQUIRE(advanced.ok()); CHECK(original.debited); CHECK(!original.active); @@ -511,21 +502,21 @@ TEST_CASE("gameplayAction.providerRoutesCommitByReservationNonceAfterAccountSwit } TEST_CASE("gameplayAction.providerRoutesRollbackByReservationNonceAfterAccountSwitch") { - ActionDefinition def = definition("nonce-rollback"); - auto cost = CostSpec::single("mana", 1); + ActionDefinition def = definition("nonce-rollback"); + auto cost = CostSpec::single("mana", 1); REQUIRE(cost.ok()); def.cost = std::move(cost).takeValue(); TestResourceAccount original; original.failCommit = true; - TestResourceAccount replacement; + TestResourceAccount replacement; TestResourceProvider resources(original); resources.switchAfterReserveTo(replacement); ActionRuntime runtime(ActionServices{nullptr, nullptr, &resources, nullptr}); auto submitted = runtime.submit(def, requestFor(def)); REQUIRE(submitted.ok()); - const ActionExecutionId id = std::move(submitted).takeValue(); - auto advanced = runtime.advance(id, eve::SimulationTick{1}, eve::Duration::zero()); + const ActionExecutionId id = std::move(submitted).takeValue(); + auto advanced = runtime.advance(id, eve::SimulationTick{1}, eve::Duration::zero()); CHECK(!advanced.ok()); CHECK(!original.active); CHECK_EQ(original.rollbackCalls, 1); @@ -534,19 +525,19 @@ TEST_CASE("gameplayAction.providerRoutesRollbackByReservationNonceAfterAccountSw } TEST_CASE("gameplayAction.providerUnavailableBeforeActiveLeavesNoReservation") { - ActionDefinition def = definition("provider-unloaded"); - auto cost = CostSpec::single("mana", 1); + ActionDefinition def = definition("provider-unloaded"); + auto cost = CostSpec::single("mana", 1); REQUIRE(cost.ok()); def.cost = std::move(cost).takeValue(); - TestResourceAccount account; + TestResourceAccount account; TestResourceProvider resources(account); resources.unload(); ActionRuntime runtime(ActionServices{nullptr, nullptr, &resources, nullptr}); auto submitted = runtime.submit(def, requestFor(def)); REQUIRE(submitted.ok()); - const ActionExecutionId id = std::move(submitted).takeValue(); - auto advanced = runtime.advance(id, eve::SimulationTick{1}, eve::Duration::zero()); + const ActionExecutionId id = std::move(submitted).takeValue(); + auto advanced = runtime.advance(id, eve::SimulationTick{1}, eve::Duration::zero()); CHECK(!advanced.ok()); CHECK_EQ(account.canAffordCalls, 0); CHECK_EQ(account.reserveCalls, 0); @@ -556,7 +547,7 @@ TEST_CASE("gameplayAction.providerUnavailableBeforeActiveLeavesNoReservation") { TEST_CASE("gameplayAction.skillAndWeaponAdaptersShareTheSamePipelineShape") { eve::rpg::SkillDefinition skill; - skill.id = "fireball"; + skill.id = "fireball"; skill.castTime = 0.25f; skill.cooldown = 4.f; skill.grantedEffects.push_back("burning"); @@ -565,14 +556,14 @@ TEST_CASE("gameplayAction.skillAndWeaponAdaptersShareTheSamePipelineShape") { skill.cost = std::move(skillCost).takeValue(); eve::weapon::WeaponDefinition weapon; - weapon.id = "rifle"; - weapon.stages.windupTime = 0.25f; - weapon.stages.activeTime = 0.05f; + weapon.id = "rifle"; + weapon.stages.windupTime = 0.25f; + weapon.stages.activeTime = 0.05f; weapon.stages.recoverTime = 0.4f; - weapon.resource.kind = eve::weapon::ResourceKind::Ammo; + weapon.resource.kind = eve::weapon::ResourceKind::Ammo; eve::weapon::AttackRequest attack; - auto skillDefinition = eve::rpg::SkillActionAdapter::makeDefinition(skill); + auto skillDefinition = eve::rpg::SkillActionAdapter::makeDefinition(skill); auto weaponDefinition = eve::weapon::WeaponActionAdapter::makeDefinition(weapon, attack); REQUIRE(skillDefinition.ok()); REQUIRE(weaponDefinition.ok()); @@ -585,17 +576,14 @@ TEST_CASE("gameplayAction.skillAndWeaponAdaptersShareTheSamePipelineShape") { CHECK(skillDefinition.value().cost.has_value()); CHECK(weaponDefinition.value().cost.has_value()); CHECK_EQ(skillDefinition.value().timing.windup, weaponDefinition.value().timing.windup); - CHECK_EQ(static_cast(skillDefinition.value().targetingMode), - static_cast(TargetingMode::None)); - CHECK_EQ(static_cast(weaponDefinition.value().targetingMode), - static_cast(TargetingMode::None)); + CHECK_EQ(static_cast(skillDefinition.value().targetingMode), static_cast(TargetingMode::None)); + CHECK_EQ(static_cast(weaponDefinition.value().targetingMode), static_cast(TargetingMode::None)); - auto skillRequest = eve::rpg::SkillActionAdapter::makeRequest(skill); + auto skillRequest = eve::rpg::SkillActionAdapter::makeRequest(skill); auto weaponRequest = eve::weapon::WeaponActionAdapter::makeRequest(weapon, attack); REQUIRE(skillRequest.ok()); REQUIRE(weaponRequest.ok()); CHECK_EQ(skillRequest.value().actionId, skillDefinition.value().id); CHECK_EQ(weaponRequest.value().actionId, weaponDefinition.value().id); - CHECK(weaponRequest.value().parameters.find("pelletCount") != - weaponRequest.value().parameters.end()); + CHECK(weaponRequest.value().parameters.find("pelletCount") != weaponRequest.value().parameters.end()); } diff --git a/test/hex_level_simulation.cpp b/test/hex_level_simulation.cpp index e707fe7e..cadea447 100644 --- a/test/hex_level_simulation.cpp +++ b/test/hex_level_simulation.cpp @@ -40,8 +40,8 @@ #include "zeroerr/assert.h" #include "zeroerr/unittest.h" -#include "RenderImageAudit.h" #include "ProcgenGridTestSupport.h" +#include "RenderImageAudit.h" #include "filesystem/Filesystem.h" #include "graphics/AmbientOcclusion.h" #include "graphics/AntiAliasing.h" diff --git a/test/housegen.cpp b/test/housegen.cpp index f1b5fd01..f8bb6ea1 100644 --- a/test/housegen.cpp +++ b/test/housegen.cpp @@ -20,7 +20,7 @@ static const char *kKit = R"({"components":[ TEST_CASE("housegen.library.validatesManifest") { HouseComponentLibrary lib; - auto loaded = lib.loadFromJson(kKit); + auto loaded = lib.loadFromJson(kKit); REQUIRE(loaded.ok()); CHECK_EQ(lib.count(), 6); auto failed = lib.loadFromJson(R"([{"id":"broken","category":"wall"}])"); @@ -30,7 +30,7 @@ TEST_CASE("housegen.library.validatesManifest") { TEST_CASE("housegen.library.validatesMaterialOverrides") { HouseComponentLibrary lib; - auto loaded = lib.loadFromJson(R"([{ + auto loaded = lib.loadFromJson(R"([{ "id":"wall.material","model":"wall.glb","category":"wall", "material":{"baseColor":[0.82,0.71,0.55,1.0],"baseColorTexture":"wall.png", "normalTexture":"wall-normal.png","heightTexture":"wall-height.png", @@ -58,20 +58,23 @@ TEST_CASE("housegen.library.validatesMaterialOverrides") { TEST_CASE("housegen.reproducibleAndSerializable") { HouseComponentLibrary lib; - auto loaded = lib.loadFromJson(kKit); + auto loaded = lib.loadFromJson(kKit); REQUIRE(loaded.ok()); HouseGenerator generator(lib); - HouseRequest r; - r.seed = 42; r.width = 5; r.depth = 4; r.floors = 2; + HouseRequest r; + r.seed = 42; + r.width = 5; + r.depth = 4; + r.floors = 2; HouseLayout a, b; - auto first = generator.generate(r, a); - auto second = generator.generate(r, b); + auto first = generator.generate(r, a); + auto second = generator.generate(r, b); REQUIRE(first.ok()); REQUIRE(second.ok()); CHECK_EQ(a.toJson(), b.toJson()); CHECK(a.instances.size() > 20); HouseLayout restored; - auto restoredResult = restored.fromJson(a.toJson()); + auto restoredResult = restored.fromJson(a.toJson()); REQUIRE(restoredResult.ok()); CHECK_EQ(restored.toJson(), a.toJson()); auto validation = restored.validate(lib); @@ -80,25 +83,24 @@ TEST_CASE("housegen.reproducibleAndSerializable") { TEST_CASE("housegen.requiresStructuralCategories") { HouseComponentLibrary lib; - auto loaded = lib.loadFromJson( - R"([{"id":"wall","model":"wall.glb","category":"wall"}])"); + auto loaded = lib.loadFromJson(R"([{"id":"wall","model":"wall.glb","category":"wall"}])"); REQUIRE(loaded.ok()); HouseGenerator generator(lib); - HouseLayout out; - auto failed = generator.generate(HouseRequest{}, out); + HouseLayout out; + auto failed = generator.generate(HouseRequest{}, out); REQUIRE(!failed.ok()); CHECK(failed.status().describe().find("foundation") != std::string::npos); } TEST_CASE("housegen.upperFloorsNeverExpandBeyondSupport") { HouseComponentLibrary lib; - auto loaded = lib.loadFromJson(kKit); + auto loaded = lib.loadFromJson(kKit); REQUIRE(loaded.ok()); HouseGenerator generator(lib); - HouseRequest r; + HouseRequest r; r.seed = 8675309; r.width = 6; r.depth = 5; r.floors = 3; HouseLayout layout; - auto generated = generator.generate(r, layout); + auto generated = generator.generate(r, layout); REQUIRE(generated.ok()); for (int z = 1; z < r.floors; ++z) { int lowerMinX = r.width, lowerMaxX = -1, lowerMinY = r.depth, lowerMaxY = -1; @@ -121,7 +123,7 @@ TEST_CASE("housegen.upperFloorsNeverExpandBeyondSupport") { TEST_CASE("housegen.shapeRoofAndEntranceVariants") { HouseComponentLibrary lib; - auto loaded = lib.loadFromJson(kKit); + auto loaded = lib.loadFromJson(kKit); REQUIRE(loaded.ok()); HouseGenerator generator(lib); const char *shapes[] = {"rectangle", "l_shape", "t_shape"}; @@ -131,10 +133,13 @@ TEST_CASE("housegen.shapeRoofAndEntranceVariants") { int floorCounts[3] = {}; for (int variant = 0; variant < 3; ++variant) { HouseRequest r; - r.seed = 100 + variant; r.width = 7; r.depth = 6; r.floors = 2; + r.seed = 100 + variant; + r.width = 7; + r.depth = 6; + r.floors = 2; r.footprint = shapes[variant]; r.roof = roofs[variant]; r.entrance = entrances[variant]; HouseLayout layout; - auto generated = generator.generate(r, layout); + auto generated = generator.generate(r, layout); REQUIRE(generated.ok()); CHECK_EQ(layout.footprintStyle, std::string(shapes[variant])); CHECK_EQ(layout.roofStyle, std::string(roofs[variant])); @@ -157,14 +162,14 @@ TEST_CASE("housegen.shapeRoofAndEntranceVariants") { TEST_CASE("housegen.everyFloorCellRequiresCoverage") { HouseComponentLibrary lib; - auto loaded = lib.loadFromJson(kKit); + auto loaded = lib.loadFromJson(kKit); REQUIRE(loaded.ok()); HouseGenerator generator(lib); - HouseRequest r; + HouseRequest r; r.seed = 8675309; r.width = 7; r.depth = 6; r.floors = 3; r.footprint = "l_shape"; HouseLayout layout; - auto generated = generator.generate(r, layout); + auto generated = generator.generate(r, layout); REQUIRE(generated.ok()); auto valid = layout.validate(lib); REQUIRE(valid.ok()); @@ -190,14 +195,17 @@ TEST_CASE("housegen.facadeKeepsCornersSolidAndWindowsAligned") { {"id":"roof","model":"roof.glb","category":"roof"} ]})"; HouseComponentLibrary lib; - auto loaded = lib.loadFromJson(facadeKit); + auto loaded = lib.loadFromJson(facadeKit); REQUIRE(loaded.ok()); HouseRequest r; - r.seed = 44; r.width = 4; r.depth = 4; r.floors = 2; + r.seed = 44; + r.width = 4; + r.depth = 4; + r.floors = 2; r.footprint = "rectangle"; r.roof = "flat"; r.entrance = "north"; - HouseLayout layout; + HouseLayout layout; HouseGenerator generator(lib); - auto generated = generator.generate(r, layout); + auto generated = generator.generate(r, layout); REQUIRE(generated.ok()); std::vector> groundWindows, upperWindows; for (const auto &instance : layout.instances) { diff --git a/test/housegen_render.cpp b/test/housegen_render.cpp index f2ba1f30..31ed9cc6 100644 --- a/test/housegen_render.cpp +++ b/test/housegen_render.cpp @@ -275,7 +275,7 @@ TEST_CASE("housegen.renderPreview") { ]})"; HouseComponentLibrary library; - auto kitResult = library.loadFromJson(kit); + auto kitResult = library.loadFromJson(kit); REQUIRE(kitResult.ok()); HouseRequest request; request.seed = static_cast(envInt("EVHOUSE_PREVIEW_SEED", 20260815)); @@ -288,7 +288,7 @@ TEST_CASE("housegen.renderPreview") { request.floorHeight = 2.4f; HouseLayout layout; HouseGenerator generator(library); - auto generated = generator.generate(request, layout); + auto generated = generator.generate(request, layout); REQUIRE(generated.ok()); auto *window = eve::window::Window::create(); @@ -343,8 +343,7 @@ TEST_CASE("housegen.renderPreview") { int roofLevel = 0; for (const auto &instance : layout.instances) { const auto component = library.find(instance.componentId); - if (component && component->get().category == "roof") - roofLevel = std::max(roofLevel, instance.z); + if (component && component->get().category == "roof") roofLevel = std::max(roofLevel, instance.z); } int roofMinX = request.width, roofMaxX = 0, roofMinZ = request.depth, roofMaxZ = 0; for (const auto &instance : layout.instances) { @@ -489,7 +488,7 @@ TEST_CASE("housegen.materialPreview") { const std::filesystem::path assetDir = eve_test_path::pathBesideTestDir(__FILE__, "assets/housegen/kenney-modular-buildings"); HouseComponentLibrary library; - auto loaded = library.loadFromFile((assetDir / "components.json").string()); + auto loaded = library.loadFromFile((assetDir / "components.json").string()); REQUIRE(loaded.ok()); REQUIRE_EQ(library.count(), 4); for (const auto &id : library.ids()) { diff --git a/test/identity_result_audit.cpp b/test/identity_result_audit.cpp index 2cba2edb..495c563d 100644 --- a/test/identity_result_audit.cpp +++ b/test/identity_result_audit.cpp @@ -8,8 +8,8 @@ #include #include -#include #include +#include #include #include #include @@ -38,8 +38,8 @@ static_assert(!std::is_convertible_v); static_assert(!std::is_convertible_v); TEST_CASE("identity.audit.domainIdsShareOneUuidImplementation") { - constexpr const char* text = "01020304-0506-0708-090a-0b0c0d0e0f10"; - const auto asset = parsedId(text); + constexpr const char* text = "01020304-0506-0708-090a-0b0c0d0e0f10"; + const auto asset = parsedId(text); REQUIRE(!asset.isNil()); CHECK_EQ(asset.format(), text); CHECK_EQ(eve::DocumentId::fromUuid(asset).format(), text); @@ -64,13 +64,11 @@ TEST_CASE("identity.audit.logicalNamespaceIsCanonicalAndNamesRemainCaseSensitive TEST_CASE("identity.audit.canonicalTransactionRestoresWithoutStringAllocators") { using namespace std::chrono_literals; - const auto clock = [] { - return std::chrono::system_clock::time_point{std::chrono::milliseconds{0x010203040506}}; - }; + const auto clock = [] { return std::chrono::system_clock::time_point{std::chrono::milliseconds{0x010203040506}}; }; const auto entropy = deterministicEntropy(); eve::transaction::Ledger source(eve::PersistentId::nil(), entropy, clock); - auto created = source.create("combat", "command"); + auto created = source.create("combat", "command"); REQUIRE(created.ok()); auto* plan = std::move(created).takeValue(); REQUIRE(plan != nullptr); @@ -94,15 +92,15 @@ TEST_CASE("identity.audit.canonicalTransactionRestoresWithoutStringAllocators") CHECK(snapshot.find("operation-0000000000000001") == std::string::npos); eve::transaction::Ledger restored(eve::PersistentId::nil(), deterministicEntropy(), clock); - auto restoredResult = restored.restore(snapshot); + auto restoredResult = restored.restore(snapshot); REQUIRE(restoredResult.ok()); auto* restoredPlan = restored.find(transactionId); REQUIRE(restoredPlan != nullptr); CHECK_EQ(restoredPlan->identity(), transactionId); CHECK(restoredPlan->findOperation(operationId) != nullptr); - const auto before = restored.snapshotJson(); - auto failedCanonical = restored.restore("{\"version\":1,\"nextTransaction\":\"2\",\"plans\":["); + const auto before = restored.snapshotJson(); + auto failedCanonical = restored.restore("{\"version\":1,\"nextTransaction\":\"2\",\"plans\":["); CHECK(!failedCanonical.ok()); CHECK_EQ(restored.snapshotJson(), before); CHECK_EQ(restored.snapshotJson(), before); @@ -113,7 +111,7 @@ TEST_CASE("identity.audit.runtimeHandleGenerationMakesStaleReuseObservable") { using Handle = eve::RuntimeHandle; constexpr Handle oldHandle(4u, 1u); - const auto next = oldHandle.nextGeneration(); + const auto next = oldHandle.nextGeneration(); REQUIRE(next.has_value()); CHECK(next.value() != oldHandle); CHECK_EQ(next->index(), oldHandle.index()); @@ -124,15 +122,13 @@ TEST_CASE("identity.audit.runtimeHandleGenerationMakesStaleReuseObservable") { TEST_CASE("identity.audit.canonicalEffectUsesUuidAndLegacyFacadeProjectsLocally") { using namespace std::chrono_literals; - const auto clock = [] { - return std::chrono::system_clock::time_point{std::chrono::milliseconds{0x010203040506}}; - }; + const auto clock = [] { return std::chrono::system_clock::time_point{std::chrono::milliseconds{0x010203040506}}; }; eve::effects::EffectDefinition definition; - definition.id = "combat:burn"; + definition.id = "combat:burn"; definition.policy.stackMode = eve::effects::StackMode::NewInstance; eve::effects::EffectContainer canonical(deterministicEntropy(), clock); - auto applied = canonical.applyCanonical(definition, "actor:1", "spell:1"); + auto applied = canonical.applyCanonical(definition, "actor:1", "spell:1"); REQUIRE(applied.ok()); const auto effectId = std::move(applied).takeValue(); CHECK(!effectId.isNil()); @@ -144,7 +140,7 @@ TEST_CASE("identity.audit.canonicalEffectUsesUuidAndLegacyFacadeProjectsLocally" CHECK(canonical.remove(effectId).ok()); eve::effects::EffectContainer legacy; - auto legacyResult = legacy.apply(definition, "actor:2", "spell:1"); + auto legacyResult = legacy.apply(definition, "actor:2", "spell:1"); REQUIRE(legacyResult.ok()); CHECK(legacyResult.value().starts_with("effect-")); REQUIRE(legacy.find(legacyResult.value()) != nullptr); diff --git a/test/network.cpp b/test/network.cpp index 35b0a513..4b1faaaf 100644 --- a/test/network.cpp +++ b/test/network.cpp @@ -1,19 +1,19 @@ #include "zeroerr/assert.h" #include "zeroerr/unittest.h" -#include "platform_event/PlatformEvent.h" -#include "network/Network.h" -#include "network/TcpSocket.h" -#include "network/UdpSocket.h" -#include "network/HttpRequest.h" -#include "network/Channel.h" -#include "network/Session.h" -#include "network/NetStream.h" -#include "network/UdpLink.h" -#include "network/NetRpc.h" -#include "network/NetHost.h" -#include "network/NetWorker.h" #include "data/ByteData.h" +#include "network/Channel.h" +#include "network/HttpRequest.h" +#include "network/NetHost.h" +#include "network/NetRpc.h" +#include "network/NetStream.h" +#include "network/NetWorker.h" +#include "network/Network.h" +#include "network/Session.h" +#include "network/TcpSocket.h" +#include "network/UdpLink.h" +#include "network/UdpSocket.h" +#include "platform_event/PlatformEvent.h" #include #include @@ -21,12 +21,12 @@ #include #include -using eve::platform_event::PlatformEvent; using eve::platform_event::Message; +using eve::platform_event::PlatformEvent; using eve::platform_event::Variant; TEST_CASE("event.VariantMessage") { - auto* ev = eve::platform_event::PlatformEvent::create(); + auto* ev = eve::platform_event::PlatformEvent::create(); std::vector args; args.push_back(Variant::makeInt(200)); args.push_back(Variant::makeString("ok")); @@ -69,7 +69,7 @@ TEST_CASE("network.NetWorker.stopFromJobDoesNotDeadlock") { TEST_CASE("network.Channel.fragmentedAndCoalescedFrames") { auto* net = eve::network::Network::create(); - auto* ev = eve::platform_event::PlatformEvent::create(); + auto* ev = eve::platform_event::PlatformEvent::create(); eve::network::TcpSocket socket(net); eve::network::Channel channel(&socket); @@ -96,7 +96,7 @@ TEST_CASE("network.Channel.fragmentedAndCoalescedFrames") { TEST_CASE("network.Channel.rejectsOversizedFrameHeader") { auto* net = eve::network::Network::create(); - auto* ev = eve::platform_event::PlatformEvent::create(); + auto* ev = eve::platform_event::PlatformEvent::create(); while (ev->pollOwned()) {} eve::network::TcpSocket socket(net); eve::network::Channel channel(&socket); @@ -197,7 +197,7 @@ TEST_CASE("network.UdpSendTo") { TEST_CASE("network.HttpGetLocal") { auto* net = eve::network::Network::create(); - auto* ev = eve::platform_event::PlatformEvent::create(); + auto* ev = eve::platform_event::PlatformEvent::create(); const uint16_t port = 39311; auto* server = net->newTcp(); REQUIRE(server->listen(port)); @@ -245,7 +245,7 @@ TEST_CASE("network.HttpGetLocal") { TEST_CASE("network.ChannelFrames") { auto* net = eve::network::Network::create(); - auto* ev = eve::platform_event::PlatformEvent::create(); + auto* ev = eve::platform_event::PlatformEvent::create(); const uint16_t port = 39321; auto* server = net->newTcp(); REQUIRE(server->listen(port)); @@ -327,7 +327,7 @@ TEST_CASE("network.Network.cppFactoriesReturnOwners") { TEST_CASE("network.Tcp.connectedAndLocalPort") { auto* net = eve::network::Network::create(); - auto* ev = eve::platform_event::PlatformEvent::create(); + auto* ev = eve::platform_event::PlatformEvent::create(); const uint16_t port = 39291; auto* server = net->newTcp(); REQUIRE(server->listen(port)); @@ -366,7 +366,7 @@ TEST_CASE("network.Tcp.connectedAndLocalPort") { TEST_CASE("network.Session.addGetRemoveCloseAll") { auto* net = eve::network::Network::create(); - auto* ev = eve::platform_event::PlatformEvent::create(); + auto* ev = eve::platform_event::PlatformEvent::create(); const uint16_t port = 39331; auto* server = net->newTcp(); REQUIRE(server->listen(port)); @@ -409,7 +409,7 @@ TEST_CASE("network.Session.addGetRemoveCloseAll") { TEST_CASE("network.Http.setHeaderAndBody") { auto* net = eve::network::Network::create(); - auto* ev = eve::platform_event::PlatformEvent::create(); + auto* ev = eve::platform_event::PlatformEvent::create(); const uint16_t port = 39341; auto* server = net->newTcp(); REQUIRE(server->listen(port)); @@ -565,7 +565,7 @@ TEST_CASE("network.NetReaderOverrunIsSticky") { TEST_CASE("network.TcpLargeSendFlushesFully") { auto* net = eve::network::Network::create(); - auto* ev = eve::platform_event::PlatformEvent::create(); + auto* ev = eve::platform_event::PlatformEvent::create(); const uint16_t port = 39351; auto* server = net->newTcp(); REQUIRE(server->listen(port)); @@ -644,7 +644,7 @@ TEST_CASE("network.TcpLargeSendFlushesFully") { TEST_CASE("network.ChannelLargeMessageReassembles") { auto* net = eve::network::Network::create(); - auto* ev = eve::platform_event::PlatformEvent::create(); + auto* ev = eve::platform_event::PlatformEvent::create(); const uint16_t port = 39361; auto* server = net->newTcp(); REQUIRE(server->listen(port)); diff --git a/test/orders.cpp b/test/orders.cpp index 4be63646..75f0adeb 100644 --- a/test/orders.cpp +++ b/test/orders.cpp @@ -19,9 +19,9 @@ std::string takeId(eve::Result result) { TEST_CASE("orders.append.priorityAndStableIds") { CommandQueue queue; - const auto first = takeId(queue.append("first", 1)); - const auto low = takeId(queue.append("low", 1)); - const auto high = takeId(queue.append("high", 10)); + const auto first = takeId(queue.append("first", 1)); + const auto low = takeId(queue.append("low", 1)); + const auto high = takeId(queue.append("high", 10)); CHECK_EQ(first, std::string("order-0000000000000001")); CHECK_EQ(low, std::string("order-0000000000000002")); @@ -36,9 +36,9 @@ TEST_CASE("orders.append.priorityAndStableIds") { TEST_CASE("orders.replace.cancelsUnfinishedDeterministically") { CommandQueue queue; - const auto active = takeId(queue.append("work")); - const auto queued = takeId(queue.append("later")); - const auto next = takeId(queue.replace("replacement", 5)); + const auto active = takeId(queue.append("work")); + const auto queued = takeId(queue.append("later")); + const auto next = takeId(queue.replace("replacement", 5)); CHECK_EQ(static_cast(queue.find(active)->get().state), static_cast(OrderState::Cancelled)); CHECK_EQ(static_cast(queue.find(queued)->get().state), static_cast(OrderState::Cancelled)); @@ -55,7 +55,7 @@ TEST_CASE("orders.replace.cancelsUnfinishedDeterministically") { TEST_CASE("orders.interrupt.honoursPriority") { CommandQueue queue; - const auto active = takeId(queue.append("critical", 20)); + const auto active = takeId(queue.append("critical", 20)); CHECK(!queue.interrupt("weak", 19).hasValue()); CHECK_EQ(queue.current()->get().id, active); @@ -68,9 +68,9 @@ TEST_CASE("orders.interrupt.honoursPriority") { TEST_CASE("orders.lifecycle.completeFailCancel") { CommandQueue queue; - const auto one = takeId(queue.append("one")); - const auto two = takeId(queue.append("two")); - const auto three = takeId(queue.append("three")); + const auto one = takeId(queue.append("one")); + const auto two = takeId(queue.append("two")); + const auto three = takeId(queue.append("three")); CHECK(queue.complete(one).ok()); CHECK_EQ(static_cast(queue.find(one)->get().state), static_cast(OrderState::Completed)); @@ -85,8 +85,8 @@ TEST_CASE("orders.lifecycle.completeFailCancel") { TEST_CASE("orders.timeout.failsAndActivatesNext") { CommandQueue queue; - const auto timed = takeId(queue.append("timed", 0, 0.5)); - const auto next = takeId(queue.append("next")); + const auto timed = takeId(queue.append("timed", 0, 0.5)); + const auto next = takeId(queue.append("next")); CHECK(queue.update(0.25).ok()); CHECK_EQ(queue.current()->get().id, timed); CHECK(queue.update(0.25).ok()); @@ -97,8 +97,8 @@ TEST_CASE("orders.timeout.failsAndActivatesNext") { TEST_CASE("orders.payload.isDeterministicJsonCompatible") { CommandQueue queue; - const auto id = takeId(queue.append("custom")); - auto* payload = &queue.find(id)->get().payload; + const auto id = takeId(queue.append("custom")); + auto* payload = &queue.find(id)->get().payload; REQUIRE(payload != nullptr); payload->setString("name", "A\nB"); payload->setNumber("x", 12.5); @@ -111,15 +111,15 @@ TEST_CASE("orders.payload.isDeterministicJsonCompatible") { TEST_CASE("orders.snapshot.roundTripAndMalformedRestorePreservesState") { CommandQueue source; - const auto first = takeId(source.append("first", 1, 3.0)); - const auto second = takeId(source.append("second", 5, 4.0)); - auto firstRef = source.find(first); + const auto first = takeId(source.append("first", 1, 3.0)); + const auto second = takeId(source.append("second", 5, 4.0)); + auto firstRef = source.find(first); REQUIRE(firstRef); firstRef->get().payload.setString("name", "alpha"); firstRef->get().payload.setNumber("amount", 12.5); REQUIRE(source.complete(first).ok()); - const auto third = takeId(source.append("third", 2, 0.5)); - auto thirdRef = source.find(third); + const auto third = takeId(source.append("third", 2, 0.5)); + auto thirdRef = source.find(third); REQUIRE(thirdRef); thirdRef->get().payload.setBool("urgent", true); @@ -149,8 +149,8 @@ TEST_CASE("orders.snapshot.roundTripAndMalformedRestorePreservesState") { auto beforeResult = restored.snapshot(); REQUIRE(beforeResult.ok()); - const auto before = std::move(beforeResult).takeValue(); - auto malformed = restored.restore("{\"schema\":\"orders.command_queue\""); + const auto before = std::move(beforeResult).takeValue(); + auto malformed = restored.restore("{\"schema\":\"orders.command_queue\""); CHECK(!malformed.ok()); auto afterMalformed = restored.snapshot(); REQUIRE(afterMalformed.ok()); diff --git a/test/physics_artifact_provider.cpp b/test/physics_artifact_provider.cpp index 6d2d1869..b94ee7f2 100644 --- a/test/physics_artifact_provider.cpp +++ b/test/physics_artifact_provider.cpp @@ -22,27 +22,26 @@ eve::PersistentId id(const char* text) { } struct ColliderPublication { - std::array vertices{-1.f, 0.f, -1.f, 1.f, 0.f, -1.f, - 1.f, 0.f, 1.f, -1.f, 0.f, 1.f}; - std::array indices{0, 2, 1, 0, 3, 2}; - eve::artifact::PartView part; + std::array vertices{-1.f, 0.f, -1.f, 1.f, 0.f, -1.f, 1.f, 0.f, 1.f, -1.f, 0.f, 1.f}; + std::array indices{0, 2, 1, 0, 3, 2}; + eve::artifact::PartView part; eve::artifact::PublicationView publication; explicit ColliderPublication(eve::PersistentId artifactId) { - part.id = artifactId; - part.role = "collider"; - part.kind = eve::artifact::PartKind::Collider; + part.id = artifactId; + part.role = "collider"; + part.kind = eve::artifact::PartKind::Collider; part.schemaVersion = 1; - part.buildKey = "test.box3d.collider.v1"; - part.bounds = {-1.f, 0.f, -1.f, 1.f, 0.f, 1.f, true}; - part.positions = std::span(vertices); - part.indices = std::span(indices); + part.buildKey = "test.box3d.collider.v1"; + part.bounds = {-1.f, 0.f, -1.f, 1.f, 0.f, 1.f, true}; + part.positions = std::span(vertices); + part.indices = std::span(indices); - publication.id = artifactId; + publication.id = artifactId; publication.schemaVersion = 1; - publication.buildKey = part.buildKey; - publication.bounds = part.bounds; - publication.parts = std::span(&part, 1); + publication.buildKey = part.buildKey; + publication.bounds = part.bounds; + publication.parts = std::span(&part, 1); } }; @@ -50,8 +49,8 @@ struct ColliderPublication { TEST_CASE("physics.artifact.stageUsesRealBox3DAndRayQuery") { eve::physics::PhysicsArtifactProvider provider; - const auto artifactId = id("018f0b7e-6e50-7a10-8c22-2c8f8e3e0201"); - ColliderPublication input(artifactId); + const auto artifactId = id("018f0b7e-6e50-7a10-8c22-2c8f8e3e0201"); + ColliderPublication input(artifactId); auto prepared = provider.prepare(input.publication); REQUIRE(prepared.ok()); @@ -77,8 +76,8 @@ TEST_CASE("physics.artifact.stageUsesRealBox3DAndRayQuery") { TEST_CASE("physics.artifact.rollbackAndValidationLeaveNoCollider") { eve::physics::PhysicsArtifactProvider provider; - const auto artifactId = id("018f0b7e-6e50-7a10-8c22-2c8f8e3e0202"); - ColliderPublication input(artifactId); + const auto artifactId = id("018f0b7e-6e50-7a10-8c22-2c8f8e3e0202"); + ColliderPublication input(artifactId); auto prepared = provider.prepare(input.publication); REQUIRE(prepared.ok()); @@ -101,8 +100,8 @@ TEST_CASE("physics.artifact.rollbackAndValidationLeaveNoCollider") { TEST_CASE("physics.artifact.restoreRebuildsBox3DAndInvalidatesOldHandle") { eve::physics::PhysicsArtifactProvider provider; - const auto artifactId = id("018f0b7e-6e50-7a10-8c22-2c8f8e3e0203"); - ColliderPublication input(artifactId); + const auto artifactId = id("018f0b7e-6e50-7a10-8c22-2c8f8e3e0203"); + ColliderPublication input(artifactId); auto prepared = provider.prepare(input.publication); REQUIRE(prepared.ok()); @@ -110,12 +109,12 @@ TEST_CASE("physics.artifact.restoreRebuildsBox3DAndInvalidatesOldHandle") { stage->commit(); stage.reset(); const auto oldHandle = provider.find(artifactId)->handle; - auto snapshot = provider.snapshotState(); + auto snapshot = provider.snapshotState(); REQUIRE(snapshot.ok()); auto encoded = std::move(snapshot).takeValue(); // A failed candidate build must leave the live Box3D world untouched. - auto malformed = encoded; + auto malformed = encoded; auto* malformedObject = malformed.getIf(); REQUIRE(malformedObject != nullptr); auto* encodedColliders = malformedObject->at("colliders").getIf(); @@ -125,7 +124,7 @@ TEST_CASE("physics.artifact.restoreRebuildsBox3DAndInvalidatesOldHandle") { auto* malformedIndices = malformedCollider->at("indices").getIf(); REQUIRE(malformedIndices != nullptr); malformedIndices->front() = eve::Value(std::int64_t(99)); - auto rejected = provider.restoreState(malformed); + auto rejected = provider.restoreState(malformed); CHECK(!rejected.ok()); CHECK_EQ(provider.size(), std::size_t(1)); CHECK(provider.isHandleLive(oldHandle)); @@ -156,17 +155,16 @@ TEST_CASE("physics.artifact.compositeColliderUsesPublicProcgenPublication") { params.setInt("width", 8); params.setInt("height", 7); params.setFloat("radius", 1.f); - const auto artifactId = eve::procgen::ArtifactId::parse( - "018f0b7e-6e50-7a10-8c22-2c8f8e3e0204"); + const auto artifactId = eve::procgen::ArtifactId::parse("018f0b7e-6e50-7a10-8c22-2c8f8e3e0204"); REQUIRE(artifactId.has_value()); auto generated = eve::procgen::generateHexTerrainArtifact(params, *artifactId); REQUIRE(generated.ok()); - eve::procgen::ArtifactStore store; - eve::procgen::ArtifactPublisher publisher(store); + eve::procgen::ArtifactStore store; + eve::procgen::ArtifactPublisher publisher(store); eve::procgen::ArtifactPublishOptions options; options.physics = true; - auto published = publisher.publish(std::move(generated).takeValue(), options); + auto published = publisher.publish(std::move(generated).takeValue(), options); REQUIRE(published.ok()); const auto persistentId = eve::PersistentId::fromUuid(*artifactId); diff --git a/test/physics_core_boundary.cpp b/test/physics_core_boundary.cpp index c1e2b296..4e01846a 100644 --- a/test/physics_core_boundary.cpp +++ b/test/physics_core_boundary.cpp @@ -11,8 +11,8 @@ #include -#include #include +#include #include using namespace eve::physics; @@ -31,30 +31,27 @@ public: return domain == SimulationBackendDomain::World2D; } - [[nodiscard]] eve::Result> create( - SimulationBackendDomain domain, void* /*state*/) override { + [[nodiscard]] eve::Result> create(SimulationBackendDomain domain, + void * /*state*/) override { if (!supports(domain)) { - return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Unsupported, - "Mock accelerator does not implement this domain")); + return eve::Result>::failure(eve::Diagnostic::error( + eve::DiagnosticCode::Unsupported, "Mock accelerator does not implement this domain")); } - return eve::Result>::success( - detail::makeMockAcceleratorBackend()); + return eve::Result>::success(detail::makeMockAcceleratorBackend()); } }; -void checkObservableContract(ISimulationBackend& backend) { - const eve::SimulationStep step{eve::SimulationTick{1}, - eve::Duration::fromNanoseconds(16666667)}; - auto applied = backend.step(step, SimulationSettings{}); - const bool appliedOk = applied.ok(); +void checkObservableContract(ISimulationBackend &backend) { + const eve::SimulationStep step{eve::SimulationTick{1}, eve::Duration::fromNanoseconds(16666667)}; + auto applied = backend.step(step, SimulationSettings{}); + const bool appliedOk = applied.ok(); CHECK(appliedOk); const SimulationObservation observation = backend.observation(); CHECK_EQ(observation.stepCount, std::uint64_t(1)); CHECK(observation.lastTick == step.tick); CHECK_EQ(observation.simulatedDuration, step.delta); - auto duplicate = backend.step(step, SimulationSettings{}); + auto duplicate = backend.step(step, SimulationSettings{}); const bool duplicateOk = duplicate.ok(); CHECK(!duplicateOk); CHECK_EQ(duplicate.code(), eve::StatusCode::Rejected); @@ -63,7 +60,7 @@ void checkObservableContract(ISimulationBackend& backend) { } // namespace TEST_CASE("physics.core.backendContract.isHeadlessAndObservable") { - auto *physics = Physics::create(); + auto *physics = Physics::create(); std::unique_ptr world(physics->newWorld(0.f, 900.f)); REQUIRE(world.get() != nullptr); @@ -77,9 +74,8 @@ TEST_CASE("physics.core.backendContract.isHeadlessAndObservable") { CHECK_EQ(backend->determinism(), SimulationDeterminism::ToleranceBounded); CHECK_EQ(backend->observation().stepCount, std::uint64_t(0)); - auto stepped = backend->step( - SimulationStep{eve::SimulationTick{1}, eve::Duration::fromNanoseconds(16666667)}, - SimulationSettings{}); + auto stepped = backend->step(SimulationStep{eve::SimulationTick{1}, eve::Duration::fromNanoseconds(16666667)}, + SimulationSettings{}); const bool steppedOk = stepped.ok(); REQUIRE(steppedOk); const SimulationObservation observation = backend->observation(); @@ -92,7 +88,7 @@ TEST_CASE("physics.core.backendContract.isHeadlessAndObservable") { TEST_CASE("physics.core.backendContract.cpuAndMockShareObservableRules") { CapabilityReset reset; - b2World rawWorld(b2Vec2_zero); + b2World rawWorld(b2Vec2_zero); auto cpu = detail::makeBox2DSimulationBackend(&rawWorld); REQUIRE(cpu.get() != nullptr); @@ -106,11 +102,10 @@ TEST_CASE("physics.core.backendContract.cpuAndMockShareObservableRules") { TEST_CASE("physics.core.backendFallbackIsStructuredAndObservable") { CapabilityReset reset; - b2World rawWorld(b2Vec2_zero); + b2World rawWorld(b2Vec2_zero); - auto absent = detail::selectSimulationBackend( - SimulationBackendDomain::World2D, - detail::makeBox2DSimulationBackend(&rawWorld), &rawWorld, true); + auto absent = detail::selectSimulationBackend(SimulationBackendDomain::World2D, + detail::makeBox2DSimulationBackend(&rawWorld), &rawWorld, true); const bool absentOk = absent.ok(); REQUIRE(absentOk); CHECK_EQ(absent.status().code(), eve::StatusCode::Applied); @@ -122,9 +117,8 @@ TEST_CASE("physics.core.backendFallbackIsStructuredAndObservable") { MockAcceleratorProvider provider; eve::cap::provide(&provider); - auto present = detail::selectSimulationBackend( - SimulationBackendDomain::World2D, - detail::makeBox2DSimulationBackend(&rawWorld), &rawWorld, true); + auto present = detail::selectSimulationBackend(SimulationBackendDomain::World2D, + detail::makeBox2DSimulationBackend(&rawWorld), &rawWorld, true); const bool presentOk = present.ok(); REQUIRE(presentOk); CHECK(present.diagnostics().empty()); @@ -134,7 +128,7 @@ TEST_CASE("physics.core.backendFallbackIsStructuredAndObservable") { } TEST_CASE("physics.core.worldUpdateNeedsNoGraphics") { - auto *physics = Physics::create(); + auto *physics = Physics::create(); std::unique_ptr world(physics->newWorld(0.f, 0.f)); REQUIRE(world.get() != nullptr); @@ -148,32 +142,31 @@ TEST_CASE("physics.core.worldUpdateNeedsNoGraphics") { } TEST_CASE("physics.core.worldAcceptsInjectedTickAndPhysicsLinkResolvesStale") { - auto *physics = Physics::create(); + auto *physics = Physics::create(); std::unique_ptr world(physics->newWorld(0.f, 0.f)); REQUIRE(world.get() != nullptr); - Body* body = world->newBody("dynamic", 0.f, 0.f); + Body *body = world->newBody("dynamic", 0.f, 0.f); body->newCircleFixture(5.f); - const eve::SimulationStep step{eve::SimulationTick{1}, - eve::Duration::fromNanoseconds(16666667)}; - auto stepped = world->step(step); - const bool steppedOk = stepped.ok(); + const eve::SimulationStep step{eve::SimulationTick{1}, eve::Duration::fromNanoseconds(16666667)}; + auto stepped = world->step(step); + const bool steppedOk = stepped.ok(); REQUIRE(steppedOk); CHECK(world->simulationTick() == step.tick); CHECK_EQ(world->simulationObservation().lastTick, step.tick); CHECK(world->backendSelectionStatus().code() == eve::StatusCode::Applied); - auto linkResult = PhysicsLink::fromBody(*body); - const bool linkOk = linkResult.ok(); + auto linkResult = PhysicsLink::fromBody(*body); + const bool linkOk = linkResult.ok(); REQUIRE(linkOk); - const PhysicsLink link = std::move(linkResult).takeValue(); - auto resolved = link.resolve(*world); - const bool resolvedOk = resolved.ok(); + const PhysicsLink link = std::move(linkResult).takeValue(); + auto resolved = link.resolve(*world); + const bool resolvedOk = resolved.ok(); REQUIRE(resolvedOk); CHECK(std::move(resolved).takeValue() == body); body->destroy(); - auto stale = link.resolve(*world); + auto stale = link.resolve(*world); const bool staleOk = stale.ok(); CHECK(!staleOk); CHECK_EQ(stale.code(), eve::StatusCode::Rejected); diff --git a/test/policyregistry.cpp b/test/policyregistry.cpp index b85a8b32..c47e7b3d 100644 --- a/test/policyregistry.cpp +++ b/test/policyregistry.cpp @@ -66,7 +66,7 @@ TEST_CASE("policyregistry.generationsInvalidateStaleHandles") { TEST_CASE("policyregistry.registryParityKeepsTombstones") { PolicyRegistry registry; - auto inserted = registry.insert("movement", "ground", 1, 2, true, "builtin", "", "{}"); + auto inserted = registry.insert("movement", "ground", 1, 2, true, "builtin", "", "{}"); REQUIRE(inserted.ok()); const auto first = std::move(inserted).takeValue(); @@ -95,7 +95,7 @@ TEST_CASE("policyregistry.registryParityKeepsTombstones") { } TEST_CASE("policyregistry.subscriptionAndSnapshotEnvelopeAreCompatible") { - PolicyRegistry registry; + PolicyRegistry registry; std::vector names; auto subscription = registry.subscribe([&](const PolicyEvent& event) { names.push_back(event.name); }); REQUIRE(registry.insert("orders", "safe", 1, 1, true, "script", "", "{}").ok()); @@ -111,7 +111,7 @@ TEST_CASE("policyregistry.subscriptionAndSnapshotEnvelopeAreCompatible") { auto envelope = registry.snapshotEnvelopeJson(hash); REQUIRE(envelope.ok()); PolicyRegistry copy; - auto restored = copy.restoreSnapshotJson(std::move(envelope).takeValue(), hash); + auto restored = copy.restoreSnapshotJson(std::move(envelope).takeValue(), hash); REQUIRE(restored.ok()); auto safe = copy.resolve("orders", "safe"); REQUIRE(safe.ok()); @@ -120,9 +120,8 @@ TEST_CASE("policyregistry.subscriptionAndSnapshotEnvelopeAreCompatible") { TEST_CASE("policyregistry.mutationPreservesObserverWarning") { PolicyRegistry registry; - auto subscription = registry.subscribe([](const PolicyEvent&) { - throw std::runtime_error("injected policy observer failure"); - }); + auto subscription = + registry.subscribe([](const PolicyEvent&) { throw std::runtime_error("injected policy observer failure"); }); auto result = registry.insert("orders", "safe", 1, 1, true, "script", "", "{}"); REQUIRE(result.ok()); diff --git a/test/procgen.cpp b/test/procgen.cpp index 675fd909..5fac60a9 100644 --- a/test/procgen.cpp +++ b/test/procgen.cpp @@ -155,17 +155,17 @@ struct ParamsLease { auto result = Procgen::release(handle); result.ignore("procgen test params cleanup"); } - ParamsLease(const ParamsLease&) = delete; - ParamsLease& operator=(const ParamsLease&) = delete; - ParamsLease(ParamsLease&& other) noexcept : handle(other.handle) { other.handle = {}; } + ParamsLease(const ParamsLease &) = delete; + ParamsLease &operator=(const ParamsLease &) = delete; + ParamsLease(ParamsLease &&other) noexcept : handle(other.handle) { other.handle = {}; } [[nodiscard]] eve::script::Borrowed view() const noexcept { return Procgen::resolve(handle); } }; -ParamsLease requireParams(const Params& source) { +ParamsLease requireParams(const Params &source) { auto result = Procgen::newParamsHandle(); REQUIRE(result.ok()); ParamsLease lease{std::move(result).takeValue()}; - auto view = lease.view(); + auto view = lease.view(); REQUIRE(view.isBound()); *view = source; return lease; @@ -179,32 +179,31 @@ struct GridLease { auto result = Procgen::release(handle); result.ignore("procgen test grid cleanup"); } - GridLease(const GridLease&) = delete; - GridLease& operator=(const GridLease&) = delete; - GridLease(GridLease&& other) noexcept : handle(other.handle) { other.handle = {}; } + GridLease(const GridLease &) = delete; + GridLease &operator=(const GridLease &) = delete; + GridLease(GridLease &&other) noexcept : handle(other.handle) { other.handle = {}; } [[nodiscard]] eve::script::Borrowed view() const noexcept { return Procgen::resolve(handle); } }; -GridLease requireGrid(Procgen& proc, const std::string& algorithm, - ProcgenParamsHandleRef params) { +GridLease requireGrid(Procgen &proc, const std::string &algorithm, ProcgenParamsHandleRef params) { auto result = proc.generateHandle(algorithm, params); REQUIRE(result.ok()); return GridLease{std::move(result).takeValue()}; } struct MeshLease { - Procgen* owner = nullptr; + Procgen *owner = nullptr; ProcgenMeshBuildHandleRef handle{}; - MeshLease(Procgen& proc, ProcgenMeshBuildHandleRef value) : owner(&proc), handle(value) {} + MeshLease(Procgen &proc, ProcgenMeshBuildHandleRef value) : owner(&proc), handle(value) {} ~MeshLease() { if (!owner || !handle.isValid()) return; auto result = owner->releaseMeshBuild(handle); result.ignore("procgen test mesh cleanup"); } - MeshLease(const MeshLease&) = delete; - MeshLease& operator=(const MeshLease&) = delete; - MeshLease(MeshLease&& other) noexcept : owner(other.owner), handle(other.handle) { - other.owner = nullptr; + MeshLease(const MeshLease &) = delete; + MeshLease &operator=(const MeshLease &) = delete; + MeshLease(MeshLease &&other) noexcept : owner(other.owner), handle(other.handle) { + other.owner = nullptr; other.handle = {}; } [[nodiscard]] eve::script::Borrowed view() const noexcept { @@ -212,31 +211,29 @@ struct MeshLease { } }; -MeshLease requireMesh(Procgen& proc, const std::string& recipe, - ProcgenParamsHandleRef params) { +MeshLease requireMesh(Procgen &proc, const std::string &recipe, ProcgenParamsHandleRef params) { auto result = proc.buildMeshHandle(recipe, params); REQUIRE(result.ok()); return MeshLease(proc, std::move(result).takeValue()); } struct PbrLease { - Procgen* owner = nullptr; + Procgen *owner = nullptr; ProcgenPbrMaterialHandleRef handle{}; - PbrLease(Procgen& proc, ProcgenPbrMaterialHandleRef value) : owner(&proc), handle(value) {} + PbrLease(Procgen &proc, ProcgenPbrMaterialHandleRef value) : owner(&proc), handle(value) {} ~PbrLease() { if (!owner || !handle.isValid()) return; auto result = owner->releasePbrMaterial(handle); result.ignore("procgen test PBR cleanup"); } - PbrLease(const PbrLease&) = delete; - PbrLease& operator=(const PbrLease&) = delete; - PbrLease(PbrLease&& other) noexcept : owner(other.owner), handle(other.handle) { - other.owner = nullptr; + PbrLease(const PbrLease &) = delete; + PbrLease &operator=(const PbrLease &) = delete; + PbrLease(PbrLease &&other) noexcept : owner(other.owner), handle(other.handle) { + other.owner = nullptr; other.handle = {}; } [[nodiscard]] eve::script::Borrowed view() const noexcept { - return owner ? owner->resolvePbrMaterial(handle) - : eve::script::Borrowed(); + return owner ? owner->resolvePbrMaterial(handle) : eve::script::Borrowed(); } }; @@ -710,8 +707,8 @@ TEST_CASE("procgen.mesh.tree.renderDump") { params.setInt("maxChildren", 2); Procgen generator; - auto treeParams = requireParams(params); - auto treeMesh = generator.generateMeshBorrowed("mesh.tree", treeParams.handle, gfx); + auto treeParams = requireParams(params); + auto treeMesh = generator.generateMeshBorrowed("mesh.tree", treeParams.handle, gfx); REQUIRE(treeMesh.isBound()); // 4px bark/foliage atlas; UVs are partitioned by the mesh recipe. @@ -858,8 +855,8 @@ TEST_CASE("procgen.mesh.bush.renderDump") { params.setInt("twigs", 6); Procgen generator; - auto bushParams = requireParams(params); - auto bushMesh = generator.generateMeshBorrowed("mesh.bush", bushParams.handle, gfx); + auto bushParams = requireParams(params); + auto bushMesh = generator.generateMeshBorrowed("mesh.bush", bushParams.handle, gfx); REQUIRE(bushMesh.isBound()); // A tiny two-tone foliage atlas (dark base / light highlight), like the tree test. @@ -1100,9 +1097,9 @@ TEST_CASE("procgen.wfc.simple.viaModule") { p.setSize(16, 12); p.setString("preset", "dungeon"); p.setInt("maxAttempts", 64); - auto params = requireParams(p); + auto params = requireParams(p); auto gridLease = requireGrid(*mod, "wfc.simple", params.handle); - auto grid = gridLease.view(); + auto grid = gridLease.view(); REQUIRE(grid.isBound()); CHECK(borderIsWall(*grid)); CHECK_EQ(grid->getWidth(), 16); @@ -1113,9 +1110,9 @@ TEST_CASE("procgen.wfc.simple.viaModule") { bad.setSize(8, 8); bad.setString("preset", "invalid"); auto badParams = requireParams(bad); - auto failed = mod->generateHandle("wfc.simple", badParams.handle); + auto failed = mod->generateHandle("wfc.simple", badParams.handle); CHECK(!failed.ok()); - const eve::Diagnostic* diagnostic = failed.error(); + const eve::Diagnostic *diagnostic = failed.error(); REQUIRE(diagnostic != nullptr); CHECK_EQ(diagnostic->code(), eve::DiagnosticCode::Failed); } @@ -1218,7 +1215,7 @@ TEST_CASE("procgen.render.hexplanetPng") { params.setInt("subdivisions", 3); params.setFloat("tileInset", 0.12f); auto planetParams = requireParams(params); - auto planetMesh = procgen->generateMeshBorrowed("mesh.hexplanet", planetParams.handle, gfx); + auto planetMesh = procgen->generateMeshBorrowed("mesh.hexplanet", planetParams.handle, gfx); REQUIRE(planetMesh.isBound()); const uint8_t oceanBlue[4] = {42, 155, 181, 255}; @@ -1403,7 +1400,7 @@ TEST_CASE("procgen.render.skyscraperPng") { params.setFloat("windowDepth", 0.06f); params.setFloat("spireHeight", 5.f); auto towerParams = requireParams(params); - auto towerMesh = procgen->generateMeshBorrowed("mesh.skyscraper", towerParams.handle, gfx); + auto towerMesh = procgen->generateMeshBorrowed("mesh.skyscraper", towerParams.handle, gfx); REQUIRE(towerMesh.isBound()); // 2x2 atlas: bottom texel dark wall, top texel bright window (matches UV convention). @@ -1490,8 +1487,8 @@ TEST_CASE("procgen.render.castlePng") { params.setInt("detail", 2); params.setFloat("towerSpacing", 15.f); auto castleParams = requireParams(params); - auto castleBuild = requireMesh(*procgen, "mesh.castle", castleParams.handle); - auto castleView = castleBuild.view(); + auto castleBuild = requireMesh(*procgen, "mesh.castle", castleParams.handle); + auto castleView = castleBuild.view(); REQUIRE(castleView.isBound()); const uint8_t stone[16] = { @@ -1739,9 +1736,9 @@ TEST_CASE("procgen.mesh.marchingcubes.viaModule") { p.setSeed(3); p.setInt("resolution", 16); p.setString("field", "torus"); - auto params = requireParams(p); + auto params = requireParams(p); auto meshLease = requireMesh(*mod, "mesh.marchingcubes", params.handle); - auto m = meshLease.view(); + auto m = meshLease.view(); REQUIRE(m.isBound()); CHECK(m->getVertexCount() > 50); CHECK(meshIndicesInRange(*m)); @@ -1758,12 +1755,12 @@ TEST_CASE("procgen.mesh.marchingcubes.viaModule") { auto badParams = requireParams(bad); auto badResult = mod->buildMeshHandle("mesh.marchingcubes", badParams.handle); CHECK(!badResult.ok()); - const eve::Diagnostic* badDiagnostic = badResult.error(); + const eve::Diagnostic *badDiagnostic = badResult.error(); REQUIRE(badDiagnostic != nullptr); CHECK_EQ(badDiagnostic->code(), eve::DiagnosticCode::Failed); auto missingResult = mod->buildMeshHandle("mesh.missing", params.handle); CHECK(!missingResult.ok()); - const eve::Diagnostic* missingDiagnostic = missingResult.error(); + const eve::Diagnostic *missingDiagnostic = missingResult.error(); REQUIRE(missingDiagnostic != nullptr); CHECK_EQ(missingDiagnostic->code(), eve::DiagnosticCode::Failed); auto missingGraphicsResult = mod->generateMeshBorrowed("mesh.marchingcubes", {}, nullptr); @@ -1899,9 +1896,9 @@ TEST_CASE("procgen.mesh.marchingcubes.sphereVolumeSignStable") { TEST_CASE("procgen.mesh.marchingcubes.moduleNullAndList") { Procgen *mod = Procgen::create(); - auto failed = mod->buildMeshHandle("mesh.marchingcubes", {}); + auto failed = mod->buildMeshHandle("mesh.marchingcubes", {}); CHECK(!failed.ok()); - const eve::Diagnostic* diagnostic = failed.error(); + const eve::Diagnostic *diagnostic = failed.error(); REQUIRE(diagnostic != nullptr); CHECK_EQ(diagnostic->code(), eve::DiagnosticCode::StaleHandle); auto failedUpload = mod->generateMeshBorrowed("mesh.marchingcubes", {}, nullptr); @@ -1920,9 +1917,9 @@ TEST_CASE("procgen.palette.applyToLayer") { Params p; p.setSeed(1); p.setSize(16, 12); - auto params = requireParams(p); + auto params = requireParams(p); auto gridLease = requireGrid(*mod, "dungeon.bsp", params.handle); - auto grid = gridLease.view(); + auto grid = gridLease.view(); REQUIRE(grid.isBound()); mod->setPaletteGid("test", "wall", 10); @@ -1930,7 +1927,7 @@ TEST_CASE("procgen.palette.applyToLayer") { mod->setPaletteGid("test", "corridor", 21); eve::map::TileLayer *layer = eve::map::TileLayer::createLayer(16, 12, 16.f, 16.f); - auto applyResult = mod->applyToLayer(gridLease.handle, "test", *layer); + auto applyResult = mod->applyToLayer(gridLease.handle, "test", *layer); CHECK(applyResult.ok()); bool sawMapped = false; for (int y = 0; y < 12; ++y) { @@ -1997,8 +1994,8 @@ TEST_CASE("procgen.texture.recipes.reproducible") { std::string err; // Fully qualify: `using namespace eve::procgen` would make bare `image::` // look outside `eve::image`. - auto a = TextureRecipeRegistry::instance().generate(id, p, err); - auto b = TextureRecipeRegistry::instance().generate(id, p, err); + auto a = TextureRecipeRegistry::instance().generate(id, p, err); + auto b = TextureRecipeRegistry::instance().generate(id, p, err); const bool aPresent = static_cast(a); const bool bPresent = static_cast(b); REQUIRE(aPresent); @@ -2017,7 +2014,7 @@ TEST_CASE("procgen.texture.generateImage.andNormal") { p.setSeed(3); p.setSize(48, 48); p.setInt("colors", 6); - auto params = requireParams(p); + auto params = requireParams(p); auto imageResult = mod->generateImageHandle("tex.marble", params.handle); REQUIRE(imageResult.ok()); auto normalResult = mod->generateNormalImageHandle("tex.marble", params.handle); @@ -2131,8 +2128,8 @@ TEST_CASE("procgen.cloud.recipes.generate") { p.setFloat("worldScale", 64.f); p.setFloat("time", 1.5f); std::string err; - auto img = TextureRecipeRegistry::instance().generate(id, p, err); - REQUIRE(static_cast(img)); + auto img = TextureRecipeRegistry::instance().generate(id, p, err); + REQUIRE(static_cast(img)); CHECK_EQ(img->getFormat(), std::string("RGBA8")); CHECK_EQ(img->getWidth(), 48); CHECK_EQ(img->getHeight(), 48); @@ -2143,10 +2140,10 @@ TEST_CASE("procgen.cloud.recipes.generate") { TEST_CASE("procgen.cloud.viaModule") { Procgen *mod = Procgen::create(); - auto fieldResult = mod->newCloudFieldHandle(); + auto fieldResult = mod->newCloudFieldHandle(); REQUIRE(fieldResult.ok()); auto fieldHandle = std::move(fieldResult).takeValue(); - auto field = mod->resolveCloudField(fieldHandle); + auto field = mod->resolveCloudField(fieldHandle); REQUIRE(field.isBound()); field->setSeed(3); field->setWorldScale(64.f); @@ -2162,7 +2159,7 @@ TEST_CASE("procgen.cloud.viaModule") { auto shadowResult = mod->newCloudShadowHandle(); REQUIRE(shadowResult.ok()); auto shadowHandle = std::move(shadowResult).takeValue(); - auto shadow = mod->resolveCloudShadow(shadowHandle); + auto shadow = mod->resolveCloudShadow(shadowHandle); REQUIRE(shadow.isBound()); shadow->setSunDirection(0.5f, 1.f, 0.f); shadow->setCloudAltitude(50.f); @@ -2174,11 +2171,9 @@ TEST_CASE("procgen.cloud.viaModule") { CHECK(!staleFactor.ok()); std::vector buf(16 * 16); - auto sampleResult = mod->sampleCloud(fieldHandle, std::span(buf), 16, 16, 0.f, - 0.f, 0.f, 64.f); + auto sampleResult = mod->sampleCloud(fieldHandle, std::span(buf), 16, 16, 0.f, 0.f, 0.f, 64.f); CHECK(sampleResult.ok()); - auto shadowSampleResult = mod->sampleCloudShadow(shadowHandle, std::span(buf), 16, 16, - 0.f, 0.f, 0.f, 64.f); + auto shadowSampleResult = mod->sampleCloudShadow(shadowHandle, std::span(buf), 16, 16, 0.f, 0.f, 0.f, 64.f); CHECK(shadowSampleResult.ok()); auto releaseField = mod->releaseCloudField(fieldHandle); CHECK(releaseField.ok()); @@ -2198,10 +2193,10 @@ TEST_CASE("procgen.texture.builtinRecipes.expanded") { p.setSize(32, 32); p.setInt("colors", 5); std::string err; - auto a = TextureRecipeRegistry::instance().generate(id, p, err); - auto b = TextureRecipeRegistry::instance().generate(id, p, err); - const bool aPresent = static_cast(a); - const bool bPresent = static_cast(b); + auto a = TextureRecipeRegistry::instance().generate(id, p, err); + auto b = TextureRecipeRegistry::instance().generate(id, p, err); + const bool aPresent = static_cast(a); + const bool bPresent = static_cast(b); REQUIRE(aPresent); REQUIRE(bPresent); CHECK_EQ(a->getFormat(), std::string("RGBA8")); @@ -2274,10 +2269,10 @@ TEST_CASE("procgen.pbr.registry.builtinsAndReproducible") { p.setSeed(7); p.setSize(32, 32); std::string err; - auto a = PbrRecipeRegistry::instance().generate(id, p, err); - auto b = PbrRecipeRegistry::instance().generate(id, p, err); - const bool aPresent = static_cast(a); - const bool bPresent = static_cast(b); + auto a = PbrRecipeRegistry::instance().generate(id, p, err); + auto b = PbrRecipeRegistry::instance().generate(id, p, err); + const bool aPresent = static_cast(a); + const bool bPresent = static_cast(b); REQUIRE(aPresent); REQUIRE(bPresent); // All six maps present, same size, reproducible. @@ -2310,7 +2305,7 @@ TEST_CASE("procgen.pbr.metallicAndRoughnessRange") { p.setFloat("roughnessLow", 0.1f); p.setFloat("roughnessHigh", 0.2f); std::string err; - auto set = PbrRecipeRegistry::instance().generate("pbr.marble", p, err); + auto set = PbrRecipeRegistry::instance().generate("pbr.marble", p, err); REQUIRE(static_cast(set)); const auto *m = static_cast(set->metallic->getData()); for (size_t i = 0; i < set->metallic->getSize(); i += 4) { @@ -2328,11 +2323,11 @@ TEST_CASE("procgen.pbr.viaModuleAndErrors") { Params p; p.setSeed(9); p.setSize(40, 40); - auto params = requireParams(p); + auto params = requireParams(p); auto pbrResult = mod->generatePbrMaterialHandle("pbr.wall", params.handle); REQUIRE(pbrResult.ok()); auto setLease = PbrLease(*mod, std::move(pbrResult).takeValue()); - auto set = setLease.view(); + auto set = setLease.view(); REQUIRE(set.isBound()); CHECK(set->albedo != nullptr); CHECK(set->normal != nullptr); @@ -2347,12 +2342,12 @@ TEST_CASE("procgen.pbr.viaModuleAndErrors") { auto missingResult = mod->generatePbrMaterialHandle("pbr.missing", params.handle); CHECK(!missingResult.ok()); - const eve::Diagnostic* missingDiagnostic = missingResult.error(); + const eve::Diagnostic *missingDiagnostic = missingResult.error(); REQUIRE(missingDiagnostic != nullptr); CHECK_EQ(missingDiagnostic->code(), eve::DiagnosticCode::NotFound); auto staleResult = mod->generatePbrMaterialHandle("pbr.wall", {}); CHECK(!staleResult.ok()); - const eve::Diagnostic* staleDiagnostic = staleResult.error(); + const eve::Diagnostic *staleDiagnostic = staleResult.error(); REQUIRE(staleDiagnostic != nullptr); CHECK_EQ(staleDiagnostic->code(), eve::DiagnosticCode::StaleHandle); } @@ -2570,9 +2565,9 @@ TEST_CASE("procgen.mesh.linearStructure.viaModule") { p.setSeed(1); p.setInt("segments", 4); p.setFloat("segLength", 1.f); - auto params = requireParams(p); + auto params = requireParams(p); auto meshLease = requireMesh(*mod, "mesh.stonewall", params.handle); - auto m = meshLease.view(); + auto m = meshLease.view(); REQUIRE(m.isBound()); CHECK(m->getVertexCount() > 0); CHECK(meshIndicesInRange(*m)); @@ -2580,7 +2575,7 @@ TEST_CASE("procgen.mesh.linearStructure.viaModule") { CHECK(mod->hasMeshRecipe("mesh.bridge")); auto missingResult = mod->buildMeshHandle("mesh.nonexistent", params.handle); CHECK(!missingResult.ok()); - const eve::Diagnostic* missingDiagnostic = missingResult.error(); + const eve::Diagnostic *missingDiagnostic = missingResult.error(); REQUIRE(missingDiagnostic != nullptr); CHECK_EQ(missingDiagnostic->code(), eve::DiagnosticCode::Failed); } @@ -2674,7 +2669,7 @@ TEST_CASE("procgen.mesh.castle.validationAndModuleDiscovery") { CHECK(!rings->description.empty()); auto missingSchema = mod->getMeshRecipeSchema("mesh.missing"); CHECK(!missingSchema.ok()); - const eve::Diagnostic* schemaDiagnostic = missingSchema.error(); + const eve::Diagnostic *schemaDiagnostic = missingSchema.error(); REQUIRE(schemaDiagnostic != nullptr); CHECK_EQ(schemaDiagnostic->code(), eve::DiagnosticCode::NotFound); Params p; @@ -2685,7 +2680,7 @@ TEST_CASE("procgen.mesh.castle.validationAndModuleDiscovery") { auto params = requireParams(p); auto failed = mod->buildMeshHandle("mesh.castle", params.handle); CHECK(!failed.ok()); - const eve::Diagnostic* failedDiagnostic = failed.error(); + const eve::Diagnostic *failedDiagnostic = failed.error(); REQUIRE(failedDiagnostic != nullptr); CHECK_EQ(failedDiagnostic->code(), eve::DiagnosticCode::Failed); } diff --git a/test/procgen_artifact_publish.cpp b/test/procgen_artifact_publish.cpp index 2509a488..829d439a 100644 --- a/test/procgen_artifact_publish.cpp +++ b/test/procgen_artifact_publish.cpp @@ -24,8 +24,8 @@ ArtifactId id(const char* text) { } struct Counters { - int prepared = 0; - int committed = 0; + int prepared = 0; + int committed = 0; int rolledBack = 0; }; @@ -38,8 +38,7 @@ public: eve::Result prepare(const eve::transaction::TransactionContext&) override { ++prepareCalls; if (failPrepare) - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Failed, name_ + " prepare")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::Failed, name_ + " prepare")); prepared = true; return eve::Result::success(); } @@ -47,9 +46,8 @@ public: eve::Result commit(const eve::transaction::TransactionContext&) override { ++commitCalls; if (failCommit) - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Failed, name_ + " commit")); - prepared = false; + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::Failed, name_ + " commit")); + prepared = false; committed = true; return eve::Result::success(); } @@ -57,8 +55,7 @@ public: eve::Result rollback(const eve::transaction::TransactionContext&) override { ++rollbackCalls; if (failRollback) - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Failed, name_ + " rollback")); + return eve::Result::failure(eve::Diagnostic::error(eve::DiagnosticCode::Failed, name_ + " rollback")); prepared = false; return eve::Result::success(); } @@ -72,16 +69,16 @@ public: return eve::Result::success(); } - bool failPrepare = false; - bool failCommit = false; - bool failRollback = false; - bool failCompensate = false; - bool prepared = false; - bool committed = false; - int prepareCalls = 0; - int commitCalls = 0; - int rollbackCalls = 0; - int compensateCalls = 0; + bool failPrepare = false; + bool failCommit = false; + bool failRollback = false; + bool failCompensate = false; + bool prepared = false; + bool committed = false; + int prepareCalls = 0; + int commitCalls = 0; + int rollbackCalls = 0; + int compensateCalls = 0; private: std::string name_; @@ -92,6 +89,7 @@ public: explicit Stage(Counters& counters) : counters_(counters) {} void commit() noexcept override { ++counters_.committed; } void rollback() noexcept override { ++counters_.rolledBack; } + private: Counters& counters_; }; @@ -100,17 +98,17 @@ template class Adapter final : public Interface { public: explicit Adapter(Counters& counters, bool fail = false) : counters_(counters), fail_(fail) {} - eve::Result> prepare( - const eve::artifact::PublicationView&) override { + eve::Result> prepare(const eve::artifact::PublicationView&) override { ++counters_.prepared; - if (fail_) return eve::Result>::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Failed, "injected adapter failure")); - return eve::Result>::success( - std::make_unique(counters_)); + if (fail_) + return eve::Result>::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "injected adapter failure")); + return eve::Result>::success(std::make_unique(counters_)); } + private: Counters& counters_; - bool fail_; + bool fail_; }; Params params() { @@ -126,12 +124,11 @@ Params params() { TEST_CASE("procgen.artifactPublish.missingCapabilityIsExplicitAndAtomic") { eve::cap::detail::clearAllRaw(); - auto generated = generateHexTerrainArtifact( - params(), id("018f0b7e-6e50-7a10-8c22-2c8f8e3dd101")); + auto generated = generateHexTerrainArtifact(params(), id("018f0b7e-6e50-7a10-8c22-2c8f8e3dd101")); REQUIRE(generated.ok()); - ArtifactStore store; + ArtifactStore store; ArtifactPublisher publisher(store); - auto result = publisher.publish(std::move(generated).takeValue(), {.graphics = true}); + auto result = publisher.publish(std::move(generated).takeValue(), {.graphics = true}); CHECK(!result.ok()); CHECK_EQ(static_cast(result.code()), static_cast(eve::StatusCode::Unsupported)); CHECK_EQ(store.size(), std::size_t(0)); @@ -140,24 +137,22 @@ TEST_CASE("procgen.artifactPublish.missingCapabilityIsExplicitAndAtomic") { TEST_CASE("procgen.artifactPublish.capabilitiesCommitHexAndCastle") { eve::cap::detail::clearAllRaw(); - Counters sceneCounts, graphicsCounts, physicsCounts; - Adapter scene(sceneCounts); + Counters sceneCounts, graphicsCounts, physicsCounts; + Adapter scene(sceneCounts); Adapter graphics(graphicsCounts); - Adapter physics(physicsCounts); + Adapter physics(physicsCounts); eve::cap::provide(&scene); eve::cap::provide(&graphics); eve::cap::provide(&physics); - ArtifactStore store; + ArtifactStore store; ArtifactPublisher publisher(store); - auto hex = generateHexTerrainArtifact(params(), - id("018f0b7e-6e50-7a10-8c22-2c8f8e3dd102")); + auto hex = generateHexTerrainArtifact(params(), id("018f0b7e-6e50-7a10-8c22-2c8f8e3dd102")); REQUIRE(hex.ok()); auto first = publisher.publish(std::move(hex).takeValue(), {true, true, true}); REQUIRE(first.ok()); std::move(first).takeValue(); - auto castle = generateCastleArtifact(params(), - id("018f0b7e-6e50-7a10-8c22-2c8f8e3dd103")); + auto castle = generateCastleArtifact(params(), id("018f0b7e-6e50-7a10-8c22-2c8f8e3dd103")); REQUIRE(castle.ok()); auto second = publisher.publish(std::move(castle).takeValue(), {true, true, true}); REQUIRE(second.ok()); @@ -171,17 +166,16 @@ TEST_CASE("procgen.artifactPublish.capabilitiesCommitHexAndCastle") { TEST_CASE("procgen.artifactPublish.adapterFailureRollsBackWithoutStoreState") { eve::cap::detail::clearAllRaw(); - Counters sceneCounts, graphicsCounts; - Adapter scene(sceneCounts); + Counters sceneCounts, graphicsCounts; + Adapter scene(sceneCounts); Adapter graphics(graphicsCounts, true); eve::cap::provide(&scene); eve::cap::provide(&graphics); - auto generated = generateHexTerrainArtifact(params(), - id("018f0b7e-6e50-7a10-8c22-2c8f8e3dd104")); + auto generated = generateHexTerrainArtifact(params(), id("018f0b7e-6e50-7a10-8c22-2c8f8e3dd104")); REQUIRE(generated.ok()); - ArtifactStore store; + ArtifactStore store; ArtifactPublisher publisher(store); - auto result = publisher.publish(std::move(generated).takeValue(), {true, true, false}); + auto result = publisher.publish(std::move(generated).takeValue(), {true, true, false}); CHECK(!result.ok()); CHECK_EQ(sceneCounts.rolledBack, 1); CHECK_EQ(sceneCounts.committed, 0); @@ -190,21 +184,19 @@ TEST_CASE("procgen.artifactPublish.adapterFailureRollsBackWithoutStoreState") { } TEST_CASE("procgen.artifactPublish.badDependencyRejectsWholeBatch") { - auto first = generateHexTerrainArtifact(params(), - id("018f0b7e-6e50-7a10-8c22-2c8f8e3dd105")); - auto second = generateCastleArtifact(params(), - id("018f0b7e-6e50-7a10-8c22-2c8f8e3dd106")); + auto first = generateHexTerrainArtifact(params(), id("018f0b7e-6e50-7a10-8c22-2c8f8e3dd105")); + auto second = generateCastleArtifact(params(), id("018f0b7e-6e50-7a10-8c22-2c8f8e3dd106")); REQUIRE(first.ok()); REQUIRE(second.ok()); auto artifacts = std::vector{}; artifacts.emplace_back(std::move(first).takeValue()); artifacts.emplace_back(std::move(second).takeValue()); - auto& composite = std::get(artifacts.back().payload); - ArtifactPart* collider = const_cast(composite.find("collider")); + auto& composite = std::get(artifacts.back().payload); + ArtifactPart* collider = const_cast(composite.find("collider")); REQUIRE(collider != nullptr); collider->dependencies = {id("018f0b7e-6e50-7a10-8c22-2c8f8e3dd199")}; ArtifactStore store; - auto result = store.publishBatch(std::move(artifacts)); + auto result = store.publishBatch(std::move(artifacts)); CHECK(!result.ok()); CHECK_EQ(static_cast(result.code()), static_cast(eve::StatusCode::NotFound)); CHECK_EQ(store.size(), std::size_t(0)); @@ -212,10 +204,8 @@ TEST_CASE("procgen.artifactPublish.badDependencyRejectsWholeBatch") { } TEST_CASE("procgen.artifactPublish.buildKeyAndPayloadAreDeterministicAcrossIdentity") { - auto first = generateHexTerrainArtifact(params(), - id("018f0b7e-6e50-7a10-8c22-2c8f8e3dd107")); - auto second = generateHexTerrainArtifact(params(), - id("018f0b7e-6e50-7a10-8c22-2c8f8e3dd108")); + auto first = generateHexTerrainArtifact(params(), id("018f0b7e-6e50-7a10-8c22-2c8f8e3dd107")); + auto second = generateHexTerrainArtifact(params(), id("018f0b7e-6e50-7a10-8c22-2c8f8e3dd108")); REQUIRE(first.ok()); REQUIRE(second.ok()); GeneratedArtifact a = std::move(first).takeValue(); @@ -225,10 +215,8 @@ TEST_CASE("procgen.artifactPublish.buildKeyAndPayloadAreDeterministicAcrossIdent const auto* bMesh = std::get(b.payload).find("mesh"); REQUIRE(aMesh != nullptr); REQUIRE(bMesh != nullptr); - CHECK_EQ(std::get(aMesh->payload).positions(), - std::get(bMesh->payload).positions()); - CHECK_EQ(std::get(aMesh->payload).indices(), - std::get(bMesh->payload).indices()); + CHECK_EQ(std::get(aMesh->payload).positions(), std::get(bMesh->payload).positions()); + CHECK_EQ(std::get(aMesh->payload).indices(), std::get(bMesh->payload).indices()); } TEST_CASE("procgen.artifactPublish.coordinatorFailureInjectionCoversPrepareCommitAndRollback") { @@ -238,9 +226,9 @@ TEST_CASE("procgen.artifactPublish.coordinatorFailureInjectionCoversPrepareCommi InjectedTransactionParticipant prepareFailure("prepare-failure"); prepareFailure.failPrepare = true; std::array prepareParticipants{&first, &prepareFailure}; - eve::transaction::Coordinator coordinator; - auto prepareResult = coordinator.execute( - eve::transaction::TransactionContext("procgen-prepare-failure"), prepareParticipants); + eve::transaction::Coordinator coordinator; + auto prepareResult = + coordinator.execute(eve::transaction::TransactionContext("procgen-prepare-failure"), prepareParticipants); CHECK(!prepareResult.ok()); CHECK_EQ(first.rollbackCalls, 1); CHECK(!first.prepared); @@ -249,8 +237,8 @@ TEST_CASE("procgen.artifactPublish.coordinatorFailureInjectionCoversPrepareCommi InjectedTransactionParticipant commitFailure("commit-failure"); commitFailure.failCommit = true; std::array commitParticipants{&committed, &commitFailure}; - auto commitResult = coordinator.execute( - eve::transaction::TransactionContext("procgen-commit-failure"), commitParticipants); + auto commitResult = + coordinator.execute(eve::transaction::TransactionContext("procgen-commit-failure"), commitParticipants); CHECK(!commitResult.ok()); CHECK_EQ(commitFailure.rollbackCalls, 1); CHECK_EQ(committed.compensateCalls, 1); @@ -261,8 +249,8 @@ TEST_CASE("procgen.artifactPublish.coordinatorFailureInjectionCoversPrepareCommi InjectedTransactionParticipant trigger("trigger"); trigger.failPrepare = true; std::array rollbackParticipants{&rollbackFailure, &trigger}; - auto rollbackResult = coordinator.execute( - eve::transaction::TransactionContext("procgen-rollback-failure"), rollbackParticipants); + auto rollbackResult = + coordinator.execute(eve::transaction::TransactionContext("procgen-rollback-failure"), rollbackParticipants); CHECK(!rollbackResult.ok()); CHECK_EQ(rollbackFailure.rollbackCalls, 1); CHECK(rollbackFailure.prepared); @@ -270,21 +258,21 @@ TEST_CASE("procgen.artifactPublish.coordinatorFailureInjectionCoversPrepareCommi } TEST_CASE("procgen.artifactPublish.webGpuDescriptorParityUsesCommonMeshView") { - const auto artifact = id("018f0b7e-6e50-7a10-8c22-2c8f8e3dd109"); - const std::vector positions{0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 1.f, 0.f}; + const auto artifact = id("018f0b7e-6e50-7a10-8c22-2c8f8e3dd109"); + const std::vector positions{0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 1.f, 0.f}; const std::vector indices{0u, 1u, 2u}; - eve::artifact::PartView part; - part.id = eve::PersistentId::fromUuid(artifact); - part.role = "mesh"; - part.kind = eve::artifact::PartKind::MeshData; - part.buildKey = "procgen/v1/descriptor/0000000000000001"; + eve::artifact::PartView part; + part.id = eve::PersistentId::fromUuid(artifact); + part.role = "mesh"; + part.kind = eve::artifact::PartKind::MeshData; + part.buildKey = "procgen/v1/descriptor/0000000000000001"; part.positions = positions; - part.indices = indices; + part.indices = indices; std::array parts{part}; - eve::artifact::PublicationView publication; - publication.id = part.id; + eve::artifact::PublicationView publication; + publication.id = part.id; publication.buildKey = part.buildKey; - publication.parts = parts; + publication.parts = parts; const auto descriptor = eve::graphics::WebGpuArtifactDescriptorAdapter::describe(publication); REQUIRE(descriptor.has_value()); diff --git a/test/procgen_core_boundary.cpp b/test/procgen_core_boundary.cpp index 87cbb01d..da32c760 100644 --- a/test/procgen_core_boundary.cpp +++ b/test/procgen_core_boundary.cpp @@ -23,8 +23,7 @@ TEST_CASE("procgen.core.cpuSurfaceIsBackendNeutral") { params.setSize(4, 3); params.setBool("decorations", true); - const RecipeDescriptor recipe = - RecipeDescriptor::grid("core.grid", "Core Grid", "test", 1, 1, 32, 32); + const RecipeDescriptor recipe = RecipeDescriptor::grid("core.grid", "Core Grid", "test", 1, 1, 32, 32); recipe.applyDefaults(params); CHECK_EQ(params.getSeed(), uint32_t(42)); CHECK_EQ(params.getWidth(), 4); @@ -74,7 +73,7 @@ TEST_CASE("procgen.params.typedStorageConversionsAndCanonicalEncoding") { CHECK(params.getBool("enabled", false)); CHECK_EQ(params.getInt("enabled", -1), 1); // Bool has only 0/1 numeric conversion CHECK_EQ(params.getString("count", "wrong-kind"), "wrong-kind"); - CHECK_EQ(params.getInt("label", -1), -1); // String is never parsed + CHECK_EQ(params.getInt("label", -1), -1); // String is never parsed CHECK(params.getBool("label", true)); // invalid kind returns the caller default const std::string canonical = params.canonicalString(); diff --git a/test/procgen_e2e.cpp b/test/procgen_e2e.cpp index bc5ad42d..944223e8 100644 --- a/test/procgen_e2e.cpp +++ b/test/procgen_e2e.cpp @@ -30,9 +30,7 @@ eve::procgen::ArtifactId artifactId(const char* text) { return *parsed; } -eve::PersistentId persistentId(eve::procgen::ArtifactId id) { - return eve::PersistentId::fromUuid(id); -} +eve::PersistentId persistentId(eve::procgen::ArtifactId id) { return eve::PersistentId::fromUuid(id); } eve::procgen::Params hexParams() { eve::procgen::Params params; @@ -52,14 +50,13 @@ eve::SnapshotHashProvider testHash() { } std::array bytes{}; for (std::size_t i = 0; i < 8; ++i) { - bytes[i] = static_cast((hash >> (i * 8u)) & 0xffu); - bytes[8 + i] = static_cast(((hash ^ 0x9e3779b97f4a7c15ull) >> (i * 8u)) & - 0xffu); + bytes[i] = static_cast((hash >> (i * 8u)) & 0xffu); + bytes[8 + i] = static_cast(((hash ^ 0x9e3779b97f4a7c15ull) >> (i * 8u)) & 0xffu); } const auto id = eve::ContentId::fromBytes(std::span(bytes)); if (!id) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Failed, "test digest construction failed")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "test digest construction failed")); return eve::Result::success(*id); }; } @@ -78,10 +75,10 @@ void resetProviders() { eve::procgen::ArtifactPublishOptions allProviders() { eve::procgen::ArtifactPublishOptions options; - options.scene = true; + options.scene = true; options.graphics = true; - options.physics = true; - options.map = true; + options.physics = true; + options.map = true; return options; } @@ -89,10 +86,10 @@ eve::procgen::ArtifactPublishOptions allProviders() { TEST_CASE("procgen.e2e.hexProvidersCommitQueryableObjects") { resetProviders(); - const auto id = artifactId("018f0b7e-6e50-7a10-8c22-2c8f8e3e0101"); - eve::procgen::ArtifactStore store; + const auto id = artifactId("018f0b7e-6e50-7a10-8c22-2c8f8e3e0101"); + eve::procgen::ArtifactStore store; eve::procgen::ArtifactPublisher publisher(store); - auto generated = eve::procgen::generateHexTerrainArtifact(hexParams(), id); + auto generated = eve::procgen::generateHexTerrainArtifact(hexParams(), id); REQUIRE(generated.ok()); REQUIRE(generated.value().metadata.contains("algorithm")); REQUIRE(generated.value().metadata.contains("determinism")); @@ -100,10 +97,10 @@ TEST_CASE("procgen.e2e.hexProvidersCommitQueryableObjects") { REQUIRE(published.ok()); std::move(published).takeValue(); - auto& scene = eve::scene::sceneArtifactProvider(); - auto& map = eve::map::mapArtifactProvider(); + auto& scene = eve::scene::sceneArtifactProvider(); + auto& map = eve::map::mapArtifactProvider(); auto& graphics = eve::graphics::graphicsArtifactProvider(); - auto& physics = eve::physics::physicsArtifactProvider(); + auto& physics = eve::physics::physicsArtifactProvider(); CHECK_EQ(store.size(), std::size_t(1)); CHECK_EQ(store.partCount(), std::size_t(4)); CHECK_EQ(scene.size(), std::size_t(1)); @@ -134,19 +131,19 @@ TEST_CASE("procgen.e2e.hexProvidersCommitQueryableObjects") { TEST_CASE("procgen.e2e.providerFailureLeavesNoHalfState") { for (int failure = 0; failure < 4; ++failure) { resetProviders(); - auto& scene = eve::scene::sceneArtifactProvider(); - auto& map = eve::map::mapArtifactProvider(); + auto& scene = eve::scene::sceneArtifactProvider(); + auto& map = eve::map::mapArtifactProvider(); auto& graphics = eve::graphics::graphicsArtifactProvider(); - auto& physics = eve::physics::physicsArtifactProvider(); + auto& physics = eve::physics::physicsArtifactProvider(); if (failure == 0) scene.setPrepareFailure(true); if (failure == 1) map.setPrepareFailure(true); if (failure == 2) graphics.setPrepareFailure(true); if (failure == 3) physics.setPrepareFailure(true); - eve::procgen::ArtifactStore store; + eve::procgen::ArtifactStore store; eve::procgen::ArtifactPublisher publisher(store); - auto generated = eve::procgen::generateHexTerrainArtifact( - hexParams(), artifactId("018f0b7e-6e50-7a10-8c22-2c8f8e3e0102")); + auto generated = + eve::procgen::generateHexTerrainArtifact(hexParams(), artifactId("018f0b7e-6e50-7a10-8c22-2c8f8e3e0102")); REQUIRE(generated.ok()); auto result = publisher.publish(std::move(generated).takeValue(), allProviders()); CHECK(!result.ok()); @@ -161,12 +158,11 @@ TEST_CASE("procgen.e2e.providerFailureLeavesNoHalfState") { TEST_CASE("procgen.e2e.absentCapabilityIsExplicit") { resetProviders(); - eve::cap::revoke( - &eve::physics::physicsArtifactProvider()); - eve::procgen::ArtifactStore store; + eve::cap::revoke(&eve::physics::physicsArtifactProvider()); + eve::procgen::ArtifactStore store; eve::procgen::ArtifactPublisher publisher(store); - auto generated = eve::procgen::generateHexTerrainArtifact( - hexParams(), artifactId("018f0b7e-6e50-7a10-8c22-2c8f8e3e0103")); + auto generated = + eve::procgen::generateHexTerrainArtifact(hexParams(), artifactId("018f0b7e-6e50-7a10-8c22-2c8f8e3e0103")); REQUIRE(generated.ok()); auto result = publisher.publish(std::move(generated).takeValue(), allProviders()); CHECK(!result.ok()); @@ -177,33 +173,33 @@ TEST_CASE("procgen.e2e.absentCapabilityIsExplicit") { TEST_CASE("procgen.e2e.snapshotRoundTripPreservesIdentityBuildKeyAndQueries") { resetProviders(); - const auto id = artifactId("018f0b7e-6e50-7a10-8c22-2c8f8e3e0104"); - eve::procgen::ArtifactStore store; + const auto id = artifactId("018f0b7e-6e50-7a10-8c22-2c8f8e3e0104"); + eve::procgen::ArtifactStore store; eve::procgen::ArtifactPublisher publisher(store); - auto generated = eve::procgen::generateHexTerrainArtifact(hexParams(), id); + auto generated = eve::procgen::generateHexTerrainArtifact(hexParams(), id); REQUIRE(generated.ok()); const auto expectedKey = generated.value().buildKey; - auto published = publisher.publish(std::move(generated).takeValue(), allProviders()); + auto published = publisher.publish(std::move(generated).takeValue(), allProviders()); REQUIRE(published.ok()); std::move(published).takeValue(); const auto* beforeCollider = eve::physics::physicsArtifactProvider().find(persistentId(id)); REQUIRE(beforeCollider != nullptr); - const auto bounds = beforeCollider->bounds; - const float rayX = (bounds.minX + bounds.maxX) * .5f; - const float rayZ = (bounds.minZ + bounds.maxZ) * .5f; - const float rayOriginY = bounds.maxY + 10.f; + const auto bounds = beforeCollider->bounds; + const float rayX = (bounds.minX + bounds.maxX) * .5f; + const float rayZ = (bounds.minZ + bounds.maxZ) * .5f; + const float rayOriginY = bounds.maxY + 10.f; const float rayDirectionY = -((bounds.maxY - bounds.minY) + 20.f); - const auto beforeHit = eve::physics::physicsArtifactProvider().rayCast( - persistentId(id), rayX, rayOriginY, rayZ, 0.f, rayDirectionY, 0.f); - const auto beforeChecksum = eve::graphics::graphicsArtifactProvider().checksum(persistentId(id)); - const auto beforeCell = eve::map::mapArtifactProvider().cell(persistentId(id), 2, 3); + const auto beforeHit = eve::physics::physicsArtifactProvider().rayCast(persistentId(id), rayX, rayOriginY, rayZ, + 0.f, rayDirectionY, 0.f); + const auto beforeChecksum = eve::graphics::graphicsArtifactProvider().checksum(persistentId(id)); + const auto beforeCell = eve::map::mapArtifactProvider().cell(persistentId(id), 2, 3); eve::procgen::ArtifactSnapshotContext context; - context.instanceId = persistentId("018f0b7e-6e50-7a10-8c22-2c8f8e3e0105"); - context.revision = eve::Revision(9); - context.tick = eve::SimulationTick(17); + context.instanceId = persistentId("018f0b7e-6e50-7a10-8c22-2c8f8e3e0105"); + context.revision = eve::Revision(9); + context.tick = eve::SimulationTick(17); context.hashProvider = testHash(); - auto snapshot = publisher.snapshot(context); + auto snapshot = publisher.snapshot(context); REQUIRE(snapshot.ok()); auto serialized = eve::serializeSnapshotEnvelope(snapshot.value()); REQUIRE(serialized.ok()); @@ -227,17 +223,17 @@ TEST_CASE("procgen.e2e.snapshotRoundTripPreservesIdentityBuildKeyAndQueries") { CHECK_EQ(eve::scene::sceneArtifactProvider().size(), std::size_t(1)); CHECK_EQ(eve::map::mapArtifactProvider().cell(persistentId(id), 2, 3), beforeCell); CHECK_EQ(eve::graphics::graphicsArtifactProvider().checksum(persistentId(id)), beforeChecksum); - const auto afterHit = eve::physics::physicsArtifactProvider().rayCast( - persistentId(id), rayX, rayOriginY, rayZ, 0.f, rayDirectionY, 0.f); + const auto afterHit = eve::physics::physicsArtifactProvider().rayCast(persistentId(id), rayX, rayOriginY, rayZ, 0.f, + rayDirectionY, 0.f); CHECK_EQ(afterHit.hit, beforeHit.hit); CHECK_EQ(afterHit.fraction, beforeHit.fraction); } TEST_CASE("procgen.e2e.fixedSeedBuildKeyAndPayloadAreStable") { - const auto first = eve::procgen::generateHexTerrainArtifact( - hexParams(), artifactId("018f0b7e-6e50-7a10-8c22-2c8f8e3e0106")); - const auto second = eve::procgen::generateHexTerrainArtifact( - hexParams(), artifactId("018f0b7e-6e50-7a10-8c22-2c8f8e3e0107")); + const auto first = + eve::procgen::generateHexTerrainArtifact(hexParams(), artifactId("018f0b7e-6e50-7a10-8c22-2c8f8e3e0106")); + const auto second = + eve::procgen::generateHexTerrainArtifact(hexParams(), artifactId("018f0b7e-6e50-7a10-8c22-2c8f8e3e0107")); REQUIRE(first.ok()); REQUIRE(second.ok()); CHECK_EQ(first.value().buildKey, second.value().buildKey); @@ -245,7 +241,7 @@ TEST_CASE("procgen.e2e.fixedSeedBuildKeyAndPayloadAreStable") { const auto* determinism = first.value().metadata.at("determinism").getIf(); REQUIRE(determinism != nullptr); CHECK_EQ(*determinism, std::string("seeded_cpu")); - const auto& firstComposite = std::get(first.value().payload); + const auto& firstComposite = std::get(first.value().payload); const auto& secondComposite = std::get(second.value().payload); REQUIRE(firstComposite.find("mesh") != nullptr); REQUIRE(secondComposite.find("mesh") != nullptr); diff --git a/test/procgen_generated_artifact.cpp b/test/procgen_generated_artifact.cpp index de9761f9..173e3b6d 100644 --- a/test/procgen_generated_artifact.cpp +++ b/test/procgen_generated_artifact.cpp @@ -33,7 +33,7 @@ TEST_CASE("procgen.generatedArtifact.buildKeyIsCanonicalAndIdentityIsSeparate") second.setInt("width", 48); second.setSeed(17); - const auto firstKey = BuildKey::forRecipe("mesh.hexterrain", first); + const auto firstKey = BuildKey::forRecipe("mesh.hexterrain", first); const auto secondKey = BuildKey::forRecipe("mesh.hexterrain", second); REQUIRE(firstKey.has_value()); REQUIRE(secondKey.has_value()); @@ -67,10 +67,10 @@ TEST_CASE("procgen.generatedArtifact.hexTerrainPublishesCompositeProducts") { const auto& composite = std::get(artifact.payload); REQUIRE_EQ(composite.children.size(), std::size_t(4)); - const ArtifactPart* mesh = composite.find("mesh"); + const ArtifactPart* mesh = composite.find("mesh"); const ArtifactPart* collider = composite.find("collider"); const ArtifactPart* topology = composite.find("topology"); - const ArtifactPart* anchors = composite.find("anchors"); + const ArtifactPart* anchors = composite.find("anchors"); REQUIRE(mesh != nullptr); REQUIRE(collider != nullptr); REQUIRE(topology != nullptr); @@ -89,12 +89,12 @@ TEST_CASE("procgen.generatedArtifact.castlePublishesCompositeProductsAndStorePro params.setInt("detail", 1); params.setInt("seed", 12); - const ArtifactId id = testId(); - auto result = generateCastleArtifact(params, id); + const ArtifactId id = testId(); + auto result = generateCastleArtifact(params, id); REQUIRE(result.ok()); - GeneratedArtifact artifact = std::move(result).takeValue(); - const BuildKey key = artifact.buildKey; - const auto& composite = std::get(artifact.payload); + GeneratedArtifact artifact = std::move(result).takeValue(); + const BuildKey key = artifact.buildKey; + const auto& composite = std::get(artifact.payload); REQUIRE(composite.has("mesh")); REQUIRE(composite.has("collider")); REQUIRE(composite.has("topology")); @@ -105,7 +105,7 @@ TEST_CASE("procgen.generatedArtifact.castlePublishesCompositeProductsAndStorePro CHECK_EQ(std::get(composite.find("anchors")->payload).getCount(), 5); ArtifactStore store; - auto published = store.publish(std::move(artifact)); + auto published = store.publish(std::move(artifact)); REQUIRE(published.ok()); CHECK_EQ(std::move(published).takeValue(), id); CHECK_EQ(store.size(), std::size_t(1)); @@ -113,7 +113,7 @@ TEST_CASE("procgen.generatedArtifact.castlePublishesCompositeProductsAndStorePro CHECK_EQ(store.findByBuildKey(key).size(), std::size_t(1)); Params sameParams = params; - auto duplicate = generateCastleArtifact(sameParams, id); + auto duplicate = generateCastleArtifact(sameParams, id); REQUIRE(duplicate.ok()); auto conflict = store.publish(std::move(duplicate).takeValue()); CHECK(!conflict.ok()); @@ -128,21 +128,21 @@ TEST_CASE("procgen.generatedArtifact.castlePublishesCompositeProductsAndStorePro } TEST_CASE("procgen.generatedArtifact.rejectsInvalidContracts") { - const ArtifactId id = testId(); - auto key = BuildKey::fromCanonical("procgen/v1/test/0000000000000001"); + const ArtifactId id = testId(); + auto key = BuildKey::fromCanonical("procgen/v1/test/0000000000000001"); REQUIRE(key.has_value()); Bounds bounds; bounds.include(0.f, 0.f, 0.f); Grid2D grid; grid.resize(1, 1); - auto nil = makeArtifact(ArtifactId::nil(), ArtifactType::Grid, eve::SchemaVersion(1), *key, - bounds, {}, {}, GeneratedArtifact::Payload(grid)); + auto nil = makeArtifact(ArtifactId::nil(), ArtifactType::Grid, eve::SchemaVersion(1), *key, bounds, {}, {}, + GeneratedArtifact::Payload(grid)); CHECK(!nil.ok()); CHECK_EQ(static_cast(nil.code()), static_cast(eve::StatusCode::Rejected)); - auto mismatch = makeArtifact(id, ArtifactType::MeshData, eve::SchemaVersion(1), *key, - bounds, {}, {}, GeneratedArtifact::Payload(grid)); + auto mismatch = makeArtifact(id, ArtifactType::MeshData, eve::SchemaVersion(1), *key, bounds, {}, {}, + GeneratedArtifact::Payload(grid)); CHECK(!mismatch.ok()); CHECK_EQ(static_cast(mismatch.code()), static_cast(eve::StatusCode::Rejected)); } @@ -161,15 +161,14 @@ TEST_CASE("procgen.meshBuild.checkedGroupRestoreIsAtomic") { auto invalid = mesh.restoreGroupData({""}, {0}, 0); REQUIRE(!invalid.ok()); - CHECK_EQ(static_cast(invalid.error()->code()), - static_cast(eve::DiagnosticCode::ProcgenGroupDataInvalid)); + CHECK_EQ(static_cast(invalid.error()->code()), static_cast(eve::DiagnosticCode::ProcgenGroupDataInvalid)); CHECK_EQ(mesh.getGroupName(0), std::string("main")); CHECK_EQ(mesh.getTriangleGroup(0), 0); } TEST_CASE("procgen.generatedArtifact.denseSnapshotRoundTripsEveryPayloadAndRebuildsIndexes") { const ArtifactId root = testId(); - const auto key = BuildKey::fromCanonical("procgen/v1/roundtrip/0000000000000001"); + const auto key = BuildKey::fromCanonical("procgen/v1/roundtrip/0000000000000001"); REQUIRE(key.has_value()); Bounds rootBounds; @@ -187,7 +186,7 @@ TEST_CASE("procgen.generatedArtifact.denseSnapshotRoundTripsEveryPayloadAndRebui gridBounds.include(0.f, 0.f, 0.f); gridBounds.include(2.f, 0.f, 1.f); - PointSet points; + PointSet points; const int point = points.add(1.f, 2.f, 3.f); points.setNormal(point, .1f, .9f, .2f); points.setYaw(point, .25f); @@ -211,46 +210,42 @@ TEST_CASE("procgen.generatedArtifact.denseSnapshotRoundTripsEveryPayloadAndRebui meshBounds.include(1.f, 1.f, 0.f); ImageData image; - image.width = 2; - image.height = 1; + image.width = 2; + image.height = 1; image.channels = 4; - image.format = "rgba8"; - image.pixels = {1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u}; + image.format = "rgba8"; + image.pixels = {1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u}; Bounds imageBounds; imageBounds.include(0.f, 0.f, 0.f); imageBounds.include(2.f, 1.f, 0.f); Collider collider; collider.vertices = mesh.positions(); - collider.indices = mesh.indices(); - collider.bounds = meshBounds; - collider.shape = "triangle_mesh"; + collider.indices = mesh.indices(); + collider.bounds = meshBounds; + collider.shape = "triangle_mesh"; - auto makePart = [&](std::string role, ArtifactId id, ArtifactType type, Bounds bounds, - ArtifactLeafPayload payload, + auto makePart = [&](std::string role, ArtifactId id, ArtifactType type, Bounds bounds, ArtifactLeafPayload payload, std::vector dependencies = {}) { - return makeArtifactPart(std::move(role), id, type, eve::SchemaVersion(1), *key, - bounds, std::move(dependencies), {}, std::move(payload)); + return makeArtifactPart(std::move(role), id, type, eve::SchemaVersion(1), *key, bounds, std::move(dependencies), + {}, std::move(payload)); }; std::vector children; - auto addPart = [&](auto result) { + auto addPart = [&](auto result) { REQUIRE(result.ok()); children.emplace_back(std::move(result).takeValue()); }; addPart(makePart("grid", root.child("grid"), ArtifactType::Grid, gridBounds, std::move(grid))); - addPart(makePart("points", root.child("points"), ArtifactType::PointSet, pointBounds, - std::move(points))); - addPart(makePart("mesh", root.child("mesh"), ArtifactType::MeshData, meshBounds, - std::move(mesh))); - addPart(makePart("image", root.child("image"), ArtifactType::ImageData, imageBounds, - std::move(image))); - addPart(makePart("collider", root.child("collider"), ArtifactType::Collider, meshBounds, - std::move(collider), {root.child("mesh")})); + addPart(makePart("points", root.child("points"), ArtifactType::PointSet, pointBounds, std::move(points))); + addPart(makePart("mesh", root.child("mesh"), ArtifactType::MeshData, meshBounds, std::move(mesh))); + addPart(makePart("image", root.child("image"), ArtifactType::ImageData, imageBounds, std::move(image))); + addPart(makePart("collider", root.child("collider"), ArtifactType::Collider, meshBounds, std::move(collider), + {root.child("mesh")})); CompositeArtifact composite; composite.children = std::move(children); - auto generated = makeArtifact(root, ArtifactType::Composite, eve::SchemaVersion(1), *key, - rootBounds, {}, {}, std::move(composite)); + auto generated = makeArtifact(root, ArtifactType::Composite, eve::SchemaVersion(1), *key, rootBounds, {}, {}, + std::move(composite)); REQUIRE(generated.ok()); ArtifactStore source; @@ -271,10 +266,8 @@ TEST_CASE("procgen.generatedArtifact.denseSnapshotRoundTripsEveryPayloadAndRebui CHECK(restored.findPart(root.child("image")) != nullptr); CHECK(restored.findPart(root.child("collider")) != nullptr); REQUIRE(restored.typeOf(root.child("mesh")).has_value()); - CHECK_EQ(static_cast(*restored.typeOf(root.child("mesh"))), - static_cast(ArtifactType::MeshData)); + CHECK_EQ(static_cast(*restored.typeOf(root.child("mesh"))), static_cast(ArtifactType::MeshData)); const auto* restoredMesh = restored.findPart(root.child("mesh")); REQUIRE(restoredMesh != nullptr); - CHECK_EQ(std::get(restoredMesh->payload).groupNames(), - std::vector{"main"}); + CHECK_EQ(std::get(restoredMesh->payload).groupNames(), std::vector{"main"}); } diff --git a/test/procgen_lsystem.cpp b/test/procgen_lsystem.cpp index 6c8c574c..b24ff502 100644 --- a/test/procgen_lsystem.cpp +++ b/test/procgen_lsystem.cpp @@ -44,12 +44,12 @@ TEST_CASE("procgen.lsystem.renderDump") { params.setFloat("leafSize", 0.5f); Procgen generator; - auto paramsResult = Procgen::newParamsHandle(); + auto paramsResult = Procgen::newParamsHandle(); REQUIRE(paramsResult.ok()); auto paramsHandle = std::move(paramsResult).takeValue(); - auto paramsView = Procgen::resolve(paramsHandle); + auto paramsView = Procgen::resolve(paramsHandle); REQUIRE(paramsView.isBound()); - *paramsView = params; + *paramsView = params; auto lsMeshView = generator.generateMeshBorrowed("mesh.lsystem", paramsHandle, gfx); REQUIRE(lsMeshView.isBound()); Mesh *lsMesh = lsMeshView.get(); @@ -175,18 +175,18 @@ TEST_CASE("procgen.lsystem.meshRecipeIsDeterministic") { auto p2Result = Procgen::newParamsHandle(); REQUIRE(p1Result.ok()); REQUIRE(p2Result.ok()); - auto p1Handle = std::move(p1Result).takeValue(); - auto p2Handle = std::move(p2Result).takeValue(); + auto p1Handle = std::move(p1Result).takeValue(); + auto p2Handle = std::move(p2Result).takeValue(); *Procgen::resolve(p1Handle) = p1; *Procgen::resolve(p2Handle) = p2; - auto m1Result = proc.buildMeshHandle("mesh.lsystem", p1Handle); - auto m2Result = proc.buildMeshHandle("mesh.lsystem", p2Handle); + auto m1Result = proc.buildMeshHandle("mesh.lsystem", p1Handle); + auto m2Result = proc.buildMeshHandle("mesh.lsystem", p2Handle); REQUIRE(m1Result.ok()); REQUIRE(m2Result.ok()); auto m1Handle = std::move(m1Result).takeValue(); auto m2Handle = std::move(m2Result).takeValue(); - auto m1 = proc.resolveMeshBuild(m1Handle); - auto m2 = proc.resolveMeshBuild(m2Handle); + auto m1 = proc.resolveMeshBuild(m1Handle); + auto m2 = proc.resolveMeshBuild(m2Handle); REQUIRE(m1.isBound()); REQUIRE(m2.isBound()); CHECK(m1->getVertexCount() > 0); @@ -206,9 +206,9 @@ TEST_CASE("procgen.lsystem.meshRecipeReportsErrors") { params.setString("style", "bogus"); auto paramsResult = Procgen::newParamsHandle(); REQUIRE(paramsResult.ok()); - auto paramsHandle = std::move(paramsResult).takeValue(); + auto paramsHandle = std::move(paramsResult).takeValue(); *Procgen::resolve(paramsHandle) = params; - auto meshResult = proc.buildMeshHandle("mesh.lsystem", paramsHandle); + auto meshResult = proc.buildMeshHandle("mesh.lsystem", paramsHandle); REQUIRE(!meshResult.ok()); CHECK(meshResult.status().describe().find("unknown style") != std::string::npos); REQUIRE(Procgen::release(paramsHandle).ok()); @@ -216,10 +216,10 @@ TEST_CASE("procgen.lsystem.meshRecipeReportsErrors") { TEST_CASE("procgen.lsystem.scriptFacade") { Procgen proc; - auto result = proc.newLSystemHandle(); + auto result = proc.newLSystemHandle(); REQUIRE(result.ok()); auto handle = std::move(result).takeValue(); - auto ls = proc.resolveLSystem(handle); + auto ls = proc.resolveLSystem(handle); REQUIRE(ls.isBound()); ls->setAxiom("F"); ls->addRule('F', "F[+F]F[-F]F"); diff --git a/test/procgen_pointset.cpp b/test/procgen_pointset.cpp index 62e5a960..28415374 100644 --- a/test/procgen_pointset.cpp +++ b/test/procgen_pointset.cpp @@ -14,16 +14,15 @@ using ContextHandle = ProcgenContextHandleRef; /** @brief Owns one module point-set handle for the duration of a test. */ struct PointSetLease { - Procgen* owner = nullptr; + Procgen* owner = nullptr; PointSetHandle handle{}; PointSetLease() = default; PointSetLease(Procgen& proc, PointSetHandle value) : owner(&proc), handle(value) {} PointSetLease(const PointSetLease&) = delete; PointSetLease& operator=(const PointSetLease&) = delete; - PointSetLease(PointSetLease&& other) noexcept - : owner(other.owner), handle(other.handle) { - other.owner = nullptr; + PointSetLease(PointSetLease&& other) noexcept : owner(other.owner), handle(other.handle) { + other.owner = nullptr; other.handle = {}; } PointSetLease& operator=(PointSetLease&& other) noexcept { @@ -64,16 +63,15 @@ PointSetLease requirePointSet(Procgen& proc, eve::Result&& resul /** @brief Converts a successful rebuild-context allocation into a test lease. */ struct ContextLease { - Procgen* owner = nullptr; + Procgen* owner = nullptr; ContextHandle handle{}; ContextLease() = default; ContextLease(Procgen& proc, ContextHandle value) : owner(&proc), handle(value) {} ContextLease(const ContextLease&) = delete; ContextLease& operator=(const ContextLease&) = delete; - ContextLease(ContextLease&& other) noexcept - : owner(other.owner), handle(other.handle) { - other.owner = nullptr; + ContextLease(ContextLease&& other) noexcept : owner(other.owner), handle(other.handle) { + other.owner = nullptr; other.handle = {}; } ContextLease& operator=(ContextLease&& other) noexcept { @@ -96,8 +94,7 @@ struct ContextLease { } [[nodiscard]] eve::script::Borrowed view() const noexcept { - return owner ? Procgen::resolve(handle) - : eve::script::Borrowed(); + return owner ? Procgen::resolve(handle) : eve::script::Borrowed(); } }; @@ -119,8 +116,7 @@ void checkResult(const eve::Result& result, eve::DiagnosticCode expected = ev if (!ok) { const eve::Diagnostic* diagnostic = result.error(); REQUIRE(diagnostic != nullptr); - if (diagnostic && expected != eve::DiagnosticCode::None) - CHECK_EQ(diagnostic->code(), expected); + if (diagnostic && expected != eve::DiagnosticCode::None) CHECK_EQ(diagnostic->code(), expected); } } @@ -132,7 +128,7 @@ TEST_CASE("procgen.pointSet.composableDeterministicPipeline") { CHECK_NE(forestSeed, proc.deriveSeed(42, "rocks")); CHECK_EQ(forestSeed, proc.deriveSeed(42, "forest")); - auto source = requirePointSet(proc, proc.sampleGridHandle(8, 6, 2.f, forestSeed, 0.f)); + auto source = requirePointSet(proc, proc.sampleGridHandle(8, 6, 2.f, forestSeed, 0.f)); auto sourceView = source.view(); REQUIRE(sourceView.isBound()); PointSet* sourcePtr = sourceView.get(); @@ -146,30 +142,30 @@ TEST_CASE("procgen.pointSet.composableDeterministicPipeline") { sourcePtr->setStringAttribute(i, "biome", "forest"); } - auto height = requirePointSet(proc, proc.filterHeightHandle(source.handle, 1.f, 2.f)); + auto height = requirePointSet(proc, proc.filterHeightHandle(source.handle, 1.f, 2.f)); auto heightView = height.view(); REQUIRE(heightView.isBound()); CHECK_EQ(heightView->getCount(), 24); - auto dense = requirePointSet(proc, proc.filterDensityHandle(height.handle, 0.5f, 1.f)); + auto dense = requirePointSet(proc, proc.filterDensityHandle(height.handle, 0.5f, 1.f)); auto denseView = dense.view(); REQUIRE(denseView.isBound()); CHECK(denseView->getCount() < heightView->getCount()); CHECK(denseView->getCount() > 0); CHECK_EQ(denseView->getStringAttribute(0, "biome", ""), std::string("forest")); - auto excluded = requirePointSet(proc, proc.excludeRadiusHandle(dense.handle, 0.f, 0.f, 3.f)); + auto excluded = requirePointSet(proc, proc.excludeRadiusHandle(dense.handle, 0.f, 0.f, 3.f)); auto excludedView = excluded.view(); REQUIRE(excludedView.isBound()); - auto pruned = requirePointSet(proc, proc.selfPruneHandle(excluded.handle, 2.1f)); + auto pruned = requirePointSet(proc, proc.selfPruneHandle(excluded.handle, 2.1f)); auto prunedView = pruned.view(); REQUIRE(prunedView.isBound()); CHECK(prunedView->getCount() <= excludedView->getCount()); - const uint32_t jitterSeed = proc.deriveSeed(forestSeed, "jitter"); - auto jitterA = requirePointSet(proc, proc.jitterPointsHandle(pruned.handle, jitterSeed, 0.5f, 0.5f)); - auto jitterB = requirePointSet(proc, proc.jitterPointsHandle(pruned.handle, jitterSeed, 0.5f, 0.5f)); - auto jitterAView = jitterA.view(); - auto jitterBView = jitterB.view(); + const uint32_t jitterSeed = proc.deriveSeed(forestSeed, "jitter"); + auto jitterA = requirePointSet(proc, proc.jitterPointsHandle(pruned.handle, jitterSeed, 0.5f, 0.5f)); + auto jitterB = requirePointSet(proc, proc.jitterPointsHandle(pruned.handle, jitterSeed, 0.5f, 0.5f)); + auto jitterAView = jitterA.view(); + auto jitterBView = jitterB.view(); REQUIRE(jitterAView.isBound()); REQUIRE(jitterBView.isBound()); CHECK_EQ(jitterAView->getCount(), jitterBView->getCount()); @@ -181,7 +177,7 @@ TEST_CASE("procgen.pointSet.composableDeterministicPipeline") { TEST_CASE("procgen.pointSet.invalidInputIsReported") { Procgen proc; - auto failed = proc.filterHeightHandle({}, 0.f, 1.f); + auto failed = proc.filterHeightHandle({}, 0.f, 1.f); CHECK(!failed.ok()); const eve::Diagnostic* diagnostic = failed.error(); REQUIRE(diagnostic != nullptr); @@ -197,10 +193,10 @@ TEST_CASE("procgen.pointSet.spatialFiltersAndHeightmapProjection") { auto sourceResult = proc.newPointSetHandle(); auto sourceHandle = std::move(sourceResult).takeValue(); - auto sourceView = proc.resolvePointSet(sourceHandle); + auto sourceView = proc.resolvePointSet(sourceHandle); REQUIRE(sourceView.isBound()); - *sourceView = source; - auto inside = requirePointSet(proc, proc.filterBoxHandle(sourceHandle, 0.5f, -1.f, 0.5f, 1.5f, 1.f, 1.5f)); + *sourceView = source; + auto inside = requirePointSet(proc, proc.filterBoxHandle(sourceHandle, 0.5f, -1.f, 0.5f, 1.5f, 1.f, 1.5f)); auto insideView = inside.view(); REQUIRE(insideView.isBound()); CHECK_EQ(insideView->getCount(), 1); @@ -215,12 +211,12 @@ TEST_CASE("procgen.pointSet.spatialFiltersAndHeightmapProjection") { auto heightmapResult = proc.newHeightmapHandle(3, 3); REQUIRE(heightmapResult.ok()); auto heightmapHandle = std::move(heightmapResult).takeValue(); - auto heightmapView = proc.resolveHeightmap(heightmapHandle); + auto heightmapView = proc.resolveHeightmap(heightmapHandle); REQUIRE(heightmapView.isBound()); for (int z = 0; z < 3; ++z) for (int x = 0; x < 3; ++x) heightmapView->setHeight(x, z, float(x)); - auto projected = requirePointSet(proc, proc.projectToHeightmapHandle( - sourceHandle, heightmapHandle, 0.f, 0.f, 1.f, 2.f)); + auto projected = + requirePointSet(proc, proc.projectToHeightmapHandle(sourceHandle, heightmapHandle, 0.f, 0.f, 1.f, 2.f)); auto projectedView = projected.view(); REQUIRE(projectedView.isBound()); CHECK_EQ(projectedView->getY(0), 0.f); @@ -229,11 +225,11 @@ TEST_CASE("procgen.pointSet.spatialFiltersAndHeightmapProjection") { CHECK(projectedView->getNormalX(1) < 0.f); CHECK(projectedView->getNormalY(1) > 0.f); - auto gentle = requirePointSet(proc, proc.filterSlopeHandle(projected.handle, 0.f, 80.f)); + auto gentle = requirePointSet(proc, proc.filterSlopeHandle(projected.handle, 0.f, 80.f)); auto gentleView = gentle.view(); REQUIRE(gentleView.isBound()); CHECK_EQ(gentleView->getCount(), 3); - auto flat = requirePointSet(proc, proc.filterSlopeHandle(projected.handle, 0.f, 1.f)); + auto flat = requirePointSet(proc, proc.filterSlopeHandle(projected.handle, 0.f, 1.f)); auto flatView = flat.view(); REQUIRE(flatView.isBound()); CHECK_EQ(flatView->getCount(), 0); @@ -257,18 +253,18 @@ TEST_CASE("procgen.pointSet.polygonAndSplineQueries") { auto polygonResult = proc.newPointSetHandle(); REQUIRE(polygonResult.ok()); auto polygonHandle = std::move(polygonResult).takeValue(); - auto polygonView = proc.resolvePointSet(polygonHandle); + auto polygonView = proc.resolvePointSet(polygonHandle); REQUIRE(polygonView.isBound()); - *polygonView = polygon; + *polygonView = polygon; auto candidatesResult = proc.newPointSetHandle(); REQUIRE(candidatesResult.ok()); auto candidatesHandle = std::move(candidatesResult).takeValue(); - auto candidatesView = proc.resolvePointSet(candidatesHandle); + auto candidatesView = proc.resolvePointSet(candidatesHandle); REQUIRE(candidatesView.isBound()); - *candidatesView = candidates; - auto inside = requirePointSet(proc, proc.filterPolygonHandle(candidatesHandle, polygonHandle)); - auto outside = requirePointSet(proc, proc.filterPolygonHandle(candidatesHandle, polygonHandle, true)); - auto insideView = inside.view(); + *candidatesView = candidates; + auto inside = requirePointSet(proc, proc.filterPolygonHandle(candidatesHandle, polygonHandle)); + auto outside = requirePointSet(proc, proc.filterPolygonHandle(candidatesHandle, polygonHandle, true)); + auto insideView = inside.view(); auto outsideView = outside.view(); REQUIRE(insideView.isBound()); REQUIRE(outsideView.isBound()); @@ -284,10 +280,10 @@ TEST_CASE("procgen.pointSet.polygonAndSplineQueries") { auto controlResult = proc.newPointSetHandle(); REQUIRE(controlResult.ok()); auto controlHandle = std::move(controlResult).takeValue(); - auto controlView = proc.resolvePointSet(controlHandle); + auto controlView = proc.resolvePointSet(controlHandle); REQUIRE(controlView.isBound()); - *controlView = control; - auto samples = requirePointSet(proc, proc.sampleSplineHandle(controlHandle, 5.f, 42, 0.f)); + *controlView = control; + auto samples = requirePointSet(proc, proc.sampleSplineHandle(controlHandle, 5.f, 42, 0.f)); auto samplesView = samples.view(); REQUIRE(samplesView.isBound()); CHECK_EQ(samplesView->getCount(), 5); @@ -303,11 +299,10 @@ TEST_CASE("procgen.pointSet.polygonAndSplineQueries") { auto nearbyResult = proc.newPointSetHandle(); REQUIRE(nearbyResult.ok()); auto nearbyHandle = std::move(nearbyResult).takeValue(); - auto nearbyView = proc.resolvePointSet(nearbyHandle); + auto nearbyView = proc.resolvePointSet(nearbyHandle); REQUIRE(nearbyView.isBound()); - *nearbyView = nearby; - auto corridor = requirePointSet(proc, proc.filterSplineDistanceHandle( - nearbyHandle, controlHandle, 0.f, 2.1f)); + *nearbyView = nearby; + auto corridor = requirePointSet(proc, proc.filterSplineDistanceHandle(nearbyHandle, controlHandle, 0.f, 2.1f)); auto corridorView = corridor.view(); REQUIRE(corridorView.isBound()); CHECK_EQ(corridorView->getCount(), 2); @@ -328,7 +323,7 @@ TEST_CASE("procgen.system.commitIsAtomicAndFailureKeepsPreviousSnapshot") { PointSet first; first.add(1.f, 2.f, 3.f); - auto initial = requireContext(proc, Procgen::beginSystemHandle("forest", 42)); + auto initial = requireContext(proc, Procgen::beginSystemHandle("forest", 42)); auto initialView = initial.view(); REQUIRE(initialView.isBound()); CHECK(initialView->publish("trees", &first)); @@ -343,11 +338,11 @@ TEST_CASE("procgen.system.commitIsAtomicAndFailureKeepsPreviousSnapshot") { CHECK_EQ(proc.getSystemDebugStageCount("forest"), 1); CHECK_EQ(proc.getSystemDebugStageName("forest", 0), std::string("candidates")); - auto debug = requirePointSet(proc, proc.getSystemDebugStageHandle("forest", "candidates")); + auto debug = requirePointSet(proc, proc.getSystemDebugStageHandle("forest", "candidates")); auto debugView = debug.view(); REQUIRE(debugView.isBound()); CHECK_EQ(debugView->getCount(), 1); - auto committed = requirePointSet(proc, proc.getSystemOutputHandle("forest", "trees")); + auto committed = requirePointSet(proc, proc.getSystemOutputHandle("forest", "trees")); auto committedView = committed.view(); REQUIRE(committedView.isBound()); CHECK_EQ(committedView->getCount(), 1); @@ -355,7 +350,7 @@ TEST_CASE("procgen.system.commitIsAtomicAndFailureKeepsPreviousSnapshot") { PointSet replacement; replacement.add(9.f, 9.f, 9.f); replacement.add(8.f, 8.f, 8.f); - auto failed = requireContext(proc, Procgen::beginSystemHandle("forest", 99)); + auto failed = requireContext(proc, Procgen::beginSystemHandle("forest", 99)); auto failedView = failed.view(); REQUIRE(failedView.isBound()); CHECK(failedView->publish("trees", &replacement)); @@ -369,7 +364,7 @@ TEST_CASE("procgen.system.commitIsAtomicAndFailureKeepsPreviousSnapshot") { CHECK_EQ(proc.getSystemSeed("forest"), uint32_t(42)); CHECK_EQ(proc.getSystemDebugStageCount("forest"), 1); - auto committedAgain = requirePointSet(proc, proc.getSystemOutputHandle("forest", "trees")); + auto committedAgain = requirePointSet(proc, proc.getSystemOutputHandle("forest", "trees")); auto committedAgainView = committedAgain.view(); REQUIRE(committedAgainView.isBound()); CHECK_EQ(committedAgainView->getCount(), 1); @@ -381,7 +376,7 @@ TEST_CASE("procgen.system.cachedBuildReusesCommittedSnapshot") { PointSet points; points.add(1.f, 0.f, 2.f); - auto miss = requireContext(proc, Procgen::beginCachedSystemHandle("rocks", 7, "layout-v1:size=32")); + auto miss = requireContext(proc, Procgen::beginCachedSystemHandle("rocks", 7, "layout-v1:size=32")); auto missView = miss.view(); REQUIRE(missView.isBound()); CHECK(!missView->isCacheHit()); @@ -393,14 +388,14 @@ TEST_CASE("procgen.system.cachedBuildReusesCommittedSnapshot") { CHECK_EQ(proc.getSystemRevision("rocks"), uint64_t(1)); CHECK_EQ(proc.getSystemBuildKey("rocks"), std::string("layout-v1:size=32")); - auto hit = requireContext(proc, Procgen::beginCachedSystemHandle("rocks", 7, "layout-v1:size=32")); + auto hit = requireContext(proc, Procgen::beginCachedSystemHandle("rocks", 7, "layout-v1:size=32")); auto hitView = hit.view(); REQUIRE(hitView.isBound()); CHECK(hitView->isCacheHit()); CHECK(!hitView->isActive()); CHECK_EQ(proc.getSystemRevision("rocks"), uint64_t(1)); - auto changed = requireContext(proc, Procgen::beginCachedSystemHandle("rocks", 7, "layout-v1:size=64")); + auto changed = requireContext(proc, Procgen::beginCachedSystemHandle("rocks", 7, "layout-v1:size=64")); auto changedView = changed.view(); REQUIRE(changedView.isBound()); CHECK(!changedView->isCacheHit()); @@ -412,7 +407,7 @@ TEST_CASE("procgen.system.stageCacheIsTransactional") { PointSet first; first.add(3.f, 0.f, 4.f); - auto initial = requireContext(proc, Procgen::beginSystemHandle("village", 11)); + auto initial = requireContext(proc, Procgen::beginSystemHandle("village", 11)); auto initialView = initial.view(); REQUIRE(initialView.isBound()); CHECK(initialView->cacheStage("lots", "lots-v1:size=16", &first)); @@ -420,7 +415,7 @@ TEST_CASE("procgen.system.stageCacheIsTransactional") { auto initialCommit = proc.commitSystem(initial.handle); CHECK(initialCommit.ok()); - auto rebuild = requireContext(proc, Procgen::beginSystemHandle("village", 11)); + auto rebuild = requireContext(proc, Procgen::beginSystemHandle("village", 11)); auto rebuildView = rebuild.view(); REQUIRE(rebuildView.isBound()); PointSet* reused = rebuildView->reuseStage("lots", "lots-v1:size=16"); @@ -438,7 +433,7 @@ TEST_CASE("procgen.system.stageCacheIsTransactional") { auto rebuildCommit = proc.commitSystem(rebuild.handle); CHECK(!rebuildCommit.ok()); - auto retry = requireContext(proc, Procgen::beginSystemHandle("village", 11)); + auto retry = requireContext(proc, Procgen::beginSystemHandle("village", 11)); auto retryView = retry.view(); REQUIRE(retryView.isBound()); PointSet* preserved = retryView->reuseStage("lots", "lots-v1:size=16"); @@ -452,7 +447,7 @@ TEST_CASE("procgen.system.automaticTraceTimingIsBalanced") { PointSet points; points.add(1.f, 0.f, 1.f); - auto measured = requireContext(proc, Procgen::beginSystemHandle("timed", 5)); + auto measured = requireContext(proc, Procgen::beginSystemHandle("timed", 5)); auto measuredView = measured.view(); REQUIRE(measuredView.isBound()); CHECK(measuredView->beginTrace("outer", 0)); @@ -468,7 +463,7 @@ TEST_CASE("procgen.system.automaticTraceTimingIsBalanced") { auto measuredCommit = proc.commitSystem(measured.handle); CHECK(measuredCommit.ok()); - auto unfinished = requireContext(proc, Procgen::beginSystemHandle("timed", 5)); + auto unfinished = requireContext(proc, Procgen::beginSystemHandle("timed", 5)); auto unfinishedView = unfinished.view(); REQUIRE(unfinishedView.isBound()); CHECK(unfinishedView->beginTrace("forgotten", 1)); @@ -480,7 +475,7 @@ TEST_CASE("procgen.system.automaticTraceTimingIsBalanced") { CHECK_EQ(unfinishedDiagnostic->code(), eve::DiagnosticCode::PreconditionViolation); CHECK_EQ(proc.getSystemRevision("timed"), uint64_t(1)); - auto unmatched = requireContext(proc, Procgen::beginSystemHandle("unmatched", 5)); + auto unmatched = requireContext(proc, Procgen::beginSystemHandle("unmatched", 5)); auto unmatchedView = unmatched.view(); REQUIRE(unmatchedView.isBound()); CHECK(!unmatchedView->endTrace(0)); @@ -491,7 +486,7 @@ TEST_CASE("procgen.system.previousRevisionSupportsHotReloadDiffs") { Procgen& proc = *Procgen::create(); PointSet first; first.add(1.f, 0.f, 1.f); - auto revision1 = requireContext(proc, Procgen::beginSystemHandle("diff", 1)); + auto revision1 = requireContext(proc, Procgen::beginSystemHandle("diff", 1)); auto revision1View = revision1.view(); REQUIRE(revision1View.isBound()); CHECK(revision1View->captureDebug("trees", &first)); @@ -506,7 +501,7 @@ TEST_CASE("procgen.system.previousRevisionSupportsHotReloadDiffs") { PointSet second = first; second.add(2.f, 0.f, 2.f); - auto revision2 = requireContext(proc, Procgen::beginSystemHandle("diff", 2)); + auto revision2 = requireContext(proc, Procgen::beginSystemHandle("diff", 2)); auto revision2View = revision2.view(); REQUIRE(revision2View.isBound()); CHECK(revision2View->captureDebug("trees", &second)); @@ -514,7 +509,7 @@ TEST_CASE("procgen.system.previousRevisionSupportsHotReloadDiffs") { auto revision2Commit = proc.commitSystem(revision2.handle); CHECK(revision2Commit.ok()); CHECK_EQ(proc.getPreviousSystemRevision("diff"), uint64_t(1)); - auto previousTrees = requirePointSet(proc, proc.getPreviousSystemDebugStageHandle("diff", "trees")); + auto previousTrees = requirePointSet(proc, proc.getPreviousSystemDebugStageHandle("diff", "trees")); auto previousTreesView = previousTrees.view(); REQUIRE(previousTreesView.isBound()); CHECK_EQ(previousTreesView->getCount(), 1); @@ -523,7 +518,7 @@ TEST_CASE("procgen.system.previousRevisionSupportsHotReloadDiffs") { CHECK(report.find("debug trees points=2 delta=+1") != std::string::npos); CHECK(report.find("debug roads points=1 delta=+1") != std::string::npos); - auto failed = requireContext(proc, Procgen::beginSystemHandle("diff", 3)); + auto failed = requireContext(proc, Procgen::beginSystemHandle("diff", 3)); auto failedView = failed.view(); REQUIRE(failedView.isBound()); failedView->fail("broken reload"); @@ -533,5 +528,4 @@ TEST_CASE("procgen.system.previousRevisionSupportsHotReloadDiffs") { auto removeResult = proc.removeSystem("diff"); CHECK(removeResult.ok()); CHECK_EQ(proc.getPreviousSystemRevision("diff"), uint64_t(0)); - } diff --git a/test/procgen_poisson.cpp b/test/procgen_poisson.cpp index 62f68e7e..cf780968 100644 --- a/test/procgen_poisson.cpp +++ b/test/procgen_poisson.cpp @@ -15,22 +15,20 @@ namespace { class PointSetLease { public: PointSetLease() = default; - PointSetLease(Procgen& owner, ProcgenPointSetHandleRef handle) - : owner_(&owner), handle_(handle) {} + PointSetLease(Procgen& owner, ProcgenPointSetHandleRef handle) : owner_(&owner), handle_(handle) {} PointSetLease(const PointSetLease&) = delete; PointSetLease& operator=(const PointSetLease&) = delete; - PointSetLease(PointSetLease&& other) noexcept - : owner_(other.owner_), handle_(other.handle_) { + PointSetLease(PointSetLease&& other) noexcept : owner_(other.owner_), handle_(other.handle_) { other.owner_ = nullptr; other.handle_ = {}; } PointSetLease& operator=(PointSetLease&& other) noexcept { if (this == &other) return *this; reset(); - owner_ = other.owner_; - handle_ = other.handle_; - other.owner_ = nullptr; + owner_ = other.owner_; + handle_ = other.handle_; + other.owner_ = nullptr; other.handle_ = {}; return *this; } @@ -47,18 +45,16 @@ public: /** @brief Resolves the handle for the duration of the current test operation. */ [[nodiscard]] eve::script::Borrowed view() const noexcept { - return owner_ ? owner_->resolvePointSet(handle_) - : eve::script::Borrowed(); + return owner_ ? owner_->resolvePointSet(handle_) : eve::script::Borrowed(); } private: - Procgen* owner_ = nullptr; - ProcgenPointSetHandleRef handle_{}; + Procgen* owner_ = nullptr; + ProcgenPointSetHandleRef handle_{}; }; /** @brief Converts a checked point-set allocation Result into a test lease. */ -PointSetLease requirePointSet(Procgen& proc, - eve::Result&& result) { +PointSetLease requirePointSet(Procgen& proc, eve::Result&& result) { const bool ok = result.ok(); if (!ok) { const eve::Diagnostic* diagnostic = result.error(); @@ -73,9 +69,9 @@ PointSetLease requirePointSet(Procgen& proc, TEST_CASE("procgen.poisson.deterministicAndSeeded") { Procgen proc; - auto a = requirePointSet(proc, proc.poissonDiskHandle(40, 30, 2.5f, 99, 300)); - auto b = requirePointSet(proc, proc.poissonDiskHandle(40, 30, 2.5f, 99, 300)); - auto c = requirePointSet(proc, proc.poissonDiskHandle(40, 30, 2.5f, 100, 300)); + auto a = requirePointSet(proc, proc.poissonDiskHandle(40, 30, 2.5f, 99, 300)); + auto b = requirePointSet(proc, proc.poissonDiskHandle(40, 30, 2.5f, 99, 300)); + auto c = requirePointSet(proc, proc.poissonDiskHandle(40, 30, 2.5f, 100, 300)); auto aView = a.view(); auto bView = b.view(); auto cView = c.view(); @@ -110,12 +106,12 @@ TEST_CASE("procgen.poisson.respectsMinSpacing") { TEST_CASE("procgen.poisson.maxPointsCapsOutput") { Procgen proc; - auto capped = requirePointSet(proc, proc.poissonDiskHandle(100, 100, 0.5f, 3, 50)); + auto capped = requirePointSet(proc, proc.poissonDiskHandle(100, 100, 0.5f, 3, 50)); auto cappedView = capped.view(); REQUIRE(cappedView.isBound()); CHECK(cappedView->getCount() <= 50); - auto tiny = requirePointSet(proc, proc.poissonDiskHandle(0, 0, 1.f, 3, 100)); + auto tiny = requirePointSet(proc, proc.poissonDiskHandle(0, 0, 1.f, 3, 100)); auto tinyView = tiny.view(); REQUIRE(tinyView.isBound()); CHECK_EQ(tinyView->getCount(), 0); diff --git a/test/procgen_runtime_generation.cpp b/test/procgen_runtime_generation.cpp index 643085b7..2823ee88 100644 --- a/test/procgen_runtime_generation.cpp +++ b/test/procgen_runtime_generation.cpp @@ -1,5 +1,5 @@ -#include "procgen/RuntimeGeneration.h" #include "procgen/Procgen.h" +#include "procgen/RuntimeGeneration.h" #include "zeroerr/assert.h" #include "zeroerr/unittest.h" @@ -15,22 +15,20 @@ namespace { class RuntimeLease { public: RuntimeLease() = default; - RuntimeLease(Procgen& owner, ProcgenRuntimeGenerationHandleRef handle) - : owner_(&owner), handle_(handle) {} + RuntimeLease(Procgen& owner, ProcgenRuntimeGenerationHandleRef handle) : owner_(&owner), handle_(handle) {} RuntimeLease(const RuntimeLease&) = delete; RuntimeLease& operator=(const RuntimeLease&) = delete; - RuntimeLease(RuntimeLease&& other) noexcept - : owner_(other.owner_), handle_(other.handle_) { + RuntimeLease(RuntimeLease&& other) noexcept : owner_(other.owner_), handle_(other.handle_) { other.owner_ = nullptr; other.handle_ = {}; } RuntimeLease& operator=(RuntimeLease&& other) noexcept { if (this == &other) return *this; reset(); - owner_ = other.owner_; - handle_ = other.handle_; - other.owner_ = nullptr; + owner_ = other.owner_; + handle_ = other.handle_; + other.owner_ = nullptr; other.handle_ = {}; return *this; } @@ -47,24 +45,21 @@ public: /** @brief Provides a short-lived borrowed view of the scheduler. */ [[nodiscard]] eve::script::Borrowed view() const noexcept { - return owner_ ? owner_->resolveRuntimeGeneration(handle_) - : eve::script::Borrowed(); + return owner_ ? owner_->resolveRuntimeGeneration(handle_) : eve::script::Borrowed(); } /** @brief Preserves the concise `runtime->operation()` test syntax. */ - [[nodiscard]] eve::script::Borrowed operator->() const noexcept { - return view(); - } + [[nodiscard]] eve::script::Borrowed operator->() const noexcept { return view(); } private: - Procgen* owner_ = nullptr; + Procgen* owner_ = nullptr; ProcgenRuntimeGenerationHandleRef handle_{}; }; /** @brief Converts a checked scheduler allocation Result into a test lease. */ RuntimeLease requireRuntime(Procgen& proc, uint32_t worldSeed) { - auto result = proc.newRuntimeGenerationHandle(worldSeed); - const bool ok = result.ok(); + auto result = proc.newRuntimeGenerationHandle(worldSeed); + const bool ok = result.ok(); if (!ok) { const eve::Diagnostic* diagnostic = result.error(); REQUIRE(diagnostic != nullptr); @@ -76,15 +71,13 @@ RuntimeLease requireRuntime(Procgen& proc, uint32_t worldSeed) { /** @brief Gives an owning lifetime to a request allocated by the scheduler API. */ using RequestLease = std::unique_ptr; -[[nodiscard]] RequestLease ownRequest(ProcgenCellRequest* request) noexcept { - return RequestLease(request); -} +[[nodiscard]] RequestLease ownRequest(ProcgenCellRequest* request) noexcept { return RequestLease(request); } } // namespace TEST_CASE("procgen.runtimeGeneration.partitionsAndPublishesCells") { - Procgen proc; - auto runtime = requireRuntime(proc, 42); + Procgen proc; + auto runtime = requireRuntime(proc, 42); const int nearLevel = runtime->addLevel(10.f, 8.f, 1.5f); const int farLevel = runtime->addLevel(40.f, 30.f, 2.f); CHECK_EQ(nearLevel, 0); @@ -269,7 +262,7 @@ TEST_CASE("procgen.runtimeGeneration.enforcesResidentCellReservations") { CHECK_EQ(runtime->getMaxActiveCells(), 2); runtime->updateSource(5.f, 5.f, 1.f, 0.f); - auto first = ownRequest(runtime->nextGenerate()); + auto first = ownRequest(runtime->nextGenerate()); auto second = ownRequest(runtime->nextGenerate()); REQUIRE(bool(first)); REQUIRE(bool(second)); diff --git a/test/procgen_scene_sink.cpp b/test/procgen_scene_sink.cpp index 351cfa12..7c5c89d2 100644 --- a/test/procgen_scene_sink.cpp +++ b/test/procgen_scene_sink.cpp @@ -110,8 +110,7 @@ TEST_CASE("procgen.sceneSink.publishesStableAttributedInstances") { reorderedView->setPointSeed(0, 99); reorderedView->points().push_back(pointsView->points()[0]); reorderedView->points().push_back(pointsView->points()[1]); - auto reorderedPublishResult = - proc.publishInstances("forest/main", reorderedHandle, "asset", "granite"); + auto reorderedPublishResult = proc.publishInstances("forest/main", reorderedHandle, "asset", "granite"); REQUIRE(reorderedPublishResult.ok()); CHECK_EQ(proc.getPublishedCreatedCount("forest/main"), 1); CHECK_EQ(proc.getPublishedReusedCount("forest/main"), 2); @@ -122,8 +121,7 @@ TEST_CASE("procgen.sceneSink.publishesStableAttributedInstances") { reorderedView->setStringAttribute(0, "instanceId", "hero-tree"); reorderedView->setStringAttribute(1, "instanceId", "hero-tree"); - auto duplicateResult = - proc.publishInstances("forest/main", reorderedHandle, "asset", "granite"); + auto duplicateResult = proc.publishInstances("forest/main", reorderedHandle, "asset", "granite"); REQUIRE(!duplicateResult.ok()); CHECK(duplicateResult.status().describe().find("duplicate instance id") != std::string::npos); CHECK_EQ(proc.getPublishedInstanceCount("forest/main"), 3); diff --git a/test/procgen_simulation.cpp b/test/procgen_simulation.cpp index befd7957..803572f1 100644 --- a/test/procgen_simulation.cpp +++ b/test/procgen_simulation.cpp @@ -67,13 +67,13 @@ struct ParamsLease { ProcgenParamsHandleRef handle{}; ParamsLease() = default; explicit ParamsLease(ProcgenParamsHandleRef value) : handle(value) {} - ParamsLease(const ParamsLease&) = delete; - ParamsLease& operator=(const ParamsLease&) = delete; - ParamsLease(ParamsLease&& other) noexcept : handle(other.handle) { other.handle = {}; } - ParamsLease& operator=(ParamsLease&& other) noexcept { + ParamsLease(const ParamsLease &) = delete; + ParamsLease &operator=(const ParamsLease &) = delete; + ParamsLease(ParamsLease &&other) noexcept : handle(other.handle) { other.handle = {}; } + ParamsLease &operator=(ParamsLease &&other) noexcept { if (this == &other) return *this; reset(); - handle = other.handle; + handle = other.handle; other.handle = {}; return *this; } @@ -84,22 +84,20 @@ struct ParamsLease { result.ignore("simulation params cleanup"); handle = {}; } - [[nodiscard]] eve::script::Borrowed view() const noexcept { - return Procgen::resolve(handle); - } + [[nodiscard]] eve::script::Borrowed view() const noexcept { return Procgen::resolve(handle); } }; struct GridLease { ProcgenGridHandleRef handle{}; GridLease() = default; explicit GridLease(ProcgenGridHandleRef value) : handle(value) {} - GridLease(const GridLease&) = delete; - GridLease& operator=(const GridLease&) = delete; - GridLease(GridLease&& other) noexcept : handle(other.handle) { other.handle = {}; } - GridLease& operator=(GridLease&& other) noexcept { + GridLease(const GridLease &) = delete; + GridLease &operator=(const GridLease &) = delete; + GridLease(GridLease &&other) noexcept : handle(other.handle) { other.handle = {}; } + GridLease &operator=(GridLease &&other) noexcept { if (this == &other) return *this; reset(); - handle = other.handle; + handle = other.handle; other.handle = {}; return *this; } @@ -110,28 +108,26 @@ struct GridLease { result.ignore("simulation grid cleanup"); handle = {}; } - [[nodiscard]] eve::script::Borrowed view() const noexcept { - return Procgen::resolve(handle); - } + [[nodiscard]] eve::script::Borrowed view() const noexcept { return Procgen::resolve(handle); } }; struct OutputLease { - Procgen* owner = nullptr; + Procgen *owner = nullptr; ProcgenOutputHandleRef handle{}; OutputLease() = default; - OutputLease(Procgen& proc, ProcgenOutputHandleRef value) : owner(&proc), handle(value) {} - OutputLease(const OutputLease&) = delete; - OutputLease& operator=(const OutputLease&) = delete; - OutputLease(OutputLease&& other) noexcept : owner(other.owner), handle(other.handle) { - other.owner = nullptr; + OutputLease(Procgen &proc, ProcgenOutputHandleRef value) : owner(&proc), handle(value) {} + OutputLease(const OutputLease &) = delete; + OutputLease &operator=(const OutputLease &) = delete; + OutputLease(OutputLease &&other) noexcept : owner(other.owner), handle(other.handle) { + other.owner = nullptr; other.handle = {}; } - OutputLease& operator=(OutputLease&& other) noexcept { + OutputLease &operator=(OutputLease &&other) noexcept { if (this == &other) return *this; reset(); - owner = other.owner; - handle = other.handle; - other.owner = nullptr; + owner = other.owner; + handle = other.handle; + other.owner = nullptr; other.handle = {}; return *this; } @@ -140,7 +136,7 @@ struct OutputLease { if (!owner || !handle.isValid()) return; auto result = owner->releaseOutput(handle); result.ignore("simulation output cleanup"); - owner = nullptr; + owner = nullptr; handle = {}; } [[nodiscard]] eve::script::Borrowed view() const noexcept { @@ -148,29 +144,29 @@ struct OutputLease { } }; -OutputLease requireOutput(Procgen& proc) { +OutputLease requireOutput(Procgen &proc) { auto result = proc.newOutputHandle(); REQUIRE(result.ok()); return OutputLease(proc, std::move(result).takeValue()); } struct MeshBuildLease { - Procgen* owner = nullptr; + Procgen *owner = nullptr; ProcgenMeshBuildHandleRef handle{}; MeshBuildLease() = default; - MeshBuildLease(Procgen& proc, ProcgenMeshBuildHandleRef value) : owner(&proc), handle(value) {} - MeshBuildLease(const MeshBuildLease&) = delete; - MeshBuildLease& operator=(const MeshBuildLease&) = delete; - MeshBuildLease(MeshBuildLease&& other) noexcept : owner(other.owner), handle(other.handle) { - other.owner = nullptr; + MeshBuildLease(Procgen &proc, ProcgenMeshBuildHandleRef value) : owner(&proc), handle(value) {} + MeshBuildLease(const MeshBuildLease &) = delete; + MeshBuildLease &operator=(const MeshBuildLease &) = delete; + MeshBuildLease(MeshBuildLease &&other) noexcept : owner(other.owner), handle(other.handle) { + other.owner = nullptr; other.handle = {}; } - MeshBuildLease& operator=(MeshBuildLease&& other) noexcept { + MeshBuildLease &operator=(MeshBuildLease &&other) noexcept { if (this == &other) return *this; reset(); - owner = other.owner; - handle = other.handle; - other.owner = nullptr; + owner = other.owner; + handle = other.handle; + other.owner = nullptr; other.handle = {}; return *this; } @@ -179,7 +175,7 @@ struct MeshBuildLease { if (!owner || !handle.isValid()) return; auto result = owner->releaseMeshBuild(handle); result.ignore("simulation mesh build cleanup"); - owner = nullptr; + owner = nullptr; handle = {}; } [[nodiscard]] eve::script::Borrowed view() const noexcept { @@ -187,25 +183,23 @@ struct MeshBuildLease { } }; -ParamsLease requireParams(const Params& source) { +ParamsLease requireParams(const Params &source) { auto result = Procgen::newParamsHandle(); REQUIRE(result.ok()); auto lease = ParamsLease(std::move(result).takeValue()); - auto view = lease.view(); + auto view = lease.view(); REQUIRE(view.isBound()); *view = source; return lease; } -GridLease requireGrid(Procgen& proc, const std::string& algorithm, - ProcgenParamsHandleRef params) { +GridLease requireGrid(Procgen &proc, const std::string &algorithm, ProcgenParamsHandleRef params) { auto result = proc.generateHandle(algorithm, params); REQUIRE(result.ok()); return GridLease(std::move(result).takeValue()); } -MeshBuildLease requireMeshBuild(Procgen& proc, const std::string& recipe, - ProcgenParamsHandleRef params) { +MeshBuildLease requireMeshBuild(Procgen &proc, const std::string &recipe, ProcgenParamsHandleRef params) { auto result = proc.buildMeshHandle(recipe, params); REQUIRE(result.ok()); return MeshBuildLease(proc, std::move(result).takeValue()); @@ -352,7 +346,7 @@ TEST_CASE("procgen.simulation.dungeonDelve") { // 1) Generate semantic dungeon // ------------------------------------------------------------------ auto gridLease = requireGrid(*proc, "wfc.simple", params.handle); - auto grid = gridLease.view(); + auto grid = gridLease.view(); REQUIRE(grid.isBound()); CHECK_EQ(grid->getWidth(), kMapW); CHECK_EQ(grid->getHeight(), kMapH); @@ -406,8 +400,8 @@ TEST_CASE("procgen.simulation.dungeonDelve") { // generateTo must produce the same GIDs for the same seed. TileLayer *layer2 = mapMod->newLayer(kMapW, kMapH, kTile, kTile); - auto out = requireOutput(*proc); - auto outView = out.view(); + auto out = requireOutput(*proc); + auto outView = out.view(); REQUIRE(outView.isBound()); outView->setTarget("tilelayer"); outView->setLayer(layer2); @@ -462,7 +456,7 @@ TEST_CASE("procgen.simulation.dungeonDelve") { // 5) Reproducibility + JSON export a designer/debug tool would save // ------------------------------------------------------------------ auto gridBLease = requireGrid(*proc, "wfc.simple", params.handle); - auto gridB = gridBLease.view(); + auto gridB = gridBLease.view(); REQUIRE(gridB.isBound()); CHECK(grid->cells() == gridB->cells()); auto jsonResult = proc->gridToJson(gridLease.handle); @@ -507,8 +501,8 @@ TEST_CASE("procgen.simulation.crystalCave") { islandP.setFloat("isolevel", 0.f); auto islandParams = requireParams(islandP); - auto islandLease = requireMeshBuild(*proc, "mesh.marchingcubes", islandParams.handle); - auto island = islandLease.view(); + auto islandLease = requireMeshBuild(*proc, "mesh.marchingcubes", islandParams.handle); + auto island = islandLease.view(); REQUIRE(island.isBound()); CHECK(island->getVertexCount() > 200); CHECK_EQ(island->getIndexCount() % 3, 0); @@ -598,7 +592,7 @@ TEST_CASE("procgen.simulation.crystalCave") { // Same island params ⇒ identical bake (cache key for a content pipeline). auto islandBLease = requireMeshBuild(*proc, "mesh.marchingcubes", islandParams.handle); - auto islandB = islandBLease.view(); + auto islandB = islandBLease.view(); REQUIRE(islandB.isBound()); CHECK(island->positions() == islandB->positions()); CHECK(island->indices() == islandB->indices()); @@ -610,9 +604,9 @@ TEST_CASE("procgen.simulation.crystalCave") { caveP.setString("field", "noise"); caveP.setFloat("scale", 2.2f); caveP.setFloat("threshold", 0.0f); - auto caveParams = requireParams(caveP); + auto caveParams = requireParams(caveP); auto caveShellLease = requireMeshBuild(*proc, "mesh.marchingcubes", caveParams.handle); - auto caveShell = caveShellLease.view(); + auto caveShell = caveShellLease.view(); REQUIRE(caveShell.isBound()); CHECK(caveShell->getVertexCount() > 100); CHECK(caveShell->positions() != island->positions()); @@ -637,8 +631,7 @@ TEST_CASE("procgen.simulation.crystalCave") { CHECK(gpuIsland->indexCount == island->getIndexCount()); // Also exercise Procgen::generateMesh convenience path. - auto gpuCrystal = proc->generateMeshBorrowed( - "mesh.marchingcubes", islandParams.handle, gfx); + auto gpuCrystal = proc->generateMeshBorrowed("mesh.marchingcubes", islandParams.handle, gfx); REQUIRE(gpuCrystal.isBound()); CHECK(gpuCrystal->indexCount == island->getIndexCount()); win->close(); @@ -676,14 +669,14 @@ TEST_CASE("procgen.simulation.caveExpedition") { p.setString("preset", "cave"); p.setInt("maxAttempts", 64); - auto params = requireParams(p); + auto params = requireParams(p); auto gridLease = requireGrid(*proc, "wfc.simple", params.handle); - auto grid = gridLease.view(); + auto grid = gridLease.view(); REQUIRE(grid.isBound()); CHECK(borderIsWallLike(*grid)); TileLayer *layer = mapMod->newLayer(kMapW, kMapH, kTile, kTile); - auto applyResult = proc->applyToLayer(gridLease.handle, "sim_cave", *layer); + auto applyResult = proc->applyToLayer(gridLease.handle, "sim_cave", *layer); CHECK(applyResult.ok()); // Collect floor cells. @@ -815,14 +808,14 @@ TEST_CASE("procgen.simulation.overworldBiomeTravel") { p.setString("preset", "terrain"); p.setInt("maxAttempts", 64); - auto params = requireParams(p); + auto params = requireParams(p); auto gridLease = requireGrid(*proc, "wfc.simple", params.handle); - auto grid = gridLease.view(); + auto grid = gridLease.view(); REQUIRE(grid.isBound()); CHECK(terrainAdjacencyOk(*grid)); TileLayer *layer = mapMod->newLayer(kMapW, kMapH, kTile, kTile); - auto applyResult = proc->applyToLayer(gridLease.handle, "sim_biome", *layer); + auto applyResult = proc->applyToLayer(gridLease.handle, "sim_biome", *layer); CHECK(applyResult.ok()); // Pick a grass cell and a dirt/stone cell if possible. @@ -874,7 +867,7 @@ TEST_CASE("procgen.simulation.overworldBiomeTravel") { // Reproducible overworld for save/load. auto grid2Lease = requireGrid(*proc, "wfc.simple", params.handle); - auto grid2 = grid2Lease.view(); + auto grid2 = grid2Lease.view(); REQUIRE(grid2.isBound()); CHECK(grid->cells() == grid2->cells()); @@ -897,8 +890,8 @@ TEST_CASE("procgen.simulation.lootPropBake") { ringP.setFloat("minorRadius", 0.18f); auto ringParams = requireParams(ringP); - auto ringLease = requireMeshBuild(*proc, "mesh.marchingcubes", ringParams.handle); - auto ring = ringLease.view(); + auto ringLease = requireMeshBuild(*proc, "mesh.marchingcubes", ringParams.handle); + auto ring = ringLease.view(); REQUIRE(ring.isBound()); CHECK(ring->getVertexCount() > 150); CHECK_EQ(ring->getIndexCount() % 3, 0); @@ -909,8 +902,8 @@ TEST_CASE("procgen.simulation.lootPropBake") { gemP.setString("field", "sphere"); gemP.setFloat("radius", 0.4f); auto gemParams = requireParams(gemP); - auto gemLease = requireMeshBuild(*proc, "mesh.marchingcubes", gemParams.handle); - auto gem = gemLease.view(); + auto gemLease = requireMeshBuild(*proc, "mesh.marchingcubes", gemParams.handle); + auto gem = gemLease.view(); REQUIRE(gem.isBound()); const int totalTris = ring->getIndexCount() / 3 + gem->getIndexCount() / 3; @@ -919,7 +912,7 @@ TEST_CASE("procgen.simulation.lootPropBake") { // Content-pipeline cache key: identical params → identical bytes. auto ring2Lease = requireMeshBuild(*proc, "mesh.marchingcubes", ringParams.handle); - auto ring2 = ring2Lease.view(); + auto ring2 = ring2Lease.view(); REQUIRE(ring2.isBound()); CHECK(ring->positions() == ring2->positions()); CHECK(ring->indices() == ring2->indices()); @@ -928,9 +921,8 @@ TEST_CASE("procgen.simulation.lootPropBake") { Params ringP2 = ringP; ringP2.setFloat("minorRadius", 0.22f); auto ring2Params = requireParams(ringP2); - auto ring3Lease = requireMeshBuild(*proc, "mesh.marchingcubes", ring2Params.handle); - auto ring3 = ring3Lease.view(); + auto ring3Lease = requireMeshBuild(*proc, "mesh.marchingcubes", ring2Params.handle); + auto ring3 = ring3Lease.view(); REQUIRE(ring3.isBound()); CHECK(ring->positions() != ring3->positions()); - } diff --git a/test/procgen_spatial_data.cpp b/test/procgen_spatial_data.cpp index 5075d2a9..32d8e3da 100644 --- a/test/procgen_spatial_data.cpp +++ b/test/procgen_spatial_data.cpp @@ -52,19 +52,19 @@ TEST_CASE("procgen.spatialData.splineAndDeterministicSampling") { TEST_CASE("procgen.spatialData.scriptFacingApiFiltersPoints") { Procgen proc; - auto includeResult = proc.boxVolumeHandle(0.f, -1.f, 0.f, 5.f, 1.f, 5.f); - auto excludeResult = proc.sphereVolumeHandle(2.f, 0.f, 2.f, 1.f); + auto includeResult = proc.boxVolumeHandle(0.f, -1.f, 0.f, 5.f, 1.f, 5.f); + auto excludeResult = proc.sphereVolumeHandle(2.f, 0.f, 2.f, 1.f); REQUIRE(includeResult.ok()); REQUIRE(excludeResult.ok()); - auto include = std::move(includeResult).takeValue(); - auto exclude = std::move(excludeResult).takeValue(); + auto include = std::move(includeResult).takeValue(); + auto exclude = std::move(excludeResult).takeValue(); auto domainResult = proc.differenceSpatialHandle(include, exclude); REQUIRE(domainResult.ok()); auto domain = std::move(domainResult).takeValue(); auto inputResult = proc.newPointSetHandle(); REQUIRE(inputResult.ok()); - auto input = std::move(inputResult).takeValue(); + auto input = std::move(inputResult).takeValue(); auto inputView = proc.resolvePointSet(input); REQUIRE(inputView.isBound()); inputView->add(1.f, 0.f, 1.f); @@ -74,8 +74,8 @@ TEST_CASE("procgen.spatialData.scriptFacingApiFiltersPoints") { auto rejectedResult = proc.filterSpatialHandle(input, domain, true); REQUIRE(acceptedResult.ok()); REQUIRE(rejectedResult.ok()); - auto accepted = std::move(acceptedResult).takeValue(); - auto rejected = std::move(rejectedResult).takeValue(); + auto accepted = std::move(acceptedResult).takeValue(); + auto rejected = std::move(rejectedResult).takeValue(); auto acceptedView = proc.resolvePointSet(accepted); auto rejectedView = proc.resolvePointSet(rejected); REQUIRE(acceptedView.isBound()); diff --git a/test/production.cpp b/test/production.cpp index 56c9540b..b90322f2 100644 --- a/test/production.cpp +++ b/test/production.cpp @@ -9,16 +9,14 @@ using namespace eve::production; TEST_CASE("production.enqueue.stableIdsAndCanonicalContext") { - WorkQueue queue; + WorkQueue queue; eve::Value::Object context; context.emplace("z", eve::Value(2)); context.emplace("a", eve::Value(1)); - auto firstResult = queue.enqueue("factory:1", "vehicle", "tank.medium", - eve::Value(std::move(context)), 10.0, 2); + auto firstResult = queue.enqueue("factory:1", "vehicle", "tank.medium", eve::Value(std::move(context)), 10.0, 2); REQUIRE(firstResult.ok()); - const auto first = std::move(firstResult).takeValue(); - auto secondResult = queue.enqueue("factory:1", "vehicle", "truck", - eve::Value(eve::Value::Object{}), 5.0, 1); + const auto first = std::move(firstResult).takeValue(); + auto secondResult = queue.enqueue("factory:1", "vehicle", "truck", eve::Value(eve::Value::Object{}), 5.0, 1); REQUIRE(secondResult.ok()); const auto second = std::move(secondResult).takeValue(); CHECK_EQ(first, std::string("task-0000000000000001")); @@ -41,29 +39,25 @@ TEST_CASE("production.enqueue.stableIdsAndCanonicalContext") { TEST_CASE("production.scheduling.prioritySlotsAndOwnersAreDeterministic") { WorkQueue queue; - auto slotsDisabled = queue.setSlotCount("base:a", 0); + auto slotsDisabled = queue.setSlotCount("base:a", 0); REQUIRE(slotsDisabled.ok()); - auto lowResult = queue.enqueue("base:a", "build", "low", - eve::Value(eve::Value::Object{}), 2.0, 1); - auto high1Result = queue.enqueue("base:a", "build", "high1", - eve::Value(eve::Value::Object{}), 2.0, 9); - auto high2Result = queue.enqueue("base:a", "build", "high2", - eve::Value(eve::Value::Object{}), 2.0, 9); - auto otherResult = queue.enqueue("base:b", "train", "unit", - eve::Value(eve::Value::Object{}), 2.0, 0); + auto lowResult = queue.enqueue("base:a", "build", "low", eve::Value(eve::Value::Object{}), 2.0, 1); + auto high1Result = queue.enqueue("base:a", "build", "high1", eve::Value(eve::Value::Object{}), 2.0, 9); + auto high2Result = queue.enqueue("base:a", "build", "high2", eve::Value(eve::Value::Object{}), 2.0, 9); + auto otherResult = queue.enqueue("base:b", "train", "unit", eve::Value(eve::Value::Object{}), 2.0, 0); REQUIRE(lowResult.ok()); REQUIRE(high1Result.ok()); REQUIRE(high2Result.ok()); REQUIRE(otherResult.ok()); - const auto low = std::move(lowResult).takeValue(); - const auto high1 = std::move(high1Result).takeValue(); - const auto high2 = std::move(high2Result).takeValue(); - const auto other = std::move(otherResult).takeValue(); - auto slotsEnabled = queue.setSlotCount("base:a", 2); + const auto low = std::move(lowResult).takeValue(); + const auto high1 = std::move(high1Result).takeValue(); + const auto high2 = std::move(high2Result).takeValue(); + const auto other = std::move(otherResult).takeValue(); + auto slotsEnabled = queue.setSlotCount("base:a", 2); REQUIRE(slotsEnabled.ok()); auto high1Task = queue.find(high1); auto high2Task = queue.find(high2); - auto lowTask = queue.find(low); + auto lowTask = queue.find(low); auto otherTask = queue.find(other); REQUIRE(high1Task); REQUIRE(high2Task); @@ -77,41 +71,35 @@ TEST_CASE("production.scheduling.prioritySlotsAndOwnersAreDeterministic") { TEST_CASE("production.advance.fixedDeltaCompletion") { WorkQueue queue; - auto firstResult = queue.enqueue("yard", "assemble", "a", - eve::Value(eve::Value::Object{}), 4.0); - auto secondResult = queue.enqueue("yard", "assemble", "b", - eve::Value(eve::Value::Object{}), 1.0); + auto firstResult = queue.enqueue("yard", "assemble", "a", eve::Value(eve::Value::Object{}), 4.0); + auto secondResult = queue.enqueue("yard", "assemble", "b", eve::Value(eve::Value::Object{}), 1.0); REQUIRE(firstResult.ok()); REQUIRE(secondResult.ok()); - const auto first = std::move(firstResult).takeValue(); - const auto second = std::move(secondResult).takeValue(); - auto firstStep = queue.advance( - {eve::SimulationTick(1), eve::Duration::fromNanoseconds(1000000000)}); + const auto first = std::move(firstResult).takeValue(); + const auto second = std::move(secondResult).takeValue(); + auto firstStep = queue.advance({eve::SimulationTick(1), eve::Duration::fromNanoseconds(1000000000)}); REQUIRE(firstStep.ok()); auto firstTask = queue.find(first); REQUIRE(firstTask); CHECK_EQ(firstTask->get().progress.seconds(), 1.0); - auto secondStep = queue.advance( - {eve::SimulationTick(2), eve::Duration::fromNanoseconds(1000000000)}); + auto secondStep = queue.advance({eve::SimulationTick(2), eve::Duration::fromNanoseconds(1000000000)}); REQUIRE(secondStep.ok()); - firstTask = queue.find(first); + firstTask = queue.find(first); auto secondTask = queue.find(second); REQUIRE(firstTask); REQUIRE(secondTask); CHECK_EQ(firstTask->get().progress.seconds(), 2.0); CHECK_EQ(firstTask->get().state == TaskState::Running, true); CHECK_EQ(secondTask->get().state == TaskState::Queued, true); - auto thirdStep = queue.advance( - {eve::SimulationTick(3), eve::Duration::fromNanoseconds(2000000000)}); + auto thirdStep = queue.advance({eve::SimulationTick(3), eve::Duration::fromNanoseconds(2000000000)}); REQUIRE(thirdStep.ok()); - firstTask = queue.find(first); + firstTask = queue.find(first); secondTask = queue.find(second); REQUIRE(firstTask); REQUIRE(secondTask); CHECK_EQ(firstTask->get().state == TaskState::Completed, true); CHECK_EQ(secondTask->get().state == TaskState::Running, true); - auto fourthStep = queue.advance( - {eve::SimulationTick(4), eve::Duration::fromNanoseconds(1000000000)}); + auto fourthStep = queue.advance({eve::SimulationTick(4), eve::Duration::fromNanoseconds(1000000000)}); REQUIRE(fourthStep.ok()); secondTask = queue.find(second); REQUIRE(secondTask); @@ -123,13 +111,13 @@ TEST_CASE("production.advance.fixedDeltaCompletion") { TEST_CASE("production.lifecycle.pauseResumeCancelFail") { WorkQueue queue; - auto aResult = queue.enqueue("owner", "kind", "a", eve::Value(eve::Value::Object{}), 2.0); - auto bResult = queue.enqueue("owner", "kind", "b", eve::Value(eve::Value::Object{}), 2.0); + auto aResult = queue.enqueue("owner", "kind", "a", eve::Value(eve::Value::Object{}), 2.0); + auto bResult = queue.enqueue("owner", "kind", "b", eve::Value(eve::Value::Object{}), 2.0); REQUIRE(aResult.ok()); REQUIRE(bResult.ok()); - const auto a = std::move(aResult).takeValue(); - const auto b = std::move(bResult).takeValue(); - auto paused = queue.pause(a); + const auto a = std::move(aResult).takeValue(); + const auto b = std::move(bResult).takeValue(); + auto paused = queue.pause(a); REQUIRE(paused.ok()); auto aTask = queue.find(a); auto bTask = queue.find(b); @@ -158,21 +146,19 @@ TEST_CASE("production.lifecycle.pauseResumeCancelFail") { TEST_CASE("production.snapshot.restoreIsDeterministicAndTransactional") { WorkQueue source; - auto slots = source.setSlotCount("yard", 2); + auto slots = source.setSlotCount("yard", 2); REQUIRE(slots.ok()); - auto idResult = source.enqueue("yard", "vehicle", "tank", - eve::Value(eve::Value::Object{{"variant", eve::Value("a")}}), - 5.0, 7); + auto idResult = + source.enqueue("yard", "vehicle", "tank", eve::Value(eve::Value::Object{{"variant", eve::Value("a")}}), 5.0, 7); REQUIRE(idResult.ok()); - const auto id = std::move(idResult).takeValue(); - auto sourceStep = source.advance( - {eve::SimulationTick(1), eve::Duration::fromNanoseconds(1250000000)}); + const auto id = std::move(idResult).takeValue(); + auto sourceStep = source.advance({eve::SimulationTick(1), eve::Duration::fromNanoseconds(1250000000)}); REQUIRE(sourceStep.ok()); auto snapshotResult = source.snapshot(); REQUIRE(snapshotResult.ok()); const auto snapshot = std::move(snapshotResult).takeValue(); - WorkQueue restored; - auto restoredResult = restored.restore(snapshot); + WorkQueue restored; + auto restoredResult = restored.restore(snapshot); REQUIRE(restoredResult.ok()); auto restoredSnapshot = restored.snapshot(); REQUIRE(restoredSnapshot.ok()); @@ -182,8 +168,8 @@ TEST_CASE("production.snapshot.restoreIsDeterministicAndTransactional") { CHECK_EQ(restoredTask->get().progress.seconds(), 1.25); auto beforeResult = restored.snapshot(); REQUIRE(beforeResult.ok()); - const auto before = std::move(beforeResult).takeValue(); - auto malformed = restored.restore("{\"broken\":true}"); + const auto before = std::move(beforeResult).takeValue(); + auto malformed = restored.restore("{\"broken\":true}"); CHECK(!malformed.ok()); auto afterMalformed = restored.snapshot(); REQUIRE(afterMalformed.ok()); diff --git a/test/production_time.cpp b/test/production_time.cpp index ea91bb53..00c51857 100644 --- a/test/production_time.cpp +++ b/test/production_time.cpp @@ -19,8 +19,7 @@ public: TEST_CASE("production.injectedStepsPauseAndRestoreTick") { WorkQueue queue; - auto idResult = queue.enqueue("factory", "vehicle", "tank", - eve::Value(eve::Value::Object{}), 1.0); + auto idResult = queue.enqueue("factory", "vehicle", "tank", eve::Value(eve::Value::Object{}), 1.0); REQUIRE(idResult.ok()); const auto id = std::move(idResult).takeValue(); @@ -51,7 +50,7 @@ TEST_CASE("production.injectedStepsPauseAndRestoreTick") { CHECK(saved.find("wallClock") == std::string::npos); WorkQueue restored; - auto restoredResult = restored.restore(saved); + auto restoredResult = restored.restore(saved); REQUIRE(restoredResult.ok()); CHECK_EQ(restored.currentTick().value(), std::uint64_t(2)); auto afterRestore = restored.advance({eve::SimulationTick(3), eve::Duration::fromNanoseconds(1)}); @@ -61,12 +60,10 @@ TEST_CASE("production.injectedStepsPauseAndRestoreTick") { TEST_CASE("production.advanceFacadeAdvancesDeterministicTick") { WorkQueue queue; - auto idResult = queue.enqueue("factory", "vehicle", "tank", - eve::Value(eve::Value::Object{}), 1.0); + auto idResult = queue.enqueue("factory", "vehicle", "tank", eve::Value(eve::Value::Object{}), 1.0); REQUIRE(idResult.ok()); - const auto id = std::move(idResult).takeValue(); - auto step = queue.advance( - {eve::SimulationTick(1), eve::Duration::fromNanoseconds(125000000)}); + const auto id = std::move(idResult).takeValue(); + auto step = queue.advance({eve::SimulationTick(1), eve::Duration::fromNanoseconds(125000000)}); REQUIRE(step.ok()); CHECK_EQ(queue.currentTick().value(), std::uint64_t(1)); auto task = queue.find(id); @@ -78,11 +75,10 @@ TEST_CASE("production.advanceFacadeAdvancesDeterministicTick") { } TEST_CASE("production.clockStepsAreTheOnlySimulationInput") { - ManualTimeSource source; + ManualTimeSource source; eve::SimulationClock clock(source, eve::Duration::fromNanoseconds(250000000)); - WorkQueue queue; - auto idResult = queue.enqueue("factory", "vehicle", "tank", - eve::Value(eve::Value::Object{}), 0.5); + WorkQueue queue; + auto idResult = queue.enqueue("factory", "vehicle", "tank", eve::Value(eve::Value::Object{}), 0.5); REQUIRE(idResult.ok()); const auto id = std::move(idResult).takeValue(); REQUIRE(clock.setRate(0.5).ok()); diff --git a/test/profile/capability_absent.cpp b/test/profile/capability_absent.cpp index dc271562..8ecb3fbc 100644 --- a/test/profile/capability_absent.cpp +++ b/test/profile/capability_absent.cpp @@ -3,7 +3,7 @@ namespace { struct MissingCapability { static constexpr const char* capabilityName = "profile.optional.absent"; - virtual ~MissingCapability() = default; + virtual ~MissingCapability() = default; }; } // namespace diff --git a/test/profile/capability_present.cpp b/test/profile/capability_present.cpp index 6f9cf8f2..9cc9c867 100644 --- a/test/profile/capability_present.cpp +++ b/test/profile/capability_present.cpp @@ -3,8 +3,8 @@ namespace { struct ProbeCapability { static constexpr const char* capabilityName = "profile.optional.present"; - virtual ~ProbeCapability() = default; - virtual int value() const = 0; + virtual ~ProbeCapability() = default; + virtual int value() const = 0; }; struct Provider final : ProbeCapability { diff --git a/test/profile/public_headers_common.cpp b/test/profile/public_headers_common.cpp index d3f5bec2..bced47ac 100644 --- a/test/profile/public_headers_common.cpp +++ b/test/profile/public_headers_common.cpp @@ -3,24 +3,24 @@ // zeroerr case and must remain usable without the full unit-test executable. #include "common/Capability.h" #include "common/Diagnostic.h" +#include "common/EventSequence.h" +#include "common/Generation.h" #include "common/Identity.h" #include "common/Json.h" #include "common/ResourceAccount.h" #include "common/ResourceRef.h" #include "common/Result.h" +#include "common/Revision.h" #include "common/RuntimeHandle.h" +#include "common/SchemaVersion.h" #include "common/Snapshot.h" #include "common/Status.h" +#include "common/StrongUint64.h" #include "common/SubjectRef.h" #include "common/Subscription.h" #include "common/Time.h" #include "common/Uri.h" #include "common/Value.h" -#include "common/EventSequence.h" -#include "common/Generation.h" -#include "common/Revision.h" -#include "common/SchemaVersion.h" -#include "common/StrongUint64.h" #include "common/VersionedRegistry.h" namespace eve::profile_check { diff --git a/test/property_validation_parity.cpp b/test/property_validation_parity.cpp index 96c849b1..68347aa4 100644 --- a/test/property_validation_parity.cpp +++ b/test/property_validation_parity.cpp @@ -1,8 +1,8 @@ #include "zeroerr/assert.h" #include "zeroerr/unittest.h" -#include "editor/EditorPropertyModel.h" #include "TestPropertyAccess.h" +#include "editor/EditorPropertyModel.h" #include #include @@ -43,8 +43,8 @@ public: return {}; } - EditorResult makeSet(const SelectionSnapshot &, const PropertyPath &, - const EditorValue &, PropertySetMode) const override { + EditorResult makeSet(const SelectionSnapshot &, const PropertyPath &, const EditorValue &, + PropertySetMode) const override { return EditorResult::applied({}); } @@ -68,11 +68,10 @@ PropertySchema editorSchema() { } // namespace TEST_CASE("property.validation_presentation_and_editor_share_type_enum_range_and_finite_rules") { - const PropertyDescriptor editorNumber = editorNumberProperty(); + const PropertyDescriptor editorNumber = editorNumberProperty(); const eve::property_access::PropertyDescriptor presentationNumber = toPresentationDescriptor(editorNumber); - const auto sharedType = eve::property_access::validatePropertyValue( - presentationNumber, eve::Value("fast")); + const auto sharedType = eve::property_access::validatePropertyValue(presentationNumber, eve::Value("fast")); const auto editorType = validatePropertyValue(editorNumber, EditorValue("fast")); CHECK(!sharedType.accepted); CHECK(!editorType.accepted()); @@ -80,18 +79,16 @@ TEST_CASE("property.validation_presentation_and_editor_share_type_enum_range_and REQUIRE_EQ(editorType.diagnostics.size(), static_cast(1)); CHECK_EQ(editorType.diagnostics.front().rule.value(), std::string("editor.property.type-mismatch")); - const double nonFinite = std::numeric_limits::quiet_NaN(); - const auto sharedFinite = eve::property_access::validatePropertyValue( - presentationNumber, eve::Value(nonFinite)); - const auto editorFinite = validatePropertyValue(editorNumber, EditorValue(nonFinite)); + const double nonFinite = std::numeric_limits::quiet_NaN(); + const auto sharedFinite = eve::property_access::validatePropertyValue(presentationNumber, eve::Value(nonFinite)); + const auto editorFinite = validatePropertyValue(editorNumber, EditorValue(nonFinite)); CHECK(!sharedFinite.accepted); CHECK(!editorFinite.accepted()); CHECK_EQ(sharedFinite.code, std::string("property_access.property.finite")); REQUIRE_EQ(editorFinite.diagnostics.size(), static_cast(1)); CHECK_EQ(editorFinite.diagnostics.front().rule.value(), std::string("editor.property.finite")); - const auto sharedRange = eve::property_access::validatePropertyValue( - presentationNumber, eve::Value(11.0)); + const auto sharedRange = eve::property_access::validatePropertyValue(presentationNumber, eve::Value(11.0)); const auto editorRange = validatePropertyValue(editorNumber, EditorValue(11.0)); CHECK(!sharedRange.accepted); CHECK(!editorRange.accepted()); @@ -99,10 +96,9 @@ TEST_CASE("property.validation_presentation_and_editor_share_type_enum_range_and REQUIRE_EQ(editorRange.diagnostics.size(), static_cast(1)); CHECK_EQ(editorRange.diagnostics.front().rule.value(), std::string("editor.property.above-maximum")); - const PropertyDescriptor editorEnum = editorEnumProperty(); + const PropertyDescriptor editorEnum = editorEnumProperty(); const eve::property_access::PropertyDescriptor presentationEnum = toPresentationDescriptor(editorEnum); - const auto sharedEnum = eve::property_access::validatePropertyValue( - presentationEnum, eve::Value("sprint")); + const auto sharedEnum = eve::property_access::validatePropertyValue(presentationEnum, eve::Value("sprint")); const auto editorEnumResult = validatePropertyValue(editorEnum, EditorValue("sprint")); CHECK(!sharedEnum.accepted); CHECK(!editorEnumResult.accepted()); @@ -112,9 +108,9 @@ TEST_CASE("property.validation_presentation_and_editor_share_type_enum_range_and } TEST_CASE("property.validation_editor_model_rejects_before_command_sink") { - PropertyProvider provider(editorSchema()); + PropertyProvider provider(editorSchema()); EditorPropertyModel model(editorSchema(), {}, &provider); - bool sinkCalled = false; + bool sinkCalled = false; model.setEditSink([&](const PropertyEditIntent &) { sinkCalled = true; return EditorResult::applied(); @@ -131,13 +127,12 @@ TEST_CASE("property.validation_editor_model_rejects_before_command_sink") { } TEST_CASE("property.validation_dynamic_model_uses_shared_finite_rule") { - const PropertyDescriptor editorNumber = editorNumberProperty(); + const PropertyDescriptor editorNumber = editorNumberProperty(); eve::property_access::PropertySchema schema; schema.properties.push_back(toPresentationDescriptor(editorNumber)); TestPropertyAccess model(std::move(schema)); - const auto rejected = model.write( - "speed", eve::Value(std::numeric_limits::infinity())); + const auto rejected = model.write("speed", eve::Value(std::numeric_limits::infinity())); CHECK(!rejected.accepted); CHECK_EQ(rejected.code, std::string("property_access.property.finite")); } @@ -149,7 +144,5 @@ TEST_CASE("property.validation_transform_descriptor_preserves_editor_object_sema const eve::property_access::PropertyDescriptor shared = toPresentationDescriptor(transform); CHECK_EQ(static_cast(shared.kind), static_cast(eve::property_access::PropertyKind::Struct)); - CHECK(eve::property_access::validatePropertyValue( - shared, eve::Value(eve::Value::Object{})) - .accepted); + CHECK(eve::property_access::validatePropertyValue(shared, eve::Value(eve::Value::Object{})).accepted); } diff --git a/test/resource_account.cpp b/test/resource_account.cpp index 38cf8c57..17825f1e 100644 --- a/test/resource_account.cpp +++ b/test/resource_account.cpp @@ -43,7 +43,7 @@ void runCommonAccountContract(Account& account, Balance&& balance) { CHECK_EQ(balance("mana"), std::int64_t{100}); const CostSpec blockedCost = makeCost({{"mana", 70}}); - auto blocked = account.reserve(blockedCost); + auto blocked = account.reserve(blockedCost); CHECK(!blocked.ok()); CHECK_EQ(static_cast(blocked.code()), static_cast(eve::StatusCode::Rejected)); CHECK_EQ(balance("mana"), std::int64_t{100}); @@ -51,26 +51,23 @@ void runCommonAccountContract(Account& account, Balance&& balance) { auto committed = account.commit(reservation); REQUIRE(committed.ok()); CHECK(committed.value().isValid()); - CHECK_EQ(static_cast(committed.value().operation), - static_cast(eve::resource::ReceiptOperation::Debit)); + CHECK_EQ(static_cast(committed.value().operation), static_cast(eve::resource::ReceiptOperation::Debit)); CHECK_EQ(balance("mana"), std::int64_t{60}); // A copied credential has the same account nonce and id, but the account // lifecycle still permits only one terminal operation. const auto copiedReservation = reservation; - auto duplicateCommit = account.commit(copiedReservation); + auto duplicateCommit = account.commit(copiedReservation); CHECK(!duplicateCommit.ok()); CHECK_EQ(static_cast(duplicateCommit.code()), static_cast(eve::StatusCode::Conflict)); auto duplicateRollback = account.rollback(copiedReservation); CHECK(!duplicateRollback.ok()); - CHECK_EQ(static_cast(duplicateRollback.code()), - static_cast(eve::StatusCode::Conflict)); + CHECK_EQ(static_cast(duplicateRollback.code()), static_cast(eve::StatusCode::Conflict)); - const CostSpec credit = makeCost({{"mana", 5}, {"stamina", 3}}); - auto credited = account.credit(credit); + const CostSpec credit = makeCost({{"mana", 5}, {"stamina", 3}}); + auto credited = account.credit(credit); REQUIRE(credited.ok()); - CHECK_EQ(static_cast(credited.value().operation), - static_cast(eve::resource::ReceiptOperation::Credit)); + CHECK_EQ(static_cast(credited.value().operation), static_cast(eve::resource::ReceiptOperation::Credit)); CHECK_EQ(balance("mana"), std::int64_t{65}); CHECK_EQ(balance("stamina"), std::int64_t{3}); } @@ -79,34 +76,28 @@ class CommitFailureParticipant final : public eve::transaction::ITransactionPart public: std::string_view name() const noexcept override { return "test-commit-failure"; } - [[nodiscard]] eve::Result prepare( - const eve::transaction::TransactionContext& context) override { + [[nodiscard]] eve::Result prepare(const eve::transaction::TransactionContext& context) override { (void)context; prepared = true; return eve::Result::success(); } - [[nodiscard]] eve::Result commit( - const eve::transaction::TransactionContext& context) override { + [[nodiscard]] eve::Result commit(const eve::transaction::TransactionContext& context) override { (void)context; return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Failed, - "injected commit failure", "test.commit")); + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "injected commit failure", "test.commit")); } - [[nodiscard]] eve::Result rollback( - const eve::transaction::TransactionContext& context) override { + [[nodiscard]] eve::Result rollback(const eve::transaction::TransactionContext& context) override { (void)context; prepared = false; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } - [[nodiscard]] eve::Result compensate( - const eve::transaction::TransactionContext& context) override { + [[nodiscard]] eve::Result compensate(const eve::transaction::TransactionContext& context) override { (void)context; - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Conflict, - "injected participant was never committed", "test.compensate")); + return eve::Result::failure(eve::Diagnostic::error( + eve::DiagnosticCode::Conflict, "injected participant was never committed", "test.compensate")); } bool prepared = false; @@ -141,7 +132,7 @@ TEST_CASE("resource.account.multiResourceDebitFailureIsAtomicForBothAdapters") { attributes.setBase("gold", 10.0); attributes.setBase("mana", 3.0); eve::attributes::AttributeSetResourceAccount attributeAccount(attributes); - auto attributeDebit = attributeAccount.debit(impossible); + auto attributeDebit = attributeAccount.debit(impossible); CHECK(!attributeDebit.ok()); CHECK_EQ(static_cast(attributeDebit.code()), static_cast(eve::StatusCode::Rejected)); CHECK_EQ(attributes.getBase("gold"), 10.0); @@ -151,7 +142,7 @@ TEST_CASE("resource.account.multiResourceDebitFailureIsAtomicForBothAdapters") { CHECK_EQ(ledger.credit("gold", 10), 10); CHECK_EQ(ledger.credit("mana", 3), 3); eve::economy::EconomyLedgerResourceAccount economyAccount(ledger); - auto economyDebit = economyAccount.debit(impossible); + auto economyDebit = economyAccount.debit(impossible); CHECK(!economyDebit.ok()); CHECK_EQ(static_cast(economyDebit.code()), static_cast(eve::StatusCode::Rejected)); CHECK_EQ(ledger.get("gold"), 10); @@ -162,12 +153,12 @@ TEST_CASE("resource.account.rollbackReleasesReservationWithoutChangingBalance") eve::attributes::AttributeSet attributes; attributes.setBase("mana", 50.0); eve::attributes::AttributeSetResourceAccount account(attributes); - const CostSpec cost = makeCost({{"mana", 30}}); + const CostSpec cost = makeCost({{"mana", 30}}); auto reservationResult = account.reserve(cost); REQUIRE(reservationResult.ok()); const auto reservation = reservationResult.value(); - auto blocked = account.canAfford(makeCost({{"mana", 25}})); + auto blocked = account.canAfford(makeCost({{"mana", 25}})); REQUIRE(blocked.ok()); CHECK(!blocked.value().affordable); CHECK_EQ(attributes.getBase("mana"), 50.0); @@ -180,10 +171,9 @@ TEST_CASE("resource.account.rollbackReleasesReservationWithoutChangingBalance") CHECK(available.value().affordable); const auto copiedReservation = reservation; - auto duplicateRollback = account.rollback(copiedReservation); + auto duplicateRollback = account.rollback(copiedReservation); CHECK(!duplicateRollback.ok()); - CHECK_EQ(static_cast(duplicateRollback.code()), - static_cast(eve::StatusCode::Conflict)); + CHECK_EQ(static_cast(duplicateRollback.code()), static_cast(eve::StatusCode::Conflict)); auto duplicateCommit = account.commit(copiedReservation); CHECK(!duplicateCommit.ok()); CHECK_EQ(static_cast(duplicateCommit.code()), static_cast(eve::StatusCode::Conflict)); @@ -196,13 +186,13 @@ TEST_CASE("resource.account.sameTypeAccountsRejectForeignCredentialsEvenWithSame secondAttributes.setBase("mana", 10.0); eve::attributes::AttributeSetResourceAccount first(firstAttributes); eve::attributes::AttributeSetResourceAccount second(secondAttributes); - const CostSpec cost = makeCost({{"mana", 5}}); + const CostSpec cost = makeCost({{"mana", 5}}); - auto firstReservationResult = first.reserve(cost); + auto firstReservationResult = first.reserve(cost); auto secondReservationResult = second.reserve(cost); REQUIRE(firstReservationResult.ok()); REQUIRE(secondReservationResult.ok()); - const auto firstReservation = firstReservationResult.value(); + const auto firstReservation = firstReservationResult.value(); const auto secondReservation = secondReservationResult.value(); CHECK_EQ(firstReservation.id.value(), secondReservation.id.value()); CHECK_NE(firstReservation.account.value(), secondReservation.account.value()); @@ -212,16 +202,13 @@ TEST_CASE("resource.account.sameTypeAccountsRejectForeignCredentialsEvenWithSame CHECK_EQ(static_cast(foreignCommit.code()), static_cast(eve::StatusCode::Conflict)); auto foreignRollback = second.rollback(firstReservation); CHECK(!foreignRollback.ok()); - CHECK_EQ(static_cast(foreignRollback.code()), - static_cast(eve::StatusCode::Conflict)); + CHECK_EQ(static_cast(foreignRollback.code()), static_cast(eve::StatusCode::Conflict)); auto reverseForeignCommit = first.commit(secondReservation); CHECK(!reverseForeignCommit.ok()); - CHECK_EQ(static_cast(reverseForeignCommit.code()), - static_cast(eve::StatusCode::Conflict)); + CHECK_EQ(static_cast(reverseForeignCommit.code()), static_cast(eve::StatusCode::Conflict)); auto reverseForeignRollback = first.rollback(secondReservation); CHECK(!reverseForeignRollback.ok()); - CHECK_EQ(static_cast(reverseForeignRollback.code()), - static_cast(eve::StatusCode::Conflict)); + CHECK_EQ(static_cast(reverseForeignRollback.code()), static_cast(eve::StatusCode::Conflict)); CHECK_EQ(secondAttributes.getBase("mana"), 10.0); auto ownCommit = first.commit(firstReservation); @@ -239,13 +226,13 @@ TEST_CASE("resource.account.economyAccountsRejectForeignCredentialsEvenWithSameI CHECK_EQ(secondLedger.credit("gold", 10), 10); eve::economy::EconomyLedgerResourceAccount first(firstLedger); eve::economy::EconomyLedgerResourceAccount second(secondLedger); - const CostSpec cost = makeCost({{"gold", 5}}); + const CostSpec cost = makeCost({{"gold", 5}}); - auto firstReservationResult = first.reserve(cost); + auto firstReservationResult = first.reserve(cost); auto secondReservationResult = second.reserve(cost); REQUIRE(firstReservationResult.ok()); REQUIRE(secondReservationResult.ok()); - const auto firstReservation = firstReservationResult.value(); + const auto firstReservation = firstReservationResult.value(); const auto secondReservation = secondReservationResult.value(); CHECK_EQ(firstReservation.id.value(), secondReservation.id.value()); CHECK_NE(firstReservation.account.value(), secondReservation.account.value()); @@ -255,16 +242,13 @@ TEST_CASE("resource.account.economyAccountsRejectForeignCredentialsEvenWithSameI CHECK_EQ(static_cast(foreignCommit.code()), static_cast(eve::StatusCode::Conflict)); auto foreignRollback = second.rollback(firstReservation); CHECK(!foreignRollback.ok()); - CHECK_EQ(static_cast(foreignRollback.code()), - static_cast(eve::StatusCode::Conflict)); + CHECK_EQ(static_cast(foreignRollback.code()), static_cast(eve::StatusCode::Conflict)); auto reverseForeignCommit = first.commit(secondReservation); CHECK(!reverseForeignCommit.ok()); - CHECK_EQ(static_cast(reverseForeignCommit.code()), - static_cast(eve::StatusCode::Conflict)); + CHECK_EQ(static_cast(reverseForeignCommit.code()), static_cast(eve::StatusCode::Conflict)); auto reverseForeignRollback = first.rollback(secondReservation); CHECK(!reverseForeignRollback.ok()); - CHECK_EQ(static_cast(reverseForeignRollback.code()), - static_cast(eve::StatusCode::Conflict)); + CHECK_EQ(static_cast(reverseForeignRollback.code()), static_cast(eve::StatusCode::Conflict)); CHECK_EQ(secondLedger.get("gold"), 10); auto ownCommit = first.commit(firstReservation); @@ -283,33 +267,29 @@ TEST_CASE("resource.account.crossAdapterCredentialsRejectCommitAndRollback") { eve::economy::EconomyLedger ledger; CHECK_EQ(ledger.credit("gold", 10), 10); eve::economy::EconomyLedgerResourceAccount economyAccount(ledger); - const CostSpec cost = makeCost({{"gold", 5}}); + const CostSpec cost = makeCost({{"gold", 5}}); auto attributeReservationResult = attributeAccount.reserve(cost); - auto economyReservationResult = economyAccount.reserve(cost); + auto economyReservationResult = economyAccount.reserve(cost); REQUIRE(attributeReservationResult.ok()); REQUIRE(economyReservationResult.ok()); const auto attributeReservation = attributeReservationResult.value(); - const auto economyReservation = economyReservationResult.value(); + const auto economyReservation = economyReservationResult.value(); CHECK_EQ(attributeReservation.id.value(), economyReservation.id.value()); CHECK_NE(attributeReservation.account.value(), economyReservation.account.value()); auto wrongEconomyCommit = economyAccount.commit(attributeReservation); CHECK(!wrongEconomyCommit.ok()); - CHECK_EQ(static_cast(wrongEconomyCommit.code()), - static_cast(eve::StatusCode::Conflict)); + CHECK_EQ(static_cast(wrongEconomyCommit.code()), static_cast(eve::StatusCode::Conflict)); auto wrongAttributeRollback = attributeAccount.rollback(economyReservation); CHECK(!wrongAttributeRollback.ok()); - CHECK_EQ(static_cast(wrongAttributeRollback.code()), - static_cast(eve::StatusCode::Conflict)); + CHECK_EQ(static_cast(wrongAttributeRollback.code()), static_cast(eve::StatusCode::Conflict)); auto wrongAttributeCommit = attributeAccount.commit(economyReservation); CHECK(!wrongAttributeCommit.ok()); - CHECK_EQ(static_cast(wrongAttributeCommit.code()), - static_cast(eve::StatusCode::Conflict)); + CHECK_EQ(static_cast(wrongAttributeCommit.code()), static_cast(eve::StatusCode::Conflict)); auto wrongEconomyRollback = economyAccount.rollback(attributeReservation); CHECK(!wrongEconomyRollback.ok()); - CHECK_EQ(static_cast(wrongEconomyRollback.code()), - static_cast(eve::StatusCode::Conflict)); + CHECK_EQ(static_cast(wrongEconomyRollback.code()), static_cast(eve::StatusCode::Conflict)); CHECK_EQ(attributes.getBase("gold"), 10.0); CHECK_EQ(ledger.get("gold"), 10); @@ -320,7 +300,7 @@ TEST_CASE("resource.account.crossAdapterCredentialsRejectCommitAndRollback") { TEST_CASE("resource.account.capacityFailureDoesNotPartiallyCreditEconomyLedger") { eve::economy::ResourceTypeRegistry::clear(); eve::economy::ResourceTypeDef definition; - definition.id = "gold"; + definition.id = "gold"; definition.stockMax = 10; CHECK(eve::economy::ResourceTypeRegistry::registerType(definition)); @@ -328,7 +308,7 @@ TEST_CASE("resource.account.capacityFailureDoesNotPartiallyCreditEconomyLedger") CHECK_EQ(ledger.credit("gold", 8), 8); CHECK_EQ(ledger.credit("wood", 1), 1); eve::economy::EconomyLedgerResourceAccount account(ledger); - const CostSpec cost = makeCost({{"gold", 4}, {"wood", 2}}); + const CostSpec cost = makeCost({{"gold", 4}, {"wood", 2}}); auto result = account.credit(cost); CHECK(!result.ok()); @@ -341,21 +321,19 @@ TEST_CASE("resource.account.capacityFailureDoesNotPartiallyCreditEconomyLedger") TEST_CASE("resource.account.transactionCommitFailureCompensatesCommittedDebit") { eve::attributes::AttributeSet attributes; attributes.setBase("mana", 100.0); - eve::attributes::AttributeSetResourceAccount account(attributes); - const CostSpec cost = makeCost({{"mana", 40}}); - eve::transaction::ResourceDebitParticipant debit(account, cost); - CommitFailureParticipant failing; + eve::attributes::AttributeSetResourceAccount account(attributes); + const CostSpec cost = makeCost({{"mana", 40}}); + eve::transaction::ResourceDebitParticipant debit(account, cost); + CommitFailureParticipant failing; std::array participants{&debit, &failing}; eve::transaction::Coordinator coordinator; - auto transaction = coordinator.execute( + auto transaction = coordinator.execute( eve::transaction::TransactionContext("resource-atomic"), - std::span(participants.data(), - participants.size())); + std::span(participants.data(), participants.size())); CHECK(!transaction.ok()); CHECK_EQ(static_cast(transaction.code()), static_cast(eve::StatusCode::Failed)); - CHECK_EQ(static_cast(debit.state()), - static_cast(eve::transaction::ResourceDebitState::Compensated)); + CHECK_EQ(static_cast(debit.state()), static_cast(eve::transaction::ResourceDebitState::Compensated)); CHECK(!failing.prepared); CHECK_EQ(attributes.getBase("mana"), 100.0); } diff --git a/test/resource_gameplay_adapters.cpp b/test/resource_gameplay_adapters.cpp index e3d4d945..49457843 100644 --- a/test/resource_gameplay_adapters.cpp +++ b/test/resource_gameplay_adapters.cpp @@ -6,10 +6,10 @@ #include "economy/EconomyLedger.h" #include "inventory/InventoryResourceAccount.h" #include "inventory/Item.h" -#include "rts/RTSEconomy.h" -#include "rts/RTSProductionAction.h" #include "orders/CommandQueue.h" #include "production/Production.h" +#include "rts/RTSEconomy.h" +#include "rts/RTSProductionAction.h" #include "transaction/AtomicResourcePayment.h" #include "weapon/WeaponAction.h" @@ -30,10 +30,9 @@ eve::resource::CostSpec makeCost(std::string_view resource, std::int64_t amount) } eve::Status injectedFailure(std::string_view path) { - return eve::Status::failure( - eve::StatusCode::Rejected, - eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, - "failure injected by resource adapter test", std::string(path))); + return eve::Status::failure(eve::StatusCode::Rejected, + eve::Diagnostic::error(eve::DiagnosticCode::PreconditionViolation, + "failure injected by resource adapter test", std::string(path))); } class TestEffect final : public eve::transaction::ITransactionParticipant { @@ -42,46 +41,39 @@ public: std::string_view name() const noexcept override { return "test-effect"; } - [[nodiscard]] eve::Result prepare( - const eve::transaction::TransactionContext&) override { + [[nodiscard]] eve::Result prepare(const eve::transaction::TransactionContext&) override { if (failPrepare) return eve::Result::failure(injectedFailure("effect.prepare")); - if (prepared || committed) - return eve::Result::failure(injectedFailure("effect.reentry")); + if (prepared || committed) return eve::Result::failure(injectedFailure("effect.reentry")); prepared = true; return eve::Result::success(); } - [[nodiscard]] eve::Result commit( - const eve::transaction::TransactionContext&) override { - if (!prepared || committed) - return eve::Result::failure(injectedFailure("effect.commit.state")); + [[nodiscard]] eve::Result commit(const eve::transaction::TransactionContext&) override { + if (!prepared || committed) return eve::Result::failure(injectedFailure("effect.commit.state")); if (failCommit) return eve::Result::failure(injectedFailure("effect.commit")); ++visibleValue_; committed = true; return eve::Result::success(); } - [[nodiscard]] eve::Result rollback( - const eve::transaction::TransactionContext&) override { - if (committed) - return eve::Result::failure(injectedFailure("effect.rollback.committed")); + [[nodiscard]] eve::Result rollback(const eve::transaction::TransactionContext&) override { + if (committed) return eve::Result::failure(injectedFailure("effect.rollback.committed")); prepared = false; return eve::Result::success(); } - [[nodiscard]] eve::Result compensate( - const eve::transaction::TransactionContext&) override { + [[nodiscard]] eve::Result compensate(const eve::transaction::TransactionContext&) override { if (!committed) return eve::Result::success(); --visibleValue_; committed = false; - prepared = false; + prepared = false; return eve::Result::success(); } bool failPrepare = false; - bool failCommit = false; - bool prepared = false; - bool committed = false; + bool failCommit = false; + bool prepared = false; + bool committed = false; private: int& visibleValue_; @@ -94,13 +86,12 @@ TEST_CASE("resource.gameplay.attributeViewsReuseOneCanonicalAccount") { attributes.setBase("mana", 10.0); attributes.setBase("stamina", 7.0); eve::attributes::AttributeSetResourceAccount base(attributes); - eve::attributes::ManaAccountAdapter mana(base, eve::attributes::AttributeResourceKind::Mana); - eve::attributes::StaminaAccountAdapter stamina( - base, eve::attributes::AttributeResourceKind::Stamina); + eve::attributes::ManaAccountAdapter mana(base, eve::attributes::AttributeResourceKind::Mana); + eve::attributes::StaminaAccountAdapter stamina(base, eve::attributes::AttributeResourceKind::Stamina); - const auto manaCost = makeCost("mana", 3); - const auto staminaCost = makeCost("stamina", 2); - auto manaReservation = mana.reserve(manaCost); + const auto manaCost = makeCost("mana", 3); + const auto staminaCost = makeCost("stamina", 2); + auto manaReservation = mana.reserve(manaCost); REQUIRE(manaReservation.ok()); auto committed = mana.commit(manaReservation.value()); REQUIRE(committed.ok()); @@ -116,48 +107,44 @@ TEST_CASE("resource.gameplay.attributeViewsReuseOneCanonicalAccount") { } TEST_CASE("resource.gameplay.cardManaPaymentIsAtomicAndUsesPlayerAccount") { - ecs::Table world; - ecs::ScopedTable guard(world); + ecs::Table world; + ecs::ScopedTable guard(world); eve::card::CardData* card = eve::card::CardData::createCard(); REQUIRE(card != nullptr); card->state()->phase = eve::card::CardState::Hand; eve::card::CardDefinition definition; - definition.id = "firebolt"; + definition.id = "firebolt"; definition.cost = 3; eve::attributes::AttributeSet playerAttributes; playerAttributes.setBase("mana", 10.0); eve::attributes::AttributeSetResourceAccount playerAccount(playerAttributes); - auto paid = eve::card::CardPlayPaymentAdapter::play(*card, definition, playerAccount, - "card.firebolt.1"); + auto paid = eve::card::CardPlayPaymentAdapter::play(*card, definition, playerAccount, "card.firebolt.1"); REQUIRE(paid.ok()); - CHECK_EQ(static_cast(card->state()->phase), - static_cast(eve::card::CardState::Played)); + CHECK_EQ(static_cast(card->state()->phase), static_cast(eve::card::CardState::Played)); CHECK_EQ(playerAttributes.getBase("mana"), 7.0); card->release(); } TEST_CASE("resource.gameplay.cardInsufficientManaLeavesEffectUnchanged") { - ecs::Table world; - ecs::ScopedTable guard(world); + ecs::Table world; + ecs::ScopedTable guard(world); eve::card::CardData* card = eve::card::CardData::createCard(); REQUIRE(card != nullptr); card->state()->phase = eve::card::CardState::Hand; eve::card::CardDefinition definition; - definition.id = "expensive"; + definition.id = "expensive"; definition.cost = 5; eve::attributes::AttributeSet playerAttributes; playerAttributes.setBase("mana", 2.0); eve::attributes::AttributeSetResourceAccount playerAccount(playerAttributes); - auto rejected = eve::card::CardPlayPaymentAdapter::play(*card, definition, playerAccount, - "card.expensive.1"); + auto rejected = eve::card::CardPlayPaymentAdapter::play(*card, definition, playerAccount, "card.expensive.1"); CHECK(!rejected.ok()); - CHECK_EQ(static_cast(card->state()->phase), - static_cast(eve::card::CardState::Hand)); + CHECK_EQ(static_cast(card->state()->phase), static_cast(eve::card::CardState::Hand)); CHECK_EQ(playerAttributes.getBase("mana"), 2.0); card->release(); @@ -166,7 +153,7 @@ TEST_CASE("resource.gameplay.cardInsufficientManaLeavesEffectUnchanged") { TEST_CASE("resource.gameplay.weaponAmmoUsesInventoryAndPrepareFailureDoesNotCharge") { eve::inventory::ItemRegistry::clear(); eve::inventory::ItemDefinition ammo; - ammo.id = "ammo"; + ammo.id = "ammo"; ammo.maxStack = 99; eve::inventory::ItemRegistry::registerItem(ammo); eve::inventory::Bag bag(1); @@ -176,28 +163,25 @@ TEST_CASE("resource.gameplay.weaponAmmoUsesInventoryAndPrepareFailureDoesNotChar eve::inventory::InventoryResourceAccount account(bag); eve::weapon::WeaponDefinition weapon; - weapon.id = "rifle"; - weapon.resource.kind = eve::weapon::ResourceKind::Ammo; - weapon.resource.cost = 1.0f; - int effectValue = 0; + weapon.id = "rifle"; + weapon.resource.kind = eve::weapon::ResourceKind::Ammo; + weapon.resource.cost = 1.0f; + int effectValue = 0; TestEffect effect(effectValue); - auto fired = eve::weapon::WeaponActionAdapter::fire(weapon, account, effect, - "weapon.rifle.1"); + auto fired = eve::weapon::WeaponActionAdapter::fire(weapon, account, effect, "weapon.rifle.1"); REQUIRE(fired.ok()); CHECK_EQ(bag.countItem("ammo"), 4); CHECK_EQ(effectValue, 1); effect.failPrepare = true; - auto rejected = eve::weapon::WeaponActionAdapter::fire(weapon, account, effect, - "weapon.rifle.2"); + auto rejected = eve::weapon::WeaponActionAdapter::fire(weapon, account, effect, "weapon.rifle.2"); CHECK(!rejected.ok()); CHECK_EQ(bag.countItem("ammo"), 4); CHECK_EQ(effectValue, 1); - effect.failPrepare = false; - effect.failCommit = true; - auto commitRejected = eve::weapon::WeaponActionAdapter::fire( - weapon, account, effect, "weapon.rifle.3"); + effect.failPrepare = false; + effect.failCommit = true; + auto commitRejected = eve::weapon::WeaponActionAdapter::fire(weapon, account, effect, "weapon.rifle.3"); CHECK(!commitRejected.ok()); CHECK_EQ(bag.countItem("ammo"), 4); CHECK_EQ(effectValue, 1); @@ -207,19 +191,19 @@ TEST_CASE("resource.gameplay.weaponAmmoUsesInventoryAndPrepareFailureDoesNotChar TEST_CASE("resource.gameplay.itemCostAndAccountNonceAreNotCrossAccount") { eve::inventory::ItemRegistry::clear(); eve::inventory::ItemDefinition potion; - potion.id = "potion"; + potion.id = "potion"; potion.maxStack = 20; eve::inventory::ItemRegistry::registerItem(potion); eve::inventory::Bag firstBag(1); eve::inventory::Bag secondBag(1); - const int potionAdded = firstBag.addItem("potion", 3); + const int potionAdded = firstBag.addItem("potion", 3); CHECK_EQ(potionAdded, 3); eve::inventory::InventoryResourceAccount first(firstBag); eve::inventory::InventoryResourceAccount second(secondBag); - auto costResult = eve::inventory::ItemCostAdapter::itemCost("potion", 2); + auto costResult = eve::inventory::ItemCostAdapter::itemCost("potion", 2); REQUIRE(costResult.ok()); - const auto cost = std::move(costResult).takeValue(); - auto reservation = first.reserve(cost); + const auto cost = std::move(costResult).takeValue(); + auto reservation = first.reserve(cost); REQUIRE(reservation.ok()); auto foreignCommit = second.commit(reservation.value()); CHECK(!foreignCommit.ok()); @@ -232,21 +216,21 @@ TEST_CASE("resource.gameplay.itemCostAndAccountNonceAreNotCrossAccount") { TEST_CASE("resource.gameplay.rtsEconomyUsesLedgerTransaction") { eve::economy::EconomyLedger ledger; eve::rts::RTSEconomyAdapter economy(ledger); - const auto income = makeCost("gold", 10); - auto credited = economy.credit(income); + const auto income = makeCost("gold", 10); + auto credited = economy.credit(income); REQUIRE(credited.ok()); CHECK_EQ(ledger.get("gold"), 10); - const auto price = makeCost("gold", 4); - int effectValue = 0; + const auto price = makeCost("gold", 4); + int effectValue = 0; TestEffect effect(effectValue); - auto paid = economy.pay(price, effect, "rts.build.1"); + auto paid = economy.pay(price, effect, "rts.build.1"); REQUIRE(paid.ok()); CHECK_EQ(ledger.get("gold"), 6); CHECK_EQ(effectValue, 1); effect.failPrepare = true; - auto rejected = economy.pay(price, effect, "rts.build.2"); + auto rejected = economy.pay(price, effect, "rts.build.2"); CHECK(!rejected.ok()); CHECK_EQ(ledger.get("gold"), 6); } @@ -262,32 +246,32 @@ TEST_CASE("resource.gameplay.cardPlayCompositionIsOneAtomicCrossWrite") { REQUIRE(card != nullptr); deck->push(card); - eve::card::CardContainerAdapter source( - eve::container::ContainerId("card:atomic-deck"), eve::card::CardContainerKind::Deck, deck); - eve::card::CardContainerAdapter destination( - eve::container::ContainerId("card:atomic-hand"), eve::card::CardContainerKind::Hand, - nullptr, hand, nullptr, eve::container::Capacity::fixed(2)); - eve::attributes::AttributeSet attributes; + eve::card::CardContainerAdapter source(eve::container::ContainerId("card:atomic-deck"), + eve::card::CardContainerKind::Deck, deck); + eve::card::CardContainerAdapter destination(eve::container::ContainerId("card:atomic-hand"), + eve::card::CardContainerKind::Hand, nullptr, hand, nullptr, + eve::container::Capacity::fixed(2)); + eve::attributes::AttributeSet attributes; attributes.setBase("mana", 10.0); eve::attributes::AttributeSetResourceAccount account(attributes); - int effectValue = 0; - TestEffect effect(effectValue); - eve::card::CardDefinition definition; - definition.id = "bolt"; + int effectValue = 0; + TestEffect effect(effectValue); + eve::card::CardDefinition definition; + definition.id = "bolt"; definition.kind = "spell"; definition.cost = 3; eve::card::CardPlayRequest request; - request.card = card; - request.definition = &definition; - request.playerAccount = &account; - request.transactionId = "card.atomic.success"; - request.composition.source = &source; - request.composition.destination = &destination; - request.composition.sourceSlot = eve::container::SlotIndex(0); + request.card = card; + request.definition = &definition; + request.playerAccount = &account; + request.transactionId = "card.atomic.success"; + request.composition.source = &source; + request.composition.destination = &destination; + request.composition.sourceSlot = eve::container::SlotIndex(0); request.composition.destinationSlot = eve::container::SlotIndex(0); - request.composition.effect = &effect; - auto played = eve::card::CardPlayPaymentAdapter::play(std::move(request)); + request.composition.effect = &effect; + auto played = eve::card::CardPlayPaymentAdapter::play(std::move(request)); REQUIRE(played.ok()); CHECK_EQ(deck->count(), 0); CHECK_EQ(hand->count(), 1); @@ -300,16 +284,16 @@ TEST_CASE("resource.gameplay.cardPlayCompositionIsOneAtomicCrossWrite") { TestEffect rejectedEffect(effectValue); rejectedEffect.failCommit = true; eve::card::CardPlayRequest rejectedRequest; - rejectedRequest.card = rejectedCard; - rejectedRequest.definition = &definition; - rejectedRequest.playerAccount = &account; - rejectedRequest.transactionId = "card.atomic.effect-failure"; - rejectedRequest.composition.source = &source; - rejectedRequest.composition.destination = &destination; - rejectedRequest.composition.sourceSlot = eve::container::SlotIndex(0); + rejectedRequest.card = rejectedCard; + rejectedRequest.definition = &definition; + rejectedRequest.playerAccount = &account; + rejectedRequest.transactionId = "card.atomic.effect-failure"; + rejectedRequest.composition.source = &source; + rejectedRequest.composition.destination = &destination; + rejectedRequest.composition.sourceSlot = eve::container::SlotIndex(0); rejectedRequest.composition.destinationSlot = eve::container::SlotIndex(1); - rejectedRequest.composition.effect = &rejectedEffect; - auto rejected = eve::card::CardPlayPaymentAdapter::play(std::move(rejectedRequest)); + rejectedRequest.composition.effect = &rejectedEffect; + auto rejected = eve::card::CardPlayPaymentAdapter::play(std::move(rejectedRequest)); CHECK(!rejected.ok()); CHECK_EQ(deck->count(), 1); CHECK_EQ(hand->count(), 1); @@ -321,10 +305,10 @@ TEST_CASE("resource.gameplay.rtsBuildUsesSharedActionOrdersProductionPayment") { eve::economy::EconomyLedger ledger; REQUIRE_EQ(ledger.credit("gold", 20), 20); eve::rts::RTSEconomyAdapter economy(ledger); - eve::production::WorkQueue production; - eve::orders::CommandQueue orders; - eve::action::ActionRuntime action; - auto productionSnapshot = [&production]() { + eve::production::WorkQueue production; + eve::orders::CommandQueue orders; + eve::action::ActionRuntime action; + auto productionSnapshot = [&production]() { auto result = production.snapshot(); return std::move(result).expect("production snapshot must serialize"); }; @@ -333,56 +317,56 @@ TEST_CASE("resource.gameplay.rtsBuildUsesSharedActionOrdersProductionPayment") { const auto cost = makeCost("gold", 5); eve::rts::RTSBuildRequest request; - request.production = &production; - request.orders = &orders; - request.action = &action; - request.account = &economy.account(); - request.cost = cost; - request.owner = "faction:one"; + request.production = &production; + request.orders = &orders; + request.action = &action; + request.account = &economy.account(); + request.cost = cost; + request.owner = "faction:one"; request.productionKind = "unit"; - request.product = "worker"; - request.duration = std::move(duration).takeValue(); - request.transactionId = "rts.build.worker"; - auto built = eve::rts::RTSProductionActionAdapter::build(std::move(request)); + request.product = "worker"; + request.duration = std::move(duration).takeValue(); + request.transactionId = "rts.build.worker"; + auto built = eve::rts::RTSProductionActionAdapter::build(std::move(request)); REQUIRE(built.ok()); CHECK_EQ(ledger.get("gold"), 15); CHECK_EQ(production.taskCount(), 1); CHECK_EQ(orders.orderCount(), 1); CHECK_EQ(action.executionCount(), 1u); - const auto productionBeforeActionFailure = productionSnapshot(); - const auto ordersBeforeActionFailure = orders.orderCount(); + const auto productionBeforeActionFailure = productionSnapshot(); + const auto ordersBeforeActionFailure = orders.orderCount(); eve::rts::RTSBuildRequest actionFailure; - actionFailure.production = &production; - actionFailure.orders = &orders; - actionFailure.action = &action; - actionFailure.account = &economy.account(); - actionFailure.cost = cost; - actionFailure.owner = "faction:one"; - actionFailure.productionKind = "unit"; - actionFailure.product = "tank"; - actionFailure.duration = eve::Duration::fromNanoseconds(1000000000); - actionFailure.transactionId = "rts.build.action-failure"; + actionFailure.production = &production; + actionFailure.orders = &orders; + actionFailure.action = &action; + actionFailure.account = &economy.account(); + actionFailure.cost = cost; + actionFailure.owner = "faction:one"; + actionFailure.productionKind = "unit"; + actionFailure.product = "tank"; + actionFailure.duration = eve::Duration::fromNanoseconds(1000000000); + actionFailure.transactionId = "rts.build.action-failure"; actionFailure.actionDefinition.condition = eve::decision::Condition::hasTag("missing"); - auto failedAction = eve::rts::RTSProductionActionAdapter::build(std::move(actionFailure)); + auto failedAction = eve::rts::RTSProductionActionAdapter::build(std::move(actionFailure)); CHECK(!failedAction.ok()); CHECK_EQ(ledger.get("gold"), 15); CHECK_EQ(productionSnapshot(), productionBeforeActionFailure); CHECK_EQ(orders.orderCount(), ordersBeforeActionFailure); eve::rts::RTSBuildRequest queueFailure; - queueFailure.production = &production; - queueFailure.orders = &orders; - queueFailure.action = &action; - queueFailure.account = &economy.account(); - queueFailure.cost = cost; - queueFailure.owner = "faction:one"; + queueFailure.production = &production; + queueFailure.orders = &orders; + queueFailure.action = &action; + queueFailure.account = &economy.account(); + queueFailure.cost = cost; + queueFailure.owner = "faction:one"; queueFailure.productionKind = "unit"; - queueFailure.product = "tank"; - queueFailure.duration = eve::Duration::fromNanoseconds(1000000000); + queueFailure.product = "tank"; + queueFailure.duration = eve::Duration::fromNanoseconds(1000000000); queueFailure.orderKind.clear(); queueFailure.transactionId = "rts.build.queue-failure"; - auto failedQueue = eve::rts::RTSProductionActionAdapter::build(std::move(queueFailure)); + auto failedQueue = eve::rts::RTSProductionActionAdapter::build(std::move(queueFailure)); CHECK(!failedQueue.ok()); CHECK_EQ(ledger.get("gold"), 15); CHECK_EQ(productionSnapshot(), productionBeforeActionFailure); diff --git a/test/resource_uri.cpp b/test/resource_uri.cpp index f814dbf3..9a4071be 100644 --- a/test/resource_uri.cpp +++ b/test/resource_uri.cpp @@ -15,7 +15,7 @@ namespace { class MemoryFileSystem final : public eve::service::IFileSystem { public: bool readFile(const std::string& path, std::vector& out) override { - lastPath = path; + lastPath = path; const auto found = files.find(path); if (found == files.end()) return false; out = found->second; @@ -27,7 +27,7 @@ public: bool fileExists(const std::string& path) override { return files.find(path) != files.end(); } std::map> files; - std::string lastPath; + std::string lastPath; }; template @@ -39,11 +39,9 @@ T take(eve::Result result) { } // namespace TEST_CASE("resourceUri.acceptsOnlyKnownVirtualSchemes") { - for (const auto text : {"asset://01020304-0506-0708-090a-0b0c0d0e0f10", - "project://textures/wood.png?quality=high#albedo", - "builtin://ui/default-font", - "generated://terrain/seed-1", - "memory://session/preview"}) { + for (const auto text : + {"asset://01020304-0506-0708-090a-0b0c0d0e0f10", "project://textures/wood.png?quality=high#albedo", + "builtin://ui/default-font", "generated://terrain/seed-1", "memory://session/preview"}) { auto uri = eve::ResourceUri::parse(text); REQUIRE(uri.ok()); CHECK(!uri.value().format().empty()); @@ -99,7 +97,7 @@ TEST_CASE("resourceReader.typedUriIsSourceOfTruth") { filesystem.files["textures/wood.bin"] = {1, 2, 3}; eve::filesystem::ResourceReader reader(filesystem); - const auto uri = take(eve::ResourceUri::parse("project://textures/wood.bin")); + const auto uri = take(eve::ResourceUri::parse("project://textures/wood.bin")); const auto bytes = take(reader.read(uri)); CHECK_EQ(bytes, std::vector({1, 2, 3})); CHECK_EQ(filesystem.lastPath, std::string("textures/wood.bin")); @@ -115,7 +113,7 @@ TEST_CASE("resourceReader.doesNotSilentlyFallbackAcrossNamespaces") { eve::filesystem::ResourceReader reader(filesystem); const auto builtin = take(eve::ResourceUri::parse("builtin://textures/wood.bin")); - auto result = reader.read(builtin); + auto result = reader.read(builtin); CHECK(!result.ok()); CHECK_EQ(result.code(), eve::StatusCode::Unsupported); CHECK(filesystem.lastPath.empty()); diff --git a/test/result.cpp b/test/result.cpp index 8b09a892..8992e27f 100644 --- a/test/result.cpp +++ b/test/result.cpp @@ -32,8 +32,7 @@ TEST_CASE("common.result.successAndObservationAccessors") { } TEST_CASE("common.result.failureCarriesStructuredDiagnostic") { - auto diagnostic = Diagnostic::error(DiagnosticCode::InvalidArgument, "width must be positive", - "window.width"); + auto diagnostic = Diagnostic::error(DiagnosticCode::InvalidArgument, "width must be positive", "window.width"); diagnostic.addDetail("received", "0"); auto result = Result::failure(std::move(diagnostic)); @@ -57,18 +56,19 @@ TEST_CASE("common.result.ignoreAndExpectAreExplicitConsumption") { const int value = Result::success(7).expect("initialization must succeed"); CHECK(value == 7); - CHECK_THROWS((Result::failure( - Diagnostic::error(DiagnosticCode::Unsupported, "not available")) + CHECK_THROWS((Result::failure(Diagnostic::error(DiagnosticCode::Unsupported, "not available")) .expect("feature must be available"), 0)); } TEST_CASE("common.result.unobservedDestructionIsCaughtInDebug") { #if !defined(ZEROERR_NO_ASSERT) - CHECK_THROWS(([] { - auto result = Result::success(1); - (void)result; - }(), 0)); + CHECK_THROWS(( + [] { + auto result = Result::success(1); + (void)result; + }(), + 0)); #else CHECK(true); #endif @@ -76,7 +76,7 @@ TEST_CASE("common.result.unobservedDestructionIsCaughtInDebug") { TEST_CASE("common.result.moveTransfersObservationResponsibility") { auto source = Result>::success(std::make_unique(9)); - auto moved = std::move(source); + auto moved = std::move(source); REQUIRE(moved.ok()); REQUIRE(moved.value().get() != nullptr); CHECK(*moved.value() == 9); @@ -101,14 +101,12 @@ TEST_CASE("common.result.moveAssignmentDoesNotOverwriteUncheckedTarget") { } TEST_CASE("common.result.compositionPreservesFailureStatus") { - auto success = Result::success(3).andThen([](int value) { - return Result::success(std::to_string(value + 1)); - }); + auto success = Result::success(3).andThen( + [](int value) { return Result::success(std::to_string(value + 1)); }); REQUIRE(success.ok()); CHECK(success.value() == "4"); - auto failure = Result::failure( - Diagnostic::error(DiagnosticCode::NotFound, "entity not found")); + auto failure = Result::failure(Diagnostic::error(DiagnosticCode::NotFound, "entity not found")); auto recovered = std::move(failure).orElse([&](const Status& status) { CHECK(status.code() == StatusCode::NotFound); return Result::success(99); @@ -122,9 +120,8 @@ TEST_CASE("common.result.voidStatusAndBoolCheck") { CHECK(result.code() == StatusCode::Applied); result.value(); - auto failed = Result::failure(Status::failure( - StatusCode::Conflict, - Diagnostic::error(DiagnosticCode::Conflict, "revision changed"))); + auto failed = Result::failure( + Status::failure(StatusCode::Conflict, Diagnostic::error(DiagnosticCode::Conflict, "revision changed"))); CHECK(!failed); REQUIRE(failed.error() != nullptr); CHECK(failed.error()->code() == DiagnosticCode::Conflict); diff --git a/test/roguelike_generator.cpp b/test/roguelike_generator.cpp index 871bd150..a20887ce 100644 --- a/test/roguelike_generator.cpp +++ b/test/roguelike_generator.cpp @@ -19,13 +19,13 @@ struct ParamsLease { ProcgenParamsHandleRef handle{}; ParamsLease() = default; explicit ParamsLease(ProcgenParamsHandleRef value) : handle(value) {} - ParamsLease(const ParamsLease&) = delete; - ParamsLease& operator=(const ParamsLease&) = delete; - ParamsLease(ParamsLease&& other) noexcept : handle(other.handle) { other.handle = {}; } - ParamsLease& operator=(ParamsLease&& other) noexcept { + ParamsLease(const ParamsLease &) = delete; + ParamsLease &operator=(const ParamsLease &) = delete; + ParamsLease(ParamsLease &&other) noexcept : handle(other.handle) { other.handle = {}; } + ParamsLease &operator=(ParamsLease &&other) noexcept { if (this == &other) return *this; reset(); - handle = other.handle; + handle = other.handle; other.handle = {}; return *this; } @@ -36,22 +36,20 @@ struct ParamsLease { result.ignore("roguelike params cleanup"); handle = {}; } - [[nodiscard]] eve::script::Borrowed view() const noexcept { - return Procgen::resolve(handle); - } + [[nodiscard]] eve::script::Borrowed view() const noexcept { return Procgen::resolve(handle); } }; struct GridLease { ProcgenGridHandleRef handle{}; GridLease() = default; explicit GridLease(ProcgenGridHandleRef value) : handle(value) {} - GridLease(const GridLease&) = delete; - GridLease& operator=(const GridLease&) = delete; - GridLease(GridLease&& other) noexcept : handle(other.handle) { other.handle = {}; } - GridLease& operator=(GridLease&& other) noexcept { + GridLease(const GridLease &) = delete; + GridLease &operator=(const GridLease &) = delete; + GridLease(GridLease &&other) noexcept : handle(other.handle) { other.handle = {}; } + GridLease &operator=(GridLease &&other) noexcept { if (this == &other) return *this; reset(); - handle = other.handle; + handle = other.handle; other.handle = {}; return *this; } @@ -62,9 +60,7 @@ struct GridLease { result.ignore("roguelike grid cleanup"); handle = {}; } - [[nodiscard]] eve::script::Borrowed view() const noexcept { - return Procgen::resolve(handle); - } + [[nodiscard]] eve::script::Borrowed view() const noexcept { return Procgen::resolve(handle); } }; ParamsLease requireParams() { @@ -73,8 +69,7 @@ ParamsLease requireParams() { return ParamsLease(std::move(result).takeValue()); } -GridLease requireGrid(Procgen& proc, const std::string& algorithm, - ProcgenParamsHandleRef params) { +GridLease requireGrid(Procgen &proc, const std::string &algorithm, ProcgenParamsHandleRef params) { auto result = proc.generateHandle(algorithm, params); REQUIRE(result.ok()); return GridLease(std::move(result).takeValue()); @@ -151,24 +146,24 @@ TEST_CASE("procgen.roguelike.reproducible") { Procgen *mod = Procgen::create(); CHECK(mod->hasAlgorithm("level.roguelike")); - auto p = requireParams(); + auto p = requireParams(); auto pView = p.view(); REQUIRE(pView.isBound()); pView->setSeed(42); pView->setSize(48, 32); pView->setInt("roomCount", 10); auto aLease = requireGrid(*mod, "level.roguelike", p.handle); - auto a = aLease.view(); + auto a = aLease.view(); REQUIRE(a.isBound()); - auto q = requireParams(); + auto q = requireParams(); auto qView = q.view(); REQUIRE(qView.isBound()); qView->setSeed(42); qView->setSize(48, 32); qView->setInt("roomCount", 10); auto bLease = requireGrid(*mod, "level.roguelike", q.handle); - auto b = bLease.view(); + auto b = bLease.view(); REQUIRE(b.isBound()); // Same seed -> identical cells and detail. @@ -178,8 +173,8 @@ TEST_CASE("procgen.roguelike.reproducible") { TEST_CASE("procgen.roguelike.structure") { Procgen *mod = Procgen::create(); - auto p = requireParams(); - auto pView = p.view(); + auto p = requireParams(); + auto pView = p.view(); REQUIRE(pView.isBound()); pView->setSeed(7); pView->setSize(48, 32); @@ -187,7 +182,7 @@ TEST_CASE("procgen.roguelike.structure") { pView->setFloat("decorDensity", 0.06f); pView->setString("decorSet", "mixed"); auto gLease = requireGrid(*mod, "level.roguelike", p.handle); - auto g = gLease.view(); + auto g = gLease.view(); REQUIRE(g.isBound()); const int floorCount = countSemantic(*g, Semantic::Floor) + countSemantic(*g, Semantic::Corridor); @@ -220,15 +215,15 @@ TEST_CASE("procgen.roguelike.structure") { TEST_CASE("procgen.roguelike.seedVaries") { Procgen *mod = Procgen::create(); - auto run = [&](uint32_t seed) { - auto p = requireParams(); + auto run = [&](uint32_t seed) { + auto p = requireParams(); auto pView = p.view(); REQUIRE(pView.isBound()); pView->setSeed(seed); pView->setSize(40, 28); pView->setInt("roomCount", 9); auto gLease = requireGrid(*mod, "level.roguelike", p.handle); - auto g = gLease.view(); + auto g = gLease.view(); REQUIRE(g.isBound()); Grid2D copy = *g; return copy; @@ -242,8 +237,8 @@ TEST_CASE("procgen.roguelike.seedVaries") { TEST_CASE("procgen.roguelike.rulesChangeLayout") { Procgen *mod = Procgen::create(); - auto gen = [&](int rooms, const std::string &pattern, const std::string &style) { - auto p = requireParams(); + auto gen = [&](int rooms, const std::string &pattern, const std::string &style) { + auto p = requireParams(); auto pView = p.view(); REQUIRE(pView.isBound()); pView->setSeed(11); @@ -252,7 +247,7 @@ TEST_CASE("procgen.roguelike.rulesChangeLayout") { pView->setString("floorPattern", pattern); pView->setString("corridorStyle", style); auto gLease = requireGrid(*mod, "level.roguelike", p.handle); - auto g = gLease.view(); + auto g = gLease.view(); REQUIRE(g.isBound()); Grid2D copy = *g; return copy; @@ -271,27 +266,27 @@ TEST_CASE("procgen.roguelike.rulesChangeLayout") { TEST_CASE("procgen.roguelike.errors") { Procgen *mod = Procgen::create(); - auto p = requireParams(); - auto pView = p.view(); + auto p = requireParams(); + auto pView = p.view(); REQUIRE(pView.isBound()); pView->setSeed(1); pView->setSize(4, 4); // too small auto failed = mod->generateHandle("level.roguelike", p.handle); CHECK(!failed.ok()); - const eve::Diagnostic* diagnostic = failed.error(); + const eve::Diagnostic *diagnostic = failed.error(); REQUIRE(diagnostic != nullptr); CHECK_EQ(diagnostic->code(), eve::DiagnosticCode::Failed); } TEST_CASE("procgen.roguelike.manualFillDetail") { Procgen *mod = Procgen::create(); - auto p = requireParams(); - auto pView = p.view(); + auto p = requireParams(); + auto pView = p.view(); REQUIRE(pView.isBound()); pView->setSeed(3); pView->setSize(32, 24); auto gLease = requireGrid(*mod, "level.roguelike", p.handle); - auto g = gLease.view(); + auto g = gLease.view(); REQUIRE(g.isBound()); // Ultimate control: script can override any cell / detail after generation. @@ -306,15 +301,15 @@ TEST_CASE("procgen.roguelike.manualFillDetail") { TEST_CASE("procgen.autotileGrid.postProcess") { Procgen *mod = Procgen::create(); - auto p = requireParams(); - auto pView = p.view(); + auto p = requireParams(); + auto pView = p.view(); REQUIRE(pView.isBound()); pView->setSeed(9); pView->setSize(40, 30); pView->setInt("roomCount", 8); pView->setInt("autotile", 0); // disable built-in autotile to test the helper alone auto gLease = requireGrid(*mod, "level.roguelike", p.handle); - auto g = gLease.view(); + auto g = gLease.view(); REQUIRE(g.isBound()); auto autotile = mod->autotileGrid(gLease.handle); CHECK(autotile.ok()); diff --git a/test/rpg_skill_condition.cpp b/test/rpg_skill_condition.cpp index 368fecdc..2006ac9e 100644 --- a/test/rpg_skill_condition.cpp +++ b/test/rpg_skill_condition.cpp @@ -1,7 +1,7 @@ +#include "rpg/RPGActor.h" #include "rpg/Skill.h" #include "rpg/SkillCondition.h" #include "rpg/SkillSystem.h" -#include "rpg/RPGActor.h" #include "zeroerr/assert.h" #include "zeroerr/unittest.h" @@ -16,7 +16,7 @@ TEST_CASE("rpg.skillConditionAdapterReadsCanonicalSkillState") { actor->setBaseAttribute("mana", 20.0); actor->learnSkill("skill.fire"); SkillDefinition definition; - definition.id = "skill.fire"; + definition.id = "skill.fire"; definition.tags = {"fire"}; const auto condition = Condition::all({ @@ -26,7 +26,7 @@ TEST_CASE("rpg.skillConditionAdapterReadsCanonicalSkillState") { Condition::compare("skill.cooldown", CompareOperator::Equal, 0.0), Condition::stateEquals("skill.phase", "idle"), }); - const auto result = SkillConditionAdapter::evaluate(actor, definition, condition); + const auto result = SkillConditionAdapter::evaluate(actor, definition, condition); REQUIRE(result.passed()); CHECK_EQ(static_cast(result.reasonCode()), static_cast(ConditionReasonCode::Passed)); @@ -43,7 +43,7 @@ TEST_CASE("rpg.skillSystemUsesConditionResultAsCompatibilityReason") { using namespace eve::rpg; SkillDefinition definition; - definition.id = "skill.guard"; + definition.id = "skill.guard"; definition.castCondition = Condition::hasTag("silenced"); SkillRegistry::registerSkill(definition); RPGActor* actor = RPGActor::createActor(); @@ -59,25 +59,22 @@ TEST_CASE("rpg.skillConditionPolicyAndAuthorityAreReadOnlyProviders") { using namespace eve::decision; using namespace eve::rpg; - RPGActor* actor = RPGActor::createActor(); + RPGActor* actor = RPGActor::createActor(); SkillDefinition definition; - definition.id = "skill.policy"; - int authorityCalls = 0; - int policyCalls = 0; + definition.id = "skill.policy"; + int authorityCalls = 0; + int policyCalls = 0; SkillConditionQueries queries; queries.authority = [&authorityCalls](std::string_view scope) -> std::optional { ++authorityCalls; return scope == "cast"; }; - queries.policy = [&policyCalls](std::string_view name, - const eve::Value&) -> std::optional { + queries.policy = [&policyCalls](std::string_view name, const eve::Value&) -> std::optional { ++policyCalls; - return name == "safe" ? std::optional(ConditionResult::success()) - : std::nullopt; + return name == "safe" ? std::optional(ConditionResult::success()) : std::nullopt; }; const auto result = SkillConditionAdapter::evaluate( - actor, definition, - Condition::all({Condition::authorityCheck("cast"), Condition::policyCall("safe")}), queries); + actor, definition, Condition::all({Condition::authorityCheck("cast"), Condition::policyCall("safe")}), queries); REQUIRE(result.passed()); CHECK_EQ(authorityCalls, 1); CHECK_EQ(policyCalls, 1); diff --git a/test/rts_composition.cpp b/test/rts_composition.cpp index 315df5e0..95680555 100644 --- a/test/rts_composition.cpp +++ b/test/rts_composition.cpp @@ -57,15 +57,15 @@ static_assert(!std::is_base_of_v); static_assert(!std::is_base_of_v); TEST_CASE("rts.compositionRootsAndBatchViews") { - ecs::Table world; + ecs::Table world; ecs::ScopedTable guard(world); - Unit* unit = Unit::createUnit(); - Building* building = Building::createBuilding(); - unit->motion()->x = 10.0f; + Unit* unit = Unit::createUnit(); + Building* building = Building::createBuilding(); + unit->motion()->x = 10.0f; building->placement()->cellX = 4; - int units = 0; + int units = 0; auto unitView = ecs::View(); for (auto it = unitView.begin(); it != unitView.end(); ++it) { auto [identity, motion, orders] = *it; @@ -76,7 +76,7 @@ TEST_CASE("rts.compositionRootsAndBatchViews") { } CHECK_EQ(units, 1); - int buildings = 0; + int buildings = 0; auto buildingView = ecs::View(); for (auto it = buildingView.begin(); it != buildingView.end(); ++it) { auto [identity, placement] = *it; @@ -91,15 +91,15 @@ TEST_CASE("rts.compositionRootsAndBatchViews") { } TEST_CASE("rts.baseViewIncludesSubclassButNotOtherRoot") { - ecs::Table world; + ecs::Table world; ecs::ScopedTable guard(world); - Unit* unit = Unit::createUnit(); + Unit* unit = Unit::createUnit(); Scout* scout = Scout::create(); initializeScout(*scout); Building* building = Building::createBuilding(); - int allUnits = 0; + int allUnits = 0; auto baseView = ecs::View(); for (auto it = baseView.begin(); it != baseView.end(); ++it) { auto [identity, motion] = *it; @@ -109,17 +109,17 @@ TEST_CASE("rts.baseViewIncludesSubclassButNotOtherRoot") { } CHECK_EQ(allUnits, 2); - int exactScouts = 0; - auto scoutView = ecs::View(); + int exactScouts = 0; + auto scoutView = ecs::View(); for (auto it = scoutView.begin(); it != scoutView.end(); ++it) ++exactScouts; CHECK_EQ(exactScouts, 1); - int unitOrderCount = 0; - auto orderView = ecs::View(); + int unitOrderCount = 0; + auto orderView = ecs::View(); for (auto it = orderView.begin(); it != orderView.end(); ++it) ++unitOrderCount; CHECK_EQ(unitOrderCount, 2); - int buildingAsUnit = 0; + int buildingAsUnit = 0; auto noBuildingView = ecs::View(); for (auto it = noBuildingView.begin(); it != noBuildingView.end(); ++it) { auto [identity, motion] = *it; @@ -135,12 +135,12 @@ TEST_CASE("rts.baseViewIncludesSubclassButNotOtherRoot") { } TEST_CASE("rts.typedLinkDetectsStaleTarget") { - ecs::Table world; + ecs::Table world; ecs::ScopedTable guard(world); Faction* faction = Faction::createFaction(); - Unit* unit = Unit::createUnit(); - auto linked = eve::rts::FactionLink::bind(ecs::handle_of(faction)); + Unit* unit = Unit::createUnit(); + auto linked = eve::rts::FactionLink::bind(ecs::handle_of(faction)); REQUIRE(linked.ok()); unit->faction()->link = std::move(linked).takeValue(); CHECK(unit->faction()->link.isBound()); @@ -156,7 +156,7 @@ TEST_CASE("rts.typedLinkDetectsStaleTarget") { TEST_CASE("rts.formationPlannerIsDeterministic") { const FormationSpec spec{FormationKind::Grid, 10.0f, 2}; - auto planned = eve::rts::FormationPlanner::plan(3, {100.0f, 200.0f}, spec); + auto planned = eve::rts::FormationPlanner::plan(3, {100.0f, 200.0f}, spec); REQUIRE(planned.ok()); const auto positions = std::move(planned).takeValue(); REQUIRE_EQ(positions.size(), 3u); @@ -169,21 +169,21 @@ TEST_CASE("rts.formationPlannerIsDeterministic") { } TEST_CASE("rts.commandFanOutUsesGenericOrdersAndFormation") { - ecs::Table world; + ecs::Table world; ecs::ScopedTable guard(world); - std::vector units; + std::vector units; std::vector handles; for (int index = 0; index < 3; ++index) { - Unit* unit = Unit::createUnit(); + Unit* unit = Unit::createUnit(); unit->motion()->speed = 100.0f; units.push_back(unit); handles.push_back(ecs::handle_of(unit)); } - const CommandSpec command{OrderKind::Move, {100.0f, 200.0f}, {}, 0, 0.0}; + const CommandSpec command{OrderKind::Move, {100.0f, 200.0f}, {}, 0, 0.0}; const FormationSpec formation{FormationKind::Grid, 10.0f, 2}; - auto receiptResult = eve::rts::CommandFanOutSystem::fanOut(handles, command, formation); + auto receiptResult = eve::rts::CommandFanOutSystem::fanOut(handles, command, formation); REQUIRE(receiptResult.ok()); const auto receipt = std::move(receiptResult).takeValue(); CHECK_EQ(receipt.requested, 3u); @@ -211,20 +211,20 @@ TEST_CASE("rts.commandFanOutUsesGenericOrdersAndFormation") { } TEST_CASE("rts.actionAdapterReusesSharedActionRuntime") { - ecs::Table world; + ecs::Table world; ecs::ScopedTable guard(world); - Unit* unit = Unit::createUnit(); + Unit* unit = Unit::createUnit(); unit->motion()->speed = 10.0f; const CommandSpec move{OrderKind::Move, {20.0f, 0.0f}, {}, 0, 0.0}; - auto order = unit->orders()->values.enqueue(move); + auto order = unit->orders()->values.enqueue(move); REQUIRE(order.ok()); std::move(order).takeValue(); eve::action::ActionRuntime runtime; - eve::rts::ActionAdapter adapter(runtime); - const eve::SimulationStep first{eve::SimulationTick{1}, eve::Duration::fromSeconds(1.0).expect("first dt")}; - auto moved = eve::rts::MotionSystem::step(first); + eve::rts::ActionAdapter adapter(runtime); + const eve::SimulationStep first{eve::SimulationTick{1}, eve::Duration::fromSeconds(1.0).expect("first dt")}; + auto moved = eve::rts::MotionSystem::step(first); REQUIRE(moved.ok()); std::move(moved).takeValue(); auto pending = eve::rts::OrderActionSystem::step(first, adapter); @@ -234,7 +234,7 @@ TEST_CASE("rts.actionAdapterReusesSharedActionRuntime") { CHECK_EQ(runtime.executionCount(), 1u); const eve::SimulationStep second{eve::SimulationTick{2}, eve::Duration::fromSeconds(1.0).expect("second dt")}; - auto completed = eve::rts::MotionSystem::step(second); + auto completed = eve::rts::MotionSystem::step(second); REQUIRE(completed.ok()); std::move(completed).takeValue(); auto action = eve::rts::OrderActionSystem::step(second, adapter); @@ -247,7 +247,7 @@ TEST_CASE("rts.actionAdapterReusesSharedActionRuntime") { TEST_CASE("rts.buildingProductionAndModuleFactoryCompose") { eve::rts::RTS module; - auto unitResult = module.newUnit(subject("00000000-0000-7000-8000-000000000011")); + auto unitResult = module.newUnit(subject("00000000-0000-7000-8000-000000000011")); REQUIRE(unitResult.ok()); Unit* unit = std::move(unitResult).takeValue(); CHECK_EQ(module.unitCount(), 1u); @@ -259,15 +259,14 @@ TEST_CASE("rts.buildingProductionAndModuleFactoryCompose") { auto duration = eve::Duration::fromSeconds(1.0); REQUIRE(duration.ok()); - auto task = building->production()->values.enqueue("faction", "train", "worker", - std::move(duration).takeValue()); + auto task = building->production()->values.enqueue("faction", "train", "worker", std::move(duration).takeValue()); REQUIRE(task.ok()); std::move(task).takeValue(); eve::action::ActionRuntime runtime; - eve::rts::ActionAdapter adapter(runtime); - const eve::SimulationStep step{eve::SimulationTick{1}, eve::Duration::fromNanoseconds(500000000)}; - auto processed = module.step(step, adapter); + eve::rts::ActionAdapter adapter(runtime); + const eve::SimulationStep step{eve::SimulationTick{1}, eve::Duration::fromNanoseconds(500000000)}; + auto processed = module.step(step, adapter); REQUIRE(processed.ok()); CHECK(processed.value() >= 2u); CHECK_EQ(building->production()->values.taskCount(), 1u); diff --git a/test/runtime_handle.cpp b/test/runtime_handle.cpp index d7881816..4bbd1982 100644 --- a/test/runtime_handle.cpp +++ b/test/runtime_handle.cpp @@ -42,7 +42,7 @@ TEST_CASE("common.runtimeHandle.invalidAndPackedBoundary") { TEST_CASE("common.runtimeHandle.bumpRejectsOverflow") { constexpr UiHandle first(4u, 1u); - const auto bumped = first.nextGeneration(); + const auto bumped = first.nextGeneration(); REQUIRE(bumped.has_value()); CHECK_EQ(bumped->index(), first.index()); CHECK_EQ(bumped->generation(), 2u); diff --git a/test/scene.cpp b/test/scene.cpp index 07b8bacf..1f11016c 100644 --- a/test/scene.cpp +++ b/test/scene.cpp @@ -141,7 +141,7 @@ TEST_CASE("Scene.TransformSystem.parentChildWorld") { h->setTree(node("root", {node("child").withPosition(1.f, 0.f, 0.f)}).withPosition(10.f, 0.f, 0.f)); TransformSystem::updateHost(h); - SceneNode *root = sceneValue(h->findById("root")); + SceneNode *root = sceneValue(h->findById("root")); SceneNode *child = sceneValue(h->findById("child")); REQUIRE(root != nullptr); REQUIRE(child != nullptr); @@ -219,8 +219,8 @@ TEST_CASE("Scene.builder.mountBuildAs") { } TEST_CASE("Scene.identity.mountFindAndOwnershipContracts") { - Scene *mod = Scene::create(); - auto mounted = mod->mountAs("checked", node("root", {node("child")})); + Scene *mod = Scene::create(); + auto mounted = mod->mountAs("checked", node("root", {node("child")})); REQUIRE(mounted.ok()); SceneHost *host = mounted.value(); REQUIRE(host != nullptr); @@ -489,8 +489,8 @@ TEST_CASE("Scene.render.parentChildOrbitPreview") { float prevHX = hand->transform()->x; for (int frame = 0; frame < 90; ++frame) { const float t = float(frame) * 0.06f; - SceneNode *arm = sceneValue(h->findById("arm")); - SceneNode *moonN = sceneValue(h->findById("moon")); + SceneNode *arm = sceneValue(h->findById("arm")); + SceneNode *moonN = sceneValue(h->findById("moon")); REQUIRE(arm != nullptr); REQUIRE(moonN != nullptr); arm->roll = t; @@ -531,7 +531,7 @@ TEST_CASE("Scene.objectId.preservedAcrossFullRebuild") { // Full rebuild keeps the binding marker on same-id nodes, not on new ones. h->setTree(node("root", {node("p"), node("q")})); SceneNode *p2 = sceneValue(h->findById("p")); - SceneNode *q = sceneValue(h->findById("q")); + SceneNode *q = sceneValue(h->findById("q")); REQUIRE(p2 != nullptr); REQUIRE(q != nullptr); CHECK_EQ(uint32_t(p2->objectId), uint32_t(o->id)); @@ -635,9 +635,8 @@ TEST_CASE("Scene.link.physics2D") { TEST_CASE("Scene.api.transformGettersAndSpaceConversion") { Scene *mod = Scene::create(); - mod->mountAs("api1", - node("root", {node("child").withPosition(1.f, 0.f, 0.f)}) - .withPosition(10.f, 0.f, 0.f)).ignore("test setup"); + mod->mountAs("api1", node("root", {node("child").withPosition(1.f, 0.f, 0.f)}).withPosition(10.f, 0.f, 0.f)) + .ignore("test setup"); mod->updateTransformsAll(); auto p = mod->getNodePositionAt("api1", "child"); @@ -797,9 +796,9 @@ TEST_CASE("Scene.transform.incrementalDirtySubtree") { // API edit on "a" → incremental path; a1 follows, sibling b untouched. CHECK(mod->setNodePosition("a", 5.f, 0.f, 0.f)); mod->updateTransformsAll(); - SceneHost *h = sceneValue(mod->findHost("inc")); + SceneHost *h = sceneValue(mod->findHost("inc")); SceneNode *a1 = sceneValue(h->findById("a1")); - SceneNode *b = sceneValue(h->findById("b")); + SceneNode *b = sceneValue(h->findById("b")); REQUIRE(a1 != nullptr); REQUIRE(b != nullptr); CHECK(approxEq(a1->world[3][0], 5.f)); @@ -858,15 +857,15 @@ TEST_CASE("Scene.reconcile.movePreservesIdentity") { TEST_CASE("Scene.serialize.roundTrip") { Scene *mod = Scene::create(); - mod->mountAs("s1", - node("root", {node("a") - .withPosition(1.f, 2.f, 3.f) - .withRotation(0.5f, 0.25f, 0.1f) - .withTag("enemy") - .withLayer(2) - .withBounds(-1.f, -1.f, -1.f, 1.f, 1.f, 1.f), - node("b", {node("b1")})}) - .withPosition(10.f, 0.f, 0.f)).ignore("test setup"); + mod->mountAs("s1", node("root", {node("a") + .withPosition(1.f, 2.f, 3.f) + .withRotation(0.5f, 0.25f, 0.1f) + .withTag("enemy") + .withLayer(2) + .withBounds(-1.f, -1.f, -1.f, 1.f, 1.f, 1.f), + node("b", {node("b1")})}) + .withPosition(10.f, 0.f, 0.f)) + .ignore("test setup"); const std::string json = mod->serializeHostAt("s1"); CHECK(json.find("\"root\"") != std::string::npos); @@ -893,8 +892,8 @@ TEST_CASE("Scene.pick.rayAndScreen") { Scene *mod = Scene::create(); mod->mountAs("pk", node("root", {node("target").withBounds(-1.f, -1.f, -1.f, 1.f, 1.f, 1.f), - node("back").withBounds(-1.f, -1.f, -1.f, 1.f, 1.f, 1.f) - .withPosition(0.f, 0.f, -10.f)})).ignore("test setup"); + node("back").withBounds(-1.f, -1.f, -1.f, 1.f, 1.f, 1.f).withPosition(0.f, 0.f, -10.f)})) + .ignore("test setup"); mod->updateTransformsAll(); CHECK(mod->pickRayAt("pk", 0.f, 0.f, 5.f, 0.f, 0.f, -1.f) == "target"); CHECK(mod->pickRayAt("pk", 10.f, 0.f, 5.f, 0.f, 0.f, -1.f).empty()); @@ -911,8 +910,8 @@ TEST_CASE("Scene.cull.frustum") { Scene *mod = Scene::create(); mod->mountAs("cu", node("root", {node("front").withBounds(-1.f, -1.f, -1.f, 1.f, 1.f, 1.f), - node("side").withBounds(-1.f, -1.f, -1.f, 1.f, 1.f, 1.f) - .withPosition(10.f, 0.f, 0.f)})).ignore("test setup"); + node("side").withBounds(-1.f, -1.f, -1.f, 1.f, 1.f, 1.f).withPosition(10.f, 0.f, 0.f)})) + .ignore("test setup"); auto *cam = eve::graphics::Camera3D::createCamera(); cam->setEye(0.f, 0.f, 5.f); cam->setTarget(0.f, 0.f, 0.f); @@ -931,8 +930,8 @@ TEST_CASE("Scene.cull.frustum") { TEST_CASE("Scene.spatial.syncOctree") { Scene *mod = Scene::create(); mod->mountAs("sp", - node("root", {node("a").withBounds(-2.f, -2.f, -2.f, 2.f, 2.f, 2.f) - .withPosition(5.f, 0.f, 0.f)})).ignore("test setup"); + node("root", {node("a").withBounds(-2.f, -2.f, -2.f, 2.f, 2.f, 2.f).withPosition(5.f, 0.f, 0.f)})) + .ignore("test setup"); mod->updateTransformsAll(); eve::spatial::Octree ot(-100.f, -100.f, -100.f, 100.f, 100.f, 100.f); REQUIRE(mod->syncSpatialIndexAt("sp", &ot)); diff --git a/test/sceneloader.cpp b/test/sceneloader.cpp index 6f343e4d..8712c468 100644 --- a/test/sceneloader.cpp +++ b/test/sceneloader.cpp @@ -322,7 +322,7 @@ TEST_CASE("SceneLoader.load.buildsGameObjectTreeWithRenderables") { // Hot-reload with an identical file is a fast no-op. SceneDiff out; - auto unchanged = loader->reload(name, &out); + auto unchanged = loader->reload(name, &out); REQUIRE(unchanged.ok()); CHECK(!unchanged.value()); CHECK(out.empty()); @@ -521,7 +521,7 @@ TEST_CASE("SceneLoader.load.pbrTextureCacheReusesTexture") { // Reload path stays healthy (diff-based) and reuses the same cached texture. SceneDiff out; - auto unchanged = loader->reload("sl_cache.obj", &out); + auto unchanged = loader->reload("sl_cache.obj", &out); REQUIRE(unchanged.ok()); CHECK(!unchanged.value()); CHECK(out.empty()); diff --git a/test/schema.cpp b/test/schema.cpp index 294d358e..b901c02c 100644 --- a/test/schema.cpp +++ b/test/schema.cpp @@ -52,8 +52,8 @@ TEST_CASE("schema.registry.supportsMultipleVersionsAndExactResolution") { legacyField.required = true; v1.fields.push_back(legacyField); SchemaDefinition v2 = v1; - v2.version = 2; - v2.title = "Unit v2"; + v2.version = 2; + v2.title = "Unit v2"; CHECK(registrationSucceeded(SchemaRegistry::registerVersioned(v1))); CHECK(registrationSucceeded(SchemaRegistry::registerVersioned(v2))); @@ -78,7 +78,7 @@ TEST_CASE("schema.registry.rejectsExactVersionConflictWithoutOverwriting") { CHECK(registrationSucceeded(SchemaRegistry::registerVersioned(definition))); definition.title = "replacement"; - auto conflict = SchemaRegistry::registerVersioned(definition); + auto conflict = SchemaRegistry::registerVersioned(definition); CHECK(!conflict.ok()); CHECK_EQ(conflict.code(), eve::StatusCode::Conflict); REQUIRE(SchemaRegistry::resolve("unit", 3) != nullptr); @@ -106,13 +106,11 @@ TEST_CASE("schema.registry.distinguishesMissingVersion") { TEST_CASE("schema.registry.acceptsCanonicalSchemaVersionAndRejectsConflictingAliases") { SchemaRegistry::clear(); - CHECK(registrationSucceeded(SchemaRegistry::registerFromJsonVersioned( - R"({"id":"unit","schemaVersion":4})"))); + CHECK(registrationSucceeded(SchemaRegistry::registerFromJsonVersioned(R"({"id":"unit","schemaVersion":4})"))); REQUIRE(SchemaRegistry::resolve("unit", 4) != nullptr); CHECK_EQ(SchemaRegistry::resolve("unit", 4)->version, 4); - auto conflict = SchemaRegistry::registerFromJsonVersioned( - R"({"id":"conflicting","version":1,"schemaVersion":2})"); + auto conflict = SchemaRegistry::registerFromJsonVersioned(R"({"id":"conflicting","version":1,"schemaVersion":2})"); CHECK(!conflict.ok()); CHECK_EQ(conflict.code(), eve::StatusCode::Failed); } @@ -120,10 +118,10 @@ TEST_CASE("schema.registry.acceptsCanonicalSchemaVersionAndRejectsConflictingAli TEST_CASE("schema.registry.legacyFacadeKeepsVersionsAndRemovesExactly") { SchemaRegistry::clear(); SchemaDefinition v1; - v1.id = "unit"; - v1.version = 1; + v1.id = "unit"; + v1.version = 1; SchemaDefinition v2 = v1; - v2.version = 2; + v2.version = 2; REQUIRE(SchemaRegistry::registerSchema(v1).ok()); REQUIRE(SchemaRegistry::registerSchema(v2).ok()); CHECK(SchemaRegistry::remove("unit", 1).ok()); @@ -139,11 +137,9 @@ TEST_CASE("schema.registry.rejectsMalformedDefinitions") { SchemaRegistry::clear(); auto emptyId = SchemaRegistry::registerFromJson(R"({"id":"","version":1})"); CHECK(!emptyId.ok()); - auto badType = SchemaRegistry::registerFromJson( - R"({"id":"bad","fields":[{"name":"x","type":"mystery"}]})"); + auto badType = SchemaRegistry::registerFromJson(R"({"id":"bad","fields":[{"name":"x","type":"mystery"}]})"); CHECK(!badType.ok()); - auto duplicate = SchemaRegistry::registerFromJson( - R"({"id":"duplicate","fields":[{"name":"x"},{"name":"x"}]})"); + auto duplicate = SchemaRegistry::registerFromJson(R"({"id":"duplicate","fields":[{"name":"x"},{"name":"x"}]})"); CHECK(!duplicate.ok()); CHECK_EQ(SchemaRegistry::count(), 0); } diff --git a/test/schema_contract.cpp b/test/schema_contract.cpp index 388540cc..9ddce787 100644 --- a/test/schema_contract.cpp +++ b/test/schema_contract.cpp @@ -1,7 +1,6 @@ #include "zeroerr/assert.h" #include "zeroerr/unittest.h" -#include "schema/SchemaBuiltins.h" #include "schema/SchemaRegistry.h" #include @@ -18,16 +17,16 @@ using eve::schema::ValueType; FieldDefinition requiredField(std::string name, ValueType type) { FieldDefinition field; - field.name = std::move(name); - field.type = type; + field.name = std::move(name); + field.type = type; field.required = true; return field; } SchemaDefinition versionedPayload(int version) { SchemaDefinition schema; - schema.id = "test:migration"; - schema.version = version; + schema.id = "test:migration"; + schema.version = version; schema.additionalProperties = false; schema.fields.push_back(requiredField("number", ValueType::Integer)); if (version >= 2) schema.fields.push_back(requiredField("label", ValueType::String)); @@ -53,21 +52,23 @@ TEST_CASE("schema.migration.multiHopCompatibilityAndCanonicalOutput") { REQUIRE(registrationSucceeded(SchemaRegistry::registerVersioned(versionedPayload(2)))); REQUIRE(registrationSucceeded(SchemaRegistry::registerVersioned(versionedPayload(3)))); - auto first = SchemaRegistry::registerMigration( - "test:migration", 1, 2, [](const Value& input) -> eve::Result { + auto first = + SchemaRegistry::registerMigration("test:migration", 1, 2, [](const Value& input) -> eve::Result { const auto* object = input.getIf(); - if (!object) return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "expected object")); + if (!object) + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "expected object")); Value::Object next = *object; next.emplace("label", Value("legacy")); return eve::Result::success(Value(std::move(next))); }); REQUIRE(first.ok()); - auto second = SchemaRegistry::registerMigration( - "test:migration", 2, 3, [](const Value& input) -> eve::Result { + auto second = + SchemaRegistry::registerMigration("test:migration", 2, 3, [](const Value& input) -> eve::Result { const auto* object = input.getIf(); - if (!object) return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "expected object")); + if (!object) + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "expected object")); Value::Object next = *object; next.emplace("ready", Value(true)); return eve::Result::success(Value(std::move(next))); @@ -79,8 +80,8 @@ TEST_CASE("schema.migration.multiHopCompatibilityAndCanonicalOutput") { CHECK(!compatibility.value().exact()); CHECK_EQ(compatibility.value().versions, std::vector({1, 2, 3})); - const Value input = objectValue(7); - auto migrated = SchemaRegistry::migrate("test:migration", 1, 3, input); + const Value input = objectValue(7); + auto migrated = SchemaRegistry::migrate("test:migration", 1, 3, input); REQUIRE(migrated.ok()); CHECK_EQ(input, objectValue(7)); const auto* result = migrated.value().getIf(); @@ -100,18 +101,15 @@ TEST_CASE("schema.migration.missingUnknownAndDowngradeAreStructuredFailures") { auto missing = SchemaRegistry::queryCompatibility("test:migration", 1, 3); CHECK(!missing.ok()); - CHECK(static_cast(missing.error()->code()) == - static_cast(eve::DiagnosticCode::Unsupported)); + CHECK(static_cast(missing.error()->code()) == static_cast(eve::DiagnosticCode::Unsupported)); auto unknown = SchemaRegistry::queryCompatibility("test:migration", 1, 99); CHECK(!unknown.ok()); - CHECK(static_cast(unknown.error()->code()) == - static_cast(eve::DiagnosticCode::UnknownVersion)); + CHECK(static_cast(unknown.error()->code()) == static_cast(eve::DiagnosticCode::UnknownVersion)); auto downgrade = SchemaRegistry::queryCompatibility("test:migration", 3, 1); CHECK(!downgrade.ok()); - CHECK(static_cast(downgrade.error()->code()) == - static_cast(eve::DiagnosticCode::Unsupported)); + CHECK(static_cast(downgrade.error()->code()) == static_cast(eve::DiagnosticCode::Unsupported)); } TEST_CASE("schema.migration.rejectsCyclesAndDowngradeEdgesWithoutRegistryMutation") { @@ -119,24 +117,18 @@ TEST_CASE("schema.migration.rejectsCyclesAndDowngradeEdgesWithoutRegistryMutatio REQUIRE(registrationSucceeded(SchemaRegistry::registerVersioned(versionedPayload(1)))); REQUIRE(registrationSucceeded(SchemaRegistry::registerVersioned(versionedPayload(2)))); REQUIRE(registrationSucceeded(SchemaRegistry::registerVersioned(versionedPayload(3)))); - REQUIRE(SchemaRegistry::registerMigration("test:migration", 1, 2, - [](const Value& input) { return eve::Result::success(input); }) - .ok()); + REQUIRE(SchemaRegistry::registerMigration("test:migration", 1, 2, [](const Value& input) { + return eve::Result::success(input); + }).ok()); - auto cycle = SchemaRegistry::registerMigration("test:migration", 2, 1, - [](const Value& input) { - return eve::Result::success(input); - }); + auto cycle = SchemaRegistry::registerMigration( + "test:migration", 2, 1, [](const Value& input) { return eve::Result::success(input); }); CHECK(!cycle.ok()); - CHECK(static_cast(cycle.error()->code()) == - static_cast(eve::DiagnosticCode::Unsupported)); - auto self = SchemaRegistry::registerMigration("test:migration", 2, 2, - [](const Value& input) { - return eve::Result::success(input); - }); + CHECK(static_cast(cycle.error()->code()) == static_cast(eve::DiagnosticCode::Unsupported)); + auto self = SchemaRegistry::registerMigration( + "test:migration", 2, 2, [](const Value& input) { return eve::Result::success(input); }); CHECK(!self.ok()); - CHECK(static_cast(self.error()->code()) == - static_cast(eve::DiagnosticCode::Conflict)); + CHECK(static_cast(self.error()->code()) == static_cast(eve::DiagnosticCode::Conflict)); auto path = SchemaRegistry::queryCompatibility("test:migration", 1, 2); REQUIRE(path.ok()); @@ -147,19 +139,16 @@ TEST_CASE("schema.migration.callbackFailurePreservesInputAndRegistry") { SchemaRegistry::clear(); REQUIRE(registrationSucceeded(SchemaRegistry::registerVersioned(versionedPayload(1)))); REQUIRE(registrationSucceeded(SchemaRegistry::registerVersioned(versionedPayload(2)))); - REQUIRE(SchemaRegistry::registerMigration( - "test:migration", 1, 2, [](const Value&) -> eve::Result { - return eve::Result::failure( - eve::Diagnostic::error(eve::DiagnosticCode::Failed, "injected migration failure")); - }) - .ok()); + REQUIRE(SchemaRegistry::registerMigration("test:migration", 1, 2, [](const Value&) -> eve::Result { + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Failed, "injected migration failure")); + }).ok()); - const Value input = objectValue(9); - const int before = SchemaRegistry::versionCount(); - auto failed = SchemaRegistry::migrate("test:migration", 1, 2, input); + const Value input = objectValue(9); + const int before = SchemaRegistry::versionCount(); + auto failed = SchemaRegistry::migrate("test:migration", 1, 2, input); CHECK(!failed.ok()); - CHECK(static_cast(failed.error()->code()) == - static_cast(eve::DiagnosticCode::Failed)); + CHECK(static_cast(failed.error()->code()) == static_cast(eve::DiagnosticCode::Failed)); CHECK_EQ(input, objectValue(9)); CHECK_EQ(SchemaRegistry::versionCount(), before); CHECK(SchemaRegistry::queryCompatibility("test:migration", 1, 2).ok()); @@ -169,14 +158,13 @@ TEST_CASE("schema.migration.invalidOutputPreservesInputAndRegistry") { SchemaRegistry::clear(); REQUIRE(registrationSucceeded(SchemaRegistry::registerVersioned(versionedPayload(1)))); REQUIRE(registrationSucceeded(SchemaRegistry::registerVersioned(versionedPayload(2)))); - REQUIRE(SchemaRegistry::registerMigration( - "test:migration", 1, 2, - [](const Value& input) { return eve::Result::success(input); }) - .ok()); + REQUIRE(SchemaRegistry::registerMigration("test:migration", 1, 2, [](const Value& input) { + return eve::Result::success(input); + }).ok()); - const Value input = objectValue(11); - const int before = SchemaRegistry::versionCount(); - auto failed = SchemaRegistry::migrate("test:migration", 1, 2, input); + const Value input = objectValue(11); + const int before = SchemaRegistry::versionCount(); + auto failed = SchemaRegistry::migrate("test:migration", 1, 2, input); CHECK(!failed.ok()); CHECK(static_cast(failed.error()->code()) == static_cast(eve::DiagnosticCode::ParseError)); CHECK_EQ(input, objectValue(11)); @@ -210,11 +198,11 @@ TEST_CASE("schema.validation.supportsNestedItemsRefsAndDiscriminatedUnion") { })"; REQUIRE(registrationSucceeded(SchemaRegistry::registerFromJsonVersioned(profile))); - const std::string valid = R"({"address":{"city":"Osaka"},"items":[{"id":"a"}],"shape":{"kind":"circle","radius":2}})"; + const std::string valid = + R"({"address":{"city":"Osaka"},"items":[{"id":"a"}],"shape":{"kind":"circle","radius":2}})"; CHECK(SchemaRegistry::validate("test:profile", 1, valid).empty()); const auto invalid = SchemaRegistry::validate( - "test:profile", 1, - R"({"address":{"city":4},"items":[{}],"shape":{"kind":"triangle","radius":-1}})"); + "test:profile", 1, R"({"address":{"city":4},"items":[{}],"shape":{"kind":"triangle","radius":-1}})"); CHECK(!invalid.empty()); CHECK_EQ(invalid[0].path, std::string("/address/city")); CHECK_EQ(invalid[0].code, std::string("type")); @@ -240,27 +228,3 @@ TEST_CASE("schema.validation.detectsSchemaReferenceCycle") { REQUIRE(!errors.empty()); CHECK_EQ(errors.back().code, std::string("schema_ref_cycle")); } - -TEST_CASE("schema.builtins.registerFormatsAndGenerateStableContracts") { - SchemaRegistry::clear(); - auto standard = eve::schema::registerStandardSchemas(); - REQUIRE(standard.ok()); - CHECK_EQ(SchemaRegistry::versionCount(), 3); - CHECK(SchemaRegistry::resolve("snapshot:envelope", 1) != nullptr); - CHECK(SchemaRegistry::resolve("game_event:envelope", 1) != nullptr); - CHECK(SchemaRegistry::resolve("definitions:metadata", 1) != nullptr); - - const auto documentation = SchemaRegistry::generateDocumentation("snapshot:envelope", 1); - REQUIRE(documentation.ok()); - const auto documentationAgain = SchemaRegistry::generateDocumentation("snapshot:envelope", 1); - REQUIRE(documentationAgain.ok()); - CHECK_EQ(documentation.value(), documentationAgain.value()); - CHECK(documentation.value().find("Eve Schema v1") != std::string::npos); - - const auto contract = SchemaRegistry::generateBindingContract("game_event:envelope", 1); - REQUIRE(contract.ok()); - const auto contractAgain = SchemaRegistry::generateBindingContract("game_event:envelope", 1); - REQUIRE(contractAgain.ok()); - CHECK_EQ(contract.value(), contractAgain.value()); - CHECK(contract.value().find("game_event:envelope") != std::string::npos); -} diff --git a/test/schema_production_integration.cpp b/test/schema_production_integration.cpp index aa227782..596eb3e3 100644 --- a/test/schema_production_integration.cpp +++ b/test/schema_production_integration.cpp @@ -8,12 +8,12 @@ namespace { eve::schema::SchemaDefinition objectSchema(std::string id) { eve::schema::SchemaDefinition schema; - schema.id = std::move(id); - schema.version = 1; + schema.id = std::move(id); + schema.version = 1; schema.additionalProperties = false; eve::schema::FieldDefinition enabled; - enabled.name = "enabled"; - enabled.type = eve::schema::ValueType::Boolean; + enabled.name = "enabled"; + enabled.type = eve::schema::ValueType::Boolean; enabled.required = true; schema.fields.push_back(std::move(enabled)); return schema; @@ -28,7 +28,7 @@ TEST_CASE("schema.productionBoundary.validatesDefinitionsBeforeMutation") { CHECK_EQ(schemaRegistration.value(), eve::schema::SchemaRegistrationStatus::Registered); eve::definitions::DefinitionRegistry registry; - auto invalid = registry.insert("ability", "dash", 1, R"({"enabled":"yes"})"); + auto invalid = registry.insert("ability", "dash", 1, R"({"enabled":"yes"})"); CHECK(!invalid.ok()); CHECK_EQ(registry.size(), 0); invalid.ignore("expected schema rejection"); @@ -45,14 +45,12 @@ TEST_CASE("schema.productionBoundary.validatesPolicyMetadataBeforeMutation") { CHECK_EQ(schemaRegistration.value(), eve::schema::SchemaRegistrationStatus::Registered); eve::policyregistry::PolicyRegistry registry; - auto invalid = registry.insert("movement", "ground", 1, 0, true, "builtin", - "policy:movement", R"({"enabled":1})"); + auto invalid = registry.insert("movement", "ground", 1, 0, true, "builtin", "policy:movement", R"({"enabled":1})"); CHECK(!invalid.ok()); CHECK_EQ(registry.size(), 0); invalid.ignore("expected schema rejection"); - auto valid = registry.insert("movement", "ground", 1, 0, true, "builtin", - "policy:movement", R"({"enabled":true})"); + auto valid = registry.insert("movement", "ground", 1, 0, true, "builtin", "policy:movement", R"({"enabled":true})"); REQUIRE(valid.ok()); CHECK_EQ(registry.size(), 1); } diff --git a/test/script_ownership.cpp b/test/script_ownership.cpp index d7010dcf..cf6b4afc 100644 --- a/test/script_ownership.cpp +++ b/test/script_ownership.cpp @@ -3,14 +3,14 @@ * @brief Contract tests for Value/Owned/Borrowed script object semantics. */ +#include "authority/Authority.h" #include "common/SquirrelOwnership.h" +#include "decision/Decision.h" #include "effects/Effects.h" #include "orders/CommandQueue.h" #include "procgen/Procgen.h" #include "production/Production.h" -#include "authority/Authority.h" #include "statepatch/StatePatch.h" -#include "decision/Decision.h" #include "zeroerr/unittest.h" @@ -25,18 +25,16 @@ TEST_CASE("scriptOwnership.commonSemanticsAndRegistryStaleGeneration") { int value = 7; }; - CHECK_EQ(std::string(eve::script::objectSemanticName(eve::script::ObjectSemantic::Value)), - std::string("value")); - CHECK_EQ(std::string(eve::script::objectSemanticName(eve::script::ObjectSemantic::Owned)), - std::string("owned")); + CHECK_EQ(std::string(eve::script::objectSemanticName(eve::script::ObjectSemantic::Value)), std::string("value")); + CHECK_EQ(std::string(eve::script::objectSemanticName(eve::script::ObjectSemantic::Owned)), std::string("owned")); CHECK_EQ(std::string(eve::script::objectSemanticName(eve::script::ObjectSemantic::Borrowed)), std::string("borrowed")); eve::script::RuntimeObjectRegistry registry; - auto created = registry.emplace(std::make_unique()); + auto created = registry.emplace(std::make_unique()); REQUIRE(created.ok()); const auto reference = std::move(created).takeValue(); - auto borrowed = registry.resolve(reference); + auto borrowed = registry.resolve(reference); REQUIRE(borrowed.isBound()); CHECK_EQ(borrowed->value, 7); CHECK(!registry.isStale(reference)); @@ -54,7 +52,7 @@ TEST_CASE("scriptOwnership.commonSemanticsAndRegistryStaleGeneration") { CHECK(registry.isStale(reference)); CHECK(!registry.isStale(replacementReference)); - const auto oldEpoch = registry.ownerEpoch(); + const auto oldEpoch = registry.ownerEpoch(); eve::script::RuntimeHandleRef unloadedReference = replacementReference; registry.clear(); CHECK(registry.isStale(unloadedReference)); @@ -111,7 +109,8 @@ TEST_CASE("scriptOwnership.procgenAndStateModulesOwnedBindings") { local batch = statepatch.ok ? statepatch.value.newBatch() : { ok = false }; if (params.ok && grid.ok && context.ok && production.ok && authority.ok && statepatch.ok && decision.ok && pointSet.ok && output.ok && cloud.ok && batch.ok && - params.value.ownerEpoch() > 0 && pointSet.value.ownerEpoch() > 0 && + params.value.ownerEpoch() > 0 && pointSet.ownerEpoch > 0 && output.ownerEpoch > 0 && + cloud.ownerEpoch > 0 && pointSet.value.empty() && output.value.getTarget() == "" && grid.value.getWidth() == 2 && grid.value.getHeight() == 3 && params.value.setSeed(42).ok && params.value.getSeed() == 42 && grid.value.fill(1).ok && grid.value.getCell(1, 2) == 1 && @@ -128,17 +127,12 @@ TEST_CASE("scriptOwnership.procgenAndStateModulesOwnedBindings") { local bReleased = batch.value.release(); local sReleased = statepatch.value.release(); local dReleased = decision.value.release(); - local pSetReleased = pointSet.value.release(); - local outputReleased = output.value.release(); - local cloudReleased = cloud.value.release(); if (pReleased.ok && gReleased.ok && cReleased.ok && qReleased.ok && aReleased.ok && bReleased.ok && sReleased.ok && dReleased.ok && params.value.isStale() && grid.value.isStale() && context.value.isStale() && production.value.isStale() && authority.value.isStale() && statepatch.value.isStale() && - pointSet.value.isStale() && output.value.isStale() && cloud.value.isStale() && - batch.value.isStale() && pSetReleased.ok && outputReleased.ok && cloudReleased.ok && - decision.value.isStale()) result = "ok"; + batch.value.isStale() && decision.value.isStale()) result = "ok"; } )")); CHECK_EQ(vm.find("result").toString(), std::string("ok")); diff --git a/test/script_property_model.cpp b/test/script_property_model.cpp index 6e01b74d..576d927f 100644 --- a/test/script_property_model.cpp +++ b/test/script_property_model.cpp @@ -120,57 +120,55 @@ TEST_CASE("scriptmodel.property_validation_matches_presentation_contract") { Runtime runtime(512, ssq::Libs::ALL); runtime.initialize(); runtime.runSource(kModelScript, "scriptmodel.nut"); - ssq::Object hero = runtime.createInstance("ModelHero"); + ssq::Object hero = runtime.createInstance("ModelHero"); ReflectedPropertyModel model(runtime, hero); - auto hpRef = model.schema().find("hp"); - auto jobRef = model.schema().find("job"); + auto hpRef = model.schema().find("hp"); + auto jobRef = model.schema().find("job"); auto tagsRef = model.schema().find("tags"); REQUIRE(hpRef.has_value()); REQUIRE(jobRef.has_value()); REQUIRE(tagsRef.has_value()); - const PropertyDescriptor *hp = &hpRef->get(); - const PropertyDescriptor *job = &jobRef->get(); + const PropertyDescriptor *hp = &hpRef->get(); + const PropertyDescriptor *job = &jobRef->get(); const PropertyDescriptor *tags = &tagsRef->get(); - const auto sharedType = validatePropertyValue(*hp, Value("fast")); + const auto sharedType = validatePropertyValue(*hp, Value("fast")); const auto runtimeType = model.write("hp", Value("fast")); CHECK(!sharedType.accepted); CHECK(!runtimeType.accepted); CHECK_EQ(sharedType.code, std::string("property_access.property.type")); CHECK_EQ(runtimeType.code, std::string("scriptmodel.property.type")); - const auto sharedFinite = validatePropertyValue( - *hp, Value(std::numeric_limits::quiet_NaN())); - const auto runtimeFinite = model.write( - "hp", Value(std::numeric_limits::quiet_NaN())); + const auto sharedFinite = validatePropertyValue(*hp, Value(std::numeric_limits::quiet_NaN())); + const auto runtimeFinite = model.write("hp", Value(std::numeric_limits::quiet_NaN())); CHECK(!sharedFinite.accepted); CHECK(!runtimeFinite.accepted); CHECK_EQ(sharedFinite.code, std::string("property_access.property.finite")); CHECK_EQ(runtimeFinite.code, std::string("scriptmodel.property.finite")); - const auto sharedMinimum = validatePropertyValue(*hp, Value(-1.0)); + const auto sharedMinimum = validatePropertyValue(*hp, Value(-1.0)); const auto runtimeMinimum = model.write("hp", Value(-1.0)); CHECK(!sharedMinimum.accepted); CHECK(!runtimeMinimum.accepted); CHECK_EQ(sharedMinimum.code, std::string("property_access.property.minimum")); CHECK_EQ(runtimeMinimum.code, std::string("scriptmodel.property.minimum")); - const auto sharedMaximum = validatePropertyValue(*hp, Value(101.0)); + const auto sharedMaximum = validatePropertyValue(*hp, Value(101.0)); const auto runtimeMaximum = model.write("hp", Value(101.0)); CHECK(!sharedMaximum.accepted); CHECK(!runtimeMaximum.accepted); CHECK_EQ(sharedMaximum.code, std::string("property_access.property.maximum")); CHECK_EQ(runtimeMaximum.code, std::string("scriptmodel.property.maximum")); - const auto sharedChoice = validatePropertyValue(*job, Value("rogue")); + const auto sharedChoice = validatePropertyValue(*job, Value("rogue")); const auto runtimeChoice = model.write("job", Value("rogue")); CHECK(!sharedChoice.accepted); CHECK(!runtimeChoice.accepted); CHECK_EQ(sharedChoice.code, std::string("property_access.property.choice")); CHECK_EQ(runtimeChoice.code, std::string("scriptmodel.property.choice")); - const auto sharedReadOnly = validatePropertyValue(*tags, Value(Value::Array{})); + const auto sharedReadOnly = validatePropertyValue(*tags, Value(Value::Array{})); const auto runtimeReadOnly = model.write("tags", Value(Value::Array{})); CHECK(!sharedReadOnly.accepted); CHECK(!runtimeReadOnly.accepted); diff --git a/test/sensing.cpp b/test/sensing.cpp index e531171d..49128362 100644 --- a/test/sensing.cpp +++ b/test/sensing.cpp @@ -10,9 +10,9 @@ using namespace eve::sensing; TEST_CASE("sensing.filtersAndSortsCandidates") { SensingWorld w; - auto b = w.upsert("b", 3, 4, "red", "unit,tank", "p1"); - auto a = w.upsert("a", 1, 0, "blue", "unit,infantry", "p1,p2"); - auto c = w.upsert("c", 2, 0, "red", "building", "p1"); + auto b = w.upsert("b", 3, 4, "red", "unit,tank", "p1"); + auto a = w.upsert("a", 1, 0, "blue", "unit,infantry", "p1,p2"); + auto c = w.upsert("c", 2, 0, "red", "building", "p1"); REQUIRE(b.ok()); REQUIRE(a.ok()); REQUIRE(c.ok()); @@ -25,8 +25,8 @@ TEST_CASE("sensing.filtersAndSortsCandidates") { } TEST_CASE("sensing.ordersByDistanceThenIdAndLimits") { SensingWorld w; - auto z = w.upsert("z", 1, 0, "", "u", ""); - auto a = w.upsert("a", -1, 0, "", "u", ""); + auto z = w.upsert("z", 1, 0, "", "u", ""); + auto a = w.upsert("a", -1, 0, "", "u", ""); REQUIRE(z.ok()); REQUIRE(a.ok()); auto count = w.circle(0, 0, 2, "u", "", "", "", "", 1); @@ -38,11 +38,11 @@ TEST_CASE("sensing.ordersByDistanceThenIdAndLimits") { } TEST_CASE("sensing.snapshotIsTransactional") { SensingWorld w; - auto inserted = w.upsert("u", 2, 3, "f", "b,a", "p"); + auto inserted = w.upsert("u", 2, 3, "f", "b,a", "p"); REQUIRE(inserted.ok()); auto s = w.snapshotJson(); SensingWorld x; - auto restored = x.restoreJson(s); + auto restored = x.restoreJson(s); REQUIRE(restored.ok()); CHECK_EQ(x.snapshotJson(), s); auto before = x.snapshotJson(); @@ -55,7 +55,7 @@ TEST_CASE("sensing.worldHandleAndScriptResultContract") { auto created = Sensing::newWorld(); REQUIRE(created.ok()); const auto reference = std::move(created).takeValue(); - auto borrowed = Sensing::resolve(reference); + auto borrowed = Sensing::resolve(reference); REQUIRE(borrowed.isBound()); auto released = Sensing::release(reference); REQUIRE(released.ok()); diff --git a/test/snapshot_consumers.cpp b/test/snapshot_consumers.cpp index d680f2ef..7d48a001 100644 --- a/test/snapshot_consumers.cpp +++ b/test/snapshot_consumers.cpp @@ -1,14 +1,14 @@ #include "zeroerr/assert.h" #include "zeroerr/unittest.h" +#include "GameEventTestSupport.h" +#include "StatePatchTestSupport.h" #include "authority/Authority.h" #include "common/Snapshot.h" -#include "GameEventTestSupport.h" #include "definitions/Definitions.h" #include "game_event/GameEvent.h" #include "production/Production.h" #include "statepatch/StatePatch.h" -#include "StatePatchTestSupport.h" #include "transaction/Transaction.h" #include @@ -51,19 +51,16 @@ SnapshotHashProvider testHashProvider() { } ContentId::Bytes bytes{}; for (int index = 0; index < 8; ++index) { - bytes[static_cast(index)] = - static_cast(left >> (56 - index * 8)); - bytes[static_cast(index + 8)] = - static_cast(right >> (56 - index * 8)); + bytes[static_cast(index)] = static_cast(left >> (56 - index * 8)); + bytes[static_cast(index + 8)] = static_cast(right >> (56 - index * 8)); } return Result::success(ContentId(bytes)); }; } -SnapshotEnvelope reseal(const SnapshotEnvelope& source, SchemaVersion version, - Value payload, const SnapshotHashProvider& hash) { - auto result = eve::makeSnapshotEnvelope(source.type, source.schema, version, - source.instanceId, source.revision, +SnapshotEnvelope reseal(const SnapshotEnvelope& source, SchemaVersion version, Value payload, + const SnapshotHashProvider& hash) { + auto result = eve::makeSnapshotEnvelope(source.type, source.schema, version, source.instanceId, source.revision, source.tick, std::move(payload), hash); REQUIRE(result.ok()); return std::move(result).takeValue(); @@ -79,11 +76,11 @@ Value withPayloadMetadata(const Value& payload, std::string field, std::uint64_t } // namespace TEST_CASE("snapshot.consumers.eventStreamEnvelopeRoundTripAndGeneratorSurvives") { - const auto hash = testHashProvider(); - const auto id = persistentId("11111111-2222-4333-8444-555555555555"); + const auto hash = testHashProvider(); + const auto id = persistentId("11111111-2222-4333-8444-555555555555"); eve::game_event::GameEventLog stream(id); - auto appended = stream.append(eve::game_event::test::envelope( - 1, "test.event", "test", "subject", "", "", 5, 1, "{\"value\":1}")); + auto appended = stream.append( + eve::game_event::test::envelope(1, "test.event", "test", "subject", "", "", 5, 1, "{\"value\":1}")); REQUIRE(appended.ok()); CHECK_EQ(appended.value().value(), uint64_t{1}); @@ -97,47 +94,44 @@ TEST_CASE("snapshot.consumers.eventStreamEnvelopeRoundTripAndGeneratorSurvives") REQUIRE(restored.restoreSnapshot(captured.value(), hash).ok()); CHECK_EQ(restored.snapshotJson(), stream.snapshotJson()); - auto old = reseal(captured.value(), SchemaVersion(0), captured.value().payload, hash); + auto old = reseal(captured.value(), SchemaVersion(0), captured.value().payload, hash); eve::game_event::GameEventLog migrated(id); REQUIRE(migrated.restoreSnapshot(old, hash).ok()); CHECK_EQ(migrated.snapshotJson(), stream.snapshotJson()); - auto newer = reseal(captured.value(), SchemaVersion(2), captured.value().payload, hash); - const std::string before = restored.snapshotJson(); - auto unknown = restored.restoreSnapshot(newer, hash); + auto newer = reseal(captured.value(), SchemaVersion(2), captured.value().payload, hash); + const std::string before = restored.snapshotJson(); + auto unknown = restored.restoreSnapshot(newer, hash); CHECK(!unknown.ok()); - CHECK_EQ(static_cast(unknown.error()->code()), - static_cast(DiagnosticCode::UnknownVersion)); + CHECK_EQ(static_cast(unknown.error()->code()), static_cast(DiagnosticCode::UnknownVersion)); CHECK_EQ(restored.snapshotJson(), before); - auto mismatched = captured.value(); + auto mismatched = captured.value(); mismatched.contentHash = ContentId::nil(); - auto mismatch = restored.restoreSnapshot(mismatched, hash); + auto mismatch = restored.restoreSnapshot(mismatched, hash); CHECK(!mismatch.ok()); - CHECK_EQ(static_cast(mismatch.error()->code()), - static_cast(DiagnosticCode::HashMismatch)); + CHECK_EQ(static_cast(mismatch.error()->code()), static_cast(DiagnosticCode::HashMismatch)); CHECK_EQ(restored.snapshotJson(), before); - auto duplicateRevision = reseal(captured.value(), SchemaVersion(1), - withPayloadMetadata(captured.value().payload, "revision", - captured.value().revision.value() + 1), hash); + auto duplicateRevision = + reseal(captured.value(), SchemaVersion(1), + withPayloadMetadata(captured.value().payload, "revision", captured.value().revision.value() + 1), hash); auto duplicateFailure = restored.restoreSnapshot(duplicateRevision, hash); CHECK(!duplicateFailure.ok()); - CHECK_EQ(static_cast(duplicateFailure.error()->code()), - static_cast(DiagnosticCode::Conflict)); + CHECK_EQ(static_cast(duplicateFailure.error()->code()), static_cast(DiagnosticCode::Conflict)); CHECK_EQ(restored.snapshotJson(), before); } TEST_CASE("snapshot.consumers.transactionLedgerEnvelopeMigratesAtomically") { - const auto hash = testHashProvider(); - const auto id = persistentId("aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee"); + const auto hash = testHashProvider(); + const auto id = persistentId("aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee"); eve::transaction::Ledger ledger(id); - auto created = ledger.create("chain", "command", - *eve::TransactionId::parse("aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeee01")); + auto created = + ledger.create("chain", "command", *eve::TransactionId::parse("aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeee01")); REQUIRE(created.ok()); auto* plan = created.value(); - auto staged = plan->stage("set", "target", "{\"value\":1}", - *eve::OperationId::parse("aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeee02")); + auto staged = + plan->stage("set", "target", "{\"value\":1}", *eve::OperationId::parse("aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeee02")); REQUIRE(staged.ok()); const auto operation = staged.value(); REQUIRE(plan->markValid(operation).ok()); @@ -150,35 +144,33 @@ TEST_CASE("snapshot.consumers.transactionLedgerEnvelopeMigratesAtomically") { REQUIRE(restored.restoreSnapshot(captured.value(), hash).ok()); CHECK_EQ(restored.snapshotJson(), ledger.snapshotJson()); - auto old = reseal(captured.value(), SchemaVersion(0), captured.value().payload, hash); + auto old = reseal(captured.value(), SchemaVersion(0), captured.value().payload, hash); eve::transaction::Ledger migrated(id); REQUIRE(migrated.restoreSnapshot(old, hash).ok()); CHECK_EQ(migrated.snapshotJson(), ledger.snapshotJson()); const std::string before = restored.snapshotJson(); - Value::Object malformedObject; + Value::Object malformedObject; malformedObject.emplace("version", Value(std::int64_t(1))); - auto malformed = reseal(captured.value(), SchemaVersion(1), - Value(std::move(malformedObject)), hash); - auto failure = restored.restoreSnapshot(malformed, hash); + auto malformed = reseal(captured.value(), SchemaVersion(1), Value(std::move(malformedObject)), hash); + auto failure = restored.restoreSnapshot(malformed, hash); CHECK(!failure.ok()); CHECK_EQ(restored.snapshotJson(), before); - auto duplicateTick = reseal(captured.value(), SchemaVersion(1), - withPayloadMetadata(captured.value().payload, "tick", - captured.value().tick.value() + 1), hash); + auto duplicateTick = + reseal(captured.value(), SchemaVersion(1), + withPayloadMetadata(captured.value().payload, "tick", captured.value().tick.value() + 1), hash); auto duplicateFailure = restored.restoreSnapshot(duplicateTick, hash); CHECK(!duplicateFailure.ok()); - CHECK_EQ(static_cast(duplicateFailure.error()->code()), - static_cast(DiagnosticCode::Conflict)); + CHECK_EQ(static_cast(duplicateFailure.error()->code()), static_cast(DiagnosticCode::Conflict)); CHECK_EQ(restored.snapshotJson(), before); } TEST_CASE("snapshot.consumers.statePatchEnvelopeRejectsHashAndRevisionMismatch") { - const auto hash = testHashProvider(); - const auto id = persistentId("12345678-1234-4123-8123-123456789012"); + const auto hash = testHashProvider(); + const auto id = persistentId("12345678-1234-4123-8123-123456789012"); eve::statepatch::Store store(id); - auto batch = eve::test_support::openStatePatchBatch(store); + auto batch = eve::test_support::openStatePatchBatch(store); REQUIRE(batch.view.isBound()); REQUIRE(batch.view->set("actor", "value", "1")); REQUIRE(store.commit(batch.view.get())); @@ -189,51 +181,48 @@ TEST_CASE("snapshot.consumers.statePatchEnvelopeRejectsHashAndRevisionMismatch") REQUIRE(restored.restoreSnapshot(captured.value(), hash).ok()); CHECK_EQ(restored.snapshotJson(), store.snapshotJson()); - auto old = reseal(captured.value(), SchemaVersion(0), captured.value().payload, hash); + auto old = reseal(captured.value(), SchemaVersion(0), captured.value().payload, hash); eve::statepatch::Store migrated(id); REQUIRE(migrated.restoreSnapshot(old, hash).ok()); CHECK_EQ(migrated.snapshotJson(), store.snapshotJson()); - const std::string before = restored.snapshotJson(); - auto mismatch = captured.value(); - mismatch.contentHash = ContentId::nil(); - auto hashFailure = restored.restoreSnapshot(mismatch, hash); + const std::string before = restored.snapshotJson(); + auto mismatch = captured.value(); + mismatch.contentHash = ContentId::nil(); + auto hashFailure = restored.restoreSnapshot(mismatch, hash); CHECK(!hashFailure.ok()); - CHECK_EQ(static_cast(hashFailure.error()->code()), - static_cast(DiagnosticCode::HashMismatch)); + CHECK_EQ(static_cast(hashFailure.error()->code()), static_cast(DiagnosticCode::HashMismatch)); CHECK_EQ(restored.snapshotJson(), before); - auto badRevision = captured.value(); + auto badRevision = captured.value(); badRevision.revision = eve::Revision(captured.value().revision.value() + 1); - auto badRevisionHash = eve::makeSnapshotEnvelope( - badRevision.type, badRevision.schema, badRevision.schemaVersion, - badRevision.instanceId, badRevision.revision, badRevision.tick, - badRevision.payload, hash); + auto badRevisionHash = eve::makeSnapshotEnvelope(badRevision.type, badRevision.schema, badRevision.schemaVersion, + badRevision.instanceId, badRevision.revision, badRevision.tick, + badRevision.payload, hash); REQUIRE(badRevisionHash.ok()); auto revisionFailure = restored.restoreSnapshot(badRevisionHash.value(), hash); CHECK(!revisionFailure.ok()); CHECK_EQ(restored.snapshotJson(), before); - auto duplicateTick = reseal(captured.value(), SchemaVersion(1), - withPayloadMetadata(captured.value().payload, "tick", - captured.value().tick.value() + 1), hash); + auto duplicateTick = + reseal(captured.value(), SchemaVersion(1), + withPayloadMetadata(captured.value().payload, "tick", captured.value().tick.value() + 1), hash); auto duplicateFailure = restored.restoreSnapshot(duplicateTick, hash); CHECK(!duplicateFailure.ok()); - CHECK_EQ(static_cast(duplicateFailure.error()->code()), - static_cast(DiagnosticCode::Conflict)); + CHECK_EQ(static_cast(duplicateFailure.error()->code()), static_cast(DiagnosticCode::Conflict)); CHECK_EQ(restored.snapshotJson(), before); } TEST_CASE("snapshot.consumers.productionEnvelopeRoundTripAndUnknownVersionIsAtomic") { - const auto hash = testHashProvider(); - const auto id = persistentId("99999999-8888-4777-8666-555555555555"); - auto snapshotJson = [](const eve::production::WorkQueue& queue) { + const auto hash = testHashProvider(); + const auto id = persistentId("99999999-8888-4777-8666-555555555555"); + auto snapshotJson = [](const eve::production::WorkQueue& queue) { auto result = queue.snapshot(); return std::move(result).expect("production snapshot must serialize"); }; eve::production::WorkQueue queue(id); - auto taskIdResult = queue.enqueue("factory", "smelt", "iron", - eve::Value(eve::Value::Object{{"ore", eve::Value(1)}}), 2.0); + auto taskIdResult = + queue.enqueue("factory", "smelt", "iron", eve::Value(eve::Value::Object{{"ore", eve::Value(1)}}), 2.0); REQUIRE(taskIdResult.ok()); auto captured = queue.snapshot(hash); @@ -242,65 +231,61 @@ TEST_CASE("snapshot.consumers.productionEnvelopeRoundTripAndUnknownVersionIsAtom REQUIRE(restored.restoreSnapshot(captured.value(), hash).ok()); CHECK_EQ(snapshotJson(restored), snapshotJson(queue)); - auto old = reseal(captured.value(), SchemaVersion(0), captured.value().payload, hash); + auto old = reseal(captured.value(), SchemaVersion(0), captured.value().payload, hash); eve::production::WorkQueue migrated(id); REQUIRE(migrated.restoreSnapshot(old, hash).ok()); CHECK_EQ(snapshotJson(migrated), snapshotJson(queue)); - auto newer = reseal(captured.value(), SchemaVersion(2), captured.value().payload, hash); - const std::string before = snapshotJson(restored); - auto unknown = restored.restoreSnapshot(newer, hash); + auto newer = reseal(captured.value(), SchemaVersion(2), captured.value().payload, hash); + const std::string before = snapshotJson(restored); + auto unknown = restored.restoreSnapshot(newer, hash); CHECK(!unknown.ok()); - CHECK_EQ(static_cast(unknown.error()->code()), - static_cast(DiagnosticCode::UnknownVersion)); + CHECK_EQ(static_cast(unknown.error()->code()), static_cast(DiagnosticCode::UnknownVersion)); CHECK_EQ(snapshotJson(restored), before); - auto duplicateRevision = reseal(captured.value(), SchemaVersion(1), - withPayloadMetadata(captured.value().payload, "revision", - captured.value().revision.value() + 1), hash); + auto duplicateRevision = + reseal(captured.value(), SchemaVersion(1), + withPayloadMetadata(captured.value().payload, "revision", captured.value().revision.value() + 1), hash); auto duplicateFailure = restored.restoreSnapshot(duplicateRevision, hash); CHECK(!duplicateFailure.ok()); - CHECK_EQ(static_cast(duplicateFailure.error()->code()), - static_cast(DiagnosticCode::Conflict)); + CHECK_EQ(static_cast(duplicateFailure.error()->code()), static_cast(DiagnosticCode::Conflict)); CHECK_EQ(snapshotJson(restored), before); } TEST_CASE("snapshot.consumers.authorityAndDefinitionsRejectDuplicateMetadataAtomically") { const auto hash = testHashProvider(); - const auto authorityId = persistentId("11111111-2222-4333-8444-555555555555"); + const auto authorityId = persistentId("11111111-2222-4333-8444-555555555555"); eve::authority::Store authority(authorityId); REQUIRE(!authority.grant("actor", "scope", "read", "test").empty()); auto authoritySnapshot = authority.snapshot(hash); REQUIRE(authoritySnapshot.ok()); eve::authority::Store authorityRestored(authorityId); REQUIRE(authorityRestored.restoreSnapshot(authoritySnapshot.value(), hash).ok()); - const std::string authorityBefore = authorityRestored.snapshotJson(); - auto authorityConflict = reseal( - authoritySnapshot.value(), SchemaVersion(1), - withPayloadMetadata(authoritySnapshot.value().payload, "revision", - authoritySnapshot.value().revision.value() + 1), hash); - auto authorityFailure = authorityRestored.restoreSnapshot(authorityConflict, hash); + const std::string authorityBefore = authorityRestored.snapshotJson(); + auto authorityConflict = reseal(authoritySnapshot.value(), SchemaVersion(1), + withPayloadMetadata(authoritySnapshot.value().payload, "revision", + authoritySnapshot.value().revision.value() + 1), + hash); + auto authorityFailure = authorityRestored.restoreSnapshot(authorityConflict, hash); CHECK(!authorityFailure.ok()); - CHECK_EQ(static_cast(authorityFailure.error()->code()), - static_cast(DiagnosticCode::Conflict)); + CHECK_EQ(static_cast(authorityFailure.error()->code()), static_cast(DiagnosticCode::Conflict)); CHECK_EQ(authorityRestored.snapshotJson(), authorityBefore); - const auto definitionsId = persistentId("aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee"); + const auto definitionsId = persistentId("aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee"); eve::definitions::DefinitionRegistry definitions(definitionsId); REQUIRE(definitions.insert("unit", "tank", 1, "{\"speed\":4}").ok()); auto definitionsSnapshot = definitions.snapshot(hash); REQUIRE(definitionsSnapshot.ok()); eve::definitions::DefinitionRegistry definitionsRestored(definitionsId); REQUIRE(definitionsRestored.restoreSnapshot(definitionsSnapshot.value(), hash).ok()); - const std::string definitionsBefore = definitionsRestored.snapshotJson(); - auto definitionsConflict = reseal( + const std::string definitionsBefore = definitionsRestored.snapshotJson(); + auto definitionsConflict = reseal( definitionsSnapshot.value(), SchemaVersion(1), - withPayloadMetadata(definitionsSnapshot.value().payload, "tick", - definitionsSnapshot.value().tick.value() + 1), hash); + withPayloadMetadata(definitionsSnapshot.value().payload, "tick", definitionsSnapshot.value().tick.value() + 1), + hash); auto definitionsFailure = definitionsRestored.restoreSnapshot(definitionsConflict, hash); CHECK(!definitionsFailure.ok()); - CHECK_EQ(static_cast(definitionsFailure.error()->code()), - static_cast(DiagnosticCode::Conflict)); + CHECK_EQ(static_cast(definitionsFailure.error()->code()), static_cast(DiagnosticCode::Conflict)); CHECK_EQ(definitionsRestored.snapshotJson(), definitionsBefore); } diff --git a/test/snapshot_envelope.cpp b/test/snapshot_envelope.cpp index ef42db2b..9ef47ece 100644 --- a/test/snapshot_envelope.cpp +++ b/test/snapshot_envelope.cpp @@ -46,21 +46,17 @@ SnapshotHashProvider testHashProvider() { } ContentId::Bytes bytes{}; for (int index = 0; index < 8; ++index) { - bytes[static_cast(index)] = - static_cast(left >> (56 - index * 8)); - bytes[static_cast(index + 8)] = - static_cast(right >> (56 - index * 8)); + bytes[static_cast(index)] = static_cast(left >> (56 - index * 8)); + bytes[static_cast(index + 8)] = static_cast(right >> (56 - index * 8)); } return Result::success(ContentId(bytes)); }; } -SnapshotEnvelope makeEnvelope(std::string type, LogicalId schema, SchemaVersion version, - PersistentId instanceId, Value payload, - const SnapshotHashProvider& hashProvider) { - auto result = eve::makeSnapshotEnvelope(std::move(type), std::move(schema), version, instanceId, - eve::Revision(7), SimulationTick(11), std::move(payload), - hashProvider); +SnapshotEnvelope makeEnvelope(std::string type, LogicalId schema, SchemaVersion version, PersistentId instanceId, + Value payload, const SnapshotHashProvider& hashProvider) { + auto result = eve::makeSnapshotEnvelope(std::move(type), std::move(schema), version, instanceId, eve::Revision(7), + SimulationTick(11), std::move(payload), hashProvider); REQUIRE(result.ok()); return std::move(result).takeValue(); } @@ -68,13 +64,13 @@ SnapshotEnvelope makeEnvelope(std::string type, LogicalId schema, SchemaVersion } // namespace TEST_CASE("snapshot.envelope.canonicalHashRoundTripAndNoWallClock") { - const auto hash = testHashProvider(); + const auto hash = testHashProvider(); Value::Object payloadObject; payloadObject.emplace("z", Value(std::int64_t(2))); payloadObject.emplace("a", Value("stable")); - const auto envelope = makeEnvelope("test.snapshot", logicalId("test:payload"), SchemaVersion(1), - persistentId("01234567-89ab-4cde-8fab-0123456789ab"), - Value(std::move(payloadObject)), hash); + const auto envelope = + makeEnvelope("test.snapshot", logicalId("test:payload"), SchemaVersion(1), + persistentId("01234567-89ab-4cde-8fab-0123456789ab"), Value(std::move(payloadObject)), hash); auto hashInput = eve::snapshotHashInput(envelope); REQUIRE(hashInput.ok()); @@ -97,25 +93,25 @@ TEST_CASE("snapshot.envelope.canonicalHashRoundTripAndNoWallClock") { } TEST_CASE("snapshot.migration.rejectsUnknownNewVersionAndHashMismatch") { - const auto hash = testHashProvider(); - const auto schema = logicalId("test:payload"); + const auto hash = testHashProvider(); + const auto schema = logicalId("test:payload"); SnapshotMigrationChain chain; - auto registration = chain.add( - schema, SchemaVersion(0), SchemaVersion(1), [](const Value& payload) -> Result { + auto registration = + chain.add(schema, SchemaVersion(0), SchemaVersion(1), [](const Value& payload) -> Result { const auto* object = payload.getIf(); if (!object) return Result::failure( eve::Diagnostic::error(DiagnosticCode::ParseError, "payload must be an object")); Value::Object migrated = *object; - migrated["version"] = Value(std::int64_t(1)); + migrated["version"] = Value(std::int64_t(1)); return Result::success(Value(std::move(migrated))); }); REQUIRE(registration.ok()); Value::Object oldPayload; oldPayload.emplace("version", Value(std::int64_t(0))); - auto old = makeEnvelope("test.snapshot", schema, SchemaVersion(0), PersistentId::nil(), - Value(std::move(oldPayload)), hash); + auto old = makeEnvelope("test.snapshot", schema, SchemaVersion(0), PersistentId::nil(), + Value(std::move(oldPayload)), hash); auto migrated = chain.migrate(std::move(old), SchemaVersion(1), hash); REQUIRE(migrated.ok()); CHECK_EQ(migrated.value().schemaVersion, SchemaVersion(1)); @@ -123,26 +119,26 @@ TEST_CASE("snapshot.migration.rejectsUnknownNewVersionAndHashMismatch") { Value::Object newPayload; newPayload.emplace("version", Value(std::int64_t(2))); - auto newer = makeEnvelope("test.snapshot", schema, SchemaVersion(2), PersistentId::nil(), - Value(std::move(newPayload)), hash); + auto newer = makeEnvelope("test.snapshot", schema, SchemaVersion(2), PersistentId::nil(), + Value(std::move(newPayload)), hash); auto rejected = chain.migrate(std::move(newer), SchemaVersion(1), hash); CHECK(!rejected.ok()); CHECK_EQ(static_cast(rejected.code()), static_cast(eve::StatusCode::Failed)); CHECK_EQ(static_cast(rejected.error()->code()), static_cast(DiagnosticCode::UnknownVersion)); - auto valid = makeEnvelope("test.snapshot", schema, SchemaVersion(1), PersistentId::nil(), - Value(Value::Object{}), hash); + auto valid = + makeEnvelope("test.snapshot", schema, SchemaVersion(1), PersistentId::nil(), Value(Value::Object{}), hash); valid.contentHash = ContentId::nil(); - auto mismatch = eve::verifySnapshotEnvelope(valid, hash); + auto mismatch = eve::verifySnapshotEnvelope(valid, hash); CHECK(!mismatch.ok()); CHECK_EQ(static_cast(mismatch.error()->code()), static_cast(DiagnosticCode::HashMismatch)); } TEST_CASE("snapshot.consumers.authorityAndDefinitionsRoundTripAndMetadata") { - const auto hash = testHashProvider(); - const auto authorityId = persistentId("11111111-2222-4333-8444-555555555555"); + const auto hash = testHashProvider(); + const auto authorityId = persistentId("11111111-2222-4333-8444-555555555555"); eve::authority::Store authority(authorityId); - const auto ruleId = authority.grant("actor", "scope", "read", "test", 3); + const auto ruleId = authority.grant("actor", "scope", "read", "test", 3); REQUIRE(!ruleId.empty()); auto authoritySnapshot = authority.snapshot(hash); REQUIRE(authoritySnapshot.ok()); @@ -150,12 +146,12 @@ TEST_CASE("snapshot.consumers.authorityAndDefinitionsRoundTripAndMetadata") { CHECK_EQ(authoritySnapshot.value().revision, eve::Revision(1)); eve::authority::Store authorityCopy(authorityId); - auto authorityRestored = authorityCopy.restoreSnapshot(authoritySnapshot.value(), hash); + auto authorityRestored = authorityCopy.restoreSnapshot(authoritySnapshot.value(), hash); REQUIRE(authorityRestored.ok()); CHECK(authorityCopy.can("actor", "scope", "read")); CHECK_EQ(authorityCopy.snapshot(hash).value().contentHash, authoritySnapshot.value().contentHash); - const auto definitionsId = persistentId("aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee"); + const auto definitionsId = persistentId("aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee"); eve::definitions::DefinitionRegistry definitions(definitionsId); REQUIRE(definitions.insert("unit", "tank", 1, "{\"speed\":4}").ok()); auto definitionsSnapshot = definitions.snapshot(hash); @@ -171,40 +167,38 @@ TEST_CASE("snapshot.consumers.authorityAndDefinitionsRoundTripAndMetadata") { } TEST_CASE("snapshot.consumers.malformedPayloadAndUnknownVersionLeaveStateUnchanged") { - const auto hash = testHashProvider(); - const auto authorityId = persistentId("99999999-8888-4777-8666-555555555555"); + const auto hash = testHashProvider(); + const auto authorityId = persistentId("99999999-8888-4777-8666-555555555555"); eve::authority::Store authority(authorityId); REQUIRE(!authority.grant("actor", "scope", "read", "before").empty()); const std::string authorityBefore = authority.snapshotJson(); Value::Object malformedAuthority; malformedAuthority.emplace("version", Value(std::int64_t(1))); - auto malformedAuthorityEnvelope = makeEnvelope( - "authority.store", logicalId("authority:store"), SchemaVersion(1), authorityId, - Value(std::move(malformedAuthority)), hash); - auto authorityFailure = authority.restoreSnapshot(malformedAuthorityEnvelope, hash); + auto malformedAuthorityEnvelope = makeEnvelope("authority.store", logicalId("authority:store"), SchemaVersion(1), + authorityId, Value(std::move(malformedAuthority)), hash); + auto authorityFailure = authority.restoreSnapshot(malformedAuthorityEnvelope, hash); CHECK(!authorityFailure.ok()); CHECK_EQ(authority.snapshotJson(), authorityBefore); Value::Object newerAuthority; newerAuthority.emplace("version", Value(std::int64_t(2))); - auto newerAuthorityEnvelope = makeEnvelope( - "authority.store", logicalId("authority:store"), SchemaVersion(2), authorityId, - Value(std::move(newerAuthority)), hash); - auto unknownAuthority = authority.restoreSnapshot(newerAuthorityEnvelope, hash); + auto newerAuthorityEnvelope = makeEnvelope("authority.store", logicalId("authority:store"), SchemaVersion(2), + authorityId, Value(std::move(newerAuthority)), hash); + auto unknownAuthority = authority.restoreSnapshot(newerAuthorityEnvelope, hash); CHECK(!unknownAuthority.ok()); CHECK_EQ(static_cast(unknownAuthority.error()->code()), static_cast(DiagnosticCode::UnknownVersion)); CHECK_EQ(authority.snapshotJson(), authorityBefore); - const auto definitionsId = persistentId("12345678-1234-4123-8123-123456789012"); + const auto definitionsId = persistentId("12345678-1234-4123-8123-123456789012"); eve::definitions::DefinitionRegistry definitions(definitionsId); REQUIRE(definitions.insert("unit", "before", 1, "{}").ok()); const std::string definitionsBefore = definitions.snapshotJson(); - Value::Object malformedDefinitions; + Value::Object malformedDefinitions; malformedDefinitions.emplace("version", Value(std::int64_t(1))); - auto malformedDefinitionsEnvelope = makeEnvelope( - "definitions.registry", logicalId("definitions:registry"), SchemaVersion(1), definitionsId, - Value(std::move(malformedDefinitions)), hash); + auto malformedDefinitionsEnvelope = + makeEnvelope("definitions.registry", logicalId("definitions:registry"), SchemaVersion(1), definitionsId, + Value(std::move(malformedDefinitions)), hash); auto definitionsFailure = definitions.restoreSnapshot(malformedDefinitionsEnvelope, hash); CHECK(!definitionsFailure.ok()); CHECK_EQ(definitions.snapshotJson(), definitionsBefore); diff --git a/test/squirrel_binding.cpp b/test/squirrel_binding.cpp index d6e8aa8c..79a70134 100644 --- a/test/squirrel_binding.cpp +++ b/test/squirrel_binding.cpp @@ -16,7 +16,7 @@ using eve::Result; using eve::Value; TEST_CASE("squirrel_binding.value_round_trip_preserves_nested_value") { - ssq::VM vm(1024, ssq::Libs::ALL); + ssq::VM vm(1024, ssq::Libs::ALL); Value::Array items; items.emplace_back(Value::Object{{"enabled", true}, {"label", "nested"}}); items.emplace_back(std::int64_t{9}); @@ -35,7 +35,7 @@ TEST_CASE("squirrel_binding.value_round_trip_preserves_nested_value") { } TEST_CASE("squirrel_binding.value_rejects_non_finite_numbers_with_path_and_source") { - ssq::VM vm(1024, ssq::Libs::ALL); + ssq::VM vm(1024, ssq::Libs::ALL); const auto top = sq_gettop(vm.getHandle()); sq_newarray(vm.getHandle(), 0); sq_pushfloat(vm.getHandle(), std::numeric_limits::infinity()); @@ -51,7 +51,7 @@ TEST_CASE("squirrel_binding.value_rejects_non_finite_numbers_with_path_and_sourc } TEST_CASE("squirrel_binding.value_enforces_depth_limit") { - ssq::VM vm(1024, ssq::Libs::ALL); + ssq::VM vm(1024, ssq::Libs::ALL); const auto top = sq_gettop(vm.getHandle()); sq_newarray(vm.getHandle(), 0); sq_newarray(vm.getHandle(), 0); @@ -59,7 +59,7 @@ TEST_CASE("squirrel_binding.value_enforces_depth_limit") { eve::script::SquirrelValueOptions options; options.maxDepth = 1; - auto result = eve::script::valueFromSquirrel(vm.getHandle(), -1, options); + auto result = eve::script::valueFromSquirrel(vm.getHandle(), -1, options); CHECK(!result.ok()); REQUIRE(result.error() != nullptr); CHECK(result.error()->path() == "$[0]"); @@ -67,15 +67,15 @@ TEST_CASE("squirrel_binding.value_enforces_depth_limit") { } TEST_CASE("squirrel_binding.result_projection_and_explicit_ignore_are_uniform") { - ssq::VM vm(1024, ssq::Libs::ALL); + ssq::VM vm(1024, ssq::Libs::ALL); ssq::Table eve = vm.addTable("eve"); eve::script::exposeResultBindings(eve); - auto projected = eve::script::projectResult( - vm.getHandle(), - Result::failure(Diagnostic::error( - DiagnosticCode::NotFound, "missing value", "payload.name", {}, "test.source")), - [](int value) { return Value(static_cast(value)); }); + auto projected = + eve::script::projectResult(vm.getHandle(), + Result::failure(Diagnostic::error(DiagnosticCode::NotFound, "missing value", + "payload.name", {}, "test.source")), + [](int value) { return Value(static_cast(value)); }); CHECK(!projected.find("ok").toBool()); CHECK(projected.find("checked").toBool()); CHECK(projected.find("hasValue").toBool() == false); @@ -86,9 +86,8 @@ TEST_CASE("squirrel_binding.result_projection_and_explicit_ignore_are_uniform") CHECK(diagnostic.find("source").toString() == "test.source"); eve.set("target", projected); - vm.run(vm.compileSource( - "ignored <- eve.result.ignore(eve.target, \"optional feature\");\n", - "result-ignore-test.nut")); + vm.run(vm.compileSource("ignored <- eve.result.ignore(eve.target, \"optional feature\");\n", + "result-ignore-test.nut")); CHECK(vm.find("ignored").toBool()); CHECK(projected.find("ignored").toBool()); CHECK(projected.find("ignoreReason").toString() == "optional feature"); diff --git a/test/statepatch.cpp b/test/statepatch.cpp index 8cb966ef..9a8d990d 100644 --- a/test/statepatch.cpp +++ b/test/statepatch.cpp @@ -1,9 +1,9 @@ #include "zeroerr/assert.h" #include "zeroerr/unittest.h" +#include "StatePatchTestSupport.h" #include "common/Module.h" #include "statepatch/StatePatch.h" -#include "StatePatchTestSupport.h" #include @@ -11,13 +11,11 @@ using namespace eve::statepatch; -namespace { - -} // namespace +namespace {} // namespace TEST_CASE("statepatch.batch.commitsCanonicalValuesAtomically") { Store store; - auto batch = eve::test_support::openStatePatchBatch(store); + auto batch = eve::test_support::openStatePatchBatch(store); REQUIRE(batch.view.isBound()); CHECK(batch.view->set("entity-b", "health", "{ \"max\": 10, \"now\": 8 }")); CHECK(batch.view->set("entity-a", "active", "true")); @@ -30,7 +28,7 @@ TEST_CASE("statepatch.batch.commitsCanonicalValuesAtomically") { TEST_CASE("statepatch.batch.conflictProducesNoPartialWrites") { Store store; - auto seed = eve::test_support::openStatePatchBatch(store); + auto seed = eve::test_support::openStatePatchBatch(store); REQUIRE(seed.view.isBound()); seed.view->set("one", "value", "1"); REQUIRE(store.commit(seed.view.get())); @@ -48,7 +46,7 @@ TEST_CASE("statepatch.batch.conflictProducesNoPartialWrites") { TEST_CASE("statepatch.batch.validatesEverythingBeforeCommit") { Store store; - auto batch = eve::test_support::openStatePatchBatch(store); + auto batch = eve::test_support::openStatePatchBatch(store); REQUIRE(batch.view.isBound()); CHECK(!batch.view->set("ok", "bad-json", "{")); batch.view->set("", "missing-subject", "1"); @@ -60,7 +58,7 @@ TEST_CASE("statepatch.batch.validatesEverythingBeforeCommit") { TEST_CASE("statepatch.batch.sequentialCasAndNoopRevision") { Store store; - auto batch = eve::test_support::openStatePatchBatch(store); + auto batch = eve::test_support::openStatePatchBatch(store); REQUIRE(batch.view.isBound()); batch.view->set("subject", "key", "1"); batch.view->setExpected("subject", "key", "2", "1"); @@ -78,7 +76,7 @@ TEST_CASE("statepatch.batch.sequentialCasAndNoopRevision") { TEST_CASE("statepatch.query.dirtyAndEventsAreDeterministic") { Store store; - auto batch = eve::test_support::openStatePatchBatch(store); + auto batch = eve::test_support::openStatePatchBatch(store); REQUIRE(batch.view.isBound()); batch.view->set("z", "b", "2"); batch.view->set("a", "c", "3"); @@ -100,7 +98,7 @@ TEST_CASE("statepatch.query.dirtyAndEventsAreDeterministic") { TEST_CASE("statepatch.remove.recordsOldValue") { Store store; - auto seed = eve::test_support::openStatePatchBatch(store); + auto seed = eve::test_support::openStatePatchBatch(store); REQUIRE(seed.view.isBound()); seed.view->set("s", "k", "{\"x\":1}"); REQUIRE(store.commit(seed.view.get())); @@ -118,7 +116,7 @@ TEST_CASE("statepatch.remove.recordsOldValue") { TEST_CASE("statepatch.snapshot.roundTripsAndRestoreIsTransactional") { Store store; - auto batch = eve::test_support::openStatePatchBatch(store); + auto batch = eve::test_support::openStatePatchBatch(store); REQUIRE(batch.view.isBound()); batch.view->set("b", "x", "[3,2,1]"); batch.view->set("a", "x", "{\"z\":0,\"a\":true}"); diff --git a/test/targeting.cpp b/test/targeting.cpp index 3ceaf73e..7bbeb36a 100644 --- a/test/targeting.cpp +++ b/test/targeting.cpp @@ -1,8 +1,8 @@ +#include "sensing/Targeting.h" #include "common/Capability.h" #include "common/Identity.h" #include "physics/TargetingLineOfSightAdapter.h" #include "physics/World3D.h" -#include "sensing/Targeting.h" #include "zeroerr/assert.h" #include "zeroerr/unittest.h" @@ -12,25 +12,25 @@ namespace { +using eve::LogicalId; using eve::PersistentId; +using eve::physics::TargetingLineOfSightAdapter; +using eve::physics::World3D; using eve::sensing::CoordinateSpace; using eve::sensing::GridPoint; using eve::sensing::ILineOfSightQuery; using eve::sensing::ISensingCandidateProvider; using eve::sensing::LineOfSightResult; +using eve::sensing::SensingCandidateProvider; +using eve::sensing::SubjectRef; using eve::sensing::TargetCandidate; using eve::sensing::TargetDomain; -using eve::sensing::TargetLocation; using eve::sensing::TargetingQuery; using eve::sensing::TargetingResolver; using eve::sensing::TargetingSpec; -using eve::sensing::SensingCandidateProvider; -using eve::sensing::SubjectRef; +using eve::sensing::TargetLocation; using eve::sensing::WorldArea; using eve::sensing::WorldPoint; -using eve::LogicalId; -using eve::physics::TargetingLineOfSightAdapter; -using eve::physics::World3D; SubjectRef subject(const char* text) { auto id = PersistentId::parse(text); @@ -57,14 +57,13 @@ class VisibleLineOfSight final : public ILineOfSightQuery { public: explicit VisibleLineOfSight(bool visible) : visible_(visible) {} - eve::Result query(const TargetLocation& from, - const TargetLocation& to) const override { + eve::Result query(const TargetLocation& from, const TargetLocation& to) const override { if (from.index() != to.index()) return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::InvalidArgument, "test LOS received mixed coordinate spaces")); if (from.index() == 1) - return eve::Result::failure(eve::Diagnostic::error( - eve::DiagnosticCode::Unsupported, "test LOS does not support grid coordinates")); + return eve::Result::failure( + eve::Diagnostic::error(eve::DiagnosticCode::Unsupported, "test LOS does not support grid coordinates")); return eve::Result::success(LineOfSightResult{visible_, std::nullopt}); } @@ -79,9 +78,9 @@ TEST_CASE("targeting.typesKeepIdentityAndCoordinateSpacesSeparate") { CHECK((!std::is_convertible_v)); CHECK((!std::is_convertible_v)); - const auto p2 = world2D(1.f, 2.f); - const auto p3 = world3D(1.f, 2.f, 3.f); - auto circle = WorldArea::circle2D(p2, 4.f); + const auto p2 = world2D(1.f, 2.f); + const auto p3 = world3D(1.f, 2.f, 3.f); + auto circle = WorldArea::circle2D(p2, 4.f); REQUIRE(circle.ok()); CHECK_EQ(circle.value().space(), CoordinateSpace::World2D); @@ -103,37 +102,37 @@ TEST_CASE("targeting.typesKeepIdentityAndCoordinateSpacesSeparate") { } TEST_CASE("targeting.providerAndResolverApplyGenericConstraints") { - ResetCapabilities reset; + ResetCapabilities reset; SensingCandidateProvider provider; - const auto origin = subject("00000000-0000-7000-8000-000000000001"); - const auto enemy = subject("00000000-0000-7000-8000-000000000002"); - const auto farEnemy = subject("00000000-0000-7000-8000-000000000003"); - const auto ally = subject("00000000-0000-7000-8000-000000000004"); - auto zoneId = LogicalId::parse("arena:central"); + const auto origin = subject("00000000-0000-7000-8000-000000000001"); + const auto enemy = subject("00000000-0000-7000-8000-000000000002"); + const auto farEnemy = subject("00000000-0000-7000-8000-000000000003"); + const auto ally = subject("00000000-0000-7000-8000-000000000004"); + auto zoneId = LogicalId::parse("arena:central"); REQUIRE(zoneId.has_value()); auto zone = eve::sensing::ZoneRef::fromLogicalId(*zoneId); REQUIRE(zone.has_value()); - REQUIRE(provider.upsert(TargetCandidate{enemy, world2D(3.f, 4.f), TargetDomain::Enemy, - {"unit", "visible"}, {*zone}}).ok()); - REQUIRE(provider.upsert(TargetCandidate{farEnemy, world2D(50.f, 0.f), TargetDomain::Enemy, - {"unit"}, {}}).ok()); - REQUIRE(provider.upsert(TargetCandidate{ally, world2D(2.f, 0.f), TargetDomain::Ally, - {"unit", "visible"}, {}}).ok()); + REQUIRE( + provider.upsert(TargetCandidate{enemy, world2D(3.f, 4.f), TargetDomain::Enemy, {"unit", "visible"}, {*zone}}) + .ok()); + REQUIRE(provider.upsert(TargetCandidate{farEnemy, world2D(50.f, 0.f), TargetDomain::Enemy, {"unit"}, {}}).ok()); + REQUIRE( + provider.upsert(TargetCandidate{ally, world2D(2.f, 0.f), TargetDomain::Ally, {"unit", "visible"}, {}}).ok()); eve::cap::provide(&provider); TargetingSpec spec; - spec.space = CoordinateSpace::World2D; - spec.domain = TargetDomain::Enemy; - spec.minCount = 1; - spec.maxCount = 2; - spec.maxRange = 10.f; + spec.space = CoordinateSpace::World2D; + spec.domain = TargetDomain::Enemy; + spec.minCount = 1; + spec.maxCount = 2; + spec.maxRange = 10.f; spec.requiredTags = {"unit"}; - spec.zone = *zone; - spec.worldArea = std::move(WorldArea::circle2D(world2D(0.f, 0.f), 10.f)).expect("area"); + spec.zone = *zone; + spec.worldArea = std::move(WorldArea::circle2D(world2D(0.f, 0.f), 10.f)).expect("area"); TargetingQuery query{origin, world2D(0.f, 0.f), spec}; - auto resolved = TargetingResolver{}.resolve(query); + auto resolved = TargetingResolver{}.resolve(query); REQUIRE(resolved.ok()); CHECK_EQ(resolved.value().subjects().size(), 1u); CHECK_EQ(resolved.value().subjects()[0], enemy); @@ -142,14 +141,14 @@ TEST_CASE("targeting.providerAndResolverApplyGenericConstraints") { TEST_CASE("targeting.missingCapabilitiesAreUnsupported") { ResetCapabilities reset; - TargetingQuery query{subject("00000000-0000-7000-8000-000000000001"), world2D(0.f, 0.f), TargetingSpec{}}; + TargetingQuery query{subject("00000000-0000-7000-8000-000000000001"), world2D(0.f, 0.f), TargetingSpec{}}; auto noCandidates = TargetingResolver{}.resolve(query); CHECK(!noCandidates.ok()); CHECK_EQ(noCandidates.code(), eve::StatusCode::Unsupported); TargetingQuery mixed{query.origin, GridPoint::grid2D(0, 0), query.spec}; - auto mixedResult = TargetingResolver{}.resolve(mixed); + auto mixedResult = TargetingResolver{}.resolve(mixed); CHECK(!mixedResult.ok()); CHECK_EQ(mixedResult.code(), eve::StatusCode::Rejected); REQUIRE(mixedResult.error()); @@ -158,27 +157,27 @@ TEST_CASE("targeting.missingCapabilitiesAreUnsupported") { SensingCandidateProvider provider; eve::cap::provide(&provider); query.spec.lineOfSight = eve::sensing::LineOfSightMode::Required; - auto noLos = TargetingResolver{}.resolve(query); + auto noLos = TargetingResolver{}.resolve(query); CHECK(!noLos.ok()); CHECK_EQ(noLos.code(), eve::StatusCode::Unsupported); } TEST_CASE("targeting.losCapabilityIsInjectedAndNeverConvertsGridCoordinates") { - ResetCapabilities reset; + ResetCapabilities reset; SensingCandidateProvider provider; - const auto origin = subject("00000000-0000-7000-8000-000000000001"); - const auto target = subject("00000000-0000-7000-8000-000000000002"); - const auto gridTarget = subject("00000000-0000-7000-8000-000000000003"); + const auto origin = subject("00000000-0000-7000-8000-000000000001"); + const auto target = subject("00000000-0000-7000-8000-000000000002"); + const auto gridTarget = subject("00000000-0000-7000-8000-000000000003"); REQUIRE(provider.upsert(TargetCandidate{target, world3D(1.f, 0.f, 0.f), TargetDomain::Enemy, {}, {}}).ok()); REQUIRE(provider.upsert(TargetCandidate{gridTarget, GridPoint::grid2D(1, 0), TargetDomain::Enemy, {}, {}}).ok()); eve::cap::provide(&provider); TargetingSpec spec; - spec.space = CoordinateSpace::World3D; - spec.domain = TargetDomain::Enemy; - spec.minCount = 1; - spec.maxCount = 1; - spec.maxRange = 10.f; + spec.space = CoordinateSpace::World3D; + spec.domain = TargetDomain::Enemy; + spec.minCount = 1; + spec.maxCount = 1; + spec.maxRange = 10.f; spec.lineOfSight = eve::sensing::LineOfSightMode::Required; TargetingQuery query{origin, world3D(0.f, 0.f, 0.f), spec}; @@ -197,34 +196,34 @@ TEST_CASE("targeting.losCapabilityIsInjectedAndNeverConvertsGridCoordinates") { CHECK_EQ(visibleResult.value().subjects().size(), 1u); TargetingSpec gridSpec; - gridSpec.space = CoordinateSpace::Grid2D; - gridSpec.minCount = 0; - gridSpec.maxCount = 1; + gridSpec.space = CoordinateSpace::Grid2D; + gridSpec.minCount = 0; + gridSpec.maxCount = 1; gridSpec.lineOfSight = eve::sensing::LineOfSightMode::Required; TargetingQuery gridQuery{origin, GridPoint::grid2D(0, 0), gridSpec}; - auto gridResult = TargetingResolver{}.resolve(gridQuery); + auto gridResult = TargetingResolver{}.resolve(gridQuery); CHECK(!gridResult.ok()); CHECK_EQ(gridResult.code(), eve::StatusCode::Unsupported); } TEST_CASE("targeting.physicsAdapterRequiresOneActiveWorld") { TargetingLineOfSightAdapter adapter; - const auto from = world3D(0.f, 0.f, 0.f); - const auto to = world3D(1.f, 0.f, 0.f); + const auto from = world3D(0.f, 0.f, 0.f); + const auto to = world3D(1.f, 0.f, 0.f); auto noWorld = adapter.query(TargetLocation{from}, TargetLocation{to}); CHECK(!noWorld.ok()); CHECK_EQ(noWorld.code(), eve::StatusCode::Unsupported); World3D first(0.f, 0.f, 0.f, false); - auto firstRegistration = adapter.addWorld(&first); + auto firstRegistration = adapter.addWorld(&first); REQUIRE(firstRegistration.ok()); auto duplicateRegistration = adapter.addWorld(&first); REQUIRE(duplicateRegistration.ok()); CHECK_EQ(duplicateRegistration.code(), eve::StatusCode::NoOp); World3D second(0.f, 0.f, 0.f, false); - auto conflictingRegistration = adapter.addWorld(&second); + auto conflictingRegistration = adapter.addWorld(&second); CHECK(!conflictingRegistration.ok()); CHECK_EQ(conflictingRegistration.code(), eve::StatusCode::Conflict); diff --git a/test/time_contract.cpp b/test/time_contract.cpp index 7a1c61e5..3afb30f2 100644 --- a/test/time_contract.cpp +++ b/test/time_contract.cpp @@ -33,15 +33,15 @@ TEST_CASE("time.durationAndTimestampUseStrongFixedUnits") { CHECK_EQ(duration.value().seconds(), 1.25); eve::MonotonicTimestamp later(150); - auto elapsed = later.since(eve::MonotonicTimestamp(50)); + auto elapsed = later.since(eve::MonotonicTimestamp(50)); REQUIRE(elapsed.ok()); CHECK_EQ(elapsed.value().nanoseconds(), std::int64_t(100)); auto backwards = eve::MonotonicTimestamp(10).since(eve::MonotonicTimestamp(11)); CHECK(!backwards.ok()); - auto maximum = eve::MonotonicTimestamp(std::numeric_limits::max()) - .since(eve::MonotonicTimestamp::zero()); + auto maximum = + eve::MonotonicTimestamp(std::numeric_limits::max()).since(eve::MonotonicTimestamp::zero()); REQUIRE(maximum.ok()); CHECK_EQ(maximum.value().nanoseconds(), std::numeric_limits::max()); @@ -54,7 +54,7 @@ TEST_CASE("time.durationAndTimestampUseStrongFixedUnits") { } TEST_CASE("time.simulationClock.fixedStepPauseAndSlowMotion") { - ManualTimeSource source; + ManualTimeSource source; eve::SimulationClock clock(source, eve::Duration::fromNanoseconds(100)); auto first = clock.sample(); @@ -112,7 +112,7 @@ TEST_CASE("time.clockUsesMonotonicOnlyAndWallClockIsMetadata") { REQUIRE(second.sample().ok()); firstSource.advance(250); secondSource.advance(250); - auto firstSteps = first.sample(); + auto firstSteps = first.sample(); auto secondSteps = second.sample(); REQUIRE(firstSteps.ok()); REQUIRE(secondSteps.ok()); @@ -123,13 +123,13 @@ TEST_CASE("time.clockUsesMonotonicOnlyAndWallClockIsMetadata") { } TEST_CASE("time.clockRejectsBackwardSourceWithoutStateMutation") { - ManualTimeSource source; + ManualTimeSource source; eve::SimulationClock clock(source, eve::Duration::fromNanoseconds(100)); REQUIRE(clock.sample().ok()); source.advance(100); auto accepted = clock.sample(); REQUIRE(accepted.ok()); - const auto tick = clock.currentTick(); + const auto tick = clock.currentTick(); const auto frame = clock.frameIndex(); source.advance(-200); @@ -144,7 +144,7 @@ TEST_CASE("time.clockRejectsTimestampDistanceOutsideDurationWithoutStateMutation source.setMonotonic(std::numeric_limits::min()); eve::SimulationClock clock(source, eve::Duration::fromNanoseconds(100)); REQUIRE(clock.sample().ok()); - const auto tick = clock.currentTick(); + const auto tick = clock.currentTick(); const auto frame = clock.frameIndex(); source.setMonotonic(std::numeric_limits::max()); @@ -157,10 +157,10 @@ TEST_CASE("time.clockRejectsTimestampDistanceOutsideDurationWithoutStateMutation } TEST_CASE("time.clockRejectsExtremeCatchUpWithoutMultiplicationOverflow") { - ManualTimeSource source; + ManualTimeSource source; eve::SimulationClock clock(source, eve::Duration::fromNanoseconds(1)); - const auto beforeTick = clock.currentTick(); - const auto beforeFrame = clock.frameIndex(); + const auto beforeTick = clock.currentTick(); + const auto beforeFrame = clock.frameIndex(); auto extreme = clock.advance(eve::Duration::fromNanoseconds(std::numeric_limits::max())); CHECK(!extreme.ok()); @@ -170,8 +170,7 @@ TEST_CASE("time.clockRejectsExtremeCatchUpWithoutMultiplicationOverflow") { CHECK_EQ(clock.currentTick(), beforeTick); CHECK_EQ(clock.frameIndex(), beforeFrame); - eve::SimulationClock boundary(source, - eve::Duration::fromNanoseconds(std::numeric_limits::max())); + eve::SimulationClock boundary(source, eve::Duration::fromNanoseconds(std::numeric_limits::max())); auto oneStep = boundary.advance(eve::Duration::fromNanoseconds(std::numeric_limits::max())); REQUIRE(oneStep.ok()); CHECK_EQ(oneStep.value().size(), std::size_t(1)); diff --git a/test/time_subscription_consumers.cpp b/test/time_subscription_consumers.cpp index e468aa60..accb7e33 100644 --- a/test/time_subscription_consumers.cpp +++ b/test/time_subscription_consumers.cpp @@ -1,21 +1,21 @@ #include "zeroerr/assert.h" #include "zeroerr/unittest.h" -#include "animation/Animation.h" #include "animation/AnimClipRegistry.h" #include "animation/AnimTrail.h" +#include "animation/Animation.h" #include "animation/Tween.h" #include "common/Time.h" #include "effects/EffectContainer.h" -#include "platform_event/PlatformEvent.h" #include "particles/ParticleEmitter.h" #include "particles/ParticleRuntime.h" #include "particles/ParticleSystem.h" #include "particles/Particles.h" +#include "platform_event/PlatformEvent.h" -#include #include #include +#include #include #include #include @@ -24,7 +24,7 @@ namespace { class TestEvent final : public eve::platform_event::PlatformEvent { public: - void pump() override {} + void pump() override {} eve::platform_event::Message* wait() override { return nullptr; } }; @@ -32,7 +32,7 @@ public: TEST_CASE("time.checkedConsumers.useSchedulerTickWithoutPrivateRate") { auto* particles = eve::particles::Particles::create(); - auto* emitter = particles->newEmitter(4); + auto* emitter = particles->newEmitter(4); emitter->setRandomSeed(7); emitter->setParticleLifetime(10.f, 10.f); emitter->setDirection(0.f); @@ -41,41 +41,37 @@ TEST_CASE("time.checkedConsumers.useSchedulerTickWithoutPrivateRate") { emitter->setFixedTimeStep(0.5f, 1); emitter->emit(1); - auto advanced = emitter->advance( - {eve::SimulationTick(1), eve::Duration::fromNanoseconds(100000000)}); + auto advanced = emitter->advance({eve::SimulationTick(1), eve::Duration::fromNanoseconds(100000000)}); REQUIRE(advanced.ok()); CHECK(std::abs(emitter->sim()->particles[0].x - 1.f) < 1e-5f); CHECK(emitter->currentSimulationTick() == eve::SimulationTick(1)); - auto duplicate = emitter->advance( - {eve::SimulationTick(1), eve::Duration::fromNanoseconds(100000000)}); + auto duplicate = emitter->advance({eve::SimulationTick(1), eve::Duration::fromNanoseconds(100000000)}); CHECK(!duplicate); CHECK(static_cast(duplicate.code()) == static_cast(eve::StatusCode::Conflict)); } TEST_CASE("time.checkedConsumers.effectsAndAnimationRecordTick") { - eve::effects::EffectContainer effects; + eve::effects::EffectContainer effects; eve::effects::EffectDefinition definition; - definition.id = "temporary"; + definition.id = "temporary"; definition.duration = 1.0; - auto applied = effects.apply(definition, "subject"); + auto applied = effects.apply(definition, "subject"); REQUIRE(applied.ok()); const std::string id = std::move(applied).takeValue(); REQUIRE(effects.find(id) != nullptr); - auto effectStep = effects.advance( - {eve::SimulationTick(3), eve::Duration::fromNanoseconds(250000000)}); + auto effectStep = effects.advance({eve::SimulationTick(3), eve::Duration::fromNanoseconds(250000000)}); REQUIRE(effectStep.ok()); CHECK(effectStep.value().tick == eve::SimulationTick(3)); CHECK(std::abs(effects.find(id)->remaining - 0.75) < 1e-9); - auto animation = eve::animation::Animation::create(); + auto animation = eve::animation::Animation::create(); std::unique_ptr tween(animation->newTween(1.f)); tween->setFrom("x", 0.f); tween->setTo("x", 10.f); tween->start(); - auto animationStep = animation->advance( - {eve::SimulationTick(3), eve::Duration::fromNanoseconds(500000000)}); + auto animationStep = animation->advance({eve::SimulationTick(3), eve::Duration::fromNanoseconds(500000000)}); REQUIRE(animationStep.ok()); CHECK(std::abs(tween->get("x") - 5.f) < 1e-4f); CHECK(animation->currentTick() == eve::SimulationTick(3)); @@ -101,7 +97,7 @@ TEST_CASE("time.checkedConsumers.animationChildrenUseInjectedStep") { auto duplicate = trail.advance({eve::SimulationTick(1), eve::Duration::zero()}); CHECK(!duplicate.ok()); - auto* animation = eve::animation::Animation::create(); + auto* animation = eve::animation::Animation::create(); std::unique_ptr hosted(animation->newTween(1.f)); hosted->setFrom("x", 0.f); hosted->setTo("x", 10.f); @@ -115,17 +111,16 @@ TEST_CASE("time.checkedConsumers.animationChildrenUseInjectedStep") { TEST_CASE("time.checkedConsumers.particleStatsCommitAsOneFrame") { auto* particles = eve::particles::Particles::create(); - auto* emitter = particles->newEmitter(4); + auto* emitter = particles->newEmitter(4); REQUIRE(emitter != nullptr); CHECK(emitter->getConfigReloadObservation() == std::string("unbound")); - auto applied = eve::particles::ParticleSimSystem::advance( - {eve::SimulationTick(1), eve::Duration::zero()}); + auto applied = eve::particles::ParticleSimSystem::advance({eve::SimulationTick(1), eve::Duration::zero()}); REQUIRE(applied.ok()); const auto committed = eve::particles::particleFrameStats(); - auto duplicate = eve::particles::ParticleSimSystem::advance( - {eve::SimulationTick(1), eve::Duration::fromNanoseconds(1)}); + auto duplicate = + eve::particles::ParticleSimSystem::advance({eve::SimulationTick(1), eve::Duration::fromNanoseconds(1)}); CHECK(!duplicate.ok()); const auto afterDuplicate = eve::particles::particleFrameStats(); CHECK(afterDuplicate.frameIndex == committed.frameIndex); @@ -133,8 +128,8 @@ TEST_CASE("time.checkedConsumers.particleStatsCommitAsOneFrame") { CHECK(afterDuplicate.emittersTotal == committed.emittersTotal); CHECK(afterDuplicate.particlesAfter == committed.particlesAfter); - auto invalid = eve::particles::ParticleSimSystem::advance( - {eve::SimulationTick(2), eve::Duration::fromNanoseconds(-1)}); + auto invalid = + eve::particles::ParticleSimSystem::advance({eve::SimulationTick(2), eve::Duration::fromNanoseconds(-1)}); CHECK(!invalid.ok()); const auto afterInvalid = eve::particles::particleFrameStats(); CHECK(afterInvalid.frameIndex == committed.frameIndex); @@ -143,12 +138,12 @@ TEST_CASE("time.checkedConsumers.particleStatsCommitAsOneFrame") { TEST_CASE("subscription.eventPoll.containsCallbackFailureAfterCommit") { TestEvent event; - int observed = 0; - auto throwing = event.subscribePoll([&](const eve::platform_event::Message&) { + int observed = 0; + auto throwing = event.subscribePoll([&](const eve::platform_event::Message&) { ++observed; throw std::runtime_error("listener failure"); }); - auto message = std::make_unique("committed"); + auto message = std::make_unique("committed"); event.push(std::move(message)); auto polled = event.pollOwned(); @@ -160,19 +155,19 @@ TEST_CASE("subscription.eventPoll.containsCallbackFailureAfterCommit") { } TEST_CASE("subscription.eventPoll.supportsReentrantSubscribeAndDispose") { - TestEvent event; - int first = 0; - int second = 0; - bool replaced = false; + TestEvent event; + int first = 0; + int second = 0; + bool replaced = false; eve::Subscription replacement; - auto primary = event.subscribePoll([&](const eve::platform_event::Message&) { + auto primary = event.subscribePoll([&](const eve::platform_event::Message&) { ++first; if (replaced) return; replaced = true; replacement.dispose(); replacement = event.subscribePoll([&](const eve::platform_event::Message&) { ++second; }); }); - replacement = event.subscribePoll([&](const eve::platform_event::Message&) { ++second; }); + replacement = event.subscribePoll([&](const eve::platform_event::Message&) { ++second; }); event.pushData("first"); auto firstPolled = event.pollOwned(); @@ -191,12 +186,12 @@ TEST_CASE("subscription.eventPoll.supportsReentrantSubscribeAndDispose") { TEST_CASE("subscription.animClipReload.supportsAbsentProviderAndReentrancy") { eve::animation::AnimClipRegistry::clear(); - int first = 0; - int second = 0; - bool replaced = false; + int first = 0; + int second = 0; + bool replaced = false; eve::Subscription replacement; - auto primary = eve::animation::AnimClipRegistry::subscribeReload( - [&](const eve::animation::AnimClipRegistry::ReloadEvent&) { + auto primary = + eve::animation::AnimClipRegistry::subscribeReload([&](const eve::animation::AnimClipRegistry::ReloadEvent&) { ++first; if (replaced) return; replaced = true; diff --git a/test/timer_cpp.cpp b/test/timer_cpp.cpp index 58406ae2..2811f80f 100644 --- a/test/timer_cpp.cpp +++ b/test/timer_cpp.cpp @@ -32,9 +32,9 @@ TEST_CASE("timer.step.deltaPositiveAfterSleep") { } TEST_CASE("timer.simulationClockUsesInjectedFixedSteps") { - auto* t = eve::timer::Timer::create(); + auto* t = eve::timer::Timer::create(); const auto before = t->getFrameIndex(); - auto steps = t->stepSimulation(); + auto steps = t->stepSimulation(); REQUIRE(steps.ok()); CHECK(t->getFrameIndex() > before); CHECK(t->getDeltaDuration().nanoseconds() >= 0); diff --git a/test/transaction.cpp b/test/transaction.cpp index 701c4133..6227fd8f 100644 --- a/test/transaction.cpp +++ b/test/transaction.cpp @@ -20,15 +20,14 @@ eve::OperationId operationId(const char* text) { return parsed ? *parsed : eve::OperationId::nil(); } -Plan* createPlan(Ledger& ledger, const char* transaction, const char* correlation = "", - const char* causation = "") { +Plan* createPlan(Ledger& ledger, const char* transaction, const char* correlation = "", const char* causation = "") { auto result = ledger.create(correlation, causation, transactionId(transaction)); REQUIRE(result.ok()); return result.value(); } -eve::OperationId stageOperation(Plan& plan, const char* operation, const char* kind, - const char* target, const char* payload) { +eve::OperationId stageOperation(Plan& plan, const char* operation, const char* kind, const char* target, + const char* payload) { auto result = plan.stage(kind, target, payload, operationId(operation)); REQUIRE(result.ok()); return result.value(); @@ -38,10 +37,10 @@ eve::OperationId stageOperation(Plan& plan, const char* operation, const char* k TEST_CASE("transaction.stage.stableIdsAndCanonicalPayload") { Ledger ledger; - Plan* plan = createPlan(ledger, "00000000-0000-4000-8000-000000000001", "rebellion-7", "order-3"); + Plan* plan = createPlan(ledger, "00000000-0000-4000-8000-000000000001", "rebellion-7", "order-3"); CHECK_EQ(plan->id(), std::string("00000000-0000-4000-8000-000000000001")); - const auto id = stageOperation(*plan, "00000000-0000-4000-8000-000000000002", "set_owner", "base-2", - "{\"z\":2,\"a\":1}"); + const auto id = + stageOperation(*plan, "00000000-0000-4000-8000-000000000002", "set_owner", "base-2", "{\"z\":2,\"a\":1}"); CHECK_EQ(id.format(), std::string("00000000-0000-4000-8000-000000000002")); CHECK_EQ(plan->operationAt(0)->payload, std::string("{\"a\":1,\"z\":2}")); CHECK_EQ(plan->correlation(), std::string("rebellion-7")); @@ -50,9 +49,9 @@ TEST_CASE("transaction.stage.stableIdsAndCanonicalPayload") { TEST_CASE("transaction.validation.requiresEveryOperation") { Ledger ledger; - Plan* plan = createPlan(ledger, "00000000-0000-4000-8000-000000000001"); - auto first = stageOperation(*plan, "00000000-0000-4000-8000-000000000002", "debit", "treasury", "10"); - auto second = stageOperation(*plan, "00000000-0000-4000-8000-000000000003", "credit", "army", "10"); + Plan* plan = createPlan(ledger, "00000000-0000-4000-8000-000000000001"); + auto first = stageOperation(*plan, "00000000-0000-4000-8000-000000000002", "debit", "treasury", "10"); + auto second = stageOperation(*plan, "00000000-0000-4000-8000-000000000003", "credit", "army", "10"); CHECK(plan->markValid(first).ok()); CHECK(!plan->validate().ok()); CHECK(plan->markInvalid(second, "capacity exceeded").ok()); @@ -65,8 +64,8 @@ TEST_CASE("transaction.validation.requiresEveryOperation") { TEST_CASE("transaction.commit.freezesPlan") { Ledger ledger; - Plan* plan = createPlan(ledger, "00000000-0000-4000-8000-000000000001"); - auto id = stageOperation(*plan, "00000000-0000-4000-8000-000000000002", "transfer", "asset-1", "null"); + Plan* plan = createPlan(ledger, "00000000-0000-4000-8000-000000000001"); + auto id = stageOperation(*plan, "00000000-0000-4000-8000-000000000002", "transfer", "asset-1", "null"); CHECK(plan->markValid(id).ok()); CHECK(plan->validate().ok()); CHECK(plan->commit().ok()); @@ -79,7 +78,7 @@ TEST_CASE("transaction.commit.freezesPlan") { TEST_CASE("transaction.rollbackAndFail.areTerminal") { Ledger ledger; - Plan* rolledBack = createPlan(ledger, "00000000-0000-4000-8000-000000000001"); + Plan* rolledBack = createPlan(ledger, "00000000-0000-4000-8000-000000000001"); CHECK(rolledBack->rollback("cancelled by script").ok()); CHECK_EQ(stateName(rolledBack->state()), std::string("rolled_back")); CHECK_EQ(rolledBack->error(), std::string("cancelled by script")); @@ -91,8 +90,8 @@ TEST_CASE("transaction.rollbackAndFail.areTerminal") { TEST_CASE("transaction.events.areDeterministic") { Ledger ledger; - Plan* plan = createPlan(ledger, "00000000-0000-4000-8000-000000000001"); - auto id = stageOperation(*plan, "00000000-0000-4000-8000-000000000002", "change", "subject", "{}"); + Plan* plan = createPlan(ledger, "00000000-0000-4000-8000-000000000001"); + auto id = stageOperation(*plan, "00000000-0000-4000-8000-000000000002", "change", "subject", "{}"); REQUIRE(plan->markValid(id).ok()); REQUIRE(plan->validate().ok()); REQUIRE(plan->commit().ok()); @@ -105,9 +104,9 @@ TEST_CASE("transaction.events.areDeterministic") { TEST_CASE("transaction.snapshot.roundTripsTransactionally") { Ledger source; - Plan* plan = createPlan(source, "00000000-0000-4000-8000-000000000001", "campaign", "command"); - auto id = stageOperation(*plan, "00000000-0000-4000-8000-000000000002", "assign", "base", - "{\"role\":\"governor\"}"); + Plan* plan = createPlan(source, "00000000-0000-4000-8000-000000000001", "campaign", "command"); + auto id = + stageOperation(*plan, "00000000-0000-4000-8000-000000000002", "assign", "base", "{\"role\":\"governor\"}"); REQUIRE(plan->markValid(id).ok()); REQUIRE(plan->validate().ok()); const std::string snapshot = source.snapshotJson(); diff --git a/test/transaction_participant.cpp b/test/transaction_participant.cpp index bcefebb9..e5b01256 100644 --- a/test/transaction_participant.cpp +++ b/test/transaction_participant.cpp @@ -1,9 +1,9 @@ #include "zeroerr/assert.h" #include "zeroerr/unittest.h" +#include "StatePatchTestSupport.h" #include "statepatch/StatePatch.h" #include "transaction/Transaction.h" -#include "StatePatchTestSupport.h" #include #include @@ -22,14 +22,15 @@ using eve::transaction::ITransactionParticipant; using eve::transaction::TransactionContext; Result participantFailure(std::string_view name, std::string_view phase) { - return Result::failure(Diagnostic::error( - DiagnosticCode::Failed, std::string(name) + " failed during " + std::string(phase), - "test." + std::string(phase))); + return Result::failure(Diagnostic::error(DiagnosticCode::Failed, + std::string(name) + " failed during " + std::string(phase), + "test." + std::string(phase))); } class RecordingParticipant final : public ITransactionParticipant { public: - RecordingParticipant(std::string name, std::vector& events) : name_(std::move(name)), events_(events) {} + RecordingParticipant(std::string name, std::vector& events) + : name_(std::move(name)), events_(events) {} std::string_view name() const noexcept override { return name_; } @@ -65,12 +66,12 @@ public: return Result::success(eve::Status::success(eve::StatusCode::Applied)); } - bool failPrepare = false; - bool failCommit = false; - bool failRollback = false; - bool failCompensate = false; - bool prepared = false; - bool committed = false; + bool failPrepare = false; + bool failCommit = false; + bool failRollback = false; + bool failCompensate = false; + bool prepared = false; + bool committed = false; std::string seenTransaction; std::string seenCorrelation; std::string seenCausation; @@ -83,7 +84,7 @@ private: seenCausation = context.causationId(); } - std::string name_; + std::string name_; std::vector& events_; }; @@ -105,14 +106,14 @@ auto participantSpan(std::array& participants) { } // namespace TEST_CASE("transaction.participants.prepareAndCommitPreserveOrderAndContext") { - std::vector events; - RecordingParticipant first("first", events); - RecordingParticipant second("second", events); + std::vector events; + RecordingParticipant first("first", events); + RecordingParticipant second("second", events); std::array participants{&first, &second}; - const TransactionContext context("tx-1", "chain-7", "command-3"); + const TransactionContext context("tx-1", "chain-7", "command-3"); eve::transaction::Coordinator coordinator; - auto result = coordinator.execute(context, participantSpan(participants)); + auto result = coordinator.execute(context, participantSpan(participants)); REQUIRE(result.ok()); const auto& receipt = result.value(); CHECK_EQ(receipt.transactionId, std::string("tx-1")); @@ -166,8 +167,8 @@ TEST_CASE("transaction.participants.commitFailureRollsBackAndCompensatesSeparate } TEST_CASE("transaction.participants.rejectNullAndDuplicateWithoutCallingParticipants") { - std::vector events; - RecordingParticipant participant("only", events); + std::vector events; + RecordingParticipant participant("only", events); eve::transaction::Coordinator coordinator; std::array nullParticipant{nullptr}; @@ -195,15 +196,15 @@ TEST_CASE("transaction.participants.compensationFailureIsNotSilentlyRolledBack") auto result = coordinator.execute(TransactionContext("tx-compensation-fail"), participantSpan(participants)); CHECK(!result); CHECK_EQ(static_cast(result.code()), static_cast(StatusCode::Failed)); - checkEvents(events, {"external.prepare", "failing.prepare", "external.commit", "failing.commit", - "failing.rollback", "external.compensate"}); + checkEvents(events, {"external.prepare", "failing.prepare", "external.commit", "failing.commit", "failing.rollback", + "external.compensate"}); CHECK(participant.committed); CHECK(result.error() != nullptr); } TEST_CASE("transaction.statepatchParticipantCommitsCandidateAtomically") { eve::statepatch::Store store; - auto seed = eve::test_support::openStatePatchBatch(store); + auto seed = eve::test_support::openStatePatchBatch(store); REQUIRE(seed.view.isBound()); CHECK(seed.view->set("actor", "value", "1")); REQUIRE(store.commit(seed.view.get())); @@ -212,11 +213,11 @@ TEST_CASE("transaction.statepatchParticipantCommitsCandidateAtomically") { REQUIRE(batch.view.isBound()); CHECK(batch.view->setExpected("actor", "value", "2", "1")); eve::statepatch::StoreTransactionParticipant participant(store, *batch.view); - std::array participants{&participant}; + std::array participants{&participant}; eve::transaction::Coordinator coordinator; - auto result = coordinator.execute(TransactionContext("tx-statepatch", "chain", "cause"), - participantSpan(participants)); + auto result = + coordinator.execute(TransactionContext("tx-statepatch", "chain", "cause"), participantSpan(participants)); REQUIRE(result.ok()); CHECK_EQ(store.get("actor", "value"), std::string("2")); CHECK_EQ(store.revision(), uint64_t{2}); @@ -226,7 +227,7 @@ TEST_CASE("transaction.statepatchParticipantCommitsCandidateAtomically") { TEST_CASE("transaction.statepatchParticipantPrepareFailureKeepsOriginalState") { eve::statepatch::Store store; - auto seed = eve::test_support::openStatePatchBatch(store); + auto seed = eve::test_support::openStatePatchBatch(store); REQUIRE(seed.view.isBound()); seed.view->set("actor", "value", "1"); REQUIRE(store.commit(seed.view.get())); @@ -236,8 +237,8 @@ TEST_CASE("transaction.statepatchParticipantPrepareFailureKeepsOriginalState") { REQUIRE(batch.view.isBound()); CHECK(!batch.view->set("actor", "value", "{")); eve::statepatch::StoreTransactionParticipant participant(store, *batch.view); - std::array participants{&participant}; - eve::transaction::Coordinator coordinator; + std::array participants{&participant}; + eve::transaction::Coordinator coordinator; auto result = coordinator.execute(TransactionContext("tx-statepatch-invalid"), participantSpan(participants)); CHECK(!result); @@ -248,7 +249,7 @@ TEST_CASE("transaction.statepatchParticipantPrepareFailureKeepsOriginalState") { TEST_CASE("transaction.statepatchParticipantRejectsExternalRevisionChangeAfterPrepare") { eve::statepatch::Store store; - auto seed = eve::test_support::openStatePatchBatch(store); + auto seed = eve::test_support::openStatePatchBatch(store); REQUIRE(seed.view.isBound()); seed.view->set("actor", "value", "1"); REQUIRE(store.commit(seed.view.get())); @@ -256,8 +257,8 @@ TEST_CASE("transaction.statepatchParticipantRejectsExternalRevisionChangeAfterPr REQUIRE(batch.view.isBound()); batch.view->setExpected("actor", "value", "2", "1"); eve::statepatch::StoreTransactionParticipant participant(store, *batch.view); - const TransactionContext context("tx-statepatch-stale"); - auto prepared = participant.prepare(context); + const TransactionContext context("tx-statepatch-stale"); + auto prepared = participant.prepare(context); REQUIRE(prepared.ok()); auto external = eve::test_support::openStatePatchBatch(store); @@ -276,7 +277,7 @@ TEST_CASE("transaction.statepatchParticipantRejectsExternalRevisionChangeAfterPr TEST_CASE("transaction.statepatchParticipantCompensateRestoresBeforeState") { eve::statepatch::Store store; - auto seed = eve::test_support::openStatePatchBatch(store); + auto seed = eve::test_support::openStatePatchBatch(store); REQUIRE(seed.view.isBound()); seed.view->set("actor", "value", "1"); REQUIRE(store.commit(seed.view.get())); @@ -286,8 +287,8 @@ TEST_CASE("transaction.statepatchParticipantCompensateRestoresBeforeState") { REQUIRE(batch.view.isBound()); batch.view->setExpected("actor", "value", "2", "1"); eve::statepatch::StoreTransactionParticipant participant(store, *batch.view); - const TransactionContext context("tx-statepatch-compensate"); - auto prepared = participant.prepare(context); + const TransactionContext context("tx-statepatch-compensate"); + auto prepared = participant.prepare(context); REQUIRE(prepared.ok()); auto committed = participant.commit(context); REQUIRE(committed.ok()); diff --git a/test/ui_control_semantics.cpp b/test/ui_control_semantics.cpp index 7ad199f6..e8dfceb0 100644 --- a/test/ui_control_semantics.cpp +++ b/test/ui_control_semantics.cpp @@ -50,8 +50,7 @@ TEST_CASE("ui.control.policies_survive_flatten_and_reconcile") { CHECK_EQ(applyNode->focusRight, std::string("right")); CHECK_EQ(applyNode->focusUp, std::string("up")); CHECK_EQ(applyNode->focusDown, std::string("down")); - CHECK_EQ(static_cast(applyNode->accessibilityRole), - static_cast(AccessibilityRole::Button)); + CHECK_EQ(static_cast(applyNode->accessibilityRole), static_cast(AccessibilityRole::Button)); CHECK_EQ(applyNode->accessibilityName, std::string("Apply changes")); WidgetDesc updated = button("Apply now", "apply") @@ -119,21 +118,21 @@ TEST_CASE("ui.control.focus_navigation_uses_neighbors_then_stable_tab_order") { CHECK(node(*host, "first")->focusRequested); node(*host, "second")->focused = false; - node(*host, "first")->focused = true; + node(*host, "first")->focused = true; CHECK(host->moveFocus(FocusDirection::Next)); CHECK(node(*host, "special")->focusRequested); - node(*host, "first")->focused = false; + node(*host, "first")->focused = false; node(*host, "special")->focused = true; CHECK(host->moveFocus(FocusDirection::Previous)); CHECK(node(*host, "second")->focusRequested); node(*host, "special")->focused = false; - node(*host, "first")->focused = true; + node(*host, "first")->focused = true; CHECK(host->moveFocus(FocusDirection::Left)); CHECK(node(*host, "second")->focusRequested); - node(*host, "first")->focused = false; + node(*host, "first")->focused = false; node(*host, "special")->focused = true; CHECK(!host->moveFocus(FocusDirection::Next, false)); } @@ -142,7 +141,7 @@ TEST_CASE("ui.control.mouse_pass_bubbles_click_and_stop_contains_it") { int childClicks = 0; int ignoredClicks = 0; int rootClicks = 0; - UIHost *host = createHost("pointer-routing"); + UIHost *host = createHost("pointer-routing"); REQUIRE(host != nullptr); WidgetDesc child = button("Action", "action", [&]() { ++childClicks; }) @@ -164,7 +163,7 @@ TEST_CASE("ui.control.mouse_pass_bubbles_click_and_stop_contains_it") { UISystem::pendingEvents().clear(); UISystem::clickQueue().clear(); UIEvent event; - event.host = host->handle(); + event.host = host->handle(); event.hostName = host->getName(); event.nodeId = action->id; event.nodeIndex = int(action - tree->nodes.data()); diff --git a/test/ui_presentation.cpp b/test/ui_presentation.cpp index dd2f3f9a..fc3e4d89 100644 --- a/test/ui_presentation.cpp +++ b/test/ui_presentation.cpp @@ -1,8 +1,8 @@ #include "zeroerr/assert.h" #include "zeroerr/unittest.h" -#include "property_access/PropertyAccess.h" #include "TestPropertyAccess.h" +#include "property_access/PropertyAccess.h" #include "ui/PropertyView.h" #include "ui/UIHost.h" @@ -74,7 +74,7 @@ UIHost *createHost(const std::string &name) { } // namespace TEST_CASE("ui.presentation.dynamic_model_validates_and_notifies") { - TestPropertyAccess model(playerSchema()); + TestPropertyAccess model(playerSchema()); std::vector changes; auto subscription = model.subscribe( [&changes](const PropertyChange &change) { changes.push_back(change); }); @@ -97,7 +97,7 @@ TEST_CASE("ui.presentation.dynamic_model_validates_and_notifies") { } TEST_CASE("ui.presentation.generated_view_binds_two_way") { - TestPropertyAccess model(playerSchema()); + TestPropertyAccess model(playerSchema()); PropertyViewOptions options; options.idPrefix = "player/"; options.title = "Player"; diff --git a/test/value.cpp b/test/value.cpp index 6b601a0b..c04b8133 100644 --- a/test/value.cpp +++ b/test/value.cpp @@ -1,7 +1,6 @@ #include "zeroerr/assert.h" #include "zeroerr/unittest.h" -#include "common/Value.h" #include "common/Json.h" #include "common/Value.h" @@ -20,7 +19,7 @@ TEST_CASE("value.json_round_trip_preserves_all_supported_kinds") { array.emplace_back("line\n中🚀"); Value value(Value::Object{{"z", "last"}, {"a", Value(std::move(array))}}); - auto encoded = value.toJson(); + auto encoded = value.toJson(); REQUIRE(encoded.ok()); CHECK_EQ(encoded.value(), R"({"a":[null,true,-7,1.25,"line\n中🚀"],"z":"last"})"); @@ -30,8 +29,7 @@ TEST_CASE("value.json_round_trip_preserves_all_supported_kinds") { } TEST_CASE("value.object_order_and_numeric_kinds_are_deterministic") { - auto parsed = Value::fromJson( - R"({"z":9223372036854775807,"a":1.0,"negativeZero":-0.0})"); + auto parsed = Value::fromJson(R"({"z":9223372036854775807,"a":1.0,"negativeZero":-0.0})"); REQUIRE(parsed.ok()); const Value::Object* object = parsed.value().getIf(); REQUIRE(object != nullptr); @@ -47,9 +45,9 @@ TEST_CASE("value.object_order_and_numeric_kinds_are_deterministic") { } TEST_CASE("value_uses_the_common_json_parser_and_serializer") { - std::string error; - eve::json::Document document = eve::json::Document::parse( - R"({"large":9223372036854775807,"fraction":1.0})", &error); + std::string error; + eve::json::Document document = + eve::json::Document::parse(R"({"large":9223372036854775807,"fraction":1.0})", &error); REQUIRE(document.valid()); CHECK(document.root().get("large").isIntegerLiteral()); CHECK(document.root().get("large").isInt64()); @@ -57,8 +55,8 @@ TEST_CASE("value_uses_the_common_json_parser_and_serializer") { CHECK(!eve::json::Document::parse(R"({"x":1,"x":2})").valid()); CHECK(!eve::json::Document::parse("Infinity").valid()); - Value value = Value::fromJson(R"({"z":2,"a":1})").expect("value parse"); - auto commonJson = eve::json::stringify(value); + Value value = Value::fromJson(R"({"z":2,"a":1})").expect("value parse"); + auto commonJson = eve::json::stringify(value); REQUIRE(commonJson.ok()); CHECK_EQ(commonJson.value(), R"({"a":1,"z":2})"); auto valueJson = value.toJson(); @@ -76,19 +74,17 @@ TEST_CASE("value.rejects_duplicate_keys_invalid_json_and_non_finite_numbers") { CHECK(!nan.ok()); CHECK_EQ(static_cast(nan.code()), static_cast(eve::StatusCode::Failed)); REQUIRE(nan.error() != nullptr); - CHECK_EQ(static_cast(nan.error()->code()), - static_cast(eve::DiagnosticCode::SerializationError)); + CHECK_EQ(static_cast(nan.error()->code()), static_cast(eve::DiagnosticCode::SerializationError)); auto infinity = Value(std::numeric_limits::infinity()).toJson(); CHECK(!infinity.ok()); - CHECK_EQ(static_cast(infinity.error()->code()), - static_cast(eve::DiagnosticCode::SerializationError)); + CHECK_EQ(static_cast(infinity.error()->code()), static_cast(eve::DiagnosticCode::SerializationError)); } TEST_CASE("presentation_value_is_the_common_value_without_a_second_storage") { static_assert(std::is_same_v); eve::Value value(eve::Value::Object{{"count", 4}}); - auto encoded = value.toJson(); + auto encoded = value.toJson(); REQUIRE(encoded.ok()); auto parsed = eve::Value::fromJson(encoded.value()); REQUIRE(parsed.ok()); diff --git a/test/vehicle_orders_adapter.cpp b/test/vehicle_orders_adapter.cpp index 94936fec..11e0af33 100644 --- a/test/vehicle_orders_adapter.cpp +++ b/test/vehicle_orders_adapter.cpp @@ -37,8 +37,8 @@ std::string takeId(eve::Result result) { TEST_CASE("vehicle.orders.adapterUsesGenericPriorityAndLifecycle") { VehicleOrderQueueAdapter adapter; - const std::string moveId = takeId(adapter.append(moveOrder(), 1)); - const std::string attackId = takeId(adapter.append(attackOrder(), 5)); + const std::string moveId = takeId(adapter.append(moveOrder(), 1)); + const std::string attackId = takeId(adapter.append(attackOrder(), 5)); REQUIRE(!moveId.empty()); REQUIRE(!attackId.empty()); @@ -54,8 +54,8 @@ TEST_CASE("vehicle.orders.adapterUsesGenericPriorityAndLifecycle") { TEST_CASE("vehicle.orders.adapterTimeoutActivatesNextDomainOrder") { VehicleOrderQueueAdapter adapter; - const std::string timedId = takeId(adapter.append(moveOrder(), 0, 0.25)); - const std::string nextId = takeId(adapter.append(attackOrder())); + const std::string timedId = takeId(adapter.append(moveOrder(), 0, 0.25)); + const std::string nextId = takeId(adapter.append(attackOrder())); REQUIRE(!timedId.empty()); REQUIRE(!nextId.empty()); @@ -70,7 +70,7 @@ TEST_CASE("vehicle.orders.adapterTimeoutActivatesNextDomainOrder") { TEST_CASE("vehicle.orders.adapterInterruptHonoursGenericPriority") { VehicleOrderQueueAdapter adapter; - const std::string activeId = takeId(adapter.append(moveOrder(), 20)); + const std::string activeId = takeId(adapter.append(moveOrder(), 20)); REQUIRE(!activeId.empty()); CHECK(!adapter.interrupt(attackOrder(), 19).hasValue()); @@ -86,9 +86,9 @@ TEST_CASE("vehicle.orders.adapterInterruptHonoursGenericPriority") { TEST_CASE("vehicle.orders.adapterReplaceRefreshesCompatibilityProjection") { eve::vehicle::VehicleEntity::Orders legacy; - VehicleOrderQueueAdapter adapter; + VehicleOrderQueueAdapter adapter; - const std::string moveId = takeId(adapter.append(moveOrder())); + const std::string moveId = takeId(adapter.append(moveOrder())); const std::string firstAttackId = takeId(adapter.append(attackOrder())); REQUIRE(!moveId.empty()); REQUIRE(!firstAttackId.empty()); @@ -106,8 +106,8 @@ TEST_CASE("vehicle.orders.adapterReplaceRefreshesCompatibilityProjection") { TEST_CASE("vehicle.orders.adapterCopyKeepsLifecycleAndPayloadIndependent") { VehicleOrderQueueAdapter original; - const std::string moveId = takeId(original.append(moveOrder(), 3)); - const std::string attackId = takeId(original.append(attackOrder(), 1)); + const std::string moveId = takeId(original.append(moveOrder(), 3)); + const std::string attackId = takeId(original.append(attackOrder(), 1)); REQUIRE(!moveId.empty()); REQUIRE(!attackId.empty()); @@ -138,7 +138,7 @@ TEST_CASE("vehicle.orders.adapterCopyKeepsLifecycleAndPayloadIndependent") { TEST_CASE("vehicle.orders.componentCopyClonesAdapterNotLegacyProjection") { eve::vehicle::VehicleEntity::Orders source; - const std::string sourceId = takeId(source.adapter->append(attackOrder(), 4, 2.0)); + const std::string sourceId = takeId(source.adapter->append(attackOrder(), 4, 2.0)); REQUIRE(!sourceId.empty()); source.adapter->syncCompatibility(source); diff --git a/test/versioned_registry.cpp b/test/versioned_registry.cpp index f6c2f1c8..466e26fa 100644 --- a/test/versioned_registry.cpp +++ b/test/versioned_registry.cpp @@ -26,7 +26,7 @@ struct EventData { static_assert(!std::is_default_constructible_v); struct ThrowingValue { - inline static bool throwOnCopy = false; + inline static bool throwOnCopy = false; inline static bool throwOnMoveAssignment = false; int payload; @@ -37,13 +37,13 @@ struct ThrowingValue { ThrowingValue(const ThrowingValue& other) { if (throwOnCopy) throw std::runtime_error("injected value copy failure"); - payload = other.payload; + payload = other.payload; generation = other.generation; } ThrowingValue& operator=(const ThrowingValue& other) { if (throwOnCopy) throw std::runtime_error("injected value assignment failure"); - payload = other.payload; + payload = other.payload; generation = other.generation; return *this; } @@ -52,14 +52,14 @@ struct ThrowingValue { ThrowingValue& operator=(ThrowingValue&& other) { if (throwOnMoveAssignment) throw std::runtime_error("injected value move assignment failure"); - payload = other.payload; + payload = other.payload; generation = other.generation; return *this; } }; struct ThrowingEventData { - inline static bool throwOnCopy = false; + inline static bool throwOnCopy = false; inline static int movesBeforeThrow = -1; eve::SchemaVersion version; @@ -86,7 +86,7 @@ struct ThrowingEventData { ThrowingEventData& operator=(ThrowingEventData&&) noexcept = default; }; -using Registry = eve::VersionedRegistry; +using Registry = eve::VersionedRegistry; using ThrowingValueRegistry = eve::VersionedRegistry; using ThrowingEventRegistry = eve::VersionedRegistry; @@ -158,9 +158,9 @@ TEST_CASE("versionedRegistry.eventsAndStateIncludeTombstones") { CHECK_EQ(registry.eventAt(2)->operation, eve::RegistryOperation::Remove); CHECK_EQ(registry.eventAt(2)->generation.value(), uint64_t{2}); - const auto image = registry.snapshotState(); - auto restored = makeRegistry(); - auto restoreResult = restored.restoreState(image); + const auto image = registry.snapshotState(); + auto restored = makeRegistry(); + auto restoreResult = restored.restoreState(image); REQUIRE(restoreResult.ok()); CHECK_EQ(restored.size(), registry.size()); CHECK_EQ(restored.tombstoneCount(), registry.tombstoneCount()); @@ -175,18 +175,17 @@ TEST_CASE("versionedRegistry.eventsAndStateIncludeTombstones") { } TEST_CASE("versionedRegistry.subscriptionIsCanonicalEventSource") { - auto registry = makeRegistry(); + auto registry = makeRegistry(); std::vector operations; - auto subscription = registry.subscribe([&](const Registry::Event& event) { - operations.push_back(event.operation); - }); + auto subscription = + registry.subscribe([&](const Registry::Event& event) { operations.push_back(event.operation); }); REQUIRE(registry.insert("unit", Value{1}, EventData{eve::SchemaVersion(1)}).ok()); REQUIRE(registry.replace("unit", Value{2}, EventData{eve::SchemaVersion(2)}).ok()); auto removed = registry.remove(std::string("unit"), EventData{eve::SchemaVersion(2)}); REQUIRE(removed.ok()); - CHECK_EQ(operations, std::vector({eve::RegistryOperation::Insert, - eve::RegistryOperation::Replace, - eve::RegistryOperation::Remove})); + CHECK_EQ(operations, + std::vector( + {eve::RegistryOperation::Insert, eve::RegistryOperation::Replace, eve::RegistryOperation::Remove})); subscription.dispose(); REQUIRE(registry.insert("other", Value{3}, EventData{eve::SchemaVersion(3)}).ok()); CHECK_EQ(operations.size(), std::size_t{3}); @@ -195,12 +194,12 @@ TEST_CASE("versionedRegistry.subscriptionIsCanonicalEventSource") { TEST_CASE("versionedRegistry.preparationFailureLeavesCanonicalStateUntouched") { ThrowingValue::throwOnCopy = false; ThrowingValueRegistry registry; - auto inserted = registry.insert("unit", ThrowingValue{1}, EventData{eve::SchemaVersion(1)}); + auto inserted = registry.insert("unit", ThrowingValue{1}, EventData{eve::SchemaVersion(1)}); REQUIRE(inserted.ok()); const auto original = std::move(inserted).takeValue(); ThrowingValue::throwOnCopy = true; - auto failed = registry.replace("unit", ThrowingValue{2}, EventData{eve::SchemaVersion(2)}); + auto failed = registry.replace("unit", ThrowingValue{2}, EventData{eve::SchemaVersion(2)}); ThrowingValue::throwOnCopy = false; CHECK(!failed.ok()); @@ -212,8 +211,7 @@ TEST_CASE("versionedRegistry.preparationFailureLeavesCanonicalStateUntouched") { CHECK_EQ(value.value().get().payload, 1); ThrowingValue::throwOnMoveAssignment = true; - auto moveAssignmentFailure = registry.replace("unit", ThrowingValue{3}, - EventData{eve::SchemaVersion(3)}); + auto moveAssignmentFailure = registry.replace("unit", ThrowingValue{3}, EventData{eve::SchemaVersion(3)}); ThrowingValue::throwOnMoveAssignment = false; CHECK(!moveAssignmentFailure.ok()); CHECK_EQ(moveAssignmentFailure.code(), eve::StatusCode::Failed); @@ -226,7 +224,7 @@ TEST_CASE("versionedRegistry.preparationFailureLeavesCanonicalStateUntouched") { TEST_CASE("versionedRegistry.eventPreparationFailureLeavesStateUntouched") { ThrowingEventRegistry registry; ThrowingEventData::throwOnCopy = true; - auto failed = registry.insert("unit", Value{1}, ThrowingEventData{eve::SchemaVersion(1)}); + auto failed = registry.insert("unit", Value{1}, ThrowingEventData{eve::SchemaVersion(1)}); ThrowingEventData::throwOnCopy = false; CHECK(!failed.ok()); @@ -237,11 +235,11 @@ TEST_CASE("versionedRegistry.eventPreparationFailureLeavesStateUntouched") { TEST_CASE("versionedRegistry.eventMoveFailureLeavesStateUntouched") { ThrowingEventRegistry registry; - ThrowingEventData data{eve::SchemaVersion(1)}; + ThrowingEventData data{eve::SchemaVersion(1)}; // One move constructs insert's parameter and one constructs mutate's // parameter; the third move constructs the candidate event. ThrowingEventData::movesBeforeThrow = 2; - auto failed = registry.insert("unit", Value{1}, std::move(data)); + auto failed = registry.insert("unit", Value{1}, std::move(data)); ThrowingEventData::movesBeforeThrow = -1; CHECK(!failed.ok()); @@ -251,14 +249,11 @@ TEST_CASE("versionedRegistry.eventMoveFailureLeavesStateUntouched") { } TEST_CASE("versionedRegistry.callbackFailureReportsAppliedWithoutRollback") { - auto registry = makeRegistry(); + auto registry = makeRegistry(); std::vector observed; - auto observer = registry.subscribe([&](const Registry::Event& event) { - observed.push_back(event.sequence); - }); - auto throwingObserver = registry.subscribe([](const Registry::Event&) { - throw std::runtime_error("injected observer failure"); - }); + auto observer = registry.subscribe([&](const Registry::Event& event) { observed.push_back(event.sequence); }); + auto throwingObserver = + registry.subscribe([](const Registry::Event&) { throw std::runtime_error("injected observer failure"); }); auto inserted = registry.insert("unit", Value{1}, EventData{eve::SchemaVersion(1)}); REQUIRE(inserted.ok()); @@ -278,7 +273,7 @@ TEST_CASE("versionedRegistry.restoreProjectorFailureIsTransactional") { REQUIRE(source.insert("source", Value{1}, EventData{eve::SchemaVersion(1)}).ok()); const auto image = source.snapshotState(); - bool failProjection = false; + bool failProjection = false; Registry target([&](Value& value, eve::Generation generation) { value.generation = generation; if (failProjection) throw std::runtime_error("injected generation projection failure"); @@ -287,7 +282,7 @@ TEST_CASE("versionedRegistry.restoreProjectorFailureIsTransactional") { const auto oldState = target.snapshotState(); failProjection = true; - auto failed = target.restoreState(image); + auto failed = target.restoreState(image); failProjection = false; CHECK(!failed.ok());