Merge branch 'master' into topiccommandlimit
This commit is contained in:
@@ -47,6 +47,7 @@
|
||||
// Subsystems shutdown in the reverse of the order they initialize in
|
||||
// The numbers just define the ordering, they are meaningless otherwise.
|
||||
|
||||
#define INIT_ORDER_FAIL2TOPIC 22
|
||||
#define INIT_ORDER_TITLE 20
|
||||
#define INIT_ORDER_GARBAGE 19
|
||||
#define INIT_ORDER_DBCORE 18
|
||||
|
||||
@@ -78,7 +78,6 @@
|
||||
if (CONFIG_GET(flag/log_manifest))
|
||||
WRITE_LOG(GLOB.world_manifest_log, "[ckey] \\ [body.real_name] \\ [mind.assigned_role] \\ [mind.special_role ? mind.special_role : "NONE"] \\ [latejoin ? "LATEJOIN":"ROUNDSTART"]")
|
||||
|
||||
|
||||
/proc/log_say(text)
|
||||
if (CONFIG_GET(flag/log_say))
|
||||
WRITE_LOG(GLOB.world_game_log, "SAY: [text]")
|
||||
@@ -121,7 +120,6 @@
|
||||
if (CONFIG_GET(flag/log_vote))
|
||||
WRITE_LOG(GLOB.world_game_log, "VOTE: [text]")
|
||||
|
||||
|
||||
/proc/log_topic(text)
|
||||
WRITE_LOG(GLOB.world_game_log, "TOPIC: [text]")
|
||||
|
||||
@@ -141,6 +139,9 @@
|
||||
if (CONFIG_GET(flag/log_job_debug))
|
||||
WRITE_LOG(GLOB.world_job_debug_log, "JOB: [text]")
|
||||
|
||||
/proc/log_subsystem(subsystem, text)
|
||||
WRITE_LOG(GLOB.subsystem_log, "[subsystem]: [text]")
|
||||
|
||||
/* Log to both DD and the logfile. */
|
||||
/proc/log_world(text)
|
||||
#ifdef USE_CUSTOM_ERROR_HANDLER
|
||||
|
||||
@@ -30,6 +30,8 @@ GLOBAL_VAR(world_virus_log)
|
||||
GLOBAL_PROTECT(world_virus_log)
|
||||
GLOBAL_VAR(world_map_error_log)
|
||||
GLOBAL_PROTECT(world_map_error_log)
|
||||
GLOBAL_VAR(subsystem_log)
|
||||
GLOBAL_PROTECT(subsystem_log)
|
||||
|
||||
GLOBAL_LIST_EMPTY(bombers)
|
||||
GLOBAL_PROTECT(bombers)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/datum/config_entry/number/fail2topic_rate_limit
|
||||
config_entry_value = 10 //deciseconds
|
||||
|
||||
/datum/config_entry/number/fail2topic_max_fails
|
||||
config_entry_value = 5
|
||||
|
||||
/datum/config_entry/string/fail2topic_rule_name
|
||||
config_entry_value = "_dd_fail2topic"
|
||||
protection = CONFIG_ENTRY_LOCKED //affects physical server configuration, no touchies!!
|
||||
|
||||
/datum/config_entry/flag/fail2topic_enabled
|
||||
config_entry_value = TRUE
|
||||
|
||||
/datum/config_entry/number/topic_max_size
|
||||
config_entry_value = 8192
|
||||
|
||||
/datum/config_entry/keyed_list/topic_rate_limit_whitelist
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_FLAG
|
||||
@@ -54,7 +54,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
var/static/restart_clear = 0
|
||||
var/static/restart_timeout = 0
|
||||
var/static/restart_count = 0
|
||||
|
||||
|
||||
var/static/random_seed
|
||||
|
||||
//current tick limit, assigned before running a subsystem.
|
||||
@@ -69,7 +69,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
if(!random_seed)
|
||||
random_seed = (TEST_RUN_PARAMETER in world.params) ? 29051994 : rand(1, 1e9)
|
||||
rand_seed(random_seed)
|
||||
|
||||
|
||||
var/list/_subsystems = list()
|
||||
subsystems = _subsystems
|
||||
if (Master != src)
|
||||
|
||||
@@ -155,6 +155,8 @@
|
||||
if(SS_SLEEPING)
|
||||
state = SS_PAUSING
|
||||
|
||||
/datum/controller/subsystem/proc/subsystem_log(msg)
|
||||
return log_subsystem(name, msg)
|
||||
|
||||
//used to initialize the subsystem AFTER the map has loaded
|
||||
/datum/controller/subsystem/Initialize(start_timeofday)
|
||||
@@ -162,7 +164,7 @@
|
||||
var/time = (REALTIMEOFDAY - start_timeofday) / 10
|
||||
var/msg = "Initialized [name] subsystem within [time] second[time == 1 ? "" : "s"]!"
|
||||
to_chat(world, "<span class='boldannounce'>[msg]</span>")
|
||||
log_world(msg)
|
||||
log_subsystem("INIT", msg)
|
||||
return time
|
||||
|
||||
//hook for printing stats to the "MC" statuspanel for admins to see performance and related stats etc.
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
SUBSYSTEM_DEF(fail2topic)
|
||||
name = "Fail2Topic"
|
||||
init_order = INIT_ORDER_FAIL2TOPIC
|
||||
flags = SS_BACKGROUND
|
||||
runlevels = ALL
|
||||
|
||||
var/list/rate_limiting = list()
|
||||
var/list/fail_counts = list()
|
||||
var/list/active_bans = list()
|
||||
|
||||
var/rate_limit
|
||||
var/max_fails
|
||||
var/rule_name
|
||||
var/enabled = FALSE
|
||||
|
||||
/datum/controller/subsystem/fail2topic/Initialize(timeofday)
|
||||
rate_limit = CONFIG_GET(number/fail2topic_rate_limit)
|
||||
max_fails = CONFIG_GET(number/fail2topic_max_fails)
|
||||
rule_name = CONFIG_GET(string/fail2topic_rule_name)
|
||||
enabled = CONFIG_GET(flag/fail2topic_enabled)
|
||||
|
||||
DropFirewallRule() // Clear the old bans if any still remain
|
||||
|
||||
if (world.system_type == UNIX && enabled)
|
||||
enabled = FALSE
|
||||
subsystem_log("DISABLED - UNIX systems are not supported.")
|
||||
if(!enabled)
|
||||
flags |= SS_NO_FIRE
|
||||
can_fire = FALSE
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/fail2topic/fire()
|
||||
while (rate_limiting.len)
|
||||
var/ip = rate_limiting[1]
|
||||
var/last_attempt = rate_limiting[ip]
|
||||
|
||||
if (world.time - last_attempt > rate_limit)
|
||||
rate_limiting -= ip
|
||||
fail_counts -= ip
|
||||
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
/datum/controller/subsystem/fail2topic/Shutdown()
|
||||
DropFirewallRule()
|
||||
|
||||
/datum/controller/subsystem/fail2topic/proc/IsRateLimited(ip)
|
||||
var/last_attempt = rate_limiting[ip]
|
||||
|
||||
var/static/datum/config_entry/keyed_list/topic_rate_limit_whitelist/cached_whitelist_entry
|
||||
if(!istype(cached_whitelist_entry))
|
||||
cached_whitelist_entry = CONFIG_GET(keyed_list/topic_rate_limit_whitelist)
|
||||
|
||||
if(istype(cached_whitelist_entry))
|
||||
if(cached_whitelist_entry.config_entry_value[ip])
|
||||
return FALSE
|
||||
|
||||
if (active_bans[ip])
|
||||
return TRUE
|
||||
|
||||
rate_limiting[ip] = world.time
|
||||
|
||||
if (isnull(last_attempt))
|
||||
return FALSE
|
||||
|
||||
if (world.time - last_attempt > rate_limit)
|
||||
fail_counts -= ip
|
||||
return FALSE
|
||||
else
|
||||
var/failures = fail_counts[ip]
|
||||
|
||||
if (isnull(failures))
|
||||
fail_counts[ip] = 1
|
||||
return TRUE
|
||||
else if (failures > max_fails)
|
||||
BanFromFirewall(ip)
|
||||
return TRUE
|
||||
else
|
||||
fail_counts[ip] = failures + 1
|
||||
return TRUE
|
||||
|
||||
/datum/controller/subsystem/fail2topic/proc/BanFromFirewall(ip)
|
||||
if (!enabled)
|
||||
return
|
||||
|
||||
active_bans[ip] = world.time
|
||||
fail_counts -= ip
|
||||
rate_limiting -= ip
|
||||
|
||||
. = shell("netsh advfirewall firewall add rule name=\"[rule_name]\" dir=in interface=any action=block remoteip=[ip]")
|
||||
|
||||
if (.)
|
||||
subsystem_log("Failed to ban [ip]. Exit code: [.].")
|
||||
else if (isnull(.))
|
||||
subsystem_log("Failed to invoke shell to ban [ip].")
|
||||
else
|
||||
subsystem_log("Banned [ip].")
|
||||
|
||||
/datum/controller/subsystem/fail2topic/proc/DropFirewallRule()
|
||||
if (!enabled)
|
||||
return
|
||||
|
||||
active_bans = list()
|
||||
|
||||
. = shell("netsh advfirewall firewall delete rule name=\"[rule_name]\"")
|
||||
|
||||
if (.)
|
||||
subsystem_log("Failed to drop firewall rule. Exit code: [.].")
|
||||
else if (isnull(.))
|
||||
subsystem_log("Failed to invoke shell for firewall rule drop.")
|
||||
else
|
||||
subsystem_log("Firewall rule dropped.")
|
||||
+189
-189
@@ -1,189 +1,189 @@
|
||||
// SETUP
|
||||
|
||||
/proc/TopicHandlers()
|
||||
. = list()
|
||||
var/list/all_handlers = subtypesof(/datum/world_topic)
|
||||
for(var/I in all_handlers)
|
||||
var/datum/world_topic/WT = I
|
||||
var/keyword = initial(WT.keyword)
|
||||
if(!keyword)
|
||||
warning("[WT] has no keyword! Ignoring...")
|
||||
continue
|
||||
var/existing_path = .[keyword]
|
||||
if(existing_path)
|
||||
warning("[existing_path] and [WT] have the same keyword! Ignoring [WT]...")
|
||||
else if(keyword == "key")
|
||||
warning("[WT] has keyword 'key'! Ignoring...")
|
||||
else
|
||||
.[keyword] = WT
|
||||
|
||||
// DATUM
|
||||
|
||||
/datum/world_topic
|
||||
var/keyword
|
||||
var/log = TRUE
|
||||
var/key_valid
|
||||
var/require_comms_key = FALSE
|
||||
|
||||
/datum/world_topic/proc/TryRun(list/input)
|
||||
key_valid = config && (CONFIG_GET(string/comms_key) == input["key"])
|
||||
if(require_comms_key && !key_valid)
|
||||
return "Bad Key"
|
||||
input -= "key"
|
||||
. = Run(input)
|
||||
if(islist(.))
|
||||
. = list2params(.)
|
||||
|
||||
/datum/world_topic/proc/Run(list/input)
|
||||
CRASH("Run() not implemented for [type]!")
|
||||
|
||||
// TOPICS
|
||||
|
||||
/datum/world_topic/ping
|
||||
keyword = "ping"
|
||||
log = FALSE
|
||||
|
||||
/datum/world_topic/ping/Run(list/input)
|
||||
. = 0
|
||||
for (var/client/C in GLOB.clients)
|
||||
++.
|
||||
|
||||
/datum/world_topic/playing
|
||||
keyword = "playing"
|
||||
log = FALSE
|
||||
|
||||
/datum/world_topic/playing/Run(list/input)
|
||||
return GLOB.player_list.len
|
||||
|
||||
/datum/world_topic/pr_announce
|
||||
keyword = "announce"
|
||||
require_comms_key = TRUE
|
||||
var/static/list/PRcounts = list() //PR id -> number of times announced this round
|
||||
|
||||
/datum/world_topic/pr_announce/Run(list/input)
|
||||
var/list/payload = json_decode(input["payload"])
|
||||
var/id = "[payload["pull_request"]["id"]]"
|
||||
if(!PRcounts[id])
|
||||
PRcounts[id] = 1
|
||||
else
|
||||
++PRcounts[id]
|
||||
if(PRcounts[id] > PR_ANNOUNCEMENTS_PER_ROUND)
|
||||
return
|
||||
|
||||
var/final_composed = "<span class='announce'>PR: [input[keyword]]</span>"
|
||||
for(var/client/C in GLOB.clients)
|
||||
C.AnnouncePR(final_composed)
|
||||
|
||||
/datum/world_topic/ahelp_relay
|
||||
keyword = "Ahelp"
|
||||
require_comms_key = TRUE
|
||||
|
||||
/datum/world_topic/ahelp_relay/Run(list/input)
|
||||
relay_msg_admins("<span class='adminnotice'><b><font color=red>HELP: </font> [input["source"]] [input["message_sender"]]: [input["message"]]</b></span>")
|
||||
|
||||
/datum/world_topic/comms_console
|
||||
keyword = "Comms_Console"
|
||||
require_comms_key = TRUE
|
||||
|
||||
/datum/world_topic/comms_console/Run(list/input)
|
||||
minor_announce(input["message"], "Incoming message from [input["message_sender"]]")
|
||||
for(var/obj/machinery/computer/communications/CM in GLOB.machines)
|
||||
CM.overrideCooldown()
|
||||
|
||||
/datum/world_topic/news_report
|
||||
keyword = "News_Report"
|
||||
require_comms_key = TRUE
|
||||
|
||||
/datum/world_topic/news_report/Run(list/input)
|
||||
minor_announce(input["message"], "Breaking Update From [input["message_sender"]]")
|
||||
|
||||
/datum/world_topic/server_hop
|
||||
keyword = "server_hop"
|
||||
|
||||
/datum/world_topic/server_hop/Run(list/input)
|
||||
var/expected_key = input[keyword]
|
||||
for(var/mob/dead/observer/O in GLOB.player_list)
|
||||
if(O.key == expected_key)
|
||||
if(O.client)
|
||||
new /obj/screen/splash(O.client, TRUE)
|
||||
break
|
||||
|
||||
/datum/world_topic/adminmsg
|
||||
keyword = "adminmsg"
|
||||
require_comms_key = TRUE
|
||||
|
||||
/datum/world_topic/adminmsg/Run(list/input)
|
||||
return IrcPm(input[keyword], input["msg"], input["sender"])
|
||||
|
||||
/datum/world_topic/namecheck
|
||||
keyword = "namecheck"
|
||||
require_comms_key = TRUE
|
||||
|
||||
/datum/world_topic/namecheck/Run(list/input)
|
||||
//Oh this is a hack, someone refactor the functionality out of the chat command PLS
|
||||
var/datum/tgs_chat_command/namecheck/NC = new
|
||||
var/datum/tgs_chat_user/user = new
|
||||
user.friendly_name = input["sender"]
|
||||
user.mention = user.friendly_name
|
||||
return NC.Run(user, input["namecheck"])
|
||||
|
||||
/datum/world_topic/adminwho
|
||||
keyword = "adminwho"
|
||||
require_comms_key = TRUE
|
||||
|
||||
/datum/world_topic/adminwho/Run(list/input)
|
||||
return ircadminwho()
|
||||
|
||||
/datum/world_topic/status
|
||||
keyword = "status"
|
||||
|
||||
/datum/world_topic/status/Run(list/input)
|
||||
if(!key_valid) //If we have a key, then it's safe to trust that this isn't a malicious packet. Also prevents the extra info from leaking
|
||||
if(GLOB.topic_status_lastcache <= world.time + 5)
|
||||
return GLOB.topic_status_cache
|
||||
GLOB.topic_status_lastcache = world.time
|
||||
. = list()
|
||||
.["version"] = GLOB.game_version
|
||||
.["mode"] = "hidden" //CIT CHANGE - hides the gamemode in topic() calls to prevent meta'ing the gamemode
|
||||
.["respawn"] = config ? !CONFIG_GET(flag/norespawn) : FALSE
|
||||
.["enter"] = GLOB.enter_allowed
|
||||
.["vote"] = CONFIG_GET(flag/allow_vote_mode)
|
||||
.["ai"] = CONFIG_GET(flag/allow_ai)
|
||||
.["host"] = world.host ? world.host : null
|
||||
.["round_id"] = GLOB.round_id
|
||||
.["players"] = GLOB.clients.len
|
||||
.["revision"] = GLOB.revdata.commit
|
||||
.["revision_date"] = GLOB.revdata.date
|
||||
|
||||
var/list/adm = get_admin_counts()
|
||||
var/list/presentmins = adm["present"]
|
||||
var/list/afkmins = adm["afk"]
|
||||
.["admins"] = presentmins.len + afkmins.len //equivalent to the info gotten from adminwho
|
||||
.["gamestate"] = SSticker.current_state
|
||||
|
||||
.["map_name"] = SSmapping.config?.map_name || "Loading..."
|
||||
|
||||
if(key_valid)
|
||||
.["active_players"] = get_active_player_count()
|
||||
if(SSticker.HasRoundStarted())
|
||||
.["real_mode"] = SSticker.mode.name
|
||||
// Key-authed callers may know the truth behind the "secret"
|
||||
|
||||
.["security_level"] = get_security_level()
|
||||
.["round_duration"] = SSticker ? round((world.time-SSticker.round_start_time)/10) : 0
|
||||
// Amount of world's ticks in seconds, useful for calculating round duration
|
||||
|
||||
//Time dilation stats.
|
||||
.["time_dilation_current"] = SStime_track.time_dilation_current
|
||||
.["time_dilation_avg"] = SStime_track.time_dilation_avg
|
||||
.["time_dilation_avg_slow"] = SStime_track.time_dilation_avg_slow
|
||||
.["time_dilation_avg_fast"] = SStime_track.time_dilation_avg_fast
|
||||
|
||||
if(SSshuttle && SSshuttle.emergency)
|
||||
.["shuttle_mode"] = SSshuttle.emergency.mode
|
||||
// Shuttle status, see /__DEFINES/stat.dm
|
||||
.["shuttle_timer"] = SSshuttle.emergency.timeLeft()
|
||||
// Shuttle timer, in seconds
|
||||
|
||||
if(!key_valid)
|
||||
GLOB.topic_status_cache = .
|
||||
// SETUP
|
||||
|
||||
/proc/TopicHandlers()
|
||||
. = list()
|
||||
var/list/all_handlers = subtypesof(/datum/world_topic)
|
||||
for(var/I in all_handlers)
|
||||
var/datum/world_topic/WT = I
|
||||
var/keyword = initial(WT.keyword)
|
||||
if(!keyword)
|
||||
warning("[WT] has no keyword! Ignoring...")
|
||||
continue
|
||||
var/existing_path = .[keyword]
|
||||
if(existing_path)
|
||||
warning("[existing_path] and [WT] have the same keyword! Ignoring [WT]...")
|
||||
else if(keyword == "key")
|
||||
warning("[WT] has keyword 'key'! Ignoring...")
|
||||
else
|
||||
.[keyword] = WT
|
||||
|
||||
// DATUM
|
||||
|
||||
/datum/world_topic
|
||||
var/keyword
|
||||
var/log = TRUE
|
||||
var/key_valid
|
||||
var/require_comms_key = FALSE
|
||||
|
||||
/datum/world_topic/proc/TryRun(list/input, addr)
|
||||
key_valid = config && (CONFIG_GET(string/comms_key) == input["key"])
|
||||
if(require_comms_key && !key_valid)
|
||||
return "Bad Key"
|
||||
input -= "key"
|
||||
. = Run(input, addr)
|
||||
if(islist(.))
|
||||
. = list2params(.)
|
||||
|
||||
/datum/world_topic/proc/Run(list/input, addr)
|
||||
CRASH("Run() not implemented for [type]!")
|
||||
|
||||
// TOPICS
|
||||
|
||||
/datum/world_topic/ping
|
||||
keyword = "ping"
|
||||
log = FALSE
|
||||
|
||||
/datum/world_topic/ping/Run(list/input, addr)
|
||||
. = 0
|
||||
for (var/client/C in GLOB.clients)
|
||||
++.
|
||||
|
||||
/datum/world_topic/playing
|
||||
keyword = "playing"
|
||||
log = FALSE
|
||||
|
||||
/datum/world_topic/playing/Run(list/input, addr)
|
||||
return GLOB.player_list.len
|
||||
|
||||
/datum/world_topic/pr_announce
|
||||
keyword = "announce"
|
||||
require_comms_key = TRUE
|
||||
var/static/list/PRcounts = list() //PR id -> number of times announced this round
|
||||
|
||||
/datum/world_topic/pr_announce/Run(list/input, addr)
|
||||
var/list/payload = json_decode(input["payload"])
|
||||
var/id = "[payload["pull_request"]["id"]]"
|
||||
if(!PRcounts[id])
|
||||
PRcounts[id] = 1
|
||||
else
|
||||
++PRcounts[id]
|
||||
if(PRcounts[id] > PR_ANNOUNCEMENTS_PER_ROUND)
|
||||
return
|
||||
|
||||
var/final_composed = "<span class='announce'>PR: [input[keyword]]</span>"
|
||||
for(var/client/C in GLOB.clients)
|
||||
C.AnnouncePR(final_composed)
|
||||
|
||||
/datum/world_topic/ahelp_relay
|
||||
keyword = "Ahelp"
|
||||
require_comms_key = TRUE
|
||||
|
||||
/datum/world_topic/ahelp_relay/Run(list/input, addr)
|
||||
relay_msg_admins("<span class='adminnotice'><b><font color=red>HELP: </font> [input["source"]] [input["message_sender"]]: [input["message"]]</b></span>")
|
||||
|
||||
/datum/world_topic/comms_console
|
||||
keyword = "Comms_Console"
|
||||
require_comms_key = TRUE
|
||||
|
||||
/datum/world_topic/comms_console/Run(list/input, addr)
|
||||
minor_announce(input["message"], "Incoming message from [input["message_sender"]]")
|
||||
for(var/obj/machinery/computer/communications/CM in GLOB.machines)
|
||||
CM.overrideCooldown()
|
||||
|
||||
/datum/world_topic/news_report
|
||||
keyword = "News_Report"
|
||||
require_comms_key = TRUE
|
||||
|
||||
/datum/world_topic/news_report/Run(list/input, addr)
|
||||
minor_announce(input["message"], "Breaking Update From [input["message_sender"]]")
|
||||
|
||||
/datum/world_topic/server_hop
|
||||
keyword = "server_hop"
|
||||
|
||||
/datum/world_topic/server_hop/Run(list/input, addr)
|
||||
var/expected_key = input[keyword]
|
||||
for(var/mob/dead/observer/O in GLOB.player_list)
|
||||
if(O.key == expected_key)
|
||||
if(O.client?.address == addr)
|
||||
new /obj/screen/splash(O.client, TRUE)
|
||||
break
|
||||
|
||||
/datum/world_topic/adminmsg
|
||||
keyword = "adminmsg"
|
||||
require_comms_key = TRUE
|
||||
|
||||
/datum/world_topic/adminmsg/Run(list/input, addr)
|
||||
return IrcPm(input[keyword], input["msg"], input["sender"])
|
||||
|
||||
/datum/world_topic/namecheck
|
||||
keyword = "namecheck"
|
||||
require_comms_key = TRUE
|
||||
|
||||
/datum/world_topic/namecheck/Run(list/input, addr)
|
||||
//Oh this is a hack, someone refactor the functionality out of the chat command PLS
|
||||
var/datum/tgs_chat_command/namecheck/NC = new
|
||||
var/datum/tgs_chat_user/user = new
|
||||
user.friendly_name = input["sender"]
|
||||
user.mention = user.friendly_name
|
||||
return NC.Run(user, input["namecheck"])
|
||||
|
||||
/datum/world_topic/adminwho
|
||||
keyword = "adminwho"
|
||||
require_comms_key = TRUE
|
||||
|
||||
/datum/world_topic/adminwho/Run(list/input, addr)
|
||||
return ircadminwho()
|
||||
|
||||
/datum/world_topic/status
|
||||
keyword = "status"
|
||||
|
||||
/datum/world_topic/status/Run(list/input, addr)
|
||||
if(!key_valid) //If we have a key, then it's safe to trust that this isn't a malicious packet. Also prevents the extra info from leaking
|
||||
if(GLOB.topic_status_lastcache <= world.time + 5)
|
||||
return GLOB.topic_status_cache
|
||||
GLOB.topic_status_lastcache = world.time
|
||||
. = list()
|
||||
.["version"] = GLOB.game_version
|
||||
.["mode"] = "hidden" //CIT CHANGE - hides the gamemode in topic() calls to prevent meta'ing the gamemode
|
||||
.["respawn"] = config ? !CONFIG_GET(flag/norespawn) : FALSE
|
||||
.["enter"] = GLOB.enter_allowed
|
||||
.["vote"] = CONFIG_GET(flag/allow_vote_mode)
|
||||
.["ai"] = CONFIG_GET(flag/allow_ai)
|
||||
.["host"] = world.host ? world.host : null
|
||||
.["round_id"] = GLOB.round_id
|
||||
.["players"] = GLOB.clients.len
|
||||
.["revision"] = GLOB.revdata.commit
|
||||
.["revision_date"] = GLOB.revdata.date
|
||||
|
||||
var/list/adm = get_admin_counts()
|
||||
var/list/presentmins = adm["present"]
|
||||
var/list/afkmins = adm["afk"]
|
||||
.["admins"] = presentmins.len + afkmins.len //equivalent to the info gotten from adminwho
|
||||
.["gamestate"] = SSticker.current_state
|
||||
|
||||
.["map_name"] = SSmapping.config?.map_name || "Loading..."
|
||||
|
||||
if(key_valid)
|
||||
.["active_players"] = get_active_player_count()
|
||||
if(SSticker.HasRoundStarted())
|
||||
.["real_mode"] = SSticker.mode.name
|
||||
// Key-authed callers may know the truth behind the "secret"
|
||||
|
||||
.["security_level"] = get_security_level()
|
||||
.["round_duration"] = SSticker ? round((world.time-SSticker.round_start_time)/10) : 0
|
||||
// Amount of world's ticks in seconds, useful for calculating round duration
|
||||
|
||||
//Time dilation stats.
|
||||
.["time_dilation_current"] = SStime_track.time_dilation_current
|
||||
.["time_dilation_avg"] = SStime_track.time_dilation_avg
|
||||
.["time_dilation_avg_slow"] = SStime_track.time_dilation_avg_slow
|
||||
.["time_dilation_avg_fast"] = SStime_track.time_dilation_avg_fast
|
||||
|
||||
if(SSshuttle && SSshuttle.emergency)
|
||||
.["shuttle_mode"] = SSshuttle.emergency.mode
|
||||
// Shuttle status, see /__DEFINES/stat.dm
|
||||
.["shuttle_timer"] = SSshuttle.emergency.timeLeft()
|
||||
// Shuttle timer, in seconds
|
||||
|
||||
if(!key_valid)
|
||||
GLOB.topic_status_cache = .
|
||||
|
||||
+11
-1
@@ -115,6 +115,7 @@ GLOBAL_LIST(topic_status_cache)
|
||||
GLOB.world_runtime_log = "[GLOB.log_directory]/runtime.log"
|
||||
GLOB.query_debug_log = "[GLOB.log_directory]/query_debug.log"
|
||||
GLOB.world_job_debug_log = "[GLOB.log_directory]/job_debug.log"
|
||||
GLOB.subsystem_log = "[GLOB.log_directory]/subsystem.log"
|
||||
|
||||
#ifdef UNIT_TESTS
|
||||
GLOB.test_log = file("[GLOB.log_directory]/tests.log")
|
||||
@@ -129,6 +130,7 @@ GLOBAL_LIST(topic_status_cache)
|
||||
start_log(GLOB.world_qdel_log)
|
||||
start_log(GLOB.world_runtime_log)
|
||||
start_log(GLOB.world_job_debug_log)
|
||||
start_log(GLOB.subsystem_log)
|
||||
|
||||
GLOB.changelog_hash = md5('html/changelog.html') //for telling if the changelog has changed recently
|
||||
if(fexists(GLOB.config_error_log))
|
||||
@@ -146,6 +148,14 @@ GLOBAL_LIST(topic_status_cache)
|
||||
/world/Topic(T, addr, master, key)
|
||||
TGS_TOPIC //redirect to server tools if necessary
|
||||
|
||||
if(!SSfail2topic)
|
||||
return "Server not initialized."
|
||||
else if(SSfail2topic.IsRateLimited(addr))
|
||||
return "Rate limited."
|
||||
|
||||
if(length(T) > CONFIG_GET(number/topic_max_size))
|
||||
return "Payload too large!"
|
||||
|
||||
var/static/list/topic_handlers = TopicHandlers()
|
||||
|
||||
var/list/input = params2list(T)
|
||||
@@ -162,7 +172,7 @@ GLOBAL_LIST(topic_status_cache)
|
||||
return
|
||||
|
||||
handler = new handler()
|
||||
return handler.TryRun(input)
|
||||
return handler.TryRun(input, addr)
|
||||
|
||||
/world/proc/AnnouncePR(announcement, list/payload)
|
||||
var/static/list/PRcounts = list() //PR id -> number of times announced this round
|
||||
|
||||
@@ -172,9 +172,8 @@
|
||||
var/total_damage = brute + burn
|
||||
|
||||
if(total_damage > can_inflict)
|
||||
var/excess = total_damage - can_inflict
|
||||
brute = round(brute * (excess / total_damage),DAMAGE_PRECISION)
|
||||
burn = round(burn * (excess / total_damage),DAMAGE_PRECISION)
|
||||
brute = round(brute * (max_damage / total_damage),DAMAGE_PRECISION)
|
||||
burn = round(burn * (max_damage / total_damage),DAMAGE_PRECISION)
|
||||
|
||||
brute_dam += brute
|
||||
burn_dam += burn
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
TGS_ERROR_LOG("Custom command [command_name] can't be used as it is empty or contains illegal characters!")
|
||||
warned_command_names[command_name] = TRUE
|
||||
continue
|
||||
|
||||
|
||||
if(command_name_types[command_name])
|
||||
if(warnings_only)
|
||||
TGS_ERROR_LOG("Custom commands [command_name_types[command_name]] and [stc] have the same name, only [command_name_types[command_name]] will be available!")
|
||||
@@ -55,24 +55,24 @@ The MIT License
|
||||
|
||||
Copyright (c) 2017 Jordan Brown
|
||||
|
||||
Permission is hereby granted, free of charge,
|
||||
to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify,
|
||||
merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom
|
||||
the Software is furnished to do so,
|
||||
Permission is hereby granted, free of charge,
|
||||
to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify,
|
||||
merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom
|
||||
the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
@@ -473,3 +473,15 @@ DISABLE_HIGH_POP_MC_MODE_AMOUNT 60
|
||||
## For reference, Goonstation uses a resolution of 21x15 for it's widescreen mode.
|
||||
## Do note that changing this value will affect the title screen. The title screen will have to be updated manually if this is changed.
|
||||
DEFAULT_VIEW 21x15
|
||||
|
||||
### FAIL2TOPIC:
|
||||
### Automated IP bans for world/Topic() spammers
|
||||
## Enabled
|
||||
FAIL2TOPIC_ENABLED
|
||||
## Minimum wait time in deciseconds between valid requests
|
||||
FAIL2TOPIC_RATE_LIMIT 10
|
||||
## Number of requests after breaching rate limit that triggers a ban
|
||||
FAIL2TOPIC_MAX_FAILS 5
|
||||
## Firewall rule name used on physical server
|
||||
FAIL2TOPIC_RULE_NAME _dd_fail2topic
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
author: "Bhijn"
|
||||
delete-after: True
|
||||
changes:
|
||||
- bugfix: "server_hop can no longer be used to remotely lobotomize a spaceman"
|
||||
@@ -0,0 +1,4 @@
|
||||
author: "Putnam3145"
|
||||
delete-after: True
|
||||
changes:
|
||||
- bugfix: "Limb damage works now"
|
||||
+3
-1
@@ -229,6 +229,7 @@
|
||||
#include "code\controllers\configuration\entries\dbconfig.dm"
|
||||
#include "code\controllers\configuration\entries\donator.dm"
|
||||
#include "code\controllers\configuration\entries\dynamic.dm"
|
||||
#include "code\controllers\configuration\entries\fail2topic.dm"
|
||||
#include "code\controllers\configuration\entries\game_options.dm"
|
||||
#include "code\controllers\configuration\entries\general.dm"
|
||||
#include "code\controllers\subsystem\acid.dm"
|
||||
@@ -245,6 +246,7 @@
|
||||
#include "code\controllers\subsystem\dcs.dm"
|
||||
#include "code\controllers\subsystem\disease.dm"
|
||||
#include "code\controllers\subsystem\events.dm"
|
||||
#include "code\controllers\subsystem\fail2topic.dm"
|
||||
#include "code\controllers\subsystem\fire_burning.dm"
|
||||
#include "code\controllers\subsystem\garbage.dm"
|
||||
#include "code\controllers\subsystem\icon_smooth.dm"
|
||||
@@ -470,8 +472,8 @@
|
||||
#include "code\datums\elements\_element.dm"
|
||||
#include "code\datums\elements\cleaning.dm"
|
||||
#include "code\datums\elements\earhealing.dm"
|
||||
#include "code\datums\elements\wuv.dm"
|
||||
#include "code\datums\elements\ghost_role_eligibility.dm"
|
||||
#include "code\datums\elements\wuv.dm"
|
||||
#include "code\datums\helper_datums\events.dm"
|
||||
#include "code\datums\helper_datums\getrev.dm"
|
||||
#include "code\datums\helper_datums\icon_snapshot.dm"
|
||||
|
||||
Reference in New Issue
Block a user