Merge branch 'master' of https://github.com/Skyrat-SS13/Skyrat-tg into upstream-24-10a

This commit is contained in:
Majkl-J
2024-10-20 04:12:02 -07:00
1411 changed files with 131464 additions and 65514 deletions
+2
View File
@@ -20,6 +20,8 @@ ADMIN_VERB(fax_panel, R_ADMIN, "Fax Panel", "View and respond to faxes sent to C
/datum/fax_panel_interface/New()
//Get all faxes, and save them to our list.
for(var/obj/machinery/fax/fax as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/fax))
if(istype(fax, /obj/machinery/fax/admin))
continue
available_faxes += WEAKREF(fax)
//Get all stamps
+7 -5
View File
@@ -484,12 +484,14 @@ ADMIN_VERB(populate_world, R_DEBUG, "Populate World", "Populate the world with t
testing("Spawned test mob at [get_area_name(tile, TRUE)] ([tile.x],[tile.y],[tile.z])")
ADMIN_VERB(toggle_ai_interact, R_ADMIN, "Toggle Admin AI Interact", "Allows you to interact with most machines as an AI would as a ghost.", ADMIN_CATEGORY_GAME)
user.AI_Interact = !user.AI_Interact
if(user.mob && isAdminGhostAI(user.mob))
user.mob.has_unlimited_silicon_privilege = user.AI_Interact
var/doesnt_have_silicon_access = !HAS_TRAIT_FROM(user, TRAIT_AI_ACCESS, ADMIN_TRAIT)
if(doesnt_have_silicon_access)
ADD_TRAIT(user, TRAIT_AI_ACCESS, ADMIN_TRAIT)
else
REMOVE_TRAIT(user, TRAIT_AI_ACCESS, ADMIN_TRAIT)
log_admin("[key_name(user)] has [user.AI_Interact ? "activated" : "deactivated"] Admin AI Interact")
message_admins("[key_name_admin(user)] has [user.AI_Interact ? "activated" : "deactivated"] their AI interaction")
log_admin("[key_name(user)] has [doesnt_have_silicon_access ? "activated" : "deactivated"] Admin AI Interact")
message_admins("[key_name_admin(user)] has [doesnt_have_silicon_access ? "activated" : "deactivated"] their AI interaction")
ADMIN_VERB(debug_statpanel, R_DEBUG, "Debug Stat Panel", "Toggles local debug of the stat panel", ADMIN_CATEGORY_DEBUG)
user.stat_panel.send_message("create_debug")
+2 -1
View File
@@ -64,7 +64,7 @@
return UI_INTERACTIVE
return ..()
/obj/effect/fun_balloon/sentience/ui_act(action, list/params)
/obj/effect/fun_balloon/sentience/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
@@ -105,6 +105,7 @@
alert_pic = src,
role_name_text = "sentience fun balloon",
)
while(LAZYLEN(candidates) && LAZYLEN(bodies))
var/mob/dead/observer/C = pick_n_take(candidates)
var/mob/living/body = pick_n_take(bodies)
+1 -1
View File
@@ -120,7 +120,7 @@
data["sprites"] = sprite_data
return data
/datum/greyscale_modify_menu/ui_act(action, params)
/datum/greyscale_modify_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
+1 -1
View File
@@ -35,7 +35,7 @@
var/exp_percent = exp / SKILL_EXP_LIST[SKILL_LEVEL_LEGENDARY]
.["skills"] += list(list("playername" = targetmind.current, "path" = type, "name" = S.name, "desc" = S.desc, "lvlnum" = lvl_num, "lvl" = lvl_name, "exp" = exp, "exp_prog" = xp_req_to_level - xp_prog_to_level, "exp_req" = xp_req_to_level, "exp_percent" = exp_percent, "max_exp" = SKILL_EXP_LIST[length(SKILL_EXP_LIST)]))
/datum/skill_panel/ui_act(action, params)
/datum/skill_panel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
+1 -1
View File
@@ -68,7 +68,7 @@
values["role"] = roles
sql_roles = ":role"
var/datum/db_query/query_check_ban = SSdbcore.NewQuery(/* SKYRAT EDIT CHANGE - MULTISERVER */{"
var/datum/db_query/query_check_ban = SSdbcore.NewQuery(/* SKYRAT EDIT CHANGE - MULTISERVER - AND [server_check] */{"
SELECT 1
FROM [format_table_name("ban")]
WHERE
+8 -4
View File
@@ -433,10 +433,12 @@
var/text = query_get_type_messages.item[5]
var/timestamp = query_get_type_messages.item[6]
var/server = query_get_type_messages.item[7]
var/editor_key = query_get_type_messages.item[9] // SKYRAT EDIT CHANGE BEGIN - MULTISERVER
// SKYRAT EDIT CHANGE BEGIN - MULTISERVER
var/editor_key = query_get_type_messages.item[9]
var/expire_timestamp = query_get_type_messages.item[10]
var/playtime = query_get_type_messages.item[11]
var/round_id = query_get_type_messages.item[12] // SKYRAT EDIT CHANGE END - MULTISERVER
var/round_id = query_get_type_messages.item[12]
// SKYRAT EDIT CHANGE END - MULTISERVER
output += "<b>"
if(type == "watchlist entry")
output += "[t_key] | "
@@ -497,13 +499,15 @@
var/text = query_get_messages.item[5]
var/timestamp = query_get_messages.item[6]
var/server = query_get_messages.item[7]
var/editor_key = query_get_messages.item[9] // SKYRAT EDIT CHANGE BEGIN - MULTISERVER
// SKYRAT EDIT CHANGE BEGIN - MULTISERVER
var/editor_key = query_get_messages.item[9]
var/age = text2num(query_get_messages.item[10])
target_key = query_get_messages.item[11]
var/expire_timestamp = query_get_messages.item[12]
var/severity = query_get_messages.item[13]
var/playtime = query_get_messages.item[14]
var/round_id = query_get_messages.item[15] // SKYRAT EDIT CHANGE END - MULTISERVER
var/round_id = query_get_messages.item[15]
// SKYRAT EDIT CHANGE END - MULTISERVER
var/alphatext = ""
var/nsd = CONFIG_GET(number/note_stale_days)
var/nfd = CONFIG_GET(number/note_fresh_days)
+3 -4
View File
@@ -1780,11 +1780,10 @@
if(!check_rights(R_ADMIN))
return
for(var/obj/machinery/fax/FAX as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/fax))
if(!is_centcom_level(FAX.z))
for(var/obj/machinery/fax/admin/FAX as anything in SSmachines.get_machines_by_type_and_subtypes(/obj/machinery/fax/admin))
if(FAX.fax_id != href_list["destination"])
continue
FAX.receive(locate(href_list["print_fax"]), href_list["fax_name"])
FAX.receive(locate(href_list["print_fax"]), href_list["sender_name"])
// SKYRAT EDIT ADDITION START
else if(href_list["pass_opfor_candidate"])
if(!check_rights(R_ADMIN))
+2 -2
View File
@@ -64,7 +64,7 @@
"SELECT /mob/living IN (@[/area/service/bar MAP contents])[1]"
What if some dumbass admin spawned a bajillion spiders and you need to kill them all?
Oh yeah you'd rather not delete all the spiders in maintenace. Only that one room the spiders were
Oh yeah you'd rather not delete all the spiders in maintenance. Only that one room the spiders were
spawned in.
"DELETE /mob/living/carbon/superior_animal/giant_spider WHERE loc.loc == marked"
@@ -109,7 +109,7 @@
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.
With great power comes great responsibility.
Here's a slightly more formal quick reference.
@@ -51,6 +51,9 @@
/proc/_get_step(Ref, Dir)
return get_step(Ref, Dir)
/proc/_hascall(object, procname)
return hascall(object, procname)
/proc/_hearers(Depth = world.view, Center = usr)
return hearers(Depth, Center)
+1 -1
View File
@@ -134,7 +134,7 @@ ADMIN_VERB(access_news_network, R_ADMIN, "Access Newscaster Network", "Allows yo
data["wanted"] = wanted_info
return data
/datum/newspanel/ui_act(action, params)
/datum/newspanel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
+8 -2
View File
@@ -218,9 +218,15 @@ ADMIN_VERB(hostile_environment, R_ADMIN, "Hostile Environment", "Disable the shu
SSshuttle.hostile_environments.Cut()
SSshuttle.checkHostileEnvironment()
ADMIN_VERB(toggle_nuke, R_DEBUG|R_ADMIN, "Toggle Nuke", "Arm or disarm a nuke.", ADMIN_CATEGORY_EVENTS, obj/machinery/nuclearbomb/nuke in world)
ADMIN_VERB(toggle_nuke, R_DEBUG|R_ADMIN, "Toggle Nuke", "Arm or disarm a nuke.", ADMIN_CATEGORY_EVENTS)
var/list/nukes = list()
for (var/obj/machinery/nuclearbomb/bomb in world)
nukes += bomb
var/obj/machinery/nuclearbomb/nuke = tgui_input_list(user, "", "Toggle Nuke", nukes)
if (isnull(nuke))
return
if(!nuke.timing)
var/newtime = input(user, "Set activation timer.", "Activate Nuke", "[nuke.timer_set]") as num|null
var/newtime = tgui_input_number(user, "Set activation timer.", "Activate Nuke", nuke.timer_set)
if(!newtime)
return
nuke.timer_set = newtime
+1 -1
View File
@@ -154,7 +154,7 @@ ADMIN_VERB(polymorph_all, R_ADMIN, "Polymorph All", "Applies the effects of the
message_admins("Mass polymorph started by [who_did_it] is complete.")
ADMIN_VERB_AND_CONTEXT_MENU(admin_smite, R_ADMIN|R_FUN, "Smite", "Smite a player with divine power.", ADMIN_CATEGORY_FUN, mob/living/target in world)
var/punishment = input(user, "Choose a punishment", "DIVINE SMITING") as null|anything in GLOB.smites
var/punishment = tgui_input_list(user, "Choose a punishment", "DIVINE SMITING", GLOB.smites)
if(QDELETED(target) || !punishment)
return
+7 -7
View File
@@ -64,7 +64,7 @@ ADMIN_VERB(borg_panel, R_ADMIN, "Show Borg Panel", ADMIN_VERB_NO_DESCRIPTION, AD
.["ais"] += list(list("name" = ai.name, "ref" = REF(ai), "connected" = (borg.connected_ai == ai)))
/datum/borgpanel/ui_act(action, params)
/datum/borgpanel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
@@ -153,15 +153,15 @@ ADMIN_VERB(borg_panel, R_ADMIN, "Show Borg Panel", ADMIN_VERB_NO_DESCRIPTION, AD
if (!borg.radio.keyslot) // There's no encryption key. This shouldn't happen but we can cope
borg.radio.channels -= channel
if (channel == RADIO_CHANNEL_SYNDICATE)
borg.radio.syndie = FALSE
borg.radio.special_channels &= ~RADIO_SPECIAL_SYNDIE
else if (channel == "CentCom")
borg.radio.independent = FALSE
borg.radio.special_channels &= ~RADIO_SPECIAL_CENTCOM
else
borg.radio.keyslot.channels -= channel
if (channel == RADIO_CHANNEL_SYNDICATE)
borg.radio.keyslot.syndie = FALSE
borg.radio.keyslot.special_channels &= ~RADIO_SPECIAL_SYNDIE
else if (channel == "CentCom")
borg.radio.keyslot.independent = FALSE
borg.radio.keyslot.special_channels &= ~RADIO_SPECIAL_CENTCOM
message_admins("[key_name_admin(user)] removed the [channel] radio channel from [ADMIN_LOOKUPFLW(borg)].")
log_silicon("[key_name(user)] removed the [channel] radio channel from [key_name(borg)].")
else // We're adding a channel
@@ -169,9 +169,9 @@ ADMIN_VERB(borg_panel, R_ADMIN, "Show Borg Panel", ADMIN_VERB_NO_DESCRIPTION, AD
borg.radio.keyslot = new()
borg.radio.keyslot.channels[channel] = 1
if (channel == RADIO_CHANNEL_SYNDICATE)
borg.radio.keyslot.syndie = TRUE
borg.radio.keyslot.special_channels |= RADIO_SPECIAL_SYNDIE
else if (channel == "CentCom")
borg.radio.keyslot.independent = TRUE
borg.radio.keyslot.special_channels |= RADIO_SPECIAL_CENTCOM
message_admins("[key_name_admin(user)] added the [channel] radio channel to [ADMIN_LOOKUPFLW(borg)].")
log_silicon("[key_name(user)] added the [channel] radio channel to [key_name(borg)].")
borg.radio.recalculateChannels()
@@ -50,7 +50,7 @@ ADMIN_VERB(ghost_pool_protection, R_ADMIN, "Ghost Pool Protection", "Choose whic
data["minigames"] = (new_role_flags & GHOSTROLE_MINIGAME)
return data
/datum/ghost_pool_menu/ui_act(action, params)
/datum/ghost_pool_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
+179 -140
View File
@@ -1,150 +1,225 @@
# Auxlua
# Objects
---
Datums, lists, typepaths, static appearances, and some other objects are represented in Luau as userdata. Certain operations can be performed on these types of objects.
## Datums
## Common metamethods
DM datums are treated as lua userdata, and can be stored in fields. Due to fundamental limitations in lua, userdata is inherently truthy. Since datum userdata can correspond to a deleted datum, which would evaluate to `null` in DM, the function [`datum:is_null()`](#datumisnull) is provided to offer a truthiness test consistent with DM.
The following metamethods are defined for all objects.
Keep in mind that BYOND can't see that a datum is referenced in a lua field, and will garbage collect it if it is not referenced anywhere in DM.
### \_\_tostring(): string
### datum:get_var(var)
Returns the string representation of the object. This uses BYOND's internal string conversion function.
Equivalent to DM's `datum.var`
### \_\_eq(other: any): boolean
### datum:set_var(var, value)
Compare the equality of two objects. While passing the same object into luau twice will return two references to the same userdata, some DM projects may override the equality operator using an `__operator==` proc definition.
Equivalent to DM's `datum.var = value`
## Datum-like Objects
### datum:call_proc(procName, ...)
Datum-like objects include datums themselves, clients (if they have not been redefined to be children of `/datum`), static appearances, and the world.
Equivalent to DM's `datum.procName(...)`
### \_\_index(index: string): any
### datum:is_null()
Access the member specified by `index`.
This function is used to evaluate the truthiness of a DM var. The lua statement `if datum:is_null() then` is equivalent to the DM statement `if(datum)`.
If `index` is a valid var for the object, the index operation will return that var's value.
If the var getting wrapper proc is set, the operation will instead call that proc with the arguments `(object, index)`.
### datum.vars
For objects other than static appearances, if `index` is a valid proc for the object, the operation will return a wrapper for that proc that can be invoked using call syntax (e.g. `object:proc(...arguments)`). If the object proc calling wrapper is set, calling the returned function will instead call the wrapper proc with the arguments `(object, proc, {...arguments})`. Note that vars will be shadowed by procs with the same name. To work around this, use the `dm.get_var` function.
Returns a userdatum that allows you to access and modifiy the vars of a DM datum by index. `datum.vars.foo` is equivalent to `datum:get_var("foo")`, while `datum.vars.foo = bar` is equivalent to `datum:set_var("foo", bar)`
### \_\_newindex(index: string, value: any): ()
---
Set the var specified by `index` to `value`, if that var exists on the object.
If the var setting wrapper proc is set, the operation will instead call that proc with the arguments `(object, index, value)`.
## Lists
In order to allow lists to be modified in-place across the DM-to-lua language barrier, lists are treated as userdata. Whenever running code that expects a DM value, auxlua will attempt to convert tables into lists.
Lists are syntactically similar to tables, with one crucial difference.
Unlike tables, numeric indices must be non-zero integers within the bounds of the list.
List references are subject to the same limitations as datum userdata, but you are less likely to encounter these limitations for regular lists.
### \_\_index(index: any): any
Some lists (`vars`, `contents`, `overlays`, `underlays`, `vis_contents`, and `vis_locs`) are inherently attached to datums, and as such, their corresponding userdata contains a weak reference to the containing datum. Use [`list:is_null`](#listisnull) to validate these types of lists.
Read the list at `index`. This works both for numeric indices and assoc keys.
Vars lists cannot be directly read this way if the var getting wrapper proc is set.
### list.len
### \_\_newindex(index: any, value: any): any
Equivalent to DM's `list.len`
Write `value` to the list at `index`. This works both for writing numeric indices and assoc keys.
Vars lists cannot be directly written this way if the var setting wrapper proc is set.
### list:get(index)
### \_\_len(): integer
Equivalent to DM's `list[index]`
Returns the length of the list, similarly to the `length` builtin in DM.
### list:set(index, value)
### Iteration
Equivalent to DM's `list[index] = value`
Lists support Luau's generalized iteration. Iteration this way returns pairs of numeric indices and list values.
For example, the statement `for _, v in L do` is logically equivalent to the DM statement `for(var/v in L)`.
### list:add(value)
# Global Fields and Modules
Equivalent to DM's `list.Add(value)`
In addition to the full extent of Luau's standard library modules, some extra functions and modules have been added.
### list:remove(value)
## Global-Level Fields
Equivalent to DM's `list.Remove(value)`
### sleep(): ()
### list:to_table()
Yields the active thread, without worrying about passing data into or out of the state.
Converts a DM list into a lua table.
Threads yielded this way are placed at the end of a queue. Call the `awaken` hook function from DM to execute the thread at the front of the queue.
### list:of_type(type_path)
### loadstring(code: string): function
Will extract only values of type `type_path`.
Luau does not inherently include the `loadstring` function common to a number of other versions of lua. This is an effective reimplementation of `loadstring`.
### list:is_null()
### print(...any): ()
A similar truthiness test to [`datum:is_null()`](#datumisnull). This function only has the possibility of returning `false` for lists that are inherently attached to a datum (`vars`, `contents`, `overlays`, `underlays`, `vis_contents`, and `vis_locs`).
Calls the print wrapper with the passed in arguments.
Raises an error if no print wrapper is set, as that means there is nothing to print with.
### list.entries
### \_state_id: integer
Returns a userdatum that allows you to access and modifiy the entries of the list by index. `list.entries.foo` is equivalent to `list:get("foo")`, while `list.entries.foo = bar` is equivalent to `list:set("foo", bar)`
The handle to the underlying luau state in the dreamluau binary.
---
## \_exec
## The dm table
The `_exec` module includes volatile fields related to the current execution context.
The `dm` table consists of the basic hooks into the DM language.
### \_next_yield_index: integer
### dm.state_id
When yielding a thread with `coroutine.yield`, it will be inserted into an internal table at the first open integer index.
This field corresponds to that first open integer index.
The address of the lua state in memory. This is a copy of the internal value used by auxlua to locate the lua state in a global hash map. `state_id` is a registry value that is indirectly obtained using the `dm` table's `__index` metamethod.
### \_limit: integer?
### dm.global_proc(proc, ...)
Calls the global proc `/proc/[proc]` with `...` as its arguments.
If set, the execution limit, rounded to the nearest millisecond.
### dm.world
A reference to DM's `world`, in the form of datum userdata. This reference is always valid, since `world` always exists.
### \_time: integer
Due to limitations inherent in the wrapper functions used on tgstation, `world:set_var` and `world:call_proc` will raise an error.
The length of successive time luau code has been executed, including recursive calls to DM and back into luau, rounded to the nearest millisecond.
### dm.global_vars
A reference to DM's `global`, in the form of datum userdata. Subject to the same limitations as `dm.world`
## dm
### dm.usr
A weak reference to DM's `usr`. As a rule of thumb, this is a reference to the mob of the client who triggered the chain of procs leading to the execution of Lua code. The following is a list of what `usr` is for the most common ways of executing Lua code:
- For resumes and awakens, which are generally executed by the MC, `usr` is (most likely) null.
- `SS13.wait` queues a resume, which gets executed by the MC. Therefore, `usr` is null after `SS13.wait` finishes.
- For chunk loads, `usr` is generally the current mob of the admin that loaded that chunk.
- For function calls done from the Lua editor, `usr` is the current mob of the admin calling the function.
- `SS13.register_signal` creates a `/datum/callback` that gets executed by the `SEND_SIGNAL` macro for the corresponding signal. As such, `usr` is the mob that triggered the chain of procs leading to the invocation of `SEND_SIGNAL`.
The `dm` module includes fields and functions for basic interaction with DM.
---
### world: userdata
## Execution Limit
A static reference to the DM `world`.
In order to prevent freezing the server with infinite loops, auxlua enforces an execution limit, defaulting to 100ms. When a single lua state has been executing for longer than this limit, it will eventually stop and produce an error.
### global_vars: userdata
To avoid exceeding the execution limit, call `sleep()` or `coroutine.yield()` before the execution limit is reached.
A static reference that functions like the DM keyword `global`. This can be indexed to read/write global vars.
### over_exec_usage(fraction = 0.95)
### global_procs: table
This function returns whether the current run of the Lua VM has executed for longer than the specified fraction of the execution limit. You can use this function to branch to a call to `sleep()` or `coroutine.yield()` to maximize the amount of work done in a single run of the Lua VM. If nil, `fraction` will default to 0.95, otherwise, it will be clamped to the range \[0, 1\].
A table that can be indexed by string for functions that wrap global procs.
---
Due to BYOND limitations, attempting to index an invalid proc returns a function logically equivalent to a no-op.
## Task management
The Lua Scripting subsystem manages the execution of tasks for each Lua state. A single fire of the subsystem behaves as follows:
- All tasks that slept since the last fire are resumed in the order they slept.
- For each queued resume, the corresponding task is resumed.
### get_var(object: userdata, var: string): function
### sleep()
Yields the current thread, scheduling it to be resumed during the next fire of SSlua. Use this function to prevent your Lua code from exceeding its allowed execution duration. Under the hood, `sleep` performs the following:
Reads the var `var` on `object`. This function can be used to get vars that are shadowed by procs declared with the same name.
- Sets the [`sleep_flag`](#sleep_flag)
- Calls `coroutine.yield()`
- Clears the sleep flag when determining whether the task slept or yielded
- Ignores the return values of `coroutine.yield()` once resumed
### new(path: string, ...any): userdata
---
Creates an instance of the object specified by `path`, with `...` as its arguments.
If the "new" wrapper is set, that proc will be called instead, with the arguments `(path, {...})`.
## The SS13 package
### is_valid_ref(ref: any): boolean
Returns true if the value passed in corresponds to a valid reference-counted DM object.
### usr: userdata?
Corresponds to the DM var `usr`.
## list
The `list` module contains wrappers for the builtin list procs, along with several other utility functions for working with lists.
### add(list: userdata, ...any): ()
Logically equivalent to the DM statement `list.Add(...)`.
### copy(list: userdata, start?: integer, end?: integer): userdata
Logically equivalent to the DM statement `list.Copy(start, end)`.
### cut(list: userdata, start?: integer, end?: integer): userdata
Logically equivalent to the DM statement `list.Cut(start, end)`.
### find(list: userdata, item: any, start?: integer, end?: integer): integer
Logically equivalent to the DM statement `list.Find(item, start, end)`.
### insert(list: userdata, index: integer, ...any): integer
Logically equivalent to the DM statement `list.Insert(item, ...)`.
### join(list: userdata, glue: string, start?: integer, end?: integer): string
Logically equivalent to the statement `list.Join(glue, start, end)`.
### remove(list: userdata, ...any): integer
Logically equivalent to the DM statement `list.Remove(...)`.
### remove_all(list: userdata, ...any): integer
Logically equivalent to the DM statement `list.RemoveAll(...)`.
### splice(list: userdata, start?: integer, end?: integer, ...any): ()
Logically equivalent to the DM statement `list.Splice(start, end, ...)`.
### swap(list: userdata, index_1: integer, index_2: integer): ()
Logically equivalent to the DM statement `list.Swap(index_1, index_2)`.
### to_table(list: userdata, deep?: boolean): table
Creates a table that is a copy of `list`. If `deep` is true, `to_table` will be called on any lists inside that list.
### from_table(table: table): userdata
Creates a list that is a copy of `table`. This is not strictly necessary, as tables are automatically converted to lists when passed back into DM, using the same internal logic as `from_table`.
### filter(list: userdata, path: string): userdata
Returns a copy of `list`, containing only elements that are objects descended from `path`.
## pointer
The `pointer` module contains utility functions for interacting with pointers.
Keep in mind that passing DM pointers into luau and manipulating them in this way can bypass wrapper procs.
### read(pointer: userdata): any
Gets the underlying data the pointer references.
### write(pointer: userdata, value: any): ()
Writes `value` to the underlying data the pointer references.
### unwrap(possible_pointer: any): any
If `possible_pointer` is a pointer, reads it. Otherwise, it is returned as-is.
# The SS13 package
The `SS13` package contains various helper functions that use code specific to tgstation.
### SS13.state
## SS13.state
A reference to the state datum (`/datum/lua_state`) handling this Lua state.
### SS13.get_runner_ckey()
## SS13.get_runner_ckey()
The ckey of the user who ran the lua script in the current context. Can be unreliable if accessed after sleeping.
### SS13.get_runner_client()
## SS13.get_runner_client()
Returns the client of the user who ran the lua script in the current context. Can be unreliable if accessed after sleeping.
### SS13.global_proc
## SS13.global_proc
A wrapper for the magic string used to tell `WrapAdminProcCall` to call a global proc.
For instance, `/datum/callback` must be instantiated with `SS13.global_proc` as its first argument to specify that it will be invoking a global proc.
The following example declares a callback which will execute the global proc `to_chat`:
@@ -152,25 +227,18 @@ The following example declares a callback which will execute the global proc `to
local callback = SS13.new("/datum/callback", SS13.global_proc, "to_chat", dm.world, "Hello World")
```
### SS13.istype(thing, type)
## SS13.istype(thing, type)
Equivalent to the DM statement `istype(thing, text2path(type))`.
### SS13.new(type, ...)
Instantiates a datum of type `type` with `...` as the arguments passed to `/proc/_new`
The following example spawns a singularity at the caller's current turf:
```lua
SS13.new("/obj/singularity", dm.global_proc("_get_step", dm.usr, 0))
```
## SS13.new(type, ...)
An alias for `dm.new`
### SS13.new_untracked(type, ...)
Works exactly like SS13.new but it does not store the value to the lua state's `references` list variable. This means that the variable could end up deleted if nothing holds a reference to it.
### SS13.is_valid(datum)
## SS13.is_valid(datum)
Can be used to determine if the datum passed is not nil, not undefined and not qdel'd all in one. A helper function that allows you to check the validity from only one function.
Example usage:
```lua
local datum = SS13.new("/datum")
dm.global_proc("qdel", datum)
dm.global_procs.qdel(datum)
print(SS13.is_valid(datum)) -- false
local null = nil
@@ -180,13 +248,13 @@ local datum = SS13.new("/datum")
print(SS13.is_valid(datum)) -- true
```
### SS13.type(string)
Converts a string into a type. Equivalent to doing `dm.global_proc("_text2path", "/path/to/type")`
## SS13.type(string)
Converts a string into a typepath. Equivalent to doing `dm.global_proc("_text2path", "/path/to/type")`
### SS13.qdel(datum)
## SS13.qdel(datum)
Deletes a datum. You shouldn't try to reference it after calling this function. Equivalent to doing `dm.global_proc("qdel", datum)`
### SS13.await(thing_to_call, proc_to_call, ...)
## SS13.await(thing_to_call, proc_to_call, ...)
Calls `proc_to_call` on `thing_to_call`, with `...` as its arguments, and sleeps until that proc returns.
Returns two return values - the first is the return value of the proc, and the second is the message of any runtime exception thrown by the called proc.
The following example calls and awaits the return of `poll_ghost_candidates`:
@@ -194,59 +262,59 @@ The following example calls and awaits the return of `poll_ghost_candidates`:
local ghosts, runtime = SS13.await(SS13.global_proc, "poll_ghost_candidates", "Would you like to be considered for something?")
```
### SS13.wait(time, timer)
## SS13.wait(time, timer)
Waits for a number of **seconds** specified with the `time` argument. You can optionally specify a timer subsystem using the `timer` argument.
Internally, this function creates a timer that will resume the current task after `time` seconds, then yields the current task by calling `coroutine.yield` with no arguments and ignores the return values. If the task is prematurely resumed, the timer will be safely deleted.
### SS13.register_signal(datum, signal, func, make_easy_clear_function)
## SS13.register_signal(datum, signal, func)
Registers the Lua function `func` as a handler for `signal` on `datum`.
Like with signal handlers written in DM, Lua signal handlers should not sleep (either by calling `sleep` or `coroutine.yield`).
If `make_easy_clear_function` is truthy, a member function taking no arguments will be created in the `SS13` table to easily unregister the signal handler.
This function returns the `/datum/callback` created to call `func` from DM.
This function returns whether the signal registration was successful.
The following example defines a function which will register a signal that makes `target` make a honking sound any time it moves:
```lua
function honk(target)
SS13.register_signal(target, "movable_moved", function(source)
dm.global_proc("playsound", target, "sound/items/bikehorn.ogg", 100, true)
dm.global_procs.playsound(target, "sound/items/bikehorn.ogg", 100, true)
end)
end
```
### SS13.unregister_signal(datum, signal, callback)
Unregister a signal previously registered using `SS13.register_signal`. `callback` should be a `datum/callback` previously returned by `SS13.register_signal`. If `callback` is not specified, **ALL** signal handlers registered on `datum` for `signal` will be unregistered.
NOTE: if `func` is an anonymous function declared inside the call to `SS13.register_signal`, it cannot be referenced in order to unregister that signal with `SS13.unregister_signal`
### SS13.set_timeout(time, func)
## SS13.unregister_signal(datum, signal, func)
Unregister a signal previously registered using `SS13.register_signal`. `func` must be a function for which a handler for the specified signal has already been registered. If `func` is `nil`, all handlers for that signal will be unregistered.
## SS13.set_timeout(time, func)
Creates a timer which will execute `func` after `time` **seconds**. `func` should not expect to be passed any arguments, as it will not be passed any. Unlike `SS13.wait`, `SS13.set_timeout` does not yield or sleep the current task, making it suitable for use in signal handlers for `SS13.register_signal`
The following example will output a message to chat after 5 seconds:
```lua
SS13.set_timeout(5, function()
dm.global_proc("to_chat", dm.world, "Hello World!")
dm.global_procs.to_chat(dm.world, "Hello World!")
end)
```
### SS13.start_loop(time, amount, func)
## SS13.start_loop(time, amount, func)
Creates a timer which will execute `func` after `time` **seconds**. `func` should not expect to be passed any arguments, as it will not be passed any. Works exactly the same as `SS13.set_timeout` except it will loop the timer `amount` times. If `amount` is set to -1, it will loop indefinitely. Returns a number value, which represents the timer's id. Can be stopped with `SS13.end_loop`
Returns a number, the timer id, which is needed to stop indefinite timers.
The following example will output a message to chat every 5 seconds, repeating 10 times:
```lua
SS13.start_loop(5, 10, function()
dm.global_proc("to_chat", dm.world, "Hello World!")
dm.global_procs.to_chat(dm.world, "Hello World!")
end)
```
The following example will output a message to chat every 5 seconds, until `SS13.end_loop(timerid)` is called:
```lua
local timerid = SS13.start_loop(5, -1, function()
dm.global_proc("to_chat", dm.world, "Hello World!")
dm.global_proc.to_chat(dm.world, "Hello World!")
end)
```
### SS13.end_loop(id)
## SS13.end_loop(id)
Prematurely ends a loop that hasn't ended yet, created with `SS13.start_loop`. Silently fails if there is no started loop with the specified id.
The following example will output a message to chat every 5 seconds and delete it after it has repeated 20 times:
```lua
@@ -254,7 +322,7 @@ local repeated_amount = 0
-- timerid won't be in the looping function's scope if declared before the function is declared.
local timerid
timerid = SS13.start_loop(5, -1, function()
dm.global_proc("to_chat", dm.world, "Hello World!")
dm.global_procs.to_chat(dm.world, "Hello World!")
repeated_amount += 1
if repeated_amount >= 20 then
SS13.end_loop(timerid)
@@ -262,35 +330,6 @@ timerid = SS13.start_loop(5, -1, function()
end)
```
### SS13.stop_all_loops()
## SS13.stop_all_loops()
Stops all current running loops that haven't ended yet.
Useful in case you accidentally left a indefinite loop running without storing the id anywhere.
### SS13.stop_tracking(datum)
Stops tracking a datum that was created via `SS13.new` so that it can be garbage collected and deleted without having to qdel. Should be used for things like callbacks and other such datums where the reference to the variable is no longer needed.
---
## Internal globals
Auxlua defines several registry values for each state. Note that there is no way to access registry values from lua code.
### sleep_flag
This flag is used to designate that a yielding task should be put in the sleep queue instead of the yield table. Once auxlua determines that a task should sleep, `sleep_flag` is cleared.
### sleep_queue
A sequence of threads, each corresponding to a task that has slept. When calling `/proc/__lua_awaken`, auxlua will dequeue the first thread from the sequence and resume it.
### yield_table
A table of threads, each corresponding to a coroutine that has yielded. When calling `/proc/__lua_resume`, auxlua will look for a thread at the index specified in the `index` argument, and resume it with the arguments specified in the `arguments` argument.
### task_info
A table of key-value-pairs, where the keys are threads, and the values are tables consisting of the following fields:
- name: A string containing the name of the task
- status: A string, either "sleep" or "yield"
- index: The task's index in `sleep_queue` or `yield_table`
+10 -5
View File
@@ -1,3 +1,12 @@
/proc/wrap_lua_get_var(datum/thing, var_name)
SHOULD_NOT_SLEEP(TRUE)
if(thing == world)
return world.vars[var_name]
if(ref(thing) == "\[0xe000001\]") //This weird fucking thing is like global.vars, but it's not a list and vars is not a valid index for it and I really don't fucking know.
return global.vars[var_name]
if(thing.can_vv_get(var_name))
return thing.vars[var_name]
/proc/wrap_lua_set_var(datum/thing_to_set, var_name, value)
SHOULD_NOT_SLEEP(TRUE)
thing_to_set.vv_edit_var(var_name, value)
@@ -11,8 +20,6 @@
ret = WrapAdminProcCall(thing_to_call, proc_name, arguments)
else
ret = HandleUserlessProcCall("lua", thing_to_call, proc_name, arguments)
if(isdatum(ret))
SSlua.gc_guard += ret
return ret
/proc/wrap_lua_global_proc_call(proc_name, list/arguments)
@@ -24,8 +31,6 @@
ret = WrapAdminProcCall(GLOBAL_PROC, proc_name, arguments)
else
ret = HandleUserlessProcCall("lua", GLOBAL_PROC, proc_name, arguments)
if(isdatum(ret))
SSlua.gc_guard += ret
return ret
/proc/wrap_lua_print(state_id, list/arguments)
@@ -38,6 +43,6 @@
if(!target_state)
return
var/print_message = jointext(arguments, "\t")
var/result = list("status" = "print", "param" = print_message)
var/result = list("status" = "print", "message" = print_message)
INVOKE_ASYNC(target_state, TYPE_PROC_REF(/datum/lua_state, log_result), result, TRUE)
log_lua("[target_state]: [print_message]")
+8 -8
View File
@@ -3,27 +3,27 @@
#define PROMISE_REJECTED 2
/**
* Auxtools hooks act as "set waitfor = 0" procs. This means that whenever
* a proc directly called from auxtools sleeps, the hook returns with whatever
* Byondapi hooks act as "set waitfor = 0" procs. This means that whenever
* a proc directly called from an external library sleeps, the hook returns with whatever
* the called proc had as its return value at the moment it slept. This may not
* be desired behavior, so this datum exists to wrap these procs.
*
* Some procs that don't sleep could take longer than the execution limit would
* allow for. We can wrap these in a promise as well.
*/
/datum/auxtools_promise
/datum/promise
var/datum/callback/callback
var/return_value
var/runtime_message
var/status = PROMISE_PENDING
/datum/auxtools_promise/New(...)
/datum/promise/New(...)
if(!usr)
usr = GLOB.lua_usr
callback = CALLBACK(arglist(args))
perform()
INVOKE_ASYNC(src, PROC_REF(perform))
/datum/auxtools_promise/proc/perform()
set waitfor = 0
sleep() //In case we have to call a super-expensive non-sleeping proc (like getFlatIcon)
/datum/promise/proc/perform()
try
return_value = callback.Invoke()
status = PROMISE_RESOLVED
+84 -37
View File
@@ -16,6 +16,12 @@
/// If set, we will force the editor to look at this chunk
var/force_view_chunk
/// If set, we will force the script input to be this
var/force_input
/// If set, the latest code execution performed from the editor raised an error, and this is the message from that error
var/last_error
/datum/lua_editor/New(state, _quick_log_index)
. = ..()
if(state)
@@ -37,37 +43,52 @@
/datum/lua_editor/ui_state(mob/user)
return GLOB.debug_state
/datum/lua_editor/ui_static_data(mob/user)
var/list/data = list()
data["documentation"] = file2text('code/modules/admin/verbs/lua/README.md')
data["auxtools_enabled"] = CONFIG_GET(flag/auxtools_enabled)
data["ss_lua_init"] = SSlua.initialized
return data
/datum/lua_editor/ui_data(mob/user)
var/list/data = list()
if(!CONFIG_GET(flag/auxtools_enabled) || !SSlua.initialized)
data["ss_lua_init"] = SSlua.initialized
if(!SSlua.initialized)
return data
data["noStateYet"] = !current_state
data["showGlobalTable"] = show_global_table
if(current_state)
if(current_state.log)
data["stateLog"] = kvpify_list(refify_list(current_state.log.Copy((page*50)+1, min((page+1)*50+1, current_state.log.len+1))))
var/list/logs = current_state.log.Copy((page*50)+1, min((page+1)*50+1, current_state.log.len+1))
for(var/i in 1 to logs.len)
var/list/log = logs[i]
log = log.Copy()
if(log["return_values"])
log["return_values"] = kvpify_list(prepare_lua_editor_list(deep_copy_without_cycles(log["return_values"])))
logs[i] = log
data["stateLog"] = logs
data["page"] = page
data["pageCount"] = CEILING(current_state.log.len/50, 1)
data["tasks"] = current_state.get_tasks()
if(show_global_table)
current_state.get_globals()
data["globals"] = kvpify_list(refify_list(current_state.globals))
data["states"] = SSlua.states
data["callArguments"] = kvpify_list(refify_list(arguments))
var/list/values = current_state.globals["values"]
values = deep_copy_without_cycles(values)
values = prepare_lua_editor_list(values)
values = kvpify_list(values)
var/list/variants = current_state.globals["variants"]
data["globals"] = list("values" = values, "variants" = variants)
if(last_error)
data["lastError"] = last_error
last_error = null
data["supressRuntimes"] = current_state.supress_runtimes
data["states"] = list()
for(var/datum/lua_state/state as anything in SSlua.states)
data["states"] += state.display_name
data["callArguments"] = kvpify_list(prepare_lua_editor_list(deep_copy_without_cycles(arguments)))
if(force_modal)
data["forceModal"] = force_modal
force_modal = null
if(force_view_chunk)
data["forceViewChunk"] = force_view_chunk
force_view_chunk = null
if(force_input)
data["force_input"] = force_input
force_input = null
return data
/datum/lua_editor/proc/traverse_list(list/path, list/root, traversal_depth_offset = 0)
@@ -99,14 +120,24 @@
else
return root
/datum/lua_editor/ui_act(action, list/params)
/datum/lua_editor/proc/run_code(code)
var/ckey = usr.ckey
current_state.ckey_last_runner = ckey
var/result = current_state.load_script(code)
var/index_with_result = current_state.log_result(result)
if(result["status"] == "error")
last_error = result["message"]
message_admins("[key_name(usr)] executed [length(code)] bytes of lua code. [ADMIN_LUAVIEW_CHUNK(current_state, index_with_result)]")
/datum/lua_editor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
if(!check_rights_for(usr.client, R_DEBUG))
var/mob/user = ui.user
if(!check_rights_for(user.client, R_DEBUG))
return
if(action == "runCodeFile")
params["code"] = file2text(input(usr, "Input File") as null|file)
params["code"] = file2text(input(user, "Input File") as null|file)
if(isnull(params["code"]))
return
action = "runCode"
@@ -116,6 +147,8 @@
if(!length(state_name))
return TRUE
var/datum/lua_state/new_state = new(state_name)
if(QDELETED(new_state))
return
SSlua.states += new_state
LAZYREMOVEASSOC(SSlua.editors, text_ref(current_state), src)
current_state = new_state
@@ -130,11 +163,14 @@
page = 0
return TRUE
if("runCode")
var/code = params["code"]
current_state.ckey_last_runner = usr.ckey
var/result = current_state.load_script(code)
var/index_with_result = current_state.log_result(result)
message_admins("[key_name(usr)] executed [length(code)] bytes of lua code. [ADMIN_LUAVIEW_CHUNK(current_state, index_with_result)]")
run_code(params["code"])
return TRUE
if("runFile")
var/code_file = input(user, "Select a script to run.", "Lua") as file|null
if(!code_file)
return TRUE
var/code = file2text(code_file)
run_code(code)
return TRUE
if("moveArgUp")
var/list/path = params["path"]
@@ -158,9 +194,9 @@
var/list/path = params["path"]
var/list/target_list = traverse_list(path, arguments)
if(target_list != arguments)
usr?.client?.mod_list_add(target_list, null, "a lua editor", "arguments")
user?.client?.mod_list_add(target_list, null, "a lua editor", "arguments")
else
var/list/vv_val = usr?.client?.vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT))
var/list/vv_val = user?.client?.vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT))
var/class = vv_val["class"]
if(!class)
return
@@ -168,53 +204,58 @@
return TRUE
if("callFunction")
var/list/recursive_indices = params["indices"]
var/list/current_list = kvpify_list(current_state.globals)
var/list/current_list = kvpify_list(current_state.globals["values"])
var/list/current_variants = current_state.globals["variants"]
var/function = list()
while(LAZYLEN(recursive_indices))
var/index = popleft(recursive_indices)
var/list/element = current_list[index]
var/key = element["key"]
var/value = element["value"]
if(!(istext(key) || isnum(key)))
to_chat(usr, span_warning("invalid key \[[key]] for function call (expected text or num)"))
var/list/variant_pair = current_variants[index]
var/key_variant = variant_pair["key"]
if(key_variant == "function" || key_variant == "thread" || key_variant == "userdata" || key_variant == "error_as_value")
to_chat(user, span_warning("invalid table key \[[key]] for function call (expected text, num, path, list, or ref, got [key_variant])"))
return
function += key
if(islist(value))
current_list = value
current_variants = variant_pair["value"]
else
var/regex/function_regex = regex("^function: 0x\[0-9a-fA-F]+$")
if(function_regex.Find(value))
break
to_chat(usr, span_warning("invalid path element \[[value]] for function call (expected list or text matching [function_regex])"))
return
if(variant_pair["value"] != "function")
to_chat(user, span_warning("invalid value \[[value]] for function call (expected list or function)"))
return
var/result = current_state.call_function(arglist(list(function) + arguments))
current_state.log_result(result)
if(result["status"] == "error")
last_error = result["message"]
arguments.Cut()
return TRUE
return
if("resumeTask")
var/task_index = params["index"]
SSlua.queue_resume(current_state, task_index, arguments)
arguments.Cut()
return TRUE
if("killTask")
var/task_info = params["info"]
SSlua.kill_task(current_state, task_info)
var/is_sleep = params["is_sleep"]
var/index = params["index"]
SSlua.kill_task(current_state, is_sleep, index)
return TRUE
if("vvReturnValue")
var/log_entry_index = params["entryIndex"]
var/list/log_entry = current_state.log[log_entry_index]
var/thing_to_debug = traverse_list(params["tableIndices"], log_entry["param"])
var/thing_to_debug = traverse_list(params["indices"], log_entry["return_values"])
if(isweakref(thing_to_debug))
var/datum/weakref/ref = thing_to_debug
thing_to_debug = ref.resolve()
INVOKE_ASYNC(usr.client, TYPE_PROC_REF(/client, debug_variables), thing_to_debug)
INVOKE_ASYNC(user.client, TYPE_PROC_REF(/client, debug_variables), thing_to_debug)
return FALSE
if("vvGlobal")
var/thing_to_debug = traverse_list(params["indices"], current_state.globals)
var/thing_to_debug = traverse_list(params["indices"], current_state.globals["values"])
if(isweakref(thing_to_debug))
var/datum/weakref/ref = thing_to_debug
thing_to_debug = ref.resolve()
INVOKE_ASYNC(usr.client, TYPE_PROC_REF(/client, debug_variables), thing_to_debug)
INVOKE_ASYNC(user.client, TYPE_PROC_REF(/client, debug_variables), thing_to_debug)
return FALSE
if("clearArgs")
arguments.Cut()
@@ -222,12 +263,18 @@
if("toggleShowGlobalTable")
show_global_table = !show_global_table
return TRUE
if("toggleSupressRuntimes")
current_state.supress_runtimes = !current_state.supress_runtimes
return TRUE
if("nextPage")
page = min(page+1, CEILING(current_state.log.len/50, 1)-1)
return TRUE
if("previousPage")
page = max(page-1, 0)
return TRUE
if("nukeLog")
current_state.log.Cut()
return TRUE
/datum/lua_editor/ui_close(mob/user)
. = ..()
+110 -70
View File
@@ -1,15 +1,15 @@
#define MAX_LOG_REPEAT_LOOKBACK 5
GLOBAL_VAR_INIT(IsLuaCall, FALSE)
GLOBAL_PROTECT(IsLuaCall)
GLOBAL_DATUM(lua_usr, /mob)
GLOBAL_PROTECT(lua_usr)
/datum/lua_state
var/name
GLOBAL_LIST_EMPTY_TYPED(lua_state_stack, /datum/weakref)
GLOBAL_PROTECT(lua_state_stack)
/// The internal ID of the lua state stored in auxlua's global map
/datum/lua_state
var/display_name
/// The internal ID of the lua state stored in dreamluau's state list
var/internal_id
/// A log of every return, yield, and error for each chunk execution and function call
@@ -18,15 +18,15 @@ GLOBAL_PROTECT(lua_usr)
/// A list of all the variables in the state's environment
var/list/globals = list()
/// A list in which to store datums and lists instantiated in lua, ensuring that they don't get garbage collected
var/list/references = list()
/// Ckey of the last user who ran a script on this lua state.
var/ckey_last_runner = ""
/// Whether the timer.lua script has been included into this lua context state.
var/timer_enabled = FALSE
/// Whether to supress logging BYOND runtimes for this state.
var/supress_runtimes = FALSE
/// Callbacks that need to be ran on next tick
var/list/functions_to_execute = list()
@@ -39,55 +39,70 @@ GLOBAL_PROTECT(lua_usr)
if(SSlua.initialized != TRUE)
qdel(src)
return
name = _name
internal_id = __lua_new_state()
display_name = _name
internal_id = DREAMLUAU_NEW_STATE()
if(isnull(internal_id))
stack_trace("dreamluau is not loaded")
qdel(src)
else if(!isnum(internal_id))
stack_trace(internal_id)
qdel(src)
/datum/lua_state/proc/check_if_slept(result)
if(result["status"] == "sleeping")
if(result["status"] == "sleep")
SSlua.sleeps += src
/datum/lua_state/proc/log_result(result, verbose = TRUE)
if(!islist(result))
return
if(!verbose && result["status"] != "errored" && result["status"] != "bad return" \
&& !(result["name"] == "input" && (result["status"] == "finished" || length(result["param"]))))
var/status = result["status"]
if(!verbose && status != "error" && status != "panic" && status != "runtime" && !(result["name"] == "input" && (status == "finished" || length(result["return_values"]))))
return
if(status == "runtime" && supress_runtimes)
return
var/append_to_log = TRUE
var/index_of_log
if(log.len)
for(var/index in log.len to max(log.len - MAX_LOG_REPEAT_LOOKBACK, 1) step -1)
var/list/entry = log[index]
if(entry["status"] == result["status"] \
&& entry["chunk"] == result["chunk"] \
&& entry["name"] == result["name"] \
&& ((entry["param"] == result["param"]) || deep_compare_list(entry["param"], result["param"])))
if(!entry["repeats"])
entry["repeats"] = 0
index_of_log = index
entry["repeats"]++
append_to_log = FALSE
break
if(!compare_lua_logs(entry, result))
continue
if(!entry["repeats"])
entry["repeats"] = 0
index_of_log = index
entry["repeats"]++
append_to_log = FALSE
break
if(append_to_log)
if(islist(result["param"]))
result["param"] = weakrefify_list(encode_text_and_nulls(result["param"]))
if(islist(result["return_values"]))
add_lua_return_value_variants(result["return_values"], result["variants"])
result["return_values"] = weakrefify_list(result["return_values"])
log += list(result)
index_of_log = log.len
INVOKE_ASYNC(src, TYPE_PROC_REF(/datum/lua_state, update_editors))
return index_of_log
/datum/lua_state/proc/parse_error(message, name)
if(copytext(message, 1, 7) == "PANIC:")
return list("status" = "panic", "message" = copytext(message, 7), "name" = name)
else
return list("status" = "error", "message" = message, "name" = name)
/datum/lua_state/proc/load_script(script)
GLOB.IsLuaCall = TRUE
var/tmp_usr = GLOB.lua_usr
GLOB.lua_usr = usr
var/result = __lua_load(internal_id, script)
GLOB.IsLuaCall = FALSE
DREAMLUAU_SET_USR
GLOB.lua_state_stack += WEAKREF(src)
var/result = DREAMLUAU_LOAD(internal_id, script, "input")
SSlua.needs_gc_cycle |= src
pop(GLOB.lua_state_stack)
GLOB.lua_usr = tmp_usr
// Internal errors unrelated to the code being executed are returned as text rather than lists
if(isnull(result))
result = list("status" = "errored", "param" = "__lua_load returned null (it may have runtimed - check the runtime logs)", "name" = "input")
result = list("status" = "error", "message" = "load returned null (it may have runtimed - check the runtime logs)", "name" = "input")
if(istext(result))
result = list("status" = "errored", "param" = result, "name" = "input")
result = parse_error(result, "input")
result["chunk"] = script
check_if_slept(result)
@@ -109,67 +124,106 @@ GLOBAL_PROTECT(lua_usr)
if(islist(function))
var/list/new_function_path = list()
for(var/path_element in function)
new_function_path += path_element
if(isweakref(path_element))
var/datum/weakref/weak_ref = path_element
var/resolved = weak_ref.hard_resolve()
if(!resolved)
return list("status" = "error", "message" = "Weakref in function path ([weak_ref] [text_ref(weak_ref)]) resolved to null.", "name" = jointext(function, "."))
new_function_path += resolved
else
new_function_path += path_element
function = new_function_path
else
function = list(function)
var/tmp_usr = GLOB.lua_usr
GLOB.lua_usr = usr
GLOB.IsLuaCall = TRUE
var/result = __lua_call(internal_id, function, call_args)
GLOB.IsLuaCall = FALSE
DREAMLUAU_SET_USR
GLOB.lua_state_stack += WEAKREF(src)
var/result = DREAMLUAU_CALL_FUNCTION(internal_id, function, call_args)
SSlua.needs_gc_cycle |= src
pop(GLOB.lua_state_stack)
GLOB.lua_usr = tmp_usr
if(isnull(result))
result = list("status" = "errored", "param" = "__lua_call returned null (it may have runtimed - check the runtime logs)", "name" = islist(function) ? jointext(function, ".") : function)
result = list("status" = "error", "message" = "call_function returned null (it may have runtimed - check the runtime logs)", "name" = jointext(function, "."))
if(istext(result))
result = list("status" = "errored", "param" = result, "name" = islist(function) ? jointext(function, ".") : function)
result = parse_error(result, jointext(function, "."))
check_if_slept(result)
return result
/datum/lua_state/proc/call_function_return_first(function, ...)
SHOULD_NOT_SLEEP(TRUE) // This function is meant to be used for signal handlers.
var/list/result = call_function(arglist(args))
log_result(result, verbose = FALSE)
INVOKE_ASYNC(src, PROC_REF(log_result), deep_copy_list(result), /*verbose = */FALSE)
if(length(result))
if(islist(result["param"]) && length(result["param"]))
return result["param"][1]
if(islist(result["return_values"]) && length(result["return_values"]))
var/return_value = result["return_values"][1]
var/variant = (islist(result["variants"]) && length(result["variants"])) && result["variants"][1]
if(islist(return_value) && islist(variant))
remove_non_dm_variants(return_value, variant)
return return_value
/datum/lua_state/proc/awaken()
GLOB.IsLuaCall = TRUE
var/result = __lua_awaken(internal_id)
GLOB.IsLuaCall = FALSE
DREAMLUAU_SET_USR
GLOB.lua_state_stack += WEAKREF(src)
var/result = DREAMLUAU_AWAKEN(internal_id)
SSlua.needs_gc_cycle |= src
pop(GLOB.lua_state_stack)
if(isnull(result))
result = list("status" = "errored", "param" = "__lua_awaken returned null (it may have runtimed - check the runtime logs)", "name" = "An attempted awaken")
result = list("status" = "error", "message" = "awaken returned null (it may have runtimed - check the runtime logs)", "name" = "An attempted awaken")
if(istext(result))
result = list("status" = "errored", "param" = result, "name" = "An attempted awaken")
result = parse_error(result, "An attempted awaken")
check_if_slept(result)
return result
/// Prefer calling SSlua.queue_resume over directly calling this
/datum/lua_state/proc/resume(index, ...)
var/call_args = length(args) > 1 ? args.Copy(2) : list()
var/msg = "[key_name(usr)] resumed a lua coroutine with arguments: [english_list(call_args)]"
log_lua(msg)
GLOB.IsLuaCall = TRUE
var/result = __lua_resume(internal_id, index, call_args)
GLOB.IsLuaCall = FALSE
DREAMLUAU_SET_USR
GLOB.lua_state_stack += WEAKREF(src)
var/result = DREAMLUAU_RESUME(internal_id, index, call_args)
SSlua.needs_gc_cycle |= src
pop(GLOB.lua_state_stack)
if(isnull(result))
result = list("status" = "errored", "param" = "__lua_resume returned null (it may have runtimed - check the runtime logs)", "name" = "An attempted resume")
result = list("status" = "error", "param" = "resume returned null (it may have runtimed - check the runtime logs)", "name" = "An attempted resume")
if(istext(result))
result = list("status" = "errored", "param" = result, "name" = "An attempted resume")
result = parse_error(result, "An attempted resumt")
check_if_slept(result)
return result
/datum/lua_state/proc/get_globals()
globals = weakrefify_list(encode_text_and_nulls(__lua_get_globals(internal_id)))
var/result = DREAMLUAU_GET_GLOBALS(internal_id)
if(isnull(result))
CRASH("get_globals returned null")
if(istext(result))
CRASH(result)
var/list/new_globals = result
var/list/values = new_globals["values"]
var/list/variants = new_globals["variants"]
add_lua_editor_variants(values, variants)
globals = list("values" = weakrefify_list(values), "variants" = variants)
/datum/lua_state/proc/get_tasks()
return __lua_get_tasks(internal_id)
var/result = DREAMLUAU_LIST_THREADS(internal_id)
if(isnull(result))
CRASH("list_threads returned null")
if(istext(result))
CRASH(result)
return result
/datum/lua_state/proc/kill_task(task_info)
__lua_kill_task(internal_id, task_info)
/datum/lua_state/proc/kill_task(is_sleep, index)
var/result = is_sleep ? DREAMLUAU_KILL_SLEEPING_THREAD(internal_id, index) : DREAMLUAU_KILL_YIELDED_THREAD(internal_id, index)
SSlua.needs_gc_cycle |= src
return result
/datum/lua_state/proc/collect_garbage()
var/result = DREAMLUAU_COLLECT_GARBAGE(internal_id)
if(!isnull(result))
CRASH(result)
/datum/lua_state/proc/update_editors()
var/list/editor_list = LAZYACCESS(SSlua.editors, text_ref(src))
@@ -177,18 +231,4 @@ GLOBAL_PROTECT(lua_usr)
for(var/datum/lua_editor/editor as anything in editor_list)
SStgui.update_uis(editor)
/// Called by lua scripts when they add an atom to var/list/references so that it gets cleared up on delete.
/datum/lua_state/proc/clear_on_delete(datum/to_clear)
RegisterSignal(to_clear, COMSIG_QDELETING, PROC_REF(on_delete))
/// Called by lua scripts when an atom they've added should soft delete and this state should stop tracking it.
/// Needs to unregister all signals.
/datum/lua_state/proc/let_soft_delete(datum/to_clear)
UnregisterSignal(to_clear, COMSIG_QDELETING, PROC_REF(on_delete))
references -= to_clear
/datum/lua_state/proc/on_delete(datum/to_clear)
SIGNAL_HANDLER
references -= to_clear
#undef MAX_LOG_REPEAT_LOOKBACK
+2 -2
View File
@@ -1,5 +1,5 @@
ADMIN_VERB(machine_upgrade, R_DEBUG, "Tweak Component Ratings", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, obj/machinery/machine in world)
var/new_rating = input(user, "Enter new rating:","Num") as num|null
ADMIN_VERB_AND_CONTEXT_MENU(machine_upgrade, R_DEBUG, "Tweak Component Ratings", ADMIN_VERB_NO_DESCRIPTION, ADMIN_CATEGORY_HIDDEN, obj/machinery/machine in world)
var/new_rating = tgui_input_number(user, "", "Enter new rating:")
if(new_rating && machine.component_parts)
for(var/obj/item/stock_parts/P in machine.component_parts)
P.rating = new_rating
+1 -1
View File
@@ -342,7 +342,7 @@ ADMIN_VERB(check_for_obstructed_atmospherics, R_DEBUG, "Check For Obstructed Atm
var/list/results = list()
results += "<h2><b>Anything that is considered to aesthetically obstruct an atmospherics machine (vent, scrubber, port) is listed below.</b> Please re-arrange to accomodate for this.</h2><br>"
results += "<h2><b>Anything that is considered to aesthetically obstruct an atmospherics machine (vent, scrubber, port) is listed below.</b> Please re-arrange to accommodate for this.</h2><br>"
// Ignore out stuff we see in normal and standard mapping that we don't care about (false alarms). Typically stuff that goes directionally off turfs or other undertile objects that we don't want to care about.
var/list/ignore_list = list(
+1 -1
View File
@@ -41,7 +41,7 @@ ADMIN_VERB(secrets, R_NONE, "Secrets", "Abuse harder than you ever have before w
#define THUNDERDOME_TEMPLATE_FILE "admin_thunderdome.dmm"
#define HIGHLANDER_DELAY_TEXT "40 seconds (crush the hope of a normal shift)"
/datum/secrets_menu/ui_act(action, params)
/datum/secrets_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
+9 -9
View File
@@ -96,8 +96,8 @@ ADMIN_VERB(start_now, R_SERVER, "Start Now", "Start the round RIGHT NOW.", ADMIN
if(SSticker.start_immediately)
SSticker.start_immediately = FALSE
SSticker.SetTimeLeft(1800)
to_chat(world, span_infoplain(span_bold("The game will start in 180 seconds.")))
SSticker.SetTimeLeft(3 MINUTES)
to_chat(world, span_big(span_notice("The game will start in 3 minutes.")))
SEND_SOUND(world, sound('sound/ai/default/attention.ogg'))
message_admins(span_adminnotice("[key_name_admin(user)] has cancelled immediate game start. Game will start in 3 minutes."))
log_admin("[key_name(user)] has cancelled immediate game start.")
@@ -151,8 +151,8 @@ ADMIN_VERB(toggle_ai, R_SERVER, "Toggle AI", "Toggle the ability to choose AI jo
if (alai)
to_chat(world, span_bold("The AI job is no longer chooseable."), confidential = TRUE)
else
to_chat(world, span_bold("The AI job is chooseable now."), confidential = TRUE)
log_admin("[key_name(usr)] toggled AI allowed.")
to_chat(world, "<B>The AI job is chooseable now.</B>", confidential = TRUE)
log_admin("[key_name(user)] toggled AI allowed.")
world.update_status()
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle AI", "[!alai ? "Disabled" : "Enabled"]")) // If you are copy-pasting this, ensure the 4th parameter is unique to the new proc!
@@ -203,8 +203,8 @@ ADMIN_VERB(delay, R_SERVER, "Delay Pre-Game", "Delay the game start.", ADMIN_CAT
SSticker.SetTimeLeft(newtime)
SSticker.start_immediately = FALSE
if(newtime < 0)
to_chat(world, span_infoplain(span_bold("The game start has been delayed.")), confidential = TRUE)
log_admin("[key_name(usr)] delayed the round start.")
to_chat(world, "<span class='infoplain'><b>The game start has been delayed.</b></span>", confidential = TRUE)
log_admin("[key_name(user)] delayed the round start.")
else
to_chat(world, span_infoplain(span_bold("The game will start in [DisplayTimeText(newtime)].")), confidential = TRUE)
SEND_SOUND(world, sound('sound/ai/default/attention.ogg'))
@@ -238,7 +238,7 @@ ADMIN_VERB(toggle_guests, R_SERVER, "Toggle Guests", "Toggle the ability for gue
if (new_guest_ban)
to_chat(world, span_bold("Guests may no longer enter the game."), confidential = TRUE)
else
to_chat(world, span_bold("Guests may now enter the game."), confidential = TRUE)
log_admin("[key_name(usr)] toggled guests game entering [!new_guest_ban ? "" : "dis"]allowed.")
message_admins(span_adminnotice("[key_name_admin(usr)] toggled guests game entering [!new_guest_ban ? "" : "dis"]allowed."))
to_chat(world, "<B>Guests may now enter the game.</B>", confidential = TRUE)
log_admin("[key_name(user)] toggled guests game entering [!new_guest_ban ? "" : "dis"]allowed.")
message_admins(span_adminnotice("[key_name_admin(user)] toggled guests game entering [!new_guest_ban ? "" : "dis"]allowed."))
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Guests", "[!new_guest_ban ? "Enabled" : "Disabled"]")) // If you are copy-pasting this, ensure the 4th parameter is unique to the new proc!
+2 -2
View File
@@ -3,7 +3,7 @@ ADMIN_VERB(shuttle_panel, R_ADMIN, "Shuttle Manipulator", "Opens the shuttle man
/obj/docking_port/mobile/proc/admin_fly_shuttle(mob/user)
var/list/options = list()
options += "-----COMPATABLE DOCKS:" //SKYRAT EDIT ADDITION
options += "-----COMPATIBLE DOCKS:" //SKYRAT EDIT ADDITION
for(var/port in SSshuttle.stationary_docking_ports)
if (istype(port, /obj/docking_port/stationary/transit))
continue // please don't do this
@@ -11,7 +11,7 @@ ADMIN_VERB(shuttle_panel, R_ADMIN, "Shuttle Manipulator", "Opens the shuttle man
if (canDock(S) == SHUTTLE_CAN_DOCK)
options[S.name || S.shuttle_id] = S
//SKYRAT EDIT ADDITION START
options += "-----INCOMPATABLE DOCKS:" //I WILL CRASH THIS SHIP WITH NO SURVIVORS!
options += "-----INCOMPATIBLE DOCKS:" //I WILL CRASH THIS SHIP WITH NO SURVIVORS!
for(var/port in SSshuttle.stationary_docking_ports)
if (istype(port, /obj/docking_port/stationary/transit))
continue // please don't do this
@@ -29,7 +29,7 @@
return "[.][item]</li>"
// This is split into a seperate proc mostly to make errors that happen not break things too much
// This is split into a separate proc mostly to make errors that happen not break things too much
/proc/_debug_variable_value(name, value, level, datum/owner, sanitize, display_flags)
if(isappearance(value))
value = get_vv_appearance(value)
@@ -64,11 +64,11 @@
var/datum/datum_value = value
return datum_value.debug_variable_value(name, level, owner, sanitize, display_flags)
if(islist(value) || (name in GLOB.vv_special_lists)) // Some special lists arent detectable as a list through istype
if(islist(value) || (name in GLOB.vv_special_lists)) // Some special lists aren't detectable as a list through istype
var/list/list_value = value
var/list/items = list()
// This is becuse some lists either dont count as lists or a locate on their ref will return null
// This is because some lists either don't count as lists or a locate on their ref will return null
var/link_vars = "Vars=[REF(value)]"
if(name in GLOB.vv_special_lists)
link_vars = "Vars=[REF(owner)];special_varname=[name]"
@@ -24,7 +24,7 @@
data["target_filter_data"] = target.filter_data
return data
/datum/filter_editor/ui_act(action, list/params)
/datum/filter_editor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
@@ -42,7 +42,7 @@
data["pixelated"] = target.appearance_flags & PIXEL_SCALE
return data
/datum/nobody_wants_to_learn_matrix_math/ui_act(action, list/params)
/datum/nobody_wants_to_learn_matrix_math/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
@@ -52,7 +52,7 @@
var/matrix_var_name = params["var_name"]
var/matrix_var_value = params["var_value"]
if(testing_matrix.vv_edit_var(matrix_var_name, matrix_var_value) == FALSE)
to_chat(src, "Your edit was rejected by the object. This is a bug with the matrix tester, not your fault, so report it on github.", confidential = TRUE)
to_chat(src, "Your edit was rejected by the object. This is a bug with the matrix tester, not your fault, so report it on GitHub.", confidential = TRUE)
return
set_transform()
if("scale")