first commit

This commit is contained in:
Reid 2023-07-18 22:22:52 -07:00
commit 5302ecc6cc
43 changed files with 2530 additions and 0 deletions

View file

@ -0,0 +1,8 @@
/**
* A test system that does nothing.
*/
function test(): void {
// A test system.
}
export = test

View file

@ -0,0 +1,108 @@
import { AnySystem, Debugger, Loop } from "@rbxts/matter"
import { Context, HotReloader } from "@rbxts/rewire"
import { ServerScriptService } from "@rbxts/services"
import { Host } from "ReplicatedStorage/hosts"
const ERROR_CONTAINER = "%s container not found"
const shared = script.FindFirstChild("shared")
const client = script.FindFirstChild("client")
const server = ServerScriptService.FindFirstChild("goopler")
?.FindFirstChild("ecs")
?.FindFirstChild("systems")
?.FindFirstChild("server")
let firstRunSystems: AnySystem[] | undefined = []
let hotReloader: HotReloader | undefined
/**
* Starts the system loader.
*
* Loads systems for the specified container into the provided loop and
* debugger. Systems are hot reloaded as they are changed.
*
* @param container - The container to load
* @param loop - The ECS loop to load systems into
* @param debug - The debugger to load systems into
*
* @throws "[container] container not found"
* This is thrown when a container necessary for the provided host doesn't
* exist.
*/
export function start<T extends unknown[]>(container: Host, loop: Loop<T>, debug: Debugger): void {
if (!firstRunSystems) return
const containers: Instance[] = []
if (!shared) throw string.format(ERROR_CONTAINER, "Shared")
containers.push(shared)
if (container === Host.All || container === Host.Client) {
if (!client) throw string.format(ERROR_CONTAINER, "Client")
containers.push(client)
}
if (container === Host.All || container === Host.Server) {
if (!server) throw string.format(ERROR_CONTAINER, "Server")
containers.push(server)
}
const systemsByModule: Map<ModuleScript, AnySystem> = new Map()
function load(module: ModuleScript, context: Context): void {
if (
module.Name.match("%.spec$")[0] !== undefined ||
module.Name.match("%.dev$")[0] !== undefined
)
return
const original = context.originalModule
const previous = systemsByModule.get(original)
const [ok, required] = pcall(require, module)
if (!ok) {
warn("Error when hot-reloading system", module.Name, required)
return
}
// Here we don't know that this is necessarily a system, but we let matter
// handle this at runtime.
const system = required as AnySystem
if (firstRunSystems) {
firstRunSystems.push(system)
} else if (previous) {
loop.replaceSystem(previous, system)
debug.replaceSystem(previous, system)
} else {
loop.scheduleSystem(system)
}
systemsByModule.set(original, system)
}
function unload(_: ModuleScript, context: Context): void {
if (context.isReloading) return
const original = context.originalModule
const previous = systemsByModule.get(original)
if (previous) {
loop.evictSystem(previous)
systemsByModule.delete(original)
}
}
hotReloader = new HotReloader()
for (const container of containers) {
hotReloader.scan(container, load, unload)
}
loop.scheduleSystems(firstRunSystems)
firstRunSystems = undefined
}
/**
* Stops loading systems.
*/
export function stop(): void {
if (firstRunSystems) return
firstRunSystems = []
hotReloader?.destroy()
}

View file

@ -0,0 +1,19 @@
import { World, useDeltaTime } from "@rbxts/matter"
import { Lifetime } from "ReplicatedStorage/ecs/components"
/**
* @todo
*/
function updateIdAttribute(world: World): void {
for (const [id, lifetime] of world.query(Lifetime)) {
const newLifetime = lifetime.patch({ elapsed: lifetime.elapsed + useDeltaTime() })
if (os.clock() > lifetime.spawnedAt + lifetime.length) {
world.despawn(id)
} else {
world.insert(id, newLifetime)
}
}
}
export = updateIdAttribute

View file

@ -0,0 +1,25 @@
import { DebugWidgets, World } from "@rbxts/matter"
import { Stats } from "@rbxts/services"
const startInstanceCount = Stats.InstanceCount
const startMemory = Stats.GetTotalMemoryUsageMb()
const startTime = os.clock()
function trackMemory(world: World, state: object, ui: DebugWidgets): void {
const currentInstanceCount = Stats.InstanceCount
const currentMemory = Stats.GetTotalMemoryUsageMb()
ui.window("Memory Stats", () => {
ui.label(`Instances: ${currentInstanceCount}`)
ui.label(`Gained Instances: ${currentInstanceCount - startInstanceCount}`)
ui.label(`Memory: ${string.format("%.1f", currentMemory)}`)
ui.label(`Gained Memory: ${string.format("%.1f", currentMemory - startMemory)}`)
ui.label(`Time: ${string.format("%.1f", os.clock() - startTime)}`)
})
}
export = trackMemory

View file

@ -0,0 +1,16 @@
import { World } from "@rbxts/matter"
import { Model } from "ReplicatedStorage/ecs/components"
import { getIdAttribute } from "ReplicatedStorage/idAttribute"
/**
* A system that updates the ID of {@link Model | Models}.
*
* @param world - The {@link World} the system operates on
*/
function updateIdAttribute(world: World): void {
for (const [id, record] of world.queryChanged(Model)) {
record.new?.model?.SetAttribute(getIdAttribute(), id)
}
}
export = updateIdAttribute