/// When sending mutiple assets, how many before we give the client a quaint little sending resources message #define ASSET_CACHE_TELL_CLIENT_AMOUNT 8 /// 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_GET(flag/asset_simple_preload)) for(var/client/C in GLOB.clients) addtimer(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_GET(flag/asset_simple_preload)) return for(var/client/C in GLOB.clients) addtimer(CALLBACK(src, PROC_REF(send_assets_slow), C, preload), 1 SECONDS) /// Register a browser asset with the asset cache system /// asset_name - the identifier of the asset /// asset - the actual asset file (or an asset_cache_item datum) /// returns a /datum/asset_cache_item. /// mutiple calls to register the same asset under the same asset_name return the same datum /datum/asset_transport/proc/register_asset(asset_name, asset) var/datum/asset_cache_item/ACI = asset if (!istype(ACI)) ACI = new(asset_name, asset) 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_asset(error_msg) else if (length(ACI.namespace)) return ACI return OACI SSassets.cache[asset_name] = ACI return ACI /// 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 (!istype(client)) if (ismob(client)) var/mob/M = client if (M.client) client = M.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_asset("ERROR: can't send asset `[asset_name]`: unregistered or invalid state: `[ACI]`") continue var/asset_file = ACI.resource if (!asset_file) log_asset("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 (GLOB.Debug2) log_asset("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, "Sending Resources...") 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]" log_asset("Sending asset `[asset_name]` to client `[client]` as `[new_asset_name]`") client << browse_rsc(ACI.resource, new_asset_name) client.sent_assets[new_asset_name] = ACI.hash addtimer(CALLBACK(client, TYPE_PROC_REF(/client, asset_cache_update_json)), 1 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE) 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 = 3) var/startingfilerate = filerate for (var/file in files) if (!client) break if (send_assets(client, file)) if (!(--filerate)) filerate = startingfilerate client.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