rewrite(phase4): implement Identifier, and small fix

This commit is contained in:
khtsly 2026-02-13 17:37:23 +07:00
parent e810c6e000
commit f6b0e62880
9 changed files with 115 additions and 88 deletions

2
.gitignore vendored
View file

@ -1,6 +1,6 @@
node_modules
docs/.vitepress/dist
docs/.vitepress/cache
wally.lock
*.lock
TestEZ
test.project.json

View file

@ -7,10 +7,10 @@ Let's try and play something with Warp!
local Schema = require(path.to.warp).Buffer.Schema
return {
Example = Schema.array(Schema.string()),
Ping = Schema.string(),
Pong = Schema.string(),
PingAll = Schema.string(),
Example = Schema.array(Schema.string),
Ping = Schema.string,
Pong = Schema.string,
PingAll = Schema.string,
}
```
```luau [Server]
@ -19,19 +19,19 @@ local Schemas = require(path.to.schemas)
-- Use schemas
for eventName, schema in Schemas do
Warp.useSchema(eventName, schema)
Warp.useSchema(eventName, schema)
end
Warp.Connect("Example", function(player, arg)
print(table.unpack(arg))
return "Hey!"
print(table.unpack(arg))
return "Hey!"
end)
Warp.Connect("Ping", function(player, ping)
if ping then
print("PING!")
Warp.Fire("Pong", true, player, "pong!") -- Fire to spesific player through reliable event
Warp.Fire("PingAll", true, "ey!") -- Fire to all clients through reliable event
end
if ping then
print("PING!")
Warp.Fire("Pong", true, player, "pong!") -- Fire to spesific player through reliable event
Warp.Fire("PingAll", true, "ey!") -- Fire to all clients through reliable event
end
end)
```
@ -42,33 +42,33 @@ local Schemas = require(path.to.schemas)
-- Use schemas
for eventName, schema in Schemas do
Warp.useSchema(eventName, schema)
Warp.useSchema(eventName, schema)
end
-- Connect the events
local connection1
connection1 = Warp.Connect("Pong", function(pong: boolean) -- we store the connection so we can disconnect it later
if pong then
print("PONG!")
end
if pong then
print("PONG!")
end
end)
Warp.Connect("PingAll", function(isPing: boolean)
if isPing then
print("I GET PINGED!")
end
if isPing then
print("I GET PINGED!")
end
end)
task.wait(3) -- lets wait a few seconds, let the server do the things first!
-- Try request a event from server!
print(Warp.Invoke("Example", 1, { "Hello!", `this is from: @{Players.LocalPlayer.Name}` }))
-- Do a ping & pong to server!
Warp.Fire("Ping", true, "ping!") -- we send through reliable event
task.wait(1) -- lets wait 1 seconds!
-- Disconnect All the events
connection1:DisconnectAll()
connection1:Disconnect()
-- or just disconnect spesific connection
Warp.Disconnect("PingAll")
Warp.DisconnectAll("PingAll")
-- Destroying/Deleting a Event?
Warp.Destroy("Pong")

View file

@ -1 +1 @@
{"name":"Warp","className":"ModuleScript","filePaths":["src/init.luau","default.project.json"],"children":[{"name":"Buffer","className":"ModuleScript","filePaths":["src/Buffer/init.luau"]},{"name":"Client","className":"ModuleScript","filePaths":["src/Client/init.luau"]},{"name":"Server","className":"ModuleScript","filePaths":["src/Server/init.luau"]},{"name":"Thread","className":"ModuleScript","filePaths":["src/Thread.luau"]}]}
{"name":"Warp","className":"ModuleScript","filePaths":["src/init.luau","default.project.json"],"children":[{"name":"Client","className":"ModuleScript","filePaths":["src/Client/init.luau"]},{"name":"Server","className":"ModuleScript","filePaths":["src/Server/init.luau"]},{"name":"Util","className":"Folder","children":[{"name":"Buffer","className":"ModuleScript","filePaths":["src/Util/Buffer/init.luau"]},{"name":"Thread","className":"ModuleScript","filePaths":["src/Util/Thread.luau"]},{"name":"Identifier","className":"ModuleScript","filePaths":["src/Util/Identifier.luau"]}]}]}

View file

@ -4,8 +4,9 @@
local Client = {}
local RunService = game:GetService("RunService")
local Thread = require("./Thread")
local Buffer = require("./Buffer")
local Thread = require("./Util/Thread")
local Buffer = require("./Util/Buffer")
local Identifier = require("./Util/Identifier")
local Event: RemoteEvent = script.Parent:WaitForChild("Event")
local UnreliableEvent: UnreliableRemoteEvent = script.Parent:WaitForChild("UnreliableEvent")
local deltaT: number, cycle: number = 0, 1 / 61
@ -16,26 +17,26 @@ type Connection = {
Disconnect: (self: Connection) -> (),
}
type Event = {
remote: string,
fn: (Player, ...any?) -> ...any?,
i: number,
c: (Player, ...any?) -> ...any?,
}
local queueEvent: { { any } } = {}
local queueUnreliableEvent: { { any } } = {}
local eventListeners: { Event } = {}
local eventSchemas: { [string]: Buffer.SchemaType } = {}
local eventSchemas: { [number]: Buffer.SchemaType } = {}
local pendingInvokes: { [string]: thread } = {}
local invokeId = 0
Client.useSchema = function(remoteName: string, schema: Buffer.SchemaType)
eventSchemas[remoteName] = schema
eventSchemas[Identifier(remoteName)] = schema
end
Client.Connect = function(remoteName: string, fn: (Player, ...any?) -> ...any?): Connection
local detail = {
remote = remoteName,
fn = fn,
i = Identifier(remoteName),
c = fn,
}
table.insert(eventListeners, detail)
return {
@ -73,40 +74,39 @@ Client.Wait = function(remoteName: string): (number, ...any?)
end
Client.DisconnectAll = function(remoteName: string)
local id = Identifier(remoteName)
for idx = #eventListeners, 1, -1 do
if eventListeners[idx].remote == remoteName then
if eventListeners[idx].i == id then
table.remove(eventListeners, idx)
end
end
end
Client.Destroy = function(remoteName: string)
Client.DisconnectAll(remoteName)
end
Client.Destroy = Client.DisconnectAll
Client.Fire = function(remoteName: string, reliable: boolean, ...: any?)
table.insert(reliable and queueEvent or queueUnreliableEvent, {
remoteName,
Identifier(remoteName),
{ ... } :: any,
})
end
Client.Invoke = function(remoteName: string, timeout: number?, ...: any?): ...any?
invokeId += 1
local id, thread = `{invokeId}`, coroutine.running()
local reqid, thread = `{invokeId}`, coroutine.running()
pendingInvokes[id] = thread
pendingInvokes[reqid] = thread
task.delay(timeout or 2, function()
local pending = pendingInvokes[id]
local pending = pendingInvokes[reqid]
if not pending then
return
end
task.spawn(pending, nil)
pendingInvokes[id] = nil
pendingInvokes[reqid] = nil
end)
table.insert(queueEvent, {
"\0",
{ remoteName, id, { ... } :: any } :: any,
0,
{ Identifier(remoteName), reqid :: any, { ... } :: any } :: any,
})
return coroutine.yield()
end
@ -121,7 +121,7 @@ if RunService:IsClient() then
local remote = content[1]
local content = content[2]
if handleInvokes then
if remote == "\1" then
if remote == 0 then
local id = content[1]
local results = content[2]
local pending = pendingInvokes[id]
@ -131,7 +131,7 @@ if RunService:IsClient() then
end
continue
end
if remote == "\0" then
if remote == 1 then
if #eventListeners == 0 then
continue
end
@ -139,11 +139,11 @@ if RunService:IsClient() then
local id = content[2]
local args = content[3]
for _, connection in eventListeners do
if connection.remote == remoteName then
if connection.i == remoteName then
Thread(function()
local results = { connection.fn(table.unpack(args)) }
local results = { connection.c(table.unpack(args)) }
table.insert(queueEvent, {
"\1",
1,
{ id, results } :: any,
})
end)
@ -157,10 +157,10 @@ if RunService:IsClient() then
continue
end
for _, connection in eventListeners do
if connection.remote ~= remote then
if connection.i ~= remote then
continue
end
Thread(connection.fn, table.unpack(content))
Thread(connection.c, table.unpack(content))
end
end
end

View file

@ -5,8 +5,9 @@ local Server = {}
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Thread = require("./Thread")
local Buffer = require("./Buffer")
local Thread = require("./Util/Thread")
local Buffer = require("./Util/Buffer")
local Identifier = require("./Util/Identifier")
local Event: RemoteEvent = script.Parent:WaitForChild("Event")
local UnreliableEvent: UnreliableRemoteEvent = script.Parent:WaitForChild("UnreliableEvent")
local deltaT: number, cycle: number = 0, 1 / 61
@ -17,8 +18,8 @@ type Connection = {
Disconnect: (self: Connection) -> (),
}
type Event = {
remote: string,
fn: (Player, ...any?) -> ...any?,
i: number,
c: (Player, ...any?) -> ...any?,
}
local queueEvent: {
@ -28,20 +29,20 @@ local queueUnreliableEvent: {
[Player]: { { any } },
} = {}
local eventListeners: { Event } = {}
local eventSchemas: { [string]: Buffer.SchemaType } = {}
local eventSchemas: { [number]: Buffer.SchemaType } = {}
local players_ready: { Player } = {}
local pendingInvokes: { [string]: thread } = {}
local invokeId = 0
Server.useSchema = function(remoteName: string, schema: Buffer.SchemaType)
eventSchemas[remoteName] = schema
eventSchemas[Identifier(remoteName)] = schema
end
Server.Connect = function(remoteName: string, fn: (Player, ...any?) -> ...any?): Connection
local detail = {
remote = remoteName,
fn = fn,
i = Identifier(remoteName),
c = fn,
}
table.insert(eventListeners, detail)
return {
@ -79,16 +80,18 @@ Server.Wait = function(remoteName: string): (number, ...any?)
end
Server.DisconnectAll = function(remoteName: string)
local id = Identifier(remoteName)
if not id then
return
end
for idx = #eventListeners, 1, -1 do
if eventListeners[idx].remote == remoteName then
if eventListeners[idx].i == id then
table.remove(eventListeners, idx)
end
end
end
Server.Destroy = function(remoteName: string)
Server.DisconnectAll(remoteName)
end
Server.Destroy = Server.DisconnectAll
Server.Fire = function(remoteName: string, reliable: boolean, player: Player, ...: any?)
local targetQueue = reliable and queueEvent or queueUnreliableEvent
@ -96,7 +99,7 @@ Server.Fire = function(remoteName: string, reliable: boolean, player: Player, ..
targetQueue[player] = {} :: any
end
table.insert(targetQueue[player], {
remoteName,
Identifier(remoteName),
{ ... } :: any,
})
end
@ -116,23 +119,23 @@ end
Server.Invoke = function(remoteName: string, player: Player, timeout: number?, ...: any?): ...any?
invokeId += 1
local id, thread = `{invokeId}`, coroutine.running()
local reqId, thread = `{invokeId}`, coroutine.running()
pendingInvokes[id] = thread
pendingInvokes[reqId] = thread
task.delay(timeout or 2, function()
local pending = pendingInvokes[id]
local pending = pendingInvokes[reqId]
if not pending then
return
end
task.spawn(pending, nil)
pendingInvokes[id] = nil
pendingInvokes[reqId] = nil
end)
if not queueEvent[player] then
queueEvent[player] = {} :: any
end
table.insert(queueEvent[player], {
"\0",
{ remoteName, id, { ... } :: any } :: any,
0,
{ Identifier(remoteName), reqId :: any, { ... } :: any } :: any,
})
return coroutine.yield()
end
@ -147,7 +150,7 @@ if RunService:IsServer() then
local remote = content[1]
local content = content[2]
if handleInvokes then
if remote == "\1" then
if remote == 1 then
local id = content[1]
local results = content[2]
local pending = pendingInvokes[id]
@ -157,7 +160,7 @@ if RunService:IsServer() then
end
continue
end
if remote == "\0" then
if remote == 0 then
if #eventListeners == 0 then
continue
end
@ -165,14 +168,14 @@ if RunService:IsServer() then
local id = content[2]
local args = content[3]
for _, connection in eventListeners do
if connection.remote == remoteName then
if connection.i == remoteName then
Thread(function()
local results = { connection.fn(table.unpack(args)) }
local results = { connection.c(player, table.unpack(args)) }
if not queueEvent[player] then
queueEvent[player] = {} :: any
end
table.insert(queueEvent[player], {
"\1",
0,
{ id, results } :: any,
})
end)
@ -186,10 +189,10 @@ if RunService:IsServer() then
continue
end
for _, connection in eventListeners do
if connection.remote ~= remote then
if connection.i ~= remote then
continue
end
Thread(connection.fn, player, table.unpack(content))
Thread(connection.c, player, table.unpack(content))
end
end
end

View file

@ -1504,14 +1504,14 @@ local function readStrict(buf: buffer, cursor: number, s: SchemaType, refs: { an
return reader(buf, cursor, refs)
end
local function writeEvents(w: Writer, events: { { any } }, schemas: { [string]: SchemaType })
local function writeEvents(w: Writer, events: { { any } }, schemas: { [number]: SchemaType })
local count = #events
wVarUInt(w, count)
for _, event in events do
local remote = event[1]
local id = event[1]
local args = event[2]
packValue(w, remote)
local schema = schemas[remote]
wByte(w, id)
local schema = schemas[id]
if schema then
packStrict(w, schema, args[1])
else
@ -1520,15 +1520,15 @@ local function writeEvents(w: Writer, events: { { any } }, schemas: { [string]:
end
end
local function readEvents(buf: buffer, refs: { any }?, schemas: { [string]: SchemaType }): { { any } }
local function readEvents(buf: buffer, refs: { any }?, schemas: { [number]: SchemaType }): { { any } }
local pos, count = 0, 0
count, pos = readVarUInt(buf, pos)
local events = table.create(count)
for i = 1, count do
local remote
remote, pos = unpackValue(buf, pos, refs)
local args
local schema = schemas[remote]
local id: number = buffer.readu8(buf, pos)
pos += 1
local args: any
local schema = schemas[id]
if schema then
local val
val, pos = readStrict(buf, pos, schema, refs)
@ -1536,7 +1536,7 @@ local function readEvents(buf: buffer, refs: { any }?, schemas: { [string]: Sche
else
args, pos = unpackValue(buf, pos, refs)
end
events[i] = { remote, args }
events[i] = { id, args }
end
return events
end

24
src/Util/Identifier.luau Normal file
View file

@ -0,0 +1,24 @@
--!optimize 2
--!native
--@EternityDev
local BITS: number = 8
local MAX_VALUE: number = bit32.lshift(1, BITS) - 1
local cache: { [string]: number } = {}
local hash: number = game.PlaceVersion + 5381
return function(name: string): number
local cached = cache[name]
if cached then
return cached
end
local b: number = hash
for i = 1, #name do
b = bit32.bxor(bit32.lshift(hash, 5) + b, string.byte(name, i))
end
local reduced = bit32.band(b, MAX_VALUE)
cache[name] = reduced
return reduced
end

View file

@ -16,7 +16,7 @@ end
local Client = require("@self/Client")
local Server = require("@self/Server")
local Buffer = require("@self/Buffer")
local Buffer = require("@self/Util/Buffer")
--[[
@class Remote