ID:1973163
 
(See the best response by Lummox JR.)
Code:
construct_header(msg)
if(!archive.button_icons["[msg]"])
var/obj/o = drawfont.QuickText(src, msg, "#E6C208", 1, layer = 93)
var/icon/draw_text = new/icon(o.icon)
var/icon/product = new/icon('Headerbackground.dmi')
draw_text.Crop(1, 32, draw_text.Width(), 10)
product.Blend(draw_text, ICON_OVERLAY, 9, -4)
archive.button_icons["[msg]"] = product

src << browse_rsc(archive.button_icons["[msg]"], "header_[archive.button_icons.Find(msg)].png")
src << output("[skin_tab["html"]]<img src=header_[archive.button_icons.Find(title)].png></body><html>", "alert.header")


Problem description:

Sometimes, the image will display as an X. I am presuming this is because the client hasn't recieved the file yet.

How do I make sure the client has recieved all image files before attempting to display the image? Something like 'sleep(100)' is a shitty estimate. Better yet, can I display images directly from the RSC instead of the client cache?

The best way I have is to use body onload="finished()", but I don't really want to do that unless I have to.
I've never had an issue with this, but then again I don't use it too often. If they are images that don't change, why not load them for the client when they enter? I made a system in the past that stored all browser images in a list, and when a client would join the server it would cycle through said list and give the client all of the images. That just seems like the simplest solution. Otherwise, you're looking at using javascript.
Best response
If you use browse() instead of output() to send output to a browser control, the issue will disappear. The browse proc is treated as a file download action (at least if browse_rsc is done first), and those get queued.
In response to Lummox JR
It is not always feasible to use browse() instead of output(), however, we have a UI module that almost exclusively uses output() to update the UI, and it suffers from the same issue.

Although I'm working on a fix right now which I'll post here if it ends up working.
Well, here's my fix for this, it's bloody huge and colveluted but it works:

/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({"
<script>
window.location.href="?asset_cache_confirm_arrival=
[job]"
</script>
"}
, "window=asset_cache_browser")

while(client && !client.completed_asset_jobs.Find(job)) // Reception is handled in Topic()
sleep(1) // Lock up the caller until this is received.

client.sending -= asset_name
client.cache |= asset_name
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

return ..()

/client/Topic()
// Global Asset cache stuff.
if(href_list["asset_cache_confirm_arrival"])
src << "ASSET JOB [href_list["asset_cache_confirm_arrival"]] ARRIVED."
var/job = text2num(href_list["asset_cache_confirm_arrival"])
completed_asset_jobs += job
return
return ..()


You'll need an invisible browser element in your skin somewhere, with the ID "asset_cache_browser".
In response to PJB3005
PJB3005 wrote:
It is not always feasible to use browse() instead of output(), however, we have a UI module that almost exclusively uses output() to update the UI, and it suffers from the same issue.

Out of curiosity, why isn't it feasible? Is output() doing something special for you here that browse() can't?

Using output() for a browser control has two wrinkles: First, it skips the file queue when browse_rsc() sends are pending. Second, in the webclient's case it actually requires a ping-pong back to the server to force the text through as a proper browse(), because the webclient relies on special server processing for browser controls.
In response to Lummox JR
The UI method I was talking about exclusively uses output() for updating it live, almost all of the UIs in our project use HTML.

We can't really use browse(), because it reloads the entire UI, Meaning complex jscript related things such as realtime charts would have to reload.

And if it's not for that there's always calling of javascript functions which might need a new image.
In response to PJB3005
The reload problem is only a factor when outputting the page itself, right? Using output() vs. browse() should make zero difference in terms of refresh behavior if you're outputting to the control rather than to a script, since output() does not append to the page but always replaces it.

The only real potential issue I can see is that because browse() subjects itself to the file queue, output() messages intended for scripts wouldn't queue up behind it like they normally would.
Yes, we actually have all UI updating done through jscript calls.
Have you considered onerror handlers for your images, or a global handler that could take care of it?