From f6b0e6288065cceed937addad12cafb08b918478 Mon Sep 17 00:00:00 2001 From: khtsly Date: Fri, 13 Feb 2026 17:37:23 +0700 Subject: [PATCH] rewrite(phase4): implement Identifier, and small fix --- .gitignore | 2 +- docs/guide/example.md | 46 +++++++++++++-------------- sourcemap.json | 2 +- src/Client/init.luau | 52 +++++++++++++++---------------- src/Server/init.luau | 55 +++++++++++++++++---------------- src/{ => Util}/Buffer/init.luau | 20 ++++++------ src/Util/Identifier.luau | 24 ++++++++++++++ src/{ => Util}/Thread.luau | 0 src/init.luau | 2 +- 9 files changed, 115 insertions(+), 88 deletions(-) rename src/{ => Util}/Buffer/init.luau (99%) create mode 100644 src/Util/Identifier.luau rename src/{ => Util}/Thread.luau (100%) diff --git a/.gitignore b/.gitignore index 22b6f82..9cba0bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ node_modules docs/.vitepress/dist docs/.vitepress/cache -wally.lock +*.lock TestEZ test.project.json \ No newline at end of file diff --git a/docs/guide/example.md b/docs/guide/example.md index ed60f6c..ca309df 100644 --- a/docs/guide/example.md +++ b/docs/guide/example.md @@ -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") diff --git a/sourcemap.json b/sourcemap.json index abdd90d..d55fdfc 100644 --- a/sourcemap.json +++ b/sourcemap.json @@ -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"]}]} \ No newline at end of file +{"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"]}]}]} \ No newline at end of file diff --git a/src/Client/init.luau b/src/Client/init.luau index 0dc162c..ea8d2ec 100644 --- a/src/Client/init.luau +++ b/src/Client/init.luau @@ -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 diff --git a/src/Server/init.luau b/src/Server/init.luau index 5eb6771..cef4110 100644 --- a/src/Server/init.luau +++ b/src/Server/init.luau @@ -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 diff --git a/src/Buffer/init.luau b/src/Util/Buffer/init.luau similarity index 99% rename from src/Buffer/init.luau rename to src/Util/Buffer/init.luau index 6985c78..0fea325 100644 --- a/src/Buffer/init.luau +++ b/src/Util/Buffer/init.luau @@ -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 diff --git a/src/Util/Identifier.luau b/src/Util/Identifier.luau new file mode 100644 index 0000000..c2697b1 --- /dev/null +++ b/src/Util/Identifier.luau @@ -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 diff --git a/src/Thread.luau b/src/Util/Thread.luau similarity index 100% rename from src/Thread.luau rename to src/Util/Thread.luau diff --git a/src/init.luau b/src/init.luau index fb57156..a8835f6 100644 --- a/src/init.luau +++ b/src/init.luau @@ -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