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
" + . += "" + if(mode == VOTE_GAMEMODE) + .+= "" + + 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) + . += "" + else + . += "" + if (additional_text.len >= i) + . += additional_text[i] + . += "" + + . += "" + + . += "
ChoicesVotesMinimum Players
[thisVote ? "" : ""][gamemode_names[choices[i]]][thisVote ? "" : ""][votes][thisVote ? "" : ""][choices[i]][thisVote ? "" : ""][votes]
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"