Vorple and run-time problem P36

I’m porting an existing project to Vorple and have run into a problem with how it handles external files. A line like

read File of Story Progress into Table of Story Progress;

yields the error

*** Run-time problem P36: File being read is not a previously saved table: table ‘Table of Story Progress’.

What does this mean?

This problem doesn’t show up in Quixe or the IDE interpreter, so I believe it has to do with Vorple’s handling of the virtual filesystem. (I don’t understand large swaths of that article, but I’m guessing it might be relevant.)

I’m using my external file to automatically save parts of the game state, like this:

"Balls"

Include Vorple by Juhana Leinonen.

Release along with the "Vorple" interpreter.

Ball Pit is a room.	
	
The file of Ball Counting (owned by another project) is called "ballcounting".

I believe the language about what project owns the file is important here, but I’m not clear on how.

When I specify (owned by another project):

  • Vorple ends up giving me problem P36
  • Quixe has no problem with it

When I specify (owned by project "VORPLE"):

  • Vorple ends up giving me problem P36
  • Quixe gives me problem P48:

*** Error on file ‘ballcounting’: tried to open a file owned by another project ***

*** Run-time problem P48: Error handling external file.

but I’m led to understand this is expected.

When I don’t specify what project owns the file:

  • Vorple gives me problem P48 instead (which makes sense I think?)
  • Quixe is fine with it

Moving on:

When play begins:
	if File of Ball Counting exists:
		read File of Ball Counting into Table of Ball Counting;
		choose row 1 in Table of Ball Counting;
		now ballcount is datum entry;
	otherwise:
		write File of Ball Counting from Table of Ball Counting.

Table of Ball Counting
datum
0

Some balls are in Ball Pit. Understand "ball" as the balls.

Ballcount is initially 0.

Instead of taking the balls:
	increment ballcount;
	say "You take a ball. You have [ballcount] balls!";
	choose row 1 in Table of Ball Counting;
	now datum entry is ballcount;
	write File of Ball Counting from Table of Ball Counting;
	
Instead of taking inventory:
	say "You have [ballcount] balls!"

The first time this game is loaded, the “File of Story Progress” doesn’t exist, and the line write File of Ball Counting from Table of Ball Counting. seems to run just fine.

When the game is restarted or the page is refreshed, the line read File of Ball Counting into Table of Ball Counting; triggers instead, and run-time problem P36 appears:

*** Run-time problem P36: File being read is not a previously saved table: table ‘Table of Ball Counting’.

The file fails to be read into the table, so no ball-collecting progress is restored.

I understand this virtual filesystem stuff just enough to know(?) that all the files of this type created by Vorple games end up in the same “inform” directory of localStorage, so I keep wondering if this is a namespace issue. In reproducing the problem I’ve been careful to use unique file names (and projects with unique IFIDs, in case that somehow matters) but I continue to see problem P36 no matter what names I use.

Disclaimer: I have no idea what I’m doing, and all of this is probably a terrible idea. But it seems to work. I think.

When I checked the contents of the ballcounting file, it spit out this:

* //EE7B379A-4384-4F39-AE18-F66CF1762E04// ballcounting
! Table of Ball Counting (1)
0

I’m not sure what all of that is, and I don’t know how to figure it out. So I just did something different.

Here’s my Inform code:

Include Vorple by Juhana Leinonen.
Release along with the "Vorple" interpreter.

Ballcount is a number that varies.

When play begins:
	execute JavaScript command "return retrieveNumber('ballcountLS')";
	now ballcount is the number returned by the JavaScript command;

Ball Pit is a room.

Some balls are in Ball Pit. Understand "ball" as the balls.

Instead of taking the balls:
	increment ballcount;
	say "You take a ball. You have [ballcount] balls!";
	execute JavaScript command "writeStorage('ballcountLS', [ballcount])".
	
Instead of taking inventory:
	say "You have [ballcount] balls!"

And my JavaScript:

const retrieveNumber = (keyName, defaultNum = 0) => {
  const ls = vorple.file.read(keyName);
  if (ls !== null) return parseInt(ls);
  return defaultNum;
}

const writeStorage = (keyName, contents) => {
  vorple.file.write(keyName, contents);
}

Or if you’re doing something that would require a table with more than one row, like autosaving the player’s inventory, you could do something like this:

A luckyCharm is a kind of thing.  A luckyCharm has a number called invCode.

