mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-23 04:51:32 +01:00
Updated SSgarbage (#18173)
* pain * sadfas * sdfa * sdfasf * sfa * sdf * might the lord have mercy on our soul * i cri everidai * adeste fideles * sdf * where will this lead, what's coming next, from your inventions * dear lord * gjvhk * i cri everidai * fsgf * sdfa * sdaf * hiuhi
This commit is contained in:
+6
-2
@@ -22,6 +22,7 @@
|
||||
#include "code\__DEFINES\_common.dm"
|
||||
#include "code\__DEFINES\_compile_helpers.dm"
|
||||
#include "code\__DEFINES\_flags.dm"
|
||||
#include "code\__DEFINES\_helpers.dm"
|
||||
#include "code\__DEFINES\_layers.dm"
|
||||
#include "code\__DEFINES\_macros.dm"
|
||||
#include "code\__DEFINES\_protect.dm"
|
||||
@@ -73,7 +74,6 @@
|
||||
#include "code\__DEFINES\lists.dm"
|
||||
#include "code\__DEFINES\logging.dm"
|
||||
#include "code\__DEFINES\machinery.dm"
|
||||
#include "code\__DEFINES\manual_unit_testing.dm"
|
||||
#include "code\__DEFINES\materials.dm"
|
||||
#include "code\__DEFINES\math_physics.dm"
|
||||
#include "code\__DEFINES\MC.dm"
|
||||
@@ -133,6 +133,7 @@
|
||||
#include "code\__DEFINES\dcs\flags.dm"
|
||||
#include "code\__DEFINES\dcs\helpers.dm"
|
||||
#include "code\__DEFINES\dcs\signals.dm"
|
||||
#include "code\__DEFINES\dcs\signals\signals_datum.dm"
|
||||
#include "code\__DEFINES\dcs\signals\signals_global.dm"
|
||||
#include "code\__DEFINES\dcs\signals\signals_record.dm"
|
||||
#include "code\__DEFINES\dcs\signals\signals_subsystem.dm"
|
||||
@@ -163,6 +164,7 @@
|
||||
#include "code\__HELPERS\names.dm"
|
||||
#include "code\__HELPERS\overlay.dm"
|
||||
#include "code\__HELPERS\overmap.dm"
|
||||
#include "code\__HELPERS\qdel.dm"
|
||||
#include "code\__HELPERS\sanitize_values.dm"
|
||||
#include "code\__HELPERS\shell.dm"
|
||||
#include "code\__HELPERS\smart_token_bucket.dm"
|
||||
@@ -376,7 +378,7 @@
|
||||
#include "code\datums\statistic.dm"
|
||||
#include "code\datums\tgs_event_handler.dm"
|
||||
#include "code\datums\tgui_module.dm"
|
||||
#include "code\datums\weakref.dm"
|
||||
#include "code\datums\weakrefs.dm"
|
||||
#include "code\datums\components\_component.dm"
|
||||
#include "code\datums\components\connect_mob_behalf.dm"
|
||||
#include "code\datums\components\local_network.dm"
|
||||
@@ -418,6 +420,7 @@
|
||||
#include "code\datums\observation\shuttle_moved.dm"
|
||||
#include "code\datums\observation\sight_set.dm"
|
||||
#include "code\datums\observation\stat_set.dm"
|
||||
#include "code\datums\observation\~cleanup.dm"
|
||||
#include "code\datums\outfits\outfit.dm"
|
||||
#include "code\datums\outfits\outfit_admin.dm"
|
||||
#include "code\datums\outfits\outfit_antag.dm"
|
||||
@@ -1466,6 +1469,7 @@
|
||||
#include "code\modules\admin\verbs\viewlist.dm"
|
||||
#include "code\modules\admin\verbs\warning.dm"
|
||||
#include "code\modules\admin\view_variables\helpers.dm"
|
||||
#include "code\modules\admin\view_variables\reference_tracking.dm"
|
||||
#include "code\modules\admin\view_variables\topic.dm"
|
||||
#include "code\modules\admin\view_variables\view_variables.dm"
|
||||
#include "code\modules\alarm\alarm.dm"
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// Stuff that is relatively "core" and is used in other defines/helpers
|
||||
|
||||
//Returns the hex value of a decimal number
|
||||
//len == length of returned string
|
||||
#define num2hex(X, len) num2text(X, len, 16)
|
||||
|
||||
//Returns an integer given a hex input, supports negative values "-ff"
|
||||
//skips preceding invalid characters
|
||||
#define hex2num(X) text2num(X, 16)
|
||||
|
||||
/// subtypesof(), typesof() without the parent path
|
||||
#define subtypesof(typepath) ( typesof(typepath) - typepath )
|
||||
|
||||
/// Takes a datum as input, returns its ref string
|
||||
#define text_ref(datum) ref(datum)
|
||||
@@ -2,9 +2,6 @@
|
||||
#define CLAMP01(x) (Clamp(x, 0, 1))
|
||||
#define JOINTEXT(X) jointext(X, null)
|
||||
#define list_find(L, needle, LIMITS...) L.Find(needle, LIMITS)
|
||||
#define hex2num(hex) text2num(hex, 16)
|
||||
#define num2hex(num, pad) num2text(num, pad, 16)
|
||||
#define text_ref(datum) (isdatum(datum) ? (datum:cached_ref ||= "\ref[datum]") : ("\ref[datum]"))
|
||||
|
||||
#define span(class, text) ("<span class='[class]'>" + text + "</span>")
|
||||
#define SPAN_NOTICE(X) ("<span class='notice'>" + X + "</span>")
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
CRASH("auxtools not loaded")
|
||||
|
||||
/world/Del()
|
||||
if(byond_version < 515)
|
||||
var/debug_server = world.GetConfig("env", "AUXTOOLS_DEBUG_DLL")
|
||||
if (debug_server)
|
||||
call(debug_server, "auxtools_shutdown")()
|
||||
var/debug_server = world.GetConfig("env", "AUXTOOLS_DEBUG_DLL")
|
||||
if (debug_server)
|
||||
call_ext(debug_server, "auxtools_shutdown")()
|
||||
. = ..()
|
||||
|
||||
@@ -1,57 +1,27 @@
|
||||
// 515 split call for external libraries into call_ext
|
||||
#if DM_VERSION < 515
|
||||
#define LIBCALL call
|
||||
#else
|
||||
#define LIBCALL call_ext
|
||||
// This file contains defines allowing targeting byond versions newer than the supported
|
||||
|
||||
//Update this whenever you need to take advantage of more recent byond features
|
||||
#define MIN_COMPILER_VERSION 515
|
||||
#define MIN_COMPILER_BUILD 1609
|
||||
#if (DM_VERSION < MIN_COMPILER_VERSION || DM_BUILD < MIN_COMPILER_BUILD) && !defined(SPACEMAN_DMM) && !defined(OPENDREAM)
|
||||
//Don't forget to update this part
|
||||
#error Your version of BYOND is too out-of-date to compile this project. Go to https://secure.byond.com/download and update.
|
||||
#error You need version 515.1609 or higher
|
||||
#endif
|
||||
|
||||
// So we want to have compile time guarantees these procs exist on local type, unfortunately 515 killed the .proc/procname syntax so we have to use nameof()
|
||||
#if DM_VERSION < 515
|
||||
// So we want to have compile time guarantees these methods exist on local type
|
||||
// We use wrappers for this in case some part of the api ever changes, and to make their function more clear
|
||||
// For the record: GLOBAL_VERB_REF would be useless as verbs can't be global.
|
||||
|
||||
/**
|
||||
* Call by name proc reference, checks if the proc exists on this type or as a global proc
|
||||
*
|
||||
* * X - The proc name
|
||||
*/
|
||||
#define PROC_REF(X) (.proc/##X)
|
||||
/// Call by name proc references, checks if the proc exists on either this type or as a global proc.
|
||||
#define PROC_REF(X) (nameof(.proc/##X))
|
||||
/// Call by name verb references, checks if the verb exists on either this type or as a global verb.
|
||||
#define VERB_REF(X) (nameof(.verb/##X))
|
||||
|
||||
/**
|
||||
* Call by name proc reference, checks if the proc exists on given type or as a global proc
|
||||
*
|
||||
* * TYPE - The type (eg. `/datum/something` or `/atom`), without trailing slash
|
||||
* * X - The proc name
|
||||
*/
|
||||
#define TYPE_PROC_REF(TYPE, X) (##TYPE.proc/##X)
|
||||
/// Call by name proc reference, checks if the proc exists on either the given type or as a global proc
|
||||
#define TYPE_PROC_REF(TYPE, X) (nameof(##TYPE.proc/##X))
|
||||
/// Call by name verb reference, checks if the verb exists on either the given type or as a global verb
|
||||
#define TYPE_VERB_REF(TYPE, X) (nameof(##TYPE.verb/##X))
|
||||
|
||||
/**
|
||||
* Call by name proc reference, checks if the proc is existing global proc
|
||||
*
|
||||
* * X - The proc name
|
||||
*/
|
||||
#define GLOBAL_PROC_REF(X) (/proc/##X)
|
||||
|
||||
#else
|
||||
|
||||
/**
|
||||
* Call by name proc reference, checks if the proc exists on this type or as a global proc
|
||||
*
|
||||
* * X - The proc name
|
||||
*/
|
||||
#define PROC_REF(X) (nameof(.proc/##X))
|
||||
|
||||
/**
|
||||
* Call by name proc reference, checks if the proc exists on given type or as a global proc
|
||||
*
|
||||
* * TYPE - The type (eg. `/datum/something` or `/atom`), without trailing slash
|
||||
* * X - The proc name
|
||||
*/
|
||||
#define TYPE_PROC_REF(TYPE, X) (nameof(##TYPE.proc/##X))
|
||||
|
||||
/**
|
||||
* Call by name proc reference, checks if the proc is existing global proc
|
||||
*
|
||||
* * X - The proc name
|
||||
*/
|
||||
#define GLOBAL_PROC_REF(X) (/proc/##X)
|
||||
|
||||
#endif
|
||||
/// Call by name proc reference, checks if the proc is an existing global proc
|
||||
#define GLOBAL_PROC_REF(X) (/proc/##X)
|
||||
|
||||
@@ -12,22 +12,6 @@
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
// /datum signals
|
||||
/// when a component is added to a datum: (/datum/component)
|
||||
#define COMSIG_COMPONENT_ADDED "component_added"
|
||||
/// before a component is removed from a datum because of RemoveComponent: (/datum/component)
|
||||
#define COMSIG_COMPONENT_REMOVING "component_removing"
|
||||
/// before a datum's Destroy() is called: (force), returning a nonzero value will cancel the qdel operation
|
||||
#define COMSIG_PARENT_PREQDELETED "parent_preqdeleted"
|
||||
/// just before a datum's Destroy() is called: (force), at this point none of the other components chose to interrupt qdel and Destroy will be called
|
||||
#define COMSIG_QDELETING "parent_qdeleting"
|
||||
/// from datum ui_act (usr, action)
|
||||
#define COMSIG_UI_ACT "COMSIG_UI_ACT"
|
||||
/// fires on the target datum when an element is attached to it (/datum/element)
|
||||
#define COMSIG_ELEMENT_ATTACH "element_attach"
|
||||
/// fires on the target datum when an element is attached to it (/datum/element)
|
||||
#define COMSIG_ELEMENT_DETACH_ON_HOST_DESTROY "ELEMENT_DETACH_ON_HOST_DESTROY"
|
||||
|
||||
|
||||
// /atom signals
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// Datum signals. Format:
|
||||
// When the signal is called: (signal arguments)
|
||||
// All signals send the source datum of the signal as the first argument
|
||||
|
||||
// /datum signals
|
||||
/// when a component is added to a datum: (/datum/component)
|
||||
#define COMSIG_COMPONENT_ADDED "component_added"
|
||||
/// before a component is removed from a datum because of RemoveComponent: (/datum/component)
|
||||
#define COMSIG_COMPONENT_REMOVING "component_removing"
|
||||
|
||||
/// before a datum's Destroy() is called: (force), returning a nonzero value will cancel the qdel operation
|
||||
/// you should only be using this if you want to block deletion
|
||||
/// that's the only functional difference between it and COMSIG_QDELETING, outside setting QDELETING to detect
|
||||
#define COMSIG_PREQDELETED "parent_preqdeleted"
|
||||
/// just before a datum's Destroy() is called: (force), at this point none of the other components chose to interrupt qdel and Destroy will be called
|
||||
#define COMSIG_QDELETING "parent_qdeleting"
|
||||
/// from datum ui_act (usr, action)
|
||||
#define COMSIG_UI_ACT "COMSIG_UI_ACT"
|
||||
/// fires on the target datum when an element is attached to it (/datum/element)
|
||||
#define COMSIG_ELEMENT_ATTACH "element_attach"
|
||||
/// fires on the target datum when an element is attached to it (/datum/element)
|
||||
#define COMSIG_ELEMENT_DETACH_ON_HOST_DESTROY "ELEMENT_DETACH_ON_HOST_DESTROY"
|
||||
@@ -1,6 +1,6 @@
|
||||
//#define TESTING
|
||||
#if DM_VERSION < 513
|
||||
#error Your version of BYOND is too old to compile the code. At least BYOND 513 is required.
|
||||
#if DM_VERSION < 515 && !defined(OPENDREAM)
|
||||
#error Your version of BYOND is too old to compile the code. At least BYOND 515 is required.
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
// !!! For manual use only, remember to recomment before PRing !!!
|
||||
// #define UNIT_TEST
|
||||
// #define MANUAL_UNIT_TEST
|
||||
|
||||
#if defined(MANUAL_UNIT_TEST) && !defined(SPACEMAN_DMM) && !defined(OPENDREAM)
|
||||
#warn Manual unit test is defined, remember to recomment it before PRing!
|
||||
#endif // MANUAL_UNIT_TEST
|
||||
@@ -310,9 +310,6 @@
|
||||
// Used for creating soft references to objects. A manner of storing an item reference
|
||||
#define SOFTREF(A) ref(A)
|
||||
|
||||
// This only works on 511 because it relies on 511's `var/something = foo = bar` syntax.
|
||||
#define WEAKREF(D) (istype(D, /datum) && !D:gcDestroyed ? (D:weakref || (D:weakref = new/datum/weakref(D))) : null)
|
||||
|
||||
#define ADD_VERB_IN(the_atom,time,verb) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(add_verb), the_atom, verb), time, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_NO_HASH_WAIT)
|
||||
#define ADD_VERB_IN_IF(the_atom,time,verb,callback) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(add_verb), the_atom, verb, callback), time, TIMER_UNIQUE | TIMER_OVERRIDE | TIMER_NO_HASH_WAIT)
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ var/byond_tracy_running_v = null // Which version of byond-tracy is currently ru
|
||||
if("tracy") lib = "tracy.dll"
|
||||
if("tracy_disk") lib = "tracy-disk.dll"
|
||||
|
||||
var/init = LIBCALL(lib, "init")()
|
||||
var/init = call_ext(lib, "init")()
|
||||
if("0" != init) CRASH("[lib] init error: [init]")
|
||||
|
||||
byond_tracy_running = 1
|
||||
@@ -56,7 +56,7 @@ var/byond_tracy_running_v = null // Which version of byond-tracy is currently ru
|
||||
if("tracy") lib = "tracy.dll"
|
||||
if("tracy_disk") lib = "tracy-disk.dll"
|
||||
|
||||
LIBCALL(lib, "destroy")()
|
||||
call_ext(lib, "destroy")()
|
||||
|
||||
byond_tracy_running = 0
|
||||
byond_tracy_running_v = null
|
||||
|
||||
+78
-26
@@ -1,36 +1,88 @@
|
||||
/**
|
||||
* THIS IS A TEMPORARY WORKAROUND WHILE WE RESOLVE HARDDELS WITH THE NEW GC
|
||||
*
|
||||
* YOU ARE ONLY ALLOWED TO REMOVE THE AMOUNT OF THEM, NOT ADD MORE
|
||||
*
|
||||
* AND FOR WHAT MATTERS, THE SAME GOES FOR QDEL_HINT_HARDDEL,
|
||||
* I DO NOT CARE HOW MANY REWRITES AND REFACTORS YOU HAVE TO DO, WHATEVER YOU ADD NEEDS TO BE GARGAGE COLLECTABLE, OR HAVE AN EXPLAINED, CLEAR, UNFIXABLE REASON WHY IT IS NOT
|
||||
*
|
||||
*/
|
||||
#define GC_TEMPORARY_HARDDEL return QDEL_HINT_HARDDEL
|
||||
|
||||
//! Defines that give qdel hints.
|
||||
//!
|
||||
//! These can be given as a return in [/atom/proc/Destroy] or by calling [/proc/qdel].
|
||||
|
||||
/// `qdel` should queue the object for deletion.
|
||||
#define QDEL_HINT_QUEUE 0
|
||||
/// `qdel` should let the object live after calling [/datum/proc/Destroy].
|
||||
#define QDEL_HINT_LETMELIVE 1
|
||||
/// Functionally the same as above. `qdel` should assume the object will gc on its own, and not check it.
|
||||
#define QDEL_HINT_IWILLGC 2
|
||||
/// `qdel` should assume this object won't GC, and queue a hard delete using a hardref.
|
||||
#define QDEL_HINT_HARDDEL 3
|
||||
/// `qdel` should assume this object won't GC, and hard delete it immediately.
|
||||
#define QDEL_HINT_HARDDEL_NOW 4
|
||||
#define QDEL_HINT_QUEUE 0
|
||||
/// `qdel` should let the object live after calling [/atom/proc/Destroy].
|
||||
#define QDEL_HINT_LETMELIVE 1
|
||||
/// Functionally the same as the above. `qdel` should assume the object will gc on its own, and not check it.
|
||||
#define QDEL_HINT_IWILLGC 2
|
||||
/// Qdel should assume this object won't GC, and queue a hard delete using a hard reference.
|
||||
#define QDEL_HINT_HARDDEL 3
|
||||
// Qdel should assume this object won't gc, and hard delete it posthaste.
|
||||
#define QDEL_HINT_HARDDEL_NOW 4
|
||||
|
||||
/*
|
||||
* If REFERENCE_TRACKING and [GC_FAILURE_HARD_LOOKUP] are not enabled, these are functionally identical to [QDEL_HINT_QUEUE].
|
||||
* If they are enabled, qdel will call this object's find_references() verb.
|
||||
*/
|
||||
#define QDEL_HINT_FINDREFERENCE 5
|
||||
/// Same behavior as [QDEL_HINT_FINDREFERENCE], but only if GC fails and a hard delete is forced.
|
||||
|
||||
#ifdef REFERENCE_TRACKING
|
||||
|
||||
//A warning on compile is treated as an error in the CI, therefore unlike TG we must avoid the warn if it's running in the CI
|
||||
#if defined(TESTING) && !defined(CIBUILDING) && !defined(OPENDREAM) && !defined(SPACEMAN_DMM)
|
||||
/* If REFERENCE_TRACKING is enabled, qdel will call this object's find_references() verb.
|
||||
|
||||
Functionally identical to [QDEL_HINT_QUEUE] if [GC_FAILURE_HARD_LOOKUP] is not enabled in _compiler_options.dm.
|
||||
*/
|
||||
#warn TG0001 qdel REFERENCE_TRACKING enabled
|
||||
#endif
|
||||
|
||||
#define QDEL_HINT_FINDREFERENCE 5
|
||||
/// Behavior as [QDEL_HINT_FINDREFERENCE], but only if the GC fails and a hard delete is forced.
|
||||
#define QDEL_HINT_IFFAIL_FINDREFERENCE 6
|
||||
//defines for the gcDestroyed var
|
||||
#endif
|
||||
|
||||
#define GC_QUEUED_FOR_QUEUING -1
|
||||
#define GC_QUEUED_FOR_HARD_DEL -2
|
||||
#define GC_CURRENTLY_BEING_QDELETED -3
|
||||
// Defines for the ssgarbage queues
|
||||
#define GC_QUEUE_FILTER 1 //! short queue to filter out quick gc successes so they don't hang around in the main queue for 5 minutes
|
||||
#define GC_QUEUE_CHECK 2 //! main queue that waits 5 minutes because thats the longest byond can hold a reference to our shit.
|
||||
#define GC_QUEUE_HARDDELETE 3 //! short queue for things that hard delete instead of going thru the gc subsystem, this is purely so if they *can* softdelete, they will soft delete rather then wasting time with a hard delete.
|
||||
#define GC_QUEUE_COUNT 3 //! Number of queues, used for allocating the nested lists. Don't forget to increase this if you add a new queue stage
|
||||
|
||||
#define QDELING(X) (X.gcDestroyed)
|
||||
#define QDELETED(X) (!X || X.gcDestroyed)
|
||||
#define QDESTROYING(X) (!X || X.gcDestroyed == GC_CURRENTLY_BEING_QDELETED)
|
||||
|
||||
#define QDEL_IN(item, time) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel), item), time, TIMER_STOPPABLE)
|
||||
#define QDEL_NULL(item) qdel(item); item = null
|
||||
#define QDEL_NULL_LIST(x) if(x) { for(var/y in x) { qdel(y) }}; if(x) {x.Cut(); x = null } // Second x check to handle items that LAZYREMOVE on qdel.
|
||||
#define QDEL_NULL_ASSOC(x) if(x) { for(var/y in x) { qdel(x[y])}}; if(x) {x.Cut(); x = null } // As above, but qdels the value rather than the key
|
||||
// Defines for the ssgarbage queue items
|
||||
#define GC_QUEUE_ITEM_QUEUE_TIME 1 //! Time this item entered the queue
|
||||
#define GC_QUEUE_ITEM_REF 2 //! Ref to the item
|
||||
#define GC_QUEUE_ITEM_GCD_DESTROYED 3 //! Item's gc_destroyed var value. Used to detect ref reuse.
|
||||
#define GC_QUEUE_ITEM_INDEX_COUNT 3 //! Number of item indexes, used for allocating the nested lists. Don't forget to increase this if you add a new queue item index
|
||||
|
||||
// Defines for the time an item has to get its reference cleaned before it fails the queue and moves to the next.
|
||||
#define GC_FILTER_QUEUE (1 SECONDS)
|
||||
#define GC_CHECK_QUEUE (5 MINUTES)
|
||||
#define GC_DEL_QUEUE (10 SECONDS)
|
||||
|
||||
|
||||
#define QDEL_ITEM_ADMINS_WARNED (1<<0) //! Set when admins are told about lag causing qdels in this type.
|
||||
#define QDEL_ITEM_SUSPENDED_FOR_LAG (1<<1) //! Set when a type can no longer be hard deleted on failure because of lag it causes while this happens.
|
||||
|
||||
// Defines for the [gc_destroyed][/datum/var/gc_destroyed] var.
|
||||
#define GC_CURRENTLY_BEING_QDELETED -2
|
||||
|
||||
#define QDELING(X) (X.gc_destroyed)
|
||||
#define QDELETED(X) (isnull(X) || QDELING(X))
|
||||
#define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
|
||||
|
||||
// This is a bit hacky, we do it to avoid people relying on a return value for the macro
|
||||
// If you need that you should use QDEL_IN_STOPPABLE instead
|
||||
#define QDEL_IN(item, time) ; \
|
||||
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel), (time) > GC_FILTER_QUEUE ? WEAKREF(item) : item), time);
|
||||
#define QDEL_IN_STOPPABLE(item, time) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel), (time) > GC_FILTER_QUEUE ? WEAKREF(item) : item), time, TIMER_STOPPABLE)
|
||||
#define QDEL_IN_CLIENT_TIME(item, time) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(qdel), item), time, TIMER_STOPPABLE | TIMER_CLIENT_TIME)
|
||||
|
||||
//Bay uses the check before deleting something, to ensure it's not null, TG does not, and since I don't want to go hunt down 3 billion runtimes, we keep bay's version
|
||||
|
||||
///Delete and null the reference, if the object isn't null
|
||||
#define QDEL_NULL(item) if(item) { qdel(item) ; item = null }
|
||||
|
||||
#define QDEL_LIST(L) if(L) { for(var/I in L) qdel(I); L.Cut(); }
|
||||
#define QDEL_LIST_IN(L, time) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(______qdel_list_wrapper), L), time, TIMER_STOPPABLE)
|
||||
#define QDEL_LIST_ASSOC(L) if(L) { for(var/I in L) { qdel(L[I]); qdel(I); } L.Cut(); }
|
||||
#define QDEL_LIST_ASSOC_VAL(L) if(L) { for(var/I in L) qdel(L[I]); L.Cut(); }
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
#define INIT_ORDER_PROFILER 101
|
||||
/*
|
||||
THIS IS A LEGACY FILE, DO NOT ADD ONTOP OF THIS
|
||||
USE code\__DEFINES\subsystems.dm AND THE APPROPRIATE DEF NAMES
|
||||
THAT ARE PRESENT THERE
|
||||
*/
|
||||
|
||||
#define SS_INIT_PERSISTENT_CONFIG 26
|
||||
#define SS_INIT_MISC_FIRST 25
|
||||
#define SS_INIT_SEEDS 24 // Plant controller setup.
|
||||
|
||||
@@ -191,10 +191,19 @@
|
||||
/// The timer key used to know how long subsystem initialization takes
|
||||
#define SS_INIT_TIMER_KEY "ss_init"
|
||||
|
||||
//! ### SS initialization load orders
|
||||
// Subsystem init_order, from highest priority to lowest priority
|
||||
// Subsystems shutdown in the reverse of the order they initialize in
|
||||
// The numbers just define the ordering, they are meaningless otherwise.
|
||||
|
||||
#define INIT_ORDER_PROFILER 101
|
||||
#define INIT_ORDER_GARBAGE 99
|
||||
|
||||
// Subsystem fire priority, from lowest to highest priority
|
||||
// If the subsystem isn't listed here it's either DEFAULT or PROCESS (if it's a processing subsystem child)
|
||||
|
||||
#define FIRE_PRIORITY_DEFAULT 50
|
||||
#define FIRE_PRIORITY_GARBAGE 15
|
||||
|
||||
|
||||
/* AURORA SHIT */
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
#define MILLISECONDS *0.01
|
||||
|
||||
#define DECISECONDS *1 //the base unit all of these defines are scaled by, because byond uses that as a unit of measurement for some fucking reason
|
||||
|
||||
#define SECOND *10
|
||||
#define SECONDS *10
|
||||
|
||||
@@ -16,3 +20,7 @@
|
||||
#define DS2TICKS(DS) ((DS)/world.tick_lag)
|
||||
|
||||
#define TICKS2DS(T) ((T) TICKS)
|
||||
|
||||
#define MS2DS(T) ((T) MILLISECONDS)
|
||||
|
||||
#define DS2MS(T) ((T) * 100)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
var/list/calling_arguments = length(args) > 2 ? args.Copy(3) : null
|
||||
|
||||
. = LIBCALL(dll, func)(arglist(calling_arguments))
|
||||
. = call_ext(dll, func)(arglist(calling_arguments))
|
||||
|
||||
if (world.timeofday - start > 10 SECONDS)
|
||||
crash_with("DLL call took longer than 10 seconds: [func]")
|
||||
|
||||
@@ -544,9 +544,6 @@
|
||||
/proc/is_list_containing_type(var/list/L, type)
|
||||
return count_by_type(L, type) == L.len
|
||||
|
||||
/proc/subtypesof(prototype)
|
||||
return (typesof(prototype) - prototype)
|
||||
|
||||
//creates every subtype of prototype (excluding prototype) and adds it to list L.
|
||||
//if no list/L is provided, one is created.
|
||||
/proc/init_subtypes(prototype, list/L)
|
||||
|
||||
@@ -68,6 +68,8 @@
|
||||
return current_species ? current_species.sanitize_name(name) : sanitizeName(name)
|
||||
|
||||
/proc/random_name(gender, species = SPECIES_HUMAN)
|
||||
SHOULD_NOT_SLEEP(TRUE)
|
||||
|
||||
var/datum/species/current_species
|
||||
if(species)
|
||||
current_species = GLOB.all_species[species]
|
||||
|
||||
@@ -8,8 +8,4 @@
|
||||
/**
|
||||
* NAMEOF that actually works in static definitions because src::type requires src to be defined
|
||||
*/
|
||||
#if DM_VERSION >= 515
|
||||
#define NAMEOF_STATIC(datum, X) (nameof(type::##X))
|
||||
#else
|
||||
#define NAMEOF_STATIC(datum, X) (#X || ##datum.##X)
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
///the underscores are to encourage people not to use this directly.
|
||||
/proc/______qdel_list_wrapper(list/L)
|
||||
QDEL_LIST(L)
|
||||
@@ -40,6 +40,15 @@
|
||||
/proc/cmp_timer(datum/timedevent/a, datum/timedevent/b)
|
||||
return a.timeToRun - b.timeToRun
|
||||
|
||||
/proc/cmp_qdel_item_time(datum/qdel_item/A, datum/qdel_item/B)
|
||||
. = B.hard_delete_time - A.hard_delete_time
|
||||
if (!.)
|
||||
. = B.destroy_time - A.destroy_time
|
||||
if (!.)
|
||||
. = B.failures - A.failures
|
||||
if (!.)
|
||||
. = B.qdels - A.qdels
|
||||
|
||||
/proc/cmp_camera(obj/machinery/camera/a, obj/machinery/camera/b)
|
||||
if (a.c_tag_order != b.c_tag_order)
|
||||
return b.c_tag_order - a.c_tag_order
|
||||
|
||||
@@ -30,10 +30,18 @@
|
||||
Manual runs area, uncomment the appropriate defines depending on what you want to do
|
||||
*/
|
||||
|
||||
//#define TESTING // Creates debug feedback messages and enables many optional testing procs/checks
|
||||
// !!! For manual use only, remember to recomment before PRing !!!
|
||||
// #define TESTING // Creates debug feedback messages and enables many optional testing procs/checks
|
||||
// #define UNIT_TEST
|
||||
// #define MANUAL_UNIT_TEST
|
||||
|
||||
#if defined(MANUAL_UNIT_TEST) && !defined(SPACEMAN_DMM) && !defined(OPENDREAM)
|
||||
#warn Manual unit test is defined, remember to recomment it before PRing!
|
||||
#endif // MANUAL_UNIT_TEST
|
||||
|
||||
#ifdef TESTING
|
||||
///Used to find the sources of harddels, quite laggy, don't be surpised if it freezes your client for a good while
|
||||
//#define REFERENCE_TRACKING
|
||||
// #define REFERENCE_TRACKING
|
||||
#ifdef REFERENCE_TRACKING
|
||||
|
||||
//Run a lookup on things hard deleting by default.
|
||||
@@ -77,7 +85,8 @@
|
||||
//Test at full capacity, the extra cost doesn't matter
|
||||
#define TIMER_DEBUG
|
||||
|
||||
#define ZASDBG
|
||||
//If you really are lost with atmos issues
|
||||
// #define ZASDBG
|
||||
|
||||
#define SQL_PREF_DEBUG
|
||||
#endif //UNIT_TEST
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
update_abilities(0, owner)
|
||||
|
||||
/obj/screen/movable/ability_master/Destroy()
|
||||
. = ..()
|
||||
//Get rid of the ability objects.
|
||||
remove_all_abilities()
|
||||
ability_objects.Cut()
|
||||
@@ -31,6 +30,8 @@
|
||||
my_mob.client.screen -= src
|
||||
my_mob = null
|
||||
|
||||
. = ..()
|
||||
|
||||
/obj/screen/movable/ability_master/MouseDrop()
|
||||
if(showing)
|
||||
return
|
||||
|
||||
@@ -149,7 +149,6 @@ var/list/global_huds
|
||||
..()
|
||||
|
||||
/datum/hud/Destroy()
|
||||
. = ..()
|
||||
grab_intent = null
|
||||
hurt_intent = null
|
||||
disarm_intent = null
|
||||
@@ -167,6 +166,8 @@ var/list/global_huds
|
||||
// item_action_list = null // ?
|
||||
mymob = null
|
||||
|
||||
. = ..()
|
||||
|
||||
/datum/hud/proc/hidden_inventory_update()
|
||||
if(!mymob) return
|
||||
if(ishuman(mymob))
|
||||
|
||||
@@ -9,7 +9,8 @@ GLOBAL_LIST_EMPTY(radial_menus)
|
||||
var/datum/radial_menu/parent
|
||||
|
||||
/obj/screen/radial/Destroy()
|
||||
qdel(parent)
|
||||
QDEL_NULL(parent)
|
||||
|
||||
return ..()
|
||||
|
||||
/obj/screen/radial/slice
|
||||
|
||||
@@ -86,8 +86,8 @@
|
||||
var/obj/item/owner
|
||||
|
||||
/obj/screen/item_action/Destroy()
|
||||
. = ..()
|
||||
owner = null
|
||||
. = ..()
|
||||
|
||||
/obj/screen/item_action/Click()
|
||||
if(!usr || !owner)
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
var/mob/spell_holder
|
||||
|
||||
/obj/screen/movable/spell_master/Destroy()
|
||||
. = ..()
|
||||
for(var/obj/screen/spell/spells in spell_objects)
|
||||
spells.spellmaster = null
|
||||
spell_objects.Cut()
|
||||
@@ -23,6 +22,8 @@
|
||||
spell_holder.client.screen -= src
|
||||
spell_holder = null
|
||||
|
||||
. = ..()
|
||||
|
||||
/obj/screen/movable/spell_master/MouseDrop()
|
||||
if(showing)
|
||||
return
|
||||
@@ -157,7 +158,6 @@
|
||||
var/icon/last_charged_icon
|
||||
|
||||
/obj/screen/spell/Destroy()
|
||||
. = ..()
|
||||
spell = null
|
||||
last_charged_icon = null
|
||||
if(spellmaster)
|
||||
@@ -168,6 +168,8 @@
|
||||
qdel(spellmaster)
|
||||
spellmaster = null
|
||||
|
||||
. = ..()
|
||||
|
||||
/obj/screen/spell/proc/update_charge(var/forced_update = 0)
|
||||
if(!spell)
|
||||
qdel(src)
|
||||
|
||||
@@ -13,7 +13,14 @@ GLOBAL_REAL(GLOB, /datum/controller/global_vars)
|
||||
GLOB = src
|
||||
|
||||
var/datum/controller/exclude_these = new
|
||||
gvars_datum_in_built_vars = exclude_these.vars + list(NAMEOF(src, gvars_datum_protected_varlist), NAMEOF(src, gvars_datum_in_built_vars), NAMEOF(src, gvars_datum_init_order))
|
||||
// I know this is dumb but the nested vars list hangs a ref to the datum. This fixes that
|
||||
var/list/controller_vars = exclude_these.vars.Copy()
|
||||
controller_vars["vars"] = null
|
||||
gvars_datum_in_built_vars = controller_vars + list(NAMEOF(src, gvars_datum_protected_varlist), NAMEOF(src, gvars_datum_in_built_vars), NAMEOF(src, gvars_datum_init_order))
|
||||
|
||||
#if MIN_COMPILER_VERSION >= 515 && MIN_COMPILER_BUILD > 1620
|
||||
#warn datum.vars hanging a ref should now be fixed, there should be no reason to remove the vars list from our controller's vars list anymore
|
||||
#endif
|
||||
QDEL_IN(exclude_these, 0) //signal logging isn't ready
|
||||
|
||||
Initialize()
|
||||
|
||||
@@ -1,39 +1,53 @@
|
||||
/*!
|
||||
## Debugging GC issues
|
||||
|
||||
In order to debug `qdel()` failures, there are several tools available.
|
||||
To enable these tools, define `TESTING` in [_compile_options.dm](https://github.com/tgstation/-tg-station/blob/master/code/_compile_options.dm).
|
||||
|
||||
First is a verb called "Find References", which lists **every** refererence to an object in the world. This allows you to track down any indirect or obfuscated references that you might have missed.
|
||||
|
||||
Complementing this is another verb, "qdel() then Find References".
|
||||
This does exactly what you'd expect; it calls `qdel()` on the object and then it finds all references remaining.
|
||||
This is great, because it means that `Destroy()` will have been called before it starts to find references,
|
||||
so the only references you'll find will be the ones preventing the object from `qdel()`ing gracefully.
|
||||
|
||||
If you have a datum or something you are not destroying directly (say via the singulo),
|
||||
the next tool is `QDEL_HINT_FINDREFERENCE`. You can return this in `Destroy()` (where you would normally `return ..()`),
|
||||
to print a list of references once it enters the GC queue.
|
||||
|
||||
Finally is a verb, "Show qdel() Log", which shows the deletion log that the garbage subsystem keeps. This is helpful if you are having race conditions or need to review the order of deletions.
|
||||
|
||||
Note that for any of these tools to work `TESTING` must be defined.
|
||||
By using these methods of finding references, you can make your life far, far easier when dealing with `qdel()` failures.
|
||||
*/
|
||||
|
||||
SUBSYSTEM_DEF(garbage)
|
||||
name = "Garbage"
|
||||
priority = SS_PRIORITY_GARBAGE
|
||||
priority = FIRE_PRIORITY_GARBAGE
|
||||
wait = 2 SECONDS
|
||||
flags = SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT
|
||||
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
|
||||
init_order = INIT_ORDER_GARBAGE
|
||||
init_stage = INITSTAGE_EARLY
|
||||
|
||||
var/collection_timeout = 3000// deciseconds to wait to let running procs finish before we just say fuck it and force del() the object
|
||||
var/delslasttick = 0 // number of del()'s we've done this tick
|
||||
var/gcedlasttick = 0 // number of things that gc'ed last tick
|
||||
var/list/collection_timeout = list(GC_FILTER_QUEUE, GC_CHECK_QUEUE, GC_DEL_QUEUE) // deciseconds to wait before moving something up in the queue to the next level
|
||||
|
||||
//Stat tracking
|
||||
var/delslasttick = 0 // number of del()'s we've done this tick
|
||||
var/gcedlasttick = 0 // number of things that gc'ed last tick
|
||||
var/totaldels = 0
|
||||
var/totalgcs = 0
|
||||
|
||||
var/highest_del_time = 0
|
||||
var/highest_del_tickusage = 0
|
||||
var/highest_del_ms = 0
|
||||
var/highest_del_type_string = ""
|
||||
|
||||
var/list/queue = list() // list of refID's of things that should be garbage collected
|
||||
// refID's are associated with the time at which they time out and need to be manually del()
|
||||
// we do this so we aren't constantly locating them and preventing them from being gc'd
|
||||
var/list/pass_counts
|
||||
var/list/fail_counts
|
||||
|
||||
var/list/tobequeued = list() //We store the references of things to be added to the queue seperately so we can spread out GC overhead over a few ticks
|
||||
|
||||
var/list/didntgc = list() // list of all types that have failed to GC associated with the number of times that's happened.
|
||||
// the types are stored as strings
|
||||
var/list/sleptDestroy = list() //Same as above but these are paths that slept during their Destroy call
|
||||
|
||||
var/list/noqdelhint = list()// list of all types that do not return a QDEL_HINT
|
||||
// all types that did not respect qdel(A, force=TRUE) and returned one
|
||||
// of the immortality qdel hints
|
||||
var/list/noforcerespect = list()
|
||||
|
||||
#ifdef REFERENCE_TRACKING
|
||||
var/list/qdel_list = list() // list of all types that have been qdel()eted
|
||||
#endif
|
||||
var/list/items = list() // Holds our qdel_item statistics datums
|
||||
|
||||
//Queue
|
||||
var/list/queues
|
||||
#ifdef REFERENCE_TRACKING
|
||||
var/list/reference_find_on_fail = list()
|
||||
#ifdef REFERENCE_TRACKING_DEBUG
|
||||
@@ -42,8 +56,15 @@ SUBSYSTEM_DEF(garbage)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/datum/controller/subsystem/garbage/PreInit()
|
||||
InitQueues()
|
||||
|
||||
/datum/controller/subsystem/garbage/stat_entry(msg)
|
||||
msg = "W:[tobequeued.len]|Q:[queue.len]|D:[delslasttick]|G:[gcedlasttick]|"
|
||||
var/list/counts = list()
|
||||
for (var/list/L in queues)
|
||||
counts += length(L)
|
||||
msg += "Q:[counts.Join(",")]|D:[delslasttick]|G:[gcedlasttick]|"
|
||||
msg += "GR:"
|
||||
if (!(delslasttick+gcedlasttick))
|
||||
msg += "n/a|"
|
||||
@@ -55,405 +76,339 @@ SUBSYSTEM_DEF(garbage)
|
||||
msg += "n/a|"
|
||||
else
|
||||
msg += "TGR:[round((totalgcs/(totaldels+totalgcs))*100, 0.01)]%"
|
||||
msg += " P:[pass_counts.Join(",")]"
|
||||
msg += "|F:[fail_counts.Join(",")]"
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/garbage/Shutdown()
|
||||
//Adds the del() log to the qdel log file
|
||||
var/list/del_log = list()
|
||||
|
||||
//sort by how long it's wasted hard deleting
|
||||
sortTim(items, cmp=/proc/cmp_qdel_item_time, associative = TRUE)
|
||||
for(var/path in items)
|
||||
var/datum/qdel_item/I = items[path]
|
||||
var/list/entry = list()
|
||||
del_log[path] = entry
|
||||
|
||||
if (I.qdel_flags & QDEL_ITEM_SUSPENDED_FOR_LAG)
|
||||
entry["SUSPENDED FOR LAG"] = TRUE
|
||||
if (I.failures)
|
||||
entry["Failures"] = I.failures
|
||||
entry["qdel() Count"] = I.qdels
|
||||
entry["Destroy() Cost (ms)"] = I.destroy_time
|
||||
|
||||
if (I.hard_deletes)
|
||||
entry["Total Hard Deletes"] = I.hard_deletes
|
||||
entry["Time Spend Hard Deleting (ms)"] = I.hard_delete_time
|
||||
entry["Highest Time Spend Hard Deleting (ms)"] = I.hard_delete_max
|
||||
if (I.hard_deletes_over_threshold)
|
||||
entry["Hard Deletes Over Threshold"] = I.hard_deletes_over_threshold
|
||||
if (I.slept_destroy)
|
||||
entry["Total Sleeps"] = I.slept_destroy
|
||||
if (I.no_respect_force)
|
||||
entry["Total Ignored Force"] = I.no_respect_force
|
||||
if (I.no_hint)
|
||||
entry["Total No Hint"] = I.no_hint
|
||||
if(LAZYLEN(I.extra_details))
|
||||
entry["Deleted Metadata"] = I.extra_details
|
||||
|
||||
log_subsystem_garbage(json_encode(del_log))
|
||||
|
||||
/datum/controller/subsystem/garbage/fire()
|
||||
HandleToBeQueued()
|
||||
if(state == SS_RUNNING)
|
||||
HandleQueue()
|
||||
//the fact that this resets its processing each fire (rather then resume where it left off) is intentional.
|
||||
var/queue = GC_QUEUE_FILTER
|
||||
|
||||
if (state == SS_PAUSED)
|
||||
state = SS_RUNNING
|
||||
while (state == SS_RUNNING)
|
||||
switch (queue)
|
||||
if (GC_QUEUE_FILTER)
|
||||
HandleQueue(GC_QUEUE_FILTER)
|
||||
queue = GC_QUEUE_FILTER+1
|
||||
if (GC_QUEUE_CHECK)
|
||||
HandleQueue(GC_QUEUE_CHECK)
|
||||
queue = GC_QUEUE_CHECK+1
|
||||
if (GC_QUEUE_HARDDELETE)
|
||||
HandleQueue(GC_QUEUE_HARDDELETE)
|
||||
if (state == SS_PAUSED) //make us wait again before the next run.
|
||||
state = SS_RUNNING
|
||||
break
|
||||
|
||||
//If you see this proc high on the profile, what you are really seeing is the garbage collection/soft delete overhead in byond.
|
||||
//Don't attempt to optimize, not worth the effort.
|
||||
/datum/controller/subsystem/garbage/proc/HandleToBeQueued()
|
||||
var/list/tobequeued = src.tobequeued
|
||||
var/starttime = world.time
|
||||
var/starttimeofday = world.timeofday
|
||||
var/idex = 1
|
||||
while((tobequeued.len - (idex - 1)) && starttime == world.time && starttimeofday == world.timeofday)
|
||||
if (MC_TICK_CHECK)
|
||||
break
|
||||
var/ref = tobequeued[idex]
|
||||
tobequeued[idex++] = null // Clear this ref to assist hard deletes in Queue().
|
||||
Queue(ref)
|
||||
|
||||
if (idex > 1)
|
||||
tobequeued.Cut(1, idex)
|
||||
|
||||
/datum/controller/subsystem/garbage/proc/HandleQueue()
|
||||
delslasttick = 0
|
||||
gcedlasttick = 0
|
||||
var/time_to_kill = world.time - collection_timeout // Anything qdel() but not GC'd BEFORE this time needs to be manually del()
|
||||
var/list/queue = src.queue
|
||||
var/starttime = world.time
|
||||
var/starttimeofday = world.timeofday
|
||||
var/idex = 1
|
||||
#ifdef REFERENCE_TRACKING
|
||||
var/ref_searching = FALSE
|
||||
#endif
|
||||
while((queue.len - (idex - 1)) && starttime == world.time && starttimeofday == world.timeofday)
|
||||
if (MC_TICK_CHECK)
|
||||
break
|
||||
#ifdef REFERENCE_TRACKING
|
||||
if (ref_searching)
|
||||
break
|
||||
#endif
|
||||
var/refID = queue[idex]
|
||||
if (!refID)
|
||||
idex++
|
||||
/datum/controller/subsystem/garbage/proc/InitQueues()
|
||||
if (isnull(queues)) // Only init the queues if they don't already exist, prevents overriding of recovered lists
|
||||
queues = new(GC_QUEUE_COUNT)
|
||||
pass_counts = new(GC_QUEUE_COUNT)
|
||||
fail_counts = new(GC_QUEUE_COUNT)
|
||||
for(var/i in 1 to GC_QUEUE_COUNT)
|
||||
queues[i] = list()
|
||||
pass_counts[i] = 0
|
||||
fail_counts[i] = 0
|
||||
|
||||
|
||||
/datum/controller/subsystem/garbage/proc/HandleQueue(level = GC_QUEUE_FILTER)
|
||||
if (level == GC_QUEUE_FILTER)
|
||||
delslasttick = 0
|
||||
gcedlasttick = 0
|
||||
var/cut_off_time = world.time - collection_timeout[level] //ignore entries newer then this
|
||||
var/list/queue = queues[level]
|
||||
var/static/lastlevel
|
||||
var/static/count = 0
|
||||
if (count) //runtime last run before we could do this.
|
||||
var/c = count
|
||||
count = 0 //so if we runtime on the Cut, we don't try again.
|
||||
var/list/lastqueue = queues[lastlevel]
|
||||
lastqueue.Cut(1, c+1)
|
||||
|
||||
lastlevel = level
|
||||
|
||||
//We do this rather then for(var/list/ref_info in queue) because that sort of for loop copies the whole list.
|
||||
//Normally this isn't expensive, but the gc queue can grow to 40k items, and that gets costly/causes overrun.
|
||||
for (var/i in 1 to length(queue))
|
||||
var/list/L = queue[i]
|
||||
if (length(L) < GC_QUEUE_ITEM_INDEX_COUNT)
|
||||
count++
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
continue
|
||||
|
||||
var/GCd_at_time = queue[refID]
|
||||
if(GCd_at_time > time_to_kill)
|
||||
var/queued_at_time = L[GC_QUEUE_ITEM_QUEUE_TIME]
|
||||
if(queued_at_time > cut_off_time)
|
||||
break // Everything else is newer, skip them
|
||||
idex++
|
||||
var/datum/A
|
||||
A = locate(refID)
|
||||
if (A && A.gcDestroyed == GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake
|
||||
#ifdef REFERENCE_TRACKING
|
||||
if(reference_find_on_fail[text_ref(A)])
|
||||
INVOKE_ASYNC(A, TYPE_PROC_REF(/datum, find_references))
|
||||
ref_searching = TRUE
|
||||
#ifdef GC_FAILURE_HARD_LOOKUP
|
||||
else
|
||||
INVOKE_ASYNC(A, TYPE_PROC_REF(/datum, find_references))
|
||||
ref_searching = TRUE
|
||||
#endif
|
||||
reference_find_on_fail -= text_ref(A)
|
||||
#endif
|
||||
count++
|
||||
|
||||
// Something's still referring to the qdel'd object. Kill it.
|
||||
var/type = A.type
|
||||
log_subsystem_garbage_harddel("-- \ref[A] | [type] was unable to be GC'd and was deleted --", type)
|
||||
didntgc["[type]"]++
|
||||
var/datum/D = L[GC_QUEUE_ITEM_REF]
|
||||
|
||||
#ifdef REFERENCE_TRACKING
|
||||
if(ref_searching)
|
||||
continue //ref searching intentionally cancels all further fires while running so things that hold references don't end up getting deleted, so we want to return here instead of continue
|
||||
#endif
|
||||
|
||||
HardDelete(A)
|
||||
|
||||
++delslasttick
|
||||
++totaldels
|
||||
else
|
||||
// 1 from the hard reference in the queue, and 1 from the variable used before this
|
||||
// If that's all we've got, send er off
|
||||
if (refcount(D) == 2)
|
||||
++gcedlasttick
|
||||
++totalgcs
|
||||
pass_counts[level]++
|
||||
#ifdef REFERENCE_TRACKING
|
||||
reference_find_on_fail -= text_ref(A)
|
||||
reference_find_on_fail -= text_ref(D) //It's deleted we don't care anymore.
|
||||
#endif
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
continue
|
||||
|
||||
if (idex > 1)
|
||||
queue.Cut(1, idex)
|
||||
// Something's still referring to the qdel'd object.
|
||||
fail_counts[level]++
|
||||
|
||||
/datum/controller/subsystem/garbage/proc/QueueForQueuing(datum/A)
|
||||
if (istype(A) && A.gcDestroyed == GC_CURRENTLY_BEING_QDELETED)
|
||||
tobequeued += A
|
||||
A.gcDestroyed = GC_QUEUED_FOR_QUEUING
|
||||
#ifdef REFERENCE_TRACKING
|
||||
var/ref_searching = FALSE
|
||||
#endif
|
||||
|
||||
/datum/controller/subsystem/garbage/proc/Queue(datum/A)
|
||||
if (!istype(A) || (!isnull(A.gcDestroyed) && A.gcDestroyed >= 0))
|
||||
switch (level)
|
||||
if (GC_QUEUE_CHECK)
|
||||
#ifdef REFERENCE_TRACKING
|
||||
if(reference_find_on_fail[text_ref(D)])
|
||||
INVOKE_ASYNC(D, TYPE_PROC_REF(/datum,find_references))
|
||||
ref_searching = TRUE
|
||||
#ifdef GC_FAILURE_HARD_LOOKUP
|
||||
else
|
||||
INVOKE_ASYNC(D, TYPE_PROC_REF(/datum,find_references))
|
||||
ref_searching = TRUE
|
||||
#endif
|
||||
reference_find_on_fail -= text_ref(D)
|
||||
#endif
|
||||
var/type = D.type
|
||||
var/datum/qdel_item/I = items[type]
|
||||
|
||||
var/message = "## TESTING: GC: -- [text_ref(D)] | [type] was unable to be GC'd --"
|
||||
message = "[message] (ref count of [refcount(D)])"
|
||||
log_world(message)
|
||||
|
||||
var/detail = D.dump_harddel_info()
|
||||
if(detail)
|
||||
LAZYADD(I.extra_details, detail)
|
||||
|
||||
#ifdef TESTING
|
||||
for(var/c in GLOB.staff) //Using testing() here would fill the logs with ADMIN_VV garbage
|
||||
var/client/admin = c
|
||||
if(!check_rights(R_ADMIN, user = admin?.mob))
|
||||
continue
|
||||
// Used to be to_chat(admin, "## TESTING: GC: -- [ADMIN_VV(D)] | [type] was unable to be GC'd --") but we do not have that macro
|
||||
to_chat(admin, "## TESTING: GC: -- <a href='?_src_=vars;Vars=\ref[D]'>VV</a> | [type] was unable to be GC'd --")
|
||||
#endif
|
||||
I.failures++
|
||||
|
||||
if (I.qdel_flags & QDEL_ITEM_SUSPENDED_FOR_LAG)
|
||||
#ifdef REFERENCE_TRACKING
|
||||
if(ref_searching)
|
||||
return //ref searching intentionally cancels all further fires while running so things that hold references don't end up getting deleted, so we want to return here instead of continue
|
||||
#endif
|
||||
continue
|
||||
if (GC_QUEUE_HARDDELETE)
|
||||
HardDelete(D)
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
continue
|
||||
|
||||
Queue(D, level+1)
|
||||
|
||||
#ifdef REFERENCE_TRACKING
|
||||
if(ref_searching)
|
||||
return
|
||||
#endif
|
||||
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
if (count)
|
||||
queue.Cut(1,count+1)
|
||||
count = 0
|
||||
|
||||
/datum/controller/subsystem/garbage/proc/Queue(datum/D, level = GC_QUEUE_FILTER)
|
||||
if (isnull(D))
|
||||
return
|
||||
if (A.gcDestroyed == GC_QUEUED_FOR_HARD_DEL)
|
||||
HardDelete(A)
|
||||
if (level > GC_QUEUE_COUNT)
|
||||
HardDelete(D)
|
||||
return
|
||||
var/gctime = world.time
|
||||
var/refid = "\ref[A]"
|
||||
var/queue_time = world.time
|
||||
|
||||
A.gcDestroyed = gctime
|
||||
if (D.gc_destroyed <= 0)
|
||||
D.gc_destroyed = queue_time
|
||||
|
||||
if (queue[refid])
|
||||
queue -= refid // Removing any previous references that were GC'd so that the current object will be at the end of the list.
|
||||
var/list/queue = queues[level]
|
||||
queue[++queue.len] = list(queue_time, D, D.gc_destroyed) // not += for byond reasons
|
||||
|
||||
queue[refid] = gctime
|
||||
//this is mainly to separate things profile wise.
|
||||
/datum/controller/subsystem/garbage/proc/HardDelete(datum/D)
|
||||
++delslasttick
|
||||
++totaldels
|
||||
var/type = D.type
|
||||
var/refID = text_ref(D)
|
||||
var/datum/qdel_item/type_info = items[type]
|
||||
var/detail = D.dump_harddel_info()
|
||||
if(detail)
|
||||
LAZYADD(type_info.extra_details, detail)
|
||||
|
||||
// For profiling.
|
||||
/datum/controller/subsystem/garbage/proc/HardDelete(datum/A)
|
||||
var/time = world.timeofday
|
||||
var/tick = world.tick_usage
|
||||
var/ticktime = world.time
|
||||
var/tick_usage = TICK_USAGE
|
||||
del(D)
|
||||
tick_usage = TICK_USAGE_TO_MS(tick_usage)
|
||||
|
||||
var/type = A.type
|
||||
var/refID = "\ref[A]"
|
||||
type_info.hard_deletes++
|
||||
type_info.hard_delete_time += tick_usage
|
||||
if (tick_usage > type_info.hard_delete_max)
|
||||
type_info.hard_delete_max = tick_usage
|
||||
if (tick_usage > highest_del_ms)
|
||||
highest_del_ms = tick_usage
|
||||
highest_del_type_string = "[type]"
|
||||
|
||||
del(A)
|
||||
var/time = MS2DS(tick_usage)
|
||||
|
||||
tick = (world.tick_usage-tick+((world.time-ticktime)/world.tick_lag*100))
|
||||
if (tick > highest_del_tickusage)
|
||||
highest_del_tickusage = tick
|
||||
time = world.timeofday - time
|
||||
if (!time && TICK_DELTA_TO_MS(tick) > 1)
|
||||
time = TICK_DELTA_TO_MS(tick)/100
|
||||
if (time > highest_del_time)
|
||||
highest_del_time = time
|
||||
if (time > 10)
|
||||
log_subsystem_garbage_error("Error: [type]([refID]) took longer then 1 second to delete (took [time/10] seconds to delete)", type, TRUE)
|
||||
message_admins("Error: [type]([refID]) took longer then 1 second to delete (took [time/10] seconds to delete).")
|
||||
postpone(time/5)
|
||||
|
||||
/datum/controller/subsystem/garbage/proc/HardQueue(datum/A)
|
||||
if (istype(A) && A.gcDestroyed == GC_CURRENTLY_BEING_QDELETED)
|
||||
tobequeued += A
|
||||
A.gcDestroyed = GC_QUEUED_FOR_HARD_DEL
|
||||
if (time > 0.1 SECONDS)
|
||||
postpone(time)
|
||||
var/threshold = 0.5 // Used to be CONFIG_GET(number/hard_deletes_overrun_threshold)
|
||||
if (threshold && (time > threshold SECONDS))
|
||||
if (!(type_info.qdel_flags & QDEL_ITEM_ADMINS_WARNED))
|
||||
log_game("Error: [type]([refID]) took longer than [threshold] seconds to delete (took [round(time/10, 0.1)] seconds to delete)")
|
||||
message_admins("Error: [type]([refID]) took longer than [threshold] seconds to delete (took [round(time/10, 0.1)] seconds to delete).")
|
||||
type_info.qdel_flags |= QDEL_ITEM_ADMINS_WARNED
|
||||
type_info.hard_deletes_over_threshold++
|
||||
var/overrun_limit = 0 // Used to be CONFIG_GET(number/hard_deletes_overrun_limit)
|
||||
if (overrun_limit && type_info.hard_deletes_over_threshold >= overrun_limit)
|
||||
type_info.qdel_flags |= QDEL_ITEM_SUSPENDED_FOR_LAG
|
||||
|
||||
/datum/controller/subsystem/garbage/Recover()
|
||||
if (istype(SSgarbage.queue))
|
||||
queue |= SSgarbage.queue
|
||||
if (istype(SSgarbage.tobequeued))
|
||||
tobequeued |= SSgarbage.tobequeued
|
||||
InitQueues() //We first need to create the queues before recovering data
|
||||
if (istype(SSgarbage.queues))
|
||||
for (var/i in 1 to SSgarbage.queues.len)
|
||||
queues[i] |= SSgarbage.queues[i]
|
||||
|
||||
// Should be treated as a replacement for the 'del' keyword.
|
||||
// Datums passed to this will be given a chance to clean up references to allow the GC to collect them.
|
||||
/proc/qdel(datum/D, force=FALSE)
|
||||
if(!D)
|
||||
/// Qdel Item: Holds statistics on each type that passes thru qdel
|
||||
/datum/qdel_item
|
||||
var/name = "" //!Holds the type as a string for this type
|
||||
var/qdels = 0 //!Total number of times it's passed thru qdel.
|
||||
var/destroy_time = 0 //!Total amount of milliseconds spent processing this type's Destroy()
|
||||
var/failures = 0 //!Times it was queued for soft deletion but failed to soft delete.
|
||||
var/hard_deletes = 0 //!Different from failures because it also includes QDEL_HINT_HARDDEL deletions
|
||||
var/hard_delete_time = 0 //!Total amount of milliseconds spent hard deleting this type.
|
||||
var/hard_delete_max = 0 //!Highest time spent hard_deleting this in ms.
|
||||
var/hard_deletes_over_threshold = 0 //!Number of times hard deletes took longer than the configured threshold
|
||||
var/no_respect_force = 0 //!Number of times it's not respected force=TRUE
|
||||
var/no_hint = 0 //!Number of times it's not even bother to give a qdel hint
|
||||
var/slept_destroy = 0 //!Number of times it's slept in its destroy
|
||||
var/qdel_flags = 0 //!Flags related to this type's trip thru qdel.
|
||||
var/list/extra_details //!Lazylist of string metadata about the deleted objects
|
||||
|
||||
/datum/qdel_item/New(mytype)
|
||||
name = "[mytype]"
|
||||
|
||||
/// Should be treated as a replacement for the 'del' keyword.
|
||||
///
|
||||
/// Datums passed to this will be given a chance to clean up references to allow the GC to collect them.
|
||||
/proc/qdel(datum/to_delete, force = FALSE)
|
||||
if(!istype(to_delete))
|
||||
del(to_delete)
|
||||
return
|
||||
#ifdef REFERENCE_TRACKING
|
||||
SSgarbage.qdel_list += "[D.type]"
|
||||
#endif
|
||||
if(!istype(D))
|
||||
del(D)
|
||||
else if(isnull(D.gcDestroyed))
|
||||
if (SEND_SIGNAL(D, COMSIG_PARENT_PREQDELETED, force)) // Give the components a chance to prevent their parent from being deleted
|
||||
|
||||
var/datum/qdel_item/trash = SSgarbage.items[to_delete.type]
|
||||
if (isnull(trash))
|
||||
trash = SSgarbage.items[to_delete.type] = new /datum/qdel_item(to_delete.type)
|
||||
trash.qdels++
|
||||
|
||||
if(!isnull(to_delete.gc_destroyed))
|
||||
if(to_delete.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
|
||||
CRASH("[to_delete.type] destroy proc was called multiple times, likely due to a qdel loop in the Destroy logic")
|
||||
return
|
||||
|
||||
if (SEND_SIGNAL(to_delete, COMSIG_PREQDELETED, force)) // Give the components a chance to prevent their parent from being deleted
|
||||
return
|
||||
|
||||
to_delete.gc_destroyed = GC_CURRENTLY_BEING_QDELETED
|
||||
var/start_time = world.time
|
||||
var/start_tick = world.tick_usage
|
||||
SEND_SIGNAL(to_delete, COMSIG_QDELETING, force) // Let the (remaining) components know about the result of Destroy
|
||||
var/hint = to_delete.Destroy(force) // Let our friend know they're about to get fucked up.
|
||||
|
||||
if(world.time != start_time)
|
||||
trash.slept_destroy++
|
||||
else
|
||||
trash.destroy_time += TICK_USAGE_TO_MS(start_tick)
|
||||
|
||||
if(isnull(to_delete))
|
||||
return
|
||||
|
||||
switch(hint)
|
||||
if (QDEL_HINT_QUEUE) //qdel should queue the object for deletion.
|
||||
SSgarbage.Queue(to_delete)
|
||||
if (QDEL_HINT_IWILLGC)
|
||||
to_delete.gc_destroyed = world.time
|
||||
return
|
||||
// this SEND_SIGNAL should be above Destroy because Destroy sets signal_enabled to FALSE
|
||||
SEND_SIGNAL(D, COMSIG_QDELETING, force) // Let the (remaining) components know about the result of Destroy
|
||||
D.gcDestroyed = GC_CURRENTLY_BEING_QDELETED
|
||||
var/start_time = world.time
|
||||
var/hint = D.Destroy(force) // Let our friend know they're about to get fucked up.
|
||||
if(world.time != start_time)
|
||||
SSgarbage.sleptDestroy["[D.type]"]++
|
||||
if(!D)
|
||||
return
|
||||
switch(hint)
|
||||
if (QDEL_HINT_QUEUE) //qdel should queue the object for deletion.
|
||||
SSgarbage.QueueForQueuing(D)
|
||||
if (QDEL_HINT_IWILLGC)
|
||||
D.gcDestroyed = world.time
|
||||
if (QDEL_HINT_LETMELIVE) //qdel should let the object live after calling destory.
|
||||
if(!force)
|
||||
to_delete.gc_destroyed = null //clear the gc variable (important!)
|
||||
return
|
||||
if (QDEL_HINT_LETMELIVE) //qdel should let the object live after calling destory.
|
||||
if(!force)
|
||||
D.gcDestroyed = null //clear the gc variable (important!)
|
||||
return
|
||||
// Returning LETMELIVE after being told to force destroy
|
||||
// indicates the objects Destroy() does not respect force
|
||||
if(!SSgarbage.noforcerespect["[D.type]"])
|
||||
SSgarbage.noforcerespect["[D.type]"] = "[D.type]"
|
||||
log_subsystem_garbage_warning("WARNING: [D.type] has been force deleted, but is \
|
||||
returning an immortal QDEL_HINT, indicating it does \
|
||||
not respect the force flag for qdel(). It has been \
|
||||
placed in the queue, further instances of this type \
|
||||
will also be queued.")
|
||||
SSgarbage.QueueForQueuing(D)
|
||||
if (QDEL_HINT_HARDDEL) //qdel should assume this object won't gc, and queue a hard delete using a hard reference to save time from the locate()
|
||||
SSgarbage.HardQueue(D)
|
||||
if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste.
|
||||
SSgarbage.HardDelete(D)
|
||||
if (QDEL_HINT_FINDREFERENCE)//qdel will, if REFERENCE_TRACKING is enabled, display all references to this object, then queue the object for deletion.
|
||||
SSgarbage.QueueForQueuing(D)
|
||||
#ifdef REFERENCE_TRACKING
|
||||
INVOKE_ASYNC(D, TYPE_PROC_REF(/datum, find_references), TRUE)
|
||||
#endif
|
||||
if (QDEL_HINT_IFFAIL_FINDREFERENCE) // qdel will, if REFERENCE_TRACKING is enabled and the object fails to collect, display all references to this object
|
||||
SSgarbage.QueueForQueuing(D)
|
||||
#ifdef REFERENCE_TRACKING
|
||||
SSgarbage.reference_find_on_fail[text_ref(D)] = TRUE
|
||||
#endif
|
||||
else
|
||||
if(!SSgarbage.noqdelhint["[D.type]"])
|
||||
SSgarbage.noqdelhint["[D.type]"] = "[D.type]"
|
||||
log_subsystem_garbage_warning("WARNING: [D.type] is not returning a qdel hint. It is being placed in the queue. Further instances of this type will also be queued.")
|
||||
SSgarbage.QueueForQueuing(D)
|
||||
else if(D.gcDestroyed == GC_CURRENTLY_BEING_QDELETED)
|
||||
CRASH("[D.type] destroy proc was called multiple times, likely due to a qdel loop in the Destroy logic")
|
||||
|
||||
/client/Destroy()
|
||||
..()
|
||||
return QDEL_HINT_HARDDEL_NOW
|
||||
|
||||
|
||||
// REFERENCE TRACKING (the old garbage-debug file) //
|
||||
// Only present if the appropriate define is set //
|
||||
#ifdef REFERENCE_TRACKING
|
||||
|
||||
/datum/verb/find_refs()
|
||||
set category = "Debug"
|
||||
set name = "Find References"
|
||||
set background = 1
|
||||
set src in world
|
||||
|
||||
find_references(FALSE)
|
||||
|
||||
/client/verb/show_qdeleted()
|
||||
set category = "Debug"
|
||||
set name = "Show qdel() Log"
|
||||
set desc = "Render the qdel() log and display it"
|
||||
|
||||
var/dat = "<B>List of things that have been qdel()eted this round</B><BR><BR>"
|
||||
|
||||
var/tmplist = list()
|
||||
for(var/elem in SSgarbage.qdel_list)
|
||||
if(!(elem in tmplist))
|
||||
tmplist[elem] = 0
|
||||
tmplist[elem]++
|
||||
|
||||
sortTim(tmplist, GLOBAL_PROC_REF(cmp_numeric_dsc), TRUE)
|
||||
|
||||
for(var/path in tmplist)
|
||||
dat += "[path] - [tmplist[path]] times<BR>"
|
||||
|
||||
usr << browse(dat, "window=qdeletedlog")
|
||||
|
||||
/datum/proc/find_references(skip_alert)
|
||||
running_find_references = type
|
||||
if(usr?.client)
|
||||
if(usr.client.running_find_references)
|
||||
testing("CANCELLED search for references to a [usr.client.running_find_references].")
|
||||
usr.client.running_find_references = null
|
||||
running_find_references = null
|
||||
SSgarbage.can_fire = TRUE
|
||||
SSgarbage.update_nextfire(reset_time = TRUE)
|
||||
return
|
||||
|
||||
if(!skip_alert && alert(usr, "Running this will lock everything up for 5+ minutes. Would you like to begin the search?", "Find References", "Yes", "No") != "Yes")
|
||||
running_find_references = null
|
||||
return
|
||||
|
||||
SSgarbage.can_fire = FALSE // Keeps the GC from failing to collect objects being searched for here
|
||||
|
||||
if(usr?.client)
|
||||
usr.client.running_find_references = type
|
||||
|
||||
//Time to search the whole game for our ref
|
||||
testing("Beginning search for references to a [type].")
|
||||
var/starting_time = world.time
|
||||
|
||||
//Yes we do actually need to do this. The searcher refuses to read weird lists
|
||||
//And global.vars is a really weird list
|
||||
var/global_vars = list()
|
||||
for(var/key in global.vars)
|
||||
global_vars[key] = global.vars[key]
|
||||
|
||||
search_var(global_vars, "Native Global", search_time = starting_time)
|
||||
testing("Finished searching native globals")
|
||||
|
||||
for(var/datum/thing in world) // atoms (don't believe its lies)
|
||||
search_var(thing, "World -> [thing.type]", search_time = starting_time)
|
||||
testing("Finished searching atoms")
|
||||
|
||||
for(var/datum/thing) // datums
|
||||
search_var(thing, "Datums -> [thing.type]", search_time = starting_time)
|
||||
testing("Finished searching datums")
|
||||
|
||||
//Warning, attempting to search clients like this will cause crashes if done on live. Watch yourself
|
||||
for(var/client/thing) // clients
|
||||
search_var(thing, "Clients -> [thing.type]", search_time = starting_time)
|
||||
testing("Finished searching clients")
|
||||
|
||||
testing("Completed all searches for references to a [type].")
|
||||
|
||||
if(usr?.client)
|
||||
usr.client.running_find_references = null
|
||||
running_find_references = null
|
||||
|
||||
SSgarbage.can_fire = TRUE //restart the garbage collector
|
||||
SSgarbage.update_nextfire(reset_time = TRUE) //restart the garbage collector
|
||||
|
||||
/datum/proc/search_var(potential_container, container_name, recursive_limit = 64, search_time = world.time)
|
||||
//If we are performing a search without a check tick, we should avoid sleeping
|
||||
#if defined(FIND_REF_NO_CHECK_TICK)
|
||||
SHOULD_NOT_SLEEP(TRUE)
|
||||
#endif
|
||||
|
||||
#ifdef REFERENCE_TRACKING_DEBUG
|
||||
if(SSgarbage.should_save_refs && !found_refs)
|
||||
found_refs = list()
|
||||
#endif
|
||||
|
||||
if(usr?.client && !usr.client.running_find_references)
|
||||
return
|
||||
|
||||
if(!recursive_limit)
|
||||
testing("Recursion limit reached. [container_name]")
|
||||
return
|
||||
|
||||
//Check each time you go down a layer. This makes it a bit slow, but it won't effect the rest of the game at all
|
||||
#ifndef FIND_REF_NO_CHECK_TICK
|
||||
CHECK_TICK
|
||||
#endif
|
||||
|
||||
if(isdatum(potential_container))
|
||||
var/datum/datum_container = potential_container
|
||||
if(datum_container.last_find_references == search_time)
|
||||
return
|
||||
|
||||
datum_container.last_find_references = search_time
|
||||
var/list/vars_list = datum_container.vars
|
||||
|
||||
for(var/varname in vars_list)
|
||||
#ifndef FIND_REF_NO_CHECK_TICK
|
||||
CHECK_TICK
|
||||
// Returning LETMELIVE after being told to force destroy
|
||||
// indicates the objects Destroy() does not respect force
|
||||
#ifdef TESTING
|
||||
if(!trash.no_respect_force)
|
||||
testing("WARNING: [to_delete.type] has been force deleted, but is \
|
||||
returning an immortal QDEL_HINT, indicating it does \
|
||||
not respect the force flag for qdel(). It has been \
|
||||
placed in the queue, further instances of this type \
|
||||
will also be queued.")
|
||||
#endif
|
||||
if (varname == "vars" || varname == "vis_locs") //Fun fact, vis_locs don't count for references
|
||||
continue
|
||||
var/variable = vars_list[varname]
|
||||
trash.no_respect_force++
|
||||
|
||||
if(variable == src)
|
||||
#ifdef REFERENCE_TRACKING_DEBUG
|
||||
if(SSgarbage.should_save_refs)
|
||||
found_refs[varname] = TRUE
|
||||
continue //End early, don't want these logging
|
||||
#endif
|
||||
testing("Found [type] [text_ref(src)] in [datum_container.type]'s [text_ref(datum_container)] [varname] var. [container_name]")
|
||||
continue
|
||||
|
||||
if(islist(variable))
|
||||
search_var(variable, "[container_name] [text_ref(datum_container)] -> [varname] (list)", recursive_limit - 1, search_time)
|
||||
|
||||
else if(islist(potential_container))
|
||||
var/normal = IS_NORMAL_LIST(potential_container)
|
||||
var/list/potential_cache = potential_container
|
||||
for(var/element_in_list in potential_cache)
|
||||
#ifndef FIND_REF_NO_CHECK_TICK
|
||||
CHECK_TICK
|
||||
SSgarbage.Queue(to_delete)
|
||||
if (QDEL_HINT_HARDDEL) //qdel should assume this object won't gc, and queue a hard delete
|
||||
SSgarbage.Queue(to_delete, GC_QUEUE_HARDDELETE)
|
||||
if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste.
|
||||
SSgarbage.HardDelete(to_delete)
|
||||
#ifdef REFERENCE_TRACKING
|
||||
if (QDEL_HINT_FINDREFERENCE) //qdel will, if REFERENCE_TRACKING is enabled, display all references to this object, then queue the object for deletion.
|
||||
SSgarbage.Queue(to_delete)
|
||||
INVOKE_ASYNC(to_delete, TYPE_PROC_REF(/datum, find_references))
|
||||
if (QDEL_HINT_IFFAIL_FINDREFERENCE) //qdel will, if REFERENCE_TRACKING is enabled and the object fails to collect, display all references to this object.
|
||||
SSgarbage.Queue(to_delete)
|
||||
SSgarbage.reference_find_on_fail[text_ref(to_delete)] = TRUE
|
||||
#endif
|
||||
else
|
||||
#ifdef TESTING
|
||||
if(!trash.no_hint)
|
||||
testing("WARNING: [to_delete.type] is not returning a qdel hint. It is being placed in the queue. Further instances of this type will also be queued.")
|
||||
#endif
|
||||
// Check normal entries
|
||||
if(element_in_list == src)
|
||||
#ifdef REFERENCE_TRACKING_DEBUG
|
||||
if(SSgarbage.should_save_refs)
|
||||
found_refs[potential_cache] = TRUE
|
||||
continue //End early, don't want these logging
|
||||
#endif
|
||||
testing("Found [type] [text_ref(src)] in list [container_name]\[[element_in_list]\]")
|
||||
continue
|
||||
|
||||
var/assoc_val = null
|
||||
if(!isnum(element_in_list) && normal)
|
||||
assoc_val = potential_cache[element_in_list]
|
||||
// Check assoc entries
|
||||
if(assoc_val == src)
|
||||
#ifdef REFERENCE_TRACKING_DEBUG
|
||||
if(SSgarbage.should_save_refs)
|
||||
found_refs[potential_cache] = TRUE
|
||||
continue //End early, don't want these logging
|
||||
#endif
|
||||
testing("Found [type] [text_ref(src)] in list [container_name]\[[element_in_list]\]")
|
||||
continue
|
||||
|
||||
//We need to run both of these checks, since our object could be hiding in either of them
|
||||
// Check normal sublists
|
||||
if(islist(element_in_list))
|
||||
search_var(element_in_list, "[container_name] -> [element_in_list] (list)", recursive_limit - 1, search_time)
|
||||
// Check assoc sublists
|
||||
if(islist(assoc_val))
|
||||
search_var(potential_container[element_in_list], "[container_name]\[[element_in_list]\] -> [assoc_val] (list)", recursive_limit - 1, search_time)
|
||||
|
||||
/proc/qdel_and_find_ref_if_fail(datum/thing_to_qdel, force = FALSE)
|
||||
thing_to_qdel.qdel_and_find_ref_if_fail(force)
|
||||
|
||||
/datum/proc/qdel_and_find_ref_if_fail(force = FALSE)
|
||||
SSgarbage.reference_find_on_fail[text_ref(src)] = TRUE
|
||||
qdel(src, force)
|
||||
|
||||
#endif
|
||||
trash.no_hint++
|
||||
SSgarbage.Queue(to_delete)
|
||||
|
||||
@@ -108,6 +108,8 @@ SUBSYSTEM_DEF(radio)
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/radio/proc/add_object(obj/device, new_frequency, filter = null)
|
||||
SHOULD_NOT_SLEEP(TRUE)
|
||||
|
||||
var/f_text = num2text(new_frequency)
|
||||
var/datum/radio_frequency/frequency = frequencies[f_text]
|
||||
|
||||
@@ -120,6 +122,8 @@ SUBSYSTEM_DEF(radio)
|
||||
return frequency
|
||||
|
||||
/datum/controller/subsystem/radio/proc/remove_object(obj/device, old_frequency)
|
||||
SHOULD_NOT_SLEEP(TRUE)
|
||||
|
||||
var/f_text = num2text(old_frequency)
|
||||
var/datum/radio_frequency/frequency = frequencies[f_text]
|
||||
|
||||
@@ -127,10 +131,14 @@ SUBSYSTEM_DEF(radio)
|
||||
frequency.remove_listener(device)
|
||||
|
||||
/datum/controller/subsystem/radio/proc/remove_object_all(obj/device)
|
||||
SHOULD_NOT_SLEEP(TRUE)
|
||||
|
||||
for(var/freq in frequencies)
|
||||
SSradio.remove_object(device, text2num(freq))
|
||||
|
||||
/datum/controller/subsystem/radio/proc/get_devices(freq, filter = RADIO_DEFAULT)
|
||||
SHOULD_NOT_SLEEP(TRUE)
|
||||
|
||||
var/datum/radio_frequency/frequency = frequencies[num2text(freq)]
|
||||
if(!frequency)
|
||||
return
|
||||
@@ -150,6 +158,8 @@ SUBSYSTEM_DEF(radio)
|
||||
|
||||
// Used to test connectivity to the telecomms network.
|
||||
/datum/controller/subsystem/radio/proc/telecomms_ping(obj/O, test_freq = PUB_FREQ)
|
||||
SHOULD_NOT_SLEEP(TRUE)
|
||||
|
||||
var/datum/signal/subspace/testsig = new(O, test_freq)
|
||||
for (var/obj/machinery/telecomms/R in SSmachinery.all_receivers)
|
||||
if(R.receive_range(testsig) >= 0)
|
||||
@@ -159,6 +169,8 @@ SUBSYSTEM_DEF(radio)
|
||||
|
||||
//callback used by objects to react to incoming radio signals
|
||||
/obj/proc/receive_signal(datum/signal/signal, receive_method, receive_param)
|
||||
SHOULD_NOT_SLEEP(TRUE)
|
||||
|
||||
return null
|
||||
|
||||
/proc/frequency_span_class(var/frequency)
|
||||
|
||||
@@ -131,7 +131,7 @@ SUBSYSTEM_DEF(spatial_grid)
|
||||
if(movable_turf)
|
||||
enter_cell(movable, movable_turf)
|
||||
|
||||
UnregisterSignal(movable, COMSIG_PARENT_PREQDELETED)
|
||||
UnregisterSignal(movable, COMSIG_PREQDELETED)
|
||||
waiting_to_add_by_type[channel_type] -= movable
|
||||
|
||||
pregenerate_more_oranges_ears(NUMBER_OF_PREGENERATED_ORANGES_EARS)
|
||||
@@ -143,7 +143,7 @@ SUBSYSTEM_DEF(spatial_grid)
|
||||
|
||||
///add a movable to the pre init queue for whichever type is specified so that when the subsystem initializes they get added to the grid
|
||||
/datum/controller/subsystem/spatial_grid/proc/enter_pre_init_queue(atom/movable/waiting_movable, type)
|
||||
RegisterSignal(waiting_movable, COMSIG_PARENT_PREQDELETED, PROC_REF(queued_item_deleted), override = TRUE)
|
||||
RegisterSignal(waiting_movable, COMSIG_PREQDELETED, PROC_REF(queued_item_deleted), override = TRUE)
|
||||
//override because something can enter the queue for two different types but that is done through unrelated procs that shouldnt know about eachother
|
||||
waiting_to_add_by_type[type] += waiting_movable
|
||||
|
||||
@@ -158,11 +158,11 @@ SUBSYSTEM_DEF(spatial_grid)
|
||||
waiting_movable_is_in_other_queues = TRUE
|
||||
|
||||
if(!waiting_movable_is_in_other_queues)
|
||||
UnregisterSignal(movable_to_remove, COMSIG_PARENT_PREQDELETED)
|
||||
UnregisterSignal(movable_to_remove, COMSIG_PREQDELETED)
|
||||
|
||||
return
|
||||
|
||||
UnregisterSignal(movable_to_remove, COMSIG_PARENT_PREQDELETED)
|
||||
UnregisterSignal(movable_to_remove, COMSIG_PREQDELETED)
|
||||
for(var/type in waiting_to_add_by_type)
|
||||
waiting_to_add_by_type[type] -= movable_to_remove
|
||||
|
||||
|
||||
+50
-24
@@ -1,15 +1,14 @@
|
||||
/datum
|
||||
var/tmp/list/active_timers
|
||||
var/tmp/datum/weakref/weakref
|
||||
var/tmp/isprocessing = 0
|
||||
|
||||
/**
|
||||
* Tick count time when this object was destroyed.
|
||||
*
|
||||
* If this is non zero then the object has been garbage collected and is awaiting either
|
||||
* a hard del by the GC subsystme, or to be autocollected (if it has no references)
|
||||
*/
|
||||
var/tmp/gcDestroyed
|
||||
var/gc_destroyed
|
||||
|
||||
var/tmp/list/active_timers
|
||||
var/tmp/isprocessing = 0
|
||||
|
||||
/// Status traits attached to this datum. associative list of the form: list(trait name (string) = list(source1, source2, source3,...))
|
||||
var/list/status_traits
|
||||
@@ -24,10 +23,9 @@
|
||||
/// Is this datum capable of sending signals?
|
||||
/// Set to true when a signal has been registered
|
||||
var/signal_enabled = FALSE
|
||||
/// A cached version of our \ref
|
||||
/// The brunt of \ref costs are in creating entries in the string tree (a tree of immutable strings)
|
||||
/// This avoids doing that more then once per datum by ensuring ref strings always have a reference to them after they're first pulled
|
||||
var/cached_ref
|
||||
|
||||
/// A weak reference to another datum
|
||||
var/datum/weakref/weak_reference
|
||||
|
||||
#ifdef REFERENCE_TRACKING
|
||||
var/running_find_references
|
||||
@@ -38,29 +36,29 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// If we have called dump_harddel_info already. Used to avoid duped calls (since we call it immediately in some cases on failure to process)
|
||||
// Create and destroy is weird and I wanna cover my bases
|
||||
var/harddel_deets_dumped = FALSE
|
||||
|
||||
// Default implementation of clean-up code.
|
||||
// This should be overridden to remove all references pointing to the object being destroyed.
|
||||
// Return the appropriate QDEL_HINT; in most cases this is QDEL_HINT_QUEUE.
|
||||
/datum/proc/Destroy(force=FALSE)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
//SHOULD_NOT_SLEEP(TRUE) //Soon my friend, soon...
|
||||
|
||||
weakref = null
|
||||
GLOB.destroyed_event.raise_event(src)
|
||||
var/ui_key = SOFTREF(src)
|
||||
if(LAZYISIN(SSnanoui.open_uis, ui_key))
|
||||
SSnanoui.close_uis(src)
|
||||
tag = null
|
||||
var/list/timers = active_timers
|
||||
active_timers = null
|
||||
if (timers)
|
||||
for (var/thing in timers)
|
||||
var/datum/timedevent/timer = thing
|
||||
if (timer.spent)
|
||||
continue
|
||||
qdel(timer)
|
||||
weak_reference = null //ensure prompt GCing of weakref.
|
||||
|
||||
// Handle components & signals
|
||||
signal_enabled = FALSE
|
||||
if(active_timers)
|
||||
var/list/timers = active_timers
|
||||
active_timers = null
|
||||
if (timers)
|
||||
for (var/thing in timers)
|
||||
var/datum/timedevent/timer = thing
|
||||
if (timer.spent)
|
||||
continue
|
||||
qdel(timer)
|
||||
|
||||
#ifdef REFERENCE_TRACKING
|
||||
#ifdef REFERENCE_TRACKING_DEBUG
|
||||
@@ -68,6 +66,19 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
GLOB.destroyed_event.raise_event(src)
|
||||
if (!isturf(src))
|
||||
cleanup_events(src)
|
||||
|
||||
var/ui_key = SOFTREF(src)
|
||||
if(LAZYISIN(SSnanoui.open_uis, ui_key))
|
||||
SSnanoui.close_uis(src)
|
||||
|
||||
|
||||
// Handle components & signals
|
||||
signal_enabled = FALSE
|
||||
|
||||
//BEGIN: ECS SHIT
|
||||
var/list/dc = datum_components
|
||||
if(dc)
|
||||
var/all_components = dc[/datum/component]
|
||||
@@ -95,6 +106,7 @@
|
||||
|
||||
for(var/target in signal_procs)
|
||||
UnregisterSignal(target, signal_procs[target])
|
||||
//END: ECS SHIT
|
||||
|
||||
return QDEL_HINT_QUEUE
|
||||
|
||||
@@ -125,3 +137,17 @@
|
||||
return FALSE
|
||||
vars[var_name] = var_value
|
||||
return TRUE
|
||||
|
||||
|
||||
/// Return text from this proc to provide extra context to hard deletes that happen to it
|
||||
/// Optional, you should use this for cases where replication is difficult and extra context is required
|
||||
/// Can be called more then once per object, use harddel_deets_dumped to avoid duplicate calls (I am so sorry)
|
||||
/datum/proc/dump_harddel_info()
|
||||
return
|
||||
|
||||
///images are pretty generic, this should help a bit with tracking harddels related to them
|
||||
/image/dump_harddel_info()
|
||||
if(harddel_deets_dumped)
|
||||
return
|
||||
harddel_deets_dumped = TRUE
|
||||
return "Image icon: [icon] - icon_state: [icon_state] [loc ? "loc: [loc] ([loc.x],[loc.y],[loc.z])" : ""]"
|
||||
|
||||
@@ -2,17 +2,22 @@
|
||||
var/list/steps
|
||||
var/atom/holder
|
||||
var/result
|
||||
var/list/steps_desc
|
||||
var/current_desc = null
|
||||
|
||||
/datum/construction/New(atom)
|
||||
..()
|
||||
holder = atom
|
||||
if(!holder) //don't want this without a holder
|
||||
spawn
|
||||
qdel(src)
|
||||
qdel(src)
|
||||
set_desc(steps.len)
|
||||
|
||||
/datum/construction/Destroy(force)
|
||||
holder = null
|
||||
|
||||
steps = null
|
||||
|
||||
. = ..()
|
||||
|
||||
/datum/construction/proc/next_step()
|
||||
steps.len--
|
||||
if(!steps.len)
|
||||
@@ -60,8 +65,7 @@
|
||||
/datum/construction/proc/spawn_result()
|
||||
if(result)
|
||||
new result(get_turf(holder))
|
||||
spawn()
|
||||
qdel(holder)
|
||||
QDEL_NULL(holder)
|
||||
|
||||
/datum/construction/proc/set_desc(index as num)
|
||||
var/list/step = steps[index]
|
||||
|
||||
@@ -1,4 +1 @@
|
||||
/****************
|
||||
* Debug Support *
|
||||
****************/
|
||||
GLOBAL_LIST_EMPTY(all_observable_events)
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
GLOBAL_LIST_EMPTY(global_listen_count)
|
||||
GLOBAL_LIST_EMPTY(event_sources_count)
|
||||
GLOBAL_LIST_EMPTY(event_listen_count)
|
||||
|
||||
/proc/cleanup_events(source)
|
||||
if(GLOB.global_listen_count && GLOB.global_listen_count[source])
|
||||
cleanup_global_listener(source, GLOB.global_listen_count[source])
|
||||
if(GLOB.event_sources_count && GLOB.event_sources_count[source])
|
||||
cleanup_source_listeners(source, GLOB.event_sources_count[source])
|
||||
if(GLOB.event_listen_count && GLOB.event_listen_count[source])
|
||||
cleanup_event_listener(source, GLOB.event_listen_count[source])
|
||||
|
||||
/singleton/observ/register(datum/event_source, datum/listener, proc_call)
|
||||
. = ..()
|
||||
if(.)
|
||||
GLOB.event_sources_count[event_source] += 1
|
||||
GLOB.event_listen_count[listener] += 1
|
||||
|
||||
/singleton/observ/unregister(datum/event_source, datum/listener, proc_call)
|
||||
. = ..()
|
||||
if(.)
|
||||
GLOB.event_sources_count[event_source] -= 1
|
||||
GLOB.event_listen_count[listener] -= 1
|
||||
|
||||
if(GLOB.event_sources_count[event_source] <= 0)
|
||||
GLOB.event_sources_count -= event_source
|
||||
if(GLOB.event_listen_count[listener] <= 0)
|
||||
GLOB.event_listen_count -= listener
|
||||
|
||||
/singleton/observ/register_global(datum/listener, proc_call)
|
||||
. = ..()
|
||||
if(.)
|
||||
GLOB.global_listen_count[listener] += 1
|
||||
|
||||
/singleton/observ/unregister_global(datum/listener, proc_call)
|
||||
. = ..()
|
||||
if(.)
|
||||
GLOB.global_listen_count[listener] -= 1
|
||||
if(GLOB.global_listen_count[listener] <= 0)
|
||||
GLOB.global_listen_count -= listener
|
||||
|
||||
/proc/cleanup_global_listener(listener, listen_count)
|
||||
GLOB.global_listen_count -= listener
|
||||
for(var/entry in GLOB.all_observable_events)
|
||||
var/singleton/observ/event = entry
|
||||
if(event.unregister_global(listener))
|
||||
log_debug("[event] - [listener] was deleted while still registered to global events.")
|
||||
if(!(--listen_count))
|
||||
return
|
||||
|
||||
/proc/cleanup_source_listeners(event_source, source_listener_count)
|
||||
GLOB.event_sources_count -= event_source
|
||||
for(var/entry in GLOB.all_observable_events)
|
||||
var/singleton/observ/event = entry
|
||||
var/proc_owners = event.event_sources[event_source]
|
||||
if(proc_owners)
|
||||
for(var/proc_owner in proc_owners)
|
||||
if(event.unregister(event_source, proc_owner))
|
||||
log_debug("[event] - [event_source] was deleted while still being listened to by [proc_owner].")
|
||||
if(!(--source_listener_count))
|
||||
return
|
||||
|
||||
/proc/cleanup_event_listener(listener, listener_count)
|
||||
GLOB.event_listen_count -= listener
|
||||
for(var/entry in GLOB.all_observable_events)
|
||||
var/singleton/observ/event = entry
|
||||
for(var/event_source in event.event_sources)
|
||||
if(event.unregister(event_source, listener))
|
||||
log_debug("[event] - [listener] was deleted while still listening to [event_source].")
|
||||
if(!(--listener_count))
|
||||
return
|
||||
@@ -1,17 +0,0 @@
|
||||
/datum/weakref
|
||||
var/ref
|
||||
|
||||
/datum/weakref/New(datum/D)
|
||||
ref = SOFTREF(D)
|
||||
|
||||
/datum/weakref/Destroy(force = 0)
|
||||
crash_with("Some fuck is trying to [force ? "force-" : ""]delete a weakref!")
|
||||
if (!force)
|
||||
return QDEL_HINT_LETMELIVE // feck off
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/weakref/proc/resolve()
|
||||
var/datum/D = locate(ref)
|
||||
if (!QDELETED(D) && D.weakref == src)
|
||||
. = D
|
||||
@@ -0,0 +1,95 @@
|
||||
/// Creates a weakref to the given input.
|
||||
/// See /datum/weakref's documentation for more information.
|
||||
/proc/WEAKREF(datum/input)
|
||||
if(istype(input) && !QDELETED(input))
|
||||
if(isweakref(input))
|
||||
return input
|
||||
|
||||
if(!input.weak_reference)
|
||||
input.weak_reference = new /datum/weakref(input)
|
||||
return input.weak_reference
|
||||
|
||||
/datum/proc/create_weakref() //Forced creation for admin proccalls
|
||||
return WEAKREF(src)
|
||||
|
||||
/**
|
||||
* A weakref holds a non-owning reference to a datum.
|
||||
* The datum can be referenced again using `resolve()`.
|
||||
*
|
||||
* To figure out why this is important, you must understand how deletion in
|
||||
* BYOND works.
|
||||
*
|
||||
* Imagine a datum as a TV in a living room. When one person enters to watch
|
||||
* TV, they turn it on. Others can come into the room and watch the TV.
|
||||
* When the last person leaves the room, they turn off the TV because it's
|
||||
* no longer being used.
|
||||
*
|
||||
* A datum being deleted tells everyone who's watching the TV to stop.
|
||||
* If everyone leaves properly (AKA cleaning up their references), then the
|
||||
* last person will turn off the TV, and everything is well.
|
||||
* However, if someone is resistant (holds a hard reference after deletion),
|
||||
* then someone has to walk in, drag them away, and turn off the TV forecefully.
|
||||
* This process is very slow, and it's known as hard deletion.
|
||||
*
|
||||
* This is where weak references come in. Weak references don't count as someone
|
||||
* watching the TV. Thus, when what it's referencing is destroyed, it will
|
||||
* hopefully clean up properly, and limit hard deletions.
|
||||
*
|
||||
* A common use case for weak references is holding onto what created itself.
|
||||
* For example, if a machine wanted to know what its last user was, it might
|
||||
* create a `var/mob/living/last_user`. However, this is a strong reference to
|
||||
* the mob, and thus will force a hard deletion when that mob is deleted.
|
||||
* It is often better in this case to instead create a weakref to the user,
|
||||
* meaning this type definition becomes `var/datum/weakref/last_user`.
|
||||
*
|
||||
* A good rule of thumb is that you should hold strong references to things
|
||||
* that you *own*. For example, a dog holding a chew toy would be the owner
|
||||
* of that chew toy, and thus a `var/obj/item/chew_toy` reference is fine
|
||||
* (as long as it is cleaned up properly).
|
||||
* However, a chew toy does not own its dog, so a `var/mob/living/dog/owner`
|
||||
* might be inferior to a weakref.
|
||||
* This is also a good rule of thumb to avoid circular references, such as the
|
||||
* chew toy example. A circular reference that doesn't clean itself up properly
|
||||
* will always hard delete.
|
||||
*/
|
||||
/datum/weakref
|
||||
var/reference
|
||||
|
||||
/datum/weakref/New(datum/thing)
|
||||
reference = text_ref(thing)
|
||||
|
||||
/datum/weakref/Destroy(force)
|
||||
var/datum/target = resolve()
|
||||
qdel(target)
|
||||
|
||||
if(!force)
|
||||
return QDEL_HINT_LETMELIVE //Let BYOND autoGC thiswhen nothing is using it anymore.
|
||||
target?.weak_reference = null
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* Retrieves the datum that this weakref is referencing.
|
||||
*
|
||||
* This will return `null` if the datum was deleted. This MUST be respected.
|
||||
*/
|
||||
/datum/weakref/proc/resolve()
|
||||
var/datum/D = locate(reference)
|
||||
return (!QDELETED(D) && D.weak_reference == src) ? D : null
|
||||
|
||||
/**
|
||||
* SERIOUSLY READ THE AUTODOC COMMENT FOR THIS PROC BEFORE EVEN THINKING ABOUT USING IT
|
||||
*
|
||||
* Like resolve, but doesn't care if the datum is being qdeleted but hasn't been deleted yet.
|
||||
*
|
||||
* The return value of this proc leaves hanging references if the datum is being qdeleted but hasn't been deleted yet.
|
||||
*
|
||||
* Do not do anything that would create a lasting reference to the return value, such as giving it a tag, putting it on the map,
|
||||
* adding it to an atom's contents or vis_contents, giving it a key (if it's a mob), attaching it to an atom (if it's an image),
|
||||
* or assigning it to a datum or list referenced somewhere other than a temporary value.
|
||||
*
|
||||
* Unless you're resolving a weakref to a datum in a COMSIG_QDELETING signal handler registered on that very same datum,
|
||||
* just use resolve instead.
|
||||
*/
|
||||
/datum/weakref/proc/hard_resolve()
|
||||
var/datum/D = locate(reference)
|
||||
return (D?.weak_reference == src) ? D : null
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
if(QDELING(A))
|
||||
// Check init_start_time to not worry about atoms created before the atoms SS that are cleaned up before this
|
||||
if (A.gcDestroyed > init_start_time)
|
||||
if (A.gc_destroyed > init_start_time)
|
||||
BadInitializeCalls[the_type] |= BAD_INIT_QDEL_BEFORE
|
||||
return TRUE
|
||||
|
||||
|
||||
@@ -222,6 +222,29 @@
|
||||
active_power_usage = 400
|
||||
var/waiting_for_user_input=0 // Fix for #274 (Mash create block injector without answering dialog to make unlimited injectors) - N3X
|
||||
|
||||
/obj/machinery/computer/scan_consolenew/Initialize()
|
||||
..()
|
||||
for(var/i=0;i<3;i++)
|
||||
buffers[i+1]=new /datum/dna2/record
|
||||
return INITIALIZE_HINT_LATELOAD
|
||||
|
||||
/obj/machinery/computer/scan_consolenew/LateInitialize()
|
||||
. = ..()
|
||||
for(dir in list(NORTH,EAST,SOUTH,WEST))
|
||||
connected = locate(/obj/machinery/dna_scannernew, get_step(src, dir))
|
||||
if(!isnull(connected))
|
||||
break
|
||||
|
||||
src.injector_ready = 1
|
||||
|
||||
/obj/machinery/computer/scan_consolenew/Destroy()
|
||||
connected = null
|
||||
disk = null
|
||||
|
||||
QDEL_LIST_ASSOC(buffers)
|
||||
|
||||
. = ..()
|
||||
|
||||
/obj/machinery/computer/scan_consolenew/attackby(obj/item/I as obj, mob/user as mob)
|
||||
if (istype(I, /obj/item/disk/data)) //INSERT SOME diskS
|
||||
if (!src.disk)
|
||||
@@ -234,31 +257,14 @@
|
||||
return ..()
|
||||
|
||||
/obj/machinery/computer/scan_consolenew/ex_act(severity)
|
||||
|
||||
switch(severity)
|
||||
if(1.0)
|
||||
//SN src = null
|
||||
qdel(src)
|
||||
return
|
||||
if(2.0)
|
||||
if (prob(50))
|
||||
//SN src = null
|
||||
qdel(src)
|
||||
return
|
||||
return
|
||||
|
||||
/obj/machinery/computer/scan_consolenew/New()
|
||||
..()
|
||||
for(var/i=0;i<3;i++)
|
||||
buffers[i+1]=new /datum/dna2/record
|
||||
spawn(5)
|
||||
for(dir in list(NORTH,EAST,SOUTH,WEST))
|
||||
connected = locate(/obj/machinery/dna_scannernew, get_step(src, dir))
|
||||
if(!isnull(connected))
|
||||
break
|
||||
spawn(250)
|
||||
src.injector_ready = 1
|
||||
return
|
||||
return
|
||||
|
||||
/obj/machinery/computer/scan_consolenew/proc/all_dna_blocks(var/list/buffer)
|
||||
|
||||
@@ -35,20 +35,31 @@
|
||||
var/pulses_remaining = 40 // Lasts 20 seconds.
|
||||
var/instability_power = 5
|
||||
var/instability_range = 6
|
||||
var/timer_id = null
|
||||
|
||||
/obj/effect/temporary_effect/destabilize/Initialize()
|
||||
..()
|
||||
return INITIALIZE_HINT_LATELOAD
|
||||
|
||||
/obj/effect/temporary_effect/destabilize/LateInitialize()
|
||||
. = ..()
|
||||
timer_id = addtimer(CALLBACK(src, PROC_REF(radiate_loop)), 5 SECONDS, TIMER_UNIQUE|TIMER_STOPPABLE)
|
||||
|
||||
/obj/effect/temporary_effect/destabilize/Destroy()
|
||||
deltimer(timer_id)
|
||||
. = ..()
|
||||
radiate_loop()
|
||||
|
||||
/obj/effect/temporary_effect/destabilize/proc/radiate_loop()
|
||||
set waitfor = FALSE
|
||||
|
||||
while(pulses_remaining)
|
||||
sleep(5)
|
||||
if(pulses_remaining && !QDELETED(src))
|
||||
for(var/mob/living/L in range(src, instability_range) )
|
||||
var/radius = max(get_dist(L, src), 1)
|
||||
// Being farther away lessens the amount of instability received.
|
||||
var/outgoing_instability = instability_power * ( 1 / (radius**2) )
|
||||
L.receive_radiated_instability(outgoing_instability)
|
||||
pulses_remaining--
|
||||
qdel(src)
|
||||
timer_id = addtimer(CALLBACK(src, PROC_REF(radiate_loop)), 5 SECONDS, TIMER_UNIQUE|TIMER_STOPPABLE)
|
||||
|
||||
else
|
||||
qdel(src)
|
||||
|
||||
|
||||
@@ -145,6 +145,8 @@
|
||||
. = equip(H, TRUE, FALSE, alt_title=alt_title)
|
||||
|
||||
/datum/job/proc/get_access(selected_title)
|
||||
SHOULD_NOT_SLEEP(TRUE)
|
||||
|
||||
if(!GLOB.config || GLOB.config.jobs_have_minimal_access)
|
||||
. = minimal_access.Copy()
|
||||
else
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
clicksound = /singleton/sound_category/keyboard_sound
|
||||
clickvol = 30
|
||||
|
||||
var/print_loc
|
||||
var/atom/print_loc
|
||||
|
||||
var/list/stored_material = list(DEFAULT_WALL_MATERIAL = 0, MATERIAL_GLASS = 0)
|
||||
var/list/storage_capacity = list(DEFAULT_WALL_MATERIAL = 0, MATERIAL_GLASS = 0)
|
||||
@@ -62,8 +62,12 @@
|
||||
populate_lathe_recipes()
|
||||
|
||||
/obj/machinery/autolathe/Destroy()
|
||||
print_loc = null
|
||||
QDEL_NULL(currently_printing)
|
||||
QDEL_NULL(wires)
|
||||
print_queue.Cut()
|
||||
|
||||
QDEL_LIST(print_queue)
|
||||
|
||||
return ..()
|
||||
|
||||
/obj/machinery/autolathe/proc/populate_lathe_recipes()
|
||||
|
||||
@@ -74,7 +74,8 @@
|
||||
GLOB.cameranet.remove_source(src)
|
||||
GLOB.cameranet.cameras -= src
|
||||
|
||||
return ..()
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
/obj/machinery/camera/set_pixel_offsets()
|
||||
pixel_x = dir & (NORTH|SOUTH) ? 0 : (dir == EAST ? -13 : 13)
|
||||
|
||||
@@ -50,8 +50,6 @@
|
||||
coinvalues["[cointype]"] = get_value(cointype)
|
||||
|
||||
/obj/machinery/computer/slot_machine/Destroy()
|
||||
if(balance)
|
||||
give_payout(balance)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/computer/slot_machine/process(delta_time)
|
||||
|
||||
@@ -48,9 +48,8 @@
|
||||
else
|
||||
layer = open_layer
|
||||
|
||||
/obj/machinery/door/airlock/Destroy()
|
||||
qdel(wifi_receiver)
|
||||
wifi_receiver = null
|
||||
/obj/machinery/door/blast/Destroy()
|
||||
QDEL_NULL(wifi_receiver)
|
||||
return ..()
|
||||
|
||||
// Proc: Bumped()
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
LAZYADD(vision_blockers, vision_blocker)
|
||||
|
||||
/obj/machinery/door/airlock/multi_tile/Destroy()
|
||||
QDEL_NULL_LIST(vision_blockers)
|
||||
QDEL_LIST(vision_blockers)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/door/airlock/multi_tile/set_opacity(var/new_opacity)
|
||||
@@ -100,7 +100,7 @@
|
||||
LAZYADD(vision_blockers, vision_blocker)
|
||||
|
||||
/obj/machinery/door/firedoor/multi_tile/Destroy()
|
||||
QDEL_NULL_LIST(vision_blockers)
|
||||
QDEL_LIST(vision_blockers)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/door/firedoor/multi_tile/set_opacity(var/new_opacity)
|
||||
|
||||
@@ -31,6 +31,10 @@
|
||||
tag_chamber_sensor = given_tag_chamber_sensor
|
||||
program = new /datum/computer/file/embedded_program/airlock(src)
|
||||
|
||||
/obj/machinery/embedded_controller/radio/airlock/Destroy()
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
//Advanced airlock controller for when you want a more versatile airlock controller - useful for turning simple access control rooms into airlocks
|
||||
/obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller
|
||||
name = "Advanced Airlock Controller"
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
/obj/machinery/embedded_controller/radio/Destroy()
|
||||
if(SSradio)
|
||||
SSradio.remove_object(src,frequency)
|
||||
return ..()
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
/obj/machinery/embedded_controller/proc/post_signal(datum/signal/signal, comm_line)
|
||||
return 0
|
||||
|
||||
@@ -155,12 +155,15 @@ Class Procs:
|
||||
/obj/machinery/Destroy()
|
||||
STOP_PROCESSING_MACHINE(src, MACHINERY_PROCESS_ALL)
|
||||
SSmachinery.machinery -= src
|
||||
|
||||
//Clear the component parts
|
||||
//If the components are inside the machine, delete them, otherwise we assume they were dropped to the ground during deconstruction,
|
||||
//and were not removed from the component_parts list by deconstruction code
|
||||
if(component_parts)
|
||||
for(var/atom/A in component_parts)
|
||||
if(A.loc == src) // If the components are inside the machine, delete them.
|
||||
if(A.loc == src)
|
||||
qdel(A)
|
||||
else // Otherwise we assume they were dropped to the ground during deconstruction, and were not removed from the component_parts list by deconstruction code.
|
||||
component_parts -= A
|
||||
component_parts = null
|
||||
|
||||
return ..()
|
||||
|
||||
@@ -451,9 +454,13 @@ Class Procs:
|
||||
M.set_dir(src.dir)
|
||||
M.state = 3
|
||||
M.icon_state = "blueprint_1"
|
||||
|
||||
for(var/obj/I in component_parts)
|
||||
I.forceMove(loc)
|
||||
component_parts -= I
|
||||
|
||||
qdel(src)
|
||||
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/proc/print(var/obj/paper, var/play_sound = 1, var/print_sfx = /singleton/sound_category/print_sound, var/print_delay = 10, var/message, var/mob/user)
|
||||
|
||||
@@ -215,7 +215,7 @@
|
||||
+ "</body></html>", "window=pdaphoto;size=192x192")
|
||||
onclose(M, "pdaphoto")
|
||||
|
||||
var/obj/machinery/blackbox_recorder/blackbox
|
||||
GLOBAL_DATUM(blackbox, /obj/machinery/blackbox_recorder)
|
||||
|
||||
/obj/machinery/blackbox_recorder
|
||||
icon = 'icons/obj/stationobjs.dmi'
|
||||
@@ -231,16 +231,20 @@ var/obj/machinery/blackbox_recorder/blackbox
|
||||
//Only one can exist in the world!
|
||||
/obj/machinery/blackbox_recorder/Initialize()
|
||||
. = ..()
|
||||
if(blackbox)
|
||||
if(istype(blackbox,/obj/machinery/blackbox_recorder))
|
||||
if(GLOB.blackbox)
|
||||
if(istype(GLOB.blackbox,/obj/machinery/blackbox_recorder))
|
||||
qdel(src)
|
||||
else
|
||||
blackbox = src
|
||||
GLOB.blackbox = src
|
||||
|
||||
/obj/machinery/blackbox_recorder/Destroy()
|
||||
feedback_set_details("blackbox_destroyed","true")
|
||||
feedback_set("blackbox_destroyed",1)
|
||||
return ..()
|
||||
|
||||
if(GLOB.blackbox == src)
|
||||
GLOB.blackbox = null
|
||||
|
||||
. = ..()
|
||||
|
||||
|
||||
#undef MESSAGE_SERVER_SPAM_REJECT
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
..()
|
||||
return
|
||||
|
||||
/obj/effect/expl_particles/Destroy()
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
/datum/effect/system/expl_particles
|
||||
var/number = 10
|
||||
var/turf/location
|
||||
|
||||
@@ -92,7 +92,8 @@
|
||||
|
||||
/obj/effect/landmark/Destroy()
|
||||
GLOB.landmarks_list -= src
|
||||
return ..()
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
/obj/effect/landmark/start
|
||||
name = "start"
|
||||
|
||||
@@ -242,7 +242,6 @@
|
||||
for(var/thing in contents)
|
||||
var/obj/O = thing
|
||||
O.forceMove(get_turf(src))
|
||||
O.throw_at_random(FALSE, 3, THROWNOBJ_KNOCKBACK_SPEED)
|
||||
var/area/A = get_area(src)
|
||||
message_all_revenants(FONT_LARGE(SPAN_WARNING("The rift keeping us here has been destroyed in [A.name]!")))
|
||||
return ..()
|
||||
|
||||
@@ -70,9 +70,7 @@
|
||||
if(!M.moved_recently && M.last_move)
|
||||
M.moved_recently = 1
|
||||
step(M, M.last_move)
|
||||
sleep(50)
|
||||
if(M)
|
||||
M.moved_recently = 0
|
||||
addtimer(CALLBACK(src, PROC_REF(update_move_recently)), 50 SECONDS)
|
||||
to_chat(M, "<span class='danger'>You feel a sharp shock!</span>")
|
||||
spark(M, 3)
|
||||
|
||||
@@ -82,6 +80,13 @@
|
||||
master.receive_signal()
|
||||
return
|
||||
|
||||
/obj/item/device/radio/electropack/proc/update_move_recently()
|
||||
var/mob/M = loc
|
||||
|
||||
if(M)
|
||||
M.moved_recently = 0
|
||||
|
||||
|
||||
/obj/item/device/radio/electropack/attack_self(mob/user as mob, flag1)
|
||||
|
||||
if(!istype(user, /mob/living/carbon/human))
|
||||
|
||||
@@ -783,6 +783,10 @@
|
||||
var/myAi = null // Atlantis: Reference back to the AI which has this radio.
|
||||
var/disabledAi = 0 // Atlantis: Used to manually disable AI's integrated radio via intellicard menu.
|
||||
|
||||
/obj/item/device/radio/headset/heads/ai_integrated/Destroy()
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
/obj/item/device/radio/headset/heads/ai_integrated/can_receive(input_frequency, level)
|
||||
return ..(input_frequency, level, !disabledAi)
|
||||
|
||||
|
||||
@@ -565,7 +565,8 @@ var/global/list/default_interrogation_channels = list(
|
||||
|
||||
/obj/item/device/radio/borg/Destroy()
|
||||
myborg = null
|
||||
return ..()
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
/obj/item/device/radio/borg/list_channels(var/mob/user)
|
||||
return list_secure_channels(user)
|
||||
|
||||
@@ -38,6 +38,10 @@ var/global/list/datum/stack_recipe/rod_recipes = list(
|
||||
stacktype = /obj/item/stack/rods
|
||||
icon_has_variants = TRUE
|
||||
|
||||
/obj/item/stack/rods/Destroy()
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
/obj/item/stack/rods/full/Initialize()
|
||||
. = ..()
|
||||
amount = max_amount
|
||||
|
||||
@@ -126,7 +126,8 @@ var/const/NO_EMAG_ACT = -50
|
||||
|
||||
/obj/item/card/id/Destroy()
|
||||
QDEL_NULL(chat_user)
|
||||
return ..()
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
/obj/item/card/id/examine(mob/user, distance)
|
||||
. = ..()
|
||||
@@ -162,6 +163,8 @@ var/const/NO_EMAG_ACT = -50
|
||||
side.Scale(128, 128)
|
||||
|
||||
/mob/proc/set_id_info(var/obj/item/card/id/id_card)
|
||||
SHOULD_NOT_SLEEP(TRUE)
|
||||
|
||||
id_card.age = 0
|
||||
id_card.registered_name = real_name
|
||||
id_card.sex = capitalize(gender)
|
||||
|
||||
@@ -38,11 +38,15 @@
|
||||
M.disable_cloaking_device()
|
||||
M.update_icon()
|
||||
|
||||
/obj/item/grenade/flashbang/clusterbang//Created by Polymorph, fixed by Sieve
|
||||
/obj/item/grenade/flashbang/clusterbang //Created by Polymorph, fixed by Sieve
|
||||
name = "clusterbang"
|
||||
icon = 'icons/obj/grenade.dmi'
|
||||
icon_state = "clusterbang"
|
||||
|
||||
/obj/item/grenade/flashbang/clusterbang/Destroy()
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
/obj/item/grenade/flashbang/clusterbang/prime()
|
||||
var/numspawned = rand(4,8)
|
||||
var/again = 0
|
||||
@@ -103,3 +107,7 @@
|
||||
var/dettime = rand(15,60)
|
||||
addtimer(CALLBACK(src, PROC_REF(prime)), dettime)
|
||||
..()
|
||||
|
||||
/obj/item/grenade/flashbang/cluster/Destroy()
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
@@ -21,10 +21,24 @@
|
||||
var/list/possible_explosions = list("Localized Limb", "Destroy Body")
|
||||
var/warning_message = "Tampering detected. Tampering detected."
|
||||
|
||||
/obj/item/implant/explosive/New()
|
||||
..()
|
||||
become_hearing_sensitive(ROUNDSTART_TRAIT)
|
||||
|
||||
/obj/item/implant/explosive/Initialize()
|
||||
. = ..()
|
||||
setFrequency(frequency)
|
||||
|
||||
/obj/item/implant/explosive/Destroy()
|
||||
lose_hearing_sensitivity(ROUNDSTART_TRAIT)
|
||||
|
||||
if(frequency)
|
||||
SSradio.remove_object_all(src)
|
||||
frequency = null
|
||||
radio_connection = null
|
||||
|
||||
. = ..()
|
||||
|
||||
/obj/item/implant/explosive/get_data()
|
||||
. = {"
|
||||
<b>Implant Specifications:</b><BR>
|
||||
@@ -180,7 +194,7 @@
|
||||
M.gib() //Simple mobs just get got
|
||||
qdel(src)
|
||||
|
||||
/proc/explosion_spread(turf/epicenter, power, adminlog = 1, z_transfer = UP|DOWN)
|
||||
/obj/item/implant/explosive/proc/explosion_spread(turf/epicenter, power, adminlog = 1, z_transfer = UP|DOWN)
|
||||
var/datum/explosiondata/data = new
|
||||
data.epicenter = epicenter
|
||||
data.rec_pow = power
|
||||
@@ -223,13 +237,6 @@
|
||||
/obj/item/implant/explosive/isLegal()
|
||||
return FALSE
|
||||
|
||||
/obj/item/implant/explosive/New()
|
||||
..()
|
||||
become_hearing_sensitive(ROUNDSTART_TRAIT)
|
||||
|
||||
/obj/item/implant/explosive/Destroy()
|
||||
return ..()
|
||||
|
||||
/obj/item/implant/explosive/full
|
||||
possible_explosions = list("Localized Limb", "Destroy Body", "Full Explosion")
|
||||
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
drop_sound = 'sound/effects/glass_step.ogg'
|
||||
surgerysound = 'sound/items/surgery/scalpel.ogg'
|
||||
|
||||
/obj/item/material/shard/Destroy()
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
/obj/item/material/shard/set_material(var/new_material)
|
||||
..(new_material)
|
||||
if(!istype(material))
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
QDEL_NULL(storage_start)
|
||||
QDEL_NULL(storage_continue)
|
||||
QDEL_NULL(storage_end)
|
||||
QDEL_NULL_LIST(storage_screens)
|
||||
QDEL_LIST(storage_screens)
|
||||
QDEL_NULL(closer)
|
||||
return ..()
|
||||
|
||||
@@ -208,7 +208,7 @@
|
||||
user.s_active = null
|
||||
if(!length(can_see_contents()))
|
||||
storage_start.vis_contents = list()
|
||||
QDEL_NULL_LIST(storage_screens)
|
||||
QDEL_LIST(storage_screens)
|
||||
storage_screens = list()
|
||||
|
||||
/obj/item/storage/proc/close_all()
|
||||
@@ -309,7 +309,7 @@
|
||||
var/endpoint = 1
|
||||
|
||||
storage_start.vis_contents = list()
|
||||
QDEL_NULL_LIST(storage_screens)
|
||||
QDEL_LIST(storage_screens)
|
||||
storage_screens = list()
|
||||
|
||||
for(var/obj/item/O in contents)
|
||||
|
||||
+3
-3
@@ -82,7 +82,7 @@ GLOBAL_PROTECT(config)
|
||||
loop_checks = FALSE
|
||||
#endif
|
||||
|
||||
#define RECOMMENDED_VERSION 510
|
||||
#define RECOMMENDED_VERSION 515
|
||||
/world/New()
|
||||
//logs
|
||||
GLOB.diary_date_string = time2text(world.realtime, "YYYY/MM/DD")
|
||||
@@ -509,14 +509,14 @@ var/list/world_api_rate_limit = list()
|
||||
else
|
||||
CRASH("Unsupported platform: [system_type]")
|
||||
|
||||
var/init_result = LIBCALL(library, "init")("block")
|
||||
var/init_result = call_ext(library, "init")("block")
|
||||
if (init_result != "0")
|
||||
CRASH("Error initializing byond-tracy: [init_result]")
|
||||
|
||||
/world/proc/init_debugger()
|
||||
var/dll = GetConfig("env", "AUXTOOLS_DEBUG_DLL")
|
||||
if (dll)
|
||||
LIBCALL(dll, "auxtools_init")()
|
||||
call_ext(dll, "auxtools_init")()
|
||||
enable_debugging()
|
||||
|
||||
#undef FAILED_DB_CONNECTION_CUTOFF
|
||||
|
||||
@@ -218,6 +218,7 @@ var/list/admin_verbs_debug = list(
|
||||
/client/proc/lighting_show_verbs,
|
||||
/client/proc/restart_controller,
|
||||
/client/proc/cmd_display_del_log,
|
||||
/client/proc/cmd_display_harddel_log,
|
||||
/client/proc/cmd_display_init_log,
|
||||
/client/proc/cmd_ss_panic,
|
||||
/client/proc/reset_openturf,
|
||||
@@ -388,6 +389,7 @@ var/list/admin_verbs_hideable = list(
|
||||
/client/proc/kill_air,
|
||||
/client/proc/kill_airgroup,
|
||||
/client/proc/cmd_display_del_log,
|
||||
/client/proc/cmd_display_harddel_log,
|
||||
/datum/admins/proc/ccannoucment,
|
||||
/client/proc/cmd_display_init_log,
|
||||
/client/proc/getruntimelog,
|
||||
@@ -476,6 +478,7 @@ var/list/admin_verbs_dev = list( //will need to be altered - Ryan784
|
||||
/client/proc/cmd_dev_bst,
|
||||
/client/proc/lighting_show_verbs,
|
||||
/client/proc/cmd_display_del_log,
|
||||
/client/proc/cmd_display_harddel_log,
|
||||
/client/proc/cmd_display_init_log,
|
||||
/client/proc/create_poll, //Allows to create polls
|
||||
/client/proc/profiler_start,
|
||||
|
||||
@@ -421,21 +421,80 @@
|
||||
/client/proc/cmd_display_del_log()
|
||||
set category = "Debug"
|
||||
set name = "Display del() Log"
|
||||
set desc = "Displays a list of things that have failed to GC this round"
|
||||
set desc = "Display del's log of everything that's passed through it."
|
||||
|
||||
var/dat = "<B>List of things that failed to GC this round</B><BR><BR>"
|
||||
for(var/path in SSgarbage.didntgc)
|
||||
dat += "[path] - [SSgarbage.didntgc[path]] times<BR>"
|
||||
var/list/dellog = list("<B>List of things that have gone through qdel this round</B><BR><BR><ol>")
|
||||
sortTim(SSgarbage.items, cmp=/proc/cmp_qdel_item_time, associative = TRUE)
|
||||
for(var/path in SSgarbage.items)
|
||||
var/datum/qdel_item/I = SSgarbage.items[path]
|
||||
dellog += "<li><u>[path]</u><ul>"
|
||||
if (I.qdel_flags & QDEL_ITEM_SUSPENDED_FOR_LAG)
|
||||
dellog += "<li>SUSPENDED FOR LAG</li>"
|
||||
if (I.failures)
|
||||
dellog += "<li>Failures: [I.failures]</li>"
|
||||
dellog += "<li>qdel() Count: [I.qdels]</li>"
|
||||
dellog += "<li>Destroy() Cost: [I.destroy_time]ms</li>"
|
||||
if (I.hard_deletes)
|
||||
dellog += "<li>Total Hard Deletes [I.hard_deletes]</li>"
|
||||
dellog += "<li>Time Spent Hard Deleting: [I.hard_delete_time]ms</li>"
|
||||
dellog += "<li>Highest Time Spent Hard Deleting: [I.hard_delete_max]ms</li>"
|
||||
if (I.hard_deletes_over_threshold)
|
||||
dellog += "<li>Hard Deletes Over Threshold: [I.hard_deletes_over_threshold]</li>"
|
||||
if (I.slept_destroy)
|
||||
dellog += "<li>Sleeps: [I.slept_destroy]</li>"
|
||||
if (I.no_respect_force)
|
||||
dellog += "<li>Ignored force: [I.no_respect_force]</li>"
|
||||
if (I.no_hint)
|
||||
dellog += "<li>No hint: [I.no_hint]</li>"
|
||||
if(LAZYLEN(I.extra_details))
|
||||
var/details = I.extra_details.Join("</li><li>")
|
||||
dellog += "<li>Extra Info: <ul><li>[details]</li></ul>"
|
||||
dellog += "</ul></li>"
|
||||
|
||||
dat += "<B>List of paths that did not return a qdel hint in Destroy()</B><BR><BR>"
|
||||
for(var/path in SSgarbage.noqdelhint)
|
||||
dat += "[path]<BR>"
|
||||
dellog += "</ol>"
|
||||
|
||||
dat += "<B>List of paths that slept in Destroy()</B><BR><BR>"
|
||||
for(var/path in SSgarbage.sleptDestroy)
|
||||
dat += "[path]<BR>"
|
||||
usr << browse(dellog.Join(), "window=dellog")
|
||||
|
||||
usr << browse(dat, "window=dellog")
|
||||
/**
|
||||
* Same as `cmd_display_del_log`, but only shows harddels
|
||||
*/
|
||||
/client/proc/cmd_display_harddel_log()
|
||||
set category = "Debug"
|
||||
set name = "Display harddel() Log"
|
||||
set desc = "Display harddel's log."
|
||||
|
||||
var/list/dellog = list("<B>List of things that have harddel'd this round</B><BR><BR><ol>")
|
||||
sortTim(SSgarbage.items, cmp=/proc/cmp_qdel_item_time, associative = TRUE)
|
||||
for(var/path in SSgarbage.items)
|
||||
var/datum/qdel_item/I = SSgarbage.items[path]
|
||||
if(I.hard_deletes)
|
||||
dellog += "<li><u>[path]</u><ul>"
|
||||
if (I.qdel_flags & QDEL_ITEM_SUSPENDED_FOR_LAG)
|
||||
dellog += "<li>SUSPENDED FOR LAG</li>"
|
||||
if (I.failures)
|
||||
dellog += "<li>Failures: [I.failures]</li>"
|
||||
dellog += "<li>qdel() Count: [I.qdels]</li>"
|
||||
dellog += "<li>Destroy() Cost: [I.destroy_time]ms</li>"
|
||||
if (I.hard_deletes)
|
||||
dellog += "<li>Total Hard Deletes [I.hard_deletes]</li>"
|
||||
dellog += "<li>Time Spent Hard Deleting: [I.hard_delete_time]ms</li>"
|
||||
dellog += "<li>Highest Time Spent Hard Deleting: [I.hard_delete_max]ms</li>"
|
||||
if (I.hard_deletes_over_threshold)
|
||||
dellog += "<li>Hard Deletes Over Threshold: [I.hard_deletes_over_threshold]</li>"
|
||||
if (I.slept_destroy)
|
||||
dellog += "<li>Sleeps: [I.slept_destroy]</li>"
|
||||
if (I.no_respect_force)
|
||||
dellog += "<li>Ignored force: [I.no_respect_force]</li>"
|
||||
if (I.no_hint)
|
||||
dellog += "<li>No hint: [I.no_hint]</li>"
|
||||
if(LAZYLEN(I.extra_details))
|
||||
var/details = I.extra_details.Join("</li><li>")
|
||||
dellog += "<li>Extra Info: <ul><li>[details]</li></ul>"
|
||||
dellog += "</ul></li>"
|
||||
|
||||
dellog += "</ol>"
|
||||
|
||||
usr << browse(dellog.Join(), "window=harddellog")
|
||||
|
||||
/client/proc/cmd_display_init_log()
|
||||
set category = "Debug"
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
#ifdef REFERENCE_TRACKING
|
||||
|
||||
/datum/proc/find_references(skip_alert)
|
||||
running_find_references = type
|
||||
if(usr?.client)
|
||||
if(usr.client.running_find_references)
|
||||
log_subsystem_garbage_harddel("CANCELLED search for references to a [usr.client.running_find_references].")
|
||||
usr.client.running_find_references = null
|
||||
running_find_references = null
|
||||
//restart the garbage collector
|
||||
SSgarbage.can_fire = TRUE
|
||||
SSgarbage.update_nextfire(reset_time = TRUE)
|
||||
return
|
||||
|
||||
if(!skip_alert && tgui_alert(usr,"Running this will lock everything up for about 5 minutes. Would you like to begin the search?", "Find References", list("Yes", "No")) != "Yes")
|
||||
running_find_references = null
|
||||
return
|
||||
|
||||
//this keeps the garbage collector from failing to collect objects being searched for in here
|
||||
SSgarbage.can_fire = FALSE
|
||||
|
||||
if(usr?.client)
|
||||
usr.client.running_find_references = type
|
||||
|
||||
log_subsystem_garbage_harddel("Beginning search for references to a [type].")
|
||||
|
||||
var/starting_time = world.time
|
||||
|
||||
log_subsystem_garbage_harddel("Refcount for [type]: [refcount(src)]")
|
||||
|
||||
//Time to search the whole game for our ref
|
||||
DoSearchVar(GLOB, "GLOB", search_time = starting_time) //globals
|
||||
log_subsystem_garbage_harddel("Finished searching globals")
|
||||
|
||||
//Yes we do actually need to do this. The searcher refuses to read weird lists
|
||||
//And global.vars is a really weird list
|
||||
var/global_vars = list()
|
||||
for(var/key in global.vars)
|
||||
global_vars[key] = global.vars[key]
|
||||
|
||||
DoSearchVar(global_vars, "Native Global", search_time = starting_time)
|
||||
log_subsystem_garbage_harddel("Finished searching native globals")
|
||||
|
||||
for(var/datum/thing in world) //atoms (don't beleive its lies)
|
||||
DoSearchVar(thing, "World -> [thing.type]", search_time = starting_time)
|
||||
log_subsystem_garbage_harddel("Finished searching atoms")
|
||||
|
||||
for(var/datum/thing) //datums
|
||||
DoSearchVar(thing, "Datums -> [thing.type]", search_time = starting_time)
|
||||
log_subsystem_garbage_harddel("Finished searching datums")
|
||||
|
||||
//Warning, attempting to search clients like this will cause crashes if done on live. Watch yourself
|
||||
#ifndef REFERENCE_DOING_IT_LIVE
|
||||
for(var/client/thing) //clients
|
||||
DoSearchVar(thing, "Clients -> [thing.type]", search_time = starting_time)
|
||||
log_subsystem_garbage_harddel("Finished searching clients")
|
||||
#endif
|
||||
|
||||
log_subsystem_garbage_harddel("Completed search for references to a [type].")
|
||||
|
||||
if(usr?.client)
|
||||
usr.client.running_find_references = null
|
||||
running_find_references = null
|
||||
|
||||
//restart the garbage collector
|
||||
SSgarbage.can_fire = TRUE
|
||||
SSgarbage.update_nextfire(reset_time = TRUE)
|
||||
|
||||
/datum/proc/DoSearchVar(potential_container, container_name, recursive_limit = 64, search_time = world.time)
|
||||
#ifdef REFERENCE_TRACKING_DEBUG
|
||||
if(SSgarbage.should_save_refs && !found_refs)
|
||||
found_refs = list()
|
||||
#endif
|
||||
|
||||
if(usr?.client && !usr.client.running_find_references)
|
||||
return
|
||||
|
||||
if(!recursive_limit)
|
||||
log_subsystem_garbage_harddel("Recursion limit reached. [container_name]")
|
||||
return
|
||||
|
||||
//Check each time you go down a layer. This makes it a bit slow, but it won't effect the rest of the game at all
|
||||
#ifndef FIND_REF_NO_CHECK_TICK
|
||||
CHECK_TICK
|
||||
#endif
|
||||
|
||||
if(isdatum(potential_container))
|
||||
var/datum/datum_container = potential_container
|
||||
if(datum_container.last_find_references == search_time)
|
||||
return
|
||||
|
||||
datum_container.last_find_references = search_time
|
||||
var/container_print = datum_container.ref_search_details()
|
||||
var/list/vars_list = datum_container.vars
|
||||
|
||||
for(var/varname in vars_list)
|
||||
#ifndef FIND_REF_NO_CHECK_TICK
|
||||
CHECK_TICK
|
||||
#endif
|
||||
if (varname == "vars" || varname == "vis_locs") //Fun fact, vis_locs don't count for references
|
||||
continue
|
||||
var/variable = vars_list[varname]
|
||||
|
||||
if(variable == src)
|
||||
#ifdef REFERENCE_TRACKING_DEBUG
|
||||
if(SSgarbage.should_save_refs)
|
||||
found_refs[varname] = TRUE
|
||||
continue //End early, don't want these logging
|
||||
#endif
|
||||
log_subsystem_garbage_harddel("Found [type] [text_ref(src)] in [datum_container.type]'s [container_print] [varname] var. [container_name]")
|
||||
continue
|
||||
|
||||
if(islist(variable))
|
||||
DoSearchVar(variable, "[container_name] [container_print] -> [varname] (list)", recursive_limit - 1, search_time)
|
||||
|
||||
else if(islist(potential_container))
|
||||
var/normal = IS_NORMAL_LIST(potential_container)
|
||||
var/list/potential_cache = potential_container
|
||||
for(var/element_in_list in potential_cache)
|
||||
#ifndef FIND_REF_NO_CHECK_TICK
|
||||
CHECK_TICK
|
||||
#endif
|
||||
//Check normal entrys
|
||||
if(element_in_list == src)
|
||||
#ifdef REFERENCE_TRACKING_DEBUG
|
||||
if(SSgarbage.should_save_refs)
|
||||
found_refs[potential_cache] = TRUE
|
||||
continue //End early, don't want these logging
|
||||
#endif
|
||||
log_subsystem_garbage_harddel("Found [type] [text_ref(src)] in list [container_name].")
|
||||
continue
|
||||
|
||||
var/assoc_val = null
|
||||
if(!isnum(element_in_list) && normal)
|
||||
assoc_val = potential_cache[element_in_list]
|
||||
//Check assoc entrys
|
||||
if(assoc_val == src)
|
||||
#ifdef REFERENCE_TRACKING_DEBUG
|
||||
if(SSgarbage.should_save_refs)
|
||||
found_refs[potential_cache] = TRUE
|
||||
continue //End early, don't want these logging
|
||||
#endif
|
||||
log_subsystem_garbage_harddel("Found [type] [text_ref(src)] in list [container_name]\[[element_in_list]\]")
|
||||
continue
|
||||
//We need to run both of these checks, since our object could be hiding in either of them
|
||||
//Check normal sublists
|
||||
if(islist(element_in_list))
|
||||
DoSearchVar(element_in_list, "[container_name] -> [element_in_list] (list)", recursive_limit - 1, search_time)
|
||||
//Check assoc sublists
|
||||
if(islist(assoc_val))
|
||||
DoSearchVar(potential_container[element_in_list], "[container_name]\[[element_in_list]\] -> [assoc_val] (list)", recursive_limit - 1, search_time)
|
||||
|
||||
/proc/qdel_and_find_ref_if_fail(datum/thing_to_del, force = FALSE)
|
||||
thing_to_del.qdel_and_find_ref_if_fail(force)
|
||||
|
||||
/datum/proc/qdel_and_find_ref_if_fail(force = FALSE)
|
||||
SSgarbage.reference_find_on_fail[text_ref(src)] = TRUE
|
||||
qdel(src, force)
|
||||
|
||||
#endif
|
||||
|
||||
// Kept outside the ifdef so overrides are easy to implement
|
||||
|
||||
/// Return info about us for reference searching purposes
|
||||
/// Will be logged as a representation of this datum if it's a part of a search chain
|
||||
/datum/proc/ref_search_details()
|
||||
return text_ref(src)
|
||||
|
||||
/datum/callback/ref_search_details()
|
||||
return "[text_ref(src)] (obj: [object] proc: [delegate] args: [json_encode(arguments)]" // Used to include `user: [user?.resolve() || "null"])` but dreamchecker no happy with non static ref
|
||||
@@ -33,6 +33,15 @@
|
||||
*/
|
||||
var/wires = WIRE_RECEIVE_ASSEMBLY | WIRE_PULSE_ASSEMBLY
|
||||
|
||||
/obj/item/device/assembly/Destroy()
|
||||
STOP_PROCESSING(SSprocessing, src)
|
||||
|
||||
holder = null
|
||||
cut_overlays()
|
||||
attached_overlays = null
|
||||
|
||||
. = ..()
|
||||
|
||||
/obj/item/device/assembly/proc/holder_movement()
|
||||
return
|
||||
|
||||
|
||||
@@ -64,8 +64,11 @@
|
||||
|
||||
/obj/item/device/onetankbomb/receive_signal() //This is mainly called by the sensor through sense() to the holder, and from the holder to here.
|
||||
visible_message("[icon2html(src, viewers(get_turf(src)))] *beep* *beep*", "*beep* *beep*")
|
||||
sleep(10)
|
||||
if(!src)
|
||||
addtimer(CALLBACK(src, PROC_REF(delayed_explosion)), 10 SECONDS)
|
||||
|
||||
|
||||
/obj/item/device/onetankbomb/proc/delayed_explosion()
|
||||
if(QDELETED(src))
|
||||
return
|
||||
if(status)
|
||||
bombtank.ignite() //if its not a dud, boom (or not boom if you made shitty mix) the ignite proc is below, in this file
|
||||
|
||||
@@ -15,6 +15,19 @@
|
||||
var/obj/item/device/assembly/a_right = null
|
||||
var/obj/special_assembly = null
|
||||
|
||||
/obj/item/device/assembly_holder/Initialize(mapload, ...)
|
||||
. = ..()
|
||||
become_hearing_sensitive()
|
||||
|
||||
/obj/item/device/assembly_holder/Destroy()
|
||||
lose_hearing_sensitivity()
|
||||
|
||||
QDEL_NULL(a_left)
|
||||
QDEL_NULL(a_right)
|
||||
QDEL_NULL(special_assembly)
|
||||
|
||||
. = ..()
|
||||
|
||||
/obj/item/device/assembly_holder/proc/detached()
|
||||
if(a_left)
|
||||
a_left.holder_movement()
|
||||
@@ -174,17 +187,6 @@
|
||||
master.receive_signal()
|
||||
return TRUE
|
||||
|
||||
/obj/item/device/assembly_holder/Initialize(mapload, ...)
|
||||
. = ..()
|
||||
become_hearing_sensitive()
|
||||
|
||||
/obj/item/device/assembly_holder/Destroy()
|
||||
if(a_left)
|
||||
a_left.holder = null
|
||||
if(a_right)
|
||||
a_right.holder = null
|
||||
return ..()
|
||||
|
||||
/obj/item/device/assembly_holder/hear_talk(mob/living/M, msg, verb, datum/language/speaking)
|
||||
if(a_right)
|
||||
a_right.hear_talk(M, msg, verb, speaking)
|
||||
|
||||
@@ -218,7 +218,8 @@
|
||||
qdel(P.network)
|
||||
P.node = null
|
||||
|
||||
return ..()
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
/obj/machinery/atmospherics/omni/atmos_init()
|
||||
for(var/datum/omni_port/P in ports)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
var/list/contained_packages
|
||||
|
||||
/obj/item/cargo_backpack/Destroy()
|
||||
QDEL_NULL_LIST(contained_packages)
|
||||
QDEL_LIST(contained_packages)
|
||||
return ..()
|
||||
|
||||
/obj/item/cargo_backpack/examine(mob/user, distance)
|
||||
|
||||
@@ -211,7 +211,7 @@ var/list/preferences_datums = list()
|
||||
|
||||
/datum/preferences/Destroy()
|
||||
. = ..()
|
||||
QDEL_NULL_LIST(char_render_holders)
|
||||
QDEL_LIST(char_render_holders)
|
||||
|
||||
/datum/preferences/proc/load_and_update_character(var/slot)
|
||||
load_character(slot)
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
/obj/item/clothing/Destroy()
|
||||
STOP_PROCESSING(SSprocessing, src)
|
||||
QDEL_NULL_LIST(accessories)
|
||||
QDEL_LIST(accessories)
|
||||
return ..()
|
||||
|
||||
//Updates the icons of the mob wearing the clothing item, if any.
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
pockets.max_w_class = storage_max_w_class
|
||||
pockets.max_storage_space = storage_max_storage_space
|
||||
|
||||
/obj/item/rig_module/storage/Destroy()
|
||||
QDEL_NULL(pockets)
|
||||
|
||||
. = ..()
|
||||
|
||||
|
||||
/obj/item/rig/attack_hand(mob/user as mob)
|
||||
var/obj/item/rig_module/storage/storage = locate() in installed_modules
|
||||
|
||||
@@ -30,6 +30,13 @@
|
||||
var/device_type
|
||||
var/obj/item/device
|
||||
|
||||
/obj/item/rig_module/device/Destroy()
|
||||
if(!ispath(src.device))
|
||||
QDEL_NULL(src.device)
|
||||
src.device = null
|
||||
|
||||
. = ..()
|
||||
|
||||
/obj/item/rig_module/device/healthscanner
|
||||
name = "health scanner module"
|
||||
desc = "A hardsuit-mounted health scanner."
|
||||
@@ -383,11 +390,17 @@
|
||||
|
||||
category = MODULE_SPECIAL
|
||||
|
||||
/obj/item/rig_module/voice/New()
|
||||
..()
|
||||
/obj/item/rig_module/voice/Initialize(mapload, ...)
|
||||
. = ..()
|
||||
|
||||
voice_holder = new(src)
|
||||
voice_holder.active = 0
|
||||
|
||||
/obj/item/rig_module/voice/Destroy()
|
||||
QDEL_NULL(voice_holder)
|
||||
|
||||
. = ..()
|
||||
|
||||
/obj/item/rig_module/voice/installed()
|
||||
..()
|
||||
holder.speech = src
|
||||
@@ -552,12 +565,19 @@
|
||||
|
||||
category = MODULE_GENERAL
|
||||
|
||||
/obj/item/rig_module/device/stamp/New()
|
||||
..()
|
||||
/obj/item/rig_module/device/stamp/Initialize()
|
||||
. = ..()
|
||||
|
||||
iastamp = new /obj/item/stamp/internalaffairs(src)
|
||||
deniedstamp = new /obj/item/stamp/denied(src)
|
||||
device = iastamp
|
||||
|
||||
/obj/item/rig_module/device/stamp/Destroy()
|
||||
QDEL_NULL(iastamp)
|
||||
QDEL_NULL(deniedstamp)
|
||||
|
||||
. = ..()
|
||||
|
||||
/obj/item/rig_module/device/stamp/engage(atom/target, mob/user)
|
||||
if(!..() || !device)
|
||||
return FALSE
|
||||
|
||||
@@ -4,15 +4,37 @@
|
||||
icon = 'icons/obj/rig_modules.dmi'
|
||||
var/icon_base = null
|
||||
w_class = ITEMSIZE_LARGE
|
||||
var/obj/item/circuitboard/board_type = null
|
||||
var/obj/item/circuitboard/target_board_type = null
|
||||
var/obj/item/rig/rig_type = /obj/item/rig
|
||||
|
||||
///The type of board, a path of `/obj/item/circuitboard`
|
||||
var/board_type = null
|
||||
|
||||
///The type of target board, a path of `/obj/item/circuitboard`
|
||||
var/target_board_type = null
|
||||
|
||||
///The type of rig, a path of `/obj/item/rig`
|
||||
var/rig_type = /obj/item/rig
|
||||
|
||||
obj_flags = OBJ_FLAG_CONDUCTABLE
|
||||
origin_tech = list(TECH_MATERIAL = 4, TECH_ENGINEERING = 3, TECH_MAGNET = 4, TECH_POWER = 4)
|
||||
|
||||
var/datum/construction/reversible/rig_assembly/construct
|
||||
|
||||
obj_flags = OBJ_FLAG_CONDUCTABLE
|
||||
|
||||
/obj/item/rig_assembly/Initialize(mapload, ...)
|
||||
. = ..()
|
||||
|
||||
construct = new /datum/construction/reversible/rig_assembly/civilian(src)
|
||||
construct.board_type = board_type
|
||||
construct.steps[5]["key"] = board_type // defining board in construction step
|
||||
construct.result = "[rig_type]"
|
||||
|
||||
/obj/item/rig_assembly/Destroy()
|
||||
QDEL_NULL(construct)
|
||||
|
||||
. = ..()
|
||||
|
||||
|
||||
/obj/item/rig_assembly/examine(mob/user, distance)
|
||||
. = ..()
|
||||
if(construct)
|
||||
@@ -35,15 +57,10 @@
|
||||
..()
|
||||
return
|
||||
|
||||
/obj/item/rig_assembly/New()
|
||||
..()
|
||||
construct = new /datum/construction/reversible/rig_assembly/civilian(src)
|
||||
construct.board_type = board_type
|
||||
construct.steps[5]["key"] = board_type // defining board in construction step
|
||||
construct.result = "[rig_type]"
|
||||
|
||||
/obj/item/rig_assembly/combat/New()
|
||||
..()
|
||||
/obj/item/rig_assembly/combat/Initialize(mapload, ...)
|
||||
. = ..()
|
||||
|
||||
construct = new /datum/construction/reversible/rig_assembly/combat(src)
|
||||
construct.board_type = board_type
|
||||
construct.target_board_type = target_board_type
|
||||
@@ -166,8 +183,12 @@
|
||||
|
||||
/datum/construction/reversible/rig_assembly
|
||||
result = null
|
||||
var/obj/item/circuitboard/rig_assembly/board_type = null
|
||||
var/obj/item/circuitboard/rig_assembly/target_board_type = null
|
||||
|
||||
///The type of board, a path of `/obj/item/circuitboard`
|
||||
var/board_type = null
|
||||
|
||||
///The type of target board, a path of `/obj/item/circuitboard`
|
||||
var/target_board_type = null
|
||||
|
||||
/datum/construction/reversible/rig_assembly/custom_action(index as num, diff as num, atom/used_atom, mob/user as mob)
|
||||
var/obj/item/I = used_atom
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
rebuild_overlay_lists(TRUE)
|
||||
|
||||
/obj/compass_holder/Destroy()
|
||||
QDEL_NULL_LIST(compass_waypoints)
|
||||
QDEL_LIST(compass_waypoints)
|
||||
. = ..()
|
||||
|
||||
/obj/compass_holder/proc/get_heading()
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
//Deletes contents of container.
|
||||
//Used when food is burned, before replacing it with a burned mess
|
||||
/obj/item/reagent_containers/cooking_container/proc/clear()
|
||||
QDEL_NULL_LIST(contents)
|
||||
QDEL_LIST(contents)
|
||||
reagents.clear_reagents()
|
||||
|
||||
/obj/item/reagent_containers/cooking_container/proc/label(var/number, var/CT = null)
|
||||
|
||||
@@ -183,4 +183,5 @@
|
||||
|
||||
/mob/living/carbon/human/Destroy()
|
||||
ghost_spawner = null
|
||||
return ..()
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
@@ -160,7 +160,7 @@
|
||||
|
||||
return 1
|
||||
|
||||
/mob/living/heavy_vehicle/proc/remove_system(var/system_hardpoint, var/mob/user, var/force)
|
||||
/mob/living/heavy_vehicle/proc/remove_system_interact(var/system_hardpoint, var/mob/user, var/force)
|
||||
if((hardpoints_locked && !force) || !hardpoints[system_hardpoint])
|
||||
return 0
|
||||
|
||||
@@ -172,6 +172,19 @@
|
||||
if(!do_after(user, delay, src) || hardpoints[system_hardpoint] != system)
|
||||
return FALSE
|
||||
|
||||
remove_system(system_hardpoint, force)
|
||||
|
||||
if(user)
|
||||
system.forceMove(get_turf(user))
|
||||
user.put_in_hands(system)
|
||||
to_chat(user, "<span class='notice'>You remove \the [system] in \the [src]'s [system_hardpoint].</span>")
|
||||
playsound(user.loc, 'sound/items/Screwdriver.ogg', 100, 1)
|
||||
|
||||
/mob/living/heavy_vehicle/proc/remove_system(var/system_hardpoint, var/force)
|
||||
if((hardpoints_locked && !force) || !hardpoints[system_hardpoint])
|
||||
return 0
|
||||
|
||||
var/obj/item/system = hardpoints[system_hardpoint]
|
||||
hardpoints[system_hardpoint] = null
|
||||
|
||||
if(system_hardpoint == selected_hardpoint)
|
||||
@@ -197,10 +210,4 @@
|
||||
refresh_hud()
|
||||
queue_icon_update()
|
||||
|
||||
if(user)
|
||||
system.forceMove(get_turf(user))
|
||||
user.put_in_hands(system)
|
||||
to_chat(user, "<span class='notice'>You remove \the [system] in \the [src]'s [system_hardpoint].</span>")
|
||||
playsound(user.loc, 'sound/items/Screwdriver.ogg', 100, 1)
|
||||
|
||||
return system
|
||||
|
||||
@@ -386,7 +386,7 @@
|
||||
|
||||
var/to_remove = tgui_input_list(user, "Which component would you like to remove?", "Remove Component", parts)
|
||||
|
||||
if(remove_system(to_remove, user))
|
||||
if(remove_system_interact(to_remove, user))
|
||||
return
|
||||
to_chat(user, "<span class='warning'>\The [src] has no hardpoint systems to remove.</span>")
|
||||
return
|
||||
|
||||
@@ -94,7 +94,8 @@
|
||||
|
||||
for(var/hardpoint in hardpoints)
|
||||
var/obj/item/S = remove_system(hardpoint, force = 1)
|
||||
qdel(S)
|
||||
if(S)
|
||||
QDEL_NULL(S)
|
||||
|
||||
hardpoints = null
|
||||
|
||||
@@ -106,7 +107,7 @@
|
||||
pilot.forceMove(get_turf(src))
|
||||
pilots = null
|
||||
|
||||
QDEL_NULL_LIST(hud_elements)
|
||||
QDEL_LIST(hud_elements)
|
||||
|
||||
if(remote_network)
|
||||
SSvirtualreality.remove_mech(src, remote_network)
|
||||
|
||||
@@ -184,7 +184,7 @@
|
||||
return ..()
|
||||
|
||||
/obj/item/integrated_circuit/passive/power/powernet/Destroy()
|
||||
qdel(IO)
|
||||
QDEL_NULL(IO)
|
||||
return ..()
|
||||
|
||||
/obj/item/integrated_circuit/passive/power/powernet/on_anchored()
|
||||
|
||||
@@ -292,6 +292,10 @@
|
||||
default_type = MATERIAL_PLASTEEL
|
||||
icon_has_variants = TRUE
|
||||
|
||||
/obj/item/stack/material/plasteel/Destroy()
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
/obj/item/stack/material/plasteel/full/Initialize()
|
||||
. = ..()
|
||||
amount = max_amount
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
if(on_open_network)
|
||||
GLOB.cameranet.remove_source(src)
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
/obj/machinery/camera/proc/update_coverage(var/network_change = 0)
|
||||
if(network_change)
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
|
||||
//This is just a commonly used configuration for the equip_to_slot_if_possible() proc, used to equip people when the rounds tarts and when events happen and such.
|
||||
/mob/proc/equip_to_slot_or_del(obj/item/W as obj, slot)
|
||||
SHOULD_NOT_SLEEP(TRUE)
|
||||
|
||||
. = equip_to_slot_if_possible(W, slot, TRUE, TRUE, FALSE, TRUE)
|
||||
|
||||
// Convinience proc. Collects crap that fails to equip either onto the mob's back, or drops it.
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
if(pAI)
|
||||
if(isturf(loc))
|
||||
drop_from_inventory(pAI, get_turf(src))
|
||||
pAI.throw_at_random(FALSE, 3, 1)
|
||||
else
|
||||
drop_from_inventory(pAI, loc)
|
||||
pAI = null
|
||||
|
||||
@@ -62,7 +62,11 @@ var/list/cleanbot_types // Going to use this to generate a list of types once th
|
||||
patrol_path = null
|
||||
target = null
|
||||
ignorelist = null
|
||||
next_dest_loc = null
|
||||
|
||||
QDEL_NULL(listener)
|
||||
SSradio.remove_object(listener, beacon_freq)
|
||||
|
||||
GLOB.janitorial_supplies -= src
|
||||
return ..()
|
||||
|
||||
@@ -353,6 +357,7 @@ var/list/cleanbot_types // Going to use this to generate a list of types once th
|
||||
|
||||
/obj/cleanbot_listener/Destroy()
|
||||
cleanbot = null
|
||||
SSradio.remove_object_all(src)
|
||||
return ..()
|
||||
|
||||
/* Assembly */
|
||||
|
||||
@@ -50,6 +50,37 @@
|
||||
|
||||
var/can_attach = TRUE // Whether they can attach to a host
|
||||
|
||||
/mob/living/carbon/alien/diona/Initialize(var/mapload, var/flower_chance = 5)
|
||||
if(prob(flower_chance))
|
||||
flower_color = get_random_colour(1)
|
||||
. = ..(mapload)
|
||||
//species = GLOB.all_species[]
|
||||
ingested = new /datum/reagents/metabolism(500, src, CHEM_INGEST)
|
||||
reagents = ingested
|
||||
set_species(SPECIES_DIONA)
|
||||
setup_dionastats()
|
||||
eat_types |= TYPE_ORGANIC
|
||||
nutrition = 0 //We dont start with biomass
|
||||
update_verbs()
|
||||
|
||||
/mob/living/carbon/alien/diona/Destroy()
|
||||
cleanupTransfer()
|
||||
QDEL_NULL(ingested)
|
||||
QDEL_NULL(vessel)
|
||||
|
||||
QDEL_NULL(DS)
|
||||
gestalt = null
|
||||
master_nymph = null
|
||||
|
||||
hat = null
|
||||
|
||||
flower_color = null
|
||||
flower_image = null
|
||||
cut_overlays()
|
||||
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
/mob/living/carbon/alien/diona/get_ingested_reagents()
|
||||
return ingested
|
||||
|
||||
@@ -92,20 +123,6 @@
|
||||
else
|
||||
..()
|
||||
|
||||
/mob/living/carbon/alien/diona/Initialize(var/mapload, var/flower_chance = 5)
|
||||
if(prob(flower_chance))
|
||||
flower_color = get_random_colour(1)
|
||||
. = ..(mapload)
|
||||
//species = GLOB.all_species[]
|
||||
ingested = new /datum/reagents/metabolism(500, src, CHEM_INGEST)
|
||||
reagents = ingested
|
||||
set_species(SPECIES_DIONA)
|
||||
setup_dionastats()
|
||||
eat_types |= TYPE_ORGANIC
|
||||
nutrition = 0 //We dont start with biomass
|
||||
update_verbs()
|
||||
|
||||
|
||||
/mob/living/carbon/alien/diona/verb/check_light()
|
||||
set category = "Abilities"
|
||||
set name = "Check light level"
|
||||
@@ -321,14 +338,6 @@
|
||||
|
||||
return TRUE
|
||||
|
||||
/mob/living/carbon/alien/diona/Destroy()
|
||||
walk_to(src, 0)
|
||||
cleanupTransfer()
|
||||
QDEL_NULL(ingested)
|
||||
QDEL_NULL(vessel)
|
||||
QDEL_NULL(DS)
|
||||
. = ..()
|
||||
|
||||
/mob/living/carbon/alien/diona/proc/wear_hat(var/obj/item/new_hat)
|
||||
if(hat)
|
||||
return
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
death(1) //Brains can die again. AND THEY SHOULD AHA HA HA HA HA HA
|
||||
ghostize() //Ghostize checks for key so nothing else is necessary.
|
||||
container = null
|
||||
return ..()
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
/mob/living/carbon/brain/IsAdvancedToolUser() // to be able to use weapons when piloting a hardsuit
|
||||
return TRUE
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
for(var/obj/item/organ/external/E in src.organs)
|
||||
E.droplimb(0,DROPLIMB_EDGE,1)
|
||||
|
||||
sleep(1)
|
||||
|
||||
for(var/obj/item/I in src)
|
||||
drop_from_inventory(I)
|
||||
I.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)), rand(1,3), round(30/I.w_class))
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
/mob/living/carbon/human/Destroy()
|
||||
GLOB.human_mob_list -= src
|
||||
GLOB.intent_listener -= src
|
||||
QDEL_NULL_LIST(organs)
|
||||
QDEL_LIST(organs)
|
||||
internal_organs_by_name = null
|
||||
internal_organs = null
|
||||
organs_by_name = null
|
||||
@@ -130,7 +130,8 @@
|
||||
// Do this last so the mob's stuff doesn't drop on del.
|
||||
QDEL_NULL(w_uniform)
|
||||
|
||||
return ..()
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
/mob/living/carbon/human/can_devour(atom/movable/victim, var/silent = FALSE)
|
||||
if(!should_have_organ(BP_STOMACH))
|
||||
|
||||
@@ -801,7 +801,7 @@
|
||||
A.ex_act(2)
|
||||
|
||||
sleep(1)
|
||||
if (A && !(A.gcDestroyed) && A.type == oldtype)
|
||||
if (A && !(A.gc_destroyed) && A.type == oldtype)
|
||||
src.visible_message("<span class='danger'>[src.name] plows into \the [aname]!</span>")
|
||||
return 0
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
friends = null
|
||||
target_mob = null
|
||||
targets = null
|
||||
QDEL_NULL_ASSOC(target_type_validator_map)
|
||||
QDEL_LIST_ASSOC_VAL(target_type_validator_map)
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/can_name(var/mob/living/M)
|
||||
|
||||
@@ -187,6 +187,14 @@
|
||||
if(dead_on_map)
|
||||
death()
|
||||
|
||||
/mob/living/simple_animal/Destroy()
|
||||
cut_overlay(blood_overlay)
|
||||
movement_target = null
|
||||
QDEL_NULL(udder)
|
||||
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
/mob/living/simple_animal/Move(NewLoc, direct)
|
||||
. = ..()
|
||||
if(.)
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
for(var/datum/computer_file/program/P in hard_drive.stored_files)
|
||||
P.event_unregistered()
|
||||
|
||||
QDEL_NULL_LIST(hard_drive.stored_files)
|
||||
QDEL_LIST(hard_drive.stored_files)
|
||||
|
||||
for(var/obj/item/computer_hardware/CH in src.get_all_components())
|
||||
uninstall_component(null, CH)
|
||||
@@ -130,8 +130,8 @@
|
||||
service.service_deactivate()
|
||||
service.service_state = PROGRAM_STATE_KILLED
|
||||
|
||||
QDEL_NULL_LIST(idle_threads)
|
||||
QDEL_NULL_LIST(enabled_services)
|
||||
QDEL_LIST(idle_threads)
|
||||
QDEL_LIST(enabled_services)
|
||||
|
||||
if(looping_sound)
|
||||
soundloop.stop(src)
|
||||
@@ -237,6 +237,17 @@
|
||||
ui_interact(user) // Re-open the UI on this computer. It should show the main screen now.
|
||||
update_icon()
|
||||
|
||||
/**
|
||||
* Same as `kill_program()` but does not try to reopen the window, also makes the linter happy as it does not sleep
|
||||
*/
|
||||
/obj/item/modular_computer/proc/kill_program_shutdown(forced = FALSE)
|
||||
SHOULD_NOT_SLEEP(TRUE)
|
||||
|
||||
if(active_program && active_program.kill_program(forced))
|
||||
active_program = null
|
||||
else
|
||||
return FALSE
|
||||
|
||||
// Returns 0 for No Signal, 1 for Low Signal and 2 for Good Signal. 3 is for wired connection (always-on)
|
||||
/obj/item/modular_computer/proc/get_ntnet_status(var/specific_action = 0)
|
||||
if(network_card)
|
||||
@@ -251,7 +262,7 @@
|
||||
|
||||
/obj/item/modular_computer/proc/shutdown_computer(var/loud = TRUE)
|
||||
SStgui.close_uis(active_program)
|
||||
kill_program(TRUE)
|
||||
kill_program_shutdown(TRUE)
|
||||
for(var/datum/computer_file/program/P in idle_threads)
|
||||
P.kill_program(TRUE)
|
||||
idle_threads.Remove(P)
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
. = ..()
|
||||
set_icon()
|
||||
|
||||
/obj/item/modular_computer/handheld/Destroy()
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
/obj/item/modular_computer/handheld/proc/set_icon()
|
||||
icon_state_unpowered = icon_state
|
||||
icon_state_broken = icon_state
|
||||
|
||||
@@ -19,11 +19,16 @@
|
||||
. = computer_host
|
||||
|
||||
/obj/item/modular_computer/silicon/Initialize(mapload)
|
||||
. = ..()
|
||||
if(istype(loc, /mob/living/silicon))
|
||||
computer_host = loc
|
||||
else
|
||||
return INITIALIZE_HINT_QDEL
|
||||
|
||||
/obj/item/modular_computer/silicon/Destroy()
|
||||
computer_host = null
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
/obj/item/modular_computer/silicon/computer_use_power(power_usage)
|
||||
// If we have host like AI, borg or pAI we handle there power
|
||||
@@ -38,10 +43,6 @@
|
||||
// If we don't have host, then we let regular computer code handle power - like batteries and tesla coils.
|
||||
return ..()
|
||||
|
||||
/obj/item/modular_computer/silicon/Destroy()
|
||||
computer_host = null
|
||||
return ..()
|
||||
|
||||
/obj/item/modular_computer/silicon/Click(location, control, params)
|
||||
return attack_self(usr)
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
screen_on = FALSE
|
||||
icon_state = "laptop-closed"
|
||||
|
||||
/obj/item/modular_computer/laptop/preset/Destroy()
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
/obj/item/modular_computer/laptop/preset/install_default_hardware()
|
||||
..()
|
||||
processor_unit = new /obj/item/computer_hardware/processor_unit(src)
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/obj/item/modular_computer/telescreen/preset/Destroy()
|
||||
. = ..()
|
||||
GC_TEMPORARY_HARDDEL
|
||||
|
||||
/obj/item/modular_computer/telescreen/preset/install_default_hardware()
|
||||
..()
|
||||
processor_unit = new/obj/item/computer_hardware/processor_unit(src)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user