diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm index dc1d0fdcdf0..5eed9591ec3 100644 --- a/code/controllers/master_controller.dm +++ b/code/controllers/master_controller.dm @@ -67,6 +67,7 @@ calculate the longest number of ticks the MC can wait between each cycle without sleep(-1) crewmonitor.generateMiniMaps() + populate_asset_cache() world << "Initializations complete" diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index d7384c6b6d1..324b2cd4dd6 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -256,9 +256,9 @@ var/global/list/obj/item/device/pda/PDAs = list() return attack_self(M) return -//NOTE: graphic resources are loaded on client login /obj/item/device/pda/attack_self(mob/user) - + var/datum/asset/simple/pda/assetcache = new() + send_asset_list(user, assetcache.assets, verify = FALSE) user.set_machine(src) if(active_uplink_check(user)) diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm new file mode 100644 index 00000000000..74404c10fd2 --- /dev/null +++ b/code/modules/client/asset_cache.dm @@ -0,0 +1,174 @@ +#define ASSET_CACHE_SEND_TIMEOUT 25 // Amount of time MAX to send an asset, if this get exceeded we cancel the sleeping. + +//List of ALL assets for the above, format is list(filename = asset). +/var/global/list/asset_cache = list() + +/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. +/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(asset_cache[asset_name], asset_name) + if(!verify || !winexists(client, "asset_cache_browser")) // 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 + 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 + +/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 + + for(var/asset in unreceived) + client << browse_rsc(asset_cache[asset], asset) + + if(!verify || !winexists(client, "asset_cache_browser")) // 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 "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) + asset_cache |= asset_name + asset_cache[asset_name] = asset + + +//From here on out it's populating the asset cache. + +/proc/populate_asset_cache() + for(var/type in typesof(/datum/asset) - list(/datum/asset, /datum/asset/simple)) + var/datum/asset/A = new type() + A.register() + +//These datums are used to populate the asset cache, the proc "register()" does this. +/datum/asset/proc/register() + return + +//If you don't need anything complicated. +/datum/asset/simple + var/assets = list() + +/datum/asset/simple/register() + for(var/asset_name in assets) + register_asset(asset_name, assets[asset_name]) + +//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_medbot.png" = 'icons/pda_icons/pda_medbot.png', + "pda_floorbot.png" = 'icons/pda_icons/pda_floorbot.png', + "pda_cleanbot.png" = 'icons/pda_icons/pda_cleanbot.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' + ) + + +//Registers HTML Interface assets. +/datum/asset/HTML_interface/register() + for(var/path in typesof(/datum/html_interface)) + var/datum/html_interface/hi = new path() + hi.registerResources() + + +//Registers NanoUI assets. +/datum/asset/NanoUI/register() + for(var/path in typesof(/datum/html_interface)) + var/datum/html_interface/hi = new path() + hi.registerResources() diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index 295d39ceef3..deab198401a 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -318,13 +318,6 @@ var/next_external_rsc = 0 //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() - spawn - // Preload the HTML interface. This needs to be done due to BYOND bug http://www.byond.com/forum/?post=1487244 - var/datum/html_interface/hi - for (var/type in typesof(/datum/html_interface)) - hi = new type(null) - hi.sendResources(src) - // Preload the crew monitor. This needs to be done due to BYOND bug http://www.byond.com/forum/?post=1487244 spawn if (crewmonitor && crewmonitor.initialized) @@ -338,33 +331,6 @@ var/next_external_rsc = 0 'html/browser/common.css', 'html/browser/scannernew.css', 'html/browser/playeroptions.css', - '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_medbot.png', - 'icons/pda_icons/pda_floorbot.png', - 'icons/pda_icons/pda_cleanbot.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/stamp_icons/large_stamp-clown.png', 'icons/stamp_icons/large_stamp-deny.png', 'icons/stamp_icons/large_stamp-ok.png', diff --git a/code/modules/html_interface/cards/cards.dm b/code/modules/html_interface/cards/cards.dm index ced6446ca95..88e4311bf5d 100644 --- a/code/modules/html_interface/cards/cards.dm +++ b/code/modules/html_interface/cards/cards.dm @@ -7,8 +7,7 @@ src.head = src.head + "" src.updateLayout("
") -/datum/html_interface/cards/sendResources(client/client) - . = ..() // we need the default resources - - client << browse_rsc('cards.css') - client << browse_rsc('cards.png') \ No newline at end of file +/datum/html_interface/cards/registerResources(var/list/resources = list()) + resources["cards.css"] = 'cards.css' + resources["cards.png"] = 'cards.png' + ..(resources) \ No newline at end of file diff --git a/code/modules/html_interface/html_interface.dm b/code/modules/html_interface/html_interface.dm index ffc04407f66..07a436813ac 100644 --- a/code/modules/html_interface/html_interface.dm +++ b/code/modules/html_interface/html_interface.dm @@ -63,6 +63,8 @@ Closes the interface on all clients. When working with byond:// links make sure to reference the HTML interface object and NOT the original object. Topic() will still be called on your object, but it will pass through the HTML interface first allowing interception at a higher level. +If you want to use custom resources(images/css/js) with an existing interface: +You have to use modules/client/asset_cache to ensure they get sent BEFORE the interface opens ** Sample code ** @@ -105,6 +107,10 @@ mob/verb/test() // The initial height of the browser control, used when the window is first shown to a client. var/height + // A type associated list of assets the interface needs. + //Sent to the client when the interface opens on the client for the first time. + var/static/asset_list = list() + /datum/html_interface/New(atom/ref, title, width = 700, height = 480, head = "") html_interfaces.Add(src) @@ -126,12 +132,19 @@ mob/verb/test() /* * Hooks */ /datum/html_interface/proc/specificRenderTitle(datum/html_interface_client/hclient, ignore_cache = FALSE) -/datum/html_interface/proc/sendResources(client/client) - client << browse_rsc('js/jquery.min.js', "jquery.min.js") - client << browse_rsc('js/bootstrap.min.js', "bootstrap.min.js") - client << browse_rsc('css/bootstrap.min.css', "bootstrap.min.css") - client << browse_rsc('css/html_interface.css', "html_interface.css") - client << browse_rsc('js/html_interface.js', "html_interface.js") +//if you need to override this, either call ..() or add your resources to asset_list +/datum/html_interface/proc/registerResources(var/list/resources = list()) + resources["jquery.min.js"] = 'js/jquery.min.js' + resources["bootstrap.min.js"] = 'js/bootstrap.min.js' + resources["bootstrap.min.css"] = 'css/bootstrap.min.css' + resources["html_interface.css"] = 'css/html_interface.css' + resources["html_interface.js"] = 'js/html_interface.js' + var/assetlist = list() + for (var/R in resources) + register_asset(R,resources[R]) + assetlist += R + + asset_list[type] = assetlist /datum/html_interface/proc/createWindow(datum/html_interface_client/hclient) winclone(hclient.client, "window", "browser_\ref[src]") @@ -207,10 +220,7 @@ mob/verb/test() hclient = getClient(hclient, TRUE) if (istype(hclient)) - // This needs to be commented out due to BYOND bug http://www.byond.com/forum/?post=1487244 - // /client/proc/send_resources() executes this per client to avoid the bug, but by using it here files may be deleted just as the HTML is loaded, - // causing file not found errors. -// src.sendResources(hclient.client) + send_asset_list(hclient.client,asset_list[type], TRUE) if (!winexists(hclient.client, "browser_\ref[src]")) src.createWindow(hclient) diff --git a/code/modules/html_interface/nanotrasen/nanotrasen.dm b/code/modules/html_interface/nanotrasen/nanotrasen.dm index 3c1cfa47ef6..5ae9407093f 100644 --- a/code/modules/html_interface/nanotrasen/nanotrasen.dm +++ b/code/modules/html_interface/nanotrasen/nanotrasen.dm @@ -29,12 +29,11 @@ The client is optional and may be a /mob, /client or /html_interface_client obje // Update the title in our custom header (in addition to default functionality) winset(hclient.client, "browser_\ref[src].uiTitle", list2params(list("text" = "[src.title]"))) -/datum/html_interface/nanotrasen/sendResources(client/client) - . = ..() // we need the default resources - - client << browse_rsc('uiBg.png') - client << browse_rsc('uiBgcenter.png') - client << browse_rsc('nanotrasen.css') +/datum/html_interface/nanotrasen/registerResources(var/list/resources = list()) + resources["uiBg.png"] = 'uiBg.png' + resources["uiBgcenter.png"] = 'uiBgcenter.png' + resources["nanotrasen.css"] = 'nanotrasen.css' + ..(resources) /datum/html_interface/nanotrasen/createWindow(datum/html_interface_client/hclient) . = ..() // we want the default window diff --git a/tgstation.dme b/tgstation.dme index d21a9b4294e..72b87999480 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -923,6 +923,7 @@ #include "code\modules\awaymissions\mission_code\spacebattle.dm" #include "code\modules\awaymissions\mission_code\stationCollision.dm" #include "code\modules\awaymissions\mission_code\wildwest.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\message.dm"