Skip to content

Commit

Permalink
feat(queues): engine working with queues (#22)
Browse files Browse the repository at this point in the history
* wip #19

* types and tests working again 🏷️ ✅

* types and tests working again 🏷️ ✅

* feat(queues): engine working with queues

* feat(queues): engine working with queues

* feat(queues): engine working with queues

* feat(queues): tests working again ✅

* feat(queues): get next loop with idealTick

* feat(queues): Updated onTick function

* feat(queues): fix types and change README.md

---------

Co-authored-by: pagoru <[email protected]>
  • Loading branch information
alqubo and pagoru authored Mar 2, 2024
1 parent caded67 commit 9880659
Show file tree
Hide file tree
Showing 7 changed files with 515 additions and 125 deletions.
147 changes: 108 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,54 +28,114 @@ Install the package with npm:
npm install darker-engine
```

### Code Example
## Concepts

### Action Queue System

This engine implements an action queue with three levels of priority: `Priority.HIGH`, `Priority.MEDIUM`, `Priority.LOW`.
Actions are added to the queue and processed based on their priority.

#### Config
With `Engine.load` config we can specify how many ticks we want per second. By default, is 60

```ts
await Engine.load({
ticksPerSecond: 40,
})
```

With `Engine.onTick` we can sets a callback function that is run on each iteration of the loop.
The callback function receives an object with the result of the last processed action, the time (`ms`) the iteration took, and the % usage of the tick.

```ts
Engine.onTick(({usage, ms, status}) => {
console.log({ms, usage, actionId: status?.id})
})
// -> { ms: 2, usage: 0.02, actionId: 1 }
```

#### Add Actions To Queue
When we use `addEntity`, `removeEntity`, `entity.updateComponent` and `entity.removeComponent` we can specify if we want to perform the action immediately or assign it a priority.

By default, they are added to the queue and assigned a medium priority (`Priority.MEDIUM`)

```ts
// Action added to HIGH priority queue
await Engine.addEntity({
priority: Priority.HIGH,
entities: [exampleEntity()]
})

// Action that is executed immediately without depending on the queue
await Engine.addEntity({
force: true,
entities: [exampleEntity()]
})
```

## Code Example

#### Declaration

```ts
import { engine as darkerEngine } from "darker-engine";
import { engine } from "darker-engine";

export const Engine = darkerEngine<IEntities, IComponents, ComponentData>();
export const Engine = engine<IEntities, IComponents, ComponentData>();

Engine.setSystems(...[]);

Engine.load();
Engine.load({
ticksPerSecond: 40
});
```

#### Enums

```ts
enum EntityType {
EXAMPLE,
EXAMPLE_ENTITY,
}

enum Components {
EXAMPLE_COMPONENT = "EXAMPLE_COMPONENT",
EXAMPLE_COMPONENT,
OTHER_COMPONENT
}

type ComponentData = {
[Components.EXAMPLE_COMPONENT]: {
foo: string;
},
[Components.OTHER_COMPONENT]: {
bar: number;
};
};
```

#### Entity

```ts
import { EntityType } from "darker-engine";
import { EntityTypeFunction } from "darker-engine";

const exampleEntity = (): EntityType<IEntities, IComponents, ComponentData> => ({
id: Engine.getUID(),
type: EntityType.EXAMPLE,
data: {},
components: [],
});
const exampleEntity: EntityTypeFunction<IEntities, IComponents, ComponentData, any> = () => ({
type: Entities.EXAMPLE_ENTITY,
data: {
[Components.EXAMPLE_COMPONENT]: {
foo: "faa",
}
},
components: [Components.EXAMPLE_COMPONENT],
})
```

#### Systems

```ts
import { SystemFunction } from "darker-engine";

const exampleSystem: SystemFunction<IComponents> = () => {
const onAdd = (entityId: number) => {};
const onUpdate = (entityId: number, component: string) => {};
const onRemove = (entityId: number) => {};
const exampleSystem: SystemFunction<Components> = async () => {
const onAdd = async (entityId: number) => {};
const onUpdate = async (entityId: number, component: string) => {};
const onRemove = async (entityId: number) => {};

return {
components: [],
Expand All @@ -89,11 +149,7 @@ const exampleSystem: SystemFunction<IComponents> = () => {
#### Full code

```ts
import {
engine as darkerEngine,
EntityType,
SystemFunction,
} from "darker-engine";
import {engine, EntityTypeFunction, SystemFunction} from "darker-engine";

enum IEntities {
EXAMPLE_ENTITY,
Expand All @@ -113,10 +169,9 @@ type ComponentData = {
};
};

export const Engine = darkerEngine<IEntities, IComponents, ComponentData>()
export const Engine = engine<IEntities, IComponents, ComponentData>()

const exampleEntity: EntityTypeFunction<IEntities, IComponents, ComponentData> = () => ({
id: Engine.getUID(),
const exampleEntity: EntityTypeFunction<IEntities, IComponents, ComponentData, void> = () => ({
type: IEntities.EXAMPLE_ENTITY,
data: {
[IComponents.EXAMPLE_COMPONENT]: {
Expand All @@ -126,12 +181,15 @@ const exampleEntity: EntityTypeFunction<IEntities, IComponents, ComponentData> =
components: [IComponents.EXAMPLE_COMPONENT],
})

const exampleSystem: SystemFunction<IComponents> = () => {
const exampleSystem: SystemFunction<IComponents> = async () => {
let interval: number

const onLoad = () => {
const onLoad = async () => {
console.log("welcome!");
Engine.addEntity(exampleEntity());

await Engine.addEntity({
entities: [exampleEntity()]
})

interval = setInterval(() => {
const entityList = Engine.getEntityList();
Expand All @@ -147,33 +205,39 @@ const exampleSystem: SystemFunction<IComponents> = () => {
}, 5000);
}

const onDestroy = () => {
const onDestroy = async () => {
clearInterval(interval);
console.log("bye!");
}

const onAdd = (id: number) => {
const onAdd = async (id: number) => {
const entity = Engine.getEntity(id);
entity.updateComponent(IComponents.EXAMPLE_COMPONENT, { foo: 'fii'});
if(entity) {
await entity.updateComponent({
component: IComponents.EXAMPLE_COMPONENT,
data: {foo: 'fii2'}
})
}
}

const onUpdate = (id: number, component?: IComponents) => {
const onUpdate = async (id: number, component?: IComponents) => {
const entity = Engine.getEntity(id);

if (component !== IComponents.EXAMPLE_COMPONENT) return;
if (!entity || component !== IComponents.EXAMPLE_COMPONENT) return;

const { foo } = entity.getComponent(IComponents.EXAMPLE_COMPONENT);
if (foo === "fii" && !entity.hasComponent(IComponents.OTHER_COMPONENT)) {
entity.removeComponent(IComponents.EXAMPLE_COMPONENT);
await entity.removeComponent({
component: IComponents.EXAMPLE_COMPONENT
})
}
}

const onRemove = (entityId: number) => {
Engine.removeEntity(entityId);
const onRemove = async (entityId: number) => {
await Engine.removeEntity({ids: [entityId]})
};

return {
id: Engine.getUID(),
components: [IComponents.EXAMPLE_COMPONENT],
onLoad,
onDestroy,
Expand All @@ -183,7 +247,12 @@ const exampleSystem: SystemFunction<IComponents> = () => {
}
}

Engine.setSystems(exampleSystem);
Engine.load()
await Engine.setSystems(exampleSystem);
await Engine.load({
ticksPerSecond: 2,
})

Engine.onTick(({usage, ms, status}) => {
console.log({ms, usage, actionId: status?.id})
})
```
Loading

0 comments on commit 9880659

Please sign in to comment.