From 4f521aad0cb2bfa02b7b05b0e68fb0ebdc1b84f4 Mon Sep 17 00:00:00 2001 From: Holly Date: Sun, 14 Jul 2024 14:23:30 +0100 Subject: [PATCH] yep that's a chunk alright --- mc/bufferstuff.lua | 37 +++--- mc/packet_names.lua | 9 ++ mc/server.lua | 289 ++++++++++++++++++++++++++------------------ mc/string_pack.lua | 1 + 4 files changed, 202 insertions(+), 134 deletions(-) create mode 100644 mc/packet_names.lua create mode 100644 mc/string_pack.lua diff --git a/mc/bufferstuff.lua b/mc/bufferstuff.lua index c6a8f47..9572f57 100644 --- a/mc/bufferstuff.lua +++ b/mc/bufferstuff.lua @@ -1,4 +1,5 @@ local util = require("util") +local sp = require("string_pack") -- quick and dirty BIG ENDIAN ONLY port of bufferstuff to lua @@ -24,7 +25,7 @@ function bufferStuff.Writer:new() end function bufferStuff.Writer:toBuffer() - return string.pack(table.concat(self.typeList, ""), table.unpack(self.dataValues)) + return sp.pack(table.concat(self.typeList, ""), table.unpack(self.dataValues)) end function bufferStuff.Writer:writeByte(value) @@ -132,30 +133,30 @@ function bufferStuff.Reader:new(data) end function bufferStuff.Reader:readByte() - local value = string.unpack(">b", string.sub(self.bufferData, self.offset, self.offset + 1)) + local value = sp.unpack(">b", string.sub(self.bufferData, self.offset, self.offset + 1)) self.offset = self.offset + 1 return value end function bufferStuff.Reader:readBool() - local value = string.unpack(">b", string.sub(self.bufferData, self.offset, self.offset + 1)) + local value = sp.unpack(">b", string.sub(self.bufferData, self.offset, self.offset + 1)) self.offset = self.offset + 1 return value >= 1 end function bufferStuff.Reader:readUByte() - local value = string.unpack(">B", string.sub(self.bufferData, self.offset, self.offset + 1)) + local value = sp.unpack(">B", string.sub(self.bufferData, self.offset, self.offset + 1)) self.offset = self.offset + 1 return value end function bufferStuff.Reader:readShortString() - local length = string.unpack(">B", string.sub(self.bufferData, self.offset, self.offset + 1)) + local length = sp.unpack(">B", string.sub(self.bufferData, self.offset, self.offset + 1)) self.offset = self.offset + 1 local text = "" for i = 1, length do - text = text .. string.char(string.unpack(">B", string.sub(self.bufferData, self.offset, self.offset + 1))) + text = text .. string.char(sp.unpack(">B", string.sub(self.bufferData, self.offset, self.offset + 1))) self.offset = self.offset + 1 end @@ -163,60 +164,60 @@ function bufferStuff.Reader:readShortString() end function bufferStuff.Reader:readShort() - local value = string.unpack(">h", string.sub(self.bufferData, self.offset, self.offset + 2)) + local value = sp.unpack(">h", string.sub(self.bufferData, self.offset, self.offset + 2)) self.offset = self.offset + 2 return value end function bufferStuff.Reader:readUShort() - local value = string.unpack(">h", string.sub(self.bufferData, self.offset, self.offset + 2)) + local value = sp.unpack(">h", string.sub(self.bufferData, self.offset, self.offset + 2)) self.offset = self.offset + 2 return value end function bufferStuff.Reader:readInt() - local value = string.unpack(">i", string.sub(self.bufferData, self.offset, self.offset + 4)) + local value = sp.unpack(">i", string.sub(self.bufferData, self.offset, self.offset + 4)) self.offset = self.offset + 4 return value end function bufferStuff.Reader:readUInt() - local value = string.unpack(">I", string.sub(self.bufferData, self.offset, self.offset + 4)) + local value = sp.unpack(">I", string.sub(self.bufferData, self.offset, self.offset + 4)) self.offset = self.offset + 4 return value end function bufferStuff.Reader:readLong() - local value = string.unpack(">l", string.sub(self.bufferData, self.offset, self.offset + 8)) + local value = sp.unpack(">l", string.sub(self.bufferData, self.offset, self.offset + 8)) self.offset = self.offset + 8 return value end function bufferStuff.Reader:readULong() - local value = string.unpack(">L", string.sub(self.bufferData, self.offset, self.offset + 8)) + local value = sp.unpack(">L", string.sub(self.bufferData, self.offset, self.offset + 8)) self.offset = self.offset + 8 return value end function bufferStuff.Reader:readFloat() - local value = string.unpack(">f", string.sub(self.bufferData, self.offset, self.offset + 4)) + local value = sp.unpack(">f", string.sub(self.bufferData, self.offset, self.offset + 4)) self.offset = self.offset + 4 return value end function bufferStuff.Reader:readDouble() - local value = string.unpack(">d", string.sub(self.bufferData, self.offset, self.offset + 8)) + local value = sp.unpack(">d", string.sub(self.bufferData, self.offset, self.offset + 8)) self.offset = self.offset + 8 return value end function bufferStuff.Reader:readString() - local length = string.unpack(">h", string.sub(self.bufferData, self.offset, self.offset + 2)) + local length = sp.unpack(">h", string.sub(self.bufferData, self.offset, self.offset + 2)) self.offset = self.offset + 2 local text = "" for i = 1, length do - text = text .. string.char(string.unpack(">B", string.sub(self.bufferData, self.offset, self.offset + 1))) + text = text .. string.char(sp.unpack(">B", string.sub(self.bufferData, self.offset, self.offset + 1))) self.offset = self.offset + 1 end @@ -224,12 +225,12 @@ function bufferStuff.Reader:readString() end function bufferStuff.Reader:readString16() - local length = string.unpack(">h", string.sub(self.bufferData, self.offset, self.offset + 2)) + local length = sp.unpack(">h", string.sub(self.bufferData, self.offset, self.offset + 2)) self.offset = self.offset + 2 local text = "" for i = 1, length do - text = text .. string.char(string.unpack(">h", string.sub(self.bufferData, self.offset, self.offset + 2))) + text = text .. string.char(sp.unpack(">h", string.sub(self.bufferData, self.offset, self.offset + 2))) self.offset = self.offset + 2 end diff --git a/mc/packet_names.lua b/mc/packet_names.lua new file mode 100644 index 0000000..56d19ec --- /dev/null +++ b/mc/packet_names.lua @@ -0,0 +1,9 @@ +local packet = require("packet") + +local packet_names = {} + +for key, value in pairs(packet) do + packet_names[value] = key +end + +return packet_names \ No newline at end of file diff --git a/mc/server.lua b/mc/server.lua index 1be36b5..6ff39cd 100644 --- a/mc/server.lua +++ b/mc/server.lua @@ -1,14 +1,22 @@ ---local chunk = require("chunk") +local chunk = require("chunk") local packet = require("packet") +local packet_names = require("packet_names") local bufferStuff = require("bufferstuff") ---local libDeflate = require("LibDeflate") --- local tempChunk = chunk:new() --- for x = 0, 15 do --- for z = 0, 15 do --- tempChunk:setBlock(2, x, 62, z) --- end --- end +local libDeflate = require("LibDeflate") + +local monitor = peripheral.wrap("back") +local oldTerm = nil +if monitor ~= nil then + oldTerm = term.redirect(monitor) +end + +local tempChunk = chunk:new() +for x = 0, 15 do + for z = 0, 15 do + tempChunk:setBlock(2, x, 62, z) + end +end local ws = nil @@ -19,136 +27,185 @@ local playerYaw = 0 local playerPitch = 0 local onGround = false +term.clear() +if monitor ~= nil then + monitor.setTextScale(2) +end + +term.setCursorPos(2, 2) +term.write("Minecraft Server lmao this is dumb") + local function doWebsocket() - ws = http.websocket("ws://localhost:25567") + if monitor ~= nil then + ws = http.websocket("wss://ws.eusv.net/mcinmc") + else + ws = http.websocket("ws://localhost:25567") + end while true do local b, bv, data, bx = os.pullEvent("websocket_message") local reader = bufferStuff.Reader:new(data) - local packetId = reader:readByte() - if packetId == packet.KeepAlive then - ws.send(data, true) - elseif packetId == packet.LoginRequest then - local protocolVersion = reader:readInt() - local username = reader:readString16() - local mapSeed = reader:readLong() - local dimension = reader:readByte() + while reader.offset <= #reader.bufferData do + local packetId = reader:readByte() + print(packet_names[packetId], packetId, reader.offset, #reader.bufferData) + if packetId == packet.LoginRequest then + local protocolVersion = reader:readInt() + local username = reader:readString16() + local mapSeed = reader:readLong() + local dimension = reader:readByte() - local writer = bufferStuff.Writer:new() - -- Login packet - writer:writeByte(packet.LoginRequest) -- Packet ID - writer:writeInt(1) -- Entity ID - writer:writeString16("") -- Login Token - writer:writeLong(0) -- Seed - writer:writeByte(0) -- Dimension - ws.send(writer:toBuffer(), true) + local writer = bufferStuff.Writer:new() + -- Login packet + writer:writeByte(packet.LoginRequest) -- Packet ID + writer:writeInt(1) -- Entity ID + writer:writeString16("") -- Login Token + writer:writeLong(0) -- Seed + writer:writeByte(0) -- Dimension + ws.send(writer:toBuffer(), true) - -- teleport - -- local teleportWriter = bufferStuff.Writer:new() - -- teleportWriter:writeByte(packet.PlayerPositionLook) - -- teleportWriter:writeDouble(8) - -- teleportWriter:writeDouble(75) - -- teleportWriter:writeDouble(75.62) - -- teleportWriter:writeDouble(8) - -- teleportWriter:writeFloat(0) - -- teleportWriter:writeFloat(0) - -- teleportWriter:writeBool(true) - -- ws.send(teleportWriter:toBuffer(), true) + -- teleport + local teleportWriter = bufferStuff.Writer:new() + teleportWriter:writeByte(packet.PlayerPosition) + teleportWriter:writeDouble(8) + teleportWriter:writeDouble(75) + teleportWriter:writeDouble(75.62) + teleportWriter:writeDouble(8) + teleportWriter:writeBool(false) + ws.send(teleportWriter:toBuffer(), true) - -- pre chunk - local preChunkWriter = bufferStuff.Writer:new() - preChunkWriter:writeByte(packet.PreChunk) - preChunkWriter:writeInt(0) - preChunkWriter:writeInt(0) - preChunkWriter:writeBool(true) - ws.send(preChunkWriter:toBuffer(), true) + -- pre chunk + local preChunkWriter = bufferStuff.Writer:new() + preChunkWriter:writeByte(packet.PreChunk) + preChunkWriter:writeInt(0) + preChunkWriter:writeInt(0) + preChunkWriter:writeBool(true) + ws.send(preChunkWriter:toBuffer(), true) - local blockWriter = bufferStuff.Writer:new() - for x = 0, 15 do - for z = 0, 15 do - blockWriter:writeByte(packet.BlockChange) - blockWriter:writeInt(x) - blockWriter:writeByte(62) - blockWriter:writeInt(z) - blockWriter:writeByte(2) - blockWriter:writeByte(0) + local blockWriter = bufferStuff.Writer:new() + for x = 0, 15 do + for z = 0, 15 do + blockWriter:writeByte(packet.BlockChange) + blockWriter:writeInt(x) + blockWriter:writeByte(62) + blockWriter:writeInt(z) + blockWriter:writeByte(20) + blockWriter:writeByte(0) + end end + ws.send(blockWriter:toBuffer(), true) + + -- chunk + -- TODO: Speed this up, it's SLOW as balls + local chunkWriter = bufferStuff.Writer:new() + local chunkData = bufferStuff.Writer:new() + for _, id in ipairs(tempChunk.blocks) do + chunkData:writeUByte(id) + end + for _, id in ipairs(tempChunk.metadata) do + chunkData:writeUByte(id) + end + for _, id in ipairs(tempChunk.blockLight) do + chunkData:writeUByte(id) + end + for _, id in ipairs(tempChunk.skyLight) do + chunkData:writeUByte(id) + end + chunkWriter:writeByte(packet.MapChunk) + chunkWriter:writeInt(0) + chunkWriter:writeShort(0) + chunkWriter:writeInt(0) + chunkWriter:writeByte(15) + chunkWriter:writeByte(127) + chunkWriter:writeByte(15) + + local compressedData = libDeflate:CompressZlib(chunkData:toBuffer()) + + chunkWriter:writeInt(#compressedData) + local chunkPacket = chunkWriter:toBuffer() + ws.send(chunkPacket .. compressedData, true) + + elseif packetId == packet.Handshake then + local username = reader:readString16() + + local writer = bufferStuff.Writer:new() + writer:writeByte(packet.Handshake) + writer:writeString16("-") + ws.send(writer:toBuffer(), true) + elseif packetId == packet.Player then + onGround = reader:readBool() + elseif packetId == packet.PlayerLook then + playerYaw = reader:readFloat() + playerPitch = reader:readFloat() + onGround = reader:readBool() + elseif packetId == packet.PlayerPosition then + playerX = reader:readDouble() + playerY = reader:readDouble() + reader:readDouble() + playerZ = reader:readDouble() + onGround = reader:readBool() + elseif packetId == packet.PlayerPositionLook then + playerX = reader:readDouble() + playerY = reader:readDouble() + reader:readDouble() + playerZ = reader:readDouble() + playerYaw = reader:readFloat() + playerPitch = reader:readFloat() + onGround = reader:readBool() end - ws.send(blockWriter:toBuffer(), true) - - -- chunk - -- local chunkWriter = bufferStuff.Writer:new() - -- local chunkData = bufferStuff.Writer:new() - -- for _, id in ipairs(tempChunk.blocks) do - -- chunkData:writeUByte(id) - -- end - -- for _, id in ipairs(tempChunk.metadata) do - -- chunkData:writeUByte(id) - -- end - -- for _, id in ipairs(tempChunk.blockLight) do - -- chunkData:writeUByte(id) - -- end - -- for _, id in ipairs(tempChunk.skyLight) do - -- chunkData:writeUByte(id) - -- end - -- chunkWriter:writeByte(packet.MapChunk) - -- chunkWriter:writeInt(0) - -- chunkWriter:writeShort(0) - -- chunkWriter:writeInt(0) - -- chunkWriter:writeByte(15) - -- chunkWriter:writeByte(127) - -- chunkWriter:writeByte(15) - - -- local compressedData = libDeflate:CompressDeflate(chunkData:toBuffer()) - - -- chunkWriter:writeInt(#compressedData) - -- local chunkPacket = chunkWriter:toBuffer() - -- ws.send(chunkPacket .. compressedData, true) - - elseif packetId == packet.Handshake then - local username = reader:readString16() - - local writer = bufferStuff.Writer:new() - writer:writeByte(packet.Handshake) - writer:writeString16("-") - ws.send(writer:toBuffer(), true) - elseif packetId == packet.Player then - onGround = reader:readBool() - elseif packetId == packet.PlayerPosition then - playerX = reader:readDouble() - playerY = reader:readDouble() - reader:readDouble() - playerZ = reader:readDouble() - onGround = reader:readBool() - elseif packetId == packet.PlayerPositionLook then - playerX = reader:readDouble() - playerY = reader:readDouble() - reader:readDouble() - playerZ = reader:readDouble() - playerYaw = reader:readFloat() - playerPitch = reader:readFloat() - onGround = reader:readBool() - elseif packet == packet.PlayerLook then - playerYaw = reader:readFloat() - playerPitch = reader:readFloat() - onGround = reader:readBool() end end end -local keepAliveWriter = bufferStuff.Writer:new() -keepAliveWriter:writeByte(packet.KeepAlive) -local keepAlivePacket = keepAliveWriter:toBuffer() +local keepalivePacket = bufferStuff.Writer:new() +keepalivePacket:writeByte(packet.KeepAlive) +keepalivePacket = keepalivePacket:toBuffer() +local d = false +local timeLastFrame = os.clock() local tickCount = 0 local function serverTickLoop() while true do - --print(playerX, playerY, playerZ, playerYaw, playerPitch, onGround) + if tickCount % 20 == 0 then + if ws ~= nil then + ws.send(keepalivePacket) + end + end - os.sleep(0.1) + os.sleep(0.05) tickCount = tickCount + 1 + + -- Term output + -- term.setCursorPos(2, 4) + -- term.write("X: " .. playerX .. " ") + -- term.setCursorPos(2, 5) + -- term.write("Y: " .. playerY .. " ") + -- term.setCursorPos(2, 6) + -- term.write("Z: " .. playerZ .. " ") + -- term.setCursorPos(2, 8) + -- term.write("Yaw: " .. playerYaw .. " ") + -- term.setCursorPos(2, 9) + -- term.write("Pitch: " .. playerPitch .. " ") + -- term.setCursorPos(2, 11) + -- term.write("On Ground: " .. tostring(onGround) .. " ") + -- term.setCursorPos(2, 13) + -- term.write("Tick Counter: " .. tostring(tickCount) .. " TPS: " .. tostring(math.floor((1 / (os.clock() - timeLastFrame)) + 0.5))) + + -- if tickCount % 20 == 0 then + -- d = not d + -- if not d then + -- term.write(" - ") + -- else + -- term.write(" | ") + -- end + -- end + + timeLastFrame = os.clock() end end -parallel.waitForAll(doWebsocket, serverTickLoop) \ No newline at end of file +parallel.waitForAll(doWebsocket, serverTickLoop) + +if monitor ~= nil then + term.redirect(oldTerm) +end \ No newline at end of file diff --git a/mc/string_pack.lua b/mc/string_pack.lua new file mode 100644 index 0000000..67f45de --- /dev/null +++ b/mc/string_pack.lua @@ -0,0 +1 @@ +local a=dofile"/rom/modules/main/cc/expect.lua".expect;local b={BIG_ENDIAN=1,LITTLE_ENDIAN=2}local c={b=1,B=1,h=1,H=1,l=1,L=1,j=1,J=1,T=1}local d={b=1,B=1,x=1,h=2,H=2,f=4,j=4,J=4,l=8,L=8,T=8,d=8,n=8}local function e(f)if f%1>=0.5 then return math.ceil(f)else return math.floor(f)end end;local function g(h)if h==0 then return 0 elseif h==-0 then return 0x80000000 elseif h==math.huge then return 0x7F800000 elseif h==-math.huge then return 0xFF800000 end;local i,j=math.frexp(h)if j>127 or j<-126 then error("number out of range",3)end;j,i=j+126,e((math.abs(i)-0.5)*0x1000000)if i>0x7FFFFF then j=j+1 end;return bit32.bor(h<0 and 0x80000000 or 0,bit32.lshift(bit32.band(j,0xFF),23),bit32.band(i,0x7FFFFF))end;local function k(h)if h==0 then return 0,0 elseif h==-0 then return 0x80000000,0 elseif h==math.huge then return 0x7FF00000,0 elseif h==-math.huge then return 0xFFF00000,0 end;local i,j=math.frexp(h)if j>1023 or j<-1022 then error("number out of range",3)end;j,i=j+1022,e((math.abs(i)-0.5)*0x20000000000000)if i>0xFFFFFFFFFFFFF then j=j+1 end;return bit32.bor(h<0 and 0x80000000 or 0,bit32.lshift(bit32.band(j,0x7FF),20),bit32.band(i/0x100000000,0xFFFFF)),bit32.band(i,0xFFFFFFFF)end;local function l(m)if m==0 then return 0 elseif m==0x80000000 then return-0 elseif m==0x7F800000 then return math.huge elseif m==0xFF800000 then return-math.huge end;local i,j=bit32.band(m,0x7FFFFF),bit32.band(bit32.rshift(m,23),0xFF)j,i=j-126,i/0x1000000+0.5;local f=math.ldexp(i,j)return bit32.btest(m,0x80000000)and-f or f end;local function n(o,p)if o==0 and p==0 then return 0 elseif o==0x80000000 and p==0 then return-0 elseif o==0x7FF00000 and p==0 then return math.huge elseif o==0xFFF00000 and p==0 then return-math.huge end;local i,j=bit32.band(o,0xFFFFF)*0x100000000+bit32.band(p,0xFFFFFFFF),bit32.band(bit32.rshift(o,20),0x7FF)j,i=j-1022,i/0x20000000000000+0.5;local f=math.ldexp(i,j)return bit32.btest(o,0x80000000)and-f or f end;local function q(r,s,t,u,v,w,x)local y=0;if u%math.min(s,v)~=0 and v>1 then local z=0;while u%math.min(s,v)~=0 and z8 then for z=0,s-9 do t[u+z]=x and r>=2^(s*8-1)~=0 and 0xFF or 0;A=A+1;y=y+1 end end;for z=A,s-1 do t[u+z]=bit32.band(bit32.rshift(r,(s-z-1)*8),0xFF)y=y+1 end else for z=0,math.min(s,8)-1 do t[u+z]=r/2^(z*8)%256;y=y+1 end;for z=8,s-1 do t[u+z]=x and r>=2^(s*8-1)~=0 and 0xFF or 0;y=y+1 end end;return y end;local function B(C,u,s,w,v,x)local D,E=0,0;if u%math.min(s,v)~=0 and v>1 then for z=0,v-1 do if u%math.min(s,v)==0 then break end;u=u+1;E=E+1 end end;for z=0,s-1 do D=D+C:byte(u+z)*2^((w==b.BIG_ENDIAN and s-z-1 or z)*8)E=E+1 end;if x and D>=2^(s*8-1)then D=D-2^(s*8)end;return D,E end;local function F(G,v)local H=d[G]or 0;if v>1 and H%v~=0 then H=H+v-H%v end;return H end;local function I(...)local J=a(1,...,"string")local w=b.LITTLE_ENDIAN;local v=1;local K=1;local L=2;local t={}local z=1;while z<=#J do local M=J:sub(z,z)z=z+1;if M=='='or M=='<'then w=b.LITTLE_ENDIAN elseif M=='>'then w=b.BIG_ENDIAN elseif M=='!'then local s=-1;while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=math.max(s,0)*10+tonumber(J:sub(z,z))z=z+1 end;if s>16 or s==0 then error(string.format("integral size (%d) out of limits [1,16]",s),2)elseif s==-1 then v=4 else v=s end elseif c[M]then local r=a(L,select(L,...),"number")L=L+1;if r>=math.pow(2,F(M,0)*8-(M:match("%l")and 1 or 0))or r<(M:match("%l")and-math.pow(2,F(M,0)*8-1)or 0)then error(string.format("bad argument #%d to 'pack' (integer overflow)",L-1),2)end;K=K+q(r,F(M,0),t,K,v,w,false)elseif M:lower()=='i'then local x=M=='i'local s=-1;while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=math.max(s,0)*10+tonumber(J:sub(z,z))z=z+1 end;if s>16 or s==0 then error(string.format("integral size (%d) out of limits [1,16]",s),2)elseif v>1 and(s~=1 and s~=2 and s~=4 and s~=8 and s~=16)then error("bad argument #1 to 'pack' (format asks for alignment not power of 2)",2)elseif s==-1 then s=4 end;local r=a(L,select(L,...),"number")L=L+1;if r>=math.pow(2,s*8-(M:match("%l")and 1 or 0))or r<(M:match("%l")and-math.pow(2,s*8-1)or 0)then error(string.format("bad argument #%d to 'pack' (integer overflow)",L-1),2)end;K=K+q(r,s,t,K,v,w,x)elseif M=='f'then local h=a(L,select(L,...),"number")L=L+1;local m=g(h)if K%math.min(4,v)~=0 and v>1 then for N=0,v-1 do if K%math.min(4,v)==0 then break end;t[K]=0;K=K+1 end end;for N=0,3 do t[K+(w==b.BIG_ENDIAN and 3-N or N)]=bit32.band(bit32.rshift(m,N*8),0xFF)end;K=K+4 elseif M=='d'or M=='n'then local h=a(L,select(L,...),"number")L=L+1;local o,p=k(h)if K%math.min(8,v)~=0 and v>1 then for N=0,v-1 do if K%math.min(8,v)==0 then break end;t[K]=0;K=K+1 end end;for N=0,3 do t[K+(w==b.BIG_ENDIAN and 7-N or N)]=bit32.band(bit32.rshift(p,N*8),0xFF)end;for N=4,7 do t[K+(w==b.BIG_ENDIAN and 7-N or N)]=bit32.band(bit32.rshift(o,(N-4)*8),0xFF)end;K=K+8 elseif M=='c'then local s=0;if z>#J or not J:sub(z,z):match("%d")then error("missing size for format option 'c'",2)end;while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if K+s0xFFFFFFFF then error("bad argument #1 to 'pack' (format result too large)",2)end;local C=a(L,select(L,...),"string")L=L+1;if#C>s then error(string.format("bad argument #%d to 'pack' (string longer than given size)",L-1),2)end;if s>0 then for N=0,s-1 do t[K+N]=C:byte(N+1)or 0 end;K=K+s end elseif M=='z'then local C=a(L,select(L,...),"string")L=L+1;for O in C:gmatch"."do if O=='\0'then error(string.format("bad argument #%d to 'pack' (string contains zeros)",L-1),2)end end;for N=0,#C-1 do t[K+N]=C:byte(N+1)end;t[K+#C]=0;K=K+#C+1 elseif M=='s'then local s=0;while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if s>16 then error(string.format("integral size (%d) out of limits [1,16]",s),2)elseif s==0 then s=4 end;local C=a(L,select(L,...),"string")L=L+1;if#C>=math.pow(2,s*8)then error(string.format("bad argument #%d to 'pack' (string length does not fit in given size)",L-1),2)end;q(#C,s,t,K,1,w,false)for N=s,#C+s-1 do t[K+N]=C:byte(N-s+1)or 0 end;K=K+#C+s elseif M=='x'then t[K]=0;K=K+1 elseif M=='X'then if z>=#J then error("invalid next option for option 'X'",2)end;local s=0;local M=J:sub(z,z)z=z+1;if M:lower()=='i'then while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if s>16 or s==0 then error(string.format("integral size (%d) out of limits [1,16]",s),2)end else s=F(M,0)end;if s<1 then error("invalid next option for option 'X'",2)end;if K%math.min(s,v)~=0 and v>1 then for N=1,v do if K%math.min(s,v)==0 then break end;t[K]=0;K=K+1 end end elseif M~=' 'then error(string.format("invalid format option '%s'",M),2)end end;return string.char(table.unpack(t))end;local function P(J)local K=0;local v=1;local z=1;while z<=#J do local M=J:sub(z,z)z=z+1;if M=='!'then local s=0;while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if s>16 then error(string.format("integral size (%d) out of limits [1,16]",s),2)elseif s==0 then v=4 else v=s end elseif c[M]then local s=F(M,0)if K%math.min(s,v)~=0 and v>1 then for N=1,v do if K%math.min(s,v)==0 then break end;K=K+1 end end;K=K+s elseif M:lower()=='i'then local s=0;while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if s>16 then error(string.format("integral size (%d) out of limits [1,16]",s))elseif v>1 and(s~=1 and s~=2 and s~=4 and s~=8 and s~=16)then error("bad argument #1 to 'pack' (format asks for alignment not power of 2)",2)elseif s==0 then s=4 end;if K%math.min(s,v)~=0 and v>1 then for N=1,v do if K%math.min(s,v)==0 then break end;K=K+1 end end;K=K+s elseif M=='f'then if K%math.min(4,v)~=0 and v>1 then for N=1,v do if K%math.min(4,v)==0 then break end;K=K+1 end end;K=K+4 elseif M=='d'or M=='n'then if K%math.min(8,v)~=0 and v>1 then for N=1,v do if K%math.min(8,v)==0 then break end;K=K+1 end end;K=K+8 elseif M=='c'then local s=0;if z>#J or not J:sub(z,z):match("%d")then error("missing size for format option 'c'",2)end;while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if K+s0x7FFFFFFF then error("bad argument #1 to 'packsize' (format result too large)",2)end;K=K+s elseif M=='x'then K=K+1 elseif M=='X'then if z>=#J then error("invalid next option for option 'X'",2)end;local s=0;local M=J:sub(z,z)z=z+1;if M:lower()=='i'then while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if s>16 or s==0 then error(string.format("integral size (%d) out of limits [1,16]",s),2)end else s=F(M,0)end;if s<1 then error("invalid next option for option 'X'",2)end;if K%math.min(s,v)~=0 and v>1 then for N=1,v do if K%math.min(s,v)==0 then break end;K=K+1 end end elseif M=='s'or M=='z'then error("bad argument #1 to 'packsize' (variable-length format)",2)elseif M~=' 'and M~='<'and M~='>'and M~='='then error(string.format("invalid format option '%s'",M),2)end end;return K end;local function Q(J,C,K)a(1,J,"string")a(2,C,"string")a(3,K,"number","nil")if K then if K<0 then K=#C+K elseif K==0 then error("bad argument #3 to 'unpack' (initial position out of string)",2)end;if K>#C or K<0 then error("bad argument #3 to 'unpack' (initial position out of string)",2)end else K=1 end;local w=b.LITTLE_ENDIAN;local v=1;local H={}local z=1;while z<=#J do local M=J:sub(z,z)z=z+1;if M=='<'or M=='='then w=b.LITTLE_ENDIAN elseif M=='>'then w=b.BIG_ENDIAN elseif M=='!'then local s=0;while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if s>16 then error(string.format("integral size (%d) out of limits [1,16]",s))elseif s==0 then v=4 else v=s end elseif c[M]then if K+F(M,0)>#C+1 then error("data string too short",2)end;local R,S=B(C,K,F(M,0),w,v,M:match("%l")~=nil)H[#H+1]=R;K=K+S elseif M:lower()=='i'then local x=M=='i'local s=0;while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if s>16 then error(string.format("integral size (%d) out of limits [1,16]",s),2)elseif s>8 then error(string.format("%d-byte integer does not fit into Lua Integer",s),2)elseif s==0 then s=4 end;if K+s>#C+1 then error("data string too short",2)end;local R,S=B(C,K,s,w,v,x)H[#H+1]=R;K=K+S elseif M=='f'then if K%math.min(4,v)~=0 and v>1 then for N=1,v do if K%math.min(4,v)==0 then break end;K=K+1 end end;if K+4>#C+1 then error("data string too short",2)end;local R=B(C,K,4,w,v,false)H[#H+1]=l(R)K=K+4 elseif M=='d'or M=='n'then if K%math.min(8,v)~=0 and v>1 then for N=1,v do if K%math.min(8,v)==0 then break end;K=K+1 end end;if K+8>#C+1 then error("data string too short",2)end;local o,p=0,0;for N=0,3 do o=bit32.bor(o,bit32.lshift(C:byte(K+N),(w==b.BIG_ENDIAN and 3-N or N)*8))end;for N=0,3 do p=bit32.bor(p,bit32.lshift(C:byte(K+N+4),(w==b.BIG_ENDIAN and 3-N or N)*8))end;if w==b.LITTLE_ENDIAN then o,p=p,o end;H[#H+1]=n(o,p)K=K+8 elseif M=='c'then local s=0;if z>#J or not J:sub(z,z):match("%d")then error("missing size for format option 'c'",2)end;while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)")end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if K+s>#C+1 then error("data string too short",2)end;H[#H+1]=C:sub(K,K+s-1)K=K+s elseif M=='z'then local s=0;while C:byte(K+s)~=0 do s=s+1;if K+s>#C then error("unfinished string for format 'z'",2)end end;H[#H+1]=C:sub(K,K+s-1)K=K+s+1 elseif M=='s'then local s=0;while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if s>16 then error(string.format("integral size (%d) out of limits [1,16]",s),2)elseif s==0 then s=4 end;if K+s>#C+1 then error("data string too short",2)end;local r,T=B(C,K,s,w,v,false)K=K+T;if K+r>#C+1 then error("data string too short",2)end;H[#H+1]=C:sub(K,K+r-1)K=K+r elseif M=='x'then K=K+1 elseif M=='X'then if z>=#J then error("invalid next option for option 'X'",2)end;local s=0;local M=J:sub(z,z)z=z+1;if M:lower()=='i'then while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if s>16 or s==0 then error(string.format("integral size (%d) out of limits [1,16]",s),2)elseif s==-1 then s=4 end else s=F(M,0)end;if s<1 then error("invalid next option for option 'X'",2)end;if K%math.min(s,v)~=0 and v>1 then for N=1,v do if K%math.min(s,v)==0 then break end;K=K+1 end end elseif M~=' 'then error(string.format("invalid format option '%s'",M),2)end end;H[#H+1]=K;return table.unpack(H)end;return{pack=I,packsize=P,unpack=Q} \ No newline at end of file