diff --git a/code/ATMOSPHERICS/he_pipes.dm b/code/ATMOSPHERICS/he_pipes.dm
index 15774f99c2f..0f9a38b09b3 100644
--- a/code/ATMOSPHERICS/he_pipes.dm
+++ b/code/ATMOSPHERICS/he_pipes.dm
@@ -54,11 +54,11 @@
if(target.initialize_directions_he & get_dir(target,src))
node2 = target
break
-
+
if(!node1 && !node2)
del(src)
return
-
+
update_icon()
return
@@ -105,10 +105,10 @@
if(target.initialize_directions_he & get_dir(target,src))
node2 = target
break
-
+
if(!node1 && !node2)
- del(src)
- return
+ qdel(src)
+ return
update_icon()
return
diff --git a/code/ATMOSPHERICS/pipes.dm b/code/ATMOSPHERICS/pipes.dm
index 2a143c80c51..b303d7d31f4 100644
--- a/code/ATMOSPHERICS/pipes.dm
+++ b/code/ATMOSPHERICS/pipes.dm
@@ -55,9 +55,10 @@
return parent.return_network(reference)
-/obj/machinery/atmospherics/pipe/Del()
+
+/obj/machinery/atmospherics/pipe/Destroy()
del(parent)
- if(air_temporary)
+ if(air_temporary && loc)
loc.assume_air(air_temporary)
..()
@@ -70,7 +71,7 @@
if(istype(W,/obj/item/device/pipe_painter))
return 0
-
+
if (istype(W, /obj/item/device/pipe_freezer))
if(!src.frozen) // If the pipe is not already frozen
user << "\red You begin to freeze the [src]"
@@ -88,7 +89,7 @@
"You hear dripping water.")
add_fingerprint(user)
- return 1
+ return 1
if (!istype(W, /obj/item/weapon/wrench))
return ..()
@@ -96,7 +97,7 @@
if (level==1 && isturf(T) && T.intact)
user << "\red You must remove the plating first."
return 1
-
+
var/datum/gas_mixture/int_air = return_air()
var/datum/gas_mixture/env_air = loc.return_air()
if ((int_air.return_pressure()-env_air.return_pressure()) > 2*ONE_ATMOSPHERE)
@@ -129,7 +130,7 @@
/*
/obj/machinery/atmospherics/pipe/add_underlay(var/obj/machinery/atmospherics/node, var/direction)
- if(istype(src, /obj/machinery/atmospherics/pipe/tank)) //todo: move tanks to unary devices
+ if(istype(src, /obj/machinery/atmospherics/pipe/tank)) //todo: move tanks to unary devices
return ..()
if(node)
@@ -182,7 +183,7 @@
/obj/machinery/atmospherics/pipe/simple/New()
..()
-
+
// Pipe colors and icon states are handled by an image cache - so color and icon should
// be null. For mapping purposes color is defined in the object definitions.
icon = null
@@ -242,7 +243,7 @@
else if(dir==12)
dir = 4
-/obj/machinery/atmospherics/pipe/simple/Del()
+/obj/machinery/atmospherics/pipe/simple/Destroy()
if(node1)
node1.disconnect(src)
if(node2)
@@ -276,7 +277,7 @@
if (meter.target == src)
new /obj/item/pipe_meter(T)
del(meter)
- del(src)
+ qdel(src)
else if(node1 && node2)
overlays += icon_manager.get_atmos_icon("pipe", , pipe_color, pipe_icon + "intact" + icon_connect_type)
else
@@ -313,9 +314,9 @@
src.connected_to = c
node2 = target
break
-
+
if(!node1 && !node2)
- del(src)
+ qdel(src)
return
var/turf/T = get_turf(src)
@@ -359,7 +360,7 @@
layer = 2.39
icon_connect_type = "-supply"
color = PIPE_COLOR_BLUE
-
+
/obj/machinery/atmospherics/pipe/simple/visible/yellow
color = PIPE_COLOR_YELLOW
@@ -394,7 +395,7 @@
layer = 2.39
icon_connect_type = "-supply"
color = PIPE_COLOR_BLUE
-
+
/obj/machinery/atmospherics/pipe/simple/visible/universal
name="Universal pipe adapter"
desc = "An adapter for regular, supply and scrubbers pipes"
@@ -567,7 +568,7 @@
else
. = PROCESS_KILL
-/obj/machinery/atmospherics/pipe/manifold/Del()
+/obj/machinery/atmospherics/pipe/manifold/Destroy()
if(node1)
node1.disconnect(src)
if(node2)
@@ -620,19 +621,19 @@
if (meter.target == src)
new /obj/item/pipe_meter(T)
del(meter)
- del(src)
+ qdel(src)
else
overlays.Cut()
overlays += icon_manager.get_atmos_icon("manifold", , pipe_color, "core" + icon_connect_type)
overlays += icon_manager.get_atmos_icon("manifold", , , "clamps" + icon_connect_type)
underlays.Cut()
-
+
var/turf/T = get_turf(src)
var/list/directions = list(NORTH, SOUTH, EAST, WEST)
var/node1_direction = get_dir(src, node1)
var/node2_direction = get_dir(src, node2)
var/node3_direction = get_dir(src, node3)
-
+
directions -= dir
directions -= add_underlay(T,node1,node1_direction,icon_connect_type)
@@ -692,7 +693,7 @@
break
if (node3)
break
-
+
if(!node1 && !node2 && !node3)
del(src)
return
@@ -804,7 +805,7 @@
else
. = PROCESS_KILL
-/obj/machinery/atmospherics/pipe/manifold4w/Del()
+/obj/machinery/atmospherics/pipe/manifold4w/Destroy()
if(node1)
node1.disconnect(src)
if(node2)
@@ -866,13 +867,13 @@
if (meter.target == src)
new /obj/item/pipe_meter(T)
del(meter)
- del(src)
+ qdel(src)
else
overlays.Cut()
overlays += icon_manager.get_atmos_icon("manifold", , pipe_color, "4way" + icon_connect_type)
overlays += icon_manager.get_atmos_icon("manifold", , , "clamps_4way" + icon_connect_type)
underlays.Cut()
-
+
/*
var/list/directions = list(NORTH, SOUTH, EAST, WEST)
@@ -884,7 +885,7 @@
for(var/D in directions)
add_underlay(,D)
*/
-
+
var/turf/T = get_turf(src)
var/list/directions = list(NORTH, SOUTH, EAST, WEST)
var/node1_direction = get_dir(src, node1)
@@ -948,7 +949,7 @@
src.connected_to = c
node4 = target
break
-
+
if(!node1 && !node2 && !node3&& !node4)
del(src)
return
@@ -1059,7 +1060,7 @@
..()
else
. = PROCESS_KILL
-/obj/machinery/atmospherics/pipe/cap/Del()
+/obj/machinery/atmospherics/pipe/cap/Destroy()
if(node)
node.disconnect(src)
@@ -1176,7 +1177,7 @@
else
. = PROCESS_KILL
-/obj/machinery/atmospherics/pipe/tank/Del()
+/obj/machinery/atmospherics/pipe/tank/Destroy()
if(node1)
node1.disconnect(src)
@@ -1263,7 +1264,7 @@
air_temporary.oxygen = (25*ONE_ATMOSPHERE*O2STANDARD)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)
air_temporary.nitrogen = (25*ONE_ATMOSPHERE*N2STANDARD)*(air_temporary.volume)/(R_IDEAL_GAS_EQUATION*air_temporary.temperature)
- ..()
+ ..()
icon_state = "air"
/obj/machinery/atmospherics/pipe/tank/oxygen
@@ -1376,7 +1377,7 @@
else
parent.mingle_with_turf(loc, volume)
-/obj/machinery/atmospherics/pipe/vent/Del()
+/obj/machinery/atmospherics/pipe/vent/Destroy()
if(node1)
node1.disconnect(src)
diff --git a/code/__HELPERS/experimental.dm b/code/__HELPERS/experimental.dm
index 0966261563b..da9c2a17446 100644
--- a/code/__HELPERS/experimental.dm
+++ b/code/__HELPERS/experimental.dm
@@ -1,5 +1,5 @@
/*
- * Experimental procs by ESwordTheCat.
+ * Experimental procs by ESwordTheCat!
*/
/*
@@ -15,7 +15,7 @@
* -1, parameter B is not a char
* -2, parameter A is not a string
*/
-/proc/EgijkAeN(const/A, const/B)
+/proc/strpos(const/A, const/B)
if (istext(A) == 0 || length(A) < 1)
return -2
@@ -31,29 +31,24 @@
. = i
i = findtext(A, B, i + 1)
-/obj/machinery/proc/getArea()
- var/area/A = loc.loc
-
- if (A != myArea)
- myArea = A
-
- . = myArea
-
/**
* Object pooling.
*
* If this file is named experimental,
* well treat this implementation as experimental experimental (redundancy intended).
*
- * WARNING, only supports /mob and /obj.
+ * WARNING, only supports /atom/movable (/mob and /obj)
*/
// Uncomment to show debug messages.
//#define DEBUG_OBJECT_POOL
-#define MAINTAINING_OBJECT_POOL_COUNT 20
+#define MAINTAINING_OBJECT_POOL_COUNT 500
-var/list/masterPool = list()
+var/global/list/masterPool = new
+
+// Read-only or compile-time vars and special exceptions.
+var/list/exclude = list("inhand_states", "loc", "locs", "parent_type", "vars", "verbs", "type", "x", "y", "z")
/*
* @args
@@ -62,27 +57,35 @@ var/list/masterPool = list()
*
* Example call: getFromPool(/obj/item/weapon/shard, loc)
*/
-/proc/getFromPool(const/A, const/B)
- if (isnull(masterPool[A]))
+/proc/getFromPool()
+ var/A = args[1]
+ var/list/B = list()
+ B += (args - A)
+ if(length(masterPool["[A]"]) <= 0)
#ifdef DEBUG_OBJECT_POOL
- world << "DEBUG_OBJECT_POOL: new proc has been called ([A])."
+ world << text("DEBUG_OBJECT_POOL: new proc has been called ([]).", A)
#endif
+ //so the GC knows we're pooling this type.
+ if(isnull(masterPool["[A]"]))
+ masterPool["[A]"] = list(new A)
+ if(B && B.len)
+ return new A(arglist(B))
+ else
+ return new A()
- return new A(B)
-
- var/atom/movable/Object = masterPool[A][1]
- masterPool[A] -= Object
- var/objectLength = length(masterPool[A])
+ var/atom/movable/O = masterPool["[A]"][1]
+ masterPool["[A]"] -= O
#ifdef DEBUG_OBJECT_POOL
- world << "DEBUG_OBJECT_POOL: getFromPool([A]) [objectLength] left."
+ world << text("DEBUG_OBJECT_POOL: getFromPool([]) [] left.", A, length(masterPool[A]))
#endif
-
- if (!objectLength)
- masterPool[A] = null
-
- Object.loc = B
- return Object
+ if(!O || !istype(O))
+ O = new A(arglist(B))
+ else
+ if(length(B))
+ O.loc = B[1]
+ O.New(arglist(B))
+ return O
/*
* @args
@@ -93,31 +96,26 @@ var/list/masterPool = list()
*
* Example call: returnToPool(src)
*/
-/proc/returnToPool(const/A)
- if (!istype(A, /atom/movable))
- return -1
+/proc/returnToPool(const/atom/movable/AM)
+ if(istype(AM.loc,/mob/living))
+ var/mob/living/L = AM.loc
+ L.unEquip(AM)
+ if(length(masterPool["[AM.type]"]) > MAINTAINING_OBJECT_POOL_COUNT)
+ #ifdef DEBUG_OBJECT_POOL
+ world << text("DEBUG_OBJECT_POOL: returnToPool([]) exceeds [] discarding...", AM.type, MAINTAINING_OBJECT_POOL_COUNT)
+ #endif
- var/atom/movable/Object = A
- Object.resetVariables()
+ qdel(AM, 1)
+ return
- switch(length(masterPool[Object.type]))
- if (MAINTAINING_OBJECT_POOL_COUNT to 1.#INF)
- #ifdef DEBUG_OBJECT_POOL
- world << "DEBUG_OBJECT_POOL: returnToPool([Object.type]) exceeds [num2text(MAINTAINING_OBJECT_POOL_COUNT)] discarding..."
- #endif
+ if(isnull(masterPool["[AM.type]"]))
+ masterPool["[AM.type]"] = list()
- return
- if (0) // In a numeric context (like a mathematical operation), null evaluates to 0.
- #ifdef DEBUG_OBJECT_POOL
- world << "DEBUG_OBJECT_POOL: [Object.type] pool is empty, recreating pool."
- #endif
-
- masterPool[Object.type] = list()
-
- masterPool[Object.type] += Object
+ AM.resetVariables()
+ masterPool["[AM.type]"] += AM
#ifdef DEBUG_OBJECT_POOL
- world << "DEBUG_OBJECT_POOL: returnToPool([Object.type]) [length(masterPool[Object.type])] left."
+ world << text("DEBUG_OBJECT_POOL: returnToPool([]) [] left.", AM.type, length(masterPool["[AM.type]"]))
#endif
#undef MAINTAINING_OBJECT_POOL_COUNT
@@ -127,20 +125,48 @@ var/list/masterPool = list()
#endif
/*
- * Override this if the object variables needed to reset.
+ * if you have a variable that needed to be preserve, override this and call ..
*
- * Example: see, code\game\objects\items\stacks\sheets\glass.dm
- * @/obj/item/weapon/shard
- * @resetVariables()
+ * example
+ *
+ * /obj/item/resetVariables()
+ * ..("var1", "var2", "var3")
+ *
+ * however, if the object has a child type an it has overridden resetVariables()
+ * this should be
+ *
+ * /obj/item/resetVariables()
+ * ..("var1", "var2", "var3", args)
+ *
+ * /obj/item/weapon/resetVariables()
+ * ..("var4")
*/
-/atom/movable
- proc/resetVariables()
- var/list/exclude = list("loc", "locs", "parent_type", "vars", "verbs", "type") // Read-only or compile-time vars and whatevs.
- exclude += args // Explicit var exclusion
- var/list/varsCopy = vars - exclude
- var/key
+/atom/movable/proc/resetVariables()
+ loc = null
- for (key in varsCopy)
- vars[key] = initial(vars[key])
+ var/list/exclude = global.exclude + args // explicit var exclusion
- vars["loc"] = null // Making sure the loc is null not a compile-time var value.
+ for(var/key in vars)
+ if(key in exclude)
+ continue
+
+ vars[key] = initial(vars[key])
+
+/proc/isInTypes(atom/Object, types)
+ var/prototype = Object.type
+ Object = null
+
+ for (var/type in params2list(types))
+ if (ispath(prototype, text2path(type)))
+ return 1
+
+ return 0
+
+
+/obj/machinery/proc/getArea()
+ var/area/A = loc.loc
+
+ if (A != myArea)
+ myArea = A
+
+ . = myArea
\ No newline at end of file
diff --git a/code/_onclick/telekinesis.dm b/code/_onclick/telekinesis.dm
index 5fb552d4384..46662142fba 100644
--- a/code/_onclick/telekinesis.dm
+++ b/code/_onclick/telekinesis.dm
@@ -108,7 +108,7 @@ var/const/tk_maxrange = 15
return
var/d = get_dist(user, target)
- if(focus)
+ if(focus)
d = max(d,get_dist(user,focus)) // whichever is further
if(d > tk_maxrange)
user << "Your mind won't reach that far."
@@ -163,7 +163,7 @@ var/const/tk_maxrange = 15
O.icon_state = "nothing"
flick("empdisable",O)
spawn(5)
- O.delete()
+ qdel(O)
return
diff --git a/code/controllers/_DynamicAreaLighting_TG.dm b/code/controllers/_DynamicAreaLighting_TG.dm
index 15e0b2e9c8f..7ed1a047a0d 100644
--- a/code/controllers/_DynamicAreaLighting_TG.dm
+++ b/code/controllers/_DynamicAreaLighting_TG.dm
@@ -160,14 +160,6 @@ atom/movable/New()
trueLuminosity = luminosity * luminosity
light = new(src)
-//Objects with opacity will trigger nearby lights to update at next lighting process.
-atom/movable/Destroy()
- if(opacity)
- if(isturf(loc))
- if(loc:lighting_lumcount > 1)
- UpdateAffectingLights()
-
- ..()
//Sets our luminosity.
//If we have no light it will create one.
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index 062a650c9ba..95ecef88978 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -134,12 +134,12 @@
var/python_path = "" //Path to the python executable. Defaults to "python" on windows and "/usr/bin/env python2" on unix
var/default_laws = 0 //Controls what laws the AI spawns with.
-
+
var/list/station_levels = list(1) // Defines which Z-levels the station exists on.
var/list/admin_levels= list(2) // Defines which Z-levels which are for admin functionality, for example including such areas as Central Command and the Syndicate Shuttle
var/list/contact_levels = list(1, 5) // Defines which Z-levels which, for example, a Code Red announcement may affect
var/list/player_levels = list(1, 3, 4, 5, 6, 7) // Defines all Z-levels a character can typically reach
-
+
var/const/minutes_to_ticks = 60 * 10
// Event settings
var/expected_round_length = 60 * 2 * minutes_to_ticks // 2 hours
@@ -168,7 +168,7 @@
src.probabilities[M.config_tag] = M.probability
if (M.votable)
src.votable_modes += M.config_tag
- del(M)
+ qdel(M)
src.votable_modes += "secret"
/datum/configuration/proc/load(filename, type = "config") //the type can also be game_options, in which case it uses a different switch. not making it separate to not copypaste code - Urist
@@ -209,9 +209,9 @@
if ("use_age_restriction_for_jobs")
config.use_age_restriction_for_jobs = 1
-
+
if ("use_age_restriction_for_antags")
- config.use_age_restriction_for_antags = 1
+ config.use_age_restriction_for_antags = 1
if ("jobs_have_minimal_access")
config.jobs_have_minimal_access = 1
@@ -462,7 +462,7 @@
if("max_maint_drones")
config.max_maint_drones = text2num(value)
-
+
if("station_levels")
config.station_levels = text2numlist(value, ";")
@@ -474,7 +474,7 @@
if("player_levels")
config.player_levels = text2numlist(value, ";")
-
+
if("expected_round_length")
config.expected_round_length = MinutesToTicks(text2num(value))
diff --git a/code/controllers/failsafe.dm b/code/controllers/failsafe.dm
index ff46b6b10b6..a2a6274a8ab 100644
--- a/code/controllers/failsafe.dm
+++ b/code/controllers/failsafe.dm
@@ -1,68 +1,78 @@
-var/datum/controller/failsafe/Failsafe
+var/global/datum/controller/failsafe/failsafe
-/datum/controller/failsafe // This thing pretty much just keeps poking the master controller
- var/processing = 0
- var/processing_interval = 100 //poke the MC every 10 seconds
+/datum/controller/failsafe // This thing pretty much just keeps poking the controllers.
+ processing_interval = 100 // Poke the controllers every 10 seconds.
- var/MC_iteration = 0
- var/MC_defcon = 0 //alert level. For every poke that fails this is raised by 1. When it reaches 5 the MC is replaced with a new one. (effectively killing any master_controller.process() and starting a new one)
+ /*
+ * Controller alert level.
+ * For every poke that fails this is raised by 1.
+ * When it reaches 5 the MC is replaced with a new one
+ * (effectively killing any controller process() and starting a new one).
+ */
- var/lighting_iteration = 0
- var/lighting_defcon = 0 //alert level for lighting controller.
+ // master
+ var/masterControllerIteration = 0
+ var/masterControllerAlertLevel = 0
+
+ // lighting
+ var/lightingControllerIteration = 0
+ var/lightingControllerAlertLevel = 0
/datum/controller/failsafe/New()
- //There can be only one failsafe. Out with the old in with the new (that way we can restart the Failsafe by spawning a new one)
- if(Failsafe != src)
- if(istype(Failsafe))
- del(Failsafe)
- Failsafe = src
- Failsafe.process()
+ . = ..()
+ // There can be only one failsafe. Out with the old in with the new (that way we can restart the Failsafe by spawning a new one).
+ if (failsafe != src)
+ if (istype(failsafe))
+ recover()
+ qdel(failsafe)
+
+ failsafe = src
+
+ failsafe.process()
/datum/controller/failsafe/proc/process()
processing = 1
+
spawn(0)
- //set background = 1
- while(1) //more efficient than recursivly calling ourself over and over. background = 1 ensures we do not trigger an infinite loop
- if(!master_controller) new /datum/controller/game_controller() //replace the missing master_controller! This should never happen.
- if(!lighting_controller) new /datum/controller/lighting() //replace the missing lighting_controller
+ set background = BACKGROUND_ENABLED
+
+ while(1) // More efficient than recursivly calling ourself over and over. background = 1 ensures we do not trigger an infinite loop.
+ iteration++
if(processing)
- if(master_controller.processing) //only poke if these overrides aren't in effect
- if(MC_iteration == controller_iteration) //master_controller hasn't finished processing in the defined interval
- switch(MC_defcon)
+ if(master_controller.processing) // Only poke if these overrides aren't in effect
+ if(masterControllerIteration == master_controller.iteration) // Master controller hasn't finished processing in the defined interval.
+ switch(masterControllerAlertLevel)
if(0 to 3)
- MC_defcon++
+ masterControllerAlertLevel++
if(4)
- admins << "Warning. The Master Controller has not fired in the last [MC_defcon*processing_interval] ticks. Automatic restart in [processing_interval] ticks."
- MC_defcon = 5
+ admins << "Warning. The master Controller has not fired in the last [masterControllerAlertLevel * processing_interval] ticks. Automatic restart in [processing_interval] ticks."
+ masterControllerAlertLevel = 5
if(5)
- admins << "Warning. The Master Controller has still not fired within the last [MC_defcon*processing_interval] ticks. Killing and restarting..."
- new /datum/controller/game_controller() //replace the old master_controller (hence killing the old one's process)
- master_controller.process() //Start it rolling again
- MC_defcon = 0
+ admins << "Warning. The master Controller has still not fired within the last [masterControllerAlertLevel * processing_interval] ticks. Killing and restarting..."
+ new /datum/controller/game_controller() // Replace the old master controller (hence killing the old one's process).
+ master_controller.process() // Start it rolling again.
+ masterControllerAlertLevel = 0
else
- MC_defcon = 0
- MC_iteration = controller_iteration
+ masterControllerAlertLevel = 0
+ masterControllerIteration = master_controller.iteration
if(lighting_controller.processing)
- if(lighting_iteration == lighting_controller.iteration) //master_controller hasn't finished processing in the defined interval
- switch(lighting_defcon)
+ if(lightingControllerIteration == lighting_controller.iteration) // Lighting controller hasn't finished processing in the defined interval.
+ switch(lightingControllerAlertLevel)
if(0 to 3)
- lighting_defcon++
+ lightingControllerAlertLevel++
if(4)
- admins << "Warning. The Lighting Controller has not fired in the last [lighting_defcon*processing_interval] ticks. Automatic restart in [processing_interval] ticks."
- lighting_defcon = 5
+ admins << "Warning. The lighting_controller controller has not fired in the last [lightingControllerAlertLevel * processing_interval] ticks. Automatic restart in [processing_interval] ticks."
+ lightingControllerAlertLevel = 5
if(5)
- admins << "Warning. The Lighting Controller has still not fired within the last [lighting_defcon*processing_interval] ticks. Killing and restarting..."
- new /datum/controller/lighting() //replace the old lighting_controller (hence killing the old one's process)
- lighting_controller.process() //Start it rolling again
- lighting_defcon = 0
+ admins << "Warning. The lighting_controller controller has still not fired within the last [lightingControllerAlertLevel * processing_interval] ticks. Killing and restarting..."
+ new /datum/controller/lighting() // Replace the old lighting_controller (hence killing the old one's process).
+ lighting_controller.process() // Start it rolling again.
+ lightingControllerAlertLevel = 0
else
- lighting_defcon = 0
- lighting_iteration = lighting_controller.iteration
- else
- MC_defcon = 0
- lighting_defcon = 0
+ lightingControllerAlertLevel = 0
+ lightingControllerIteration = lighting_controller.iteration
- sleep(processing_interval)
\ No newline at end of file
+ sleep(processing_interval)
diff --git a/code/controllers/garbage.dm b/code/controllers/garbage.dm
index 412a4bdef10..b120d83abcb 100644
--- a/code/controllers/garbage.dm
+++ b/code/controllers/garbage.dm
@@ -1,124 +1,182 @@
+#define GC_COLLECTIONS_PER_TICK 300 // Was 100.
+#define GC_COLLECTION_TIMEOUT (30 SECONDS)
+#define GC_FORCE_DEL_PER_TICK 60
+//#define GC_DEBUG
-#define GC_COLLECTIONS_PER_TICK 250 // Was 100
-#define GC_COLLECTION_TIMEOUT 100 // 10s
-var/global/datum/controller/garbage_collector/garbage
-var/global/list/uncollectable_vars=list(
- "alpha",
- "bestF",
- "bounds",
- "bound_height",
- "bound_width",
- "ckey",
- "color",
- "contents",
- "gender",
- "group",
- "key",
- //"loc",
- "locs",
- "luminosity",
- "parent",
- "parent_type",
- "step_size",
- "glide_size",
- "gc_destroyed",
- "step_x",
- "step_y",
- "step_z",
- "tag",
- "thermal_conductivity",
- "type",
- "vars",
- "verbs",
- "x",
- "y",
- "z",
-)
-/datum/controller/garbage_collector
- var/list/queue=list()
- var/list/destroyed=list()
- var/waiting=0
- var/del_everything=1
- var/turf/trashbin=null
+var/list/gc_hard_del_types = new
+var/datum/garbage_collector/garbageCollector
- New()
- trashbin=locate(0,0,CENTCOMM_Z)
+/client/verb/gc_dump_hdl()
+ set name = "(GC) Hard Del List"
+ set desc = "List types that are hard del()'d by the GC."
+ set category = "Debug"
- proc/AddTrash(var/atom/movable/A)
- if(!A)
- return
- if(del_everything)
- del(A)
- return
- A.loc=trashbin
- queue.Add(A)
- waiting++
+ for(var/A in gc_hard_del_types)
+ usr << "[A] = [gc_hard_del_types[A]]"
- proc/Pop()
- var/atom/movable/A = queue[1]
- if(!A)
- if(isnull(A))
- var/loopcheck = 0
- while(queue.Remove(null))
- loopcheck++
- if(loopcheck > 50)
- break
- return
- if(del_everything)
- del(A)
- return
- if(!istype(A,/atom/movable))
- testing("GC given a [A.type].")
- del(A)
- return
- for(var/vname in A.vars)
- if(!issaved(A.vars[vname]))
- continue
- if(vname in uncollectable_vars)
- continue
- //testing("Unsetting [vname] in [A.type]!")
- A.vars[vname]=null
- A.loc=null
- destroyed.Add("\ref[A]")
- queue.Remove(A)
+/datum/garbage_collector
+ var/list/queue = new
+ var/del_everything = 0
- proc/process()
- for(var/i=0;i= world.time - GC_COLLECTION_TIMEOUT)
- // Something's still referring to the qdel'd object. Kill it.
- del(A)
- destroyed.Remove(refID)
+ // To let them know how hardworking am I :^).
+ var/dels_count = 0
+ var/hard_dels = 0
+ var/soft_dels = 0
-/**
-* NEVER USE THIS FOR ANYTHING OTHER THAN /atom/movable
-* OTHER TYPES CANNOT BE QDEL'D BECAUSE THEIR LOC IS LOCKED OR THEY DON'T HAVE ONE.
-*/
-/proc/qdel(var/atom/movable/A)
- if(!A) return
- if(!istype(A))
- warning("qdel() passed object of type [A.type]. qdel() can only handle /atom/movable types.")
- del(A)
+/datum/garbage_collector/proc/addTrash(const/atom/movable/AM)
+ if(!istype(AM))
return
- if(!garbage)
- del(A)
+
+ if(del_everything)
+ del(AM)
+ hard_dels++
+ dels_count++
return
- // Let our friend know they're about to get fucked up.
- A.Destroy()
- garbage.AddTrash(A)
+
+ queue["\ref[AM]"] = world.timeofday
+
+/datum/garbage_collector/proc/process()
+ var/remainingCollectionPerTick = GC_COLLECTIONS_PER_TICK
+ var/remainingForceDelPerTick = GC_FORCE_DEL_PER_TICK
+ var/collectionTimeScope = world.timeofday - GC_COLLECTION_TIMEOUT
+ if(narsie_cometh) return //don't even fucking bother, its over.
+ while(queue.len && --remainingCollectionPerTick >= 0)
+ var/refID = queue[1]
+ var/destroyedAtTime = queue[refID]
+
+ if(destroyedAtTime > collectionTimeScope)
+ break
+
+ var/atom/movable/AM = locate(refID)
+ if(AM) // Something's still referring to the qdel'd object. del it.
+ if(isnull(AM.gcDestroyed))
+ queue -= refID
+ continue
+ if(remainingForceDelPerTick <= 0)
+ break
+
+ #ifdef GC_DEBUG
+ WARNING("gc process force delete [AM.type]")
+ #endif
+
+ AM.hard_deleted = 1
+ del AM
+
+ hard_dels++
+ remainingForceDelPerTick--
+
+#ifdef GC_DEBUG
+#undef GC_DEBUG
+#endif
+
+#undef GC_FORCE_DEL_PER_TICK
+#undef GC_COLLECTION_TIMEOUT
+#undef GC_COLLECTIONS_PER_TICK
+
+/datum/garbage_collector/proc/dequeue(id)
+ if (queue)
+ queue -= id
+
+ dels_count++
+
+/*
+ * NEVER USE THIS FOR ANYTHING OTHER THAN /atom/movable
+ * OTHER TYPES CANNOT BE QDEL'D BECAUSE THEIR LOC IS LOCKED OR THEY DON'T HAVE ONE.
+ */
+/proc/qdel(const/atom/movable/AM, ignore_pooling = 0)
+ if(isnull(AM))
+ return
+
+ if(isnull(garbageCollector))
+ del(AM)
+ return
+
+ if(!istype(AM))
+ WARNING("qdel() passed object of type [AM.type]. qdel() can only handle /atom/movable types.")
+ del(AM)
+ garbageCollector.hard_dels++
+ garbageCollector.dels_count++
+ return
+
+ //We are object pooling this.
+ if(("[AM.type]" in masterPool) && !ignore_pooling)
+ returnToPool(AM)
+ return
+
+ if(isnull(AM.gcDestroyed))
+ // Let our friend know they're about to get fucked up.
+ AM.Destroy()
+
+ garbageCollector.addTrash(AM)
+
+/datum/controller
+ var/processing = 0
+ var/iteration = 0
+ var/processing_interval = 0
+
+/datum/controller/proc/recover() // If we are replacing an existing controller (due to a crash) we attempt to preserve as much as we can.
+
+/*
+ * Like Del(), but for qdel.
+ * Called BEFORE qdel moves shit.
+ */
+/datum/proc/Destroy()
+ del(src)
/client/proc/qdel_toggle()
set name = "Toggle qdel Behavior"
set desc = "Toggle qdel usage between normal and force del()."
set category = "Debug"
- garbage.del_everything = !garbage.del_everything
- world << "GC: qdel turned [garbage.del_everything?"off":"on"]."
- log_admin("[key_name(usr)] turned qdel [garbage.del_everything?"off":"on"].")
- message_admins("\blue [key_name(usr)] turned qdel [garbage.del_everything?"off":"on"].", 1)
\ No newline at end of file
+ garbageCollector.del_everything = !garbageCollector.del_everything
+ world << "GC: qdel turned [garbageCollector.del_everything ? "off" : "on"]."
+ log_admin("[key_name(usr)] turned qdel [garbageCollector.del_everything ? "off" : "on"].")
+ message_admins("\blue [key_name(usr)] turned qdel [garbageCollector.del_everything ? "off" : "on"].", 1)
+
+/*/client/var/running_find_references
+
+/atom/verb/find_references()
+ set category = "Debug"
+ set name = "Find References"
+ set background = 1
+ set src in world
+
+ if(!usr || !usr.client)
+ return
+
+ if(usr.client.running_find_references)
+ testing("CANCELLED search for references to a [usr.client.running_find_references].")
+ usr.client.running_find_references = null
+ return
+
+ if(alert("Running this will create a lot of lag until it finishes. You can cancel it by running it again. Would you like to begin the search?", "Find References", "Yes", "No") == "No")
+ return
+ qdel(src)
+ // Remove this object from the list of things to be auto-deleted.
+ if(garbageCollector)
+ garbageCollector.queue -= "\ref[src]"
+
+ usr.client.running_find_references = type
+ testing("Beginning search for references to a [type].")
+ var/list/things = list()
+ for(var/client/thing)
+ things += thing
+ for(var/datum/thing)
+ things += thing
+ for(var/atom/thing)
+ things += thing
+ for(var/event/thing)
+ things += thing
+ testing("Collected list of things in search for references to a [type]. ([things.len] Thing\s)")
+ for(var/datum/thing in things)
+ if(!usr.client.running_find_references) return
+ for(var/varname in thing.vars)
+ var/variable = thing.vars[varname]
+ if(variable == src)
+ testing("Found [src.type] \ref[src] in [thing.type]'s [varname] var.")
+ else if(islist(variable))
+ if(src in variable)
+ testing("Found [src.type] \ref[src] in [thing.type]'s [varname] list var.")
+ testing("Completed search for references to a [type].")
+ usr.client.running_find_references = null
+*/
\ No newline at end of file
diff --git a/code/controllers/lighting_controller.dm b/code/controllers/lighting_controller.dm
index c118fa43abb..fd1a2305cb1 100644
--- a/code/controllers/lighting_controller.dm
+++ b/code/controllers/lighting_controller.dm
@@ -1,10 +1,8 @@
var/datum/controller/lighting/lighting_controller = new ()
datum/controller/lighting
- var/processing = 0
- var/processing_interval = 5 //setting this too low will probably kill the server. Don't be silly with it!
+ processing_interval = 5 //setting this too low will probably kill the server. Don't be silly with it!
var/process_cost = 0
- var/iteration = 0
var/lighting_states = 7
diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm
index 518cfafd522..98c44b42b6e 100644
--- a/code/controllers/master_controller.dm
+++ b/code/controllers/master_controller.dm
@@ -11,7 +11,6 @@ var/global/air_processing_killed = 0
var/global/pipe_processing_killed = 0
datum/controller/game_controller
- var/processing = 0
var/breather_ticks = 2 //a somewhat crude attempt to iron over the 'bumps' caused by high-cpu use by letting the MC have a breather for this many ticks after every loop
var/minimum_ticks = 20 //The minimum length of time between MC ticks
@@ -28,7 +27,7 @@ datum/controller/game_controller
var/events_cost = 0
var/puddles_cost
var/ticker_cost = 0
- var/gc_cost = 0
+ var/garbageCollectorCost = 0
var/total_cost = 0
var/last_thing_processed
@@ -36,6 +35,7 @@ datum/controller/game_controller
var/list/shuttle_list // For debugging and VV
var/datum/ore_distribution/asteroid_ore_map // For debugging and VV.
+ var/global/datum/garbage_collector/garbageCollector
datum/controller/game_controller/New()
//There can be only one master_controller. Out with the old and in with the new.
@@ -71,9 +71,6 @@ datum/controller/game_controller/proc/setup()
ticker = new /datum/controller/gameticker()
*/
- if(!garbage)
- garbage = new /datum/controller/garbage_collector()
-
color_windows_init()
setup_objects()
@@ -134,7 +131,7 @@ datum/controller/game_controller/proc/process()
spawn(0)
//set background = 1
while(1) //far more efficient than recursively calling ourself
- if(!Failsafe) new /datum/controller/failsafe()
+ if(!failsafe) new /datum/controller/failsafe()
if(processing)
@@ -246,12 +243,12 @@ datum/controller/game_controller/proc/process()
// GC
timer = world.timeofday
- last_thing_processed = garbage.type
- garbage.process()
- gc_cost = (world.timeofday - timer) / 10
+ last_thing_processed = garbageCollector.type
+ garbageCollector.process()
+ garbageCollectorCost = (world.timeofday - timer) / 10
//TIMING
- total_cost = air_cost + sun_cost + mobs_cost + diseases_cost + machines_cost + aibots_cost + objects_cost + networks_cost + powernets_cost + nano_cost + events_cost + puddles_cost + ticker_cost + gc_cost
+ total_cost = air_cost + sun_cost + mobs_cost + diseases_cost + machines_cost + aibots_cost + objects_cost + networks_cost + powernets_cost + nano_cost + events_cost + puddles_cost + ticker_cost + garbageCollectorCost
var/end_time = world.timeofday
if(end_time < start_time) //why not just use world.time instead?
diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm
index 20252b0403b..90e877b0443 100644
--- a/code/controllers/verbs.dm
+++ b/code/controllers/verbs.dm
@@ -49,7 +49,7 @@
return
-/client/proc/debug_controller(controller in list("Master","Failsafe","Ticker","Lighting","Air","Jobs","Sun","Radio","Supply","Shuttles","Emergency Shuttle","Configuration","pAI", "Cameras","Garbage", "Transfer Controller","Event"))
+/client/proc/debug_controller(controller in list("Master","failsafe","Ticker","Lighting","Air","Jobs","Sun","Radio","Supply","Shuttles","Emergency Shuttle","Configuration","pAI", "Cameras","Garbage", "Transfer Controller","Event","Scheduler"))
set category = "Debug"
set name = "Debug Controller"
set desc = "Debug the various periodic loop controllers for the game (be careful!)"
@@ -59,18 +59,15 @@
if("Master")
debug_variables(master_controller)
feedback_add_details("admin_verb","DMC")
- if("Failsafe")
- debug_variables(Failsafe)
- feedback_add_details("admin_verb","DFailsafe")
+ if ("failsafe")
+ debug_variables(failsafe)
+ feedback_add_details("admin_verb", "dfailsafe")
if("Ticker")
debug_variables(ticker)
feedback_add_details("admin_verb","DTicker")
if("Lighting")
debug_variables(lighting_controller)
feedback_add_details("admin_verb","DLighting")
- if("Garbage")
- debug_variables(garbage)
- feedback_add_details("admin_verb","DGarbage")
if("Air")
debug_variables(air_master)
feedback_add_details("admin_verb","DAir")
@@ -103,7 +100,13 @@
feedback_add_details("admin_verb","DCameras")
if("Event")
debug_variables(event_manager)
- feedback_add_details("admin_verb","DEvent")
-
+ feedback_add_details("admin_verb","DEvent")
+ if("Garbage")
+ debug_variables(garbageCollector)
+ feedback_add_details("admin_verb","DGarbage")
+ if("Scheduler")
+ debug_variables(processScheduler)
+ feedback_add_details("admin_verb","DprocessScheduler")
+
message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.")
return
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index fa22e742970..61b63bb6a9d 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -1,3 +1,6 @@
+var/global/list/del_profiling = list()
+var/global/list/gdel_profiling = list()
+var/global/list/ghdel_profiling = list()
/atom
layer = 2
var/level = 2
@@ -24,16 +27,18 @@
// Garbage collection
var/gc_destroyed=null
-/atom/Del()
- // Pass to Destroy().
- if(!gc_destroyed)
- Destroy()
- ..()
-/atom/proc/Destroy()
- gc_destroyed=world.time
+/atom/Destroy()
+ SetOpacity(0)
+ if(reagents)
+ reagents.Destroy()
+ reagents = null
+
+ // Idea by ChuckTheSheep to make the object even more unreferencable.
+ invisibility = 101
+
/atom/proc/CheckParts()
return
@@ -242,7 +247,7 @@ its easier to just keep the beam vertical.
return
/atom/proc/emag_act()
- return
+ return
/atom/proc/hitby(atom/movable/AM as mob|obj)
if (density)
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 28c188467b4..419f17b24ad 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -15,13 +15,67 @@
var/mob/pulledby = null
var/area/areaMaster
-
+ var/hard_deleted = 0
/atom/movable/New()
. = ..()
areaMaster = get_area_master(src)
+/atom/movable/Destroy()
+ if(opacity)
+ if(isturf(loc))
+ if(loc:lighting_lumcount > 1)
+ UpdateAffectingLights()
+ gcDestroyed = "Bye, world!"
+ tag = null
+ loc = null
+/*
+ if(istype(beams) && beams.len)
+ for(var/obj/effect/beam/B in beams)
+ if(B && B.target == src)
+ B.target = null
+ if(B.master && B.master.target == src)
+ B.master.target = null
+ beams.len = 0
+*/
+ ..()
+
+/proc/delete_profile(var/type, code = 0)
+ if(!ticker || !ticker.current_state < 3) return
+ switch(code)
+ if(0)
+ if (!("[type]" in del_profiling))
+ del_profiling["[type]"] = 0
+
+ del_profiling["[type]"] += 1
+ if(1)
+ if (!("[type]" in ghdel_profiling))
+ ghdel_profiling["[type]"] = 0
+
+ ghdel_profiling["[type]"] += 1
+ if(2)
+ if (!("[type]" in gdel_profiling))
+ gdel_profiling["[type]"] = 0
+
+ gdel_profiling["[type]"] += 1
+ if(garbageCollector)
+ garbageCollector.soft_dels++
+
+/atom/movable/Del()
+ if (gcDestroyed)
+ garbageCollector.dequeue("\ref[src]")
+
+ if (hard_deleted)
+ delete_profile("[type]", 1)
+ else
+ delete_profile("[type]", 2)
+ else // direct del calls or nulled explicitly.
+ delete_profile("[type]", 0)
+ Destroy()
+
+ ..()
+
// Used in shuttle movement and AI eye stuff.
// Primarily used to notify objects being moved by a shuttle/bluespace fuckup.
/atom/movable/proc/setLoc(var/T, var/teleported=0)
diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm
index 1f98e54cb75..197da35056d 100644
--- a/code/game/objects/structures.dm
+++ b/code/game/objects/structures.dm
@@ -25,7 +25,8 @@
/obj/structure/Destroy()
if(hascall(src, "unbuckle"))
src:unbuckle()
-
+ ..()
+
/obj/structure/mech_melee_attack(obj/mecha/M)
if(M.damtype == "brute")
M.occupant_message("You hit [src].")
diff --git a/code/game/objects/structures/musician.dm b/code/game/objects/structures/musician.dm
index ea3b35be1ae..6ed7da87a3b 100644
--- a/code/game/objects/structures/musician.dm
+++ b/code/game/objects/structures/musician.dm
@@ -19,7 +19,7 @@
instrumentDir = dir
instrumentObj = obj
-/datum/song/proc/Destroy()
+/datum/song/Destroy()
instrumentObj = null
return
diff --git a/code/game/supplyshuttle.dm b/code/game/supplyshuttle.dm
index bf1b9577b21..94ad7725c1b 100644
--- a/code/game/supplyshuttle.dm
+++ b/code/game/supplyshuttle.dm
@@ -131,9 +131,8 @@ var/list/mechtoys = list(
var/comment = null
/datum/controller/supply
- var/processing = 1
- var/processing_interval = 300
- var/iteration = 0
+ processing = 1
+ processing_interval = 300
//supply points
var/points = 50
var/points_per_process = 1
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 144790401d0..cac65e0a0e7 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -855,7 +855,7 @@ var/list/slot_equipment_priority = list( \
return 0
src << message
return 1
-
+
/mob/proc/is_muzzled()
return 0
@@ -873,25 +873,14 @@ var/list/slot_equipment_priority = list( \
stat(null, "CPU:\t[world.cpu]")
stat(null, "Instances:\t[world.contents.len]")
- if(master_controller)
- /* HANDLED THROUGH PROCESS SCHEDULER
- stat(null, "MasterController-[last_tick_duration] ([master_controller.processing?"On":"Off"]-[controller_iteration])")
- stat(null, "Air-[master_controller.air_cost]")
- stat(null, "Sun-[master_controller.sun_cost]")
- stat(null, "Mob-[master_controller.mobs_cost]\t#[mob_list.len]")
- stat(null, "Dis-[master_controller.diseases_cost]\t#[active_diseases.len]")
- stat(null, "Mch-[master_controller.machines_cost]\t#[machines.len]")
- stat(null, "Bots-[master_controller.aibots_cost]\t#[aibots.len]")
- stat(null, "Obj-[master_controller.objects_cost]\t#[processing_objects.len]")
- stat(null, "PiNet-[master_controller.networks_cost]\t#[pipe_networks.len]")
- stat(null, "PoNet-[master_controller.powernets_cost]\t#[powernets.len]")
- stat(null, "NanoUI-[master_controller.nano_cost]\t#[nanomanager.processing_uis.len]")
-// stat(null, "GC-[master_controller.gc_cost]\t#[garbage.queue.len]")
- stat(null, "Tick-[master_controller.ticker_cost]")*/
- stat(null,"Events-[master_controller.events_cost]\t#[event_manager.active_events.len]")
- stat(null, "ALL-[master_controller.total_cost]")
+ if (garbageCollector)
+ stat(null, "\tqdel - [garbageCollector.del_everything ? "off" : "on"]")
+ stat(null, "\ton queue - [garbageCollector.queue.len]")
+ stat(null, "\ttotal delete - [garbageCollector.dels_count]")
+ stat(null, "\tsoft delete - [garbageCollector.soft_dels]")
+ stat(null, "\thard delete - [garbageCollector.hard_dels]")
else
- stat(null, "MasterController-ERROR")
+ stat(null, "Garbage Controller is not running.")
if(processScheduler.getIsRunning())
var/datum/controller/process/process
@@ -926,6 +915,9 @@ var/list/slot_equipment_priority = list( \
process = processScheduler.getProcess("disease")
stat(null, "DIS([active_diseases.len])\t - #[process.getTicks()]\t - [process.getLastRunTime()]")
+ process = processScheduler.getProcess("garbage")
+ stat(null, "GAR\t - #[process.getTicks()]\t - [process.getLastRunTime()]")
+
//process = processScheduler.getProcess("sun")
//stat(null, "SUN\t - #[process.getTicks()]\t - [process.getLastRunTime()]")
diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm
index c9726924f43..3af4d2fbf2c 100644
--- a/code/modules/power/singularity/singularity.dm
+++ b/code/modules/power/singularity/singularity.dm
@@ -1,6 +1,7 @@
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:33
// Added spess ghoasts/cameras to this so they don't add to the lag. - N3X
+var/global/narsie_cometh = 0
var/global/list/uneatable = list(
/turf/space,
/obj/effect/overlay,
@@ -508,6 +509,7 @@ var/global/list/uneatable = list(
if(emergency_shuttle && emergency_shuttle.can_call())
emergency_shuttle.call_evac()
emergency_shuttle.launch_time = 0 // Cannot recall
+ narsie_cometh = 1
/obj/machinery/singularity/narsie/proc/spawn_animation()
@@ -541,7 +543,7 @@ var/global/list/uneatable = list(
if(istype(A,/obj/))
var/obj/O = A
O.ex_act(1.0)
- if(O) del(O)
+ if(O) qdel(O)
else if(isturf(A))
var/turf/T = A
@@ -565,7 +567,7 @@ var/global/list/uneatable = list(
var/obj/O = A
machines -= O
processing_objects -= O
- O.loc = null
+ qdel(O)
else if(isturf(A))
var/turf/T = A
if(istype(T,/turf/simulated/wall))
diff --git a/paradise.dme b/paradise.dme
index a60078bd76c..c7692b54575 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -181,6 +181,7 @@
#include "code\controllers\Processes\disease.dm"
#include "code\controllers\Processes\emergencyShuttle.dm"
#include "code\controllers\Processes\event.dm"
+#include "code\controllers\Processes\garbage.dm"
#include "code\controllers\Processes\inactivity.dm"
#include "code\controllers\Processes\lighting.dm"
#include "code\controllers\Processes\machinery.dm"