mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-10 01:34:01 +00:00
[MIRROR] [Ready] CDN browser assets! (#312)
* [Ready] CDN browser assets! (#52681) Rewrites the asset_cache system to handle sending assets to a CDN via a webroot. see https://github.com/MrStonedOne/tgstation/blob/asset-cdn/code/modules/asset_cache/readme.md Fixed a lot of bugs with assets, removed some dead code. Changes: Moved asset cache code to transport datums, the currently loaded one is located at SSassets.transport, asset cache calls made before the config is loaded use the simple browse_rsc transport. Added subsystem call for when the config loads or reloads. Added a webroot CDN asset transport. assets are saved to a file in a format based on the file's hash (currently md5). Assets that don't use get_asset_url or get_url_mappings (such as browser assets referred to by static html files like changelog.html or static css files) can be saved to browse_rsc even when in cdn asset mode by setting legacy to TRUE on the datum returned by register_assets Added a system for saving assets on a cdn in a hash based namespace (folder), assets within the same namespace will always be able to refer to each other by relative names. (used to allow cdn'ing font awesome without having to make something that regenerates it's css files.). The simple/namespaced asset cache datum helper will handle generating a namespace composed of the combined md5 of everything in the same datum, as well as registering them properly. Moved external resource from a snowflake loaded file to a config entry, added it to resources.txt To ensure the system breaks in local testing in any situation that wouldn't work in cdn mode, the simple transport will mutate the filenames of non-legacy and non-namespaced assets and return this with get_asset_url. Simple transport's passive send of all roundstart assets to all clients is now a config that defaults to off. this is to break race conditions during local testings from devs accidentally relying on this instead of using send() properly. cl refactor: Interface assets (js/css/images) can now be managed using an external webserver instead of byond's one at a time file transfer queue. admin: Adds admin verb toggle-cdn that allows admins to disable the external webserver asset transport and revert to the old system. Useful if the webserver backing this goes down (thanks cloudflare). config: New config file, resources.txt, (must be loaded by an $include statement from the main config) server: The external_rsc_urls.txt config has been moved to the main config system. /cl Porting notes: Interface webpages must refer to their assets (css/js/image/etc) by a generated url, or the asset must register itself as a legacy asset. The system is designed to break in localtest (on simple/legacy mode) in most situations that would break in cdn mode. Requires latest tgui. The webserver must set the proper CORS headers for font files or font awesome (and other fonts) won't load. /tg/'s webserver config: https://gist.github.com/MrStonedOne/523388b2f161af832292d98a8aad0eae * [Ready] CDN browser assets! Co-authored-by: Kyle Spier-Swenson <kyleshome@gmail.com>
This commit is contained in:
@@ -1,8 +1,3 @@
|
||||
//Sends resource files to client cache
|
||||
/client/proc/getFiles(...)
|
||||
for(var/file in args)
|
||||
src << browse_rsc(file)
|
||||
|
||||
/client/proc/browse_files(root_type=BROWSE_ROOT_ALL_LOGS, max_iterations=10, list/valid_extensions=list("txt","log","htm", "html"))
|
||||
// wow why was this ever a parameter
|
||||
var/root = "data/logs/"
|
||||
|
||||
@@ -1103,6 +1103,14 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0
|
||||
/// Save file used in icon2base64. Used for converting icons to base64.
|
||||
GLOBAL_DATUM_INIT(dummySave, /savefile, new("tmp/dummySave.sav")) //Cache of icons for the browser output
|
||||
|
||||
|
||||
/// 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))]"
|
||||
|
||||
|
||||
/**
|
||||
* Converts an icon to base64. Operates by putting the icon in the iconCache savefile,
|
||||
* exporting it as text, and then parsing the base64 from that.
|
||||
@@ -1138,10 +1146,10 @@ GLOBAL_DATUM_INIT(dummySave, /savefile, new("tmp/dummySave.sav")) //Cache of ico
|
||||
if (isfile(thing)) //special snowflake
|
||||
var/name = sanitize_filename("[generate_asset_name(thing)].png")
|
||||
if (!SSassets.cache[name])
|
||||
register_asset(name, thing)
|
||||
SSassets.transport.register_asset(name, thing)
|
||||
for (var/thing2 in targets)
|
||||
send_asset(thing2, key)
|
||||
return "<img class='icon icon-misc' src=\"[url_encode(name)]\">"
|
||||
SSassets.transport.send_assets(thing2, name)
|
||||
return "<img class='icon icon-misc' src='[SSassets.transport.get_asset_url(name)]'>"
|
||||
var/atom/A = thing
|
||||
if (isnull(dir))
|
||||
dir = A.dir
|
||||
@@ -1163,11 +1171,11 @@ GLOBAL_DATUM_INIT(dummySave, /savefile, new("tmp/dummySave.sav")) //Cache of ico
|
||||
|
||||
key = "[generate_asset_name(I)].png"
|
||||
if(!SSassets.cache[key])
|
||||
register_asset(key, I)
|
||||
SSassets.transport.register_asset(key, I)
|
||||
for (var/thing2 in targets)
|
||||
send_asset(thing2, key)
|
||||
SSassets.transport.send_assets(thing2, key)
|
||||
|
||||
return "<img class='icon icon-[icon_state]' src=\"[url_encode(key)]\">"
|
||||
return "<img class='icon icon-[icon_state]' src='[SSassets.transport.get_asset_url(key)]'>"
|
||||
|
||||
/proc/icon2base64html(thing)
|
||||
if (!thing)
|
||||
|
||||
@@ -487,3 +487,8 @@ GLOBAL_LIST_INIT(modulo_angle_to_dir, list(NORTH,NORTHEAST,EAST,SOUTHEAST,SOUTH,
|
||||
return "turf"
|
||||
else //regex everything else (works for /proc too)
|
||||
return lowertext(replacetext("[the_type]", "[type2parent(the_type)]/", ""))
|
||||
|
||||
/// Return html to load a url.
|
||||
/// for use inside of browse() calls to html assets that might be loaded on a cdn.
|
||||
/proc/url2htmlloader(url)
|
||||
return {"<html><head><meta http-equiv="refresh" content="0;URL='[url]'"/></head><body onLoad="parent.location='[url]'"></body></html>"}
|
||||
|
||||
@@ -53,6 +53,9 @@
|
||||
LoadPolicy()
|
||||
LoadChatFilter()
|
||||
|
||||
if (Master)
|
||||
Master.OnConfigLoad()
|
||||
|
||||
/datum/controller/configuration/proc/full_wipe()
|
||||
if(IsAdminAdvancedProcCall())
|
||||
return
|
||||
|
||||
30
code/controllers/configuration/entries/resources.dm
Normal file
30
code/controllers/configuration/entries/resources.dm
Normal file
@@ -0,0 +1,30 @@
|
||||
/datum/config_entry/keyed_list/external_rsc_urls
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_FLAG
|
||||
|
||||
/datum/config_entry/flag/asset_simple_preload
|
||||
|
||||
/datum/config_entry/string/asset_transport
|
||||
/datum/config_entry/string/asset_transport/ValidateAndSet(str_val)
|
||||
return (lowertext(str_val) in list("simple", "webroot")) && ..(lowertext(str_val))
|
||||
|
||||
/datum/config_entry/string/asset_cdn_webroot
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/string/asset_cdn_webroot/ValidateAndSet(str_var)
|
||||
if (!str_var || trim(str_var) == "")
|
||||
return FALSE
|
||||
if (str_var && str_var[length(str_var)] != "/")
|
||||
str_var += "/"
|
||||
return ..(str_var)
|
||||
|
||||
/datum/config_entry/string/asset_cdn_url
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
default = null
|
||||
|
||||
/datum/config_entry/string/asset_cdn_url/ValidateAndSet(str_var)
|
||||
if (!str_var || trim(str_var) == "")
|
||||
return FALSE
|
||||
if (str_var && str_var[length(str_var)] != "/")
|
||||
str_var += "/"
|
||||
return ..(str_var)
|
||||
@@ -633,3 +633,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
processing = CONFIG_GET(number/mc_tick_rate/base_mc_tick_rate)
|
||||
else if (client_count > CONFIG_GET(number/mc_tick_rate/high_pop_mc_mode_amount))
|
||||
processing = CONFIG_GET(number/mc_tick_rate/high_pop_mc_tick_rate)
|
||||
|
||||
/datum/controller/master/proc/OnConfigLoad()
|
||||
for (var/thing in subsystems)
|
||||
var/datum/controller/subsystem/SS = thing
|
||||
SS.OnConfigLoad()
|
||||
|
||||
@@ -208,6 +208,8 @@
|
||||
if(SS_SLEEPING)
|
||||
state = SS_PAUSING
|
||||
|
||||
/// Called after the config has been loaded or reloaded.
|
||||
/datum/controller/subsystem/proc/OnConfigLoad()
|
||||
|
||||
//used to initialize the subsystem AFTER the map has loaded
|
||||
/datum/controller/subsystem/Initialize(start_timeofday)
|
||||
|
||||
@@ -4,6 +4,23 @@ SUBSYSTEM_DEF(assets)
|
||||
flags = SS_NO_FIRE
|
||||
var/list/cache = list()
|
||||
var/list/preload = list()
|
||||
var/datum/asset_transport/transport = new()
|
||||
|
||||
/datum/controller/subsystem/assets/OnConfigLoad()
|
||||
var/newtransporttype = /datum/asset_transport
|
||||
switch (CONFIG_GET(string/asset_transport))
|
||||
if ("webroot")
|
||||
newtransporttype = /datum/asset_transport/webroot
|
||||
|
||||
if (newtransporttype == transport.type)
|
||||
return
|
||||
|
||||
var/datum/asset_transport/newtransport = new newtransporttype ()
|
||||
if (newtransport.validate_config())
|
||||
transport = newtransport
|
||||
transport.Load()
|
||||
|
||||
|
||||
|
||||
/datum/controller/subsystem/assets/Initialize(timeofday)
|
||||
for(var/type in typesof(/datum/asset))
|
||||
@@ -11,8 +28,6 @@ SUBSYSTEM_DEF(assets)
|
||||
if (type != initial(A._abstract))
|
||||
get_asset_datum(type)
|
||||
|
||||
preload = cache.Copy() //don't preload assets generated during the round
|
||||
transport.Initialize(cache)
|
||||
|
||||
for(var/client/C in GLOB.clients)
|
||||
addtimer(CALLBACK(GLOBAL_PROC, .proc/getFilesSlow, C, preload, FALSE), 10)
|
||||
..()
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
var/window_options = "can_close=1;can_minimize=1;can_maximize=0;can_resize=1;titlebar=1;" // window option is set using window_id
|
||||
var/stylesheets[0]
|
||||
var/scripts[0]
|
||||
var/title_image
|
||||
var/head_elements
|
||||
var/body_elements
|
||||
var/head_content = ""
|
||||
var/content = ""
|
||||
var/static/datum/asset/simple/namespaced/common/common_asset = get_asset_datum(/datum/asset/simple/namespaced/common)
|
||||
|
||||
|
||||
/datum/browser/New(nuser, nwindow_id, ntitle = 0, nwidth = 0, nheight = 0, atom/nref = null)
|
||||
@@ -27,7 +27,6 @@
|
||||
height = nheight
|
||||
if (nref)
|
||||
ref = nref
|
||||
add_stylesheet("common", 'html/browser/common.css') // this CSS sheet is common to all UIs
|
||||
|
||||
/datum/browser/proc/add_head_content(nhead_content)
|
||||
head_content = nhead_content
|
||||
@@ -35,9 +34,6 @@
|
||||
/datum/browser/proc/set_window_options(nwindow_options)
|
||||
window_options = nwindow_options
|
||||
|
||||
/datum/browser/proc/set_title_image(ntitle_image)
|
||||
//title_image = ntitle_image
|
||||
|
||||
/datum/browser/proc/add_stylesheet(name, file)
|
||||
if (istype(name, /datum/asset/spritesheet))
|
||||
var/datum/asset/spritesheet/sheet = name
|
||||
@@ -48,11 +44,11 @@
|
||||
stylesheets[asset_name] = file
|
||||
|
||||
if (!SSassets.cache[asset_name])
|
||||
register_asset(asset_name, file)
|
||||
SSassets.transport.register_asset(asset_name, file)
|
||||
|
||||
/datum/browser/proc/add_script(name, file)
|
||||
scripts["[ckey(name)].js"] = file
|
||||
register_asset("[ckey(name)].js", file)
|
||||
SSassets.transport.register_asset("[ckey(name)].js", file)
|
||||
|
||||
/datum/browser/proc/set_content(ncontent)
|
||||
content = ncontent
|
||||
@@ -62,15 +58,13 @@
|
||||
|
||||
/datum/browser/proc/get_header()
|
||||
var/file
|
||||
head_content += "<link rel='stylesheet' type='text/css' href='[common_asset.get_url_mappings()["common.css"]]'>"
|
||||
for (file in stylesheets)
|
||||
head_content += "<link rel='stylesheet' type='text/css' href='[file]'>"
|
||||
head_content += "<link rel='stylesheet' type='text/css' href='[SSassets.transport.get_asset_url(file)]'>"
|
||||
|
||||
|
||||
for (file in scripts)
|
||||
head_content += "<script type='text/javascript' src='[file]'></script>"
|
||||
|
||||
var/title_attributes = "class='uiTitle'"
|
||||
if (title_image)
|
||||
title_attributes = "class='uiTitle icon' style='background-image: url([title_image]);'"
|
||||
head_content += "<script type='text/javascript' src='[SSassets.transport.get_asset_url(file)]'></script>"
|
||||
|
||||
return {"<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
@@ -81,7 +75,7 @@
|
||||
</head>
|
||||
<body scroll=auto>
|
||||
<div class='uiWrapper'>
|
||||
[title ? "<div class='uiTitleWrapper'><div [title_attributes]><tt>[title]</tt></div></div>" : ""]
|
||||
[title ? "<div class='uiTitleWrapper'><div class='uiTitle'><tt>[title]</tt></div></div>" : ""]
|
||||
<div class='uiContent'>
|
||||
"}
|
||||
//" This is here because else the rest of the file looks like a string in notepad++.
|
||||
@@ -107,10 +101,11 @@
|
||||
var/window_size = ""
|
||||
if (width && height)
|
||||
window_size = "size=[width]x[height];"
|
||||
common_asset.send(user)
|
||||
if (stylesheets.len)
|
||||
send_asset_list(user, stylesheets)
|
||||
SSassets.transport.send_assets(user, stylesheets)
|
||||
if (scripts.len)
|
||||
send_asset_list(user, scripts)
|
||||
SSassets.transport.send_assets(user, scripts)
|
||||
user << browse(get_content(), "window=[window_id];[window_size][window_options]")
|
||||
if (use_onclose)
|
||||
setup_onclose()
|
||||
@@ -419,12 +414,6 @@
|
||||
if (A.selectedbutton)
|
||||
return list("button" = A.selectedbutton, "settings" = A.settings)
|
||||
|
||||
// This will allow you to show an icon in the browse window
|
||||
// This is added to mob so that it can be used without a reference to the browser object
|
||||
// There is probably a better place for this...
|
||||
/mob/proc/browse_rsc_icon(icon, icon_state, dir = -1)
|
||||
|
||||
|
||||
// Registers the on-close verb for a browse window (client/verb/.windowclose)
|
||||
// this will be called when the close-button of a window is pressed.
|
||||
//
|
||||
|
||||
@@ -277,7 +277,6 @@ GLOBAL_LIST_INIT(arcade_prize_pool, list(
|
||||
if(user.client) //mainly here to avoid a runtime when the player gets gibbed when losing the emag mode.
|
||||
var/datum/browser/popup = new(user, "arcade", "Space Villain 2000")
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
|
||||
popup.open()
|
||||
|
||||
|
||||
@@ -844,7 +843,6 @@ GLOBAL_LIST_INIT(arcade_prize_pool, list(
|
||||
dat += "<P ALIGN=Right><a href='byond://?src=[REF(src)];close=1'>Close</a></P>"
|
||||
var/datum/browser/popup = new(user, "arcade", "The Orion Trail",400,700)
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
|
||||
popup.open()
|
||||
return
|
||||
|
||||
|
||||
@@ -355,7 +355,6 @@ GLOBAL_VAR_INIT(time_last_changed_position, 0)
|
||||
dat = list("<tt>", header.Join(), body, "<br></tt>")
|
||||
var/datum/browser/popup = new(user, "id_com", src.name, 900, 620)
|
||||
popup.set_content(dat.Join())
|
||||
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
|
||||
popup.open()
|
||||
|
||||
/obj/machinery/computer/card/Topic(href, href_list)
|
||||
|
||||
@@ -445,7 +445,6 @@
|
||||
|
||||
|
||||
var/datum/browser/popup = new(user, "communications", "Communications Console", 400, 500)
|
||||
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
|
||||
|
||||
if(issilicon(user))
|
||||
var/dat2 = interact_ai(user) // give the AI a different interact proc to limit its access
|
||||
|
||||
@@ -252,6 +252,10 @@
|
||||
ui = new(user, src, "DnaConsole")
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/computer/scan_consolenew/ui_assets()
|
||||
. = ..() || list()
|
||||
. += get_asset_datum(/datum/asset/simple/genetics)
|
||||
|
||||
/obj/machinery/computer/scan_consolenew/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
|
||||
@@ -172,7 +172,6 @@
|
||||
dat += "<A href='?src=[REF(src)];login=1'>{Log In}</A>"
|
||||
var/datum/browser/popup = new(user, "med_rec", "Medical Records Console", 600, 400)
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
|
||||
popup.open()
|
||||
|
||||
/obj/machinery/computer/med_data/Topic(href, href_list)
|
||||
|
||||
@@ -67,7 +67,6 @@
|
||||
add_fingerprint(usr)
|
||||
var/datum/browser/popup = new(user, "computer", title, 400, 500)
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
|
||||
popup.open()
|
||||
|
||||
/obj/machinery/computer/pod/process()
|
||||
|
||||
@@ -65,7 +65,6 @@
|
||||
dat += "<HR><A href='?src=[REF(src)];lock=1'>{Log Out}</A>"
|
||||
var/datum/browser/popup = new(user, "computer", "Prisoner Management Console", 400, 500)
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
|
||||
popup.open()
|
||||
return
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
dat += {"
|
||||
|
||||
<head>
|
||||
<script src="jquery.min.js"></script>
|
||||
<script src="[SSassets.transport.get_asset_url("jquery.min.js")]"></script>
|
||||
<script type='text/javascript'>
|
||||
|
||||
function updateSearch(){
|
||||
@@ -258,7 +258,6 @@
|
||||
dat += "<A href='?src=[REF(src)];choice=Log In'>{Log In}</A>"
|
||||
var/datum/browser/popup = new(user, "secure_rec", "Security Records Console", 600, 400)
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
|
||||
popup.open()
|
||||
return
|
||||
|
||||
|
||||
@@ -88,7 +88,6 @@
|
||||
|
||||
var/datum/browser/popup = new(user, "warrant", "Security Warrant Console", 600, 400)
|
||||
popup.set_content(dat.Join())
|
||||
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
|
||||
popup.open()
|
||||
|
||||
/obj/machinery/computer/warrant/Topic(href, href_list)
|
||||
|
||||
@@ -254,7 +254,6 @@
|
||||
|
||||
/obj/machinery/door_buttons/airlock_controller/ui_interact(mob/user)
|
||||
var/datum/browser/popup = new(user, "computer", name)
|
||||
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
|
||||
popup.set_content(returnText())
|
||||
popup.open()
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
. = ..()
|
||||
user.set_machine(src)
|
||||
var/datum/browser/popup = new(user, "computer", name) // Set up the popup browser window
|
||||
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
|
||||
popup.set_content(return_text())
|
||||
popup.open()
|
||||
|
||||
|
||||
@@ -495,7 +495,6 @@ GLOBAL_LIST_EMPTY(allCasters)
|
||||
dat+="<A href='?src=[REF(src)];setScreen=[0]'>Return</A>"
|
||||
var/datum/browser/popup = new(human_or_robot_user, "newscaster_main", "Newscaster Unit #[unit_no]", 400, 600)
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(human_or_robot_user.browse_rsc_icon(icon, icon_state))
|
||||
popup.open()
|
||||
|
||||
/obj/machinery/newscaster/Topic(href, href_list)
|
||||
|
||||
@@ -211,7 +211,6 @@ GLOBAL_LIST_EMPTY(req_console_ckey_departments)
|
||||
CRASH("No UI for src. Screen var is: [screen]")
|
||||
var/datum/browser/popup = new(user, "req_console", "[department] Requests Console", 450, 440)
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
|
||||
popup.open()
|
||||
return
|
||||
|
||||
|
||||
@@ -160,7 +160,6 @@
|
||||
|
||||
var/datum/browser/popup = new(user, "slotmachine", "Slot Machine")
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
|
||||
popup.open()
|
||||
|
||||
/obj/machinery/computer/slot_machine/Topic(href, href_list)
|
||||
|
||||
@@ -239,7 +239,6 @@
|
||||
message = defaultmsg
|
||||
var/datum/browser/popup = new(user, "hologram_console", name, 700, 700)
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
|
||||
popup.open()
|
||||
|
||||
/obj/machinery/computer/message_monitor/proc/BruteForce(mob/user)
|
||||
|
||||
@@ -196,7 +196,6 @@
|
||||
|
||||
var/datum/browser/popup = new(user, "instrument", instrumentObj.name, 700, 500)
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(user.browse_rsc_icon(instrumentObj.icon, instrumentObj.icon_state))
|
||||
popup.open()
|
||||
|
||||
/datum/song/proc/ParseSong(text)
|
||||
|
||||
@@ -34,8 +34,6 @@ GLOBAL_VAR(restart_counter)
|
||||
|
||||
log_world("World loaded at [time_stamp()]!")
|
||||
|
||||
SetupExternalRSC()
|
||||
|
||||
make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once)
|
||||
|
||||
GLOB.config_error_log = GLOB.world_manifest_log = GLOB.world_pda_log = GLOB.world_job_debug_log = GLOB.sql_error_log = GLOB.world_href_log = GLOB.world_runtime_log = GLOB.world_attack_log = GLOB.world_game_log = GLOB.world_econ_log = GLOB.world_shuttle_log = "data/logs/config_error.[GUID()].log" //temporary file used to record errors with loading config, moved to log directory once logging is set bl
|
||||
@@ -98,16 +96,6 @@ GLOBAL_VAR(restart_counter)
|
||||
#endif
|
||||
SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, /proc/_addtimer, cb, 10 SECONDS))
|
||||
|
||||
/world/proc/SetupExternalRSC()
|
||||
#if (PRELOAD_RSC == 0)
|
||||
GLOB.external_rsc_urls = world.file2list("[global.config.directory]/external_rsc_urls.txt","\n")
|
||||
var/i=1
|
||||
while(i<=GLOB.external_rsc_urls.len)
|
||||
if(GLOB.external_rsc_urls[i])
|
||||
i++
|
||||
else
|
||||
GLOB.external_rsc_urls.Cut(i,i+1)
|
||||
#endif
|
||||
|
||||
/world/proc/SetupLogs()
|
||||
var/override_dir = params[OVERRIDE_LOG_DIRECTORY_PARAMETER]
|
||||
|
||||
@@ -125,7 +125,8 @@ GLOBAL_PROTECT(admin_verbs_server)
|
||||
/client/proc/forcerandomrotate,
|
||||
/client/proc/adminchangemap,
|
||||
/client/proc/panicbunker,
|
||||
/client/proc/toggle_hub
|
||||
/client/proc/toggle_hub,
|
||||
/client/proc/toggle_cdn
|
||||
)
|
||||
GLOBAL_LIST_INIT(admin_verbs_debug, world.AVerbsDebug())
|
||||
GLOBAL_PROTECT(admin_verbs_debug)
|
||||
@@ -176,7 +177,8 @@ GLOBAL_PROTECT(admin_verbs_debug)
|
||||
/datum/admins/proc/view_refs,
|
||||
/datum/admins/proc/view_del_failures,
|
||||
#endif
|
||||
/client/proc/check_timer_sources
|
||||
/client/proc/check_timer_sources,
|
||||
/client/proc/toggle_cdn
|
||||
)
|
||||
GLOBAL_LIST_INIT(admin_verbs_possess, list(/proc/possess, /proc/release))
|
||||
GLOBAL_PROTECT(admin_verbs_possess)
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
/datum/admins/proc/edit_admin_permissions(action, target, operation, page)
|
||||
if(!check_rights(R_PERMISSIONS))
|
||||
return
|
||||
var/list/output = list("<link rel='stylesheet' type='text/css' href='panels.css'><a href='?_src_=holder;[HrefToken()];editrightsbrowser=1'>\[Permissions\]</a>")
|
||||
var/datum/asset/asset_cache_datum = get_asset_datum(/datum/asset/group/permissions)
|
||||
asset_cache_datum.send(usr)
|
||||
var/list/output = list("<link rel='stylesheet' type='text/css' href='[SSassets.transport.get_asset_url("panels.css")]'><a href='?_src_=holder;[HrefToken()];editrightsbrowser=1'>\[Permissions\]</a>")
|
||||
if(action)
|
||||
output += " | <a href='?_src_=holder;[HrefToken()];editrightsbrowserlog=1;editrightspage=0'>\[Log\]</a> | <a href='?_src_=holder;[HrefToken()];editrightsbrowsermanage=1'>\[Management\]</a><hr style='background:#000000; border:0; height:3px'>"
|
||||
else
|
||||
@@ -92,7 +94,7 @@
|
||||
<head>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
|
||||
<title>Permissions Panel</title>
|
||||
<script type='text/javascript' src='search.js'></script>
|
||||
<script type='text/javascript' src='[SSassets.transport.get_asset_url("search.js")]'></script>
|
||||
</head>
|
||||
<body onload='selectTextField();updateSearch();'>
|
||||
<div id='main'><table id='searchable' cellspacing='0'>
|
||||
@@ -137,7 +139,7 @@
|
||||
if(IsAdminAdvancedProcCall())
|
||||
to_chat(usr, "<span class='admin prefix'>Admin Edit blocked: Advanced ProcCall detected.</span>", confidential = TRUE)
|
||||
return
|
||||
var/datum/asset/permissions_assets = get_asset_datum(/datum/asset/simple/permissions)
|
||||
var/datum/asset/permissions_assets = get_asset_datum(/datum/asset/simple/namespaced/common)
|
||||
permissions_assets.send(src)
|
||||
var/admin_key = href_list["key"]
|
||||
var/admin_ckey = ckey(admin_key)
|
||||
|
||||
@@ -502,7 +502,7 @@
|
||||
alphatext = "filter: alpha(opacity=[alpha]); opacity: [alpha/100];"
|
||||
var/list/data = list("<div style='margin:0px;[alphatext]'><p class='severity'>")
|
||||
if(severity)
|
||||
data += "<img src='[severity]_button.png' height='24' width='24'></img> "
|
||||
data += "<img src='[SSassets.transport.get_asset_url("[severity]_button.png")]' height='24' width='24'></img> "
|
||||
data += "<b>[timestamp] | [server] | [admin_key][secret ? " | <i>- Secret</i>" : ""]"
|
||||
if(expire_timestamp)
|
||||
data += " | Expires [expire_timestamp]"
|
||||
|
||||
@@ -65,14 +65,16 @@
|
||||
set name = "Spawn reagent container"
|
||||
if(!check_rights())
|
||||
return
|
||||
|
||||
var/datum/asset/asset_datum = get_asset_datum(/datum/asset/simple/namespaced/common)
|
||||
asset_datum.send()
|
||||
//Could somebody tell me why this isn't using the browser datum, given that it copypastes all of browser datum's html
|
||||
var/dat = {"
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
|
||||
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
|
||||
<link rel='stylesheet' type='text/css' href='common.css'>
|
||||
<link rel='stylesheet' type='text/css' href='[SSassets.transport.get_asset_url("common.css")]'>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.7/js/select2.full.min.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.7/css/select2.min.css">
|
||||
|
||||
@@ -79,3 +79,31 @@
|
||||
load_admins()
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Reload All Admins") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
message_admins("[key_name_admin(usr)] manually reloaded admins")
|
||||
|
||||
/client/proc/toggle_cdn()
|
||||
set name = "Toggle CDN"
|
||||
set category = "Server"
|
||||
var/static/admin_disabled_cdn_transport = null
|
||||
if (alert(usr, "Are you sure you want to toggle the CDN asset transport?", "Confirm", "Yes", "No") != "Yes")
|
||||
return
|
||||
var/current_transport = CONFIG_GET(string/asset_transport)
|
||||
if (!current_transport || current_transport == "simple")
|
||||
if (admin_disabled_cdn_transport)
|
||||
CONFIG_SET(string/asset_transport, admin_disabled_cdn_transport)
|
||||
admin_disabled_cdn_transport = null
|
||||
SSassets.OnConfigLoad()
|
||||
message_admins("[key_name_admin(usr)] re-enabled the CDN asset transport")
|
||||
log_admin("[key_name(usr)] re-enabled the CDN asset transport")
|
||||
else
|
||||
to_chat(usr, "<span class='adminnotice'>The CDN is not enabled!</span>")
|
||||
if (alert(usr, "The CDN asset transport is not enabled! If you having issues with assets you can also try disabling filename mutations.", "The CDN asset transport is not enabled!", "Try disabling filename mutations", "Nevermind") == "Try disabling filename mutations")
|
||||
SSassets.transport.dont_mutate_filenames = !SSassets.transport.dont_mutate_filenames
|
||||
message_admins("[key_name_admin(usr)] [(SSassets.transport.dont_mutate_filenames ? "disabled" : "re-enabled")] asset filename transforms")
|
||||
log_admin("[key_name(usr)] [(SSassets.transport.dont_mutate_filenames ? "disabled" : "re-enabled")] asset filename transforms")
|
||||
else
|
||||
admin_disabled_cdn_transport = current_transport
|
||||
CONFIG_SET(string/asset_transport, "simple")
|
||||
SSassets.OnConfigLoad()
|
||||
SSassets.transport.dont_mutate_filenames = TRUE
|
||||
message_admins("[key_name_admin(usr)] disabled the CDN asset transport")
|
||||
log_admin("[key_name(usr)] disabled the CDN asset transport")
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
<head>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
|
||||
<title>[title]</title>
|
||||
<link rel="stylesheet" type="text/css" href="view_variables.css">
|
||||
<link rel="stylesheet" type="text/css" href="[SSassets.transport.get_asset_url("view_variables.css")]">
|
||||
</head>
|
||||
<body onload='selectTextField()' onkeydown='return handle_keydown()' onkeyup='handle_keyup()'>
|
||||
<script type="text/javascript">
|
||||
|
||||
@@ -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]")
|
||||
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))]"
|
||||
|
||||
@@ -12,10 +12,6 @@
|
||||
|
||||
/// 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
|
||||
var/list/preloaded_assets = json_decode(json)
|
||||
|
||||
@@ -26,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
|
||||
|
||||
@@ -5,19 +5,31 @@
|
||||
**/
|
||||
/datum/asset_cache_item
|
||||
var/name
|
||||
var/url
|
||||
var/md5
|
||||
var/hash
|
||||
var/resource
|
||||
var/ext = ""
|
||||
var/legacy = FALSE //! Should this file also be sent via the legacy browse_rsc system when cdn transports are enabled?
|
||||
var/namespace = null //! 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_parent = FALSE //! True if this is the parent css or html file for an asset's namespace
|
||||
|
||||
/datum/asset_cache_item/New(name, file)
|
||||
if (!isfile(file))
|
||||
file = fcopy_rsc(file)
|
||||
md5 = md5(file)
|
||||
if (!md5)
|
||||
md5 = md5(fcopy_rsc(file))
|
||||
if (!md5)
|
||||
hash = md5(file)
|
||||
if (!hash)
|
||||
hash = md5(fcopy_rsc(file))
|
||||
if (!hash)
|
||||
CRASH("invalid asset sent to asset cache")
|
||||
debug_world_log("asset cache unexpected success of second fcopy_rsc")
|
||||
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
|
||||
|
||||
@@ -29,22 +29,26 @@ GLOBAL_LIST_EMPTY(asset_datums)
|
||||
//If you don't need anything complicated.
|
||||
/datum/asset/simple
|
||||
_abstract = /datum/asset/simple
|
||||
var/assets = list()
|
||||
var/assets = list() //! 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/legacy = FALSE //! set to true to have this asset also be sent via browse_rsc when cdn asset transports are enabled.
|
||||
|
||||
/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 = TRUE
|
||||
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
|
||||
@@ -88,12 +92,12 @@ GLOBAL_LIST_EMPTY(asset_datums)
|
||||
ensure_stripped()
|
||||
for(var/size_id in sizes)
|
||||
var/size = sizes[size_id]
|
||||
register_asset("[name]_[size_id].png", size[SPRSZ_STRIPPED])
|
||||
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)
|
||||
register_asset(res_name, fcopy_rsc(fname))
|
||||
SSassets.transport.register_asset(res_name, fcopy_rsc(fname))
|
||||
fdel(fname)
|
||||
|
||||
/datum/asset/spritesheet/send(client/C)
|
||||
@@ -102,14 +106,14 @@ GLOBAL_LIST_EMPTY(asset_datums)
|
||||
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(C, all)
|
||||
|
||||
/datum/asset/spritesheet/get_url_mappings()
|
||||
if (!name)
|
||||
return
|
||||
. = list("spritesheet_[name].css" = get_asset_url("spritesheet_[name].css"))
|
||||
. = 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")
|
||||
|
||||
|
||||
|
||||
@@ -134,7 +138,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('[SSassets.transport.get_asset_url("[name]_[size_id].png")]') no-repeat;}"
|
||||
|
||||
for (var/sprite_id in sprites)
|
||||
var/sprite = sprites[sprite_id]
|
||||
@@ -188,7 +192,7 @@ 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]
|
||||
@@ -243,7 +247,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 +257,52 @@ 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]))
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
/datum/asset/simple/irv
|
||||
)
|
||||
|
||||
/datum/asset/simple/changelog
|
||||
/datum/asset/simple/namespaced/changelog
|
||||
assets = list(
|
||||
"88x31.png" = 'html/88x31.png',
|
||||
"bug-minus.png" = 'html/bug-minus.png',
|
||||
@@ -132,37 +132,45 @@
|
||||
"chrome-wrench.png" = 'html/chrome-wrench.png',
|
||||
"changelog.css" = 'html/changelog.css'
|
||||
)
|
||||
parents = list("changelog.html" = 'html/changelog.html')
|
||||
|
||||
|
||||
/datum/asset/group/goonchat
|
||||
children = list(
|
||||
/datum/asset/simple/jquery,
|
||||
/datum/asset/simple/goonchat,
|
||||
/datum/asset/simple/namespaced/goonchat,
|
||||
/datum/asset/spritesheet/goonchat,
|
||||
/datum/asset/simple/fontawesome
|
||||
/datum/asset/simple/namespaced/fontawesome
|
||||
)
|
||||
|
||||
/datum/asset/simple/jquery
|
||||
legacy = TRUE
|
||||
assets = list(
|
||||
"jquery.min.js" = 'code/modules/goonchat/browserassets/js/jquery.min.js',
|
||||
)
|
||||
|
||||
/datum/asset/simple/goonchat
|
||||
/datum/asset/simple/namespaced/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',
|
||||
)
|
||||
parents = list(
|
||||
//this list intentionally left empty (parent namespaced assets can't be referred to by name, only by generated url, and goonchat isn't smart enough for that. yet)
|
||||
)
|
||||
|
||||
/datum/asset/simple/fontawesome
|
||||
/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',
|
||||
"font-awesome.css" = 'html/font-awesome/css/all.min.css',
|
||||
"v4shim.css" = 'html/font-awesome/css/v4-shims.min.css'
|
||||
)
|
||||
parents = list("font-awesome.css" = 'html/font-awesome/css/all.min.css')
|
||||
|
||||
/datum/asset/spritesheet/goonchat
|
||||
name = "chat"
|
||||
@@ -182,9 +190,25 @@
|
||||
|
||||
..()
|
||||
|
||||
/datum/asset/simple/lobby
|
||||
assets = list(
|
||||
"playeroptions.css" = 'html/browser/playeroptions.css'
|
||||
)
|
||||
|
||||
/datum/asset/simple/namespaced/common
|
||||
assets = list("padlock.png" = 'html/padlock.png')
|
||||
parents = list("common.css" = 'html/browser/common.css')
|
||||
|
||||
/datum/asset/simple/permissions
|
||||
assets = list(
|
||||
"padlock.png" = 'html/padlock.png'
|
||||
"search.js" = 'html/admin/search.js',
|
||||
"panels.css" = 'html/admin/panels.css'
|
||||
)
|
||||
|
||||
/datum/asset/group/permissions
|
||||
children = list(
|
||||
/datum/asset/simple/permissions,
|
||||
/datum/asset/simple/namespaced/common
|
||||
)
|
||||
|
||||
/datum/asset/simple/notes
|
||||
|
||||
37
code/modules/asset_cache/readme.md
Normal file
37
code/modules/asset_cache/readme.md
Normal 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.
|
||||
140
code/modules/asset_cache/transports/asset_transport.dm
Normal file
140
code/modules/asset_cache/transports/asset_transport.dm
Normal file
@@ -0,0 +1,140 @@
|
||||
/// When sending mutiple assets, how many before we give the client a quaint little sending resources message
|
||||
#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8
|
||||
|
||||
/// Base browse_rsc asset transport
|
||||
/datum/asset_transport
|
||||
var/name = "Simple browse_rsc asset transport"
|
||||
var/static/list/preload
|
||||
/// Don't mutate the filename of assets when sending via browse_rsc.
|
||||
/// This is to make it easier to debug issues with assets, and allow server operators to bypass issues that make it to production.
|
||||
/// If turning this on fixes asset issues, something isn't using get_asset_url and the asset isn't marked legacy, fix one of those.
|
||||
var/dont_mutate_filenames = FALSE
|
||||
|
||||
/// Called when the transport is loaded by the config controller, not called on the default transport unless it gets loaded by a config change.
|
||||
/datum/asset_transport/proc/Load()
|
||||
if (CONFIG_GET(flag/asset_simple_preload))
|
||||
for(var/client/C in GLOB.clients)
|
||||
addtimer(CALLBACK(src, .proc/send_assets_slow, C, preload), 1 SECONDS)
|
||||
|
||||
/// Initialize - Called when SSassets initializes.
|
||||
/datum/asset_transport/proc/Initialize(list/assets)
|
||||
preload = assets.Copy()
|
||||
if (!CONFIG_GET(flag/asset_simple_preload))
|
||||
return
|
||||
for(var/client/C in GLOB.clients)
|
||||
addtimer(CALLBACK(src, .proc/send_assets_slow, C, preload), 1 SECONDS)
|
||||
|
||||
|
||||
/// Register a browser asset with the asset cache system
|
||||
/// asset_name - the identifier of the asset
|
||||
/// asset - the actual asset file (or an asset_cache_item datum)
|
||||
/// returns a /datum/asset_cache_item.
|
||||
/// mutiple calls to register the same asset under the same asset_name return the same datum
|
||||
/datum/asset_transport/proc/register_asset(asset_name, asset)
|
||||
var/datum/asset_cache_item/ACI = asset
|
||||
if (!istype(ACI))
|
||||
ACI = new(asset_name, asset)
|
||||
if (!ACI || !ACI.hash)
|
||||
CRASH("ERROR: Invalid asset: [asset_name]:[asset]:[ACI]")
|
||||
if (SSassets.cache[asset_name])
|
||||
var/datum/asset_cache_item/OACI = SSassets.cache[asset_name]
|
||||
OACI.legacy = ACI.legacy = (ACI.legacy|OACI.legacy)
|
||||
OACI.namespace_parent = ACI.namespace_parent = (ACI.namespace_parent | OACI.namespace_parent)
|
||||
OACI.namespace = OACI.namespace || ACI.namespace
|
||||
if (OACI.hash != ACI.hash)
|
||||
var/error_msg = "ERROR: new asset added to the asset cache with the same name as another asset: [asset_name] existing asset hash: [OACI.hash] new asset hash:[ACI.hash]"
|
||||
stack_trace(error_msg)
|
||||
log_asset(error_msg)
|
||||
else
|
||||
if (length(ACI.namespace))
|
||||
return ACI
|
||||
return OACI
|
||||
|
||||
SSassets.cache[asset_name] = ACI
|
||||
return ACI
|
||||
|
||||
|
||||
/// Returns a url for a given asset.
|
||||
/// asset_name - Name of the asset.
|
||||
/// asset_cache_item - asset cache item datum for the asset, optional, overrides asset_name
|
||||
/datum/asset_transport/proc/get_asset_url(asset_name, datum/asset_cache_item/asset_cache_item)
|
||||
if (!istype(asset_cache_item))
|
||||
asset_cache_item = SSassets.cache[asset_name]
|
||||
if (dont_mutate_filenames || asset_cache_item.legacy || (asset_cache_item.namespace && !asset_cache_item.namespace_parent)) // to ensure code that breaks on cdns breaks in local testing, we only use the normal filename on legacy assets and name space assets.
|
||||
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
|
||||
if (!dont_mutate_filenames && !ACI.legacy && (!ACI.namespace || ACI.namespace_parent))
|
||||
new_asset_name = "asset.[ACI.hash][ACI.ext]"
|
||||
if (client.sent_assets[new_asset_name] == asset_hash)
|
||||
if (GLOB.Debug2)
|
||||
log_asset("DEBUG: Skipping send of `[asset_name]` (as `[new_asset_name]`) for `[client]` because it already exists in the client's sent_assets list")
|
||||
continue
|
||||
unreceived[asset_name] = ACI
|
||||
|
||||
if (unreceived.len)
|
||||
if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT)
|
||||
to_chat(client, "Sending Resources...")
|
||||
|
||||
for (var/asset_name in unreceived)
|
||||
var/new_asset_name = asset_name
|
||||
var/datum/asset_cache_item/ACI = unreceived[asset_name]
|
||||
if (!dont_mutate_filenames && !ACI.legacy && (!ACI.namespace || ACI.namespace_parent))
|
||||
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, /client/proc/asset_cache_update_json), 1 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
/// Precache files without clogging up the browse() queue, used for passively sending files on connection start.
|
||||
/datum/asset_transport/proc/send_assets_slow(client/client, list/files, filerate = 3)
|
||||
var/startingfilerate = filerate
|
||||
for (var/file in files)
|
||||
if (!client)
|
||||
break
|
||||
if (send_assets(client, file))
|
||||
if (!(--filerate))
|
||||
filerate = startingfilerate
|
||||
client.browse_queue_flush()
|
||||
stoplag(0) //queuing calls like this too quickly can cause issues in some client versions
|
||||
|
||||
/// Check the config is valid to load this transport
|
||||
/// Returns TRUE or FALSE
|
||||
/datum/asset_transport/proc/validate_config(log = TRUE)
|
||||
return TRUE
|
||||
87
code/modules/asset_cache/transports/webroot_transport.dm
Normal file
87
code/modules/asset_cache/transports/webroot_transport.dm
Normal 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_GET(string/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_GET(string/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 = ""
|
||||
var/filename = "asset.[asset_cache_item.hash][asset_cache_item.ext]"
|
||||
if (length(asset_cache_item.namespace))
|
||||
base = "namespaces/[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_GET(string/asset_cdn_url))
|
||||
if (log)
|
||||
log_asset("ERROR: [type]: Invalid Config: ASSET_CDN_URL")
|
||||
return FALSE
|
||||
if (!CONFIG_GET(string/asset_cdn_webroot))
|
||||
if (log)
|
||||
log_asset("ERROR: [type]: Invalid Config: ASSET_CDN_WEBROOT")
|
||||
return FALSE
|
||||
return TRUE
|
||||
@@ -204,9 +204,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
|
||||
///////////
|
||||
//CONNECT//
|
||||
///////////
|
||||
#if (PRELOAD_RSC == 0)
|
||||
GLOBAL_LIST_EMPTY(external_rsc_urls)
|
||||
#endif
|
||||
|
||||
/client/New(TopicData)
|
||||
var/tdata = TopicData //save this for later use
|
||||
@@ -874,29 +871,25 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
|
||||
return inactivity
|
||||
return FALSE
|
||||
|
||||
//send resources to the client. It's here in its own proc so we can move it around easiliy if need be
|
||||
/// Send resources to the client.
|
||||
/// Sends both game resources and browser assets.
|
||||
/client/proc/send_resources()
|
||||
#if (PRELOAD_RSC == 0)
|
||||
var/static/next_external_rsc = 0
|
||||
if(GLOB.external_rsc_urls && GLOB.external_rsc_urls.len)
|
||||
next_external_rsc = WRAP(next_external_rsc+1, 1, GLOB.external_rsc_urls.len+1)
|
||||
preload_rsc = GLOB.external_rsc_urls[next_external_rsc]
|
||||
var/list/external_rsc_urls = CONFIG_GET(keyed_list/external_rsc_urls)
|
||||
if(length(external_rsc_urls.len))
|
||||
next_external_rsc = WRAP(next_external_rsc+1, 1, external_rsc_urls.len+1)
|
||||
preload_rsc = external_rsc_urls[next_external_rsc]
|
||||
#endif
|
||||
//get the common files
|
||||
getFiles(
|
||||
'html/search.js',
|
||||
'html/panels.css',
|
||||
'html/browser/common.css',
|
||||
'html/browser/scannernew.css',
|
||||
'html/browser/playeroptions.css',
|
||||
)
|
||||
|
||||
spawn (10) //removing this spawn causes all clients to not get verbs.
|
||||
|
||||
//load info on what assets the client has
|
||||
src << browse('code/modules/asset_cache/validate_assets.html', "window=asset_cache_browser")
|
||||
|
||||
//Precache the client with all other assets slowly, so as to not block other browse() calls
|
||||
addtimer(CALLBACK(GLOBAL_PROC, /proc/getFilesSlow, src, SSassets.preload, FALSE), 5 SECONDS)
|
||||
if (CONFIG_GET(flag/asset_simple_preload))
|
||||
addtimer(CALLBACK(SSassets.transport, /datum/asset_transport.proc/send_assets_slow, src, SSassets.transport.preload), 5 SECONDS)
|
||||
|
||||
#if (PRELOAD_RSC == 0)
|
||||
for (var/name in GLOB.vox_sounds)
|
||||
|
||||
@@ -101,7 +101,6 @@
|
||||
dat += "<A href='?src=[REF(src)];back=1'>\[Go Back\]</A><BR>"
|
||||
var/datum/browser/popup = new(user, "publiclibrary", name, 600, 400)
|
||||
popup.set_content(jointext(dat, ""))
|
||||
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
|
||||
popup.open()
|
||||
|
||||
/obj/machinery/computer/libraryconsole/Topic(href, href_list)
|
||||
@@ -325,7 +324,6 @@
|
||||
|
||||
var/datum/browser/popup = new(user, "library", name, 600, 400)
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
|
||||
popup.open()
|
||||
|
||||
/obj/machinery/computer/bookmanagement/proc/findscanner(viewrange)
|
||||
@@ -545,7 +543,6 @@
|
||||
dat += "<BR>"
|
||||
var/datum/browser/popup = new(user, "scanner", name, 600, 400)
|
||||
popup.set_content(dat)
|
||||
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
|
||||
popup.open()
|
||||
|
||||
/obj/machinery/libraryscanner/Topic(href, href_list)
|
||||
|
||||
@@ -68,7 +68,6 @@ interface with the mining shuttle at the landing site if a mobile beacon is also
|
||||
|
||||
var/datum/browser/popup = new(user, "computer", "base management", 550, 300) //width, height
|
||||
popup.set_content("<center>[dat]</center>")
|
||||
popup.set_title_image(usr.browse_rsc_icon(src.icon, src.icon_state))
|
||||
popup.open()
|
||||
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@
|
||||
return
|
||||
|
||||
/mob/dead/new_player/proc/new_player_panel()
|
||||
var/datum/asset/asset_datum = get_asset_datum(/datum/asset/simple/lobby)
|
||||
asset_datum.send(client)
|
||||
var/output = "<center><p><a href='byond://?src=[REF(src)];show_preferences=1'>Setup Character</a></p>"
|
||||
|
||||
if(SSticker.current_state <= GAME_STATE_PREGAME)
|
||||
@@ -91,7 +93,6 @@
|
||||
|
||||
output += "</center>"
|
||||
|
||||
//src << browse(output,"window=playersetup;size=210x240;can_close=0")
|
||||
var/datum/browser/popup = new(src, "playersetup", "<div align='center'>New Player Options</div>", 250, 265)
|
||||
popup.set_window_options("can_close=0")
|
||||
popup.set_content(output)
|
||||
|
||||
@@ -253,8 +253,8 @@
|
||||
prepared_options = shuffle(poll.options)
|
||||
var/list/output = list({"<html><head><meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
|
||||
<script src="jquery.min.js"></script>
|
||||
<script src="jquery-ui.custom-core-widgit-mouse-sortable-min.js"></script>
|
||||
<script src="[SSassets.transport.get_asset_url("jquery.min.js")]"></script>
|
||||
<script src="[SSassets.transport.get_asset_url("jquery-ui.custom-core-widgit-mouse-sortable-min.js")]"></script>
|
||||
<style>
|
||||
#sortable { list-style-type: none; margin: 0; padding: 2em; }
|
||||
#sortable li { min-height: 1em; margin: 0px 1px 1px 1px; padding: 1px; border: 1px solid black; border-radius: 5px; background-color: white; cursor:move;}
|
||||
|
||||
@@ -80,3 +80,8 @@
|
||||
data["npcs"] = npcs
|
||||
|
||||
return data
|
||||
|
||||
/datum/orbit_menu/ui_assets()
|
||||
. = ..() || list()
|
||||
. += get_asset_datum(/datum/asset/simple/orbit)
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
<head>
|
||||
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'>
|
||||
<style type=\"text/css\">
|
||||
body { background-image:url('html/paigrid.png'); }
|
||||
body { background-image:url('[SSassets.transport.get_asset_url("paigrid.png")]'); }
|
||||
|
||||
#header { text-align:center; color:white; font-size: 30px; height: 35px; width: 100%; letter-spacing: 2px; z-index: 5}
|
||||
#content {position: relative; left: 10px; height: 400px; width: 100%; z-index: 0}
|
||||
|
||||
@@ -139,7 +139,6 @@
|
||||
. = ..()
|
||||
var/datum/browser/popup = new(user, "teg", "Thermo-Electric Generator", 460, 300)
|
||||
popup.set_content(get_menu())
|
||||
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
|
||||
popup.open()
|
||||
|
||||
/obj/machinery/power/generator/Topic(href, href_list)
|
||||
|
||||
@@ -186,7 +186,6 @@
|
||||
|
||||
var/datum/browser/popup = new(user, "server_com", src.name, 900, 620)
|
||||
popup.set_content(dat.Join())
|
||||
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
|
||||
popup.open()
|
||||
|
||||
/obj/machinery/computer/rdservercontrol/attackby(obj/item/D, mob/user, params)
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
|
||||
var/datum/browser/popup = new(user, "computer", M ? M.name : "shuttle", 300, 200)
|
||||
popup.set_content("<center>[dat]</center>")
|
||||
popup.set_title_image(usr.browse_rsc_icon(src.icon, src.icon_state))
|
||||
popup.open()
|
||||
|
||||
/obj/machinery/computer/shuttle/Topic(href, href_list)
|
||||
|
||||
@@ -85,9 +85,12 @@
|
||||
))
|
||||
else
|
||||
window.send_message("ping")
|
||||
window.send_asset(get_asset_datum(/datum/asset/simple/fontawesome))
|
||||
|
||||
var/flushqueue = window.send_asset(get_asset_datum(/datum/asset/simple/namespaced/fontawesome))
|
||||
for(var/datum/asset/asset in src_object.ui_assets(user))
|
||||
window.send_asset(asset)
|
||||
flushqueue |= window.send_asset(asset)
|
||||
if (flushqueue)
|
||||
user.client.browse_queue_flush()
|
||||
window.send_message("update", get_payload(
|
||||
with_data = TRUE,
|
||||
with_static_data = TRUE))
|
||||
@@ -143,11 +146,13 @@
|
||||
* Makes an asset available to use in tgui.
|
||||
*
|
||||
* required asset datum/asset
|
||||
*
|
||||
* return bool - true if an asset was actually sent
|
||||
*/
|
||||
/datum/tgui/proc/send_asset(datum/asset/asset)
|
||||
if(!window)
|
||||
CRASH("send_asset() can only be called after open().")
|
||||
window.send_asset(asset)
|
||||
return window.send_asset(asset)
|
||||
|
||||
/**
|
||||
* public
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
inline_styles += "<link rel=\"stylesheet\" type=\"text/css\" href=\"[url]\">\n"
|
||||
else if(copytext(name, -3) == ".js")
|
||||
inline_scripts += "<script type=\"text/javascript\" defer src=\"[url]\"></script>\n"
|
||||
asset.send()
|
||||
asset.send(client)
|
||||
html = replacetextEx(html, "<!-- tgui:styles -->\n", inline_styles)
|
||||
html = replacetextEx(html, "<!-- tgui:scripts -->\n", inline_scripts)
|
||||
// Open the window
|
||||
@@ -185,6 +185,8 @@
|
||||
* Makes an asset available to use in tgui.
|
||||
*
|
||||
* required asset datum/asset
|
||||
*
|
||||
* return bool - TRUE if any assets had to be sent to the client
|
||||
*/
|
||||
/datum/tgui_window/proc/send_asset(datum/asset/asset)
|
||||
if(!client || !asset)
|
||||
@@ -194,7 +196,7 @@
|
||||
send_message("asset/stylesheet", spritesheet.css_filename())
|
||||
send_message("asset/mappings", asset.get_url_mappings())
|
||||
sent_assets += list(asset)
|
||||
asset.send(client)
|
||||
return asset.send(client)
|
||||
|
||||
/**
|
||||
* private
|
||||
|
||||
@@ -4,6 +4,7 @@ $include game_options.txt
|
||||
$include dbconfig.txt
|
||||
$include comms.txt
|
||||
$include antag_rep.txt
|
||||
$include resources.txt
|
||||
|
||||
# You can use the @ character at the beginning of a config option to lock it from being edited in-game
|
||||
# Example usage:
|
||||
|
||||
39
config/resources.txt
Normal file
39
config/resources.txt
Normal file
@@ -0,0 +1,39 @@
|
||||
# External resources
|
||||
# Set this to the location of a .zip with the server's .rsc inside of it.
|
||||
# If you set this mutiple times, the server will rotate between the links.
|
||||
# To use this, the compile option PRELOAD_RSC must be set to 0 to keep byond from preloading resources
|
||||
|
||||
EXTERNAL_RSC_URLS http://tgstation13.download/byond/tgstationv2.zip
|
||||
|
||||
|
||||
########################
|
||||
# Browser Asset Config #
|
||||
########################
|
||||
# Browser assets are any file included in interfaces. css, images, javascript, etc.
|
||||
# This handles configuring how we get these to the player so interfaces can access them.
|
||||
|
||||
# Asset Transport
|
||||
# The normal way of getting assets to clients is to use the internal byond system. This can be slow and delay the opening of interface windows. It also doesn't allow the internal IE windows byond uses to cache anything.
|
||||
# You can instead have the server save them to a website via a folder within the game server that the web server can read. This could be a simple webserver or something backed by a CDN.
|
||||
# Valid values: simple, webroot. Simple is the default.
|
||||
#ASSET_TRANSPORT webroot
|
||||
|
||||
|
||||
# Simple asset transport configurable values.
|
||||
|
||||
# Uncomment this to have the server passively send all browser assets to each client in the background. (instead of waiting for them to be needed)
|
||||
# This should be uncommented in production and commented in development
|
||||
#ASSET_SIMPLE_PRELOAD
|
||||
|
||||
|
||||
# Webroot asset transport configurable values.
|
||||
|
||||
# Local folder to save assets to.
|
||||
# Assets will be saved in the format of asset.MD5HASH.EXT or in namespaces/hash/ as ASSET_FILE_NAME or asset.MD5HASH.EXT
|
||||
#ASSET_CDN_WEBROOT data/asset-store/
|
||||
|
||||
# URL the folder from above can be accessed from.
|
||||
# for best results the webserver powering this should return a long cache validity time, as all assets sent via this transport use hash based urls
|
||||
# if you want to test this locally, you simpily run the `localhost-asset-webroot-server.py` python3 script to host assets stored in `data/asset-store/` via http://localhost:58715/
|
||||
#ASSET_CDN_URL http://localhost:58715/
|
||||
|
||||
@@ -78,9 +78,9 @@
|
||||
/client/verb/changelog()
|
||||
set name = "Changelog"
|
||||
set category = "OOC"
|
||||
var/datum/asset/changelog = get_asset_datum(/datum/asset/simple/changelog)
|
||||
var/datum/asset/simple/namespaced/changelog = get_asset_datum(/datum/asset/simple/namespaced/changelog)
|
||||
changelog.send(src)
|
||||
src << browse('html/changelog.html', "window=changes;size=675x650")
|
||||
src << browse(changelog.get_htmlloader("changelog.html"), "window=changes;size=675x650")
|
||||
if(prefs.lastchangelog != GLOB.changelog_hash)
|
||||
prefs.lastchangelog = GLOB.changelog_hash
|
||||
prefs.save_preferences()
|
||||
|
||||
@@ -246,6 +246,7 @@
|
||||
#include "code\controllers\configuration\entries\dbconfig.dm"
|
||||
#include "code\controllers\configuration\entries\game_options.dm"
|
||||
#include "code\controllers\configuration\entries\general.dm"
|
||||
#include "code\controllers\configuration\entries\resources.dm"
|
||||
#include "code\controllers\subsystem\achievements.dm"
|
||||
#include "code\controllers\subsystem\acid.dm"
|
||||
#include "code\controllers\subsystem\adjacent_air.dm"
|
||||
@@ -1573,11 +1574,12 @@
|
||||
#include "code\modules\assembly\signaler.dm"
|
||||
#include "code\modules\assembly\timer.dm"
|
||||
#include "code\modules\assembly\voice.dm"
|
||||
#include "code\modules\asset_cache\asset_cache.dm"
|
||||
#include "code\modules\asset_cache\asset_cache_client.dm"
|
||||
#include "code\modules\asset_cache\asset_cache_item.dm"
|
||||
#include "code\modules\asset_cache\asset_list.dm"
|
||||
#include "code\modules\asset_cache\asset_list_items.dm"
|
||||
#include "code\modules\asset_cache\transports\asset_transport.dm"
|
||||
#include "code\modules\asset_cache\transports\webroot_transport.dm"
|
||||
#include "code\modules\atmospherics\multiz.dm"
|
||||
#include "code\modules\atmospherics\environmental\LINDA_fire.dm"
|
||||
#include "code\modules\atmospherics\environmental\LINDA_system.dm"
|
||||
|
||||
15
tools/localhost-asset-webroot-server.py
Normal file
15
tools/localhost-asset-webroot-server.py
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env python3
|
||||
from http.server import HTTPServer, SimpleHTTPRequestHandler
|
||||
import os
|
||||
|
||||
class CORSRequestHandler(SimpleHTTPRequestHandler):
|
||||
def end_headers(self):
|
||||
self.send_header('Access-Control-Allow-Origin', '*')
|
||||
self.send_header('Access-Control-Allow-Methods', 'GET')
|
||||
self.send_header('Cache-Control', 'no-store, no-cache, must-revalidate')
|
||||
return super(CORSRequestHandler, self).end_headers()
|
||||
|
||||
os.makedirs('../data/asset-store/', exist_ok=True)
|
||||
os.chdir('../data/asset-store/')
|
||||
httpd = HTTPServer(('localhost', 58715), CORSRequestHandler)
|
||||
httpd.serve_forever()
|
||||
Reference in New Issue
Block a user