Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using serialization for saving and loading #67

Open
luetkemj opened this issue Dec 27, 2021 · 5 comments
Open

Using serialization for saving and loading #67

luetkemj opened this issue Dec 27, 2021 · 5 comments
Labels
bug Something isn't working

Comments

@luetkemj
Copy link

Serializing a world seems to work but deserializing throws the following error:

Uncaught TypeError: Cannot read properties of undefined (reading 'Symbol(storeBase)')
at bitecs.v0.3.34.js:472

Here's some repro code:

import { createWorld, defineSerializer, defineDeserializer } from "bitecs";

let packet;

export const save = (world) => {
  const serialize = defineSerializer(world);
  packet = serialize(world);
};

export const load = () => {
  let newWorld = createWorld();
  const deserialize = defineDeserializer(newWorld);
  deserialize(newWorld, packet);
  return newWorld;
};

I don't get an error if I use defineSerializer with same world that I serialized but I'm trying to figure out how that's useful. If I have the world already, there's no reason to deserialize.

Not sure if bug or I'm missing something...

@NateTheGreatt
Copy link
Owner

seems like a bug! thanks for reporting, i'll work on a fix ASAP

@NateTheGreatt
Copy link
Owner

NateTheGreatt commented Feb 22, 2022

@luetkemj it seems this error indicates that newWorld does not know about a component that world knows about. components are normally automatically registered with worlds when they are either added to an entity or a query with that component is called on a world, but it can also be done explicitly. i created an example which reproduces the error:

import { 
  Types, 
  createWorld,
  defineSerializer,
  defineDeserializer,
  defineComponent,
  registerComponent,
  addEntity,
  addComponent
} from "bitecs"

const save = (world) => {
  const serialize = defineSerializer(world)
  return serialize(world)
}

const load = (world, packet) => {
  const deserialize = defineDeserializer(world)
  deserialize(world, packet)
}

const worldA = createWorld()
const worldB = createWorld()

const C = defineComponent({
  x: Types.f32,
  y: Types.f32,
  z: Types.f32,
})

registerComponent(worldA, C)
// uncomment this line and the error goes away
// registerComponent(worldB, C)

const eidA = addEntity(worldA)

addComponent(worldA, C, eidA)

const packet = save(worldA)

load(worldB, packet)

because serializers and deserializers need to have the same exact config up-front, using worlds creates a bit of an edge case if those worlds don't know about the same exact components. to avoid this error one should explicitly pass in all components to both the serializer and deserializer for now.

however, i will be introducing a new function to a future release which should make this easier: getWorldComponents

i'm now wondering if it's a bad idea to allow a world to be passed into the initial serializer config, and instead if something like this should replace that feature:

const serialize = defineSerializer(getWorldComponents(world))

what do you think?

@luetkemj
Copy link
Author

luetkemj commented Feb 22, 2022

const serialize = defineSerializer(getWorldComponents(world))

@NateTheGreatt That feels a lot more intuitive to me. If there's a legitimate usecase for passing in the world directly that I'm missing, good documentation should make things clear about when to do what.

@O4epegb
Copy link

O4epegb commented Jun 20, 2022

Would also love to see some in-build solution for that!

So far I've been using this to keep track of all components (to avoid forgetting to register one):

const definedComponents: ComponentType<any>[] = [];

const defineComponent: typeof defineComponentBitecs = (schema, size) => {
  const component = defineComponentBitecs(schema, size);
 
  definedComponents.push(component);

  return component;
};

// component definitions with custom `defineComponent`
// ...

export const registerAllComponents = (world: IWorld) => {
  registerComponents(world, definedComponents);
};

@lunarnet76
Copy link

lunarnet76 commented Jun 6, 2024

Thank you Nate for your code, I didn't realise you code register a component against a world, if I register all components then it works!!!

also, in case anybody needs it, it took me a while how to actually save it to a file (in nodejs, I am using electron)

fs.writeFileSync(this.FilePath(saveName), Buffer.from(data));

and to read
this.typedArrayToBuffer(fs.readFileSync(this.FilePath(saveName)));

with
typedArrayToBuffer(array: Uint8Array): ArrayBuffer {
    return array.buffer.slice(array.byteOffset, array.byteLength + array.byteOffset)
}

(I am unsure why it works but it works for me, which is good enough for now!)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants