The 515 MegaPR early downport (#7783)

Co-authored-by: Selis <selis@xynolabs.com>
Co-authored-by: Selis <sirlionfur@hotmail.de>
Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
Co-authored-by: SatinIsle <thesatinisle@gmail.com>
Co-authored-by: Heroman <alesha3000@list.ru>
Co-authored-by: Casey <a.roaming.shadow@gmail.com>
Co-authored-by: Raeschen <rycoop29@gmail.com>
This commit is contained in:
Cadyn
2024-02-27 11:17:32 -08:00
committed by GitHub
parent 96a43a09c1
commit b90f7ec922
254 changed files with 2135 additions and 1576 deletions

View File

@@ -1,110 +0,0 @@
/*
Asset cache quick users guide:
Make a datum in asset_list_items.dm with your assets for your thing.
Checkout asset_list.dm for the helper subclasses
The simple subclass will most like be of use for most cases.
Then call get_asset_datum() with the type of the datum you created and store the return
Then call .send(client) on that stored return value.
Note: If your code uses output() with assets you will need to call asset_flush on the client and wait for it to return before calling output(). You only need do this if .send(client) returned TRUE
*/
//When sending mutiple assets, how many before we give the client a quaint little sending resources message
#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8
//This proc sends the asset to the client, but only if it needs it.
//This proc blocks(sleeps) unless verify is set to false
/proc/send_asset(client/client, asset_name)
return send_asset_list(client, list(asset_name))
/// Sends a list of assets to a client
/// This proc will no longer block, use client.asset_flush() if you to need know when the client has all assets (such as for output()). (This is not required for browse() calls as they use the same message queue as asset sends)
/// client - a client or mob
/// asset_list - A list of asset filenames to be sent to the client.
/// Returns TRUE if any assets were sent.
/proc/send_asset_list(client/client, list/asset_list)
if(!istype(client))
if(ismob(client))
var/mob/M = client
if(M.client)
client = M.client
else
return
else
return
var/list/unreceived = list()
for (var/asset_name in asset_list)
var/datum/asset_cache_item/asset = SSassets.cache[asset_name]
if (!asset)
continue
var/asset_file = asset.resource
if (!asset_file)
continue
var/asset_md5 = asset.md5
if (client.sent_assets[asset_name] == asset_md5)
continue
unreceived[asset_name] = asset_md5
if (unreceived.len)
if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT)
to_chat(client, "Sending Resources...")
for(var/asset in unreceived)
var/datum/asset_cache_item/ACI
if ((ACI = SSassets.cache[asset]))
// log_asset("Sending asset [asset] to client [client]") CHOMP Edit Commenting this out to STOP THE FUCKING SPAM. THE LOGS ARE BLOATING
client << browse_rsc(ACI.resource, asset)
client.sent_assets |= unreceived
addtimer(CALLBACK(client, /client/proc/asset_cache_update_json), 1 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE)
return TRUE
return FALSE
//This proc will download the files without clogging up the browse() queue, used for passively sending files on connection start.
//The proc calls procs that sleep for long times.
/proc/getFilesSlow(client/client, list/files, register_asset = TRUE, filerate = 3)
var/startingfilerate = filerate
for(var/file in files)
if (!client)
break
if (register_asset)
register_asset(file, files[file])
if (send_asset(client, file))
if (!(--filerate))
filerate = startingfilerate
client.asset_flush()
stoplag(0) //queuing calls like this too quickly can cause issues in some client versions
//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up.
//icons and virtual assets get copied to the dyn rsc before use
/proc/register_asset(asset_name, asset)
var/datum/asset_cache_item/ACI = new(asset_name, asset)
//this is technically never something that was supported and i want metrics on how often it happens if at all.
if (SSassets.cache[asset_name])
var/datum/asset_cache_item/OACI = SSassets.cache[asset_name]
if (OACI.md5 != ACI.md5)
stack_trace("ERROR: new asset added to the asset cache with the same name as another asset: [asset_name] existing asset md5: [OACI.md5] new asset md5:[ACI.md5]")
else
var/list/stacktrace = gib_stack_trace()
log_asset("WARNING: dupe asset added to the asset cache: [asset_name] existing asset md5: [OACI.md5] new asset md5:[ACI.md5]\n[stacktrace.Join("\n")]")
SSassets.cache[asset_name] = ACI
return ACI
/// Returns the url of the asset, currently this is just its name, here to allow further work cdn'ing assets.
/// Can be given an asset as well, this is just a work around for buggy edge cases where two assets may have the same name, doesn't matter now, but it will when the cdn comes.
/proc/get_asset_url(asset_name, asset = null)
var/datum/asset_cache_item/ACI = SSassets.cache[asset_name]
return ACI?.url
//Generated names do not include file extention.
//Used mainly for code that deals with assets in a generic way
//The same asset will always lead to the same asset name
/proc/generate_asset_name(file)
return "asset.[md5(fcopy_rsc(file))]"

View File

@@ -1,5 +1,5 @@
/// Process asset cache client topic calls for "asset_cache_confirm_arrival=[INT]"
/// Process asset cache client topic calls for `"asset_cache_confirm_arrival=[INT]"`
/client/proc/asset_cache_confirm_arrival(job_id)
var/asset_cache_job = round(text2num(job_id))
//because we skip the limiter, we have to make sure this is a valid arrival and not somebody tricking us into letting them append to a list without limit.
@@ -10,17 +10,9 @@
return asset_cache_job || TRUE
/// Process asset cache client topic calls for "asset_cache_preload_data=[HTML+JSON_STRING]
/// Process asset cache client topic calls for `"asset_cache_preload_data=[HTML+JSON_STRING]"`
/client/proc/asset_cache_preload_data(data)
/*var/jsonend = findtextEx(data, "{{{ENDJSONDATA}}}")
if (!jsonend)
CRASH("invalid asset_cache_preload_data, no jsonendmarker")*/
//var/json = html_decode(copytext(data, 1, jsonend))
var/json = data
// This is a stupid workaround to BYOND injecting this pngfix mess into IE7 clients browse()
json = replacetext(json, "<!--\[if lt IE 7.]>\n<script defer type=text/javascript src=pngfix.js></script>\n<!\[endif]-->", "")
var/list/preloaded_assets = json_decode(json)
for (var/preloaded_asset in preloaded_assets)
@@ -30,19 +22,17 @@
sent_assets |= preloaded_assets
/// Updates the client side stored html/json combo file used to keep track of what assets the client has between restarts/reconnects.
/client/proc/asset_cache_update_json(verify = FALSE, list/new_assets = list())
/// Updates the client side stored json file used to keep track of what assets the client has between restarts/reconnects.
/client/proc/asset_cache_update_json()
if (world.time - connection_time < 10 SECONDS) //don't override the existing data file on a new connection
return
if (!islist(new_assets))
new_assets = list("[new_assets]" = md5(SSassets.cache[new_assets]))
src << browse(json_encode(new_assets|sent_assets), "file=asset_data.json&display=0")
src << browse(json_encode(sent_assets), "file=asset_data.json&display=0")
/// Blocks until all currently sending browser assets have been sent.
/// Blocks until all currently sending browse and browse_rsc assets have been sent.
/// Due to byond limitations, this proc will sleep for 1 client round trip even if the client has no pending asset sends.
/// This proc will return an untrue value if it had to return before confirming the send, such as timeout or the client going away.
/client/proc/asset_flush(timeout = 50)
/client/proc/browse_queue_flush(timeout = 50)
var/job = ++last_asset_job
var/t = 0
var/timeout_time = timeout

View File

@@ -1,23 +1,54 @@
/**
* # asset_cache_item
*
* An internal datum containing info on items in the asset cache. Mainly used to cache md5 info for speed.
**/
* # asset_cache_item
*
* An internal datum containing info on items in the asset cache. Mainly used to cache md5 info for speed.
*/
/datum/asset_cache_item
/// the name of this asset item, becomes the key in SSassets.cache list
var/name
var/url
var/md5
/// md5() of the file this asset item represents.
var/hash
/// the file this asset represents
var/resource
/// our file extension e.g. .png, .gif, etc
var/ext = ""
/// Should this file also be sent via the legacy browse_rsc system
/// when cdn transports are enabled?
var/legacy = FALSE
/// Used by the cdn system to keep legacy css assets with their parent
/// css file. (css files resolve urls relative to the css file, so the
/// legacy system can't be used if the css file itself could go out over
/// the cdn)
var/namespace = null
/// True if this is the parent css or html file for an asset's namespace
var/namespace_parent = FALSE
/// TRUE for keeping local asset names when browse_rsc backend is used
var/keep_local_name = FALSE
/datum/asset_cache_item/New(name, file)
///pass in a valid file_hash if you have one to save it from needing to do it again.
///pass in a valid dmi file path string e.g. "icons/path/to/dmi_file.dmi" to make generating the hash less expensive
/datum/asset_cache_item/New(name, file, file_hash, dmi_file_path)
if (!isfile(file))
file = fcopy_rsc(file)
md5 = md5(file)
if (!md5)
md5 = md5(fcopy_rsc(file))
if (!md5)
CRASH("invalid asset sent to asset cache")
log_world("asset cache unexpected success of second fcopy_rsc")
hash = file_hash
//the given file is directly from a dmi file and is thus in the rsc already, we know that its file_hash will be correct
if(!hash)
if(dmi_file_path)
hash = md5(file)
else
hash = md5asfile(file) //icons sent to the rsc md5 incorrectly when theyre given incorrect data
if (!hash)
CRASH("invalid asset sent to asset cache")
src.name = name
url = name
var/extstart = findlasttext(name, ".")
if (extstart)
ext = ".[copytext(name, extstart+1)]"
resource = file
/datum/asset_cache_item/vv_edit_var(var_name, var_value)
return FALSE
/datum/asset_cache_item/CanProcCall(procname)
return FALSE

View File

@@ -1,3 +1,4 @@
#define ASSET_CROSS_ROUND_CACHE_DIRECTORY "tmp/assets"
//These datums are used to populate the asset cache, the proc "register()" does this.
//Place any asset datums you create in asset_list_items.dm
@@ -6,46 +7,101 @@
GLOBAL_LIST_EMPTY(asset_datums)
//get an assetdatum or make a new one
/proc/get_asset_datum(type)
//does NOT ensure it's filled, if you want that use get_asset_datum()
/proc/load_asset_datum(type)
return GLOB.asset_datums[type] || new type()
/proc/get_asset_datum(type)
var/datum/asset/loaded_asset = GLOB.asset_datums[type] || new type()
return loaded_asset.ensure_ready()
/datum/asset
var/_abstract = /datum/asset
var/cached_serialized_url_mappings
var/cached_serialized_url_mappings_transport_type
/// Whether or not this asset should be loaded in the "early assets" SS
var/early = FALSE
/// Whether or not this asset can be cached across rounds of the same commit under the `CACHE_ASSETS` config.
/// This is not a *guarantee* the asset will be cached. Not all asset subtypes respect this field, and the
/// config can, of course, be disabled.
/// Disable this if your asset can change between rounds on the same exact version of the code.
var/cross_round_cachable = FALSE
/datum/asset/New()
GLOB.asset_datums[type] = src
register()
/// Stub that allows us to react to something trying to get us
/// Not useful here, more handy for sprite sheets
/datum/asset/proc/ensure_ready()
return src
/// Stub to hook into if your asset is having its generation queued by SSasset_loading
/datum/asset/proc/queued_generation()
CRASH("[type] inserted into SSasset_loading despite not implementing /proc/queued_generation")
/datum/asset/proc/get_url_mappings()
return list()
/// Returns a cached tgui message of URL mappings
/datum/asset/proc/get_serialized_url_mappings()
if (isnull(cached_serialized_url_mappings) || cached_serialized_url_mappings_transport_type != SSassets.transport.type)
cached_serialized_url_mappings = TGUI_CREATE_MESSAGE("asset/mappings", get_url_mappings())
cached_serialized_url_mappings_transport_type = SSassets.transport.type
return cached_serialized_url_mappings
/datum/asset/proc/register()
return
/datum/asset/proc/send(client)
return
/// Returns whether or not the asset should attempt to read from cache
/datum/asset/proc/should_refresh()
return !cross_round_cachable || !config.cache_assets
//If you don't need anything complicated.
/// Simply takes any generated file and saves it to the round-specific /logs folder. Useful for debugging potential issues with spritesheet generation/display.
/// Only called when the SAVE_SPRITESHEETS config option is uncommented.
/datum/asset/proc/save_to_logs(file_name, file_location)
var/asset_path = "[log_path]/generated_assets/[file_name]"
fdel(asset_path) // just in case, sadly we can't use rust_g stuff here.
fcopy(file_location, asset_path)
/// If you don't need anything complicated.
/datum/asset/simple
_abstract = /datum/asset/simple
/// list of assets for this datum in the form of:
/// asset_filename = asset_file. At runtime the asset_file will be
/// converted into a asset_cache datum.
var/assets = list()
/// Set to true to have this asset also be sent via the legacy browse_rsc
/// system when cdn transports are enabled?
var/legacy = FALSE
/// TRUE for keeping local asset names when browse_rsc backend is used
var/keep_local_name = FALSE
/datum/asset/simple/register()
for(var/asset_name in assets)
assets[asset_name] = register_asset(asset_name, assets[asset_name])
var/datum/asset_cache_item/ACI = SSassets.transport.register_asset(asset_name, assets[asset_name])
if (!ACI)
log_asset("ERROR: Invalid asset: [type]:[asset_name]:[ACI]")
continue
if (legacy)
ACI.legacy = legacy
if (keep_local_name)
ACI.keep_local_name = keep_local_name
assets[asset_name] = ACI
/datum/asset/simple/send(client)
. = send_asset_list(client, assets)
. = SSassets.transport.send_assets(client, assets)
/datum/asset/simple/get_url_mappings()
. = list()
for (var/asset_name in assets)
var/datum/asset_cache_item/ACI = assets[asset_name]
if (!ACI)
continue
.[asset_name] = ACI.url
.[asset_name] = SSassets.transport.get_asset_url(asset_name, assets[asset_name])
// For registering or sending multiple others at once
/datum/asset/group
@@ -54,7 +110,7 @@ GLOBAL_LIST_EMPTY(asset_datums)
/datum/asset/group/register()
for(var/type in children)
get_asset_datum(type)
load_asset_datum(type)
/datum/asset/group/send(client/C)
for(var/type in children)
@@ -78,40 +134,122 @@ GLOBAL_LIST_EMPTY(asset_datums)
/datum/asset/spritesheet
_abstract = /datum/asset/spritesheet
cross_round_cachable = TRUE
var/name
/// List of arguments to pass into queuedInsert
/// Exists so we can queue icon insertion, mostly for stuff like preferences
var/list/to_generate = list()
var/list/sizes = list() // "32x32" -> list(10, icon/normal, icon/stripped)
var/list/sprites = list() // "foo_bar" -> list("32x32", 5)
var/list/cached_spritesheets_needed
var/generating_cache = FALSE
var/fully_generated = FALSE
/// If this asset should be fully loaded on new
/// Defaults to false so we can process this stuff nicely
var/load_immediately = FALSE
VAR_PRIVATE
// Kept in state so that the result is the same, even when the files are created, for this run
should_refresh = null
/datum/asset/spritesheet/proc/should_load_immediately()
#ifdef DO_NOT_DEFER_ASSETS
return TRUE
#else
return load_immediately
#endif
/datum/asset/spritesheet/should_refresh()
if (..())
return TRUE
if (isnull(should_refresh))
// `fexists` seems to always fail on static-time
should_refresh = !fexists(css_cache_filename()) || !fexists(data_cache_filename())
return should_refresh
/datum/asset/spritesheet/register()
SHOULD_NOT_OVERRIDE(TRUE)
if (!name)
CRASH("spritesheet [type] cannot register without a name")
if (!should_refresh() && read_from_cache())
fully_generated = TRUE
return
// If it's cached, may as well load it now, while the loading is cheap
if(config.cache_assets && cross_round_cachable)
load_immediately = TRUE
create_spritesheets()
if(should_load_immediately())
realize_spritesheets(yield = FALSE)
else
SSasset_loading.queue_asset(src)
/datum/asset/spritesheet/proc/realize_spritesheets(yield)
if(fully_generated)
return
while(length(to_generate))
var/list/stored_args = to_generate[to_generate.len]
to_generate.len--
queuedInsert(arglist(stored_args))
if(yield && TICK_CHECK)
return
ensure_stripped()
for(var/size_id in sizes)
var/size = sizes[size_id]
register_asset("[name]_[size_id].png", size[SPRSZ_STRIPPED])
var/res_name = "spritesheet_[name].css"
var/fname = "data/spritesheets/[res_name]"
fdel(fname)
text2file(generate_css(), fname)
register_asset(res_name, fcopy_rsc(fname))
fdel(fname)
SSassets.transport.register_asset("[name]_[size_id].png", size[SPRSZ_STRIPPED])
var/css_name = "spritesheet_[name].css"
var/file_directory = "data/spritesheets/[css_name]"
fdel(file_directory)
text2file(generate_css(), file_directory)
SSassets.transport.register_asset(css_name, fcopy_rsc(file_directory))
/datum/asset/spritesheet/send(client/C)
if(config.save_spritesheets)
save_to_logs(file_name = css_name, file_location = file_directory)
fdel(file_directory)
if (config.cache_assets && cross_round_cachable)
write_to_cache()
fully_generated = TRUE
// If we were ever in there, remove ourselves
SSasset_loading.dequeue_asset(src)
/datum/asset/spritesheet/queued_generation()
realize_spritesheets(yield = TRUE)
/datum/asset/spritesheet/ensure_ready()
if(!fully_generated)
realize_spritesheets(yield = FALSE)
return ..()
/datum/asset/spritesheet/send(client/client)
if (!name)
return
if (!should_refresh())
return send_from_cache(client)
var/all = list("spritesheet_[name].css")
for(var/size_id in sizes)
all += "[name]_[size_id].png"
. = send_asset_list(C, all)
. = SSassets.transport.send_assets(client, all)
/datum/asset/spritesheet/get_url_mappings()
if (!name)
return
. = list("spritesheet_[name].css" = get_asset_url("spritesheet_[name].css"))
if (!should_refresh())
return get_cached_url_mappings()
. = list("spritesheet_[name].css" = SSassets.transport.get_asset_url("spritesheet_[name].css"))
for(var/size_id in sizes)
.["[name]_[size_id].png"] = get_asset_url("[name]_[size_id].png")
.["[name]_[size_id].png"] = SSassets.transport.get_asset_url("[name]_[size_id].png")
/datum/asset/spritesheet/proc/ensure_stripped(sizes_to_strip = sizes)
for(var/size_id in sizes_to_strip)
@@ -120,13 +258,19 @@ GLOBAL_LIST_EMPTY(asset_datums)
continue
// save flattened version
var/fname = "data/spritesheets/[name]_[size_id].png"
fcopy(size[SPRSZ_ICON], fname)
var/error = rustg_dmi_strip_metadata(fname)
var/png_name = "[name]_[size_id].png"
var/file_directory = "data/spritesheets/[png_name]"
fcopy(size[SPRSZ_ICON], file_directory)
var/error = rustg_dmi_strip_metadata(file_directory)
if(length(error))
stack_trace("Failed to strip [name]_[size_id].png: [error]")
size[SPRSZ_STRIPPED] = icon(fname)
fdel(fname)
stack_trace("Failed to strip [png_name]: [error]")
size[SPRSZ_STRIPPED] = icon(file_directory)
// this is useful here for determining if weird sprite issues (like having a white background) are a cause of what we're doing DM-side or not since we can see the full flattened thing at-a-glance.
if(config.save_spritesheets)
save_to_logs(file_name = png_name, file_location = file_directory)
fdel(file_directory)
/datum/asset/spritesheet/proc/generate_css()
var/list/out = list()
@@ -134,7 +278,7 @@ GLOBAL_LIST_EMPTY(asset_datums)
for (var/size_id in sizes)
var/size = sizes[size_id]
var/icon/tiny = size[SPRSZ_ICON]
out += ".[name][size_id]{display:inline-block;width:[tiny.Width()]px;height:[tiny.Height()]px;background:url('[get_asset_url("[name]_[size_id].png")]') no-repeat;}"
out += ".[name][size_id]{display:inline-block;width:[tiny.Width()]px;height:[tiny.Height()]px;background:url('[get_background_url("[name]_[size_id].png")]') no-repeat;}"
for (var/sprite_id in sprites)
var/sprite = sprites[sprite_id]
@@ -152,10 +296,109 @@ GLOBAL_LIST_EMPTY(asset_datums)
return out.Join("\n")
/datum/asset/spritesheet/proc/css_cache_filename()
return "[ASSET_CROSS_ROUND_CACHE_DIRECTORY]/spritesheet.[name].css"
/datum/asset/spritesheet/proc/data_cache_filename()
return "[ASSET_CROSS_ROUND_CACHE_DIRECTORY]/spritesheet.[name].json"
/datum/asset/spritesheet/proc/read_from_cache()
return read_css_from_cache() && read_data_from_cache()
/datum/asset/spritesheet/proc/read_css_from_cache()
var/replaced_css = file2text(css_cache_filename())
var/regex/find_background_urls = regex(@"background:url\('%(.+?)%'\)", "g")
while (find_background_urls.Find(replaced_css))
var/asset_id = find_background_urls.group[1]
var/asset_cache_item = SSassets.transport.register_asset(asset_id, "[ASSET_CROSS_ROUND_CACHE_DIRECTORY]/spritesheet.[asset_id]")
var/asset_url = SSassets.transport.get_asset_url(asset_cache_item = asset_cache_item)
replaced_css = replacetext(replaced_css, find_background_urls.match, "background:url('[asset_url]')")
LAZYADD(cached_spritesheets_needed, asset_id)
var/finalized_name = "spritesheet_[name].css"
var/replaced_css_filename = "data/spritesheets/[finalized_name]"
rustg_file_write(replaced_css, replaced_css_filename)
SSassets.transport.register_asset(finalized_name, replaced_css_filename)
if(config.save_spritesheets)
save_to_logs(file_name = finalized_name, file_location = replaced_css_filename)
fdel(replaced_css_filename)
return TRUE
/datum/asset/spritesheet/proc/read_data_from_cache()
var/json = json_decode(file2text(data_cache_filename()))
if (islist(json["sprites"]))
sprites = json["sprites"]
return TRUE
/datum/asset/spritesheet/proc/send_from_cache(client/client)
if (isnull(cached_spritesheets_needed))
stack_trace("cached_spritesheets_needed was null when sending assets from [type] from cache")
cached_spritesheets_needed = list()
return SSassets.transport.send_assets(client, cached_spritesheets_needed + "spritesheet_[name].css")
/// Returns the URL to put in the background:url of the CSS asset
/datum/asset/spritesheet/proc/get_background_url(asset)
if (generating_cache)
return "%[asset]%"
else
return SSassets.transport.get_asset_url(asset)
/datum/asset/spritesheet/proc/write_to_cache()
write_css_to_cache()
write_data_to_cache()
/datum/asset/spritesheet/proc/write_css_to_cache()
for (var/size_id in sizes)
fcopy(SSassets.cache["[name]_[size_id].png"].resource, "[ASSET_CROSS_ROUND_CACHE_DIRECTORY]/spritesheet.[name]_[size_id].png")
generating_cache = TRUE
var/mock_css = generate_css()
generating_cache = FALSE
rustg_file_write(mock_css, css_cache_filename())
/datum/asset/spritesheet/proc/write_data_to_cache()
rustg_file_write(json_encode(list(
"sprites" = sprites,
)), data_cache_filename())
/datum/asset/spritesheet/proc/get_cached_url_mappings()
var/list/mappings = list()
mappings["spritesheet_[name].css"] = SSassets.transport.get_asset_url("spritesheet_[name].css")
for (var/asset_name in cached_spritesheets_needed)
mappings[asset_name] = SSassets.transport.get_asset_url(asset_name)
return mappings
/// Override this in order to start the creation of the spritehseet.
/// This is where all your Insert, InsertAll, etc calls should be inside.
/datum/asset/spritesheet/proc/create_spritesheets()
SHOULD_CALL_PARENT(FALSE)
CRASH("create_spritesheets() not implemented for [type]!")
/datum/asset/spritesheet/proc/Insert(sprite_name, icon/I, icon_state="", dir=SOUTH, frame=1, moving=FALSE)
if(should_load_immediately())
queuedInsert(sprite_name, I, icon_state, dir, frame, moving)
else
to_generate += list(args.Copy())
/datum/asset/spritesheet/proc/queuedInsert(sprite_name, icon/I, icon_state="", dir=SOUTH, frame=1, moving=FALSE)
I = icon(I, icon_state=icon_state, dir=dir, frame=frame, moving=moving)
if (!I || !length(icon_states(I))) // that direction or state doesn't exist
return
//var/start_usage = world.tick_usage
//any sprite modifications we want to do (aka, coloring a greyscaled asset)
I = ModifyInserted(I)
var/size_id = "[I.Width()]x[I.Height()]"
var/size = sizes[size_id]
@@ -164,14 +407,34 @@ GLOBAL_LIST_EMPTY(asset_datums)
if (size)
var/position = size[SPRSZ_COUNT]++
// Icons are essentially representations of files + modifications
// Because of this, byond keeps them in a cache. It does this in a really dumb way tho
// It's essentially a FIFO queue. So after we do icon() some amount of times, our old icons go out of cache
// When this happens it becomes impossible to modify them, trying to do so will instead throw a
// "bad icon" error.
// What we're doing here is ensuring our icon is in the cache by refreshing it, so we can modify it w/o runtimes.
var/icon/sheet = size[SPRSZ_ICON]
var/icon/sheet_copy = icon(sheet)
size[SPRSZ_STRIPPED] = null
sheet.Insert(I, icon_state=sprite_name)
sheet_copy.Insert(I, icon_state=sprite_name)
size[SPRSZ_ICON] = sheet_copy
sprites[sprite_name] = list(size_id, position)
else
sizes[size_id] = size = list(1, I, null)
sprites[sprite_name] = list(size_id, 0)
//SSblackbox.record_feedback("tally", "spritesheet_queued_insert_time", TICK_USAGE_TO_MS(start_usage), name)
/**
* A simple proc handing the Icon for you to modify before it gets turned into an asset.
*
* Arguments:
* * I: icon being turned into an asset
*/
/datum/asset/spritesheet/proc/ModifyInserted(icon/pre_asset)
return pre_asset
/datum/asset/spritesheet/proc/InsertAll(prefix, icon/I, list/directions)
if (length(prefix))
prefix = "[prefix]-"
@@ -188,14 +451,14 @@ GLOBAL_LIST_EMPTY(asset_datums)
return {"<link rel="stylesheet" href="[css_filename()]" />"}
/datum/asset/spritesheet/proc/css_filename()
return get_asset_url("spritesheet_[name].css")
return SSassets.transport.get_asset_url("spritesheet_[name].css")
/datum/asset/spritesheet/proc/icon_tag(sprite_name)
var/sprite = sprites[sprite_name]
if (!sprite)
return null
var/size_id = sprite[SPR_SIZE]
return {"<span class="[name][size_id] [sprite_name]"></span>"}
return {"<span class='[name][size_id] [sprite_name]'></span>"}
/datum/asset/spritesheet/proc/icon_class_name(sprite_name)
var/sprite = sprites[sprite_name]
@@ -204,6 +467,19 @@ GLOBAL_LIST_EMPTY(asset_datums)
var/size_id = sprite[SPR_SIZE]
return {"[name][size_id] [sprite_name]"}
/**
* Returns the size class (ex design32x32) for a given sprite's icon
*
* Arguments:
* * sprite_name - The sprite to get the size of
*/
/datum/asset/spritesheet/proc/icon_size_id(sprite_name)
var/sprite = sprites[sprite_name]
if (!sprite)
return null
var/size_id = sprite[SPR_SIZE]
return "[name][size_id]"
#undef SPR_SIZE
#undef SPR_IDX
#undef SPRSZ_COUNT
@@ -211,14 +487,31 @@ GLOBAL_LIST_EMPTY(asset_datums)
#undef SPRSZ_STRIPPED
/datum/asset/changelog_item
_abstract = /datum/asset/changelog_item
var/item_filename
/datum/asset/changelog_item/New(date)
item_filename = sanitize_filename("[date].yml")
SSassets.transport.register_asset(item_filename, file("html/changelogs_ch/archive/" + item_filename))
/datum/asset/changelog_item/send(client)
if (!item_filename)
return
. = SSassets.transport.send_assets(client, item_filename)
/datum/asset/changelog_item/get_url_mappings()
if (!item_filename)
return
. = list("[item_filename]" = SSassets.transport.get_asset_url(item_filename))
/datum/asset/spritesheet/simple
_abstract = /datum/asset/spritesheet/simple
var/list/assets
/datum/asset/spritesheet/simple/register()
/datum/asset/spritesheet/simple/create_spritesheets()
for (var/key in assets)
Insert(key, assets[key])
..()
//Generates assets based on iconstates of a single icon
/datum/asset/simple/icon_states
@@ -243,7 +536,7 @@ GLOBAL_LIST_EMPTY(asset_datums)
if (generic_icon_names)
asset_name = "[generate_asset_name(asset)].png"
register_asset(asset_name, asset)
SSassets.transport.register_asset(asset_name, asset)
/datum/asset/simple/icon_states/multiple_icons
_abstract = /datum/asset/simple/icon_states/multiple_icons
@@ -253,4 +546,79 @@ GLOBAL_LIST_EMPTY(asset_datums)
for(var/i in icons)
..(i)
/// Namespace'ed assets (for static css and html files)
/// When sent over a cdn transport, all assets in the same asset datum will exist in the same folder, as their plain names.
/// Used to ensure css files can reference files by url() without having to generate the css at runtime, both the css file and the files it depends on must exist in the same namespace asset datum. (Also works for html)
/// For example `blah.css` with asset `blah.png` will get loaded as `namespaces/a3d..14f/f12..d3c.css` and `namespaces/a3d..14f/blah.png`. allowing the css file to load `blah.png` by a relative url rather then compute the generated url with get_url_mappings().
/// The namespace folder's name will change if any of the assets change. (excluding parent assets)
/datum/asset/simple/namespaced
_abstract = /datum/asset/simple/namespaced
/// parents - list of the parent asset or assets (in name = file assoicated format) for this namespace.
/// parent assets must be referenced by their generated url, but if an update changes a parent asset, it won't change the namespace's identity.
var/list/parents = list()
/datum/asset/simple/namespaced/register()
if (legacy)
assets |= parents
var/list/hashlist = list()
var/list/sorted_assets = sortList(assets)
for (var/asset_name in sorted_assets)
var/datum/asset_cache_item/ACI = new(asset_name, sorted_assets[asset_name])
if (!ACI?.hash)
log_asset("ERROR: Invalid asset: [type]:[asset_name]:[ACI]")
continue
hashlist += ACI.hash
sorted_assets[asset_name] = ACI
var/namespace = md5(hashlist.Join())
for (var/asset_name in parents)
var/datum/asset_cache_item/ACI = new(asset_name, parents[asset_name])
if (!ACI?.hash)
log_asset("ERROR: Invalid asset: [type]:[asset_name]:[ACI]")
continue
ACI.namespace_parent = TRUE
sorted_assets[asset_name] = ACI
for (var/asset_name in sorted_assets)
var/datum/asset_cache_item/ACI = sorted_assets[asset_name]
if (!ACI?.hash)
log_asset("ERROR: Invalid asset: [type]:[asset_name]:[ACI]")
continue
ACI.namespace = namespace
assets = sorted_assets
..()
/// Get a html string that will load a html asset.
/// Needed because byond doesn't allow you to browse() to a url.
/datum/asset/simple/namespaced/proc/get_htmlloader(filename)
return url2htmlloader(SSassets.transport.get_asset_url(filename, assets[filename]))
/// A subtype to generate a JSON file from a list
/datum/asset/json
_abstract = /datum/asset/json
/// The filename, will be suffixed with ".json"
var/name
/datum/asset/json/send(client)
return SSassets.transport.send_assets(client, "[name].json")
/datum/asset/json/get_url_mappings()
return list(
"[name].json" = SSassets.transport.get_asset_url("[name].json"),
)
/datum/asset/json/register()
var/filename = "data/[name].json"
fdel(filename)
text2file(json_encode(generate()), filename)
SSassets.transport.register_asset("[name].json", fcopy_rsc(filename))
fdel(filename)
/// Returns the data that will be JSON encoded
/datum/asset/json/proc/generate()
SHOULD_CALL_PARENT(FALSE)
CRASH("generate() not implemented for [type]!")
#undef ASSET_CROSS_ROUND_CACHE_DIRECTORY

