LastOliveGames / becsy

A multithreaded Entity Component System (ECS) for TypeScript and JavaScript, inspired by ECSY and bitecs.
MIT License
202 stars 17 forks source link

World.terminate() never resolves #18

Closed calebj0seph closed 2 years ago

calebj0seph commented 2 years ago

Consider the following:

import { System, World } from '@lastolivegames/becsy';

class SystemA extends System {}

class SystemB extends System {
  constructor() {
    super();
    this.schedule((s) => s.after(SystemA));
  }
}

class SystemC extends System {
  constructor() {
    super();
    this.schedule((s) => s.after(SystemA));
  }
}

const startAndTerminate = async () => {
  console.log('Starting world');

  const world = await World.create({
    defs: [SystemA, SystemB, SystemC],
  });

  await world.terminate();

  console.log('Terminated!');
};

startAndTerminate();

The line console.log('Terminated!') will never execute as world.terminate() never resolves.

This is currently breaking our application as we need to wait for the world to terminate before creating a new world when another page is loaded.

Seems like the bug is in SimplePlan.finalize() on line 82 and 90: https://github.com/LastOliveGames/becsy/blob/d97a10632dcd7df75cf5142da0c4e89d6161fdd6/src/planner.ts#L82;L90

this.graph.traverse() can return an empty array, meaning the line if (!systems) return resolve() never gets executed causing the promise to never resolve. Checking if (!systems?.length) return resolve() instead seems like it would fix the issue.

pkaminski commented 2 years ago

Thanks, reproduced. The problem is actually with aliasing of the traversal array in an attempt to minimize GC -- I'll come up with a fix tonight.

pkaminski commented 2 years ago

Fixed in 0.12.3.

calebj0seph commented 2 years ago

Thanks for fixing this so quickly! Really appreciated 😃