When play begins:
	execute JavaScript command "return fillArrFromLS('charmsLS', charmsArr)";
	let N be the number returned by the JavaScript command;
	if N is greater than -1:
		repeat with index running from 0 to N:
			execute JavaScript command "return retrieveFromArr([index], charmsArr)";
			let code be the number returned by the JavaScript command;
			repeat with item running through luckyCharms:
				if the invCode of the item is code:
					now the player has the item.

The green clover is a luckyCharm.  The invCode of the green clover is 1.  The green clover is in the Ball Pit.
The blue diamond is a luckyCharm.  The invCode of the blue diamond is 2.  The blue diamond is in the Ball Pit.
The pink heart is a luckyCharm.  The invCode of the pink heart is 3.  The pink heart is in the Ball Pit.
The purple horseshoe is a luckyCharm.  The invCode of the purple horseshoe is 4.  The purple horseshoe is in the Ball Pit.

Carry out taking a luckyCharm:
	execute JavaScript command "addItemToArr(charmsArr, [invCode of the noun])";
	execute JavaScript command "setStorageFromArr('charmsLS', charmsArr)".
	
Carry out dropping a luckyCharm:
	execute JavaScript command "removeItemFromArr(charmsArr, [invCode of the noun])";
	execute JavaScript command "setStorageFromArr('charmsLS', charmsArr)".
let charmsArr = [];

const fillArrFromLS = (keyName, arr) => {
  let ls = vorple.file.read(keyName);
  if (ls !== null & ls !== '') {
    ls = ls.split(', ');
    for (i = 0; i < ls.length; i++) {
      arr.push(parseInt(ls[i]));
    }
  }
  return arr.length - 1;
}

const retrieveFromArr = (index, arr) => {
  return arr[index];
}

const addItemToArr = (arr, num) => {
  arr.push(num);
}

const removeItemFromArr = (arr, num) => {
  let index = arr.indexOf(num);
  arr.splice(index, 1);
}

const setStorageFromArr = (keyName, arr) => {
  let str = '';
  for (i = 0; i < arr.length; i++) {
    if (i === 0) str = str.concat(arr[i]);
    else str = str.concat(', ', arr[i]);
  }
  vorple.file.write(keyName, str);
}
1 Like

Thanks, this might be just what I need! I do need to store some array-style progress so thank you for taking the time to write a much more involved example.

Toward understanding run-time problem P36 I thought I’d explain the format of the file:

* //EE7B379A-4384-4F39-AE18-F66CF1762E04// ballcounting
! Table of Ball Counting (1)
0

The asterisk signifies that the file is “ready to read.” When not ready, it’s a hyphen. For all I know, my problems have something to do with Vorple changing this asterisk to a hyphen when I’m not looking, but in that case I’d expect the error message to say something about that…

I had assumed that //EE7B379A-4384-4F39-AE18-F66CF1762E04// was the IFID of the project that “owns” the file (so if we specified (owned by project "VORPLE") this line would read //VORPLE//, but the documentation says this is the IFID of the project which last wrote to the file. I guess the notion of ownership is not contained in the external file. So I’m more confused than ever about how ownership works.

Obviously “Table of Ball Counting” is the name of the original table that got written into the file, but it’s kind of interesting that it’s there. I forgot (or never knew) that the actual table name got written into the file, and I interpreted the P36 error message as pulling that name out of the game itself, not out of the external file.

I’m still not sure what “is not a previously saved table” could mean (since the table name in the file clearly matches the table name in the game), but this seems to rule out the ownership of the file as the source of the problem. Or maybe I shouldn’t say that.

1 Like

Actually, it is. What I posted before is what it gave me when I just read the file, but I got curious and poked at it some more. When I looked at the file info, it did list the project that owns it in the contents.

contents: "* //VORPLE// ballcounting\n* //EE7B379A-4384-4F39-AE18-F66CF1762E04// ballcounting\n! Table of Ball Counting (1)\n0 \n"
directory: "/inform"
header: {project: "VORPLE", ready: true}
isDirectory: false
name: "ballcounting"
path: "/inform/ballcounting"

Thank you for explaining stuff, and you’re welcome. I needed to figure this out anyway for a project that I’m working on. And about that, there’s something I’ve been wanting to ask you. I’ll send you a PM.

I’ve managed to implement Harkness’s workaround in my own project, to great success, and for a moment I even gave Harkness a solution checkmark—but I am not yet satisfied. I would still like to know what run-time problem P36 actually means, even if a solution is not forthcoming or possible.

2 Likes

It means that TableRead() encountered some parsing error in the table contents.