View File

@@ -260,29 +260,26 @@
/datum/asset/spritesheet/pipes
name = "pipes"
/datum/asset/spritesheet/pipes/register()
/datum/asset/spritesheet/pipes/create_spritesheets()
for(var/each in list('icons/obj/pipe-item.dmi', 'icons/obj/pipes/disposal.dmi'))
InsertAll("", each, global.alldirs)
..()
//VOREStation Add
/datum/asset/spritesheet/vore
name = "vore"
/datum/asset/spritesheet/vore/register()
/datum/asset/spritesheet/vore/create_spritesheets()
var/icon/downscaled = icon('modular_chomp/icons/mob/screen_full_vore_ch.dmi') //CHOMPedit: preserving save data
downscaled.Scale(240, 240)
InsertAll("", downscaled)
..()
/datum/asset/spritesheet/vore_fixed //This should be getting loaded in the TGUI vore panel but the game refuses to do so, for some reason. It only loads the vore spritesheet. //CHOMPedit
name = "fixedvore" //CHOMPedit
/datum/asset/spritesheet/vore_fixed/register() //CHOMPedi start: preserving save data
/datum/asset/spritesheet/vore_fixed/create_spritesheets() //CHOMPedi start: preserving save data
var/icon/downscaledVF = icon('icons/mob/screen_full_vore.dmi')
downscaledVF.Scale(240, 240)
InsertAll("", downscaledVF) //CHOMpedit end
..()
//VOREStation Add End
@@ -348,7 +345,7 @@
/datum/asset/spritesheet/vending
name = "vending"
/datum/asset/spritesheet/vending/register()
/datum/asset/spritesheet/vending/create_spritesheets()
populate_vending_products()
for(var/atom/item as anything in GLOB.vending_products)
if(!ispath(item, /atom))
@@ -386,7 +383,6 @@
var/imgid = replacetext(replacetext("[item]", "/obj/item/", ""), "/", "-")
Insert(imgid, I)
return ..()
// this is cursed but necessary or else vending product icons can be missing
// basically, if there's any vending machines that aren't already mapped in, our register() will not know
@@ -447,10 +443,10 @@
assets["patch[i].png"] = icon('icons/obj/chemical.dmi', "patch[i]") // CHOMPedit
for(var/asset_name in assets)
register_asset(asset_name, assets[asset_name])
SSassets.transport.register_asset(asset_name, assets[asset_name])
/datum/asset/chem_master/send(client)
send_asset_list(client, assets, verify)
SSassets.transport.send_assets(client, assets, verify)
//Cloning pod sprites for UIs
/datum/asset/cloning
@@ -462,10 +458,10 @@
assets["pod_cloning.gif"] = icon('icons/obj/cloning.dmi', "pod_cloning")
assets["pod_mess.gif"] = icon('icons/obj/cloning.dmi', "pod_mess")
for(var/asset_name in assets)
register_asset(asset_name, assets[asset_name])
SSassets.transport.register_asset(asset_name, assets[asset_name])
/datum/asset/cloning/send(client)
send_asset_list(client, assets, verify)
SSassets.transport.send_assets(client, assets, verify)
// VOREStation Add
/datum/asset/cloning/resleeving
@@ -476,15 +472,14 @@
assets["synthprinter.gif"] = icon('icons/obj/machines/synthpod.dmi', "pod_0")
assets["synthprinter_working.gif"] = icon('icons/obj/machines/synthpod.dmi', "pod_1")
for(var/asset_name in assets)
register_asset(asset_name, assets[asset_name])
SSassets.transport.register_asset(asset_name, assets[asset_name])
// VOREStation Add End
/datum/asset/spritesheet/sheetmaterials
name = "sheetmaterials"
/datum/asset/spritesheet/sheetmaterials/register()
/datum/asset/spritesheet/sheetmaterials/create_spritesheets()
InsertAll("", 'icons/obj/stacks.dmi')
..()
// Nanomaps
/datum/asset/simple/nanomaps

View File

@@ -1,2 +1,6 @@
/datum/asset/spritesheet/chat
name = "chat"
/datum/asset/spritesheet/chat/create_spritesheets()
//honk
//This function has to be overridden otherwise it will generate runtimes

View File

@@ -1,9 +1,8 @@
/datum/asset/simple/fontawesome
/datum/asset/simple/namespaced/fontawesome
assets = list(
"fa-regular-400.eot" = 'html/font-awesome/webfonts/fa-regular-400.eot',
"fa-regular-400.woff" = 'html/font-awesome/webfonts/fa-regular-400.woff',
"fa-solid-900.eot" = 'html/font-awesome/webfonts/fa-solid-900.eot',
"fa-solid-900.woff" = 'html/font-awesome/webfonts/fa-solid-900.woff',
"font-awesome.css" = 'html/font-awesome/css/all.min.css',
"v4shim.css" = 'html/font-awesome/css/v4-shims.min.css'
"fa-regular-400.ttf" = 'html/font-awesome/webfonts/fa-regular-400.ttf',
"fa-solid-900.ttf" = 'html/font-awesome/webfonts/fa-solid-900.ttf',
"fa-v4compatibility.ttf" = 'html/font-awesome/webfonts/fa-v4compatibility.ttf',
"v4shim.css" = 'html/font-awesome/css/v4-shims.min.css',
)
parents = list("font-awesome.css" = 'html/font-awesome/css/all.min.css')

