fenomas / noa

Experimental voxel game engine.
MIT License
608 stars 86 forks source link

voxels.index is undefined #171

Closed itzTheMeow closed 2 years ago

itzTheMeow commented 2 years ago

When i try to call noa.world.setChunkData("id", data), i get this error: image

I set up a primitive system to save/load the world on-demand:

export function saveGame() {
  let loadedChunks = [
    noa.world._getChunkByCoords(0, 0, 0),
    noa.world._getChunkByCoords(32, 0, 0),
    noa.world._getChunkByCoords(0, 32, 0),
    noa.world._getChunkByCoords(0, 0, 32),
  ];
  let chunks = loadedChunks.map((c) => [c.requestID, c.voxels]);
  let crunched = chunks.map((c) => [
    c[0],
    c[1].data.length,
    { ...c[1], ...{ data: VoxelCrunch.encode(c[1].data) } },
  ]);

  localStorage.setItem("savegame", JSON.stringify(crunched));
}

export function loadGame() {
  let saveGameData = localStorage.getItem("savegame");
  if (!saveGameData) return;
  let chunks = JSON.parse(saveGameData);

  let uncrunched = chunks.map((c) => [
    c[0],
    {
      ...c[2],
      ...{
        data: VoxelCrunch.decode(
          new Uint8Array(Object.values(c[2].data)),
          new Uint16Array(c[1])
        ),
      },
    },
  ]);

  console.log(uncrunched);
}

sort of following #39 worldDataNeeded doesn't emit on block updates

i am calling noa.world.setChunkData(id, Uint16Array) from the uncrunched array. it seems to be encoding/decoding properly image index 0 being the chunk request ID, and index 1 being the .voxels data from _getChunkByCoords

this .index function does not seem to exist: image

itzTheMeow commented 2 years ago

my world is 64x64x64 from 0-64, which is why i'm only saving 4 chunks (which are size 32) the remove chunk listener won't fire because the player doesn't move far enough away

fenomas commented 2 years ago

Hi, the issue here is that noa.world.setChunkData expects to receive an ndarray object, rather than a typed array. Ndarray is basically a wrapper around a typed array.

When noa requests a chunk of data it sends an empty ndarray, so the easiest approach is to un-crunch stored data into that:

noa.world.on('worldDataNeeded', (id, array, x, y, z, worldName) => {
    var crunchedData = // data that was previously encoded
    VoxelCrunch.decode( crunchedData, array.data )
})

The naming is a bit confusing, but the array param in that handler is the empty ndarray, and array.data is the ndarray's internal typed array.

itzTheMeow commented 2 years ago

That works, and im able to save/load data now! Thanks.

Is there a way to load world data after the game has been loaded? (change the current world to something else)

fenomas commented 2 years ago

There are two things that might help here.

One is, there is an option to manually control the timing of chunk loading, reloading, and unloading. Normally the engine decides which chunks to load and unload, depending on their distance from the player entity, and sends your game client events accordingly. However if you create noa with the manuallyControlChunkLoading flag, or you can dynamically set it as a property on noa.world, then the engine will assume you're going to decide which chunks are needed. There are APIs to manually load or unload chunks, which you can call when you want a chunk to be added to the world, removed, or overwritten with new data.

Note though, that the only difference between default and manual chunk loading is whether you or the engine decides which chunks need data. The rest of the flow is the same - when you call manuallyLoadChunk you don't directly pass in new data, the engine will (asynchronously) send an event requesting data, and then after that you call setChunkData, same as you would normally.

The other feature you might want is, when you want to completely reload all world data you can change the noa.worldName property. Then the engine will assume that all current data is stale, and unload/reload everything.

The latter feature (worldName) is meant for things like Overworld/Nether in minecraft - where you have different sets of world data that you want to swap between. The first feature (manual chunk loading) is meant for cases where you're getting data from a server, and you want the server to control what data is loaded and unloaded for each player, rather than the client.

Hope this helps!

itzTheMeow commented 2 years ago

noa.worldName should work, thanks