mirror of
				https://github.com/Ukendio/jecs.git
				synced 2025-11-03 18:39:19 +00:00 
			
		
		
		
	Networking example
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	
This commit is contained in:
		
							parent
							
								
									3ae84301f2
								
							
						
					
					
						commit
						6a8d991185
					
				
					 35 changed files with 769 additions and 947 deletions
				
			
		| 
						 | 
					@ -1,76 +1,55 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    "name": "demo",
 | 
						"name": "demo",
 | 
				
			||||||
    "tree": {
 | 
						"emitLegacyScripts": false,
 | 
				
			||||||
        "$className": "DataModel",
 | 
						"tree": {
 | 
				
			||||||
        "ReplicatedStorage": {
 | 
							"$className": "DataModel",
 | 
				
			||||||
            "$className": "ReplicatedStorage",
 | 
							"ReplicatedStorage": {
 | 
				
			||||||
            "$path": "src/ReplicatedStorage",
 | 
								"$className": "ReplicatedStorage",
 | 
				
			||||||
            "ecs": {
 | 
								"$path": "src/ReplicatedStorage",
 | 
				
			||||||
                "$path": "../jecs.luau"
 | 
								"ecs": {
 | 
				
			||||||
            },
 | 
									"$path": "../jecs.luau"
 | 
				
			||||||
            "net": {
 | 
								},
 | 
				
			||||||
                "$path": "net/client.luau"
 | 
								"Packages": {
 | 
				
			||||||
            },
 | 
									"$path": "Packages"
 | 
				
			||||||
            "Packages": {
 | 
								}
 | 
				
			||||||
                "$path": "Packages"
 | 
							},
 | 
				
			||||||
            }
 | 
							"ServerScriptService": {
 | 
				
			||||||
        },
 | 
								"$className": "ServerScriptService",
 | 
				
			||||||
        "ServerScriptService": {
 | 
								"$path": "src/ServerScriptService"
 | 
				
			||||||
            "$className": "ServerScriptService",
 | 
							},
 | 
				
			||||||
            "$path": "src/ServerScriptService",
 | 
							"Workspace": {
 | 
				
			||||||
            "net": {
 | 
								"$properties": {
 | 
				
			||||||
                "$path": "net/server.luau"
 | 
									"FilteringEnabled": true
 | 
				
			||||||
            }
 | 
								},
 | 
				
			||||||
        },
 | 
								"Baseplate": {
 | 
				
			||||||
        "Workspace": {
 | 
									"$className": "Part",
 | 
				
			||||||
            "$properties": {
 | 
									"$properties": {
 | 
				
			||||||
                "FilteringEnabled": true
 | 
										"Anchored": true,
 | 
				
			||||||
            },
 | 
										"Color": [0.38823, 0.37254, 0.38823],
 | 
				
			||||||
            "Baseplate": {
 | 
										"Locked": true,
 | 
				
			||||||
                "$className": "Part",
 | 
										"Position": [0, -10, 0],
 | 
				
			||||||
                "$properties": {
 | 
										"Size": [512, 20, 512]
 | 
				
			||||||
                    "Anchored": true,
 | 
									}
 | 
				
			||||||
                    "Color": [
 | 
								}
 | 
				
			||||||
                        0.38823,
 | 
							},
 | 
				
			||||||
                        0.37254,
 | 
							"Lighting": {
 | 
				
			||||||
                        0.38823
 | 
								"$properties": {
 | 
				
			||||||
                    ],
 | 
									"Ambient": [0, 0, 0],
 | 
				
			||||||
                    "Locked": true,
 | 
									"Brightness": 2,
 | 
				
			||||||
                    "Position": [
 | 
									"GlobalShadows": true,
 | 
				
			||||||
                        0,
 | 
									"Outlines": false,
 | 
				
			||||||
                        -10,
 | 
									"Technology": "Voxel"
 | 
				
			||||||
                        0
 | 
								}
 | 
				
			||||||
                    ],
 | 
							},
 | 
				
			||||||
                    "Size": [
 | 
							"SoundService": {
 | 
				
			||||||
                        512,
 | 
								"$properties": {
 | 
				
			||||||
                        20,
 | 
									"RespectFilteringEnabled": true
 | 
				
			||||||
                        512
 | 
								}
 | 
				
			||||||
                    ]
 | 
							},
 | 
				
			||||||
                }
 | 
							"StarterPlayer": {
 | 
				
			||||||
            }
 | 
								"StarterPlayerScripts": {
 | 
				
			||||||
        },
 | 
									"$path": "src/StarterPlayer/StarterPlayerScripts"
 | 
				
			||||||
        "Lighting": {
 | 
								}
 | 
				
			||||||
            "$properties": {
 | 
							}
 | 
				
			||||||
                "Ambient": [
 | 
						}
 | 
				
			||||||
                    0,
 | 
					}
 | 
				
			||||||
                    0,
 | 
					 | 
				
			||||||
                    0
 | 
					 | 
				
			||||||
                ],
 | 
					 | 
				
			||||||
                "Brightness": 2,
 | 
					 | 
				
			||||||
                "GlobalShadows": true,
 | 
					 | 
				
			||||||
                "Outlines": false,
 | 
					 | 
				
			||||||
                "Technology": "Voxel"
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        "SoundService": {
 | 
					 | 
				
			||||||
            "$properties": {
 | 
					 | 
				
			||||||
                "RespectFilteringEnabled": true
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        "StarterPlayer": {
 | 
					 | 
				
			||||||
            "StarterPlayerScripts": {
 | 
					 | 
				
			||||||
                "$path": "src/StarterPlayer/StarterPlayerScripts"
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										28
									
								
								demo/src/ReplicatedStorage/collect.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								demo/src/ReplicatedStorage/collect.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,28 @@
 | 
				
			||||||
 | 
					local function collect<T...>(
 | 
				
			||||||
 | 
						signal: {
 | 
				
			||||||
 | 
							Connect: (RBXScriptSignal<T...>, fn: (T...) -> ()) -> RBXScriptConnection
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					): () -> (T...)
 | 
				
			||||||
 | 
						local enqueued = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local i = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local connection = (signal :: any):Connect(function(...)
 | 
				
			||||||
 | 
							table.insert(enqueued, { ... })
 | 
				
			||||||
 | 
							i += 1
 | 
				
			||||||
 | 
						end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return function(): any
 | 
				
			||||||
 | 
							if i == 0 then
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							i -= 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							local args: any = table.remove(enqueued, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return unpack(args)
 | 
				
			||||||
 | 
						end, connection
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return collect
 | 
				
			||||||
							
								
								
									
										36
									
								
								demo/src/ReplicatedStorage/components.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								demo/src/ReplicatedStorage/components.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,36 @@
 | 
				
			||||||
 | 
					local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
				
			||||||
 | 
					local jecs = require(ReplicatedStorage.ecs)
 | 
				
			||||||
 | 
					local types = require("./types")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local Networked = jecs.tag()
 | 
				
			||||||
 | 
					local NetworkedPair = jecs.tag()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local Renderable = jecs.component() :: jecs.Id<Instance>
 | 
				
			||||||
 | 
					jecs.meta(Renderable, Networked)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local Poison = jecs.component() :: jecs.Id<number>
 | 
				
			||||||
 | 
					jecs.meta(Poison, Networked)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local Health = jecs.component() :: jecs.Id<number>
 | 
				
			||||||
 | 
					jecs.meta(Health, Networked)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local Player = jecs.component() :: jecs.Id<Player>
 | 
				
			||||||
 | 
					jecs.meta(Player, Networked)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local components = {
 | 
				
			||||||
 | 
						Renderable = Renderable,
 | 
				
			||||||
 | 
						Player = Player,
 | 
				
			||||||
 | 
						Poison = Poison,
 | 
				
			||||||
 | 
						Health = Health,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Networked = Networked,
 | 
				
			||||||
 | 
						NetworkedPair = NetworkedPair,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					for name, component in components do
 | 
				
			||||||
 | 
						jecs.meta(component, jecs.Name, name)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return components
 | 
				
			||||||
| 
						 | 
					@ -1,4 +0,0 @@
 | 
				
			||||||
_G.JECS_DEBUG = true
 | 
					 | 
				
			||||||
_G.JECS_HI_COMPONENT_ID = 32
 | 
					 | 
				
			||||||
require(game:GetService("ReplicatedStorage").ecs)
 | 
					 | 
				
			||||||
return
 | 
					 | 
				
			||||||
							
								
								
									
										13
									
								
								demo/src/ReplicatedStorage/main.client.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								demo/src/ReplicatedStorage/main.client.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,13 @@
 | 
				
			||||||
 | 
					local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
				
			||||||
 | 
					local jecs = require(ReplicatedStorage.ecs)
 | 
				
			||||||
 | 
					local schedule = require(ReplicatedStorage.schedule)
 | 
				
			||||||
 | 
					local observers_add = require(ReplicatedStorage.observers_add)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local SYSTEM = schedule.SYSTEM
 | 
				
			||||||
 | 
					local RUN = schedule.RUN
 | 
				
			||||||
 | 
					require(ReplicatedStorage.components)
 | 
				
			||||||
 | 
					local world = observers_add(jecs.world())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local systems = ReplicatedStorage.systems
 | 
				
			||||||
 | 
					SYSTEM(world, systems.receive_replication)
 | 
				
			||||||
 | 
					RUN(world)
 | 
				
			||||||
							
								
								
									
										190
									
								
								demo/src/ReplicatedStorage/observers_add.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								demo/src/ReplicatedStorage/observers_add.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,190 @@
 | 
				
			||||||
 | 
					local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
				
			||||||
 | 
					local jecs = require(ReplicatedStorage.ecs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Observer<T...> = {
 | 
				
			||||||
 | 
						callback: (jecs.Entity) -> (),
 | 
				
			||||||
 | 
						query: jecs.Query<T...>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type PatchedWorld = jecs.World & {
 | 
				
			||||||
 | 
						added: <T>(PatchedWorld, jecs.Id<T>, (e: jecs.Entity, id: jecs.Id<T>, value: T) -> ()) -> (),
 | 
				
			||||||
 | 
						removed: (PatchedWorld, jecs.Id, (e: jecs.Entity, id: jecs.Id) -> ()) -> (),
 | 
				
			||||||
 | 
						changed: <T>(PatchedWorld, jecs.Id<T>, (e: jecs.Entity, id: jecs.Id<T>, value: T) -> ()) -> (),
 | 
				
			||||||
 | 
						-- deleted: (PatchedWorld, () -> ()) -> () -> (),
 | 
				
			||||||
 | 
						observer: (PatchedWorld, Observer<any>) -> (),
 | 
				
			||||||
 | 
						monitor: (PatchedWorld, Observer<any>) -> (),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function observers_new(world, description)
 | 
				
			||||||
 | 
						local query = description.query
 | 
				
			||||||
 | 
						local callback = description.callback
 | 
				
			||||||
 | 
						local terms = query.filter_with :: { jecs.Id }
 | 
				
			||||||
 | 
						if not terms then
 | 
				
			||||||
 | 
							local ids = query.ids
 | 
				
			||||||
 | 
							query.filter_with = ids
 | 
				
			||||||
 | 
							terms = ids
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local entity_index = world.entity_index :: any
 | 
				
			||||||
 | 
						local function emplaced(entity: jecs.Entity)
 | 
				
			||||||
 | 
							local r = jecs.entity_index_try_get_fast(
 | 
				
			||||||
 | 
								entity_index, entity :: any)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if not r then
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							local archetype = r.archetype
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if jecs.query_match(query, archetype) then
 | 
				
			||||||
 | 
								callback(entity)
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, term in terms do
 | 
				
			||||||
 | 
							world:added(term, emplaced)
 | 
				
			||||||
 | 
							world:changed(term, emplaced)
 | 
				
			||||||
 | 
					 	end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function monitors_new(world, description)
 | 
				
			||||||
 | 
						local query = description.query
 | 
				
			||||||
 | 
						local callback = description.callback
 | 
				
			||||||
 | 
						local terms = query.filter_with :: { jecs.Id }
 | 
				
			||||||
 | 
						if not terms then
 | 
				
			||||||
 | 
							local ids = query.ids
 | 
				
			||||||
 | 
							query.filter_with = ids
 | 
				
			||||||
 | 
							terms = ids
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local entity_index = world.entity_index :: any
 | 
				
			||||||
 | 
						local function emplaced(entity: jecs.Entity)
 | 
				
			||||||
 | 
							local r = jecs.entity_index_try_get_fast(
 | 
				
			||||||
 | 
								entity_index, entity :: any)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if not r then
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							local archetype = r.archetype
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if jecs.query_match(query, archetype) then
 | 
				
			||||||
 | 
								callback(entity, jecs.OnAdd)
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local function removed(entity: jecs.Entity, component: jecs.Id)
 | 
				
			||||||
 | 
							local r = jecs.entity_index_try_get_fast(
 | 
				
			||||||
 | 
								entity_index, entity :: any)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if not r then
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							local archetype = r.archetype
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if jecs.query_match(query, archetype) then
 | 
				
			||||||
 | 
								callback(entity, jecs.OnRemove)
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, term in terms do
 | 
				
			||||||
 | 
							world:added(term, emplaced)
 | 
				
			||||||
 | 
							world:removed(term, removed)
 | 
				
			||||||
 | 
					 	end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function observers_add(world: jecs.World): PatchedWorld
 | 
				
			||||||
 | 
						local signals = {
 | 
				
			||||||
 | 
							added = {},
 | 
				
			||||||
 | 
							emplaced = {},
 | 
				
			||||||
 | 
							removed = {},
 | 
				
			||||||
 | 
							deleted = {}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						world = world :: jecs.World & {[string]: any}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						world.added = function(_, component, fn)
 | 
				
			||||||
 | 
							local listeners = signals.added[component]
 | 
				
			||||||
 | 
							if not listeners then
 | 
				
			||||||
 | 
								listeners = {}
 | 
				
			||||||
 | 
								signals.added[component] = listeners
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								local idr = jecs.id_record_ensure(world :: any, component :: any)
 | 
				
			||||||
 | 
								local rw = jecs.pair(component, jecs.Wildcard)
 | 
				
			||||||
 | 
								local idr_r = jecs.id_record_ensure(world :: any, rw :: any)
 | 
				
			||||||
 | 
								local function on_add(entity: number, id: number, value: any)
 | 
				
			||||||
 | 
									for _, listener in listeners do
 | 
				
			||||||
 | 
										listener(entity, id, value)
 | 
				
			||||||
 | 
									end
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
								world:set(component, jecs.OnAdd, on_add)
 | 
				
			||||||
 | 
								idr.hooks.on_add = on_add :: any
 | 
				
			||||||
 | 
								idr_r.hooks.on_add = on_add :: any
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
							table.insert(listeners, fn)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						world.changed = function(_, component, fn)
 | 
				
			||||||
 | 
							local listeners = signals.emplaced[component]
 | 
				
			||||||
 | 
							if not listeners then
 | 
				
			||||||
 | 
								listeners = {}
 | 
				
			||||||
 | 
								signals.emplaced[component] = listeners
 | 
				
			||||||
 | 
								local idr = jecs.id_record_ensure(world :: any, component :: any)
 | 
				
			||||||
 | 
								local rw = jecs.pair(component, jecs.Wildcard)
 | 
				
			||||||
 | 
								local idr_r = jecs.id_record_ensure(world :: any, rw :: any)
 | 
				
			||||||
 | 
								local function on_change(entity: number, id: number, value: any)
 | 
				
			||||||
 | 
									for _, listener in listeners do
 | 
				
			||||||
 | 
										listener(entity, id, value)
 | 
				
			||||||
 | 
									end
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
								world:set(component, jecs.OnChange, on_change)
 | 
				
			||||||
 | 
								idr.hooks.on_change = on_change :: any
 | 
				
			||||||
 | 
								idr_r.hooks.on_change = on_change :: any
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
							table.insert(listeners, fn)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						world.removed = function(_, component, fn)
 | 
				
			||||||
 | 
							local listeners = signals.removed[component]
 | 
				
			||||||
 | 
							if not listeners then
 | 
				
			||||||
 | 
								listeners = {}
 | 
				
			||||||
 | 
								signals.removed[component] = listeners
 | 
				
			||||||
 | 
								local idr = jecs.id_record_ensure(world :: any, component :: any)
 | 
				
			||||||
 | 
								local rw = jecs.pair(component, jecs.Wildcard)
 | 
				
			||||||
 | 
								local idr_r = jecs.id_record_ensure(world :: any, rw :: any)
 | 
				
			||||||
 | 
								local function on_remove(entity: number, id: number, value: any)
 | 
				
			||||||
 | 
									for _, listener in listeners do
 | 
				
			||||||
 | 
										listener(entity, id, value)
 | 
				
			||||||
 | 
									end
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
								world:set(component, jecs.OnRemove, on_remove)
 | 
				
			||||||
 | 
								idr.hooks.on_remove = on_remove :: any
 | 
				
			||||||
 | 
								idr_r.hooks.on_remove = on_remove :: any
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
							table.insert(listeners, fn)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						world.signals = signals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						world.observer = observers_new
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						world.monitor = monitors_new
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						-- local world_delete = world.delete
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						-- world.deleted = function(_, fn)
 | 
				
			||||||
 | 
						-- 	local listeners = signals.deleted
 | 
				
			||||||
 | 
						-- 	table.insert(listeners, fn)
 | 
				
			||||||
 | 
						-- end
 | 
				
			||||||
 | 
						-- world.delete = function(world, entity)
 | 
				
			||||||
 | 
						-- 	world_delete(world, entity)
 | 
				
			||||||
 | 
						-- 	for _, fn in signals.deleted do
 | 
				
			||||||
 | 
						-- 		fn(entity)
 | 
				
			||||||
 | 
						-- 	end
 | 
				
			||||||
 | 
						-- end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return world :: PatchedWorld
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return observers_add
 | 
				
			||||||
							
								
								
									
										50
									
								
								demo/src/ReplicatedStorage/remotes.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								demo/src/ReplicatedStorage/remotes.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,50 @@
 | 
				
			||||||
 | 
					local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
				
			||||||
 | 
					local types = require("../ReplicatedStorage/types")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Signal<T...> = {
 | 
				
			||||||
 | 
						Connect: (Signal<T...>, fn: (T...) -> ()) -> RBXScriptConnection
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					type Remote<T...> = {
 | 
				
			||||||
 | 
						FireClient: (Remote<T...>, T...) -> (),
 | 
				
			||||||
 | 
						FireAllClients: (Remote<T...>, T...) -> (),
 | 
				
			||||||
 | 
						FireServer: (Remote<T...>) -> (),
 | 
				
			||||||
 | 
						OnServerEvent: {
 | 
				
			||||||
 | 
							Connect: (any, fn: (Player, T...) -> () ) -> ()
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						OnClientEvent: {
 | 
				
			||||||
 | 
							Connect: (any, fn: (T...) -> () ) -> ()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function stream_ensure(name): Remote<any>
 | 
				
			||||||
 | 
						local remote = ReplicatedStorage:FindFirstChild(name)
 | 
				
			||||||
 | 
						if not remote then
 | 
				
			||||||
 | 
							remote = Instance.new("RemoteEvent")
 | 
				
			||||||
 | 
							remote.Name = name
 | 
				
			||||||
 | 
							remote.Parent = ReplicatedStorage
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
						return remote :: any
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function datagram_ensure(name): Remote<any>
 | 
				
			||||||
 | 
						local remote = ReplicatedStorage:FindFirstChild(name)
 | 
				
			||||||
 | 
						if not remote then
 | 
				
			||||||
 | 
							remote = Instance.new("UnreliableRemoteEvent")
 | 
				
			||||||
 | 
							remote.Name = name
 | 
				
			||||||
 | 
							remote.Parent = ReplicatedStorage
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
						return remote :: any
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return {
 | 
				
			||||||
 | 
						input = datagram_ensure("input") :: Remote<string>,
 | 
				
			||||||
 | 
						replication = stream_ensure("replication") :: Remote<{
 | 
				
			||||||
 | 
							[string]: {
 | 
				
			||||||
 | 
								set: { types.Entity }?,
 | 
				
			||||||
 | 
								values: { any }?,
 | 
				
			||||||
 | 
								removed: { types.Entity }?
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}>,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										136
									
								
								demo/src/ReplicatedStorage/schedule.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								demo/src/ReplicatedStorage/schedule.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,136 @@
 | 
				
			||||||
 | 
					local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
				
			||||||
 | 
					local jabby = require(ReplicatedStorage.Packages.jabby)
 | 
				
			||||||
 | 
					local jecs = require(ReplicatedStorage.ecs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jabby.set_check_function(function() return true end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local scheduler = jabby.scheduler.create("jabby scheduler")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jabby.register({
 | 
				
			||||||
 | 
						applet = jabby.applets.scheduler,
 | 
				
			||||||
 | 
						name = "Scheduler",
 | 
				
			||||||
 | 
						configuration = {
 | 
				
			||||||
 | 
							scheduler = scheduler,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local ContextActionService = game:GetService("ContextActionService")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function create_widget(_, state: Enum.UserInputState)
 | 
				
			||||||
 | 
						local client = jabby.obtain_client()
 | 
				
			||||||
 | 
					    if state ~= Enum.UserInputState.Begin then return end
 | 
				
			||||||
 | 
					    client.spawn_app(client.apps.home, nil)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local RunService = game:GetService("RunService")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local System = jecs.component() :: jecs.Id<{
 | 
				
			||||||
 | 
						fn: () -> (),
 | 
				
			||||||
 | 
						name: string,
 | 
				
			||||||
 | 
					}>
 | 
				
			||||||
 | 
					local DependsOn = jecs.component()
 | 
				
			||||||
 | 
					local Phase = jecs.tag()
 | 
				
			||||||
 | 
					local Event = jecs.component() :: jecs.Id<RBXScriptSignal>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local pair = jecs.pair
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local types = require(ReplicatedStorage.types)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function ECS_PHASE(world, after: types.Entity)
 | 
				
			||||||
 | 
						local phase = world:entity()
 | 
				
			||||||
 | 
						world:add(phase, Phase)
 | 
				
			||||||
 | 
						if after then
 | 
				
			||||||
 | 
							local dependency = pair(DependsOn, after)
 | 
				
			||||||
 | 
							world:add(phase, dependency)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return phase
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local Heartbeat = jecs.tag()
 | 
				
			||||||
 | 
					jecs.meta(Heartbeat, Phase)
 | 
				
			||||||
 | 
					jecs.meta(Heartbeat, Event, RunService.Heartbeat)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local PreSimulation = jecs.tag()
 | 
				
			||||||
 | 
					jecs.meta(PreSimulation, Phase)
 | 
				
			||||||
 | 
					jecs.meta(PreSimulation, Event, RunService.PreSimulation)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local PreAnimation = jecs.tag()
 | 
				
			||||||
 | 
					jecs.meta(PreAnimation, Phase)
 | 
				
			||||||
 | 
					jecs.meta(PreAnimation, Event, RunService.PreAnimation)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local PreRender = jecs.tag()
 | 
				
			||||||
 | 
					jecs.meta(PreRender, Phase)
 | 
				
			||||||
 | 
					jecs.meta(PreRender, Event, RunService.PreRender)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function ECS_SYSTEM(world: types.World, mod: ModuleScript, phase: types.Entity?)
 | 
				
			||||||
 | 
						local system = world:entity()
 | 
				
			||||||
 | 
						local p = phase or Heartbeat
 | 
				
			||||||
 | 
						local fn = require(mod) :: (...any) -> ()
 | 
				
			||||||
 | 
						world:set(system, System, {
 | 
				
			||||||
 | 
							fn = fn(world, 0) or fn,
 | 
				
			||||||
 | 
							name = mod.Name,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						local depends_on = DependsOn :: jecs.Entity
 | 
				
			||||||
 | 
						world:add(system, pair(depends_on, p))
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					local function find_systems_w_phase(world: types.World, systems, phase: types.Entity)
 | 
				
			||||||
 | 
						local phase_name = world:get(phase, jecs.Name) :: string
 | 
				
			||||||
 | 
						for _, s in world:query(System):with(pair(DependsOn, phase)) do
 | 
				
			||||||
 | 
							table.insert(systems, {
 | 
				
			||||||
 | 
								id = scheduler:register_system({
 | 
				
			||||||
 | 
									phase = phase_name,
 | 
				
			||||||
 | 
									name = s.name,
 | 
				
			||||||
 | 
								}),
 | 
				
			||||||
 | 
								fn = s.fn
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
						for after in world:query(Phase, pair(DependsOn, phase)) do
 | 
				
			||||||
 | 
							find_systems_w_phase(world, systems, after)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
						return systems
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function ECS_RUN(world: types.World)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						jabby.register({
 | 
				
			||||||
 | 
							applet = jabby.applets.world,
 | 
				
			||||||
 | 
							name = "MyWorld",
 | 
				
			||||||
 | 
							configuration = {
 | 
				
			||||||
 | 
								world = world,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if RunService:IsClient() then
 | 
				
			||||||
 | 
							ContextActionService:BindAction("Open Jabby Home", create_widget, false, Enum.KeyCode.F4)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for phase, event in world:query(Event, Phase) do
 | 
				
			||||||
 | 
							local systems = find_systems_w_phase(world, {}, phase)
 | 
				
			||||||
 | 
							event:Connect(function(...)
 | 
				
			||||||
 | 
								for _, system in systems do
 | 
				
			||||||
 | 
									scheduler:run(system.id, system.fn, world, ...)
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
							end)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return {
 | 
				
			||||||
 | 
						PHASE = ECS_PHASE,
 | 
				
			||||||
 | 
						SYSTEM = ECS_SYSTEM,
 | 
				
			||||||
 | 
						RUN = ECS_RUN,
 | 
				
			||||||
 | 
						phases = {
 | 
				
			||||||
 | 
							Heartbeat = Heartbeat,
 | 
				
			||||||
 | 
							PreSimulation = PreSimulation,
 | 
				
			||||||
 | 
							PreAnimation = PreAnimation,
 | 
				
			||||||
 | 
							PreRender = PreRender
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						components = {
 | 
				
			||||||
 | 
							System = System,
 | 
				
			||||||
 | 
							DependsOn = DependsOn,
 | 
				
			||||||
 | 
							Phase = Phase,
 | 
				
			||||||
 | 
							Event = Event,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,34 +0,0 @@
 | 
				
			||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
					 | 
				
			||||||
local RunService = game:GetService("RunService")
 | 
					 | 
				
			||||||
local UserInputService = game:GetService("UserInputService")
 | 
					 | 
				
			||||||
local jabby = require(ReplicatedStorage.Packages.jabby)
 | 
					 | 
				
			||||||
local std = ReplicatedStorage.std
 | 
					 | 
				
			||||||
local scheduler = require(std.scheduler)
 | 
					 | 
				
			||||||
local world = require(std.world)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function start(modules)
 | 
					 | 
				
			||||||
	for _, module in modules do
 | 
					 | 
				
			||||||
		require(module)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
	local events = scheduler.COLLECT()
 | 
					 | 
				
			||||||
	scheduler.BEGIN(events)
 | 
					 | 
				
			||||||
	jabby.set_check_function(function(player)
 | 
					 | 
				
			||||||
		return true
 | 
					 | 
				
			||||||
	end)
 | 
					 | 
				
			||||||
	if RunService:IsClient() then
 | 
					 | 
				
			||||||
		local player = game:GetService("Players").LocalPlayer
 | 
					 | 
				
			||||||
		local playergui = player:WaitForChild("PlayerGui")
 | 
					 | 
				
			||||||
		local client = jabby.obtain_client()
 | 
					 | 
				
			||||||
		UserInputService.InputBegan:Connect(function(input)
 | 
					 | 
				
			||||||
			if input.KeyCode == Enum.KeyCode.F4 then
 | 
					 | 
				
			||||||
				local home = playergui:FindFirstChild("Home")
 | 
					 | 
				
			||||||
				if home then
 | 
					 | 
				
			||||||
					home:Destroy()
 | 
					 | 
				
			||||||
				end
 | 
					 | 
				
			||||||
				client.spawn_app(client.apps.home)
 | 
					 | 
				
			||||||
			end
 | 
					 | 
				
			||||||
		end)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
return start
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,40 +0,0 @@
 | 
				
			||||||
--!optimize 2
 | 
					 | 
				
			||||||
--!native
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-- original author @centau
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local FAILURE = -1 
 | 
					 | 
				
			||||||
local RUNNING = 0 
 | 
					 | 
				
			||||||
local SUCCESS = 1 
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function SEQUENCE(nodes)
 | 
					 | 
				
			||||||
	return function(...)
 | 
					 | 
				
			||||||
		for _, node in nodes do
 | 
					 | 
				
			||||||
			local status = node(...)
 | 
					 | 
				
			||||||
			if 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 > FAILURE then
 | 
					 | 
				
			||||||
				return status
 | 
					 | 
				
			||||||
			end
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
		return FAILURE
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local bt = {
 | 
					 | 
				
			||||||
	SEQUENCE = SEQUENCE,
 | 
					 | 
				
			||||||
	FALLBACK = FALLBACK,
 | 
					 | 
				
			||||||
	RUNNING = RUNNING,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
return bt
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,67 +0,0 @@
 | 
				
			||||||
--!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
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,30 +0,0 @@
 | 
				
			||||||
local jecs = require(game:GetService("ReplicatedStorage").ecs)
 | 
					 | 
				
			||||||
local world = require(script.Parent.world)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Entity<T = nil> = jecs.Entity<T>
 | 
					 | 
				
			||||||
local components: {
 | 
					 | 
				
			||||||
	Character: Entity<Model>,
 | 
					 | 
				
			||||||
	Mob: Entity,
 | 
					 | 
				
			||||||
	Model: Entity<Model>,
 | 
					 | 
				
			||||||
	Player: Entity,
 | 
					 | 
				
			||||||
	Target: Entity,
 | 
					 | 
				
			||||||
	Transform: Entity<{ new: CFrame, old: CFrame }>,
 | 
					 | 
				
			||||||
	Velocity: Entity<number>,
 | 
					 | 
				
			||||||
	Previous: Entity,
 | 
					 | 
				
			||||||
} =
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		Character = world:component(),
 | 
					 | 
				
			||||||
		Mob = world:component(),
 | 
					 | 
				
			||||||
		Model = world:component(),
 | 
					 | 
				
			||||||
		Player = world:component(),
 | 
					 | 
				
			||||||
		Target = world:component(),
 | 
					 | 
				
			||||||
		Transform = world:component(),
 | 
					 | 
				
			||||||
		Velocity = world:component(),
 | 
					 | 
				
			||||||
		Previous = world:component(),
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
for name, component in components :: {[string]: jecs.Entity} do 
 | 
					 | 
				
			||||||
	world:set(component, jecs.Name, name)
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
return table.freeze(components)
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,19 +0,0 @@
 | 
				
			||||||
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
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,14 +0,0 @@
 | 
				
			||||||
local std = game:GetService("ReplicatedStorage").std
 | 
					 | 
				
			||||||
local Players = game:GetService("Players")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local scheduler = require(std.scheduler)
 | 
					 | 
				
			||||||
local PHASE = scheduler.PHASE
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
return {
 | 
					 | 
				
			||||||
    PlayerAdded = PHASE({
 | 
					 | 
				
			||||||
        event = Players.PlayerAdded
 | 
					 | 
				
			||||||
    }),
 | 
					 | 
				
			||||||
    PlayerRemoved = PHASE({
 | 
					 | 
				
			||||||
        event = Players.PlayerRemoving
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,26 +0,0 @@
 | 
				
			||||||
local world = require(script.Parent.world)
 | 
					 | 
				
			||||||
local jecs = require(game:GetService("ReplicatedStorage").ecs)
 | 
					 | 
				
			||||||
local refs: {[any]: jecs.Entity} = {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function fini(key): () -> ()
 | 
					 | 
				
			||||||
	return function()
 | 
					 | 
				
			||||||
		refs[key] = nil
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function noop() end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function ref(key): (jecs.Entity, () -> ())
 | 
					 | 
				
			||||||
	if not key then
 | 
					 | 
				
			||||||
		return world:entity(), noop
 | 
					 | 
				
			||||||
	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 e, fini(key)
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
return ref
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,171 +0,0 @@
 | 
				
			||||||
--!native
 | 
					 | 
				
			||||||
--!optimize 2
 | 
					 | 
				
			||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
					 | 
				
			||||||
local RunService = game:GetService("RunService")
 | 
					 | 
				
			||||||
local jabby = require(ReplicatedStorage.Packages.jabby)
 | 
					 | 
				
			||||||
local jecs = require(ReplicatedStorage.ecs)
 | 
					 | 
				
			||||||
local pair = jecs.pair
 | 
					 | 
				
			||||||
local Name = jecs.Name
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type World = jecs.World
 | 
					 | 
				
			||||||
type Entity<T = nil> = jecs.Entity<T>
 | 
					 | 
				
			||||||
type Id<T = unknown> = jecs.Id<T>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type System = {
 | 
					 | 
				
			||||||
	callback: (world: World) -> (),
 | 
					 | 
				
			||||||
	id: number,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Systems = { System }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Events = {
 | 
					 | 
				
			||||||
	RenderStepped: Systems,
 | 
					 | 
				
			||||||
	Heartbeat: Systems,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local world = require(script.Parent.world)
 | 
					 | 
				
			||||||
local Disabled = world:entity()
 | 
					 | 
				
			||||||
local System = world:component() :: Id<{ callback: (any) -> (), name: string}>
 | 
					 | 
				
			||||||
local DependsOn = world:entity()
 | 
					 | 
				
			||||||
local Event = world:component() :: Id<RBXScriptSignal>
 | 
					 | 
				
			||||||
local Phase = world:entity()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local PreRender = world:entity()
 | 
					 | 
				
			||||||
local Heartbeat = world:entity()
 | 
					 | 
				
			||||||
local PreAnimation = world:entity()
 | 
					 | 
				
			||||||
local PreSimulation = world:entity()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local sys: System
 | 
					 | 
				
			||||||
local dt: number
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local jabby_scheduler = jabby.scheduler.create("Scheduler")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local a, b, c, d
 | 
					 | 
				
			||||||
local function run()
 | 
					 | 
				
			||||||
	local id = sys.id
 | 
					 | 
				
			||||||
	jabby_scheduler:run(id, sys.callback, a, b, c, d)
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
world:add(Heartbeat, Phase)
 | 
					 | 
				
			||||||
world:set(Heartbeat, Event, RunService.Heartbeat)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
world:add(PreSimulation, Phase)
 | 
					 | 
				
			||||||
world:set(PreSimulation, Event, RunService.PreSimulation)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
world:add(PreAnimation, Phase)
 | 
					 | 
				
			||||||
world:set(PreAnimation, Event, RunService.PreAnimation)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
jabby.register({
 | 
					 | 
				
			||||||
	applet = jabby.applets.world,
 | 
					 | 
				
			||||||
	name = "MyWorld",
 | 
					 | 
				
			||||||
	configuration = {
 | 
					 | 
				
			||||||
	world = world,
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
jabby.register({
 | 
					 | 
				
			||||||
	applet = jabby.applets.scheduler,
 | 
					 | 
				
			||||||
	name = "Scheduler",
 | 
					 | 
				
			||||||
	configuration = {
 | 
					 | 
				
			||||||
		scheduler = jabby_scheduler,
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
if RunService:IsClient() then 
 | 
					 | 
				
			||||||
	world:add(PreRender, Phase)
 | 
					 | 
				
			||||||
	world:set(PreRender, Event, (RunService :: RunService).PreRender)
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function begin(events: { [RBXScriptSignal]: Systems })
 | 
					 | 
				
			||||||
	local connections = {}
 | 
					 | 
				
			||||||
	for event, systems in events do
 | 
					 | 
				
			||||||
		if not event then
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
		local event_name = tostring(event)
 | 
					 | 
				
			||||||
		connections[event] = event:Connect(function(...)
 | 
					 | 
				
			||||||
			debug.profilebegin(event_name)
 | 
					 | 
				
			||||||
			for _, s in systems do
 | 
					 | 
				
			||||||
				sys = s
 | 
					 | 
				
			||||||
				a, b, c, d = ...
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				for _ in run do
 | 
					 | 
				
			||||||
					break
 | 
					 | 
				
			||||||
				end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			end
 | 
					 | 
				
			||||||
			debug.profileend()
 | 
					 | 
				
			||||||
		end)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
	return connections
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function scheduler_collect_systems_under_phase_recursive(systems, phase: Entity)
 | 
					 | 
				
			||||||
	local phase_name = world:get(phase, Name)
 | 
					 | 
				
			||||||
	for _, s in world:query(System):with(pair(DependsOn, phase)) do
 | 
					 | 
				
			||||||
		table.insert(systems, {
 | 
					 | 
				
			||||||
			id = jabby_scheduler:register_system({
 | 
					 | 
				
			||||||
				name = s.name,
 | 
					 | 
				
			||||||
				phase = phase_name,
 | 
					 | 
				
			||||||
			} :: any),
 | 
					 | 
				
			||||||
			callback = s.callback,
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
	for after in world:query(Phase):with(pair(DependsOn, phase)):iter() do
 | 
					 | 
				
			||||||
		scheduler_collect_systems_under_phase_recursive(systems, after)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function scheduler_collect_systems_under_event(event)
 | 
					 | 
				
			||||||
	local systems = {}
 | 
					 | 
				
			||||||
	scheduler_collect_systems_under_phase_recursive(systems, event)
 | 
					 | 
				
			||||||
	return systems
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function scheduler_collect_systems_all()
 | 
					 | 
				
			||||||
	local events = {}
 | 
					 | 
				
			||||||
	for phase, event in world:query(Event):with(Phase) do
 | 
					 | 
				
			||||||
		events[event] = scheduler_collect_systems_under_event(phase)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
	return events
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function scheduler_phase_new(d: { after: Entity?, event: RBXScriptSignal? })
 | 
					 | 
				
			||||||
	local phase = world:entity()
 | 
					 | 
				
			||||||
	world:add(phase, Phase)
 | 
					 | 
				
			||||||
	local after = d.after
 | 
					 | 
				
			||||||
	if after then 
 | 
					 | 
				
			||||||
		local dependency = pair(DependsOn, after :: Entity)
 | 
					 | 
				
			||||||
		world:add(phase, dependency)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	local event = d.event
 | 
					 | 
				
			||||||
	if event then 
 | 
					 | 
				
			||||||
		world:set(phase, Event, event)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
	return phase
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function scheduler_systems_new(callback: (any) -> (), phase: Entity?) 
 | 
					 | 
				
			||||||
	local system = world:entity()
 | 
					 | 
				
			||||||
	world:set(system, System, { callback = callback, name = debug.info(callback, "n") })
 | 
					 | 
				
			||||||
	local depends_on = DependsOn :: jecs.Entity
 | 
					 | 
				
			||||||
	local p: Entity = phase or Heartbeat
 | 
					 | 
				
			||||||
	world:add(system, pair(depends_on, p))
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
	return system
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
return {
 | 
					 | 
				
			||||||
	SYSTEM = scheduler_systems_new,
 | 
					 | 
				
			||||||
	BEGIN = begin,
 | 
					 | 
				
			||||||
	PHASE = scheduler_phase_new, 
 | 
					 | 
				
			||||||
	COLLECT = scheduler_collect_systems_all,
 | 
					 | 
				
			||||||
	phases = {
 | 
					 | 
				
			||||||
		Heartbeat = Heartbeat,
 | 
					 | 
				
			||||||
		PreSimulation = PreSimulation,
 | 
					 | 
				
			||||||
		PreAnimation = PreAnimation,
 | 
					 | 
				
			||||||
		PreRender = PreRender
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,4 +0,0 @@
 | 
				
			||||||
local jecs = require(game:GetService("ReplicatedStorage").ecs)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-- I like the idea of only having the world be a singleton.
 | 
					 | 
				
			||||||
return jecs.World.new() :: jecs.World
 | 
					 | 
				
			||||||
							
								
								
									
										67
									
								
								demo/src/ReplicatedStorage/systems/receive_replication.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								demo/src/ReplicatedStorage/systems/receive_replication.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,67 @@
 | 
				
			||||||
 | 
					local types = require("../types")
 | 
				
			||||||
 | 
					local jecs = require(game:GetService("ReplicatedStorage").ecs)
 | 
				
			||||||
 | 
					local remotes = require("../remotes")
 | 
				
			||||||
 | 
					local collect = require("../collect")
 | 
				
			||||||
 | 
					local client_ids = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function ecs_map_get(world: types.World, id: types.Entity)
 | 
				
			||||||
 | 
						local deserialised_id = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if not world:exists(id) or not world:contains(id) then
 | 
				
			||||||
 | 
							deserialised_id = world:entity(id)
 | 
				
			||||||
 | 
							client_ids[id] = deserialised_id
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							deserialised_id = client_ids[id]
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return deserialised_id
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local function ecs_make_alive_id(world: types.World, id: jecs.Id)
 | 
				
			||||||
 | 
						local rel = jecs.ECS_PAIR_FIRST(id)
 | 
				
			||||||
 | 
						local tgt = jecs.ECS_PAIR_SECOND(id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ecs_map_get(world, rel)
 | 
				
			||||||
 | 
						ecs_map_get(world, tgt)
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local snapshots = collect(remotes.replication.OnClientEvent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return function(world: types.World)
 | 
				
			||||||
 | 
					    return function()
 | 
				
			||||||
 | 
					        for snapshot in snapshots do
 | 
				
			||||||
 | 
					            for key, map in snapshot do
 | 
				
			||||||
 | 
					            	local id = (tonumber(key) :: any) :: jecs.Id
 | 
				
			||||||
 | 
					                if jecs.IS_PAIR(id) then
 | 
				
			||||||
 | 
					                	ecs_make_alive_id(world, id)
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                local set = map.set
 | 
				
			||||||
 | 
					                if set then
 | 
				
			||||||
 | 
						                if jecs.is_tag(world, id) then
 | 
				
			||||||
 | 
					                       	for _, entity in set do
 | 
				
			||||||
 | 
					                        	entity = ecs_map_get(world, entity)
 | 
				
			||||||
 | 
					                      		world:add(entity, id)
 | 
				
			||||||
 | 
					                       	end
 | 
				
			||||||
 | 
					                    else
 | 
				
			||||||
 | 
							                local values = map.values :: { any }
 | 
				
			||||||
 | 
							                for i, entity in set do
 | 
				
			||||||
 | 
												entity = ecs_map_get(world, entity)
 | 
				
			||||||
 | 
					                      		world:set(entity, id, values[i])
 | 
				
			||||||
 | 
					                       	end
 | 
				
			||||||
 | 
									    end
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                local removed = map.removed
 | 
				
			||||||
 | 
					                if removed then
 | 
				
			||||||
 | 
					                    for i, e in removed do
 | 
				
			||||||
 | 
					                        if not world:contains(e) then
 | 
				
			||||||
 | 
					                            continue
 | 
				
			||||||
 | 
					                        end
 | 
				
			||||||
 | 
					                        world:remove(e, id)
 | 
				
			||||||
 | 
					                    end
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -1,48 +0,0 @@
 | 
				
			||||||
local events = {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function trackers_invoke(event, component, entity, ...)
 | 
					 | 
				
			||||||
	local trackers = events[event][component]
 | 
					 | 
				
			||||||
	if not trackers then
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, tracker in trackers do
 | 
					 | 
				
			||||||
		tracker(entity, data)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function trackers_init(event, component, fn)
 | 
					 | 
				
			||||||
	local ob = events[event]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return {
 | 
					 | 
				
			||||||
		connect = function(component, fn)
 | 
					 | 
				
			||||||
			local trackers = ob[component]
 | 
					 | 
				
			||||||
			if not trackers then
 | 
					 | 
				
			||||||
				trackers = {}
 | 
					 | 
				
			||||||
				ob[component] = trackers
 | 
					 | 
				
			||||||
			end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			table.insert(trackers, fn)
 | 
					 | 
				
			||||||
		end,
 | 
					 | 
				
			||||||
		invoke = function(component, ...)
 | 
					 | 
				
			||||||
			trackers_invoke(event, component, ...)
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return function(component, fn)
 | 
					 | 
				
			||||||
		local trackers = ob[component]
 | 
					 | 
				
			||||||
		if not trackers then
 | 
					 | 
				
			||||||
			trackers = {}
 | 
					 | 
				
			||||||
			ob[component] = trackers
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		table.insert(trackers, fn)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local trackers = {
 | 
					 | 
				
			||||||
	emplace = trackers_init("emplace"),
 | 
					 | 
				
			||||||
	add = trackers_init("added"),
 | 
					 | 
				
			||||||
	remove = trackers_init("removed")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
return trackers
 | 
					 | 
				
			||||||
							
								
								
									
										8
									
								
								demo/src/ReplicatedStorage/types.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								demo/src/ReplicatedStorage/types.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					local jecs = require(game:GetService("ReplicatedStorage").ecs)
 | 
				
			||||||
 | 
					local observers_add = require("../ReplicatedStorage/observers_add")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type World = typeof(observers_add(jecs.world()))
 | 
				
			||||||
 | 
					export type Entity = jecs.Entity
 | 
				
			||||||
 | 
					export type Id<T> = jecs.Id<T>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return {}
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,19 @@
 | 
				
			||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
					local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
				
			||||||
local start = require(ReplicatedStorage.start)
 | 
					local ServerScriptService = game:GetService("ServerScriptService")
 | 
				
			||||||
 | 
					local jecs = require(ReplicatedStorage.ecs)
 | 
				
			||||||
 | 
					local schedule = require(ReplicatedStorage.schedule)
 | 
				
			||||||
 | 
					local observers_add = require(ReplicatedStorage.observers_add)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
start(script.Parent:WaitForChild("systems"):GetChildren())
 | 
					local SYSTEM = schedule.SYSTEM
 | 
				
			||||||
 | 
					local RUN = schedule.RUN
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require(ReplicatedStorage.components)
 | 
				
			||||||
 | 
					local world = observers_add(jecs.world())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local systems = ServerScriptService.systems
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SYSTEM(world, systems.replication)
 | 
				
			||||||
 | 
					SYSTEM(world, systems.players_added)
 | 
				
			||||||
 | 
					SYSTEM(world, systems.poison_hurts)
 | 
				
			||||||
 | 
					SYSTEM(world, systems.life_is_painful)
 | 
				
			||||||
 | 
					RUN(world, 0)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										12
									
								
								demo/src/ServerScriptService/systems/life_is_painful.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								demo/src/ServerScriptService/systems/life_is_painful.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,12 @@
 | 
				
			||||||
 | 
					local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
				
			||||||
 | 
					local ct = require(ReplicatedStorage.components)
 | 
				
			||||||
 | 
					local types = require(ReplicatedStorage.types)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return function(world: types.World, dt: number)
 | 
				
			||||||
 | 
						for e in world:query(ct.Player):without(ct.Health) do
 | 
				
			||||||
 | 
							world:set(e, ct.Health, 100)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
						for e in world:query(ct.Player, ct.Health):without(ct.Poison) do
 | 
				
			||||||
 | 
							world:set(e, ct.Poison, 10)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -1,88 +0,0 @@
 | 
				
			||||||
--!optimize 2
 | 
					 | 
				
			||||||
--!native
 | 
					 | 
				
			||||||
--!strict
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
					 | 
				
			||||||
local blink = require(game:GetService("ServerScriptService").net)
 | 
					 | 
				
			||||||
local jecs = require(ReplicatedStorage.ecs)
 | 
					 | 
				
			||||||
local __ = jecs.Wildcard
 | 
					 | 
				
			||||||
local std = ReplicatedStorage.std
 | 
					 | 
				
			||||||
local ref = require(std.ref)
 | 
					 | 
				
			||||||
local interval = require(std.interval)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local world = require(std.world)
 | 
					 | 
				
			||||||
local cts = require(std.components)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local Mob = cts.Mob
 | 
					 | 
				
			||||||
local Transform = cts.Transform
 | 
					 | 
				
			||||||
local Velocity = cts.Velocity
 | 
					 | 
				
			||||||
local Player = cts.Player
 | 
					 | 
				
			||||||
local Character = cts.Character
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local characters = world
 | 
					 | 
				
			||||||
	:query(Character)
 | 
					 | 
				
			||||||
	:with(Player)
 | 
					 | 
				
			||||||
	:cached()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local moving_mobs = world
 | 
					 | 
				
			||||||
	:query(Transform, Velocity)
 | 
					 | 
				
			||||||
	:with(Mob)
 | 
					 | 
				
			||||||
	:cached()
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function mobsMove(dt: number)
 | 
					 | 
				
			||||||
	local targets = {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, character in characters do
 | 
					 | 
				
			||||||
		table.insert(targets, (character.PrimaryPart :: Part).Position)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for mob, transform, v in moving_mobs do
 | 
					 | 
				
			||||||
		local cf = transform.new
 | 
					 | 
				
			||||||
		local p = cf.Position
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local target
 | 
					 | 
				
			||||||
		local closest
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		for _, pos in targets do
 | 
					 | 
				
			||||||
			local distance = (p - pos).Magnitude
 | 
					 | 
				
			||||||
			if not target or distance < closest then
 | 
					 | 
				
			||||||
				target = pos
 | 
					 | 
				
			||||||
				closest = distance
 | 
					 | 
				
			||||||
			end
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if not target then
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local moving = CFrame.new(p + (target - p).Unit * dt * v)
 | 
					 | 
				
			||||||
		transform.new = moving
 | 
					 | 
				
			||||||
		blink.UpdateTransform.FireAll(mob, moving)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local throttle = interval(5)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function spawnMobs() 
 | 
					 | 
				
			||||||
	if throttle() then
 | 
					 | 
				
			||||||
		local p = Vector3.new(0, 5, 0)
 | 
					 | 
				
			||||||
		local cf = CFrame.new(p)
 | 
					 | 
				
			||||||
		local v = 5
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local e = world:entity()
 | 
					 | 
				
			||||||
		world:set(e, Velocity, v)
 | 
					 | 
				
			||||||
		world:set(e, Transform, { new = cf })
 | 
					 | 
				
			||||||
		world:add(e, Mob)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		blink.SpawnMob.FireAll(e, cf, v)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local scheduler = require(std.scheduler)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
scheduler.SYSTEM(spawnMobs)
 | 
					 | 
				
			||||||
scheduler.SYSTEM(mobsMove)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
return 0
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,40 +0,0 @@
 | 
				
			||||||
local Players = game:GetService("Players")
 | 
					 | 
				
			||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local std = ReplicatedStorage.std
 | 
					 | 
				
			||||||
local ref = require(std.ref)
 | 
					 | 
				
			||||||
local collect = require(std.collect)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local cts = require(std.components)
 | 
					 | 
				
			||||||
local world = require(std.world)
 | 
					 | 
				
			||||||
local Player = cts.Player
 | 
					 | 
				
			||||||
local Character = cts.Character
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local conn = {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function playersAdded(player: Player)
 | 
					 | 
				
			||||||
	local e = ref(player.UserId)
 | 
					 | 
				
			||||||
	world:set(e, Player, player)
 | 
					 | 
				
			||||||
	local characterAdd = player.CharacterAdded
 | 
					 | 
				
			||||||
	conn[e] = characterAdd:Connect(function(rig)
 | 
					 | 
				
			||||||
		while rig.Parent ~= workspace do
 | 
					 | 
				
			||||||
			task.wait()
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
		world:set(e, Character, rig)
 | 
					 | 
				
			||||||
	end)
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function playersRemoved(player: Player) 
 | 
					 | 
				
			||||||
	local e = ref(player.UserId)
 | 
					 | 
				
			||||||
	world:clear(e)
 | 
					 | 
				
			||||||
	local connection = conn[e]
 | 
					 | 
				
			||||||
	connection:Disconnect()
 | 
					 | 
				
			||||||
	conn[e] = nil
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local scheduler = require(std.scheduler)
 | 
					 | 
				
			||||||
local phases = require(std.phases)
 | 
					 | 
				
			||||||
scheduler.SYSTEM(playersAdded, phases.PlayerAdded)
 | 
					 | 
				
			||||||
scheduler.SYSTEM(playersRemoved, phases.PlayerRemoved)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
return 0
 | 
					 | 
				
			||||||
							
								
								
									
										20
									
								
								demo/src/ServerScriptService/systems/players_added.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								demo/src/ServerScriptService/systems/players_added.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
				
			||||||
 | 
					local collect = require(ReplicatedStorage.collect)
 | 
				
			||||||
 | 
					local types = require(ReplicatedStorage.types)
 | 
				
			||||||
 | 
					local ct = require(ReplicatedStorage.components)
 | 
				
			||||||
 | 
					local Players = game:GetService("Players")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					local player_added = collect(Players.PlayerAdded)
 | 
				
			||||||
 | 
					return function(world: types.World, dt: number)
 | 
				
			||||||
 | 
						for player in player_added do
 | 
				
			||||||
 | 
							local entity = world:entity()
 | 
				
			||||||
 | 
							world:set(entity, ct.Player, player)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for entity, player in world:query(ct.Player):without(ct.Renderable) do
 | 
				
			||||||
 | 
							local character = player.Character
 | 
				
			||||||
 | 
							if character and character.Parent ~= nil then
 | 
				
			||||||
 | 
								world:set(entity, ct.Renderable, character)
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										12
									
								
								demo/src/ServerScriptService/systems/poison_hurts.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								demo/src/ServerScriptService/systems/poison_hurts.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,12 @@
 | 
				
			||||||
 | 
					local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
				
			||||||
 | 
					local ct = require(ReplicatedStorage.components)
 | 
				
			||||||
 | 
					return function(world, dt)
 | 
				
			||||||
 | 
						for e, poison, health in world:query(ct.Poison, ct.Health) do
 | 
				
			||||||
 | 
							local health_after_tick = health - poison * dt * 0.05
 | 
				
			||||||
 | 
							if health_after_tick < 0 then
 | 
				
			||||||
 | 
								world:remove(e, ct.Health)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
							world:set(e, ct.Health, health_after_tick)
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										122
									
								
								demo/src/ServerScriptService/systems/replication.luau
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								demo/src/ServerScriptService/systems/replication.luau
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,122 @@
 | 
				
			||||||
 | 
					local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
				
			||||||
 | 
					local types = require("../../ReplicatedStorage/types")
 | 
				
			||||||
 | 
					local ct = require("../../ReplicatedStorage/components")
 | 
				
			||||||
 | 
					local jecs = require(ReplicatedStorage.ecs)
 | 
				
			||||||
 | 
					local remotes = require("../../ReplicatedStorage/remotes")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return function(world: types.World)
 | 
				
			||||||
 | 
					    local storages = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for component in world:query(ct.Networked) do
 | 
				
			||||||
 | 
					        local is_tag = jecs.is_tag(world, component)
 | 
				
			||||||
 | 
					        local storage = {} :: { [types.Entity]: any }
 | 
				
			||||||
 | 
					        storages[component] = storage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if is_tag then
 | 
				
			||||||
 | 
					            world:added(component, function(entity)
 | 
				
			||||||
 | 
					                storage[entity] = true
 | 
				
			||||||
 | 
					            end)
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            world:added(component, function(entity, _, value)
 | 
				
			||||||
 | 
					                storage[entity] = value
 | 
				
			||||||
 | 
					            end)
 | 
				
			||||||
 | 
					            world:changed(component, function(entity, _, value)
 | 
				
			||||||
 | 
					            	storage[entity] = value
 | 
				
			||||||
 | 
					            end)
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        world:removed(component, function(entity)
 | 
				
			||||||
 | 
					            storage[entity] = "jecs.Remove"
 | 
				
			||||||
 | 
					        end)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for relation in world:query(ct.NetworkedPair) do
 | 
				
			||||||
 | 
					        world:added(relation, function(entity, id, value)
 | 
				
			||||||
 | 
					            local is_tag = jecs.is_tag(world, id)
 | 
				
			||||||
 | 
					            local storage = storages[id]
 | 
				
			||||||
 | 
					            if not storage then
 | 
				
			||||||
 | 
					                storage = {}
 | 
				
			||||||
 | 
					                storages[id] = storage
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					            if is_tag then
 | 
				
			||||||
 | 
					                storage[entity] = true
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                storage[entity] = value
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					        end)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        world:changed(relation, function(entity, id, value)
 | 
				
			||||||
 | 
					            local is_tag = jecs.is_tag(world, id)
 | 
				
			||||||
 | 
					            if is_tag then
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            local storage = storages[id]
 | 
				
			||||||
 | 
					            if not storage then
 | 
				
			||||||
 | 
					                storage = {}
 | 
				
			||||||
 | 
					                storages[id] = storage
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            storage[entity] = value
 | 
				
			||||||
 | 
					        end :: <T>(types.Entity, types.Id<T>, T) -> ())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        world:removed(relation, function(entity, id)
 | 
				
			||||||
 | 
					            local storage = storages[id]
 | 
				
			||||||
 | 
					            if not storage then
 | 
				
			||||||
 | 
					                storage = {}
 | 
				
			||||||
 | 
					                storages[id] = storage
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            storage[entity] = "jecs.Remove"
 | 
				
			||||||
 | 
					        end)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return function()
 | 
				
			||||||
 | 
					        local snapshot = {} :: {
 | 
				
			||||||
 | 
					        	[string]: {
 | 
				
			||||||
 | 
					         		set: { types.Entity }?,
 | 
				
			||||||
 | 
					           		values: { any }?,
 | 
				
			||||||
 | 
					             	removed: { types.Entity }?
 | 
				
			||||||
 | 
					         	}
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        local set_ids = {} :: { types.Entity }
 | 
				
			||||||
 | 
					        local removed_ids = {} :: { types.Entity }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for component, storage in storages do
 | 
				
			||||||
 | 
					            local set_values = {}
 | 
				
			||||||
 | 
					            local set_n = 0
 | 
				
			||||||
 | 
					            local removed_n = 0
 | 
				
			||||||
 | 
					            for e, v in storage do
 | 
				
			||||||
 | 
					                if v ~= "jecs.Remove" then
 | 
				
			||||||
 | 
					                    set_n += 1
 | 
				
			||||||
 | 
					                    set_ids[set_n] = e
 | 
				
			||||||
 | 
					                    set_values[set_n] = v or true
 | 
				
			||||||
 | 
					                elseif world:contains(e) then
 | 
				
			||||||
 | 
					                    removed_n += 1
 | 
				
			||||||
 | 
					                    removed_ids[removed_n] = e
 | 
				
			||||||
 | 
					                end
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            table.clear(storage)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            local dirty = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if set_n > 0 or removed_n > 0 then
 | 
				
			||||||
 | 
					            	dirty = true
 | 
				
			||||||
 | 
					            end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if dirty then
 | 
				
			||||||
 | 
						            snapshot[tostring(component)] = {
 | 
				
			||||||
 | 
					                    set = if set_n > 0 then table.move(set_ids, 1, set_n, 1, {}) else nil,
 | 
				
			||||||
 | 
					                    values = if set_n > 0 then set_values else nil,
 | 
				
			||||||
 | 
					                    removed = if removed_n > 0 then table.move(removed_ids, 1, removed_n, 1, {} :: { types.Entity }) else nil
 | 
				
			||||||
 | 
					                } :: any
 | 
				
			||||||
 | 
						        end
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if next(snapshot) ~= nil then
 | 
				
			||||||
 | 
					        	remotes.replication:FireAllClients(snapshot)
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -1,4 +0,0 @@
 | 
				
			||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
					 | 
				
			||||||
local start = require(ReplicatedStorage.start)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
start(script.Parent:WaitForChild("systems"):GetChildren())
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,67 +0,0 @@
 | 
				
			||||||
--!optimize 2
 | 
					 | 
				
			||||||
--!native
 | 
					 | 
				
			||||||
--!strict
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
					 | 
				
			||||||
local jecs = require(ReplicatedStorage.ecs)
 | 
					 | 
				
			||||||
local __ = jecs.Wildcard
 | 
					 | 
				
			||||||
local std = ReplicatedStorage.std
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local world = require(std.world)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local Position = world:component() :: jecs.Entity<vector>
 | 
					 | 
				
			||||||
local Previous = jecs.Rest
 | 
					 | 
				
			||||||
local pre = jecs.pair(Position, Previous)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local added = world
 | 
					 | 
				
			||||||
	:query(Position)
 | 
					 | 
				
			||||||
	:without(pre)
 | 
					 | 
				
			||||||
	:cached()
 | 
					 | 
				
			||||||
local changed = world
 | 
					 | 
				
			||||||
	:query(Position, pre)
 | 
					 | 
				
			||||||
	:cached()
 | 
					 | 
				
			||||||
local removed = world
 | 
					 | 
				
			||||||
	:query(pre)
 | 
					 | 
				
			||||||
	:without(Position)
 | 
					 | 
				
			||||||
	:cached()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local children = {}
 | 
					 | 
				
			||||||
for i = 1, 10 do
 | 
					 | 
				
			||||||
	local e = world:entity()
 | 
					 | 
				
			||||||
	world:set(e, Position, vector.create(i, i, i))
 | 
					 | 
				
			||||||
	table.insert(children, e)
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
local function flip()
 | 
					 | 
				
			||||||
	return math.random() > 0.5
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
local function system()
 | 
					 | 
				
			||||||
	for i, child in children do
 | 
					 | 
				
			||||||
		world:set(child, Position, vector.create(i,i,i))
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
	for e, p in added:iter() do
 | 
					 | 
				
			||||||
		world:set(e, pre, p)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
	for i, child in children do
 | 
					 | 
				
			||||||
		if flip() then
 | 
					 | 
				
			||||||
			world:set(child, Position, vector.create(i + 1, i + 1, i + 1))
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
	for e, new, old in changed:iter() do
 | 
					 | 
				
			||||||
		if new ~= old then
 | 
					 | 
				
			||||||
			world:set(e, pre, new)
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i, child in children do
 | 
					 | 
				
			||||||
		world:remove(child, Position)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for e in removed:iter() do
 | 
					 | 
				
			||||||
		world:remove(e, pre)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
local scheduler = require(std.scheduler)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
scheduler.SYSTEM(system)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
return 0
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,90 +0,0 @@
 | 
				
			||||||
--!optimize 2
 | 
					 | 
				
			||||||
--!native
 | 
					 | 
				
			||||||
--!strict
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
					 | 
				
			||||||
local jecs = require(ReplicatedStorage.ecs)
 | 
					 | 
				
			||||||
local __ = jecs.Wildcard
 | 
					 | 
				
			||||||
local std = ReplicatedStorage.std
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local world = require(std.world)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local Position = world:component() :: jecs.Entity<vector>
 | 
					 | 
				
			||||||
local Previous = jecs.Rest
 | 
					 | 
				
			||||||
local pre = jecs.pair(Position, Previous)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local added = world
 | 
					 | 
				
			||||||
	:query(Position)
 | 
					 | 
				
			||||||
	:without(pre)
 | 
					 | 
				
			||||||
	:cached()
 | 
					 | 
				
			||||||
local changed = world
 | 
					 | 
				
			||||||
	:query(Position, pre)
 | 
					 | 
				
			||||||
	:cached()
 | 
					 | 
				
			||||||
local removed = world
 | 
					 | 
				
			||||||
	:query(pre)
 | 
					 | 
				
			||||||
	:without(Position)
 | 
					 | 
				
			||||||
	:cached()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local children = {}
 | 
					 | 
				
			||||||
for i = 1, 10 do
 | 
					 | 
				
			||||||
	local e = world:entity()
 | 
					 | 
				
			||||||
	world:set(e, Position, vector.create(i, i, i))
 | 
					 | 
				
			||||||
	table.insert(children, e)
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
local function flip()
 | 
					 | 
				
			||||||
	return math.random() > 0.5
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
local entity_index = world.entity_index
 | 
					 | 
				
			||||||
local function copy(archetypes, id)
 | 
					 | 
				
			||||||
	for _, archetype in archetypes do
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local to = jecs.archetype_traverse_add(world, pre, archetype)
 | 
					 | 
				
			||||||
		local columns = to.columns
 | 
					 | 
				
			||||||
		local records = to.records
 | 
					 | 
				
			||||||
		local old = columns[records[pre].column]
 | 
					 | 
				
			||||||
		local new =	columns[records[id].column]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if to ~= archetype then
 | 
					 | 
				
			||||||
			for _, entity in archetype.entities do
 | 
					 | 
				
			||||||
				local r = jecs.entity_index_try_get_fast(entity_index, entity)
 | 
					 | 
				
			||||||
				jecs.entity_move(entity_index, entity, r, to)
 | 
					 | 
				
			||||||
			end
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		table.move(new, 1, #new, 1, old)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
local function system2()
 | 
					 | 
				
			||||||
	for i, child in children do
 | 
					 | 
				
			||||||
		world:set(child, Position, vector.create(i,i,i))
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
	for e, p in added:iter() do
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
	copy(added:archetypes(), Position)
 | 
					 | 
				
			||||||
	for i, child in children do
 | 
					 | 
				
			||||||
		if flip() then
 | 
					 | 
				
			||||||
			world:set(child, Position, vector.create(i + 1, i + 1, i + 1))
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for e, new, old in changed:iter() do
 | 
					 | 
				
			||||||
		if new ~= old then
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	copy(changed:archetypes(), Position)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for i, child in children do
 | 
					 | 
				
			||||||
		world:remove(child, Position)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for e in removed:iter() do
 | 
					 | 
				
			||||||
		world:remove(e, pre)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
local scheduler = require(std.scheduler)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
scheduler.SYSTEM(system2)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
return 0
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,46 +0,0 @@
 | 
				
			||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
					 | 
				
			||||||
local blink = require(ReplicatedStorage.net)
 | 
					 | 
				
			||||||
local std = ReplicatedStorage.std
 | 
					 | 
				
			||||||
local world = require(std.world)
 | 
					 | 
				
			||||||
local ref = require(std.ref)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local cts = require(std.components)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local Model = cts.Model
 | 
					 | 
				
			||||||
local Transform = cts.Transform
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local moved_models = world:query(Model, Transform):cached()
 | 
					 | 
				
			||||||
local updated_models = {}
 | 
					 | 
				
			||||||
local i = 0
 | 
					 | 
				
			||||||
local function processed(n)
 | 
					 | 
				
			||||||
	i += 1
 | 
					 | 
				
			||||||
	if i > n then
 | 
					 | 
				
			||||||
		i = 0
 | 
					 | 
				
			||||||
		return true
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
	return false
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function move(dt: number)
 | 
					 | 
				
			||||||
	for entity, model in moved_models do
 | 
					 | 
				
			||||||
		if updated_models[entity] then
 | 
					 | 
				
			||||||
			updated_models[entity] = nil
 | 
					 | 
				
			||||||
			model.PrimaryPart.CFrame = transform
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function syncTransforms()
 | 
					 | 
				
			||||||
	for _, id, cf in blink.UpdateTransform.Iter() do
 | 
					 | 
				
			||||||
		local e = ref("server-" .. tostring(id))
 | 
					 | 
				
			||||||
		world:set(e, Transform, cf)
 | 
					 | 
				
			||||||
		moved_models[e] = true
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local scheduler = require(std.scheduler)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
scheduler.SYSTEM(move)
 | 
					 | 
				
			||||||
scheduler.SYSTEM(syncTransforms)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
return 0
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,31 +0,0 @@
 | 
				
			||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
					 | 
				
			||||||
local blink = require(ReplicatedStorage.net)
 | 
					 | 
				
			||||||
local std = ReplicatedStorage.std
 | 
					 | 
				
			||||||
local ref = require(std.ref)
 | 
					 | 
				
			||||||
local world = require(std.world)
 | 
					 | 
				
			||||||
local cts = require(std.components)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function syncMobs()
 | 
					 | 
				
			||||||
	for _, id, cf, vel in blink.SpawnMob.Iter() do
 | 
					 | 
				
			||||||
		local part = Instance.new("Part")
 | 
					 | 
				
			||||||
		part.Size = Vector3.one * 5
 | 
					 | 
				
			||||||
		part.BrickColor = BrickColor.Red()
 | 
					 | 
				
			||||||
		part.Anchored = true
 | 
					 | 
				
			||||||
		local model = Instance.new("Model")
 | 
					 | 
				
			||||||
		model.PrimaryPart = part
 | 
					 | 
				
			||||||
		part.Parent = model
 | 
					 | 
				
			||||||
		model.Parent = workspace
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		local e = ref("server-" .. tostring(id))
 | 
					 | 
				
			||||||
		world:set(e, cts.Transform, { new = cf, old = cf })
 | 
					 | 
				
			||||||
		world:set(e, cts.Velocity, vel)
 | 
					 | 
				
			||||||
		world:set(e, cts.Model, model)
 | 
					 | 
				
			||||||
		world:add(e, cts.Mob)
 | 
					 | 
				
			||||||
	end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local scheduler = require(std.scheduler)
 | 
					 | 
				
			||||||
scheduler.SYSTEM(syncMobs)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
return 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,44 +0,0 @@
 | 
				
			||||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 | 
					 | 
				
			||||||
local std = ReplicatedStorage.std
 | 
					 | 
				
			||||||
local world = require(std.world)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local A = world:component()
 | 
					 | 
				
			||||||
local B = world:component()
 | 
					 | 
				
			||||||
local C = world:component()
 | 
					 | 
				
			||||||
local D = world:component()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function flip() 
 | 
					 | 
				
			||||||
    return math.random() >= 0.15
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
for i = 1, 2^8 do 
 | 
					 | 
				
			||||||
    local e = world:entity()
 | 
					 | 
				
			||||||
    if flip() then 
 | 
					 | 
				
			||||||
        world:set(e, A, true)
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
    if flip() then 
 | 
					 | 
				
			||||||
        world:set(e, B, true)
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
    if flip() then 
 | 
					 | 
				
			||||||
        world:set(e, C, true)
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
    if flip() then 
 | 
					 | 
				
			||||||
        world:set(e, D, true)
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local function uncached() 
 | 
					 | 
				
			||||||
    for _ in world:query(A, B, C, D) do 
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local q = world:query(A, B, C, D):cached()
 | 
					 | 
				
			||||||
local function cached() 
 | 
					 | 
				
			||||||
    for _ in q do 
 | 
					 | 
				
			||||||
    end
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
local scheduler = require(std.scheduler)
 | 
					 | 
				
			||||||
scheduler.SYSTEM(uncached)
 | 
					 | 
				
			||||||
scheduler.SYSTEM(cached)
 | 
					 | 
				
			||||||
return 0
 | 
					 | 
				
			||||||
| 
						 | 
					@ -2672,8 +2672,8 @@ return {
 | 
				
			||||||
	ECS_META_RESET = ECS_META_RESET,
 | 
						ECS_META_RESET = ECS_META_RESET,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	IS_PAIR = (ECS_IS_PAIR :: any) :: <P, O>(pair: Pair<P, O>) -> boolean,
 | 
						IS_PAIR = (ECS_IS_PAIR :: any) :: <P, O>(pair: Pair<P, O>) -> boolean,
 | 
				
			||||||
	ECS_PAIR_FIRST = ECS_PAIR_FIRST,
 | 
						ECS_PAIR_FIRST = ECS_PAIR_FIRST :: <P, O>(pair: Pair<P, O>) -> Id<P>,
 | 
				
			||||||
	ECS_PAIR_SECOND = ECS_PAIR_SECOND,
 | 
						ECS_PAIR_SECOND = ECS_PAIR_SECOND :: <P, O>(pair: Pair<P, O>) -> Id<O>,
 | 
				
			||||||
	pair_first = (ecs_pair_first :: any) :: <P, O>(world: World, pair: Pair<P, O>) -> Id<P>,
 | 
						pair_first = (ecs_pair_first :: any) :: <P, O>(world: World, pair: Pair<P, O>) -> Id<P>,
 | 
				
			||||||
	pair_second = (ecs_pair_second :: any) :: <P, O>(world: World, pair: Pair<P, O>) -> Id<O>,
 | 
						pair_second = (ecs_pair_second :: any) :: <P, O>(world: World, pair: Pair<P, O>) -> Id<O>,
 | 
				
			||||||
	entity_index_get_alive = entity_index_get_alive,
 | 
						entity_index_get_alive = entity_index_get_alive,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,3 +3,4 @@ wally = "upliftgames/wally@0.3.2"
 | 
				
			||||||
rojo = "rojo-rbx/rojo@7.4.4"
 | 
					rojo = "rojo-rbx/rojo@7.4.4"
 | 
				
			||||||
stylua = "johnnymorganz/stylua@2.0.1"
 | 
					stylua = "johnnymorganz/stylua@2.0.1"
 | 
				
			||||||
Blink = "1Axen/Blink@0.14.1"
 | 
					Blink = "1Axen/Blink@0.14.1"
 | 
				
			||||||
 | 
					wally-package-types = "JohnnyMorganz/wally-package-types@1.4.2"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue