From 583dffcad4bf8edf8303dfac1668a517d92dd3e7 Mon Sep 17 00:00:00 2001 From: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com> Date: Tue, 17 Aug 2021 11:57:08 +0100 Subject: [PATCH 01/19] Multi instance support --- .../configuration/configuration_core.dm | 101 ++++++++++++++---- code/game/world.dm | 5 +- 2 files changed, 85 insertions(+), 21 deletions(-) diff --git a/code/controllers/configuration/configuration_core.dm b/code/controllers/configuration/configuration_core.dm index c4938d3efc6..c8b490e26cc 100644 --- a/code/controllers/configuration/configuration_core.dm +++ b/code/controllers/configuration/configuration_core.dm @@ -44,6 +44,8 @@ GLOBAL_DATUM_INIT(configuration, /datum/server_configuration, new()) var/datum/configuration_section/url_configuration/url /// Holder for the voting configuration datum var/datum/configuration_section/vote_configuration/vote + /// Raw data. Stored here to avoid passing data between procs constantly + var/list/raw_data = list() /datum/server_configuration/Destroy(force) @@ -51,6 +53,16 @@ GLOBAL_DATUM_INIT(configuration, /datum/server_configuration, new()) // This is going to stay existing. I dont care. return QDEL_HINT_LETMELIVE +/datum/server_configuration/vv_get_var(var_name) + if(var_name == "raw_data") // NO! + return FALSE + . = ..() + +/datum/server_configuration/vv_edit_var(var_name, var_value) + if(var_name == "raw_data") // NO! + return FALSE + . = ..() + /datum/server_configuration/CanProcCall(procname) return FALSE // No thanks @@ -83,32 +95,81 @@ GLOBAL_DATUM_INIT(configuration, /datum/server_configuration, new()) if(!fexists(config_file)) config_file = "config/example/config.toml" // Fallback to example if user hasnt setup config properly var/raw_json = rustg_toml2json(config_file) - var/list/raw_config_data = json_decode(raw_json) + raw_data = json_decode(raw_json) // Now pass through all our stuff - admin.load_data(raw_config_data["admin_configuration"]) - afk.load_data(raw_config_data["afk_configuration"]) - custom_sprites.load_data(raw_config_data["custom_sprites_configuration"]) - database.load_data(raw_config_data["database_configuration"]) - discord.load_data(raw_config_data["discord_configuration"]) - event.load_data(raw_config_data["event_configuration"]) - gamemode.load_data(raw_config_data["gamemode_configuration"]) - gateway.load_data(raw_config_data["gateway_configuration"]) - general.load_data(raw_config_data["general_configuration"]) - ipintel.load_data(raw_config_data["ipintel_configuration"]) - jobs.load_data(raw_config_data["job_configuration"]) - logging.load_data(raw_config_data["logging_configuration"]) - mc.load_data(raw_config_data["mc_configuration"]) - movement.load_data(raw_config_data["movement_configuration"]) - overflow.load_data(raw_config_data["overflow_configuration"]) - ruins.load_data(raw_config_data["ruin_configuration"]) - system.load_data(raw_config_data["system_configuration"]) - url.load_data(raw_config_data["url_configuration"]) - vote.load_data(raw_config_data["voting_configuration"]) + safe_load(admin, "admin_configuration") + safe_load(afk, "afk_configuration") + safe_load(custom_sprites, "custom_sprites_configuration") + safe_load(database, "database_configuration") + safe_load(discord, "discord_configuration") + safe_load(event, "event_configuration") + safe_load(gamemode, "gamemode_configuration") + safe_load(gateway, "gateway_configuration") + safe_load(general, "general_configuration") + safe_load(ipintel, "ipintel_configuration") + safe_load(jobs, "job_configuration") + safe_load(logging, "logging_configuration") + safe_load(mc, "mc_configuration") + safe_load(movement, "movement_configuration") + safe_load(overflow, "overflow_configuration") + safe_load(ruins, "ruin_configuration") + safe_load(system, "system_configuration") + safe_load(url, "url_configuration") + safe_load(vote, "voting_configuration") + + // Clear our list to save RAM + raw_data = list() // And report the load DIRECT_OUTPUT(world.log, "Config loaded in [stop_watch(start)]s") +// Proc to load up instance-specific overrides +/datum/server_configuration/proc/load_overrides() + var/override_file = "config/overrides_[world.port].toml" + if(!fexists(override_file)) + DIRECT_OUTPUT(world.log, "Overrides not found for this instance.") + return + + DIRECT_OUTPUT(world.log, "Overrides found for this instance. Loading them.") + var/start = start_watch() // Time tracking + + var/raw_json = rustg_toml2json(override_file) + raw_data = json_decode(raw_json) + + // Now safely load our overrides. + // Due to the nature of config wrappers, only vars that exist in the config file are applied to the config datums. + // This means that an override missing a key doesnt null it out from the main server + safe_load(admin, "admin_configuration") + safe_load(afk, "afk_configuration") + safe_load(custom_sprites, "custom_sprites_configuration") + safe_load(database, "database_configuration") + safe_load(discord, "discord_configuration") + safe_load(event, "event_configuration") + safe_load(gamemode, "gamemode_configuration") + safe_load(gateway, "gateway_configuration") + safe_load(general, "general_configuration") + safe_load(ipintel, "ipintel_configuration") + safe_load(jobs, "job_configuration") + safe_load(logging, "logging_configuration") + safe_load(mc, "mc_configuration") + safe_load(movement, "movement_configuration") + safe_load(overflow, "overflow_configuration") + safe_load(ruins, "ruin_configuration") + safe_load(system, "system_configuration") + safe_load(url, "url_configuration") + safe_load(vote, "voting_configuration") + + // Clear our list to save RAM + raw_data = list() + + // And report the load + DIRECT_OUTPUT(world.log, "Config overrides loaded in [stop_watch(start)]s") + +// Only loads the data for a config section if that key exists in the JSON +/datum/server_configuration/proc/safe_load(datum/configuration_section/CS, section) + if(!isnull(raw_data[section])) + CS.load_data(raw_data[section]) /datum/configuration_section /// See __config_defines.dm diff --git a/code/game/world.dm b/code/game/world.dm index a5ccc80fe7b..ac0234ab2dc 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -20,7 +20,10 @@ GLOBAL_LIST_INIT(map_transition_config, list(CC_TRANSITION_CONFIG)) //temporary file used to record errors with loading config and the database, moved to log directory once logging is set up GLOB.config_error_log = GLOB.world_game_log = GLOB.world_runtime_log = GLOB.sql_log = "data/logs/config_error.log" - GLOB.configuration.load_configuration() + GLOB.configuration.load_configuration() // Load up the base config.toml + // Load up overrides for this specific instance, based on port + // If this instance is listening on port 6666, the server will look for config/overrides_6666.toml + GLOB.configuration.load_overrides() // Right off the bat, load up the DB SSdbcore.CheckSchemaVersion() // This doesnt just check the schema version, it also connects to the db! This needs to happen super early! I cannot stress this enough! From e19a83f9be3e709db408b33be3b68924147c335d Mon Sep 17 00:00:00 2001 From: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com> Date: Tue, 17 Aug 2021 14:05:46 +0100 Subject: [PATCH 02/19] SSinstancing (I need to test this with 2 machines) --- .../configuration/configuration_core.dm | 39 +++++------- .../sections/instancing_configuration.dm | 20 ++++++ code/controllers/subsystem/instancing.dm | 63 +++++++++++++++++++ code/datums/peer_server.dm | 32 ++++++++++ code/modules/admin/admin_verbs.dm | 3 +- code/modules/admin/verbs/view_instances.dm | 22 +++++++ code/modules/world_topic/server_discovery.dm | 12 ++++ config/example/config.toml | 16 +++++ paradise.dme | 5 ++ 9 files changed, 186 insertions(+), 26 deletions(-) create mode 100644 code/controllers/configuration/sections/instancing_configuration.dm create mode 100644 code/controllers/subsystem/instancing.dm create mode 100644 code/datums/peer_server.dm create mode 100644 code/modules/admin/verbs/view_instances.dm create mode 100644 code/modules/world_topic/server_discovery.dm diff --git a/code/controllers/configuration/configuration_core.dm b/code/controllers/configuration/configuration_core.dm index c8b490e26cc..33f4e45d76c 100644 --- a/code/controllers/configuration/configuration_core.dm +++ b/code/controllers/configuration/configuration_core.dm @@ -24,6 +24,8 @@ GLOBAL_DATUM_INIT(configuration, /datum/server_configuration, new()) var/datum/configuration_section/gateway_configuration/gateway /// Holder for the general configuration datum var/datum/configuration_section/general_configuration/general + /// Holder for the instancing configuration datum + var/datum/configuration_section/instancing_configuration/instancing /// Holder for the IPIntel configuration datum var/datum/configuration_section/ipintel_configuration/ipintel /// Holder for the job configuration datum @@ -79,6 +81,7 @@ GLOBAL_DATUM_INIT(configuration, /datum/server_configuration, new()) gamemode = new() gateway = new() general = new() + instancing = new() ipintel = new() jobs = new() logging = new() @@ -98,6 +101,15 @@ GLOBAL_DATUM_INIT(configuration, /datum/server_configuration, new()) raw_data = json_decode(raw_json) // Now pass through all our stuff + load_all_sections() + + // Clear our list to save RAM + raw_data = list() + + // And report the load + DIRECT_OUTPUT(world.log, "Config loaded in [stop_watch(start)]s") + +/datum/server_configuration/proc/load_all_sections() safe_load(admin, "admin_configuration") safe_load(afk, "afk_configuration") safe_load(custom_sprites, "custom_sprites_configuration") @@ -107,6 +119,7 @@ GLOBAL_DATUM_INIT(configuration, /datum/server_configuration, new()) safe_load(gamemode, "gamemode_configuration") safe_load(gateway, "gateway_configuration") safe_load(general, "general_configuration") + safe_load(instancing, "instancing_configuration") safe_load(ipintel, "ipintel_configuration") safe_load(jobs, "job_configuration") safe_load(logging, "logging_configuration") @@ -118,12 +131,6 @@ GLOBAL_DATUM_INIT(configuration, /datum/server_configuration, new()) safe_load(url, "url_configuration") safe_load(vote, "voting_configuration") - // Clear our list to save RAM - raw_data = list() - - // And report the load - DIRECT_OUTPUT(world.log, "Config loaded in [stop_watch(start)]s") - // Proc to load up instance-specific overrides /datum/server_configuration/proc/load_overrides() var/override_file = "config/overrides_[world.port].toml" @@ -140,25 +147,7 @@ GLOBAL_DATUM_INIT(configuration, /datum/server_configuration, new()) // Now safely load our overrides. // Due to the nature of config wrappers, only vars that exist in the config file are applied to the config datums. // This means that an override missing a key doesnt null it out from the main server - safe_load(admin, "admin_configuration") - safe_load(afk, "afk_configuration") - safe_load(custom_sprites, "custom_sprites_configuration") - safe_load(database, "database_configuration") - safe_load(discord, "discord_configuration") - safe_load(event, "event_configuration") - safe_load(gamemode, "gamemode_configuration") - safe_load(gateway, "gateway_configuration") - safe_load(general, "general_configuration") - safe_load(ipintel, "ipintel_configuration") - safe_load(jobs, "job_configuration") - safe_load(logging, "logging_configuration") - safe_load(mc, "mc_configuration") - safe_load(movement, "movement_configuration") - safe_load(overflow, "overflow_configuration") - safe_load(ruins, "ruin_configuration") - safe_load(system, "system_configuration") - safe_load(url, "url_configuration") - safe_load(vote, "voting_configuration") + load_all_sections() // Clear our list to save RAM raw_data = list() diff --git a/code/controllers/configuration/sections/instancing_configuration.dm b/code/controllers/configuration/sections/instancing_configuration.dm new file mode 100644 index 00000000000..bc5c6c8406a --- /dev/null +++ b/code/controllers/configuration/sections/instancing_configuration.dm @@ -0,0 +1,20 @@ +/// Config holder for stuff relating to multi-server instances +/datum/configuration_section/instancing_configuration + /// ID of this specific server + var/server_id = "paradise_main" + /// List of all peer servers + var/list/datum/peer_server/peers = list() + +/datum/configuration_section/instancing_configuration/load_data(list/data) + // Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line + CONFIG_LOAD_STR(server_id, data["server_id"]) + + if(islist(data["peer_servers"])) + for(var/list/server in data["peer_servers"]) + if(server["server_port"] == world.port) // Skip our own instance + continue + var/datum/peer_server/PS = new() + PS.internal_ip = server["internal_ip"] + PS.server_port = server["server_port"] + PS.commskey = server["commskey"] + peers.Add(PS) diff --git a/code/controllers/subsystem/instancing.dm b/code/controllers/subsystem/instancing.dm new file mode 100644 index 00000000000..ec4c289bc86 --- /dev/null +++ b/code/controllers/subsystem/instancing.dm @@ -0,0 +1,63 @@ +SUBSYSTEM_DEF(instancing) + name = "Instancing" + runlevels = RUNLEVEL_INIT | RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME + /// Have we announced startup yet + var/startup_announced = FALSE + /// Is a check currently running? + var/check_running = FALSE + +/datum/controller/subsystem/instancing/Initialize(start_timeofday) + // Do an initial peer check + check_peers() + return ..() + +/datum/controller/subsystem/instancing/fire(resumed) + check_peers() + + +/** + * Refreshes all peers on the server + * + * Called periodically during fire() as well as when a new peer reports itself as online + * Only one instance of this proc may run at a time + * + */ +/datum/controller/subsystem/instancing/proc/check_peers() + set waitfor = FALSE // This has sleeps if it cant topic so we dont want to bog things down + if(check_running) + return + + check_running = TRUE // Dont allow multiple of these + // A NOTE TO ANYONE ELSE WHO LOOKS AT THIS + // THESE TIMINGS ARE A FUCKING NIGHTMARE AND YOU WILL NEED DEBUG LOGGING TO TEST THEM + // DO NOT FUCK WITH THE TIMINGS -aa + for(var/datum/peer_server/PS in GLOB.configuration.instancing.peers) + // If the server hasnt been discovered and its been more than 5 minutes + if((!PS.discovered && PS.last_operation_time + 5 MINUTES > world.time)) + continue + + if(PS.last_operation_time + 1 MINUTES > world.time) + continue // Only run main operations once every minute anyway + + var/peer_response = world.Export("byond://[PS.internal_ip]:[PS.server_port]?server_discovery&key=[PS.commskey]") + if(!peer_response) + PS.online = FALSE // Peer is offline + PS.last_operation_time = world.time + continue + + // We got a response + PS.discovered = TRUE + PS.online = TRUE + var/list/peer_data = json_decode(peer_response) + + PS.external_ip = peer_data["external_ip"] + PS.server_id = peer_data["server_id"] + PS.server_name = peer_data["server_name"] + PS.playercount = peer_data["playercount"] + + PS.last_operation_time = world.time + + check_running = FALSE + +/datum/controller/subsystem/instancing/proc/message_all_peers(include_offline = FALSE) + return diff --git a/code/datums/peer_server.dm b/code/datums/peer_server.dm new file mode 100644 index 00000000000..81f78be68b4 --- /dev/null +++ b/code/datums/peer_server.dm @@ -0,0 +1,32 @@ +/** + * # Peer server + * + * This datum is essentially a model class to represent another instance of Paracode running + * + * It contains communications passes, an internal IP to communicate to, an external IP for players to connect to + * + */ +/datum/peer_server + // The following options will be loaded from configuration + /// Peer server internal IP, used for communications + var/internal_ip + /// Port of the server + var/server_port = 0 + /// Comms key for this server + var/commskey + + // The following options will be pulled from the peer at runtime + /// Peer server external IP, used for routing players around + var/external_ip + /// Peer server ID, used internally. Pulled from the peer on startup. + var/server_id + /// Peer server name, presented to players. Pulled from the peer on startup. + var/server_name + /// Have we done initial data discovery yet? + var/discovered = FALSE + /// Is the peer server online? + var/online = FALSE + /// Playercount of the peer server on last ping + var/playercount + /// Last world.time that an operation was attempted + var/last_operation_time diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 598705668ae..6a3103172ee 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -67,7 +67,8 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list( /client/proc/toggle_mentor_chat, /client/proc/toggle_advanced_interaction, /*toggle admin ability to interact with not only machines, but also atoms such as buttons and doors*/ /client/proc/list_ssds_afks, - /client/proc/ccbdb_lookup_ckey + /client/proc/ccbdb_lookup_ckey, + /client/proc/view_instances )) GLOBAL_LIST_INIT(admin_verbs_ban, list( /client/proc/ban_panel, diff --git a/code/modules/admin/verbs/view_instances.dm b/code/modules/admin/verbs/view_instances.dm new file mode 100644 index 00000000000..3aa83809516 --- /dev/null +++ b/code/modules/admin/verbs/view_instances.dm @@ -0,0 +1,22 @@ +/client/proc/view_instances() + set name = "View Server Instances" + set desc = "View the running server instances" + set category = "Server" + + if(!check_rights(R_ADMIN)) + return + + to_chat(usr, "Server instances info") + for(var/datum/peer_server/PS in GLOB.configuration.instancing.peers) + // We havnt even been discovered, so we cant even be online + if(!PS.discovered) + to_chat(usr, "#[PS.server_port] (Undiscovered) - OFFLINE") + continue + + // We exist but arent online at the moment + if(!PS.online) + to_chat(usr, "ID [PS.server_id] - OFFLINE") + continue + + // If we are here, we are online, so we can do a rich report + to_chat(usr, "ID [PS.server_id] - ONLINE (Players: [PS.playercount])") diff --git a/code/modules/world_topic/server_discovery.dm b/code/modules/world_topic/server_discovery.dm new file mode 100644 index 00000000000..8558f5c380c --- /dev/null +++ b/code/modules/world_topic/server_discovery.dm @@ -0,0 +1,12 @@ +/datum/world_topic_handler/server_discovery + topic_key = "server_discovery" + requires_commskey = TRUE + +/datum/world_topic_handler/server_discovery/execute(list/input, key_valid) + // We need to send the stuff to make a /datum/peer_server on the other side + var/list/server_data = list() + server_data["external_ip"] = world.internet_address + server_data["server_id"] = GLOB.configuration.instancing.server_id + server_data["server_name"] = GLOB.configuration.general.server_name + server_data["playercount"] = length(GLOB.clients) + return json_encode(server_data) diff --git a/config/example/config.toml b/config/example/config.toml index 254f793221e..b73e378be14 100644 --- a/config/example/config.toml +++ b/config/example/config.toml @@ -22,6 +22,7 @@ # - gamemode_configuration # - gateway_configuration # - general_configuration +# - instancing_configuration # - ipintel_configuration # - job_configuration # - logging_configuration @@ -376,6 +377,21 @@ random_ai_lawset = true ################################################################ +[instancing_configuration] +# This section contains all the information for instancing servers (Multiple paracode servers on the same database) + +# ID of this specific server. Override when needed +server_id = "paradise_main" +# List of all peer servers. Add all of the servers inside this configuration. +# The code will ignore its own instance by port checking +peer_servers = [ + #{internal_ip = "10.0.0.30", server_port = 6666, commskey = "please_use_something_secure"} +] + + +################################################################ + + [ipintel_configuration] # This section contains all the information for IPIntel (The Anti VPN system) diff --git a/paradise.dme b/paradise.dme index 11176b1befe..25499536c1c 100644 --- a/paradise.dme +++ b/paradise.dme @@ -196,6 +196,7 @@ #include "code\controllers\configuration\sections\gamemode_configuration.dm" #include "code\controllers\configuration\sections\gateway_configuration.dm" #include "code\controllers\configuration\sections\general_configuration.dm" +#include "code\controllers\configuration\sections\instancing_configuration.dm" #include "code\controllers\configuration\sections\ipintel_configuration.dm" #include "code\controllers\configuration\sections\job_configuration.dm" #include "code\controllers\configuration\sections\logging_configuration.dm" @@ -228,6 +229,7 @@ #include "code\controllers\subsystem\icon_smooth.dm" #include "code\controllers\subsystem\idlenpcpool.dm" #include "code\controllers\subsystem\input.dm" +#include "code\controllers\subsystem\instancing.dm" #include "code\controllers\subsystem\ipintel.dm" #include "code\controllers\subsystem\jobs.dm" #include "code\controllers\subsystem\late_mapping.dm" @@ -288,6 +290,7 @@ #include "code\datums\mind.dm" #include "code\datums\mixed.dm" #include "code\datums\mutable_appearance.dm" +#include "code\datums\peer_server.dm" #include "code\datums\periodic_news.dm" #include "code\datums\pipe_datums.dm" #include "code\datums\progressbar.dm" @@ -1221,6 +1224,7 @@ #include "code\modules\admin\verbs\ticklag.dm" #include "code\modules\admin\verbs\toggledebugverbs.dm" #include "code\modules\admin\verbs\tripAI.dm" +#include "code\modules\admin\verbs\view_instances.dm" #include "code\modules\admin\verbs\vox_raiders.dm" #include "code\modules\admin\verbs\SDQL2\SDQL_2.dm" #include "code\modules\admin\verbs\SDQL2\SDQL_2_parser.dm" @@ -2508,6 +2512,7 @@ #include "code\modules\world_topic\manifest.dm" #include "code\modules\world_topic\ping.dm" #include "code\modules\world_topic\players.dm" +#include "code\modules\world_topic\server_discovery.dm" #include "code\modules\world_topic\status.dm" #include "goon\code\datums\browserOutput.dm" #include "interface\interface.dm" From fdcc88b7ca3e8293cfbc4be556167b95d86dfd07 Mon Sep 17 00:00:00 2001 From: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com> Date: Tue, 17 Aug 2021 14:30:00 +0100 Subject: [PATCH 03/19] Startup notifications --- code/controllers/subsystem/instancing.dm | 46 +++++++++++++++++-- code/modules/admin/admin_verbs.dm | 3 +- code/modules/admin/verbs/view_instances.dm | 10 ++++ code/modules/world_topic/instance_announce.dm | 7 +++ paradise.dme | 1 + 5 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 code/modules/world_topic/instance_announce.dm diff --git a/code/controllers/subsystem/instancing.dm b/code/controllers/subsystem/instancing.dm index ec4c289bc86..11ee97ef93f 100644 --- a/code/controllers/subsystem/instancing.dm +++ b/code/controllers/subsystem/instancing.dm @@ -3,6 +3,8 @@ SUBSYSTEM_DEF(instancing) runlevels = RUNLEVEL_INIT | RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME /// Have we announced startup yet var/startup_announced = FALSE + /// Has our initial check complete? Used as part of the above + var/initial_check_complete = FALSE /// Is a check currently running? var/check_running = FALSE @@ -13,6 +15,10 @@ SUBSYSTEM_DEF(instancing) /datum/controller/subsystem/instancing/fire(resumed) check_peers() + if(initial_check_complete && !startup_announced) + startup_announced = TRUE + var/startup_msg = "The server [GLOB.configuration.general.server_name] is now starting up. The map is [SSmapping.map_datum.fluff_name] ([SSmapping.map_datum.technical_name])" + INVOKE_ASYNC(src, .proc/message_all_peers, startup_msg) // Async /** @@ -21,8 +27,10 @@ SUBSYSTEM_DEF(instancing) * Called periodically during fire() as well as when a new peer reports itself as online * Only one instance of this proc may run at a time * + * Arguments: + * * force - Do we want to force check all of them */ -/datum/controller/subsystem/instancing/proc/check_peers() +/datum/controller/subsystem/instancing/proc/check_peers(force = FALSE) set waitfor = FALSE // This has sleeps if it cant topic so we dont want to bog things down if(check_running) return @@ -34,10 +42,13 @@ SUBSYSTEM_DEF(instancing) for(var/datum/peer_server/PS in GLOB.configuration.instancing.peers) // If the server hasnt been discovered and its been more than 5 minutes if((!PS.discovered && PS.last_operation_time + 5 MINUTES > world.time)) - continue + if(!force) // No, code review. This is not going in the same line. + continue + // Only run main operations once every minute anyway if(PS.last_operation_time + 1 MINUTES > world.time) - continue // Only run main operations once every minute anyway + if(!force) + continue var/peer_response = world.Export("byond://[PS.internal_ip]:[PS.server_port]?server_discovery&key=[PS.commskey]") if(!peer_response) @@ -58,6 +69,31 @@ SUBSYSTEM_DEF(instancing) PS.last_operation_time = world.time check_running = FALSE + initial_check_complete = TRUE -/datum/controller/subsystem/instancing/proc/message_all_peers(include_offline = FALSE) - return +/** + * Message all peers + * + * Wrapper for [topic_all_peers] to autoformat a message topic. Will send a server-wide announcement to the other servers + * including any relevant detail + * Arguments: + * * message - Message to send to the other servers + * * include_offline - Whether to topic offline servers on the off chance they came online + */ +/datum/controller/subsystem/instancing/proc/message_all_peers(message, include_offline = FALSE) + var/topic_string = "instance_announce&msg=[html_encode(message)]" + topic_all_peers(topic_string, include_offline) + +/** + * Sends a topic to all peers + * + * Sends a raw topic to the other servers. WILL APPEND &key=[commskey] ON THE END. PLEASE ACCOUNT FOR THIS. + * + * Arguments: + * * raw_topic - The raw topic to send to the other servers + * * include_offline - Whether to topic offline servers on the off chance they came online + */ +/datum/controller/subsystem/instancing/proc/topic_all_peers(raw_topic, include_offline = FALSE) + for(var/datum/peer_server/PS in GLOB.configuration.instancing.peers) + if(PS.online || include_offline) + world.Export("byond://[PS.internal_ip]:[PS.server_port]?[raw_topic]&key=[PS.commskey]") diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 6a3103172ee..60758052863 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -138,7 +138,8 @@ GLOBAL_LIST_INIT(admin_verbs_server, list( /client/proc/toggle_antagHUD_restrictions, /client/proc/set_ooc, /client/proc/reset_ooc, - /client/proc/set_next_map + /client/proc/set_next_map, + /client/proc/refresh_instances )) GLOBAL_LIST_INIT(admin_verbs_debug, list( /client/proc/cmd_admin_list_open_jobs, diff --git a/code/modules/admin/verbs/view_instances.dm b/code/modules/admin/verbs/view_instances.dm index 3aa83809516..3cedf9614a0 100644 --- a/code/modules/admin/verbs/view_instances.dm +++ b/code/modules/admin/verbs/view_instances.dm @@ -20,3 +20,13 @@ // If we are here, we are online, so we can do a rich report to_chat(usr, "ID [PS.server_id] - ONLINE (Players: [PS.playercount])") + +/client/proc/refresh_instances() + set name = "Force Refresh Server Instances" + set desc = "Force refresh the local cache of server instances" + set category = "Server" + + if(!check_rights(R_SERVER)) + return + + SSinstancing.check_peers(TRUE) // Force check diff --git a/code/modules/world_topic/instance_announce.dm b/code/modules/world_topic/instance_announce.dm new file mode 100644 index 00000000000..e63ce9a290c --- /dev/null +++ b/code/modules/world_topic/instance_announce.dm @@ -0,0 +1,7 @@ +/datum/world_topic_handler/instance_announce + topic_key = "instance_announce" + requires_commskey = TRUE + +/datum/world_topic_handler/instance_announce/execute(list/input, key_valid) + var/msg = input["msg"] + to_chat(world, "Attention: [msg]") diff --git a/paradise.dme b/paradise.dme index 25499536c1c..4f3e928c357 100644 --- a/paradise.dme +++ b/paradise.dme @@ -2509,6 +2509,7 @@ #include "code\modules\world_topic\_topic_base.dm" #include "code\modules\world_topic\adminmsg.dm" #include "code\modules\world_topic\announce.dm" +#include "code\modules\world_topic\instance_announce.dm" #include "code\modules\world_topic\manifest.dm" #include "code\modules\world_topic\ping.dm" #include "code\modules\world_topic\players.dm" From 3560f8fceabd8d8cbed4aa897e382fd4eed0f3f5 Mon Sep 17 00:00:00 2001 From: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com> Date: Tue, 17 Aug 2021 14:36:09 +0100 Subject: [PATCH 04/19] Behaviour tweak --- code/controllers/subsystem/instancing.dm | 12 ++++-------- tgui/packages/common/target/npmlist.json | 1 + 2 files changed, 5 insertions(+), 8 deletions(-) create mode 100644 tgui/packages/common/target/npmlist.json diff --git a/code/controllers/subsystem/instancing.dm b/code/controllers/subsystem/instancing.dm index 11ee97ef93f..ffc8808e427 100644 --- a/code/controllers/subsystem/instancing.dm +++ b/code/controllers/subsystem/instancing.dm @@ -1,9 +1,7 @@ SUBSYSTEM_DEF(instancing) name = "Instancing" runlevels = RUNLEVEL_INIT | RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME - /// Have we announced startup yet - var/startup_announced = FALSE - /// Has our initial check complete? Used as part of the above + /// Has our initial check complete? Used to halt init but not lag the server var/initial_check_complete = FALSE /// Is a check currently running? var/check_running = FALSE @@ -11,15 +9,13 @@ SUBSYSTEM_DEF(instancing) /datum/controller/subsystem/instancing/Initialize(start_timeofday) // Do an initial peer check check_peers() + UNTIL(initial_check_complete) // Wait here a bit + var/startup_msg = "The server [GLOB.configuration.general.server_name] is now starting up. The map is [SSmapping.map_datum.fluff_name] ([SSmapping.map_datum.technical_name])" + message_all_peers(startup_msg) return ..() /datum/controller/subsystem/instancing/fire(resumed) check_peers() - if(initial_check_complete && !startup_announced) - startup_announced = TRUE - var/startup_msg = "The server [GLOB.configuration.general.server_name] is now starting up. The map is [SSmapping.map_datum.fluff_name] ([SSmapping.map_datum.technical_name])" - INVOKE_ASYNC(src, .proc/message_all_peers, startup_msg) // Async - /** * Refreshes all peers on the server diff --git a/tgui/packages/common/target/npmlist.json b/tgui/packages/common/target/npmlist.json new file mode 100644 index 00000000000..f9c06d0a205 --- /dev/null +++ b/tgui/packages/common/target/npmlist.json @@ -0,0 +1 @@ +{"name":"common","version":"3.0.0"} \ No newline at end of file From 6381e692dc9f50b7e75f7879284ae15cc2f94681 Mon Sep 17 00:00:00 2001 From: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com> Date: Tue, 17 Aug 2021 15:14:25 +0100 Subject: [PATCH 05/19] FUCKING VALUES --- code/datums/peer_server.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/datums/peer_server.dm b/code/datums/peer_server.dm index 81f78be68b4..ed7abc97f6a 100644 --- a/code/datums/peer_server.dm +++ b/code/datums/peer_server.dm @@ -29,4 +29,4 @@ /// Playercount of the peer server on last ping var/playercount /// Last world.time that an operation was attempted - var/last_operation_time + var/last_operation_time = 0 From ac6b9a4b10555cada7d4897092c2f2fde4bf4ce7 Mon Sep 17 00:00:00 2001 From: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com> Date: Tue, 17 Aug 2021 15:27:42 +0100 Subject: [PATCH 06/19] *scream --- code/controllers/subsystem/instancing.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/controllers/subsystem/instancing.dm b/code/controllers/subsystem/instancing.dm index ffc8808e427..7abe4ba2b09 100644 --- a/code/controllers/subsystem/instancing.dm +++ b/code/controllers/subsystem/instancing.dm @@ -8,7 +8,7 @@ SUBSYSTEM_DEF(instancing) /datum/controller/subsystem/instancing/Initialize(start_timeofday) // Do an initial peer check - check_peers() + check_peers(TRUE) // Force because of time memes UNTIL(initial_check_complete) // Wait here a bit var/startup_msg = "The server [GLOB.configuration.general.server_name] is now starting up. The map is [SSmapping.map_datum.fluff_name] ([SSmapping.map_datum.technical_name])" message_all_peers(startup_msg) From bbbe8c1d453e69bec29283ad1888292e1cb77227 Mon Sep 17 00:00:00 2001 From: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com> Date: Tue, 17 Aug 2021 16:45:29 +0100 Subject: [PATCH 07/19] Even more stuff --- code/controllers/subsystem/instancing.dm | 12 ++++++------ code/modules/world_topic/instance_announce.dm | 9 ++++++++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/code/controllers/subsystem/instancing.dm b/code/controllers/subsystem/instancing.dm index 7abe4ba2b09..87ad3f06e7c 100644 --- a/code/controllers/subsystem/instancing.dm +++ b/code/controllers/subsystem/instancing.dm @@ -10,8 +10,8 @@ SUBSYSTEM_DEF(instancing) // Do an initial peer check check_peers(TRUE) // Force because of time memes UNTIL(initial_check_complete) // Wait here a bit - var/startup_msg = "The server [GLOB.configuration.general.server_name] is now starting up. The map is [SSmapping.map_datum.fluff_name] ([SSmapping.map_datum.technical_name])" - message_all_peers(startup_msg) + var/startup_msg = "The server [GLOB.configuration.general.server_name] is now starting up. The map is [SSmapping.map_datum.fluff_name] ([SSmapping.map_datum.technical_name]). You can connect with the Switch Server verb." + message_all_peers(startup_msg, send_reping = TRUE) return ..() /datum/controller/subsystem/instancing/fire(resumed) @@ -76,9 +76,9 @@ SUBSYSTEM_DEF(instancing) * * message - Message to send to the other servers * * include_offline - Whether to topic offline servers on the off chance they came online */ -/datum/controller/subsystem/instancing/proc/message_all_peers(message, include_offline = FALSE) +/datum/controller/subsystem/instancing/proc/message_all_peers(message, include_offline = FALSE, send_reping = FALSE) var/topic_string = "instance_announce&msg=[html_encode(message)]" - topic_all_peers(topic_string, include_offline) + topic_all_peers(topic_string, include_offline, send_reping) /** * Sends a topic to all peers @@ -89,7 +89,7 @@ SUBSYSTEM_DEF(instancing) * * raw_topic - The raw topic to send to the other servers * * include_offline - Whether to topic offline servers on the off chance they came online */ -/datum/controller/subsystem/instancing/proc/topic_all_peers(raw_topic, include_offline = FALSE) +/datum/controller/subsystem/instancing/proc/topic_all_peers(raw_topic, include_offline = FALSE, send_reping = FALSE) for(var/datum/peer_server/PS in GLOB.configuration.instancing.peers) if(PS.online || include_offline) - world.Export("byond://[PS.internal_ip]:[PS.server_port]?[raw_topic]&key=[PS.commskey]") + world.Export("byond://[PS.internal_ip]:[PS.server_port]?[raw_topic]&key=[PS.commskey][send_reping ? "&repoll=1" : ""]") diff --git a/code/modules/world_topic/instance_announce.dm b/code/modules/world_topic/instance_announce.dm index e63ce9a290c..250046cd53b 100644 --- a/code/modules/world_topic/instance_announce.dm +++ b/code/modules/world_topic/instance_announce.dm @@ -4,4 +4,11 @@ /datum/world_topic_handler/instance_announce/execute(list/input, key_valid) var/msg = input["msg"] - to_chat(world, "Attention: [msg]") + if(input["repoll"]) // Repoll peers + UNTIL(!SSinstancing.check_running) // If we are running, wait + SSinstancing.check_peers(TRUE) // Manually check, with FORCE + UNTIL(!SSinstancing.check_running) // Wait for completion + + // Now that we repolled, we can tell the playerbase + to_chat(world, "
Attention

