mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-06 06:32:56 +01:00
Transfer server-client scripts over network
This commit is contained in:
@@ -123,6 +123,7 @@ rct_window* window_title_menu_open()
|
||||
static void window_title_menu_scenarioselect_callback(const utf8* path)
|
||||
{
|
||||
context_load_park_from_file(path);
|
||||
game_load_scripts();
|
||||
}
|
||||
|
||||
static void window_title_menu_mouseup(rct_window* w, rct_widgetindex widgetIndex)
|
||||
|
||||
@@ -810,7 +810,11 @@ namespace OpenRCT2
|
||||
}
|
||||
network_begin_server(gNetworkStartPort, gNetworkStartAddress);
|
||||
}
|
||||
else
|
||||
#endif // DISABLE_NETWORK
|
||||
{
|
||||
game_load_scripts();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STARTUP_ACTION_EDIT:
|
||||
|
||||
@@ -588,7 +588,10 @@ void game_load_init()
|
||||
|
||||
audio_stop_title_music();
|
||||
gGameSpeed = 1;
|
||||
}
|
||||
|
||||
void game_load_scripts()
|
||||
{
|
||||
GetContext()->GetScriptEngine().LoadPlugins();
|
||||
}
|
||||
|
||||
@@ -812,6 +815,7 @@ static void game_load_or_quit_no_save_prompt_callback(int32_t result, const utf8
|
||||
game_finish();
|
||||
window_close_by_class(WC_EDITOR_OBJECT_SELECTION);
|
||||
context_load_park_from_file(path);
|
||||
game_load_scripts();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -156,6 +156,7 @@ void update_palette_effects();
|
||||
void game_load_or_quit_no_save_prompt();
|
||||
void load_from_sv6(const char* path);
|
||||
void game_load_init();
|
||||
void game_load_scripts();
|
||||
void game_finish();
|
||||
void pause_toggle();
|
||||
bool game_is_paused();
|
||||
|
||||
@@ -194,6 +194,7 @@ public:
|
||||
void Client_Send_GAMEINFO();
|
||||
void Client_Send_OBJECTS(const std::vector<std::string>& objects);
|
||||
void Server_Send_OBJECTS(NetworkConnection& connection, const std::vector<const ObjectRepositoryItem*>& objects) const;
|
||||
void Server_Send_SCRIPTS(NetworkConnection& connection) const;
|
||||
|
||||
NetworkStats_t GetStats() const;
|
||||
json_t* GetServerInfoAsJson() const;
|
||||
@@ -309,6 +310,7 @@ private:
|
||||
void Client_Handle_TOKEN(NetworkConnection& connection, NetworkPacket& packet);
|
||||
void Server_Handle_TOKEN(NetworkConnection& connection, NetworkPacket& packet);
|
||||
void Client_Handle_OBJECTS(NetworkConnection& connection, NetworkPacket& packet);
|
||||
void Client_Handle_SCRIPTS(NetworkConnection& connection, NetworkPacket& packet);
|
||||
void Client_Handle_GAMESTATE(NetworkConnection& connection, NetworkPacket& packet);
|
||||
void Server_Handle_OBJECTS(NetworkConnection& connection, NetworkPacket& packet);
|
||||
|
||||
@@ -344,6 +346,7 @@ Network::Network()
|
||||
client_command_handlers[NETWORK_COMMAND_GAMEINFO] = &Network::Client_Handle_GAMEINFO;
|
||||
client_command_handlers[NETWORK_COMMAND_TOKEN] = &Network::Client_Handle_TOKEN;
|
||||
client_command_handlers[NETWORK_COMMAND_OBJECTS] = &Network::Client_Handle_OBJECTS;
|
||||
client_command_handlers[NETWORK_COMMAND_SCRIPTS] = &Network::Client_Handle_SCRIPTS;
|
||||
client_command_handlers[NETWORK_COMMAND_GAMESTATE] = &Network::Client_Handle_GAMESTATE;
|
||||
server_command_handlers.resize(NETWORK_COMMAND_MAX, nullptr);
|
||||
server_command_handlers[NETWORK_COMMAND_AUTH] = &Network::Server_Handle_AUTH;
|
||||
@@ -624,6 +627,8 @@ bool Network::BeginServer(uint16_t port, const std::string& address)
|
||||
_serverState.gamestateSnapshotsEnabled = gConfigNetwork.desync_debugging;
|
||||
_advertiser = CreateServerAdvertiser(listening_port);
|
||||
|
||||
game_load_scripts();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1465,6 +1470,37 @@ void Network::Server_Send_OBJECTS(NetworkConnection& connection, const std::vect
|
||||
connection.QueuePacket(std::move(packet));
|
||||
}
|
||||
|
||||
void Network::Server_Send_SCRIPTS(NetworkConnection& connection) const
|
||||
{
|
||||
using namespace OpenRCT2::Scripting;
|
||||
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
const auto& plugins = scriptEngine.GetPlugins();
|
||||
std::vector<std::shared_ptr<Plugin>> pluginsToSend;
|
||||
for (const auto& plugin : plugins)
|
||||
{
|
||||
const auto& metadata = plugin->GetMetadata();
|
||||
if (metadata.Type == OpenRCT2::Scripting::PluginType::ServerClient)
|
||||
{
|
||||
pluginsToSend.push_back(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
log_verbose("Server sends %u scripts", pluginsToSend.size());
|
||||
std::unique_ptr<NetworkPacket> packet(NetworkPacket::Allocate());
|
||||
*packet << (uint32_t)NETWORK_COMMAND_SCRIPTS << (uint32_t)pluginsToSend.size();
|
||||
for (const auto& plugin : pluginsToSend)
|
||||
{
|
||||
const auto& metadata = plugin->GetMetadata();
|
||||
log_verbose("Script %s", metadata.Name.c_str());
|
||||
|
||||
const auto& code = plugin->GetCode();
|
||||
*packet << (uint32_t)code.size();
|
||||
packet->Write((const uint8_t*)code.c_str(), code.size());
|
||||
}
|
||||
connection.QueuePacket(std::move(packet));
|
||||
}
|
||||
|
||||
NetworkStats_t Network::GetStats() const
|
||||
{
|
||||
NetworkStats_t stats = {};
|
||||
@@ -2339,6 +2375,7 @@ void Network::Server_Client_Joined(const char* name, const std::string& keyhash,
|
||||
auto& objManager = context->GetObjectManager();
|
||||
auto objects = objManager.GetPackableObjects();
|
||||
Server_Send_OBJECTS(connection, objects);
|
||||
Server_Send_SCRIPTS(connection);
|
||||
|
||||
// Log player joining event
|
||||
std::string playerNameHash = player->Name + " (" + keyhash + ")";
|
||||
@@ -2398,6 +2435,21 @@ void Network::Client_Handle_OBJECTS(NetworkConnection& connection, NetworkPacket
|
||||
Client_Send_OBJECTS(requested_objects);
|
||||
}
|
||||
|
||||
void Network::Client_Handle_SCRIPTS(NetworkConnection& connection, NetworkPacket& packet)
|
||||
{
|
||||
auto& scriptEngine = GetContext()->GetScriptEngine();
|
||||
|
||||
uint32_t numScripts{};
|
||||
packet >> numScripts;
|
||||
for (uint32_t i = 0; i < numScripts; i++)
|
||||
{
|
||||
uint32_t codeLength{};
|
||||
packet >> codeLength;
|
||||
auto code = std::string_view((const char*)packet.Read(codeLength), codeLength);
|
||||
scriptEngine.AddNetworkPlugin(code);
|
||||
}
|
||||
}
|
||||
|
||||
void Network::Client_Handle_GAMESTATE(NetworkConnection& connection, NetworkPacket& packet)
|
||||
{
|
||||
uint32_t tick;
|
||||
@@ -2683,6 +2735,7 @@ void Network::Client_Handle_MAP([[maybe_unused]] NetworkConnection& connection,
|
||||
if (LoadMap(&ms))
|
||||
{
|
||||
game_load_init();
|
||||
game_load_scripts();
|
||||
_serverState.tick = gCurrentTicks;
|
||||
// window_network_status_open("Loaded new map from network");
|
||||
_serverState.state = NETWORK_SERVER_STATE_OK;
|
||||
|
||||
@@ -68,6 +68,7 @@ enum NETWORK_COMMAND
|
||||
NETWORK_COMMAND_PLAYERINFO,
|
||||
NETWORK_COMMAND_REQUEST_GAMESTATE,
|
||||
NETWORK_COMMAND_GAMESTATE,
|
||||
NETWORK_COMMAND_SCRIPTS,
|
||||
NETWORK_COMMAND_MAX,
|
||||
NETWORK_COMMAND_INVALID = -1
|
||||
};
|
||||
|
||||
@@ -24,28 +24,29 @@ Plugin::Plugin(duk_context* context, const std::string& path)
|
||||
{
|
||||
}
|
||||
|
||||
void Plugin::SetCode(const std::string_view& code)
|
||||
{
|
||||
_code = code;
|
||||
}
|
||||
|
||||
void Plugin::Load()
|
||||
{
|
||||
if (!_path.empty())
|
||||
{
|
||||
LoadCodeFromFile();
|
||||
}
|
||||
|
||||
std::string projectedVariables = "console,context,date,map,network,park";
|
||||
if (!gOpenRCT2Headless)
|
||||
{
|
||||
projectedVariables += ",ui";
|
||||
}
|
||||
std::string code;
|
||||
{
|
||||
std::ifstream fs(_path);
|
||||
if (fs.is_open())
|
||||
{
|
||||
fs.seekg(0, std::ios::end);
|
||||
code.reserve(fs.tellg());
|
||||
fs.seekg(0, std::ios::beg);
|
||||
code.assign(std::istreambuf_iterator<char>(fs), std::istreambuf_iterator<char>());
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap the script in a function and pass the global objects as variables
|
||||
// so that if the script modifies them, they are not modified for other scripts.
|
||||
|
||||
// clang-format off
|
||||
auto code = _code;
|
||||
code =
|
||||
" (function(" + projectedVariables + ") {"
|
||||
" var __metadata__ = null;"
|
||||
@@ -99,6 +100,20 @@ void Plugin::Update()
|
||||
{
|
||||
}
|
||||
|
||||
void Plugin::LoadCodeFromFile()
|
||||
{
|
||||
std::string code;
|
||||
std::ifstream fs(_path);
|
||||
if (fs.is_open())
|
||||
{
|
||||
fs.seekg(0, std::ios::end);
|
||||
code.reserve(fs.tellg());
|
||||
fs.seekg(0, std::ios::beg);
|
||||
code.assign(std::istreambuf_iterator<char>(fs), std::istreambuf_iterator<char>());
|
||||
}
|
||||
_code = std::move(code);
|
||||
}
|
||||
|
||||
static std::string TryGetString(const DukValue& value, const std::string& message)
|
||||
{
|
||||
if (value.type() != DukValue::Type::STRING)
|
||||
@@ -144,8 +159,8 @@ PluginType Plugin::ParsePluginType(const std::string_view& type)
|
||||
if (type == "server")
|
||||
return PluginType::Server;
|
||||
if (type == "client")
|
||||
return PluginType::Server;
|
||||
return PluginType::Client;
|
||||
if (type == "server_client")
|
||||
return PluginType::Server;
|
||||
return PluginType::ServerClient;
|
||||
throw std::invalid_argument("Unknown plugin type.");
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ namespace OpenRCT2::Scripting
|
||||
duk_context* _context{};
|
||||
std::string _path;
|
||||
PluginMetadata _metadata;
|
||||
std::string _code;
|
||||
bool _hasStarted{};
|
||||
|
||||
public:
|
||||
@@ -62,11 +63,21 @@ namespace OpenRCT2::Scripting
|
||||
return _path;
|
||||
};
|
||||
|
||||
bool HasPath() const
|
||||
{
|
||||
return _path.empty();
|
||||
}
|
||||
|
||||
const PluginMetadata& GetMetadata() const
|
||||
{
|
||||
return _metadata;
|
||||
}
|
||||
|
||||
const std::string& GetCode() const
|
||||
{
|
||||
return _code;
|
||||
}
|
||||
|
||||
bool HasStarted() const
|
||||
{
|
||||
return _hasStarted;
|
||||
@@ -79,12 +90,15 @@ namespace OpenRCT2::Scripting
|
||||
Plugin(const Plugin&) = delete;
|
||||
Plugin(Plugin&&) = delete;
|
||||
|
||||
void SetCode(const std::string_view& code);
|
||||
void Load();
|
||||
void Start();
|
||||
void Stop();
|
||||
void Update();
|
||||
|
||||
private:
|
||||
void LoadCodeFromFile();
|
||||
|
||||
static PluginMetadata GetMetadata(const DukValue& dukMetadata);
|
||||
static PluginType ParsePluginType(const std::string_view& type);
|
||||
};
|
||||
|
||||
@@ -113,7 +113,7 @@ void ScriptEngine::LoadPlugins()
|
||||
}
|
||||
}
|
||||
|
||||
if (gConfigPlugin.enable_hot_reloading)
|
||||
if (gConfigPlugin.enable_hot_reloading && network_get_mode() == NETWORK_MODE_NONE)
|
||||
{
|
||||
SetupHotReloading();
|
||||
}
|
||||
@@ -123,10 +123,15 @@ void ScriptEngine::LoadPlugins()
|
||||
}
|
||||
|
||||
void ScriptEngine::LoadPlugin(const std::string& path)
|
||||
{
|
||||
auto plugin = std::make_shared<Plugin>(_context, path);
|
||||
LoadPlugin(plugin);
|
||||
}
|
||||
|
||||
void ScriptEngine::LoadPlugin(std::shared_ptr<Plugin>& plugin)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto plugin = std::make_shared<Plugin>(_context, path);
|
||||
ScriptExecutionInfo::PluginScope scope(_execInfo, plugin);
|
||||
plugin->Load();
|
||||
|
||||
@@ -240,7 +245,7 @@ void ScriptEngine::StartPlugins()
|
||||
{
|
||||
for (auto& plugin : _plugins)
|
||||
{
|
||||
if (!plugin->HasStarted())
|
||||
if (!plugin->HasStarted() && ShouldStartPlugin(plugin))
|
||||
{
|
||||
ScriptExecutionInfo::PluginScope scope(_execInfo, plugin);
|
||||
try
|
||||
@@ -257,12 +262,36 @@ void ScriptEngine::StartPlugins()
|
||||
_pluginsStarted = true;
|
||||
}
|
||||
|
||||
bool ScriptEngine::ShouldStartPlugin(const std::shared_ptr<Plugin>& plugin)
|
||||
{
|
||||
auto networkMode = network_get_mode();
|
||||
if (networkMode == NETWORK_MODE_CLIENT)
|
||||
{
|
||||
// Only client plugins and plugins downloaded from server should be started
|
||||
const auto& metadata = plugin->GetMetadata();
|
||||
if (metadata.Type == PluginType::Server)
|
||||
{
|
||||
LogPluginInfo(plugin, "Server plugin not started");
|
||||
return false;
|
||||
}
|
||||
else if (metadata.Type == PluginType::ServerClient && plugin->HasPath())
|
||||
{
|
||||
LogPluginInfo(plugin, "Server / client plugin not started");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptEngine::StopPlugins()
|
||||
{
|
||||
for (auto& plugin : _plugins)
|
||||
{
|
||||
StopPlugin(plugin);
|
||||
LogPluginInfo(plugin, "Stopped");
|
||||
if (plugin->HasStarted())
|
||||
{
|
||||
StopPlugin(plugin);
|
||||
LogPluginInfo(plugin, "Stopped");
|
||||
}
|
||||
}
|
||||
_pluginsStarted = false;
|
||||
}
|
||||
@@ -332,6 +361,13 @@ void ScriptEngine::LogPluginInfo(const std::shared_ptr<Plugin>& plugin, const st
|
||||
_console.WriteLine("[" + pluginName + "] " + std::string(message));
|
||||
}
|
||||
|
||||
void ScriptEngine::AddNetworkPlugin(const std::string_view& code)
|
||||
{
|
||||
auto plugin = std::make_shared<Plugin>(_context, std::string());
|
||||
plugin->SetCode(code);
|
||||
LoadPlugin(plugin);
|
||||
}
|
||||
|
||||
static std::string Stringify(duk_context* ctx, duk_idx_t idx)
|
||||
{
|
||||
auto type = duk_get_type(ctx, idx);
|
||||
|
||||
@@ -123,6 +123,10 @@ namespace OpenRCT2::Scripting
|
||||
{
|
||||
return _execInfo;
|
||||
}
|
||||
std::vector<std::shared_ptr<Plugin>>& GetPlugins()
|
||||
{
|
||||
return _plugins;
|
||||
}
|
||||
|
||||
void LoadPlugins();
|
||||
void UnloadPlugins();
|
||||
@@ -136,13 +140,17 @@ namespace OpenRCT2::Scripting
|
||||
_pluginStoppedSubscriptions.push_back(callback);
|
||||
}
|
||||
|
||||
void AddNetworkPlugin(const std::string_view& code);
|
||||
|
||||
private:
|
||||
void Initialise();
|
||||
void StartPlugins();
|
||||
void StopPlugins();
|
||||
void LoadPlugin(const std::string& path);
|
||||
void LoadPlugin(std::shared_ptr<Plugin>& plugin);
|
||||
void StopPlugin(std::shared_ptr<Plugin> plugin);
|
||||
bool ShouldLoadScript(const std::string& path);
|
||||
bool ShouldStartPlugin(const std::shared_ptr<Plugin> &plugin);
|
||||
void SetupHotReloading();
|
||||
void AutoReloadPlugins();
|
||||
void ProcessREPL();
|
||||
|
||||
Reference in New Issue
Block a user