diff --git a/code/ATMOSPHERICS/components/tvalve.dm b/code/ATMOSPHERICS/components/tvalve.dm
index 0c841a4ad31..65d38a131df 100644
--- a/code/ATMOSPHERICS/components/tvalve.dm
+++ b/code/ATMOSPHERICS/components/tvalve.dm
@@ -180,7 +180,6 @@
/obj/machinery/atmospherics/tvalve/process()
..()
. = PROCESS_KILL
- //machines.Remove(src)
return
diff --git a/code/ATMOSPHERICS/datum_pipe_network.dm b/code/ATMOSPHERICS/datum_pipe_network.dm
index 84414613428..74134e6ff21 100644
--- a/code/ATMOSPHERICS/datum_pipe_network.dm
+++ b/code/ATMOSPHERICS/datum_pipe_network.dm
@@ -1,6 +1,6 @@
-var/global/list/datum/pipe_network/pipe_networks = list()
+var/global/list/datum/pipe_network/pipe_networks = list() // TODO - Move into SSmachines
-datum/pipe_network
+/datum/pipe_network
var/list/datum/gas_mixture/gases = list() //All of the gas_mixtures continuously connected in this network
var/volume = 0 //caches the total volume for atmos machines to use in gas calculations
@@ -11,13 +11,8 @@ datum/pipe_network
var/update = 1
//var/datum/gas_mixture/air_transient = null
- New()
- //air_transient = new()
-
- ..()
-
Destroy()
- pipe_networks -= src
+ STOP_PROCESSING_PIPENET(src)
for(var/datum/pipeline/line_member in line_members)
line_member.network = null
for(var/obj/machinery/atmospherics/normal_member in normal_members)
@@ -41,13 +36,14 @@ datum/pipe_network
if(!start_normal)
qdel(src)
+ return
start_normal.network_expand(src, reference)
update_network_gases()
if((normal_members.len>0)||(line_members.len>0))
- pipe_networks += src
+ START_PROCESSING_PIPENET(src)
else
qdel(src)
diff --git a/code/__defines/MC.dm b/code/__defines/MC.dm
index b2f3c814fed..5620f51b3b0 100644
--- a/code/__defines/MC.dm
+++ b/code/__defines/MC.dm
@@ -10,6 +10,19 @@
Master.current_ticklimit = original_tick_limit;\
}
+// Boilerplate code for multi-step processors. See machines.dm for example use.
+#define INTERNAL_PROCESS_STEP(this_step, initial_step, proc_to_call, cost_var, next_step)\
+if(current_step == this_step || (initial_step && !resumed)) /* So we start at step 1 if not resumed.*/ {\
+ timer = TICK_USAGE;\
+ proc_to_call(resumed);\
+ cost_var = MC_AVERAGE(cost_var, TICK_DELTA_TO_MS(TICK_USAGE - timer));\
+ if(state != SS_RUNNING){\
+ return;\
+ }\
+ resumed = 0;\
+ current_step = next_step;\
+}
+
// Used to smooth out costs to try and avoid oscillation.
#define MC_AVERAGE_FAST(average, current) (0.7 * (average) + 0.3 * (current))
#define MC_AVERAGE(average, current) (0.8 * (average) + 0.2 * (current))
diff --git a/code/__defines/machinery.dm b/code/__defines/machinery.dm
index deaaec0fad6..b577225e010 100644
--- a/code/__defines/machinery.dm
+++ b/code/__defines/machinery.dm
@@ -105,3 +105,37 @@ var/list/restricted_camera_networks = list(NETWORK_ERT,NETWORK_MERCENARY,"Secret
#define ATMOS_DEFAULT_VOLUME_FILTER 200 // L.
#define ATMOS_DEFAULT_VOLUME_MIXER 200 // L.
#define ATMOS_DEFAULT_VOLUME_PIPE 70 // L.
+
+// Fancy-pants START/STOP_PROCESSING() macros that lets us custom define what the list is.
+#define START_PROCESSING_IN_LIST(DATUM, LIST) \
+if (DATUM.is_processing) {\
+ if(DATUM.is_processing != #LIST)\
+ {\
+ crash_with("Failed to start processing. [log_info_line(DATUM)] is already being processed by [DATUM.is_processing] but queue attempt occured on [#LIST]."); \
+ }\
+} else {\
+ DATUM.is_processing = #LIST;\
+ LIST += DATUM;\
+}
+
+#define STOP_PROCESSING_IN_LIST(DATUM, LIST) \
+if(DATUM.is_processing) {\
+ if(LIST.Remove(DATUM)) {\
+ DATUM.is_processing = null;\
+ } else {\
+ crash_with("Failed to stop processing. [log_info_line(DATUM)] is being processed by [is_processing] and not found in SSmachines.[#LIST]"); \
+ }\
+}
+
+// Note - I would prefer these be defined machines.dm, but some are used prior in file order. ~Leshana
+#define START_MACHINE_PROCESSING(Datum) START_PROCESSING_IN_LIST(Datum, global.machines)
+#define STOP_MACHINE_PROCESSING(Datum) STOP_PROCESSING_IN_LIST(Datum, global.machines)
+
+#define START_PROCESSING_PIPENET(Datum) START_PROCESSING_IN_LIST(Datum, global.pipe_networks)
+#define STOP_PROCESSING_PIPENET(Datum) STOP_PROCESSING_IN_LIST(Datum, global.pipe_networks)
+
+#define START_PROCESSING_POWERNET(Datum) START_PROCESSING_IN_LIST(Datum, global.powernets)
+#define STOP_PROCESSING_POWERNET(Datum) STOP_PROCESSING_IN_LIST(Datum, global.powernets)
+
+#define START_PROCESSING_POWER_OBJECT(Datum) START_PROCESSING_IN_LIST(Datum, global.processing_power_items)
+#define STOP_PROCESSING_POWER_OBJECT(Datum) STOP_PROCESSING_IN_LIST(Datum, global.processing_power_items)
diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm
index 85eeeae876b..be04fa1d7f3 100644
--- a/code/__defines/subsystems.dm
+++ b/code/__defines/subsystems.dm
@@ -12,4 +12,8 @@
var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_GAME, RUNLEVEL_POSTGAME)
#define RUNLEVEL_FLAG_TO_INDEX(flag) (log(2, flag) + 1) // Convert from the runlevel bitfield constants to index in runlevel_flags list
-#define INIT_ORDER_LIGHTING 0
\ No newline at end of file
+// 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_MACHINES 10
+#define INIT_ORDER_LIGHTING 0
diff --git a/code/controllers/subsystems/machines.dm b/code/controllers/subsystems/machines.dm
new file mode 100644
index 00000000000..77385440247
--- /dev/null
+++ b/code/controllers/subsystems/machines.dm
@@ -0,0 +1,160 @@
+#define SSMACHINES_PIPENETS 1
+#define SSMACHINES_MACHINERY 2
+#define SSMACHINES_POWERNETS 3
+#define SSMACHINES_POWER_OBJECTS 4
+
+//
+// SSmachines subsystem - Processing machines, pipenets, and powernets!
+//
+// Implementation Plan:
+// PHASE 1 - Add subsystem using the existing global list vars
+// PHASE 2 - Move the global list vars into the subsystem.
+
+SUBSYSTEM_DEF(machines)
+ name = "Machines"
+ priority = 100
+ init_order = INIT_ORDER_MACHINES
+ flags = SS_KEEP_TIMING
+ runlevels = RUNLEVEL_GAME|RUNLEVEL_POSTGAME
+
+ var/current_step = SSMACHINES_PIPENETS
+
+ var/cost_pipenets = 0
+ var/cost_machinery = 0
+ var/cost_powernets = 0
+ var/cost_power_objects = 0
+
+ // TODO - PHASE 2 - Switch these from globals to instance vars
+ // var/list/pipenets = list()
+ // var/list/machinery = list()
+ // var/list/powernets = list()
+ // var/list/power_objects = list()
+
+ var/list/current_run = list()
+
+/datum/controller/subsystem/machines/Initialize(timeofday)
+ SSmachines.makepowernets()
+ // TODO - Move world-creation time setup of atmos machinery and pipenets to here
+ fire()
+ ..()
+
+/datum/controller/subsystem/machines/fire(resumed = 0)
+ var/timer = TICK_USAGE
+
+ INTERNAL_PROCESS_STEP(SSMACHINES_PIPENETS,TRUE,process_pipenets,cost_pipenets,SSMACHINES_MACHINERY)
+ INTERNAL_PROCESS_STEP(SSMACHINES_MACHINERY,FALSE,process_machinery,cost_machinery,SSMACHINES_POWERNETS)
+ INTERNAL_PROCESS_STEP(SSMACHINES_POWERNETS,FALSE,process_powernets,cost_powernets,SSMACHINES_POWER_OBJECTS)
+ INTERNAL_PROCESS_STEP(SSMACHINES_POWER_OBJECTS,FALSE,process_power_objects,cost_power_objects,SSMACHINES_PIPENETS)
+
+// rebuild all power networks from scratch - only called at world creation or by the admin verb
+// The above is a lie. Turbolifts also call this proc.
+/datum/controller/subsystem/machines/proc/makepowernets()
+ // TODO - check to not run while in the middle of a tick!
+ for(var/datum/powernet/PN in powernets)
+ qdel(PN)
+ powernets.Cut()
+
+ for(var/obj/structure/cable/PC in cable_list)
+ if(!PC.powernet)
+ var/datum/powernet/NewPN = new()
+ NewPN.add_cable(PC)
+ propagate_network(PC,PC.powernet)
+
+/datum/controller/subsystem/machines/stat_entry()
+ var/msg = list()
+ msg += "C:{"
+ msg += "PI:[round(cost_pipenets,1)]|"
+ msg += "MC:[round(cost_machinery,1)]|"
+ msg += "PN:[round(cost_powernets,1)]|"
+ msg += "PO:[round(cost_power_objects,1)]"
+ msg += "} "
+ msg += "PI:[global.pipe_networks.len]|"
+ msg += "MC:[global.machines.len]|"
+ msg += "PN:[global.powernets.len]|"
+ msg += "PO:[global.processing_power_items.len]|"
+ msg += "MC/MS:[round((cost ? global.machines.len/cost_machinery : 0),0.1)]"
+ ..(jointext(msg, null))
+
+/datum/controller/subsystem/machines/proc/process_pipenets(resumed = 0)
+ if (!resumed)
+ src.current_run = global.pipe_networks.Copy()
+ //cache for sanic speed (lists are references anyways)
+ var/list/current_run = src.current_run
+ while(current_run.len)
+ var/datum/pipe_network/PN = current_run[current_run.len]
+ current_run.len--
+ if(istype(PN) && !QDELETED(PN))
+ PN.process(wait)
+ else
+ global.pipe_networks.Remove(PN)
+ if(!QDELETED(PN))
+ PN.is_processing = null
+ if(MC_TICK_CHECK)
+ return
+
+/datum/controller/subsystem/machines/proc/process_machinery(resumed = 0)
+ if (!resumed)
+ src.current_run = global.machines.Copy()
+
+ var/list/current_run = src.current_run
+ while(current_run.len)
+ var/obj/machinery/M = current_run[current_run.len]
+ current_run.len--
+ if(istype(M) && !QDELETED(M) && !(M.process(wait) == PROCESS_KILL))
+ if(M.use_power)
+ M.auto_use_power()
+ else
+ global.machines.Remove(M)
+ if(!QDELETED(M))
+ M.is_processing = null
+ if(MC_TICK_CHECK)
+ return
+
+/datum/controller/subsystem/machines/proc/process_powernets(resumed = 0)
+ if (!resumed)
+ src.current_run = global.powernets.Copy()
+
+ var/list/current_run = src.current_run
+ while(current_run.len)
+ var/datum/powernet/PN = current_run[current_run.len]
+ current_run.len--
+ if(istype(PN) && !QDELETED(PN))
+ PN.reset(wait)
+ else
+ global.powernets.Remove(PN)
+ if(!QDELETED(PN))
+ PN.is_processing = null
+ if(MC_TICK_CHECK)
+ return
+
+// Actually only processes power DRAIN objects.
+// Currently only used by powersinks. These items get priority processed before machinery
+/datum/controller/subsystem/machines/proc/process_power_objects(resumed = 0)
+ if (!resumed)
+ src.current_run = global.processing_power_items.Copy()
+
+ var/list/current_run = src.current_run
+ while(current_run.len)
+ var/obj/item/I = current_run[current_run.len]
+ current_run.len--
+ if(!I.pwr_drain(wait)) // 0 = Process Kill, remove from processing list.
+ global.processing_power_items.Remove(I)
+ I.is_processing = null
+ if(MC_TICK_CHECK)
+ return
+
+/datum/controller/subsystem/machines/Recover()
+ // TODO - PHASE 2
+ // if (istype(SSmachines.pipenets))
+ // pipenets = SSmachines.pipenets
+ // if (istype(SSmachines.machinery))
+ // machinery = SSmachines.machinery
+ // if (istype(SSmachines.powernets))
+ // powernets = SSmachines.powernets
+ // if (istype(SSmachines.power_objects))
+ // power_objects = SSmachines.power_objects
+
+#undef SSMACHINES_PIPENETS
+#undef SSMACHINES_MACHINERY
+#undef SSMACHINES_POWER
+#undef SSMACHINES_POWER_OBJECTS
diff --git a/code/datums/datum.dm b/code/datums/datum.dm
index f509b660427..a9fea93f9d7 100644
--- a/code/datums/datum.dm
+++ b/code/datums/datum.dm
@@ -6,6 +6,7 @@
/datum
var/gc_destroyed //Time when this object was destroyed.
var/weakref/weakref // Holder of weakref instance pointing to this datum
+ var/is_processing = FALSE // If this datum is in an MC processing list, this will be set to its name.
#ifdef TESTING
var/tmp/running_find_references
diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm
index 351c1bd3b33..e210119445f 100644
--- a/code/game/machinery/camera/presets.dm
+++ b/code/game/machinery/camera/presets.dm
@@ -203,12 +203,7 @@ var/global/list/engineering_networks = list(
/obj/machinery/camera/proc/upgradeMotion()
assembly.upgrades.Add(new /obj/item/device/assembly/prox_sensor(assembly))
setPowerUsage()
- if(!(src in machines))
- if(!machinery_sort_required && ticker)
- dd_insertObjectList(machines, src)
- else
- machines += src
- machinery_sort_required = 1
+ START_MACHINE_PROCESSING(src)
update_coverage()
/obj/machinery/camera/proc/setPowerUsage()
diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm
index f476c295e20..7e867af9a72 100644
--- a/code/game/machinery/machinery.dm
+++ b/code/game/machinery/machinery.dm
@@ -118,16 +118,12 @@ Class Procs:
..(l)
if(d)
set_dir(d)
- if(!machinery_sort_required && ticker)
- dd_insertObjectList(machines, src)
- else
- machines += src
- machinery_sort_required = 1
+ START_MACHINE_PROCESSING(src)
if(circuit)
circuit = new circuit(src)
/obj/machinery/Destroy()
- machines -= src
+ STOP_MACHINE_PROCESSING(src)
if(component_parts)
for(var/atom/A in component_parts)
if(A.loc == src) // If the components are inside the machine, delete them.
diff --git a/code/game/machinery/syndicatebeacon.dm b/code/game/machinery/syndicatebeacon.dm
index 442703a42e6..cf2449f1d35 100644
--- a/code/game/machinery/syndicatebeacon.dm
+++ b/code/game/machinery/syndicatebeacon.dm
@@ -94,7 +94,7 @@
singulo.target = src
icon_state = "[icontype]1"
active = 1
- machines |= src
+ START_MACHINE_PROCESSING(src)
if(user)
user << "You activate the beacon."
diff --git a/code/game/objects/items/devices/powersink.dm b/code/game/objects/items/devices/powersink.dm
index e4c1bb3f202..f3bc620d089 100644
--- a/code/game/objects/items/devices/powersink.dm
+++ b/code/game/objects/items/devices/powersink.dm
@@ -26,7 +26,7 @@
/obj/item/device/powersink/Destroy()
processing_objects.Remove(src)
- processing_power_items.Remove(src)
+ STOP_PROCESSING_POWER_OBJECT(src)
..()
/obj/item/device/powersink/attackby(var/obj/item/I, var/mob/user)
@@ -50,7 +50,7 @@
else
if (mode == 2)
processing_objects.Remove(src) // Now the power sink actually stops draining the station's power if you unhook it. --NeoFite
- processing_power_items.Remove(src)
+ STOP_PROCESSING_POWER_OBJECT(src)
anchored = 0
mode = 0
src.visible_message("[user] detaches [src] from the cable!")
@@ -74,14 +74,14 @@
mode = 2
icon_state = "powersink1"
processing_objects.Add(src)
- processing_power_items.Add(src)
+ START_PROCESSING_POWER_OBJECT(src)
if(2) //This switch option wasn't originally included. It exists now. --NeoFite
src.visible_message("[user] deactivates [src]!")
mode = 1
set_light(0)
icon_state = "powersink0"
processing_objects.Remove(src)
- processing_power_items.Remove(src)
+ STOP_PROCESSING_POWER_OBJECT(src)
/obj/item/device/powersink/pwr_drain()
if(!attached)
diff --git a/code/global.dm b/code/global.dm
index 05edcd38c93..3ce2395d1ca 100644
--- a/code/global.dm
+++ b/code/global.dm
@@ -6,9 +6,9 @@
// Items that ask to be called every cycle.
var/global/datum/datacore/data_core = null
var/global/list/all_areas = list()
-var/global/list/machines = list()
+var/global/list/machines = list() // TODO - Move into SSmachines
var/global/list/processing_objects = list()
-var/global/list/processing_power_items = list()
+var/global/list/processing_power_items = list() // TODO - Move into SSmachines
var/global/list/active_diseases = list()
var/global/list/med_hud_users = list() // List of all entities using a medical HUD.
var/global/list/sec_hud_users = list() // List of all entities using a security HUD.
@@ -107,7 +107,7 @@ var/list/IClog = list()
var/list/OOClog = list()
var/list/adminlog = list()
-var/list/powernets = list()
+var/list/powernets = list() // TODO - Move into SSmachines
var/Debug2 = 0
var/datum/debug/debugobj
diff --git a/code/modules/power/antimatter/shielding.dm b/code/modules/power/antimatter/shielding.dm
index 4c937d5d2ab..6a2dc580567 100644
--- a/code/modules/power/antimatter/shielding.dm
+++ b/code/modules/power/antimatter/shielding.dm
@@ -151,7 +151,7 @@ proc/cardinalrange(var/center)
/obj/machinery/am_shielding/proc/setup_core()
processing = 1
- machines.Add(src)
+ START_MACHINE_PROCESSING(src)
if(!control_unit) return
control_unit.linked_cores.Add(src)
control_unit.reported_core_efficiency += efficiency
diff --git a/code/modules/power/powernet.dm b/code/modules/power/powernet.dm
index ebd88df3e2c..d8f57baf3b5 100644
--- a/code/modules/power/powernet.dm
+++ b/code/modules/power/powernet.dm
@@ -15,7 +15,7 @@
var/problem = 0 // If this is not 0 there is some sort of issue in the powernet. Monitors will display warnings.
/datum/powernet/New()
- powernets += src
+ START_PROCESSING_POWERNET(src)
..()
/datum/powernet/Destroy()
@@ -25,7 +25,7 @@
for(var/obj/machinery/power/M in nodes)
nodes -= M
M.powernet = null
- powernets -= src
+ STOP_PROCESSING_POWERNET(src)
return ..()
//Returns the amount of excess power (before refunding to SMESs) from last tick.
diff --git a/html/changelogs/Leshana-ss-machines.yml b/html/changelogs/Leshana-ss-machines.yml
new file mode 100644
index 00000000000..28faf351977
--- /dev/null
+++ b/html/changelogs/Leshana-ss-machines.yml
@@ -0,0 +1,4 @@
+author: Leshana
+delete-after: True
+changes:
+ - tweak: "Convert the machinery controller to a StonedMC subsystem"
diff --git a/polaris.dme b/polaris.dme
index be6dea1a121..143f6d03774 100644
--- a/polaris.dme
+++ b/polaris.dme
@@ -158,7 +158,6 @@
#include "code\controllers\Processes\event.dm"
#include "code\controllers\Processes\game_master.dm"
#include "code\controllers\Processes\inactivity.dm"
-#include "code\controllers\Processes\machinery.dm"
#include "code\controllers\Processes\mob.dm"
#include "code\controllers\Processes\nanoui.dm"
#include "code\controllers\Processes\obj.dm"
@@ -176,6 +175,7 @@
#include "code\controllers\subsystems\creation.dm"
#include "code\controllers\subsystems\garbage.dm"
#include "code\controllers\subsystems\lighting.dm"
+#include "code\controllers\subsystems\machines.dm"
#include "code\datums\ai_law_sets.dm"
#include "code\datums\ai_laws.dm"
#include "code\datums\browser.dm"