mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-23 05:00:55 +01:00
Rework Master Controller, Failsafe, and Subsystem code
This commit is contained in:
@@ -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.")
|
||||
|
||||
@@ -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)
|
||||
@@ -0,0 +1,4 @@
|
||||
/datum/controller
|
||||
var/name
|
||||
// The object used for the clickable stat() button.
|
||||
var/obj/effect/statclick/statclick
|
||||
@@ -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 << "<font color='red' size='2'><b>Warning. The Master Controller has not fired in the last [MC_defcon*processing_interval] ticks. Automatic restart in [processing_interval] ticks.</b></font>"
|
||||
MC_defcon = 5
|
||||
admins << "<span class='boldannounce'>Warning: DEFCON [defcon]. The Master Controller has not fired in the last [defcon * processing_interval] ticks. Automatic restart in [processing_interval] ticks.</span>"
|
||||
defcon = 5
|
||||
if(5)
|
||||
admins << "<font color='red' size='2'><b>Warning. The Master Controller has still not fired within the last [MC_defcon*processing_interval] ticks. Killing and restarting...</b></font>"
|
||||
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 << "<span class='boldannounce'>Warning: DEFCON [defcon]. The Master Controller has still not fired within the last [defcon * processing_interval] ticks. Killing and restarting...</span>"
|
||||
// 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])"))
|
||||
|
||||
@@ -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 << "<span class='boldannounce'>Initializing Subsystems...</span>"
|
||||
|
||||
|
||||
// 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 << "<span class='boldannounce'>Initializations Complete!</span>"
|
||||
|
||||
// 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])"))
|
||||
@@ -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 << "<span class='boldannounce'>Initializing Subsystems...</span>"
|
||||
|
||||
|
||||
//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, i<max_secret_rooms, i++)
|
||||
make_mining_asteroid_secret()
|
||||
|
||||
//Eventually all this other setup stuff should be contained in subsystems and done in subsystem.Initialize()
|
||||
for(var/datum/subsystem/S in subsystems)
|
||||
S.Initialize(world.timeofday, zlevel)
|
||||
sleep(-1)
|
||||
|
||||
crewmonitor.generateMiniMaps()
|
||||
populate_asset_cache()
|
||||
|
||||
world << "<span class='boldannounce'>Initializations Complete!</span>"
|
||||
|
||||
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])"))
|
||||
@@ -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)
|
||||
..()
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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 << "<FONT color='blue'><B>Welcome to [station_name()], enjoy your stay!</B></FONT>"
|
||||
world << sound('sound/AI/welcome.ogg')
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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 << "<span class='boldannounce'>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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
*/
|
||||
*/
|
||||
@@ -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))
|
||||
|
||||
+1
-2
@@ -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
|
||||
|
||||
+4
-1
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user