How does Memory work?



  • I've read the https://docs.screeps.com/global-objects.html, but I didn't find the answer...

    I want to save an instance of a class to the Memory. My code is:

    class MyClass {
        constructor(name) {
            this.name = name;
        }
        run() {
            console.log("Run in class")
        }
    }
    let instance = new MyClass('name');
    Memory.classes = {};
    Memory.classes['name'] = instance;
    console.log("'run' method for 'Memory.scripts['name']' (Not in main loop) is:  " + Memory.classes['name'].run)
    module.exports.loop = function () {
        console.log("'run' method for 'Memory.scripts['name']' (In main loop) is:  " + Memory.classes['name'].run)
    }
    

    The console output is:

    [22:40:22]'run' method for 'Memory.scripts['name']' (Not in main loop) is:  run() {
            console.log("Run in class")
        }
    [22:40:22]'run' method for 'Memory.scripts['name']' (In main loop) is:  run() {
            console.log("Run in class")
        }
    [22:40:23]'run' method for 'Memory.scripts['name']' (In main loop) is:  undefined
    [22:40:24]'run' method for 'Memory.scripts['name']' (In main loop) is:  undefined
    [22:40:25]'run' method for 'Memory.scripts['name']' (In main loop) is:  undefined
    [22:40:26]'run' method for 'Memory.scripts['name']' (In main loop) is:  undefined
    

    In the first game tick, the Memory.classes['name'].run is defined. It's a function run().

    But in the following ticks the Memory.classes['name'].run is undefined.

    I would be grateful if someone could help me figure it out.



  • Short answer, you can't.

    The reason for this is your code only executes in tiny spurts once per tick, between ticks you could lose the global memory and it would have to be recreated.

    Store data you need to be persistent in the Memory object, "Memory" that's part of your global memory set up for you at the beginning of each tick. Game structures have a shortcut to the memory object that is handy for storing the data pertinent to that game structure. For example, the Creep object has a property, "memory" that points to the place in the memory object associated with that creep, keyed off it's name. So the Memory object for creep "myCreep" would be "Memory.creeps.myCreep" and can be accessed through the creep as "creep.memory"

    Data that you can just recreate can be stored in global memory, but know that it could simply "go away" and you will have to rebuild it.



  • Short answer: Indeed, you can't.

    Better longer answer is that you can ONLY save primitive objects to Memory. As stated in the documentation, this is because at the end of each Tick, Memory is serialized by JSON.stringify and the resulting string is saved to the backend.

    On the next tick, the backend gives back a plain string which is passed to JSON.parse to produce the Memory object available to your script.

    For this reason, you cannot save any class instances, functions, or Game Object references to Memory as JSON.parse will not know how to reinstantiate the class/function on the next tick.

    The usual solutions are to save Game Object Ids (e.g. Memory.mainSpawnId = Game.spawns.Spawn1.id). Ids are strings, and will be returned from memory on the next tick, and you can look up the Game Object instance on that tick with: const spawn = Game.getObjectById(Memory.mainSpawnId)

    You should save to Memory just enough primitive information to reinstantiate your needed classes on later ticks.


    Unrelated to the Memory object, there is another caching technique where you cache real object instances on the Global scope. (e.g. global.MainClass = new MyClass())

    This object actually will survive the Tick, as it exists in your virtual javascript instance on the server. (However, I believe you cannot store actual Game Objects (e.g. Creeps, Structures, etc) this way, as they are regenerated every tick by the runtime).

    However, while Memory has a system guarantee to ALWAYS survive till the next tick, Global scope has no such guarantee. And anything saved on Global Scope can be cleared without warning by the Runtime if it restarts your isolated VM. (see: https://wiki.screepspl.us/index.php/Global_reset). This is what Smokeman is referring to as you should only store data on Global Scope that you can regenerate/recalculate from the information you have in Memory.

    Combining these two techniques efficiently is a large part of optimizing Screeps 🙂