Rock the UI Away - Removes VueUI and adds TGUI (#16509)

* tgui the beginning

* binaries and the like

* Bring in the last of it

* Example radio UI

* delete example

* NTOS Main Menu, start on manifest, tgui states

* tasks.json

* gunnery ui pt 1

* okay

* fix everything

* scss update

* oops

* manifest gigablast

* downloader part 1

* download prt 2

* NTOSDownloader final

* mfw committing to_worlds

* gunnery console pt2

* i cooked

* targeting (finished)

* one vueui down

* voting ui almost done

* MY MIND FEELS LIKE AN ARCH ENEMYYYY

* voting ui down

* photocopier

* ntos config + download fixes

* photocopier 2

* refactor define

* NTOS client manager + fixes

* fax machine final (it also uses toner now)

* marching forwards... left behind...

* ntnrc part 1

* canister

* add quotes

* portable pumps pt1 + more backgrounds

* oops

* finish the portable pump

* freezers

so I'll keep on pushing forward... you haven't seen the last of me... oooooooh...

* doors ui pt1

* finish doors UI (forgive me wildkins it's a bit of shitcode)

* vitals monitor, make things use labeled lists, new backgrounds

* mais j'envoyé aucun mayday...

* maglock pt1

* pour ça je me suis perdu...

* infrared

* fix that

* prox sensor pt1

* prox sensor

* signaler (this was actually pretty hard)

* atmos control pt1

* atmos control pt1.1

* atmos pt 2

* fuel injector

* multitool UI

* jammer

* list viewer

* APC

* portgen

* targeting console updates + SMES ui

* new themes, shield generator

* supermatter

* Add ore detector and (shitty) NTNet Relay

* orderterminal pt1

* orderterminal pt2

* smartfridge

* Add (air-)tank GUI update ore detector size

* Adds Transfer Valves

* Add AtmoScrubber

* analyzer pt1

* weapons analyzer pt2

* bodyscanner pt1

* bodyscanner pt2

* fix this shitcode

* seed storage

* appearance changer

* appearance changer final

* sleeper pt1

* sleeper

* gps

* vehicles

* chem dispenser

* lily request

* holopad

* tgui modules pt1

* ppanel

* damage menu

* fixes

* im here too now

* follow menu, search bars

* quikpay

* quikpay fixes

* circuit printer

* ppanel

* ppanel updates

* pai

* turret controls (i want to kill myself)

* tweak

* remove the boardgame

* guntracker

* implant tracker

* penal mechs

come close to me, come close to me

* chem codex

* pai radio

* doorjack

* pai directives

* signaler removal, sensors

* ghost spawner

* spawnpoint

* fixes

* teleporter

* one more to the chopping block

* account database

* remove divider

* scanner, atmos

* latejoin ui pt1

* latejoin

* records pt1

* RECORDS UI DONE

* delete interpreter & records

* CHAT FUCKING CLIENT

* data updates

* fix some things

* final UI, log

* basic nanoui fix

* antag panel

* remove vueui

* atm update

* vending update

* warrants, cameras

* ntmonitor

* time comes for all

* preserve this legacy

* bring that back (oops)

* rcon, ui auto update for computer UIs, remove rcon computers

* alarm monitoring (a bit broke and also todo: add custom alarm monitoring programs to a few consoles)

* A LIKE SUPREME

* a

* power monitor

* lights on

* fuck this code, fuck nanoui modules, and fuck nanoui

* LEAVE IT OH SO FAR BEHIND

* fix alarm monitoring for synths

* I SAW IN YOU WHAT LIFE WAS MISSING

* comms console

* idcard and record updates

* turn the light on

* arcade

* pt2

* news browser

* static

* crusher

* f

* COULD I JUST SLEIGH THE GOLD FROM THE BALLS? I'M SO FRUSTRATED OH COULD YOU TELL? IF I HEAR ONE MORE VUEUI OR ONE NANOUI I'M GONNA LOSE IT SO LET ME GOOOOOOOOOOOOOOOOO

* codeowners & suit sensors

* html ui style removal

* make lint happy

* resist and disorder

* i slowly get up and turn off the noise, already fed up...

* pleaseeeeeeeeeeeeeee

* THE CREDIT LARP IS NECESSARY

* i hold the keys

* RISE UP

* fix that?

* harry's suggestions xoxo

* runtime fix pt2

* You are the only thing that I still care about

* adds build workflow

* Update update_tgui.yml

* adds some needed steps

* ATM

* misc fixes and tweaks

* fixes 2

* make newscasters usable and fix use power on freezers

* turret control is clearer

---------

Co-authored-by: John Wildkins <john.wildkins@gmail.com>
Co-authored-by: Matt Atlas <liermattia@gmail.com>
Co-authored-by: harryob <55142896+harryob@users.noreply.github.com>
Co-authored-by: Werner <Arrow768@users.noreply.github.com>
Co-authored-by: Geeves <ggrobler447@gmail.com>
Co-authored-by: harryob <me@harryob.live>
This commit is contained in:
Matt Atlas
2023-06-25 19:03:33 +02:00
committed by GitHub
co-authored by John Wildkins Matt Atlas harryob Werner Geeves harryob
parent 4ce7ba118b
commit 31956c7eb0
1117 changed files with 80951 additions and 44002 deletions
+765
View File
@@ -0,0 +1,765 @@
/*
Asset cache quick users guide:
Make a datum at the bottom of this file with your assets for your thing.
The simple subsystem 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.
You can set verify to TRUE if you want send() to sleep until the client has the assets.
*/
// Amount of time(ds) MAX to send per asset, if this get exceeded we cancel the sleeping.
// This is doubled for the first asset, then added per asset after
#define ASSET_CACHE_SEND_TIMEOUT 7
#define ASSET_CROSS_ROUND_CACHE_DIRECTORY "tmp/assets"
//all of our asset datums, used for referring to these later
var/list/asset_datums = list()
//get an assetdatum or make a new one
//does NOT ensure it's filled, if you want that use get_asset_datum()
/proc/load_asset_datum(type)
return asset_datums[type] || new type()
/proc/get_asset_datum(type)
var/datum/asset/loaded_asset = asset_datums[type] || new type()
return loaded_asset.ensure_ready()
/proc/simple_asset_ensure_is_sent(client, type)
var/datum/asset/simple/asset = get_asset_datum(type)
asset.send(client)
/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.
var/cross_round_cachable = FALSE
/datum/asset/New()
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.
/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/list/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)
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)
. = SSassets.transport.send_assets(client, assets)
/datum/asset/simple/get_url_mappings()
. = list()
for (var/asset_name in assets)
.[asset_name] = SSassets.transport.get_asset_url(asset_name, assets[asset_name])
/datum/asset/group
_abstract = /datum/asset/group
var/list/children
/datum/asset/group/register()
for(var/type in children)
get_asset_datum(type)
/datum/asset/group/send(client/C)
for(var/type in children)
var/datum/asset/A = get_asset_datum(type)
A.send(C) || .
/datum/asset/group/get_url_mappings()
. = list()
for(var/type in children)
var/datum/asset/A = get_asset_datum(type)
. += A.get_url_mappings()
/datum/asset/group/goonchat
children = list(
/datum/asset/simple/jquery,
/datum/asset/simple/goonchat,
/datum/asset/simple/namespaced/fontawesome,
/datum/asset/spritesheet/goonchat
)
// spritesheet implementation
#define SPR_SIZE 1
#define SPR_IDX 2
#define SPRSZ_COUNT 1
#define SPRSZ_ICON 2
#define SPRSZ_STRIPPED 3
/datum/asset/spritesheet
_abstract = /datum/asset/spritesheet
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/verify = 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 = TRUE
/datum/asset/spritesheet/register()
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(cross_round_cachable)
load_immediately = TRUE
create_spritesheets()
if(should_load_immediately())
realize_spritesheets(yield = FALSE)
for(var/size_id in sizes)
var/size = sizes[size_id]
SSassets.transport.register_asset("[name]_[size_id].png", size[SPRSZ_STRIPPED])
/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"
. = SSassets.transport.send_assets(client, all)
/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]
SSassets.transport.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)
SSassets.transport.register_asset(res_name, fcopy_rsc(fname))
fdel(fname)
if (cross_round_cachable)
write_to_cache()
fully_generated = TRUE
/// 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/create_spritesheets()
return
/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")
/datum/asset/spritesheet/proc/write_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, "[ASSET_CROSS_ROUND_CACHE_DIRECTORY]/spritesheet.[name].css")
/datum/asset/spritesheet/get_url_mappings()
if (!name)
return
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"] = SSassets.transport.get_asset_url("[name]_[size_id].png")
/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
/datum/asset/spritesheet/proc/read_from_cache()
var/replaced_css = file2text("[ASSET_CROSS_ROUND_CACHE_DIRECTORY]/spritesheet.[name].css")
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/replaced_css_filename = "data/spritesheets/spritesheet_[name].css"
rustg_file_write(replaced_css, replaced_css_filename)
SSassets.transport.register_asset("spritesheet_[name].css", replaced_css_filename)
fdel(replaced_css_filename)
return TRUE
// LEMON NOTE
// A GOON CODER SAYS BAD ICON ERRORS CAN BE THROWN BY THE "ICON CACHE"
// APPARENTLY IT MAKES ICONS IMMUTABLE
// LOOK INTO USING THE MUTABLE APPEARANCE PATTERN HERE
/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
//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]
if (sprites[sprite_name])
CRASH("duplicate sprite \"[sprite_name]\" in sheet [name] ([type])")
if (size)
var/position = size[SPRSZ_COUNT]++
var/icon/sheet = size[SPRSZ_ICON]
var/icon/sheet_copy = icon(sheet)
size[SPRSZ_STRIPPED] = null
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)
/**
* 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/ensure_stripped(sizes_to_strip = sizes)
for(var/size_id in sizes_to_strip)
var/size = sizes[size_id]
if (size[SPRSZ_STRIPPED])
continue
var/fname = "data/spritesheets/[name]_[size_id].png"
fcopy(size[SPRSZ_ICON], fname)
var/error = dll_call(RUST_G, "dmi_strip_metadata", fname)
if(length(error))
crash_with("Failed to strip [name]_[size_id].png: [error]")
size[SPRSZ_STRIPPED] = icon(fname)
/datum/asset/spritesheet/proc/generate_css()
var/list/out = list()
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_background_url("[name]_[size_id].png")]') no-repeat;}"
for (var/sprite_id in sprites)
var/sprite = sprites[sprite_id]
var/size_id = sprite[SPR_SIZE]
var/idx = sprite[SPR_IDX]
var/size = sizes[size_id]
var/icon/tiny = size[SPRSZ_ICON]
var/icon/big = size[SPRSZ_STRIPPED]
var/per_line = big.Width() / tiny.Width()
var/x = (idx % per_line) * tiny.Width()
var/y = round(idx / per_line) * tiny.Height()
out += ".[name][size_id].[sprite_id]{background-position:-[x]px -[y]px;}"
return out.Join("\n")
/datum/asset/spritesheet/proc/should_load_immediately()
return TRUE
/datum/asset/spritesheet/proc/Insert(sprite_name, icon/I, icon_state="", dir=SOUTH, frame=1, moving=FALSE, icon/forced=FALSE)
if(should_load_immediately())
queuedInsert(sprite_name, I, icon_state, dir, frame, moving)
else
to_generate += list(args.Copy())
/datum/asset/spritesheet/proc/InsertAll(prefix, icon/I, list/directions)
if (length(prefix))
prefix = "[prefix]-"
if (!directions)
directions = list(SOUTH)
for (var/icon_state_name in icon_states(I))
for (var/direction in directions)
var/prefix2 = (directions.len > 1) ? "[dir2text(direction)]-" : ""
Insert("[prefix][prefix2][icon_state_name]", I, icon_state=icon_state_name, dir=direction)
/datum/asset/spritesheet/proc/css_tag()
return {"<link rel="stylesheet" href="spritesheet_[name].css" />"}
/datum/asset/spritesheet/proc/css_filename()
return SSassets.transport.get_asset_url("spritesheet_[name].css")
/datum/asset/spritesheet/proc/icon_tag(sprite_name, var/html=TRUE)
var/sprite = sprites[sprite_name]
if (!sprite)
return null
var/size_id = sprite[SPR_SIZE]
if(html)
return {"<span class="[name][size_id] [sprite_name]"></span>"}
return "[name][size_id] [sprite_name]"
#undef SPR_SIZE
#undef SPR_IDX
#undef SPRSZ_COUNT
#undef SPRSZ_ICON
#undef SPRSZ_STRIPPED
/datum/asset/spritesheet/simple
_abstract = /datum/asset/spritesheet/simple
var/list/assets
/datum/asset/spritesheet/simple/register()
for (var/key in assets)
Insert(key, assets[key])
..()
//Generates assets based on iconstates of a single icon
/datum/asset/simple/icon_states
_abstract = /datum/asset/simple/icon_states
var/icon
var/list/directions = list(SOUTH)
var/frame = 1
var/movement_states = FALSE
var/prefix = "default" //asset_name = "[prefix].[icon_state_name].png"
var/generic_icon_names = FALSE //generate icon filenames using generate_asset_name() instead the above format
/datum/asset/simple/icon_states/register(_icon = icon)
for(var/icon_state_name in icon_states(_icon))
for(var/direction in directions)
var/asset = icon(_icon, icon_state_name, direction, frame, movement_states)
if (!asset)
continue
asset = fcopy_rsc(asset) //dedupe
var/prefix2 = (directions.len > 1) ? "[dir2text(direction)]." : ""
var/asset_name = sanitize_filename("[prefix].[prefix2][icon_state_name].png")
if (generic_icon_names)
asset_name = "[generate_asset_name(asset)].png"
SSassets.transport.register_asset(asset_name, asset)
/datum/asset/simple/icon_states/multiple_icons
_abstract = /datum/asset/simple/icon_states/multiple_icons
var/list/icons
/datum/asset/simple/icon_states/multiple_icons/register()
for(var/i in icons)
..(i)
//DEFINITIONS FOR ASSET DATUMS START HERE.
/datum/asset/simple/faction_icons
legacy = TRUE
assets = list(
"faction_EPMC.png" = 'html/images/factions/ECFlogo.png',
"faction_Zeng.png" = 'html/images/factions/zenghulogo.png',
"faction_Zavod.png" = 'html/images/factions/zavodlogo.png',
"faction_NT.png" = 'html/images/factions/nanotrasenlogo.png',
"faction_Idris.png" = 'html/images/factions/idrislogo.png',
"faction_Hepht.png" = 'html/images/factions/hephlogo.png',
"faction_INDEP.png" = 'html/images/factions/unaffiliatedlogo.png',
"faction_PMCG.png" = 'html/images/factions/pmcglogo.png',
"faction_Orion.png" = 'html/images/factions/orionlogo.png',
"faction_SCC.png" = 'html/images/factions/scclogo.png'
)
/datum/asset/simple/jquery
legacy = TRUE
assets = list(
"jquery.min.js" = 'code/modules/goonchat/browserassets/js/jquery.min.js',
)
/datum/asset/simple/goonchat
legacy = TRUE
assets = list(
"json2.min.js" = 'code/modules/goonchat/browserassets/js/json2.min.js',
"browserOutput.js" = 'code/modules/goonchat/browserassets/js/browserOutput.js',
"browserOutput.css" = 'code/modules/goonchat/browserassets/css/browserOutput.css',
"browserOutput_white.css" = 'code/modules/goonchat/browserassets/css/browserOutput_white.css'
)
/datum/asset/simple/namespaced/fontawesome
legacy = TRUE
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',
"v4shim.css" = 'html/font-awesome/css/v4-shims.min.css'
)
parents = list("font-awesome.css" = 'html/font-awesome/css/all.min.css')
/datum/asset/simple/namespaced/tgfont
assets = list(
"tgfont.eot" = file("tgui/packages/tgfont/static/tgfont.eot"),
"tgfont.woff2" = file("tgui/packages/tgfont/static/tgfont.woff2"),
)
parents = list(
"tgfont.css" = file("tgui/packages/tgfont/static/tgfont.css"),
)
/datum/asset/simple/misc
legacy = TRUE
assets = list(
"search.js" = 'html/search.js',
"panels.css" = 'html/panels.css',
"loading.gif" = 'html/images/loading.gif',
"ie-truth.min.js" = 'html/iestats/ie-truth.min.js',
"conninfo.min.js" = 'html/iestats/conninfo.min.js',
"copyright_infrigement.png" = 'html/images/copyright_infrigement.png',
"88x31.png" = 'html/images/88x31.png'
)
/datum/asset/simple/paper
legacy = TRUE
assets = list(
"talisman.png" = 'html/images/talisman.png',
"barcode0.png" = 'html/images/barcode0.png',
"barcode1.png" = 'html/images/barcode1.png',
"barcode2.png" = 'html/images/barcode2.png',
"barcode3.png" = 'html/images/barcode3.png',
"scclogo.png" = 'html/images/factions/scclogo.png',
"scclogo_small.png" = 'html/images/factions/scclogo_small.png',
"nanotrasenlogo.png" = 'html/images/factions/nanotrasenlogo.png',
"nanotrasenlogo_small.png" = 'html/images/factions/nanotrasenlogo_small.png',
"zhlogo.png" = 'html/images/factions/zenghulogo.png',
"zhlogo_small.png" = 'html/images/factions/zenghulogo_small.png',
"idrislogo.png" = 'html/images/factions/idrislogo.png',
"idrislogo_small.png" = 'html/images/factions/idrislogo_small.png',
"eridanilogo.png" = 'html/images/factions/ECFlogo.png',
"eridanilogo_small.png" = 'html/images/factions/ECFlogo_small.png',
"pmcglogo.png" = 'html/images/factions/pmcglogo.png',
"pmcglogo_small.png" = 'html/images/factions/pmcglogo_small.png',
"zavodlogo.png" = 'html/images/factions/zavodlogo.png',
"zavodlogo_small.png" = 'html/images/factions/zavodlogo_small.png',
"orionlogo.png" = 'html/images/factions/orionlogo.png',
"orionlogo_small.png" = 'html/images/factions/orionlogo_small.png',
"hplogolarge.png" = 'html/images/hplogolarge.png',
"hplogo.png" = 'html/images/factions/hephlogo.png',
"hplogo_small.png" = 'html/images/factions/hephlogo_small.png',
"beflag.png" = 'html/images/beflag.png',
"beflag_small.png" = 'html/images/beflag_small.png',
"elyraflag.png" = 'html/images/elyraflag.png',
"elyraflag_small.png" = 'html/images/elyraflag_small.png',
"solflag.png" = 'html/images/solflag.png',
"solflag_small.png" = 'html/images/solflag_small.png',
"cocflag.png" = 'html/images/cocflag.png',
"cocflag_small.png" = 'html/images/cocflag_small.png',
"domflag.png" = 'html/images/domflag.png',
"domflag_small.png" = 'html/images/domflag_small.png',
"nralakkflag.png" = 'html/images/nralakkflag.png',
"nralakkflag_small.png" = 'html/images/nralakkflag_small.png',
"praflag.png" = 'html/images/praflag.png',
"praflag_small.png" = 'html/images/praflag_small.png',
"dpraflag.png" = 'html/images/dpraflag.png',
"dpraflag_small.png" = 'html/images/dpraflag_small.png',
"nkaflag.png" = 'html/images/nkaflag.png',
"nkaflag_small.png" = 'html/images/nkaflag_small.png',
"izweskiflag.png" = 'html/images/izweskiflag.png',
"izweskiflag_small.png" = 'html/images/izweskiflag_small.png',
"goldenlogo.png" = 'html/images/factions/goldenlogo.png',
"goldenlogo_small.png" = 'html/images/factions/goldenlogo_small.png',
//scan images that appear on sensors
"no_data.png" = 'html/images/scans/no_data.png',
"horizon.png" = 'html/images/scans/horizon.png',
"intrepid.png" = 'html/images/scans/intrepid.png',
"spark.png" = 'html/images/scans/spark.png',
"canary.png" = 'html/images/scans/canary.png',
"corvette.png" = 'html/images/scans/corvette.png',
"elyran_corvette.png" = 'html/images/scans/elyran_corvette.png',
"dominian_corvette.png" = 'html/images/scans/dominian_corvette.png',
"tcfl_cetus.png" = 'html/images/scans/tcfl_cetus.png',
"unathi_corvette.png" = 'html/images/scans/unathi_corvette.png',
"unathi_freighter1.png" = 'html/images/scans/unathi_freighter1.png',
"unathi_freighter2.png" = 'html/images/scans/unathi_freighter2.png',
"unathi_guild_station.png" = 'html/images/scans/unathi_guild_station.png',
"unathi_diona_freighter.png" = 'html/images/scans/unathi_diona_freighter.png',
"hegemony_corvette.png" = 'html/images/scans/hegemony_corvette.png',
"ranger.png" = 'html/images/scans/ranger.png',
"oe_platform.png" = 'html/images/scans/oe_platform.png',
"hospital.png" = 'html/images/scans/hospital.png',
"skrell_freighter.png" = 'html/images/scans/skrell_freighter.png',
"diona.png" = 'html/images/scans/diona.png',
"hailstorm.png" = 'html/images/scans/hailstorm.png',
"headmaster.png" = 'html/images/scans/headmaster.png',
"pss.png" = 'html/images/scans/pss.png',
"nka_freighter.png" = 'html/images/scans/nka_freighter.png',
"pra_freighter.png" = 'html/images/scans/pra_freighter.png',
"tramp_freighter.png" = 'html/images/scans/tramp_freighter.png',
"line_cruiser.png" = 'html/images/scans/line_cruiser.png',
//planet scan images
"exoplanet_empty.png" = 'html/images/scans/exoplanets/exoplanet_empty.png',
"barren.png" = 'html/images/scans/exoplanets/barren.png',
"lava.png" = 'html/images/scans/exoplanets/lava.png',
"grove.png" = 'html/images/scans/exoplanets/grove.png',
"desert.png" = 'html/images/scans/exoplanets/desert.png',
"snow.png" = 'html/images/scans/exoplanets/snow.png',
"adhomai.png" = 'html/images/scans/exoplanets/adhomai.png',
"raskara.png" = 'html/images/scans/exoplanets/raskara.png',
"comet.png" = 'html/images/scans/exoplanets/comet.png',
"asteroid.png" = 'html/images/scans/exoplanets/asteroid.png',
//end scan images
"bluebird.woff" = 'html/fonts/OFL/Bluebird.woff',
"grandhotel.woff" = 'html/fonts/OFL/GrandHotel.woff',
"lashema.woff" = 'html/fonts/OFL/Lashema.woff',
"sourcecodepro.woff" = 'html/fonts/OFL/SourceCodePro.woff',
"sovjetbox.woff" = 'html/fonts/OFL/SovjetBox.woff',
"torsha.woff" = 'html/fonts/OFL/Torsha.woff',
"web3of9ascii.woff" = 'html/fonts/OFL/Web3Of9ASCII.woff',
"zeshit.woff" = 'html/fonts/OFL/zeshit.woff',
"bilboinc.woff" = 'html/fonts/OFL/BilboINC.woff',
"fproject.woff" = 'html/fonts/OFL/FProject.woff',
"gelasio.woff" = 'html/fonts/OFL/Gelasio.woff',
"mo5v56.woff" = 'html/fonts/OFL/Mo5V56.woff',
"runasans.woff" = 'html/fonts/OFL/RunaSans.woff',
"classica.woff" = 'html/fonts/OFL/Classica.woff',
"stormning.woff" = 'html/fonts/OFL/Stormning.woff',
"copt-b.woff" = 'html/fonts/OFL/Copt-B.woff',
"ducados.woff" = 'html/fonts/OFL/Ducados.woff',
"kawkabmono.woff" = 'html/fonts/OFL/KawkabMono.woff',
"kaushanscript.woff" = 'html/fonts/OFL/KaushanScript.woff'
)
/datum/asset/simple/changelog
legacy = TRUE
assets = list(
"changelog.css" = 'html/changelog.css',
"changelog.js" = 'html/changelog.js'
)
/datum/asset/spritesheet/goonchat
name = "chat"
/datum/asset/spritesheet/goonchat/register()
var/icon/I = icon('icons/accent_tags.dmi')
for(var/path in subtypesof(/datum/accent))
var/datum/accent/A = new path
if(A.tag_icon)
Insert(A.tag_icon, I, A.tag_icon)
..()
/datum/asset/spritesheet/vending
name = "vending"
/datum/asset/spritesheet/vending/create_spritesheets()
var/vending_products = list()
for(var/obj/machinery/vending/vendor as anything in typesof(/obj/machinery/vending))
vendor = new vendor()
for(var/each in list(vendor.products, vendor.contraband, vendor.premium))
vending_products |= each
qdel(vendor)
for(var/path in vending_products)
var/atom/item = path
if(!ispath(item, /atom))
continue
var/icon_file = initial(item.icon)
var/icon_state = initial(item.icon_state)
#ifdef UNIT_TEST
var/icon_states_list = icon_states(icon_file)
if(!(icon_state in icon_states_list))
var/icon_states_string
for(var/s in icon_states_list)
if(!icon_states_string)
icon_states_string = "[json_encode(s)](\ref[s])"
else
icon_states_string += ", [json_encode(s)](\ref[s])"
stack_trace("[item] has an invalid icon state, icon=[icon_file], icon_state=[json_encode(icon_state)](\ref[icon_state]), icon_states=[icon_states_string]")
continue
#endif
var/icon/I = icon(icon_file, icon_state, SOUTH)
var/c = initial(item.color)
if(!isnull(c) && c != "#FFFFFF")
I.Blend(c, ICON_MULTIPLY)
var/imgid = ckey("[item]")
item = new item()
if(ispath(item, /obj/item/seeds))
// thanks seeds for being overlays defined at runtime
var/obj/item/seeds/S = item
if(!S.seed && S.seed_type && !isnull(SSplants.seeds) && SSplants.seeds[S.seed_type])
S.seed = SSplants.seeds[S.seed_type]
I = S.update_appearance(TRUE)
Insert(imgid, I, forced=I)
else
item.update_icon()
if(item.overlay_queued)
item.compile_overlays()
if(item.overlays.len)
I = getFlatIcon(item) // forgive me for my performance sins
Insert(imgid, I, forced=I)
else
Insert(imgid, I)
qdel(item)
/datum/asset/spritesheet/chem_master
name = "chemmaster"
var/list/bottle_sprites = list("bottle-1", "bottle-2", "bottle-3", "bottle-4", "bottle-5", "bottle-6")
var/max_pill_sprite = 20
/datum/asset/spritesheet/chem_master/register()
for (var/i = 1 to max_pill_sprite)
Insert("pill[i]", 'icons/obj/chemical.dmi', "pill[i]")
for (var/sprite in bottle_sprites)
Insert(sprite, icon('icons/obj/chemical.dmi', sprite))
return ..()
/// 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 = sort_list(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]))
/// Generate a filename for this asset
/// The same asset will always lead to the same asset name
/// (Generated names do not include file extention.)
/proc/generate_asset_name(file)
return "asset.[md5(fcopy_rsc(file))]"
@@ -0,0 +1,45 @@
/// 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.
if (asset_cache_job > 0 && asset_cache_job <= last_asset_job && !(completed_asset_jobs["[asset_cache_job]"]))
completed_asset_jobs["[asset_cache_job]"] = TRUE
last_completed_asset_job = max(last_completed_asset_job, asset_cache_job)
else
return asset_cache_job || TRUE
/// Process asset cache client topic calls for `"asset_cache_preload_data=[HTML+JSON_STRING]"`
/client/proc/asset_cache_preload_data(data)
var/json = data
var/list/preloaded_assets = json_decode(json)
for (var/preloaded_asset in preloaded_assets)
if (copytext(preloaded_asset, findlasttext(preloaded_asset, ".")+1) in list("js", "jsm", "htm", "html"))
preloaded_assets -= preloaded_asset
continue
sent_assets |= preloaded_assets
/// 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
src << browse(json_encode(sent_assets), "file=asset_data.json&display=0")
/// 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/browse_queue_flush(timeout = 50)
var/job = ++last_asset_job
var/t = 0
var/timeout_time = timeout
src << browse({"<script>window.location.href="?asset_cache_confirm_arrival=[job]"</script>"}, "window=asset_cache_browser&file=asset_cache_send_verify.htm")
while(!completed_asset_jobs["[job]"] && t < timeout_time) // Reception is handled in Topic()
stoplag(1) // Lock up the caller until this is received.
t++
if (t < timeout_time)
return TRUE
@@ -0,0 +1,54 @@
/**
* # 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
/// 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
///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)
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
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
+22
View File
@@ -0,0 +1,22 @@
/datum/asset/simple/ntos
assets = list(
"batt_100.gif" = 'icons/NTOS/status_icons/batt_100.gif',
"batt_80.gif" = 'icons/NTOS/status_icons/batt_80.gif',
"batt_60.gif" = 'icons/NTOS/status_icons/batt_60.gif',
"batt_40.gif" = 'icons/NTOS/status_icons/batt_40.gif',
"batt_20.gif" = 'icons/NTOS/status_icons/batt_20.gif',
"batt_5.gif" = 'icons/NTOS/status_icons/batt_5.gif',
"sig_none.gif" = 'icons/NTOS/status_icons/sig_none.gif',
"sig_low.gif" = 'icons/NTOS/status_icons/sig_low.gif',
"sig_high.gif" = 'icons/NTOS/status_icons/sig_high.gif',
"sig_lan.gif" = 'icons/NTOS/status_icons/sig_lan.gif',
"charging.gif" = 'icons/NTOS/status_icons/charging.gif',
"alarm_green.gif" = 'icons/NTOS/status_icons/alarm_green.gif',
"alarm_red.gif" = 'icons/NTOS/status_icons/alarm_red.gif',
"downloader_finished.gif" = 'icons/NTOS/status_icons/downloader_finished.gif',
"downloader_running.gif" = 'icons/NTOS/status_icons/downloader_running.gif',
"ntnrc_idle.gif" = 'icons/NTOS/status_icons/ntnrc_idle.gif',
"ntnrc_new.gif" = 'icons/NTOS/status_icons/ntnrc_new.gif',
"power_norm.gif" = 'icons/NTOS/status_icons/power_norm.gif',
"power_warn.gif" = 'icons/NTOS/status_icons/power_warn.gif'
)
+13
View File
@@ -0,0 +1,13 @@
/datum/asset/simple/tgui
keep_local_name = TRUE
assets = list(
"tgui.bundle.js" = file("tgui/public/tgui.bundle.js"),
"tgui.bundle.css" = file("tgui/public/tgui.bundle.css"),
)
/datum/asset/simple/tgui_panel
keep_local_name = TRUE
assets = list(
"tgui-panel.bundle.js" = file("tgui/public/tgui-panel.bundle.js"),
"tgui-panel.bundle.css" = file("tgui/public/tgui-panel.bundle.css"),
)
@@ -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 = TRUE
/// 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 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 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 (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
@@ -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
@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
</head>
<body>
<script>
//this is used over window.location because window.location has a character limit in IE.
function sendbyond(text) {
var xhr = new XMLHttpRequest();
xhr.open('GET', '?'+text, true);
xhr.send(null);
}
var xhr = new XMLHttpRequest();
xhr.open('GET', 'asset_data.json', true);
xhr.responseType = 'text';
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
var status = xhr.status;
if (status >= 200 && status < 400) {
sendbyond('asset_cache_preload_data=' + encodeURIComponent(xhr.responseText));
}
}
};
xhr.send(null);
</script>
</body>
</html>