This commit is contained in:
AffectedArc07
2019-05-29 14:03:03 +01:00
1946 changed files with 54314 additions and 142643 deletions
+90 -41
View File
@@ -2,38 +2,46 @@
//How many new ckey matches before we revert the stickyban to it's roundstart state
//These are exclusive, so once it goes over one of these numbers, it reverts the ban
#define STICKYBAN_MAX_MATCHES 20
#define STICKYBAN_MAX_EXISTING_USER_MATCHES 5 //ie, users who were connected before the ban triggered
#define STICKYBAN_MAX_ADMIN_MATCHES 2
#define STICKYBAN_MAX_MATCHES 15
#define STICKYBAN_MAX_EXISTING_USER_MATCHES 3 //ie, users who were connected before the ban triggered
#define STICKYBAN_MAX_ADMIN_MATCHES 1
/world/IsBanned(key,address,computer_id,type,real_bans_only=FALSE)
if (!key || !address || !computer_id)
/world/IsBanned(key, address, computer_id, type, real_bans_only=FALSE)
debug_world_log("isbanned(): '[args.Join("', '")]'")
if (!key || (!real_bans_only && (!address || !computer_id)))
if(real_bans_only)
return FALSE
log_access("Failed Login (invalid data): [key] [address]-[computer_id]")
return list("reason"="invalid login data", "desc"="Error: Could not check ban status, Please try again. Error message: Your computer provided invalid or blank information to the server on connection (byond username, IP, and Computer ID.) Provided information for reference: Username:'[key]' IP:'[address]' Computer ID:'[computer_id]'. (If you continue to get this error, please restart byond or contact byond support.)")
if (text2num(computer_id) == 2147483647) //this cid causes stickybans to go haywire
log_access("Failed Login (invalid cid): [key] [address]-[computer_id]")
return list("reason"="invalid login data", "desc"="Error: Could not check ban status, Please try again. Error message: Your computer provided an invalid Computer ID.)")
var/admin = 0
var/admin = FALSE
var/ckey = ckey(key)
//IsBanned can get re-called on a user in certain situations, this prevents that leading to repeated messages to admins.
var/static/list/checkedckeys = list()
//magic voodo to check for a key in a list while also adding that key to the list without having to do two associated lookups
var/message = !checkedckeys[ckey]++
if(GLOB.admin_datums[ckey] || GLOB.deadmins[ckey])
admin = 1
admin = TRUE
var/client/C = GLOB.directory[ckey]
//Whitelist
if(CONFIG_GET(flag/usewhitelist))
if(!real_bans_only && !C && CONFIG_GET(flag/usewhitelist))
if(!check_whitelist(ckey))
if (admin)
log_admin("The admin [key] has been allowed to bypass the whitelist")
message_admins("<span class='adminnotice'>The admin [key] has been allowed to bypass the whitelist</span>")
addclientmessage(ckey,"<span class='adminnotice'>You have been allowed to bypass the whitelist</span>")
if (message)
message_admins("<span class='adminnotice'>The admin [key] has been allowed to bypass the whitelist</span>")
addclientmessage(ckey,"<span class='adminnotice'>You have been allowed to bypass the whitelist</span>")
else
log_access("Failed Login: [key] - Not on whitelist")
return list("reason"="whitelist", "desc" = "\nReason: You are not on the white list for this server")
//Guest Checking
if(!real_bans_only && IsGuestKey(key))
if(!real_bans_only && !C && IsGuestKey(key))
if (CONFIG_GET(flag/guest_ban))
log_access("Failed Login: [key] - Guests not allowed")
return list("reason"="guest", "desc"="\nReason: Guests not allowed. Please sign in with a byond account.")
@@ -43,14 +51,19 @@
//Population Cap Checking
var/extreme_popcap = CONFIG_GET(number/extreme_popcap)
if(!real_bans_only && extreme_popcap && living_player_count() >= extreme_popcap && !admin)
log_access("Failed Login: [key] - Population cap reached")
return list("reason"="popcap", "desc"= "\nReason: [CONFIG_GET(string/extreme_popcap_message)]")
if(!real_bans_only && !C && extreme_popcap && !admin)
var/popcap_value = GLOB.clients.len
if(popcap_value >= extreme_popcap && !GLOB.joined_player_list.Find(ckey))
if(!CONFIG_GET(flag/byond_member_bypass_popcap) || !world.IsSubscribed(ckey, "BYOND"))
log_access("Failed Login: [key] - Population cap reached")
return list("reason"="popcap", "desc"= "\nReason: [CONFIG_GET(string/extreme_popcap_message)]")
if(CONFIG_GET(flag/sql_enabled))
if(!SSdbcore.Connect())
var/msg = "Ban database connection failure. Key [ckey] not checked"
log_world(msg)
message_admins(msg)
if (message)
message_admins(msg)
else
var/list/ban_details = is_banned_from_with_details(ckey, address, computer_id, "Server")
for(var/i in ban_details)
@@ -58,12 +71,14 @@
if(text2num(i["applies_to_admins"]))
var/msg = "Admin [key] is admin banned, and has been disallowed access."
log_admin(msg)
message_admins(msg)
if (message)
message_admins(msg)
else
var/msg = "Admin [key] has been allowed to bypass a matching non-admin ban on [i["key"]] [i["ip"]]-[i["computerid"]]."
log_admin(msg)
message_admins(msg)
addclientmessage(ckey,"<span class='adminnotice'>Admin [key] has been allowed to bypass a matching non-admin ban on [i["key"]] [i["ip"]]-[i["computerid"]].</span>")
if (message)
message_admins(msg)
addclientmessage(ckey,"<span class='adminnotice'>Admin [key] has been allowed to bypass a matching non-admin ban on [i["key"]] [i["ip"]]-[i["computerid"]].</span>")
continue
var/expires = "This is a permanent ban."
if(i["expiration_time"])
@@ -74,18 +89,23 @@
[expires]"}
log_access("Failed Login: [key] [computer_id] [address] - Banned (#[i["id"]])")
return list("reason"="Banned","desc"="[desc]")
var/list/ban = ..() //default pager ban stuff
if (ban)
if (!admin)
. = ban
if (real_bans_only)
return
var/bannedckey = "ERROR"
if (ban["ckey"])
bannedckey = ban["ckey"]
var/newmatch = FALSE
var/client/C = GLOB.directory[ckey]
var/cachedban = SSstickyban.cache[bannedckey]
var/list/cachedban = SSstickyban.cache[bannedckey]
//rogue ban in the process of being reverted.
if (cachedban && cachedban["reverting"])
if (cachedban && (cachedban["reverting"] || cachedban["timeout"]))
world.SetConfig("ban", bannedckey, null)
return null
if (cachedban && ckey != bannedckey)
@@ -98,51 +118,80 @@
if (newmatch && cachedban)
var/list/newmatches = cachedban["matches_this_round"]
var/list/pendingmatches = cachedban["matches_this_round"]
var/list/newmatches_connected = cachedban["existing_user_matches_this_round"]
var/list/newmatches_admin = cachedban["admin_matches_this_round"]
newmatches[ckey] = ckey
if (C)
newmatches_connected[ckey] = ckey
newmatches_connected = cachedban["existing_user_matches_this_round"]
pendingmatches[ckey] = ckey
sleep(STICKYBAN_ROGUE_CHECK_TIME)
pendingmatches -= ckey
if (admin)
newmatches_admin[ckey] = ckey
if (cachedban["reverting"] || cachedban["timeout"])
return null
newmatches[ckey] = ckey
if (\
newmatches.len > STICKYBAN_MAX_MATCHES || \
newmatches.len+pendingmatches.len > STICKYBAN_MAX_MATCHES || \
newmatches_connected.len > STICKYBAN_MAX_EXISTING_USER_MATCHES || \
newmatches_admin.len > STICKYBAN_MAX_ADMIN_MATCHES \
)
if (cachedban["reverting"])
return null
cachedban["reverting"] = TRUE
)
var/action
if (ban["fromdb"])
cachedban["timeout"] = TRUE
action = "putting it on timeout for the remainder of the round"
else
cachedban["reverting"] = TRUE
action = "reverting to its roundstart state"
world.SetConfig("ban", bannedckey, null)
log_game("Stickyban on [bannedckey] detected as rogue, reverting to its roundstart state")
message_admins("Stickyban on [bannedckey] detected as rogue, reverting to its roundstart state")
//we always report this
log_game("Stickyban on [bannedckey] detected as rogue, [action]")
message_admins("Stickyban on [bannedckey] detected as rogue, [action]")
//do not convert to timer.
spawn (5)
world.SetConfig("ban", bannedckey, null)
sleep(1)
world.SetConfig("ban", bannedckey, null)
cachedban["matches_this_round"] = list()
cachedban["existing_user_matches_this_round"] = list()
cachedban["admin_matches_this_round"] = list()
cachedban -= "reverting"
world.SetConfig("ban", bannedckey, list2stickyban(cachedban))
if (!ban["fromdb"])
cachedban = cachedban.Copy() //so old references to the list still see the ban as reverting
cachedban["matches_this_round"] = list()
cachedban["existing_user_matches_this_round"] = list()
cachedban["admin_matches_this_round"] = list()
cachedban -= "reverting"
SSstickyban.cache[bannedckey] = cachedban
world.SetConfig("ban", bannedckey, list2stickyban(cachedban))
return null
if (ban["fromdb"])
if(SSdbcore.Connect())
INVOKE_ASYNC(SSdbcore, /datum/controller/subsystem/dbcore/proc.QuerySelect, list(
SSdbcore.NewQuery("INSERT INTO [format_table_name("stickyban_matched_ckey")] (matched_ckey, stickyban) VALUES ('[sanitizeSQL(ckey)]', '[sanitizeSQL(bannedckey)]') ON DUPLICATE KEY UPDATE last_matched = now()"),
SSdbcore.NewQuery("INSERT INTO [format_table_name("stickyban_matched_ip")] (matched_ip, stickyban) VALUES ( INET_ATON('[sanitizeSQL(address)]'), '[sanitizeSQL(bannedckey)]') ON DUPLICATE KEY UPDATE last_matched = now()"),
SSdbcore.NewQuery("INSERT INTO [format_table_name("stickyban_matched_cid")] (matched_cid, stickyban) VALUES ('[sanitizeSQL(computer_id)]', '[sanitizeSQL(bannedckey)]') ON DUPLICATE KEY UPDATE last_matched = now()")
), FALSE, TRUE)
//byond will not trigger isbanned() for "global" host bans,
//ie, ones where the "apply to this game only" checkbox is not checked (defaults to not checked)
//So it's safe to let admins walk thru host/sticky bans here
if (admin)
log_admin("The admin [key] has been allowed to bypass a matching host/sticky ban on [bannedckey]")
message_admins("<span class='adminnotice'>The admin [key] has been allowed to bypass a matching host/sticky ban on [bannedckey]</span>")
addclientmessage(ckey,"<span class='adminnotice'>You have been allowed to bypass a matching host/sticky ban on [bannedckey]</span>")
if (message)
message_admins("<span class='adminnotice'>The admin [key] has been allowed to bypass a matching host/sticky ban on [bannedckey]</span>")
addclientmessage(ckey,"<span class='adminnotice'>You have been allowed to bypass a matching host/sticky ban on [bannedckey]</span>")
return null
if (C) //user is already connected!.
to_chat(C, "You are about to get disconnected for matching a sticky ban after you connected. If this turns out to be the ban evasion detection system going haywire, we will automatically detect this and revert the matches. if you feel that this is the case, please wait EXACTLY 6 seconds then reconnect using file -> reconnect to see if the match was reversed.")
to_chat(C, "You are about to get disconnected for matching a sticky ban after you connected. If this turns out to be the ban evasion detection system going haywire, we will automatically detect this and revert the matches. if you feel that this is the case, please wait EXACTLY 6 seconds then reconnect using file -> reconnect to see if the match was automatically reversed.")
var/desc = "\nReason:(StickyBan) You, or another user of this computer or connection ([bannedckey]) is banned from playing here. The ban reason is:\n[ban["message"]]\nThis ban was applied by [ban["admin"]]\nThis is a BanEvasion Detection System ban, if you think this ban is a mistake, please wait EXACTLY 6 seconds, then try again before filing an appeal.\n"
. = list("reason" = "Stickyban", "desc" = desc)
+1 -1
View File
@@ -2,7 +2,7 @@
if(!message || !subject)
return
var/F = file("[GLOB.log_directory]/[subject].html")
WRITE_FILE(F, "<small>[time_stamp()] [REF(src)] ([x],[y],[z])</small> || [src] [message]<br>")
WRITE_FILE(F, "[time_stamp()] [REF(src)] ([x],[y],[z]) || [src] [message]<br>")
/client/proc/investigate_show()
set name = "Investigate"
+1 -1
View File
@@ -23,7 +23,7 @@ GLOBAL_PROTECT(protected_ranks)
name = init_name
if(!name)
qdel(src)
throw EXCEPTION("Admin rank created without name.")
CRASH("Admin rank created without name.")
return
if(init_rights)
rights = init_rights
+15 -13
View File
@@ -1,7 +1,7 @@
//admin verb groups - They can overlap if you so wish. Only one of each verb will exist in the verbs list regardless
//the procs are cause you can't put the comments in the GLOB var define
GLOBAL_PROTECT(admin_verbs_default)
GLOBAL_LIST_INIT(admin_verbs_default, world.AVerbsDefault())
GLOBAL_PROTECT(admin_verbs_default)
/world/proc/AVerbsDefault()
return list(
/client/proc/deadmin, /*destroys our own admin datum so we can play as a regular player*/
@@ -19,8 +19,8 @@ GLOBAL_LIST_INIT(admin_verbs_default, world.AVerbsDefault())
/client/proc/cmd_admin_pm_panel, /*admin-pm list*/
/client/proc/stop_sounds
)
GLOBAL_PROTECT(admin_verbs_admin)
GLOBAL_LIST_INIT(admin_verbs_admin, world.AVerbsAdmin())
GLOBAL_PROTECT(admin_verbs_admin)
/world/proc/AVerbsAdmin()
return list(
/client/proc/invisimin, /*allows our mob to go invisible/visible*/
@@ -74,11 +74,10 @@ GLOBAL_LIST_INIT(admin_verbs_admin, world.AVerbsAdmin())
/client/proc/respawn_character,
/datum/admins/proc/open_borgopanel
)
GLOBAL_PROTECT(admin_verbs_ban)
GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/unban_panel, /client/proc/ban_panel, /client/proc/stickybanpanel))
GLOBAL_PROTECT(admin_verbs_sounds)
GLOBAL_PROTECT(admin_verbs_ban)
GLOBAL_LIST_INIT(admin_verbs_sounds, list(/client/proc/play_local_sound, /client/proc/play_sound, /client/proc/set_round_end_sound))
GLOBAL_PROTECT(admin_verbs_fun)
GLOBAL_PROTECT(admin_verbs_sounds)
GLOBAL_LIST_INIT(admin_verbs_fun, list(
/client/proc/cmd_admin_dress,
/client/proc/cmd_admin_gib_self,
@@ -103,10 +102,11 @@ GLOBAL_LIST_INIT(admin_verbs_fun, list(
/client/proc/smite,
/client/proc/admin_away
))
GLOBAL_PROTECT(admin_verbs_spawn)
GLOBAL_PROTECT(admin_verbs_fun)
GLOBAL_LIST_INIT(admin_verbs_spawn, list(/datum/admins/proc/spawn_atom, /datum/admins/proc/podspawn_atom, /datum/admins/proc/spawn_cargo, /datum/admins/proc/spawn_objasmob, /client/proc/respawn_character))
GLOBAL_PROTECT(admin_verbs_server)
GLOBAL_PROTECT(admin_verbs_spawn)
GLOBAL_LIST_INIT(admin_verbs_server, world.AVerbsServer())
GLOBAL_PROTECT(admin_verbs_server)
/world/proc/AVerbsServer()
return list(
/datum/admins/proc/startnow,
@@ -124,8 +124,8 @@ GLOBAL_LIST_INIT(admin_verbs_server, world.AVerbsServer())
/client/proc/panicbunker,
/client/proc/toggle_hub
)
GLOBAL_PROTECT(admin_verbs_debug)
GLOBAL_LIST_INIT(admin_verbs_debug, world.AVerbsDebug())
GLOBAL_PROTECT(admin_verbs_debug)
/world/proc/AVerbsDebug()
return list(
/client/proc/restart_controller,
@@ -165,15 +165,14 @@ GLOBAL_LIST_INIT(admin_verbs_debug, world.AVerbsDebug())
/client/proc/reload_configuration,
/datum/admins/proc/create_or_modify_area,
)
GLOBAL_PROTECT(admin_verbs_possess)
GLOBAL_LIST_INIT(admin_verbs_possess, list(/proc/possess, /proc/release))
GLOBAL_PROTECT(admin_verbs_permissions)
GLOBAL_PROTECT(admin_verbs_possess)
GLOBAL_LIST_INIT(admin_verbs_permissions, list(/client/proc/edit_admin_permissions))
GLOBAL_PROTECT(admin_verbs_poll)
GLOBAL_PROTECT(admin_verbs_permissions)
GLOBAL_LIST_INIT(admin_verbs_poll, list(/client/proc/create_poll))
GLOBAL_PROTECT(admin_verbs_poll)
//verbs which can be hidden - needs work
GLOBAL_PROTECT(admin_verbs_hideable)
GLOBAL_LIST_INIT(admin_verbs_hideable, list(
/client/proc/set_ooc,
/client/proc/reset_ooc,
@@ -237,6 +236,7 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
/client/proc/toggle_combo_hud,
/client/proc/debug_huds
))
GLOBAL_PROTECT(admin_verbs_hideable)
/client/proc/add_admin_verbs()
if(holder)
@@ -333,11 +333,12 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
set name = "Aghost"
if(!holder)
return
. = TRUE
if(isobserver(mob))
//re-enter
var/mob/dead/observer/ghost = mob
if(!ghost.mind || !ghost.mind.current) //won't do anything if there is no body
return
return FALSE
if(!ghost.can_reenter_corpse)
log_admin("[key_name(usr)] re-entered corpse")
message_admins("[key_name_admin(usr)] re-entered corpse")
@@ -346,6 +347,7 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
SSblackbox.record_feedback("tally", "admin_verb", 1, "Admin Reenter") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
else if(isnewplayer(mob))
to_chat(src, "<font color='red'>Error: Aghost: Can't admin-ghost whilst in the lobby. Join or Observe first.</font>")
return FALSE
else
//ghostize
log_admin("[key_name(usr)] admin ghosted.")
+1 -1
View File
@@ -80,7 +80,7 @@ GLOBAL_VAR(antag_prototypes)
var/list/result = list()
if(!current)
result += "<span class='bad'>No body!</span>"
if(current && current.has_trait(TRAIT_MINDSHIELD))
if(current && HAS_TRAIT(current, TRAIT_MINDSHIELD))
result += "<span class='good'>Mindshielded</span>"
//Move these to mob
if(iscyborg(current))
+2 -2
View File
@@ -39,11 +39,11 @@ GLOBAL_PROTECT(href_token)
return
if(!ckey)
QDEL_IN(src, 0)
throw EXCEPTION("Admin datum created without a ckey")
CRASH("Admin datum created without a ckey")
return
if(!istype(R))
QDEL_IN(src, 0)
throw EXCEPTION("Admin datum created without a rank")
CRASH("Admin datum created without a rank")
return
target = ckey
name = "[ckey]'s admin datum ([R])"
+8
View File
@@ -269,6 +269,14 @@
log_admin("[key_name(usr)] forcefully deadmined [admin_key]")
D.deactivate() //after logs so the deadmined admin can see the message.
/datum/admins/proc/auto_deadmin()
to_chat(owner, "<span class='interface'>You are now a normal player.</span>")
var/old_owner = owner
deactivate()
message_admins("[old_owner] deadmined via auto-deadmin config.")
log_admin("[old_owner] deadmined via auto-deadmin config.")
return TRUE
/datum/admins/proc/change_admin_rank(admin_ckey, admin_key, use_db, datum/admins/D, legacy_only)
var/datum/admin_rank/R
var/list/rank_names = list()
+1 -1
View File
@@ -82,7 +82,7 @@
body += "<a href='?_src_=holder;[HrefToken()];adminplayerobservefollow="+ref+"'>FLW</a> - "
body += "<a href='?_src_=holder;[HrefToken()];individuallog="+ref+"'>LOGS</a><br>"
if(antagonist > 0)
body += "<font size='2'><a href='?_src_=holder;[HrefToken()];secrets=check_antagonist'><font color='red'><b>Antagonist</b></font></a></font>";
body += "<font size='2'><a href='?_src_=holder;[HrefToken()];check_antagonist=1'><font color='red'><b>Antagonist</b></font></a></font>";
body += "</td></tr></table>";
+1 -2
View File
@@ -18,7 +18,6 @@
<BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=clear_virus'>Cure all diseases currently in existence</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=list_bombers'>Bombing List</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=check_antagonist'>Show current traitors and objectives</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=list_signalers'>Show last [length(GLOB.lastsignalers)] signalers</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=list_lawchanges'>Show last [length(GLOB.lawchanges)] law changes</A><BR>
<A href='?src=[REF(src)];[HrefToken()];secrets=showailaws'>Show AI Laws</A><BR>
@@ -418,7 +417,7 @@
H.equip_to_slot_or_del(I, SLOT_W_UNIFORM)
qdel(olduniform)
if(droptype == "Yes")
I.add_trait(TRAIT_NODROP, ADMIN_TRAIT)
ADD_TRAIT(I, TRAIT_NODROP, ADMIN_TRAIT)
else
to_chat(H, "You're not kawaii enough for this.")
+40 -25
View File
@@ -97,16 +97,16 @@
<div class='inputbox'></div></label>
<input type='text' name='keytext' size='26' value='[player_key]'>
<label class='inputlabel checkbox'>IP:
<input type='checkbox' id='ipcheck' name='ipcheck' value='1'[player_ip ? " checked": ""]>
<input type='checkbox' id='ipcheck' name='ipcheck' value='1'[isnull(duration) ? " checked" : ""]>
<div class='inputbox'></div></label>
<input type='text' name='iptext' size='18' value='[player_ip]'>
<label class='inputlabel checkbox'>CID:
<input type='checkbox' id='cidcheck' name='cidcheck' value='1'[player_cid ? " checked": ""]>
<input type='checkbox' id='cidcheck' name='cidcheck' value='1' checked>
<div class='inputbox'></div></label>
<input type='text' name='cidtext' size='14' value='[player_cid]'>
<br>
<label class='inputlabel checkbox'>Use IP and CID from last connection of key
<input type='checkbox' id='lastconn' name='lastconn' value='1'>
<input type='checkbox' id='lastconn' name='lastconn' value='1' [(isnull(duration) && !player_ip) || (!player_cid) ? " checked": ""]>
<div class='inputbox'></div></label>
<label class='inputlabel checkbox'>Applies to Admins
<input type='checkbox' id='applyadmins' name='applyadmins' value='1'[applies_to_admins ? " checked": ""]>
@@ -286,7 +286,9 @@
return
var/list/error_state = list()
var/player_key
var/ip_check = FALSE
var/player_ip
var/cid_check = FALSE
var/player_cid
var/use_last_connection = FALSE
var/applies_to_admins = FALSE
@@ -308,21 +310,26 @@
player_key = href_list["keytext"]
if(!player_key)
error_state += "Key was ticked but none was provided."
if(href_list["ipcheck"])
ip_check = TRUE
if(href_list["cidcheck"])
cid_check = TRUE
if(href_list["lastconn"])
use_last_connection = TRUE
if(!player_key)
error_state += "A key must be provided to use IP and CID from last connection."
if(player_key)
use_last_connection = TRUE
else
if(href_list["ipcheck"])
if(ip_check)
player_ip = href_list["iptext"]
if(!player_ip)
if(!player_ip && !use_last_connection)
error_state += "IP was ticked but none was provided."
if(href_list["cidcheck"])
if(cid_check)
player_cid = href_list["cidtext"]
if(!player_cid)
if(!player_cid && !use_last_connection)
error_state += "CID was ticked but none was provided."
if(!use_last_connection && !player_ip && !player_cid && !player_key)
error_state += "At least a key, IP or CID must be provided."
if(use_last_connection && !ip_check && !cid_check)
error_state += "Use last connection was ticked, but neither IP nor CID was."
if(href_list["applyadmins"])
applies_to_admins = TRUE
switch(href_list["radioduration"])
@@ -384,11 +391,11 @@
to_chat(usr, "<span class='danger'>Ban not [edit_id ? "edited" : "created"] because the following errors were present:\n[error_state.Join("\n")]</span>")
return
if(edit_id)
edit_ban(edit_id, player_key, player_ip, player_cid, use_last_connection, applies_to_admins, duration, interval, reason, mirror_edit, old_key, old_ip, old_cid, old_applies, page, admin_key, changes)
edit_ban(edit_id, player_key, ip_check, player_ip, cid_check, player_cid, use_last_connection, applies_to_admins, duration, interval, reason, mirror_edit, old_key, old_ip, old_cid, old_applies, page, admin_key, changes)
else
create_ban(player_key, player_ip, player_cid, use_last_connection, applies_to_admins, duration, interval, severity, reason, roles_to_ban)
create_ban(player_key, ip_check, player_ip, cid_check, player_cid, use_last_connection, applies_to_admins, duration, interval, severity, reason, roles_to_ban)
/datum/admins/proc/create_ban(player_key, player_ip, player_cid, use_last_connection, applies_to_admins, duration, interval, severity, reason, list/roles_to_ban)
/datum/admins/proc/create_ban(player_key, ip_check, player_ip, cid_check, player_cid, use_last_connection, applies_to_admins, duration, interval, severity, reason, list/roles_to_ban)
if(!check_rights(R_BAN))
return
if(!SSdbcore.Connect())
@@ -405,13 +412,15 @@
if(query_create_ban_get_player.NextRow())
player_key = query_create_ban_get_player.item[1]
if(use_last_connection)
player_ip = query_create_ban_get_player.item[2]
player_cid = query_create_ban_get_player.item[3]
if(ip_check)
player_ip = query_create_ban_get_player.item[2]
if(cid_check)
player_cid = query_create_ban_get_player.item[3]
else
if(use_last_connection)
to_chat(usr, "<span class='danger'>Ban not created. [player_key]/([player_ckey]) hasn't been seen before, unable to use IP and CID from last connection.</span>")
qdel(query_create_ban_get_player)
return
if(alert(usr, "[player_key]/([player_ckey]) has not been seen before, unable to use IP and CID from last connection. Are you sure you want to create a ban for them?", "Unknown key", "Yes", "No", "Cancel") != "Yes")
qdel(query_create_ban_get_player)
return
else
if(alert(usr, "[player_key]/([player_ckey]) has not been seen before, are you sure you want to create a ban for them?", "Unknown key", "Yes", "No", "Cancel") != "Yes")
qdel(query_create_ban_get_player)
@@ -633,7 +642,7 @@
to_chat(i, "<span class='boldannounce'>[usr.client.key] has removed a ban from [role] for your IP or CID.")
unban_panel(player_key, admin_key, player_ip, player_cid, page)
/datum/admins/proc/edit_ban(ban_id, player_key, player_ip, player_cid, use_last_connection, applies_to_admins, duration, interval, reason, mirror_edit, old_key, old_ip, old_cid, old_applies, admin_key, page, list/changes)
/datum/admins/proc/edit_ban(ban_id, player_key, ip_check, player_ip, cid_check, player_cid, use_last_connection, applies_to_admins, duration, interval, reason, mirror_edit, old_key, old_ip, old_cid, old_applies, admin_key, page, list/changes)
if(!check_rights(R_BAN))
return
if(!SSdbcore.Connect())
@@ -653,13 +662,19 @@
player_key = query_edit_ban_get_player.item[1]
bantime = query_edit_ban_get_player.item[2]
if(use_last_connection)
player_ip = query_edit_ban_get_player.item[3]
player_cid = query_edit_ban_get_player.item[4]
if(ip_check)
player_ip = query_edit_ban_get_player.item[3]
if(cid_check)
player_cid = query_edit_ban_get_player.item[4]
else
if(use_last_connection)
to_chat(usr, "<span class='danger'>Ban not edited. [player_key]/([player_ckey]) hasn't been seen before, unable to use IP and CID from last connection.</span>")
qdel(query_edit_ban_get_player)
return
if(alert(usr, "[player_key]/([player_ckey]) has not been seen before, unable to use IP and CID from last connection. Are you sure you want to edit a ban for them?", "Unknown key", "Yes", "No", "Cancel") != "Yes")
qdel(query_edit_ban_get_player)
return
else
if(alert(usr, "[player_key]/([player_ckey]) has not been seen before, are you sure you want to edit a ban for them?", "Unknown key", "Yes", "No", "Cancel") != "Yes")
qdel(query_edit_ban_get_player)
return
qdel(query_edit_ban_get_player)
if(applies_to_admins && (applies_to_admins != old_applies))
var/admin_ckey = sanitizeSQL(usr.client.ckey)
@@ -753,4 +768,4 @@
. += player_cid
else
. += "NULL"
. = jointext(., "/")
. = jointext(., "/")
+1 -1
View File
@@ -504,7 +504,7 @@
output += ruler
var/datum/browser/browser = new(usr, "Note panel", "Manage player notes", 1000, 500)
var/datum/asset/notes_assets = get_asset_datum(/datum/asset/simple/notes)
notes_assets.send(src)
notes_assets.send(usr.client)
browser.set_content(jointext(output, ""))
browser.open()
+270 -43
View File
@@ -7,7 +7,7 @@
if ("add")
var/list/ban = list()
var/ckey
ban["admin"] = usr.key
ban["admin"] = usr.ckey
ban["type"] = list("sticky")
ban["reason"] = "(InGameBan)([usr.key])" //this will be displayed in dd only
@@ -22,6 +22,7 @@
if (get_stickyban_from_ckey(ckey))
to_chat(usr, "<span class='adminnotice'>Error: Can not add a stickyban: User already has a current sticky ban</span>")
return
if (data["reason"])
ban["message"] = data["reason"]
@@ -31,7 +32,19 @@
return
ban["message"] = "[reason]"
if(SSdbcore.Connect())
var/datum/DBQuery/query_create_stickyban = SSdbcore.NewQuery("INSERT INTO [format_table_name("stickyban")] (ckey, reason, banning_admin) VALUES ('[sanitizeSQL(ckey)]', '[sanitizeSQL(ban["message"])]', '[sanitizeSQL(usr.ckey)]')")
if (query_create_stickyban.warn_execute())
ban["fromdb"] = TRUE
qdel(query_create_stickyban)
world.SetConfig("ban",ckey,list2stickyban(ban))
ban = stickyban2list(list2stickyban(ban))
ban["matches_this_round"] = list()
ban["existing_user_matches_this_round"] = list()
ban["admin_matches_this_round"] = list()
ban["pending_matches_this_round"] = list()
SSstickyban.cache[ckey] = ban
log_admin_private("[key_name(usr)] has stickybanned [ckey].\nReason: [ban["message"]]")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has stickybanned [ckey].\nReason: [ban["message"]]</span>")
@@ -51,6 +64,16 @@
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>")
return
world.SetConfig("ban",ckey, null)
SSstickyban.cache -= ckey
if (SSdbcore.Connect())
SSdbcore.QuerySelect(list(
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban")] WHERE ckey = '[sanitizeSQL(ckey)]'"),
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = '[sanitizeSQL(ckey)]'"),
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_cid")] WHERE stickyban = '[sanitizeSQL(ckey)]'"),
SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ip")] WHERE stickyban = '[sanitizeSQL(ckey)]'")
), warn = TRUE, qdel = TRUE)
log_admin_private("[key_name(usr)] removed [ckey]'s stickyban")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] removed [ckey]'s stickyban</span>")
@@ -67,18 +90,12 @@
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>")
return
var/found = 0
//we have to do it this way because byond keeps the case in its sticky ban matches WHY!!!
for (var/key in ban["keys"])
if (ckey(key) == alt)
found = 1
break
if (!found)
var/key = LAZYACCESS(ban["keys"], alt)
if (!key)
to_chat(usr, "<span class='adminnotice'>Error: [alt] is not linked to [ckey]'s sticky ban!</span>")
return
if (alert("Are you sure you want to disassociate [alt] from [ckey]'s sticky ban? \nNote: Nothing stops byond from re-linking them","Are you sure","Yes","No") == "No")
if (alert("Are you sure you want to disassociate [alt] from [ckey]'s sticky ban? \nNote: Nothing stops byond from re-linking them, Use \[E] to exempt them","Are you sure","Yes","No") == "No")
return
//we have to do this again incase something changes
@@ -87,19 +104,22 @@
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>")
return
found = 0
for (var/key in ban["keys"])
if (ckey(key) == alt)
ban["keys"] -= key
found = 1
break
key = LAZYACCESS(ban["keys"], alt)
if (!found)
if (!key)
to_chat(usr, "<span class='adminnotice'>Error: [alt] link to [ckey]'s sticky ban disappeared.</span>")
return
LAZYREMOVE(ban["keys"], alt)
world.SetConfig("ban",ckey,list2stickyban(ban))
SSstickyban.cache[ckey] = ban
if (SSdbcore.Connect())
var/datum/DBQuery/query_remove_stickyban_alt = SSdbcore.NewQuery("DELETE FROM [format_table_name("stickyban_matched_ckey")] WHERE stickyban = '[sanitizeSQL(ckey)]' AND matched_ckey = '[sanitizeSQL(alt)]'")
query_remove_stickyban_alt.warn_execute()
qdel(query_remove_stickyban_alt)
log_admin_private("[key_name(usr)] has disassociated [alt] from [ckey]'s sticky ban")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has disassociated [alt] from [ckey]'s sticky ban</span>")
@@ -124,14 +144,171 @@
world.SetConfig("ban",ckey,list2stickyban(ban))
SSstickyban.cache[ckey] = ban
if (SSdbcore.Connect())
var/datum/DBQuery/query_edit_stickyban = SSdbcore.NewQuery("UPDATE [format_table_name("stickyban")] SET reason = '[sanitizeSQL(reason)]' WHERE ckey = '[sanitizeSQL(ckey)]'")
query_edit_stickyban.warn_execute()
qdel(query_edit_stickyban)
log_admin_private("[key_name(usr)] has edited [ckey]'s sticky ban reason from [oldreason] to [reason]")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has edited [ckey]'s sticky ban reason from [oldreason] to [reason]</span>")
if ("exempt")
if (!data["ckey"])
return
var/ckey = data["ckey"]
if (!data["alt"])
return
var/alt = ckey(data["alt"])
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>")
return
var/key = LAZYACCESS(ban["keys"], alt)
if (!key)
to_chat(usr, "<span class='adminnotice'>Error: [alt] is not linked to [ckey]'s sticky ban!</span>")
return
if (alert("Are you sure you want to exempt [alt] from [ckey]'s sticky ban?","Are you sure","Yes","No") == "No")
return
//we have to do this again incase something changes
ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>")
return
key = LAZYACCESS(ban["keys"], alt)
if (!key)
to_chat(usr, "<span class='adminnotice'>Error: [alt]'s link to [ckey]'s sticky ban disappeared.</span>")
return
LAZYREMOVE(ban["keys"], alt)
key["exempt"] = TRUE
LAZYSET(ban["whitelist"], alt, key)
world.SetConfig("ban",ckey,list2stickyban(ban))
SSstickyban.cache[ckey] = ban
if (SSdbcore.Connect())
var/datum/DBQuery/query_exempt_stickyban_alt = SSdbcore.NewQuery("UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 1 WHERE stickyban = '[sanitizeSQL(ckey)]' AND matched_ckey = '[sanitizeSQL(alt)]'")
query_exempt_stickyban_alt.warn_execute()
qdel(query_exempt_stickyban_alt)
log_admin_private("[key_name(usr)] has exempted [alt] from [ckey]'s sticky ban")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has exempted [alt] from [ckey]'s sticky ban</span>")
if ("unexempt")
if (!data["ckey"])
return
var/ckey = data["ckey"]
if (!data["alt"])
return
var/alt = ckey(data["alt"])
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>")
return
var/key = LAZYACCESS(ban["whitelist"], alt)
if (!key)
to_chat(usr, "<span class='adminnotice'>Error: [alt] is not exempt from [ckey]'s sticky ban!</span>")
return
if (alert("Are you sure you want to unexempt [alt] from [ckey]'s sticky ban?","Are you sure","Yes","No") == "No")
return
//we have to do this again incase something changes
ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: The ban disappeared.</span>")
return
key = LAZYACCESS(ban["whitelist"], alt)
if (!key)
to_chat(usr, "<span class='adminnotice'>Error: [alt]'s exemption from [ckey]'s sticky ban disappeared.</span>")
return
LAZYREMOVE(ban["whitelist"], alt)
key["exempt"] = FALSE
LAZYSET(ban["keys"], alt, key)
world.SetConfig("ban",ckey,list2stickyban(ban))
SSstickyban.cache[ckey] = ban
if (SSdbcore.Connect())
var/datum/DBQuery/query_unexempt_stickyban_alt = SSdbcore.NewQuery("UPDATE [format_table_name("stickyban_matched_ckey")] SET exempt = 0 WHERE stickyban = '[sanitizeSQL(ckey)]' AND matched_ckey = '[sanitizeSQL(alt)]'")
query_unexempt_stickyban_alt.warn_execute()
qdel(query_unexempt_stickyban_alt)
log_admin_private("[key_name(usr)] has unexempted [alt] from [ckey]'s sticky ban")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has unexempted [alt] from [ckey]'s sticky ban</span>")
if ("timeout")
if (!data["ckey"])
return
if (!SSdbcore.Connect())
to_chat(usr, "<span class='adminnotice'>No database connection!</span>")
return
var/ckey = data["ckey"]
if (alert("Are you sure you want to put [ckey]'s stickyban on timeout until next round (or removed)?","Are you sure","Yes","No") == "No")
return
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>")
return
ban["timeout"] = TRUE
world.SetConfig("ban", ckey, null)
var/cachedban = SSstickyban.cache[ckey]
if (cachedban)
cachedban["timeout"] = TRUE
log_admin_private("[key_name(usr)] has put [ckey]'s sticky ban on timeout.")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has put [ckey]'s sticky ban on timeout.</span>")
if ("untimeout")
if (!data["ckey"])
return
if (!SSdbcore.Connect())
to_chat(usr, "<span class='adminnotice'>No database connection!</span>")
return
var/ckey = data["ckey"]
if (alert("Are you sure you want to lift the timeout on [ckey]'s stickyban?","Are you sure","Yes","No") == "No")
return
var/ban = get_stickyban_from_ckey(ckey)
var/cachedban = SSstickyban.cache[ckey]
if (cachedban)
cachedban["timeout"] = FALSE
if (!ban)
if (!cachedban)
to_chat(usr, "<span class='adminnotice'>Error: No sticky ban for [ckey] found!</span>")
return
ban = cachedban
ban["timeout"] = FALSE
world.SetConfig("ban",ckey,list2stickyban(ban))
log_admin_private("[key_name(usr)] has taken [ckey]'s sticky ban off of timeout.")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has taken [ckey]'s sticky ban off of timeout.</span>")
if ("revert")
if (!data["ckey"])
return
var/ckey = data["ckey"]
if (alert("Are you sure you want to revert the sticky ban on [ckey] to its state at round start?","Are you sure","Yes","No") == "No")
if (alert("Are you sure you want to revert the sticky ban on [ckey] to its state at round start (or last edit)?","Are you sure","Yes","No") == "No")
return
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
@@ -150,14 +327,22 @@
world.SetConfig("ban",ckey,list2stickyban(cached_ban))
/datum/admins/proc/stickyban_gethtml(ckey, ban)
. = {"
/datum/admins/proc/stickyban_gethtml(ckey)
var/ban = get_stickyban_from_ckey(ckey)
if (!ban)
return
var/timeout
if (SSdbcore.Connect())
timeout = "<a href='?_src_=holder;[HrefToken()];stickyban=[(ban["timeout"] ? "untimeout" : "timeout")]&ckey=[ckey]'>\[[(ban["timeout"] ? "untimeout" : "timeout" )]\]</a>"
else
timeout = "<a href='?_src_=holder;[HrefToken()];stickyban=revert&ckey=[ckey]'>\[revert\]</a>"
. = list({"
<a href='?_src_=holder;[HrefToken()];stickyban=remove&ckey=[ckey]'>\[-\]</a>
<a href='?_src_=holder;[HrefToken()];stickyban=revert&ckey=[ckey]'>\[revert\]</a>
[timeout]
<b>[ckey]</b>
<br />"
[ban["message"]] <b><a href='?_src_=holder;[HrefToken()];stickyban=edit&ckey=[ckey]'>\[Edit\]</a></b><br />
"}
"})
if (ban["admin"])
. += "[ban["admin"]]<br />"
else
@@ -166,19 +351,24 @@
for (var/key in ban["keys"])
if (ckey(key) == ckey)
continue
. += "<li><a href='?_src_=holder;[HrefToken()];stickyban=remove_alt&ckey=[ckey]&alt=[ckey(key)]'>\[-\]</a>[key]</li>"
. += "<li><a href='?_src_=holder;[HrefToken()];stickyban=remove_alt&ckey=[ckey]&alt=[ckey(key)]'>\[-\]</a>[key]<a href='?_src_=holder;[HrefToken()];stickyban=exempt&ckey=[ckey]&alt=[ckey(key)]'>\[E\]</a></li>"
for (var/key in ban["whitelist"])
if (ckey(key) == ckey)
continue
. += "<li><a href='?_src_=holder;[HrefToken()];stickyban=remove_alt&ckey=[ckey]&alt=[ckey(key)]'>\[-\]</a>[key]<a href='?_src_=holder;[HrefToken()];stickyban=unexempt&ckey=[ckey]&alt=[ckey(key)]'>\[UE\]</a></li>"
. += "</ol>\n"
/datum/admins/proc/stickyban_show()
if(!check_rights(R_BAN))
return
var/list/bans = sortList(world.GetConfig("ban"))
var/banhtml = ""
var/list/bans = sticky_banned_ckeys()
var/list/banhtml = list()
for(var/key in bans)
var/ckey = ckey(key)
var/ban = stickyban2list(world.GetConfig("ban",key))
banhtml += "<br /><hr />\n"
banhtml += stickyban_gethtml(ckey,ban)
banhtml += stickyban_gethtml(ckey)
var/html = {"
<head>
@@ -186,22 +376,49 @@
</head>
<body>
<h2>All Sticky Bans:</h2> <a href='?_src_=holder;[HrefToken()];stickyban=add'>\[+\]</a><br>
[banhtml]
[banhtml.Join("")]
</body>
"}
usr << browse(html,"window=stickybans;size=700x400")
/proc/sticky_banned_ckeys()
if (SSdbcore.Connect() || length(SSstickyban.dbcache))
if (SSstickyban.dbcacheexpire < world.time)
SSstickyban.Populatedbcache()
if (SSstickyban.dbcacheexpire)
return SSstickyban.dbcache.Copy()
return sortList(world.GetConfig("ban"))
/proc/get_stickyban_from_ckey(var/ckey)
. = list()
if (!ckey)
return null
ckey = ckey(ckey)
. = null
for (var/key in world.GetConfig("ban"))
if (ckey(key) == ckey)
. = stickyban2list(world.GetConfig("ban",key))
break
if (SSdbcore.Connect() || length(SSstickyban.dbcache))
if (SSstickyban.dbcacheexpire < world.time)
SSstickyban.Populatedbcache()
if (SSstickyban.dbcacheexpire)
. = SSstickyban.dbcache[ckey]
//reset the cache incase its a newer ban (but only if we didn't update the cache recently)
if (!. && SSstickyban.dbcacheexpire != world.time+STICKYBAN_DB_CACHE_TIME)
SSstickyban.dbcacheexpire = 1
SSstickyban.Populatedbcache()
. = SSstickyban.dbcache[ckey]
if (.)
var/list/cachedban = SSstickyban.cache["[ckey]"]
if (cachedban)
.["timeout"] = cachedban["timeout"]
/proc/stickyban2list(var/ban)
.["fromdb"] = TRUE
return
. = stickyban2list(world.GetConfig("ban", ckey)) || stickyban2list(world.GetConfig("ban", ckey(ckey))) || list()
if (!length(.))
return null
/proc/stickyban2list(ban, strictdb = TRUE)
if (!ban)
return null
. = params2list(ban)
@@ -212,30 +429,40 @@
var/ckey = ckey(key)
ckeys[ckey] = ckey //to make searching faster.
.["keys"] = ckeys
if (.["whitelist"])
var/keys = splittext(.["whitelist"], ",")
var/ckeys = list()
for (var/key in keys)
var/ckey = ckey(key)
ckeys[ckey] = ckey //to make searching faster.
.["whitelist"] = ckeys
.["type"] = splittext(.["type"], ",")
.["IP"] = splittext(.["IP"], ",")
.["computer_id"] = splittext(.["computer_id"], ",")
. -= "fromdb"
/proc/list2stickyban(var/list/ban)
/proc/list2stickyban(list/ban)
if (!ban || !islist(ban))
return null
. = ban.Copy()
if (.["keys"])
.["keys"] = jointext(.["keys"], ",")
if (.["IP"])
.["IP"] = jointext(.["IP"], ",")
if (.["computer_id"])
.["computer_id"] = jointext(.["computer_id"], ",")
if (.["whitelist"])
.["whitelist"] = jointext(.["whitelist"], ",")
if (.["type"])
.["type"] = jointext(.["type"], ",")
//internal tracking only, shouldn't be stored
. -= "reverting"
. -= "matches_this_round"
. -= "existing_user_matches_this_round"
. -= "admin_matches_this_round"
. -= "matches_this_round"
. -= "reverting"
. -= "pending_matches_this_round"
//storing these can sometimes cause sticky bans to start matching everybody
// and isn't even needed for sticky ban matching, as the hub tracks these separately
. -= "IP"
. -= "computer_id"
. = list2params(.)
+13 -8
View File
@@ -228,7 +228,6 @@
message_admins("<span class='adminnotice'>[key_name_admin(usr)] called the Emergency Shuttle to the station.</span>")
href_list["secrets"] = "check_antagonist"
else if(href_list["edit_shuttle_time"])
if(!check_rights(R_SERVER))
@@ -241,7 +240,6 @@
log_admin("[key_name(usr)] edited the Emergency Shuttle's timeleft to [timer] seconds.")
minor_announce("The emergency shuttle will reach its destination in [round(SSshuttle.emergency.timeLeft(600))] minutes.")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] edited the Emergency Shuttle's timeleft to [timer] seconds.</span>")
href_list["secrets"] = "check_antagonist"
else if(href_list["trigger_centcom_recall"])
if(!check_rights(R_ADMIN))
return
@@ -316,13 +314,14 @@
if(isnull(SSticker.admin_delay_notice))
return
else
if(alert(usr, "Really cancel current round end delay? The reason for the current delay is: \"[SSticker.admin_delay_notice]\"", "Undelay round end", "Yes", "No") != "Yes")
return
SSticker.admin_delay_notice = null
SSticker.delay_end = !SSticker.delay_end
var/reason = SSticker.delay_end ? "for reason: [SSticker.admin_delay_notice]" : "."//laziness
var/msg = "[SSticker.delay_end ? "delayed" : "undelayed"] the round end [reason]"
log_admin("[key_name(usr)] [msg]")
message_admins("[key_name_admin(usr)] [msg]")
href_list["secrets"] = "check_antagonist"
if(SSticker.ready_for_reboot && !SSticker.delay_end) //we undelayed after standard reboot would occur
SSticker.standard_reboot()
@@ -604,13 +603,17 @@
return
if (SSticker.HasRoundStarted())
return alert(usr, "The game has already started.", null, null, null, null)
if (askuser(usr, "The game has already started. Would you like to save this as the default mode effective next round?", "Save mode", "Yes", "Cancel", Timeout = null) == 1)
SSticker.save_mode(href_list["c_mode2"])
HandleCMode()
return
GLOB.master_mode = href_list["c_mode2"]
log_admin("[key_name(usr)] set the mode as [GLOB.master_mode].")
message_admins("<span class='adminnotice'>[key_name_admin(usr)] set the mode as [GLOB.master_mode].</span>")
to_chat(world, "<span class='adminnotice'><b>The mode is now: [GLOB.master_mode]</b></span>")
Game() // updates the main game menu
SSticker.save_mode(GLOB.master_mode)
if (askuser(usr, "Would you like to save this as the default mode for the server?", "Save mode", "Yes", "No", Timeout = null) == 1)
SSticker.save_mode(GLOB.master_mode)
HandleCMode()
else if(href_list["f_secret2"])
@@ -930,8 +933,12 @@
var/atom/movable/AM = locate(href_list["adminplayerobservefollow"])
var/client/C = usr.client
var/can_ghost = TRUE
if(!isobserver(usr))
C.admin_ghost()
can_ghost = C.admin_ghost()
if(!can_ghost)
return
var/mob/dead/observer/A = C.mob
A.ManualFollow(AM)
@@ -1894,8 +1901,6 @@
if(!check_rights(R_ADMIN))
return
if(SSticker.HasRoundStarted())
return alert(usr, "The game has already started.", null, null, null, null)
var/dat = {"<B>What mode do you wish to play?</B><HR>"}
for(var/mode in config.modes)
dat += {"<A href='?src=[REF(src)];[HrefToken()];c_mode2=[mode]'>[config.mode_names[mode]]</A><br>"}
+26 -1
View File
@@ -61,6 +61,10 @@
"SELECT /mob WHERE client MAP client WHERE holder MAP holder"
You can also generate a new list on the fly using a selector array. @[] will generate a list of objects based off the selector provided.
"SELECT /mob/living IN (@[/area/crew_quarters/bar MAP contents])[1]"
What if some dumbass admin spawned a bajillion spiders and you need to kill them all?
Oh yeah you'd rather not delete all the spiders in maintenace. Only that one room the spiders were
spawned in.
@@ -879,6 +883,22 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
dummy[result] = assoc
result = dummy
val += result
else if(expression[i] == "@\[")
var/list/search_tree = expression[++i]
var/already_searching = (state == SDQL2_STATE_SEARCHING) //In case we nest, don't want to break out of the searching state until we're all done.
if(!already_searching)
state = SDQL2_STATE_SEARCHING
val = Search(search_tree)
SDQL2_STAGE_SWITCH_CHECK
if(!already_searching)
state = SDQL2_STATE_EXECUTING
else
state = SDQL2_STATE_SEARCHING
else
val = world.SDQL_var(object, expression, i, object, superuser, src)
i = expression.len
@@ -968,6 +988,10 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
return null
start++
long = start < expression.len
else if(expression[start] == "(" && long)
v = query.SDQL_expression(source, expression[start + 1])
start++
long = start < expression.len
else if(D != null && (!long || expression[start + 1] == ".") && (expression[start] in D.vars))
if(D.can_vv_get(expression[start]) || superuser)
v = D.vars[expression[start]]
@@ -1060,7 +1084,8 @@ GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null
"=" = list("", "="),
"<" = list("", "=", ">"),
">" = list("", "="),
"!" = list("", "="))
"!" = list("", "="),
"@" = list("\["))
var/word = ""
var/list/query_list = list()
+53 -48
View File
@@ -19,24 +19,28 @@
//
// from_item : 'world' | expression
//
// call_function : <function name> '(' [arguments] ')'
// arguments : expression [',' arguments]
// call_function : <function name> '(' [expression_list] ')'
//
// object_type : <type path>
//
// assignments : assignment [',' assignments]
// assignment : <variable name> '=' expression
// variable : <variable name> | <variable name> '.' variable | '[' <hex number> ']' | '[' <hex number> ']' '.' variable
// variable : <variable name> | variable '.' variable | variable '[' <list index> ']' | '{' <ref as hex number> '}' | '(' expression ')' | call_function
//
// bool_expression : expression comparitor expression [bool_operator bool_expression]
// expression : ( unary_expression | '(' expression ')' | value ) [binary_operator expression]
// unary_expression : unary_operator ( unary_expression | value | '(' expression ')' )
// expression_list : expression [',' expression_list]
// unary_expression : unary_operator ( unary_expression | value )
//
// comparitor : '=' | '==' | '!=' | '<>' | '<' | '<=' | '>' | '>='
// value : variable | string | number | 'null' | object_type
// value : variable | string | number | 'null' | object_type | array | selectors_array
// unary_operator : '!' | '-' | '~'
// binary_operator : comparitor | '+' | '-' | '/' | '*' | '&' | '|' | '^' | '%'
// bool_operator : 'AND' | '&&' | 'OR' | '||'
//
// array : '[' expression_list ']'
// selectors_array : '@[' object_selectors ']'
//
// string : ''' <some text> ''' | '"' <some text > '"'
// number : <some digits>
//
@@ -336,7 +340,7 @@
return i
//variable: <variable name> | <variable name> '.' variable | '[' <hex number> ']' | '[' <hex number> ']' '.' variable
//variable: <variable name> | variable '.' variable | variable '[' <list index> ']' | '{' <ref as hex number> '}' | '(' expression ')' | call_function
/datum/SDQL_parser/proc/variable(i, list/node)
var/list/L = list(token(i))
node[++node.len] = L
@@ -348,6 +352,16 @@
if(token(i) != "}")
parse_error("Missing } at end of pointer.")
else if(token(i) == "(") // not a proc but an expression
var/list/sub_expression = list()
i = expression(i + 1, sub_expression)
if(token(i) != ")")
parse_error("Missing ) at end of expression.")
L[++L.len] = sub_expression
if(token(i + 1) == ".")
L += "."
i = variable(i + 2, L)
@@ -424,7 +438,7 @@
return i + 1
//array: '[' expression, expression, ... ']'
//array: '[' expression_list ']'
/datum/SDQL_parser/proc/array(var/i, var/list/node)
// Arrays get turned into this: list("[", list(exp_1a = exp_1b, ...), ...), "[" is to mark the next node as an array.
if(copytext(token(i), 1, 2) != "\[")
@@ -468,18 +482,13 @@
temp_expression_list = list()
i = expression(i, temp_expression_list)
// Ok, what the fuck BYOND?
// Not having these lines here causes the parser to die
// on an error saying that list/token() doesn't exist as a proc.
// These lines prevent that.
// I assume the compiler/VM is shitting itself and swapping out some variables internally?
// While throwing in debug logging it disappeared
// And these 3 lines prevent it from happening while being quiet.
// So.. it works.
// Don't touch it.
var/whatthefuck = i
whatthefuck = src.type
whatthefuck = whatthefuck
#if MIN_COMPILER_VERSION > 512
#warn Remove this outdated workaround
#elif DM_BUILD < 1467
// http://www.byond.com/forum/post/2445083
var/dummy = src.type
dummy = dummy
#endif
while(token(i) && token(i) != "]")
@@ -490,6 +499,23 @@
return i + 1
//selectors_array: '@[' object_selectors ']'
/datum/SDQL_parser/proc/selectors_array(var/i, var/list/node)
if(token(i) == "@\[")
node += token(i++)
if(token(i) != "]")
var/list/select = list()
i = object_selectors(i, select)
node[++node.len] = select
if(token(i) != "]")
parse_error("Expected ']' to close selector array, but found '[token(i)]'")
else
parse_error("Selector array expected a selector, but found nothing")
else
parse_error("Expected '@\[' but found '[token(i)]'")
return i + 1
//call_function: <function name> ['(' [arguments] ')']
/datum/SDQL_parser/proc/call_function(i, list/node, list/arguments)
if(length(tokenl(i)))
@@ -520,25 +546,12 @@
return i + 1
//expression: ( unary_expression | '(' expression ')' | value ) [binary_operator expression]
//expression: ( unary_expression | value ) [binary_operator expression]
/datum/SDQL_parser/proc/expression(i, list/node)
if(token(i) in unary_operators)
i = unary_expression(i, node)
else if(token(i) == "(")
var/list/expr = list()
i = expression(i + 1, expr)
if(token(i) != ")")
parse_error("Missing ) at end of expression.")
else
i++
node[++node.len] = expr
else
i = value(i, node)
@@ -558,7 +571,7 @@
return i
//unary_expression: unary_operator ( unary_expression | value | '(' expression ')' )
//unary_expression: unary_operator ( unary_expression | value )
/datum/SDQL_parser/proc/unary_expression(i, list/node)
if(token(i) in unary_operators)
@@ -570,19 +583,6 @@
if(token(i) in unary_operators)
i = unary_expression(i, unary_exp)
else if(token(i) == "(")
var/list/expr = list()
i = expression(i + 1, expr)
if(token(i) != ")")
parse_error("Missing ) at end of expression.")
else
i++
unary_exp[++unary_exp.len] = expr
else
i = value(i, unary_exp)
@@ -607,7 +607,7 @@
return i + 1
//value: variable | string | number | 'null' | object_type
//value: variable | string | number | 'null' | object_type | array | selectors_array
/datum/SDQL_parser/proc/value(i, list/node)
if(token(i) == "null")
node += "null"
@@ -626,8 +626,13 @@
else if(copytext(token(i), 1, 2) == "\[") // Start a list.
i = array(i, node)
else if(copytext(token(i), 1, 3) == "@\[")
i = selectors_array(i, node)
else if(copytext(token(i), 1, 2) == "/")
i = object_type(i, node)
else
i = variable(i, node)
+2 -1
View File
@@ -258,6 +258,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
var/admin_msg = "<span class='adminnotice'><span class='adminhelp'>Ticket [TicketHref("#[id]", ref_src)]</span><b>: [LinkedReplyName(ref_src)] [FullMonty(ref_src)]:</b> <span class='linkify'>[keywords_lookup(msg)]</span></span>"
AddInteraction("<font color='red'>[LinkedReplyName(ref_src)]: [msg]</font>")
log_admin_private("Ticket #[id]: [key_name(initiator)]: [msg]")
//send this msg to all admins
for(var/client/X in GLOB.admins)
@@ -477,7 +478,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
// Used for methods where input via arg doesn't work
/client/proc/get_adminhelp()
var/msg = input(src, "Please describe your problem concisely and an admin will help as soon as they're able.", "Adminhelp contents") as text
var/msg = input(src, "Please describe your problem concisely and an admin will help as soon as they're able.", "Adminhelp contents") as text|null
adminhelp(msg)
/client/verb/adminhelp(msg as text)
+30 -30
View File
@@ -6,7 +6,7 @@
set category = null
set name = "Admin PM Mob"
if(!holder)
to_chat(src, "<font color='red'>Error: Admin-PM-Context: Only administrators may use this command.</font>")
to_chat(src, "<span class='danger'>Error: Admin-PM-Context: Only administrators may use this command.</span>")
return
if( !ismob(M) || !M.client )
return
@@ -18,7 +18,7 @@
set category = "Admin"
set name = "Admin PM"
if(!holder)
to_chat(src, "<font color='red'>Error: Admin-PM-Panel: Only administrators may use this command.</font>")
to_chat(src, "<span class='danger'>Error: Admin-PM-Panel: Only administrators may use this command.</span>")
return
var/list/client/targets[0]
for(var/client/T)
@@ -37,7 +37,7 @@
/client/proc/cmd_ahelp_reply(whom)
if(prefs.muted & MUTE_ADMINHELP)
to_chat(src, "<font color='red'>Error: Admin-PM: You are unable to use admin PM-s (muted).</font>")
to_chat(src, "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>")
return
var/client/C
if(istext(whom))
@@ -48,14 +48,14 @@
C = whom
if(!C)
if(holder)
to_chat(src, "<font color='red'>Error: Admin-PM: Client not found.</font>")
to_chat(src, "<span class='danger'>Error: Admin-PM: Client not found.</span>")
return
var/datum/admin_help/AH = C.current_ticket
if(AH)
message_admins("[key_name_admin(src)] has started replying to [key_name_admin(C, 0, 0)]'s admin help.")
var/msg = input(src,"Message:", "Private message to [key_name(C, 0, 0)]") as message|null
var/msg = input(src,"Message:", "Private message to [C.holder?.fakekey ? "an Administrator" : key_name(C, 0, 0)].") as message|null
if (!msg)
message_admins("[key_name_admin(src)] has cancelled their reply to [key_name_admin(C, 0, 0)]'s admin help.")
return
@@ -65,12 +65,12 @@
//Fetching a message if needed. src is the sender and C is the target client
/client/proc/cmd_admin_pm(whom, msg)
if(prefs.muted & MUTE_ADMINHELP)
to_chat(src, "<font color='red'>Error: Admin-PM: You are unable to use admin PM-s (muted).</font>")
to_chat(src, "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>")
return
if(!holder && !current_ticket) //no ticket? https://www.youtube.com/watch?v=iHSPf6x1Fdo
to_chat(src, "<font color='red'>You can no longer reply to this ticket, please open another one by using the Adminhelp verb if need be.</font>")
to_chat(src, "<font color='blue'>Message: [msg]</font>")
to_chat(src, "<span class='danger'>You can no longer reply to this ticket, please open another one by using the Adminhelp verb if need be.</span>")
to_chat(src, "<span class='notice'>Message: [msg]</span>")
return
var/client/recipient
@@ -95,14 +95,14 @@
if(!msg)
return
if(holder)
to_chat(src, "<font color='red'>Error: Use the admin IRC channel, nerd.</font>")
to_chat(src, "<span class='danger'>Error: Use the admin IRC channel, nerd.</span>")
return
else
if(!recipient)
if(holder)
to_chat(src, "<font color='red'>Error: Admin-PM: Client not found.</font>")
to_chat(src, "<span class='danger'>Error: Admin-PM: Client not found.</span>")
if(msg)
to_chat(src, msg)
return
@@ -112,18 +112,18 @@
//get message text, limit it's length.and clean/escape html
if(!msg)
msg = input(src,"Message:", "Private message to [key_name(recipient, 0, 0)]") as message|null
msg = input(src,"Message:", "Private message to [recipient.holder?.fakekey ? "an Administrator" : key_name(recipient, 0, 0)].") as message|null
msg = trim(msg)
if(!msg)
return
if(prefs.muted & MUTE_ADMINHELP)
to_chat(src, "<font color='red'>Error: Admin-PM: You are unable to use admin PM-s (muted).</font>")
to_chat(src, "<span class='danger'>Error: Admin-PM: You are unable to use admin PM-s (muted).</span>")
return
if(!recipient)
if(holder)
to_chat(src, "<font color='red'>Error: Admin-PM: Client not found.</font>")
to_chat(src, "<span class='danger'>Error: Admin-PM: Client not found.</span>")
else
current_ticket.MessageNoRecipient(msg)
return
@@ -145,15 +145,15 @@
var/keywordparsedmsg = keywords_lookup(msg)
if(irc)
to_chat(src, "<font color='blue'>PM to-<b>Admins</b>: <span class='linkify'>[rawmsg]</span></font>")
to_chat(src, "<span class='notice'>PM to-<b>Admins</b>: <span class='linkify'>[rawmsg]</span></span>")
var/datum/admin_help/AH = admin_ticket_log(src, "<font color='red'>Reply PM from-<b>[key_name(src, TRUE, TRUE)] to <i>IRC</i>: [keywordparsedmsg]</font>")
ircreplyamount--
send2irc("[AH ? "#[AH.id] " : ""]Reply: [ckey]", rawmsg)
else
if(recipient.holder)
if(holder) //both are admins
to_chat(recipient, "<font color='red'>Admin PM from-<b>[key_name(src, recipient, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span></font>")
to_chat(src, "<font color='blue'>Admin PM to-<b>[key_name(recipient, src, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span></font>")
to_chat(recipient, "<span class='danger'>Admin PM from-<b>[key_name(src, recipient, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span></span>")
to_chat(src, "<span class='notice'>Admin PM to-<b>[key_name(recipient, src, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span></span>")
//omg this is dumb, just fill in both their tickets
var/interaction_message = "<font color='purple'>PM from-<b>[key_name(src, recipient, 1)]</b> to-<b>[key_name(recipient, src, 1)]</b>: [keywordparsedmsg]</font>"
@@ -162,10 +162,10 @@
admin_ticket_log(recipient, interaction_message)
else //recipient is an admin but sender is not
var/replymsg = "<font color='red'>Reply PM from-<b>[key_name(src, recipient, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span></font>"
admin_ticket_log(src, replymsg)
to_chat(recipient, replymsg)
to_chat(src, "<font color='blue'>PM to-<b>Admins</b>: <span class='linkify'>[msg]</span></font>")
var/replymsg = "Reply PM from-<b>[key_name(src, recipient, 1)]</b>: <span class='linkify'>[keywordparsedmsg]</span>"
admin_ticket_log(src, "<font color='red'>[replymsg]</font>")
to_chat(recipient, "<span class='danger'>[replymsg]</span>")
to_chat(src, "<span class='notice'>PM to-<b>Admins</b>: <span class='linkify'>[msg]</span></span>")
//play the receiving admin the adminhelp sound (if they have them enabled)
if(recipient.prefs.toggles & SOUND_ADMINHELP)
@@ -177,11 +177,11 @@
new /datum/admin_help(msg, recipient, TRUE)
to_chat(recipient, "<font color='red' size='4'><b>-- Administrator private message --</b></font>")
to_chat(recipient, "<font color='red'>Admin PM from-<b>[key_name(src, recipient, 0)]</b>: <span class='linkify'>[msg]</span></font>")
to_chat(recipient, "<font color='red'><i>Click on the administrator's name to reply.</i></font>")
to_chat(src, "<font color='blue'>Admin PM to-<b>[key_name(recipient, src, 1)]</b>: <span class='linkify'>[msg]</span></font>")
to_chat(recipient, "<span class='adminsay'>Admin PM from-<b>[key_name(src, recipient, 0)]</b>: <span class='linkify'>[msg]</span></span>")
to_chat(recipient, "<span class='adminsay'><i>Click on the administrator's name to reply.</i></span>")
to_chat(src, "<span class='notice'>Admin PM to-<b>[key_name(recipient, src, 1)]</b>: <span class='linkify'>[msg]</span></span>")
admin_ticket_log(recipient, "<font color='blue'>PM From [key_name_admin(src)]: [keywordparsedmsg]</font>")
admin_ticket_log(recipient, "<font color='purple'>PM From [key_name_admin(src)]: [keywordparsedmsg]</font>")
//always play non-admin recipients the adminhelp sound
SEND_SOUND(recipient, sound('sound/effects/adminhelp.ogg'))
@@ -200,20 +200,20 @@
return
else //neither are admins
to_chat(src, "<font color='red'>Error: Admin-PM: Non-admin to non-admin PM communication is forbidden.</font>")
to_chat(src, "<span class='danger'>Error: Admin-PM: Non-admin to non-admin PM communication is forbidden.</span>")
return
if(irc)
log_admin_private("PM: [key_name(src)]->IRC: [rawmsg]")
for(var/client/X in GLOB.admins)
to_chat(X, "<font color='blue'><B>PM: [key_name(src, X, 0)]-&gt;IRC:</B> [keywordparsedmsg]</font>")
to_chat(X, "<span class='notice'><B>PM: [key_name(src, X, 0)]-&gt;IRC:</B> [keywordparsedmsg]</span>")
else
window_flash(recipient, ignorepref = TRUE)
log_admin_private("PM: [key_name(src)]->[key_name(recipient)]: [rawmsg]")
//we don't use message_admins here because the sender/receiver might get it too
for(var/client/X in GLOB.admins)
if(X.key!=key && X.key!=recipient.key) //check client/X is an admin and isn't the sender or recipient
to_chat(X, "<font color='blue'><B>PM: [key_name(src, X, 0)]-&gt;[key_name(recipient, X, 0)]:</B> [keywordparsedmsg]</font>" )
to_chat(X, "<span class='notice'><B>PM: [key_name(src, X, 0)]-&gt;[key_name(recipient, X, 0)]:</B> [keywordparsedmsg]</span>" )
@@ -296,10 +296,10 @@
msg = emoji_parse(msg)
to_chat(C, "<font color='red' size='4'><b>-- Administrator private message --</b></font>")
to_chat(C, "<font color='red'>Admin PM from-<b><a href='?priv_msg=[stealthkey]'>[adminname]</A></b>: [msg]</font>")
to_chat(C, "<font color='red'><i>Click on the administrator's name to reply.</i></font>")
to_chat(C, "<span class='adminsay'>Admin PM from-<b><a href='?priv_msg=[stealthkey]'>[adminname]</A></b>: [msg]</span>")
to_chat(C, "<span class='adminsay'><i>Click on the administrator's name to reply.</i></span>")
admin_ticket_log(C, "<font color='blue'>PM From [irc_tagged]: [msg]</font>")
admin_ticket_log(C, "<font color='purple'>PM From [irc_tagged]: [msg]</font>")
window_flash(C, ignorepref = TRUE)
//always play non-admin recipients the adminhelp sound
+2 -2
View File
@@ -11,12 +11,12 @@
mob.log_talk(msg, LOG_ASAY)
msg = keywords_lookup(msg)
var/custom_asay_color = (CONFIG_GET(flag/allow_admin_asaycolor) && prefs.asaycolor) ? "<font color=[prefs.asaycolor]>" : null
var/custom_asay_color = (CONFIG_GET(flag/allow_admin_asaycolor) && prefs.asaycolor) ? "<font color=[prefs.asaycolor]>" : "<font color='#FF4500'>"
msg = "<span class='adminsay'><span class='prefix'>ADMIN:</span> <EM>[key_name(usr, 1)]</EM> [ADMIN_FLW(mob)]: [custom_asay_color]<span class='message linkify'>[msg]</span></span>[custom_asay_color ? "</font>":null]"
to_chat(GLOB.admins, msg)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Asay") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/get_admin_say()
var/msg = input(src, null, "asay \"text\"") as text
var/msg = input(src, null, "asay \"text\"") as text|null
cmd_admin_say(msg)
+41 -47
View File
@@ -29,66 +29,62 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that
/client/proc/callproc()
set category = "Debug"
set name = "Advanced ProcCall"
set waitfor = 0
set waitfor = FALSE
if(!check_rights(R_DEBUG))
return
var/datum/target = null
var/targetselected = 0
var/targetselected = FALSE
var/returnval = null
switch(alert("Proc owned by something?",,"Yes","No"))
if("Yes")
targetselected = 1
var/list/value = vv_get_value(default_class = VV_ATOM_REFERENCE, classes = list(VV_ATOM_REFERENCE, VV_DATUM_REFERENCE, VV_MOB_REFERENCE, VV_CLIENT))
if (!value["class"] || !value["value"])
return
target = value["value"]
if("No")
target = null
targetselected = 0
if(alert("Proc owned by something?",,"Yes","No") == "Yes")
targetselected = TRUE
var/list/value = vv_get_value(default_class = VV_ATOM_REFERENCE, classes = list(VV_ATOM_REFERENCE, VV_DATUM_REFERENCE, VV_MOB_REFERENCE, VV_CLIENT))
if (!value["class"] || !value["value"])
return
target = value["value"]
var/procname = input("Proc path, eg: /proc/fake_blood","Path:", null) as text|null
if(!procname)
var/procpath = input("Proc path, eg: /proc/fake_blood","Path:", null) as text|null
if(!procpath)
return
//strip away everything but the proc name
var/list/proclist = splittext(procname, "/")
var/list/proclist = splittext(procpath, "/")
if (!length(proclist))
return
procname = proclist[proclist.len]
var/proctype = "proc"
if ("verb" in proclist)
proctype = "verb"
if(targetselected && !hascall(target, procname))
to_chat(usr, "<font color='red'>Error: callproc(): type [target.type] has no [proctype] named [procname].</font>")
return
else
var/procpath = text2path("[proctype]/[procname]")
if (!procpath)
to_chat(usr, "<font color='red'>Error: callproc(): proc [procname] does not exist. (Did you forget the /proc/ part?)</font>")
var/procname = proclist[proclist.len]
var/proctype = ("verb" in proclist) ? "verb" :"proc"
if(targetselected)
if(!hascall(target, procname))
to_chat(usr, "<span class='warning'>Error: callproc(): type [target.type] has no [proctype] named [procpath].</span>")
return
else
procpath = "/[proctype]/[procname]"
if(!text2path(procpath))
to_chat(usr, "<span class='warning'>Error: callproc(): [procpath] does not exist.</span>")
return
var/list/lst = get_callproc_args()
if(!lst)
return
if(targetselected)
if(!target)
to_chat(usr, "<font color='red'>Error: callproc(): owner of proc no longer exists.</font>")
to_chat(usr, "<span class='warning'>Error: callproc(): owner of proc no longer exists.</span>")
return
var/msg = "[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]."
var/msg = "[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no argument"]."
log_admin(msg)
message_admins(msg)
admin_ticket_log(target, msg)
returnval = WrapAdminProcCall(target, procname, lst) // Pass the lst as an argument list to the proc
returnval = WrapAdminProcCall(target, procname, lst)
else
//this currently has no hascall protection. wasn't able to get it working.
log_admin("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].")
message_admins("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].")
returnval = WrapAdminProcCall(GLOBAL_PROC, procname, lst) // Pass the lst as an argument list to the proc
var/msg = "[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no argument"]."
log_admin(msg)
message_admins(msg)
returnval = WrapAdminProcCall(GLOBAL_PROC, procpath, lst) //calling globals needs full qualified name (e.g /proc/foo)
. = get_callproc_returnval(returnval, procname)
if(.)
to_chat(usr, .)
@@ -108,8 +104,8 @@ GLOBAL_LIST_EMPTY(AdminProcCallSpamPrevention)
GLOBAL_PROTECT(AdminProcCallSpamPrevention)
/proc/WrapAdminProcCall(datum/target, procname, list/arguments)
if(target && procname == "Del")
to_chat(usr, "Calling Del() is not allowed")
if(target != GLOBAL_PROC && procname == "Del")
to_chat(usr, "<span class='warning'>Calling Del() is not allowed</span>")
return
if(target != GLOBAL_PROC && !target.CanProcCall(procname))
@@ -156,7 +152,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
/client/proc/callproc_datum(datum/A as null|area|mob|obj|turf)
set category = "Debug"
set name = "Atom ProcCall"
set waitfor = 0
set waitfor = FALSE
if(!check_rights(R_DEBUG))
return
@@ -165,7 +161,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
if(!procname)
return
if(!hascall(A,procname))
to_chat(usr, "<font color='red'>Error: callproc_datum(): type [A.type] has no proc named [procname].</font>")
to_chat(usr, "<span class='warning'>Error: callproc_datum(): type [A.type] has no proc named [procname].</span>")
return
var/list/lst = get_callproc_args()
if(!lst)
@@ -174,8 +170,8 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
if(!A || !IsValidSrc(A))
to_chat(usr, "<span class='warning'>Error: callproc_datum(): owner of proc no longer exists.</span>")
return
log_admin("[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].")
var/msg = "[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]."
log_admin(msg)
message_admins(msg)
admin_ticket_log(A, msg)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Atom ProcCall") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -185,8 +181,6 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
if(.)
to_chat(usr, .)
/client/proc/get_callproc_args()
var/argnum = input("Number of arguments","Number:",0) as num|null
if(isnull(argnum))
@@ -210,7 +204,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
. = ""
if(islist(returnval))
var/list/returnedlist = returnval
. = "<font color='blue'>"
. = "<span class='notice'>"
if(returnedlist.len)
var/assoc_check = returnedlist[1]
if(istext(assoc_check) && (returnedlist[assoc_check] != null))
@@ -224,10 +218,10 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
. += "\n[elem]"
else
. = "[procname] returned an empty list"
. += "</font>"
. += "</span>"
else
. = "<font color='blue'>[procname] returned: [!isnull(returnval) ? returnval : "null"]</font>"
. = "<span class='notice'>[procname] returned: [!isnull(returnval) ? returnval : "null"]</span>"
/client/proc/Cell()
@@ -238,7 +232,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
var/turf/T = get_turf(mob)
if(!isturf(T))
return
show_air_status_to(T, usr)
atmosanalyzer_scan(usr, T, TRUE)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Air Status In Location") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/cmd_admin_robotize(mob/M in GLOB.mob_list)
+2 -19
View File
@@ -1,27 +1,10 @@
/proc/show_air_status_to(turf/target, mob/user)
var/datum/gas_mixture/env = target.return_air()
var/list/env_gases = env.gases
var/burning = FALSE
if(isopenturf(target))
var/turf/open/T = target
if(T.active_hotspot)
burning = TRUE
var/list/lines = list("<span class='adminnotice'>[AREACOORD(target)]: [env.temperature] K ([env.temperature - T0C] C), [env.return_pressure()] kPa[(burning)?(", <font color='red'>burning</font>"):(null)]</span>")
for(var/id in env_gases)
var/gas = env_gases[id]
var/moles = gas[MOLES]
if (moles >= 0.00001)
lines += "[gas[GAS_META][META_GAS_NAME]]: [moles] mol"
to_chat(usr, lines.Join("\n"))
/client/proc/air_status(turf/target)
set category = "Debug"
set name = "Display Air Status"
if(!isturf(target))
return
show_air_status_to(target, usr)
atmosanalyzer_scan(usr, target, TRUE)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Air Status") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/fix_next_move()
@@ -62,7 +45,7 @@
var/output = "<b>Radio Report</b><hr>"
for (var/fq in SSradio.frequencies)
output += "<b>Freq: [fq]</b><br>"
var/list/datum/radio_frequency/fqs = SSradio.frequencies[fq]
var/datum/radio_frequency/fqs = SSradio.frequencies[fq]
if (!fqs)
output += "&nbsp;&nbsp;<b>ERROR</b><br>"
continue
+1 -1
View File
@@ -19,7 +19,6 @@
//- Identify how hard it is to break into the area and where the weak points are
//- Check if the area has too much empty space. If so, make it smaller and replace the rest with maintenance tunnels.
GLOBAL_PROTECT(admin_verbs_debug_mapping)
GLOBAL_LIST_INIT(admin_verbs_debug_mapping, list(
/client/proc/camera_view, //-errorage
/client/proc/sec_camera_report, //-errorage
@@ -53,6 +52,7 @@ GLOBAL_LIST_INIT(admin_verbs_debug_mapping, list(
/client/proc/debug_z_levels,
/client/proc/place_ruin
))
GLOBAL_PROTECT(admin_verbs_debug_mapping)
/obj/effect/debugging/mapfix_marker
name = "map fix marker"
+4 -2
View File
@@ -67,7 +67,7 @@
if(istext(web_sound_input))
var/web_sound_url = ""
var/stop_web_sounds = FALSE
var/pitch
var/list/music_extra_data = list()
if(length(web_sound_input))
web_sound_input = trim(web_sound_input)
@@ -95,6 +95,8 @@
var/webpage_url = title
if (data["webpage_url"])
webpage_url = "<a href=\"[data["webpage_url"]]\">[title]</a>"
music_extra_data["start"] = data["start_time"]
music_extra_data["end"] = data["end_time"]
var/res = alert(usr, "Show the title of and link to this song to the players?\n[title]",, "No", "Yes", "Cancel")
switch(res)
@@ -126,7 +128,7 @@
var/client/C = M.client
if((C.prefs.toggles & SOUND_MIDI) && C.chatOutput && !C.chatOutput.broken && C.chatOutput.loaded)
if(!stop_web_sounds)
C.chatOutput.sendMusic(web_sound_url, pitch)
C.chatOutput.sendMusic(web_sound_url, music_extra_data)
else
C.chatOutput.stopMusic()
+1 -1
View File
@@ -34,7 +34,7 @@
deity = "Nar'Sie"
else if(isliving(usr))
var/mob/living/L = usr
if(L.has_trait(TRAIT_SPIRITUAL))
if(HAS_TRAIT(L, TRAIT_SPIRITUAL))
cross.icon_state = "holylight"
font_color = "blue"
prayer_type = "SPIRITUAL PRAYER"
+8 -7
View File
@@ -298,7 +298,7 @@
if(candidates.len)
ckey = input("Pick the player you want to respawn as a xeno.", "Suitable Candidates") as null|anything in candidates
else
to_chat(usr, "<font color='red'>Error: create_xeno(): no suitable candidates.</font>")
to_chat(usr, "<span class='danger'>Error: create_xeno(): no suitable candidates.</span>")
if(!istext(ckey))
return 0
@@ -1103,11 +1103,10 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
if(!weather_type)
return
var/z_level = input("Z-Level to target? Leave blank to target current Z-Level.", "Z-Level") as num|null
var/turf/T = get_turf(mob)
var/z_level = input("Z-Level to target?", "Z-Level", T?.z) as num|null
if(!isnum(z_level))
if(!src.mob)
return
z_level = src.mob.z
return
SSweather.run_weather(weather_type, z_level)
@@ -1243,7 +1242,7 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggled Hub Visibility", "[GLOB.hub_visibility ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/client/proc/smite(mob/living/carbon/human/target as mob)
/client/proc/smite(mob/living/target as mob)
set name = "Smite"
set category = "Fun"
if(!check_rights(R_ADMIN) || !check_rights(R_FUN))
@@ -1261,7 +1260,9 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
var/turf/T = get_step(get_step(target, NORTH), NORTH)
T.Beam(target, icon_state="lightning[rand(1,12)]", time = 5)
target.adjustFireLoss(75)
target.electrocution_animation(40)
if(ishuman(target))
var/mob/living/carbon/human/H = target
H.electrocution_animation(40)
to_chat(target, "<span class='userdanger'>The gods have punished you for your sins!</span>")
if(ADMIN_PUNISHMENT_BRAINDAMAGE)
target.adjustBrainLoss(199, 199)