View File

@@ -1,4 +1,5 @@
/datum/asset/simple/jquery
legacy = TRUE
assets = list(
"jquery.min.js" = 'code/modules/tooltip/jquery.min.js',
"jquery.min.js" = 'html/jquery/jquery.min.js',
)

View File

@@ -1,6 +1,8 @@
/datum/asset/simple/tgfont
/datum/asset/simple/namespaced/tgfont
assets = list(
"tgfont.eot" = file("tgui/packages/tgfont/dist/tgfont.eot"),
"tgfont.woff2" = file("tgui/packages/tgfont/dist/tgfont.woff2"),
)
parents = list(
"tgfont.css" = file("tgui/packages/tgfont/dist/tgfont.css"),
)

View File

@@ -0,0 +1,37 @@
# Asset cache system
## Framework for managing browser assets (javascript,css,images,etc)
This manages getting the asset to the client without doing unneeded re-sends, as well as utilizing any configured cdns.
There are two frameworks for using this system:
### Asset datum:
Make a datum in asset_list_items.dm with your browser assets for your thing.
Checkout asset_list.dm for the helper subclasses
The `simple` subclass will most likely be of use for most cases.
Call get_asset_datum() with the type of the datum you created to get your asset cache datum
Call .send(client|usr) on that datum to send the asset to the client. Depending on the asset transport this may or may not block.
Call .get_url_mappings() to get an associated list with the urls your assets can be found at.
### Manual backend:
See the documentation for `/datum/asset_transport` for the backend api the asset datums utilize.
The global variable `SSassets.transport` contains the currently configured transport.
### Notes:
Because byond browse() calls use non-blocking queues, if your code uses output() (which bypasses all of these queues) to invoke javascript functions you will need to first have the javascript announce to the server it has loaded before trying to invoke js functions.
To make your code work with any CDNs configured by the server, you must make sure assets are referenced from the url returned by `get_url_mappings()` or by asset_transport's `get_asset_url()`. (TGUI also has helpers for this.) If this can not be easily done, you can bypass the cdn using legacy assets, see the simple asset datum for details.
CSS files that use url() can be made to use the CDN without needing to rewrite all url() calls in code by using the namespaced helper datum. See the documentation for `/datum/asset/simple/namespaced` for details.

