Streamlining JS for Reloaded Game

(Quest TA, offline, v5.8)

I have an issue with my Map Demo game which uses KV's grid image library and JS script. When saving the game, there is no issue; it is only when a game is reloaded that there is a problem...

When only a small portion of the map has been created and displayed, there is no noticeable issue reloading. But as more and more of the map is shown, reloading the game takes geometrically longer (as opposed to arithmetically). Thus, when the entire map has been created/shown (about 400 rooms), reloading that game takes about 1 minute and 15 seconds! Absurd!

I'm pretty sure that the issue has to do with reloading a large # of rooms with KV's grid images. I don't think the issue has anything to do with excessive image file size -- I've reduced the sizes of all the images but it makes no difference in reloading time. Almost all of the exits in the game are created and destroyed on the fly as the player moves about, so there are only a few exits in the game at any given time. Also, there are no errors in the JS console. I've even compared (in Notepad++) a saved game with a tiny map to a saved game with the full map -- I see no obvious differences in the code other than the number of rooms.

What follows is all of the KV grid image code that is run upon reloading a game, and in chronological order. From the UserInit tab, the Quest function SetupGridImages is called:

<function name="SetupGridImages"><![CDATA[
    SetGridImgPath("")
    foreach (room, AllObjects()) {                       // NOTE the "foreach" here!!!
        if (HasAttribute(room,"grid_image")) {
            JS.eval ("imagesToCheck.push('"+room.grid_image+"');checkImages();")
        }
    }
]]></function>

<function name="SetGridImgPath" parameters="filename">
    JS.setImagePath (GetFileUrl("_FILENAME_"))
</function>

<-------------------------- code from KV's library is above this line, code from KV's JS script is below --------------------------------->

var questImagePath = "";

setImagePath = function(path) {
    questImagePath = path;
};

var imagesToCheck = [];

function checkImages(){
    imagesToCheck.forEach(function(img){                // NOTE the "forEach" here!!!
        isFileGood(img)
    });
};

// This function was updated by KV after Q5.8 to delete images that were caching in a reloaded game -
function isFileGood(url){
    var imgFile = getFileUrlJS(url);
    $('body').append("<img style='display:none' onload='$(this).remove();' onerror='imgFail(this);$(this).remove();'src='"+imgFile+"'/>");
};

getFileUrlJS = function(filename){
    if(filename.indexOf("://") > 0) {
        return (filename);
    } else {
	return questImagePath.replace("_FILENAME_", filename);
    }
};

var failedImgs = [];

function imgFail(imgFailed){
    failedImgs.push(imgFailed.src);
};

However, I think I may know what's at least partly causing the problem -- note the 2 instances of "foreach" in the code, 1 inside the SetupGridImages Quest library function, and 1 inside the checkImages() JS script function. A "forEach" loop is running inside another "foreach" loop! Could this be what is causing the (geometric) reloading delay? If so, how can this code be written more efficiently?

For reference, the original thread for this topic (gulp) is here:
http://textadventures.co.uk/forum/samples/topic/yn_iueiaokmshfcpkyahga/code-for-printing-an-image-instead-of-a-standard-map-room-mostly-mostly-finito

Thanks for reading and Happy Holidays!


Not sure if this is the right thread, but okay.


Quest just sucks with graphics. I'm moving on to Unity.


I moved the second forEach loop outside of the first foreach loop, but that made no difference in reloading time so that's not the issue.

I strongly suspect that the issue lies somewhere within this code (which runs within the second forEach loop):

function isFileGood(url){
    var imgFile = getFileUrlJS(url);
    $('body').append("<img style='display:none' onload='$(this).remove();' onerror='imgFail(this);$(this).remove();'src='"+imgFile+"'/>");
};

The logic in that code is broken if the map is already loaded.

On startup, it adds <img> tags for every map image to the document. Once they've loaded, it discards them. If they fail, it sets a flag that the image is unavailable so that the map won't attempt to load them. This isn't normally a big deal; the browser will wait for the images to load in the background, and it's pretty streamlined so the player probably won't notice.

If the map is already partly generated, it will ignore those flags - it doesn't know if the image has succeeded, or is still loading. It's attempting to load all the images before checking if they exist.

Or more precisely, at the same time. It's trying to load all of the map images for testing purposes, and all the ones for rooms you've already visited in order to display them. And the map code will block waiting for its images to load before it continues, which means that your game start is slowed down waiting for all of the room images to be downloaded, some of them twice.

Oh, and it could be a server issue. Your browser may be sending two requests for the same image at the same time. If a document contains two <img> tags with the same URI, the browser will normally download it once and display it twice. But depending on its optimisations, this javascript may prevent that happening. And a server may, depending on its configuration, detect multiple requests for the same file from the same client as a possible malware red flag, and insert an arbitrary delay.

The sane way to do this would be to have your image check run in the background, but set a flag when it is successful. Before removing the <img> it would save the rasterized data to a lookup table, so that the map code can then display them without any need to download them again. If the map comes to display an image that isn't checked yet, it should add it to the queue and then set a reference, so that it will try again as soon as the image is available.

(oh, and forEach over an array with a single element isn't noticeably slow, but it's a bit silly. It's a long time since I've seen forEach in javascript at all, because $.each is more common, but it shouldn't be a problem)


How much of this applies to an offline game?


Not sure. You're still loading a large number of images; I'm unsure if Chromium does any kind of throttling for local files.

But then they're not local files, I think. If they're inside the .quest archive, they're zipped. So each file is a request to the backend, and I'm not sure if it unzips the whole archive in memory, or if it'll have to decompress it each time a file is requested.

In javascript-based games, I've seen a workaround for this issue where they patch a hundred images together into a grid, and then cut it up into separate rasters as needed; but that's more complex code, which I'm not fluent enough in Paper to replicate.


Ok, thanks for looking.


This topic is now closed. Topics are closed after 60 days of inactivity.

Support

Forums