[msg]
") + SEND_SOUND(world, sound('sound/misc/notice2.ogg')) // Same as captains priority announce From 890e7e5ce5f91ca0878ebf8a01a67978a172ebfa Mon Sep 17 00:00:00 2001 From: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com> Date: Mon, 20 Sep 2021 23:50:51 +0100 Subject: [PATCH 08/19] I think this is instance communication --- .github/CODEOWNERS | 1 + SQL/paradise_schema.sql | 14 +- SQL/updates/25-26.sql | 11 ++ code/__DEFINES/misc.dm | 2 +- .../configuration/configuration_core.dm | 4 - .../sections/instancing_configuration.dm | 20 --- .../sections/system_configuration.dm | 7 + code/controllers/subsystem/instancing.dm | 165 ++++++++++++------ code/game/world.dm | 1 + code/modules/admin/admin_verbs.dm | 1 - code/modules/admin/verbs/view_instances.dm | 12 +- code/modules/client/client_procs.dm | 5 + code/modules/world_topic/instance_announce.dm | 9 +- code/modules/world_topic/server_discovery.dm | 12 -- config/example/config.toml | 24 +-- paradise.dme | 2 - 16 files changed, 163 insertions(+), 127 deletions(-) create mode 100644 SQL/updates/25-26.sql delete mode 100644 code/controllers/configuration/sections/instancing_configuration.dm delete mode 100644 code/modules/world_topic/server_discovery.dm diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ccd774cdaa8..894a16ac470 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -5,6 +5,7 @@ ### AffectedArc07 # Actual Code +/code/controllers/subsystem/instancing.dm @AffectedArc07 /code/controllers/subsystem/mapping.dm @AffectedArc07 /code/controllers/subsystem/ticker.dm @AffectedArc07 /tgui/ @AffectedArc07 diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql index 3ed1cbca688..eb0deeb93e2 100644 --- a/SQL/paradise_schema.sql +++ b/SQL/paradise_schema.sql @@ -597,5 +597,15 @@ CREATE TABLE `2fa_secrets` ( `date_setup` DATETIME NOT NULL DEFAULT current_timestamp(), `last_time` DATETIME NULL DEFAULT NULL, PRIMARY KEY (`ckey`) USING BTREE -) -COLLATE='utf8mb4_general_ci' ENGINE=InnoDB; +) COLLATE='utf8mb4_general_ci' ENGINE=InnoDB; + +-- +-- Table structure for table `instance_data_cache` +-- +CREATE TABLE `instance_data_cache` ( + `server_id` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_general_ci', + `key_name` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_general_ci', + `key_value` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_general_ci', + `last_updated` TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + PRIMARY KEY (`server_id`, `key_name`) USING HASH +) COLLATE='utf8mb4_general_ci' ENGINE=MEMORY; diff --git a/SQL/updates/25-26.sql b/SQL/updates/25-26.sql new file mode 100644 index 00000000000..c1e3834ab8d --- /dev/null +++ b/SQL/updates/25-26.sql @@ -0,0 +1,11 @@ +# Updates DB from 25 to 26 -AffectedArc07 +# Adds a new table for instance data caching, and server_id fields on other tables +# NOTE: If this line makes it into the final PR I will be upset with myself + +CREATE TABLE `instance_data_cache` ( + `server_id` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_general_ci', + `key_name` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_general_ci', + `key_value` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_general_ci', + `last_updated` TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + PRIMARY KEY (`server_id`, `key_name`) USING HASH +) COLLATE='utf8mb4_general_ci' ENGINE=MEMORY; diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 2b95577ef3c..e16635d1c07 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -365,7 +365,7 @@ #define INVESTIGATE_BOMB "bombs" // The SQL version required by this version of the code -#define SQL_VERSION 25 +#define SQL_VERSION 26 // Vending machine stuff #define CAT_NORMAL 1 diff --git a/code/controllers/configuration/configuration_core.dm b/code/controllers/configuration/configuration_core.dm index ad2cf3c5bda..81c2c6787bf 100644 --- a/code/controllers/configuration/configuration_core.dm +++ b/code/controllers/configuration/configuration_core.dm @@ -24,8 +24,6 @@ GLOBAL_DATUM_INIT(configuration, /datum/server_configuration, new()) var/datum/configuration_section/gateway_configuration/gateway /// Holder for the general configuration datum var/datum/configuration_section/general_configuration/general - /// Holder for the instancing configuration datum - var/datum/configuration_section/instancing_configuration/instancing /// Holder for the IPIntel configuration datum var/datum/configuration_section/ipintel_configuration/ipintel /// Holder for the job configuration datum @@ -83,7 +81,6 @@ GLOBAL_DATUM_INIT(configuration, /datum/server_configuration, new()) gamemode = new() gateway = new() general = new() - instancing = new() ipintel = new() jobs = new() logging = new() @@ -122,7 +119,6 @@ GLOBAL_DATUM_INIT(configuration, /datum/server_configuration, new()) safe_load(gamemode, "gamemode_configuration") safe_load(gateway, "gateway_configuration") safe_load(general, "general_configuration") - safe_load(instancing, "instancing_configuration") safe_load(ipintel, "ipintel_configuration") safe_load(jobs, "job_configuration") safe_load(logging, "logging_configuration") diff --git a/code/controllers/configuration/sections/instancing_configuration.dm b/code/controllers/configuration/sections/instancing_configuration.dm deleted file mode 100644 index bc5c6c8406a..00000000000 --- a/code/controllers/configuration/sections/instancing_configuration.dm +++ /dev/null @@ -1,20 +0,0 @@ -/// Config holder for stuff relating to multi-server instances -/datum/configuration_section/instancing_configuration - /// ID of this specific server - var/server_id = "paradise_main" - /// List of all peer servers - var/list/datum/peer_server/peers = list() - -/datum/configuration_section/instancing_configuration/load_data(list/data) - // Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line - CONFIG_LOAD_STR(server_id, data["server_id"]) - - if(islist(data["peer_servers"])) - for(var/list/server in data["peer_servers"]) - if(server["server_port"] == world.port) // Skip our own instance - continue - var/datum/peer_server/PS = new() - PS.internal_ip = server["internal_ip"] - PS.server_port = server["server_port"] - PS.commskey = server["commskey"] - peers.Add(PS) diff --git a/code/controllers/configuration/sections/system_configuration.dm b/code/controllers/configuration/sections/system_configuration.dm index 42e563d7057..d4d4a3fe28c 100644 --- a/code/controllers/configuration/sections/system_configuration.dm +++ b/code/controllers/configuration/sections/system_configuration.dm @@ -16,6 +16,10 @@ var/_2fa_auth_host = null /// List of IP addresses which bypass world topic rate limiting var/list/topic_ip_ratelimit_bypass = list() + /// Server instance ID + var/instance_id = "paradise_main" + /// Server internal IP + var/internal_ip = "127.0.0.1" /datum/configuration_section/system_configuration/load_data(list/data) // Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line @@ -28,3 +32,6 @@ CONFIG_LOAD_STR(_2fa_auth_host, data["_2fa_auth_host"]) CONFIG_LOAD_LIST(topic_ip_ratelimit_bypass, data["topic_ip_ratelimit_bypass"]) + + CONFIG_LOAD_STR(instance_id, data["instance_id"]) + CONFIG_LOAD_STR(internal_ip, data["internal_ip"]) diff --git a/code/controllers/subsystem/instancing.dm b/code/controllers/subsystem/instancing.dm index 87ad3f06e7c..30214e010a8 100644 --- a/code/controllers/subsystem/instancing.dm +++ b/code/controllers/subsystem/instancing.dm @@ -1,84 +1,124 @@ SUBSYSTEM_DEF(instancing) name = "Instancing" runlevels = RUNLEVEL_INIT | RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME + wait = 30 SECONDS + flags = SS_KEEP_TIMING /// Has our initial check complete? Used to halt init but not lag the server var/initial_check_complete = FALSE /// Is a check currently running? var/check_running = FALSE /datum/controller/subsystem/instancing/Initialize(start_timeofday) - // Do an initial peer check - check_peers(TRUE) // Force because of time memes - UNTIL(initial_check_complete) // Wait here a bit + // Dont even bother if we arent connected + if(!SSdbcore.IsConnected()) + flags |= SS_NO_FIRE + return ..() + update_heartbeat() // Make sure you do this before announcing to peers, or no one will hear your announcement var/startup_msg = "The server [GLOB.configuration.general.server_name] is now starting up. The map is [SSmapping.map_datum.fluff_name] ([SSmapping.map_datum.technical_name]). You can connect with the Switch Server verb." - message_all_peers(startup_msg, send_reping = TRUE) + message_all_peers(startup_msg) return ..() /datum/controller/subsystem/instancing/fire(resumed) - check_peers() + update_heartbeat() + update_playercache() /** - * Refreshes all peers on the server + * Playercache updater * - * Called periodically during fire() as well as when a new peer reports itself as online - * Only one instance of this proc may run at a time - * - * Arguments: - * * force - Do we want to force check all of them + * Updates the player cache in the DB. Different from heartbeat so we can force invoke it on player operations */ -/datum/controller/subsystem/instancing/proc/check_peers(force = FALSE) - set waitfor = FALSE // This has sleeps if it cant topic so we dont want to bog things down - if(check_running) +/datum/controller/subsystem/instancing/proc/update_playercache(optional_ckey) + // You may be wondering, why the fuck is an "optional ckey" variable here + // Well, this is invoked in client/New(), and needs to read from GLOB.clients + // However, this proc sleeps, and if you sleep during client/New() once the client is in GLOB.clients, stuff breaks bad + // (See my comment rambling in client/New()) + // By passing the ckey through, we can sleep in this proc and still get the data + if(!SSdbcore.IsConnected()) return + // First iterate clients to get ckeys + var/list/ckeys = list() + for(var/client/C in GLOB.clients) // No code review. I am not doing the `as anything` bullshit, because we *do* need the type checks here to avoid null clients which do happen sometimes + ckeys += C.ckey + // Add our optional + if(optional_ckey) + ckeys += optional_ckey + // Note: We dont have to sort the list here. The only time this is read for is a search, + // and since BYOND lists are linked lists internally, order doesnt matter + var/ckey_json = json_encode(ckeys) - check_running = TRUE // Dont allow multiple of these - // A NOTE TO ANYONE ELSE WHO LOOKS AT THIS - // THESE TIMINGS ARE A FUCKING NIGHTMARE AND YOU WILL NEED DEBUG LOGGING TO TEST THEM - // DO NOT FUCK WITH THE TIMINGS -aa - for(var/datum/peer_server/PS in GLOB.configuration.instancing.peers) - // If the server hasnt been discovered and its been more than 5 minutes - if((!PS.discovered && PS.last_operation_time + 5 MINUTES > world.time)) - if(!force) // No, code review. This is not going in the same line. - continue + // Yes I care about performance savings this much here to mass execute this shit + var/list/datum/db_query/queries = list() + queries += SSdbcore.NewQuery("UPDATE instance_data_cache SET key_value=:json WHERE key_name='playerlist' AND server_id=:sid", list( + "json" = ckey_json, + "sid" = GLOB.configuration.system.instance_id + )) + queries += SSdbcore.NewQuery("UPDATE instance_data_cache SET key_value=:count WHERE key_name='playercount' AND server_id=:sid", list( + "count" = length(ckeys), + "sid" = GLOB.configuration.system.instance_id + )) - // Only run main operations once every minute anyway - if(PS.last_operation_time + 1 MINUTES > world.time) - if(!force) - continue + SSdbcore.MassExecute(queries, TRUE, TRUE, FALSE, FALSE) - var/peer_response = world.Export("byond://[PS.internal_ip]:[PS.server_port]?server_discovery&key=[PS.commskey]") - if(!peer_response) - PS.online = FALSE // Peer is offline - PS.last_operation_time = world.time - continue +/** + * Heartbeat updater + * + * Updates the heartbeat in the DB. Used so other servers can see when this one was alive + */ +/datum/controller/subsystem/instancing/proc/update_heartbeat() + // this could probably just go in fire() but clean code and profiler ease who cares + var/datum/db_query/dbq = SSdbcore.NewQuery("UPDATE instance_data_cache SET key_value=NOW() WHERE key_name='heartbeat' AND server_id=:sid", list( + "sid" = GLOB.configuration.system.instance_id + )) + dbq.warn_execute() + qdel(dbq) - // We got a response - PS.discovered = TRUE - PS.online = TRUE - var/list/peer_data = json_decode(peer_response) +/** + * Seed data + * + * Seeds all our data into the DB for other servers to discover from. + * This is called during world/New() instead of on initialize so it can be done *instantly* + */ +/datum/controller/subsystem/instancing/proc/seed_data() + // We need to seed a lot of keys, so lets just use a key-value-pair-map to do this easily + var/list/kvp_map = list() + kvp_map["server_name"] = GLOB.configuration.general.server_name // Name of the server + kvp_map["server_port"] = world.port // Server port (used for redirection and topics) + kvp_map["topic_key"] = GLOB.configuration.system.topic_key // Server topic key (used for topics) + kvp_map["internal_ip"] = GLOB.configuration.system.internal_ip // Server internal IP (used for topics) + kvp_map["playercount"] = length(GLOB.clients) // Server client count (used for status info) + kvp_map["playerlist"] = json_encode(list()) // Server client list. Used for dupe login checks. This gets filled in later + kvp_map["heartbeat"] = SQLtime() // SQL timestamp for heartbeat purposes. Any server without a heartbeat in the last 60 seconds can be considered dead + // Also note for above. You may say "But AA you dont need to JSON encode it, just use "\[]"." + // Well to that I say, no. This is meant to be JSON regardless, and it should represent that. This proc is ran once during world/New() + // An extra nanosecond of load will make zero difference. - PS.external_ip = peer_data["external_ip"] - PS.server_id = peer_data["server_id"] - PS.server_name = peer_data["server_name"] - PS.playercount = peer_data["playercount"] + for(var/key in kvp_map) + var/datum/db_query/dbq = SSdbcore.NewQuery("INSERT INTO instance_data_cache (server_id, key_name, key_value) VALUES (:sid, :kn, :kv) ON DUPLICATE KEY UPDATE key_value=:kv2", // Is this necessary? Who knows! + list( + "sid" = GLOB.configuration.system.instance_id, + "kn" = key, + "kv" = "[kvp_map[key]]", // String encoding IS necessary since these tables use strings, not ints + "kv2" = "[kvp_map[key]]", // Dont know if I need the second but better to be safe + ) + ) + dbq.warn_execute(FALSE) // Do NOT async execute here because world/New() shouldnt sleep. EVER. You get issues if you do. + qdel(dbq) - PS.last_operation_time = world.time - - check_running = FALSE - initial_check_complete = TRUE /** * Message all peers * - * Wrapper for [topic_all_peers] to autoformat a message topic. Will send a server-wide announcement to the other servers - * including any relevant detail + * Wrapper for [topic_all_peers] to format the input into a message topic. Will send a server-wide announcement to the other servers + * * Arguments: * * message - Message to send to the other servers * * include_offline - Whether to topic offline servers on the off chance they came online */ -/datum/controller/subsystem/instancing/proc/message_all_peers(message, include_offline = FALSE, send_reping = FALSE) +/datum/controller/subsystem/instancing/proc/message_all_peers(message) + if(!SSdbcore.IsConnected()) + return var/topic_string = "instance_announce&msg=[html_encode(message)]" - topic_all_peers(topic_string, include_offline, send_reping) + topic_all_peers(topic_string) /** * Sends a topic to all peers @@ -89,7 +129,28 @@ SUBSYSTEM_DEF(instancing) * * raw_topic - The raw topic to send to the other servers * * include_offline - Whether to topic offline servers on the off chance they came online */ -/datum/controller/subsystem/instancing/proc/topic_all_peers(raw_topic, include_offline = FALSE, send_reping = FALSE) - for(var/datum/peer_server/PS in GLOB.configuration.instancing.peers) - if(PS.online || include_offline) - world.Export("byond://[PS.internal_ip]:[PS.server_port]?[raw_topic]&key=[PS.commskey][send_reping ? "&repoll=1" : ""]") +/datum/controller/subsystem/instancing/proc/topic_all_peers(raw_topic) + // Someone here is going to say "AA you shouldnt put load on the DB server you can do sorting in BYOND" + // Well let me put it this way. The DB server is an entirely different machine to BYOND,w ith this entire dataset being stored in its RAM, not even on disk + // By making the DB server do the work, we can offload from BYOND, which is already strained + var/datum/db_query/dbq1 = SSdbcore.NewQuery({" + SELECT server_id, key_name, key_value FROM instance_data_cache WHERE server_id IN + (SELECT server_id FROM instance_data_cache WHERE server_id !=:sid AND + key_name='heartbeat' AND last_updated BETWEEN NOW() - INTERVAL 60 SECOND AND NOW()) + AND key_name IN ("topic_key", "internal_ip", "server_port")"}, list( + "sid" = GLOB.configuration.system.instance_id + )) + if(!dbq1.warn_execute()) + qdel(dbq1) + return + + var/servers_outer = list() + while(dbq1.NextRow()) + if(!servers_outer[dbq1.item[1]]) + servers_outer[dbq1.item[1]] = list() + + servers_outer[dbq1.item[1]][dbq1.item[2]] = dbq1.item[3] // This should assoc load our data + + for(var/server in servers_outer) + var/server_data = servers_outer[server] + world.Export("byond://[server_data["internal_ip"]]:[server_data["server_port"]]?[raw_topic]&key=[server_data["topic_key"]]") diff --git a/code/game/world.dm b/code/game/world.dm index 2153a5517b5..e8d048c2d4e 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -30,6 +30,7 @@ GLOBAL_LIST_INIT(map_transition_config, list(CC_TRANSITION_CONFIG)) // Right off the bat, load up the DB SSdbcore.CheckSchemaVersion() // This doesnt just check the schema version, it also connects to the db! This needs to happen super early! I cannot stress this enough! SSdbcore.SetRoundID() // Set the round ID here + SSinstancing.seed_data() // Set us up in the DB // Setup all log paths and stamp them with startups, including round IDs SetupLogs() diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 2a1d77da5e1..3768ccbf833 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -139,7 +139,6 @@ GLOBAL_LIST_INIT(admin_verbs_server, list( /client/proc/set_ooc, /client/proc/reset_ooc, /client/proc/set_next_map, - /client/proc/refresh_instances, /client/proc/manage_queue, /client/proc/add_queue_server_bypass )) diff --git a/code/modules/admin/verbs/view_instances.dm b/code/modules/admin/verbs/view_instances.dm index 3cedf9614a0..bbef0b184dc 100644 --- a/code/modules/admin/verbs/view_instances.dm +++ b/code/modules/admin/verbs/view_instances.dm @@ -7,6 +7,8 @@ return to_chat(usr, "Server instances info") + to_chat(usr, "AA you need to finish this you lazy oaf") + /* for(var/datum/peer_server/PS in GLOB.configuration.instancing.peers) // We havnt even been discovered, so we cant even be online if(!PS.discovered) @@ -20,13 +22,5 @@ // If we are here, we are online, so we can do a rich report to_chat(usr, "ID [PS.server_id] - ONLINE (Players: [PS.playercount])") + */ -/client/proc/refresh_instances() - set name = "Force Refresh Server Instances" - set desc = "Force refresh the local cache of server instances" - set category = "Server" - - if(!check_rights(R_SERVER)) - return - - SSinstancing.check_peers(TRUE) // Force check diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index c841c759229..9e7858e20b1 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -326,12 +326,16 @@ if(length(related_accounts_cid)) log_admin("[key_name(src)] Alts by CID: [jointext(related_accounts_cid, " ")]") + // This sleeps so it has to go here. Dont fucking move it. + SSinstancing.update_playercache(ckey) + // This has to go here to avoid issues // If you sleep past this point, you will get SSinput errors as well as goonchat errors // DO NOT STUFF RANDOM SQL QUERIES BELOW THIS POINT WITHOUT USING `INVOKE_ASYNC()` OR SIMILAR // YOU WILL BREAK STUFF. SERIOUSLY. -aa07 GLOB.clients += src + spawn() // Goonchat does some non-instant checks in start() chatOutput.start() @@ -434,6 +438,7 @@ GLOB.admins -= src GLOB.directory -= ckey GLOB.clients -= src + SSinstancing.update_playercache() // Clear us out QDEL_NULL(chatOutput) if(movingmob) movingmob.client_mobs_in_contents -= mob diff --git a/code/modules/world_topic/instance_announce.dm b/code/modules/world_topic/instance_announce.dm index 250046cd53b..d4ecaa1ba85 100644 --- a/code/modules/world_topic/instance_announce.dm +++ b/code/modules/world_topic/instance_announce.dm @@ -3,12 +3,5 @@ requires_commskey = TRUE /datum/world_topic_handler/instance_announce/execute(list/input, key_valid) - var/msg = input["msg"] - if(input["repoll"]) // Repoll peers - UNTIL(!SSinstancing.check_running) // If we are running, wait - SSinstancing.check_peers(TRUE) // Manually check, with FORCE - UNTIL(!SSinstancing.check_running) // Wait for completion - - // Now that we repolled, we can tell the playerbase - to_chat(world, "
Attention

