-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
334 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
#include "KReplaySystem.hh" | ||
|
||
#include "host/Option.hh" | ||
#include "host/SceneCreatorDynamic.hh" | ||
|
||
#include <abstract/File.hh> | ||
|
||
#include <game/system/RaceManager.hh> | ||
|
||
/// @brief Initializes the system. | ||
void KReplaySystem::init() { | ||
ASSERT(m_currentGhostFileName); | ||
ASSERT(m_currentRawGhost); | ||
ASSERT(m_currentGhost); | ||
|
||
auto *sceneCreator = new Host::SceneCreatorDynamic; | ||
m_sceneMgr = new EGG::SceneManager(sceneCreator); | ||
|
||
System::RaceConfig::RegisterInitCallback(OnInit, nullptr); | ||
Abstract::File::Remove("results.txt"); | ||
|
||
m_sceneMgr->changeScene(0); | ||
} | ||
|
||
/// @brief Executes a frame. | ||
void KReplaySystem::calc() { | ||
m_sceneMgr->calc(); | ||
} | ||
|
||
/// @brief Executes a run. | ||
/// @details A run consists of replaying a ghost. | ||
/// @return Whether the run was successful or not. | ||
bool KReplaySystem::run() { | ||
while (!calcEnd()) { | ||
calc(); | ||
} | ||
|
||
return success(); | ||
} | ||
|
||
/// @brief Parses non-generic command line options. | ||
/// @details The only currently accepted option is the ghost flag. | ||
/// @param argc The number of arguments. | ||
/// @param argv The arguments. | ||
void KReplaySystem::parseOptions(int argc, char **argv) { | ||
if (argc < 2) { | ||
PANIC("Expected ghost argument!"); | ||
} | ||
|
||
for (int i = 0; i < argc; ++i) { | ||
std::optional<Host::EOption> flag = Host::Option::CheckFlag(argv[i]); | ||
if (!flag || *flag == Host::EOption::Invalid) { | ||
WARN("Expected a flag! Got: %s", argv[i]); | ||
continue; | ||
} | ||
|
||
switch (*flag) { | ||
case Host::EOption::Ghost: { | ||
ASSERT(i + 1 < argc); | ||
|
||
m_currentGhostFileName = argv[++i]; | ||
m_currentRawGhost = Abstract::File::Load(m_currentGhostFileName, m_currentRawGhostSize); | ||
|
||
if (m_currentRawGhostSize < System::RKG_HEADER_SIZE || | ||
m_currentRawGhostSize > System::RKG_UNCOMPRESSED_INPUT_DATA_SECTION_SIZE) { | ||
PANIC("File cannot be a ghost! Check the file size."); | ||
} | ||
|
||
// Creating the raw ghost file validates it | ||
System::RawGhostFile file = System::RawGhostFile(m_currentRawGhost); | ||
|
||
m_currentGhost = new System::GhostFile(file); | ||
ASSERT(m_currentGhost); | ||
} break; | ||
case Host::EOption::Invalid: | ||
default: | ||
PANIC("Invalid flag!"); | ||
break; | ||
} | ||
} | ||
} | ||
|
||
KReplaySystem *KReplaySystem::CreateInstance() { | ||
ASSERT(!s_instance); | ||
s_instance = new KReplaySystem; | ||
return static_cast<KReplaySystem *>(s_instance); | ||
} | ||
|
||
void KReplaySystem::DestroyInstance() { | ||
ASSERT(s_instance); | ||
auto *instance = s_instance; | ||
s_instance = nullptr; | ||
delete instance; | ||
} | ||
|
||
KReplaySystem *KReplaySystem::Instance() { | ||
return static_cast<KReplaySystem *>(s_instance); | ||
} | ||
|
||
KReplaySystem::KReplaySystem() | ||
: m_currentGhostFileName(nullptr), m_currentGhost(nullptr), m_currentRawGhost(nullptr), | ||
m_currentRawGhostSize(0) {} | ||
|
||
KReplaySystem::~KReplaySystem() { | ||
if (s_instance) { | ||
s_instance = nullptr; | ||
WARN("KReplaySystem instance not explicitly handled!"); | ||
} | ||
|
||
delete m_sceneMgr; | ||
delete m_currentGhost; | ||
delete m_currentRawGhost; | ||
} | ||
|
||
bool KReplaySystem::calcEnd() const { | ||
constexpr u16 MAX_MINUTE_COUNT = 10; | ||
|
||
const auto *raceManager = System::RaceManager::Instance(); | ||
if (raceManager->stage() == System::RaceManager::Stage::FinishGlobal) { | ||
return true; | ||
} | ||
|
||
if (raceManager->timerManager().currentTimer().min >= MAX_MINUTE_COUNT) { | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
void KReplaySystem::reportFail(const char *msg) const { | ||
std::string report(m_currentGhostFileName); | ||
report += "\n" + std::string(msg); | ||
Abstract::File::Append("results.txt", report.c_str(), report.size()); | ||
} | ||
|
||
bool KReplaySystem::success() const { | ||
const auto *raceManager = System::RaceManager::Instance(); | ||
if (raceManager->stage() != System::RaceManager::Stage::FinishGlobal) { | ||
reportFail("Race didn't finish"); | ||
return false; | ||
} | ||
|
||
s32 desyncingTimerIdx = getDesyncingTimerIdx(); | ||
if (desyncingTimerIdx != -1) { | ||
char msgBuffer[128]; | ||
|
||
const auto [correct, incorrect] = getDesyncingTimer(desyncingTimerIdx); | ||
if (desyncingTimerIdx == 0) { | ||
snprintf(msgBuffer, sizeof(msgBuffer), | ||
"Final timer desync! Expected [%d:%d:%d], got [%d:%d:%d]", correct.min, | ||
correct.sec, correct.mil, incorrect.min, incorrect.sec, incorrect.mil); | ||
} else { | ||
snprintf(msgBuffer, sizeof(msgBuffer), | ||
"Lap %d timer desync! Expected [%d:%d:%d], got [%d:%d:%d]", desyncingTimerIdx, | ||
correct.min, correct.sec, correct.mil, incorrect.min, incorrect.sec, | ||
incorrect.mil); | ||
} | ||
reportFail(msgBuffer); | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
/// @brief Finds the desyncing timer index, if one exists. | ||
/// @return -1 if there's no desync, 0 if the final timer desyncs, and 1+ if a lap timer desyncs. | ||
s32 KReplaySystem::getDesyncingTimerIdx() const { | ||
const auto &player = System::RaceManager::Instance()->player(); | ||
if (m_currentGhost->raceTimer() != player.raceTimer()) { | ||
return 0; | ||
} | ||
|
||
for (size_t i = 0; i < 3; ++i) { | ||
if (m_currentGhost->lapTimer(i) != player.getLapSplit(i + 1)) { | ||
return i + 1; | ||
} | ||
} | ||
|
||
return -1; | ||
} | ||
|
||
/// @brief Gets the desyncing timer according to the index. | ||
/// @param i Index to the desyncing timer. Cannot be -1. | ||
/// @return The pair of timers. The first is the correct one, and the second is the incorrect one. | ||
std::pair<const System::Timer &, const System::Timer &> KReplaySystem::getDesyncingTimer( | ||
s32 i) const { | ||
auto cond = i <=> 0; | ||
ASSERT(cond != std::strong_ordering::less); | ||
|
||
if (cond == std::strong_ordering::equal) { | ||
const auto &correct = m_currentGhost->raceTimer(); | ||
const auto &incorrect = System::RaceManager::Instance()->player().raceTimer(); | ||
ASSERT(correct != incorrect); | ||
return std::pair<const System::Timer &, const System::Timer &>(correct, incorrect); | ||
} else if (cond == std::strong_ordering::greater) { | ||
const auto &correct = m_currentGhost->lapTimer(i - 1); | ||
const auto &incorrect = System::RaceManager::Instance()->player().lapTimer(i - 1); | ||
ASSERT(correct != incorrect); | ||
return std::pair<const System::Timer &, const System::Timer &>(correct, incorrect); | ||
} | ||
|
||
// This is unreachable | ||
return std::pair(System::Timer(), System::Timer()); | ||
} | ||
|
||
void KReplaySystem::OnInit(System::RaceConfig *config, void * /* arg */) { | ||
config->setGhost(Instance()->m_currentRawGhost); | ||
config->raceScenario().players[0].type = System::RaceConfig::Player::Type::Ghost; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
#include "host/KSystem.hh" | ||
|
||
#include <egg/core/SceneManager.hh> | ||
|
||
#include <game/system/RaceConfig.hh> | ||
|
||
/// @brief Kinoko system designed to execute replays. | ||
class KReplaySystem : public KSystem { | ||
public: | ||
void init() override; | ||
void calc() override; | ||
bool run() override; | ||
void parseOptions(int argc, char **argv) override; | ||
|
||
static KReplaySystem *CreateInstance(); | ||
static void DestroyInstance(); | ||
static KReplaySystem *Instance(); | ||
|
||
private: | ||
KReplaySystem(); | ||
KReplaySystem(const KReplaySystem &) = delete; | ||
KReplaySystem(KReplaySystem &&) = delete; | ||
~KReplaySystem() override; | ||
|
||
bool calcEnd() const; | ||
void reportFail(const char *msg) const; | ||
|
||
bool success() const; | ||
s32 getDesyncingTimerIdx() const; | ||
std::pair<const System::Timer &, const System::Timer &> getDesyncingTimer(s32 i) const; | ||
|
||
static void OnInit(System::RaceConfig *config, void *arg); | ||
|
||
EGG::SceneManager *m_sceneMgr; | ||
|
||
const char *m_currentGhostFileName; | ||
const System::GhostFile *m_currentGhost; | ||
const u8 *m_currentRawGhost; | ||
size_t m_currentRawGhostSize; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.