diff --git a/code/__HELPERS/chat.dm b/code/__HELPERS/chat.dm
new file mode 100644
index 0000000000..57824b6286
--- /dev/null
+++ b/code/__HELPERS/chat.dm
@@ -0,0 +1,74 @@
+/*
+
+Here's how to use the chat system with configs
+
+send2adminchat is a simple function that broadcasts to admin channels
+
+send2chat is a bit verbose but can be very specific
+
+The second parameter is a string, this string should be read from a config.
+What this does is dictacte which TGS4 channels can be sent to.
+
+For example if you have the following channels in tgs4 set up
+- Channel 1, Tag: asdf
+- Channel 2, Tag: bombay,asdf
+- Channel 3, Tag: Hello my name is asdf
+- Channel 4, No Tag
+- Channel 5, Tag: butts
+
+and you make the call:
+
+send2chat("I sniff butts", CONFIG_GET(string/where_to_send_sniff_butts))
+
+and the config option is set like:
+
+WHERE_TO_SEND_SNIFF_BUTTS asdf
+
+It will be sent to channels 1 and 2
+
+Alternatively if you set the config option to just:
+
+WHERE_TO_SEND_SNIFF_BUTTS
+
+it will be sent to all connected chats.
+
+In TGS3 it will always be sent to all connected designated game chats.
+*/
+
+/**
+ * Sends a message to TGS chat channels.
+ *
+ * message - The message to send.
+ * channel_tag - Required. If "", the message with be sent to all connected (Game-type for TGS3) channels. Otherwise, it will be sent to TGS4 channels with that tag (Delimited by ','s).
+ */
+/proc/send2chat(message, channel_tag)
+ if(channel_tag == null || !world.TgsAvailable())
+ return
+
+ var/datum/tgs_version/version = world.TgsVersion()
+ if(channel_tag == "" || version.suite == 3)
+ world.TgsTargetedChatBroadcast(message, FALSE)
+ return
+
+ var/list/channels_to_use = list()
+ for(var/I in world.TgsChatChannelInfo())
+ var/datum/tgs_chat_channel/channel = I
+ var/list/applicable_tags = splittext(channel.custom_tag, ",")
+ if(channel_tag in applicable_tags)
+ channels_to_use += channel
+
+ if(channels_to_use.len)
+ world.TgsChatBroadcast(message, channels_to_use)
+
+/**
+ * Sends a message to TGS admin chat channels.
+ *
+ * category - The category of the mssage.
+ * message - The message to send.
+ */
+/proc/send2adminchat(category, message, embed_links = FALSE)
+ category = replacetext(replacetext(category, "\proper", ""), "\improper", "")
+ message = replacetext(replacetext(message, "\proper", ""), "\improper", "")
+ // if(!embed_links)
+ // message = GLOB.has_discord_embeddable_links.Replace(replacetext(message, "`", ""), " ```$1``` ")
+ world.TgsTargetedChatBroadcast("[category] | [message]", TRUE)
diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm
index e1c1e3df07..2263ac2e27 100644
--- a/code/__HELPERS/roundend.dm
+++ b/code/__HELPERS/roundend.dm
@@ -204,7 +204,7 @@
var/survival_rate = GLOB.joined_player_list.len ? "[PERCENT(popcount[POPCOUNT_SURVIVORS]/GLOB.joined_player_list.len)]%" : "there's literally no player"
- send2irc("Server", "A round of [mode.name] just ended[mode_result == "undefined" ? "." : " with a [mode_result]."] Survival rate: [survival_rate]")
+ send2adminchat("Server", "A round of [mode.name] just ended[mode_result == "undefined" ? "." : " with a [mode_result]."] Survival rate: [survival_rate]")
if(length(CONFIG_GET(keyed_list/cross_server)))
send_news_report()
diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm
index 7c251edd88..53a6f54062 100644
--- a/code/__HELPERS/text.dm
+++ b/code/__HELPERS/text.dm
@@ -13,10 +13,6 @@
* SQL sanitization
*/
-// Run all strings to be used in an SQL query through this proc first to properly escape out injection attempts.
-/proc/sanitizeSQL(t)
- return SSdbcore.Quote("[t]")
-
/proc/format_table_name(table as text)
return CONFIG_GET(string/feedback_tableprefix) + table
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index 794a495ae3..babcd5de54 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -1575,33 +1575,6 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
for(var/i in 1 to items_list[each_item])
new each_item(where_to)
-//sends a message to chat
-//config_setting should be one of the following
-//null - noop
-//empty string - use TgsTargetBroadcast with admin_only = FALSE
-//other string - use TgsChatBroadcast with the tag that matches config_setting, only works with TGS4, if using TGS3 the above method is used
-/proc/send2chat(message, config_setting)
- if(config_setting == null)
- return
-
- UNTIL(GLOB.tgs_initialized)
- if(!world.TgsAvailable())
- return
-
- var/datum/tgs_version/version = world.TgsVersion()
- if(config_setting == "" || version.suite == 3)
- world.TgsTargetedChatBroadcast(message, FALSE)
- return
-
- var/list/channels_to_use = list()
- for(var/I in world.TgsChatChannelInfo())
- var/datum/tgs_chat_channel/channel = I
- if(channel.tag == config_setting)
- channels_to_use += channel
-
- if(channels_to_use.len)
- world.TgsChatBroadcast()
-
//Checks to see if either the victim has a garlic necklace or garlic in their blood
/proc/blood_sucking_checks(var/mob/living/carbon/target, check_neck, check_blood)
//Bypass this if the target isnt carbon.
diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm
index 59b78577af..f153b5965c 100644
--- a/code/_onclick/ai.dm
+++ b/code/_onclick/ai.dm
@@ -48,7 +48,7 @@
to_chat(src, "You're experiencing a bug. Reconnect immediately to fix it. Admins have been notified.")
if(REALTIMEOFDAY >= chnotify + 9000)
chnotify = REALTIMEOFDAY
- send2irc_adminless_only("NOCHEAT", message)
+ send2tgs_adminless_only("NOCHEAT", message)
return
var/list/modifiers = params2list(params)
@@ -113,7 +113,7 @@
A.AICtrlClick(src)
/mob/living/silicon/ai/AltClickOn(var/atom/A)
A.AIAltClick(src)
-
+
/*
The following criminally helpful code is just the previous code cleaned up;
diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm
index 3c93952b65..9b6f4b88a5 100644
--- a/code/controllers/configuration/entries/general.dm
+++ b/code/controllers/configuration/entries/general.dm
@@ -319,6 +319,11 @@
/datum/config_entry/flag/panic_bunker // prevents people the server hasn't seen before from connecting
+/datum/config_entry/number/panic_bunker_living // living time in minutes that a player needs to pass the panic bunker
+
+/datum/config_entry/string/panic_bunker_message
+ config_entry_value = "Sorry but the server is currently not accepting connections from never before seen players."
+
/datum/config_entry/number/notify_new_player_age // how long do we notify admins of a new player
min_val = -1
diff --git a/code/controllers/subsystem/blackbox.dm b/code/controllers/subsystem/blackbox.dm
index 455ccb8d14..a343f4c578 100644
--- a/code/controllers/subsystem/blackbox.dm
+++ b/code/controllers/subsystem/blackbox.dm
@@ -14,12 +14,14 @@ SUBSYSTEM_DEF(blackbox)
"explosion" = 2,
"time_dilation_current" = 3,
"science_techweb_unlock" = 2,
- "round_end_stats" = 2) //associative list of any feedback variables that have had their format changed since creation and their current version, remember to update this
+ "round_end_stats" = 2,
+ "testmerged_prs" = 2) //associative list of any feedback variables that have had their format changed since creation and their current version, remember to update this
/datum/controller/subsystem/blackbox/Initialize()
triggertime = world.time
record_feedback("amount", "random_seed", Master.random_seed)
record_feedback("amount", "dm_version", DM_VERSION)
+ record_feedback("amount", "dm_build", DM_BUILD)
record_feedback("amount", "byond_version", world.byond_version)
record_feedback("amount", "byond_build", world.byond_build)
. = ..()
@@ -95,18 +97,24 @@ SUBSYSTEM_DEF(blackbox)
if (!SSdbcore.Connect())
return
+ var/list/special_columns = list(
+ "datetime" = "NOW()"
+ )
var/list/sqlrowlist = list()
-
for (var/datum/feedback_variable/FV in feedback)
- var/sqlversion = 1
- if(FV.key in versions)
- sqlversion = versions[FV.key]
- sqlrowlist += list(list("datetime" = "Now()", "round_id" = GLOB.round_id, "key_name" = "'[sanitizeSQL(FV.key)]'", "key_type" = "'[FV.key_type]'", "version" = "[sqlversion]", "json" = "'[sanitizeSQL(json_encode(FV.json))]'"))
+ sqlrowlist += list(list(
+ "round_id" = GLOB.round_id,
+ "key_name" = FV.key,
+ "key_type" = FV.key_type,
+ "version" = versions[FV.key] || 1,
+ "json" = json_encode(FV.json)
+ ))
if (!length(sqlrowlist))
return
- SSdbcore.MassInsert(format_table_name("feedback"), sqlrowlist, ignore_errors = TRUE, delayed = TRUE)
+ SSdbcore.MassInsert(format_table_name("feedback"), sqlrowlist, ignore_errors = TRUE, delayed = TRUE, special_columns = special_columns)
+
/datum/controller/subsystem/blackbox/proc/Seal()
if(sealed)
@@ -176,7 +184,7 @@ feedback data can be recorded in 5 formats:
"tally"
used to track the number of occurances of multiple related values i.e. how many times each type of gun is fired
further calls to the same key will:
- add or subtract from the saved value of the data key if it already exists
+ add or subtract from the saved value of the data key if it already exists
append the key and it's value if it doesn't exist
calls: SSblackbox.record_feedback("tally", "example", 1, "sample data")
SSblackbox.record_feedback("tally", "example", 4, "sample data")
@@ -188,7 +196,7 @@ feedback data can be recorded in 5 formats:
the final element in the data list is used as the tracking key, all prior elements are used for nesting
all data list elements must be strings
further calls to the same key will:
- add or subtract from the saved value of the data key if it already exists in the same multi-dimensional position
+ add or subtract from the saved value of the data key if it already exists in the same multi-dimensional position
append the key and it's value if it doesn't exist
calls: SSblackbox.record_feedback("nested tally", "example", 1, list("fruit", "orange", "apricot"))
SSblackbox.record_feedback("nested tally", "example", 2, list("fruit", "orange", "orange"))
@@ -289,6 +297,7 @@ Versioning
query_log_ahelp.Execute()
qdel(query_log_ahelp)
+
/datum/controller/subsystem/blackbox/proc/ReportDeath(mob/living/L)
set waitfor = FALSE
if(sealed)
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 198c380f41..884e209512 100755
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -268,7 +268,7 @@ SUBSYSTEM_DEF(ticker)
if(!GLOB.Debug2)
if(!can_continue)
log_game("[mode.name] failed pre_setup, cause: [mode.setup_error]")
- send2irc("SSticker", "[mode.name] failed pre_setup, cause: [mode.setup_error]")
+ send2adminchat("SSticker", "[mode.name] failed pre_setup, cause: [mode.setup_error]")
message_admins("[mode.name] failed pre_setup, cause: [mode.setup_error]")
QDEL_NULL(mode)
to_chat(world, "Error setting up [GLOB.master_mode]. Reverting to pre-game lobby.")
@@ -334,7 +334,7 @@ SUBSYSTEM_DEF(ticker)
var/list/adm = get_admin_counts()
var/list/allmins = adm["present"]
- send2irc("Server", "Round [GLOB.round_id ? "#[GLOB.round_id]:" : "of"] [hide_mode ? "secret":"[mode.name]"] has started[allmins.len ? ".":" with no active admins online!"]")
+ send2adminchat("Server", "Round [GLOB.round_id ? "#[GLOB.round_id]:" : "of"] [hide_mode ? "secret":"[mode.name]"] has started[allmins.len ? ".":" with no active admins online!"]")
setup_done = TRUE
for(var/i in GLOB.start_landmarks_list)
diff --git a/code/datums/world_topic.dm b/code/datums/world_topic.dm
index 7a1f0f6980..c09031cb08 100644
--- a/code/datums/world_topic.dm
+++ b/code/datums/world_topic.dm
@@ -90,7 +90,7 @@
if(!is_new_ckey)
log_admin("AUTO BUNKER: [ckeytobypass] given access (incoming comms from [sender]).")
message_admins("AUTO BUNKER: [ckeytobypass] given access (incoming comms from [sender]).")
- send2irc("Panic Bunker", "AUTO BUNKER: [ckeytobypass] given access (incoming comms from [sender]).")
+ send2adminchat("Panic Bunker", "AUTO BUNKER: [ckeytobypass] given access (incoming comms from [sender]).")
return "Success"
/datum/world_topic/ahelp_relay
diff --git a/code/modules/admin/DB_ban/functions.dm b/code/modules/admin/DB_ban/functions.dm
index 1f62b487fa..4c00e8f010 100644
--- a/code/modules/admin/DB_ban/functions.dm
+++ b/code/modules/admin/DB_ban/functions.dm
@@ -80,8 +80,11 @@
var/client/banned_client = banned_mob?.client
var/banned_mob_guest_key = had_banned_mob && IsGuestKey(banned_mob.key)
banned_mob = null
- var/sql_ckey = sanitizeSQL(ckey)
- var/datum/db_query/query_add_ban_get_ckey = SSdbcore.NewQuery("SELECT 1 FROM [format_table_name("player")] WHERE ckey = '[sql_ckey]'")
+ var/datum/db_query/query_add_ban_get_ckey = SSdbcore.NewQuery({"
+ SELECT 1
+ FROM [format_table_name("player")]
+ WHERE ckey = :ckey"},
+ list("ckey" = ckey))
if(!query_add_ban_get_ckey.warn_execute())
qdel(query_add_ban_get_ckey)
return
@@ -122,10 +125,11 @@
else
adminwho += ", [C]"
- reason = sanitizeSQL(reason)
- var/sql_a_ckey = sanitizeSQL(a_ckey)
if(maxadminbancheck)
- var/datum/db_query/query_check_adminban_amt = SSdbcore.NewQuery("SELECT count(id) AS num FROM [format_table_name("ban")] WHERE (a_ckey = '[sql_a_ckey]') AND (bantype = 'ADMIN_PERMABAN' OR (bantype = 'ADMIN_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
+ var/datum/db_query/query_check_adminban_amt = SSdbcore.NewQuery({"
+ SELECT count(id) AS num FROM [format_table_name("ban")]
+ WHERE (a_ckey = :a_ckey) AND (bantype = 'ADMIN_PERMABAN' OR (bantype = 'ADMIN_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)
+ "}, list("a_ckey" = a_ckey))
if(!query_check_adminban_amt.warn_execute())
qdel(query_check_adminban_amt)
return
@@ -143,13 +147,15 @@
computerid = "0"
if(!ip)
ip = "0.0.0.0"
- var/sql_job = sanitizeSQL(job)
- var/sql_computerid = sanitizeSQL(computerid)
- var/sql_ip = sanitizeSQL(ip)
- var/sql_a_computerid = sanitizeSQL(a_computerid)
- var/sql_a_ip = sanitizeSQL(a_ip)
- var/sql = "INSERT INTO [format_table_name("ban")] (`bantime`,`server_ip`,`server_port`,`round_id`,`bantype`,`reason`,`job`,`duration`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`) VALUES (Now(), INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', '[GLOB.round_id]', '[bantype_str]', '[reason]', '[sql_job]', [(duration)?"[duration]":"0"], Now() + INTERVAL [(duration>0) ? duration : 0] MINUTE, '[sql_ckey]', '[sql_computerid]', INET_ATON('[sql_ip]'), '[sql_a_ckey]', '[sql_a_computerid]', INET_ATON('[sql_a_ip]'), '[who]', '[adminwho]')"
- var/datum/db_query/query_add_ban = SSdbcore.NewQuery(sql)
+ var/datum/db_query/query_add_ban = SSdbcore.NewQuery({"
+ INSERT INTO [format_table_name("ban")] (`bantime`,`server_ip`,`server_port`,`round_id`,`bantype`,`reason`,`job`,`duration`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`)
+ VALUES (Now(), INET_ATON(:internet_address), :port, :round_id, :bantype_str, :reason, :job, :duration, Now() + INTERVAL :expiration_time MINUTE, :ckey, :computerid, INET_ATON(:ip), :a_ckey, :a_computerid, INET_ATON(:a_ip), :who, :adminwho)
+ "}, list("internet_address" = world.internet_address ? world.internet_address : 0,
+ "port" = world.port, "round_id" = GLOB.round_id, "bantype_str" = bantype_str, "reason" = reason,
+ "job" = job, "duration" = duration ? "[duration]":"0", "expiration_time" = (duration > 0) ? duration : 0,
+ "ckey" = ckey, "computerid" = computerid, "ip" = ip,
+ "a_ckey" = a_ckey, "a_computerid" = a_computerid, "a_ip" = a_ip, "who" = who, "adminwho" = adminwho
+ ))
if(!query_add_ban.warn_execute())
qdel(query_add_ban)
return
@@ -160,7 +166,7 @@
var/datum/admin_help/AH = admin_ticket_log(ckey, msg)
if(announceinirc)
- send2irc("BAN ALERT","[a_key] applied a [bantype_str] on [bankey]")
+ send2adminchat("BAN ALERT","[a_key] applied a [bantype_str] on [bankey]")
if(kickbannedckey)
if(AH)
@@ -212,11 +218,11 @@
bantype_sql = "(bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now() ) )"
else
bantype_sql = "bantype = '[bantype_str]'"
- var/sql_ckey = sanitizeSQL(ckey)
- var/sql = "SELECT id FROM [format_table_name("ban")] WHERE ckey = '[sql_ckey]' AND [bantype_sql] AND (unbanned is null OR unbanned = false)"
+ var/sql = "SELECT id FROM [format_table_name("ban")] WHERE ckey = :ckey AND [bantype_sql] AND (unbanned is null OR unbanned = false)"
+ var/list/sql_args = list("ckey" = ckey)
if(job)
- var/sql_job = sanitizeSQL(job)
- sql += " AND job = '[sql_job]'"
+ sql += " AND job = :job"
+ sql_args["job"] = job
if(!SSdbcore.Connect())
return
@@ -224,7 +230,7 @@
var/ban_id
var/ban_number = 0 //failsafe
- var/datum/db_query/query_unban_get_id = SSdbcore.NewQuery(sql)
+ var/datum/db_query/query_unban_get_id = SSdbcore.NewQuery(sql, sql_args)
if(!query_unban_get_id.warn_execute())
qdel(query_unban_get_id)
return
@@ -278,14 +284,12 @@
return
qdel(query_edit_ban_get_details)
- reason = sanitizeSQL(reason)
var/value
switch(param)
if("reason")
if(!value)
value = input("Insert the new reason for [p_key]'s ban", "New Reason", "[reason]", null) as null|text
- value = sanitizeSQL(value)
if(!value)
to_chat(usr, "Cancelled")
return
@@ -448,20 +452,28 @@
if(adminckey || playerckey || ip || cid)
var/list/searchlist = list()
+ var/list/searchlist_args = list()
if(playerckey)
- searchlist += "ckey = '[sanitizeSQL(ckey(playerckey))]'"
+ searchlist += "ckey = :playerckey"
+ searchlist_args["playerckey"] = playerckey
if(adminckey)
- searchlist += "a_ckey = '[sanitizeSQL(ckey(adminckey))]'"
+ searchlist += "a_ckey = :adminckey"
+ searchlist_args["adminckey"] = adminckey
if(ip)
- searchlist += "ip = INET_ATON('[sanitizeSQL(ip)]')"
+ searchlist += "ip = INET_ATON(:ip)"
+ searchlist_args["ip"] = ip
if(cid)
- searchlist += "computerid = '[sanitizeSQL(cid)]'"
- var/search = searchlist.Join(" AND ")
+ searchlist += "computerid = :cid"
+ searchlist_args["cid"] = cid
+ var/search = searchlist.Join(" AND ") // x = x AND y = z
var/bancount = 0
var/bansperpage = 15
var/pagecount = 0
page = text2num(page)
- var/datum/db_query/query_count_bans = SSdbcore.NewQuery("SELECT COUNT(id) FROM [format_table_name("ban")] WHERE [search]")
+ var/datum/db_query/query_count_bans = SSdbcore.NewQuery({"
+ SELECT COUNT(id) FROM [format_table_name("ban")]
+ WHERE [search]
+ "}, searchlist_args)
if(!query_count_bans.warn_execute())
qdel(query_count_bans)
return
@@ -489,7 +501,11 @@
output += "
OPTIONS | "
output += ""
var/limit = " LIMIT [bansperpage * page], [bansperpage]"
- var/datum/db_query/query_search_bans = SSdbcore.NewQuery("SELECT id, bantime, bantype, reason, job, duration, expiration_time, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), ckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey), a_ckey), unbanned, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].unbanned_ckey), unbanned_ckey), unbanned_datetime, edits, round_id FROM [format_table_name("ban")] WHERE [search] ORDER BY bantime DESC[limit]")
+
+ var/datum/db_query/query_search_bans = SSdbcore.NewQuery({"
+ SELECT id, bantime, bantype, reason, job, duration, expiration_time, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].ckey), ckey), IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey), a_ckey), unbanned, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].unbanned_ckey), unbanned_ckey), unbanned_datetime, edits, round_id
+ FROM [format_table_name("ban")]
+ WHERE [search] ORDER BY bantime DESC[limit]"}, searchlist_args)
if(!query_search_bans.warn_execute())
qdel(query_search_bans)
return
diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm
index dbc98fb0d7..79ba4abae7 100644
--- a/code/modules/admin/admin_ranks.dm
+++ b/code/modules/admin/admin_ranks.dm
@@ -119,16 +119,12 @@ GLOBAL_PROTECT(protected_ranks)
set waitfor = FALSE
if(IsAdminAdvancedProcCall())
- to_chat(usr, "Admin rank DB Sync blocked: Advanced ProcCall detected.")
+ to_chat(usr, "Admin rank DB Sync blocked: Advanced ProcCall detected.", confidential = TRUE)
return
var/list/sql_ranks = list()
for(var/datum/admin_rank/R in GLOB.protected_ranks)
- var/sql_rank = sanitizeSQL(R.name)
- var/sql_flags = sanitizeSQL(R.include_rights)
- var/sql_exclude_flags = sanitizeSQL(R.exclude_rights)
- var/sql_can_edit_flags = sanitizeSQL(R.can_edit_rights)
- sql_ranks += list(list("rank" = "'[sql_rank]'", "flags" = "[sql_flags]", "exclude_flags" = "[sql_exclude_flags]", "can_edit_flags" = "[sql_can_edit_flags]"))
+ sql_ranks += list(list("rank" = R.name, "flags" = R.include_rights, "exclude_flags" = R.exclude_rights, "can_edit_flags" = R.can_edit_rights))
SSdbcore.MassInsert(format_table_name("admin_ranks"), sql_ranks, duplicate_key = TRUE)
//load our rank - > rights associations
diff --git a/code/modules/admin/banjob.dm b/code/modules/admin/banjob.dm
index c6d8e9a5b0..8ebef00508 100644
--- a/code/modules/admin/banjob.dm
+++ b/code/modules/admin/banjob.dm
@@ -4,7 +4,10 @@
return FALSE
if(!M.client) //no cache. fallback to a datum/db_query
- var/datum/db_query/query_jobban_check_ban = SSdbcore.NewQuery("SELECT reason FROM [format_table_name("ban")] WHERE ckey = '[sanitizeSQL(M.ckey)]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned) AND job = '[sanitizeSQL(rank)]'")
+ var/datum/db_query/query_jobban_check_ban = SSdbcore.NewQuery({"
+ SELECT reason FROM [format_table_name("ban")]
+ WHERE ckey = :ckey AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned) AND job = :rank
+ "}, list("ckey" = M.ckey, "rank" = rank))
if(!query_jobban_check_ban.warn_execute())
qdel(query_jobban_check_ban)
return
@@ -28,7 +31,10 @@
return
if(C && istype(C))
C.jobbancache = list()
- var/datum/db_query/query_jobban_build_cache = SSdbcore.NewQuery("SELECT job, reason FROM [format_table_name("ban")] WHERE ckey = '[sanitizeSQL(C.ckey)]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)")
+ var/datum/db_query/query_jobban_build_cache = SSdbcore.NewQuery({"
+ SELECT job, reason FROM [format_table_name("ban")]
+ WHERE ckey = :ckey AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)
+ "}, list("ckey" = C.ckey))
if(!query_jobban_build_cache.warn_execute())
qdel(query_jobban_build_cache)
return
diff --git a/code/modules/admin/create_poll.dm b/code/modules/admin/create_poll.dm
index a6797ceb5f..e0225775f5 100644
--- a/code/modules/admin/create_poll.dm
+++ b/code/modules/admin/create_poll.dm
@@ -34,8 +34,9 @@
var/endtime = input("Set end time for poll as format YYYY-MM-DD HH:MM:SS. All times in server time. HH:MM:SS is optional and 24-hour. Must be later than starting time for obvious reasons.", "Set end time", SQLtime()) as text
if(!endtime)
return
- endtime = sanitizeSQL(endtime)
- var/datum/db_query/query_validate_time = SSdbcore.NewQuery("SELECT IF(STR_TO_DATE('[endtime]','%Y-%c-%d %T') > NOW(), STR_TO_DATE('[endtime]','%Y-%c-%d %T'), 0)")
+ var/datum/db_query/query_validate_time = SSdbcore.NewQuery({"
+ SELECT IF(STR_TO_DATE(:endtime,'%Y-%c-%d %T') > NOW(), STR_TO_DATE(:endtime,'%Y-%c-%d %T'), 0)
+ "}, list("endtime" = endtime))
if(!query_validate_time.warn_execute() || QDELETED(usr) || !src)
qdel(query_validate_time)
return
@@ -63,11 +64,9 @@
dontshow = 0
else
return
- var/sql_ckey = sanitizeSQL(ckey)
var/question = input("Write your question","Question") as message|null
if(!question)
return
- question = sanitizeSQL(question)
var/list/sql_option_list = list()
if(polltype != POLLTYPE_TEXT)
var/add_option = 1
@@ -75,7 +74,6 @@
var/option = input("Write your option","Option") as message|null
if(!option)
return
- option = sanitizeSQL(option)
var/default_percentage_calc = 0
if(polltype != POLLTYPE_IRV)
switch(alert("Should this option be included by default when poll result percentages are generated?",,"Yes","No","Cancel"))
@@ -92,32 +90,22 @@
var/descmax = ""
if(polltype == POLLTYPE_RATING)
minval = input("Set minimum rating value.","Minimum rating") as num|null
- if(minval)
- minval = sanitizeSQL(minval)
- else if(minval == null)
+ if(minval == null)
return
maxval = input("Set maximum rating value.","Maximum rating") as num|null
- if(maxval)
- maxval = sanitizeSQL(maxval)
if(minval >= maxval)
to_chat(src, "Maximum rating value can't be less than or equal to minimum rating value")
continue
- else if(maxval == null)
+ if(maxval == null)
return
descmin = input("Optional: Set description for minimum rating","Minimum rating description") as message|null
- if(descmin)
- descmin = sanitizeSQL(descmin)
- else if(descmin == null)
+ if(descmin == null)
return
descmid = input("Optional: Set description for median rating","Median rating description") as message|null
- if(descmid)
- descmid = sanitizeSQL(descmid)
- else if(descmid == null)
+ if(descmid == null)
return
descmax = input("Optional: Set description for maximum rating","Maximum rating description") as message|null
- if(descmax)
- descmax = sanitizeSQL(descmax)
- else if(descmax == null)
+ if(descmax == null)
return
sql_option_list += list(list("text" = "'[option]'", "minval" = "'[minval]'", "maxval" = "'[maxval]'", "descmin" = "'[descmin]'", "descmid" = "'[descmid]'", "descmax" = "'[descmax]'", "default_percentage_calc" = "'[default_percentage_calc]'"))
switch(alert(" ",,"Add option","Finish", "Cancel"))
@@ -129,7 +117,13 @@
return 0
var/m1 = "[key_name(usr)] has created a new server poll. Poll type: [polltype] - Admin Only: [adminonly ? "Yes" : "No"] - Question: [question]"
var/m2 = "[key_name_admin(usr)] has created a new server poll. Poll type: [polltype] - Admin Only: [adminonly ? "Yes" : "No"]
Question: [question]"
- var/datum/db_query/query_polladd_question = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_question")] (polltype, starttime, endtime, question, adminonly, multiplechoiceoptions, createdby_ckey, createdby_ip, dontshow) VALUES ('[polltype]', '[starttime]', '[endtime]', '[question]', '[adminonly]', '[choice_amount]', '[sql_ckey]', INET_ATON('[address]'), '[dontshow]')")
+ var/datum/db_query/query_polladd_question = SSdbcore.NewQuery({"
+ INSERT INTO [format_table_name("poll_question")] (polltype, starttime, endtime, question, adminonly, multiplechoiceoptions, createdby_ckey, createdby_ip, dontshow)
+ VALUES (:polltype, :starttime, :endtime, :question, :adminonly, :choice_amount, :ckey, INET_ATON(:address), '[dontshow]')
+ "}, list("polltype" = polltype, "starttime" = starttime, "endtime" = endtime,
+ "question" = question, "adminonly" = adminonly, "choice_amount" = choice_amount,
+ "ckey" = ckey, "address" = address, "dontshow" = dontshow
+ ))
if(!query_polladd_question.warn_execute())
qdel(query_polladd_question)
return
diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm
index 4348ae7661..4c4f50c4cd 100644
--- a/code/modules/admin/verbs/adminhelp.dm
+++ b/code/modules/admin/verbs/adminhelp.dm
@@ -205,7 +205,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
MessageNoRecipient(msg)
//send it to irc if nobody is on and tell us how many were on
- var/admin_number_present = send2irc_adminless_only(initiator_ckey, "Ticket #[id]: [name]")
+ var/admin_number_present = send2tgs_adminless_only(initiator_ckey, "Ticket #[id]: [name]")
log_admin_private("Ticket #[id]: [key_name(initiator)]: [name] - heard by [admin_number_present] non-AFK admins who have +BAN.")
if(admin_number_present <= 0)
to_chat(C, "No active admins are online, your adminhelp was sent to the admin irc.")
@@ -222,7 +222,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
/datum/admin_help/proc/AddInteraction(formatted_message)
if(heard_by_no_admins && usr && usr.ckey != initiator_ckey)
heard_by_no_admins = FALSE
- send2irc(initiator_ckey, "Ticket #[id]: Answered by [key_name(usr)]")
+ send2adminchat(initiator_ckey, "Ticket #[id]: Answered by [key_name(usr)]")
_interactions += "[TIME_STAMP("hh:mm:ss", FALSE)]: [formatted_message]"
//Removes the ahelp verb and returns it after 2 minutes
@@ -573,7 +573,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
. = list("total" = list(), "noflags" = list(), "afk" = list(), "stealth" = list(), "present" = list())
for(var/client/X in GLOB.admins)
.["total"] += X
- if(requiredflags != 0 && !check_rights_for(X, requiredflags))
+ if(requiredflags != NONE && !check_rights_for(X, requiredflags))
.["noflags"] += X
else if(X.is_afk())
.["afk"] += X
@@ -582,7 +582,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
else
.["present"] += X
-/proc/send2irc_adminless_only(source, msg, requiredflags = R_BAN)
+/proc/send2tgs_adminless_only(source, msg, requiredflags = R_BAN)
var/list/adm = get_admin_counts(requiredflags)
var/list/activemins = adm["present"]
. = activemins.len
@@ -596,29 +596,52 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
final = "[msg] - No admins online"
else
final = "[msg] - All admins stealthed\[[english_list(stealthmins)]\], AFK\[[english_list(afkmins)]\], or lacks +BAN\[[english_list(powerlessmins)]\]! Total: [allmins.len] "
- send2irc(source,final)
+ send2adminchat(source,final)
send2otherserver(source,final)
-
-/proc/send2irc(msg,msg2)
- msg = replacetext(replacetext(msg, "\proper", ""), "\improper", "")
- msg2 = replacetext(replacetext(msg2, "\proper", ""), "\improper", "")
- world.TgsTargetedChatBroadcast("[msg] | [msg2]", TRUE)
-
-/proc/send2otherserver(source,msg,type = "Ahelp")
- var/comms_key = CONFIG_GET(string/comms_key)
- if(!comms_key)
+/**
+ * Sends a message to a set of cross-communications-enabled servers using world topic calls
+ *
+ * Arguments:
+ * * source - Who sent this message
+ * * msg - The message body
+ * * type - The type of message, becomes the topic command under the hood
+ * * target_servers - A collection of servers to send the message to, defined in config
+ * * additional_data - An (optional) associated list of extra parameters and data to send with this world topic call
+ */
+/proc/send2otherserver(source, msg, type = "Ahelp", target_servers, list/additional_data = list())
+ if(!CONFIG_GET(string/comms_key))
+ debug_world_log("Server cross-comms message not sent for lack of configured key")
return
- var/list/message = list()
- message["message_sender"] = source
- message["message"] = msg
- message["source"] = "([CONFIG_GET(string/cross_comms_name)])"
- message["key"] = comms_key
- message += type
+
+ var/our_id = CONFIG_GET(string/cross_comms_name)
+ additional_data["message_sender"] = source
+ additional_data["message"] = msg
+ additional_data["source"] = "([our_id])"
+ additional_data += type
var/list/servers = CONFIG_GET(keyed_list/cross_server)
for(var/I in servers)
- world.Export("[servers[I]]?[list2params(message)]")
+ if(I == our_id) //No sending to ourselves
+ continue
+ if(target_servers && !(I in target_servers))
+ continue
+ world.send_cross_comms(I, additional_data)
+
+/// Sends a message to a given cross comms server by name (by name for security).
+/world/proc/send_cross_comms(server_name, list/message, auth = TRUE)
+ set waitfor = FALSE
+ if (auth)
+ var/comms_key = CONFIG_GET(string/comms_key)
+ if(!comms_key)
+ debug_world_log("Server cross-comms message not sent for lack of configured key")
+ return
+ message["key"] = comms_key
+ var/list/servers = CONFIG_GET(keyed_list/cross_server)
+ var/server_url = servers[server_name]
+ if (!server_url)
+ CRASH("Invalid cross comms config: [server_name]")
+ world.Export("[server_url]?[list2params(message)]")
/proc/ircadminwho()
diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm
index 2931c52a5c..e19f3ea329 100644
--- a/code/modules/admin/verbs/adminpm.dm
+++ b/code/modules/admin/verbs/adminpm.dm
@@ -161,7 +161,7 @@
to_chat(src, "PM to-Admins: [rawmsg]", confidential = TRUE)
var/datum/admin_help/AH = admin_ticket_log(src, "Reply PM from-[key_name(src, TRUE, TRUE)] to External: [keywordparsedmsg]")
ircreplyamount--
- send2irc("[AH ? "#[AH.id] " : ""]Reply: [ckey]", rawmsg)
+ send2adminchat("[AH ? "#[AH.id] " : ""]Reply: [ckey]", rawmsg)
else
var/badmin = FALSE //Lets figure out if an admin is getting bwoinked.
diff --git a/code/modules/admin/verbs/panicbunker.dm b/code/modules/admin/verbs/panicbunker.dm
index daaf15c70f..92bc840c5a 100644
--- a/code/modules/admin/verbs/panicbunker.dm
+++ b/code/modules/admin/verbs/panicbunker.dm
@@ -13,7 +13,7 @@
if (new_pb && !SSdbcore.Connect())
message_admins("The Database is not connected! Panic bunker will not work until the connection is reestablished.")
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Panic Bunker", "[new_pb ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
- send2irc("Panic Bunker", "[key_name(usr)] has toggled the Panic Bunker, it is now [new_pb ? "enabled" : "disabled"].")
+ send2adminchat("Panic Bunker", "[key_name(usr)] has toggled the Panic Bunker, it is now [new_pb ? "enabled" : "disabled"].")
/client/proc/addbunkerbypass(ckeytobypass as text)
set category = "Special Verbs"
@@ -28,7 +28,7 @@
SSpersistence.SavePanicBunker() //we can do this every time, it's okay
log_admin("[key_name(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
message_admins("[key_name_admin(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
- send2irc("Panic Bunker", "[key_name(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
+ send2adminchat("Panic Bunker", "[key_name(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
/client/proc/revokebunkerbypass(ckeytobypass as text)
set category = "Special Verbs"
@@ -42,4 +42,4 @@
SSpersistence.SavePanicBunker()
log_admin("[key_name(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")
message_admins("[key_name_admin(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")
- send2irc("Panic Bunker", "[key_name(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")
+ send2adminchat("Panic Bunker", "[key_name(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")
diff --git a/code/modules/cargo/packs/costumes_toys.dm b/code/modules/cargo/packs/costumes_toys.dm
index 08f9a927c6..05d3737253 100644
--- a/code/modules/cargo/packs/costumes_toys.dm
+++ b/code/modules/cargo/packs/costumes_toys.dm
@@ -318,7 +318,7 @@
/obj/item/clothing/under/suit/white, // white is a weird color for a groom but some people are weird
/obj/item/clothing/under/suit/polychromic,
/obj/item/clothing/under/suit/polychromic, // in case you can't be satisfied with the most fitting choices, of course.
- /obj/item/clothing/under/dress/wedding,
+ /obj/item/clothing/under/dress/wedding,
/obj/item/clothing/under/dress/wedding, // this is what you actually bought the crate for. You can't get these anywhere else.
/obj/item/clothing/under/dress/wedding/orange,
/obj/item/clothing/under/dress/wedding/orange,
@@ -333,4 +333,4 @@
/obj/item/storage/fancy/ringbox/silver,
/obj/item/storage/fancy/ringbox/silver) //diamond rings cost the same price as this crate via cargo so we're not giving you two for free. Wedding rings are traditionally less valuable anyway.
crate_name = "wedding crate"
-
+
diff --git a/code/modules/cargo/packs/misc.dm b/code/modules/cargo/packs/misc.dm
index 9c15e75cd6..a158f0f1c1 100644
--- a/code/modules/cargo/packs/misc.dm
+++ b/code/modules/cargo/packs/misc.dm
@@ -42,18 +42,21 @@
/datum/supply_pack/misc/book_crate
name = "Book Crate"
- desc = "Surplus from the Nanotrasen Archives, these five books are sure to be good reads."
+ desc = "Surplus from the Nanotrasen Archives, these seven books are sure to be good reads."
+ // cost = CARGO_CRATE_VALUE * 3
cost = 1500
contains = list(/obj/item/book/codex_gigas,
/obj/item/book/manual/random/,
/obj/item/book/manual/random/,
/obj/item/book/manual/random/,
- /obj/item/book/random/triple)
+ /obj/item/book/random,
+ /obj/item/book/random,
+ /obj/item/book/random)
crate_type = /obj/structure/closet/crate/wooden
/datum/supply_pack/misc/paper
name = "Bureaucracy Crate"
- desc = "High stacks of papers on your desk Are a big problem - make it Pea-sized with these bureaucratic supplies! Contains five pens, some camera film, hand labeler supplies, a paper bin, three folders, two clipboards and two stamps as well as a briefcase."//that was too forced
+ desc = "High stacks of papers on your desk Are a big problem - make it Pea-sized with these bureaucratic supplies! Contains six pens, some camera film, hand labeler supplies, a paper bin, a carbon paper bin, three folders, a laser pointer, two clipboards and two stamps."//that was too forced
cost = 1500
contains = list(/obj/structure/filingcabinet/chestdrawer/wheeled,
/obj/item/camera_film,
@@ -61,9 +64,11 @@
/obj/item/hand_labeler_refill,
/obj/item/hand_labeler_refill,
/obj/item/paper_bin,
+ /obj/item/paper_bin/carbon,
/obj/item/pen/fourcolor,
/obj/item/pen/fourcolor,
/obj/item/pen,
+ /obj/item/pen/fountain,
/obj/item/pen/blue,
/obj/item/pen/red,
/obj/item/folder/blue,
@@ -73,7 +78,7 @@
/obj/item/clipboard,
/obj/item/stamp,
/obj/item/stamp/denied,
- /obj/item/storage/briefcase)
+ /obj/item/laser_pointer/purple)
crate_name = "bureaucracy crate"
/datum/supply_pack/misc/captain_pen
@@ -94,6 +99,30 @@
crate_type = /obj/structure/closet/crate/wooden
crate_name = "calligraphy crate"
+/datum/supply_pack/misc/toner
+ name = "Toner Crate"
+ desc = "Spent too much ink printing butt pictures? Fret not, with these six toner refills, you'll be printing butts 'till the cows come home!'"
+ cost = 200 * 4
+ contains = list(/obj/item/toner,
+ /obj/item/toner,
+ /obj/item/toner,
+ /obj/item/toner,
+ /obj/item/toner,
+ /obj/item/toner)
+ crate_name = "toner crate"
+
+/datum/supply_pack/misc/toner_large
+ name = "Toner Crate (Large)"
+ desc = "Tired of changing toner cartridges? These six extra heavy duty refills contain roughly five times as much toner as the base model!"
+ cost = 200 * 6
+ contains = list(/obj/item/toner/large,
+ /obj/item/toner/large,
+ /obj/item/toner/large,
+ /obj/item/toner/large,
+ /obj/item/toner/large,
+ /obj/item/toner/large)
+ crate_name = "large toner crate"
+
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////// Entertainment ///////////////////////////////
//////////////////////////////////////////////////////////////////////////////
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index faa202ee53..4a300f64a0 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -415,7 +415,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if (nnpa >= 0)
message_admins("New user: [key_name_admin(src)] is connecting here for the first time.")
if (CONFIG_GET(flag/irc_first_connection_alert))
- send2irc_adminless_only("New-user", "[key_name(src)] is connecting for the first time!")
+ send2tgs_adminless_only("New-user", "[key_name(src)] is connecting for the first time!")
else if (isnum(cached_player_age) && cached_player_age < nnpa)
message_admins("New user: [key_name_admin(src)] just connected with an age of [cached_player_age] day[(player_age==1?"":"s")]")
if(CONFIG_GET(flag/use_account_age_for_jobs) && account_age >= 0)
@@ -423,7 +423,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if(account_age >= 0 && account_age < nnpa)
message_admins("[key_name_admin(src)] (IP: [address], ID: [computer_id]) is a new BYOND account [account_age] day[(account_age==1?"":"s")] old, created on [account_join_date].")
if (CONFIG_GET(flag/irc_first_connection_alert))
- send2irc_adminless_only("new_byond_user", "[key_name(src)] (IP: [address], ID: [computer_id]) is a new BYOND account [account_age] day[(account_age==1?"":"s")] old, created on [account_join_date].")
+ send2tgs_adminless_only("new_byond_user", "[key_name(src)] (IP: [address], ID: [computer_id]) is a new BYOND account [account_age] day[(account_age==1?"":"s")] old, created on [account_join_date].")
get_message_output("watchlist entry", ckey)
check_ip_intel()
validate_key_in_db()
@@ -519,7 +519,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
"Forever alone :("\
)
- send2irc("Server", "[cheesy_message] (No admins online)")
+ send2adminchat("Server", "[cheesy_message] (No admins online)")
QDEL_LIST_ASSOC_VAL(char_render_holders)
if(movingmob != null)
movingmob.client_mobs_in_contents -= mob
@@ -576,7 +576,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
var/living_recs = CONFIG_GET(number/panic_bunker_living)
//Relies on pref existing, but this proc is only called after that occurs, so we're fine.
var/minutes = get_exp_living(pure_numeric = TRUE)
- if(minutes <= living_recs && !CONFIG_GET(flag/panic_bunker_interview))
+ if(minutes <= living_recs) // && !CONFIG_GET(flag/panic_bunker_interview)
var/reject_message = "Failed Login: [key] - Account attempting to connect during panic bunker, but they do not have the required living time [minutes]/[living_recs]"
log_access(reject_message)
message_admins("[reject_message]")
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 0e7fda80b1..882a544bf1 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -1331,9 +1331,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
/datum/preferences/proc/process_link(mob/user, list/href_list)
if(href_list["jobbancheck"])
- var/job = sanitizeSQL(href_list["jobbancheck"])
- var/sql_ckey = sanitizeSQL(user.ckey)
- var/datum/db_query/query_get_jobban = SSdbcore.NewQuery("SELECT reason, bantime, duration, expiration_time, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey), a_ckey) FROM [format_table_name("ban")] WHERE ckey = '[sql_ckey]' AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned) AND job = '[job]'")
+ var/job = href_list["jobbancheck"]
+ var/datum/db_query/query_get_jobban = SSdbcore.NewQuery({"
+ SELECT reason, bantime, duration, expiration_time, IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("ban")].a_ckey), a_ckey)
+ FROM [format_table_name("ban")] WHERE ckey = :ckey AND (bantype = 'JOB_PERMABAN' OR (bantype = 'JOB_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned) AND job = :job
+ "}, list("ckey" = user.ckey, "job" = job))
if(!query_get_jobban.warn_execute())
qdel(query_get_jobban)
return
@@ -1348,7 +1350,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(text2num(duration) > 0)
text += ". The ban is for [duration] minutes and expires on [expiration_time] (server time)"
text += "."
- to_chat(user, text)
+ to_chat(user, text, confidential = TRUE)
qdel(query_get_jobban)
return
diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm
index c242509344..5a8325fd64 100644
--- a/code/modules/client/verbs/ooc.dm
+++ b/code/modules/client/verbs/ooc.dm
@@ -179,7 +179,7 @@ GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8")
to_chat(usr, "Sorry, that function is not enabled on this server.")
return
- browse_messages(null, usr.ckey, null, TRUE, override = TRUE)
+ browse_messages(null, usr.ckey, null, TRUE)
/client/proc/self_playtime()
set name = "View tracked playtime"
diff --git a/code/modules/jobs/job_exp.dm b/code/modules/jobs/job_exp.dm
index f982d7e96e..d9b98eea0a 100644
--- a/code/modules/jobs/job_exp.dm
+++ b/code/modules/jobs/job_exp.dm
@@ -1,7 +1,6 @@
GLOBAL_LIST_EMPTY(exp_to_update)
GLOBAL_PROTECT(exp_to_update)
-
// Procs
/datum/job/proc/required_playtime_remaining(client/C)
if(!C)
@@ -57,6 +56,7 @@ GLOBAL_PROTECT(exp_to_update)
amount += explist[job]
return amount
+// todo: port tgui exp
/client/proc/get_exp_report()
if(!CONFIG_GET(flag/use_exp_tracking))
return "Tracking is disabled in the server configuration file."
@@ -121,12 +121,11 @@ GLOBAL_PROTECT(exp_to_update)
return_text += ""
return return_text
-
-/client/proc/get_exp_living()
- if(!prefs.exp)
- return "No data"
+/client/proc/get_exp_living(pure_numeric = FALSE)
+ if(!prefs.exp || !prefs.exp[EXP_TYPE_LIVING])
+ return pure_numeric ? 0 : "No data"
var/exp_living = text2num(prefs.exp[EXP_TYPE_LIVING])
- return get_exp_format(exp_living)
+ return pure_numeric ? exp_living : get_exp_format(exp_living)
/proc/get_exp_format(expnum)
if(expnum > 60)
@@ -148,7 +147,7 @@ GLOBAL_PROTECT(exp_to_update)
set waitfor = FALSE
var/list/old_minutes = GLOB.exp_to_update
GLOB.exp_to_update = null
- SSdbcore.MassInsert(format_table_name("role_time"), old_minutes, "ON DUPLICATE KEY UPDATE minutes = minutes + VALUES(minutes)")
+ SSdbcore.MassInsert(format_table_name("role_time"), old_minutes, duplicate_key = "ON DUPLICATE KEY UPDATE minutes = minutes + VALUES(minutes)")
//resets a client's exp to what was in the db.
/client/proc/set_exp_from_db()
@@ -262,8 +261,8 @@ GLOBAL_PROTECT(exp_to_update)
CRASH("invalid job value [jtype]:[jvalue]")
LAZYINITLIST(GLOB.exp_to_update)
GLOB.exp_to_update.Add(list(list(
- "job" = "'[sanitizeSQL(jtype)]'",
- "ckey" = "'[sanitizeSQL(ckey)]'",
+ "job" = jtype,
+ "ckey" = ckey,
"minutes" = jvalue)))
prefs.exp[jtype] += jvalue
addtimer(CALLBACK(SSblackbox,/datum/controller/subsystem/blackbox/proc/update_exp_db),20,TIMER_OVERRIDE|TIMER_UNIQUE)
@@ -289,4 +288,3 @@ GLOBAL_PROTECT(exp_to_update)
prefs.db_flags = 0 //This PROBABLY won't happen, but better safe than sorry.
qdel(flags_read)
return TRUE
-
diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm
index 877be6788d..6897004583 100644
--- a/code/modules/library/lib_machines.dm
+++ b/code/modules/library/lib_machines.dm
@@ -440,7 +440,7 @@
if(!GLOB.news_network)
alert("No news network found on station. Aborting.")
var/channelexists = 0
- for(var/datum/newscaster/feed_channel/FC in GLOB.news_network.network_channels)
+ for(var/datum/news/feed_channel/FC in GLOB.news_network.network_channels)
if(FC.channel_name == "Nanotrasen Book Club")
channelexists = 1
break
@@ -490,9 +490,9 @@
if(href_list["printbible"])
if(printer_cooldown < world.time)
var/obj/item/storage/book/bible/B = new /obj/item/storage/book/bible(src.loc)
- if(GLOB.bible_icon_state && GLOB.bible_inhand_icon_state)
+ if(GLOB.bible_icon_state && GLOB.bible_item_state)
B.icon_state = GLOB.bible_icon_state
- B.inhand_icon_state = GLOB.bible_inhand_icon_state
+ B.item_state = GLOB.bible_item_state
B.name = GLOB.bible_name
B.deity_name = GLOB.deity
printer_cooldown = world.time + PRINTER_COOLDOWN
diff --git a/code/modules/mob/dead/new_player/poll.dm b/code/modules/mob/dead/new_player/poll.dm
index 3deeb28a96..9d0001604c 100644
--- a/code/modules/mob/dead/new_player/poll.dm
+++ b/code/modules/mob/dead/new_player/poll.dm
@@ -456,20 +456,30 @@
//lets add the vote, first we generate an insert statement.
var/sqlrowlist = ""
+ var/list/sql_args = list("pollid" = pollid, "ckey" = ckey, "address" = address, "rank" = rank) // will always exist
+ var/__unique_N = 0
for (var/vote in numberedvotelist)
+ __unique_N++
if (sqlrowlist != "")
sqlrowlist += ", " //a comma (,) at the start of the first row to insert will trigger a SQL error
- sqlrowlist += "(Now(), [pollid], [vote], '[sanitizeSQL(ckey)]', INET_ATON('[sanitizeSQL(address)]'), '[sanitizeSQL(rank)]')"
+ sqlrowlist += "(Now(), :pollid, :vote[__unique_N], :ckey, INET_ATON(:address), :rank)"
+ sql_args["vote[__unique_N]"] = vote
//now lets delete their old votes (if any)
- var/datum/db_query/query_irv_del_old = SSdbcore.NewQuery("DELETE FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'")
+ var/datum/db_query/query_irv_del_old = SSdbcore.NewQuery({"
+ DELETE FROM [format_table_name("poll_vote")]
+ WHERE pollid = :pollid AND ckey = :ckey
+ "}, list("pollid" = pollid, "ckey" = ckey))
if(!query_irv_del_old.warn_execute())
qdel(query_irv_del_old)
return 0
qdel(query_irv_del_old)
//now to add the new ones.
- var/datum/db_query/query_irv_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_vote")] (datetime, pollid, optionid, ckey, ip, adminrank) VALUES [sqlrowlist]")
+ var/datum/db_query/query_irv_vote = SSdbcore.NewQuery({"
+ INSERT INTO [format_table_name("poll_vote")] (datetime, pollid, optionid, ckey, ip, adminrank)
+ VALUES [sqlrowlist]
+ "}, sql_args)
if(!query_irv_vote.warn_execute())
qdel(query_irv_vote)
return 0
@@ -493,10 +503,13 @@
var/voted = poll_check_voted(pollid)
if(isnull(voted) || voted) //Failed or already voted.
return
- var/adminrank = sanitizeSQL(poll_rank())
+ var/adminrank = poll_rank()
if(!adminrank)
return
- var/datum/db_query/query_option_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_vote")] (datetime, pollid, optionid, ckey, ip, adminrank) VALUES (Now(), [pollid], [optionid], '[ckey]', INET_ATON('[client.address]'), '[adminrank]')")
+ var/datum/db_query/query_option_vote = SSdbcore.NewQuery({"
+ INSERT INTO [format_table_name("poll_vote")] (datetime, pollid, optionid, ckey, ip, adminrank)
+ VALUES (Now(), :pollid, :optionid, :ckey, INET_ATON(:address), :adminrank)
+ "}, list("pollid" = pollid, "optionid" = optionid, "ckey" = ckey, "address" = client.address, "adminrank" = adminrank))
if(!query_option_vote.warn_execute())
qdel(query_option_vote)
return
@@ -522,18 +535,23 @@
var/voted = poll_check_voted(pollid, text = TRUE, silent = TRUE)
if(isnull(voted))
return
- var/adminrank = sanitizeSQL(poll_rank())
+ var/adminrank = poll_rank()
if(!adminrank)
return
- replytext = sanitizeSQL(replytext)
if(!(length(replytext) > 0) || !(length(replytext) <= 8000))
to_chat(usr, "The text you entered was invalid or too long. Please correct the text and submit again.")
return
var/datum/db_query/query_text_vote
if(!voted)
- query_text_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_textreply")] (datetime ,pollid ,ckey ,ip ,replytext ,adminrank) VALUES (Now(), [pollid], '[ckey]', INET_ATON('[client.address]'), '[replytext]', '[adminrank]')")
+ query_text_vote = SSdbcore.NewQuery({"
+ INSERT INTO [format_table_name("poll_textreply")] (datetime, pollid, ckey, ip, replytext, adminrank)
+ VALUES (Now(), :pollid, :ckey, INET_ATON(:address), :replytext, :adminrank)
+ "}, list("pollid" = pollid, "ckey" = ckey, "address" = client.address, "replytext" = replytext, "adminrank" = adminrank))
else
- query_text_vote = SSdbcore.NewQuery("UPDATE [format_table_name("poll_textreply")] SET datetime = Now(), ip = INET_ATON('[client.address]'), replytext = '[replytext]' WHERE pollid = '[pollid]' AND ckey = '[ckey]'")
+ query_text_vote = SSdbcore.NewQuery({"
+ UPDATE [format_table_name("poll_textreply")]
+ SET datetime = Now(), ip = INET_ATON(:address), replytext = :replytext WHERE pollid = :pollid AND ckey = :ckey
+ "}, list("address" = client.address, "replytext" = replytext, "pollid" = pollid, "ckey" = ckey))
if(!query_text_vote.warn_execute())
qdel(query_text_vote)
return
@@ -565,8 +583,10 @@
var/adminrank = "Player"
if(client.holder)
adminrank = client.holder.rank.name
- adminrank = sanitizeSQL(adminrank)
- var/datum/db_query/query_numval_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_vote")] (datetime ,pollid ,optionid ,ckey ,ip ,adminrank, rating) VALUES (Now(), [pollid], [optionid], '[ckey]', INET_ATON('[client.address]'), '[adminrank]', [(isnull(rating)) ? "null" : rating])")
+ var/datum/db_query/query_numval_vote = SSdbcore.NewQuery({"
+ INSERT INTO [format_table_name("poll_vote")] (datetime ,pollid ,optionid ,ckey ,ip ,adminrank, rating)
+ VALUES (Now(), :pollid, :optionid, :ckey, INET_ATON(:address), :adminrank, :rating)
+ "}, list("pollid" = pollid, "optionid" = optionid, "ckey" = ckey, "address" = client.address, "adminrank" = adminrank, "rating" = isnull(rating) ? "null" : rating))
if(!query_numval_vote.warn_execute())
qdel(query_numval_vote)
return
@@ -609,8 +629,10 @@
var/adminrank = "Player"
if(!QDELETED(client) && client.holder)
adminrank = client.holder.rank.name
- adminrank = sanitizeSQL(adminrank)
- var/datum/db_query/query_multi_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_vote")] (datetime, pollid, optionid, ckey, ip, adminrank) VALUES (Now(), [pollid], [optionid], '[ckey]', INET_ATON('[client.address]'), '[adminrank]')")
+ var/datum/db_query/query_multi_vote = SSdbcore.NewQuery({"
+ INSERT INTO [format_table_name("poll_vote")] (datetime, pollid, optionid, ckey, ip, adminrank)
+ VALUES (Now(), :pollid, :optionid, :ckey, INET_ATON(:address), :adminrank)
+ "}, list("pollid" = pollid, "optionid" = optionid, "ckey" = ckey, "address" = client.address, "adminrank" = adminrank))
if(!query_multi_vote.warn_execute())
qdel(query_multi_vote)
return 1
diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm
index a6fdfdc793..a6453e58b7 100644
--- a/code/modules/mob/living/life.dm
+++ b/code/modules/mob/living/life.dm
@@ -24,7 +24,7 @@
break
var/msg = "[key_name_admin(src)] [ADMIN_JMP(src)] was found to have no .loc with an attached client, if the cause is unknown it would be wise to ask how this was accomplished."
message_admins(msg)
- send2irc_adminless_only("Mob", msg, R_ADMIN)
+ send2tgs_adminless_only("Mob", msg, R_ADMIN)
log_game("[key_name(src)] was found to have no .loc with an attached client.")
// This is a temporary error tracker to make sure we've caught everything
diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm
index 5d354693ef..cc37fbbcc4 100644
--- a/code/modules/shuttle/emergency.dm
+++ b/code/modules/shuttle/emergency.dm
@@ -398,7 +398,7 @@
return
mode = SHUTTLE_DOCKED
setTimer(SSshuttle.emergencyDockTime)
- send2irc("Server", "The Emergency Shuttle has docked with the station.")
+ send2adminchat("Server", "The Emergency Shuttle has docked with the station.")
priority_announce("The Emergency Shuttle has docked with the station. You have [timeLeft(600)] minutes to board the Emergency Shuttle.", null, "shuttledock", "Priority")
ShuttleDBStuff()
diff --git a/config/config.txt b/config/config.txt
index 72df74beb1..4f8ced5522 100644
--- a/config/config.txt
+++ b/config/config.txt
@@ -366,6 +366,13 @@ NOTIFY_NEW_PLAYER_ACCOUNT_AGE 1
## Requires database
#PANIC_BUNKER
+## If a player connects during a bunker with less then or this amount of living time (Minutes), we deny the connection
+#PANIC_BUNKER_LIVING 90
+
+## The message the Panic Bunker gives when someone is rejected by it
+## %minutes% is replaced with PANIC_BUNKER_LIVING on runtime, remove it if you don't want this
+#PANIC_BUNKER_MESSAGE Sorry, but the server is currently not accepting connections from players with less than %minutes% minutes of living time.
+
## If panic bunker is on and a player is rejected (see above), attempt to send them to this connected server (see below) instead.
## You probably want this to be the same as CROSS_SERVER_ADDRESS
#PANIC_SERVER_ADDRESS byond://address:port
diff --git a/modular_citadel/code/modules/mentor/mentor.dm b/modular_citadel/code/modules/mentor/mentor.dm
index 79345e6478..fc9f9a6b63 100644
--- a/modular_citadel/code/modules/mentor/mentor.dm
+++ b/modular_citadel/code/modules/mentor/mentor.dm
@@ -71,14 +71,14 @@ GLOBAL_PROTECT(mentor_href_token)
if(findtextEx(line, "#", 1, 2))
continue
new /datum/mentors(line)
- else//Database
+ else //Database
if(!SSdbcore.Connect())
log_world("Failed to connect to database in load_mentors(). Reverting to legacy system.")
WRITE_FILE(GLOB.world_game_log, "Failed to connect to database in load_mentors(). Reverting to legacy system.")
CONFIG_SET(flag/mentor_legacy_system, TRUE)
load_mentors()
return
- var/datum/DBQuery/query_load_mentors = SSdbcore.NewQuery("SELECT ckey FROM [format_table_name("mentor")]")
+ var/datum/db_query/query_load_mentors = SSdbcore.NewQuery("SELECT ckey FROM [format_table_name("mentor")]")
if(!query_load_mentors.Execute())
return
while(query_load_mentors.NextRow())
diff --git a/modular_citadel/code/modules/mentor/mentor_memo.dm b/modular_citadel/code/modules/mentor/mentor_memo.dm
index 7f376266a0..a92ec7232c 100644
--- a/modular_citadel/code/modules/mentor/mentor_memo.dm
+++ b/modular_citadel/code/modules/mentor/mentor_memo.dm
@@ -27,12 +27,11 @@
if(!SSdbcore.IsConnected())
to_chat(src, "Failed to establish database connection.")
return
- var/sql_ckey = sanitizeSQL(ckey)
switch(task)
if("Write")
var/datum/db_query/query_memocheck = SSdbcore.NewQuery({"
- SELECT ckey FROM [format_table_name("mentor_memo")] WHERE ckey = :sql_ckey
- "}, list("sql_ckey" = sql_ckey))
+ SELECT ckey FROM [format_table_name("mentor_memo")] WHERE ckey = :ckey
+ "}, list("ckey" = ckey))
if(!query_memocheck.Execute())
var/err = query_memocheck.ErrorMsg()
qdel(query_memocheck)
@@ -47,11 +46,10 @@
var/memotext = input(src,"Write your Memo","Memo") as message
if(!memotext)
return
- memotext = sanitizeSQL(memotext)
var/datum/db_query/query_memoadd = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("mentor_memo")] (ckey, memotext, timestamp)
- VALUES (:sql_ckey, :memotext, :timestamp)
- "}, list("sql_ckey" = sql_ckey, "memotext" = memotext, "timestamp" = SQLtime()))
+ VALUES (:ckey, :memotext, :timestamp)
+ "}, list("ckey" = ckey, "memotext" = memotext, "timestamp" = SQLtime()))
if(!query_memoadd.Execute())
var/err = query_memoadd.ErrorMsg()
qdel(query_memoadd)
@@ -78,10 +76,9 @@
var/target_ckey = input(src, "Select whose memo to edit", "Select memo") as null|anything in memolist
if(!target_ckey)
return
- var/target_sql_ckey = sanitizeSQL(target_ckey)
var/datum/db_query/query_memofind = SSdbcore.NewQuery({"
- SELECT memotext FROM [format_table_name("mentor_memo")] WHERE ckey = :target_sql_ckey
- "}, list("target_sql_ckey" = target_sql_ckey))
+ SELECT memotext FROM [format_table_name("mentor_memo")] WHERE ckey = :target_ckey
+ "}, list("target_ckey" = target_ckey))
if(!query_memofind.Execute())
var/err = query_memofind.ErrorMsg()
qdel(query_memofind)
@@ -93,23 +90,22 @@
var/new_memo = input("Input new memo", "New Memo", "[old_memo]", null) as message
if(!new_memo)
return
- new_memo = sanitizeSQL(new_memo)
- var/edit_text = "Edited by [sql_ckey] on [SQLtime()] from
[old_memo]
to
[new_memo]
"
- edit_text = sanitizeSQL(edit_text)
+ var/edit_text = "Edited by [ckey] on [SQLtime()] from
[old_memo]
to
[new_memo]
"
var/datum/db_query/update_query = SSdbcore.NewQuery({"
- UPDATE [format_table_name("mentor_memo")] SET memotext = :new_memo, last_editor = :sql_ckey, edits = CONCAT(:edit_text,'[]') WHERE ckey = :target_sql_ckey
- "}, list("new_memo" = new_memo, "sql_ckey" = sql_ckey, "edit_text" = (edit_text ? "" : edit_text), "target_sql_ckey" = target_sql_ckey))
+ UPDATE [format_table_name("mentor_memo")]
+ SET memotext = :new_memo, last_editor = :ckey, edits = :edit_text WHERE ckey = :target_ckey
+ "}, list("new_memo" = new_memo, "ckey" = ckey, "edit_text" = (edit_text ? "" : edit_text), "target_ckey" = target_ckey))
if(!update_query.Execute())
var/err = update_query.ErrorMsg()
qdel(update_query)
log_game("SQL ERROR editing memo. Error : \[[err]\]\n")
return
- if(target_sql_ckey == sql_ckey)
+ if(target_ckey == ckey)
log_admin("[key_name(src)] has edited their mentor memo from [old_memo] to [new_memo]")
message_admins("[key_name_admin(src)] has edited their mentor memo from
[old_memo]
to
[new_memo]")
else
- log_admin("[key_name(src)] has edited [target_sql_ckey]'s mentor memo from [old_memo] to [new_memo]")
- message_admins("[key_name_admin(src)] has edited [target_sql_ckey]'s mentor memo from
[old_memo]
to
[new_memo]")
+ log_admin("[key_name(src)] has edited [target_ckey]'s mentor memo from [old_memo] to [new_memo]")
+ message_admins("[key_name_admin(src)] has edited [target_ckey]'s mentor memo from
[old_memo]
to
[new_memo]")
qdel(update_query)
else
qdel(query_memofind)
@@ -153,18 +149,17 @@
var/target_ckey = input(src, "Select whose mentor memo to delete", "Select mentor memo") as null|anything in memolist
if(!target_ckey)
return
- var/target_sql_ckey = sanitizeSQL(target_ckey)
var/datum/db_query/query_memodel = SSdbcore.NewQuery({"
- DELETE FROM [format_table_name("memo")] WHERE ckey = :target_sql_ckey
- "}, list("target_sql_ckey" = target_sql_ckey))
+ DELETE FROM [format_table_name("memo")] WHERE ckey = :target_ckey
+ "}, list("target_ckey" = target_ckey))
if(!query_memodel.Execute())
var/err = query_memodel.ErrorMsg()
qdel(query_memodel)
log_game("SQL ERROR removing memo. Error : \[[err]\]\n")
return
- if(target_sql_ckey == sql_ckey)
+ if(target_ckey == ckey)
log_admin("[key_name(src)] has removed their mentor memo.")
message_admins("[key_name_admin(src)] has removed their mentor memo.")
else
- log_admin("[key_name(src)] has removed [target_sql_ckey]'s mentor memo.")
- message_admins("[key_name_admin(src)] has removed [target_sql_ckey]'s mentor memo.")
+ log_admin("[key_name(src)] has removed [target_ckey]'s mentor memo.")
+ message_admins("[key_name_admin(src)] has removed [target_ckey]'s mentor memo.")
diff --git a/tgstation.dme b/tgstation.dme
index 5135ae3b9d..f53a015ea4 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -159,6 +159,7 @@
#include "code\__HELPERS\angles.dm"
#include "code\__HELPERS\areas.dm"
#include "code\__HELPERS\AStar.dm"
+#include "code\__HELPERS\chat.dm"
#include "code\__HELPERS\cmp.dm"
#include "code\__HELPERS\custom_holoforms.dm"
#include "code\__HELPERS\dates.dm"