[msg]
") + to_chat(world, "
Attention

[input["msg"]]
") SEND_SOUND(world, sound('sound/misc/notice2.ogg')) // Same as captains priority announce diff --git a/code/modules/world_topic/server_discovery.dm b/code/modules/world_topic/server_discovery.dm deleted file mode 100644 index 8558f5c380c..00000000000 --- a/code/modules/world_topic/server_discovery.dm +++ /dev/null @@ -1,12 +0,0 @@ -/datum/world_topic_handler/server_discovery - topic_key = "server_discovery" - requires_commskey = TRUE - -/datum/world_topic_handler/server_discovery/execute(list/input, key_valid) - // We need to send the stuff to make a /datum/peer_server on the other side - var/list/server_data = list() - server_data["external_ip"] = world.internet_address - server_data["server_id"] = GLOB.configuration.instancing.server_id - server_data["server_name"] = GLOB.configuration.general.server_name - server_data["playercount"] = length(GLOB.clients) - return json_encode(server_data) diff --git a/config/example/config.toml b/config/example/config.toml index 4f765f7ab88..9d3f1303ace 100644 --- a/config/example/config.toml +++ b/config/example/config.toml @@ -142,7 +142,7 @@ ipc_screens = [ # Enable/disable the database on a whole sql_enabled = false # SQL version. If this is a mismatch, round start will be delayed -sql_version = 25 +sql_version = 26 # SQL server address. Can be an IP or DNS name sql_address = "127.0.0.1" # SQL server port @@ -376,21 +376,6 @@ random_ai_lawset = true ################################################################ -[instancing_configuration] -# This section contains all the information for instancing servers (Multiple paracode servers on the same database) - -# ID of this specific server. Override when needed -server_id = "paradise_main" -# List of all peer servers. Add all of the servers inside this configuration. -# The code will ignore its own instance by port checking -peer_servers = [ - #{internal_ip = "10.0.0.30", server_port = 6666, commskey = "please_use_something_secure"} -] - - -################################################################ - - [ipintel_configuration] # This section contains all the information for IPIntel (The Anti VPN system) @@ -735,6 +720,13 @@ shutdown_on_reboot = false #_2fa_auth_host = "http://127.0.0.1:8080" # List of IP addresses to be ignored by the world/Topic rate limiting. Useful if you have other services topic_ip_ratelimit_bypass = ["127.0.0.1"] +# Server instance ID. This is used for tagging the server in the database +# You do NOT want to change this once you are running in production +instance_id = "aa_debug" +# Server internal IP. Used if you are splitting instances over multiple internal IPs. +# In most cases this is just 127.0.0.1 +internal_ip = "127.0.0.1" + ################################################################ diff --git a/paradise.dme b/paradise.dme index e3135864f0c..89df5cbfbc0 100644 --- a/paradise.dme +++ b/paradise.dme @@ -196,7 +196,6 @@ #include "code\controllers\configuration\sections\gamemode_configuration.dm" #include "code\controllers\configuration\sections\gateway_configuration.dm" #include "code\controllers\configuration\sections\general_configuration.dm" -#include "code\controllers\configuration\sections\instancing_configuration.dm" #include "code\controllers\configuration\sections\ipintel_configuration.dm" #include "code\controllers\configuration\sections\job_configuration.dm" #include "code\controllers\configuration\sections\logging_configuration.dm" @@ -2533,7 +2532,6 @@ #include "code\modules\world_topic\ping.dm" #include "code\modules\world_topic\players.dm" #include "code\modules\world_topic\queue_status.dm" -#include "code\modules\world_topic\server_discovery.dm" #include "code\modules\world_topic\status.dm" #include "goon\code\datums\browserOutput.dm" #include "interface\interface.dm" From ec88e662e1b7a5be606748a45f4d80c6276ed14a Mon Sep 17 00:00:00 2001 From: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com> Date: Tue, 21 Sep 2021 00:21:05 +0100 Subject: [PATCH 09/19] Fixes --- code/controllers/subsystem/instancing.dm | 4 +++- code/modules/world_topic/instance_announce.dm | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/code/controllers/subsystem/instancing.dm b/code/controllers/subsystem/instancing.dm index 30214e010a8..ad9cfcbaaa6 100644 --- a/code/controllers/subsystem/instancing.dm +++ b/code/controllers/subsystem/instancing.dm @@ -117,7 +117,7 @@ SUBSYSTEM_DEF(instancing) /datum/controller/subsystem/instancing/proc/message_all_peers(message) if(!SSdbcore.IsConnected()) return - var/topic_string = "instance_announce&msg=[html_encode(message)]" + var/topic_string = "instance_announce&msg=[url_encode(message)]" topic_all_peers(topic_string) /** @@ -151,6 +151,8 @@ SUBSYSTEM_DEF(instancing) servers_outer[dbq1.item[1]][dbq1.item[2]] = dbq1.item[3] // This should assoc load our data + qdel(dbq1) + for(var/server in servers_outer) var/server_data = servers_outer[server] world.Export("byond://[server_data["internal_ip"]]:[server_data["server_port"]]?[raw_topic]&key=[server_data["topic_key"]]") diff --git a/code/modules/world_topic/instance_announce.dm b/code/modules/world_topic/instance_announce.dm index d4ecaa1ba85..dc58e11ce4f 100644 --- a/code/modules/world_topic/instance_announce.dm +++ b/code/modules/world_topic/instance_announce.dm @@ -3,5 +3,6 @@ requires_commskey = TRUE /datum/world_topic_handler/instance_announce/execute(list/input, key_valid) - to_chat(world, "
Attention

