Increasingly Bad Performance [SOLVED]

Io

So I almost have a game done. I'm running some final balance/typo/bug checks, and one thing that has consistently stuck out to me is how the performance in the game just TANKS the longer you play it. Which is a downright weird thing to say about a text game.

This'll be a bit long, but please bear with me.

A large part of the game is team fights. You and up to 3 allies vs up to 4 enemies in turn-based combat. A lot of it's laid out like a puzzle; "which teammates do I want for this fight? How do I avoid THIS enemy attack?" etc.

Here's the performance issue: When you are IN the fight, the performance gets worse and worse the longer you play the game. So at the start you choose to shoot enemy 1 with a doubleshot. You go into a special 'This is what happens this turn' room and see:

You shoot Enemy1 twice!
X damage!
X damage!

Ally1 puts a shield around Ally3!

Ally2 heals Ally3 for X hp!

Ally3 zaps Enemy1! X damage!

Enemy1 does something.

Enemy2 does something.

CONTINUE HYPERLINK

And then you press the Continue hyperlink, the screen clears, you're brought to the 'choose your attack' room. And the cycle repeats.

The problem is, as time goes on, it begins to stutter. Pauses occur between lines, seemingly at random, regardless of what's happening in the actual fight. The worse the problem is, the longer these pauses are and there can even be multiple ones per new description. There's no rhyme or reason as to WHERE the pauses occur, not that I can tell even after using debug-prints statements.

These pauses also occur on the 'choose your attack' room, since the 'choose your attack' room generates its description via script. The description is generally as follows:

YOU
50/60 HP
30/75 Energy

Ally1
60/60 HP
50/75 Energy

Ally2
50/50 HP
35/100 Energy

Ally3
100/150 HP
50/55 Energy

Enemy1
900/999 HP
900/999 Energy

ATTACKS Hyperlink
MISC actions Hyperlink
RUN AWAY Hyperlink

And there'd be random pauses at, during, and before various chunks of text.

As for how the fights work: I use a lot of Objects and Clones. When a fight starts, I make a clone of a FighterBaseObject that's in a DebugRoom, set its HP and Alias and etc, and move it to the Fight room. I repeat this for all enemies and nonpermanent allies (Permanent Ally FighterObjects are cloned at the start and are simply moved around).

This gives me a LOT of flexibility with the fights. I can even make robust buffs/debuffs by having a similar cloning habit with BuffBaseObjects moved into various FighterBaseObjects.

When a given object is no longer needed, I use destroy() to get rid of it, as opposed to Remove so there isn't a million useless objects from past fights just floating around.

Here are some things I know the issue isn't.

I know it's not one specific fight causing the problem. If I use my developer debug commands to instantly start Encounter 99, defeat that, Encounter 100, 101, and 102, then when I go into Encounter 103 the performance will be virtually unplayable.

But if I instead instantly go to Encounter 103, it's just fine until partway into the fight.

I know it's not my Fight Code being too complicated
After every character takes their turn, the game checks every clone of FighterObject, and every BuffObject in each FighterObject, and manipulates their attributes as needed with the state of the fight. Since there's a fair few 4v4 fights, I thought maybe it's just taking a while to process?

But no. Encounters 99,100,101,103 might all be 4v4s, but Encounter 6, for instance, is much simpler, so is Encounter 7. But if I go through those, the performance still becomes bad.

I'm reasonably certain it's not excessive generation/deletion of FighterObjects and BuffObjects by fights
If I developer-debug right into encounters 99-103, I get the slowdown issue.

But if I do not, if I play normally, walking around a bunch of rooms so that actual fighting is a minority of the play, and go through Encounters 1-4, then encounter 5 will still have terrible performance just like 103 does.

Now, this COULD be because of another Object clones I produce; you travel around in a spaceship through various solar systems, which I populate via CelestialObjectBase clones to create the stars and planets, and destroy() them when you change systems. But the amount of objects destroyed in this manner is MINUSCULE compared to how many FighterObjects and BuffObjects are destroyed when I go through encounters 99-103.

