1#include "KReplaySystem.hh"
3#include "host/Option.hh"
4#include "host/SceneCreatorDynamic.hh"
6#include <abstract/File.hh>
7#include <egg/core/Heap.hh>
9#include <game/system/RaceManager.hh>
17 ASSERT(m_currentGhostFileName);
18 ASSERT(m_currentRawGhost);
19 ASSERT(m_currentGhost);
21 auto *sceneCreator = EGG::egg_new<Host::SceneCreatorDynamic>();
22 m_sceneMgr = EGG::egg_new<EGG::SceneManager>(sceneCreator);
24 System::RaceConfig::RegisterInitCallback(
OnInit,
nullptr);
25 Abstract::File::Remove(
"results.txt");
27 m_sceneMgr->changeScene(0);
52 PANIC(
"Expected ghost argument!");
55 for (
int i = 0; i < argc; ++i) {
56 std::optional<Host::EOption> flag = Host::Option::CheckFlag(argv[i]);
57 if (!flag || *flag == Host::EOption::Invalid) {
58 WARN(
"Expected a flag! Got: %s", argv[i]);
63 case Host::EOption::Ghost: {
66 m_currentGhostFileName = argv[++i];
68 Abstract::File::LoadHost(m_currentGhostFileName, m_currentRawGhostSize);
70 if (m_currentRawGhostSize < System::RKG_HEADER_SIZE ||
72 PANIC(
"File cannot be a ghost! Check the file size.");
78 m_currentGhost = EGG::egg_new<System::GhostFile>(file);
79 ASSERT(m_currentGhost);
81 case Host::EOption::Invalid:
83 PANIC(
"Invalid flag!");
91 s_instance = EGG::egg_new<KReplaySystem>();
95void KReplaySystem::DestroyInstance() {
97 auto *instance = s_instance;
99 EGG::egg_delete(instance);
102KReplaySystem::KReplaySystem()
103 : m_currentGhostFileName(nullptr), m_currentGhost(nullptr), m_currentRawGhost(nullptr),
104 m_currentRawGhostSize(0) {}
106KReplaySystem::~KReplaySystem() {
108 s_instance =
nullptr;
109 WARN(
"KReplaySystem instance not explicitly handled!");
112 EGG::egg_delete(m_sceneMgr);
113 EGG::egg_delete(m_currentGhost);
114 delete[] m_currentRawGhost;
120 constexpr u16 MAX_MINUTE_COUNT = 10;
122 const auto *raceManager = System::RaceManager::Instance();
123 if (raceManager->stage() == System::RaceManager::Stage::FinishGlobal) {
127 if (raceManager->timerManager().currentTimer().min >= MAX_MINUTE_COUNT) {
137 std::string report(m_currentGhostFileName);
138 report +=
"\n" + std::string(msg);
139 Abstract::File::Append(
"results.txt", report.c_str(), report.size());
146 std::ostringstream oss;
147 oss << std::setw(2) << std::setfill(
'0') << timer.min <<
":" << std::setw(2)
148 << std::setfill(
'0') << timer.sec <<
"." << std::setw(3) << std::setfill(
'0')
153 const auto *raceManager = System::RaceManager::Instance();
154 if (raceManager->stage() != System::RaceManager::Stage::FinishGlobal) {
155 m_sceneMgr->currentScene()->heap()->enableAllocation();
161 if (desyncingTimerIdx != -1) {
162 m_sceneMgr->currentScene()->heap()->enableAllocation();
166 if (desyncingTimerIdx == 0) {
167 msg =
"Final timer desync!";
169 msg =
"Lap " + std::to_string(desyncingTimerIdx) +
" timer desync!";
172 msg +=
" Expected " + format(correct) +
", got " + format(incorrect);
183 const auto &player = System::RaceManager::Instance()->player();
184 if (m_currentGhost->raceTimer() != player.raceTimer()) {
188 for (
size_t i = 0; i < 3; ++i) {
189 if (m_currentGhost->lapTimer(i) != player.getLapSplit(i + 1)) {
202 ASSERT(cond != std::strong_ordering::less);
204 if (cond == std::strong_ordering::equal) {
205 const auto &correct = m_currentGhost->raceTimer();
206 const auto &incorrect = System::RaceManager::Instance()->player().raceTimer();
207 ASSERT(correct != incorrect);
208 return DesyncingTimerPair(correct, incorrect);
209 }
else if (cond == std::strong_ordering::greater) {
210 const auto &correct = m_currentGhost->lapTimer(i - 1);
211 const auto &incorrect = System::RaceManager::Instance()->player().lapTimer(i - 1);
212 ASSERT(correct != incorrect);
213 return DesyncingTimerPair(correct, incorrect);
224 config->setGhost(Instance()->m_currentRawGhost);
225 config->raceScenario().players[0].type = System::RaceConfig::Player::Type::Ghost;
Kinoko system designed to execute replays.
s32 getDesyncingTimerIdx() const
Finds the desyncing timer index, if one exists.
void parseOptions(int argc, char **argv) override
Parses non-generic command line options.
void reportFail(const std::string &msg) const
Reports failure to file.
bool calcEnd() const
Determines whether or not the ghost simulation should end.
bool run() override
Executes a run.
static void OnInit(System::RaceConfig *config, void *arg)
Initializes the race configuration as needed for replays.
DesyncingTimerPair getDesyncingTimer(s32 i) const
Gets the desyncing timer according to the index.
bool success() const
Determines whether the simulation was a success or not.
void calc() override
Executes a frame.
void init() override
Initializes the system.
Initializes the player with parameters specified in the provided ghost file.
The binary data of a ghost saved to a file.
A simple struct to represent a lap or race finish time.