Progress is lost on refresh when using <<link>> and <<replace>>. How to remember the page?

Twine v2.6.1
Sugarcube v2.36.1

Hello. I created a page using <<link>> and <<replace>> macros. Everything works fast, without any transitions, as I wanted.

The problem that the current progress is lost every time I refresh the page.

So my idea was to render the page before refreshing the page.
I tried to achieve that via javascript:

window.onbeforeunload = function () {
    Engine.play(passage());
};

it works. The progress is saved before the refresh.
But with this javascript code, if I push twineā€™s ā€œrestartā€ button everything goes red (a lot of mistakes pop up). Seems like all the ā€œspecialā€ passages are skipped every 2nd ā€œrestartā€ and the function canā€™t find the name of the current passage.
image
Interesting fact, if I push ā€œrestartā€ again everything becomes ok again. Next time I push I can see mistakes again, and so on. So it isnā€™t the solution. Something is wrong.

Any suggestions how to improve the code or any better ways to solve the problem?

I think you are fighting the engine here ā€” the game is meant to forget what happens after you load the passage, until you load the next.

Play state (the passage you are on, the state of any story variables etc.) is only saved when you transition from one passage to another. An automatic session save makes sure that the last state (the current passage as it was when you loaded it) is restored when you refresh, even if the player hasnā€™t made an explicit save, but itā€™s still meant to be a disaster recovery situation ā€” you arenā€™t meant to be reloading in that way.

The normal way people try to cope with this is to reload the passage after every change, with <<goto `passage()`>> or the equivalent.

If your problem is just the transition, you can suppress that with CSS for your passage, and use that method.

All of the main Story Formats that include a History system (like SugarCube & Harlowe) are specifically designed to only update that progress History during the Passage Transition process.
eg. they require Passage transitioning to work correctly.

The main Story Formats are also designed to reprocess the contents of the Passage currently being visited if the web-page is refreshed for any reason, like when a mobile web-browser is given focus after a call is taken or a message/sms is read.

So if you want to create a Passage that tracks dynamic changes made to its HTML structure in reaction to end-user interactions then you need to use something other than (or in conjunction with) the default Story Variable system. And luckily SugarCube includes three functions (memorize(), recall(), and forget()) that can be used to store data in the web-browserā€™s LocalStorage area.

note: Because you didnā€™t supply any examples of the variables or data youā€™re using to track the current state of the ā€˜Puzzleā€™ I current include that information in my own code examples, so I will just use comments in place of real variables/values. All the following examples are untested, and will be Twee Notation based if they include the contents of more than a single Passage.

1: Initialise the variables being used to track the current state of the ā€˜Puzzleā€™.

Because Story Variables are reset (upon page refresh or Save loading) to the value they had just before the ā€˜currentā€™ Passage was processed the variable(s) being used to track the current state of your ā€˜Puzzleā€™ will need to be initialise before the Passage that contains it is visited. And one place to do that is in the body of the link that leads to that Passage.

Blah blah
<<link "Start Puzzle" "Puzzle">>
	/* initialise the puzzle's data structure */
	<<set $puzzle to { /* the object properties and default values used */ }>>
	<<run memorize('puzzle', $puzzle)>>
<</link>>

2: Load current state of ā€˜Puzzleā€™ for each (re)visit to the Passage.

This should be done early in the processing of the Passageā€™s content, so the later content can use that data when displaying the ā€˜Puzzleā€™.

<<set $puzzle to recall('puzzle')>>

/* the code that (re)builds the visualisation of 'puzzle' based on the current state,

3: Persist the new current state each time it changes.

This is best done in the code that gets called when end-user interacts with the ā€˜puzzleā€™.
note: Because I donā€™t know how you handle such interactions I will used a basic link to represent them.

<<link "Tile 1">>
	/* 1. update the current state of the puzzle. */
	<<set $puzzle to { /* the new values */ }>>

	/* 2. update the current visualisation of the puzzle. */
	<<replace "#puzzle">>/* with something different */<</replace>>

	/* 3. persist the current state to memory. */
	<<run memorize('puzzle', $puzzle)>>
<</link>>

4: Forget the current state of the ā€˜Puzzleā€™ when transitioning to ā€˜nextā€™ Passage.

The default maximum size of a web-browserā€™s LocalStorage area is between 2-5 MBs per domain, and depending on the brand of web-browser that 2-5 MBs may need to be shared between all locally opened HTML files. And that same LocalStorage area is also used by Story Formats to store the (slot based) Saves the end-user creates, so cleaning up any old data is a good idea.

One place to do this forgetting is in the body of the link used to exit the ā€˜Puzzleā€™ Passage.

<<link "Exit Puzzle" "Some Other Passage">>
	<<run forget('puzzle')>>
<</link>>

The following is an example of all of the above put together.

:: Some Passage
Blah blah
<<link "Start Puzzle" "Puzzle">>
	/* initialise the puzzle's data structure */
	<<set $puzzle to { /* the object properties and default values used */ }>>
	<<run memorize('puzzle', $puzzle)>>
<</link>>

:: Puzzle
<<set $puzzle to recall('puzzle')>>

/* 1. determine what to display this time */

/* 2. display it */
<div id="puzzle">
	<<link "Tile 1">>
		/* 1. determine the new state of the puzzle. */
		<<set $puzzle to { /* the new values */ }>>

		/* 2. update the current visualisation of the puzzle. */
		<<replace "#puzzle">>/* with something different */<</replace>>

		/* 3. persist the current state to memory. */
		<<run memorize('puzzle', $puzzle)>>
	<</link>>
	<<link "Tile 2">>
		/* 1. determine the new state of the puzzle. */
		/* 2. update the current visualisation of the puzzle. */
		/* 3. persist the current state to memory. */
		<<run memorize('puzzle', $puzzle)>>
	<</link>>
</div>

<<link "Exit Puzzle" "Some Other Passage">>
	<<run forget('puzzle')>>
<</link>>

:: Some Other Passage
You completed the 'puzzle'.
1 Like

OOh, thanks. I will go through it step by step and then give my answer, but it will take some time :sweat_smile: