From 22c85075f45a66a2a69e5600a7ae35aba44b7d3c Mon Sep 17 00:00:00 2001 From: ShadowLarkens Date: Sun, 16 Feb 2020 16:00:01 -0500 Subject: [PATCH] Ported tg asset cache --- code/__defines/subsystems.dm | 1 + code/controllers/subsystems/assets.dm | 17 ++ code/controllers/subsystems/nanoui.dm | 5 +- code/game/objects/items/devices/PDA/PDA.dm | 3 +- code/modules/client/asset_cache.dm | 287 +++++++++++++++++++++ code/modules/client/client procs.dm | 60 +---- code/modules/mob/mob.dm | 20 -- code/modules/vchat/vchat_client.dm | 2 +- interface/skin.dmf | 8 + vorestation.dme | 2 + 10 files changed, 330 insertions(+), 75 deletions(-) create mode 100644 code/controllers/subsystems/assets.dm create mode 100644 code/modules/client/asset_cache.dm diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm index 55aea1682e..3b3067bddf 100644 --- a/code/__defines/subsystems.dm +++ b/code/__defines/subsystems.dm @@ -62,6 +62,7 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G #define INIT_ORDER_DEFAULT 0 #define INIT_ORDER_LIGHTING 0 #define INIT_ORDER_AIR -1 +#define INIT_ORDER_ASSETS -3 #define INIT_ORDER_PLANETS -4 #define INIT_ORDER_HOLOMAPS -5 #define INIT_ORDER_OVERLAY -6 diff --git a/code/controllers/subsystems/assets.dm b/code/controllers/subsystems/assets.dm new file mode 100644 index 0000000000..cd531db614 --- /dev/null +++ b/code/controllers/subsystems/assets.dm @@ -0,0 +1,17 @@ +SUBSYSTEM_DEF(assets) + name = "Assets" + init_order = INIT_ORDER_ASSETS + flags = SS_NO_FIRE + var/list/cache = list() + var/list/preload = list() + +/datum/controller/subsystem/assets/Initialize(timeofday) + for(var/type in typesof(/datum/asset) - list(/datum/asset, /datum/asset/simple)) + var/datum/asset/A = new type() + A.register() + + preload = cache.Copy() //don't preload assets generated during the round + + for(var/client/C in GLOB.clients) + addtimer(CALLBACK(GLOBAL_PROC, .proc/getFilesSlow, C, preload, FALSE), 10) + return ..() \ No newline at end of file diff --git a/code/controllers/subsystems/nanoui.dm b/code/controllers/subsystems/nanoui.dm index 520973514d..78f47e8c7b 100644 --- a/code/controllers/subsystems/nanoui.dm +++ b/code/controllers/subsystems/nanoui.dm @@ -24,7 +24,7 @@ SUBSYSTEM_DEF(nanoui) for(var/filename in filenames) if(copytext(filename, length(filename)) != "/") // filenames which end in "/" are actually directories, which we want to ignore if(fexists(path + filename)) - asset_files.Add(fcopy_rsc(path + filename)) // add this file to asset_files for sending to clients when they connect + asset_files[filename] = fcopy_rsc(path + filename) // add this file to asset_files for sending to clients when they connect .=..() for(var/i in GLOB.clients) send_resources(i) @@ -49,5 +49,4 @@ SUBSYSTEM_DEF(nanoui) /datum/controller/subsystem/nanoui/proc/send_resources(client) if(!subsystem_initialized) return - for(var/file in asset_files) - client << browse_rsc(file) // send the file to the client + getFilesSlow(client, asset_files) \ No newline at end of file diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index 771684775f..05f50f3eff 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -647,8 +647,9 @@ var/global/list/obj/item/device/pda/PDAs = list() // auto update every Master Controller tick ui.set_auto_update(auto_update) -//NOTE: graphic resources are loaded on client login /obj/item/device/pda/attack_self(mob/user as mob) + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/pda) + assets.send(user) user.set_machine(src) diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm new file mode 100644 index 0000000000..0e2f2f9f21 --- /dev/null +++ b/code/modules/client/asset_cache.dm @@ -0,0 +1,287 @@ +/* +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 + +//When sending mutiple assets, how many before we give the client a quaint little sending resources message +#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8 + +//When passively preloading assets, how many to send at once? Too high creates noticable lag where as too low can flood the client's cache with "verify" files +#define ASSET_CACHE_PRELOAD_CONCURRENT 3 + +/client + var/list/cache = list() // List of all assets sent to this client by the asset cache. + var/list/completed_asset_jobs = list() // List of all completed jobs, awaiting acknowledgement. + var/list/sending = list() + var/last_asset_job = 0 // Last job done. + +//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(var/client/client, var/asset_name, var/verify = TRUE) + if(!istype(client)) + if(ismob(client)) + var/mob/M = client + if(M.client) + client = M.client + + else + return 0 + + else + return 0 + + if(client.cache.Find(asset_name) || client.sending.Find(asset_name)) + return 0 + + client << browse_rsc(SSassets.cache[asset_name], asset_name) + if(!verify) // Can't access the asset cache browser, rip. + client.cache += asset_name + return 1 + + client.sending |= asset_name + var/job = ++client.last_asset_job + + client << browse({" + + "}, "window=asset_cache_browser") + + var/t = 0 + var/timeout_time = (ASSET_CACHE_SEND_TIMEOUT * client.sending.len) + ASSET_CACHE_SEND_TIMEOUT + while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic() + sleep(1) // Lock up the caller until this is received. + t++ + + if(client) + client.sending -= asset_name + client.cache |= asset_name + client.completed_asset_jobs -= job + + return 1 + +//This proc blocks(sleeps) unless verify is set to false +/proc/send_asset_list(var/client/client, var/list/asset_list, var/verify = TRUE) + if(!istype(client)) + if(ismob(client)) + var/mob/M = client + if(M.client) + client = M.client + + else + return 0 + + else + return 0 + + var/list/unreceived = asset_list - (client.cache + client.sending) + if(!unreceived || !unreceived.len) + return 0 + if(unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT) + to_chat(client, "Sending Resources...") + for(var/asset in unreceived) + if(asset in SSassets.cache) + client << browse_rsc(SSassets.cache[asset], asset) + + if(!verify) // Can't access the asset cache browser, rip. + client.cache += unreceived + return 1 + + client.sending |= unreceived + var/job = ++client.last_asset_job + + client << browse({" + + "}, "window=asset_cache_browser") + + var/t = 0 + var/timeout_time = ASSET_CACHE_SEND_TIMEOUT * client.sending.len + while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic() + sleep(1) // Lock up the caller until this is received. + t++ + + if(client) + client.sending -= unreceived + client.cache |= unreceived + client.completed_asset_jobs -= job + + return 1 + +//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(var/client/client, var/list/files, var/register_asset = TRUE) + var/concurrent_tracker = 1 + for(var/file in files) + if(!client) + break + if(register_asset) + register_asset(file, files[file]) + if(concurrent_tracker >= ASSET_CACHE_PRELOAD_CONCURRENT) + concurrent_tracker = 1 + send_asset(client, file) + else + concurrent_tracker++ + send_asset(client, file, verify = FALSE) + sleep(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. +//if it's an icon or something be careful, you'll have to copy it before further use. +/proc/register_asset(var/asset_name, var/asset) + SSassets.cache[asset_name] = asset + +//These datums are used to populate the asset cache, the proc "register()" does this. + +//all of our asset datums, used for referring to these later +/var/global/list/asset_datums = list() + +//get a assetdatum or make a new one +/proc/get_asset_datum(var/type) + if(!(type in asset_datums)) + return new type() + return asset_datums[type] + +/datum/asset/New() + asset_datums[type] = src + +/datum/asset/proc/register() + return + +/datum/asset/proc/send(client) + return + +//If you don't need anything complicated. +/datum/asset/simple + var/assets = list() + var/verify = FALSE + +/datum/asset/simple/register() + for(var/asset_name in assets) + register_asset(asset_name, assets[asset_name]) +/datum/asset/simple/send(client) + send_asset_list(client,assets,verify) + + +//DEFINITIONS FOR ASSET DATUMS START HERE. +/datum/asset/simple/pda + assets = list( + "pda_atmos.png" = 'icons/pda_icons/pda_atmos.png', + "pda_back.png" = 'icons/pda_icons/pda_back.png', + "pda_bell.png" = 'icons/pda_icons/pda_bell.png', + "pda_blank.png" = 'icons/pda_icons/pda_blank.png', + "pda_boom.png" = 'icons/pda_icons/pda_boom.png', + "pda_bucket.png" = 'icons/pda_icons/pda_bucket.png', + "pda_crate.png" = 'icons/pda_icons/pda_crate.png', + "pda_cuffs.png" = 'icons/pda_icons/pda_cuffs.png', + "pda_eject.png" = 'icons/pda_icons/pda_eject.png', + "pda_exit.png" = 'icons/pda_icons/pda_exit.png', + "pda_flashlight.png" = 'icons/pda_icons/pda_flashlight.png', + "pda_honk.png" = 'icons/pda_icons/pda_honk.png', + "pda_mail.png" = 'icons/pda_icons/pda_mail.png', + "pda_medical.png" = 'icons/pda_icons/pda_medical.png', + "pda_menu.png" = 'icons/pda_icons/pda_menu.png', + "pda_mule.png" = 'icons/pda_icons/pda_mule.png', + "pda_notes.png" = 'icons/pda_icons/pda_notes.png', + "pda_power.png" = 'icons/pda_icons/pda_power.png', + "pda_rdoor.png" = 'icons/pda_icons/pda_rdoor.png', + "pda_reagent.png" = 'icons/pda_icons/pda_reagent.png', + "pda_refresh.png" = 'icons/pda_icons/pda_refresh.png', + "pda_scanner.png" = 'icons/pda_icons/pda_scanner.png', + "pda_signaler.png" = 'icons/pda_icons/pda_signaler.png', + "pda_status.png" = 'icons/pda_icons/pda_status.png' + ) + +/datum/asset/simple/generic + assets = list( + "search.js" = 'html/search.js', + "panels.css" = 'html/panels.css', + "loading.gif" = 'html/images/loading.gif', + "ntlogo.png" = 'html/images/ntlogo.png', + "sglogo.png" = 'html/images/sglogo.png', + "talisman.png" = 'html/images/talisman.png', + "paper_bg.png" = 'html/images/paper_bg.png', + "no_image32.png" = 'html/images/no_image32.png', + "sos_1.png" = 'icons/spideros_icons/sos_1.png', + "sos_2.png" = 'icons/spideros_icons/sos_2.png', + "sos_3.png" = 'icons/spideros_icons/sos_3.png', + "sos_4.png" = 'icons/spideros_icons/sos_4.png', + "sos_5.png" = 'icons/spideros_icons/sos_5.png', + "sos_6.png" = 'icons/spideros_icons/sos_6.png', + "sos_7.png" = 'icons/spideros_icons/sos_7.png', + "sos_8.png" = 'icons/spideros_icons/sos_8.png', + "sos_9.png" = 'icons/spideros_icons/sos_9.png', + "sos_10.png" = 'icons/spideros_icons/sos_10.png', + "sos_11.png" = 'icons/spideros_icons/sos_11.png', + "sos_12.png" = 'icons/spideros_icons/sos_12.png', + "sos_13.png" = 'icons/spideros_icons/sos_13.png', + "sos_14.png" = 'icons/spideros_icons/sos_14.png' + ) + +/datum/asset/simple/changelog + assets = list( + "88x31.png" = 'html/88x31.png', + "bug-minus.png" = 'html/bug-minus.png', + "cross-circle.png" = 'html/cross-circle.png', + "hard-hat-exclamation.png" = 'html/hard-hat-exclamation.png', + "image-minus.png" = 'html/image-minus.png', + "image-plus.png" = 'html/image-plus.png', + "map-pencil.png" = 'html/map-pencil.png', + "music-minus.png" = 'html/music-minus.png', + "music-plus.png" = 'html/music-plus.png', + "tick-circle.png" = 'html/tick-circle.png', + "wrench-screwdriver.png" = 'html/wrench-screwdriver.png', + "spell-check.png" = 'html/spell-check.png', + "burn-exclamation.png" = 'html/burn-exclamation.png', + "chevron.png" = 'html/chevron.png', + "chevron-expand.png" = 'html/chevron-expand.png', + "changelog.css" = 'html/changelog.css', + "changelog.js" = 'html/changelog.js', + "changelog.html" = 'html/changelog.html' + ) + +/datum/asset/nanoui + var/list/common = list() + + var/list/common_dirs = list( + "nano/css/", + "nano/images/", + "nano/js/" + ) + var/list/uncommon_dirs = list( + "nano/templates/" + ) + +/datum/asset/nanoui/register() + // Crawl the directories to find files. + for(var/path in common_dirs) + var/list/filenames = flist(path) + for(var/filename in filenames) + if(copytext(filename, length(filename)) != "/") // Ignore directories. + if(fexists(path + filename)) + common[filename] = fcopy_rsc(path + filename) + register_asset(filename, common[filename]) + for(var/path in uncommon_dirs) + var/list/filenames = flist(path) + for(var/filename in filenames) + if(copytext(filename, length(filename)) != "/") // Ignore directories. + if(fexists(path + filename)) + register_asset(filename, fcopy_rsc(path + filename)) + +/datum/asset/nanoui/send(client, uncommon) + if(!islist(uncommon)) + uncommon = list(uncommon) + + send_asset_list(client, uncommon) + send_asset_list(client, common) diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index aac5e440a1..1c16ec249c 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -34,6 +34,11 @@ #endif + if(href_list["asset_cache_confirm_arrival"]) + var/job = text2num(href_list["asset_cache_confirm_arrival"]) + completed_asset_jobs += job + return + //search the href for script injection if( findtext(href,"[custom_event_msg]") to_chat(src, "
") + if(!winexists(src, "asset_cache_browser")) // The client is using a custom skin, tell them. + to_chat(src, "Unable to access asset cache browser, if you are using a custom skin file, please allow DS to download the updated version, if you are not, then make a bug report. This is not a critical issue but can cause issues with resource downloading, as it is impossible to know when extra resources arrived to you.") if(holder) add_admin_verbs() @@ -365,56 +372,9 @@ //send resources to the client. It's here in its own proc so we can move it around easiliy if need be /client/proc/send_resources() - - getFiles( - 'html/search.js', - 'html/panels.css', - 'html/images/loading.gif', - 'html/images/ntlogo.png', - 'html/images/sglogo.png', - 'html/images/talisman.png', - 'html/images/paper_bg.png', - 'html/images/no_image32.png', - 'icons/pda_icons/pda_atmos.png', - 'icons/pda_icons/pda_back.png', - 'icons/pda_icons/pda_bell.png', - 'icons/pda_icons/pda_blank.png', - 'icons/pda_icons/pda_boom.png', - 'icons/pda_icons/pda_bucket.png', - 'icons/pda_icons/pda_crate.png', - 'icons/pda_icons/pda_cuffs.png', - 'icons/pda_icons/pda_eject.png', - 'icons/pda_icons/pda_exit.png', - 'icons/pda_icons/pda_flashlight.png', - 'icons/pda_icons/pda_honk.png', - 'icons/pda_icons/pda_mail.png', - 'icons/pda_icons/pda_medical.png', - 'icons/pda_icons/pda_menu.png', - 'icons/pda_icons/pda_mule.png', - 'icons/pda_icons/pda_notes.png', - 'icons/pda_icons/pda_power.png', - 'icons/pda_icons/pda_rdoor.png', - 'icons/pda_icons/pda_reagent.png', - 'icons/pda_icons/pda_refresh.png', - 'icons/pda_icons/pda_scanner.png', - 'icons/pda_icons/pda_signaler.png', - 'icons/pda_icons/pda_status.png', - 'icons/spideros_icons/sos_1.png', - 'icons/spideros_icons/sos_2.png', - 'icons/spideros_icons/sos_3.png', - 'icons/spideros_icons/sos_4.png', - 'icons/spideros_icons/sos_5.png', - 'icons/spideros_icons/sos_6.png', - 'icons/spideros_icons/sos_7.png', - 'icons/spideros_icons/sos_8.png', - 'icons/spideros_icons/sos_9.png', - 'icons/spideros_icons/sos_10.png', - 'icons/spideros_icons/sos_11.png', - 'icons/spideros_icons/sos_12.png', - 'icons/spideros_icons/sos_13.png', - 'icons/spideros_icons/sos_14.png' - ) - + spawn (10) //removing this spawn causes all clients to not get verbs. + //Precache the client with all other assets slowly, so as to not block other browse() calls + getFilesSlow(src, SSassets.preload, register_asset = FALSE) mob/proc/MayRespawn() return 0 diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index dca352e184..9c9bbd68e5 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -408,26 +408,6 @@ /client/verb/changes() set name = "Changelog" set category = "OOC" - getFiles( - 'html/88x31.png', - 'html/bug-minus.png', - 'html/cross-circle.png', - 'html/hard-hat-exclamation.png', - 'html/image-minus.png', - 'html/image-plus.png', - 'html/map-pencil.png', - 'html/music-minus.png', - 'html/music-plus.png', - 'html/tick-circle.png', - 'html/wrench-screwdriver.png', - 'html/spell-check.png', - 'html/burn-exclamation.png', - 'html/chevron.png', - 'html/chevron-expand.png', - 'html/changelog.css', - 'html/changelog.js', - 'html/changelog.html' - ) src << browse('html/changelog.html', "window=changes;size=675x650") if(prefs.lastchangelog != changelog_hash) prefs.lastchangelog = changelog_hash diff --git a/code/modules/vchat/vchat_client.dm b/code/modules/vchat/vchat_client.dm index 6b8e58a392..10ac3ba538 100644 --- a/code/modules/vchat/vchat_client.dm +++ b/code/modules/vchat/vchat_client.dm @@ -76,7 +76,7 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of ic if(!winexists(owner, "htmloutput")) spawn() - alert(owner.mob, "Updated chat window does not exist. If you are using a custom skin file please allow the game to update.") + alert(owner, "Updated chat window does not exist. If you are using a custom skin file please allow the game to update.") become_broken() return FALSE diff --git a/interface/skin.dmf b/interface/skin.dmf index 89df204df3..a2d7b94c18 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -1113,6 +1113,14 @@ window "mainwindow" icon = 'icons\\virgoicon.png' macro = "macro" menu = "menu" + elem "asset_cache_browser" + type = BROWSER + pos = 0,0 + size = 200x200 + anchor1 = none + anchor2 = none + is-visible = false + saved-params = "" elem "hotkey_toggle" type = BUTTON pos = 560,420 diff --git a/vorestation.dme b/vorestation.dme index bd7bd8594a..7813e23ffb 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -227,6 +227,7 @@ #include "code\controllers\subsystems\ai.dm" #include "code\controllers\subsystems\air.dm" #include "code\controllers\subsystems\airflow.dm" +#include "code\controllers\subsystems\assets.dm" #include "code\controllers\subsystems\atoms.dm" #include "code\controllers\subsystems\bellies_vr.dm" #include "code\controllers\subsystems\character_setup.dm" @@ -1613,6 +1614,7 @@ #include "code\modules\catalogue\cataloguer.dm" #include "code\modules\catalogue\cataloguer_visuals.dm" #include "code\modules\catalogue\cataloguer_vr.dm" +#include "code\modules\client\asset_cache.dm" #include "code\modules\client\client defines.dm" #include "code\modules\client\client procs.dm" #include "code\modules\client\client procs_vr.dm"