mirror of
https://github.com/Ukendio/jecs.git
synced 2025-06-19 15:59:18 +00:00
Rework observers addon interface
This commit is contained in:
parent
35b5f04a7c
commit
56b52286b8
5 changed files with 61 additions and 780 deletions
|
@ -1,26 +1,22 @@
|
|||
local jecs = require("@jecs")
|
||||
|
||||
type Observer = {
|
||||
callback: (jecs.Entity) -> (),
|
||||
query: jecs.Query<...any>,
|
||||
}
|
||||
|
||||
type Monitor = {
|
||||
callback: (jecs.Entity, jecs.Entity) -> (),
|
||||
query: jecs.Query<any>
|
||||
}
|
||||
|
||||
export type PatchedWorld = jecs.World & {
|
||||
added: <T>(PatchedWorld, jecs.Id<T>, (e: jecs.Entity, id: jecs.Id, value: T) -> ()) -> () -> (),
|
||||
removed: <T>(PatchedWorld, jecs.Id<T>, (e: jecs.Entity, id: jecs.Id) -> ()) -> () -> (),
|
||||
changed: <T>(PatchedWorld, jecs.Id<T>, (e: jecs.Entity, id: jecs.Id, value: T) -> ()) -> () -> (),
|
||||
observer: (PatchedWorld, Observer) -> (),
|
||||
monitor: (PatchedWorld, Monitor) -> (),
|
||||
observer: (
|
||||
PatchedWorld,
|
||||
any,
|
||||
(jecs.Entity) -> ()
|
||||
) -> (),
|
||||
monitor: (
|
||||
PatchedWorld,
|
||||
any,
|
||||
(jecs.Entity, jecs.Id) -> ()
|
||||
) -> ()
|
||||
}
|
||||
|
||||
local function observers_new(world, description)
|
||||
local query = description.query
|
||||
local callback = description.callback
|
||||
local function observers_new(world, query, callback)
|
||||
local terms = query.filter_with :: { jecs.Id }
|
||||
if not terms then
|
||||
local ids = query.ids
|
||||
|
@ -94,9 +90,7 @@ local function join(world, component)
|
|||
end
|
||||
end
|
||||
|
||||
local function monitors_new(world, description)
|
||||
local query = description.query
|
||||
local callback = description.callback
|
||||
local function monitors_new(world, query, callback)
|
||||
local terms = query.filter_with :: { jecs.Id }
|
||||
if not terms then
|
||||
local ids = query.ids
|
||||
|
@ -131,7 +125,8 @@ local function monitors_new(world, description)
|
|||
local archetype = r.archetype
|
||||
|
||||
if jecs.query_match(query, archetype) then
|
||||
callback(entity, jecs.OnRemove)
|
||||
local EcsOnRemove = jecs.OnRemove :: jecs.Id
|
||||
callback(entity, EcsOnRemove)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -162,7 +157,7 @@ local function observers_add(world: jecs.World): PatchedWorld
|
|||
listeners = {}
|
||||
signals.added[component] = listeners
|
||||
|
||||
local function on_add(entity: number, id: number, value: any)
|
||||
local function on_add(entity, id, value)
|
||||
for _, listener in listeners :: any do
|
||||
listener(entity, id, value)
|
||||
end
|
||||
|
@ -170,13 +165,14 @@ local function observers_add(world: jecs.World): PatchedWorld
|
|||
local existing_hook = world:get(component, jecs.OnAdd)
|
||||
if existing_hook then
|
||||
table.insert(listeners, existing_hook)
|
||||
local idr = world.component_index[component]
|
||||
if idr then
|
||||
idr.hooks.on_add = on_add
|
||||
end
|
||||
end
|
||||
|
||||
world:set(component, jecs.OnAdd, on_add)
|
||||
local idr = world.component_index[component]
|
||||
if idr then
|
||||
idr.hooks.on_add = on_add
|
||||
else
|
||||
world:set(component, jecs.OnAdd, on_add)
|
||||
end
|
||||
end
|
||||
table.insert(listeners, fn)
|
||||
return function()
|
||||
|
@ -196,7 +192,7 @@ local function observers_add(world: jecs.World): PatchedWorld
|
|||
if not listeners then
|
||||
listeners = {}
|
||||
signals.emplaced[component] = listeners
|
||||
local function on_change(entity: number, id: number, value: any)
|
||||
local function on_change(entity, id, value: any)
|
||||
for _, listener in listeners :: any do
|
||||
listener(entity, id, value)
|
||||
end
|
||||
|
@ -204,12 +200,13 @@ local function observers_add(world: jecs.World): PatchedWorld
|
|||
local existing_hook = world:get(component, jecs.OnChange)
|
||||
if existing_hook then
|
||||
table.insert(listeners, existing_hook)
|
||||
local idr = world.component_index[component]
|
||||
if idr then
|
||||
idr.hooks.on_change = on_change
|
||||
end
|
||||
end
|
||||
world:set(component, jecs.OnChange, on_change)
|
||||
local idr = world.component_index[component]
|
||||
if idr then
|
||||
idr.hooks.on_change = on_change
|
||||
else
|
||||
world:set(component, jecs.OnChange, on_change)
|
||||
end
|
||||
end
|
||||
table.insert(listeners, fn)
|
||||
return function()
|
||||
|
@ -229,23 +226,26 @@ local function observers_add(world: jecs.World): PatchedWorld
|
|||
if not listeners then
|
||||
listeners = {}
|
||||
signals.removed[component] = listeners
|
||||
local function on_remove(entity: number, id: number, value: any)
|
||||
local function on_remove(entity, id)
|
||||
for _, listener in listeners :: any do
|
||||
listener(entity, id, value)
|
||||
listener(entity, id)
|
||||
end
|
||||
end
|
||||
local existing_hook = world:get(component, jecs.OnRemove)
|
||||
if existing_hook then
|
||||
table.insert(listeners, existing_hook)
|
||||
local idr = world.component_index[component]
|
||||
if idr then
|
||||
idr.hooks.on_remove = on_remove
|
||||
end
|
||||
end
|
||||
|
||||
world:set(component, jecs.OnRemove, on_remove)
|
||||
local idr = world.component_index[component]
|
||||
if idr then
|
||||
idr.hooks.on_remove = on_remove
|
||||
else
|
||||
world:set(component, jecs.OnRemove, on_remove)
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(listeners, fn)
|
||||
|
||||
return function()
|
||||
local n = #listeners
|
||||
local i = table.find(listeners, fn)
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
option ClientOutput = "../net/client.luau"
|
||||
option ServerOutput = "../net/server.luau"
|
||||
|
||||
event UpdateTransform {
|
||||
From: Server,
|
||||
Type: Unreliable,
|
||||
Call: SingleSync,
|
||||
Poll: true,
|
||||
Data: (f64, CFrame)
|
||||
}
|
||||
|
||||
event SpawnMob {
|
||||
From: Server,
|
||||
Type: Reliable,
|
||||
Call: SingleSync,
|
||||
Poll: true,
|
||||
Data: (f64, CFrame, u8)
|
||||
}
|
|
@ -1,336 +0,0 @@
|
|||
--!strict
|
||||
--!native
|
||||
--!optimize 2
|
||||
--!nolint LocalShadow
|
||||
--#selene: allow(shadowing)
|
||||
-- File generated by Blink v0.14.1 (https://github.com/1Axen/Blink)
|
||||
-- This file is not meant to be edited
|
||||
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
local RunService = game:GetService("RunService")
|
||||
|
||||
if not RunService:IsClient() then
|
||||
error("Client network module can only be required from the client.")
|
||||
end
|
||||
|
||||
local Reliable: RemoteEvent = ReplicatedStorage:WaitForChild("BLINK_RELIABLE_REMOTE") :: RemoteEvent
|
||||
local Unreliable: UnreliableRemoteEvent =
|
||||
ReplicatedStorage:WaitForChild("BLINK_UNRELIABLE_REMOTE") :: UnreliableRemoteEvent
|
||||
|
||||
local Invocations = 0
|
||||
|
||||
local SendSize = 64
|
||||
local SendOffset = 0
|
||||
local SendCursor = 0
|
||||
local SendBuffer = buffer.create(64)
|
||||
local SendInstances = {}
|
||||
|
||||
local RecieveCursor = 0
|
||||
local RecieveBuffer = buffer.create(64)
|
||||
|
||||
local RecieveInstances = {}
|
||||
local RecieveInstanceCursor = 0
|
||||
|
||||
type Entry = {
|
||||
value: any,
|
||||
next: Entry?,
|
||||
}
|
||||
|
||||
type Queue = {
|
||||
head: Entry?,
|
||||
tail: Entry?,
|
||||
}
|
||||
|
||||
type BufferSave = {
|
||||
Size: number,
|
||||
Cursor: number,
|
||||
Buffer: buffer,
|
||||
Instances: { Instance },
|
||||
}
|
||||
|
||||
local function Read(Bytes: number)
|
||||
local Offset = RecieveCursor
|
||||
RecieveCursor += Bytes
|
||||
return Offset
|
||||
end
|
||||
|
||||
local function Save(): BufferSave
|
||||
return {
|
||||
Size = SendSize,
|
||||
Cursor = SendCursor,
|
||||
Buffer = SendBuffer,
|
||||
Instances = SendInstances,
|
||||
}
|
||||
end
|
||||
|
||||
local function Load(Save: BufferSave?)
|
||||
if Save then
|
||||
SendSize = Save.Size
|
||||
SendCursor = Save.Cursor
|
||||
SendOffset = Save.Cursor
|
||||
SendBuffer = Save.Buffer
|
||||
SendInstances = Save.Instances
|
||||
return
|
||||
end
|
||||
|
||||
SendSize = 64
|
||||
SendCursor = 0
|
||||
SendOffset = 0
|
||||
SendBuffer = buffer.create(64)
|
||||
SendInstances = {}
|
||||
end
|
||||
|
||||
local function Invoke()
|
||||
if Invocations == 255 then
|
||||
Invocations = 0
|
||||
end
|
||||
|
||||
local Invocation = Invocations
|
||||
Invocations += 1
|
||||
return Invocation
|
||||
end
|
||||
|
||||
local function Allocate(Bytes: number)
|
||||
local InUse = (SendCursor + Bytes)
|
||||
if InUse > SendSize then
|
||||
--> Avoid resizing the buffer for every write
|
||||
while InUse > SendSize do
|
||||
SendSize *= 1.5
|
||||
end
|
||||
|
||||
local Buffer = buffer.create(SendSize)
|
||||
buffer.copy(Buffer, 0, SendBuffer, 0, SendCursor)
|
||||
SendBuffer = Buffer
|
||||
end
|
||||
|
||||
SendOffset = SendCursor
|
||||
SendCursor += Bytes
|
||||
|
||||
return SendOffset
|
||||
end
|
||||
|
||||
local function CreateQueue(): Queue
|
||||
return {
|
||||
head = nil,
|
||||
tail = nil,
|
||||
}
|
||||
end
|
||||
|
||||
local function Pop(queue: Queue): any
|
||||
local head = queue.head
|
||||
if head == nil then
|
||||
return
|
||||
end
|
||||
|
||||
queue.head = head.next
|
||||
return head.value
|
||||
end
|
||||
|
||||
local function Push(queue: Queue, value: any)
|
||||
local entry: Entry = {
|
||||
value = value,
|
||||
next = nil,
|
||||
}
|
||||
|
||||
if queue.tail ~= nil then
|
||||
queue.tail.next = entry
|
||||
end
|
||||
|
||||
queue.tail = entry
|
||||
|
||||
if queue.head == nil then
|
||||
queue.head = entry
|
||||
end
|
||||
end
|
||||
|
||||
local Types = {}
|
||||
local Calls = table.create(256)
|
||||
|
||||
local Events: any = {
|
||||
Reliable = table.create(256),
|
||||
Unreliable = table.create(256),
|
||||
}
|
||||
|
||||
local Queue: any = {
|
||||
Reliable = table.create(256),
|
||||
Unreliable = table.create(256),
|
||||
}
|
||||
|
||||
Queue.Unreliable[0] = CreateQueue()
|
||||
Queue.Reliable[0] = CreateQueue()
|
||||
|
||||
function Types.ReadEVENT_UpdateTransform(): (number, CFrame)
|
||||
-- Read BLOCK: 32 bytes
|
||||
local BLOCK_START = Read(32)
|
||||
local Value1 = buffer.readf64(RecieveBuffer, BLOCK_START + 0)
|
||||
local X = buffer.readf32(RecieveBuffer, BLOCK_START + 8)
|
||||
local Y = buffer.readf32(RecieveBuffer, BLOCK_START + 12)
|
||||
local Z = buffer.readf32(RecieveBuffer, BLOCK_START + 16)
|
||||
local Position = Vector3.new(X, Y, Z)
|
||||
local rX = buffer.readf32(RecieveBuffer, BLOCK_START + 20)
|
||||
local rY = buffer.readf32(RecieveBuffer, BLOCK_START + 24)
|
||||
local rZ = buffer.readf32(RecieveBuffer, BLOCK_START + 28)
|
||||
local Value2 = CFrame.new(Position) * CFrame.fromOrientation(rX, rY, rZ)
|
||||
return Value1, Value2
|
||||
end
|
||||
|
||||
function Types.WriteEVENT_UpdateTransform(Value1: number, Value2: CFrame): ()
|
||||
-- Allocate BLOCK: 33 bytes
|
||||
local BLOCK_START = Allocate(33)
|
||||
buffer.writeu8(SendBuffer, BLOCK_START + 0, 0)
|
||||
buffer.writef64(SendBuffer, BLOCK_START + 1, Value1)
|
||||
local Vector = Value2.Position
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 9, Vector.X)
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 13, Vector.Y)
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 17, Vector.Z)
|
||||
local rX, rY, rZ = Value2:ToOrientation()
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 21, rX)
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 25, rY)
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 29, rZ)
|
||||
end
|
||||
|
||||
function Types.ReadEVENT_SpawnMob(): (number, CFrame, number)
|
||||
-- Read BLOCK: 33 bytes
|
||||
local BLOCK_START = Read(33)
|
||||
local Value1 = buffer.readf64(RecieveBuffer, BLOCK_START + 0)
|
||||
local X = buffer.readf32(RecieveBuffer, BLOCK_START + 8)
|
||||
local Y = buffer.readf32(RecieveBuffer, BLOCK_START + 12)
|
||||
local Z = buffer.readf32(RecieveBuffer, BLOCK_START + 16)
|
||||
local Position = Vector3.new(X, Y, Z)
|
||||
local rX = buffer.readf32(RecieveBuffer, BLOCK_START + 20)
|
||||
local rY = buffer.readf32(RecieveBuffer, BLOCK_START + 24)
|
||||
local rZ = buffer.readf32(RecieveBuffer, BLOCK_START + 28)
|
||||
local Value2 = CFrame.new(Position) * CFrame.fromOrientation(rX, rY, rZ)
|
||||
local Value3 = buffer.readu8(RecieveBuffer, BLOCK_START + 32)
|
||||
return Value1, Value2, Value3
|
||||
end
|
||||
|
||||
function Types.WriteEVENT_SpawnMob(Value1: number, Value2: CFrame, Value3: number): ()
|
||||
-- Allocate BLOCK: 34 bytes
|
||||
local BLOCK_START = Allocate(34)
|
||||
buffer.writeu8(SendBuffer, BLOCK_START + 0, 0)
|
||||
buffer.writef64(SendBuffer, BLOCK_START + 1, Value1)
|
||||
local Vector = Value2.Position
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 9, Vector.X)
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 13, Vector.Y)
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 17, Vector.Z)
|
||||
local rX, rY, rZ = Value2:ToOrientation()
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 21, rX)
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 25, rY)
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 29, rZ)
|
||||
buffer.writeu8(SendBuffer, BLOCK_START + 33, Value3)
|
||||
end
|
||||
|
||||
local function StepReplication()
|
||||
if SendCursor <= 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local Buffer = buffer.create(SendCursor)
|
||||
buffer.copy(Buffer, 0, SendBuffer, 0, SendCursor)
|
||||
Reliable:FireServer(Buffer, SendInstances)
|
||||
|
||||
SendSize = 64
|
||||
SendCursor = 0
|
||||
SendOffset = 0
|
||||
SendBuffer = buffer.create(64)
|
||||
table.clear(SendInstances)
|
||||
end
|
||||
|
||||
local Elapsed = 0
|
||||
RunService.Heartbeat:Connect(function(DeltaTime: number)
|
||||
Elapsed += DeltaTime
|
||||
if Elapsed >= (1 / 61) then
|
||||
Elapsed -= (1 / 61)
|
||||
StepReplication()
|
||||
end
|
||||
end)
|
||||
|
||||
Reliable.OnClientEvent:Connect(function(Buffer: buffer, Instances: { Instance })
|
||||
RecieveCursor = 0
|
||||
RecieveBuffer = Buffer
|
||||
RecieveInstances = Instances
|
||||
RecieveInstanceCursor = 0
|
||||
local Size = buffer.len(RecieveBuffer)
|
||||
while RecieveCursor < Size do
|
||||
-- Read BLOCK: 1 bytes
|
||||
local BLOCK_START = Read(1)
|
||||
local Index = buffer.readu8(RecieveBuffer, BLOCK_START + 0)
|
||||
if Index == 0 then
|
||||
Push(Queue.Reliable[0], table.pack(Types.ReadEVENT_SpawnMob()))
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
Unreliable.OnClientEvent:Connect(function(Buffer: buffer, Instances: { Instance })
|
||||
RecieveCursor = 0
|
||||
RecieveBuffer = Buffer
|
||||
RecieveInstances = Instances
|
||||
RecieveInstanceCursor = 0
|
||||
local Size = buffer.len(RecieveBuffer)
|
||||
while RecieveCursor < Size do
|
||||
-- Read BLOCK: 1 bytes
|
||||
local BLOCK_START = Read(1)
|
||||
local Index = buffer.readu8(RecieveBuffer, BLOCK_START + 0)
|
||||
if Index == 0 then
|
||||
Push(Queue.Unreliable[0], table.pack(Types.ReadEVENT_UpdateTransform()))
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
return {
|
||||
StepReplication = StepReplication,
|
||||
|
||||
UpdateTransform = {
|
||||
Iter = function(): () -> (number, number, CFrame)
|
||||
local index = 0
|
||||
local queue = Queue.Unreliable[0]
|
||||
return function(): (number, number, CFrame)
|
||||
index += 1
|
||||
local arguments = Pop(queue)
|
||||
if arguments ~= nil then
|
||||
return index, unpack(arguments, 1, arguments.n)
|
||||
end
|
||||
return
|
||||
end
|
||||
end,
|
||||
Next = function(): () -> (number, number, CFrame)
|
||||
local index = 0
|
||||
local queue = Queue.Unreliable[0]
|
||||
return function(): (number, number, CFrame)
|
||||
index += 1
|
||||
local arguments = Pop(queue)
|
||||
if arguments ~= nil then
|
||||
return index, unpack(arguments, 1, arguments.n)
|
||||
end
|
||||
return
|
||||
end
|
||||
end,
|
||||
},
|
||||
SpawnMob = {
|
||||
Iter = function(): () -> (number, number, CFrame, number)
|
||||
local index = 0
|
||||
local queue = Queue.Reliable[0]
|
||||
return function(): (number, number, CFrame, number)
|
||||
index += 1
|
||||
local arguments = Pop(queue)
|
||||
if arguments ~= nil then
|
||||
return index, unpack(arguments, 1, arguments.n)
|
||||
end
|
||||
return
|
||||
end
|
||||
end,
|
||||
Next = function(): () -> (number, number, CFrame, number)
|
||||
local index = 0
|
||||
local queue = Queue.Reliable[0]
|
||||
return function(): (number, number, CFrame, number)
|
||||
index += 1
|
||||
local arguments = Pop(queue)
|
||||
if arguments ~= nil then
|
||||
return index, unpack(arguments, 1, arguments.n)
|
||||
end
|
||||
return
|
||||
end
|
||||
end,
|
||||
},
|
||||
}
|
|
@ -1,372 +0,0 @@
|
|||
--!strict
|
||||
--!native
|
||||
--!optimize 2
|
||||
--!nolint LocalShadow
|
||||
--#selene: allow(shadowing)
|
||||
-- File generated by Blink v0.14.1 (https://github.com/1Axen/Blink)
|
||||
-- This file is not meant to be edited
|
||||
|
||||
local Players = game:GetService("Players")
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
local RunService = game:GetService("RunService")
|
||||
|
||||
if not RunService:IsServer() then
|
||||
error("Server network module can only be required from the server.")
|
||||
end
|
||||
|
||||
local Reliable: RemoteEvent = ReplicatedStorage:FindFirstChild("BLINK_RELIABLE_REMOTE") :: RemoteEvent
|
||||
if not Reliable then
|
||||
local RemoteEvent = Instance.new("RemoteEvent")
|
||||
RemoteEvent.Name = "BLINK_RELIABLE_REMOTE"
|
||||
RemoteEvent.Parent = ReplicatedStorage
|
||||
Reliable = RemoteEvent
|
||||
end
|
||||
|
||||
local Unreliable: UnreliableRemoteEvent =
|
||||
ReplicatedStorage:FindFirstChild("BLINK_UNRELIABLE_REMOTE") :: UnreliableRemoteEvent
|
||||
if not Unreliable then
|
||||
local UnreliableRemoteEvent = Instance.new("UnreliableRemoteEvent")
|
||||
UnreliableRemoteEvent.Name = "BLINK_UNRELIABLE_REMOTE"
|
||||
UnreliableRemoteEvent.Parent = ReplicatedStorage
|
||||
Unreliable = UnreliableRemoteEvent
|
||||
end
|
||||
|
||||
local Invocations = 0
|
||||
|
||||
local SendSize = 64
|
||||
local SendOffset = 0
|
||||
local SendCursor = 0
|
||||
local SendBuffer = buffer.create(64)
|
||||
local SendInstances = {}
|
||||
|
||||
local RecieveCursor = 0
|
||||
local RecieveBuffer = buffer.create(64)
|
||||
|
||||
local RecieveInstances = {}
|
||||
local RecieveInstanceCursor = 0
|
||||
|
||||
type Entry = {
|
||||
value: any,
|
||||
next: Entry?,
|
||||
}
|
||||
|
||||
type Queue = {
|
||||
head: Entry?,
|
||||
tail: Entry?,
|
||||
}
|
||||
|
||||
type BufferSave = {
|
||||
Size: number,
|
||||
Cursor: number,
|
||||
Buffer: buffer,
|
||||
Instances: { Instance },
|
||||
}
|
||||
|
||||
local function Read(Bytes: number)
|
||||
local Offset = RecieveCursor
|
||||
RecieveCursor += Bytes
|
||||
return Offset
|
||||
end
|
||||
|
||||
local function Save(): BufferSave
|
||||
return {
|
||||
Size = SendSize,
|
||||
Cursor = SendCursor,
|
||||
Buffer = SendBuffer,
|
||||
Instances = SendInstances,
|
||||
}
|
||||
end
|
||||
|
||||
local function Load(Save: BufferSave?)
|
||||
if Save then
|
||||
SendSize = Save.Size
|
||||
SendCursor = Save.Cursor
|
||||
SendOffset = Save.Cursor
|
||||
SendBuffer = Save.Buffer
|
||||
SendInstances = Save.Instances
|
||||
return
|
||||
end
|
||||
|
||||
SendSize = 64
|
||||
SendCursor = 0
|
||||
SendOffset = 0
|
||||
SendBuffer = buffer.create(64)
|
||||
SendInstances = {}
|
||||
end
|
||||
|
||||
local function Invoke()
|
||||
if Invocations == 255 then
|
||||
Invocations = 0
|
||||
end
|
||||
|
||||
local Invocation = Invocations
|
||||
Invocations += 1
|
||||
return Invocation
|
||||
end
|
||||
|
||||
local function Allocate(Bytes: number)
|
||||
local InUse = (SendCursor + Bytes)
|
||||
if InUse > SendSize then
|
||||
--> Avoid resizing the buffer for every write
|
||||
while InUse > SendSize do
|
||||
SendSize *= 1.5
|
||||
end
|
||||
|
||||
local Buffer = buffer.create(SendSize)
|
||||
buffer.copy(Buffer, 0, SendBuffer, 0, SendCursor)
|
||||
SendBuffer = Buffer
|
||||
end
|
||||
|
||||
SendOffset = SendCursor
|
||||
SendCursor += Bytes
|
||||
|
||||
return SendOffset
|
||||
end
|
||||
|
||||
local function CreateQueue(): Queue
|
||||
return {
|
||||
head = nil,
|
||||
tail = nil,
|
||||
}
|
||||
end
|
||||
|
||||
local function Pop(queue: Queue): any
|
||||
local head = queue.head
|
||||
if head == nil then
|
||||
return
|
||||
end
|
||||
|
||||
queue.head = head.next
|
||||
return head.value
|
||||
end
|
||||
|
||||
local function Push(queue: Queue, value: any)
|
||||
local entry: Entry = {
|
||||
value = value,
|
||||
next = nil,
|
||||
}
|
||||
|
||||
if queue.tail ~= nil then
|
||||
queue.tail.next = entry
|
||||
end
|
||||
|
||||
queue.tail = entry
|
||||
|
||||
if queue.head == nil then
|
||||
queue.head = entry
|
||||
end
|
||||
end
|
||||
|
||||
local Types = {}
|
||||
local Calls = table.create(256)
|
||||
|
||||
local Events: any = {
|
||||
Reliable = table.create(256),
|
||||
Unreliable = table.create(256),
|
||||
}
|
||||
|
||||
local Queue: any = {
|
||||
Reliable = table.create(256),
|
||||
Unreliable = table.create(256),
|
||||
}
|
||||
|
||||
function Types.ReadEVENT_UpdateTransform(): (number, CFrame)
|
||||
-- Read BLOCK: 32 bytes
|
||||
local BLOCK_START = Read(32)
|
||||
local Value1 = buffer.readf64(RecieveBuffer, BLOCK_START + 0)
|
||||
local X = buffer.readf32(RecieveBuffer, BLOCK_START + 8)
|
||||
local Y = buffer.readf32(RecieveBuffer, BLOCK_START + 12)
|
||||
local Z = buffer.readf32(RecieveBuffer, BLOCK_START + 16)
|
||||
local Position = Vector3.new(X, Y, Z)
|
||||
local rX = buffer.readf32(RecieveBuffer, BLOCK_START + 20)
|
||||
local rY = buffer.readf32(RecieveBuffer, BLOCK_START + 24)
|
||||
local rZ = buffer.readf32(RecieveBuffer, BLOCK_START + 28)
|
||||
local Value2 = CFrame.new(Position) * CFrame.fromOrientation(rX, rY, rZ)
|
||||
return Value1, Value2
|
||||
end
|
||||
|
||||
function Types.WriteEVENT_UpdateTransform(Value1: number, Value2: CFrame): ()
|
||||
-- Allocate BLOCK: 33 bytes
|
||||
local BLOCK_START = Allocate(33)
|
||||
buffer.writeu8(SendBuffer, BLOCK_START + 0, 0)
|
||||
buffer.writef64(SendBuffer, BLOCK_START + 1, Value1)
|
||||
local Vector = Value2.Position
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 9, Vector.X)
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 13, Vector.Y)
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 17, Vector.Z)
|
||||
local rX, rY, rZ = Value2:ToOrientation()
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 21, rX)
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 25, rY)
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 29, rZ)
|
||||
end
|
||||
|
||||
function Types.ReadEVENT_SpawnMob(): (number, CFrame, number)
|
||||
-- Read BLOCK: 33 bytes
|
||||
local BLOCK_START = Read(33)
|
||||
local Value1 = buffer.readf64(RecieveBuffer, BLOCK_START + 0)
|
||||
local X = buffer.readf32(RecieveBuffer, BLOCK_START + 8)
|
||||
local Y = buffer.readf32(RecieveBuffer, BLOCK_START + 12)
|
||||
local Z = buffer.readf32(RecieveBuffer, BLOCK_START + 16)
|
||||
local Position = Vector3.new(X, Y, Z)
|
||||
local rX = buffer.readf32(RecieveBuffer, BLOCK_START + 20)
|
||||
local rY = buffer.readf32(RecieveBuffer, BLOCK_START + 24)
|
||||
local rZ = buffer.readf32(RecieveBuffer, BLOCK_START + 28)
|
||||
local Value2 = CFrame.new(Position) * CFrame.fromOrientation(rX, rY, rZ)
|
||||
local Value3 = buffer.readu8(RecieveBuffer, BLOCK_START + 32)
|
||||
return Value1, Value2, Value3
|
||||
end
|
||||
|
||||
function Types.WriteEVENT_SpawnMob(Value1: number, Value2: CFrame, Value3: number): ()
|
||||
-- Allocate BLOCK: 34 bytes
|
||||
local BLOCK_START = Allocate(34)
|
||||
buffer.writeu8(SendBuffer, BLOCK_START + 0, 0)
|
||||
buffer.writef64(SendBuffer, BLOCK_START + 1, Value1)
|
||||
local Vector = Value2.Position
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 9, Vector.X)
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 13, Vector.Y)
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 17, Vector.Z)
|
||||
local rX, rY, rZ = Value2:ToOrientation()
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 21, rX)
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 25, rY)
|
||||
buffer.writef32(SendBuffer, BLOCK_START + 29, rZ)
|
||||
buffer.writeu8(SendBuffer, BLOCK_START + 33, Value3)
|
||||
end
|
||||
|
||||
local PlayersMap: { [Player]: BufferSave } = {}
|
||||
|
||||
Players.PlayerRemoving:Connect(function(Player)
|
||||
PlayersMap[Player] = nil
|
||||
end)
|
||||
|
||||
local function StepReplication()
|
||||
for Player, Send in PlayersMap do
|
||||
if Send.Cursor <= 0 then
|
||||
continue
|
||||
end
|
||||
|
||||
local Buffer = buffer.create(Send.Cursor)
|
||||
buffer.copy(Buffer, 0, Send.Buffer, 0, Send.Cursor)
|
||||
Reliable:FireClient(Player, Buffer, Send.Instances)
|
||||
|
||||
Send.Size = 64
|
||||
Send.Cursor = 0
|
||||
Send.Buffer = buffer.create(64)
|
||||
table.clear(Send.Instances)
|
||||
end
|
||||
end
|
||||
|
||||
RunService.Heartbeat:Connect(StepReplication)
|
||||
|
||||
Reliable.OnServerEvent:Connect(function(Player: Player, Buffer: buffer, Instances: { Instance })
|
||||
RecieveCursor = 0
|
||||
RecieveBuffer = Buffer
|
||||
RecieveInstances = Instances
|
||||
RecieveInstanceCursor = 0
|
||||
local Size = buffer.len(RecieveBuffer)
|
||||
while RecieveCursor < Size do
|
||||
-- Read BLOCK: 1 bytes
|
||||
local BLOCK_START = Read(1)
|
||||
local Index = buffer.readu8(RecieveBuffer, BLOCK_START + 0)
|
||||
end
|
||||
end)
|
||||
|
||||
Unreliable.OnServerEvent:Connect(function(Player: Player, Buffer: buffer, Instances: { Instance })
|
||||
RecieveCursor = 0
|
||||
RecieveBuffer = Buffer
|
||||
RecieveInstances = Instances
|
||||
RecieveInstanceCursor = 0
|
||||
local Size = buffer.len(RecieveBuffer)
|
||||
while RecieveCursor < Size do
|
||||
-- Read BLOCK: 1 bytes
|
||||
local BLOCK_START = Read(1)
|
||||
local Index = buffer.readu8(RecieveBuffer, BLOCK_START + 0)
|
||||
end
|
||||
end)
|
||||
|
||||
return {
|
||||
StepReplication = StepReplication,
|
||||
|
||||
UpdateTransform = {
|
||||
Fire = function(Player: Player, Value1: number, Value2: CFrame): ()
|
||||
Load()
|
||||
Types.WriteEVENT_UpdateTransform(Value1, Value2)
|
||||
local Buffer = buffer.create(SendCursor)
|
||||
buffer.copy(Buffer, 0, SendBuffer, 0, SendCursor)
|
||||
Unreliable:FireClient(Player, Buffer, SendInstances)
|
||||
end,
|
||||
FireAll = function(Value1: number, Value2: CFrame): ()
|
||||
Load()
|
||||
Types.WriteEVENT_UpdateTransform(Value1, Value2)
|
||||
local Buffer = buffer.create(SendCursor)
|
||||
buffer.copy(Buffer, 0, SendBuffer, 0, SendCursor)
|
||||
Unreliable:FireAllClients(Buffer, SendInstances)
|
||||
end,
|
||||
FireList = function(List: { Player }, Value1: number, Value2: CFrame): ()
|
||||
Load()
|
||||
Types.WriteEVENT_UpdateTransform(Value1, Value2)
|
||||
local Buffer = buffer.create(SendCursor)
|
||||
buffer.copy(Buffer, 0, SendBuffer, 0, SendCursor)
|
||||
for _, Player in List do
|
||||
Unreliable:FireClient(Player, Buffer, SendInstances)
|
||||
end
|
||||
end,
|
||||
FireExcept = function(Except: Player, Value1: number, Value2: CFrame): ()
|
||||
Load()
|
||||
Types.WriteEVENT_UpdateTransform(Value1, Value2)
|
||||
local Buffer = buffer.create(SendCursor)
|
||||
buffer.copy(Buffer, 0, SendBuffer, 0, SendCursor)
|
||||
for _, Player in Players:GetPlayers() do
|
||||
if Player == Except then
|
||||
continue
|
||||
end
|
||||
Unreliable:FireClient(Player, Buffer, SendInstances)
|
||||
end
|
||||
end,
|
||||
},
|
||||
SpawnMob = {
|
||||
Fire = function(Player: Player, Value1: number, Value2: CFrame, Value3: number): ()
|
||||
Load(PlayersMap[Player])
|
||||
Types.WriteEVENT_SpawnMob(Value1, Value2, Value3)
|
||||
PlayersMap[Player] = Save()
|
||||
end,
|
||||
FireAll = function(Value1: number, Value2: CFrame, Value3: number): ()
|
||||
Load()
|
||||
Types.WriteEVENT_SpawnMob(Value1, Value2, Value3)
|
||||
local Buffer, Size, Instances = SendBuffer, SendCursor, SendInstances
|
||||
for _, Player in Players:GetPlayers() do
|
||||
Load(PlayersMap[Player])
|
||||
local Position = Allocate(Size)
|
||||
buffer.copy(SendBuffer, Position, Buffer, 0, Size)
|
||||
table.move(Instances, 1, #Instances, #SendInstances + 1, SendInstances)
|
||||
PlayersMap[Player] = Save()
|
||||
end
|
||||
end,
|
||||
FireList = function(List: { Player }, Value1: number, Value2: CFrame, Value3: number): ()
|
||||
Load()
|
||||
Types.WriteEVENT_SpawnMob(Value1, Value2, Value3)
|
||||
local Buffer, Size, Instances = SendBuffer, SendCursor, SendInstances
|
||||
for _, Player in List do
|
||||
Load(PlayersMap[Player])
|
||||
local Position = Allocate(Size)
|
||||
buffer.copy(SendBuffer, Position, Buffer, 0, Size)
|
||||
table.move(Instances, 1, #Instances, #SendInstances + 1, SendInstances)
|
||||
PlayersMap[Player] = Save()
|
||||
end
|
||||
end,
|
||||
FireExcept = function(Except: Player, Value1: number, Value2: CFrame, Value3: number): ()
|
||||
Load()
|
||||
Types.WriteEVENT_SpawnMob(Value1, Value2, Value3)
|
||||
local Buffer, Size, Instances = SendBuffer, SendCursor, SendInstances
|
||||
for _, Player in Players:GetPlayers() do
|
||||
if Player == Except then
|
||||
continue
|
||||
end
|
||||
Load(PlayersMap[Player])
|
||||
local Position = Allocate(Size)
|
||||
buffer.copy(SendBuffer, Position, Buffer, 0, Size)
|
||||
table.move(Instances, 1, #Instances, #SendInstances + 1, SendInstances)
|
||||
PlayersMap[Player] = Save()
|
||||
end
|
||||
end,
|
||||
},
|
||||
}
|
|
@ -4,10 +4,24 @@ local test = testkit.test()
|
|||
local CASE, TEST, FINISH, CHECK = test.CASE, test.TEST, test.FINISH, test.CHECK
|
||||
local observers_add = require("@addons/observers")
|
||||
|
||||
|
||||
TEST("addons/observers", function()
|
||||
local world = observers_add(jecs.world())
|
||||
|
||||
do CASE "Should work even if set after the component has been used"
|
||||
local A = world:component()
|
||||
|
||||
world:set(world:entity(), A, 1)
|
||||
local ran = false
|
||||
world:added(A, function()
|
||||
ran = true
|
||||
end)
|
||||
|
||||
local entity = world:entity()
|
||||
world:set(entity, A, 2)
|
||||
|
||||
CHECK(ran)
|
||||
end
|
||||
|
||||
do CASE "Should not override hook"
|
||||
local A = world:component()
|
||||
|
||||
|
@ -17,12 +31,12 @@ TEST("addons/observers", function()
|
|||
end
|
||||
|
||||
world:set(A, jecs.OnAdd, counter)
|
||||
world:set(world:entity(), A, true)
|
||||
CHECK(count == 1)
|
||||
world:added(A, counter)
|
||||
world:set(world:entity(), A, true)
|
||||
CHECK(count == 2)
|
||||
world:set(world:entity(), A, true)
|
||||
|
||||
CHECK(count == 3)
|
||||
CHECK(count == 4)
|
||||
end
|
||||
|
||||
do CASE "Ensure ordering between signals and observers"
|
||||
|
@ -33,10 +47,8 @@ TEST("addons/observers", function()
|
|||
local function counter()
|
||||
count += 1
|
||||
end
|
||||
world:observer({
|
||||
callback = counter,
|
||||
query = world:query(A, B),
|
||||
})
|
||||
|
||||
world:observer(world:query(A, B), counter)
|
||||
|
||||
world:added(A, counter)
|
||||
world:added(A, counter)
|
||||
|
@ -56,10 +68,8 @@ TEST("addons/observers", function()
|
|||
local function counter()
|
||||
count += 1
|
||||
end
|
||||
world:observer({
|
||||
query = world:query(A),
|
||||
callback = counter
|
||||
})
|
||||
|
||||
world:observer(world:query(A), counter)
|
||||
|
||||
local e = world:entity()
|
||||
world:set(e, A, true)
|
||||
|
@ -79,10 +89,7 @@ TEST("addons/observers", function()
|
|||
count += 1
|
||||
end
|
||||
|
||||
world:monitor({
|
||||
query = world:query(A),
|
||||
callback = counter
|
||||
})
|
||||
world:monitor(world:query(A), counter)
|
||||
|
||||
local e = world:entity()
|
||||
world:set(e, A, true)
|
||||
|
|
Loading…
Reference in a new issue