Bicameral Engine
GPU-resident engine for a magic-research open-world game
Loading...
Searching...
No Matches
replay_session.h
Go to the documentation of this file.
1// replay_session.h — 走っている世界を再生ファイルに記録する側(ReplayRecorder)と、再生ファイルを流して確かめる側(ReplayPlayer)(T-0086、15 §2)。
2//
3// どちらも CPU の帳簿だけ(GPU を知らない)。フレームのループ(frame/frame_loop.cpp)が次のように使う:
4// 記録: GPU のキューへ足したコマンドを AddCommands、読み戻した刻みごとのハッシュを AddHash → 終わるときに Write。
5// 書くのは「最後にハッシュを読み戻した刻み」までに適用されるコマンドだけ(その先の刻みの結果は確かめられないので)。
6// 再生: 毎フレーム TakeCommands(まだ記録していない最初の適用の刻み, 足せる数)で、刻みに間に合う分だけ(先読みは数刻み)GPU のキューへ足す。
7// 読み戻したハッシュを CheckHash に渡す。ファイルの最後のハッシュまで確かめたら Finished()。
8// 間に合わなかったコマンド(キューの空きが足りずに刻みを過ぎた)とハッシュの不一致は失敗として数える。
9#pragma once
10
11#include <cstdint>
12#include <expected>
13#include <filesystem>
14#include <span>
15#include <string>
16#include <vector>
17
18#include "core/aliases.h"
19#include "save/replay_file.h"
20
21namespace bicameral::save {
22
24 public:
25 void AddCommands(std::span<const sim::Command> commands);
26 void AddHash(uint64_t tick, uint64_t hash);
27
28 // 記録を再生ファイルの形にする(仮の世界。刻み 0 の全部 0 の状態から)
29 [[nodiscard]] ReplayFile Build() const;
30 [[nodiscard]] std::expected<void, std::string> Write(const fs::path& path) const;
31
32 private:
33 std::vector<sim::Command> m_commands;
34 std::vector<ReplayTickHash> m_hashes;
35 };
36
38 public:
39 static constexpr uint64_t LOOKAHEAD_TICKS = 8; // 何刻み先のコマンドまで先に GPU のキューへ足すか
40
41 explicit ReplayPlayer(ReplayFile replay);
42
43 // 仮の世界の再生ファイルを読む(刻み 0 の全部 0 の状態から始まるものだけ)
44 [[nodiscard]] static std::expected<ReplayPlayer, std::string> Load(const fs::path& path);
45
46 // このフレームに GPU のキューへ足すコマンド(targetTick < applyTick + LOOKAHEAD_TICKS のものを、最大 limit 個)。
47 // applyTick = まだ記録していない最初の適用の刻み。それより前のコマンドは間に合わないので捨てて失敗に数える
48 [[nodiscard]] std::vector<sim::Command> TakeCommands(uint64_t applyTick, uint32_t limit);
49
50 // 読み戻した S(tick) のハッシュを、ファイルにあれば突き合わせる(ファイルは間引いてあってよい)
51 void CheckHash(uint64_t tick, uint64_t hash);
52
53 // 巻き戻し(保存点 + 再生。T-0143): 世界が刻み tick の境界へ戻った。tick 以降のコマンドをもう一度足し、
54 // S(tick + 1) からのハッシュをもう一度突き合わせる(同じコマンドなら同じハッシュになるはず。ADR-0008)
55 void Rewind(uint64_t tick);
56
57 // --- 結果 ---
58 [[nodiscard]] bool Finished() const { return m_nextHash == m_replay.tickHashes.size(); }
59 [[nodiscard]] bool Passed() const { return Finished() && m_mismatches == 0 && m_lateCommands == 0; }
60
61 [[nodiscard]] uint64_t Matches() const { return m_matches; }
62 [[nodiscard]] uint64_t Mismatches() const { return m_mismatches; }
63 [[nodiscard]] uint64_t LateCommands() const { return m_lateCommands; }
64
65 // --- ファイルの中身 ---
66 [[nodiscard]] size_t HashCount() const { return m_replay.tickHashes.size(); }
67 [[nodiscard]] size_t CommandCount() const { return m_replay.commands.size(); }
68 [[nodiscard]] uint64_t LastTick() const {
69 return m_replay.tickHashes.empty() ? 0 : m_replay.tickHashes.back().tick;
70 }
71
72 private:
74
75 // --- どこまで進んだか ---
76 size_t m_nextCommand = 0; // 次に足すコマンド
77 size_t m_nextHash = 0; // 次に突き合わせるハッシュ
78
79 // --- 数えたもの ---
80 uint64_t m_matches = 0;
81 uint64_t m_mismatches = 0;
82 uint64_t m_lateCommands = 0;
83 };
84
85} // namespace bicameral::save
std::vector< sim::Command > TakeCommands(uint64_t applyTick, uint32_t limit)
static constexpr uint64_t LOOKAHEAD_TICKS
void CheckHash(uint64_t tick, uint64_t hash)
static std::expected< ReplayPlayer, std::string > Load(const fs::path &path)
void AddCommands(std::span< const sim::Command > commands)
std::vector< sim::Command > m_commands
std::vector< ReplayTickHash > m_hashes
std::expected< void, std::string > Write(const fs::path &path) const
void AddHash(uint64_t tick, uint64_t hash)