2025-06-03 23:02:00 -07:00
|
|
|
/* Copyright (C) 2026 Wildfire Games.
|
2023-12-02 16:30:12 -08:00
|
|
|
* This file is part of 0 A.D.
|
2010-08-06 15:16:05 -07:00
|
|
|
*
|
2023-12-02 16:30:12 -08:00
|
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
2010-08-06 15:16:05 -07:00
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
2023-12-02 16:30:12 -08:00
|
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
2010-08-06 15:16:05 -07:00
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2023-12-02 16:30:12 -08:00
|
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
2010-08-06 15:16:05 -07:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "precompiled.h"
|
|
|
|
|
|
|
|
|
|
#include "Replay.h"
|
|
|
|
|
|
2025-07-23 10:19:42 -07:00
|
|
|
#include "lib/code_generation.h"
|
|
|
|
|
#include "lib/debug.h"
|
2010-08-06 15:16:05 -07:00
|
|
|
#include "lib/file/file_system.h"
|
2025-07-23 10:19:42 -07:00
|
|
|
#include "lib/path.h"
|
|
|
|
|
#include "lib/timer.h"
|
2016-08-02 04:11:10 -07:00
|
|
|
#include "ps/CLogger.h"
|
2025-07-23 10:19:42 -07:00
|
|
|
#include "ps/CStr.h"
|
|
|
|
|
#include "ps/Errors.h"
|
2021-05-20 07:36:42 -07:00
|
|
|
#include "ps/Game.h"
|
|
|
|
|
#include "ps/GameSetup/CmdLineArgs.h"
|
2025-07-23 10:19:42 -07:00
|
|
|
#include "ps/GameSetup/GameSetup.h"
|
2021-05-20 07:36:42 -07:00
|
|
|
#include "ps/GameSetup/Paths.h"
|
2010-08-06 15:16:05 -07:00
|
|
|
#include "ps/Loader.h"
|
2015-09-21 10:00:21 -07:00
|
|
|
#include "ps/Mod.h"
|
2010-09-17 10:45:50 -07:00
|
|
|
#include "ps/Profile.h"
|
|
|
|
|
#include "ps/ProfileViewer.h"
|
2025-07-23 10:19:42 -07:00
|
|
|
#include "ps/Profiler2.h"
|
2015-09-21 10:00:21 -07:00
|
|
|
#include "ps/Pyrogenesis.h"
|
2016-02-15 07:57:23 -08:00
|
|
|
#include "ps/Util.h"
|
|
|
|
|
#include "ps/VisualReplay.h"
|
2025-07-23 10:19:42 -07:00
|
|
|
#include "scriptinterface/JSON.h"
|
2021-05-13 10:23:52 -07:00
|
|
|
#include "scriptinterface/Object.h"
|
Move Script* classes to Script namespace
- Rename ScriptEngine, ScriptContext, ScriptInterface, ScriptRequest to
Script::Engine, Script::Context, Script::Interface, Script::Request
- Remove 'Script' prefix from filenames:
ScriptContext.* → Context.*
ScriptInterface.* → Interface.*
ScriptRequest.* → Request.*
ScriptEngine.* → Engine.*
ScriptConversions.* → Conversions.*
ScriptExceptions.* → Exceptions.*
ScriptForward.* → ForwardDeclarations.*
ScriptStats.* → Stats.*
- Update all includes, forward declarations, and friend classes
- Use namespace Script { ... } in .cpp definitions to avoid repetitive
Script:: prefix (keeping global callbacks outside)
- Rename internal implementation structs:
ScriptInterface_impl → Interface_impl
ScriptFunction → Function
- Update copyright year to 2026 in all touched files
- Suppress pre-existing cppcheck warnings (uninitvar, nullPointer, unknown
macro) by adding them to suppressions-list.txt (these are not caused
by this refactor)
Fixes #7516
2026-06-02 14:06:50 -07:00
|
|
|
#include "scriptinterface/Context.h"
|
|
|
|
|
#include "scriptinterface/Interface.h"
|
|
|
|
|
#include "scriptinterface/Request.h"
|
|
|
|
|
#include "scriptinterface/Stats.h"
|
2025-07-23 10:19:42 -07:00
|
|
|
#include "simulation2/Simulation2.h"
|
2019-09-25 03:06:12 -07:00
|
|
|
#include "simulation2/components/ICmpGuiInterface.h"
|
|
|
|
|
#include "simulation2/helpers/Player.h"
|
2010-08-06 15:16:05 -07:00
|
|
|
#include "simulation2/helpers/SimulationCommand.h"
|
2025-08-03 08:30:17 -07:00
|
|
|
#include "simulation2/system/Component.h"
|
2025-07-23 10:19:42 -07:00
|
|
|
#include "simulation2/system/Entity.h"
|
2010-08-06 15:16:05 -07:00
|
|
|
|
2016-02-15 07:57:23 -08:00
|
|
|
#include <ctime>
|
2010-08-10 12:48:35 -07:00
|
|
|
#include <fstream>
|
2025-07-23 10:19:42 -07:00
|
|
|
#include <memory>
|
2010-08-06 15:16:05 -07:00
|
|
|
|
2018-03-22 06:53:04 -07:00
|
|
|
/**
|
|
|
|
|
* Number of turns between two saved profiler snapshots.
|
|
|
|
|
* Keep in sync with source/tools/replayprofile/graph.js
|
|
|
|
|
*/
|
|
|
|
|
static const int PROFILE_TURN_INTERVAL = 20;
|
|
|
|
|
|
Move Script* classes to Script namespace
- Rename ScriptEngine, ScriptContext, ScriptInterface, ScriptRequest to
Script::Engine, Script::Context, Script::Interface, Script::Request
- Remove 'Script' prefix from filenames:
ScriptContext.* → Context.*
ScriptInterface.* → Interface.*
ScriptRequest.* → Request.*
ScriptEngine.* → Engine.*
ScriptConversions.* → Conversions.*
ScriptExceptions.* → Exceptions.*
ScriptForward.* → ForwardDeclarations.*
ScriptStats.* → Stats.*
- Update all includes, forward declarations, and friend classes
- Use namespace Script { ... } in .cpp definitions to avoid repetitive
Script:: prefix (keeping global callbacks outside)
- Rename internal implementation structs:
ScriptInterface_impl → Interface_impl
ScriptFunction → Function
- Update copyright year to 2026 in all touched files
- Suppress pre-existing cppcheck warnings (uninitvar, nullPointer, unknown
macro) by adding them to suppressions-list.txt (these are not caused
by this refactor)
Fixes #7516
2026-06-02 14:06:50 -07:00
|
|
|
CReplayLogger::CReplayLogger(const Script::Interface& scriptInterface) :
|
2015-09-11 10:44:50 -07:00
|
|
|
m_ScriptInterface(scriptInterface), m_Stream(NULL)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CReplayLogger::~CReplayLogger()
|
|
|
|
|
{
|
|
|
|
|
delete m_Stream;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CReplayLogger::StartGame(JS::MutableHandleValue attribs)
|
2010-08-06 15:16:05 -07:00
|
|
|
{
|
Move Script* classes to Script namespace
- Rename ScriptEngine, ScriptContext, ScriptInterface, ScriptRequest to
Script::Engine, Script::Context, Script::Interface, Script::Request
- Remove 'Script' prefix from filenames:
ScriptContext.* → Context.*
ScriptInterface.* → Interface.*
ScriptRequest.* → Request.*
ScriptEngine.* → Engine.*
ScriptConversions.* → Conversions.*
ScriptExceptions.* → Exceptions.*
ScriptForward.* → ForwardDeclarations.*
ScriptStats.* → Stats.*
- Update all includes, forward declarations, and friend classes
- Use namespace Script { ... } in .cpp definitions to avoid repetitive
Script:: prefix (keeping global callbacks outside)
- Rename internal implementation structs:
ScriptInterface_impl → Interface_impl
ScriptFunction → Function
- Update copyright year to 2026 in all touched files
- Suppress pre-existing cppcheck warnings (uninitvar, nullPointer, unknown
macro) by adding them to suppressions-list.txt (these are not caused
by this refactor)
Fixes #7516
2026-06-02 14:06:50 -07:00
|
|
|
Script::Request rq(m_ScriptInterface);
|
2018-02-17 08:53:14 -08:00
|
|
|
|
2015-09-21 10:00:21 -07:00
|
|
|
// Add timestamp, since the file-modification-date can change
|
2021-05-13 10:23:52 -07:00
|
|
|
Script::SetProperty(rq, attribs, "timestamp", (double)std::time(nullptr));
|
2015-09-21 10:00:21 -07:00
|
|
|
|
|
|
|
|
// Add engine version and currently loaded mods for sanity checks when replaying
|
2025-10-20 04:54:34 -07:00
|
|
|
Script::SetProperty(rq, attribs, "engine_serialization_version", PS_SERIALIZATION_VERSION);
|
2021-06-01 23:50:16 -07:00
|
|
|
JS::RootedValue mods(rq.cx);
|
|
|
|
|
Script::ToJSVal(rq, &mods, g_Mods.GetEnabledModsData());
|
2021-05-13 10:23:52 -07:00
|
|
|
Script::SetProperty(rq, attribs, "mods", mods);
|
2015-09-21 10:00:21 -07:00
|
|
|
|
Replay: name replay folders after matchID instead of a per-machine index
Replay folders were named YYYY-MM-DD_NNNN, a per-machine sequence
counter. The same multiplayer match got a different folder name per
participant, and unrelated matches could share a name, making it hard
to correlate replays across players (rating aggregation, replay
sharing, bug reports) without parsing commands.txt first.
The engine already generates a matchID once per match, synced to all
participants before the match starts. Replay folders are now named
YYYY-MM-DD_<matchID>, so the same match produces the same folder name
everywhere.
The date comes from a new "startTime" attribute, set once by the host
(StartTime.js, alongside MatchID.js) and synced like matchID, instead
of each machine reading its own local clock. createDateIndexSubdirectory
is untouched (still used by OOS logs and serialization tests); a new
createReplaySubdirectory is added for replays only, following the same
collision-handling pattern (incrementing suffix) as the existing
function. This also means resuming a match (network rejoin, or a
singleplayer game reloaded via StartSavedGame, both of which reuse the
same matchID) saves an additional replay instead of overwriting the
original.
Fixes #8922
2026-07-06 22:49:19 -07:00
|
|
|
std::wstring matchID;
|
|
|
|
|
Script::GetProperty(rq, attribs, "matchID", matchID);
|
|
|
|
|
ENSURE(!matchID.empty());
|
|
|
|
|
|
|
|
|
|
double startTime = 0;
|
|
|
|
|
Script::GetProperty(rq, attribs, "startTime", startTime);
|
|
|
|
|
|
|
|
|
|
m_Directory = createReplaySubdirectory(VisualReplay::GetDirectoryPath(), matchID, startTime);
|
2022-03-05 10:30:38 -08:00
|
|
|
debug_printf("FILES| Replay written to '%s'\n", m_Directory.string8().c_str());
|
2010-08-06 15:16:05 -07:00
|
|
|
|
2024-10-27 03:33:42 -07:00
|
|
|
m_Stream = new std::ofstream(OsString(m_Directory / L"commands.txt"), std::ofstream::out | std::ofstream::trunc);
|
2021-05-14 03:18:03 -07:00
|
|
|
*m_Stream << "start " << Script::StringifyJSON(rq, attribs, false) << "\n";
|
2010-08-06 15:16:05 -07:00
|
|
|
}
|
|
|
|
|
|
2015-01-24 06:46:52 -08:00
|
|
|
void CReplayLogger::Turn(u32 n, u32 turnLength, std::vector<SimulationCommand>& commands)
|
2010-08-06 15:16:05 -07:00
|
|
|
{
|
Move Script* classes to Script namespace
- Rename ScriptEngine, ScriptContext, ScriptInterface, ScriptRequest to
Script::Engine, Script::Context, Script::Interface, Script::Request
- Remove 'Script' prefix from filenames:
ScriptContext.* → Context.*
ScriptInterface.* → Interface.*
ScriptRequest.* → Request.*
ScriptEngine.* → Engine.*
ScriptConversions.* → Conversions.*
ScriptExceptions.* → Exceptions.*
ScriptForward.* → ForwardDeclarations.*
ScriptStats.* → Stats.*
- Update all includes, forward declarations, and friend classes
- Use namespace Script { ... } in .cpp definitions to avoid repetitive
Script:: prefix (keeping global callbacks outside)
- Rename internal implementation structs:
ScriptInterface_impl → Interface_impl
ScriptFunction → Function
- Update copyright year to 2026 in all touched files
- Suppress pre-existing cppcheck warnings (uninitvar, nullPointer, unknown
macro) by adding them to suppressions-list.txt (these are not caused
by this refactor)
Fixes #7516
2026-06-02 14:06:50 -07:00
|
|
|
Script::Request rq(m_ScriptInterface);
|
2010-08-06 15:16:05 -07:00
|
|
|
*m_Stream << "turn " << n << " " << turnLength << "\n";
|
2017-02-04 18:18:33 -08:00
|
|
|
for (SimulationCommand& command : commands)
|
2021-05-14 03:18:03 -07:00
|
|
|
*m_Stream << "cmd " << command.player << " " << Script::StringifyJSON(rq, &command.data, false) << "\n";
|
2010-08-06 15:16:05 -07:00
|
|
|
*m_Stream << "end\n";
|
|
|
|
|
m_Stream->flush();
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-05 14:30:32 -08:00
|
|
|
void CReplayLogger::Hash(const std::string& hash, bool quick)
|
2010-08-06 15:16:05 -07:00
|
|
|
{
|
2011-03-05 14:30:32 -08:00
|
|
|
if (quick)
|
|
|
|
|
*m_Stream << "hash-quick " << Hexify(hash) << "\n";
|
|
|
|
|
else
|
|
|
|
|
*m_Stream << "hash " << Hexify(hash) << "\n";
|
2010-08-06 15:16:05 -07:00
|
|
|
}
|
|
|
|
|
|
2019-09-25 03:06:12 -07:00
|
|
|
void CReplayLogger::SaveMetadata(const CSimulation2& simulation)
|
|
|
|
|
{
|
|
|
|
|
CmpPtr<ICmpGuiInterface> cmpGuiInterface(simulation, SYSTEM_ENTITY);
|
|
|
|
|
if (!cmpGuiInterface)
|
|
|
|
|
{
|
|
|
|
|
LOGERROR("Could not save replay metadata!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
Move Script* classes to Script namespace
- Rename ScriptEngine, ScriptContext, ScriptInterface, ScriptRequest to
Script::Engine, Script::Context, Script::Interface, Script::Request
- Remove 'Script' prefix from filenames:
ScriptContext.* → Context.*
ScriptInterface.* → Interface.*
ScriptRequest.* → Request.*
ScriptEngine.* → Engine.*
ScriptConversions.* → Conversions.*
ScriptExceptions.* → Exceptions.*
ScriptForward.* → ForwardDeclarations.*
ScriptStats.* → Stats.*
- Update all includes, forward declarations, and friend classes
- Use namespace Script { ... } in .cpp definitions to avoid repetitive
Script:: prefix (keeping global callbacks outside)
- Rename internal implementation structs:
ScriptInterface_impl → Interface_impl
ScriptFunction → Function
- Update copyright year to 2026 in all touched files
- Suppress pre-existing cppcheck warnings (uninitvar, nullPointer, unknown
macro) by adding them to suppressions-list.txt (these are not caused
by this refactor)
Fixes #7516
2026-06-02 14:06:50 -07:00
|
|
|
Script::Interface& scriptInterface = simulation.GetScriptInterface();
|
|
|
|
|
Script::Request rq(scriptInterface);
|
2019-09-25 03:06:12 -07:00
|
|
|
|
2020-11-13 05:18:22 -08:00
|
|
|
JS::RootedValue arg(rq.cx);
|
|
|
|
|
JS::RootedValue metadata(rq.cx);
|
2019-09-25 03:06:12 -07:00
|
|
|
cmpGuiInterface->ScriptCall(INVALID_PLAYER, L"GetReplayMetadata", arg, &metadata);
|
|
|
|
|
|
|
|
|
|
const OsPath fileName = g_Game->GetReplayLogger().GetDirectory() / L"metadata.json";
|
|
|
|
|
CreateDirectories(fileName.Parent(), 0700);
|
|
|
|
|
|
2024-10-27 03:33:42 -07:00
|
|
|
std::ofstream stream (OsString(fileName), std::ofstream::out | std::ofstream::trunc);
|
2022-03-05 09:28:00 -08:00
|
|
|
if (stream)
|
|
|
|
|
{
|
|
|
|
|
stream << Script::StringifyJSON(rq, &metadata, false);
|
|
|
|
|
stream.close();
|
2022-03-05 10:30:38 -08:00
|
|
|
debug_printf("FILES| Replay metadata written to '%s'\n", fileName.string8().c_str());
|
2022-03-05 09:28:00 -08:00
|
|
|
}
|
|
|
|
|
else
|
2022-03-05 10:30:38 -08:00
|
|
|
debug_printf("FILES| Failed to write replay metadata to '%s'\n", fileName.string8().c_str());
|
2022-03-05 09:28:00 -08:00
|
|
|
|
2019-09-25 03:06:12 -07:00
|
|
|
}
|
|
|
|
|
|
2015-09-21 10:00:21 -07:00
|
|
|
OsPath CReplayLogger::GetDirectory() const
|
|
|
|
|
{
|
|
|
|
|
return m_Directory;
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-06 15:16:05 -07:00
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
CReplayPlayer::CReplayPlayer() :
|
|
|
|
|
m_Stream(NULL)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CReplayPlayer::~CReplayPlayer()
|
|
|
|
|
{
|
|
|
|
|
delete m_Stream;
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-25 07:54:00 -07:00
|
|
|
void CReplayPlayer::Load(const OsPath& path)
|
2010-08-06 15:16:05 -07:00
|
|
|
{
|
2011-04-30 06:01:45 -07:00
|
|
|
ENSURE(!m_Stream);
|
2010-08-06 15:16:05 -07:00
|
|
|
|
2024-10-27 03:33:42 -07:00
|
|
|
m_Stream = new std::ifstream(OsString(path));
|
2011-04-30 06:01:45 -07:00
|
|
|
ENSURE(m_Stream->good());
|
2010-08-06 15:16:05 -07:00
|
|
|
}
|
|
|
|
|
|
2021-06-01 23:50:16 -07:00
|
|
|
namespace
|
|
|
|
|
{
|
|
|
|
|
CStr ModListToString(const std::vector<const Mod::ModData*>& list)
|
2018-03-22 08:26:27 -07:00
|
|
|
{
|
|
|
|
|
CStr text;
|
2021-06-01 23:50:16 -07:00
|
|
|
for (const Mod::ModData* data : list)
|
|
|
|
|
text += data->m_Pathname + " (" + data->m_Version + ")\n";
|
2018-03-22 08:26:27 -07:00
|
|
|
return text;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-01 23:50:16 -07:00
|
|
|
void CheckReplayMods(const std::vector<Mod::ModData>& replayMods)
|
2018-03-22 08:26:27 -07:00
|
|
|
{
|
2021-06-01 23:50:16 -07:00
|
|
|
std::vector<const Mod::ModData*> replayData;
|
|
|
|
|
replayData.reserve(replayMods.size());
|
|
|
|
|
for (const Mod::ModData& data : replayMods)
|
|
|
|
|
replayData.push_back(&data);
|
|
|
|
|
if (!Mod::AreModsPlayCompatible(g_Mods.GetEnabledModsData(), replayData))
|
|
|
|
|
LOGWARNING("Incompatible replay mods detected.\nThe mods of the replay are:\n%s\nThese mods are enabled:\n%s",
|
|
|
|
|
ModListToString(replayData), ModListToString(g_Mods.GetEnabledModsData()));
|
2018-03-22 08:26:27 -07:00
|
|
|
}
|
2021-06-01 23:50:16 -07:00
|
|
|
} // anonymous namespace
|
2018-05-28 19:14:38 -07:00
|
|
|
|
2025-09-18 07:12:41 -07:00
|
|
|
void CReplayPlayer::Replay(const int serializationtestturn, const int rejointestturn, const bool ooslog,
|
|
|
|
|
const bool testHashFull, const bool testHashQuick)
|
2010-08-06 15:16:05 -07:00
|
|
|
{
|
2011-04-30 06:01:45 -07:00
|
|
|
ENSURE(m_Stream);
|
2010-08-06 15:16:05 -07:00
|
|
|
|
2010-09-17 10:45:50 -07:00
|
|
|
new CProfileViewer;
|
|
|
|
|
new CProfileManager;
|
Move Script* classes to Script namespace
- Rename ScriptEngine, ScriptContext, ScriptInterface, ScriptRequest to
Script::Engine, Script::Context, Script::Interface, Script::Request
- Remove 'Script' prefix from filenames:
ScriptContext.* → Context.*
ScriptInterface.* → Interface.*
ScriptRequest.* → Request.*
ScriptEngine.* → Engine.*
ScriptConversions.* → Conversions.*
ScriptExceptions.* → Exceptions.*
ScriptForward.* → ForwardDeclarations.*
ScriptStats.* → Stats.*
- Update all includes, forward declarations, and friend classes
- Use namespace Script { ... } in .cpp definitions to avoid repetitive
Script:: prefix (keeping global callbacks outside)
- Rename internal implementation structs:
ScriptInterface_impl → Interface_impl
ScriptFunction → Function
- Update copyright year to 2026 in all touched files
- Suppress pre-existing cppcheck warnings (uninitvar, nullPointer, unknown
macro) by adding them to suppressions-list.txt (these are not caused
by this refactor)
Fixes #7516
2026-06-02 14:06:50 -07:00
|
|
|
g_ScriptStatsTable = new Script::CScriptStatsTable;
|
2010-09-17 10:45:50 -07:00
|
|
|
g_ProfileViewer.AddRootTable(g_ScriptStatsTable);
|
2016-01-22 17:02:57 -08:00
|
|
|
|
2020-11-14 02:57:50 -08:00
|
|
|
const int contextSize = 384 * 1024 * 1024;
|
2024-11-23 09:36:23 -08:00
|
|
|
const int heapGrowthBytesGCTrigger = 12 * 1024 * 1024;
|
Move Script* classes to Script namespace
- Rename ScriptEngine, ScriptContext, ScriptInterface, ScriptRequest to
Script::Engine, Script::Context, Script::Interface, Script::Request
- Remove 'Script' prefix from filenames:
ScriptContext.* → Context.*
ScriptInterface.* → Interface.*
ScriptRequest.* → Request.*
ScriptEngine.* → Engine.*
ScriptConversions.* → Conversions.*
ScriptExceptions.* → Exceptions.*
ScriptForward.* → ForwardDeclarations.*
ScriptStats.* → Stats.*
- Update all includes, forward declarations, and friend classes
- Use namespace Script { ... } in .cpp definitions to avoid repetitive
Script:: prefix (keeping global callbacks outside)
- Rename internal implementation structs:
ScriptInterface_impl → Interface_impl
ScriptFunction → Function
- Update copyright year to 2026 in all touched files
- Suppress pre-existing cppcheck warnings (uninitvar, nullPointer, unknown
macro) by adding them to suppressions-list.txt (these are not caused
by this refactor)
Fixes #7516
2026-06-02 14:06:50 -07:00
|
|
|
g_ScriptContext = std::make_shared<Script::Context>(contextSize, heapGrowthBytesGCTrigger);
|
2010-09-17 10:45:50 -07:00
|
|
|
|
2010-08-06 15:16:05 -07:00
|
|
|
std::vector<SimulationCommand> commands;
|
2010-09-17 10:45:50 -07:00
|
|
|
u32 turn = 0;
|
2010-08-09 02:49:24 -07:00
|
|
|
u32 turnLength = 0;
|
2010-08-06 15:16:05 -07:00
|
|
|
|
2015-05-06 13:28:28 -07:00
|
|
|
{
|
2010-08-06 15:16:05 -07:00
|
|
|
std::string type;
|
2018-03-22 08:26:27 -07:00
|
|
|
|
2010-08-06 15:16:05 -07:00
|
|
|
while ((*m_Stream >> type).good())
|
|
|
|
|
{
|
|
|
|
|
if (type == "start")
|
|
|
|
|
{
|
2021-05-20 07:36:42 -07:00
|
|
|
std::string attribsStr;
|
|
|
|
|
{
|
Move Script* classes to Script namespace
- Rename ScriptEngine, ScriptContext, ScriptInterface, ScriptRequest to
Script::Engine, Script::Context, Script::Interface, Script::Request
- Remove 'Script' prefix from filenames:
ScriptContext.* → Context.*
ScriptInterface.* → Interface.*
ScriptRequest.* → Request.*
ScriptEngine.* → Engine.*
ScriptConversions.* → Conversions.*
ScriptExceptions.* → Exceptions.*
ScriptForward.* → ForwardDeclarations.*
ScriptStats.* → Stats.*
- Update all includes, forward declarations, and friend classes
- Use namespace Script { ... } in .cpp definitions to avoid repetitive
Script:: prefix (keeping global callbacks outside)
- Rename internal implementation structs:
ScriptInterface_impl → Interface_impl
ScriptFunction → Function
- Update copyright year to 2026 in all touched files
- Suppress pre-existing cppcheck warnings (uninitvar, nullPointer, unknown
macro) by adding them to suppressions-list.txt (these are not caused
by this refactor)
Fixes #7516
2026-06-02 14:06:50 -07:00
|
|
|
Script::Interface scriptInterface("Engine", "Replay", g_ScriptContext);
|
|
|
|
|
Script::Request rq(scriptInterface);
|
2021-05-20 07:36:42 -07:00
|
|
|
std::getline(*m_Stream, attribsStr);
|
|
|
|
|
JS::RootedValue attribs(rq.cx);
|
|
|
|
|
if (!Script::ParseJSON(rq, attribsStr, &attribs))
|
|
|
|
|
{
|
|
|
|
|
LOGERROR("Error parsing JSON attributes: %s", attribsStr);
|
|
|
|
|
// TODO: do something cleverer than crashing.
|
|
|
|
|
ENSURE(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Load the mods specified in the replay.
|
2021-06-01 23:50:16 -07:00
|
|
|
std::vector<Mod::ModData> replayMods;
|
|
|
|
|
if (!Script::GetProperty(rq, attribs, "mods", replayMods))
|
|
|
|
|
{
|
|
|
|
|
LOGERROR("Could not get replay mod information.");
|
|
|
|
|
// TODO: do something cleverer than crashing.
|
|
|
|
|
ENSURE(false);
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-20 07:36:42 -07:00
|
|
|
std::vector<CStr> mods;
|
2021-06-01 23:50:16 -07:00
|
|
|
for (const Mod::ModData& data : replayMods)
|
|
|
|
|
mods.emplace_back(data.m_Pathname);
|
2021-05-20 07:36:42 -07:00
|
|
|
|
|
|
|
|
// Ignore the return value, we check below.
|
2021-08-22 02:54:16 -07:00
|
|
|
g_Mods.UpdateAvailableMods(scriptInterface);
|
|
|
|
|
g_Mods.EnableMods(mods, false);
|
2021-06-01 23:50:16 -07:00
|
|
|
CheckReplayMods(replayMods);
|
2021-05-20 07:36:42 -07:00
|
|
|
|
2021-06-01 23:50:16 -07:00
|
|
|
MountMods(Paths(g_CmdLineArgs), g_Mods.GetEnabledMods());
|
2021-05-20 07:36:42 -07:00
|
|
|
}
|
2010-08-06 15:16:05 -07:00
|
|
|
|
2025-09-18 07:12:41 -07:00
|
|
|
if (serializationtestturn >= 0 && rejointestturn >= 0)
|
2025-10-15 02:50:35 -07:00
|
|
|
LOGERROR("serializationtest and rejointest can't be activ at the same time.");
|
|
|
|
|
|
|
|
|
|
SimulationDebugOptions debugOption{};
|
2025-09-18 07:12:41 -07:00
|
|
|
if (serializationtestturn >= 0)
|
|
|
|
|
{
|
|
|
|
|
debugOption.test =
|
|
|
|
|
SimulationDebugOptions::SerializationTest{serializationtestturn};
|
|
|
|
|
}
|
2021-05-20 07:36:42 -07:00
|
|
|
if (rejointestturn >= 0)
|
2025-10-15 02:50:35 -07:00
|
|
|
debugOption.test = SimulationDebugOptions::RejoinTest{rejointestturn};
|
|
|
|
|
if (ooslog)
|
|
|
|
|
debugOption.oosLog = true;
|
|
|
|
|
|
|
|
|
|
g_Game = new CGame(false, debugOption);
|
2016-08-02 04:11:10 -07:00
|
|
|
|
Move Script* classes to Script namespace
- Rename ScriptEngine, ScriptContext, ScriptInterface, ScriptRequest to
Script::Engine, Script::Context, Script::Interface, Script::Request
- Remove 'Script' prefix from filenames:
ScriptContext.* → Context.*
ScriptInterface.* → Interface.*
ScriptRequest.* → Request.*
ScriptEngine.* → Engine.*
ScriptConversions.* → Conversions.*
ScriptExceptions.* → Exceptions.*
ScriptForward.* → ForwardDeclarations.*
ScriptStats.* → Stats.*
- Update all includes, forward declarations, and friend classes
- Use namespace Script { ... } in .cpp definitions to avoid repetitive
Script:: prefix (keeping global callbacks outside)
- Rename internal implementation structs:
ScriptInterface_impl → Interface_impl
ScriptFunction → Function
- Update copyright year to 2026 in all touched files
- Suppress pre-existing cppcheck warnings (uninitvar, nullPointer, unknown
macro) by adding them to suppressions-list.txt (these are not caused
by this refactor)
Fixes #7516
2026-06-02 14:06:50 -07:00
|
|
|
Script::Request rq(g_Game->GetSimulation2()->GetScriptInterface());
|
2021-05-20 07:36:42 -07:00
|
|
|
JS::RootedValue attribs(rq.cx);
|
|
|
|
|
ENSURE(Script::ParseJSON(rq, attribsStr, &attribs));
|
2015-05-05 15:34:41 -07:00
|
|
|
g_Game->StartGame(&attribs, "");
|
2011-04-06 19:32:16 -07:00
|
|
|
|
|
|
|
|
// TODO: Non progressive load can fail - need a decent way to handle this
|
2025-10-20 12:35:40 -07:00
|
|
|
PS::Loader::NonprogressiveLoad();
|
2011-04-06 19:32:16 -07:00
|
|
|
|
2015-05-05 15:34:41 -07:00
|
|
|
PSRETURN ret = g_Game->ReallyStartGame();
|
2011-04-30 06:01:45 -07:00
|
|
|
ENSURE(ret == PSRETURN_OK);
|
2010-08-06 15:16:05 -07:00
|
|
|
}
|
|
|
|
|
else if (type == "turn")
|
|
|
|
|
{
|
|
|
|
|
*m_Stream >> turn >> turnLength;
|
2015-04-12 12:38:31 -07:00
|
|
|
debug_printf("Turn %u (%u)...\n", turn, turnLength);
|
2010-08-06 15:16:05 -07:00
|
|
|
}
|
|
|
|
|
else if (type == "cmd")
|
|
|
|
|
{
|
2013-06-10 17:05:57 -07:00
|
|
|
player_id_t player;
|
2010-08-06 15:16:05 -07:00
|
|
|
*m_Stream >> player;
|
|
|
|
|
|
|
|
|
|
std::string line;
|
|
|
|
|
std::getline(*m_Stream, line);
|
Move Script* classes to Script namespace
- Rename ScriptEngine, ScriptContext, ScriptInterface, ScriptRequest to
Script::Engine, Script::Context, Script::Interface, Script::Request
- Remove 'Script' prefix from filenames:
ScriptContext.* → Context.*
ScriptInterface.* → Interface.*
ScriptRequest.* → Request.*
ScriptEngine.* → Engine.*
ScriptConversions.* → Conversions.*
ScriptExceptions.* → Exceptions.*
ScriptForward.* → ForwardDeclarations.*
ScriptStats.* → Stats.*
- Update all includes, forward declarations, and friend classes
- Use namespace Script { ... } in .cpp definitions to avoid repetitive
Script:: prefix (keeping global callbacks outside)
- Rename internal implementation structs:
ScriptInterface_impl → Interface_impl
ScriptFunction → Function
- Update copyright year to 2026 in all touched files
- Suppress pre-existing cppcheck warnings (uninitvar, nullPointer, unknown
macro) by adding them to suppressions-list.txt (these are not caused
by this refactor)
Fixes #7516
2026-06-02 14:06:50 -07:00
|
|
|
Script::Request rq(g_Game->GetSimulation2()->GetScriptInterface());
|
2020-11-13 05:18:22 -08:00
|
|
|
JS::RootedValue data(rq.cx);
|
2021-05-14 03:18:03 -07:00
|
|
|
Script::ParseJSON(rq, line, &data);
|
2024-11-17 07:25:17 -08:00
|
|
|
Script::DeepFreezeObject(rq, data);
|
2020-11-13 05:18:22 -08:00
|
|
|
commands.emplace_back(SimulationCommand(player, rq.cx, data));
|
2010-08-06 15:16:05 -07:00
|
|
|
}
|
2011-03-05 14:30:32 -08:00
|
|
|
else if (type == "hash" || type == "hash-quick")
|
2010-09-17 10:45:50 -07:00
|
|
|
{
|
|
|
|
|
std::string replayHash;
|
|
|
|
|
*m_Stream >> replayHash;
|
2018-05-28 19:14:38 -07:00
|
|
|
TestHash(type, replayHash, testHashFull, testHashQuick);
|
2010-09-17 10:45:50 -07:00
|
|
|
}
|
2010-08-06 15:16:05 -07:00
|
|
|
else if (type == "end")
|
|
|
|
|
{
|
2011-11-29 12:32:43 -08:00
|
|
|
{
|
|
|
|
|
g_Profiler2.RecordFrameStart();
|
|
|
|
|
PROFILE2("frame");
|
|
|
|
|
g_Profiler2.IncrementFrameNumber();
|
|
|
|
|
PROFILE2_ATTR("%d", g_Profiler2.GetFrameNumber());
|
|
|
|
|
|
2015-05-05 15:34:41 -07:00
|
|
|
g_Game->GetSimulation2()->Update(turnLength, commands);
|
2011-11-29 12:32:43 -08:00
|
|
|
commands.clear();
|
|
|
|
|
}
|
2010-08-06 15:16:05 -07:00
|
|
|
|
2010-09-17 10:45:50 -07:00
|
|
|
g_Profiler.Frame();
|
|
|
|
|
|
2018-03-22 06:53:04 -07:00
|
|
|
if (turn % PROFILE_TURN_INTERVAL == 0)
|
2010-09-17 10:45:50 -07:00
|
|
|
g_ProfileViewer.SaveToFile();
|
2010-08-06 15:16:05 -07:00
|
|
|
}
|
|
|
|
|
else
|
2025-04-10 21:24:32 -07:00
|
|
|
debug_printf("Unrecognized replay token %s\n", type.c_str());
|
2010-08-06 15:16:05 -07:00
|
|
|
}
|
2015-05-06 13:28:28 -07:00
|
|
|
}
|
2010-09-03 02:55:14 -07:00
|
|
|
|
2015-07-30 09:43:22 -07:00
|
|
|
SAFE_DELETE(m_Stream);
|
|
|
|
|
|
2011-11-29 12:32:43 -08:00
|
|
|
g_Profiler2.SaveToFile();
|
|
|
|
|
|
2010-09-03 02:55:14 -07:00
|
|
|
std::string hash;
|
2015-05-05 15:34:41 -07:00
|
|
|
bool ok = g_Game->GetSimulation2()->ComputeStateHash(hash, false);
|
2011-04-30 06:01:45 -07:00
|
|
|
ENSURE(ok);
|
2015-02-13 17:45:13 -08:00
|
|
|
debug_printf("# Final state: %s\n", Hexify(hash).c_str());
|
2011-03-02 16:16:14 -08:00
|
|
|
timer_DisplayClientTotals();
|
|
|
|
|
|
2015-05-06 13:28:28 -07:00
|
|
|
SAFE_DELETE(g_Game);
|
|
|
|
|
|
2014-03-28 13:26:32 -07:00
|
|
|
// Must be explicitly destructed here to avoid callbacks from the JSAPI trying to use g_Profiler2 when
|
|
|
|
|
// it's already destructed.
|
2020-11-14 02:57:50 -08:00
|
|
|
g_ScriptContext.reset();
|
2016-01-22 17:02:57 -08:00
|
|
|
|
2010-09-17 10:45:50 -07:00
|
|
|
delete &g_Profiler;
|
|
|
|
|
delete &g_ProfileViewer;
|
2015-05-05 15:34:41 -07:00
|
|
|
SAFE_DELETE(g_ScriptStatsTable);
|
2010-08-06 15:16:05 -07:00
|
|
|
}
|
2018-05-28 19:14:38 -07:00
|
|
|
|
|
|
|
|
void CReplayPlayer::TestHash(const std::string& hashType, const std::string& replayHash, const bool testHashFull, const bool testHashQuick)
|
|
|
|
|
{
|
|
|
|
|
bool quick = (hashType == "hash-quick");
|
|
|
|
|
if ((quick && !testHashQuick) || (!quick && !testHashFull))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
std::string hash;
|
|
|
|
|
ENSURE(g_Game->GetSimulation2()->ComputeStateHash(hash, quick));
|
|
|
|
|
|
|
|
|
|
std::string hexHash = Hexify(hash);
|
|
|
|
|
|
|
|
|
|
if (hexHash == replayHash)
|
|
|
|
|
debug_printf("%s ok (%s)\n", hashType.c_str(), hexHash.c_str());
|
|
|
|
|
else
|
|
|
|
|
debug_printf("%s MISMATCH (%s != %s)\n", hashType.c_str(), hexHash.c_str(), replayHash.c_str());
|
|
|
|
|
}
|