world/Topic Refactor (#14850)

* world/Topic Refactor

* These arent needed anymore

* How could I forget this

* Biggest edit ever

* Forgot this

* Mini refactor
This commit is contained in:
AffectedArc07
2020-12-09 11:24:33 -05:00
committed by GitHub
parent c5d66be971
commit d6af52dd2b
14 changed files with 316 additions and 240 deletions
+5
View File
@@ -0,0 +1,5 @@
# NOTE
Please keep things in this directory standardised!
This means one topic handler per DM file, and the filename being the key. For an example, see `ping.dm`
@@ -0,0 +1,63 @@
#define WORLD_TOPIC_STRIKES_THRESHOLD 5
#define WORLD_TOPIC_LOCKOUT_TIME 1 MINUTES
/datum/world_topic_spam_prevention_handler
/// Amount of strikes. [WORLD_TOPIC_STRIKES_THRESHOLD] strikes is a lockout of [WORLD_TOPIC_LOCKOUT_TIME]
var/strikes = 0
/// Time of last request
var/last_request = 0
/// Is this IP currently locked out
var/locked_out = FALSE
/// Unlock time
var/unlock_time = 0
/**
* Lockout handler
*
* Updates strikes and timers of the most recent client to topic the server
* including any relevant detail
*/
/datum/world_topic_spam_prevention_handler/proc/check_lockout()
// Check if they are already locked out
if(locked_out && (unlock_time >= world.time))
// Relock out for another minute if youre spamming
unlock_time = world.time + WORLD_TOPIC_LOCKOUT_TIME
return TRUE
// If they were locked out and are now allowed, unlock them
if(locked_out && (unlock_time < world.time))
strikes = 0
locked_out = FALSE
// Allow a grace period of 0.5 seconds per topic, or you get a strike
if(last_request + 5 > world.time)
strikes++
if(strikes >= WORLD_TOPIC_STRIKES_THRESHOLD)
locked_out = TRUE
unlock_time = world.time + WORLD_TOPIC_LOCKOUT_TIME
return TRUE
// If we got here, assume they arent locked out
last_request = world.time
return FALSE
/*
Uncomment this if you modify the topic limiter, trust me, youll need to test it
/client/verb/debug_limiter()
if(!GLOB.world_topic_spam_prevention_handlers[address])
GLOB.world_topic_spam_prevention_handlers[address] = new /datum/world_topic_spam_prevention_handler
var/datum/world_topic_spam_prevention_handler/sph = GLOB.world_topic_spam_prevention_handlers[address]
var/result = sph.check_lockout()
to_chat(usr, "Strikes: [sph.strikes]")
to_chat(usr, "Last request: [sph.last_request]")
to_chat(usr, "Locked out: [sph.locked_out]")
to_chat(usr, "Unlock time: [sph.unlock_time]")
to_chat(usr, "SPH Result: [result]")
to_chat(usr, "World.time: [world.time]")
to_chat(usr, "LR DIFF: [(sph.last_request - world.time)/10]s")
to_chat(usr, "LO DIFF: [(sph.unlock_time - world.time)/10]s")
to_chat(usr, "<hr>")
*/
+36
View File
@@ -0,0 +1,36 @@
/datum/world_topic_handler
/// Key which invokes this topic
var/topic_key
/// Set this to TRUE if the topic handler needs an authorised comms key
var/requires_commskey = FALSE
/**
* Invokes the world/Topic handler
*
* This includes sanity checking for if the key is required, as well as other sanity checks
* DO NOT OVERRIDE
* Arguments:
* * input - The list of topic data, sent from [world/Topic]
*/
/datum/world_topic_handler/proc/invoke(list/input)
SHOULD_NOT_OVERRIDE(TRUE)
var/authorised = (config.comms_password && input["key"] == config.comms_password) // No password means no comms, not any password
if(requires_commskey && !authorised)
// Try keep all returns in JSON unless absolutely necessary (?ping for example)
return(json_encode(list("error" = "Invalid Key")))
return execute(input, authorised)
/**
* Actually executes the user's topic
*
* Override this to do your work in subtypes of this topic
*
* Arguments:
* * input - The list of topic data, sent from [world/Topic]
* * key_valid - Has the user entered the correct auth key
*/
/datum/world_topic_handler/proc/execute(list/input, key_valid = FALSE)
PRIVATE_PROC(TRUE) // Ensures no one can call this arbitrarily without checking key auth
CRASH("execute() not implemented/overridden for [type]")
+38
View File
@@ -0,0 +1,38 @@
/datum/world_topic_handler/adminmsg
topic_key = "adminmsg"
requires_commskey = TRUE
/datum/world_topic_handler/adminmsg/execute(list/input, key_valid)
/*
We got an adminmsg from the Discord bot, so lets split the input then validate the input. Expected output:
1. adminmsg = ckey of person the message is to
2. msg = contents of message, params2list requires
3. sender = the discord name that send the message.
*/
var/client/C
for(var/client/K in GLOB.clients)
if(K.ckey == input["adminmsg"])
C = K
break
if(!C)
return json_encode(list("error" = "No client with that name on server"))
var/message = "<font color='red'>Discord PM from <b><a href='?discord_msg=1'>[input["sender"]]</a></b>: [input["msg"]]</font>"
var/amessage = "<font color='blue'>Discord PM from <a href='?discord_msg=1'>[input["sender"]]</a> to <b>[key_name_admin(C)]</b>: [input["msg"]]</font>"
// THESE TWO VARS DO VERY DIFFERENT THINGS. DO NOT ATTEMPT TO COMBINE THEM
C.received_discord_pm = world.time
C.last_discord_pm_time = 0
SEND_SOUND(C, 'sound/effects/adminhelp.ogg')
to_chat(C, message)
for(var/client/A in GLOB.admins)
// GLOB.admins includes anyone with a holder datum (mentors too). This makes sure only admins see ahelps
if(check_rights(R_ADMIN, FALSE, A.mob))
if(A != C)
to_chat(A, amessage)
return json_encode(list("success" = "Message Successful"))
+11
View File
@@ -0,0 +1,11 @@
/datum/world_topic_handler/announce
topic_key = "announce"
requires_commskey = TRUE
/datum/world_topic_handler/announce/execute(list/input, key_valid)
var/prtext = input["announce"]
var/pr_substring = copytext(prtext, 1, 23)
if(pr_substring == "Pull Request merged by")
GLOB.pending_server_update = TRUE
for(var/client/C in GLOB.clients)
to_chat(C, "<span class='announce'>PR: [prtext]</span>")
+7
View File
@@ -0,0 +1,7 @@
/datum/world_topic_handler/hostannounce
topic_key = "hostannounce"
requires_commskey = TRUE
/datum/world_topic_handler/hostannounce/execute(list/input, key_valid)
GLOB.pending_server_update = TRUE
to_chat(world, "<hr><span style='color: #12A5F4'><b>Server Announcement:</b> [input["message"]]</span><hr>")
+35
View File
@@ -0,0 +1,35 @@
/datum/world_topic_handler/manifest
topic_key = "manifest"
/datum/world_topic_handler/manifest/execute(list/input, key_valid)
var/list/positions = list()
var/list/set_names = list(
"heads" = GLOB.command_positions,
"sec" = GLOB.security_positions,
"eng" = GLOB.engineering_positions,
"med" = GLOB.medical_positions,
"sci" = GLOB.science_positions,
"car" = GLOB.supply_positions,
"srv" = GLOB.service_positions,
"civ" = GLOB.civilian_positions,
"bot" = GLOB.nonhuman_positions
)
for(var/datum/data/record/t in GLOB.data_core.general)
var/name = t.fields["name"]
var/rank = t.fields["rank"]
var/real_rank = t.fields["real_rank"]
var/department = FALSE
for(var/k in set_names)
if(real_rank in set_names[k])
if(!positions[k])
positions[k] = list()
positions[k][name] = rank
department = TRUE
if(!department)
if(!positions["misc"])
positions["misc"] = list()
positions["misc"][name] = rank
return json_encode(positions)
+14
View File
@@ -0,0 +1,14 @@
/datum/world_topic_handler/ping
topic_key = "ping"
/datum/world_topic_handler/ping/execute(list/input, key_valid)
/*
Basically a more efficient version of
if("ping" in input)
var/x = 1
for(var/client/C)
x++
return x
*/
return length(GLOB.clients) + 1
+10
View File
@@ -0,0 +1,10 @@
/datum/world_topic_handler/playerlist
topic_key = "playerlist"
/datum/world_topic_handler/playerlist/execute(list/input, key_valid)
var/list/keys = list()
for(var/I in GLOB.clients)
var/client/C = I
keys += C.key
return json_encode(keys)
+53
View File
@@ -0,0 +1,53 @@
/datum/world_topic_handler/status
topic_key = "status"
/datum/world_topic_handler/status/execute(list/input, key_valid)
var/list/status_info = list()
var/list/admins = list()
status_info["version"] = GLOB.revision_info.commit_hash
status_info["mode"] = GLOB.master_mode
status_info["respawn"] = GLOB.abandon_allowed
status_info["enter"] = GLOB.enter_allowed
status_info["vote"] = config.allow_vote_mode
status_info["ai"] = config.allow_ai
status_info["host"] = world.host ? world.host : null
status_info["players"] = list()
status_info["roundtime"] = worldtime2text()
status_info["stationtime"] = station_time_timestamp()
status_info["oldstationtime"] = classic_worldtime2text() // more "consistent" indication of the round's running time
status_info["listed"] = "Public"
if(!world.hub_password)
status_info["listed"] = "Invisible"
var/player_count = 0
var/admin_count = 0
for(var/client/C in GLOB.clients)
if(C.holder)
if(C.holder.fakekey)
continue //so stealthmins aren't revealed by the hub
admin_count++
admins += list(list(C.key, C.holder.rank))
player_count++
status_info["players"] = player_count
status_info["admins"] = admin_count
status_info["map_name"] = GLOB.map_name ? GLOB.map_name : "Unknown"
// Add more info if we are authed
if(key_valid)
if(SSticker && SSticker.mode)
status_info["real_mode"] = SSticker.mode.name
status_info["security_level"] = get_security_level()
status_info["ticker_state"] = SSticker.current_state
if(SSshuttle && SSshuttle.emergency)
// Shuttle status, see /__DEFINES/stat.dm
status_info["shuttle_mode"] = SSshuttle.emergency.mode
// Shuttle timer, in seconds
status_info["shuttle_timer"] = SSshuttle.emergency.timeLeft()
for(var/i in 1 to admins.len)
var/list/A = admins[i]
status_info["admin[i - 1]"] = A[1]
status_info["adminrank[i - 1]"] = A[2]
return json_encode(status_info)