diff --git a/code/__defines/_tick.dm b/code/__defines/_tick.dm
index 2c761b86f9..943f74116d 100644
--- a/code/__defines/_tick.dm
+++ b/code/__defines/_tick.dm
@@ -5,5 +5,12 @@
#define TICK_CHECK ( TICK_USAGE > Master.current_ticklimit )
#define CHECK_TICK if TICK_CHECK stoplag()
-
+=======
#define TICK_USAGE world.tick_usage
+
+#define TICK_CHECK ( TICK_USAGE > Master.current_ticklimit )
+#define CHECK_TICK ( TICK_CHECK ? stoplag() : 0 )
+>>>>>>> ae64d73... Upgrades SDQL2 and refactors it to a datum (#5793)
+
+#define TICK_CHECK_HIGH_PRIORITY ( TICK_USAGE > 95 )
+#define CHECK_TICK_HIGH_PRIORITY ( TICK_CHECK_HIGH_PRIORITY? stoplag() : 0 )
diff --git a/code/__defines/flags.dm b/code/__defines/flags.dm
index 5de8214191..27b1afcc1c 100644
--- a/code/__defines/flags.dm
+++ b/code/__defines/flags.dm
@@ -1,6 +1,19 @@
-#define ALL (~0)
+/*
+ These defines are specific to the atom/flags_1 bitmask
+*/
+#define ALL (~0) //For convenience.
#define NONE 0
+//for convenience
+#define ENABLE_BITFIELD(variable, flag) (variable |= (flag))
+#define DISABLE_BITFIELD(variable, flag) (variable &= ~(flag))
+#define CHECK_BITFIELD(variable, flag) (variable & flag)
+
+//check if all bitflags specified are present
+#define CHECK_MULTIPLE_BITFIELDS(flagvar, flags) ((flagvar & (flags)) == flags)
+
+GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768))
+
// datum_flags
#define DF_VAR_EDITED (1<<0)
#define DF_ISPROCESSING (1<<1)
diff --git a/code/_global_vars/mobs.dm b/code/_global_vars/mobs.dm
index f2dd9f5b9a..7bfe2a8dbd 100644
--- a/code/_global_vars/mobs.dm
+++ b/code/_global_vars/mobs.dm
@@ -2,5 +2,7 @@ GLOBAL_LIST_EMPTY(admins) //all clients whom are admins
GLOBAL_PROTECT(admins)
GLOBAL_LIST_EMPTY(deadmins) //all ckeys who have used the de-admin verb.
GLOBAL_LIST_EMPTY(stealthminID)
+<<<<<<< HEAD
GLOBAL_LIST_EMPTY(directory) //all ckeys with associated client
-GLOBAL_LIST_EMPTY(players_by_zlevel)
+GLOBAL_LIST_EMPTY(clients)
+GLOBAL_LIST_EMPTY(players_by_zlevel)
\ No newline at end of file
diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm
index 4a249b050c..6b924ef082 100644
--- a/code/_helpers/game.dm
+++ b/code/_helpers/game.dm
@@ -432,7 +432,7 @@ proc/isInSight(var/atom/A, var/atom/B)
/proc/Show2Group4Delay(obj/O, list/group, delay=0)
if(!isobj(O)) return
- if(!group) group = clients
+ if(!group) group = GLOB.clients
for(var/client/C in group)
C.screen += O
if(delay)
diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm
index 2e776c82d0..057e468bb4 100644
--- a/code/_helpers/global_lists.dm
+++ b/code/_helpers/global_lists.dm
@@ -1,6 +1,4 @@
-var/list/clients = list() //list of all clients
var/list/admins = list() //list of all clients whom are admins
-var/list/directory = list() //list of all ckeys with associated client
//Since it didn't really belong in any other category, I'm putting this here
//This is for procs to replace all the goddamn 'in world's that are chilling around the code
diff --git a/code/_helpers/time.dm b/code/_helpers/time.dm
index d4c918a55a..a8dacb6367 100644
--- a/code/_helpers/time.dm
+++ b/code/_helpers/time.dm
@@ -147,74 +147,31 @@ var/round_start_time = 0
//Takes a value of time in deciseconds.
//Returns a text value of that number in hours, minutes, or seconds.
-/proc/DisplayTimeText(time_value, truncate = FALSE)
- var/second = (time_value)*0.1
- var/second_adjusted = null
- var/second_rounded = FALSE
- var/minute = null
- var/hour = null
- var/day = null
-
+/proc/DisplayTimeText(time_value, round_seconds_to = 0.1)
+ var/second = round(time_value * 0.1, round_seconds_to)
if(!second)
- return "0 seconds"
- if(second >= 60)
- minute = FLOOR(second/60, 1)
- second = round(second - (minute*60), 0.1)
- second_rounded = TRUE
- if(second) //check if we still have seconds remaining to format, or if everything went into minute.
- second_adjusted = round(second) //used to prevent '1 seconds' being shown
- if(day || hour || minute)
- if(second_adjusted == 1 && second >= 1)
- second = " and 1 second"
- else if(second > 1)
- second = " and [second_adjusted] seconds"
- else //shows a fraction if seconds is < 1
- if(second_rounded) //no sense rounding again if it's already done
- second = " and [second] seconds"
- else
- second = " and [round(second, 0.1)] seconds"
- else
- if(second_adjusted == 1 && second >= 1)
- second = "[truncate ? "second" : "1 second"]"
- else if(second > 1)
- second = "[second_adjusted] seconds"
- else
- if(second_rounded)
- second = "[second] seconds"
- else
- second = "[round(second, 0.1)] seconds"
- else
- second = null
-
- if(!minute)
- return "[second]"
- if(minute >= 60)
- hour = FLOOR(minute/60, 1)
- minute = (minute - (hour*60))
- if(minute) //alot simpler from here since you don't have to worry about fractions
- if(minute != 1)
- if((day || hour) && second)
- minute = ", [minute] minutes"
- else if((day || hour) && !second)
- minute = " and [minute] minutes"
- else
- minute = "[minute] minutes"
- else
- if((day || hour) && second)
- minute = ", 1 minute"
- else if((day || hour) && !second)
- minute = " and 1 minute"
- else
- minute = "[truncate ? "minute" : "1 minute"]"
- else
- minute = null
-
- if(!hour)
- return "[minute][second]"
- if(hour >= 24)
- day = FLOOR(hour/24, 1)
- hour = (hour - (day*24))
+ return "right now"
+ if(second < 60)
+ return "[second] second[(second != 1)? "s":""]"
+ var/minute = FLOOR(second / 60, 1)
+ second = MODULUS(second, 60)
+ var/secondT
+ if(second)
+ secondT = " and [second] second[(second != 1)? "s":""]"
+ if(minute < 60)
+ return "[minute] minute[(minute != 1)? "s":""][secondT]"
+ var/hour = FLOOR(minute / 60, 1)
+ minute = MODULUS(minute, 60)
+ var/minuteT
+ if(minute)
+ minuteT = " and [minute] minute[(minute != 1)? "s":""]"
+ if(hour < 24)
+ return "[hour] hour[(hour != 1)? "s":""][minuteT][secondT]"
+ var/day = FLOOR(hour / 24, 1)
+ hour = MODULUS(hour, 24)
+ var/hourT
if(hour)
+<<<<<<< HEAD
if(hour != 1)
if(day && (minute || second))
hour = ", [hour] hours"
@@ -240,3 +197,7 @@ var/round_start_time = 0
day = "[truncate ? "day" : "1 day"]"
return "[day][hour][minute][second]"
+=======
+ hourT = " and [hour] hour[(hour != 1)? "s":""]"
+ return "[day] day[(day != 1)? "s":""][hourT][minuteT][secondT]"
+>>>>>>> ae64d73... Upgrades SDQL2 and refactors it to a datum (#5793)
diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm
index 1836fbf30f..68cc85a364 100644
--- a/code/_helpers/unsorted.dm
+++ b/code/_helpers/unsorted.dm
@@ -1461,3 +1461,5 @@ var/mob/dview/dview_mob = new
/proc/pass()
return
+
+#define NAMEOF(datum, X) (#X || ##datum.##X)
diff --git a/code/controllers/subsystems/vote.dm b/code/controllers/subsystems/vote.dm
index 480e0da813..b3f9f42c51 100644
--- a/code/controllers/subsystems/vote.dm
+++ b/code/controllers/subsystems/vote.dm
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
SUBSYSTEM_DEF(vote)
name = "Vote"
wait = 10
@@ -371,3 +372,378 @@ SUBSYSTEM_DEF(vote)
if(SSvote)
src << browse(SSvote.interface(src), "window=vote;size=500x[300 + SSvote.choices.len * 25]")
+=======
+SUBSYSTEM_DEF(vote)
+ name = "Vote"
+ wait = 10
+ priority = FIRE_PRIORITY_VOTE
+ runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
+ flags = SS_KEEP_TIMING | SS_NO_INIT
+ var/list/round_voters = list()
+
+ //Current vote
+ var/initiator
+ var/started_time
+ var/time_remaining
+ var/duration
+ var/mode
+ var/question
+ var/list/choices = list()
+ var/list/gamemode_names = list()
+ var/list/voted = list()
+ var/list/current_votes = list()
+ var/list/additional_text = list()
+
+/datum/controller/subsystem/vote/fire(resumed)
+ if(mode)
+ time_remaining = round((started_time + duration - world.time)/10)
+ if(mode == VOTE_GAMEMODE && ticker.current_state >= GAME_STATE_SETTING_UP)
+ to_chat(world, "Gamemode vote aborted: Game has already started.")
+ reset()
+ return
+ if(time_remaining <= 0)
+ result()
+ reset()
+
+/datum/controller/subsystem/vote/proc/autotransfer()
+ initiate_vote(VOTE_CREW_TRANSFER, "the server", 1)
+ log_debug("The server has called a crew transfer vote.")
+
+/datum/controller/subsystem/vote/proc/autogamemode()
+ initiate_vote(VOTE_GAMEMODE, "the server", 1)
+ log_debug("The server has called a gamemode vote.")
+
+/datum/controller/subsystem/vote/proc/reset()
+ initiator = null
+ started_time = null
+ duration = null
+ time_remaining = null
+ mode = null
+ question = null
+ choices.Cut()
+ voted.Cut()
+ current_votes.Cut()
+ additional_text.Cut()
+
+/datum/controller/subsystem/vote/proc/get_result() // Get the highest number of votes
+ var/greatest_votes = 0
+ var/total_votes = 0
+
+ for(var/option in choices)
+ var/votes = choices[option]
+ total_votes += votes
+ if(votes > greatest_votes)
+ greatest_votes = votes
+
+ if(!config.vote_no_default && choices.len) // Default-vote for everyone who didn't vote
+ var/non_voters = (GLOB.clients.len - total_votes)
+ if(non_voters > 0)
+ if(mode == VOTE_RESTART)
+ choices["Continue Playing"] += non_voters
+ if(choices["Continue Playing"] >= greatest_votes)
+ greatest_votes = choices["Continue Playing"]
+ else if(mode == VOTE_GAMEMODE)
+ if(master_mode in choices)
+ choices[master_mode] += non_voters
+ if(choices[master_mode] >= greatest_votes)
+ greatest_votes = choices[master_mode]
+ else if(mode == VOTE_CREW_TRANSFER)
+ var/factor = 0.5
+ switch(world.time / (10 * 60)) // minutes
+ if(0 to 60)
+ factor = 0.5
+ if(61 to 120)
+ factor = 0.8
+ if(121 to 240)
+ factor = 1
+ if(241 to 300)
+ factor = 1.2
+ else
+ factor = 1.4
+ choices["Initiate Crew Transfer"] = round(choices["Initiate Crew Transfer"] * factor)
+ world << "Crew Transfer Factor: [factor]"
+ greatest_votes = max(choices["Initiate Crew Transfer"], choices["Continue The Round"])
+
+ . = list() // Get all options with that many votes and return them in a list
+ if(greatest_votes)
+ for(var/option in choices)
+ if(choices[option] == greatest_votes)
+ . += option
+
+/datum/controller/subsystem/vote/proc/announce_result()
+ var/list/winners = get_result()
+ var/text
+ if(winners.len > 0)
+ if(winners.len > 1)
+ if(mode != VOTE_GAMEMODE || ticker.hide_mode == 0) // Here we are making sure we don't announce potential game modes
+ text = "Vote Tied Between:\n"
+ for(var/option in winners)
+ text += "\t[option]\n"
+ . = pick(winners)
+
+ for(var/key in current_votes)
+ if(choices[current_votes[key]] == .)
+ round_voters += key // Keep track of who voted for the winning round.
+ if(mode != VOTE_GAMEMODE || . == "Extended" || ticker.hide_mode == 0) // Announce Extended gamemode, but not other gamemodes
+ text += "Vote Result: [mode == VOTE_GAMEMODE ? gamemode_names[.] : .]"
+ else
+ text += "The vote has ended."
+
+ else
+ text += "Vote Result: Inconclusive - No Votes!"
+ if(mode == VOTE_ADD_ANTAGONIST)
+ antag_add_failed = 1
+ log_vote(text)
+ to_chat(world, "[text]")
+
+/datum/controller/subsystem/vote/proc/result()
+ . = announce_result()
+ var/restart = 0
+ if(.)
+ switch(mode)
+ if(VOTE_RESTART)
+ if(. == "Restart Round")
+ restart = 1
+ if(VOTE_GAMEMODE)
+ if(master_mode != .)
+ world.save_mode(.)
+ if(ticker && ticker.mode)
+ restart = 1
+ else
+ master_mode = .
+ if(VOTE_CREW_TRANSFER)
+ if(. == "Initiate Crew Transfer")
+ init_shift_change(null, 1)
+ if(VOTE_ADD_ANTAGONIST)
+ if(isnull(.) || . == "None")
+ antag_add_failed = 1
+ else
+ additional_antag_types |= antag_names_to_ids[.]
+
+ if(mode == VOTE_GAMEMODE) //fire this even if the vote fails.
+ if(!round_progressing)
+ round_progressing = 1
+ world << "The round will start soon."
+
+ if(restart)
+ world << "World restarting due to vote..."
+ feedback_set_details("end_error", "restart vote")
+ if(blackbox)
+ blackbox.save_all_data_to_sql()
+ sleep(50)
+ log_game("Rebooting due to restart vote")
+ world.Reboot()
+
+/datum/controller/subsystem/vote/proc/submit_vote(ckey, newVote)
+ if(mode)
+ if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder)
+ return
+ if(current_votes[ckey])
+ choices[choices[current_votes[ckey]]]--
+ if(newVote && newVote >= 1 && newVote <= choices.len)
+ choices[choices[newVote]]++
+ current_votes[ckey] = newVote
+ else
+ current_votes[ckey] = null
+
+/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, automatic = FALSE, time = config.vote_period)
+ if(!mode)
+ if(started_time != null && !(check_rights(R_ADMIN) || automatic))
+ var/next_allowed_time = (started_time + config.vote_delay)
+ if(next_allowed_time > world.time)
+ return 0
+
+ reset()
+
+ switch(vote_type)
+ if(VOTE_RESTART)
+ choices.Add("Restart Round", "Continue Playing")
+ if(VOTE_GAMEMODE)
+ if(ticker.current_state >= GAME_STATE_SETTING_UP)
+ return 0
+ choices.Add(config.votable_modes)
+ for(var/F in choices)
+ var/datum/game_mode/M = gamemode_cache[F]
+ if(!M)
+ continue
+ gamemode_names[M.config_tag] = capitalize(M.name) //It's ugly to put this here but it works
+ additional_text.Add("
[M.required_players] | ")
+ gamemode_names["secret"] = "Secret"
+ if(VOTE_CREW_TRANSFER)
+ if(!check_rights(R_ADMIN|R_MOD, 0)) // The gods care not for the affairs of the mortals
+ if(get_security_level() == "red" || get_security_level() == "delta")
+ initiator_key << "The current alert status is too high to call for a crew transfer!"
+ return 0
+ if(ticker.current_state <= GAME_STATE_SETTING_UP)
+ initiator_key << "The crew transfer button has been disabled!"
+ return 0
+ question = "End the shift?"
+ choices.Add("Initiate Crew Transfer", "Continue The Round")
+ if(VOTE_ADD_ANTAGONIST)
+ if(!config.allow_extra_antags || ticker.current_state >= GAME_STATE_SETTING_UP)
+ return 0
+ for(var/antag_type in all_antag_types)
+ var/datum/antagonist/antag = all_antag_types[antag_type]
+ if(!(antag.id in additional_antag_types) && antag.is_votable())
+ choices.Add(antag.role_text)
+ choices.Add("None")
+ if(VOTE_CUSTOM)
+ question = sanitizeSafe(input(usr, "What is the vote for?") as text|null)
+ if(!question)
+ return 0
+ for(var/i = 1 to 10)
+ var/option = capitalize(sanitize(input(usr, "Please enter an option or hit cancel to finish") as text|null))
+ if(!option || mode || !usr.client)
+ break
+ choices.Add(option)
+ else
+ return 0
+
+ mode = vote_type
+ initiator = initiator_key
+ started_time = world.time
+ duration = time
+ var/text = "[capitalize(mode)] vote started by [initiator]."
+ if(mode == VOTE_CUSTOM)
+ text += "\n[question]"
+
+ log_vote(text)
+
+ world << "[text]\nType vote or click here to place your votes.\nYou have [config.vote_period / 10] seconds to vote."
+ if(vote_type == VOTE_CREW_TRANSFER || vote_type == VOTE_GAMEMODE || vote_type == VOTE_CUSTOM)
+ world << sound('sound/ambience/alarm4.ogg', repeat = 0, wait = 0, volume = 50, channel = 3)
+
+ if(mode == VOTE_GAMEMODE && round_progressing)
+ round_progressing = 0
+ world << "Round start has been delayed."
+
+ time_remaining = round(config.vote_period / 10)
+ return 1
+ return 0
+
+/datum/controller/subsystem/vote/proc/interface(var/client/C)
+ if(!istype(C))
+ return
+ var/admin = FALSE
+ if(C.holder)
+ if(C.holder.rights & R_ADMIN)
+ admin = TRUE
+
+ . = "Voting Panel"
+ if(mode)
+ if(question)
+ . += "Vote: '[question]'
"
+ else
+ . += "Vote: [capitalize(mode)]
"
+ . += "Time Left: [time_remaining] s
"
+ . += "| Choices | Votes | "
+ if(mode == VOTE_GAMEMODE)
+ .+= "Minimum Players |
"
+
+ for(var/i = 1 to choices.len)
+ var/votes = choices[choices[i]]
+ if(!votes)
+ votes = 0
+ . += ""
+ var/thisVote = (current_votes[C.ckey] == i)
+ if(mode == VOTE_GAMEMODE)
+ . += "| [thisVote ? "" : ""][gamemode_names[choices[i]]][thisVote ? "" : ""] | [votes] | "
+ else
+ . += "[thisVote ? "" : ""][choices[i]][thisVote ? "" : ""] | [votes] | "
+ if (additional_text.len >= i)
+ . += additional_text[i]
+ . += "
"
+
+ . += "| Unvote |
"
+
+ . += "
"
+ if(admin)
+ . += "(Cancel Vote) "
+ else
+ . += "Start a vote:
"
+
+ . += "Close"
+
+/datum/controller/subsystem/vote/Topic(href, href_list[])
+ if(!usr || !usr.client)
+ return
+ switch(href_list["vote"])
+ if("close")
+ usr << browse(null, "window=vote")
+ return
+
+ if("cancel")
+ if(usr.client.holder)
+ reset()
+ if("toggle_restart")
+ if(usr.client.holder)
+ config.allow_vote_restart = !config.allow_vote_restart
+ if("toggle_gamemode")
+ if(usr.client.holder)
+ config.allow_vote_mode = !config.allow_vote_mode
+
+ if(VOTE_RESTART)
+ if(config.allow_vote_restart || usr.client.holder)
+ initiate_vote(VOTE_RESTART, usr.key)
+ if(VOTE_GAMEMODE)
+ if(config.allow_vote_mode || usr.client.holder)
+ initiate_vote(VOTE_GAMEMODE, usr.key)
+ if(VOTE_CREW_TRANSFER)
+ if(config.allow_vote_restart || usr.client.holder)
+ initiate_vote(VOTE_CREW_TRANSFER, usr.key)
+ if(VOTE_ADD_ANTAGONIST)
+ if(config.allow_extra_antags || usr.client.holder)
+ initiate_vote(VOTE_ADD_ANTAGONIST, usr.key)
+ if(VOTE_CUSTOM)
+ if(usr.client.holder)
+ initiate_vote(VOTE_CUSTOM, usr.key)
+
+ if("unvote")
+ submit_vote(usr.ckey, null)
+
+ else
+ var/t = round(text2num(href_list["vote"]))
+ if(t) // It starts from 1, so there's no problem
+ submit_vote(usr.ckey, t)
+ usr.client.vote()
+
+/client/verb/vote()
+ set category = "OOC"
+ set name = "Vote"
+
+ if(SSvote)
+ src << browse(SSvote.interface(src), "window=vote;size=500x[300 + SSvote.choices.len * 25]")
+>>>>>>> ae64d73... Upgrades SDQL2 and refactors it to a datum (#5793)
diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm
new file mode 100644
index 0000000000..1938b0d913
--- /dev/null
+++ b/code/datums/datumvars.dm
@@ -0,0 +1,21 @@
+#define VV_MSG_MARKED "
Marked Object"
+#define VV_MSG_EDITED "
Var Edited"
+#define VV_MSG_DELETED "
Deleted"
+
+/datum/proc/CanProcCall(procname)
+ return TRUE
+
+/datum/proc/can_vv_get(var_name)
+ //Pending refactor of vv_hidden
+ //return TRUE
+
+ return !(var_name in VV_hidden())
+
+/datum/proc/vv_edit_var(var_name, var_value) //called whenever a var is edited
+ //Pending refactor of vv_static
+ //if(var_name == NAMEOF(src, vars))
+ // return FALSE
+ if(var_name in VV_static())
+ return FALSE
+ vars[var_name] = var_value
+ return TRUE
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 8ab82e5e84..23a80006ce 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -464,7 +464,7 @@ proc/display_roundstart_logout_report()
if(L.ckey)
var/found = 0
- for(var/client/C in clients)
+ for(var/client/C in GLOB.clients)
if(C.ckey == L.ckey)
found = 1
break
diff --git a/code/game/verbs/advanced_who.dm b/code/game/verbs/advanced_who.dm
index a452446e46..bbde65fd07 100644
--- a/code/game/verbs/advanced_who.dm
+++ b/code/game/verbs/advanced_who.dm
@@ -8,7 +8,7 @@
var/list/Lines = list()
if(holder && (R_ADMIN & holder.rights || R_MOD & holder.rights))
- for(var/client/C in clients)
+ for(var/client/C in GLOB.clients)
var/entry = "\t[C.key]"
if(C.holder && C.holder.fakekey)
entry += " (as [C.holder.fakekey])"
@@ -52,7 +52,7 @@
Lines += entry
else
- for(var/client/C in clients)
+ for(var/client/C in GLOB.clients)
var/entry = "\t"
if(C.holder && C.holder.fakekey)
entry += "[C.holder.fakekey]"
diff --git a/code/game/verbs/ignore.dm b/code/game/verbs/ignore.dm
index c154628087..ea1ad6a0b7 100644
--- a/code/game/verbs/ignore.dm
+++ b/code/game/verbs/ignore.dm
@@ -42,7 +42,7 @@
/client/proc/is_key_ignored(var/key_to_check)
key_to_check = ckey(key_to_check)
if(key_to_check in prefs.ignored_players)
- if(directory[key_to_check] in admins) // This is here so this is only evaluated if someone is actually being blocked.
+ if(GLOB.directory[key_to_check] in admins) // This is here so this is only evaluated if someone is actually being blocked.
return 0
return 1
return 0
\ No newline at end of file
diff --git a/code/game/verbs/ooc.dm b/code/game/verbs/ooc.dm
index 8ef0fbb012..0c6fa0fed7 100644
--- a/code/game/verbs/ooc.dm
+++ b/code/game/verbs/ooc.dm
@@ -57,7 +57,7 @@
if(holder.rights & R_ADMIN)
ooc_style = "admin"
- for(var/client/target in clients)
+ for(var/client/target in GLOB.clients)
if(target.is_preference_enabled(/datum/client_preference/show_ooc))
if(target.is_key_ignored(key)) // If we're ignored by this person, then do nothing.
continue
@@ -124,7 +124,7 @@
var/list/in_range = get_mobs_and_objs_in_view_fast(T,world.view,0)
var/list/m_viewers = in_range["mobs"]
- var/list/receivers = list() // Clients, not mobs.
+ var/list/receivers = list() //Clients, not mobs.
var/list/r_receivers = list()
var/display_name = key
diff --git a/code/game/verbs/who.dm b/code/game/verbs/who.dm
index 06402a90a4..c030e25649 100644
--- a/code/game/verbs/who.dm
+++ b/code/game/verbs/who.dm
@@ -8,7 +8,7 @@
var/list/Lines = list()
if(holder && (R_ADMIN & holder.rights || R_MOD & holder.rights))
- for(var/client/C in clients)
+ for(var/client/C in GLOB.clients)
var/entry = "\t[C.key]"
if(C.holder && C.holder.fakekey)
entry += " (as [C.holder.fakekey])"
@@ -51,7 +51,7 @@
entry += " (?)"
Lines += entry
else
- for(var/client/C in clients)
+ for(var/client/C in GLOB.clients)
if(C.holder && C.holder.fakekey)
Lines += C.holder.fakekey
else
diff --git a/code/modules/admin/DB ban/functions.dm b/code/modules/admin/DB ban/functions.dm
index e4d2298ace..d9e1efc972 100644
--- a/code/modules/admin/DB ban/functions.dm
+++ b/code/modules/admin/DB ban/functions.dm
@@ -64,7 +64,7 @@ datum/admins/proc/DB_ban_record(var/bantype, var/mob/banned_mob, var/duration =
a_ip = src.owner:address
var/who
- for(var/client/C in clients)
+ for(var/client/C in GLOB.clients)
if(!who)
who = "[C]"
else
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 7e2ddb0467..8daa899692 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -286,7 +286,7 @@ proc/admin_notice(var/message, var/rights)
dat += ""
var/p_age = "unknown"
- for(var/client/C in clients)
+ for(var/client/C in GLOB.clients)
if(C.ckey == key)
p_age = C.player_age
break
@@ -730,28 +730,28 @@ var/datum/announcement/minor/admin_min_announcer = new
//Split on pipe or \n
decomposed = splittext(message,regex("\\||$","m"))
decomposed += "0" //Tack on a final 0 sleep to make 3-per-message evenly
-
+
//Time to find how they screwed up.
//Wasn't the right length
if((decomposed.len) % 3) //+1 to accomidate the lack of a wait time for the last message
to_chat(usr,"You passed [decomposed.len] segments (senders+messages+pauses). You must pass a multiple of 3, minus 1 (no pause after the last message). That means a sender and message on every other line (starting on the first), separated by a pipe character (|), and a number every other line that is a pause in seconds.")
return
-
+
//Too long a conversation
if((decomposed.len / 3) > 20)
to_chat(usr,"This conversation is too long! 20 messages maximum, please.")
return
-
+
//Missed some sleeps, or sanitized to nothing.
for(var/i = 1; i < decomposed.len; i++)
-
+
//Sanitize sender
var/clean_sender = sanitize(decomposed[i])
if(!clean_sender)
to_chat(usr,"One part of your conversation was not able to be sanitized. It was the sender of the [(i+2)/3]\th message.")
return
decomposed[i] = clean_sender
-
+
//Sanitize message
var/clean_message = sanitize(decomposed[++i])
if(!clean_message)
diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm
index 772c1886a8..52b9ddcedd 100644
--- a/code/modules/admin/admin_ranks.dm
+++ b/code/modules/admin/admin_ranks.dm
@@ -100,7 +100,7 @@ var/list/admin_ranks = list() //list of all ranks with associated rights
var/datum/admins/D = new /datum/admins(rank, rights, ckey)
//find the client for a ckey if they are connected and associate them with the new admin datum
- D.associate(directory[ckey])
+ D.associate(GLOB.directory[ckey])
else
//The current admin system uses SQL
@@ -125,7 +125,7 @@ var/list/admin_ranks = list() //list of all ranks with associated rights
var/datum/admins/D = new /datum/admins(rank, rights, ckey)
//find the client for a ckey if they are connected and associate them with the new admin datum
- D.associate(directory[ckey])
+ D.associate(GLOB.directory[ckey])
if(!admin_datums)
error("The database query in load_admins() resulted in no admins being added to the list. Reverting to legacy system.")
log_misc("The database query in load_admins() resulted in no admins being added to the list. Reverting to legacy system.")
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 2d66bd653f..1c3eb225fc 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -215,8 +215,11 @@ var/list/admin_verbs_debug = list(
/client/proc/enable_debug_verbs,
/client/proc/callproc,
/client/proc/callproc_target,
+<<<<<<< HEAD
/client/proc/debug_process,
/client/proc/SDQL_query,
+=======
+>>>>>>> ae64d73... Upgrades SDQL2 and refactors it to a datum (#5793)
/client/proc/SDQL2_query,
/client/proc/Jump,
/client/proc/debug_rogueminer,
@@ -645,7 +648,7 @@ var/list/admin_verbs_event_manager = list(
return
var/datum/preferences/D
- var/client/C = directory[warned_ckey]
+ var/client/C = GLOB.directory[warned_ckey]
if(C) D = C.prefs
else D = preferences_datums[warned_ckey]
diff --git a/code/modules/admin/callproc/callproc.dm b/code/modules/admin/callproc/callproc.dm
index 9b3da2b92f..df7937d2c5 100644
--- a/code/modules/admin/callproc/callproc.dm
+++ b/code/modules/admin/callproc/callproc.dm
@@ -20,7 +20,7 @@
if("Area or Turf")
target = input("Select target:", "Target", get_turf(usr)) as null|area|turf in world
if("Client")
- target = input("Select target:", "Target", usr.client) as null|anything in clients
+ target = input("Select target:", "Target", usr.client) as null|anything in GLOB.clients
else
return
if(!target)
@@ -110,7 +110,7 @@
if(isnull(current)) return
if("client")
- current = input("Select client for [arguments.len+1]\th argument") as null|anything in clients
+ current = input("Select client for [arguments.len+1]\th argument") as null|anything in GLOB.clients
if(isnull(current)) return
if("mob's area")
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 6fcb620163..0fdccdc534 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -175,7 +175,7 @@
else
D = new /datum/admins(new_rank, rights, adm_ckey)
- var/client/C = directory[adm_ckey] //find the client with the specified ckey (if they are logged in)
+ var/client/C = GLOB.directory[adm_ckey] //find the client with the specified ckey (if they are logged in)
D.associate(C) //link up with the client and add verbs
message_admins("[key_name_admin(usr)] edited the admin rank of [adm_ckey] to [new_rank]")
diff --git a/code/modules/admin/verbs/SDQL.dm b/code/modules/admin/verbs/SDQL.dm
deleted file mode 100644
index 969e5cd47c..0000000000
--- a/code/modules/admin/verbs/SDQL.dm
+++ /dev/null
@@ -1,497 +0,0 @@
-
-//Structured Datum Query Language. Basically SQL meets BYOND objects.
-
-//Note: For use in BS12, need text_starts_with proc, and to modify the action on select to use BS12's object edit command(s).
-
-/client/proc/SDQL_query(query_text as message)
- set category = "Admin"
- if(!check_rights(R_DEBUG)) //Shouldn't happen... but just to be safe.
- message_admins("ERROR: Non-admin [usr.key] attempted to execute a SDQL query!")
- log_admin("Non-admin [usr.key] attempted to execute a SDQL query!")
-
- var/list/query_list = SDQL_tokenize(query_text)
-
- if(query_list.len < 2)
- if(query_list.len > 0)
- usr << "SDQL: Too few discrete tokens in query \"[query_text]\". Please check your syntax and try again."
- return
-
- if(!(lowertext(query_list[1]) in list("select", "delete", "update")))
- usr << "SDQL: Unknown query type: \"[query_list[1]]\" in query \"[query_text]\". Please check your syntax and try again."
- return
-
- var/list/types = list()
-
- var/i
- for(i = 2; i <= query_list.len; i += 2)
- types += query_list[i]
-
- if(i + 1 >= query_list.len || query_list[i + 1] != ",")
- break
-
- i++
-
- var/list/from = list()
-
- if(i <= query_list.len)
- if(lowertext(query_list[i]) in list("from", "in"))
- for(i++; i <= query_list.len; i += 2)
- from += query_list[i]
-
- if(i + 1 >= query_list.len || query_list[i + 1] != ",")
- break
-
- i++
-
- if(from.len < 1)
- from += "world"
-
- var/list/set_vars = list()
-
- if(lowertext(query_list[1]) == "update")
- if(i <= query_list.len && lowertext(query_list[i]) == "set")
- for(i++; i <= query_list.len; i++)
- if(i + 2 <= query_list.len && query_list[i + 1] == "=")
- set_vars += query_list[i]
- set_vars[query_list[i]] = query_list[i + 2]
-
- else
- usr << "SDQL: Invalid set parameter in query \"[query_text]\". Please check your syntax and try again."
- return
-
- i += 3
-
- if(i >= query_list.len || query_list[i] != ",")
- break
-
- if(set_vars.len < 1)
- usr << "SDQL: Invalid or missing set in query \"[query_text]\". Please check your syntax and try again."
- return
-
- var/list/where = list()
-
- if(i <= query_list.len && lowertext(query_list[i]) == "where")
- where = query_list.Copy(i + 1)
-
- var/list/from_objs = list()
- if("world" in from)
- from_objs += world
- else
- for(var/f in from)
- if(copytext(f, 1, 2) == "'" || copytext(f, 1, 2) == "\"")
- from_objs += locate(copytext(f, 2, length(f)))
- else if(copytext(f, 1, 2) != "/")
- from_objs += locate(f)
- else
- var/f2 = text2path(f)
- if(text_starts_with(f, "/mob"))
- for(var/mob/m in mob_list)
- if(istype(m, f2))
- from_objs += m
-
- else if(text_starts_with(f, "/turf/space"))
- for(var/turf/space/m in turfs)
- if(istype(m, f2))
- from_objs += m
-
- else if(text_starts_with(f, "/turf/simulated"))
- for(var/turf/simulated/m in turfs)
- if(istype(m, f2))
- from_objs += m
-
- else if(text_starts_with(f, "/turf/unsimulated"))
- for(var/turf/unsimulated/m in turfs)
- if(istype(m, f2))
- from_objs += m
-
- else if(text_starts_with(f, "/turf"))
- for(var/turf/m in turfs)
- if(istype(m, f2))
- from_objs += m
-
- else if(text_starts_with(f, "/area"))
- for(var/area/m in all_areas)
- if(istype(m, f2))
- from_objs += m
-
- else if(text_starts_with(f, "/obj/item"))
- for(var/obj/item/m in world)
- if(istype(m, f2))
- from_objs += m
-
- else if(text_starts_with(f, "/obj/machinery"))
- for(var/obj/machinery/m in machines)
- if(istype(m, f2))
- from_objs += m
-
- else if(text_starts_with(f, "/obj"))
- for(var/obj/m in world)
- if(istype(m, f2))
- from_objs += m
-
- else if(text_starts_with(f, "/atom"))
- for(var/atom/m in world)
- if(istype(m, f2))
- from_objs += m
-/*
- else
- for(var/datum/m in nope)
- if(istype(m, f2))
- from_objs += m
-*/
-
- var/list/objs = list()
-
- for(var/from_obj in from_objs)
- if("*" in types)
- objs += from_obj:contents
- else
- for(var/f in types)
- if(copytext(f, 1, 2) == "'" || copytext(f, 1, 2) == "\"")
- objs += locate(copytext(f, 2, length(f))) in from_obj
- else if(copytext(f, 1, 2) != "/")
- objs += locate(f) in from_obj
- else
- var/f2 = text2path(f)
- if(text_starts_with(f, "/mob"))
- for(var/mob/m in from_obj)
- if(istype(m, f2))
- objs += m
-
- else if(text_starts_with(f, "/turf/space"))
- for(var/turf/space/m in from_obj)
- if(istype(m, f2))
- objs += m
-
- else if(text_starts_with(f, "/turf/simulated"))
- for(var/turf/simulated/m in from_obj)
- if(istype(m, f2))
- objs += m
-
- else if(text_starts_with(f, "/turf/unsimulated"))
- for(var/turf/unsimulated/m in from_obj)
- if(istype(m, f2))
- objs += m
-
- else if(text_starts_with(f, "/turf"))
- for(var/turf/m in from_obj)
- if(istype(m, f2))
- objs += m
-
- else if(text_starts_with(f, "/area"))
- for(var/area/m in from_obj)
- if(istype(m, f2))
- objs += m
-
- else if(text_starts_with(f, "/obj/item"))
- for(var/obj/item/m in from_obj)
- if(istype(m, f2))
- objs += m
-
- else if(text_starts_with(f, "/obj/machinery"))
- for(var/obj/machinery/m in from_obj)
- if(istype(m, f2))
- objs += m
-
- else if(text_starts_with(f, "/obj"))
- for(var/obj/m in from_obj)
- if(istype(m, f2))
- objs += m
-
- else if(text_starts_with(f, "/atom"))
- for(var/atom/m in from_obj)
- if(istype(m, f2))
- objs += m
-
- else
- for(var/datum/m in from_obj)
- if(istype(m, f2))
- objs += m
-
-
- for(var/datum/t in objs)
- var/currently_false = 0
- for(i = 1, i - 1 < where.len, i++)
- var/v = where[i++]
- var/compare_op = where[i++]
- if(!(compare_op in list("==", "=", "<>", "<", ">", "<=", ">=", "!=")))
- usr << "SDQL: Unknown comparison operator [compare_op] in where clause following [v] in query \"[query_text]\". Please check your syntax and try again."
- return
-
- var/j
- for(j = i, j <= where.len, j++)
- if(lowertext(where[j]) in list("and", "or", ";"))
- break
-
- if(!currently_false)
- var/value = SDQL_text2value(t, v)
- var/result = SDQL_evaluate(t, where.Copy(i, j))
-
- switch(compare_op)
- if("=", "==")
- currently_false = !(value == result)
-
- if("!=", "<>")
- currently_false = !(value != result)
-
- if("<")
- currently_false = !(value < result)
-
- if(">")
- currently_false = !(value > result)
-
- if("<=")
- currently_false = !(value <= result)
-
- if(">=")
- currently_false = !(value >= result)
-
-
- if(j > where.len || lowertext(where[j]) == ";")
- break
- else if(lowertext(where[j]) == "or")
- if(currently_false)
- currently_false = 0
- else
- break
-
- i = j
-
- if(currently_false)
- objs -= t
-
-
-
- usr << "SQDL Query: [query_text]"
- message_admins("[usr] executed SDQL query: \"[query_text]\".")
-/*
- for(var/t in types)
- usr << "Type: [t]"
-
- for(var/t in from)
- usr << "From: [t]"
-
- for(var/t in set_vars)
- usr << "Set: [t] = [set_vars[t]]"
-
- if(where.len)
- var/where_str = ""
- for(var/t in where)
- where_str += "[t] "
-
- usr << "Where: [where_str]"
-
- usr << "From objects:"
- for(var/datum/t in from_objs)
- usr << t
-
- usr << "Objects:"
- for(var/datum/t in objs)
- usr << t
-*/
- switch(lowertext(query_list[1]))
- if("delete")
- for(var/datum/t in objs)
- qdel(t)
-
- if("update")
- for(var/datum/t in objs)
- objs[t] = list()
- for(var/v in set_vars)
- if(v in t.vars)
- objs[t][v] = SDQL_text2value(t, set_vars[v])
-
- for(var/datum/t in objs)
- for(var/v in objs[t])
- t.vars[v] = objs[t][v]
-
- if("select")
- var/text = ""
- for(var/datum/t in objs)
- if(istype(t, /atom))
- var/atom/a = t
-
- if(a.x)
- text += "\ref[t]: [t] at ([a.x], [a.y], [a.z])
"
-
- else if(a.loc && a.loc.x)
- text += "\ref[t]: [t] in [a.loc] at ([a.loc.x], [a.loc.y], [a.loc.z])
"
-
- else
- text += "\ref[t]: [t]
"
-
- else
- text += "\ref[t]: [t]
"
-
- //text += "[t]
"
- usr << browse(text, "window=sdql_result")
-
-
-/client/Topic(href,href_list[],hsrc)
- if(href_list["SDQL_select"])
- debug_variables(locate(href_list["SDQL_select"]))
-
- ..()
-
-
-/proc/SDQL_evaluate(datum/object, list/equation)
- if(equation.len == 0)
- return null
-
- else if(equation.len == 1)
- return SDQL_text2value(object, equation[1])
-
- else if(equation[1] == "!")
- return !SDQL_evaluate(object, equation.Copy(2))
-
- else if(equation[1] == "-")
- return -SDQL_evaluate(object, equation.Copy(2))
-
-
- else
- usr << "SDQL: Sorry, equations not yet supported :("
- return null
-
-
-/proc/SDQL_text2value(datum/object, text)
- if(text2num(text) != null)
- return text2num(text)
- else if(text == "null")
- return null
- else if(copytext(text, 1, 2) == "'" || copytext(text, 1, 2) == "\"" )
- return copytext(text, 2, length(text))
- else if(copytext(text, 1, 2) == "/")
- return text2path(text)
- else
- if(findtext(text, "."))
- var/split = findtext(text, ".")
- var/v = copytext(text, 1, split)
-
- if((v in object.vars) && istype(object.vars[v], /datum))
- return SDQL_text2value(object.vars[v], copytext(text, split + 1))
- else
- return null
-
- else
- if(text in object.vars)
- return object.vars[text]
- else
- return null
-
-
-/proc/text_starts_with(text, start)
- if(copytext(text, 1, length(start) + 1) == start)
- return 1
- else
- return 0
-
-
-
-
-
-/proc/SDQL_tokenize(query_text)
-
- var/list/whitespace = list(" ", "\n", "\t")
- var/list/single = list("(", ")", ",", "+", "-")
- var/list/multi = list(
- "=" = list("", "="),
- "<" = list("", "=", ">"),
- ">" = list("", "="),
- "!" = list("", "="))
-
- var/word = ""
- var/list/query_list = list()
- var/len = length(query_text)
-
- for(var/i = 1, i <= len, i++)
- var/char = copytext(query_text, i, i + 1)
-
- if(char in whitespace)
- if(word != "")
- query_list += word
- word = ""
-
- else if(char in single)
- if(word != "")
- query_list += word
- word = ""
-
- query_list += char
-
- else if(char in multi)
- if(word != "")
- query_list += word
- word = ""
-
- var/char2 = copytext(query_text, i + 1, i + 2)
-
- if(char2 in multi[char])
- query_list += "[char][char2]"
- i++
-
- else
- query_list += char
-
- else if(char == "'")
- if(word != "")
- usr << "SDQL: You have an error in your SDQL syntax, unexpected ' in query: \"[query_text]\" following \"[word]\". Please check your syntax, and try again."
- return null
-
- word = "'"
-
- for(i++, i <= len, i++)
- char = copytext(query_text, i, i + 1)
-
- if(char == "'")
- if(copytext(query_text, i + 1, i + 2) == "'")
- word += "'"
- i++
-
- else
- break
-
- else
- word += char
-
- if(i > len)
- usr << "SDQL: You have an error in your SDQL syntax, unmatched ' in query: \"[query_text]\". Please check your syntax, and try again."
- return null
-
- query_list += "[word]'"
- word = ""
-
- else if(char == "\"")
- if(word != "")
- usr << "SDQL: You have an error in your SDQL syntax, unexpected \" in query: \"[query_text]\" following \"[word]\". Please check your syntax, and try again."
- return null
-
- word = "\""
-
- for(i++, i <= len, i++)
- char = copytext(query_text, i, i + 1)
-
- if(char == "\"")
- if(copytext(query_text, i + 1, i + 2) == "'")
- word += "\""
- i++
-
- else
- break
-
- else
- word += char
-
- if(i > len)
- usr << "SDQL: You have an error in your SDQL syntax, unmatched \" in query: \"[query_text]\". Please check your syntax, and try again."
- return null
-
- query_list += "[word]\""
- word = ""
-
- else
- word += char
-
- if(word != "")
- query_list += word
-
- return query_list
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2.dm b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
new file mode 100644
index 0000000000..571d709f6e
--- /dev/null
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2.dm
@@ -0,0 +1,1127 @@
+#define SDQL_qdel_datum(d) qdel(d)
+
+//SDQL2 datumized, /tg/station special!
+
+/*
+Welcome admins, badmins and coders alike, to Structured Datum Query Language.
+SDQL allows you to powerfully run code on batches of objects (or single objects, it's still unmatched
+even there.)
+When I say "powerfully" I mean it you're in for a ride.
+
+Ok so say you want to get a list of every mob. How does one do this?
+"SELECT /mob"
+This will open a list of every object in world that is a /mob.
+And you can VV them if you need.
+
+What if you want to get every mob on a *specific z-level*?
+"SELECT /mob WHERE z == 4"
+
+What if you want to select every mob on even numbered z-levels?
+"SELECT /mob WHERE z % 2 == 0"
+
+Can you see where this is going? You can select objects with an arbitrary expression.
+These expressions can also do variable access and proc calls (yes, both on-object and globals!)
+Keep reading!
+
+Ok. What if you want to get every machine in the SSmachine process list? Looping through world is kinda
+slow.
+
+"SELECT * IN SSmachines.machinery"
+
+Here "*" as type functions as a wildcard.
+We know everything in the global SSmachines.machinery list is a machine.
+
+You can specify "IN " to return a list to operate on.
+This can be any list that you can wizard together from global variables and global proc calls.
+Every variable/proc name in the "IN" block is global.
+It can also be a single object, in which case the object is wrapped in a list for you.
+So yeah SDQL is unironically better than VV for complex single-object operations.
+
+You can of course combine these.
+"SELECT * IN SSmachines.machinery WHERE z == 4"
+"SELECT * IN SSmachines.machinery WHERE stat & 2" // (2 is NOPOWER, can't use defines from SDQL. Sorry!)
+"SELECT * IN SSmachines.machinery WHERE stat & 2 && z == 4"
+
+The possibilities are endless (just don't crash the server, ok?).
+
+Oh it gets better.
+
+You can use "MAP " to run some code per object and use the result. For example:
+
+"SELECT /obj/machinery/power/smes MAP [charge / capacity * 100, RCon_tag, src]"
+
+This will give you a list of all the APCs, their charge AND RCon tag. Useful eh?
+
+[] being a list here. Yeah you can write out lists directly without > lol lists in VV. Color matrix
+shenanigans inbound.
+
+After the "MAP" segment is executed, the rest of the query executes as if it's THAT object you just made
+(here the list).
+Yeah, by the way, you can chain these MAP / WHERE things FOREVER!
+
+"SELECT /mob WHERE client MAP client WHERE holder MAP holder"
+
+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.
+
+"DELETE /mob/living/carbon/superior_animal/giant_spider WHERE loc.loc == marked"
+
+Here I used VV to mark the area they were in, and since loc.loc = area, voila.
+Only the spiders in a specific area are gone.
+
+Or you know if you want to catch spiders that crawled into lockers too (how even?)
+
+"DELETE /mob/living/carbon/superior_animal/giant_spider WHERE global.get_area(src) == marked"
+
+What else can you do?
+
+Well suppose you'd rather gib those spiders instead of simply flat deleting them...
+
+"CALL gib() ON /mob/living/carbon/superior_animal/giant_spider WHERE global.get_area(src) == marked"
+
+Or you can have some fun..
+
+"CALL forceMove(marked) ON /mob/living/carbon/superior_animal"
+
+You can also run multiple queries sequentially:
+
+"CALL forceMove(marked) ON /mob/living/carbon/superior_animal; CALL gib() ON
+/mob/living/carbon/superior_animal"
+
+And finally, you can directly modify variables on objects.
+
+"UPDATE /mob WHERE client SET client.color = [0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]"
+
+Don't crash the server, OK?
+
+A quick recommendation: before you run something like a DELETE or another query.. Run it through SELECT
+first.
+You'd rather not gib every player on accident.
+Or crash the server.
+
+By the way, queries are slow and take a while. Be patient.
+They don't hang the entire server though.
+
+With great power comes great responsability.
+
+Here's a slightly more formal quick reference.
+
+The 4 queries you can do are:
+
+"SELECT "
+"CALL ON "
+"UPDATE SET var=,var2="
+"DELETE "
+
+"" in this context is " [IN ] [chain of MAP/WHERE modifiers]"
+
+"IN" (or "FROM", that works too but it's kinda weird to read),
+is the list of objects to work on. This defaults to world if not provided.
+But doing something like "IN living_mob_list" is quite handy and can optimize your query.
+All names inside the IN block are global scope, so you can do living_mob_list (a global var) easily.
+You can also run it on a single object. Because SDQL is that convenient even for single operations.
+
+ filters out objects of, well, that type easily. "*" is a wildcard and just takes everything in
+the source list.
+
+And then there's the MAP/WHERE chain.
+These operate on each individual object being ran through the query.
+They're both expressions like IN, but unlike it the expression is scoped *on the object*.
+So if you do "WHERE z == 4", this does "src.z", effectively.
+If you want to access global variables, you can do `global.living_mob_list`.
+Same goes for procs.
+
+MAP "changes" the object into the result of the expression.
+WHERE "drops" the object if the expression is falsey (0, null or "")
+
+What can you do inside expressions?
+
+* Proc calls
+* Variable reads
+* Literals (numbers, strings, type paths, etc...)
+* \ref referencing: {0x30000cc} grabs the object with \ref [0x30000cc]
+* Lists: [a, b, c] or [a: b, c: d]
+* Math and stuff.
+* A few special variables: src (the object currently scoped on), usr (your mob),
+marked (your marked datum), global(global scope)
+
+TG ADDITIONS START:
+Add USING keyword to the front of the query to use options system
+The defaults aren't necessarily implemented, as there is no need to.
+Available options: (D) means default
+PROCCALL = (D)ASYNC, BLOCKING
+SELECT = FORCE_NULLS, (D)SKIP_NULLS
+PRIORITY = HIGH, (D) NORMAL
+AUTOGC = (D) AUTOGC, KEEP_ALIVE
+
+Example: USING PROCCALL = BLOCKING, SELECT = FORCE_NULLS, PRIORITY = HIGH SELECT /mob FROM world WHERE z == 1
+
+*/
+
+
+#define SDQL2_STATE_ERROR 0
+#define SDQL2_STATE_IDLE 1
+#define SDQL2_STATE_PRESEARCH 2
+#define SDQL2_STATE_SEARCHING 3
+#define SDQL2_STATE_EXECUTING 4
+#define SDQL2_STATE_SWITCHING 5
+#define SDQL2_STATE_HALTING 6
+
+#define SDQL2_VALID_OPTION_TYPES list("proccall", "select", "priority", "autogc")
+#define SDQL2_VALID_OPTION_VALUES list("async", "blocking", "force_nulls", "skip_nulls", "high", "normal", "keep_alive")
+
+#define SDQL2_OPTION_SELECT_OUTPUT_SKIP_NULLS (1<<0)
+#define SDQL2_OPTION_BLOCKING_CALLS (1<<1)
+#define SDQL2_OPTION_HIGH_PRIORITY (1<<2) //High priority SDQL query, allow using almost all of the tick.
+#define SDQL2_OPTION_DO_NOT_AUTOGC (1<<3)
+
+#define SDQL2_OPTIONS_DEFAULT (SDQL2_OPTION_SELECT_OUTPUT_SKIP_NULLS)
+
+#define SDQL2_IS_RUNNING (state == SDQL2_STATE_EXECUTING || state == SDQL2_STATE_SEARCHING || state == SDQL2_STATE_SWITCHING || state == SDQL2_STATE_PRESEARCH)
+#define SDQL2_HALT_CHECK if(!SDQL2_IS_RUNNING) {state = SDQL2_STATE_HALTING; return FALSE;};
+
+#define SDQL2_TICK_CHECK ((options & SDQL2_OPTION_HIGH_PRIORITY)? CHECK_TICK_HIGH_PRIORITY : CHECK_TICK)
+
+#define SDQL2_STAGE_SWITCH_CHECK if(state != SDQL2_STATE_SWITCHING){\
+ if(state == SDQL2_STATE_HALTING){\
+ state = SDQL2_STATE_IDLE;\
+ return FALSE}\
+ state = SDQL2_STATE_ERROR;\
+ CRASH("SDQL2 fatal error");};
+
+/client/proc/SDQL2_query(query_text as message)
+ set category = "Debug"
+ if(!check_rights(R_DEBUG)) //Shouldn't happen... but just to be safe.
+ message_admins("ERROR: Non-admin [key_name(usr)] attempted to execute a SDQL query!")
+ log_admin("Non-admin [key_name(usr)] attempted to execute a SDQL query!")
+ return FALSE
+ var/list/results = world.SDQL2_query(query_text, key_name_admin(usr), "[key_name(usr)]")
+ if(length(results) == 3)
+ for(var/I in 1 to 3)
+ to_chat(usr, results[I])
+
+/world/proc/SDQL2_query(query_text, log_entry1, log_entry2)
+ var/query_log = "executed SDQL query(s): \"[query_text]\"."
+ message_admins("[log_entry1] [query_log]")
+ query_log = "[log_entry2] [query_log]"
+ log_game(query_log)
+
+ var/start_time_total = REALTIMEOFDAY
+
+ if(!length(query_text))
+ return
+ var/list/query_list = SDQL2_tokenize(query_text)
+ if(!length(query_list))
+ return
+ var/list/querys = SDQL_parse(query_list)
+ if(!length(querys))
+ return
+ var/list/datum/SDQL2_query/running = list()
+ for(var/list/query_tree in querys)
+ var/datum/SDQL2_query/query = new /datum/SDQL2_query(query_tree)
+ if(QDELETED(query))
+ continue
+ if(usr)
+ query.show_next_to_key = usr.ckey
+ running += query
+ var/msg = "Starting query #[query.id] - [query.get_query_text()]."
+ if(usr)
+ to_chat(usr, "[msg]")
+ log_admin(msg)
+ query.ARun()
+ var/finished = FALSE
+ var/objs_all = 0
+ var/objs_eligible = 0
+ var/selectors_used = FALSE
+ var/list/combined_refs = list()
+ do
+ CHECK_TICK
+ finished = TRUE
+ for(var/i in running)
+ var/datum/SDQL2_query/query = i
+ if(QDELETED(query))
+ running -= query
+ continue
+ else if(query.state != SDQL2_STATE_IDLE)
+ finished = FALSE
+ else if(query.state == SDQL2_STATE_ERROR)
+ if(usr)
+ to_chat(usr, "SDQL query [query.get_query_text()] errored. It will NOT be automatically garbage collected. Please remove manually.")
+ running -= query
+ else
+ if(query.finished)
+ objs_all += islist(query.obj_count_all)? length(query.obj_count_all) : query.obj_count_all
+ objs_eligible += islist(query.obj_count_eligible)? length(query.obj_count_eligible) : query.obj_count_eligible
+ selectors_used |= query.where_switched
+ combined_refs |= query.select_refs
+ running -= query
+ //if(!CHECK_BITFIELD(query.options, SDQL2_OPTION_DO_NOT_AUTOGC))
+ //QDEL_IN(query, 50) Maybe when vorestation finally ports timers..
+ else
+ if(usr)
+ to_chat(usr, "SDQL query [query.get_query_text()] was halted. It will NOT be automatically garbage collected. Please remove manually.")
+ running -= query
+ while(!finished)
+
+ var/end_time_total = REALTIMEOFDAY - start_time_total
+ return list("SDQL query combined results: [query_text]",\
+ "SDQL query completed: [objs_all] objects selected by path, and [selectors_used ? objs_eligible : objs_all] objects executed on after WHERE filtering/MAPping if applicable.",\
+ "SDQL combined querys took [DisplayTimeText(end_time_total)] to complete.") + combined_refs
+
+GLOBAL_LIST_INIT(sdql2_queries, GLOB.sdql2_queries || list())
+GLOBAL_DATUM_INIT(sdql2_vv_statobj, /obj/effect/statclick/SDQL2_VV_all, new(null, "VIEW VARIABLES (all)", null))
+
+/datum/SDQL2_query
+ var/list/query_tree
+ var/state = SDQL2_STATE_IDLE
+ var/options = SDQL2_OPTIONS_DEFAULT
+ var/superuser = FALSE //Run things like proccalls without using admin protections
+ var/allow_admin_interact = TRUE //Allow admins to do things to this excluding varedit these two vars
+ var/static/id_assign = 1
+ var/id = 0
+
+ var/qdel_on_finish = FALSE
+
+ //Last run
+ //General
+ var/finished = FALSE
+ var/start_time
+ var/end_time
+ var/where_switched = FALSE
+ var/show_next_to_key
+ //Select query only
+ var/list/select_refs
+ var/list/select_text
+ //Runtime tracked
+ //These three are weird. For best performance, they are only a number when they're not being changed by the SDQL searching/execution code. They only become numbers when they finish changing.
+ var/list/obj_count_all
+ var/list/obj_count_eligible
+ var/list/obj_count_finished
+
+ //Statclick
+ var/obj/effect/statclick/SDQL2_delete/delete_click
+ var/obj/effect/statclick/SDQL2_action/action_click
+
+/datum/SDQL2_query/New(list/tree, SU = FALSE, admin_interact = TRUE, _options = SDQL2_OPTIONS_DEFAULT, finished_qdel = FALSE)
+ LAZYADD(GLOB.sdql2_queries, src)
+ superuser = SU
+ allow_admin_interact = admin_interact
+ query_tree = tree
+ options = _options
+ id = id_assign++
+ qdel_on_finish = finished_qdel
+
+/datum/SDQL2_query/Destroy()
+ state = SDQL2_STATE_HALTING
+ query_tree = null
+ obj_count_all = null
+ obj_count_eligible = null
+ obj_count_finished = null
+ select_text = null
+ select_refs = null
+ GLOB.sdql2_queries -= src
+ return ..()
+
+/datum/SDQL2_query/proc/get_query_text()
+ var/list/out = list()
+ recursive_list_print(out, query_tree)
+ return out.Join()
+
+/proc/recursive_list_print(list/output = list(), list/input, datum/callback/datum_handler, datum/callback/atom_handler)
+ output += "\[ "
+ for(var/i in 1 to input.len)
+ var/final = i == input.len
+ var/key = input[i]
+
+ //print the key
+ if(islist(key))
+ recursive_list_print(output, key, datum_handler, atom_handler)
+ else if(is_proper_datum(key) && (datum_handler || (istype(key, /atom) && atom_handler)))
+ if(istype(key, /atom) && atom_handler)
+ output += atom_handler.Invoke(key)
+ else
+ output += datum_handler.Invoke(key)
+ else
+ output += "[key]"
+
+ //print the value
+ var/is_value = (!isnum(key) && !isnull(input[key]))
+ if(is_value)
+ var/value = input[key]
+ if(islist(value))
+ recursive_list_print(output, value, datum_handler, atom_handler)
+ else if(is_proper_datum(value) && (datum_handler || (istype(value, /atom) && atom_handler)))
+ if(istype(value, /atom) && atom_handler)
+ output += atom_handler.Invoke(value)
+ else
+ output += datum_handler.Invoke(value)
+ else
+ output += " = [value]"
+
+ if(!final)
+ output += " , "
+
+ output += " \]"
+
+/datum/SDQL2_query/proc/text_state()
+ switch(state)
+ if(SDQL2_STATE_ERROR)
+ return "###ERROR"
+ if(SDQL2_STATE_IDLE)
+ return "####IDLE"
+ if(SDQL2_STATE_PRESEARCH)
+ return "PRESEARCH"
+ if(SDQL2_STATE_SEARCHING)
+ return "SEARCHING"
+ if(SDQL2_STATE_EXECUTING)
+ return "EXECUTING"
+ if(SDQL2_STATE_SWITCHING)
+ return "SWITCHING"
+ if(SDQL2_STATE_HALTING)
+ return "##HALTING"
+
+/datum/SDQL2_query/proc/generate_stat()
+ if(!allow_admin_interact)
+ return
+ if(!delete_click)
+ delete_click = new(null, "INITIALIZING", src)
+ if(!action_click)
+ action_click = new(null, "INITIALIZNG", src)
+ stat("[id] ", delete_click.update("DELETE QUERY | STATE : [text_state()] | ALL/ELIG/FIN \
+ [islist(obj_count_all)? length(obj_count_all) : (isnull(obj_count_all)? "0" : obj_count_all)]/\
+ [islist(obj_count_eligible)? length(obj_count_eligible) : (isnull(obj_count_eligible)? "0" : obj_count_eligible)]/\
+ [islist(obj_count_finished)? length(obj_count_finished) : (isnull(obj_count_finished)? "0" : obj_count_finished)] - [get_query_text()]"))
+ stat(" ", action_click.update("[SDQL2_IS_RUNNING? "HALT" : "RUN"]"))
+
+/datum/SDQL2_query/proc/delete_click()
+ admin_del(usr)
+
+/datum/SDQL2_query/proc/action_click()
+ if(SDQL2_IS_RUNNING)
+ admin_halt(usr)
+ else
+ admin_run(usr)
+
+/datum/SDQL2_query/proc/admin_halt(user = usr)
+ if(!SDQL2_IS_RUNNING)
+ return
+ var/msg = "[key_name(user)] has halted query #[id]"
+ message_admins(msg)
+ log_admin(msg)
+ state = SDQL2_STATE_HALTING
+
+/datum/SDQL2_query/proc/admin_run(mob/user = usr)
+ if(SDQL2_IS_RUNNING)
+ return
+ var/msg = "[key_name(user)] has (re)started query #[id]"
+ message_admins(msg)
+ log_admin(msg)
+ show_next_to_key = user.ckey
+ ARun()
+
+/datum/SDQL2_query/proc/admin_del(user = usr)
+ var/msg = "[key_name(user)] has stopped + deleted query #[id]"
+ message_admins(msg)
+ log_admin(msg)
+ qdel(src)
+
+/datum/SDQL2_query/proc/set_option(name, value)
+ switch(name)
+ if("select")
+ switch(value)
+ if("force_nulls")
+ DISABLE_BITFIELD(options, SDQL2_OPTION_SELECT_OUTPUT_SKIP_NULLS)
+ if("proccall")
+ switch(value)
+ if("blocking")
+ ENABLE_BITFIELD(options, SDQL2_OPTION_BLOCKING_CALLS)
+ if("priority")
+ switch(value)
+ if("high")
+ ENABLE_BITFIELD(options, SDQL2_OPTION_HIGH_PRIORITY)
+ if("autogc")
+ switch(value)
+ if("keep_alive")
+ ENABLE_BITFIELD(options, SDQL2_OPTION_DO_NOT_AUTOGC)
+
+/datum/SDQL2_query/proc/ARun()
+ INVOKE_ASYNC(src, .proc/Run)
+
+/datum/SDQL2_query/proc/Run()
+ if(SDQL2_IS_RUNNING)
+ return FALSE
+ if(query_tree["options"])
+ for(var/name in query_tree["options"])
+ var/value = query_tree["options"][name]
+ set_option(name, value)
+ select_refs = list()
+ select_text = null
+ obj_count_all = 0
+ obj_count_eligible = 0
+ obj_count_finished = 0
+ start_time = REALTIMEOFDAY
+
+ state = SDQL2_STATE_PRESEARCH
+ var/list/search_tree = PreSearch()
+ SDQL2_STAGE_SWITCH_CHECK
+
+ state = SDQL2_STATE_SEARCHING
+ var/list/found = Search(search_tree)
+ SDQL2_STAGE_SWITCH_CHECK
+
+ state = SDQL2_STATE_EXECUTING
+ Execute(found)
+ SDQL2_STAGE_SWITCH_CHECK
+
+ end_time = REALTIMEOFDAY
+ state = SDQL2_STATE_IDLE
+ finished = TRUE
+ . = TRUE
+ if(show_next_to_key)
+ var/client/C = GLOB.directory[show_next_to_key]
+ if(C)
+ var/mob/showmob = C.mob
+ to_chat(showmob, "SDQL query results: [get_query_text()]
\
+ SDQL query completed: [islist(obj_count_all)? length(obj_count_all) : obj_count_all] objects selected by path, and \
+ [where_switched? "[islist(obj_count_eligible)? length(obj_count_eligible) : obj_count_eligible] objects executed on after WHERE keyword selection." : ""]
\
+ SDQL query took [DisplayTimeText(end_time - start_time)] to complete.")
+ if(length(select_text))
+ var/text = islist(select_text)? select_text.Join() : select_text
+ var/static/result_offset = 0
+ showmob << browse(text, "window=SDQL-result-[result_offset++]")
+ show_next_to_key = null
+ if(qdel_on_finish)
+ qdel(src)
+
+/datum/SDQL2_query/proc/PreSearch()
+ SDQL2_HALT_CHECK
+ switch(query_tree[1])
+ if("explain")
+ SDQL_testout(query_tree["explain"])
+ state = SDQL2_STATE_HALTING
+ return
+ if("call")
+ . = query_tree["on"]
+ if("select", "delete", "update")
+ . = query_tree[query_tree[1]]
+ state = SDQL2_STATE_SWITCHING
+
+/datum/SDQL2_query/proc/Search(list/tree)
+ SDQL2_HALT_CHECK
+ var/type = tree[1]
+ var/list/from = tree[2]
+ var/list/objs = SDQL_from_objs(from)
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+ objs = SDQL_get_all(type, objs)
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+
+ // 1 and 2 are type and FROM.
+ var/i = 3
+ while (i <= tree.len)
+ var/key = tree[i++]
+ var/list/expression = tree[i++]
+ switch (key)
+ if ("map")
+ for(var/j = 1 to objs.len)
+ var/x = objs[j]
+ objs[j] = SDQL_expression(x, expression)
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+
+ if ("where")
+ where_switched = TRUE
+ var/list/out = list()
+ obj_count_eligible = out
+ for(var/x in objs)
+ if(SDQL_expression(x, expression))
+ out += x
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+ objs = out
+ if(islist(obj_count_eligible))
+ obj_count_eligible = objs.len
+ else
+ obj_count_eligible = obj_count_all
+ . = objs
+ state = SDQL2_STATE_SWITCHING
+
+/datum/SDQL2_query/proc/SDQL_from_objs(list/tree)
+ if("world" in tree)
+ return world
+ return SDQL_expression(world, tree)
+
+/datum/SDQL2_query/proc/SDQL_get_all(type, location)
+ var/list/out = list()
+ obj_count_all = out
+
+// If only a single object got returned, wrap it into a list so the for loops run on it.
+ if(!islist(location) && location != world)
+ location = list(location)
+
+ if(type == "*")
+ for(var/i in location)
+ var/datum/d = i
+ if(d.can_vv_get() || superuser)
+ out += d
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+ return out
+ if(istext(type))
+ type = text2path(type)
+ var/typecache = typecacheof(type)
+
+ if(ispath(type, /mob))
+ for(var/mob/d in location)
+ if(typecache[d.type] && (d.can_vv_get() || superuser))
+ out += d
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+
+ else if(ispath(type, /turf))
+ for(var/turf/d in location)
+ if(typecache[d.type] && (d.can_vv_get() || superuser))
+ out += d
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+
+ else if(ispath(type, /obj))
+ for(var/obj/d in location)
+ if(typecache[d.type] && (d.can_vv_get() || superuser))
+ out += d
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+
+ else if(ispath(type, /area))
+ for(var/area/d in location)
+ if(typecache[d.type] && (d.can_vv_get() || superuser))
+ out += d
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+
+ else if(ispath(type, /atom))
+ for(var/atom/d in location)
+ if(typecache[d.type] && (d.can_vv_get() || superuser))
+ out += d
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+
+ else if(ispath(type, /datum))
+ if(location == world) //snowflake for byond shortcut
+ for(var/datum/d) //stupid byond trick to have it not return atoms to make this less laggy
+ if(typecache[d.type] && (d.can_vv_get() || superuser))
+ out += d
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+ else
+ for(var/datum/d in location)
+ if(typecache[d.type] && (d.can_vv_get() || superuser))
+ out += d
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+ obj_count_all = out.len
+ return out
+
+/datum/SDQL2_query/proc/Execute(list/found)
+ SDQL2_HALT_CHECK
+ select_refs = list()
+ select_text = list()
+ switch(query_tree[1])
+ if("call")
+ for(var/i in found)
+ if(!is_proper_datum(i))
+ continue
+ world.SDQL_var(i, query_tree["call"][1], null, i, superuser, src)
+ obj_count_finished++
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+
+ if("delete")
+ for(var/datum/d in found)
+ SDQL_qdel_datum(d)
+ obj_count_finished++
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+
+ if("select")
+ var/list/text_list = list()
+ var/print_nulls = !(options & SDQL2_OPTION_SELECT_OUTPUT_SKIP_NULLS)
+ obj_count_finished = select_refs
+ for(var/i in found)
+ SDQL_print(i, text_list, print_nulls)
+ select_refs["\ref[i]"] = TRUE
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+ select_text = text_list
+
+ if("update")
+ if("set" in query_tree)
+ var/list/set_list = query_tree["set"]
+ for(var/d in found)
+ if(!is_proper_datum(d))
+ continue
+ SDQL_internal_vv(d, set_list)
+ obj_count_finished++
+ SDQL2_TICK_CHECK
+ SDQL2_HALT_CHECK
+ if(islist(obj_count_finished))
+ obj_count_finished = obj_count_finished.len
+ state = SDQL2_STATE_SWITCHING
+
+/datum/SDQL2_query/proc/SDQL_print(object, list/text_list, print_nulls = TRUE)
+ if(is_proper_datum(object))
+ text_list += "\ref[object] : [object]"
+ if(istype(object, /atom))
+ var/atom/A = object
+ var/turf/T = A.loc
+ var/area/a
+ if(istype(T))
+ text_list += " at [T] [ADMIN_COORDJMP(T)]"
+ a = T.loc
+ else
+ var/turf/final = get_turf(T) //Recursive, hopefully?
+ if(istype(final))
+ text_list += " at [final] [ADMIN_COORDJMP(final)]"
+ a = final.loc
+ else
+ text_list += " at nonexistant location"
+ if(a)
+ text_list += " in area [a]"
+ if(T.loc != a)
+ text_list += " inside [T]"
+ text_list += "
"
+ else if(islist(object))
+ var/list/L = object
+ var/first = TRUE
+ text_list += "\["
+ for (var/x in L)
+ if (!first)
+ text_list += ", "
+ first = FALSE
+ SDQL_print(x, text_list)
+ if (!isnull(x) && !isnum(x) && L[x] != null)
+ text_list += " -> "
+ SDQL_print(L[L[x]])
+ text_list += "]
"
+ else
+ if(isnull(object))
+ if(print_nulls)
+ text_list += "NULL
"
+ else
+ text_list += "[object]
"
+
+/datum/SDQL2_query/CanProcCall()
+ if(!allow_admin_interact)
+ return FALSE
+ return ..()
+
+/datum/SDQL2_query/vv_edit_var(var_name, var_value)
+ if(!allow_admin_interact)
+ return FALSE
+ if(var_name == NAMEOF(src, superuser) || var_name == NAMEOF(src, allow_admin_interact) || var_name == NAMEOF(src, query_tree))
+ return FALSE
+ return ..()
+
+/datum/SDQL2_query/proc/SDQL_internal_vv(d, list/set_list)
+ for(var/list/sets in set_list)
+ var/datum/temp = d
+ var/i = 0
+ for(var/v in sets)
+ if(++i == sets.len)
+ if(superuser)
+ if(temp.vars.Find(v))
+ temp.vars[v] = SDQL_expression(d, set_list[sets])
+ else
+ temp.vv_edit_var(v, SDQL_expression(d, set_list[sets]))
+ break
+ if(temp.vars.Find(v) && (istype(temp.vars[v], /datum) || istype(temp.vars[v], /client)))
+ temp = temp.vars[v]
+ else
+ break
+
+/datum/SDQL2_query/proc/SDQL_function_blocking(datum/object, procname, list/arguments, source)
+ var/list/new_args = list()
+ for(var/arg in arguments)
+ new_args[++new_args.len] = SDQL_expression(source, arg)
+ if(object == GLOB) // Global proc.
+ procname = "/proc/[procname]"
+ return call(procname)(arglist(new_args))
+ return call(object, procname)(arglist(new_args))
+
+/datum/SDQL2_query/proc/SDQL_function_async(datum/object, procname, list/arguments, source)
+ set waitfor = FALSE
+ return SDQL_function_blocking(object, procname, arguments, source)
+
+/datum/SDQL2_query/proc/SDQL_expression(datum/object, list/expression, start = 1)
+ var/result = 0
+ var/val
+
+ for(var/i = start, i <= expression.len, i++)
+ var/op = ""
+
+ if(i > start)
+ op = expression[i]
+ i++
+
+ var/list/ret = SDQL_value(object, expression, i)
+ val = ret["val"]
+ i = ret["i"]
+
+ if(op != "")
+ switch(op)
+ if("+")
+ result = (result + val)
+ if("-")
+ result = (result - val)
+ if("*")
+ result = (result * val)
+ if("/")
+ result = (result / val)
+ if("&")
+ result = (result & val)
+ if("|")
+ result = (result | val)
+ if("^")
+ result = (result ^ val)
+ if("%")
+ result = (result % val)
+ if("=", "==")
+ result = (result == val)
+ if("!=", "<>")
+ result = (result != val)
+ if("<")
+ result = (result < val)
+ if("<=")
+ result = (result <= val)
+ if(">")
+ result = (result > val)
+ if(">=")
+ result = (result >= val)
+ if("and", "&&")
+ result = (result && val)
+ if("or", "||")
+ result = (result || val)
+ else
+ to_chat(usr, "SDQL2: Unknown op [op]")
+ result = null
+ else
+ result = val
+
+ return result
+
+/datum/SDQL2_query/proc/SDQL_value(datum/object, list/expression, start = 1)
+ var/i = start
+ var/val = null
+
+ if(i > expression.len)
+ return list("val" = null, "i" = i)
+
+ if(istype(expression[i], /list))
+ val = SDQL_expression(object, expression[i])
+
+ else if(expression[i] == "TRUE")
+ val = TRUE
+
+ else if(expression[i] == "FALSE")
+ val = FALSE
+
+ else if(expression[i] == "!")
+ var/list/ret = SDQL_value(object, expression, i + 1)
+ val = !ret["val"]
+ i = ret["i"]
+
+ else if(expression[i] == "~")
+ var/list/ret = SDQL_value(object, expression, i + 1)
+ val = ~ret["val"]
+ i = ret["i"]
+
+ else if(expression[i] == "-")
+ var/list/ret = SDQL_value(object, expression, i + 1)
+ val = -ret["val"]
+ i = ret["i"]
+
+ else if(expression[i] == "null")
+ val = null
+
+ else if(isnum(expression[i]))
+ val = expression[i]
+
+ else if(ispath(expression[i]))
+ val = expression[i]
+
+ else if(copytext(expression[i], 1, 2) in list("'", "\""))
+ val = copytext(expression[i], 2, length(expression[i]))
+
+ else if(expression[i] == "\[")
+ var/list/expressions_list = expression[++i]
+ val = list()
+ for(var/list/expression_list in expressions_list)
+ var/result = SDQL_expression(object, expression_list)
+ var/assoc
+ if(expressions_list[expression_list] != null)
+ assoc = SDQL_expression(object, expressions_list[expression_list])
+ if(assoc != null)
+ // Need to insert the key like this to prevent duplicate keys fucking up.
+ var/list/dummy = list()
+ dummy[result] = assoc
+ result = dummy
+ val += result
+ else
+ val = world.SDQL_var(object, expression, i, object, superuser, src)
+ i = expression.len
+
+ return list("val" = val, "i" = i)
+
+/proc/SDQL_parse(list/query_list)
+ var/datum/SDQL_parser/parser = new()
+ var/list/querys = list()
+ var/list/query_tree = list()
+ var/pos = 1
+ var/querys_pos = 1
+ var/do_parse = 0
+
+ for(var/val in query_list)
+ if(val == ";")
+ do_parse = 1
+ else if(pos >= query_list.len)
+ query_tree += val
+ do_parse = 1
+
+ if(do_parse)
+ parser.query = query_tree
+ var/list/parsed_tree
+ parsed_tree = parser.parse()
+ if(parsed_tree.len > 0)
+ querys.len = querys_pos
+ querys[querys_pos] = parsed_tree
+ querys_pos++
+ else //There was an error so don't run anything, and tell the user which query has errored.
+ to_chat(usr, "Parsing error on [querys_pos]\th query. Nothing was executed.")
+ return list()
+ query_tree = list()
+ do_parse = 0
+ else
+ query_tree += val
+ pos++
+
+ qdel(parser)
+ return querys
+
+/proc/SDQL_testout(list/query_tree, indent = 0)
+ var/static/whitespace = " "
+ var/spaces = ""
+ for(var/s = 0, s < indent, s++)
+ spaces += whitespace
+
+ for(var/item in query_tree)
+ if(istype(item, /list))
+ to_chat(usr, "[spaces](")
+ SDQL_testout(item, indent + 1)
+ to_chat(usr, "[spaces])")
+
+ else
+ to_chat(usr, "[spaces][item]")
+
+ if(!isnum(item) && query_tree[item])
+
+ if(istype(query_tree[item], /list))
+ to_chat(usr, "[spaces][whitespace](")
+ SDQL_testout(query_tree[item], indent + 2)
+ to_chat(usr, "[spaces][whitespace])")
+
+ else
+ to_chat(usr, "[spaces][whitespace][query_tree[item]]")
+
+//Staying as a world proc as this is called too often for changes to offset the potential IsAdminAdvancedProcCall checking overhead.
+/world/proc/SDQL_var(object, list/expression, start = 1, source, superuser, datum/SDQL2_query/query)
+ var/v
+ var/static/list/exclude = list("usr", "src", "marked", "global")
+ var/long = start < expression.len
+ var/datum/D
+ if(is_proper_datum(object))
+ D = object
+
+ if (object == world && (!long || expression[start + 1] == ".") && !(expression[start] in exclude))
+ to_chat(usr, "World variables are not allowed to be accessed. Use global.")
+ return null
+
+ else if(expression [start] == "{" && long)
+ if(lowertext(copytext(expression[start + 1], 1, 3)) != "0x")
+ to_chat(usr, "Invalid pointer syntax: [expression[start + 1]]")
+ return null
+ v = locate("\[[expression[start + 1]]]")
+ if(!v)
+ to_chat(usr, "Invalid pointer: [expression[start + 1]]")
+ return null
+ 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]]
+ else
+ v = "SECRET"
+ else if(D != null && long && expression[start + 1] == ":" && hascall(D, expression[start]))
+ v = expression[start]
+ else if(!long || expression[start + 1] == ".")
+ switch(expression[start])
+ if("usr")
+ v = usr
+ if("src")
+ v = source
+ if("marked")
+ if(usr.client && usr.client.holder && usr.client.holder.marked_datum)
+ v = usr.client.holder.marked_datum
+ else
+ return null
+ if("world")
+ v = world
+ if("global")
+ v = GLOB
+ if("MC")
+ v = Master
+ if("FS")
+ v = Failsafe
+ if("CFG")
+ v = config
+ else
+ return null
+ else if(object == GLOB) // Shitty ass hack kill me.
+ v = expression[start]
+ if(long)
+ if(expression[start + 1] == ".")
+ return SDQL_var(v, expression[start + 2], null, source, superuser, query)
+ else if(expression[start + 1] == ":")
+ return (query.options & SDQL2_OPTION_BLOCKING_CALLS)? query.SDQL_function_async(object, v, expression[start + 2], source) : query.SDQL_function_blocking(object, v, expression[start + 2], source)
+ else if(expression[start + 1] == "\[" && islist(v))
+ var/list/L = v
+ var/index = query.SDQL_expression(source, expression[start + 2])
+ if(isnum(index) && (!(round(index) == index) || L.len < index))
+ to_chat(usr, "Invalid list index: [index]")
+ return null
+ return L[index]
+ return v
+
+/proc/SDQL2_tokenize(query_text)
+
+ var/list/whitespace = list(" ", "\n", "\t")
+ var/list/single = list("(", ")", ",", "+", "-", ".", "\[", "]", "{", "}", ";", ":")
+ var/list/multi = list(
+ "=" = list("", "="),
+ "<" = list("", "=", ">"),
+ ">" = list("", "="),
+ "!" = list("", "="))
+
+ var/word = ""
+ var/list/query_list = list()
+ var/len = length(query_text)
+
+ for(var/i = 1, i <= len, i++)
+ var/char = copytext(query_text, i, i + 1)
+
+ if(char in whitespace)
+ if(word != "")
+ query_list += word
+ word = ""
+
+ else if(char in single)
+ if(word != "")
+ query_list += word
+ word = ""
+
+ query_list += char
+
+ else if(char in multi)
+ if(word != "")
+ query_list += word
+ word = ""
+
+ var/char2 = copytext(query_text, i + 1, i + 2)
+
+ if(char2 in multi[char])
+ query_list += "[char][char2]"
+ i++
+
+ else
+ query_list += char
+
+ else if(char == "'")
+ if(word != "")
+ to_chat(usr, "SDQL2: You have an error in your SDQL syntax, unexpected ' in query: \"[query_text]\" following \"[word]\". Please check your syntax, and try again.")
+ return null
+
+ word = "'"
+
+ for(i++, i <= len, i++)
+ char = copytext(query_text, i, i + 1)
+
+ if(char == "'")
+ if(copytext(query_text, i + 1, i + 2) == "'")
+ word += "'"
+ i++
+
+ else
+ break
+
+ else
+ word += char
+
+ if(i > len)
+ to_chat(usr, "SDQL2: You have an error in your SDQL syntax, unmatched ' in query: \"[query_text]\". Please check your syntax, and try again.")
+ return null
+
+ query_list += "[word]'"
+ word = ""
+
+ else if(char == "\"")
+ if(word != "")
+ to_chat(usr, "SDQL2: You have an error in your SDQL syntax, unexpected \" in query: \"[query_text]\" following \"[word]\". Please check your syntax, and try again.")
+ return null
+
+ word = "\""
+
+ for(i++, i <= len, i++)
+ char = copytext(query_text, i, i + 1)
+
+ if(char == "\"")
+ if(copytext(query_text, i + 1, i + 2) == "'")
+ word += "\""
+ i++
+
+ else
+ break
+
+ else
+ word += char
+
+ if(i > len)
+ to_chat(usr, "SDQL2: You have an error in your SDQL syntax, unmatched \" in query: \"[query_text]\". Please check your syntax, and try again.")
+ return null
+
+ query_list += "[word]\""
+ word = ""
+
+ else
+ word += char
+
+ if(word != "")
+ query_list += word
+ return query_list
+
+/proc/is_proper_datum(thing)
+ return istype(thing, /datum) || istype(thing, /client)
+
+/obj/effect/statclick/SDQL2_delete/Click()
+ var/datum/SDQL2_query/Q = target
+ Q.delete_click()
+
+/obj/effect/statclick/SDQL2_action/Click()
+ var/datum/SDQL2_query/Q = target
+ Q.action_click()
+
+/obj/effect/statclick/SDQL2_VV_all
+ name = "VIEW VARIABLES"
+
+/obj/effect/statclick/SDQL2_VV_all/Click()
+ usr.client.debug_variables(GLOB.sdql2_queries)
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm b/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm
new file mode 100644
index 0000000000..e6504475c5
--- /dev/null
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2_parser.dm
@@ -0,0 +1,635 @@
+
+//I'm pretty sure that this is a recursive [s]descent[/s] ascent parser.
+
+
+//Spec
+
+//////////
+//
+// query : select_query | delete_query | update_query | call_query | explain
+// explain : 'EXPLAIN' query
+// select_query : 'SELECT' object_selectors
+// delete_query : 'DELETE' object_selectors
+// update_query : 'UPDATE' object_selectors 'SET' assignments
+// call_query : 'CALL' variable 'ON' object_selectors // Note here: 'variable' does function calls. This simplifies parsing.
+//
+// select_item : '*' | object_type
+//
+// object_selectors : select_item [('FROM' | 'IN') from_item] [modifier_list]
+// modifier_list : ('WHERE' bool_expression | 'MAP' expression) [modifier_list]
+//
+// from_item : 'world' | expression
+//
+// call_function : '(' [arguments] ')'
+// arguments : expression [',' arguments]
+//
+// object_type :
+//
+// assignments : assignment [',' assignments]
+// assignment : '=' expression
+// variable : | '.' variable | '[' ']' | '[' ']' '.' variable
+//
+// 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 ')' )
+// comparitor : '=' | '==' | '!=' | '<>' | '<' | '<=' | '>' | '>='
+// value : variable | string | number | 'null' | object_type
+// unary_operator : '!' | '-' | '~'
+// binary_operator : comparitor | '+' | '-' | '/' | '*' | '&' | '|' | '^' | '%'
+// bool_operator : 'AND' | '&&' | 'OR' | '||'
+//
+// string : ''' ''' | '"' '"'
+// number :
+//
+//////////
+
+/datum/SDQL_parser
+ var/query_type
+ var/error = 0
+
+ var/list/query
+ var/list/tree
+
+ var/list/boolean_operators = list("and", "or", "&&", "||")
+ var/list/unary_operators = list("!", "-", "~")
+ var/list/binary_operators = list("+", "-", "/", "*", "&", "|", "^", "%")
+ var/list/comparitors = list("=", "==", "!=", "<>", "<", "<=", ">", ">=")
+
+/datum/SDQL_parser/New(query_list)
+ query = query_list
+
+/datum/SDQL_parser/proc/parse_error(error_message)
+ error = 1
+ to_chat(usr, "SQDL2 Parsing Error: [error_message]")
+ return query.len + 1
+
+/datum/SDQL_parser/proc/parse()
+ tree = list()
+ query_options(1, tree)
+
+ if(error)
+ return list()
+ else
+ return tree
+
+/datum/SDQL_parser/proc/token(i)
+ if(i <= query.len)
+ return query[i]
+
+ else
+ return null
+
+/datum/SDQL_parser/proc/tokens(i, num)
+ if(i + num <= query.len)
+ return query.Copy(i, i + num)
+
+ else
+ return null
+
+/datum/SDQL_parser/proc/tokenl(i)
+ return lowertext(token(i))
+
+/datum/SDQL_parser/proc/query_options(i, list/node)
+ var/list/options = list()
+ if(tokenl(i) == "using")
+ i = option_assignments(i + 1, node, options)
+ query(i, node)
+ if(length(options))
+ node["options"] = options
+
+//option_assignment: query_option '=' define
+/datum/SDQL_parser/proc/option_assignment(i, list/node, list/assignment_list = list())
+ var/type = tokenl(i)
+ if(!(type in SDQL2_VALID_OPTION_TYPES))
+ parse_error("Invalid option type: [type]")
+ if(!(token(i + 1) == "="))
+ parse_error("Invalid option assignment symbol: [token(i + 1)]")
+ var/val = tokenl(i + 2)
+ if(!(val in SDQL2_VALID_OPTION_VALUES))
+ parse_error("Invalid optoin value: [val]")
+ assignment_list[type] = val
+ return (i + 3)
+
+//option_assignments: option_assignment, [',' option_assignments]
+/datum/SDQL_parser/proc/option_assignments(i, list/node, list/store)
+ i = option_assignment(i, node, store)
+
+ if(token(i) == ",")
+ i = option_assignments(i + 1, node, store)
+
+ return i
+
+//query: select_query | delete_query | update_query
+/datum/SDQL_parser/proc/query(i, list/node)
+ query_type = tokenl(i)
+
+ switch(query_type)
+ if("select")
+ select_query(i, node)
+
+ if("delete")
+ delete_query(i, node)
+
+ if("update")
+ update_query(i, node)
+
+ if("call")
+ call_query(i, node)
+
+ if("explain")
+ node += "explain"
+ node["explain"] = list()
+ query(i + 1, node["explain"])
+
+
+// select_query: 'SELECT' object_selectors
+/datum/SDQL_parser/proc/select_query(i, list/node)
+ var/list/select = list()
+ i = object_selectors(i + 1, select)
+
+ node["select"] = select
+ return i
+
+
+//delete_query: 'DELETE' object_selectors
+/datum/SDQL_parser/proc/delete_query(i, list/node)
+ var/list/select = list()
+ i = object_selectors(i + 1, select)
+
+ node["delete"] = select
+
+ return i
+
+
+//update_query: 'UPDATE' object_selectors 'SET' assignments
+/datum/SDQL_parser/proc/update_query(i, list/node)
+ var/list/select = list()
+ i = object_selectors(i + 1, select)
+
+ node["update"] = select
+
+ if(tokenl(i) != "set")
+ i = parse_error("UPDATE has misplaced SET")
+
+ var/list/set_assignments = list()
+ i = assignments(i + 1, set_assignments)
+
+ node["set"] = set_assignments
+
+ return i
+
+
+//call_query: 'CALL' call_function ['ON' object_selectors]
+/datum/SDQL_parser/proc/call_query(i, list/node)
+ var/list/func = list()
+ i = variable(i + 1, func) // Yes technically does anything variable() matches but I don't care, if admins fuck up this badly then they shouldn't be allowed near SDQL.
+
+ node["call"] = func
+
+ if(tokenl(i) != "on")
+ return parse_error("You need to specify what to call ON.")
+
+ var/list/select = list()
+ i = object_selectors(i + 1, select)
+
+ node["on"] = select
+
+ return i
+
+// object_selectors: select_item [('FROM' | 'IN') from_item] [modifier_list]
+/datum/SDQL_parser/proc/object_selectors(i, list/node)
+ i = select_item(i, node)
+
+ if (tokenl(i) == "from" || tokenl(i) == "in")
+ i++
+ var/list/from = list()
+ i = from_item(i, from)
+ node[++node.len] = from
+
+ else
+ node[++node.len] = list("world")
+
+ i = modifier_list(i, node)
+ return i
+
+// modifier_list: ('WHERE' bool_expression | 'MAP' expression) [modifier_list]
+/datum/SDQL_parser/proc/modifier_list(i, list/node)
+ while (TRUE)
+ if (tokenl(i) == "where")
+ i++
+ node += "where"
+ var/list/expr = list()
+ i = bool_expression(i, expr)
+ node[++node.len] = expr
+
+ else if (tokenl(i) == "map")
+ i++
+ node += "map"
+ var/list/expr = list()
+ i = expression(i, expr)
+ node[++node.len] = expr
+
+ else
+ return i
+
+//select_list:select_item [',' select_list]
+/datum/SDQL_parser/proc/select_list(i, list/node)
+ i = select_item(i, node)
+
+ if(token(i) == ",")
+ i = select_list(i + 1, node)
+
+ return i
+
+//assignments: assignment, [',' assignments]
+/datum/SDQL_parser/proc/assignments(i, list/node)
+ i = assignment(i, node)
+
+ if(token(i) == ",")
+ i = assignments(i + 1, node)
+
+ return i
+
+
+//select_item: '*' | select_function | object_type
+/datum/SDQL_parser/proc/select_item(i, list/node)
+ if (token(i) == "*")
+ node += "*"
+ i++
+
+ else if (copytext(token(i), 1, 2) == "/")
+ i = object_type(i, node)
+
+ else
+ i = parse_error("Expected '*' or type path for select item")
+
+ return i
+
+// Standardized method for handling the IN/FROM and WHERE options.
+/datum/SDQL_parser/proc/selectors(i, list/node)
+ while (token(i))
+ var/tok = tokenl(i)
+ if (tok in list("from", "in"))
+ var/list/from = list()
+ i = from_item(i + 1, from)
+
+ node["from"] = from
+ continue
+
+ if (tok == "where")
+ var/list/where = list()
+ i = bool_expression(i + 1, where)
+
+ node["where"] = where
+ continue
+
+ parse_error("Expected either FROM, IN or WHERE token, found [token(i)] instead.")
+ return i + 1
+
+ if (!node.Find("from"))
+ node["from"] = list("world")
+
+ return i
+
+//from_item: 'world' | expression
+/datum/SDQL_parser/proc/from_item(i, list/node)
+ if(token(i) == "world")
+ node += "world"
+ i++
+
+ else
+ i = expression(i, node)
+
+ return i
+
+
+//bool_expression: expression [bool_operator bool_expression]
+/datum/SDQL_parser/proc/bool_expression(i, list/node)
+
+ var/list/bool = list()
+ i = expression(i, bool)
+
+ node[++node.len] = bool
+
+ if(tokenl(i) in boolean_operators)
+ i = bool_operator(i, node)
+ i = bool_expression(i, node)
+
+ return i
+
+
+//assignment: '=' expression
+/datum/SDQL_parser/proc/assignment(var/i, var/list/node, var/list/assignment_list = list())
+ assignment_list += token(i)
+
+ if(token(i + 1) == ".")
+ i = assignment(i + 2, node, assignment_list)
+
+ else if(token(i + 1) == "=")
+ var/exp_list = list()
+ node[assignment_list] = exp_list
+
+ i = expression(i + 2, exp_list)
+
+ else
+ parse_error("Assignment expected, but no = found")
+
+ return i
+
+
+//variable: | '.' variable | '[' ']' | '[' ']' '.' variable
+/datum/SDQL_parser/proc/variable(i, list/node)
+ var/list/L = list(token(i))
+ node[++node.len] = L
+
+ if(token(i) == "{")
+ L += token(i + 1)
+ i += 2
+
+ if(token(i) != "}")
+ parse_error("Missing } at end of pointer.")
+
+ if(token(i + 1) == ".")
+ L += "."
+ i = variable(i + 2, L)
+
+ else if (token(i + 1) == "(") // OH BOY PROC
+ var/list/arguments = list()
+ i = call_function(i, null, arguments)
+ L += ":"
+ L[++L.len] = arguments
+
+ else if (token(i + 1) == "\[")
+ var/list/expression = list()
+ i = expression(i + 2, expression)
+ if (token(i) != "]")
+ parse_error("Missing ] at the end of list access.")
+
+ L += "\["
+ L[++L.len] = expression
+ i++
+
+ else
+ i++
+
+ return i
+
+
+//object_type:
+/datum/SDQL_parser/proc/object_type(i, list/node)
+
+ if (copytext(token(i), 1, 2) != "/")
+ return parse_error("Expected type, but it didn't begin with /")
+
+ var/path = text2path(token(i))
+ if (path == null)
+ return parse_error("Nonexistant type path: [token(i)]")
+
+ node += path
+
+ return i + 1
+
+
+//comparitor: '=' | '==' | '!=' | '<>' | '<' | '<=' | '>' | '>='
+/datum/SDQL_parser/proc/comparitor(i, list/node)
+
+ if(token(i) in list("=", "==", "!=", "<>", "<", "<=", ">", ">="))
+ node += token(i)
+
+ else
+ parse_error("Unknown comparitor [token(i)]")
+
+ return i + 1
+
+
+//bool_operator: 'AND' | '&&' | 'OR' | '||'
+/datum/SDQL_parser/proc/bool_operator(i, list/node)
+
+ if(tokenl(i) in list("and", "or", "&&", "||"))
+ node += token(i)
+
+ else
+ parse_error("Unknown comparitor [token(i)]")
+
+ return i + 1
+
+
+//string: ''' ''' | '"' '"'
+/datum/SDQL_parser/proc/string(i, list/node)
+
+ if(copytext(token(i), 1, 2) in list("'", "\""))
+ node += token(i)
+
+ else
+ parse_error("Expected string but found '[token(i)]'")
+
+ return i + 1
+
+//array: '[' expression, expression, ... ']'
+/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) != "\[")
+ parse_error("Expected an array but found '[token(i)]'")
+ return i + 1
+
+ node += token(i) // Add the "["
+
+ var/list/expression_list = list()
+
+ i++
+ if(token(i) != "]")
+ var/list/temp_expression_list = list()
+ var/tok
+ do
+ tok = token(i)
+ if (tok == "," || tok == ":")
+ if (temp_expression_list == null)
+ parse_error("Found ',' or ':' without expression in an array.")
+ return i + 1
+
+ expression_list[++expression_list.len] = temp_expression_list
+ temp_expression_list = null
+ if (tok == ":")
+ temp_expression_list = list()
+ i = expression(i + 1, temp_expression_list)
+ expression_list[expression_list[expression_list.len]] = temp_expression_list
+ temp_expression_list = null
+ tok = token(i)
+ if (tok != ",")
+ if (tok == "]")
+ break
+
+ parse_error("Expected ',' or ']' after array assoc value, but found '[token(i)]'")
+ return i
+
+
+ i++
+ continue
+
+ 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
+
+ while(token(i) && token(i) != "]")
+
+ if (temp_expression_list)
+ expression_list[++expression_list.len] = temp_expression_list
+
+ node[++node.len] = expression_list
+
+ return i + 1
+
+//call_function: ['(' [arguments] ')']
+/datum/SDQL_parser/proc/call_function(i, list/node, list/arguments)
+ if(length(tokenl(i)))
+ var/procname = ""
+ if(tokenl(i) == "global" && token(i + 1) == ".") // Global proc.
+ i += 2
+ procname = "global."
+ node += procname + token(i++)
+ if(token(i) != "(")
+ parse_error("Expected ( but found '[token(i)]'")
+
+ else if(token(i + 1) != ")")
+ var/list/temp_expression_list = list()
+ do
+ i = expression(i + 1, temp_expression_list)
+ if(token(i) == ",")
+ arguments[++arguments.len] = temp_expression_list
+ temp_expression_list = list()
+ continue
+
+ while(token(i) && token(i) != ")")
+
+ arguments[++arguments.len] = temp_expression_list // The code this is copy pasted from won't be executed when it's the last param, this fixes that.
+ else
+ i++
+ else
+ parse_error("Expected a function but found nothing")
+ return i + 1
+
+
+//expression: ( unary_expression | '(' 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)
+
+ if(token(i) in binary_operators)
+ i = binary_operator(i, node)
+ i = expression(i, node)
+
+ else if(token(i) in comparitors)
+ i = binary_operator(i, node)
+
+ var/list/rhs = list()
+ i = expression(i, rhs)
+
+ node[++node.len] = rhs
+
+
+ return i
+
+
+//unary_expression: unary_operator ( unary_expression | value | '(' expression ')' )
+/datum/SDQL_parser/proc/unary_expression(i, list/node)
+
+ if(token(i) in unary_operators)
+ var/list/unary_exp = list()
+
+ unary_exp += token(i)
+ i++
+
+ 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)
+
+ node[++node.len] = unary_exp
+
+
+ else
+ parse_error("Expected unary operator but found '[token(i)]'")
+
+ return i
+
+
+//binary_operator: comparitor | '+' | '-' | '/' | '*' | '&' | '|' | '^' | '%'
+/datum/SDQL_parser/proc/binary_operator(i, list/node)
+
+ if(token(i) in (binary_operators + comparitors))
+ node += token(i)
+
+ else
+ parse_error("Unknown binary operator [token(i)]")
+
+ return i + 1
+
+
+//value: variable | string | number | 'null' | object_type
+/datum/SDQL_parser/proc/value(i, list/node)
+ if(token(i) == "null")
+ node += "null"
+ i++
+
+ else if(lowertext(copytext(token(i), 1, 3)) == "0x" && isnum(hex2num(copytext(token(i), 3))))
+ node += hex2num(copytext(token(i), 3))
+ i++
+
+ else if(isnum(text2num(token(i))))
+ node += text2num(token(i))
+ i++
+
+ else if(copytext(token(i), 1, 2) in list("'", "\""))
+ i = string(i, node)
+
+ else if(copytext(token(i), 1, 2) == "\[") // Start a list.
+ i = array(i, node)
+ else if(copytext(token(i), 1, 2) == "/")
+ i = object_type(i, node)
+ else
+ i = variable(i, node)
+
+ return i
\ No newline at end of file
diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm
new file mode 100644
index 0000000000..efca1ea17e
--- /dev/null
+++ b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm
@@ -0,0 +1,215 @@
+// Wrappers for BYOND default procs which can't directly be called by call().
+
+/proc/_abs(A)
+ return abs(A)
+
+/proc/_animate(atom/A, set_vars, time = 10, loop = 1, easing = LINEAR_EASING, flags = null)
+ var/mutable_appearance/MA = new()
+ for(var/v in set_vars)
+ MA.vars[v] = set_vars[v]
+ animate(A, appearance = MA, time, loop, easing, flags)
+
+/proc/_acrccos(A)
+ return arccos(A)
+
+/proc/_arcsin(A)
+ return arcsin(A)
+
+/proc/_ascii2text(A)
+ return ascii2text(A)
+
+/proc/_block(Start, End)
+ return block(Start, End)
+
+/proc/_ckey(Key)
+ return ckey(Key)
+
+/proc/_ckeyEx(Key)
+ return ckeyEx(Key)
+
+/proc/_copytext(T, Start = 1, End = 0)
+ return copytext(T, Start, End)
+
+/proc/_cos(X)
+ return cos(X)
+
+/proc/_get_dir(Loc1, Loc2)
+ return get_dir(Loc1, Loc2)
+
+/proc/_get_dist(Loc1, Loc2)
+ return get_dist(Loc1, Loc2)
+
+/proc/_get_step(Ref, Dir)
+ return get_step(Ref, Dir)
+
+/proc/_hearers(Depth = world.view, Center = usr)
+ return hearers(Depth, Center)
+
+/proc/_image(icon, loc, icon_state, layer, dir)
+ return image(icon, loc, icon_state, layer, dir)
+
+/proc/_istype(object, type)
+ return istype(object, type)
+
+/proc/_ispath(path, type)
+ return ispath(path, type)
+
+/proc/_length(E)
+ return length(E)
+
+/proc/_link(thing, url)
+ thing << link(url)
+
+/proc/_locate(X, Y, Z)
+ if (isnull(Y)) // Assuming that it's only a single-argument call.
+ return locate(X)
+
+ return locate(X, Y, Z)
+
+/proc/_log(X, Y)
+ return log(X, Y)
+
+/proc/_lowertext(T)
+ return lowertext(T)
+
+/proc/_matrix(a, b, c, d, e, f)
+ return matrix(a, b, c, d, e, f)
+
+/proc/_max(...)
+ return max(arglist(args))
+
+/proc/_md5(T)
+ return md5(T)
+
+/proc/_min(...)
+ return min(arglist(args))
+
+/proc/_new(type, arguments)
+ return new type (arglist(arguments))
+
+/proc/_num2text(N, SigFig = 6)
+ return num2text(N, SigFig)
+
+/proc/_ohearers(Dist, Center = usr)
+ return ohearers(Dist, Center)
+
+/proc/_orange(Dist, Center = usr)
+ return orange(Dist, Center)
+
+/proc/_output(thing, msg, control)
+ thing << output(msg, control)
+
+/proc/_oview(Dist, Center = usr)
+ return oview(Dist, Center)
+
+/proc/_oviewers(Dist, Center = usr)
+ return oviewers(Dist, Center)
+
+/proc/_params2list(Params)
+ return params2list(Params)
+
+/proc/_pick(...)
+ return pick(arglist(args))
+
+/proc/_prob(P)
+ return prob(P)
+
+/proc/_rand(L = 0, H = 1)
+ return rand(L, H)
+
+/proc/_range(Dist, Center = usr)
+ return range(Dist, Center)
+
+/proc/_regex(pattern, flags)
+ return regex(pattern, flags)
+
+/proc/_REGEX_QUOTE(text)
+ return REGEX_QUOTE(text)
+
+/proc/_REGEX_QUOTE_REPLACEMENT(text)
+ return REGEX_QUOTE_REPLACEMENT(text)
+
+/proc/_replacetext(Haystack, Needle, Replacement, Start = 1,End = 0)
+ return replacetext(Haystack, Needle, Replacement, Start, End)
+
+/proc/_replacetextEx(Haystack, Needle, Replacement, Start = 1,End = 0)
+ return replacetextEx(Haystack, Needle, Replacement, Start, End)
+
+/proc/_rgb(R, G, B)
+ return rgb(R, G, B)
+
+/proc/_rgba(R, G, B, A)
+ return rgb(R, G, B, A)
+
+/proc/_roll(dice)
+ return roll(dice)
+
+/proc/_round(A, B = 1)
+ return round(A, B)
+
+/proc/_sin(X)
+ return sin(X)
+
+/proc/_list_add(list/L, ...)
+ if (args.len < 2)
+ return
+ L += args.Copy(2)
+
+/proc/_list_copy(list/L, Start = 1, End = 0)
+ return L.Copy(Start, End)
+
+/proc/_list_cut(list/L, Start = 1, End = 0)
+ L.Cut(Start, End)
+
+/proc/_list_find(list/L, Elem, Start = 1, End = 0)
+ return L.Find(Elem, Start, End)
+
+/proc/_list_insert(list/L, Index, Item)
+ return L.Insert(Index, Item)
+
+/proc/_list_join(list/L, Glue, Start = 0, End = 1)
+ return L.Join(Glue, Start, End)
+
+/proc/_list_remove(list/L, ...)
+ if (args.len < 2)
+ return
+ L -= args.Copy(2)
+
+/proc/_list_set(list/L, key, value)
+ L[key] = value
+
+/proc/_list_numerical_add(L, key, num)
+ L[key] += num
+
+/proc/_list_swap(list/L, Index1, Index2)
+ L.Swap(Index1, Index2)
+
+/proc/_walk(ref, dir, lag)
+ walk(ref, dir, lag)
+
+/proc/_walk_towards(ref, trg, lag)
+ walk_towards(ref, trg, lag)
+
+/proc/_walk_to(ref, trg, min, lag)
+ walk_to(ref, trg, min, lag)
+
+/proc/_walk_away(ref, trg, max, lag)
+ walk_away(ref, trg, max, lag)
+
+/proc/_walk_rand(ref, lag)
+ walk_rand(ref, lag)
+
+/proc/_step(ref, dir)
+ step(ref, dir)
+
+/proc/_step_rand(ref)
+ step_rand(ref)
+
+/proc/_step_to(ref, trg, min)
+ step_to(ref, trg, min)
+
+/proc/_step_towards(ref, trg)
+ step_towards(ref, trg)
+
+/proc/_step_away(ref, trg, max)
+ step_away(ref, trg, max)
diff --git a/code/modules/admin/verbs/SDQL_2.dm b/code/modules/admin/verbs/SDQL_2.dm
deleted file mode 100644
index aa138e62ea..0000000000
--- a/code/modules/admin/verbs/SDQL_2.dm
+++ /dev/null
@@ -1,426 +0,0 @@
-
-
-/client/proc/SDQL2_query(query_text as message)
- set category = "Admin"
- if(!check_rights(R_DEBUG)) //Shouldn't happen... but just to be safe.
- message_admins("ERROR: Non-admin [usr.key] attempted to execute a SDQL query!")
- log_admin("Non-admin [usr.key] attempted to execute a SDQL query!")
-
- if(!query_text || length(query_text) < 1)
- return
-
- //world << query_text
-
- var/list/query_list = SDQL2_tokenize(query_text)
-
- if(!query_list || query_list.len < 1)
- return
-
- var/list/query_tree = SDQL_parse(query_list)
-
- if(query_tree.len < 1)
- return
-
- var/list/from_objs = list()
- var/list/select_types = list()
-
- switch(query_tree[1])
- if("explain")
- SDQL_testout(query_tree["explain"])
- return
-
- if("call")
- if("on" in query_tree)
- select_types = query_tree["on"]
- else
- return
-
- if("select", "delete", "update")
- select_types = query_tree[query_tree[1]]
-
- from_objs = SDQL_from_objs(query_tree["from"])
-
- var/list/objs = list()
-
- for(var/type in select_types)
- var/char = copytext(type, 1, 2)
-
- if(char == "/" || char == "*")
- for(var/from in from_objs)
- objs += SDQL_get_all(type, from)
-
- else if(char == "'" || char == "\"")
- objs += locate(copytext(type, 2, length(type)))
-
- if("where" in query_tree)
- var/objs_temp = objs
- objs = list()
- for(var/datum/d in objs_temp)
- if(SDQL_expression(d, query_tree["where"]))
- objs += d
-
- //usr << "Query: [query_text]"
- message_admins("[usr] executed SDQL query: \"[query_text]\".")
-
- switch(query_tree[1])
- if("delete")
- for(var/datum/d in objs)
- qdel(d)
-
- if("select")
- var/text = ""
- for(var/datum/t in objs)
- if(istype(t, /atom))
- var/atom/a = t
-
- if(a.x)
- text += "\ref[t]: [t] at ([a.x], [a.y], [a.z])
"
-
- else if(a.loc && a.loc.x)
- text += "\ref[t]: [t] in [a.loc] at ([a.loc.x], [a.loc.y], [a.loc.z])
"
-
- else
- text += "\ref[t]: [t]
"
-
- else
- text += "\ref[t]: [t]
"
-
- usr << browse(text, "window=SDQL-result")
-
- if("update")
- if("set" in query_tree)
- var/list/set_list = query_tree["set"]
- for(var/datum/d in objs)
- var/list/vals = list()
- for(var/v in set_list)
- if(v in d.vars)
- vals += v
- vals[v] = SDQL_expression(d, set_list[v])
-
- if(istype(d, /turf))
- for(var/v in vals)
- if(v == "x" || v == "y" || v == "z")
- continue
-
- d.vars[v] = vals[v]
-
- else
- for(var/v in vals)
- d.vars[v] = vals[v]
-
-
-
-
-
-/proc/SDQL_parse(list/query_list)
- var/datum/SDQL_parser/parser = new(query_list)
- var/list/query_tree = parser.parse()
-
- qdel(parser)
-
- return query_tree
-
-
-
-/proc/SDQL_testout(list/query_tree, indent = 0)
- var/spaces = ""
- for(var/s = 0, s < indent, s++)
- spaces += " "
-
- for(var/item in query_tree)
- if(istype(item, /list))
- world << "[spaces]("
- SDQL_testout(item, indent + 1)
- world << "[spaces])"
-
- else
- world << "[spaces][item]"
-
- if(!isnum(item) && query_tree[item])
-
- if(istype(query_tree[item], /list))
- world << "[spaces] ("
- SDQL_testout(query_tree[item], indent + 2)
- world << "[spaces] )"
-
- else
- world << "[spaces] [query_tree[item]]"
-
-
-
-/proc/SDQL_from_objs(list/tree)
- if("world" in tree)
- return list(world)
-
- var/list/out = list()
-
- for(var/type in tree)
- var/char = copytext(type, 1, 2)
-
- if(char == "/")
- out += SDQL_get_all(type, world)
-
- else if(char == "'" || char == "\"")
- out += locate(copytext(type, 2, length(type)))
-
- return out
-
-
-/proc/SDQL_get_all(type, location)
- var/list/out = list()
-
- if(type == "*")
- for(var/datum/d in location)
- out += d
-
- return out
-
- type = text2path(type)
-
- if(ispath(type, /mob))
- for(var/mob/d in location)
- if(istype(d, type))
- out += d
-
- else if(ispath(type, /turf))
- for(var/turf/d in location)
- if(istype(d, type))
- out += d
-
- else if(ispath(type, /obj))
- for(var/obj/d in location)
- if(istype(d, type))
- out += d
-
- else if(ispath(type, /area))
- for(var/area/d in location)
- if(istype(d, type))
- out += d
-
- else if(ispath(type, /atom))
- for(var/atom/d in location)
- if(istype(d, type))
- out += d
-
- else
- for(var/datum/d in location)
- if(istype(d, type))
- out += d
-
- return out
-
-
-/proc/SDQL_expression(datum/object, list/expression, start = 1)
- var/result = 0
- var/val
-
- for(var/i = start, i <= expression.len, i++)
- var/op = ""
-
- if(i > start)
- op = expression[i]
- i++
-
- var/list/ret = SDQL_value(object, expression, i)
- val = ret["val"]
- i = ret["i"]
-
- if(op != "")
- switch(op)
- if("+")
- result += val
- if("-")
- result -= val
- if("*")
- result *= val
- if("/")
- result /= val
- if("&")
- result &= val
- if("|")
- result |= val
- if("^")
- result ^= val
- if("=", "==")
- result = (result == val)
- if("!=", "<>")
- result = (result != val)
- if("<")
- result = (result < val)
- if("<=")
- result = (result <= val)
- if(">")
- result = (result > val)
- if(">=")
- result = (result >= val)
- if("and", "&&")
- result = (result && val)
- if("or", "||")
- result = (result || val)
- else
- usr << "SDQL2: Unknown op [op]"
- result = null
- else
- result = val
-
- return result
-
-/proc/SDQL_value(datum/object, list/expression, start = 1)
- var/i = start
- var/val = null
-
- if(i > expression.len)
- return list("val" = null, "i" = i)
-
- if(istype(expression[i], /list))
- val = SDQL_expression(object, expression[i])
-
- else if(expression[i] == "!")
- var/list/ret = SDQL_value(object, expression, i + 1)
- val = !ret["val"]
- i = ret["i"]
-
- else if(expression[i] == "~")
- var/list/ret = SDQL_value(object, expression, i + 1)
- val = ~ret["val"]
- i = ret["i"]
-
- else if(expression[i] == "-")
- var/list/ret = SDQL_value(object, expression, i + 1)
- val = -ret["val"]
- i = ret["i"]
-
- else if(expression[i] == "null")
- val = null
-
- else if(isnum(expression[i]))
- val = expression[i]
-
- else if(copytext(expression[i], 1, 2) in list("'", "\""))
- val = copytext(expression[i], 2, length(expression[i]))
-
- else
- val = SDQL_var(object, expression, i)
- i = expression.len
-
- return list("val" = val, "i" = i)
-
-/proc/SDQL_var(datum/object, list/expression, start = 1)
-
- if(expression[start] in object.vars)
-
- if(start < expression.len && expression[start + 1] == ".")
- return SDQL_var(object.vars[expression[start]], expression[start + 2])
-
- else
- return object.vars[expression[start]]
-
- else
- return null
-
-/proc/SDQL2_tokenize(query_text)
-
- var/list/whitespace = list(" ", "\n", "\t")
- var/list/single = list("(", ")", ",", "+", "-", ".")
- var/list/multi = list(
- "=" = list("", "="),
- "<" = list("", "=", ">"),
- ">" = list("", "="),
- "!" = list("", "="))
-
- var/word = ""
- var/list/query_list = list()
- var/len = length(query_text)
-
- for(var/i = 1, i <= len, i++)
- var/char = copytext(query_text, i, i + 1)
-
- if(char in whitespace)
- if(word != "")
- query_list += word
- word = ""
-
- else if(char in single)
- if(word != "")
- query_list += word
- word = ""
-
- query_list += char
-
- else if(char in multi)
- if(word != "")
- query_list += word
- word = ""
-
- var/char2 = copytext(query_text, i + 1, i + 2)
-
- if(char2 in multi[char])
- query_list += "[char][char2]"
- i++
-
- else
- query_list += char
-
- else if(char == "'")
- if(word != "")
- usr << "SDQL2: You have an error in your SDQL syntax, unexpected ' in query: \"[query_text]\" following \"[word]\". Please check your syntax, and try again."
- return null
-
- word = "'"
-
- for(i++, i <= len, i++)
- char = copytext(query_text, i, i + 1)
-
- if(char == "'")
- if(copytext(query_text, i + 1, i + 2) == "'")
- word += "'"
- i++
-
- else
- break
-
- else
- word += char
-
- if(i > len)
- usr << "SDQL2: You have an error in your SDQL syntax, unmatched ' in query: \"[query_text]\". Please check your syntax, and try again."
- return null
-
- query_list += "[word]'"
- word = ""
-
- else if(char == "\"")
- if(word != "")
- usr << "SDQL2: You have an error in your SDQL syntax, unexpected \" in query: \"[query_text]\" following \"[word]\". Please check your syntax, and try again."
- return null
-
- word = "\""
-
- for(i++, i <= len, i++)
- char = copytext(query_text, i, i + 1)
-
- if(char == "\"")
- if(copytext(query_text, i + 1, i + 2) == "'")
- word += "\""
- i++
-
- else
- break
-
- else
- word += char
-
- if(i > len)
- usr << "SDQL2: You have an error in your SDQL syntax, unmatched \" in query: \"[query_text]\". Please check your syntax, and try again."
- return null
-
- query_list += "[word]\""
- word = ""
-
- else
- word += char
-
- if(word != "")
- query_list += word
-
- return query_list
diff --git a/code/modules/admin/verbs/SDQL_2_parser.dm b/code/modules/admin/verbs/SDQL_2_parser.dm
deleted file mode 100644
index b97d256584..0000000000
--- a/code/modules/admin/verbs/SDQL_2_parser.dm
+++ /dev/null
@@ -1,531 +0,0 @@
-//I'm pretty sure that this is a recursive [s]descent[/s] ascent parser.
-
-
-
-//Spec
-
-//////////
-//
-// query : select_query | delete_query | update_query | call_query | explain
-// explain : 'EXPLAIN' query
-//
-// select_query : 'SELECT' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]
-// delete_query : 'DELETE' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]
-// update_query : 'UPDATE' select_list [('FROM' | 'IN') from_list] 'SET' assignments ['WHERE' bool_expression]
-// call_query : 'CALL' call_function ['ON' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]]
-//
-// select_list : select_item [',' select_list]
-// select_item : '*' | select_function | object_type
-// select_function : count_function
-// count_function : 'COUNT' '(' '*' ')' | 'COUNT' '(' object_types ')'
-//
-// from_list : from_item [',' from_list]
-// from_item : 'world' | object_type
-//
-// call_function : ['(' [arguments] ')']
-// arguments : expression [',' arguments]
-//
-// object_type : | string
-//
-// assignments : assignment, [',' assignments]
-// assignment : '=' expression
-// variable : | '.' variable
-//
-// 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 ')' )
-// comparitor : '=' | '==' | '!=' | '<>' | '<' | '<=' | '>' | '>='
-// value : variable | string | number | 'null'
-// unary_operator : '!' | '-' | '~'
-// binary_operator : comparitor | '+' | '-' | '/' | '*' | '&' | '|' | '^'
-// bool_operator : 'AND' | '&&' | 'OR' | '||'
-//
-// string : ''' ''' | '"' '"'
-// number :
-//
-//////////
-
-/datum/SDQL_parser
- var/query_type
- var/error = 0
-
- var/list/query
- var/list/tree
-
- var/list/select_functions = list("count")
- var/list/boolean_operators = list("and", "or", "&&", "||")
- var/list/unary_operators = list("!", "-", "~")
- var/list/binary_operators = list("+", "-", "/", "*", "&", "|", "^")
- var/list/comparitors = list("=", "==", "!=", "<>", "<", "<=", ">", ">=")
-
-
-
-/datum/SDQL_parser/New(query_list)
- query = query_list
-
-
-
-/datum/SDQL_parser/proc/parse_error(error_message)
- error = 1
- usr << "SQDL2 Parsing Error: [error_message]"
- return query.len + 1
-
-/datum/SDQL_parser/proc/parse()
- tree = list()
- query(1, tree)
-
- if(error)
- return list()
- else
- return tree
-
-/datum/SDQL_parser/proc/token(i)
- if(i <= query.len)
- return query[i]
-
- else
- return null
-
-/datum/SDQL_parser/proc/tokens(i, num)
- if(i + num <= query.len)
- return query.Copy(i, i + num)
-
- else
- return null
-
-/datum/SDQL_parser/proc/tokenl(i)
- return lowertext(token(i))
-
-
-
-/datum/SDQL_parser/proc
-
-//query: select_query | delete_query | update_query
- query(i, list/node)
- query_type = tokenl(i)
-
- switch(query_type)
- if("select")
- select_query(i, node)
-
- if("delete")
- delete_query(i, node)
-
- if("update")
- update_query(i, node)
-
- if("call")
- call_query(i, node)
-
- if("explain")
- node += "explain"
- node["explain"] = list()
- query(i + 1, node["explain"])
-
-
-// select_query: 'SELECT' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]
- select_query(i, list/node)
- var/list/select = list()
- i = select_list(i + 1, select)
-
- node += "select"
- node["select"] = select
-
- var/list/from = list()
- if(tokenl(i) in list("from", "in"))
- i = from_list(i + 1, from)
- else
- from += "world"
-
- node += "from"
- node["from"] = from
-
- if(tokenl(i) == "where")
- var/list/where = list()
- i = bool_expression(i + 1, where)
-
- node += "where"
- node["where"] = where
-
- return i
-
-
-//delete_query: 'DELETE' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]
- delete_query(i, list/node)
- var/list/select = list()
- i = select_list(i + 1, select)
-
- node += "delete"
- node["delete"] = select
-
- var/list/from = list()
- if(tokenl(i) in list("from", "in"))
- i = from_list(i + 1, from)
- else
- from += "world"
-
- node += "from"
- node["from"] = from
-
- if(tokenl(i) == "where")
- var/list/where = list()
- i = bool_expression(i + 1, where)
-
- node += "where"
- node["where"] = where
-
- return i
-
-
-//update_query: 'UPDATE' select_list [('FROM' | 'IN') from_list] 'SET' assignments ['WHERE' bool_expression]
- update_query(i, list/node)
- var/list/select = list()
- i = select_list(i + 1, select)
-
- node += "update"
- node["update"] = select
-
- var/list/from = list()
- if(tokenl(i) in list("from", "in"))
- i = from_list(i + 1, from)
- else
- from += "world"
-
- node += "from"
- node["from"] = from
-
- if(tokenl(i) != "set")
- i = parse_error("UPDATE has misplaced SET")
-
- var/list/set_assignments = list()
- i = assignments(i + 1, set_assignments)
-
- node += "set"
- node["set"] = set_assignments
-
- if(tokenl(i) == "where")
- var/list/where = list()
- i = bool_expression(i + 1, where)
-
- node += "where"
- node["where"] = where
-
- return i
-
-
-//call_query: 'CALL' call_function ['ON' select_list [('FROM' | 'IN') from_list] ['WHERE' bool_expression]]
- call_query(i, list/node)
- var/list/func = list()
- i = call_function(i + 1, func)
-
- node += "call"
- node["call"] = func
-
- if(tokenl(i) != "on")
- return i
-
- var/list/select = list()
- i = select_list(i + 1, select)
-
- node += "on"
- node["on"] = select
-
- var/list/from = list()
- if(tokenl(i) in list("from", "in"))
- i = from_list(i + 1, from)
- else
- from += "world"
-
- node += "from"
- node["from"] = from
-
- if(tokenl(i) == "where")
- var/list/where = list()
- i = bool_expression(i + 1, where)
-
- node += "where"
- node["where"] = where
-
- return i
-
-
-//select_list: select_item [',' select_list]
- select_list(i, list/node)
- i = select_item(i, node)
-
- if(token(i) == ",")
- i = select_list(i + 1, node)
-
- return i
-
-
-//from_list: from_item [',' from_list]
- from_list(i, list/node)
- i = from_item(i, node)
-
- if(token(i) == ",")
- i = from_list(i + 1, node)
-
- return i
-
-
-//assignments: assignment, [',' assignments]
- assignments(i, list/node)
- i = assignment(i, node)
-
- if(token(i) == ",")
- i = assignments(i + 1, node)
-
- return i
-
-
-//select_item: '*' | select_function | object_type
- select_item(i, list/node)
-
- if(token(i) == "*")
- node += "*"
- i++
-
- else if(tokenl(i) in select_functions)
- i = select_function(i, node)
-
- else
- i = object_type(i, node)
-
- return i
-
-
-//from_item: 'world' | object_type
- from_item(i, list/node)
-
- if(token(i) == "world")
- node += "world"
- i++
-
- else
- i = object_type(i, node)
-
- return i
-
-
-//bool_expression: expression [bool_operator bool_expression]
- bool_expression(i, list/node)
-
- var/list/bool = list()
- i = expression(i, bool)
-
- node[++node.len] = bool
-
- if(tokenl(i) in boolean_operators)
- i = bool_operator(i, node)
- i = bool_expression(i, node)
-
- return i
-
-
-//assignment: '=' expression
- assignment(i, list/node)
-
- node += token(i)
-
- if(token(i + 1) == "=")
- var/varname = token(i)
- node[varname] = list()
-
- i = expression(i + 2, node[varname])
-
- else
- parse_error("Assignment expected, but no = found")
-
- return i
-
-
-//variable: | '.' variable
- variable(i, list/node)
- var/list/L = list(token(i))
- node[++node.len] = L
-
- if(token(i + 1) == ".")
- L += "."
- i = variable(i + 2, L)
-
- else
- i++
-
- return i
-
-
-//object_type: | string
- object_type(i, list/node)
-
- if(copytext(token(i), 1, 2) == "/")
- node += token(i)
-
- else
- i = string(i, node)
-
- return i + 1
-
-
-//comparitor: '=' | '==' | '!=' | '<>' | '<' | '<=' | '>' | '>='
- comparitor(i, list/node)
-
- if(token(i) in list("=", "==", "!=", "<>", "<", "<=", ">", ">="))
- node += token(i)
-
- else
- parse_error("Unknown comparitor [token(i)]")
-
- return i + 1
-
-
-//bool_operator: 'AND' | '&&' | 'OR' | '||'
- bool_operator(i, list/node)
-
- if(tokenl(i) in list("and", "or", "&&", "||"))
- node += token(i)
-
- else
- parse_error("Unknown comparitor [token(i)]")
-
- return i + 1
-
-
-//string: ''' ''' | '"' '"'
- string(i, list/node)
-
- if(copytext(token(i), 1, 2) in list("'", "\""))
- node += token(i)
-
- else
- parse_error("Expected string but found '[token(i)]'")
-
- return i + 1
-
-
-//call_function: ['(' [arguments] ')']
- call_function(i, list/node)
-
- parse_error("Sorry, function calls aren't available yet")
-
- return i
-
-
-//select_function: count_function
- select_function(i, list/node)
-
- parse_error("Sorry, function calls aren't available yet")
-
- return i
-
-
-//expression: ( unary_expression | '(' expression ')' | value ) [binary_operator expression]
- 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)
-
- if(token(i) in binary_operators)
- i = binary_operator(i, node)
- i = expression(i, node)
-
- else if(token(i) in comparitors)
- i = binary_operator(i, node)
-
- var/list/rhs = list()
- i = expression(i, rhs)
-
- node[++node.len] = rhs
-
-
- return i
-
-
-//unary_expression: unary_operator ( unary_expression | value | '(' expression ')' )
- unary_expression(i, list/node)
-
- if(token(i) in unary_operators)
- var/list/unary_exp = list()
-
- unary_exp += token(i)
- i++
-
- 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)
-
- node[++node.len] = unary_exp
-
-
- else
- parse_error("Expected unary operator but found '[token(i)]'")
-
- return i
-
-
-//binary_operator: comparitor | '+' | '-' | '/' | '*' | '&' | '|' | '^'
- binary_operator(i, list/node)
-
- if(token(i) in (binary_operators + comparitors))
- node += token(i)
-
- else
- parse_error("Unknown binary operator [token(i)]")
-
- return i + 1
-
-
-//value: variable | string | number | 'null'
- value(i, list/node)
-
- if(token(i) == "null")
- node += "null"
- i++
-
- else if(isnum(text2num(token(i))))
- node += text2num(token(i))
- i++
-
- else if(copytext(token(i), 1, 2) in list("'", "\""))
- i = string(i, node)
-
- else
- i = variable(i, node)
-
- return i
-
-
-
-
-/*EXPLAIN SELECT * WHERE 42 = 6 * 9 OR val = - 5 == 7*/
\ No newline at end of file
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index 482c9d8360..321655bb15 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -592,7 +592,7 @@
if("Dead Mobs")
usr << jointext(dead_mob_list,",")
if("Clients")
- usr << jointext(clients,",")
+ usr << jointext(GLOB.clients,",")
/client/proc/cmd_debug_using_map()
set category = "Debug"
diff --git a/code/modules/admin/verbs/getlogs.dm b/code/modules/admin/verbs/getlogs.dm
index 3f6e4c9e74..9e9ad67027 100644
--- a/code/modules/admin/verbs/getlogs.dm
+++ b/code/modules/admin/verbs/getlogs.dm
@@ -27,7 +27,7 @@
src << "Only Admins may use this command."
return
- var/client/target = input(src,"Choose somebody to grant access to the server's runtime logs (permissions expire at the end of each round):","Grant Permissions",null) as null|anything in clients
+ var/client/target = input(src,"Choose somebody to grant access to the server's runtime logs (permissions expire at the end of each round):","Grant Permissions",null) as null|anything in GLOB.clients
if(!istype(target,/client))
src << "Error: giveruntimelog(): Client not found."
return
@@ -99,7 +99,7 @@
set category = "Admin"
set name = "Show Server Attack Log"
set desc = "Shows today's server attack log."
-
+
to_chat(usr,"This verb doesn't actually do anything.")
/*
@@ -113,4 +113,3 @@
feedback_add_details("admin_verb","SSAL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
return
*/
-
\ No newline at end of file
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index 1e8528acb9..bbb618e7c8 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -62,7 +62,7 @@
var/highlight_special_characters = 1
- for(var/client/C in clients)
+ for(var/client/C in GLOB.clients)
if(C.player_age == "Requires database")
missing_ages = 1
continue
@@ -363,7 +363,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
return
//I frontload all the questions so we don't have a half-done process while you're reading.
- var/client/picked_client = input(src, "Please specify which client's character to spawn.", "Client", "") as null|anything in clients
+ var/client/picked_client = input(src, "Please specify which client's character to spawn.", "Client", "") as null|anything in GLOB.clients
if(!picked_client)
return
diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm
index c31482cb8b..95455bba3f 100644
--- a/code/modules/client/client procs.dm
+++ b/code/modules/client/client procs.dm
@@ -44,7 +44,7 @@
//Admin PM
if(href_list["priv_msg"])
var/client/C = locate(href_list["priv_msg"])
- if(ismob(C)) //Old stuff can feed-in mobs instead of clients
+ if(ismob(C)) //Old stuff can feed-in mobs instead ofGLOB.clients
var/mob/M = C
C = M.client
cmd_admin_pm(C,null)
@@ -108,8 +108,8 @@
src << "If the title screen is black, resources are still downloading. Please be patient until the title screen appears."
- clients += src
- directory[ckey] = src
+ GLOB.clients += src
+ GLOB.directory[ckey] = src
GLOB.ahelp_tickets.ClientLogin(src)
@@ -181,8 +181,8 @@
holder.owner = null
admins -= src
GLOB.ahelp_tickets.ClientLogout(src)
- directory -= ckey
- clients -= src
+ GLOB.directory -= ckey
+ GLOB.clients -= src
return ..()
/client/Destroy()
diff --git a/code/modules/mob/living/silicon/pai/admin.dm b/code/modules/mob/living/silicon/pai/admin.dm
index 069c5d2c68..b2e6bd0288 100644
--- a/code/modules/mob/living/silicon/pai/admin.dm
+++ b/code/modules/mob/living/silicon/pai/admin.dm
@@ -7,7 +7,7 @@
return
if(!pai_key)
- var/client/C = input("Select client") as null|anything in clients
+ var/client/C = input("Select client") as null|anything in GLOB.clients
if(!C) return
pai_key = C.key
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 48d89a399e..48c741c140 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -1,4 +1,4 @@
-/mob/Destroy()//This makes sure that mobs with clients/keys are not just deleted from the game.
+/mob/Destroy()//This makes sure that mobs withGLOB.clients/keys are not just deleted from the game.
mob_list -= src
dead_mob_list -= src
living_mob_list -= src
@@ -695,6 +695,10 @@
stat("World Time:", world.time)
stat("Real time of day:", REALTIMEOFDAY)
stat(null)
+ if(GLOB)
+ GLOB.stat_entry()
+ else
+ stat("Globals:", "ERROR")
if(Master)
Master.stat_entry()
else
@@ -710,6 +714,14 @@
if(statpanel("Tickets"))
GLOB.ahelp_tickets.stat_entry()
+
+
+ if(length(GLOB.sdql2_queries))
+ if(statpanel("SDQL2"))
+ stat("Access Global SDQL2 List", GLOB.sdql2_vv_statobj)
+ for(var/i in GLOB.sdql2_queries)
+ var/datum/SDQL2_query/Q = i
+ Q.generate_stat()
if(listed_turf && client)
if(!TurfAdjacent(listed_turf))
diff --git a/code/modules/random_map/drop/droppod.dm b/code/modules/random_map/drop/droppod.dm
index c7afde1d7f..1d5fcb8d8f 100644
--- a/code/modules/random_map/drop/droppod.dm
+++ b/code/modules/random_map/drop/droppod.dm
@@ -172,7 +172,7 @@
spawned_mobs |= M
else
var/list/candidates = list()
- for(var/client/player in clients)
+ for(var/client/player in GLOB.clients)
if(player.mob && istype(player.mob, /mob/observer/dead))
candidates |= player
diff --git a/code/world.dm b/code/world.dm
index f9453d8b5d..bd4e6212f3 100644
--- a/code/world.dm
+++ b/code/world.dm
@@ -157,7 +157,7 @@ var/world_topic_spam_protect_time = world.timeofday
var/list/players = list()
var/list/admins = list()
- for(var/client/C in clients)
+ for(var/client/C in GLOB.clients)
if(C.holder)
if(C.holder.fakekey)
continue
@@ -175,7 +175,7 @@ var/world_topic_spam_protect_time = world.timeofday
var/n = 0
var/admins = 0
- for(var/client/C in clients)
+ for(var/client/C in GLOB.clients)
if(C.holder)
if(C.holder.fakekey)
continue //so stealthmins aren't revealed by the hub
@@ -346,7 +346,7 @@ var/world_topic_spam_protect_time = world.timeofday
var/client/C
var/req_ckey = ckey(input["adminmsg"])
- for(var/client/K in clients)
+ for(var/client/K in GLOB.clients)
if(K.ckey == req_ckey)
C = K
break
@@ -499,7 +499,7 @@ var/world_topic_spam_protect_time = world.timeofday
var/ckey = copytext(line, 1, length(line)+1)
var/datum/admins/D = new /datum/admins(title, rights, ckey)
- D.associate(directory[ckey])
+ D.associate(GLOB.directory[ckey])
/world/proc/load_mentors()
if(config.admin_legacy_system)
@@ -519,7 +519,7 @@ var/world_topic_spam_protect_time = world.timeofday
var/ckey = copytext(line, 1, length(line)+1)
var/datum/admins/D = new /datum/admins(title, rights, ckey)
- D.associate(directory[ckey])
+ D.associate(GLOB.directory[ckey])
/world/proc/update_status()
var/s = ""
diff --git a/vorestation.dme b/vorestation.dme
index 75d3043cfc..ecd9d7cdcb 100644
--- a/vorestation.dme
+++ b/vorestation.dme
@@ -248,6 +248,7 @@
#include "code\datums\computerfiles.dm"
#include "code\datums\datacore.dm"
#include "code\datums\datum.dm"
+#include "code\datums\datumvars.dm"
#include "code\datums\EPv2.dm"
#include "code\datums\ghost_query.dm"
#include "code\datums\hierarchy.dm"
@@ -1426,14 +1427,14 @@
#include "code\modules\admin\verbs\possess.dm"
#include "code\modules\admin\verbs\pray.dm"
#include "code\modules\admin\verbs\randomverbs.dm"
-#include "code\modules\admin\verbs\SDQL.dm"
-#include "code\modules\admin\verbs\SDQL_2.dm"
-#include "code\modules\admin\verbs\SDQL_2_parser.dm"
#include "code\modules\admin\verbs\smite.dm"
#include "code\modules\admin\verbs\smite_vr.dm"
#include "code\modules\admin\verbs\striketeam.dm"
#include "code\modules\admin\verbs\ticklag.dm"
#include "code\modules\admin\verbs\tripAI.dm"
+#include "code\modules\admin\verbs\SDQL2\SDQL_2.dm"
+#include "code\modules\admin\verbs\SDQL2\SDQL_2_parser.dm"
+#include "code\modules\admin\verbs\SDQL2\SDQL_2_wrappers.dm"
#include "code\modules\admin\view_variables\helpers.dm"
#include "code\modules\admin\view_variables\topic.dm"
#include "code\modules\admin\view_variables\view_variables.dm"