mirror of
				https://github.com/Ukendio/jecs.git
				synced 2025-11-04 02:49:18 +00:00 
			
		
		
		
	Add systems to demo
This commit is contained in:
		
							parent
							
								
									ecd7b9f89e
								
							
						
					
					
						commit
						52060dbb06
					
				
					 21 changed files with 489 additions and 152 deletions
				
			
		| 
						 | 
					@ -3,24 +3,15 @@
 | 
				
			||||||
    "tree": {
 | 
					    "tree": {
 | 
				
			||||||
        "$className": "DataModel",
 | 
					        "$className": "DataModel",
 | 
				
			||||||
        "ReplicatedStorage": {
 | 
					        "ReplicatedStorage": {
 | 
				
			||||||
            "Shared": {
 | 
					            "$className": "ReplicatedStorage",
 | 
				
			||||||
                "$path": "demo/src/shared"
 | 
					            "$path": "demo/src/ReplicatedStorage",
 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "ecs": {
 | 
					            "ecs": {
 | 
				
			||||||
                "$path": "src"
 | 
					                "$path": "src"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "ServerScriptService": {
 | 
					        "ServerScriptService": {
 | 
				
			||||||
            "Server": {
 | 
					            "$className": "ServerScriptService",
 | 
				
			||||||
                "$path": "demo/src/server"
 | 
					            "$path": "demo/src/ServerScriptService"
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        "StarterPlayer": {
 | 
					 | 
				
			||||||
            "StarterPlayerScripts": {
 | 
					 | 
				
			||||||
                "Client": {
 | 
					 | 
				
			||||||
                    "$path": "demo/src/client"
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "Workspace": {
 | 
					        "Workspace": {
 | 
				
			||||||
            "$properties": {
 | 
					            "$properties": {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,17 +1,15 @@
 | 
				
			||||||
# example
 | 
					# Demo
 | 
				
			||||||
Generated by [Rojo](https://github.com/rojo-rbx/rojo) 7.4.1.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Getting Started
 | 
					## Build with Rojo
 | 
				
			||||||
To build the place from scratch, use:
 | 
					To build the place, run the following commands from the root of the repository:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```bash
 | 
					```bash
 | 
				
			||||||
rojo build -o "example.rbxlx"
 | 
					cd demo
 | 
				
			||||||
 | 
					rojo build -o "demo.rbxl"
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Next, open `example.rbxlx` in Roblox Studio and start the Rojo server:
 | 
					Next, open `demo.rbxl` in Roblox Studio and start the Rojo server:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```bash
 | 
					```bash
 | 
				
			||||||
rojo serve
 | 
					rojo serve
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					 | 
				
			||||||
For more help, check out [the Rojo documentation](https://rojo.space/docs).
 | 
					 | 
				
			||||||
							
								
								
									
										40
									
								
								demo/src/ReplicatedStorage/std/bt.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								demo/src/ReplicatedStorage/std/bt.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,40 @@
 | 
				
			||||||
 | 
					--!optimize 2
 | 
				
			||||||
 | 
					--!native
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- original author @centau
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local SUCCESS = 0
 | 
				
			||||||
 | 
					local FAILURE = 1
 | 
				
			||||||
 | 
					local RUNNING = 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function SEQUENCE(nodes)
 | 
				
			||||||
 | 
					    return function(...)
 | 
				
			||||||
 | 
					        for _, node in nodes do
 | 
				
			||||||
 | 
					            local status = node(...)
 | 
				
			||||||
 | 
					            if status == FAILURE or status == RUNNING then
 | 
				
			||||||
 | 
					                return status
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        return SUCCESS
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function FALLBACK(nodes)
 | 
				
			||||||
 | 
					    return function(...)
 | 
				
			||||||
 | 
					        for _, node in nodes do
 | 
				
			||||||
 | 
					            local status = node(...)
 | 
				
			||||||
 | 
					            if status == SUCCESS or status == RUNNING then
 | 
				
			||||||
 | 
					                return status
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					        return FAILURE
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local bt = {
 | 
				
			||||||
 | 
					    SEQUENCE = SEQUENCE,
 | 
				
			||||||
 | 
					    FALLBACK = FALLBACK,
 | 
				
			||||||
 | 
					    RUNNING = RUNNING
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return bt
 | 
				
			||||||
| 
						 | 
					@ -1,73 +1,8 @@
 | 
				
			||||||
--!optimize 2
 | 
					 | 
				
			||||||
--!native
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local jecs = require(game:GetService("ReplicatedStorage").ecs)
 | 
					local jecs = require(game:GetService("ReplicatedStorage").ecs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type World = jecs.WorldShim
 | 
					local world = require(script.Parent.world)
 | 
				
			||||||
type Entity<T = any> = jecs.Entity<T>
 | 
					local sparse = ((world :: any) :: jecs.World).entityIndex.sparse
 | 
				
			||||||
 | 
					type World = world.World
 | 
				
			||||||
local function panic(str)
 | 
					 | 
				
			||||||
    -- We don't want to interrupt the loop when we error
 | 
					 | 
				
			||||||
    task.spawn(error, str)
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function Scheduler(world, ...)
 | 
					 | 
				
			||||||
    local systems = { ... }
 | 
					 | 
				
			||||||
    local systemsNames = {}
 | 
					 | 
				
			||||||
    local N = #systems
 | 
					 | 
				
			||||||
    local system
 | 
					 | 
				
			||||||
    local dt
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for i, module in systems do
 | 
					 | 
				
			||||||
        local sys = require(module)
 | 
					 | 
				
			||||||
        systems[i] = sys
 | 
					 | 
				
			||||||
        local file, line = debug.info(2, "sl")
 | 
					 | 
				
			||||||
        systemsNames[sys] = `{file}->::{line}::->{debug.info(sys, "n")}`
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    local function run()
 | 
					 | 
				
			||||||
        local name = systemsNames[system]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        debug.profilebegin(name)
 | 
					 | 
				
			||||||
        debug.setmemorycategory(name)
 | 
					 | 
				
			||||||
        system(world, dt)
 | 
					 | 
				
			||||||
        debug.profileend()
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    local function loop(sinceLastFrame)
 | 
					 | 
				
			||||||
        debug.profilebegin("loop()")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for i = N, 1, -1 do
 | 
					 | 
				
			||||||
            system = systems[i]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            dt = sinceLastFrame
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            local didNotYield, why = xpcall(function()
 | 
					 | 
				
			||||||
                for _ in run do end
 | 
					 | 
				
			||||||
            end, debug.traceback)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if didNotYield then
 | 
					 | 
				
			||||||
				continue
 | 
					 | 
				
			||||||
			end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if string.find(why, "thread is not yieldable") then
 | 
					 | 
				
			||||||
				N -= 1
 | 
					 | 
				
			||||||
				local name = table.remove(systems, i)
 | 
					 | 
				
			||||||
				panic("Not allowed to yield in the systems."
 | 
					 | 
				
			||||||
    				.. "\n"
 | 
					 | 
				
			||||||
    				.. `System: {name} has been ejected`
 | 
					 | 
				
			||||||
				)
 | 
					 | 
				
			||||||
			else
 | 
					 | 
				
			||||||
			    panic(why)
 | 
					 | 
				
			||||||
			end
 | 
					 | 
				
			||||||
        end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        debug.profileend()
 | 
					 | 
				
			||||||
        debug.resetmemorycategory()
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return loop
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Tracker<T> = { track: (world: World, fn: (changes: {
 | 
					type Tracker<T> = { track: (world: World, fn: (changes: {
 | 
				
			||||||
        added: () -> () -> (number, T),
 | 
					        added: () -> () -> (number, T),
 | 
				
			||||||
| 
						 | 
					@ -97,7 +32,7 @@ local function diff(a, b)
 | 
				
			||||||
    return false
 | 
					    return false
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local function ChangeTracker<T>(world, T: Entity<T>): Tracker<T>
 | 
					local function ChangeTracker<T>(T: Entity<T>): Tracker<T>
 | 
				
			||||||
    local PreviousT = jecs.pair(jecs.Rest, T)
 | 
					    local PreviousT = jecs.pair(jecs.Rest, T)
 | 
				
			||||||
    local add = {}
 | 
					    local add = {}
 | 
				
			||||||
    local added
 | 
					    local added
 | 
				
			||||||
| 
						 | 
					@ -142,7 +77,7 @@ local function ChangeTracker<T>(world, T: Entity<T>): Tracker<T>
 | 
				
			||||||
                id, new, old = q.next()
 | 
					                id, new, old = q.next()
 | 
				
			||||||
            end
 | 
					            end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            local record = world.entityIndex.sparse[id]
 | 
					            local record = sparse[id]
 | 
				
			||||||
            local archetype = record.archetype
 | 
					            local archetype = record.archetype
 | 
				
			||||||
            local column = archetype.records[PreviousT].column
 | 
					            local column = archetype.records[PreviousT].column
 | 
				
			||||||
            local data = if is_trivial then new else table.clone(new)
 | 
					            local data = if is_trivial then new else table.clone(new)
 | 
				
			||||||
| 
						 | 
					@ -197,62 +132,4 @@ local function ChangeTracker<T>(world, T: Entity<T>): Tracker<T>
 | 
				
			||||||
    return tracker
 | 
					    return tracker
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
local bt
 | 
					return ChangeTracker
 | 
				
			||||||
do
 | 
					 | 
				
			||||||
    local SUCCESS = 0
 | 
					 | 
				
			||||||
    local FAILURE = 1
 | 
					 | 
				
			||||||
    local RUNNING = 2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    local function SEQUENCE(nodes)
 | 
					 | 
				
			||||||
        return function(...)
 | 
					 | 
				
			||||||
            for _, node in nodes do
 | 
					 | 
				
			||||||
                local status = node(...)
 | 
					 | 
				
			||||||
                if status == FAILURE or status == RUNNING then
 | 
					 | 
				
			||||||
                    return status
 | 
					 | 
				
			||||||
                end
 | 
					 | 
				
			||||||
            end
 | 
					 | 
				
			||||||
            return SUCCESS
 | 
					 | 
				
			||||||
        end
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
    local function FALLBACK(nodes)
 | 
					 | 
				
			||||||
        return function(...)
 | 
					 | 
				
			||||||
            for _, node in nodes do
 | 
					 | 
				
			||||||
                local status = node(...)
 | 
					 | 
				
			||||||
                if status == SUCCESS or status == RUNNING then
 | 
					 | 
				
			||||||
                    return status
 | 
					 | 
				
			||||||
                end
 | 
					 | 
				
			||||||
            end
 | 
					 | 
				
			||||||
            return FAILURE
 | 
					 | 
				
			||||||
        end
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
    bt = {
 | 
					 | 
				
			||||||
        SEQUENCE = SEQUENCE,
 | 
					 | 
				
			||||||
        FALLBACK = FALLBACK,
 | 
					 | 
				
			||||||
        RUNNING = RUNNING
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function interval(s)
 | 
					 | 
				
			||||||
    local pin
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    local function throttle()
 | 
					 | 
				
			||||||
        if not pin then
 | 
					 | 
				
			||||||
            pin = os.clock()
 | 
					 | 
				
			||||||
        end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        local elapsed = os.clock() - pin > s
 | 
					 | 
				
			||||||
        if elapsed then
 | 
					 | 
				
			||||||
            pin = os.clock()
 | 
					 | 
				
			||||||
        end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return elapsed
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
    return throttle
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
return {
 | 
					 | 
				
			||||||
    Scheduler = Scheduler,
 | 
					 | 
				
			||||||
    ChangeTracker = ChangeTracker,
 | 
					 | 
				
			||||||
    interval = interval,
 | 
					 | 
				
			||||||
    BehaviorTree = bt
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										67
									
								
								demo/src/ReplicatedStorage/std/collect.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								demo/src/ReplicatedStorage/std/collect.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,67 @@
 | 
				
			||||||
 | 
					--!nonstrict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--[[
 | 
				
			||||||
 | 
						local signal = Signal.new() :: Signal.Signal<string, string>
 | 
				
			||||||
 | 
						local events = collect(signal)
 | 
				
			||||||
 | 
						local function system(world)
 | 
				
			||||||
 | 
							for id, str1, str2 in events do
 | 
				
			||||||
 | 
								--
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					--[[
 | 
				
			||||||
 | 
					original author by @memorycode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MIT License
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Copyright (c) 2024 Michael
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
				
			||||||
 | 
					of this software and associated documentation files (the "Software"), to deal
 | 
				
			||||||
 | 
					in the Software without restriction, including without limitation the rights
 | 
				
			||||||
 | 
					to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
				
			||||||
 | 
					copies of the Software, and to permit persons to whom the Software is
 | 
				
			||||||
 | 
					furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The above copyright notice and this permission notice shall be included in all
 | 
				
			||||||
 | 
					copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
				
			||||||
 | 
					AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
				
			||||||
 | 
					OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
				
			||||||
 | 
					SOFTWARE.
 | 
				
			||||||
 | 
					--]]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Signal<T...> = { [any]: any }
 | 
				
			||||||
 | 
					local function collect<T...>(event: Signal<T...>)
 | 
				
			||||||
 | 
						local storage = {}
 | 
				
			||||||
 | 
						local mt = {}
 | 
				
			||||||
 | 
						local iter = function()
 | 
				
			||||||
 | 
							local n = #storage
 | 
				
			||||||
 | 
							return function()
 | 
				
			||||||
 | 
								if n <= 0 then
 | 
				
			||||||
 | 
									mt.__iter = nil
 | 
				
			||||||
 | 
									return nil
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								n -= 1
 | 
				
			||||||
 | 
								return n + 1, unpack(table.remove(storage, 1) :: any)
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local disconnect = event:Connect(function(...)
 | 
				
			||||||
 | 
							table.insert(storage, { ... })
 | 
				
			||||||
 | 
							mt.__iter = iter
 | 
				
			||||||
 | 
						end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						setmetatable(storage, mt)
 | 
				
			||||||
 | 
						return (storage :: any) :: () -> (number, T...), function()
 | 
				
			||||||
 | 
							disconnect()
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return collect
 | 
				
			||||||
							
								
								
									
										14
									
								
								demo/src/ReplicatedStorage/std/components.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								demo/src/ReplicatedStorage/std/components.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,14 @@
 | 
				
			||||||
 | 
					local jecs = require(game:GetService("ReplicatedStorage").ecs)
 | 
				
			||||||
 | 
					local world = require(script.Parent.world)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local components = {
 | 
				
			||||||
 | 
					    Character = world:component(),
 | 
				
			||||||
 | 
					    Mob = world:component(),
 | 
				
			||||||
 | 
					    Model = world:component() :: jecs.Entity<Model>,
 | 
				
			||||||
 | 
					    Player = world:component(),
 | 
				
			||||||
 | 
					    Target = world:component(),
 | 
				
			||||||
 | 
					    Transform = world:component(),
 | 
				
			||||||
 | 
					    Velocity = world:component(),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return table.freeze(components)
 | 
				
			||||||
							
								
								
									
										11
									
								
								demo/src/ReplicatedStorage/std/ctx.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								demo/src/ReplicatedStorage/std/ctx.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					local world = require(script.Parent.world)
 | 
				
			||||||
 | 
					local handle = require(script.Parent.handle)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local singleton = world:entity()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function ctx()
 | 
				
			||||||
 | 
					    -- Cannot cache handles because they will get invalidated
 | 
				
			||||||
 | 
					    return handle(singleton)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return ctx
 | 
				
			||||||
							
								
								
									
										52
									
								
								demo/src/ReplicatedStorage/std/handle.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								demo/src/ReplicatedStorage/std/handle.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,52 @@
 | 
				
			||||||
 | 
					local jecs = require(game:GetService("ReplicatedStorage").ecs)
 | 
				
			||||||
 | 
					local world = require(script.Parent.world)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Handle = {
 | 
				
			||||||
 | 
					    has: (self: Handle, id: jecs.Entity) -> boolean,
 | 
				
			||||||
 | 
					    get: <T>(self: Handle, id: jecs.Entity<T>) -> T?,
 | 
				
			||||||
 | 
					    add: <T>(self: Handle, id: jecs.Entity<T>) -> Handle,
 | 
				
			||||||
 | 
					    set: <T>(self: Handle, id: jecs.Entity<T>, value: T) -> Handle
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local handle: (e: jecs.Entity) -> Handle
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					do
 | 
				
			||||||
 | 
					    local e
 | 
				
			||||||
 | 
					    local function has(_, id)
 | 
				
			||||||
 | 
					        return world:has(e, id)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    local function get(_, id)
 | 
				
			||||||
 | 
					        return world:get(e, id)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    local function set(self, id, value)
 | 
				
			||||||
 | 
					        world:set(e, id, value)
 | 
				
			||||||
 | 
					        return self
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    local function add(self, id)
 | 
				
			||||||
 | 
					        world:add(e, id)
 | 
				
			||||||
 | 
					        return self
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    local function clear(self)
 | 
				
			||||||
 | 
					        world:clear(e)
 | 
				
			||||||
 | 
					        return self
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    local function id()
 | 
				
			||||||
 | 
					        return e
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local entity = {
 | 
				
			||||||
 | 
					        has = has,
 | 
				
			||||||
 | 
					        get = get,
 | 
				
			||||||
 | 
					        set = set,
 | 
				
			||||||
 | 
					        add = add,
 | 
				
			||||||
 | 
					        clear = clear,
 | 
				
			||||||
 | 
					        id = id,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function handle(id)
 | 
				
			||||||
 | 
					        e = id
 | 
				
			||||||
 | 
					        return entity
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return handle
 | 
				
			||||||
							
								
								
									
										20
									
								
								demo/src/ReplicatedStorage/std/init.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								demo/src/ReplicatedStorage/std/init.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					local jecs = require(game:GetService("ReplicatedStorage").ecs)
 | 
				
			||||||
 | 
					local world = require(script.world)
 | 
				
			||||||
 | 
					export type World = world.World
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local std = {
 | 
				
			||||||
 | 
					    ChangeTracker = require(script.changetracker),
 | 
				
			||||||
 | 
					    Scheduler = require(script.scheduler),
 | 
				
			||||||
 | 
					    bt = require(script.bt),
 | 
				
			||||||
 | 
					    collect = require(script.collect),
 | 
				
			||||||
 | 
					    components = require(script.components),
 | 
				
			||||||
 | 
					    ctx = require(script.ctx),
 | 
				
			||||||
 | 
					    handle = require(script.handle),
 | 
				
			||||||
 | 
					    interval = require(script.interval),
 | 
				
			||||||
 | 
					    ref = require(script.ref),
 | 
				
			||||||
 | 
					    world = world,
 | 
				
			||||||
 | 
					    pair = jecs.pair,
 | 
				
			||||||
 | 
					    __ = jecs.w,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return std
 | 
				
			||||||
							
								
								
									
										19
									
								
								demo/src/ReplicatedStorage/std/interval.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								demo/src/ReplicatedStorage/std/interval.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,19 @@
 | 
				
			||||||
 | 
					local function interval(s)
 | 
				
			||||||
 | 
					    local pin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local function throttle()
 | 
				
			||||||
 | 
					        if not pin then
 | 
				
			||||||
 | 
					            pin = os.clock()
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        local elapsed = os.clock() - pin > s
 | 
				
			||||||
 | 
					        if elapsed then
 | 
				
			||||||
 | 
					            pin = os.clock()
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return elapsed
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    return throttle
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return interval
 | 
				
			||||||
							
								
								
									
										18
									
								
								demo/src/ReplicatedStorage/std/ref.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								demo/src/ReplicatedStorage/std/ref.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					local world = require(script.Parent.world)
 | 
				
			||||||
 | 
					local handle = require(script.Parent.handle)
 | 
				
			||||||
 | 
					local refs = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function ref(key)
 | 
				
			||||||
 | 
					    if not key then
 | 
				
			||||||
 | 
					        return handle(world:entity())
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    local e = refs[key]
 | 
				
			||||||
 | 
					    if not e then
 | 
				
			||||||
 | 
					        e = world:entity()
 | 
				
			||||||
 | 
					        refs[key] = e
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					    -- Cannot cache handles because they will get invalidated
 | 
				
			||||||
 | 
					    return handle(e)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return ref
 | 
				
			||||||
							
								
								
									
										64
									
								
								demo/src/ReplicatedStorage/std/scheduler.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								demo/src/ReplicatedStorage/std/scheduler.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,64 @@
 | 
				
			||||||
 | 
					local function panic(str)
 | 
				
			||||||
 | 
					    -- We don't want to interrupt the loop when we error
 | 
				
			||||||
 | 
					    task.spawn(error, str)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function Scheduler(...)
 | 
				
			||||||
 | 
					    local systems = { ... }
 | 
				
			||||||
 | 
					    local systemsNames = {}
 | 
				
			||||||
 | 
					    local N = #systems
 | 
				
			||||||
 | 
					    local system
 | 
				
			||||||
 | 
					    local dt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for i, module in systems do
 | 
				
			||||||
 | 
					        local sys = require(module)
 | 
				
			||||||
 | 
					        systems[i] = sys
 | 
				
			||||||
 | 
					        local file, line = debug.info(2, "sl")
 | 
				
			||||||
 | 
					        systemsNames[sys] = `{file}->::{line}::->{debug.info(sys, "n")}`
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local function run()
 | 
				
			||||||
 | 
					        local name = systemsNames[system]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        debug.profilebegin(name)
 | 
				
			||||||
 | 
					        debug.setmemorycategory(name)
 | 
				
			||||||
 | 
					        system(dt)
 | 
				
			||||||
 | 
					        debug.profileend()
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local function loop(sinceLastFrame)
 | 
				
			||||||
 | 
					        debug.profilebegin("loop()")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for i = N, 1, -1 do
 | 
				
			||||||
 | 
					            system = systems[i]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            dt = sinceLastFrame
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            local didNotYield, why = xpcall(function()
 | 
				
			||||||
 | 
					                for _ in run do end
 | 
				
			||||||
 | 
					            end, debug.traceback)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if didNotYield then
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if string.find(why, "thread is not yieldable") then
 | 
				
			||||||
 | 
									N -= 1
 | 
				
			||||||
 | 
									local name = table.remove(systems, i)
 | 
				
			||||||
 | 
									panic("Not allowed to yield in the systems."
 | 
				
			||||||
 | 
					    				.. "\n"
 | 
				
			||||||
 | 
					    				.. `System: {name} has been ejected`
 | 
				
			||||||
 | 
									)
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
								    panic(why)
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        debug.profileend()
 | 
				
			||||||
 | 
					        debug.resetmemorycategory()
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return loop
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return Scheduler
 | 
				
			||||||
							
								
								
									
										4
									
								
								demo/src/ReplicatedStorage/std/world.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								demo/src/ReplicatedStorage/std/world.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					local jecs = require(game:GetService("ReplicatedStorage").ecs)
 | 
				
			||||||
 | 
					export type World = jecs.WorldShim
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return jecs.World.new()
 | 
				
			||||||
							
								
								
									
										4
									
								
								demo/src/ServerScriptService/main.server.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								demo/src/ServerScriptService/main.server.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
				
			||||||
 | 
					local std = require(ReplicatedStorage.std)
 | 
				
			||||||
 | 
					local loop = std.Scheduler(unpack(script.Parent.systems:GetChildren()))
 | 
				
			||||||
 | 
					game:GetService("RunService").Heartbeat:Connect(loop)
 | 
				
			||||||
							
								
								
									
										54
									
								
								demo/src/ServerScriptService/systems/mobsMove.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								demo/src/ServerScriptService/systems/mobsMove.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,54 @@
 | 
				
			||||||
 | 
					local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local jecs = require(ReplicatedStorage.ecs)
 | 
				
			||||||
 | 
					local pair = jecs.pair
 | 
				
			||||||
 | 
					local __ = jecs.Wildcard
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local std = require(ReplicatedStorage.std)
 | 
				
			||||||
 | 
					local world = std.world
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local cts = std.components
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local Mob = cts.Mob
 | 
				
			||||||
 | 
					local Model = cts.Model
 | 
				
			||||||
 | 
					local Transform = cts.Transform
 | 
				
			||||||
 | 
					local Velocity = cts.Velocity
 | 
				
			||||||
 | 
					local Target = cts.Target
 | 
				
			||||||
 | 
					local Player = cts.Player
 | 
				
			||||||
 | 
					local Character = cts.Character
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function mobsMove(dt: number)
 | 
				
			||||||
 | 
					    local players = world:query(Character):with(Player)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for mob, cf, v in world:query(Transform, Velocity)
 | 
				
			||||||
 | 
					        :with(Mob, Model)
 | 
				
			||||||
 | 
					    do
 | 
				
			||||||
 | 
					        local p = cf.Position
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        local target
 | 
				
			||||||
 | 
					        for playerId, character in players do
 | 
				
			||||||
 | 
					            local pos = character.PrimaryPart.Position
 | 
				
			||||||
 | 
					            if true then
 | 
				
			||||||
 | 
					                target = pos
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            if not target then
 | 
				
			||||||
 | 
					                target = pos
 | 
				
			||||||
 | 
					            elseif (p - pos).Magnitude < (p - target) then
 | 
				
			||||||
 | 
					                target = pos
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not target then
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        local moving = CFrame.new(p + (target - p).Unit * dt * v)
 | 
				
			||||||
 | 
					        --local record = world.entityIndex.sparse[mob]
 | 
				
			||||||
 | 
					        --local archetype = record.archetype
 | 
				
			||||||
 | 
					        --archetype.columns[archetype.records[Transform].column][record.row] = moving
 | 
				
			||||||
 | 
					        world:set(mob, Transform, moving)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return mobsMove
 | 
				
			||||||
							
								
								
									
										29
									
								
								demo/src/ServerScriptService/systems/move.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								demo/src/ServerScriptService/systems/move.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,29 @@
 | 
				
			||||||
 | 
					local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local std = require(ReplicatedStorage.std)
 | 
				
			||||||
 | 
					local world = std.world
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local cts = std.components
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local Model = cts.Model
 | 
				
			||||||
 | 
					local Transform = cts.Transform
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function move(dt: number)
 | 
				
			||||||
 | 
					    -- for i, archetype in world:query(Transform, Model):archetypes() do
 | 
				
			||||||
 | 
					    --     local columns = archetype.columns
 | 
				
			||||||
 | 
					    --     local records = archetype.records
 | 
				
			||||||
 | 
					    --     local M = columns[records[Model].column]
 | 
				
			||||||
 | 
					    --     local CF = columns[records[Transform].column]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    --     for row, entity in archetype.entities do
 | 
				
			||||||
 | 
					    --         local model, cf = M[row], CF[row]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    --         model.PrimaryPart.CFrame = cf
 | 
				
			||||||
 | 
					    --     end
 | 
				
			||||||
 | 
					    -- end
 | 
				
			||||||
 | 
					    for _, cf, model in world:query(Transform, Model) do
 | 
				
			||||||
 | 
					        model.PrimaryPart.CFrame = cf
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return move
 | 
				
			||||||
							
								
								
									
										37
									
								
								demo/src/ServerScriptService/systems/players.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								demo/src/ServerScriptService/systems/players.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,37 @@
 | 
				
			||||||
 | 
					local Players = game:GetService("Players")
 | 
				
			||||||
 | 
					local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local std = require(ReplicatedStorage.std)
 | 
				
			||||||
 | 
					local ref = std.ref
 | 
				
			||||||
 | 
					local collect = std.collect
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local cts = std.components
 | 
				
			||||||
 | 
					local Player = cts.Player
 | 
				
			||||||
 | 
					local Character = cts.Character
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local playersAdded = collect(Players.PlayerAdded)
 | 
				
			||||||
 | 
					local playersRemoved = collect(Players.PlayerRemoving)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local connections = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function players()
 | 
				
			||||||
 | 
					    for _, player in playersAdded do
 | 
				
			||||||
 | 
					        local e = ref(player.UserId):set(Player, player)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        connections[e.id()] = player.CharacterAdded:Connect(
 | 
				
			||||||
 | 
					            function(character)
 | 
				
			||||||
 | 
					                while character.Parent ~= workspace do
 | 
				
			||||||
 | 
					                    task.wait()
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					                e:set(Character, character)
 | 
				
			||||||
 | 
					            end)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for _, player in playersRemoved do
 | 
				
			||||||
 | 
					        local id = ref(player.UserId):clear().id()
 | 
				
			||||||
 | 
					        connections[id]:Disconnect()
 | 
				
			||||||
 | 
					        connections[id] = nil
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return players
 | 
				
			||||||
							
								
								
									
										37
									
								
								demo/src/ServerScriptService/systems/spawnMobs.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								demo/src/ServerScriptService/systems/spawnMobs.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,37 @@
 | 
				
			||||||
 | 
					local std = require(game:GetService("ReplicatedStorage").std)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local ref = std.ref
 | 
				
			||||||
 | 
					local interval = std.interval
 | 
				
			||||||
 | 
					local cts = std.components
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local Mob = cts.Mob
 | 
				
			||||||
 | 
					local Model = cts.Model
 | 
				
			||||||
 | 
					local Transform = cts.Transform
 | 
				
			||||||
 | 
					local Velocity = cts.Velocity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local throttle = interval(5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function spawnMobs(world: std.World)
 | 
				
			||||||
 | 
					    if throttle() then
 | 
				
			||||||
 | 
					        local p = Vector3.new(0, 5, 0)
 | 
				
			||||||
 | 
					        local cf = CFrame.new(p)
 | 
				
			||||||
 | 
					        local v = 5
 | 
				
			||||||
 | 
					        local part = Instance.new("Part")
 | 
				
			||||||
 | 
					        part.Anchored = true
 | 
				
			||||||
 | 
					        part.CanCollide = false
 | 
				
			||||||
 | 
					        part.BrickColor = BrickColor.Blue()
 | 
				
			||||||
 | 
					        part.Size = Vector3.one * 5
 | 
				
			||||||
 | 
					        local model = Instance.new("Model")
 | 
				
			||||||
 | 
					        part.Parent = model
 | 
				
			||||||
 | 
					        model.PrimaryPart = part
 | 
				
			||||||
 | 
					        model.Parent = workspace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ref()
 | 
				
			||||||
 | 
					            :set(Velocity, v)
 | 
				
			||||||
 | 
					            :set(Transform, cf)
 | 
				
			||||||
 | 
					            :set(Model, model)
 | 
				
			||||||
 | 
					            :add(Mob)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return spawnMobs
 | 
				
			||||||
| 
						 | 
					@ -1 +0,0 @@
 | 
				
			||||||
print("Hello world, from client!")
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1 +0,0 @@
 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					@ -732,6 +732,9 @@ do
 | 
				
			||||||
       	replace = noop :: (Query, ...any) -> (),
 | 
					       	replace = noop :: (Query, ...any) -> (),
 | 
				
			||||||
        with = Arm,
 | 
					        with = Arm,
 | 
				
			||||||
       	without = Arm,
 | 
					       	without = Arm,
 | 
				
			||||||
 | 
					        archetypes = function()
 | 
				
			||||||
 | 
					            return {}
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setmetatable(EmptyQuery, EmptyQuery)
 | 
					    setmetatable(EmptyQuery, EmptyQuery)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue