Kinoko
A reimplementation of Mario Kart Wii's physics engine in C++
Toggle main menu visibility
Loading...
Searching...
No Matches
KReplaySystem.cc
1
#include "KReplaySystem.hh"
2
3
#include "host/Option.hh"
4
#include "host/SceneCreatorDynamic.hh"
5
6
#include <abstract/File.hh>
7
8
#include <game/system/RaceManager.hh>
9
10
#include <iomanip>
11
12
namespace
Kinoko {
13
15
void
KReplaySystem::init
() {
16
ASSERT(m_currentGhostFileName);
17
ASSERT(m_currentRawGhost);
18
ASSERT(m_currentGhost);
19
20
auto
*sceneCreator =
new
Host::SceneCreatorDynamic
;
21
m_sceneMgr =
new
EGG::SceneManager
(sceneCreator);
22
23
System::RaceConfig::RegisterInitCallback(
OnInit
,
nullptr
);
24
Abstract::File::Remove(
"results.txt"
);
25
26
m_sceneMgr->changeScene(0);
27
}
28
30
void
KReplaySystem::calc
() {
31
m_sceneMgr->calc();
32
}
33
37
bool
KReplaySystem::run
() {
38
while
(!
calcEnd
()) {
39
calc
();
40
}
41
42
return
success
();
43
}
44
49
void
KReplaySystem::parseOptions
(
int
argc,
char
**argv) {
50
if
(argc < 2) {
51
PANIC(
"Expected ghost argument!"
);
52
}
53
54
for
(
int
i = 0; i < argc; ++i) {
55
std::optional<Host::EOption> flag = Host::Option::CheckFlag(argv[i]);
56
if
(!flag || *flag == Host::EOption::Invalid) {
57
WARN(
"Expected a flag! Got: %s"
, argv[i]);
58
continue
;
59
}
60
61
switch
(*flag) {
62
case
Host::EOption::Ghost: {
63
ASSERT(i + 1 < argc);
64
65
m_currentGhostFileName = argv[++i];
66
m_currentRawGhost = Abstract::File::Load(m_currentGhostFileName, m_currentRawGhostSize);
67
68
if
(m_currentRawGhostSize < System::RKG_HEADER_SIZE ||
69
m_currentRawGhostSize >
sizeof
(
System::RawGhostFile
)) {
70
PANIC(
"File cannot be a ghost! Check the file size."
);
71
}
72
73
// Creating the raw ghost file validates it
74
System::RawGhostFile
file =
System::RawGhostFile
(m_currentRawGhost);
75
76
m_currentGhost =
new
System::GhostFile
(file);
77
ASSERT(m_currentGhost);
78
}
break
;
79
case
Host::EOption::Invalid:
80
default
:
81
PANIC(
"Invalid flag!"
);
82
break
;
83
}
84
}
85
}
86
87
KReplaySystem
*KReplaySystem::CreateInstance() {
88
ASSERT(!s_instance);
89
s_instance =
new
KReplaySystem
;
90
return
static_cast<
KReplaySystem
*
>
(s_instance);
91
}
92
93
void
KReplaySystem::DestroyInstance() {
94
ASSERT(s_instance);
95
auto
*instance = s_instance;
96
s_instance =
nullptr
;
97
delete
instance;
98
}
99
100
KReplaySystem::KReplaySystem()
101
: m_currentGhostFileName(nullptr), m_currentGhost(nullptr), m_currentRawGhost(nullptr),
102
m_currentRawGhostSize(0) {}
103
104
KReplaySystem::~KReplaySystem() {
105
if
(s_instance) {
106
s_instance =
nullptr
;
107
WARN(
"KReplaySystem instance not explicitly handled!"
);
108
}
109
110
delete
m_sceneMgr;
111
delete
m_currentGhost;
112
delete
m_currentRawGhost;
113
}
114
117
bool
KReplaySystem::calcEnd
()
const
{
118
constexpr
u16 MAX_MINUTE_COUNT = 10;
119
120
const
auto
*raceManager = System::RaceManager::Instance();
121
if
(raceManager->stage() == System::RaceManager::Stage::FinishGlobal) {
122
return
true
;
123
}
124
125
if
(raceManager->timerManager().currentTimer().min >= MAX_MINUTE_COUNT) {
126
return
true
;
127
}
128
129
return
false
;
130
}
131
134
void
KReplaySystem::reportFail
(
const
std::string &msg)
const
{
135
std::string report(m_currentGhostFileName);
136
report +=
"\n"
+ std::string(msg);
137
Abstract::File::Append(
"results.txt"
, report.c_str(), report.size());
138
}
139
142
bool
KReplaySystem::success
()
const
{
143
auto
format = [](
const
System::Timer
&timer) {
144
std::ostringstream oss;
145
oss << std::setw(2) << std::setfill(
'0'
) << timer.min <<
":"
<< std::setw(2)
146
<< std::setfill(
'0'
) << timer.sec <<
"."
<< std::setw(3) << std::setfill(
'0'
)
147
<< timer.mil;
148
return
oss.str();
149
};
150
151
const
auto
*raceManager = System::RaceManager::Instance();
152
if
(raceManager->stage() != System::RaceManager::Stage::FinishGlobal) {
153
m_sceneMgr->currentScene()->heap()->enableAllocation();
154
reportFail
(
"Race didn't finish"
);
155
return
false
;
156
}
157
158
s32 desyncingTimerIdx =
getDesyncingTimerIdx
();
159
if
(desyncingTimerIdx != -1) {
160
m_sceneMgr->currentScene()->heap()->enableAllocation();
161
std::string msg;
162
163
const
auto
[correct, incorrect] =
getDesyncingTimer
(desyncingTimerIdx);
164
if
(desyncingTimerIdx == 0) {
165
msg =
"Final timer desync!"
;
166
}
else
{
167
msg =
"Lap "
+ std::to_string(desyncingTimerIdx) +
" timer desync!"
;
168
}
169
170
msg +=
" Expected "
+ format(correct) +
", got "
+ format(incorrect);
171
reportFail
(msg);
172
return
false
;
173
}
174
175
return
true
;
176
}
177
180
s32
KReplaySystem::getDesyncingTimerIdx
()
const
{
181
const
auto
&player = System::RaceManager::Instance()->player();
182
if
(m_currentGhost->raceTimer() != player.raceTimer()) {
183
return
0;
184
}
185
186
for
(
size_t
i = 0; i < 3; ++i) {
187
if
(m_currentGhost->lapTimer(i) != player.getLapSplit(i + 1)) {
188
return
i + 1;
189
}
190
}
191
192
return
-1;
193
}
194
198
KReplaySystem::DesyncingTimerPair
KReplaySystem::getDesyncingTimer
(s32 i)
const
{
199
auto
cond = i <=> 0;
200
ASSERT(cond != std::strong_ordering::less);
201
202
if
(cond == std::strong_ordering::equal) {
203
const
auto
&correct = m_currentGhost->raceTimer();
204
const
auto
&incorrect = System::RaceManager::Instance()->player().raceTimer();
205
ASSERT(correct != incorrect);
206
return
DesyncingTimerPair(correct, incorrect);
207
}
else
if
(cond == std::strong_ordering::greater) {
208
const
auto
&correct = m_currentGhost->lapTimer(i - 1);
209
const
auto
&incorrect = System::RaceManager::Instance()->player().lapTimer(i - 1);
210
ASSERT(correct != incorrect);
211
return
DesyncingTimerPair(correct, incorrect);
212
}
213
214
// This is unreachable
215
return
DesyncingTimerPair(
System::Timer
(),
System::Timer
());
216
}
217
221
void
KReplaySystem::OnInit
(
System::RaceConfig
*config,
void
*
/* arg */
) {
222
config->setGhost(Instance()->m_currentRawGhost);
223
config->raceScenario().players[0].type = System::RaceConfig::Player::Type::Ghost;
224
}
225
226
}
// namespace Kinoko
Kinoko::EGG::SceneManager
Manages the scene stack and transitions between scenes.
Definition
SceneManager.hh:17
Kinoko::Host::SceneCreatorDynamic
Definition
SceneCreatorDynamic.hh:12
Kinoko::KReplaySystem
Kinoko system designed to execute replays.
Definition
KReplaySystem.hh:12
Kinoko::KReplaySystem::getDesyncingTimerIdx
s32 getDesyncingTimerIdx() const
Finds the desyncing timer index, if one exists.
Definition
KReplaySystem.cc:180
Kinoko::KReplaySystem::parseOptions
void parseOptions(int argc, char **argv) override
Parses non-generic command line options.
Definition
KReplaySystem.cc:49
Kinoko::KReplaySystem::reportFail
void reportFail(const std::string &msg) const
Reports failure to file.
Definition
KReplaySystem.cc:134
Kinoko::KReplaySystem::calcEnd
bool calcEnd() const
Determines whether or not the ghost simulation should end.
Definition
KReplaySystem.cc:117
Kinoko::KReplaySystem::run
bool run() override
Executes a run.
Definition
KReplaySystem.cc:37
Kinoko::KReplaySystem::OnInit
static void OnInit(System::RaceConfig *config, void *arg)
Initializes the race configuration as needed for replays.
Definition
KReplaySystem.cc:221
Kinoko::KReplaySystem::getDesyncingTimer
DesyncingTimerPair getDesyncingTimer(s32 i) const
Gets the desyncing timer according to the index.
Definition
KReplaySystem.cc:198
Kinoko::KReplaySystem::success
bool success() const
Determines whether the simulation was a success or not.
Definition
KReplaySystem.cc:142
Kinoko::KReplaySystem::calc
void calc() override
Executes a frame.
Definition
KReplaySystem.cc:30
Kinoko::KReplaySystem::init
void init() override
Initializes the system.
Definition
KReplaySystem.cc:15
Kinoko::System::GhostFile
Parsed representation of a binary ghost file.
Definition
GhostFile.hh:77
Kinoko::System::RaceConfig
Initializes the player with parameters specified in the provided ghost file.
Definition
RaceConfig.hh:23
Kinoko::System::RawGhostFile
The binary data of a ghost saved to a file.
Definition
GhostFile.hh:45
Kinoko::System::Timer
A simple struct to represent a lap or race finish time.
Definition
TimerManager.hh:8
host
KReplaySystem.cc
Made by
Malleo
. Logo by
vabold
. Website generated by
Doxygen
1.17.0