From a97a57427801b3ac0b2f7d7bac3619e2e0f4d5ea Mon Sep 17 00:00:00 2001 From: Leshana Date: Thu, 28 Dec 2017 15:20:16 -0500 Subject: [PATCH] Transformed the machinery processor into an StonedMC subsystem * This is PHASE 1 of a multi-phase conversion. In this first phase we implement the subsystem, but leave it processing the existing global list variables. In the next phase we will switch to use datum variables in the subsystem. The main reason for splitting into two phases is ease of code review; change the meaningful code without the hundreds of machines -> SSmachines.machinery substitutions. * We did declare macros for adding/removing things to the processing lists, and convert everywhere to use the macros. * Added var/is_processing to /datum to keep track of whether an instance is already in a processing list (prevents it being in the list twice!) and also debugging, making sure its not in two lists etc. * NOTE: The global machines list is **no longer sorted** for performance reasons. As far as I know, the only module that actually ever cared was cameras. Our camera system already handles its own sorting in the cameranets anyway, so it should no longer be needed. --- code/ATMOSPHERICS/components/tvalve.dm | 1 - code/ATMOSPHERICS/datum_pipe_network.dm | 14 +- code/__defines/MC.dm | 13 ++ code/__defines/machinery.dm | 34 ++++ code/__defines/subsystems.dm | 6 +- code/controllers/subsystems/machines.dm | 160 +++++++++++++++++++ code/datums/datum.dm | 1 + code/game/machinery/camera/presets.dm | 7 +- code/game/machinery/machinery.dm | 8 +- code/game/machinery/syndicatebeacon.dm | 2 +- code/game/objects/items/devices/powersink.dm | 8 +- code/global.dm | 6 +- code/modules/power/antimatter/shielding.dm | 2 +- code/modules/power/powernet.dm | 4 +- html/changelogs/Leshana-ss-machines.yml | 4 + polaris.dme | 2 +- 16 files changed, 237 insertions(+), 35 deletions(-) create mode 100644 code/controllers/subsystems/machines.dm create mode 100644 html/changelogs/Leshana-ss-machines.yml diff --git a/code/ATMOSPHERICS/components/tvalve.dm b/code/ATMOSPHERICS/components/tvalve.dm index 0c841a4ad3..65d38a131d 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 8441461342..74134e6ff2 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 b2f3c814fe..5620f51b3b 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 deaaec0fad..b577225e01 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 85eeeae876..be04fa1d7f 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 0000000000..7738544024 --- /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 f509b66042..a9fea93f9d 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 351c1bd3b3..e210119445 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 f476c295e2..7e867af9a7 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 442703a42e..cf2449f1d3 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 e4c1bb3f20..f3bc620d08 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 05edcd38c9..3ce2395d1c 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 4c937d5d2a..6a2dc58056 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 ebd88df3e2..d8f57baf3b 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 0000000000..28faf35197 --- /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 be6dea1a12..143f6d0377 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"