View File

@@ -0,0 +1,162 @@
/// 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.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.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.
* returns a /datum/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_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, "<span class='infoplain'>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]"
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 = 6)
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
#undef ASSET_CACHE_TELL_CLIENT_AMOUNT

View File

@@ -0,0 +1,87 @@
/// CDN Webroot asset transport.
/datum/asset_transport/webroot
name = "CDN Webroot asset transport"
/datum/asset_transport/webroot/Load()
if (validate_config(log = FALSE))
load_existing_assets()
/// Processes thru any assets that were registered before we were loaded as a transport.
/datum/asset_transport/webroot/proc/load_existing_assets()
for (var/asset_name in SSassets.cache)
var/datum/asset_cache_item/ACI = SSassets.cache[asset_name]
save_asset_to_webroot(ACI)
/// Register a browser asset with the asset cache system
/// We also save it to the CDN webroot at this step instead of waiting for send_assets()
/// asset_name - the identifier of the asset
/// asset - the actual asset file or an asset_cache_item datum.
/datum/asset_transport/webroot/register_asset(asset_name, asset)
. = ..()
var/datum/asset_cache_item/ACI = .
if (istype(ACI) && ACI.hash)
save_asset_to_webroot(ACI)
/// Saves the asset to the webroot taking into account namespaces and hashes.
/datum/asset_transport/webroot/proc/save_asset_to_webroot(datum/asset_cache_item/ACI)
var/webroot = config.asset_cdn_webroot
var/newpath = "[webroot][get_asset_suffex(ACI)]"
if (fexists(newpath))
return
if (fexists("[newpath].gz")) //its a common pattern in webhosting to save gzip'ed versions of text files and let the webserver serve them up as gzip compressed normal files, sometimes without keeping the original version.
return
return fcopy(ACI.resource, newpath)
/// 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/webroot/get_asset_url(asset_name, datum/asset_cache_item/asset_cache_item)
if (!istype(asset_cache_item))
asset_cache_item = SSassets.cache[asset_name]
var/url = config.asset_cdn_url //config loading will handle making sure this ends in a /
return "[url][get_asset_suffex(asset_cache_item)]"
/datum/asset_transport/webroot/proc/get_asset_suffex(datum/asset_cache_item/asset_cache_item)
var/base = "[copytext(asset_cache_item.hash, 1, 3)]/"
var/filename = "asset.[asset_cache_item.hash][asset_cache_item.ext]"
if (length(asset_cache_item.namespace))
base = "namespaces/[copytext(asset_cache_item.namespace, 1, 3)]/[asset_cache_item.namespace]/"
if (!asset_cache_item.namespace_parent)
filename = "[asset_cache_item.name]"
return base + filename
/// webroot asset sending - does nothing unless passed legacy assets
/datum/asset_transport/webroot/send_assets(client/client, list/asset_list)
. = FALSE
var/list/legacy_assets = list()
if (!islist(asset_list))
asset_list = list(asset_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]
if (!ACI)
legacy_assets += asset_name //pass it on to base send_assets so it can output an error
continue
if (ACI.legacy)
legacy_assets[asset_name] = ACI
if (length(legacy_assets))
. = ..(client, legacy_assets)
/// webroot slow asset sending - does nothing.
/datum/asset_transport/webroot/send_assets_slow(client/client, list/files, filerate)
return FALSE
/datum/asset_transport/webroot/validate_config(log = TRUE)
if (!config.asset_cdn_url)
if (log)
log_asset("ERROR: [type]: Invalid Config: ASSET_CDN_URL")
return FALSE
if (!config.asset_cdn_webroot)
if (log)
log_asset("ERROR: [type]: Invalid Config: ASSET_CDN_WEBROOT")
return FALSE
return TRUE

View File

@@ -26,4 +26,4 @@
</script>
</body>
</html>
</html>