diff --git a/code/__DEFINES/_globals.dm b/code/__DEFINES/_globals.dm index b372242d43f..5ff291b4e3d 100644 --- a/code/__DEFINES/_globals.dm +++ b/code/__DEFINES/_globals.dm @@ -2,16 +2,16 @@ //? Basic -//Creates a global initializer with a given InitValue expression, do not use +/// Creates a global initializer with a given InitValue expression, do not use #define GLOBAL_MANAGED(X, InitValue)\ /datum/controller/global_vars/proc/InitGlobal##X(){\ ##X = ##InitValue;\ gvars_datum_init_order += #X;\ } -//Creates an empty global initializer, do not use +/// Creates an empty global initializer, do not use #define GLOBAL_UNMANAGED(X) /datum/controller/global_vars/proc/InitGlobal##X() { return; } -//Prevents a given global from being VV'd +/// Prevents a given global from being VV'd #ifndef TESTING #define GLOBAL_PROTECT(X)\ /datum/controller/global_vars/InitGlobal##X(){\ @@ -38,35 +38,41 @@ //? Managed Globals -//Defines a global var on the controller, do not use +/// Defines a global var on the controller, do not use #define GLOBAL_RAW(X) /datum/controller/global_vars/var/global##X -//Create an untyped global with an initializer expression +/// Create an untyped global with an initializer expression #define GLOBAL_VAR_INIT(X, InitValue) GLOBAL_RAW(/##X); GLOBAL_MANAGED(X, InitValue) -//Create a global const var, do not use +/// Create a global const var, do not use #define GLOBAL_VAR_CONST(X, InitValue) GLOBAL_RAW(/const/##X) = InitValue; GLOBAL_UNMANAGED(X) -//Create a list global with an initializer expression +/// Create a list global with an initializer expression #define GLOBAL_LIST_INIT(X, InitValue) GLOBAL_RAW(/list/##X); GLOBAL_MANAGED(X, InitValue) -//Create a list global that is initialized as an empty list +/// Create a list global that is initialized as an empty list #define GLOBAL_LIST_EMPTY(X) GLOBAL_LIST_INIT(X, list()) -// Create a typed list global with an initializer expression +/// Create a typed list global with an initializer expression #define GLOBAL_LIST_INIT_TYPED(X, Typepath, InitValue) GLOBAL_RAW(/list##Typepath/X); GLOBAL_MANAGED(X, InitValue) -// Create a typed list global that is initialized as an empty list +/// Create a typed list global that is initialized as an empty list #define GLOBAL_LIST_EMPTY_TYPED(X, Typepath) GLOBAL_LIST_INIT_TYPED(X, Typepath, list()) -//Create a typed global with an initializer expression +/// Create an alist global with an initializer expression +#define GLOBAL_ALIST_INIT(X, InitValue) GLOBAL_RAW(/alist/##X); GLOBAL_MANAGED(X, InitValue) + +// Create an alist global that is initialized as an empty list +#define GLOBAL_ALIST_EMPTY(X) GLOBAL_ALIST_INIT(X, alist()) + +/// Create a typed global with an initializer expression #define GLOBAL_DATUM_INIT(X, Typepath, InitValue) GLOBAL_RAW(Typepath/##X); GLOBAL_MANAGED(X, InitValue) -//Create an untyped null global +/// Create an untyped null global #define GLOBAL_VAR(X) GLOBAL_RAW(/##X); GLOBAL_UNMANAGED(X) -//Create a null global list +/// Create a null global list #define GLOBAL_LIST(X) GLOBAL_RAW(/list/##X); GLOBAL_UNMANAGED(X) -//Create an typed null global +/// Create a typed null global #define GLOBAL_DATUM(X, Typepath) GLOBAL_RAW(Typepath/##X); GLOBAL_UNMANAGED(X) diff --git a/code/__DEFINES/time.dm b/code/__DEFINES/time.dm index 1bb3b5e7415..e0ed1f0b2a3 100644 --- a/code/__DEFINES/time.dm +++ b/code/__DEFINES/time.dm @@ -1,20 +1,62 @@ +///number of deciseconds in a day +#define MIDNIGHT_ROLLOVER 864000 + +///displays the current time into the round, with a lot of extra code just there for ensuring it looks okay after an entire day passes +#define ROUND_TIME(...) ( "[STATION_TIME_PASSED() > MIDNIGHT_ROLLOVER ? "[round(STATION_TIME_PASSED()/MIDNIGHT_ROLLOVER)]:[gameTimestamp(wtime = STATION_TIME_PASSED())]" : gameTimestamp(wtime = STATION_TIME_PASSED())]" ) + +///Returns the time that has passed since the game started +#define STATION_TIME_PASSED(...) (world.time - SSticker.round_start_time) + +/// Define that just has the current in-universe year for use in whatever context you might want to display that in. (For example, 2022 -> 2562 given a 540 year offset) +#define CURRENT_STATION_YEAR (GLOB.year_integer + STATION_YEAR_OFFSET) + +/// In-universe, SS13 rp is set 544 years in the future from the real-world day, hence this number for determining the year-offset for the in-game year. +#define STATION_YEAR_OFFSET 544 + +#define JANUARY 1 +#define FEBRUARY 2 +#define MARCH 3 +#define APRIL 4 +#define MAY 5 +#define JUNE 6 +#define JULY 7 +#define AUGUST 8 +#define SEPTEMBER 9 +#define OCTOBER 10 +#define NOVEMBER 11 +#define DECEMBER 12 + +/** + * Days of the week to make it easier to reference them. + * When using time2text(), please use "DDD" to find the weekday. Refrain from using "Day" + */ +#define MONDAY "Mon" +#define TUESDAY "Tue" +#define WEDNESDAY "Wed" +#define THURSDAY "Thu" +#define FRIDAY "Fri" +#define SATURDAY "Sat" +#define SUNDAY "Sun" + #define MILLISECONDS *0.01 #define DECISECONDS *1 //the base unit all of these defines are scaled by, because byond uses that as a unit of measurement for some fucking reason -#define SECOND *10 +#define SECOND SECONDS #define SECONDS *10 -#define MINUTE *600 -#define MINUTES *600 +#define MINUTE MINUTES +#define MINUTES SECONDS*60 -#define HOUR *36000 -#define HOURS *36000 +#define HOUR MINUTES +#define HOURS MINUTES*60 -#define DAY *864000 -#define DAYS *864000 +#define DAY DAYS +#define DAYS HOURS*24 -#define TICK *world.tick_lag +#define YEARS DAYS*365 //fuck leap days, they were removed in 2069 + +#define TICK TICKS #define TICKS *world.tick_lag /// Convert deciseconds to ticks @@ -24,6 +66,104 @@ #define MS2DS(T) ((T) MILLISECONDS) +/*Timezones*/ + +/// Line Islands Time +#define TIMEZONE_LINT 14 + +// Chatham Daylight Time +#define TIMEZONE_CHADT 13.75 + +/// Tokelau Time +#define TIMEZONE_TKT 13 + +/// Tonga Time +#define TIMEZONE_TOT 13 + +/// New Zealand Daylight Time +#define TIMEZONE_NZDT 13 + +/// New Zealand Standard Time +#define TIMEZONE_NZST 12 + +/// Norfolk Time +#define TIMEZONE_NFT 11 + +/// Lord Howe Standard Time +#define TIMEZONE_LHST 10.5 + +/// Australian Eastern Standard Time +#define TIMEZONE_AEST 10 + +/// Australian Central Standard Time +#define TIMEZONE_ACST 9.5 + +/// Australian Central Western Standard Time +#define TIMEZONE_ACWST 8.75 + +/// Australian Western Standard Time +#define TIMEZONE_AWST 8 + +/// Christmas Island Time +#define TIMEZONE_CXT 7 + +/// Cocos Islands Time +#define TIMEZONE_CCT 6.5 + +/// Central European Summer Time +#define TIMEZONE_CEST 2 + +/// Coordinated Universal Time +#define TIMEZONE_UTC 0 + +/// Eastern Daylight Time +#define TIMEZONE_EDT -4 + +/// Eastern Standard Time +#define TIMEZONE_EST -5 + +/// Central Daylight Time +#define TIMEZONE_CDT -5 + +/// Central Standard Time +#define TIMEZONE_CST -6 + +/// Mountain Daylight Time +#define TIMEZONE_MDT -6 + +/// Mountain Standard Time +#define TIMEZONE_MST -7 + +/// Pacific Daylight Time +#define TIMEZONE_PDT -7 + +/// Pacific Standard Time +#define TIMEZONE_PST -8 + +/// Alaska Daylight Time +#define TIMEZONE_AKDT -8 + +/// Alaska Standard Time +#define TIMEZONE_AKST -9 + +/// Hawaii-Aleutian Daylight Time +#define TIMEZONE_HDT -9 + +/// Hawaii Standard Time +#define TIMEZONE_HST -10 + +/// Cook Island Time +#define TIMEZONE_CKT -10 + +/// Niue Time +#define TIMEZONE_NUT -11 + +/// Anywhere on Earth +#define TIMEZONE_ANYWHERE_ON_EARTH -12 + +/// in the grim darkness of the thirteenth space station there is no timezones, since they break IC game times. Use this for all IC/round time values +#define NO_TIMEZONE 0 + #define GAMETIMESTAMP(format, wtime) time2text(wtime, format) #define WORLDTIME2TEXT(format) GAMETIMESTAMP(format, world.time) #define WORLDTIMEOFDAY2TEXT(format) GAMETIMESTAMP(format, world.timeofday) @@ -31,38 +171,16 @@ #define STATION_TIME(display_only, wtime) ((((wtime - SSticker.SSticker.round_start_time) * SSticker.station_time_rate_multiplier) + SSticker.gametime_offset) % 864000) #define STATION_TIME_TIMESTAMP(format, wtime) time2text(STATION_TIME(TRUE, wtime), format) -#define JANUARY 1 -#define FEBRUARY 2 -#define MARCH 3 -#define APRIL 4 -#define MAY 5 -#define JUNE 6 -#define JULY 7 -#define AUGUST 8 -#define SEPTEMBER 9 -#define OCTOBER 10 -#define NOVEMBER 11 -#define DECEMBER 12 - /// use for rapid actions that make messages to throttle messages #define CHATSPAM_THROTTLE_DEFAULT (!(world.time % 5)) /// ditto #define CHATSPAM_THROTTLE(every) (!(world.time % every)) +// maths.dm //* REALTIMEOFDAY, because we don't have chrono::steady_clock *// //* Automatically adjusts to the server rolling over midnight *// //* so this is monotonically increasing wall-time. *// #define REALTIMEOFDAY (world.timeofday + (MIDNIGHT_ROLLOVER * MIDNIGHT_ROLLOVER_CHECK)) -#define MIDNIGHT_ROLLOVER 864000 -#define MIDNIGHT_ROLLOVER_CHECK (global.midnight_rollover_last_timeofday != world.timeofday ? update_midnight_rollover() : global.midnight_rollovers) -#define MIDNIGHT_ROLLOVER_CHECK_STANDALONE if(global.midnight_rollover_last_timeofday != world.timeofday) update_midnight_rollover() - -GLOBAL_REAL_VAR(midnight_rollovers) = 0 -GLOBAL_REAL_VAR(midnight_rollover_last_timeofday) = world.timeofday - -/proc/update_midnight_rollover() - if (world.timeofday < global.midnight_rollover_last_timeofday) //TIME IS GOING BACKWARDS! - ++global.midnight_rollovers - midnight_rollover_last_timeofday = world.timeofday - return global.midnight_rollovers +#define MIDNIGHT_ROLLOVER_CHECK ( GLOB.rollovercheck_last_timeofday != world.timeofday ? update_midnight_rollover() : GLOB.midnight_rollovers ) +#define MIDNIGHT_ROLLOVER_CHECK_STANDALONE if(GLOB.rollovercheck_last_timeofday != world.timeofday) update_midnight_rollover() diff --git a/code/__HELPERS/guid.dm b/code/__HELPERS/guid.dm index e81205c2e63..f5a7e717c4d 100644 --- a/code/__HELPERS/guid.dm +++ b/code/__HELPERS/guid.dm @@ -1,13 +1,12 @@ /** * Returns a GUID like identifier (using a mostly made up record format) * GUIDs are not on their own suitable for access or security tokens, as most of their bits are predictable. - * * (But may make a nice salt to one) */ /proc/GUID() var/const/GUID_VERSION = "b" var/const/GUID_VARIANT = "d" - var/node_id = copytext(md5("[rand() * rand(1, 9999999)][world.name][world.hub][world.hub_password][world.internet_address][world.address][world.contents.len][world.status][world.port][rand() * rand(1,9999999)]"), 1, 13) + var/node_id = copytext_char(md5("[rand() * rand(1, 9999999)][world.name][world.hub][world.hub_password][world.internet_address][world.address][world.contents.len][world.status][world.port][rand() * rand(1,9999999)]"), 1, 13) var/time_high = "[num2hex(text2num(time2text(world.realtime,"YYYY")), 2)][num2hex(world.realtime, 6)]" diff --git a/code/__HELPERS/time.dm b/code/__HELPERS/time.dm index 0aa06f9637c..e871ad87a73 100644 --- a/code/__HELPERS/time.dm +++ b/code/__HELPERS/time.dm @@ -1,28 +1,127 @@ -GLOBAL_VAR_INIT(startup_year, text2num(time2text(world.time, "YYYY"))) -GLOBAL_VAR_INIT(startup_month, text2num(time2text(world.time, "MM"))) -GLOBAL_VAR_INIT(startup_day, text2num(time2text(world.time, "DD"))) +// HEY! this doesnt return date now since this is used in logs, and logs already have date hints. also roundid exists which is bound to a date in the db +/// Returns UTC timestamp with the specifified format and optionally deciseconds +/proc/time_stamp(format = "hh:mm:ss", show_ds) + var/time_string = time2text(world.timeofday, format, TIMEZONE_UTC) + return show_ds ? "[time_string]:[world.timeofday % 10]" : time_string -#define TimeOfGame (get_game_time()) -#define TimeOfTick (TICK_USAGE*0.01*world.tick_lag) +/// Returns timestamp since the server started, for use with world.time +/proc/gameTimestamp(format = "hh:mm:ss", wtime=world.time) + return time2text(wtime, format, NO_TIMEZONE) -/proc/get_game_time() - var/global/time_offset = 0 - var/global/last_time = 0 - var/global/last_usage = 0 +///returns timestamp in a sql and a not-quite-compliant ISO 8601 friendly format. Do not use for SQL, use NOW() instead +/proc/ISOtime(timevar) + return time2text(timevar || world.timeofday, "YYYY-MM-DD hh:mm:ss", world.timezone) - var/wtime = world.time - var/wusage = TICK_USAGE * 0.01 +GLOBAL_VAR_INIT(midnight_rollovers, 0) +GLOBAL_VAR_INIT(rollovercheck_last_timeofday, 0) +/proc/update_midnight_rollover() + if (world.timeofday < GLOB.rollovercheck_last_timeofday) //TIME IS GOING BACKWARDS! + GLOB.midnight_rollovers++ + GLOB.rollovercheck_last_timeofday = world.timeofday + return GLOB.midnight_rollovers - if(last_time < wtime && last_usage > 1) - time_offset += last_usage - 1 +///Returns a string day as an integer in ISO format 1 (Monday) - 7 (Sunday) +/proc/weekday_to_iso(ddd) + switch (ddd) + if (MONDAY) + return 1 + if (TUESDAY) + return 2 + if (WEDNESDAY) + return 3 + if (THURSDAY) + return 4 + if (FRIDAY) + return 5 + if (SATURDAY) + return 6 + if (SUNDAY) + return 7 - last_time = wtime - last_usage = wusage +///Returns an integer in ISO format 1 (Monday) - 7 (Sunday) as a string day +/proc/iso_to_weekday(ddd) + switch (ddd) + if (1) + return MONDAY + if (2) + return TUESDAY + if (3) + return WEDNESDAY + if (4) + return THURSDAY + if (5) + return FRIDAY + if (6) + return SATURDAY + if (7) + return SUNDAY - return wtime + (time_offset + wusage) * world.tick_lag +/// Returns the day (mon, tues, wen...) in number format, 1 (monday) - 7 (sunday) from the passed in date (year, month, day) +/// All inputs are expected indexed at 1 +/proc/day_of_month(year, month, day) + // https://en.wikipedia.org/wiki/Zeller%27s_congruence + var/m = month < 3 ? month + 12 : month // month (march = 3, april = 4...february = 14) + var/K = year % 100 // year of century + var/J = round(year / 100) // zero-based century + // day 0-6 saturday to friday: + var/h = (day + round(13 * (m + 1) / 5) + K + round(K / 4) + round(J / 4) - 2 * J) % 7 + //convert to ISO 1-7 monday first format + return ((h + 5) % 7) + 1 + +/proc/first_day_of_month(year, month) + return day_of_month(year, month, 1) + +/** + * Takes a value of time in deciseconds. + * Returns a text value of that number in hours, minutes, or seconds. + */ +/proc/DisplayTimeText(time_value, round_seconds_to = 0.1) + var/second = FLOOR(time_value * 0.1, round_seconds_to) + if(!second) + return "right now" + if(second < 60) + return "[second] second[(second != 1)? "s":""]" + var/minute = FLOOR(second / 60, 1) + second = FLOOR(MODULUS_F(second, 60), round_seconds_to) + var/secondT + if(second) + secondT = " and [second] second[(second != 1)? "s":""]" + if(minute < 60) + return "[minute] minute[(minute != 1)? "s":""][secondT]" + var/hour = FLOOR(minute / 60, 1) + minute = MODULUS_F(minute, 60) + var/minuteT + if(minute) + minuteT = " and [minute] minute[(minute != 1)? "s":""]" + if(hour < 24) + return "[hour] hour[(hour != 1)? "s":""][minuteT][secondT]" + var/day = FLOOR(hour / 24, 1) + hour = MODULUS_F(hour, 24) + var/hourT + if(hour) + hourT = " and [hour] hour[(hour != 1)? "s":""]" + return "[day] day[(day != 1)? "s":""][hourT][minuteT][secondT]" + +/proc/daysSince(realtimev) + return round((world.realtime - realtimev) / (24 HOURS)) + +/** + * Converts a time expressed in deciseconds (like world.time) to the 12-hour time format. + * the format arg is the format passed down to time2text() (e.g. "hh:mm" is hours and minutes but not seconds). + * the timezone is the time value offset from the local time. It's to be applied outside time2text() to get the AM/PM right. + */ +/proc/time_to_twelve_hour(time, format = "hh:mm:ss", timezone = TIMEZONE_UTC) + time = MODULUS_F(time + (timezone * (1 HOURS)), 24 HOURS) + var/am_pm = "AM" + if(time > 12 HOURS) + am_pm = "PM" + if(time > 13 HOURS) + time -= 12 HOURS // e.g. 4:16 PM but not 00:42 PM + else if (time < 1 HOURS) + time += 12 HOURS // e.g. 12.23 AM + return "[time2text(time, format)] [am_pm]" #define worldtime2stationtime(time) SStime_keep.render_galactic_time_short(time) -#define round_duration_in_ds (SSticker.round_start_time ? world.time - SSticker.round_start_time : 0) #define station_time_in_ds (world.time + SStime_keep.get_galactic_time_offset()) // TODO: remove @@ -33,33 +132,6 @@ GLOBAL_VAR_INIT(startup_day, text2num(time2text(world.time, "DD"))) /proc/stationdate2text() return SStime_keep.render_galactic_date() -/// ISO 8601 -/proc/time_stamp() - var/date_portion = time2text(world.timeofday, "YYYY-MM-DD") - var/time_portion = time2text(world.timeofday, "hh:mm:ss") - return "[date_portion]T[time_portion]" - -/proc/gameTimestamp(format = "hh:mm:ss", wtime=null) - if(!wtime) - wtime = world.time - return time2text(wtime, format, 0) - -/** - * Returns 1 if it is the selected month and day. - */ -/proc/isDay(var/month, var/day) - if(isnum(month) && isnum(day)) - /// Get the current month. - var/MM = text2num(time2text(world.timeofday, "MM")) - /// Get the current day. - var/DD = text2num(time2text(world.timeofday, "DD")) - if(month == MM && day == DD) - return TRUE - - // Uncomment this out when debugging! - // else - // return TRUE - /var/next_duration_update = 0 /var/last_round_duration = 0 @@ -71,7 +143,7 @@ GLOBAL_VAR_INIT(startup_day, text2num(time2text(world.time, "DD"))) return last_round_duration /// 1/10 of a second, not real milliseconds but whatever. - var/mills = round_duration_in_ds + var/mills = STATION_TIME_PASSED() /// Not really needed, but I'll leave it here for refrence.. or something. //var/secs = ((mills % 36000) % 600) / 10 var/mins = round((mills % 36000) / 600) @@ -98,37 +170,3 @@ GLOBAL_VAR_INIT(startup_day, text2num(time2text(world.time, "DD"))) return 5 else return 1 - -/** - * Takes a value of time in deciseconds. - * Returns a text value of that number in hours, minutes, or seconds. - */ -/proc/DisplayTimeText(time_value, round_seconds_to = 0.1) - var/second = FLOOR(time_value * 0.1, round_seconds_to) - if(!second) - return "right now" - if(second < 60) - return "[second] second[(second != 1)? "s":""]" - var/minute = FLOOR(second / 60, 1) - second = MODULUS_F(second, 60) - var/secondT - if(second) - secondT = " and [second] second[(second != 1)? "s":""]" - if(minute < 60) - return "[minute] minute[(minute != 1)? "s":""][secondT]" - var/hour = FLOOR(minute / 60, 1) - minute = MODULUS_F(minute, 60) - var/minuteT - if(minute) - minuteT = " and [minute] minute[(minute != 1)? "s":""]" - if(hour < 24) - return "[hour] hour[(hour != 1)? "s":""][minuteT][secondT]" - var/day = FLOOR(hour / 24, 1) - hour = MODULUS_F(hour, 24) - var/hourT - if(hour) - hourT = " and [hour] hour[(hour != 1)? "s":""]" - return "[day] day[(day != 1)? "s":""][hourT][minuteT][secondT]" - -/proc/daysSince(realtimev) - return round((world.realtime - realtimev) / (24 HOURS)) diff --git a/code/_globals/time_vars.dm b/code/_globals/time_vars.dm index 24a897ebd30..7486933d5dc 100644 --- a/code/_globals/time_vars.dm +++ b/code/_globals/time_vars.dm @@ -1,3 +1,6 @@ +GLOBAL_VAR_INIT(year, time2text(world.realtime, "YYYY", NO_TIMEZONE)) +GLOBAL_VAR_INIT(year_integer, text2num(year)) // = 2013??? + /// 12:00 in seconds GLOBAL_VAR_INIT(gametime_offset, 432000) diff --git a/code/controllers/globals.dm b/code/controllers/globals.dm index 0b4cad574bf..7b8fa6e6c6a 100644 --- a/code/controllers/globals.dm +++ b/code/controllers/globals.dm @@ -1,9 +1,10 @@ +// See initialization order in /code/game/world.dm GLOBAL_REAL(GLOB, /datum/controller/global_vars) /datum/controller/global_vars name = "Global Variables" - var/list/gvars_datum_protected_varlist + var/static/list/gvars_datum_protected_varlist var/list/gvars_datum_in_built_vars var/list/gvars_datum_init_order @@ -13,31 +14,23 @@ GLOBAL_REAL(GLOB, /datum/controller/global_vars) GLOB = src var/datum/controller/exclude_these = new - gvars_datum_in_built_vars = list() - for(var/key in exclude_these.vars) - gvars_datum_in_built_vars += key - gvars_datum_in_built_vars += list( - "gvars_datum_protected_varlist", - "gvars_datum_in_built_vars", - "gvars_datum_init_order", - ) + // I know this is dumb but the nested vars list hangs a ref to the datum. This fixes that + // I have an issue report open, lummox has not responded. It might be a FeaTuRE + // Sooo we gotta be dumb + var/list/controller_vars = exclude_these.vars.Copy() + controller_vars["vars"] = null + gvars_datum_in_built_vars = controller_vars + list(NAMEOF(src, gvars_datum_protected_varlist), NAMEOF(src, gvars_datum_in_built_vars), NAMEOF(src, gvars_datum_init_order)) + + QDEL_IN(exclude_these, 0) //signal logging isn't ready log_world("[vars.len - gvars_datum_in_built_vars.len] global variables") - Initialize(exclude_these) + Initialize() /datum/controller/global_vars/Destroy(force) - stack_trace("There was an attempt to qdel the global vars holder!") - if(!force) - return QDEL_HINT_LETMELIVE - - QDEL_NULL(statclick) - gvars_datum_protected_varlist.Cut() - gvars_datum_in_built_vars.Cut() - - GLOB = null - - return ..() + // This is done to prevent an exploit where admins can get around protected vars + SHOULD_CALL_PARENT(FALSE) + return QDEL_HINT_IWILLGC /datum/controller/global_vars/stat_entry() return "Edit" @@ -47,23 +40,29 @@ GLOBAL_REAL(GLOB, /datum/controller/global_vars) return FALSE return ..() -/datum/controller/global_vars/Initialize(exclude_these) +// requires vv pr +// /datum/controller/global_vars/vv_get_var(var_name) +// switch(var_name) +// if (NAMEOF(src, vars)) +// return debug_variable(var_name, list(), 0, src) +// return debug_variable(var_name, vars[var_name], 0, src, display_flags = VV_ALWAYS_CONTRACT_LIST) + +/datum/controller/global_vars/Initialize() gvars_datum_init_order = list() - gvars_datum_protected_varlist = list("gvars_datum_protected_varlist") + gvars_datum_protected_varlist = list(NAMEOF(src, gvars_datum_protected_varlist) = TRUE) + var/list/global_procs = typesof(/datum/controller/global_vars/proc) + var/expected_len = vars.len - gvars_datum_in_built_vars.len + if(global_procs.len != expected_len) + warning("Unable to detect all global initialization procs! Expected [expected_len] got [global_procs.len]!") + if(global_procs.len) + var/list/expected_global_procs = vars - gvars_datum_in_built_vars + for(var/I in global_procs) + expected_global_procs -= replacetext("[I]", "InitGlobal", "") + log_world("Missing procs: [expected_global_procs.Join(", ")]") - //? See https://github.com/tgstation/tgstation/issues/26954 - for(var/I in typesof(/datum/controller/global_vars/proc)) - var/CLEANBOT_RETURNS = "[I]" - pass(CLEANBOT_RETURNS) - - for(var/I in (vars - gvars_datum_in_built_vars)) + for(var/I in global_procs) var/start_tick = world.time - var/start_time = REALTIMEOFDAY - call(src, "InitGlobal[I]")() + call(src, I)() var/end_tick = world.time - var/end_time = REALTIMEOFDAY if(end_tick - start_tick) - stack_trace("Global [I] slept during initialization!") - if((end_time - start_time) > 0.1 SECONDS) - log_world("global-variable-lag-detection: - [I] took [end_time - start_time]ds to init.") - QDEL_NULL(exclude_these) + warning("Global [replacetext("[I]", "InitGlobal", "")] slept during initialization!") diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 0cc47944f75..66db476442e 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -209,7 +209,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new return MC_RESTART_RTN_SUCCESS /datum/controller/master/Recover() - var/msg = "## DEBUG: [time2text(world.timeofday)] MC restarted. Reports:\n" + var/msg = "## DEBUG: [time2text(world.timeofday, "DDD MMM DD hh:mm:ss YYYY", TIMEZONE_UTC)] MC restarted. Reports:\n" var/list/master_attributes = Master.vars var/list/filtered_variables = list( NAMEOF(src, name), diff --git a/code/controllers/subsystem/events.dm b/code/controllers/subsystem/events.dm index 7601802fa67..6c89d15f351 100644 --- a/code/controllers/subsystem/events.dm +++ b/code/controllers/subsystem/events.dm @@ -3,6 +3,8 @@ SUBSYSTEM_DEF(events) wait = 2 SECONDS init_order = INIT_ORDER_EVENTS + runlevels = RUNLEVEL_GAME + /// Current holidays var/list/holidays = list() @@ -132,10 +134,10 @@ SUBSYSTEM_DEF(events) if(!force && !CONFIG_GET(flag/allow_holidays)) return // Holiday stuff was not enabled in the config! - var/YY = text2num(time2text(world.timeofday, "YY")) // get the current year - var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month - var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day - var/DDD = time2text(world.timeofday, "DDD") // get the current weekday + var/YY = text2num(time2text(world.timeofday, "YY", world.timezone)) // get the current year + var/MM = text2num(time2text(world.timeofday, "MM", world.timezone)) // get the current month + var/DD = text2num(time2text(world.timeofday, "DD", world.timezone)) // get the current day + var/DDD = time2text(world.timeofday, "DDD", world.timezone) // get the current weekday var/W = weekdayofthemonth() // is this the first monday? second? etc. for(var/H in subtypesof(/datum/holiday)) diff --git a/code/controllers/subsystem/game_master.dm b/code/controllers/subsystem/game_master.dm index 6167c617639..b324feb3cb0 100644 --- a/code/controllers/subsystem/game_master.dm +++ b/code/controllers/subsystem/game_master.dm @@ -58,7 +58,7 @@ SUBSYSTEM_DEF(gamemaster) if(ignore_time_restrictions) return TRUE // Last minute antagging is bad for humans to do, so the GM will respect the start and end of the round. - var/mills = round_duration_in_ds + var/mills = STATION_TIME_PASSED() var/mins = round((mills % 36000) / 600) var/hours = round(mills / 36000) diff --git a/code/controllers/subsystem/ipintel.dm b/code/controllers/subsystem/ipintel.dm index e0f1dfd5cbb..7fc711f8a83 100644 --- a/code/controllers/subsystem/ipintel.dm +++ b/code/controllers/subsystem/ipintel.dm @@ -138,11 +138,11 @@ SUBSYSTEM_DEF(ipintel) /datum/controller/subsystem/ipintel/proc/ipintel_cache_fetch_impl(address) PRIVATE_PROC(TRUE) - var/datum/db_query/fetch = SSdbcore.NewQuery( - "SELECT date, intel, TIMESTAMPDIFF(MINUTE,date,NOW()) FROM [DB_PREFIX_TABLE_NAME("ipintel")] WHERE ip = INET_ATON(:ip)", - list( - "ip" = address, - ) + var/datum/db_query/fetch = SSdbcore.NewQuery({" + SELECT date, intel, TIMESTAMPDIFF(MINUTE,date,NOW()) + FROM [DB_PREFIX_TABLE_NAME("ipintel")] + WHERE ip = INET_ATON(:ip) + "}, list("ip" = address) ) fetch.Execute() if(fetch.NextRow()) @@ -195,7 +195,7 @@ SUBSYSTEM_DEF(ipintel) var/cached_realtime /datum/ipintel/New() - cached_timestamp = time_stamp() + cached_timestamp = ISOtime() cached_realtime = world.realtime /datum/ipintel/proc/is_valid() diff --git a/code/controllers/subsystem/nightshift.dm b/code/controllers/subsystem/nightshift.dm index 3cfafc71015..19857f49643 100644 --- a/code/controllers/subsystem/nightshift.dm +++ b/code/controllers/subsystem/nightshift.dm @@ -28,7 +28,7 @@ SUBSYSTEM_DEF(nightshift) if(resumed) update_nightshift(resumed = TRUE) return - if(round_duration_in_ds < nightshift_first_check) + if(STATION_TIME_PASSED() < nightshift_first_check) return check_nightshift() @@ -50,7 +50,7 @@ SUBSYSTEM_DEF(nightshift) //nightshift_end_time = 207,000 deciseconds after midnight (7:30 AM) //if time is greater than start time (between 7:30pm and 11:59pm) OR less than end time (between midnight and 7:29am) it should turn on. //If time rolls over to midnight, station_time will keep incrementing so there is no need for a special case. - var/time = station_time_in_ds + var/time = station_time_in_ds var/night_time = (time > nightshift_start_time) || (time < nightshift_end_time) if(high_security_mode != emergency) @@ -82,7 +82,7 @@ SUBSYSTEM_DEF(nightshift) currentrun -= APC if (APC.area && (APC.z in (LEGACY_MAP_DATUM).station_levels)) APC.set_nightshift(nightshift_active && (APC.area.nightshift_level & nightshift_level), TRUE) - + //TODO: redo below logic: as-is, it does not allow the nightshift subsystem to actually finish processing //if(MC_TICK_CHECK && !forced) // subsystem will be in state SS_IDLE if forced by an admin diff --git a/code/controllers/subsystem/statpanel.dm b/code/controllers/subsystem/statpanel.dm index 06f86e757ba..4ae9af4d50c 100644 --- a/code/controllers/subsystem/statpanel.dm +++ b/code/controllers/subsystem/statpanel.dm @@ -114,10 +114,11 @@ SUBSYSTEM_DEF(statpanels) . = list() //L += SSmapping.stat_map_name INJECT_STATPANEL_DATA_ENTRY(., "Round ID", "[GLOB?.round_id || "ERROR"]") + INJECT_STATPANEL_DATA_ENTRY(., "Server Time", "[time2text(world.timeofday, "YYYY-MM-DD hh:mm:ss", world.timezone)]") + INJECT_STATPANEL_DATA_ENTRY(., "Round Time", ROUND_TIME()) // VIRGO START INJECT_STATPANEL_DATA_ENTRY(., "Station Time", stationtime2text()) INJECT_STATPANEL_DATA_ENTRY(., "Station Date", stationdate2text()) - INJECT_STATPANEL_DATA_ENTRY(., "Round Duration", roundduration2text()) // VIRGO END INJECT_STATPANEL_DATA_ENTRY(., "Time dilation", SStime_track.stat_time_text) //L += SSshuttle.emergency_shuttle_stat_text diff --git a/code/controllers/subsystem/tgui.dm b/code/controllers/subsystem/tgui.dm index dfbbc6dbe64..6a980e4e98e 100644 --- a/code/controllers/subsystem/tgui.dm +++ b/code/controllers/subsystem/tgui.dm @@ -37,7 +37,7 @@ SUBSYSTEM_DEF(tgui) ntos_error = "" basehtml = replacetextEx(basehtml, "", ntos_error) - basehtml = replacetextEx(basehtml, "", "Nanotrasen (c) 2525-[GLOB.startup_year + 544]") + basehtml = replacetextEx(basehtml, "", "Nanotrasen (c) 2525-[CURRENT_STATION_YEAR]") /datum/controller/subsystem/tgui/Shutdown() close_all_uis() diff --git a/code/controllers/subsystem/time_keep.dm b/code/controllers/subsystem/time_keep.dm index e8feca68b1b..339c9c81d2d 100644 --- a/code/controllers/subsystem/time_keep.dm +++ b/code/controllers/subsystem/time_keep.dm @@ -16,7 +16,7 @@ SUBSYSTEM_DEF(time_keep) name = "Time Keeping" subsystem_flags = SS_NO_FIRE | SS_NO_INIT - var/galactic_year_offset = 544 + var/galactic_year_offset = STATION_YEAR_OFFSET var/static/galactic_time_offset = null var/list/possible_roundstart_offsets = list( @@ -54,13 +54,13 @@ SUBSYSTEM_DEF(time_keep) // in date rendering code. MIDNIGHT_ROLLOVER_CHECK_STANDALONE if(manual_offset) - var/use_time = galactic_time_offset + manual_offset - (global.midnight_rollovers DAYS) - return "[num2text(time2text(use_time, "YYYY")) + galactic_year_offset]-[time2text(use_time, "MM-DD", 0)]" + var/use_time = galactic_time_offset + manual_offset - (GLOB.midnight_rollovers DAYS) + return "[num2text(time2text(use_time, "YYYY", NO_TIMEZONE)) + galactic_year_offset]-[time2text(use_time, "MM-DD", NO_TIMEZONE)]" else - var/use_time = galactic_time_offset + world.time - (global.midnight_rollovers DAYS) + var/use_time = galactic_time_offset + world.time - (GLOB.midnight_rollovers DAYS) var/rollovers = floor(use_time / (1 DAY)) if(rollovers != cached_galactic_date_rollovers) - cached_galactic_date = "[text2num(time2text(use_time, "YYYY")) + galactic_year_offset]-[time2text(use_time, "MM-DD", 0)]" + cached_galactic_date = "[text2num(time2text(use_time, "YYYY", NO_TIMEZONE)) + galactic_year_offset]-[time2text(use_time, "MM-DD", NO_TIMEZONE)]" return cached_galactic_date /** @@ -70,10 +70,10 @@ SUBSYSTEM_DEF(time_keep) /datum/controller/subsystem/time_keep/proc/render_galactic_time(manual_offset) if(manual_offset) // TODO: slow path that doesn't cache - return time2text(galactic_time_offset + manual_offset, "hh:mm:ss", 0) + return time2text(galactic_time_offset + manual_offset, "hh:mm:ss", NO_TIMEZONE) else // TODO: cache fast path - return time2text(galactic_time_offset + world.time, "hh:mm:ss", 0) + return time2text(galactic_time_offset + world.time, "hh:mm:ss", NO_TIMEZONE) /** * Returns time from start of current round. Default offset is current time. @@ -82,10 +82,10 @@ SUBSYSTEM_DEF(time_keep) /datum/controller/subsystem/time_keep/proc/render_galactic_time_short(manual_offset) if(manual_offset) // TODO: slow path that doesn't cache - return time2text(galactic_time_offset + manual_offset, "hh:mm", 0) + return time2text(galactic_time_offset + manual_offset, "hh:mm", NO_TIMEZONE) else // TODO: cache fast path - return time2text(galactic_time_offset + world.time, "hh:mm", 0) + return time2text(galactic_time_offset + world.time, "hh:mm", NO_TIMEZONE) /** * * **warning**: this proc return should never be used to render date. BYOND is weird. diff --git a/code/controllers/subsystem/transfer.dm b/code/controllers/subsystem/transfer.dm index 9820da271ec..f12ee411839 100644 --- a/code/controllers/subsystem/transfer.dm +++ b/code/controllers/subsystem/transfer.dm @@ -17,10 +17,10 @@ SUBSYSTEM_DEF(transfer) /datum/controller/subsystem/transfer/fire(resumed) currenttick = currenttick + 1 - if(round_duration_in_ds >= shift_hard_end - 1 MINUTE) + if(STATION_TIME_PASSED() >= shift_hard_end - 1 MINUTE) init_shift_change(null, 1) shift_hard_end = timerbuffer + config_legacy.vote_autotransfer_interval //If shuttle somehow gets recalled, let's force it to call again next time a vote would occur. timerbuffer = timerbuffer + config_legacy.vote_autotransfer_interval //Just to make sure a vote doesn't occur immediately afterwords. - else if(round_duration_in_ds >= timerbuffer - 1 MINUTE) + else if(STATION_TIME_PASSED() >= timerbuffer - 1 MINUTE) SSvote.autotransfer() timerbuffer = timerbuffer + config_legacy.vote_autotransfer_interval diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index f5b71ab2dc0..f7452cb0e35 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -232,6 +232,7 @@ var/global/list/additional_antag_types = list() if(SSemergencyshuttle && auto_recall_shuttle) SSemergencyshuttle.auto_recall = 1 + // yikes! this should be NOW() feedback_set_details("round_start","[time2text(world.realtime)]") if(SSticker && SSticker.mode) feedback_set_details("game_mode","[SSticker.mode]") diff --git a/code/game/gamemodes/game_mode_latespawn.dm b/code/game/gamemodes/game_mode_latespawn.dm index 28ec49d21f8..56d6c55a007 100644 --- a/code/game/gamemodes/game_mode_latespawn.dm +++ b/code/game/gamemodes/game_mode_latespawn.dm @@ -34,7 +34,7 @@ if(SSemergencyshuttle.shuttle && (SSemergencyshuttle.shuttle.moving_status == SHUTTLE_WARMUP || SSemergencyshuttle.shuttle.moving_status == SHUTTLE_INTRANSIT)) return // Don't do anything if the shuttle's coming. - var/mills = round_duration_in_ds + var/mills = STATION_TIME_PASSED() var/mins = round((mills % 36000) / 600) var/hours = round(mills / 36000) diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index d8c7dfee320..7361b211a6c 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -79,7 +79,7 @@ newMsg.body = msg newMsg.time_stamp = "[stationtime2text()]" newMsg.is_admin_message = adminMessage - newMsg.post_time = round_duration_in_ds // Should be almost universally unique + newMsg.post_time = STATION_TIME_PASSED() // Should be almost universally unique if(message_type) newMsg.message_type = message_type if(photo) diff --git a/code/game/objects/items/devices/PDA/radio.dm b/code/game/objects/items/devices/PDA/radio.dm index 284a382cdba..b94908c879b 100644 --- a/code/game/objects/items/devices/PDA/radio.dm +++ b/code/game/objects/items/devices/PDA/radio.dm @@ -143,7 +143,7 @@ return last_transmission = world.time - var/time = time2text(world.realtime,"hh:mm:ss") + var/time = time2text(world.realtime,"hh:mm:ss", NO_TIMEZONE) var/turf/T = get_turf(src) lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]") diff --git a/code/game/objects/items/devices/tape_recorder/tape_recorder.dm b/code/game/objects/items/devices/tape_recorder/tape_recorder.dm index 8638bc6a00a..d663eb94222 100644 --- a/code/game/objects/items/devices/tape_recorder/tape_recorder.dm +++ b/code/game/objects/items/devices/tape_recorder/tape_recorder.dm @@ -422,15 +422,15 @@ switch(opcode) if(/datum/cassette_opcode/next_is_audible_emote) // constructed += "*[name]* --- [msg] ---" - constructed += "[time2text(current_time, "mm:ss")] (unrecognizable / not speech)" + constructed += "[time2text(current_time, "mm:ss", NO_TIMEZONE)] (unrecognizable / not speech)" if(/datum/cassette_opcode/next_is_direct_broadcast) constructed += "
[time2text(current_time, "mm:ss")]
[msg]
" else lang = head[3] if(lang.id == LANGUAGE_ID_COMMON) - constructed += "[time2text(current_time, "mm:ss")] [name]: [msg]" + constructed += "[time2text(current_time, "mm:ss", NO_TIMEZONE)] [name]: [msg]" else - constructed += "[time2text(current_time, "mm:ss")] [name]: (unknown language)" + constructed += "[time2text(current_time, "mm:ss", NO_TIMEZONE)] [name]: (unknown language)" P.info = constructed.Join("
") P.name = "Transcript" print_cooldown() diff --git a/code/game/objects/items/devices/taperecorder.dm b/code/game/objects/items/devices/taperecorder.dm index 4ef1aa9807c..6a21b6379aa 100644 --- a/code/game/objects/items/devices/taperecorder.dm +++ b/code/game/objects/items/devices/taperecorder.dm @@ -410,13 +410,13 @@ /obj/item/cassette_tape/proc/record_speech(text) timestamp += used_capacity - storedinfo += "\[[time2text(used_capacity*10,"mm:ss")]\] [text]" + storedinfo += "\[[time2text(used_capacity*10,"mm:ss", NO_TIMEZONE)]\] [text]" //shows up on the printed transcript as (Unrecognized sound) /obj/item/cassette_tape/proc/record_noise(text) timestamp += used_capacity - storedinfo += "*\[[time2text(used_capacity*10,"mm:ss")]\] [text]" + storedinfo += "*\[[time2text(used_capacity*10,"mm:ss", NO_TIMEZONE)]\] [text]" /obj/item/cassette_tape/attackby(obj/item/I, mob/user, params) diff --git a/code/game/statistics.dm b/code/game/statistics.dm index 2a67a26b905..8c415331b2e 100644 --- a/code/game/statistics.dm +++ b/code/game/statistics.dm @@ -41,7 +41,6 @@ laname = sanitizeSQL(H.lastattacker:real_name) lakey = sanitizeSQL(H.lastattacker:key) - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") var/coord = "[H.x], [H.y], [H.z]" if(!SSdbcore.Connect()) @@ -49,14 +48,13 @@ else var/datum/db_query/query = SSdbcore.NewQuery( "INSERT INTO [DB_PREFIX_TABLE_NAME("death")] (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES \ - (:name, :key, :job, :special, :pod, :time, :laname, :lakey, :gender, :bruteloss, :fireloss, :brainloss, :oxyloss, :coord)", + (:name, :key, :job, :special, :pod, NOW(), :laname, :lakey, :gender, :bruteloss, :fireloss, :brainloss, :oxyloss, :coord)", list( "name" = sqlname, "key" = sqlkey, "job" = sqljob, "special" = sqlspecial, "pod" = sqlpod, - "time" = sqltime, "laname" = laname, "lakey" = lakey,, "gender" = H.gender, @@ -91,7 +89,6 @@ if(H.lastattacker) laname = sanitizeSQL(H.lastattacker:real_name) lakey = sanitizeSQL(H.lastattacker:key) - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") var/coord = "[H.x], [H.y], [H.z]" if(!SSdbcore.Connect()) @@ -99,14 +96,13 @@ else var/datum/db_query/query = SSdbcore.NewQuery( "INSERT INTO [DB_PREFIX_TABLE_NAME("death")] (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES \ - (:name, :key, :job, :special, :pod, :time, :laname, :lakey, :geender, :bruteloss, :fireloss, :brainloss, :oxyloss, :coord)", + (:name, :key, :job, :special, :pod, NOW(), :laname, :lakey, :geender, :bruteloss, :fireloss, :brainloss, :oxyloss, :coord)", list( "name" = sqlname, "key" = sqlkey, "job" = sqljob, "special" = sqlspecial, "pod" = sqlpod, - "time" = sqltime, "laname" = laname, "lakey" = lakey,, "gender" = H.gender, diff --git a/code/game/world.dm b/code/game/world.dm index ec614d6487a..f51f0183a61 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -31,6 +31,38 @@ GLOBAL_LIST(topic_status_cache) loop_checks = FALSE #endif +/** + * WORLD INITIALIZATION + * THIS IS THE INIT ORDER: + * + * BYOND => + * - (secret init native) => + * - world.init_byond_tracy() + * - (Start native profiling) + * - Master => <- gets initialized as soon as we enter the master = new block + * - config *unloaded + * - (all subsystems) PreInit() + * - GLOB => + * - make_datum_reference_lists() + * - (/static variable inits, reverse declaration order) + * - (all pre-mapped atoms) /atom/New() + * - config.Load() + * - world.InitTgs() => + * - TgsNew() *may sleep + * - GLOB.rev_data.load_tgs_info() + * - world.ConfigLoaded() => + * - SSdbcore.InitializeRound() + * - world.SetupLogs() + * - load_admins() + * - ... + * - Master.Initialize() => + * - (all subsystems) Initialize() + * - Master.StartProcessing() => + * - Master.Loop() => + * - Failsafe + * - world.RunUnattendedFunctions() + */ + /** * World creation * @@ -62,43 +94,29 @@ GLOBAL_LIST(topic_status_cache) #warn USE_BYOND_TRACY is enabled init_byond_tracy() #endif - log_world("World loaded at [TIME_STAMP("hh:mm:ss", FALSE)]!") + log_world("World loaded at [time_stamp()]!") var/tempfile = "data/logs/config_error.[GUID()].log" //temporary file used to record errors with loading config, moved to log directory once logging is set // citadel edit: world runtime log removed due to world.log shunt doing that for us GLOB.config_error_log = GLOB.world_href_log = GLOB.world_map_error_log = GLOB.world_attack_log = GLOB.world_game_log = tempfile world.Profile(PROFILE_START) - make_datum_reference_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once) setupgenetics() GLOB.revdata = new + // First possible sleep() InitTgs() // load configuration load_legacy_configuration() config.Load(params[OVERRIDE_CONFIG_DIRECTORY_PARAMETER]) - config.update_world_viewsize() //! Since world.view is immutable, we load it here. - Configuration.Initialize() - //SetupLogs depends on the RoundID, so lets check - //DB schema and set RoundID if we can - SSdbcore.CheckSchemaVersion() - SSdbcore.SetRoundID() - SetupLogs() + ConfigLoaded() // shunt redirected world log from Master's init back into world log proper, now that logging has been set up. shunt_redirected_log() - if(config && config_legacy.server_name != null && config_legacy.server_suffix && world.port > 0) - // dumb and hardcoded but I don't care~ - config_legacy.server_name += " #[(world.port % 1000) / 100]" - - // TODO - Figure out what this is. Can you assign to world.log? - // if(config && Configuration.get_entry(/datum/toml_config_entry/backend/logging/toggles/runtime)) - // log = file("data/logs/runtime/[time2text(world.realtime,"YYYY-MM-DD-(hh-mm-ss)")]-runtime.log") - callHook("startup") //Emergency Fix load_mods() @@ -106,6 +124,9 @@ GLOBAL_LIST(topic_status_cache) src.update_status() + // Someone make it so this call isn't necessary + make_datum_reference_lists() + . = ..() // Create frame types. @@ -114,25 +135,53 @@ GLOBAL_LIST(topic_status_cache) // Create robolimbs for chargen. populate_robolimb_list() - if(fexists(RESTART_COUNTER_PATH)) - GLOB.restart_counter = text2num(trim(file2text(RESTART_COUNTER_PATH))) - fdel(RESTART_COUNTER_PATH) - if(NO_INIT_PARAMETER in params) return Master.Initialize(10, FALSE, TRUE) - #ifdef UNIT_TESTS - HandleTestRun() - #endif - if(config_legacy.ToRban) - addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(ToRban_autoupdate)), 5 MINUTES) + RunUnattendedFunctions() +/// Initializes TGS and loads the returned revising info into GLOB.revdata /world/proc/InitTgs() TgsNew(new /datum/tgs_event_handler/impl, TGS_SECURITY_TRUSTED) GLOB.revdata.load_tgs_info() +/// Runs after config is loaded but before Master is initialized +/world/proc/ConfigLoaded() + config.update_world_viewsize() //! Since world.view is immutable, we load it here. + Configuration.Initialize() + + // pretend these 2 ssdbcore funcs are InitializeRound() + SSdbcore.CheckSchemaVersion() + SSdbcore.SetRoundID() + + SetupLogs() + + load_admins() + + // legacy shit + if(config && config_legacy.server_name != null && config_legacy.server_suffix && world.port > 0) + // dumb and hardcoded but I don't care~ + config_legacy.server_name += " #[(world.port % 1000) / 100]" + + if(fexists(RESTART_COUNTER_PATH)) + GLOB.restart_counter = text2num(trim(file2text(RESTART_COUNTER_PATH))) + fdel(RESTART_COUNTER_PATH) + +/// Runs after the call to Master.Initialize, but before the delay kicks in. Used to turn the world execution into some single function then exit +/world/proc/RunUnattendedFunctions() + #ifdef UNIT_TESTS + HandleTestRun() + #endif + + #ifdef PERFORMANCE_TESTS + queue_performance_tests() + #endif + // this is horrible + if(config_legacy.ToRban) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(ToRban_autoupdate)), 5 MINUTES) + /world/proc/HandleTestRun() //trigger things to run the whole process Master.sleep_offline_after_initializations = FALSE @@ -146,16 +195,35 @@ GLOBAL_LIST(topic_status_cache) #endif SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(_addtimer), cb, 10 SECONDS)) +/world/proc/queue_performance_tests() + //trigger things to run the whole process + Master.sleep_offline_after_initializations = FALSE + SSticker.start_immediately = TRUE + var/datum/callback/cb = CALLBACK(src, PROC_REF(run_performance_tests)) + SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(_addtimer), cb, 10 SECONDS)) + +/// Stub proc intended to be filled with code that does some test, profiles it, and logs that test. +/// Intended to be used with line by line macros, but you should live your truth +/world/proc/run_performance_tests() + // In case we do somethin that could otherwise end the round + SSticker.delay_end = TRUE + // Your code goes here + + // Logging goes here + // (sample line by line) stat_tracking_export_to_csv_later("file_name.csv", GLOB.cost_list, GLOB.count_list) + SSticker.delay_end = FALSE + shutdown() + /world/proc/SetupLogs() var/override_dir = params[OVERRIDE_LOG_DIRECTORY_PARAMETER] if(!override_dir) var/realtime = world.realtime - var/texttime = time2text(realtime, "YYYY/MM/DD") + var/texttime = time2text(realtime, "YYYY/MM/DD", TIMEZONE_UTC) GLOB.log_directory = "data/logs/[texttime]/round-" if(GLOB.round_id) GLOB.log_directory += "[GLOB.round_id]" else - var/timestamp = replacetext(TIME_STAMP("hh:mm:ss", FALSE), ":", ".") + var/timestamp = replacetext(time_stamp(), ":", ".") GLOB.log_directory += "[timestamp]" else GLOB.log_directory = "data/logs/[override_dir]" @@ -187,8 +255,9 @@ GLOBAL_LIST(topic_status_cache) start_log(GLOB.tgui_log) start_log(GLOB.subsystem_log) - var/latest_changelog = file("[global.config.directory]/../html/changelogs/archive/" + time2text(world.timeofday, "YYYY-MM") + ".yml") + var/latest_changelog = file("[global.config.directory]/../html/changelogs/archive/" + time2text(world.timeofday, "YYYY-MM", TIMEZONE_UTC) + ".yml") GLOB.changelog_hash = fexists(latest_changelog) ? md5(latest_changelog) : 0 //for telling if the changelog has changed recently + if(fexists(GLOB.config_error_log)) fcopy(GLOB.config_error_log, "[GLOB.log_directory]/config_error.log") fdel(GLOB.config_error_log) @@ -203,6 +272,13 @@ GLOBAL_LIST(topic_status_cache) global.event_logger.setup_logger(GLOB.log_directory) +#ifndef USE_CUSTOM_ERROR_HANDLER + world.log = file("[GLOB.log_directory]/dd.log") +#else + if (TgsAvailable()) // why + world.log = file("[GLOB.log_directory]/dd.log") //not all runtimes trigger world/Error, so this is the only way to ensure we can see all of them. +#endif + /world/proc/_setup_logs_boilerplate() /world/Topic(T, addr, master, key) @@ -522,6 +598,7 @@ GLOBAL_LIST(topic_status_cache) //* Log Shunter *// //! LOG SHUNTER STUFF, LEAVE THIS ALONE +// well maybe have a genisis proc so master is 100% initialized (with globals) which makes it impossible to not exist when we are new()ing stuff /** * so it turns out that if GLOB init or something before world.log redirect runtimes we have no way of catching it in CI * which is really bad?? because we kind of need it?? diff --git a/code/global.dm b/code/global.dm index 2292e958de6..41da908e6f4 100644 --- a/code/global.dm +++ b/code/global.dm @@ -20,7 +20,7 @@ var/list/hit_appends = list("-OOF", "-ACK", "-UGH", "-HRNK", "-HURGH", "-GLORF") // var/const/star_name = "Vir" // var/const/starsys_name = "Vir" var/const/game_version = "Citadel Station RP" -var/game_year = (text2num(time2text(world.realtime, "YYYY")) + 544) +var/game_year = (text2num(time2text(world.realtime, "YYYY", 0)) + 544) var/master_mode = "extended" // "extended" var/secret_force_mode = "secret" // if this is anything but "secret", the secret rotation will forceably choose this mode. diff --git a/code/modules/client/data/player_data.dm b/code/modules/client/data/player_data.dm index 591ed3bb33b..787a6450e59 100644 --- a/code/modules/client/data/player_data.dm +++ b/code/modules/client/data/player_data.dm @@ -146,9 +146,10 @@ GLOBAL_LIST_EMPTY(player_data) player_misc = list() var/datum/db_query/insert if(migrate_firstseen) - insert = SSdbcore.ExecuteQuery( - "INSERT INTO [DB_PREFIX_TABLE_NAME("player")] (flags, firstseen, lastseen, misc) VALUES (:flags, :fs, Now(), :misc)", - list( + insert = SSdbcore.ExecuteQuery({" + INSERT INTO [DB_PREFIX_TABLE_NAME("player")] (`flags`, `firstseen`, `lastseen`, `misc`) + VALUES (:flags, :fs, NOW(), :misc) + "}, list( "flags" = player_flags, "fs" = migrate_firstseen, "misc" = safe_json_encode(player_misc), @@ -156,14 +157,15 @@ GLOBAL_LIST_EMPTY(player_data) ) player_first_seen = migrate_firstseen else - insert = SSdbcore.ExecuteQuery( - "INSERT INTO [DB_PREFIX_TABLE_NAME("player")] (flags, firstseen, lastseen, misc) VALUES (:flags, Now(), Now(), :misc)", - list( + insert = SSdbcore.ExecuteQuery({" + INSERT INTO [DB_PREFIX_TABLE_NAME("player")] (`flags`, `firstseen`, `lastseen`, `misc`) + VALUES (:flags, NOW(), NOW(), :misc) + "}, list( "flags" = player_flags, "misc" = safe_json_encode(player_misc), ) ) - player_first_seen = time_stamp() + player_first_seen = ISOtime() // pretend i am Now() var/insert_id = insert.last_insert_id if(istext(insert_id)) insert_id = text2num(insert_id) diff --git a/code/modules/client/onboarding/age_verification.dm b/code/modules/client/onboarding/age_verification.dm index 43dbc2a52fb..16508bee168 100644 --- a/code/modules/client/onboarding/age_verification.dm +++ b/code/modules/client/onboarding/age_verification.dm @@ -51,7 +51,7 @@ dat += "" dat += "" dat += "