feat: Add spawner util

+ Added spawner utility

+ Bumped to v0.1.4
This commit is contained in:
Mark Marks 2024-09-24 19:47:49 +02:00
parent 33913122fd
commit dcb55661ac
6 changed files with 501 additions and 2 deletions

View file

@ -15,3 +15,4 @@ A set of utilities for [Jecs](https://github.com/ukendio/jecs)
- [replicator](/lib/replicator.luau) - Keep track of all entities with the passed components and calculate differences
- [ref](/lib/ref.luau) - Reference entities by key
- [command_buffer](/lib/command_buffer.luau) - Buffer commands to prevent iterator invalidation
- [spawner](/lib/spawner.luau) - Spawn entities with required components

View file

@ -3,18 +3,26 @@
local jecs = require("@jecs")
local WORLD = require("./world")
local collect = require("./collect")
export type collect_signal_like<T...> = collect.signal_like<any, T...>
export type collect_verbose_signal_like<D, T...> = collect.signal_like<D, T...>
local command_buffer = require("./command_buffer")
export type command_buffer = command_buffer.command_buffer
local handle = require("./handle")
export type handle = handle.handle
local ref = require("./ref")
local replicator = require("./replicator")
export type replicator = replicator.replicator
export type changes = replicator.changes
local spawner = require("./spawner")
export type spawner<T...> = spawner.spawner<T...>
--- Set the world for all utilities.
--- Should be called once per context before any utility is used.
--- @param world jecs.World
@ -30,4 +38,5 @@ return {
replicator = replicator,
ref = ref,
command_buffer = command_buffer,
spawner = spawner,
}

49
lib/spawner.luau Normal file
View file

@ -0,0 +1,49 @@
--!strict
local spawner_type = require("./spawner_type")
local WORLD = require("./world").get
local handle = require("./handle")
export type spawner<T...> = spawner_type.spawner<T...>
--- Creates an entity spawner.
--- ```luau
--- local spawner = jecs_utils.spawner(components.part, components.velocity, components.position)
--- for _ = 1, 1000 do
--- spawner.spawn(part_template:Clone(), Vector3.zero, Vector3.zero)
--- end
--- ```
--- @param ... T... -- Components to use.
--- @return spawner<T...>
local function spawner(...)
local components = { ... }
local world = WORLD()
local function spawn(...)
local passed = { ... }
local entity = world:entity()
for idx, component in components do
world:set(entity, component, passed[idx])
end
return entity
end
local function spawn_with_handle(...)
local passed = { ... }
local entity = handle(world:entity())
for idx, component in components do
entity:set(component, passed[idx])
end
return entity
end
return {
spawn = spawn,
spawn_with_handle = spawn_with_handle,
}
end
return (spawner :: any) :: spawner_type.create_spawner

391
lib/spawner_type.luau Normal file
View file

@ -0,0 +1,391 @@
--!strict
local jecs = require("@jecs")
type entity<T = nil> = jecs.Entity<T>
type id<T = nil> = jecs.Id<T>
local handle = require("./handle")
export type spawner<T...> = {
--- Creates an entity with the given components.
--- @param ... T...
--- @return entity
spawn: (T...) -> entity,
--- Creates an entity with the given components and returns a handle to it.
--- @param ... T...
--- @return handle
spawn_with_handle: (T...) -> handle.handle,
}
-- Very beautiful type incoming!
-- Sadly this has to be done, components are of different types than their values (`entity<T>` vs `T`)
export type create_spawner =
(<A>(id<A>) -> spawner<A>)
& (<A, B>(id<A>, id<B>) -> spawner<A, B>)
& (<A, B, C>(id<A>, id<B>, id<C>) -> spawner<A, B, C>)
& (<A, B, C, D>(id<A>, id<B>, id<C>, id<D>) -> spawner<A, B, C, D>)
& (<A, B, C, D, E>(id<A>, id<B>, id<C>, id<D>, id<E>) -> spawner<A, B, C, D, E>)
& (<A, B, C, D, E, F>(id<A>, id<B>, id<C>, id<D>, id<E>, id<F>) -> spawner<A, B, C, D, E, F>)
& (<A, B, C, D, E, F, G>(id<A>, id<B>, id<C>, id<D>, id<E>, id<F>, id<G>) -> spawner<A, B, C, D, E, F, G>)
& (<A, B, C, D, E, F, G, H>(
id<A>,
id<B>,
id<C>,
id<D>,
id<E>,
id<F>,
id<G>,
id<H>
) -> spawner<A, B, C, D, E, F, G, H>)
& (<A, B, C, D, E, F, G, H, I>(
id<A>,
id<B>,
id<C>,
id<D>,
id<E>,
id<F>,
id<G>,
id<H>,
id<I>
) -> spawner<A, B, C, D, E, F, G, H, I>)
& (<A, B, C, D, E, F, G, H, I, J>(
id<A>,
id<B>,
id<C>,
id<D>,
id<E>,
id<F>,
id<G>,
id<H>,
id<I>,
id<J>
) -> spawner<A, B, C, D, E, F, G, H, I, J>)
& (<A, B, C, D, E, F, G, H, I, J, K>(
id<A>,
id<B>,
id<C>,
id<D>,
id<E>,
id<F>,
id<G>,
id<H>,
id<I>,
id<J>,
id<K>
) -> spawner<A, B, C, D, E, F, G, H, I, J, K>)
& (<A, B, C, D, E, F, G, H, I, J, K, L>(
id<A>,
id<B>,
id<C>,
id<D>,
id<E>,
id<F>,
id<G>,
id<H>,
id<I>,
id<J>,
id<K>,
id<L>
) -> spawner<A, B, C, D, E, F, G, H, I, J, K, L>)
& (<A, B, C, D, E, F, G, H, I, J, K, L, M>(
id<A>,
id<B>,
id<C>,
id<D>,
id<E>,
id<F>,
id<G>,
id<H>,
id<I>,
id<J>,
id<K>,
id<L>,
id<M>
) -> spawner<A, B, C, D, E, F, G, H, I, J, K, L, M>)
& (<A, B, C, D, E, F, G, H, I, J, K, L, M, N>(
id<A>,
id<B>,
id<C>,
id<D>,
id<E>,
id<F>,
id<G>,
id<H>,
id<I>,
id<J>,
id<K>,
id<L>,
id<M>,
id<N>
) -> spawner<A, B, C, D, E, F, G, H, I, J, K, L, M, N>)
& (<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O>(
id<A>,
id<B>,
id<C>,
id<D>,
id<E>,
id<F>,
id<G>,
id<H>,
id<I>,
id<J>,
id<K>,
id<L>,
id<M>,
id<N>,
id<O>
) -> spawner<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O>)
& (<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P>(
id<A>,
id<B>,
id<C>,
id<D>,
id<E>,
id<F>,
id<G>,
id<H>,
id<I>,
id<J>,
id<K>,
id<L>,
id<M>,
id<N>,
id<O>,
id<P>
) -> spawner<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P>)
& (<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q>(
id<A>,
id<B>,
id<C>,
id<D>,
id<E>,
id<F>,
id<G>,
id<H>,
id<I>,
id<J>,
id<K>,
id<L>,
id<M>,
id<N>,
id<O>,
id<P>,
id<Q>
) -> spawner<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q>)
& (<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R>(
id<A>,
id<B>,
id<C>,
id<D>,
id<E>,
id<F>,
id<G>,
id<H>,
id<I>,
id<J>,
id<K>,
id<L>,
id<M>,
id<N>,
id<O>,
id<P>,
id<Q>,
id<R>
) -> spawner<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R>)
& (<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S>(
id<A>,
id<B>,
id<C>,
id<D>,
id<E>,
id<F>,
id<G>,
id<H>,
id<I>,
id<J>,
id<K>,
id<L>,
id<M>,
id<N>,
id<O>,
id<P>,
id<Q>,
id<R>,
id<S>
) -> spawner<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S>)
& (<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T>(
id<A>,
id<B>,
id<C>,
id<D>,
id<E>,
id<F>,
id<G>,
id<H>,
id<I>,
id<J>,
id<K>,
id<L>,
id<M>,
id<N>,
id<O>,
id<P>,
id<Q>,
id<R>,
id<S>,
id<T>
) -> spawner<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T>)
& (<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U>(
id<A>,
id<B>,
id<C>,
id<D>,
id<E>,
id<F>,
id<G>,
id<H>,
id<I>,
id<J>,
id<K>,
id<L>,
id<M>,
id<N>,
id<O>,
id<P>,
id<Q>,
id<R>,
id<S>,
id<T>,
id<U>
) -> spawner<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U>)
& (<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V>(
id<A>,
id<B>,
id<C>,
id<D>,
id<E>,
id<F>,
id<G>,
id<H>,
id<I>,
id<J>,
id<K>,
id<L>,
id<M>,
id<N>,
id<O>,
id<P>,
id<Q>,
id<R>,
id<S>,
id<T>,
id<U>,
id<V>
) -> spawner<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V>)
& (<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W>(
id<A>,
id<B>,
id<C>,
id<D>,
id<E>,
id<F>,
id<G>,
id<H>,
id<I>,
id<J>,
id<K>,
id<L>,
id<M>,
id<N>,
id<O>,
id<P>,
id<Q>,
id<R>,
id<S>,
id<T>,
id<U>,
id<V>,
id<W>
) -> spawner<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W>)
& (<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X>(
id<A>,
id<B>,
id<C>,
id<D>,
id<E>,
id<F>,
id<G>,
id<H>,
id<I>,
id<J>,
id<K>,
id<L>,
id<M>,
id<N>,
id<O>,
id<P>,
id<Q>,
id<R>,
id<S>,
id<T>,
id<U>,
id<V>,
id<W>,
id<X>
) -> spawner<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X>)
& (<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y>(
id<A>,
id<B>,
id<C>,
id<D>,
id<E>,
id<F>,
id<G>,
id<H>,
id<I>,
id<J>,
id<K>,
id<L>,
id<M>,
id<N>,
id<O>,
id<P>,
id<Q>,
id<R>,
id<S>,
id<T>,
id<U>,
id<V>,
id<W>,
id<X>,
id<Y>
) -> spawner<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y>)
& (<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z>(
id<A>,
id<B>,
id<C>,
id<D>,
id<E>,
id<F>,
id<G>,
id<H>,
id<I>,
id<J>,
id<K>,
id<L>,
id<M>,
id<N>,
id<O>,
id<P>,
id<Q>,
id<R>,
id<S>,
id<T>,
id<U>,
id<V>,
id<W>,
id<X>,
id<Y>,
id<Z>
) -> spawner<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z>)
return {}

View file

@ -1,14 +1,17 @@
--!strict
-- stylua: ignore start
local jecs = require("../jecs")
local jecs = require("@jecs")
local jecs_utils = require("@jecs_utils")
local testkit = require("@testkit")
type entity<T = nil> = jecs.Entity<T>
local collect = jecs_utils.collect
local handle = jecs_utils.handle
local replicator = jecs_utils.replicator
local ref = jecs_utils.ref
local command_buffer = jecs_utils.command_buffer
local spawner = jecs_utils.spawner
local signal = require("./signal")
@ -233,5 +236,51 @@ TEST("jecs_utils.command_buffer", function()
end
end)
TEST("jecs_utils.spawner()", function()
do CASE "spawn"
local world = jecs.World.new()
jecs_utils.initialize(world)
local c1: entity<number> = world:component()
local c2: entity<string> = world:component()
local c3: entity<{}> = world:component()
local t1 = world:entity()
local entity_spawner = spawner(c1, c2, c3, t1)
local tbl = {}
local idx = entity_spawner.spawn(1234, "abcdef", tbl)
CHECK(world:contains(idx))
CHECK(world:get(idx, c1) == 1234)
CHECK(world:get(idx, c2) == "abcdef")
CHECK(world:get(idx, c3) == tbl)
CHECK(world:has(idx, t1))
end
do CASE "spawn_with_handle"
local world = jecs.World.new()
jecs_utils.initialize(world)
local c1: entity<number> = world:component()
local c2: entity<string> = world:component()
local c3: entity<{}> = world:component()
local t1 = world:entity()
local entity_spawner = spawner(c1, c2, c3, t1)
local tbl = {}
local ent = entity_spawner.spawn_with_handle(1234, "abcdef", tbl)
CHECK(world:contains(ent:id()))
CHECK(ent:get(c1) == 1234)
CHECK(ent:get(c2) == "abcdef")
CHECK(ent:get(c3) == tbl)
CHECK(ent:has(t1))
end
end)
FINISH()
-- stylua: ignore end

View file

@ -1,6 +1,6 @@
[package]
name = "mark-marks/jecs-utils"
version = "0.1.3"
version = "0.1.4"
registry = "https://github.com/UpliftGames/wally-index"
realm = "shared"
license = "MIT"