1
0
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:
Ted John
2020-02-22 22:18:22 +00:00
parent 639faa2085
commit dc64d3541d
10 changed files with 155 additions and 18 deletions

View File

@@ -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)

View File

@@ -810,7 +810,11 @@ namespace OpenRCT2
}
network_begin_server(gNetworkStartPort, gNetworkStartAddress);
}
else
#endif // DISABLE_NETWORK
{
game_load_scripts();
}
break;
}
case STARTUP_ACTION_EDIT:

View File

@@ -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();
}
}

View File

@@ -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();

View File

@@ -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;

View File

@@ -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
};

View File

@@ -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.");
}

View File

@@ -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);
};

View File

@@ -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);

View File

@@ -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();