mirror of
https://github.com/yogstation13/Yogstation.git
synced 2025-02-26 09:04:50 +00:00
* Initial start of lavaland ruin budget system Weights are dumb, we're gonna use BUDGETS! To be clear, each round, there will be a budget for ruins. HERE IS A ROUGH APPROXIMATION OF THE AIMED PROCESS - Pick a ruin - Can we afford it? If not, start again. - Try a bunch of times to place it. - Did we place it? Good, then reduce our budget accordingly Does this look a little like syndicate surplus crates? That's the point. * Subclassery * Budget based ruin loader is GO * Cryostasis (with a s) * Adjusted costs, added no-duplication * No dupes on the major ruins * Deleted files, budget is config * Corrected description * Fixed ruins failing to load, duplication * The zoo goldgrub will no longer burrow away * Space descriptions and names * No more weird Xenonest loops, should be all good * Adjusted costs based on feedback
248 lines
8.2 KiB
Plaintext
248 lines
8.2 KiB
Plaintext
/**
|
|
* 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"
|
|
|
|
// are we processing (higher values increase the processing delay)
|
|
var/processing = 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
|
|
|
|
var/list/priority_queue = list() //any time we pause or skip a ss for tick reasons, we run it first next tick
|
|
|
|
/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
|
|
|
|
/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)
|
|
CHECK_TICK
|
|
return
|
|
world << "<span class='boldannounce'>Initializing subsystems...</span>"
|
|
|
|
var/tally = 0
|
|
var/obj/effect/spawner/lootdrop/maintenance/L = new()
|
|
// Grab it before it gets deleted
|
|
var/list/loot = L.loot.Copy()
|
|
for(var/item in loot)
|
|
tally += loot[item]
|
|
world.log << "There are [tally] items in the \
|
|
maintenance loot table."
|
|
|
|
preloadTemplates()
|
|
// Pick a random away mission.
|
|
createRandomZlevel()
|
|
// Generate mining.
|
|
|
|
var/mining_type = MINETYPE
|
|
if(mining_type == "lavaland")
|
|
seedRuins(5, config.lavaland_budget, \
|
|
/area/lavaland/surface/outdoors, lava_ruins_templates)
|
|
spawn_rivers()
|
|
else
|
|
make_mining_asteroid_secrets()
|
|
|
|
// deep space ruins
|
|
seedRuins(7, rand(0,2), /area/space, space_ruins_templates)
|
|
seedRuins(8, rand(0,2), /area/space, space_ruins_templates)
|
|
seedRuins(9, rand(0,2), /area/space, space_ruins_templates)
|
|
|
|
// Set up Z-level transistions.
|
|
setup_map_transitions()
|
|
|
|
// Sort subsystems by priority, so they initialize in the correct order.
|
|
sortTim(subsystems, /proc/cmp_subsystem_priority)
|
|
|
|
// Initialize subsystems.
|
|
for(var/datum/subsystem/SS in subsystems)
|
|
SS.Initialize(world.timeofday, zlevel)
|
|
CHECK_TICK
|
|
|
|
world << "<span class='boldannounce'>Initializations complete!</span>"
|
|
|
|
// Sort subsystems by display setting for easy access.
|
|
sortTim(subsystems, /proc/cmp_subsystem_display)
|
|
|
|
// 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()
|
|
var/timer = world.time
|
|
for(var/datum/subsystem/SS in subsystems)
|
|
timer += world.tick_lag
|
|
SS.can_fire = 1
|
|
SS.next_fire = timer + rand(0, SS.wait) // Stagger subsystems.
|
|
|
|
// Used to smooth out costs to try and avoid oscillation.
|
|
#define MC_AVERAGE_FAST(average, current) (0.7 * (average) + 0.3 * (current))
|
|
#define MC_AVERAGE(average, current) (0.8 * (average) + 0.2 * (current))
|
|
#define MC_AVERAGE_SLOW(average, current) (0.9 * (average) + 0.1 * (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)
|
|
if (SS.can_fire)
|
|
timer += world.tick_lag
|
|
SS.next_fire = timer
|
|
|
|
var/list/subsystemstorun = subsystems.Copy()
|
|
var/start_time
|
|
while(1) // More efficient than recursion.
|
|
if(processing) //are we processing
|
|
++iteration
|
|
start_time = world.timeofday
|
|
if (!subsystemstorun.len)
|
|
subsystemstorun = subsystems.Copy()
|
|
var/priorityrunning = 0 //so we know if there are priority queue items
|
|
//this is a queue for any SS skipped or paused for tick reasons, to be ran first next tick
|
|
if (priority_queue.len)
|
|
priorityrunning = priority_queue.len
|
|
subsystemstorun = priority_queue | subsystemstorun
|
|
|
|
var/ran_subsystems = 0
|
|
while(subsystemstorun.len)
|
|
var/datum/subsystem/SS = subsystemstorun[1]
|
|
subsystemstorun.Cut(1, 2)
|
|
|
|
if (world.tick_usage > TICK_LIMIT_MC)
|
|
break
|
|
if(priorityrunning)
|
|
if(!priority_queue.len || !(SS in priority_queue))
|
|
priorityrunning = 0 //end of priority queue items
|
|
else
|
|
priority_queue -= SS
|
|
if(SS.can_fire > 0)
|
|
if(priorityrunning || ((SS.next_fire <= world.time) && (SS.last_fire + (SS.wait * 0.75) <= world.time || SS.paused)))
|
|
//we can't reset SS.paused after we fire, incase it pauses again, so we cache it and
|
|
// send it to SS.fire()
|
|
var/paused = SS.paused
|
|
if(!priorityrunning && !paused && (world.tick_usage + SS.tick_usage > TICK_LIMIT_TO_RUN) && (SS.last_fire + (SS.wait*1.25) > world.time))
|
|
if(!SS.dynamic_wait)
|
|
priority_queue += SS
|
|
continue
|
|
SS.paused = 0
|
|
ran_subsystems = 1
|
|
timer = world.timeofday
|
|
last_type_processed = SS.type
|
|
if (!paused) //only set this when we aren't resuming
|
|
SS.last_fire = world.time
|
|
var/tick_usage = world.tick_usage
|
|
SS.fire(paused) // Fire the subsystem
|
|
|
|
if(priorityrunning)
|
|
priorityrunning--
|
|
var/newusage = max(world.tick_usage - tick_usage, 0)
|
|
if(newusage < SS.tick_usage)
|
|
SS.tick_usage = MC_AVERAGE_SLOW(SS.tick_usage,world.tick_usage - tick_usage)
|
|
else
|
|
SS.tick_usage = MC_AVERAGE_FAST(SS.tick_usage,world.tick_usage - tick_usage)
|
|
SS.cost = max(MC_AVERAGE(SS.cost, world.timeofday - timer), 0)
|
|
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)
|
|
//smooth out wait changes, but only if going down
|
|
if(newwait < oldwait)
|
|
newwait = MC_AVERAGE(oldwait, newwait)
|
|
SS.wait = Clamp(newwait, SS.dwait_lower, SS.dwait_upper)
|
|
if(SS.paused)
|
|
SS.next_fire = world.time //run it next tick if we can, but don't priority queue it, as we are a dynamic wait subsystem
|
|
else //not paused, it ran all the way thru, normal wait time
|
|
SS.next_fire = SS.last_fire + SS.wait //we use last_fire so that its next fire is based off when it started this fire, not when it finished.
|
|
else
|
|
if(!paused)
|
|
SS.next_fire += SS.wait
|
|
if(!SS.paused)
|
|
SS.times_fired++
|
|
|
|
cost = max(MC_AVERAGE(cost, world.timeofday - start_time), 0)
|
|
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 are loading the server too much, sleep a bit extra...
|
|
if(world.cpu >= 75)
|
|
extrasleep += (extrasleep + world.tick_lag) * ((world.cpu-50)/10)
|
|
sleep(world.tick_lag*processing+extrasleep)
|
|
|
|
else
|
|
sleep(50)
|
|
|
|
#undef MC_AVERAGE_FAST
|
|
#undef MC_AVERAGE
|
|
#undef MC_AVERAGE_SLOW
|
|
|
|
/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.01)]ds (Iteration:[Master.iteration])"))
|
|
|