diff --git a/code/controllers/admin.dm b/code/controllers/admin.dm index de9da078fc2..99b7452a12b 100644 --- a/code/controllers/admin.dm +++ b/code/controllers/admin.dm @@ -1,6 +1,4 @@ -//TODO: rewrite and standardise all controller datums to the datum/controller type -//TODO: allow all controllers to be deleted for clean restarts (see WIP master controller stuff) - MC done - lighting done - +// Clickable stat() button. /obj/effect/statclick var/target @@ -23,12 +21,16 @@ class = "subsystem" else if(istype(target, /datum/controller)) class = "controller" + else if(istype(target, /datum)) + class = "datum" else class = "unknown" usr.client.debug_variables(target) message_admins("Admin [key_name_admin(usr)] is debugging the [target] [class].") + +// Debug verbs. /client/proc/restart_controller(controller in list("Master", "Failsafe")) set category = "Debug" set name = "Restart Controller" @@ -38,36 +40,11 @@ return switch(controller) if("Master") - new /datum/controller/game_controller() - master_controller.process() + new/datum/controller/master() + Master.process() feedback_add_details("admin_verb","RMC") if("Failsafe") new /datum/controller/failsafe() feedback_add_details("admin_verb","RFailsafe") message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.") - -/client/proc/debug_controller(controller in list("Master", "Failsafe", "Ticker", "Jobs", "Radio", "Configuration", "Cameras")) - set category = "Debug" - set name = "Debug Controller" - set desc = "Debug the various periodic loop controllers for the game (be careful!)" - - if(!holder) - return - switch(controller) - if("Master") - debug_variables(master_controller) - if("Failsafe") - debug_variables(Failsafe) - if("Ticker") - debug_variables(ticker) - if("Jobs") - debug_variables(SSjob) - if("Radio") - debug_variables(radio_controller) - if("Configuration") - debug_variables(config) - if("Cameras") - debug_variables(cameranet) - - message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.") diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 9d891ac4864..b68ec62bd32 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -11,6 +11,8 @@ var/autoadmin_rank = "Game Admin" /datum/configuration + var/name = "Configuration" // datum name + var/server_name = null // server name (the name of the game window) var/station_name = null // station name (the name of the station in-game) var/server_suffix = 0 // generate numeric suffix based on server port @@ -183,6 +185,8 @@ var/maprotation = 1 var/maprotatechancedelta = 0.75 + // The object used for the clickable stat() button. + var/obj/effect/statclick/statclick /datum/configuration/New() @@ -699,3 +703,9 @@ if(M.required_players <= crew) runnable_modes[M] = probabilities[M.config_tag] return runnable_modes + +/datum/configuration/proc/stat_entry() + if(!statclick) + statclick = new/obj/effect/statclick/debug("Edit", src) + + stat("[name]:", statclick) \ No newline at end of file diff --git a/code/controllers/controller.dm b/code/controllers/controller.dm new file mode 100644 index 00000000000..7c87599e699 --- /dev/null +++ b/code/controllers/controller.dm @@ -0,0 +1,4 @@ +/datum/controller + var/name + // The object used for the clickable stat() button. + var/obj/effect/statclick/statclick \ No newline at end of file diff --git a/code/controllers/failsafe.dm b/code/controllers/failsafe.dm index d6c80c5a381..61b3a2cf9b3 100644 --- a/code/controllers/failsafe.dm +++ b/code/controllers/failsafe.dm @@ -1,52 +1,65 @@ + /** + * Failsafe + * + * Pretty much pokes the MC to make sure it's still alive. + **/ + var/datum/controller/failsafe/Failsafe /datum/controller/failsafe // This thing pretty much just keeps poking the master controller - var/name = "Failsafe" - var/processing_interval = 100 //poke the MC every 10 seconds - set to 0 to disable - 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) + name = "Failsafe" - var/obj/effect/statclick/statclick // clickable stat button + // The length of time to check on the MC (in deciseconds). + // Set to 0 to disable. + var/processing_interval = 100 + // The alert level. For every failed poke, we drop a DEFCON level. Once we hit DEFCON 1, restart the MC. + var/defcon = 0 + + // Track the MC iteration to make sure its still on track. + var/master_iteration = 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) + // Highlander-style: there can only be one! Kill off the old and replace it with the new. if(Failsafe != src) if(istype(Failsafe)) qdel(Failsafe) Failsafe = src Failsafe.process() - /datum/controller/failsafe/process() spawn(0) - 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. - + while(1) // More efficient than recursion, 1 to avoid an infinite loop. + if(!Master) + // Replace the missing Master! This should never, ever happen. + new /datum/controller/master() + // Only poke it if overrides are not in effect. if(processing_interval > 0) - if(master_controller.processing_interval > 0) //only poke if these overrides aren't in effect - if(MC_iteration == master_controller.iteration) //master_controller hasn't finished processing in the defined interval - switch(MC_defcon) - if(0 to 3) - ++MC_defcon + if(Master.processing_interval > 0) + // Check if processing is done yet. + if(Master.iteration == master_iteration) + switch(defcon) + if(1 to 3) + ++defcon 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: DEFCON [defcon]. The Master Controller has not fired in the last [defcon * processing_interval] ticks. Automatic restart in [processing_interval] ticks." + defcon = 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: DEFCON [defcon]. The Master Controller has still not fired within the last [defcon * processing_interval] ticks. Killing and restarting..." + // Replace the old master controller by creating a new one. + new/datum/controller/master() + // Get it rolling again. + Master.process() + defcon = 0 else - MC_defcon = 0 - MC_iteration = master_controller.iteration + defcon = 0 + master_iteration = Master.iteration sleep(processing_interval) else - MC_defcon = 0 + defcon = 0 sleep(100) /datum/controller/failsafe/proc/stat_entry() if(!statclick) statclick = new/obj/effect/statclick/debug("Initializing...", src) - stat("Failsafe Controller:", statclick.update("Defcon: [Failsafe.MC_defcon] (Interval: [Failsafe.processing_interval] | Iteration: [Failsafe.MC_iteration])")) + stat("Failsafe Controller:", statclick.update("Defcon: [Failsafe.defcon] (Interval: [Failsafe.processing_interval] | Iteration: [Failsafe.master_iteration])")) diff --git a/code/controllers/master.dm b/code/controllers/master.dm new file mode 100644 index 00000000000..2f94574a797 --- /dev/null +++ b/code/controllers/master.dm @@ -0,0 +1,198 @@ + /** + * CarnMC + * + * Simplified MC; designed to fail fast and respawn. + * This ensures Master.process() never doubles up. + * It will kill itself and any sleeping procs if needed. + * + * All core systems are subsystems. + * They are process()'d by this Master Controller. + **/ + +var/global/datum/controller/master/Master = new() + +/datum/controller/master + name = "Master" + + // The minimum length of time between MC ticks (in deciseconds). + // The highest this can be without affecting schedules is the GCD of all subsystem waits. + // Set to 0 to disable all processing. + var/processing_interval = 1 + // The iteration of the MC. + var/iteration = 0 + // The cost (in deciseconds) of the MC loop. + var/cost = 0 + + // A list of subsystems to process(). + var/list/subsystems = list() + // The cost of running the subsystems (in deciseconds). + var/subsystem_cost = 0 + // The type of the last subsystem to be process()'d. + var/last_type_processed + +/datum/controller/master/New() + // Highlander-style: there can only be one! Kill off the old and replace it with the new. + if(Master != src) + if(istype(Master)) + Recover() + qdel(Master) + else + init_subtypes(/datum/subsystem, subsystems) + Master = src + processing_interval = calculate_gcd() + +/datum/controller/master/Destroy() + ..() + // Tell qdel() to Del() this object. + return QDEL_HINT_HARDDEL_NOW + +/datum/controller/master/proc/Recover() + var/msg = "## DEBUG: [time2text(world.timeofday)] MC restarted. Reports:\n" + for(var/varname in Master.vars) + switch(varname) + if("name", "tag", "bestF", "type", "parent_type", "vars", "statclick") // Built-in junk. + continue + else + var/varval = Master.vars[varname] + if(istype(varval, /datum)) // Check if it has a type var. + var/datum/D = varval + msg += "\t [varname] = [D.type]\n" + else + msg += "\t [varname] = [varval]\n" + world.log << msg + + subsystems = Master.subsystems + +/datum/controller/master/proc/Setup(zlevel) + // Per-Z-level subsystems. + if (zlevel && zlevel > 0 && zlevel <= world.maxz) + for(var/datum/subsystem/SS in subsystems) + SS.Initialize(world.timeofday, zlevel) + sleep(-1) + return + world << "Initializing Subsystems..." + + + // Sort subsystems by priority, so they initialize in the correct order. + sortTim(subsystems, /proc/cmp_subsystem_priority) + + // Pick a random away mission. + createRandomZlevel() + // Set up Z-level transistions. + setup_map_transitions() + + // Generate asteroid. + for(1 to max_secret_rooms) + make_mining_asteroid_secret() + + // Initialize subsystems. + for(var/datum/subsystem/SS in subsystems) + SS.Initialize(world.timeofday, zlevel) + sleep(-1) + + world << "Initializations Complete!" + + // Set world options. + world.sleep_offline = 1 + world.fps = config.fps + + sleep(-1) + + // Loop. + Master.process() + +// Notify the MC that the round has started. +/datum/controller/master/proc/RoundStart() + for(var/datum/subsystem/SS in subsystems) + SS.can_fire = 1 + SS.next_fire = world.time + rand(0, SS.wait) // Stagger subsystems. + +// Used to smooth out costs to try and avoid oscillation. +#define MC_AVERAGE(average, current) (0.8 * (average) + 0.2 * (current)) +/datum/controller/master/process() + if(!Failsafe) + new/datum/controller/failsafe() // (re)Start the failsafe. + spawn(0) + // Schedule the first run of the Subsystems. + var/timer = world.time + for(var/datum/subsystem/SS in subsystems) + timer += processing_interval + SS.next_fire = timer + + var/start_time + while(1) // More efficient than recursion, 1 to avoid an infinite loop. + if(processing_interval > 0) + ++iteration + var/startingtick = world.time // Store when we started this iteration. + start_time = world.timeofday + + var/ran_subsystems = 0 + for(var/datum/subsystem/SS in subsystems) + if(SS.can_fire > 0) + if(SS.next_fire <= world.time && SS.last_fire + (SS.wait * 0.5) <= world.time) // Check if it's time. + ran_subsystems = 1 + timer = world.timeofday + last_type_processed = SS.type + SS.last_fire = world.time + SS.fire() // Fire the subsystem and record the cost. + SS.cost = MC_AVERAGE(SS.cost, world.timeofday - timer) + if(SS.dynamic_wait) // Adjust wait depending on lag. + var/oldwait = SS.wait + var/global_delta = (subsystem_cost - (SS.cost / (SS.wait / 10))) - 1 + var/newwait = (SS.cost - SS.dwait_buffer + global_delta) * SS.dwait_delta + newwait = newwait * (world.cpu / 100 + 1) + newwait = MC_AVERAGE(oldwait, newwait) + SS.wait = Clamp(newwait, SS.dwait_lower, SS.dwait_upper) + if(oldwait != SS.wait) + processing_interval = calculate_gcd() + SS.next_fire += SS.wait + ++SS.times_fired + // If we caused BYOND to miss a tick, stop processing for a bit... + if(startingtick < world.time || start_time + 1 < world.timeofday) + break + sleep(-1) + + cost = MC_AVERAGE(cost, world.timeofday - start_time) + if(ran_subsystems) + var/oldcost = subsystem_cost + var/newcost = 0 + for(var/datum/subsystem/SS in subsystems) + if (!SS.can_fire) + continue + newcost += SS.cost / (SS.wait / 10) + subsystem_cost = MC_AVERAGE(oldcost, newcost) + + var/extrasleep = 0 + // If we caused BYOND to miss a tick, sleep a bit extra... + if(startingtick < world.time || start_time + 1 < world.timeofday) + extrasleep += world.tick_lag * 2 + // If we are loading the server too much, sleep a bit extra... + if(world.cpu > 80) + extrasleep += extrasleep + processing_interval + sleep(processing_interval + extrasleep) + else + sleep(50) +#undef MC_AVERAGE + +// Determine the GCD of subsystem waits: the longest the MC can wait while still staying on schedule. +/datum/controller/master/proc/calculate_gcd() + var/GCD + // The shortest possible fire rate is the lowest of two ticks or 1 decisecond. + var/minimumInterval = min(world.tick_lag * 2, 1) + + // Loop over each subsystem and determine the GCD based on its wait value. + for(var/datum/subsystem/SS in subsystems) + if(SS.wait) + GCD = Gcd(round(SS.wait * 10), GCD) + GCD = round(GCD) + // If the GCD is less than the minimum, just use the minimum. + if(GCD < minimumInterval * 10) + GCD = minimumInterval * 10 + // Return GCD. + return GCD / 10 + +/datum/controller/master/proc/stat_entry() + if(!statclick) + statclick = new/obj/effect/statclick/debug("Initializing...", src) + + stat("Master Controller:", statclick.update("[round(Master.cost, 0.001)]ds (Interval: [Master.processing_interval] | Iteration:[Master.iteration])")) diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm deleted file mode 100644 index 3fb82f885d2..00000000000 --- a/code/controllers/master_controller.dm +++ /dev/null @@ -1,179 +0,0 @@ -//simplified MC that is designed to fail when procs 'break'. When it fails it's just replaced with a new one. -//It ensures master_controller.process() is never doubled up by killing the MC (hence terminating any of its sleeping procs) - -//Update: all core-systems are now placed inside subsystem datums. This makes them highly configurable and easy to work with. - -var/global/datum/controller/game_controller/master_controller = new() - -/datum/controller/game_controller - var/name = "Master" - var/processing_interval = 1 // The minimum length of time between MC ticks (in deciseconds). The highest this can be without affecting schedules, is the GCD of all subsystem var/wait. Set to 0 to disable all processing. - var/iteration = 0 - var/cost = 0 - var/SSCostPerSecond = 0 - var/last_thing_processed - - var/list/subsystems = list() - - var/obj/effect/statclick/statclick // clickable stat button - -/datum/controller/game_controller/New() - //There can be only one master_controller. Out with the old and in with the new. - if(master_controller != src) - if(istype(master_controller)) - Recover() - qdel(master_controller) - else - init_subtypes(/datum/subsystem, subsystems) - - master_controller = src - calculateGCD() - -/datum/controller/game_controller/Destroy() - ..() - return QDEL_HINT_HARDDEL_NOW - - -/* -calculate the longest number of ticks the MC can wait between each cycle without causing subsystems to not fire on schedule -*/ -/datum/controller/game_controller/proc/calculateGCD() - var/GCD - //shortest fire rate is the lower of two ticks or 1ds - var/minimumInterval = min(world.tick_lag*2,1) - for(var/datum/subsystem/SS in subsystems) - if(SS.wait) - GCD = Gcd(round(SS.wait*10), GCD) - GCD = round(GCD) - if(GCD < minimumInterval*10) - GCD = minimumInterval*10 - processing_interval = GCD/10 - -/datum/controller/game_controller/proc/setup(zlevel) - if (zlevel && zlevel > 0 && zlevel <= world.maxz) - for(var/datum/subsystem/S in subsystems) - S.Initialize(world.timeofday, zlevel) - sleep(-1) - return - world << "Initializing Subsystems..." - - - //sort subsystems by priority, so they initialize in the correct order - sortTim(subsystems, /proc/cmp_subsystem_priority) - - createRandomZlevel() //gate system - setup_map_transitions() - for(var/i=0, iInitializations Complete!" - - world.sleep_offline = 1 - world.fps = config.fps - - sleep(-1) - - process() - -//used for smoothing out the cost values so they don't fluctuate wildly -#define MC_AVERAGE(average, current) (0.8*(average) + 0.2*(current)) - -/datum/controller/game_controller/process() - if(!Failsafe) - new /datum/controller/failsafe() // (re)Start the failsafe. - spawn(0) - var/timer = world.time - for(var/datum/subsystem/SS in subsystems) - timer += processing_interval - SS.next_fire = timer - - var/start_time - - while(1) //far more efficient than recursively calling ourself - if(processing_interval > 0) - ++iteration - var/startingtick = world.time - start_time = world.timeofday - var/SubSystemRan = 0 - for(var/datum/subsystem/SS in subsystems) - if(SS.can_fire > 0) - if(SS.next_fire <= world.time && SS.last_fire+(SS.wait*0.5) <= world.time) - SubSystemRan = 1 - timer = world.timeofday - last_thing_processed = SS.type - SS.last_fire = world.time - SS.fire() - SS.cost = MC_AVERAGE(SS.cost, world.timeofday - timer) - if (SS.dynamic_wait) - var/oldwait = SS.wait - var/GlobalCostDelta = (SSCostPerSecond-(SS.cost/(SS.wait/10)))-1 - var/NewWait = (SS.cost-SS.dwait_buffer+GlobalCostDelta)*SS.dwait_delta - NewWait = NewWait*(world.cpu/100+1) - NewWait = MC_AVERAGE(oldwait,NewWait) - SS.wait = Clamp(NewWait,SS.dwait_lower,SS.dwait_upper) - if (oldwait != SS.wait) - calculateGCD() - SS.next_fire += SS.wait - ++SS.times_fired - //we caused byond to miss a tick, stop processing for a bit - if (startingtick < world.time || start_time+1 < world.timeofday) - break - sleep(-1) - - cost = MC_AVERAGE(cost, world.timeofday - start_time) - if(SubSystemRan) - calculateSScost() - var/extrasleep = 0 - if(startingtick < world.time || start_time+1 < world.timeofday) - //we caused byond to miss a tick, sleep a bit extra - extrasleep += world.tick_lag*2 - if(world.cpu > 80) - extrasleep += extrasleep+processing_interval - sleep(processing_interval+extrasleep) - else - sleep(50) - -/datum/controller/game_controller/proc/calculateSScost() - var/newcost = 0 - for(var/datum/subsystem/SS in subsystems) - if (!SS.can_fire) - continue - newcost += SS.cost/(SS.wait/10) - SSCostPerSecond = MC_AVERAGE(SSCostPerSecond,newcost) - -#undef MC_AVERAGE - -/datum/controller/game_controller/proc/roundHasStarted() - for(var/datum/subsystem/SS in subsystems) - SS.can_fire = 1 - SS.next_fire = world.time + rand(0,SS.wait) - -/datum/controller/game_controller/proc/Recover() - var/msg = "## DEBUG: [time2text(world.timeofday)] MC restarted. Reports:\n" - for(var/varname in master_controller.vars) - switch(varname) - if("tag","bestF","type","parent_type","vars") continue - else - var/varval = master_controller.vars[varname] - if(istype(varval,/datum)) - var/datum/D = varval - msg += "\t [varname] = [D.type]\n" - else - msg += "\t [varname] = [varval]\n" - world.log << msg - - subsystems = master_controller.subsystems - -/datum/controller/game_controller/proc/stat_entry() - if(!statclick) - statclick = new/obj/effect/statclick/debug("Initializing...", src) - - stat("Master Controller:", statclick.update("[round(master_controller.cost, 0.001)]ds (Interval: [master_controller.processing_interval] | Iteration:[master_controller.iteration])")) diff --git a/code/controllers/subsystem/assets.dm b/code/controllers/subsystem/assets.dm new file mode 100644 index 00000000000..06cd9c0bcc9 --- /dev/null +++ b/code/controllers/subsystem/assets.dm @@ -0,0 +1,23 @@ +var/datum/subsystem/assets/SSasset + +/datum/subsystem/assets + name = "Assets" + priority = 19 + + var/list/cache = list() + +/datum/subsystem/assets/New() + NEW_SS_GLOBAL(SSassets) + +/datum/subsystem/assets/Initialize(timeofday, zlevel) + if (zlevel) + return ..() + for(var/type in typesof(/datum/asset) - list(/datum/asset, /datum/asset/simple)) + var/datum/asset/A = new type() + A.register() + + for(var/client/C in clients) + // Doing this to a client too soon after they've connected can cause issues, also the proc we call sleeps. + spawn(10) + getFilesSlow(C, cache, FALSE) + ..() diff --git a/code/controllers/subsystem/minimap.dm b/code/controllers/subsystem/minimap.dm new file mode 100644 index 00000000000..55983952e63 --- /dev/null +++ b/code/controllers/subsystem/minimap.dm @@ -0,0 +1,181 @@ +// Activate this to debug tile mismatches in the minimap. +// This will store the full information on each tile and compare it the next time you run the minimap. +// It can be used to find out what's changed since the last iteration. +// Only activate this when you need it - this should never be active on a live server! +// #define MINIMAP_DEBUG + +var/datum/subsystem/minimap/SSminimap + +/datum/subsystem/minimap + name = "Minimap" + priority = 20 + + var/const/MAX_ICON_DIMENSION = 1024 + var/const/ICON_SIZE = 4 + +/datum/subsystem/minimap/New() + NEW_SS_GLOBAL(SSminimap) + +/datum/subsystem/minimap/Initialize(timeofday, zlevel) + if (zlevel) + return ..() + for(var/z = 1 to world.maxz) + generate(z) + for (var/z = 1 to world.maxz) + register_asset("minimap_[z].png", file("[getMinimapFile(z)].png")) + ..() + +/datum/subsystem/minimap/proc/generate(z, x1 = 1, y1 = 1, x2 = world.maxx, y2 = world.maxy) + var/result_path = "[src.getMinimapFile(z)].png" + var/hash_path = "[src.getMinimapFile(z)].md5" + var/list/tiles = block(locate(x1, y1, z), locate(x2, y2, z)) + var/hash = "" + var/temp + var/obj/obj + + #ifdef MINIMAP_DEBUG + var/tiledata_path = "data/minimaps/debug_tiledata_[z].sav" + var/savefile/F = new/savefile(tiledata_path) + #endif + + // Note for future developer: If you have tiles on the map with random or dynamic icons this hash check will fail + // every time. You'll have to modify this code to generate a unique hash for your object. + // Don't forget to modify the minimap generation code to use a default icon (or skip generation altogether). + for (var/turf/tile in tiles) + if (istype(tile.loc, /area/asteroid) || istype(tile.loc, /area/mine/unexplored) || istype(tile, /turf/simulated/mineral) || (istype(tile.loc, /area/space) && istype(tile, /turf/simulated/floor/plating/asteroid))) + temp = "/area/asteroid" + else if (istype(tile.loc, /area/mine) && istype(tile, /turf/simulated/floor/plating/asteroid)) + temp = "/area/mine/explored" + else if (tile.loc.type == /area/start || (tile.type == /turf/space && !(locate(/obj/structure/lattice) in tile)) || istype(tile, /turf/space/transit)) + temp = "/turf/space" + if (locate(/obj/structure/lattice/catwalk) in tile) + + else + else if (tile.type == /turf/space) + if (locate(/obj/structure/lattice/catwalk) in tile) + temp = "/obj/structure/lattice/catwalk" + else + temp = "/obj/structure/lattice" + else if (tile.type == /turf/simulated/floor/plating/abductor) + temp = "/turf/simulated/floor/plating/abductor" + else if (tile.type == /turf/simulated/floor/plating && (locate(/obj/structure/window/shuttle) in tile)) + temp = "/obj/structure/window/shuttle" + else + temp = "[tile.icon][tile.icon_state][tile.dir]" + + obj = locate(/obj/structure/transit_tube) in tile + + if (obj) temp = "[temp]/obj/structure/transit_tube[obj.icon_state][obj.dir]" + + #ifdef MINIMAP_DEBUG + if (F["/[tile.y]/[tile.x]"] && F["/[tile.y]/[tile.x]"] != temp) + CRASH("Mismatch: [tile.type] at [tile.x],[tile.y],[tile.z] ([tile.icon], [tile.icon_state], [tile.dir])") + else + F["/[tile.y]/[tile.x]"] << temp + #endif + + hash = md5("[hash][temp]") + + if (fexists(result_path)) + if (!fexists(hash_path) || trim(file2text(hash_path)) != hash) + fdel(result_path) + fdel(hash_path) + + if (!fexists(result_path)) + ASSERT(x1 > 0) + ASSERT(y1 > 0) + ASSERT(x2 <= world.maxx) + ASSERT(y2 <= world.maxy) + + var/icon/map_icon = new/icon('html/mapbase1024.png') + + // map_icon is fine and contains only 1 direction at this point. + + ASSERT(map_icon.Width() == MAX_ICON_DIMENSION && map_icon.Height() == MAX_ICON_DIMENSION) + + + var/i = 0 + var/icon/turf_icon + var/icon/obj_icon + var/old_icon + var/old_icon_state + var/old_dir + var/new_icon + var/new_icon_state + var/new_dir + + for (var/turf/tile in tiles) + if (tile.loc.type != /area/start && (tile.type != /turf/space || (locate(/obj/structure/lattice) in tile) || (locate(/obj/structure/transit_tube) in tile)) && !istype(tile, /turf/space/transit)) + if (istype(tile.loc, /area/asteroid) || istype(tile.loc, /area/mine/unexplored) || istype(tile, /turf/simulated/mineral) || (istype(tile.loc, /area/space) && istype(tile, /turf/simulated/floor/plating/asteroid))) + new_icon = 'icons/turf/mining.dmi' + new_icon_state = "rock" + new_dir = 2 + else if (istype(tile.loc, /area/mine) && istype(tile, /turf/simulated/floor/plating/asteroid)) + new_icon = 'icons/turf/floors.dmi' + new_icon_state = "asteroid" + new_dir = 2 + else if (tile.type == /turf/simulated/floor/plating/abductor) + new_icon = 'icons/turf/floors.dmi' + new_icon_state = "alienpod1" + new_dir = 2 + else if (tile.type == /turf/space) + obj = locate(/obj/structure/lattice) in tile + + if (!obj) obj = locate(/obj/structure/transit_tube) in tile + + ASSERT(obj != null) + + if (obj) + new_icon = obj.icon + new_dir = obj.dir + new_icon_state = obj.icon_state + else if (tile.type == /turf/simulated/floor/plating && (locate(/obj/structure/window/shuttle) in tile)) + new_icon = 'icons/obj/structures.dmi' + new_dir = 2 + new_icon_state = "swindow" + else + new_icon = tile.icon + new_icon_state = tile.icon_state + new_dir = tile.dir + + if (new_icon != old_icon || new_icon_state != old_icon_state || new_dir != old_dir) + old_icon = new_icon + old_icon_state = new_icon_state + old_dir = new_dir + + turf_icon = new/icon(new_icon, new_icon_state, new_dir, 1, 0) + turf_icon.Scale(ICON_SIZE, ICON_SIZE) + + if (tile.type != /turf/space || (locate(/obj/structure/lattice) in tile)) + obj = locate(/obj/structure/transit_tube) in tile + + if (obj) + obj_icon = new/icon(obj.icon, obj.icon_state, obj.dir, 1, 0) + obj_icon.Scale(ICON_SIZE, ICON_SIZE) + turf_icon.Blend(obj_icon, ICON_OVERLAY) + + map_icon.Blend(turf_icon, ICON_OVERLAY, ((tile.x - 1) * ICON_SIZE), ((tile.y - 1) * ICON_SIZE)) + + if ((++i) % 512 == 0) sleep(1) // deliberate delay to avoid lag spikes + + else + sleep(-1) // avoid sleeping if possible: prioritize pending procs + + // BYOND BUG: map_icon now contains 4 directions? Create a new icon with only a single state. + var/icon/result_icon = new/icon() + + result_icon.Insert(map_icon, "", SOUTH, 1, 0) + + fcopy(result_icon, result_path) + text2file(hash, hash_path) + +/datum/subsystem/minimap/proc/getMinimapFile(zlevel) + return "data/minimaps/map_[zlevel]" + +/datum/subsystem/minimap/proc/sendMinimaps(client/client) + for (var/z = 1 to world.maxz) + send_asset(client, "minimap_[z].png") + +#ifdef MINIMAP_DEBUG +#undef MINIMAP_DEBUG +#endif \ No newline at end of file diff --git a/code/controllers/subsystem/nano.dm b/code/controllers/subsystem/nano.dm index 44665c1a57d..d3abbc9f871 100644 --- a/code/controllers/subsystem/nano.dm +++ b/code/controllers/subsystem/nano.dm @@ -11,13 +11,11 @@ var/datum/subsystem/nano/SSnano /datum/subsystem/nano/New() - NEW_SS_GLOBAL(SSnano) // Register the subsystem. - + NEW_SS_GLOBAL(SSnano) /datum/subsystem/nano/stat_entry() ..("O:[open_uis.len]|P:[processing_uis.len]") // Show how many interfaces we have open/are processing. - /datum/subsystem/nano/fire() // Process UIs. for(var/thing in processing_uis) var/datum/nanoui/ui = thing diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index cc39f2d6e6a..e0e07ae2303 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -20,7 +20,7 @@ var/datum/subsystem/ticker/ticker var/list/datum/mind/minds = list() //The characters in the game. Used for objective tracking. - //These bible variables should be a preference + //These bible variables should be a preference var/Bible_icon_state //icon_state the chaplain has chosen for his bible var/Bible_item_state //item_state the chaplain has chosen for his bible var/Bible_name //name of the bible @@ -178,8 +178,7 @@ var/datum/subsystem/ticker/ticker equip_characters() data_core.manifest() - master_controller.roundHasStarted() - + Master.RoundStart() world << "Welcome to [station_name()], enjoy your stay!" world << sound('sound/AI/welcome.ogg') diff --git a/code/controllers/subsystems.dm b/code/controllers/subsystems.dm index 9c2c6e74dbf..32486ba9486 100644 --- a/code/controllers/subsystems.dm +++ b/code/controllers/subsystems.dm @@ -1,32 +1,34 @@ #define NEW_SS_GLOBAL(varname) if(varname != src){if(istype(varname)){Recover();qdel(varname);}varname = src;} /datum/subsystem - //things you will want to define + // Metadata; you should define these. var/name //name of the subsystem var/priority = 0 //priority affects order of initialization. Higher priorities are initialized first, lower priorities later. Can be decimal and negative values. var/wait = 20 //time to wait (in deciseconds) between each call to fire(). Must be a positive integer. - //Dynamic Wait - A system for scaling a subsystem's fire rate based on lag - //The algorithm is: (cost-dwait_buffer+AvgCostOfAllOtherSSPerSecond)*dwait_delta - //defaults are pretty sane for most use cases. - //you can change how quickly it starts scaling back with dwait_buffer, - //and you can change how much it scales back with dwait_delta + // Dynamic Wait + // A system for scaling a subsystem's fire rate based on lag. + // The algorithm is: (cost - dwait_buffer + subsystem_cost) * dwait_delta + // Defaults are pretty sane for most use cases. + // You can change how quickly it starts scaling back with dwait_buffer, + // and you can change how much it scales back with dwait_delta. var/dynamic_wait = 0 //changes the wait based on the amount of time it took to process var/dwait_upper = 20 //longest wait can be under dynamic_wait var/dwait_lower = 5 //shortest wait can be under dynamic_wait var/dwait_delta = 7 //How much should processing time effect dwait. or basically: wait = cost*dwait_delta var/dwait_buffer = 0.7 //This number is subtracted from the processing time before calculating its new wait - //things you will probably want to leave alone + // Bookkeeping variables; probably shouldn't mess with these. var/can_fire = 0 //prevent fire() calls var/last_fire = 0 //last world.time we called fire() var/next_fire = 0 //scheduled world.time for next fire() var/cost = 0 //average time to execute var/times_fired = 0 //number of times we have called fire() - var/obj/effect/statclick/statclick // clickable stat button + // The object used for the clickable stat() button. + var/obj/effect/statclick/statclick -//used to initialize the subsystem BEFORE the map has loaded +// Used to initialize the subsystem BEFORE the map has loaded /datum/subsystem/New() //previously, this would have been named 'process()' but that name is used everywhere for different things! diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index 8b1269d8883..9ecc9cd17ec 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -26,10 +26,6 @@ var/global/datum/crewmonitor/crewmonitor = new var/list/jobs var/list/interfaces var/list/data - var/const/MAX_ICON_DIMENSION = 1024 - var/const/ICON_SIZE = 4 - var/initialized = FALSE - var/max_initialized_zlevel = 0 /datum/crewmonitor/New() . = ..() @@ -257,176 +253,7 @@ var/global/datum/crewmonitor/crewmonitor = new /datum/crewmonitor/proc/queueUpdate(z) addtimer(crewmonitor, "update", 5, TRUE, z) -/datum/crewmonitor/proc/generateMiniMaps() - for(var/z = 1 to world.maxz) - generateMiniMap(z) - for (var/z = 1 to world.maxz) - register_asset("minimap_[z].png", file("[getMinimapFile(z)].png")) - - max_initialized_zlevel = world.maxz - - world << "All minimaps have been generated." - initialized = TRUE - /datum/crewmonitor/proc/sendResources(var/client/client) send_asset(client, "crewmonitor.js") send_asset(client, "crewmonitor.css") - for (var/z = 1 to max_initialized_zlevel) - send_asset(client, "minimap_[z].png") - -/datum/crewmonitor/proc/getMinimapFile(z) - return "data/minimaps/map_[z]" - -// Activate this to debug tile mismatches in the minimap. -// This will store the full information on each tile and compare it the next time you run the minimap. -// It can be used to find out what's changed since the last iteration. -// Only activate this when you need it - this should never be active on a live server! -// #define MINIMAP_DEBUG - -/datum/crewmonitor/proc/generateMiniMap(z, x1 = 1, y1 = 1, x2 = world.maxx, y2 = world.maxy) - var/result_path = "[src.getMinimapFile(z)].png" - var/hash_path = "[src.getMinimapFile(z)].md5" - var/list/tiles = block(locate(x1, y1, z), locate(x2, y2, z)) - var/hash = "" - var/temp - var/obj/obj - - #ifdef MINIMAP_DEBUG - var/tiledata_path = "data/minimaps/debug_tiledata_[z].sav" - var/savefile/F = new/savefile(tiledata_path) - #endif - - // Note for future developer: If you have tiles on the map with random or dynamic icons this hash check will fail - // every time. You'll have to modify this code to generate a unique hash for your object. - // Don't forget to modify the minimap generation code to use a default icon (or skip generation altogether). - for (var/turf/tile in tiles) - if (istype(tile.loc, /area/asteroid) || istype(tile.loc, /area/mine/unexplored) || istype(tile, /turf/simulated/mineral) || (istype(tile.loc, /area/space) && istype(tile, /turf/simulated/floor/plating/asteroid))) - temp = "/area/asteroid" - else if (istype(tile.loc, /area/mine) && istype(tile, /turf/simulated/floor/plating/asteroid)) - temp = "/area/mine/explored" - else if (tile.loc.type == /area/start || (tile.type == /turf/space && !(locate(/obj/structure/lattice) in tile)) || istype(tile, /turf/space/transit)) - temp = "/turf/space" - if (locate(/obj/structure/lattice/catwalk) in tile) - - else - else if (tile.type == /turf/space) - if (locate(/obj/structure/lattice/catwalk) in tile) - temp = "/obj/structure/lattice/catwalk" - else - temp = "/obj/structure/lattice" - else if (tile.type == /turf/simulated/floor/plating/abductor) - temp = "/turf/simulated/floor/plating/abductor" - else if (tile.type == /turf/simulated/floor/plating && (locate(/obj/structure/window/shuttle) in tile)) - temp = "/obj/structure/window/shuttle" - else - temp = "[tile.icon][tile.icon_state][tile.dir]" - - obj = locate(/obj/structure/transit_tube) in tile - - if (obj) temp = "[temp]/obj/structure/transit_tube[obj.icon_state][obj.dir]" - - #ifdef MINIMAP_DEBUG - if (F["/[tile.y]/[tile.x]"] && F["/[tile.y]/[tile.x]"] != temp) - CRASH("Mismatch: [tile.type] at [tile.x],[tile.y],[tile.z] ([tile.icon], [tile.icon_state], [tile.dir])") - else - F["/[tile.y]/[tile.x]"] << temp - #endif - - hash = md5("[hash][temp]") - - if (fexists(result_path)) - if (!fexists(hash_path) || trim(file2text(hash_path)) != hash) - fdel(result_path) - fdel(hash_path) - - if (!fexists(result_path)) - ASSERT(x1 > 0) - ASSERT(y1 > 0) - ASSERT(x2 <= world.maxx) - ASSERT(y2 <= world.maxy) - - var/icon/map_icon = new/icon('html/mapbase1024.png') - - // map_icon is fine and contains only 1 direction at this point. - - ASSERT(map_icon.Width() == MAX_ICON_DIMENSION && map_icon.Height() == MAX_ICON_DIMENSION) - - - var/i = 0 - var/icon/turf_icon - var/icon/obj_icon - var/old_icon - var/old_icon_state - var/old_dir - var/new_icon - var/new_icon_state - var/new_dir - - for (var/turf/tile in tiles) - if (tile.loc.type != /area/start && (tile.type != /turf/space || (locate(/obj/structure/lattice) in tile) || (locate(/obj/structure/transit_tube) in tile)) && !istype(tile, /turf/space/transit)) - if (istype(tile.loc, /area/asteroid) || istype(tile.loc, /area/mine/unexplored) || istype(tile, /turf/simulated/mineral) || (istype(tile.loc, /area/space) && istype(tile, /turf/simulated/floor/plating/asteroid))) - new_icon = 'icons/turf/mining.dmi' - new_icon_state = "rock" - new_dir = 2 - else if (istype(tile.loc, /area/mine) && istype(tile, /turf/simulated/floor/plating/asteroid)) - new_icon = 'icons/turf/floors.dmi' - new_icon_state = "asteroid" - new_dir = 2 - else if (tile.type == /turf/simulated/floor/plating/abductor) - new_icon = 'icons/turf/floors.dmi' - new_icon_state = "alienpod1" - new_dir = 2 - else if (tile.type == /turf/space) - obj = locate(/obj/structure/lattice) in tile - - if (!obj) obj = locate(/obj/structure/transit_tube) in tile - - ASSERT(obj != null) - - if (obj) - new_icon = obj.icon - new_dir = obj.dir - new_icon_state = obj.icon_state - else if (tile.type == /turf/simulated/floor/plating && (locate(/obj/structure/window/shuttle) in tile)) - new_icon = 'icons/obj/structures.dmi' - new_dir = 2 - new_icon_state = "swindow" - else - new_icon = tile.icon - new_icon_state = tile.icon_state - new_dir = tile.dir - - if (new_icon != old_icon || new_icon_state != old_icon_state || new_dir != old_dir) - old_icon = new_icon - old_icon_state = new_icon_state - old_dir = new_dir - - turf_icon = new/icon(new_icon, new_icon_state, new_dir, 1, 0) - turf_icon.Scale(ICON_SIZE, ICON_SIZE) - - if (tile.type != /turf/space || (locate(/obj/structure/lattice) in tile)) - obj = locate(/obj/structure/transit_tube) in tile - - if (obj) - obj_icon = new/icon(obj.icon, obj.icon_state, obj.dir, 1, 0) - obj_icon.Scale(ICON_SIZE, ICON_SIZE) - turf_icon.Blend(obj_icon, ICON_OVERLAY) - - map_icon.Blend(turf_icon, ICON_OVERLAY, ((tile.x - 1) * ICON_SIZE), ((tile.y - 1) * ICON_SIZE)) - - if ((++i) % 512 == 0) sleep(1) // deliberate delay to avoid lag spikes - - else - sleep(-1) // avoid sleeping if possible: prioritize pending procs - - // BYOND BUG: map_icon now contains 4 directions? Create a new icon with only a single state. - var/icon/result_icon = new/icon() - - result_icon.Insert(map_icon, "", SOUTH, 1, 0) - - fcopy(result_icon, result_path) - text2file(hash, hash_path) - -#ifdef MINIMAP_DEBUG -#undef MINIMAP_DEBUG -#endif + SSminimap.sendMinimaps(client) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 894d847646b..07881623118 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -118,7 +118,6 @@ var/list/admin_verbs_debug = list( /client/proc/cmd_admin_list_open_jobs, /client/proc/Debug2, /client/proc/cmd_debug_make_powernets, - /client/proc/debug_controller, /client/proc/cmd_debug_mob_lists, /client/proc/cmd_admin_delete, /client/proc/cmd_debug_del_all, @@ -203,7 +202,6 @@ var/list/admin_verbs_hideable = list( /client/proc/Debug2, /client/proc/reload_admins, /client/proc/cmd_debug_make_powernets, - /client/proc/debug_controller, /client/proc/startSinglo, /client/proc/cmd_debug_mob_lists, /client/proc/cmd_debug_del_all, diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index 74384f6a8c4..ad7d2574683 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -58,7 +58,7 @@ var/intercom_range_display_status = 0 set category = "Mapping" set name = "Camera Report" - if(!master_controller) + if(!Master) alert(usr,"Master_controller not found.","Sec Camera Report") return 0 diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index 721a7827941..2ce122f85fd 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -17,9 +17,6 @@ You can set verify to TRUE if you want send() to sleep until the client has the //When sending mutiple assets, how many before we give the client a quaint little sending resources message #define ASSET_CACHE_TELL_CLIENT_AMOUNT 8 -//List of ALL assets for the above, format is list(filename = asset). -/var/global/list/asset_cache = list() - /client var/list/cache = list() // List of all assets sent to this client by the asset cache. var/list/completed_asset_jobs = list() // List of all completed jobs, awaiting acknowledgement. @@ -44,7 +41,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the if(client.cache.Find(asset_name) || client.sending.Find(asset_name)) return 0 - client << browse_rsc(asset_cache[asset_name], asset_name) + client << browse_rsc(SSasset.cache[asset_name], asset_name) if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip. if (client) client.cache += asset_name @@ -94,8 +91,8 @@ You can set verify to TRUE if you want send() to sleep until the client has the if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT) client << "Sending Resources..." for(var/asset in unreceived) - if (asset in asset_cache) - client << browse_rsc(asset_cache[asset], asset) + if (asset in SSasset.cache) + client << browse_rsc(SSasset.cache[asset], asset) if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip. if (client) @@ -127,7 +124,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the //This proc will download the files without clogging up the browse() queue, used for passively sending files on connection start. //The proc calls procs that sleep for long times. -proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE) +/proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE) for(var/file in files) if (!client) break @@ -139,18 +136,7 @@ proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE) //This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up. //if it's an icon or something be careful, you'll have to copy it before further use. /proc/register_asset(var/asset_name, var/asset) - asset_cache[asset_name] = asset - -//From here on out it's populating the asset cache. -/proc/populate_asset_cache() - for(var/type in typesof(/datum/asset) - list(/datum/asset, /datum/asset/simple)) - var/datum/asset/A = new type() - A.register() - - for(var/client/C in clients) - //doing this to a client too soon after they've connected can cause issues, also the proc we call sleeps - spawn(10) - getFilesSlow(C, asset_cache, FALSE) + SSasset.cache[asset_name] = asset //These datums are used to populate the asset cache, the proc "register()" does this. diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index 162739f4b2c..f05466bf81d 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -179,7 +179,7 @@ var/next_external_rsc = 0 else if (isnum(player_age) && player_age < config.notify_new_player_age) message_admins("New user: [key_name_admin(src)] just connected with an age of [player_age] day[(player_age==1?"":"s")]") - + sync_client_with_db() send_resources() @@ -328,4 +328,4 @@ var/next_external_rsc = 0 ) spawn (10) //Precache the client with all other assets slowly, so as to not block other browse() calls - getFilesSlow(src, asset_cache, register_asset = FALSE) + getFilesSlow(src, SSasset.cache, register_asset = FALSE) diff --git a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm index dfa2e7bb970..a858c975505 100644 --- a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm +++ b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm @@ -7,12 +7,17 @@ var/const/CHUNK_SIZE = 16 // Only chunk sizes that are to the power of 2. E.g: 2 var/datum/cameranet/cameranet = new() /datum/cameranet + var/name = "Camera Net" // Name to show for VV and stat() + // The cameras on the map, no matter if they work or not. Updated in obj/machinery/camera.dm by New() and Del(). var/list/cameras = list() // The chunks of the map, mapping the areas that the cameras can see. var/list/chunks = list() var/ready = 0 + // The object used for the clickable stat() button. + var/obj/effect/statclick/statclick + // Checks if a chunk has been Generated in x, y, z. /datum/cameranet/proc/chunkGenerated(x, y, z) x &= ~(CHUNK_SIZE - 1) @@ -142,6 +147,11 @@ var/datum/cameranet/cameranet = new() return 1 return 0 +/datum/cameranet/proc/stat_entry() + if(!statclick) + statclick = new/obj/effect/statclick/debug("Initializing...", src) + + stat(name, statclick.update("Cameras: [cameranet.cameras.len] | Chunks: [cameranet.chunks.len]")) // Debug verb for VVing the chunk that the turf is in. /* @@ -151,4 +161,4 @@ var/datum/cameranet/cameranet = new() if(cameranet.chunkGenerated(x, y, z)) var/datum/camerachunk/chunk = cameranet.getCameraChunk(x, y, z) usr.client.debug_variables(chunk) -*/ +*/ \ No newline at end of file diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index ff20083da39..1c252214669 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -653,20 +653,21 @@ var/list/slot_equipment_priority = list( \ stat("Location:", "([x], [y], [z])") stat("CPU:", "[world.cpu]") stat("Instances:", "[world.contents.len]") - if(master_controller) - master_controller.stat_entry() + config.stat_entry() + if(Master) + Master.stat_entry() else stat("Master Controller:", "ERROR") if(Failsafe) Failsafe.stat_entry() else stat("Failsafe Controller:", "ERROR") - if(master_controller) - stat("Subsystems:", "[round(master_controller.SSCostPerSecond, 0.001)]ds") + if(Master) + stat("Subsystems:", "[round(Master.subsystem_cost, 0.001)]ds") stat(null) - for(var/datum/subsystem/SS in master_controller.subsystems) - if(SS.can_fire) - SS.stat_entry() + for(var/datum/subsystem/SS in Master.subsystems) + SS.stat_entry() + cameranet.stat_entry() if(listed_turf && client) if(!TurfAdjacent(listed_turf)) diff --git a/code/world.dm b/code/world.dm index d47c2a97b62..fd74041e456 100644 --- a/code/world.dm +++ b/code/world.dm @@ -54,9 +54,8 @@ var/global/list/map_transition_config = MAP_TRANSITION_CONFIG data_core = new /datum/datacore() - spawn(-1) - master_controller.setup() + Master.Setup() process_teleport_locs() //Sets up the wizard teleport locations SortAreas() //Build the list of all existing areas and sort it alphabetically diff --git a/tgstation.dme b/tgstation.dme index eb4129eb273..49766e6760f 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -140,16 +140,19 @@ #include "code\ATMOSPHERICS\pipes\heat_exchange\simple.dm" #include "code\controllers\admin.dm" #include "code\controllers\configuration.dm" +#include "code\controllers\controller.dm" #include "code\controllers\failsafe.dm" -#include "code\controllers\master_controller.dm" +#include "code\controllers\master.dm" #include "code\controllers\subsystems.dm" #include "code\controllers\subsystem\air.dm" +#include "code\controllers\subsystem\assets.dm" #include "code\controllers\subsystem\diseases.dm" #include "code\controllers\subsystem\events.dm" #include "code\controllers\subsystem\garbage.dm" #include "code\controllers\subsystem\jobs.dm" #include "code\controllers\subsystem\lighting.dm" #include "code\controllers\subsystem\machines.dm" +#include "code\controllers\subsystem\minimap.dm" #include "code\controllers\subsystem\mobs.dm" #include "code\controllers\subsystem\nano.dm" #include "code\controllers\subsystem\npcpool.dm"