Files
vgstation13/code/modules/asset_cache/transports/asset_transport.dm
ShiftyRail d79c1fe070 Byond 516 v2.0 (#37553)
* The TGS thing

* Revert the 516 revert

* Further segment the world/New() proc

* Fixes an issue here
2025-05-12 00:50:25 -05:00

172 lines
6.9 KiB
Plaintext

/// When sending mutiple assets, how many before we give the client a quaint little sending resources message
#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8
/// How many assets can be sent at once during legacy asset transport
#define SLOW_ASSET_SEND_RATE 6
/// Base browse_rsc asset transport
/datum/asset_transport
var/name = "Simple browse_rsc asset transport"
var/static/list/preload
/// Don't mutate the filename of assets when sending via browse_rsc.
/// This is to make it easier to debug issues with assets, and allow server operators to bypass issues that make it to production.
/// If turning this on fixes asset issues, something isn't using get_asset_url and the asset isn't marked legacy, fix one of those.
var/dont_mutate_filenames = FALSE
/// Called when the transport is loaded by the config controller, not called on the default transport unless it gets loaded by a config change.
/datum/asset_transport/proc/Load()
if (config.asset_simple_preload)
for(var/client/C in global.clients)
add_timer(new /callback(src, PROC_REF(send_assets_slow), C, preload), 1 SECONDS)
/// Initialize - Called when SSassets initializes.
/datum/asset_transport/proc/Initialize(list/assets)
preload = assets.Copy()
if (!config.asset_simple_preload)
return
for(var/client/C in global.clients)
add_timer(new /callback(src, PROC_REF(send_assets_slow), C, preload), 1 SECONDS)
/**
* Register a browser asset with the asset cache system.
* returns a /datum/tg_asset_cache_item.
* mutiple calls to register the same asset under the same asset_name return the same datum.
*
* Arguments:
* * asset_name - the identifier of the asset.
* * asset - the actual asset file (or an asset_cache_item datum).
* * file_hash - optional, a hash of the contents of the asset files contents. used so asset_cache_item doesnt have to hash it again
* * dmi_file_path - optional, means that the given asset is from the rsc and thus we dont need to do some expensive operations
*/
/datum/asset_transport/proc/register_asset(asset_name, asset, file_hash, dmi_file_path)
var/datum/asset_cache_item/ACI = asset
if (!istype(ACI))
ACI = new(asset_name, asset, file_hash, dmi_file_path)
if (!ACI || !ACI.hash)
CRASH("ERROR: Invalid asset: [asset_name]:[asset]:[ACI]")
if (SSassets.cache[asset_name])
var/datum/asset_cache_item/OACI = SSassets.cache[asset_name]
OACI.legacy = ACI.legacy = (ACI.legacy|OACI.legacy)
OACI.namespace_parent = ACI.namespace_parent = (ACI.namespace_parent | OACI.namespace_parent)
OACI.namespace = OACI.namespace || ACI.namespace
if (OACI.hash != ACI.hash)
var/error_msg = "ERROR: new asset added to the asset cache with the same name as another asset: [asset_name] existing asset hash: [OACI.hash] new asset hash:[ACI.hash]"
stack_trace(error_msg)
log_debug(error_msg)
else
if (length(ACI.namespace))
return ACI
return OACI
SSassets.cache[asset_name] = ACI
return ACI
/// Immediately removes an asset from the asset cache.
/datum/asset_transport/proc/unregister_asset(asset_name)
SSassets.cache[asset_name] = null
SSassets.cache.Remove(null)
/// Returns a url for a given asset.
/// asset_name - Name of the asset.
/// asset_cache_item - asset cache item datum for the asset, optional, overrides asset_name
/datum/asset_transport/proc/get_asset_url(asset_name, datum/asset_cache_item/asset_cache_item)
if (!istype(asset_cache_item))
asset_cache_item = SSassets.cache[asset_name]
// To ensure code that breaks on cdns breaks in local testing, we only
// use the normal filename on legacy assets and name space assets.
var/keep_local_name = dont_mutate_filenames \
|| asset_cache_item.legacy \
|| asset_cache_item.keep_local_name \
|| (asset_cache_item.namespace && !asset_cache_item.namespace_parent)
if (keep_local_name)
return url_encode(asset_cache_item.name)
return url_encode("asset.[asset_cache_item.hash][asset_cache_item.ext]")
/// Sends a list of browser assets to a client
/// client - a client or mob
/// asset_list - A list of asset filenames to be sent to the client. Can optionally be assoicated with the asset's asset_cache_item datum.
/// Returns TRUE if any assets were sent.
/datum/asset_transport/proc/send_assets(client/client, list/asset_list)
#if defined(UNIT_TESTS)
return
#endif
if (!istype(client))
if (ismob(client))
var/mob/our_mob = client
if (our_mob.client)
client = our_mob.client
else //no stacktrace because this will mainly happen because the client went away
return
else
CRASH("Invalid argument: client: `[client]`")
if (!islist(asset_list))
asset_list = list(asset_list)
var/list/unreceived = list()
for (var/asset_name in asset_list)
var/datum/asset_cache_item/ACI = asset_list[asset_name]
if (!istype(ACI) && !(ACI = SSassets.cache[asset_name]))
log_debug("ERROR: can't send asset `[asset_name]`: unregistered or invalid state: `[ACI]`")
continue
var/asset_file = ACI.resource
if (!asset_file)
log_debug("ERROR: can't send asset `[asset_name]`: invalid registered resource: `[ACI.resource]`")
continue
var/asset_hash = ACI.hash
var/new_asset_name = asset_name
var/keep_local_name = dont_mutate_filenames \
|| ACI.legacy \
|| ACI.keep_local_name \
|| (ACI.namespace && !ACI.namespace_parent)
if (!keep_local_name)
new_asset_name = "asset.[ACI.hash][ACI.ext]"
if (client.sent_assets[new_asset_name] == asset_hash)
if (global.Debug2)
log_debug("DEBUG: Skipping send of `[asset_name]` (as `[new_asset_name]`) for `[client]` because it already exists in the client's sent_assets list")
continue
unreceived[asset_name] = ACI
if (unreceived.len)
if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT)
to_chat(client, ("<span class='info'>Sending Resources...</span>"))
for (var/asset_name in unreceived)
var/new_asset_name = asset_name
var/datum/asset_cache_item/ACI = unreceived[asset_name]
var/keep_local_name = dont_mutate_filenames \
|| ACI.legacy \
|| ACI.keep_local_name \
|| (ACI.namespace && !ACI.namespace_parent)
if (!keep_local_name)
new_asset_name = "asset.[ACI.hash][ACI.ext]"
client << browse_rsc(ACI.resource, new_asset_name)
client.sent_assets[new_asset_name] = ACI.hash
add_timer(new /callback(client, TYPE_PROC_REF(/client, asset_cache_update_json)), 1 SECONDS)
return TRUE
return FALSE
/// Precache files without clogging up the browse() queue, used for passively sending files on connection start.
/datum/asset_transport/proc/send_assets_slow(client/client, list/files, filerate = SLOW_ASSET_SEND_RATE)
var/startingfilerate = filerate
for (var/file in files)
if (!client)
break
if (send_assets(client, file))
if (!(--filerate))
filerate = startingfilerate
client.tg_browse_queue_flush()
stoplag(0) //queuing calls like this too quickly can cause issues in some client versions
/// Check the config is valid to load this transport
/// Returns TRUE or FALSE
/datum/asset_transport/proc/validate_config(log = TRUE)
return TRUE
#undef ASSET_CACHE_TELL_CLIENT_AMOUNT