mirror of
https://github.com/imezx/Warp.git
synced 2026-06-02 12:18:32 +00:00
feat: XOR encoding
This commit is contained in:
parent
39143db390
commit
ce8cfeef5e
4 changed files with 146 additions and 38 deletions
|
|
@ -1 +1 @@
|
|||
{"name":"warp","className":"ModuleScript","filePaths":["src/init.luau","default.project.json"],"children":[{"name":"Client","className":"ModuleScript","filePaths":["src/Client/init.luau"]},{"name":"Replication","className":"ModuleScript","filePaths":["src/Replication/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":"Identifier","className":"ModuleScript","filePaths":["src/Util/Identifier.luau"]},{"name":"Thread","className":"ModuleScript","filePaths":["src/Util/Thread.luau"]}]}]}
|
||||
{"name":"warp","className":"ModuleScript","filePaths":["src/init.luau","default.project.json"],"children":[{"name":"Client","className":"ModuleScript","filePaths":["src/Client/init.luau"]},{"name":"Replication","className":"ModuleScript","filePaths":["src/Replication/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":"Identifier","className":"ModuleScript","filePaths":["src/Util/Identifier.luau"]},{"name":"Thread","className":"ModuleScript","filePaths":["src/Util/Thread.luau"]},{"name":"Xor","className":"ModuleScript","filePaths":["src/Util/Xor.luau"]}]}]}
|
||||
|
|
@ -3,10 +3,12 @@
|
|||
--@EternityDev
|
||||
local Client = {}
|
||||
|
||||
local RunService = game:GetService("RunService")
|
||||
local Thread = require("./Util/Thread")
|
||||
local Buffer = require("./Util/Buffer")
|
||||
local Replication = require("./Replication")
|
||||
local Xor = require("./Util/Xor")
|
||||
|
||||
local RunService = game:GetService("RunService")
|
||||
local Event: RemoteEvent = script.Parent:WaitForChild("Event")
|
||||
local UnreliableEvent: UnreliableRemoteEvent = script.Parent:WaitForChild("UnreliableEvent")
|
||||
local deltaT: number, cycle: number = 0, 1 / 61
|
||||
|
|
@ -26,6 +28,7 @@ local queueUnreliableEvent: { { any } } = {}
|
|||
local eventListeners: { [number]: { Event } } = {}
|
||||
local eventSchemas: { [number]: Buffer.SchemaType } = {}
|
||||
|
||||
local lastDelta: { [number]: { any } } = {}
|
||||
local pendingInvokes: { [string]: thread } = {}
|
||||
local invokeId = 0
|
||||
|
||||
|
|
@ -165,13 +168,12 @@ end
|
|||
|
||||
if RunService:IsClient() then
|
||||
local function processIncoming(b: buffer, ref: { Instance }?, handleInvokes: boolean)
|
||||
if type(b) ~= "buffer" then
|
||||
return
|
||||
end
|
||||
local contents = Buffer.readEvents(b, ref, eventSchemas)
|
||||
if type(b) ~= "buffer" then return end
|
||||
local decoded: buffer = handleInvokes and Xor.decodeClient(b) or b
|
||||
local contents = Buffer.readEvents(decoded, ref, eventSchemas)
|
||||
for _, content in contents do
|
||||
local remote = content[1]
|
||||
local content = content[2]
|
||||
local args = content[2]
|
||||
if handleInvokes then
|
||||
if remote == 0 then
|
||||
local id = content[1]
|
||||
|
|
@ -184,17 +186,12 @@ if RunService:IsClient() then
|
|||
continue
|
||||
end
|
||||
if remote == 1 then
|
||||
local remoteName = content[1]
|
||||
local id = content[2]
|
||||
local args = content[3]
|
||||
local remoteName, id, rargs = args[1], args[2], args[3]
|
||||
local connections = eventListeners[remoteName]
|
||||
if connections and #connections > 0 then
|
||||
Thread(function()
|
||||
local results = { connections[1].c(table.unpack(args)) }
|
||||
table.insert(queueEvent, {
|
||||
1,
|
||||
{ id, results } :: any,
|
||||
})
|
||||
local results = { connections[1].c(table.unpack(rargs)) }
|
||||
table.insert(queueEvent, { 1, { id, results } :: any })
|
||||
end)
|
||||
end
|
||||
continue
|
||||
|
|
@ -203,7 +200,7 @@ if RunService:IsClient() then
|
|||
local connections = eventListeners[remote]
|
||||
if connections then
|
||||
for _, connection in connections do
|
||||
Thread(connection.c, table.unpack(content))
|
||||
Thread(connection.c, table.unpack(args))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -219,21 +216,20 @@ if RunService:IsClient() then
|
|||
|
||||
RunService.PostSimulation:Connect(function(d: number)
|
||||
deltaT += d
|
||||
if deltaT < cycle then
|
||||
return
|
||||
end
|
||||
if deltaT < cycle then return end
|
||||
deltaT = 0
|
||||
|
||||
-- reliable
|
||||
--reliable
|
||||
if #queueEvent > 0 then
|
||||
Buffer.writeEvents(writer, queueEvent, eventSchemas)
|
||||
do
|
||||
local buf, ref = Buffer.buildWithRefs(writer)
|
||||
local encoded = Xor.encodeClient(buf)
|
||||
Buffer.reset(writer)
|
||||
if not ref or #ref == 0 then
|
||||
Event:FireServer(buf)
|
||||
Event:FireServer(encoded)
|
||||
else
|
||||
Event:FireServer(buf, ref)
|
||||
Event:FireServer(encoded, ref)
|
||||
end
|
||||
end
|
||||
table.clear(queueEvent)
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@
|
|||
--@EternityDev
|
||||
local Server = {}
|
||||
|
||||
local Players = game:GetService("Players")
|
||||
local RunService = game:GetService("RunService")
|
||||
local Thread = require("./Util/Thread")
|
||||
local Buffer = require("./Util/Buffer")
|
||||
local Identifier = require("./Util/Identifier")
|
||||
local Replication = require("./Replication")
|
||||
local Xor = require("./Util/Xor")
|
||||
|
||||
local Players = game:GetService("Players")
|
||||
local RunService = game:GetService("RunService")
|
||||
local Event: RemoteEvent = script.Parent:WaitForChild("Event")
|
||||
local _repl: RemoteEvent = script.Parent:WaitForChild("_repl")
|
||||
local UnreliableEvent: UnreliableRemoteEvent = script.Parent:WaitForChild("UnreliableEvent")
|
||||
|
|
@ -198,14 +200,15 @@ if RunService:IsServer() then
|
|||
player_bytes[player] = bytes
|
||||
end
|
||||
|
||||
local contents = Buffer.readEvents(b, ref, eventSchemas)
|
||||
local decoded = handleInvokes and Xor.decodeServer(player, b) or b
|
||||
local contents = Buffer.readEvents(decoded, ref, eventSchemas)
|
||||
for _, content in contents do
|
||||
local remote = content[1]
|
||||
local content = content[2]
|
||||
local d = content[2]
|
||||
if handleInvokes then
|
||||
if remote == 1 then
|
||||
local id = content[1]
|
||||
local results = content[2]
|
||||
local id = d[1]
|
||||
local results = d[2]
|
||||
local pending = pendingInvokes[id]
|
||||
if pending then
|
||||
task.spawn(pending :: any, table.unpack(results))
|
||||
|
|
@ -217,9 +220,9 @@ if RunService:IsServer() then
|
|||
if #eventListeners == 0 then
|
||||
continue
|
||||
end
|
||||
local remoteName = content[1]
|
||||
local id = content[2]
|
||||
local args = content[3]
|
||||
local remoteName = d[1]
|
||||
local id = d[2]
|
||||
local args = d[3]
|
||||
local connections = eventListeners[remoteName]
|
||||
if connections and #connections > 0 then
|
||||
Thread(function()
|
||||
|
|
@ -239,7 +242,7 @@ if RunService:IsServer() then
|
|||
local connections = eventListeners[remote]
|
||||
if connections then
|
||||
for _, connection in connections do
|
||||
Thread(connection.c, player, table.unpack(content))
|
||||
Thread(connection.c, player, table.unpack(d))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -268,11 +271,12 @@ if RunService:IsServer() then
|
|||
Buffer.writeEvents(writer, content, eventSchemas)
|
||||
do
|
||||
local buf, ref = Buffer.buildWithRefs(writer)
|
||||
local encoded = Xor.encodeServer(player, buf)
|
||||
Buffer.reset(writer)
|
||||
if not ref or #ref == 0 then
|
||||
Event:FireClient(player, buf)
|
||||
Event:FireClient(player, encoded)
|
||||
else
|
||||
Event:FireClient(player, buf, ref)
|
||||
Event:FireClient(player, encoded, ref)
|
||||
end
|
||||
end
|
||||
player_bytes[player] = 0
|
||||
|
|
@ -324,6 +328,7 @@ if RunService:IsServer() then
|
|||
table.clear(queueUnreliableEvent[player])
|
||||
queueUnreliableEvent[player] = nil
|
||||
end
|
||||
Xor.remove(player)
|
||||
end)
|
||||
for _, player: Player in ipairs(Players:GetPlayers()) do
|
||||
onAdded(player)
|
||||
|
|
|
|||
107
src/Util/Xor.luau
Normal file
107
src/Util/Xor.luau
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
--!strict
|
||||
--!native
|
||||
--!optimize 2
|
||||
local Xor = {}
|
||||
|
||||
type XorEntry = { buf: buffer, len: number }
|
||||
|
||||
local serverPrev: { [Player]: XorEntry } = {}
|
||||
local clientPrev: { [Player]: XorEntry } = {}
|
||||
|
||||
local clientOutPrev: XorEntry? = nil
|
||||
local clientInPrev: XorEntry? = nil
|
||||
|
||||
local function xorApply(current: buffer, curLen: number, prev: buffer, prevLen: number): buffer
|
||||
local out = buffer.create(curLen)
|
||||
local common = if curLen < prevLen then curLen else prevLen
|
||||
|
||||
local wordEnd = (common // 4) * 4
|
||||
for off = 0, wordEnd - 4, 4 do
|
||||
buffer.writeu32(out, off, bit32.bxor(buffer.readu32(current, off), buffer.readu32(prev, off)))
|
||||
end
|
||||
for i = wordEnd, common - 1 do
|
||||
buffer.writeu8(out, i, bit32.bxor(buffer.readu8(current, i), buffer.readu8(prev, i)))
|
||||
end
|
||||
|
||||
if curLen > common then
|
||||
buffer.copy(out, common, current, common, curLen - common)
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
local function storeEntry(entry: XorEntry?, src: buffer, len: number): XorEntry
|
||||
local e: XorEntry = entry or { buf = buffer.create(len > 64 and len or 64), len = 0 }
|
||||
|
||||
if buffer.len(e.buf) < len then
|
||||
e.buf = buffer.create(len * 2)
|
||||
end
|
||||
|
||||
buffer.copy(e.buf, 0, src, 0, len)
|
||||
e.len = len
|
||||
return e
|
||||
end
|
||||
|
||||
function Xor.remove(player: Player)
|
||||
serverPrev[player] = nil
|
||||
clientPrev[player] = nil
|
||||
end
|
||||
|
||||
function Xor.encodeServer(player: Player, buf: buffer): buffer
|
||||
local len = buffer.len(buf)
|
||||
local entry = serverPrev[player]
|
||||
|
||||
if not entry then
|
||||
serverPrev[player] = storeEntry(nil, buf, len)
|
||||
return buf
|
||||
end
|
||||
|
||||
local encoded = xorApply(buf, len, entry.buf, entry.len)
|
||||
serverPrev[player] = storeEntry(entry, buf, len)
|
||||
return encoded
|
||||
end
|
||||
|
||||
function Xor.decodeClient(buf: buffer): buffer
|
||||
local len = buffer.len(buf)
|
||||
local prev = clientInPrev
|
||||
|
||||
if not prev then
|
||||
clientInPrev = storeEntry(nil, buf, len)
|
||||
return buf
|
||||
end
|
||||
|
||||
local recovered = xorApply(buf, len, prev.buf, prev.len)
|
||||
clientInPrev = storeEntry(prev, recovered, len)
|
||||
return recovered
|
||||
end
|
||||
|
||||
function Xor.encodeClient(buf: buffer): buffer
|
||||
local len = buffer.len(buf)
|
||||
local prev = clientOutPrev
|
||||
|
||||
if not prev then
|
||||
clientOutPrev = storeEntry(nil, buf, len)
|
||||
return buf
|
||||
end
|
||||
|
||||
local encoded = xorApply(buf, len, prev.buf, prev.len)
|
||||
clientOutPrev = storeEntry(prev, buf, len)
|
||||
return encoded
|
||||
end
|
||||
|
||||
function Xor.decodeServer(player: Player, buf: buffer): buffer
|
||||
local len = buffer.len(buf)
|
||||
local entry = clientPrev[player]
|
||||
|
||||
if not entry then
|
||||
clientPrev[player] = storeEntry(nil, buf, len)
|
||||
return buf
|
||||
end
|
||||
|
||||
local recovered = xorApply(buf, len, entry.buf, entry.len)
|
||||
clientPrev[player] = storeEntry(entry, recovered, len)
|
||||
return recovered
|
||||
end
|
||||
|
||||
Xor._apply = xorApply
|
||||
|
||||
return Xor
|
||||
Loading…
Reference in a new issue