The only idea I have is that somehow the game stores every move you ever make, and over the course of a longer playtime slowsdown immensely for some reason.

I would seriously appreciate some help. This is an ambitious game for me, but this performance issue makes it unplayable; I cannot publish something in this state!


Launch Task Manager... watch the memory value...
It could be that you are using up your memory and the CPU it trying to get by on what's left...
Instead of cloning and destroying them, why not recycle them?
Keep a few copies, and change their stats as needed.


Io

@DarkLizerd I have tried that before, the memory hovers at around 50-60% iirc, with no apparent relation to what's actively going on ingame.

I could recycle, but then what happens if you go from a fight with 4v4 to 4v1? What do I do with the remaining 3? What about any buffs those 3 had? What if I reuse those 3, what do I do with their buffobjects?

It just seems like such a hassle, for no benefit that I can find.


My first thought is whether the delay occurs the same on the desktop and online players.
(If it does, the issue is pretty easy to pin down, because timing is different between the two)

Sorry if you've already checked that.


Io

How would I test the difference between desktop and online players? I'm editing it on the dowloadable version of quest, I cannot test it online? Unless there's some way of doing so that I am missing?


Io

Bumping because still unsolved. I don't know, is this a frowned upon thing to do? I really want an idea of what to do.


could you create a sample game with just your battle code? Then we could recreate the problem.


Io

@Pertex, alright, I've created a Performance Test game with what I believe are all the relevant parts. Though like I said earlier, I'm not actually confident that it's the battle code causing the problem.

http://textadventures.co.uk/games/view/kncfkkv-wucsidayvhnhug/performancetest


no, its not the battle code, I dont get performance problems.
Does the problem appear while running a walkthrough? Perhaps at the same position when you start the walkthrough several times?


Io

I didn't actually use a walkthrough for testing - never learned how. I had an optional "JumpAhead" function that fires at the start of the game, which brings you to a place in the game that I control ahead of time by altering the code.

It reads a DebugRoom.JumpList stringlist for stuff like "BeatEnemy1", "StartFinalMission" etc and does certain things.

I get the performance issue whether I use the JumpAhead or not, and when I do, where I jump has no impact on the issue.


To use a Walk-through

  1. Go to your Tree Of Stuff and Scroll Down to the Bottom.

  2. Above Advanced (depending on your set up) you'll see Walkthrough.

  3. Through the GUI, Right Click on Walkthrough and Create A New Walkthrough.
    3a. In CodeView. (see below)

  <walkthrough name="Random">
    <steps type="simplestringlist">
    </steps>
  </walkthrough>
  1. In the GUI, click on the new Walkthrough Object. You'll see these symbols (the best I can interpret) on the right...
+ - / > O 
  1. The Circle records your choices. So click that and run through your game up to the point where the problem happens, then click STOP as normal and all your choices up to that point will be recorded.

  2. Hit the PLAY button to rush through those choices each time :) and there's your Walkthrough.

Anoynnn.


Io

Thanks Anonynn. Sadly through the looks of it, the walkthrough wouldn't be much help here, as the game as fairly open ended and has a small amount of variance; sometimes a fight may last 5 or 4 turns depending on how enemies RNG dodge, and I can't see any way to add conditions to check what's going on in the game.

And as I mentioned earlier, the problem isn't something that just suddenly happens out of nowhere. It's a steady decrease in performance over time, eventually becoming worse than I can bear.


I'm not as knowledge-able as all of you guys (io, mrangel, pixie, pertex, etc), but here's my thoughts:

with modern machines/CPUs/memory-sticks, etc, bad/slow performance can only be caused in a few ways:

  1. the software (quest) being used itself has problems (not enough memory allocated to it from the computer memory, for it to then allocate enough memory for peoples' games, etc software-hardware stuff, or see #2-4 below for the software itself's own internal coding)

  2. too much data: usually this culprit is error'ed/bugged or inefficient, scripting and/or game design, involving cloning (creating more data)

  3. too much operations: can be any number of error'ed/bugged or inefficient, scripting and/or game design, but generally (easiest suspect) it's often due to (near infinite or infinite) looping and/or usage of recursion and/or doing too many levels/layers/nesting of array/list/dictionary 'foreach-ing'/iterations

  4. the software doesn't allocate enough memory for your game


so, I'd start with looking at all of your game's functionality that involves cloning and checking for any scripting that uses looping, recursion, and/or many layers/levels/nesting of iterating (foreach-ing) arrays/lists/dictionaries

you might have to make test games for every such functionality within your game, if the issue can't be seen via looking at the scripting itself

you might also want/need to provide others who're willing and able to help with this troubleshooting with your entire game code, so they can get to work on trying to figure out the problem(s), if it is your game/game-code that is the issue...


for the cloning issue:

you could have scripting so that every time you create a clone, or any scripting creating a (non-clone) object, to count/store/display the number of clones, and/or in displaying the gotten the number of total objects, via 'ListCount (AllObjects ())', and/or n displaying the number of non-clone Objects via subtracting the total objects - the clone objects

to show you how many clones and/or total objects (objects and their clones) (and/or the number of non-clone Objects via subtracting the total objects - the clone objects) are within the game


If you're sure it's happening with clones… might be worth checking if you're destroying clones that might still be in an objectlist somewhere.
Not sure, but a quick look through the code makes me think this could potentially confuse the GC and lead to some slowdown later on.


Io

hegemonkhan, good idea about ListCount(AllObjects()) to see what's going on. How do I use that to specifically only target clones of any arbitrary object, rather than only specifically one type of object's clones? I've had the idea about me having an 'open ended' loop, but despite my best efforts I can't actually find one anywhere.

mrangel, could it be that using

FilterByAttribute(ObjectListSort(GetDirectChildren(Fight), "order", "alias"), "prototype", FightingCharacterBase)

could be creating a bunch of 'junk object lists' that confuses destroy() and causes this issue? Because I do that a LOT.


I'm not sure. I know FilterByAttribute is creating a new list, but that should be a local variable and be cleaned up efficiently. As far as I'm aware, sorting should be in-place.

Though if you're worried about slowdown, it might be good to make that statement a little more efficient. Your filter will probably remove items from the list, which would make it quicker to sort. So if there were 10 objects in the room but only 8 of them are FightingCharacterBase clones, you'd get a 27% speed increase by rearranging it to:

ObjectListSort(FilterByAttribute(GetDirectChildren(Fight), "prototype", FightingCharacterBase), "order", "alias")

One more thought that comes to mind. If you save the game when it's running slowly, and then load it again, does it start running at a reasonable speed again?
If you restart the game and immediately start playing again, does it continue to work slowly for a short time?


Io
One more thought that comes to mind. If you save the game when it's running slowly, and then load it again, does it start running at a reasonable speed again?
If you restart the game and immediately start playing again, does it continue to work slowly for a short time?

A very good idea. I'll give it a test.


Io

Put testing it off a while, but no, the performance issues do not go away by reloading a save. If I save the a game that has had its performance deteriorate, and load, the performance stays as it was.


How about if you load a performance-degraded game at a later date, after Quest hasn't been running for a while, or after a reboot?

If it's still degraded, this implies there's junk data within the game. Try opening the save file in a text editor, and look for anything that shouldn't be there. Is the save noticeably larger than the original game.aslx? If so, try to work out why. Look for objects that should have been deleted, or very large dictionaries.
(noting that it will always be larger than your original game file, as the save includes all of your libraries)

If it's only degraded when reloading the game right away (or if saving a degraded game and then loading a non-degraded one is still slow), this must be a bug in the Quest desktop player itself.


Io

Performance remains degraded after I restarted my laptop.

I opened two games in Notepad; TestSpace1, which has had its performance degraded, and TestSpace2, which is a save from the absolute start of a fresh game and is undegraded. TestSpace1 is 5883 KB, TestSpace2 is 4022 KB - the file I get when I Publish my game is 700KB. I put them into https://www.diffchecker.com/. It claims there is 63542 Removals and 67892 Additions.

The differences start after Line 460, which is

<template name="EditorObjectUseGiveUseonitsown">Use (on its own)</template>

Line 461, for instance, is

<template name="EditorObjectUseGiveAction">Action</template> on TestSpace2
vs
  <template name="EditorObjectUseGiveMenucaption">Menu caption (leave blank for default)</template> on TestSpace1

Most of this just seems to be the stuff pushed out of order. But if I look at the end of TestSpace1 vs TestSpace2 I come across something... interesting. There appears to be a log of... every single thing that was EVER printed. For TestSpace2 it's short, just a generic warning to save often. For TestSpace1 it's... pretty big. It doesn't seem to hold a record of all the functions that go on behind the screen, but it does hold stuff like

"The guns shoot at Ally3, dealing <b>X Damage</b> and reducing his Armor!"

And even some data on when I ClearScreen. This data alone is, when copy-pasted into a Word document, 146KB, 403 pages, and 123,000 words, at barely a quarter of the way into the game!

This is THE only significant difference I can find between the two. There's some stuff with lastobjects type showing my GearFabricator holding a bunch of verbs and the like, but those are small.

So... it does seem like the game keeps a log of every single thing it ever prints, even though I am making liberal use of ClearScreen to keep things looking nice on the player's end.


So... it does seem like the game keeps a log of every single thing it ever prints, even though I am making liberal use of ClearScreen to keep things looking nice on the player's end.

Yeah; the transcript is designed for debugging. The JS pushes it back to Quest immediately before saving.

In 5.8 it was changed so that clearing the screen just sets dispay: none on previous text, so it can stil be accessed for debugging purposes.

At 100kb+ in hidden paragraphs, it's entirely possible they're causing a performance hit on the browser front end.

If you delete most of the transcript from the save file and reload it, does it still run slowly?


If this helps, you may want to try doing:
JS.eval("saveClearedText = false;")

(In which case CearScreen will actually erase the previous text)


Io

Deleting the transcript and reloading it clears the performance issue 100%! Oh my god could this be it?! Just gotta test a fresh game with your JS script...

Yes! It's working! The performance degradation is no more! Thank you so much; I was about ready to give up on this whole thing because of this issue.


wow, so it's the the debugging log/record/activation-record of cleared (displayed) text, that's building up too much and slowing down the system

might want to let pixie know, if not aware of this thread, and have the 'saveClearedText = false' as the default setting, but informing quest user's that if they need the debugging log, of how to toggle it back on (saveClearedText = true)


@HK -
Yes, good thinking!

@mrangel -
So just put JS.eval("saveClearedText = false;") under the Start tab? Or the UserInitInterface tab?


I'd put it in UI Initialisation. Possibly have a "debug mode" variable that the player can use to enable it again so you can use it for debugging.

Of course, if you go to the javascript debugger while testing your game (Ctrl+Shift+J I think), you can type saveClearedText = true; to make clearscreen hide rather than deleting again, and $('#divOutput style:contains(clearedScreen)').remove(); to make previously-cleared text visible again for debugging purposes.


đź‘Ť


Io

Hmm, where is the UI Initialisation? I'd just been putting it in a frequently used bit of code, since the game 'forgets' to stop saving the log every time I reload the save.


The UII tab is the same as the "Advanced Scripts" tab on the game object. Then the code will run in a new game and a reloaded game.


Io

Got it, thanks! For some reason I had Show Advanced Scripts turned off?


By default, the Advanced Scripts tab is hidden. To make it visible, go to the Features tab under the same "game" object, and check "Show advanced scripts for the game object".


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

Support

Forums