Replace ScriptException::Raise in Engine functions

Now exceptions can be thrown. The function throwing exceptions becomes
cleaner and doesn't require a `ScriptRequest` anymore.
This commit is contained in:
phosit
2025-06-18 08:16:42 +02:00
committed by Phosit
parent 616fa4a006
commit 9822a22573
9 changed files with 87 additions and 106 deletions
+5 -6
View File
@@ -42,6 +42,7 @@
#include <boost/random/linear_congruential.hpp>
#include <set>
#include <stdexcept>
#include <string>
#include <vector>
@@ -198,9 +199,8 @@ private:
if (!VfsFileExists(filename))
{
ScriptException::Raise(rq, "Terrain file \"%s\" does not exist!",
filename.string8().c_str());
return JS::UndefinedValue();
throw std::runtime_error{fmt::format("Terrain file \"{}\" does not exist!",
filename.string8().c_str())};
}
CFileUnpacker unpacker;
@@ -208,9 +208,8 @@ private:
if (unpacker.GetVersion() < CMapIO::FILE_READ_VERSION)
{
ScriptException::Raise(rq, "Could not load terrain file \"%s\" too old version!",
filename.string8().c_str());
return JS::UndefinedValue();
throw std::runtime_error{fmt::format(
"Could not load terrain file \"{}\" too old version!", filename.string8().c_str())};
}
// unpack size
+4 -10
View File
@@ -535,22 +535,16 @@ void CGUI::SetGlobalHotkey(const CStr& hotkeyTag, const CStr& eventName, JS::Han
ScriptRequest rq(*m_ScriptInterface);
if (hotkeyTag.empty())
{
ScriptException::Raise(rq, "Cannot assign a function to an empty hotkey identifier!");
return;
}
throw std::invalid_argument{"Cannot assign a function to an empty hotkey identifier!"};
// Only support "Press", "Keydown" and "Release" events.
if (eventName != EventNamePress && eventName != EventNameKeyDown && eventName != EventNameRelease)
{
ScriptException::Raise(rq, "Cannot assign a function to an unsupported event!");
return;
}
throw std::invalid_argument{"Cannot assign a function to an unsupported event!"};
if (!function.isObject() || !JS::IsCallable(&function.toObject()))
{
ScriptException::Raise(rq, "Cannot assign non-function value to global hotkey '%s'", hotkeyTag.c_str());
return;
throw std::invalid_argument{fmt::format(
"Cannot assign non-function value to global hotkey '{}'", hotkeyTag.c_str())};
}
UnsetGlobalHotkey(hotkeyTag, eventName);
@@ -32,6 +32,8 @@
#include <js/Value.h>
#include <string>
#include <stdexcept>
namespace JSI_GUIManager
{
// Note that the initData argument may only contain clonable data.
+10 -18
View File
@@ -41,6 +41,7 @@
#include <js/Value.h>
#include <sodium/core.h>
#include <sodium/crypto_hash_sha256.h>
#include <stdexcept>
#include <string>
class ScriptInterface;
@@ -60,13 +61,11 @@ void SetRankedGame(bool isRanked)
#if CONFIG2_LOBBY
void StartXmppClient(const ScriptRequest& rq, const std::wstring& username, const std::wstring& password, const std::wstring& room, const std::wstring& nick, int historyRequestSize)
void StartXmppClient(const std::wstring& username, const std::wstring& password, const std::wstring& room,
const std::wstring& nick, int historyRequestSize)
{
if (g_XmppClient)
{
ScriptException::Raise(rq, "Cannot call StartXmppClient with an already initialized XmppClient!");
return;
}
throw std::logic_error{"Cannot call StartXmppClient with an already initialized XmppClient!"};
g_XmppClient =
IXmppClient::create(
@@ -80,12 +79,12 @@ void StartXmppClient(const ScriptRequest& rq, const std::wstring& username, cons
g_rankedGame = true;
}
void StartRegisterXmppClient(const ScriptRequest& rq, const std::wstring& username, const std::wstring& password)
void StartRegisterXmppClient(const std::wstring& username, const std::wstring& password)
{
if (g_XmppClient)
{
ScriptException::Raise(rq, "Cannot call StartRegisterXmppClient with an already initialized XmppClient!");
return;
throw std::logic_error{
"Cannot call StartRegisterXmppClient with an already initialized XmppClient!"};
}
g_XmppClient =
@@ -99,13 +98,10 @@ void StartRegisterXmppClient(const ScriptRequest& rq, const std::wstring& userna
true);
}
void StopXmppClient(const ScriptRequest& rq)
void StopXmppClient()
{
if (!g_XmppClient)
{
ScriptException::Raise(rq, "Cannot call StopXmppClient without an initialized XmppClient!");
return;
}
throw std::logic_error{"Cannot call StopXmppClient without an initialized XmppClient!"};
SAFE_DELETE(g_XmppClient);
g_rankedGame = false;
@@ -127,11 +123,7 @@ IXmppClient* XmppGetter(const ScriptRequest&, JS::CallArgs&)
void SendRegisterGame(const ScriptInterface& scriptInterface, JS::HandleValue data)
{
if (!g_XmppClient)
{
ScriptRequest rq(scriptInterface);
ScriptException::Raise(rq, "Cannot call SendRegisterGame without an initialized XmppClient!");
return;
}
throw std::logic_error{"Cannot call SendRegisterGame without an initialized XmppClient!"};
// Prevent JS mods to register matches in the lobby that were started with lobby authentication disabled
if (!g_NetServer || !g_NetServer->UseLobbyAuth())
@@ -42,6 +42,7 @@
#include "third_party/encryption/pkcs5_pbkdf2.h"
#include <optional>
#include <stdexcept>
namespace JSI_Network
{
@@ -65,8 +66,8 @@ bool HasNetClient()
return !!g_NetClient;
}
void StartNetworkHost(const ScriptRequest& rq, const CStrW& playerName, const u16 serverPort,
const CStr& password, const bool continueSavedGame, bool storeReplay)
void StartNetworkHost(const CStrW& playerName, const u16 serverPort, const CStr& password,
const bool continueSavedGame, bool storeReplay)
{
ENSURE(!g_NetClient);
ENSURE(!g_NetServer);
@@ -78,18 +79,16 @@ void StartNetworkHost(const ScriptRequest& rq, const CStrW& playerName, const u1
if (!g_NetServer->SetupConnection(serverPort))
{
ScriptException::Raise(rq, "Failed to start server");
SAFE_DELETE(g_NetServer);
return;
throw std::runtime_error{"Failed to start server"};
}
// In lobby, we send our public ip and port on request to the players who want to connect.
// Thus we need to know our public IP and use STUN to get it.
if (hasLobby && !g_NetServer->SetConnectionData())
{
ScriptException::Raise(rq, "Failed to resolve public IP-address.");
SAFE_DELETE(g_NetServer);
return;
throw std::runtime_error{"Failed to resolve public IP-address."};
}
// Generate a secret to identify the host client.
@@ -131,13 +130,13 @@ void StartNetworkHost(const ScriptRequest& rq, const CStrW& playerName, const u1
if (!g_NetClient->SetupConnection(nullptr))
{
ScriptException::Raise(rq, "Failed to connect to server");
SAFE_DELETE(g_NetClient);
SAFE_DELETE(g_Game);
throw std::runtime_error{"Failed to connect to server"};
}
}
void StartNetworkJoin(const ScriptRequest& rq, const CStrW& playerName, const CStr& serverAddress, u16 serverPort, bool storeReplay)
void StartNetworkJoin(const CStrW& playerName, const CStr& serverAddress, u16 serverPort, bool storeReplay)
{
ENSURE(!g_NetClient);
ENSURE(!g_NetServer);
@@ -150,9 +149,9 @@ void StartNetworkJoin(const ScriptRequest& rq, const CStrW& playerName, const CS
if (!g_NetClient->SetupConnection(nullptr))
{
ScriptException::Raise(rq, "Failed to connect to server");
SAFE_DELETE(g_NetClient);
SAFE_DELETE(g_Game);
throw std::runtime_error{"Failed to connect to server"};
}
}
@@ -224,13 +223,10 @@ void AssignNetworkPlayer(int playerID, const CStr& guid)
g_NetClient->SendAssignPlayerMessage(playerID, guid);
}
void KickPlayer(const ScriptRequest& rq, const CStrW& playerName, bool ban)
void KickPlayer(const CStrW& playerName, bool ban)
{
if (!g_NetClient)
{
ScriptException::Raise(rq, "g_NetClient is null.");
return;
}
throw std::logic_error{"g_NetClient is null."};
g_NetClient->SendKickPlayerMessage(playerName, ban);
}
@@ -247,8 +243,8 @@ void SendNetworkChat(const ScriptRequest& rq, const CStrW& message, JS::HandleVa
auto receivers = std::make_optional<std::vector<std::string>>();
if (!Script::FromJSVal(rq, handle, *receivers))
{
ScriptException::Raise(rq, "The second argument to `SendNetworkChat` has to be either an Array "
"or a nullish value.");
throw std::invalid_argument{"The second argument to `SendNetworkChat` has to be either an "
"Array or a nullish value."};
return;
}
@@ -291,7 +287,10 @@ void StartNetworkGame(const ScriptInterface& scriptInterface, JS::HandleValue sa
if (loadResult)
g_NetClient->SendStartSavedGameMessage(attributesAsString, loadResult->savedState);
else
ScriptException::Raise(rq, "Failed to load the saved game: \"%ls\"", savegameID.c_str());
{
throw std::runtime_error{fmt::format("Failed to load the saved game: \"{}\"",
utf8_from_wstring(savegameID).c_str())};
}
}
void SetTurnLength(int length)
+10 -14
View File
@@ -34,6 +34,8 @@
#include "simulation2/Simulation2.h"
#include "soundmanager/SoundManager.h"
#include <stdexcept>
namespace JSI_Game
{
void StartGame(const ScriptInterface& guiInterface, JS::HandleValue attribs, int playerID, bool storeReplay)
@@ -67,7 +69,7 @@ int GetPlayerID()
return g_Game->GetPlayerID();
}
void SetPlayerID(const ScriptRequest& rq, int id)
void SetPlayerID(int id)
{
if (!g_Game)
return;
@@ -79,10 +81,10 @@ void SetPlayerID(const ScriptRequest& rq, int id)
if (g_Game->CheatsEnabled() || g_Game->IsVisualReplay())
g_Game->SetPlayerID(id);
else
ScriptException::Raise(rq, "Changing player ID with cheats disabled is prohibited");
throw std::logic_error{"Changing player ID with cheats disabled is prohibited"};
}
void SetViewedPlayer(const ScriptRequest& rq, int id)
void SetViewedPlayer(int id)
{
if (!g_Game || g_Game->GetViewedPlayerID() == id)
return;
@@ -92,7 +94,7 @@ void SetViewedPlayer(const ScriptRequest& rq, int id)
if (playerID == -1 || g_Game->CheatsEnabled() || g_Game->PlayerFinished(playerID) || g_Game->IsVisualReplay())
g_Game->SetViewedPlayerID(id);
else
ScriptException::Raise(rq, "Changing the perspective with cheats disabled is prohibited");
std::logic_error{"Changing the perspective with cheats disabled is prohibited"};
}
float GetSimRate()
@@ -113,24 +115,18 @@ int GetPendingTurns()
return g_Game->GetTurnManager()->GetPendingTurns();
}
bool IsPaused(const ScriptRequest& rq)
bool IsPaused()
{
if (!g_Game)
{
ScriptException::Raise(rq, "Game is not started");
return false;
}
throw std::logic_error{"Game is not started"};
return g_Game->m_Paused;
}
void SetPaused(const ScriptRequest& rq, bool pause, bool sendMessage)
void SetPaused(bool pause, bool sendMessage)
{
if (!g_Game)
{
ScriptException::Raise(rq, "Game is not started");
return;
}
throw std::logic_error{"Game is not started"};
g_Game->m_Paused = pause;
+5 -3
View File
@@ -1,4 +1,4 @@
/* Copyright (C) 2023 Wildfire Games.
/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -26,6 +26,8 @@
#include "scriptinterface/Object.h"
#include "scriptinterface/ScriptConversions.h"
#include <stdexcept>
extern void RestartEngine();
// To avoid copying data needlessly in GetEngineInfo, implement a ToJSVal for pointer types.
@@ -137,8 +139,8 @@ JS::Value GetAvailableMods(const ScriptRequest& rq)
JS::RootedValue json(rq.cx);
if (!Script::ParseJSON(rq, data.m_Text, &json))
{
ScriptException::Raise(rq, "Error parsing mod.json of '%s'", data.m_Pathname.c_str());
continue;
throw std::runtime_error{fmt::format("Error parsing mod.json of '{}'",
data.m_Pathname.c_str())};
}
Script::SetProperty(rq, ret, data.m_Pathname.c_str(), json);
}
+12 -9
View File
@@ -29,6 +29,7 @@
#include <algorithm>
#include <sstream>
#include <stdexcept>
namespace JSI_VFS
{
@@ -61,7 +62,7 @@ constexpr std::array<std::wstring_view, 2> MAPS{L"simulation/"sv, L"maps/"sv};
// Tests whether the current script context is allowed to read from the given directory
template<auto& restriction>
bool PathRestrictionMet(const ScriptRequest& rq, const std::wstring& filePath)
bool PathRestrictionMet(const std::wstring& filePath)
{
if (std::any_of(restriction.begin(), restriction.end(), [&](const std::wstring_view allowedPath)
{
@@ -80,7 +81,9 @@ bool PathRestrictionMet(const ScriptRequest& rq, const std::wstring& filePath)
allowedPaths += L"\"" + static_cast<std::wstring>(restriction[i]) + L"\"";
}
ScriptException::Raise(rq, "Restricted access to %s. This part of the engine may only read from %s!", utf8_from_wstring(filePath).c_str(), utf8_from_wstring(allowedPaths).c_str());
throw std::logic_error{fmt::format(
"Restricted access to {}. This part of the engine may only read from {}!",
utf8_from_wstring(filePath).c_str(), utf8_from_wstring(allowedPaths).c_str())};
return false;
}
@@ -123,7 +126,7 @@ template<auto& restriction>
JS::Value BuildDirEntList(const ScriptRequest& rq, const std::wstring& path, const std::wstring& filterStr,
bool recurse)
{
if (!PathRestrictionMet<restriction>(rq, path))
if (!PathRestrictionMet<restriction>(path))
return JS::NullValue();
// convert to const wchar_t*; if there's no filter, pass 0 for speed
@@ -143,9 +146,9 @@ JS::Value BuildDirEntList(const ScriptRequest& rq, const std::wstring& path, con
// Return true iff the file exits
template<auto& restriction>
bool FileExists(const ScriptRequest& rq, const std::wstring& filename)
bool FileExists(const std::wstring& filename)
{
return PathRestrictionMet<restriction>(rq, filename) && g_VFS->GetFileInfo(filename, 0) == INFO::OK;
return PathRestrictionMet<restriction>(filename) && g_VFS->GetFileInfo(filename, 0) == INFO::OK;
}
// Return current size of file.
@@ -162,7 +165,7 @@ unsigned int GetFileSize(const std::wstring& filename)
template<auto& restriction>
JS::Value ReadFile(const ScriptRequest& rq, const std::wstring& filename)
{
if (!PathRestrictionMet<restriction>(rq, filename))
if (!PathRestrictionMet<restriction>(filename))
return JS::NullValue();
CVFSFile file;
@@ -184,7 +187,7 @@ JS::Value ReadFile(const ScriptRequest& rq, const std::wstring& filename)
template<auto& restriction>
JS::Value ReadFileLines(const ScriptRequest& rq, const std::wstring& filename)
{
if (!PathRestrictionMet<restriction>(rq, filename))
if (!PathRestrictionMet<restriction>(filename))
return JS::NullValue();
CVFSFile file;
@@ -221,7 +224,7 @@ template<auto& restriction>
JS::Value ReadJSONFile(const ScriptInterface& scriptInterface, const std::wstring& filePath)
{
ScriptRequest rq(scriptInterface);
if (!PathRestrictionMet<restriction>(rq, filePath))
if (!PathRestrictionMet<restriction>(filePath))
return JS::NullValue();
JS::RootedValue out(rq.cx);
@@ -235,7 +238,7 @@ void WriteJSONFile(const ScriptInterface& scriptInterface, const std::wstring& f
JS::HandleValue val1)
{
ScriptRequest rq(scriptInterface);
if (!PathRestrictionMet<restriction>(rq, filePath))
if (!PathRestrictionMet<restriction>(filePath))
return;
// TODO: This is a workaround because we need to pass a MutableHandle to StringifyJSON.
+23 -29
View File
@@ -34,6 +34,7 @@
#include "simulation2/system/ParamNode.h"
#include "simulation2/system/SimContext.h"
#include <stdexcept>
#include <string_view>
/**
@@ -168,10 +169,8 @@ void CComponentManager::Script_RegisterComponentType_Common(int iid, const std::
// Find the C++ component that wraps the interface
int cidWrapper = GetScriptWrapper(iid);
if (cidWrapper == CID__Invalid)
{
ScriptException::Raise(rq, "Invalid interface id");
return;
}
throw std::invalid_argument{"Invalid interface id"};
const ComponentType& ctWrapper = m_ComponentTypesById[cidWrapper];
bool mustReloadComponents = false; // for hotloading
@@ -181,8 +180,8 @@ void CComponentManager::Script_RegisterComponentType_Common(int iid, const std::
{
if (reRegister)
{
ScriptException::Raise(rq, "ReRegistering component type that was not registered before '%s'", cname.c_str());
return;
throw std::logic_error{fmt::format(
"ReRegistering component type that was not registered before '{}'", cname.c_str())};
}
// Allocate a new cid number
cid = m_NextScriptComponentTypeId++;
@@ -196,8 +195,8 @@ void CComponentManager::Script_RegisterComponentType_Common(int iid, const std::
if (!m_CurrentlyHotloading && !reRegister)
{
ScriptException::Raise(rq, "Registering component type with already-registered name '%s'", cname.c_str());
return;
throw std::logic_error{fmt::format(
"Registering component type with already-registered name '{}'", cname.c_str())};
}
const ComponentType& ctPrevious = m_ComponentTypesById[cid];
@@ -205,8 +204,9 @@ void CComponentManager::Script_RegisterComponentType_Common(int iid, const std::
// We can only replace scripted component types, not native ones
if (ctPrevious.type != CT_Script)
{
ScriptException::Raise(rq, "Loading script component type with same name '%s' as native component", cname.c_str());
return;
throw std::logic_error{fmt::format(
"Loading script component type with same name '%s' as native component",
cname.c_str())};
}
// We don't support changing the IID of a component type (it would require fiddling
@@ -216,8 +216,8 @@ void CComponentManager::Script_RegisterComponentType_Common(int iid, const std::
// ...though it only matters if any components exist with this type
if (!m_ComponentsByTypeId[cid].empty())
{
ScriptException::Raise(rq, "Hotloading script component type mustn't change interface ID");
return;
throw std::logic_error{
"Hotloading script component type mustn't change interface ID"};
}
}
@@ -243,15 +243,10 @@ void CComponentManager::Script_RegisterComponentType_Common(int iid, const std::
JS::RootedValue protoVal(rq.cx);
if (!Script::GetProperty(rq, ctor, "prototype", &protoVal))
{
ScriptException::Raise(rq, "Failed to get property 'prototype'");
return;
}
throw std::runtime_error("Failed to get property 'prototype'");
if (!protoVal.isObject())
{
ScriptException::Raise(rq, "Component has no constructor");
return;
}
throw std::invalid_argument{"Component has no constructor"};
std::string schema = "<empty/>";
if (Script::HasProperty(rq, protoVal, "Schema"))
@@ -279,7 +274,7 @@ void CComponentManager::Script_RegisterComponentType_Common(int iid, const std::
if (!Script::EnumeratePropertyNames(rq, protoVal, false, methods))
{
ScriptException::Raise(rq, "Failed to enumerate component properties.");
throw std::runtime_error{"Failed to enumerate component properties."};
return;
}
@@ -301,10 +296,9 @@ void CComponentManager::Script_RegisterComponentType_Common(int iid, const std::
auto mit = m_MessageTypeIdsByName.find(std::string{name});
if (mit == m_MessageTypeIdsByName.end())
{
ScriptException::Raise(rq,
"Registered component has unrecognized '%s' message handler method",
method.c_str());
return;
throw std::invalid_argument{fmt::format(
"Registered component has unrecognized '{}' message handler method",
method.c_str())};
}
// If we have already subscribed in classInit, do not subscribe again
@@ -363,8 +357,8 @@ void CComponentManager::Script_RegisterInterface(const std::string& name)
// they're probably unintentional and should be reported
if (!m_CurrentlyHotloading)
{
ScriptRequest rq(m_ScriptInterface);
ScriptException::Raise(rq, "Registering interface with already-registered name '%s'", name.c_str());
throw std::logic_error{fmt::format(
"Registering interface with already-registered name '{}'", name.c_str())};
}
return;
}
@@ -385,8 +379,8 @@ void CComponentManager::Script_RegisterMessageType(const std::string& name)
// they're probably unintentional and should be reported
if (!m_CurrentlyHotloading)
{
ScriptRequest rq(m_ScriptInterface);
ScriptException::Raise(rq, "Registering message type with already-registered name '%s'", name.c_str());
throw std::logic_error{fmt::format(
"Registering message type with already-registered name '{}'", name.c_str())};
}
return;
}