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"