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] 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"