[input["msg"]]
") + var/msg = input["msg"] + to_chat(world, "
Attention

[msg]
") SEND_SOUND(world, sound('sound/misc/notice2.ogg')) // Same as captains priority announce From 14a6fb2c90cb1ab5a1dbfa305d44055cb8db33ff Mon Sep 17 00:00:00 2001 From: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com> Date: Sat, 2 Oct 2021 19:45:13 +0100 Subject: [PATCH 10/19] Test aaaaaaaaaaaaaaa --- code/controllers/subsystem/instancing.dm | 38 ++++++++++++++++++++---- code/modules/admin/IsBanned.dm | 6 ++++ 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/code/controllers/subsystem/instancing.dm b/code/controllers/subsystem/instancing.dm index ad9cfcbaaa6..6849d71f1fd 100644 --- a/code/controllers/subsystem/instancing.dm +++ b/code/controllers/subsystem/instancing.dm @@ -3,10 +3,6 @@ SUBSYSTEM_DEF(instancing) runlevels = RUNLEVEL_INIT | RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME wait = 30 SECONDS flags = SS_KEEP_TIMING - /// Has our initial check complete? Used to halt init but not lag the server - var/initial_check_complete = FALSE - /// Is a check currently running? - var/check_running = FALSE /datum/controller/subsystem/instancing/Initialize(start_timeofday) // Dont even bother if we arent connected @@ -131,7 +127,7 @@ SUBSYSTEM_DEF(instancing) */ /datum/controller/subsystem/instancing/proc/topic_all_peers(raw_topic) // Someone here is going to say "AA you shouldnt put load on the DB server you can do sorting in BYOND" - // Well let me put it this way. The DB server is an entirely different machine to BYOND,w ith this entire dataset being stored in its RAM, not even on disk + // Well let me put it this way. The DB server is an entirely different machine to BYOND, with this entire dataset being stored in its RAM, not even on disk // By making the DB server do the work, we can offload from BYOND, which is already strained var/datum/db_query/dbq1 = SSdbcore.NewQuery({" SELECT server_id, key_name, key_value FROM instance_data_cache WHERE server_id IN @@ -156,3 +152,35 @@ SUBSYSTEM_DEF(instancing) for(var/server in servers_outer) var/server_data = servers_outer[server] world.Export("byond://[server_data["internal_ip"]]:[server_data["server_port"]]?[raw_topic]&key=[server_data["topic_key"]]") + + +/** + * Player checker + * + * Check all connected peers to see if a player exists in the player cache + * This is used to make sure players dont log into 2 servers at once + * Arguments: + * ckey - The ckey to check if they are logged into another server + */ +/datum/controller/subsystem/instancing/proc/check_player(ckey) + // Please see above rant on L133 + var/datum/db_query/dbq1 = SSdbcore.NewQuery({" + SELECT server_id, key_value FROM instance_data_cache WHERE server_id IN + (SELECT server_id FROM instance_data_cache WHERE server_id != :sid AND + key_name='heartbeat' AND last_updated BETWEEN NOW() - INTERVAL 60 SECOND AND NOW()) + AND key_name IN ("playerlist")"}, list( + "sid" = GLOB.configuration.system.instance_id + )) + if(!dbq1.warn_execute()) + qdel(dbq1) + return + + while(dbq1.NextRow()) + var/list/other_server_cache = json_decode(dbq1.item[2]) + if(ckey in other_server_cache) + var/target_server = dbq1.item[1] // Yes. This var is necessary. + qdel(dbq1) + return target_server + + qdel(dbq1) + return null // If we are here, it means we didnt find our player on another server diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm index aadb658470f..9e04eb3c8ba 100644 --- a/code/modules/admin/IsBanned.dm +++ b/code/modules/admin/IsBanned.dm @@ -27,6 +27,12 @@ if(A && (A.rights & R_ADMIN)) admin = 1 + // Lets see if they are logged in on another paradise server + if(SSdbcore.IsConnected()) + var/other_server_login = SSinstancing.check_player(ckey) + if(other_server_login) + return list("reason"="duplicate login", "desc"="\nReason: You are already logged in on server '[other_server_login]'. Please contact the server host if you believe this is an error.") + //Guest Checking if(GLOB.configuration.general.guest_ban && IsGuestKey(key)) log_adminwarn("Failed Login: [key] [computer_id] [address] - Guests not allowed") From dd52f3a487583c0ce7ec717c3cda1cc1f10e3aab Mon Sep 17 00:00:00 2001 From: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com> Date: Tue, 12 Oct 2021 20:29:19 +0100 Subject: [PATCH 11/19] Deconflict --- SQL/updates/26-27.sql | 11 +++++++++++ code/__DEFINES/misc.dm | 2 +- config/example/config.toml | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 SQL/updates/26-27.sql diff --git a/SQL/updates/26-27.sql b/SQL/updates/26-27.sql new file mode 100644 index 00000000000..8b1291a99ba --- /dev/null +++ b/SQL/updates/26-27.sql @@ -0,0 +1,11 @@ +# Updates DB from 26 to 27 -AffectedArc07 +# Adds a new table for instance data caching, and server_id fields on other tables + +CREATE TABLE `instance_data_cache` ( + `server_id` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_general_ci', + `key_name` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_general_ci', + `key_value` VARCHAR(50) NOT NULL COLLATE 'utf8mb4_general_ci', + `last_updated` TIMESTAMP NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(), + PRIMARY KEY (`server_id`, `key_name`) USING HASH +) COLLATE='utf8mb4_general_ci' ENGINE=MEMORY; + diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index e16635d1c07..c6b7e9ac430 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -365,7 +365,7 @@ #define INVESTIGATE_BOMB "bombs" // The SQL version required by this version of the code -#define SQL_VERSION 26 +#define SQL_VERSION 27 // Vending machine stuff #define CAT_NORMAL 1 diff --git a/config/example/config.toml b/config/example/config.toml index 1c269333da9..a929c964de5 100644 --- a/config/example/config.toml +++ b/config/example/config.toml @@ -142,7 +142,7 @@ ipc_screens = [ # Enable/disable the database on a whole sql_enabled = false # SQL version. If this is a mismatch, round start will be delayed -sql_version = 26 +sql_version = 27 # SQL server address. Can be an IP or DNS name sql_address = "127.0.0.1" # SQL server port From 980f21b4f4dcf34c57d34a5e22f0c0e5db9b2a0f Mon Sep 17 00:00:00 2001 From: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com> Date: Tue, 12 Oct 2021 20:47:53 +0100 Subject: [PATCH 12/19] DB schemas --- SQL/paradise_schema.sql | 6 ++++++ SQL/updates/26-27.sql | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql index 166c543b3bb..4931c2e0556 100644 --- a/SQL/paradise_schema.sql +++ b/SQL/paradise_schema.sql @@ -114,6 +114,7 @@ CREATE TABLE `death` ( `pod` text NOT NULL COMMENT 'Place of death', `coord` text NOT NULL COMMENT 'X, Y, Z POD', `tod` datetime NOT NULL COMMENT 'Time of death', + `server_id` TEXT NULL DEFAULT NULL, `job` text NOT NULL, `special` text NOT NULL, `name` text NOT NULL, @@ -197,6 +198,7 @@ CREATE TABLE `ban` ( `bantime` datetime NOT NULL, `ban_round_id` INT(11) NULL DEFAULT NULL, `serverip` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL, + `server_id` VARCHAR(50) NULL DEFAULT NULL COLLATE utf8mb4_general_ci, `bantype` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL, `reason` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, `job` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL, @@ -320,6 +322,7 @@ CREATE TABLE `karma` ( `receiverspecial` text, `isnegative` tinyint(1) DEFAULT NULL, `spenderip` text NOT NULL, + `server_id` TEXT NULL DEFAULT NULL, `time` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=73614 DEFAULT CHARSET=utf8mb4; @@ -374,6 +377,7 @@ CREATE TABLE `legacy_population` ( `id` int(11) NOT NULL AUTO_INCREMENT, `playercount` int(11) DEFAULT NULL, `admincount` int(11) DEFAULT NULL, + `server_id` VARCHAR(50) NULL DEFAULT NULL, `time` datetime NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2550 DEFAULT CHARSET=utf8mb4; @@ -532,6 +536,7 @@ CREATE TABLE `connection_log` ( `ckey` varchar(32) NOT NULL, `ip` varchar(32) NOT NULL, `computerid` varchar(32) NOT NULL, + `server_id` VARCHAR(50) NULL DEFAULT NULL, `result` ENUM('ESTABLISHED','DROPPED - IPINTEL','DROPPED - BANNED','DROPPED - INVALID') NOT NULL DEFAULT 'ESTABLISHED' COLLATE 'utf8mb4_general_ci', PRIMARY KEY (`id`), KEY `ckey` (`ckey`), @@ -584,6 +589,7 @@ CREATE TABLE `round` ( `shuttle_name` VARCHAR(64) NULL, `map_name` VARCHAR(32) NULL, `station_name` VARCHAR(80) NULL, + `server_id` VARCHAR(50) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/SQL/updates/26-27.sql b/SQL/updates/26-27.sql index 8b1291a99ba..3acef9a45a8 100644 --- a/SQL/updates/26-27.sql +++ b/SQL/updates/26-27.sql @@ -9,3 +9,22 @@ CREATE TABLE `instance_data_cache` ( PRIMARY KEY (`server_id`, `key_name`) USING HASH ) COLLATE='utf8mb4_general_ci' ENGINE=MEMORY; +ALTER TABLE `ban` + ADD COLUMN `server_id` VARCHAR(50) NULL DEFAULT NULL AFTER `serverip`; + +ALTER TABLE `connection_log` + ADD COLUMN `server_id` VARCHAR(50) NULL DEFAULT NULL AFTER `computerid`; + +ALTER TABLE `death` + ADD COLUMN `server_id` TEXT NULL AFTER `tod`; + +ALTER TABLE `karma` + ADD COLUMN `server_id` TEXT NULL AFTER `spenderip`; + +ALTER TABLE `legacy_population` + ADD COLUMN `server_id` VARCHAR(50) NULL DEFAULT NULL AFTER `admincount`; + +# Notes already has a column for this + +ALTER TABLE `round` + ADD COLUMN `server_id` VARCHAR(50) NULL DEFAULT NULL AFTER `station_name`; From 7dc3d906d1843f47398e3bfa6496ab4c8804d6ae Mon Sep 17 00:00:00 2001 From: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com> Date: Wed, 13 Oct 2021 18:26:41 +0100 Subject: [PATCH 13/19] Actually tack the values on --- SQL/updates/26-27.sql | 2 ++ code/__HELPERS/unsorted.dm | 5 +++-- code/controllers/subsystem/blackbox.dm | 7 ++++--- code/controllers/subsystem/dbcore.dm | 4 ++-- code/controllers/subsystem/statistics.dm | 5 +++-- code/modules/admin/db_ban/functions.dm | 7 ++++--- code/modules/admin/sql_notes.dm | 9 ++------- code/modules/karma/karma.dm | 7 ++++--- 8 files changed, 24 insertions(+), 22 deletions(-) diff --git a/SQL/updates/26-27.sql b/SQL/updates/26-27.sql index 3acef9a45a8..b4931236600 100644 --- a/SQL/updates/26-27.sql +++ b/SQL/updates/26-27.sql @@ -25,6 +25,8 @@ ALTER TABLE `legacy_population` ADD COLUMN `server_id` VARCHAR(50) NULL DEFAULT NULL AFTER `admincount`; # Notes already has a column for this +ALTER TABLE `notes` + CHANGE COLUMN `server` `server` VARCHAR(50) NULL COLLATE 'utf8mb4_general_ci' AFTER `edits`; ALTER TABLE `round` ADD COLUMN `server_id` VARCHAR(50) NULL DEFAULT NULL AFTER `station_name`; diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 6d3f119af47..399f669761e 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -2061,11 +2061,12 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) /proc/log_connection(ckey, ip, cid, connection_type) ASSERT(connection_type in list(CONNECTION_TYPE_ESTABLISHED, CONNECTION_TYPE_DROPPED_IPINTEL, CONNECTION_TYPE_DROPPED_BANNED, CONNECTION_TYPE_DROPPED_INVALID)) - var/datum/db_query/query_accesslog = SSdbcore.NewQuery("INSERT INTO connection_log (`datetime`, `ckey`, `ip`, `computerid`, `result`) VALUES(Now(), :ckey, :ip, :cid, :result)", list( + var/datum/db_query/query_accesslog = SSdbcore.NewQuery("INSERT INTO connection_log (`datetime`, `ckey`, `ip`, `computerid`, `result`, `server_id`) VALUES(Now(), :ckey, :ip, :cid, :result, :server_id)", list( "ckey" = ckey, "ip" = "[ip ? ip : ""]", // This is important. NULL is not the same as "", and if you directly open the `.dmb` file, you get a NULL IP. "cid" = cid, - "result" = connection_type + "result" = connection_type, + "server_id" = GLOB.configuration.system.instance_id )) query_accesslog.warn_execute() qdel(query_accesslog) diff --git a/code/controllers/subsystem/blackbox.dm b/code/controllers/subsystem/blackbox.dm index 37e4299b06d..273fd71dbc1 100644 --- a/code/controllers/subsystem/blackbox.dm +++ b/code/controllers/subsystem/blackbox.dm @@ -287,8 +287,8 @@ SUBSYSTEM_DEF(blackbox) lakey = L.lastattackerckey var/datum/db_query/deathquery = SSdbcore.NewQuery({" - INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) - VALUES (:name, :key, :job, :special, :pod, NOW(), :laname, :lakey, :gender, :bruteloss, :fireloss, :brainloss, :oxyloss, :coord)"}, + INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord, server_id) + VALUES (:name, :key, :job, :special, :pod, NOW(), :laname, :lakey, :gender, :bruteloss, :fireloss, :brainloss, :oxyloss, :coord, :server_id)"}, list( "name" = L.real_name, "key" = L.key, @@ -302,7 +302,8 @@ SUBSYSTEM_DEF(blackbox) "fireloss" = L.getFireLoss(), "brainloss" = L.getBrainLoss(), "oxyloss" = L.getOxyLoss(), - "coord" = "[L.x], [L.y], [L.z]" + "coord" = "[L.x], [L.y], [L.z]", + "server_id" = GLOB.configuration.system.instance_id ) ) deathquery.warn_execute() diff --git a/code/controllers/subsystem/dbcore.dm b/code/controllers/subsystem/dbcore.dm index f6974a2fb17..ed7e8c25596 100644 --- a/code/controllers/subsystem/dbcore.dm +++ b/code/controllers/subsystem/dbcore.dm @@ -160,8 +160,8 @@ SUBSYSTEM_DEF(dbcore) if(!IsConnected()) return var/datum/db_query/query_round_initialize = SSdbcore.NewQuery( - "INSERT INTO round (initialize_datetime, server_ip, server_port) VALUES (Now(), INET_ATON(:internet_address), :port)", - list("internet_address" = world.internet_address || "0", "port" = "[world.port]") + "INSERT INTO round (initialize_datetime, server_ip, server_port, server_id) VALUES (Now(), INET_ATON(:internet_address), :port, :server_id)", + list("internet_address" = world.internet_address || "0", "port" = "[world.port]", "server_id" = GLOB.configuration.system.instance_id) ) query_round_initialize.Execute(async = FALSE) GLOB.round_id = "[query_round_initialize.last_insert_id]" diff --git a/code/controllers/subsystem/statistics.dm b/code/controllers/subsystem/statistics.dm index 63c20742904..01ebaf215f7 100644 --- a/code/controllers/subsystem/statistics.dm +++ b/code/controllers/subsystem/statistics.dm @@ -18,10 +18,11 @@ SUBSYSTEM_DEF(statistics) return else var/datum/db_query/statquery = SSdbcore.NewQuery( - "INSERT INTO legacy_population (playercount, admincount, time) VALUES (:playercount, :admincount, NOW())", + "INSERT INTO legacy_population (playercount, admincount, time, server_id) VALUES (:playercount, :admincount, NOW(), :server_id)", list( "playercount" = length(GLOB.clients), - "admincount" = length(GLOB.admins) + "admincount" = length(GLOB.admins), + "server_id" = GLOB.configuration.system.instance_id ) ) statquery.warn_execute() diff --git a/code/modules/admin/db_ban/functions.dm b/code/modules/admin/db_ban/functions.dm index d16b61ae690..999ba84b5e6 100644 --- a/code/modules/admin/db_ban/functions.dm +++ b/code/modules/admin/db_ban/functions.dm @@ -146,8 +146,8 @@ qdel(adm_query) var/datum/db_query/query_insert = SSdbcore.NewQuery({" - INSERT INTO ban (`id`,`bantime`,`serverip`,`bantype`,`reason`,`job`,`duration`,`rounds`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`,`edits`,`unbanned`,`unbanned_datetime`,`unbanned_ckey`,`unbanned_computerid`,`unbanned_ip`,`ban_round_id`,`unbanned_round_id`) - VALUES (null, Now(), :serverip, :bantype_str, :reason, :job, :duration, :rounds, Now() + INTERVAL :duration MINUTE, :ckey, :computerid, :ip, :a_ckey, :a_computerid, :a_ip, :who, :adminwho, '', null, null, null, null, null, :roundid, null) + INSERT INTO ban (`id`,`bantime`,`serverip`,`bantype`,`reason`,`job`,`duration`,`rounds`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`,`edits`,`unbanned`,`unbanned_datetime`,`unbanned_ckey`,`unbanned_computerid`,`unbanned_ip`,`ban_round_id`,`unbanned_round_id`, `server_id`) + VALUES (null, Now(), :serverip, :bantype_str, :reason, :job, :duration, :rounds, Now() + INTERVAL :duration MINUTE, :ckey, :computerid, :ip, :a_ckey, :a_computerid, :a_ip, :who, :adminwho, '', null, null, null, null, null, :roundid, null, :server_id) "}, list( // Get ready for parameters "serverip" = serverip, @@ -164,7 +164,8 @@ "a_ip" = a_ip, "who" = who, "adminwho" = adminwho, - "roundid" = GLOB.round_id + "roundid" = GLOB.round_id, + "server_id" = GLOB.configuration.system.instance_id )) if(!query_insert.warn_execute()) qdel(query_insert) diff --git a/code/modules/admin/sql_notes.dm b/code/modules/admin/sql_notes.dm index af7a7150d92..2fc02253315 100644 --- a/code/modules/admin/sql_notes.dm +++ b/code/modules/admin/sql_notes.dm @@ -1,5 +1,4 @@ -// Do not attemtp to remove the blank string from the server arg. It will break DB saving. -/proc/add_note(target_ckey, notetext, timestamp, adminckey, logged = 1, server = "", checkrights = 1, show_after = TRUE, automated = FALSE) +/proc/add_note(target_ckey, notetext, timestamp, adminckey, logged = 1, checkrights = 1, show_after = TRUE, automated = FALSE) if(checkrights && !check_rights(R_ADMIN|R_MOD)) return if(!SSdbcore.IsConnected()) @@ -53,10 +52,6 @@ else if(usr && (usr.ckey == ckey(adminckey))) // Don't ckeyize special note sources adminckey = ckey(adminckey) - if(!server) - if(GLOB.configuration.general.server_name) - server = GLOB.configuration.general.server_name - // Force cast this to 1/0 incase someone tries to feed bad data automated = !!automated @@ -67,7 +62,7 @@ "targetckey" = target_ckey, "notetext" = notetext, "adminkey" = adminckey, - "server" = server, + "server" = GLOB.configuration.system.instance_id, "crewnum" = crew_number, "roundid" = GLOB.round_id, "automated" = automated diff --git a/code/modules/karma/karma.dm b/code/modules/karma/karma.dm index dd0743cfcef..f684a8b261c 100644 --- a/code/modules/karma/karma.dm +++ b/code/modules/karma/karma.dm @@ -16,15 +16,16 @@ return var/datum/db_query/log_query = SSdbcore.NewQuery({" - INSERT INTO karma (spendername, spenderkey, receivername, receiverkey, receiverrole, receiverspecial, spenderip, time) - VALUES (:sname, :skey, :rname, :rkey, :rrole, :rspecial, :sip, Now())"}, list( + INSERT INTO karma (spendername, spenderkey, receivername, receiverkey, receiverrole, receiverspecial, spenderip, time, server_id) + VALUES (:sname, :skey, :rname, :rkey, :rrole, :rspecial, :sip, Now(), :server_id)"}, list( "sname" = spender.name, "skey" = spender.ckey, "rname" = receiver.name, "rkey" = receiver.ckey, "rrole" = receiverrole, "rspecial" = receiverspecial, - "sip" = spender.client.address + "sip" = spender.client.address, + "server_id" = GLOB.configuration.system.instance_id )) if(!log_query.warn_execute()) From d966e5f36c0f5adf17a65e210559aa01c47cbd9a Mon Sep 17 00:00:00 2001 From: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com> Date: Sun, 17 Oct 2021 16:04:16 +0100 Subject: [PATCH 14/19] Switch server verb --- code/controllers/master.dm | 2 +- code/game/verbs/switch_server.dm | 49 ++++++++++++++++++++++ code/modules/admin/verbs/view_instances.dm | 35 +++++++++------- paradise.dme | 1 + 4 files changed, 72 insertions(+), 15 deletions(-) create mode 100644 code/game/verbs/switch_server.dm diff --git a/code/controllers/master.dm b/code/controllers/master.dm index d445cf6f3b6..13a2a065a02 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -62,7 +62,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new /// For scheduling different subsystems for different stages of the round var/current_runlevel /// Do we want to sleep until players log in? - var/sleep_offline_after_initializations = TRUE + var/sleep_offline_after_initializations = FALSE // No we dont var/static/restart_clear = 0 var/static/restart_timeout = 0 diff --git a/code/game/verbs/switch_server.dm b/code/game/verbs/switch_server.dm new file mode 100644 index 00000000000..6fc07b8f684 --- /dev/null +++ b/code/game/verbs/switch_server.dm @@ -0,0 +1,49 @@ +/client/verb/switch_server() + set name = "Switch Server" + set desc = "Switch to a different Paradise server" + set category = "OOC" + + // First get our peers + var/datum/db_query/dbq1 = SSdbcore.NewQuery({" + SELECT server_id, key_name, key_value FROM instance_data_cache WHERE server_id IN + (SELECT server_id FROM instance_data_cache WHERE + key_name='heartbeat' AND last_updated BETWEEN NOW() - INTERVAL 60 SECOND AND NOW()) + AND key_name IN ("playercount", "server_port", "server_name")"}) + if(!dbq1.warn_execute()) + qdel(dbq1) + return + + var/servers_outer = list() + while(dbq1.NextRow()) + if(!servers_outer[dbq1.item[1]]) + servers_outer[dbq1.item[1]] = list() + + servers_outer[dbq1.item[1]][dbq1.item[2]] = dbq1.item[3] // This should assoc load our data + + // Format the server names into an assoc list of K: name V: port + var/list/formatted_servers = list() + for(var/server in servers_outer) + var/server_data = servers_outer[server] + formatted_servers["[server_data["server_name"]] - ([server_data["playercount"]] playing)"] = text2num(server_data["server_port"]) + + if(length(formatted_servers) == 1) + to_chat(usr, "You are already connected to the one online instance!") + return + + var/selected_server = input(usr, "Select a server", "Server hop") as anything in formatted_servers + if(!selected_server) + return // Should never happen + + if(formatted_servers[selected_server] == world.port) + to_chat(usr, "You are already connected to this instance!") + return + + // Now we reconnect them + to_chat(usr, "Now connecting you to: [selected_server]") + + if(watchlisted) // I mean why not + message_admins("[key_name_admin(usr)] is on the watchlist and just jumped to [selected_server]") + + // Formulate a connection URL + var/target = "byond://[world.internet_address]:[formatted_servers[selected_server]]" + src << link(target) diff --git a/code/modules/admin/verbs/view_instances.dm b/code/modules/admin/verbs/view_instances.dm index bbef0b184dc..65d6105da3a 100644 --- a/code/modules/admin/verbs/view_instances.dm +++ b/code/modules/admin/verbs/view_instances.dm @@ -7,20 +7,27 @@ return to_chat(usr, "Server instances info") - to_chat(usr, "AA you need to finish this you lazy oaf") - /* - for(var/datum/peer_server/PS in GLOB.configuration.instancing.peers) - // We havnt even been discovered, so we cant even be online - if(!PS.discovered) - to_chat(usr, "#[PS.server_port] (Undiscovered) - OFFLINE") - continue + var/datum/db_query/dbq1 = SSdbcore.NewQuery({" + SELECT server_id, key_name, key_value FROM instance_data_cache WHERE server_id IN + (SELECT server_id FROM instance_data_cache WHERE + key_name='heartbeat' AND last_updated BETWEEN NOW() - INTERVAL 60 SECOND AND NOW()) + AND key_name IN ("playercount")"}) + if(!dbq1.warn_execute()) + qdel(dbq1) + return - // We exist but arent online at the moment - if(!PS.online) - to_chat(usr, "ID [PS.server_id] - OFFLINE") - continue + var/servers_outer = list() + while(dbq1.NextRow()) + if(!servers_outer[dbq1.item[1]]) + servers_outer[dbq1.item[1]] = list() - // If we are here, we are online, so we can do a rich report - to_chat(usr, "ID [PS.server_id] - ONLINE (Players: [PS.playercount])") - */ + servers_outer[dbq1.item[1]][dbq1.item[2]] = dbq1.item[3] // This should assoc load our data + qdel(dbq1) + + for(var/server in servers_outer) + var/server_data = servers_outer[server] + var/players = text2num(server_data["playercount"]) + + to_chat(usr, "[server] - [players] player[players == 1 ? "" : "s"] online.") + to_chat(usr, "Offline instances are not reported") diff --git a/paradise.dme b/paradise.dme index 0b2b1346c29..259a5eac574 100644 --- a/paradise.dme +++ b/paradise.dme @@ -1162,6 +1162,7 @@ #include "code\game\verbs\ooc.dm" #include "code\game\verbs\randomtip.dm" #include "code\game\verbs\suicide.dm" +#include "code\game\verbs\switch_server.dm" #include "code\game\verbs\webmap.dm" #include "code\game\verbs\who.dm" #include "code\modules\admin\admin.dm" From b105b44bede933553cf906399042258ba7752a1b Mon Sep 17 00:00:00 2001 From: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com> Date: Sun, 17 Oct 2021 16:30:19 +0100 Subject: [PATCH 15/19] Lets give this a go --- code/modules/admin/admin_verbs.dm | 1 + code/modules/admin/verbs/gsay.dm | 27 +++++++++++++++++++++++++++ code/modules/world_topic/gsay.dm | 18 ++++++++++++++++++ paradise.dme | 2 ++ 4 files changed, 48 insertions(+) create mode 100644 code/modules/admin/verbs/gsay.dm create mode 100644 code/modules/world_topic/gsay.dm diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 019ccedade9..a78f3219e47 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -45,6 +45,7 @@ GLOBAL_LIST_INIT(admin_verbs_admin, list( /datum/admins/proc/toggleemoji, /*toggles using emoji in ooc for everyone*/ /client/proc/game_panel, /*game panel, allows to change game-mode etc*/ /client/proc/cmd_admin_say, /*admin-only ooc chat*/ + /client/proc/gsay, /*cross-server asay*/ /datum/admins/proc/PlayerNotes, /client/proc/cmd_mentor_say, /datum/admins/proc/show_player_notes, diff --git a/code/modules/admin/verbs/gsay.dm b/code/modules/admin/verbs/gsay.dm new file mode 100644 index 00000000000..d777fa422b2 --- /dev/null +++ b/code/modules/admin/verbs/gsay.dm @@ -0,0 +1,27 @@ +// GSAY +// Like asay but global between instances! +// *Insert changeling hivemind :g joke here* + +/client/proc/gsay(msg as text) + set name = "gsay" + set hidden = TRUE + if(!check_rights(R_ADMIN)) + return + + if(!msg) + return + msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) + + // To whoever says "Why dont you just topic the full message with formatting" + // This lets us use this in other apps, like a discord bot, without HTML parsing + // It also removes a way to put whatever HTML we want in the chat window + var/built_topic = "gsay&msg=[url_encode(msg)]&usr=[url_encode(usr.ckey)]&src=[url_encode(GLOB.configuration.system.instance_id)]" + + // Send to peers + SSinstancing.topic_all_peers(built_topic) + // Send to online admins + for(var/client/C in GLOB.admins) + if(R_ADMIN & C.holder.rights) + to_chat(C, "GSAY: [usr.ckey]@[GLOB.configuration.system.instance_id]: [msg]") + + SSblackbox.record_feedback("tally", "admin_verb", 1, "gsay") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/world_topic/gsay.dm b/code/modules/world_topic/gsay.dm new file mode 100644 index 00000000000..971375d8658 --- /dev/null +++ b/code/modules/world_topic/gsay.dm @@ -0,0 +1,18 @@ +// Just dumps the text in the admin chat box +/datum/world_topic_handler/gsay + topic_key = "gsay" + requires_commskey = TRUE + +/datum/world_topic_handler/gsay/execute(list/input, key_valid) + if(!input["msg"] || !input["usr"] || !input["src"]) + return json_encode(list("error" = "Malformed request")) + + var/message = input["msg"] + var/user = input["usr"] + var/source = input["source"] + + // Send to online admins + for(var/client/C in GLOB.admins) + if(R_ADMIN & C.holder.rights) + to_chat(C, "GSAY: [user]@[source]: [message]") + diff --git a/paradise.dme b/paradise.dme index 259a5eac574..5488d42c65c 100644 --- a/paradise.dme +++ b/paradise.dme @@ -1210,6 +1210,7 @@ #include "code\modules\admin\verbs\freeze.dm" #include "code\modules\admin\verbs\getlogs.dm" #include "code\modules\admin\verbs\gimmick_team.dm" +#include "code\modules\admin\verbs\gsay.dm" #include "code\modules\admin\verbs\infiltratorteam_syndicate.dm" #include "code\modules\admin\verbs\logging_view.dm" #include "code\modules\admin\verbs\manage_queue.dm" @@ -2522,6 +2523,7 @@ #include "code\modules\world_topic\_topic_base.dm" #include "code\modules\world_topic\adminmsg.dm" #include "code\modules\world_topic\announce.dm" +#include "code\modules\world_topic\gsay.dm" #include "code\modules\world_topic\instance_announce.dm" #include "code\modules\world_topic\manifest.dm" #include "code\modules\world_topic\ping.dm" From 767d8e08410e4a6448477ec74a8bb156250da280 Mon Sep 17 00:00:00 2001 From: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com> Date: Sun, 17 Oct 2021 16:37:40 +0100 Subject: [PATCH 16/19] Fix --- code/modules/world_topic/gsay.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/world_topic/gsay.dm b/code/modules/world_topic/gsay.dm index 971375d8658..16378cd32eb 100644 --- a/code/modules/world_topic/gsay.dm +++ b/code/modules/world_topic/gsay.dm @@ -9,7 +9,7 @@ var/message = input["msg"] var/user = input["usr"] - var/source = input["source"] + var/source = input["src"] // Send to online admins for(var/client/C in GLOB.admins) From 1d750acf78a9795611feba11547fc079bb9e0ab2 Mon Sep 17 00:00:00 2001 From: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com> Date: Sun, 17 Oct 2021 18:59:54 +0100 Subject: [PATCH 17/19] Flatty fixes --- code/controllers/subsystem/instancing.dm | 6 ++---- config/example/config.toml | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/code/controllers/subsystem/instancing.dm b/code/controllers/subsystem/instancing.dm index 6849d71f1fd..8fd80a9bad7 100644 --- a/code/controllers/subsystem/instancing.dm +++ b/code/controllers/subsystem/instancing.dm @@ -39,7 +39,7 @@ SUBSYSTEM_DEF(instancing) if(optional_ckey) ckeys += optional_ckey // Note: We dont have to sort the list here. The only time this is read for is a search, - // and since BYOND lists are linked lists internally, order doesnt matter + // and order doesnt matter for that. var/ckey_json = json_encode(ckeys) // Yes I care about performance savings this much here to mass execute this shit @@ -108,7 +108,6 @@ SUBSYSTEM_DEF(instancing) * * Arguments: * * message - Message to send to the other servers - * * include_offline - Whether to topic offline servers on the off chance they came online */ /datum/controller/subsystem/instancing/proc/message_all_peers(message) if(!SSdbcore.IsConnected()) @@ -123,7 +122,6 @@ SUBSYSTEM_DEF(instancing) * * Arguments: * * raw_topic - The raw topic to send to the other servers - * * include_offline - Whether to topic offline servers on the off chance they came online */ /datum/controller/subsystem/instancing/proc/topic_all_peers(raw_topic) // Someone here is going to say "AA you shouldnt put load on the DB server you can do sorting in BYOND" @@ -163,7 +161,7 @@ SUBSYSTEM_DEF(instancing) * ckey - The ckey to check if they are logged into another server */ /datum/controller/subsystem/instancing/proc/check_player(ckey) - // Please see above rant on L133 + // Please see above rant on L127 var/datum/db_query/dbq1 = SSdbcore.NewQuery({" SELECT server_id, key_value FROM instance_data_cache WHERE server_id IN (SELECT server_id FROM instance_data_cache WHERE server_id != :sid AND diff --git a/config/example/config.toml b/config/example/config.toml index a929c964de5..b341021d4ec 100644 --- a/config/example/config.toml +++ b/config/example/config.toml @@ -727,7 +727,7 @@ topic_ip_ratelimit_bypass = ["127.0.0.1"] is_production = false # Server instance ID. This is used for tagging the server in the database # You do NOT want to change this once you are running in production -instance_id = "aa_debug" +instance_id = "paradise_main" # Server internal IP. Used if you are splitting instances over multiple internal IPs. # In most cases this is just 127.0.0.1 internal_ip = "127.0.0.1" From 4e40353cfa9375bcf2ebd1182e4591663d36fedc Mon Sep 17 00:00:00 2001 From: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com> Date: Sun, 17 Oct 2021 21:06:02 +0100 Subject: [PATCH 18/19] Check your conflict resolution people --- code/controllers/configuration/configuration_core.dm | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/code/controllers/configuration/configuration_core.dm b/code/controllers/configuration/configuration_core.dm index 0550280527b..c96e96016e8 100644 --- a/code/controllers/configuration/configuration_core.dm +++ b/code/controllers/configuration/configuration_core.dm @@ -97,8 +97,7 @@ GLOBAL_DATUM_INIT(configuration, /datum/server_configuration, new()) var/config_file = "config/config.toml" if(!fexists(config_file)) config_file = "config/example/config.toml" // Fallback to example if user hasnt setup config properly - var/raw_json = rustg_read_toml_file(config_file) - raw_data = json_decode(raw_json) + raw_data = rustg_read_toml_file(config_file) // Now pass through all our stuff load_all_sections() @@ -141,8 +140,7 @@ GLOBAL_DATUM_INIT(configuration, /datum/server_configuration, new()) DIRECT_OUTPUT(world.log, "Overrides found for this instance. Loading them.") var/start = start_watch() // Time tracking - var/raw_json = rustg_read_toml_file(override_file) - raw_data = json_decode(raw_json) + raw_data = rustg_read_toml_file(override_file) // Now safely load our overrides. // Due to the nature of config wrappers, only vars that exist in the config file are applied to the config datums. From de48c081bfbda4753e0e87e9b659b9c3d50a4c1d Mon Sep 17 00:00:00 2001 From: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com> Date: Wed, 20 Oct 2021 17:50:43 +0100 Subject: [PATCH 19/19] Elastic logic --- code/controllers/subsystem/metrics.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/controllers/subsystem/metrics.dm b/code/controllers/subsystem/metrics.dm index 95055f99216..181f24cfb08 100644 --- a/code/controllers/subsystem/metrics.dm +++ b/code/controllers/subsystem/metrics.dm @@ -28,6 +28,7 @@ SUBSYSTEM_DEF(metrics) out["elapsed_real"] = (REALTIMEOFDAY - world_init_time) out["client_count"] = length(GLOB.clients) out["round_id"] = text2num(GLOB.round_id) // This is so we can filter the metrics by a single round ID + out["server_id"] = GLOB.configuration.system.instance_id // And this is so we can filter by instance ID // Funnel in all SS metrics var/list/ss_data = list()