Skip to content

Commit

Permalink
Merge branch 'master' into project/apollo_controller
Browse files Browse the repository at this point in the history
  • Loading branch information
realforest2001 committed Jul 2, 2023
2 parents 271d2c5 + 44fd728 commit 263e889
Show file tree
Hide file tree
Showing 65 changed files with 780 additions and 219 deletions.
6 changes: 3 additions & 3 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,10 @@ These are the few directives we have for project maintainers.
- Try to get secondary maintainer approval before merging if you are able to.
- PRs with empty commits intended to generate a changelog.
- Do not merge PRs that contain content from the [banned content list](./CONTRIBUTING.md#banned-content).
- Do not merge PRs that contain balance changes without GA approval. Exceptions include:
- Any PR that has been un-reviewed by a GA for 7 days.
- Do not merge PRs that contain balance changes without Maintainer Manager approval. Exceptions include:
- Any PR that has been un-reviewed by a Maintainer Manager for 7 days.
- Do not remove the DNM label that another Maintainer has applied. Exceptions include:
- GAs removing a DNM label placed by a Maintainer for Balance/Design reasons
- Maintainer Managers removing a DNM label placed by a Maintainer for Balance/Design reasons

These are not steadfast rules as maintainers are expected to use their best judgement when operating.

Expand Down
4 changes: 3 additions & 1 deletion .github/guides/STANDARDS.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ While we normally encourage (and in some cases, even require) bringing out of da
This is a simple one - as we will eventually move to 515, we will need to ditch this kind of callback. So please don't add any new ones. Make our lives easier.

### PROC_REF Macros
When referencing procs in RegisterSignal, Callback and other procs you should use PROC_REF,TYPE_PROC_REF and GLOBAL_PROC_REF macros.
When referencing procs in RegisterSignal, Callback and other procs you should use PROC_REF, TYPE_PROC_REF and GLOBAL_PROC_REF macros.
They ensure compilation fails if the reffered to procs change names or get removed.
The macro to be used depends on how the proc you're in relates to the proc you want to use:

Expand Down Expand Up @@ -168,6 +168,8 @@ This is a simple one - as we will eventually move to 515, we will need to ditch
addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(funny)), 100))
```

Note that the same rules go for verbs too! We have VERB_REF() and TYPE_VERB_REF() as you need it in these same cases. GLOBAL_VERB_REF() isn't a thing however, as verbs are not global.

### Signal Handlers

All procs that are registered to listen for signals using `RegisterSignal()` must contain at the start of the proc `SIGNAL_HANDLER` eg;
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/generate_documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
run: |
~/dmdoc
touch dmdoc/.nojekyll
echo codedocs.tgstation13.org > dmdoc/CNAME
echo docs.cm-ss13.com > dmdoc/CNAME
- name: Deploy
uses: JamesIves/[email protected]
with:
Expand Down
1 change: 1 addition & 0 deletions code/__DEFINES/conflict.dm
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@
//OB timings
#define OB_TRAVEL_TIMING 12 SECONDS
#define OB_CRASHING_DOWN 1 SECONDS
#define OB_CLUSTER_DURATION 45 SECONDS
//=================================================

//Health of various items
Expand Down
12 changes: 9 additions & 3 deletions code/__DEFINES/subsystems.dm
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@
#define SS_INIT_INPUT 85
#define SS_INIT_FAIL_TO_TOPIC 84
#define SS_INIT_TOPIC 83
#define SS_INIT_RUST 26
#define SS_INIT_RUST 30
#define SS_INIT_INFLUXDRIVER 28
#define SS_INIT_SUPPLY_SHUTTLE 25
#define SS_INIT_GARBAGE 24
#define SS_INIT_EVENTS 23.5
Expand All @@ -133,7 +134,9 @@
#define SS_INIT_MORE_INIT 16
#define SS_INIT_AIR 15
#define SS_INIT_TELEPORTER 13
#define SS_INIT_LIGHTING 12
#define SS_INIT_INFLUXMCSTATS 12
#define SS_INIT_INFLUXSTATS 11
#define SS_INIT_LIGHTING 10
#define SS_INIT_DEFCON 9
#define SS_INIT_LAW 6
#define SS_INIT_FZ_TRANSITIONS 5
Expand Down Expand Up @@ -212,12 +215,15 @@
#define SS_PRIORITY_UNSPECIFIED 30
#define SS_PRIORITY_PROCESS 25
#define SS_PRIORITY_SOUNDSCAPE 24
#define SS_PRIORITY_INFLUXDRIVER 23
#define SS_PRIORITY_PAGER_STATUS 22
#define SS_PRIORITY_LIGHTING 20
#define SS_PRIORITY_TRACKING 19
#define SS_PRIORITY_DATABASE 15
#define SS_PRIORITY_MINIMAPS 11
#define SS_PRIORITY_PING 10
#define SS_PRIORITY_DATABASE 15
#define SS_PRIORITY_INFLUXMCSTATS 9
#define SS_PRIORITY_INFLUXSTATS 8
#define SS_PRIORITY_PLAYTIME 5
#define SS_PRIORITY_PERFLOGGING 4
#define SS_PRIORITY_CORPSESPAWNER 3
Expand Down
3 changes: 3 additions & 0 deletions code/__DEFINES/traits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@
//ie. naming a regulation tape "example" will become regulation tape (example)
#define TRAIT_ITEM_RENAME_SPECIAL "t_item_rename_special"

// This item can't be implanted into someone, regardless of the size of the item.
#define TRAIT_ITEM_NOT_IMPLANTABLE "t_item_not_implantable"

//-- structure traits --
// TABLE TRAITS
/// If the table is being flipped, prevent any changes that will mess with adjacency handling
Expand Down
2 changes: 1 addition & 1 deletion code/__DEFINES/weapon_stats.dm
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#define HUMAN_UNIVERSAL_DAMAGEMULT 1

#define RECOIL_BUILDUP_VIEWPUNCH_MULTIPLIER 0.1

#define BASE_VELOCITY_BONUS 0

#define PROJ_BASE_ACCURACY_MULT 0.01
#define PROJ_BASE_DAMAGE_MULT 0.01
Expand Down
5 changes: 5 additions & 0 deletions code/__HELPERS/text.dm
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,11 @@
t = "0[t]"
return t

/proc/pad_trailing(text, padding, size)
while (length(text) < size)
text = "[text][padding]"
return text

//Adds 'u' number of spaces ahead of the text 't'
/proc/add_lspace(t, u)
while(length(t) < u)
Expand Down
44 changes: 37 additions & 7 deletions code/_byond_version_compat.dm
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,49 @@
#define LIBCALL call_ext
#endif

// So we want to have compile time guarantees these procs exist on local type, unfortunately 515 killed the .proc/procname syntax so we have to use nameof()
// So we want to have compile time guarantees these methods exist on local type, unfortunately 515 killed the .proc/procname and .verb/verbname syntax so we have to use nameof()
// For the record: GLOBAL_VERB_REF would be useless as verbs can't be global.

#if DM_VERSION < 515
/// Call by name proc reference, checks if the proc exists on this type or as a global proc

/// Call by name proc references, checks if the proc exists on either this type or as a global proc.
#define PROC_REF(X) (.proc/##X)
/// Call by name proc reference, checks if the proc exists on given type or as a global proc
/// Call by name verb references, checks if the verb exists on either this type or as a global verb.
#define VERB_REF(X) (.verb/##X)

/// Call by name proc reference, checks if the proc exists on either the given type or as a global proc
#define TYPE_PROC_REF(TYPE, X) (##TYPE.proc/##X)
/// Call by name proc reference, checks if the proc is existing global proc
/// Call by name verb reference, checks if the verb exists on either the given type or as a global verb
#define TYPE_VERB_REF(TYPE, X) (##TYPE.verb/##X)

/// Call by name proc reference, checks if the proc is an existing global proc
#define GLOBAL_PROC_REF(X) (/proc/##X)

#else
/// Call by name proc reference, checks if the proc exists on this type or as a global proc

/// Call by name proc references, checks if the proc exists on either this type or as a global proc.
#define PROC_REF(X) (nameof(.proc/##X))
/// Call by name proc reference, checks if the proc exists on given type or as a global proc
/// Call by name verb references, checks if the verb exists on either this type or as a global verb.
#define VERB_REF(X) (nameof(.verb/##X))

/// Call by name proc reference, checks if the proc exists on either the given type or as a global proc
#define TYPE_PROC_REF(TYPE, X) (nameof(##TYPE.proc/##X))
/// Call by name proc reference, checks if the proc is existing global proc
/// Call by name verb reference, checks if the verb exists on either the given type or as a global verb
#define TYPE_VERB_REF(TYPE, X) (nameof(##TYPE.verb/##X))

/// Call by name proc reference, checks if the proc is an existing global proc
#define GLOBAL_PROC_REF(X) (/proc/##X)

#endif

#if (DM_VERSION == 515)
/// fcopy will crash on 515 linux if given a non-existant file, instead of returning 0 like on 514 linux or 515 windows
/// var case matches documentation for fcopy.
/world/proc/__fcopy(Src, Dst)
if (istext(Src) && !fexists(Src))
return 0
return fcopy(Src, Dst)

#define fcopy(Src, Dst) world.__fcopy(Src, Dst)

#endif
1 change: 1 addition & 0 deletions code/_globalvars/global_lists.dm
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ GLOBAL_LIST_EMPTY(GeneralFaxes) //Inter-machine faxes
GLOBAL_LIST_EMPTY(fax_contents) //List of fax contents to maintain it even if source paper is deleted

GLOBAL_LIST_EMPTY(failed_fultons) //A list of fultoned items which weren't collected and fell back down
GLOBAL_LIST_EMPTY(larva_burst_by_hive)

GLOBAL_LIST_INIT_TYPED(custom_huds_list, /datum/custom_hud, setup_all_huds())
GLOBAL_LIST_INIT_TYPED(custom_human_huds, /datum/custom_hud, setup_human_huds())
Expand Down
4 changes: 2 additions & 2 deletions code/_onclick/click.dm
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
return

face_atom(A)
if(mods["middle"])
if(mods["middle"])
return
// Special type of click.
if (is_mob_restrained())
Expand Down Expand Up @@ -334,7 +334,7 @@
if(prefs.adaptive_zoom)
INVOKE_ASYNC(src, PROC_REF(adaptive_zoom))
else if(prefs.auto_fit_viewport)
INVOKE_ASYNC(src, .verb/fit_viewport)
INVOKE_ASYNC(src, VERB_REF(fit_viewport))

/client/proc/get_adaptive_zoom_factor()
if(!prefs.adaptive_zoom)
Expand Down
19 changes: 19 additions & 0 deletions code/controllers/configuration/entries/general.dm
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,25 @@ This maintains a list of ip addresses that are able to bypass topic filtering.

/datum/config_entry/string/round_results_webhook_url

/// InfluxDB v2 Host to connect to for sending statistics (over HTTP API)
/datum/config_entry/string/influxdb_host
/// InfluxDB v2 Bucket to send staistics to
/datum/config_entry/string/influxdb_bucket
/// InfluxDB v2 Organization to access buckets of
/datum/config_entry/string/influxdb_org
/// InfluxDB v2 API Token to access the organization and bucket
/datum/config_entry/string/influxdb_token

/// How often to snapshot general game statistics to influxdb driver
/datum/config_entry/number/influxdb_stats_period
config_entry_value = 30
/// How often to snapshot MC statistics
/datum/config_entry/number/influxdb_mcstats_period
config_entry_value = 60
/// How often to send queued influxdb statistics
/datum/config_entry/number/influxdb_send_period
config_entry_value = 10

/// logs all timers in buckets on automatic bucket reset (Useful for timer debugging)
/datum/config_entry/flag/log_timers_on_bucket_reset

Expand Down
132 changes: 132 additions & 0 deletions code/controllers/subsystem/influxdriver.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/// Sends collected statistics to an influxdb v2 backend periodically
SUBSYSTEM_DEF(influxdriver)
name = "InfluxDB Driver"
wait = 10 SECONDS
init_order = SS_INIT_INFLUXDRIVER
priority = SS_PRIORITY_INFLUXDRIVER
runlevels = RUNLEVELS_DEFAULT|RUNLEVEL_LOBBY

var/list/send_queue = list()

/// Maximum amount of metric lines to send at most in one request
/// This is neccessary because sending a lot of metrics can get expensive
/// and drive the subsystem into overtime, but we can't split the work as it'd be even less efficient
var/max_batch = 150

/// Last timestamp in microseconds
var/timestamp_cache_realtime
/// Last tick time the timestamp was taken at
var/timestamp_cache_worldtime

/datum/controller/subsystem/influxdriver/Initialize()
var/period = text2num(CONFIG_GET(number/influxdb_send_period))
if(isnum(period))
wait = max(period * (1 SECONDS), 2 SECONDS)
return SS_INIT_SUCCESS

/datum/controller/subsystem/influxdriver/stat_entry(msg)
msg += "period=[wait] queue=[length(send_queue)]"
return ..()

/datum/controller/subsystem/influxdriver/proc/unix_timestamp_string() // pending change to rust-g
return RUSTG_CALL(RUST_G, "unix_timestamp")()

/datum/controller/subsystem/influxdriver/proc/update_timestamp()
PRIVATE_PROC(TRUE)
// We make only one request to rustg per game tick, so we cache the result per world.time
var/whole_timestamp = unix_timestamp_string() // Format "7129739474.4758981" - timestamp with up to 7-8 decimals
var/list/tsparts = splittext(whole_timestamp, ".")
var/fractional = copytext(pad_trailing(tsparts[2], "0", 6), 1, 7) // in microseconds
timestamp_cache_worldtime = world.time
timestamp_cache_realtime = "[tsparts[1]][fractional]"

/datum/controller/subsystem/influxdriver/fire(resumed)
var/maxlen = min(length(send_queue)+1, max_batch)
var/list/queue = send_queue.Copy(1, maxlen)
send_queue.Cut(1, maxlen)
flush_queue(queue)

/// Flushes measurements batch to InfluxDB backend
/datum/controller/subsystem/influxdriver/proc/flush_queue(list/queue)
PRIVATE_PROC(TRUE)

var/host = CONFIG_GET(string/influxdb_host)
var/token = CONFIG_GET(string/influxdb_token)
var/bucket = CONFIG_GET(string/influxdb_bucket)
var/org = CONFIG_GET(string/influxdb_org)

if(!host || !token || !bucket || !org)
can_fire = FALSE
return

if(!length(queue))
return // Nothing to do

var/url = "[host]/api/v2/write?org=[org]&bucket=[bucket]&precision=us" // microseconds
var/list/headers = list()
headers["Authorization"] = "Token [token]"
headers["Content-Type"] = "text/plain; charset=utf-8"
headers["Accept"] = "application/json"

var/datum/http_request/request = new
var/payload = ""
for(var/line in queue)
payload += "[line]\n"
request.prepare(RUSTG_HTTP_METHOD_POST, url, payload, headers)
request.begin_async()
// TODO possibly check back result of request later

/// Enqueues sending to InfluxDB Backend selected measurement values - round_id and timestamp are filled in automatically
/datum/controller/subsystem/influxdriver/proc/enqueue_stats(measurement, list/tags, list/fields)
. = FALSE
var/valid = FALSE
var/serialized = "[measurement],round_id=[GLOB.round_id]"
if(tags)
for(var/tag in tags)
var/serialized_tag = serialize_field(tag, tags[tag])
if(serialized_tag)
serialized += ",[serialized_tag]"
serialized += " "
var/comma = ""
for(var/field in fields)
var/serialized_field = serialize_field(field, fields[field])
if(serialized_field)
valid = TRUE
serialized += "[comma][serialized_field]"
comma = ","
if(!valid)
CRASH("Attempted to serialize to InfluxDB backend an invalid measurement (likely has no fields)")
if(timestamp_cache_worldtime != world.time)
update_timestamp()
serialized += " [timestamp_cache_realtime]"
send_queue += serialized
return TRUE

/// Enqueues sending varied stats in a dumb and simpler format directly as: measurement count=
/datum/controller/subsystem/influxdriver/proc/enqueue_stats_crude(measurement, value, field_name = "count")
. = FALSE
var/serialized_field = serialize_field(field_name, value)
if(!length(serialized_field))
return
if(timestamp_cache_worldtime != world.time)
update_timestamp()
var/serialized = "[measurement],round_id=[GLOB.round_id] [serialized_field] [timestamp_cache_realtime]"
send_queue += serialized
return TRUE

/// Puts a single field or tag value into InfluxDB Line format
/datum/controller/subsystem/influxdriver/proc/serialize_field(field, value)
var/static/regex/whitelistedCharacters = regex(@{"([^a-zA-Z0-9_]+)"}, "g")
var/sanitized_field = whitelistedCharacters.Replace("[field]", "")
if(!length(sanitized_field) || copytext(sanitized_field, 1, 2) == "_")
CRASH("Invalid tag/field for InfluxDB serialization: '[sanitized_field]' (original: '[field]')")
var/sanitized_value
if(isnum(value))
sanitized_value = value
else if(istext(value))
sanitized_value = whitelistedCharacters.Replace("[value]", "")
if(!length(sanitized_value) || copytext(sanitized_value, 1, 2) == "_")
CRASH("Invalid value for InfluxDB serialization: '[sanitized_value]' (original: '[value]')")
else
CRASH("Invalid value type passed for InfluxDB serialization: '[value]'")
return "[sanitized_field]=[sanitized_value]"
Loading

0 comments on commit 263e889

Please sign in to comment.