Preinit Transience (Also How Does Preinit Even Work) [TADS 3]

EDIT: I’ve figured out the problem. I’m just keeping this here as a reminder of how frayed I was becoming, before I finally figured it out. Details on the solution are here, just a few posts down!


A record of my manic, confused state

Okay, I’ve run some tests, and I’m reaching a rather frustrated level of abject confusion here. I feel like I’m completely misunderstanding something.

What I am trying to do is set up a bunch of pre-computated data to my gameMain.t3 game file with a PreinitObject, but selectively remove certain unnecessary data at the end of the process, so it’s not also included in that file. Namely, these are things like temporary objects and variables that hold mid-process information, but are ultimately unneeded once the whole process is complete, and we have our final values.

However, it seems I might be having difficulties saving any data to gameMain.t3 at all.

Let me begin, by stating the assumptions I’m currently working on:

List of Assumptions
  1. By defining an object which inherits from PreinitObject, anything in its execute() method will be run at the end of game compilation.
  2. The aforementioned game compilation stage is when your code files get compiled into the final gameMain.t3 file, which is used for either debugging or shipping to the player.
  3. As a result, these data are already formatted and ready to dump into interpreter memory when a player loads the gameMain.t3 file, eliminating time spent performing pre-computation. This is effectively similar to packing your game with a built-in game save that is loaded to start play.
  4. objects marked as transient are not written to game saves. As a result, when a game save is loaded later, anything that was once referring to a transient object is now nil.
  5. My expectation here is that if any PreinitObject is working with objects that are marked as transient, they will not be saved to the final gameMain.t3 file, because of point number 4 (above).

However—my dear artists, players, enthusiasts, future visitors, and post-anthropocene data-scrapers—I am gathering some evidence and hints from my short experiments that seem to really throw a wrench into this pocket watch of gears. Namely:

  1. There is quite a delay before my current project loads, compared to previous projects. This leads me to suspect that the execute() method of my PreinitObject is happening when the interpreter loads the gameMain.t3 file, and not before the creation of the gameMain.t3 file has concluded, as I had assumed. An alternative explanation is that the data dump into memory takes a lot longer than I had anticipated, but I also have not run into the memory ceiling problem, which our dear @jbg has warned me of. Because my computer is rather beefy on the CPU side of the tech spectrum, I’m a little perplexed by this delay (in what should be a simple SSD-to-RAM load procedure).
  2. The transient objects that are created during the execute() method are still there once the game starts. I have tried every combination of approaches for this, and I get the same result. I have defined a transient object reference as an initial property, I have assigned a new transient instance to a initially-nil property within the execute() method, etc.
  3. The only thing which finally worked was instantiating the object as just a normal reference, and then making the reference nil by the end of the execute() method of my PreinitObject.

I learned of point number 3’s method of removing temporary mid-calculation objects from @johnnywz00 here:

He also included this, but I freely admit that I don’t quite follow:

My questions for clarification: When you define a new object within execute() during preinit, are these not saved as a result? Do I need to store these values and object references in a specific place for them to be included in the final gameMain.t3 file…? Do you mean creating a new object assigned to a local variable only, and not to a property of the PreinitObject?

At this point, it’s becoming increasingly-clear that I have no clue about:

  1. What’s going on
  2. How to create the data which needs saving
  3. Where to store it so that it makes it to gameMain.t3
  4. How to prevent mid-computation temporary data from being saved as well

I’m wondering if I should be running t3RunGC() at the end of the execute() method, too. I’m not sure if gameMain.t3 is having the post-compile memory dumped as-is, or if I need to do some explicit spring-cleaning of memory allocation before execute() concludes, in order to keep gameMain.t3 from having a bloated size. Alternatively, it’s possible that the data needs to be dropped into a specific place, in order for it to be save with the game file.

I had first learned of the PreinitObject business from @jnelson here:

Full disclosure: I am doing all of my TADS 3 hilarity on an Ubuntu machine. I am using VSCode, with the absolutely brilliant TADS 3 extension, created by our esteemed @Tomas.

Maybe I am not using the correct compiler arguments, when I save and test my project?

I’m not sure! Maybe I’m not even writing the data in the correct place!

Either way, I have included some sample code below, which demonstrates my current level of understanding:

My code files and compiler settings

My (stripped-down) code file (gameMain.t) is below:

#charset "us-ascii"
#include <tads.h>
#include "advlite.h"

#define CLOCK_COUNT 3600

orbitSystem: PreinitObject {
    radiansPerClock = static new BigNumber('0.00174532925')

    // The values loaded into this vector are what I'm trying to precompute,
    // to prevent slow loads on the player's side.
    // I want this vector to ship with the game.t3 file.
    sineVector = static new Vector(CLOCK_COUNT)

    execute() {
        // Set up our sine vector
        local bigInterpolation = new BigNumber(360);
        for (local i = 0; i < CLOCK_COUNT; i++) {
            local theta = radiansPerClock * i;
            local interSine = theta.sine() * bigInterpolation;
            sineVector.append(toInteger(interSine));
        }
    }
}

gameMain: GameMainDef {
    initialPlayerChar = me

    showIntro() {
        say('Test value: ' + orbitSystem.sineVector[3]);
    }
}

testRoom: Room { 'Test Room'
    "Oh wow, a test room!!"
}

+me: Actor {
    person = 2
}

Makefile.t3m is as follows:

## Makefile for an adv3Lite library game template

-I liblite
-D LANGUAGE=english
-Fy obj
-Fo obj
-FI /usr/local/share/frobtads/tads3/include
-FL /usr/local/share/frobtads/tads3/lib
-we
-v
-d

##sources
-lib system
-lib adv3Lite/adv3Lite
-source gameMain.t

-res
GameInfo.txt

Compiler: t3make
I don’t see any other settings in VSCode that might hint at extra compiler options; I think it’s all defined in Makefile.t3m, shown above.

Core Question:
With the above code files, will I successfully compile gameMain.t3 to include an orbitSystem object, with an attached sineVector, already full of values, both in the debug-version game and the final shipped-version game?

I am lost, confused, and vulnerable in the software engineering sense. I’m having difficulty finding answers in the documentation, and the results perplex me.

Send help.

Thank you all!