From f2bafad199e1529ab32cb5115d188a864738e8ba Mon Sep 17 00:00:00 2001 From: loadstring1 <156520308+loadstring1@users.noreply.github.com> Date: Wed, 19 Nov 2025 23:14:22 +0100 Subject: [PATCH 1/5] add ReflectionService --- Modules/DefaultProperies.luau | 125 +++++----------------------------- 1 file changed, 17 insertions(+), 108 deletions(-) diff --git a/Modules/DefaultProperies.luau b/Modules/DefaultProperies.luau index 759a1d0..0754918 100644 --- a/Modules/DefaultProperies.luau +++ b/Modules/DefaultProperies.luau @@ -1,87 +1,26 @@ --- TODO: replace with ReflectionService when available +local sub=string.sub +local lower=string.lower +local insert=table.insert + +local ReflectionService=game:GetService("ReflectionService") +local security=SecurityCapabilities.fromCurrent() -local HttpService = game:GetService("HttpService") -local RawApiDump = script:FindFirstChildWhichIsA("Configuration") local IsApiInitialized = false local Classes = {} local DefaultProperties = {} DefaultProperties.InitializeApiDump = function() - local Dump = RawApiDump:GetAttribute("Dump") - local Decoded = HttpService:JSONDecode(Dump) - local DecodedClasses = Decoded.Classes - - for Position, Class in pairs(DecodedClasses) do - local Tags = Class.Tags - local Members = Class.Members - local ClassName = Class.Name - - -- filter out services - if Tags then - for _, Tag in pairs(Tags) do - if Tag == "Service" then - DecodedClasses[Position] = nil - continue - end - end - end - - -- filter out properties locked behind capabilities, filter out members that are not properties, filter out hidden properties, etc - if Members then - for MemberPosition, Property in pairs(Members) do - local Type = Property.MemberType - local Tags = Property.Tags - local Name = Property.Name - local NameFirstLetter = Name:sub(1, 1) - local Security = Property.Security - local SecurityType = type(Security) - - if Type ~= "Property" then - Members[MemberPosition] = nil - continue - end - - if Tags then - for _, Tag in pairs(Tags) do - if Tag == "NotScriptable" or Tag == "ReadOnly" then - Members[MemberPosition] = nil - continue - end - end - end - - -- filter out legacy property references - if NameFirstLetter == NameFirstLetter:lower() then - Members[MemberPosition] = nil - continue - end - - if SecurityType == "string" then - if Security ~= "None" then - Members[MemberPosition] = nil - continue - end - elseif SecurityType == "table" then - if Security.Read ~= "None" then -- we will include properties that can be read but not written - Members[MemberPosition] = nil - continue - end - end - end + for _, Class in ReflectionService:GetClasses({Security=security}) do + if Class.Permits.New==nil then continue end + + if typeof(Classes[Class.Name])~="table" then Classes[Class.Name]={} end + for _,Property in ReflectionService:GetPropertiesOfClass(Class.Name,{Security=security}) do + local NameFirstLetter = sub(Property.Name,1, 1) + if Property.Permits.Write==nil or NameFirstLetter==lower(NameFirstLetter) then continue end + insert(Classes[Class.Name],Property.Name) end end - - for _, Class in pairs(DecodedClasses) do - local Superclass = Class.Superclass - local Members = Class.Members - local ClassName = Class.Name - - Classes[ClassName] = { - Superclass = Superclass, - Members = Members - } - end - + IsApiInitialized = true return true @@ -92,37 +31,7 @@ DefaultProperties.IsApiInitialized = function() end DefaultProperties.GetPropertiesOfClass = function(ClassName) - local Properties = {} - local IterateThroughProperties - - IterateThroughProperties = function(ClassName) - local ClassInfo = Classes[ClassName] - - if ClassInfo then - local Members = ClassInfo.Members - local Superclass = ClassInfo.Superclass - - for Position, Property in pairs(Members) do - local Name = Property.Name - - if not table.find(Properties, Name) then -- todo: dont use table.find and table.insert - table.insert(Properties, Name) - end - end - - if Superclass then - IterateThroughProperties(Superclass) - end - end - end - - IterateThroughProperties(ClassName) - - return Properties -end - -DefaultProperties.GetClasses = function() - return Classes + return Classes[ClassName] end -return DefaultProperties +return DefaultProperties \ No newline at end of file From 65245ba2717f172748a0fa837635238ccd44c9c4 Mon Sep 17 00:00:00 2001 From: loadstring1 <156520308+loadstring1@users.noreply.github.com> Date: Thu, 20 Nov 2025 13:31:23 +0100 Subject: [PATCH 2/5] revert reflectionservice (pull request denied) --- Modules/DefaultProperies.luau | 125 +++++++++++++++++++++++++++++----- 1 file changed, 108 insertions(+), 17 deletions(-) diff --git a/Modules/DefaultProperies.luau b/Modules/DefaultProperies.luau index 0754918..759a1d0 100644 --- a/Modules/DefaultProperies.luau +++ b/Modules/DefaultProperies.luau @@ -1,26 +1,87 @@ -local sub=string.sub -local lower=string.lower -local insert=table.insert - -local ReflectionService=game:GetService("ReflectionService") -local security=SecurityCapabilities.fromCurrent() +-- TODO: replace with ReflectionService when available +local HttpService = game:GetService("HttpService") +local RawApiDump = script:FindFirstChildWhichIsA("Configuration") local IsApiInitialized = false local Classes = {} local DefaultProperties = {} DefaultProperties.InitializeApiDump = function() - for _, Class in ReflectionService:GetClasses({Security=security}) do - if Class.Permits.New==nil then continue end - - if typeof(Classes[Class.Name])~="table" then Classes[Class.Name]={} end - for _,Property in ReflectionService:GetPropertiesOfClass(Class.Name,{Security=security}) do - local NameFirstLetter = sub(Property.Name,1, 1) - if Property.Permits.Write==nil or NameFirstLetter==lower(NameFirstLetter) then continue end - insert(Classes[Class.Name],Property.Name) + local Dump = RawApiDump:GetAttribute("Dump") + local Decoded = HttpService:JSONDecode(Dump) + local DecodedClasses = Decoded.Classes + + for Position, Class in pairs(DecodedClasses) do + local Tags = Class.Tags + local Members = Class.Members + local ClassName = Class.Name + + -- filter out services + if Tags then + for _, Tag in pairs(Tags) do + if Tag == "Service" then + DecodedClasses[Position] = nil + continue + end + end + end + + -- filter out properties locked behind capabilities, filter out members that are not properties, filter out hidden properties, etc + if Members then + for MemberPosition, Property in pairs(Members) do + local Type = Property.MemberType + local Tags = Property.Tags + local Name = Property.Name + local NameFirstLetter = Name:sub(1, 1) + local Security = Property.Security + local SecurityType = type(Security) + + if Type ~= "Property" then + Members[MemberPosition] = nil + continue + end + + if Tags then + for _, Tag in pairs(Tags) do + if Tag == "NotScriptable" or Tag == "ReadOnly" then + Members[MemberPosition] = nil + continue + end + end + end + + -- filter out legacy property references + if NameFirstLetter == NameFirstLetter:lower() then + Members[MemberPosition] = nil + continue + end + + if SecurityType == "string" then + if Security ~= "None" then + Members[MemberPosition] = nil + continue + end + elseif SecurityType == "table" then + if Security.Read ~= "None" then -- we will include properties that can be read but not written + Members[MemberPosition] = nil + continue + end + end + end end end - + + for _, Class in pairs(DecodedClasses) do + local Superclass = Class.Superclass + local Members = Class.Members + local ClassName = Class.Name + + Classes[ClassName] = { + Superclass = Superclass, + Members = Members + } + end + IsApiInitialized = true return true @@ -31,7 +92,37 @@ DefaultProperties.IsApiInitialized = function() end DefaultProperties.GetPropertiesOfClass = function(ClassName) - return Classes[ClassName] + local Properties = {} + local IterateThroughProperties + + IterateThroughProperties = function(ClassName) + local ClassInfo = Classes[ClassName] + + if ClassInfo then + local Members = ClassInfo.Members + local Superclass = ClassInfo.Superclass + + for Position, Property in pairs(Members) do + local Name = Property.Name + + if not table.find(Properties, Name) then -- todo: dont use table.find and table.insert + table.insert(Properties, Name) + end + end + + if Superclass then + IterateThroughProperties(Superclass) + end + end + end + + IterateThroughProperties(ClassName) + + return Properties +end + +DefaultProperties.GetClasses = function() + return Classes end -return DefaultProperties \ No newline at end of file +return DefaultProperties From 1bb1a23a2fe43c04aa9c08af99177453f17f1c81 Mon Sep 17 00:00:00 2001 From: loadstring1 <156520308+loadstring1@users.noreply.github.com> Date: Thu, 20 Nov 2025 13:33:21 +0100 Subject: [PATCH 3/5] copy paste new base94 --- Modules/Encoders/Base94.luau | 236 ++++++++++++++++------------------- 1 file changed, 106 insertions(+), 130 deletions(-) diff --git a/Modules/Encoders/Base94.luau b/Modules/Encoders/Base94.luau index 4ad8d6a..2c339dd 100644 --- a/Modules/Encoders/Base94.luau +++ b/Modules/Encoders/Base94.luau @@ -1,152 +1,128 @@ ---!native --!optimize 2 +--!native +--!strict --- written by @WalletOverflow on roblox - --- i wonder if declaring a variable pointing to the functions directly would make any difference or if luau automatically optimizes it this way... --- well at least now we are no longer indexing to the tables like the buffer table during run time :smirk: --- i love 2 ms optimizations!! -local string_char = string.char -local string_byte = string.byte -local table_concat = table.concat -local math_floor = math.floor -local bit32_lshift = bit32.lshift -local bit32_rshift = bit32.rshift -local bit32_bor = bit32.bor -local bit32_band = bit32.band -local buffer_create = buffer.create -local buffer_len = buffer.len -local buffer_readu8 = buffer.readu8 -local buffer_writeu8 = buffer.writeu8 - -local alphabet = (function() - local chars = {} - - for code = 32, 127 do - if code ~= 34 and code ~= 92 then - chars[#chars + 1] = string_char(code) - end - end +local DIRECT_LOOKUP = {} +local BIT_MASK = {} - return table_concat(chars) -end)() +local ENCODE_MAP = {} +local DECODE_MAP = {} -local lookupValueToCharacter = buffer_create(94) -local lookupCharacterToValue = buffer_create(256) -local powersOf94 = {94^4, 94^3, 94^2, 94^1, 1} +local Base94 = {} -for i = 0, 93 do - local charCode = string_byte(alphabet, i + 1) +local j = 0 +for i = 32, 127 do + if i ~= 34 and i ~= 92 then + ENCODE_MAP[j] = i + DECODE_MAP[i] = j + j += 1 + end +end - buffer_writeu8(lookupValueToCharacter, i, charCode) - buffer_writeu8(lookupCharacterToValue, charCode, i) +for entry = 0, 8835 do + DIRECT_LOOKUP[entry] = bit32.bor( + bit32.lshift(ENCODE_MAP[entry // 94], 8), + ENCODE_MAP[entry % 94] + ) + + BIT_MASK[entry] = (bit32.band(entry, 8191) < 644) and 14 or 13 end -local function encode(input: buffer): buffer - local inLen = buffer_len(input) - local full = math_floor(inLen / 4) - local rem = inLen % 4 - local outLen = full * 5 + (rem > 0 and rem + 1 or 0) - local out = buffer_create(outLen) - - -- full 4-byte chunks - for ci = 0, full - 1 do - local baseIn = ci * 4 - local chunk = bit32_bor( - bit32_lshift(buffer_readu8(input, baseIn), 24), - bit32_lshift(buffer_readu8(input, baseIn + 1), 16), - bit32_lshift(buffer_readu8(input, baseIn + 2), 8), - buffer_readu8(input, baseIn + 3) - ) - - -- decompose into five 0–93 digits and write directly to the buffer, - -- big-endian (most significant digit first) - local baseOut = ci * 5 - local tempChunk = chunk - for i = 4, 0, -1 do - local digit = tempChunk % 94 - tempChunk = math_floor(tempChunk / 94) - buffer_writeu8(out, baseOut + i, buffer_readu8(lookupValueToCharacter, digit)) +function Base94.encode(input: buffer): buffer + local length = buffer.len(input) + + local output = buffer.create(math.ceil(length * 1.25) + 2) + + local counter = 0 + local bits = 0 + local offset = 0 + + for i = 0, length - 1 do + counter = bit32.bor(counter, bit32.lshift(buffer.readu8(input, i), bits)) + bits += 8 + + if bits > 13 then + local entry = bit32.band(counter, 8191) + + if entry < 644 then + entry = bit32.band(counter, 16383) + counter = bit32.rshift(counter, 14) + bits -= 14 + else + counter = bit32.rshift(counter, 13) + bits -= 13 + end + + buffer.writeu16(output, offset, DIRECT_LOOKUP[entry]) + offset += 2 end end - - if rem > 0 then - local baseIn = full * 4 - local chunk = 0 - - if rem >= 1 then chunk = bit32_bor(bit32_lshift(chunk, 8), buffer_readu8(input, baseIn)) end - if rem >= 2 then chunk = bit32_bor(bit32_lshift(chunk, 8), buffer_readu8(input, baseIn + 1)) end - if rem >= 3 then chunk = bit32_bor(bit32_lshift(chunk, 8), buffer_readu8(input, baseIn + 2)) end - - local baseOut = full * 5 - local requiredChars = rem + 1 - - for i = requiredChars - 1, 0, -1 do - local digit = chunk % 94 - chunk = math_floor(chunk / 94) - buffer_writeu8(out, baseOut + i, buffer_readu8(lookupValueToCharacter, digit)) + + if bits > 0 then + if bits > 7 or counter > 93 then + buffer.writeu16(output, offset, DIRECT_LOOKUP[counter]) + offset += 2 + else + buffer.writeu8(output, offset, ENCODE_MAP[counter]) + offset += 1 end end - return out -end + local sliced = buffer.create(offset) + buffer.copy(sliced, 0, output, 0, offset) -local function decode(input: buffer): buffer - local inLen = buffer_len(input) - local full = math_floor(inLen / 5) - local rem = inLen % 5 - if rem == 1 then rem = 0 end -- 1-char tail is invalid padding - local outLen = full * 4 + (rem > 0 and rem - 1 or 0) - local out = buffer_create(outLen) - - -- full 5-char chunks - for ci = 0, full - 1 do - local baseIn = ci * 5 - local value = 0 - - -- reconstruct number using horner's method for efficiency :smirk: - for i = 0, 4 do - local c = buffer_readu8(input, baseIn + i) - local d = buffer_readu8(lookupCharacterToValue, c) - value = value * 94 + d - end + return sliced +end - -- extract b1..b4 - local baseOut = ci * 4 - for i = 0, 3 do - local shift = 24 - (i * 8) - local byte = bit32_band(bit32_rshift(value, shift), 0xFF) - buffer_writeu8(out, baseOut + i, byte) +function Base94.decode(Input: buffer): buffer + local InputLength = buffer.len(Input) + local output = buffer.create(InputLength) + + local offset = 0 + local counter = 0 + local bits = 0 + local entry = -1 + + for i = 0, InputLength - 1 do + local val = DECODE_MAP[buffer.readu8(Input, i)] + if entry == -1 then + entry = val + else + entry += val * 94 + + counter = bit32.bor(counter, bit32.lshift(entry, bits)) + bits += BIT_MASK[entry] + + if bits >= 16 then + buffer.writeu16(output, offset, counter) + offset += 2 + counter = bit32.rshift(counter, 16) + bits -= 16 + elseif bits >= 8 then + buffer.writeu8(output, offset, counter) + offset += 1 + counter = bit32.rshift(counter, 8) + bits -= 8 + end + + entry = -1 end end - - -- partial tail - if rem > 0 then - local baseIn = full * 5 - local value = 0 - - -- reconstruct the number from the big-endian digits - for i = 0, rem - 1 do - local c = buffer_readu8(input, baseIn + i) - local d = buffer_readu8(lookupCharacterToValue, c) - value = value * 94 + d - end - - local requiredBytes = rem - 1 - local baseOut = full * 4 - - -- extract the original bytes from the reconstructed number, big-endian - for i = requiredBytes - 1, 0, -1 do - local byte = bit32_band(value, 0xFF) - value = bit32_rshift(value, 8) - buffer_writeu8(out, baseOut + i, byte) + + if entry ~= -1 then + counter = bit32.bor(counter, bit32.lshift(entry, bits)) + if bit32.bor(counter, 0) ~= 0 then + if bits + (entry < 94 and 7 or 13) >= 8 then + buffer.writeu8(output, offset, counter) + offset += 1 + end end end + + local sliced = buffer.create(offset) + buffer.copy(sliced, 0, output, 0, offset) - return out + return sliced end -return { - encode = encode, - decode = decode, -} +return Base94 From a7eebefddd251cdbbb3091cb5765f0b42d36b5c3 Mon Sep 17 00:00:00 2001 From: loadstring1 <156520308+loadstring1@users.noreply.github.com> Date: Thu, 20 Nov 2025 13:43:08 +0100 Subject: [PATCH 4/5] skip internal metamethods with direct function localization --- Modules/Encoders/Base94.luau | 67 +++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/Modules/Encoders/Base94.luau b/Modules/Encoders/Base94.luau index 2c339dd..95d8f54 100644 --- a/Modules/Encoders/Base94.luau +++ b/Modules/Encoders/Base94.luau @@ -2,6 +2,17 @@ --!native --!strict +local buffer_len=buffer.len +local buffer_create=buffer.create +local buffer_readu8=buffer.readu8 +local buffer_writeu16=buffer.writeu16 +local buffer_writeu8=buffer.writeu8 +local buffer_copy=buffer.copy +local bit32_bor=bit32.bor +local bit32_lshift=bit32.lshift +local bit32_rshift=bit32.rshift +local bit32_band=bit32.band + local DIRECT_LOOKUP = {} local BIT_MASK = {} @@ -20,63 +31,63 @@ for i = 32, 127 do end for entry = 0, 8835 do - DIRECT_LOOKUP[entry] = bit32.bor( - bit32.lshift(ENCODE_MAP[entry // 94], 8), + DIRECT_LOOKUP[entry] = bit32_bor( + bit32_lshift(ENCODE_MAP[entry // 94], 8), ENCODE_MAP[entry % 94] ) - BIT_MASK[entry] = (bit32.band(entry, 8191) < 644) and 14 or 13 + BIT_MASK[entry] = (bit32_band(entry, 8191) < 644) and 14 or 13 end function Base94.encode(input: buffer): buffer - local length = buffer.len(input) + local length = buffer_len(input) - local output = buffer.create(math.ceil(length * 1.25) + 2) + local output = buffer_create(math.ceil(length * 1.25) + 2) local counter = 0 local bits = 0 local offset = 0 for i = 0, length - 1 do - counter = bit32.bor(counter, bit32.lshift(buffer.readu8(input, i), bits)) + counter = bit32_bor(counter, bit32_lshift(buffer_readu8(input, i), bits)) bits += 8 if bits > 13 then - local entry = bit32.band(counter, 8191) + local entry = bit32_band(counter, 8191) if entry < 644 then - entry = bit32.band(counter, 16383) - counter = bit32.rshift(counter, 14) + entry = bit32_band(counter, 16383) + counter = bit32_rshift(counter, 14) bits -= 14 else - counter = bit32.rshift(counter, 13) + counter = bit32_rshift(counter, 13) bits -= 13 end - buffer.writeu16(output, offset, DIRECT_LOOKUP[entry]) + buffer_writeu16(output, offset, DIRECT_LOOKUP[entry]) offset += 2 end end if bits > 0 then if bits > 7 or counter > 93 then - buffer.writeu16(output, offset, DIRECT_LOOKUP[counter]) + buffer_writeu16(output, offset, DIRECT_LOOKUP[counter]) offset += 2 else - buffer.writeu8(output, offset, ENCODE_MAP[counter]) + buffer_writeu8(output, offset, ENCODE_MAP[counter]) offset += 1 end end - local sliced = buffer.create(offset) - buffer.copy(sliced, 0, output, 0, offset) + local sliced = buffer_create(offset) + buffer_copy(sliced, 0, output, 0, offset) return sliced end function Base94.decode(Input: buffer): buffer - local InputLength = buffer.len(Input) - local output = buffer.create(InputLength) + local InputLength = buffer_len(Input) + local output = buffer_create(InputLength) local offset = 0 local counter = 0 @@ -84,24 +95,24 @@ function Base94.decode(Input: buffer): buffer local entry = -1 for i = 0, InputLength - 1 do - local val = DECODE_MAP[buffer.readu8(Input, i)] + local val = DECODE_MAP[buffer_readu8(Input, i)] if entry == -1 then entry = val else entry += val * 94 - counter = bit32.bor(counter, bit32.lshift(entry, bits)) + counter = bit32_bor(counter, bit32_lshift(entry, bits)) bits += BIT_MASK[entry] if bits >= 16 then - buffer.writeu16(output, offset, counter) + buffer_writeu16(output, offset, counter) offset += 2 - counter = bit32.rshift(counter, 16) + counter = bit32_rshift(counter, 16) bits -= 16 elseif bits >= 8 then - buffer.writeu8(output, offset, counter) + buffer_writeu8(output, offset, counter) offset += 1 - counter = bit32.rshift(counter, 8) + counter = bit32_rshift(counter, 8) bits -= 8 end @@ -110,17 +121,17 @@ function Base94.decode(Input: buffer): buffer end if entry ~= -1 then - counter = bit32.bor(counter, bit32.lshift(entry, bits)) - if bit32.bor(counter, 0) ~= 0 then + counter = bit32_bor(counter, bit32_lshift(entry, bits)) + if bit32_bor(counter, 0) ~= 0 then if bits + (entry < 94 and 7 or 13) >= 8 then - buffer.writeu8(output, offset, counter) + buffer_writeu8(output, offset, counter) offset += 1 end end end - local sliced = buffer.create(offset) - buffer.copy(sliced, 0, output, 0, offset) + local sliced = buffer_create(offset) + buffer_copy(sliced, 0, output, 0, offset) return sliced end From 5b047e876890fb119def5fc11102268582e1514c Mon Sep 17 00:00:00 2001 From: loadstring1 <156520308+loadstring1@users.noreply.github.com> Date: Thu, 20 Nov 2025 13:44:14 +0100 Subject: [PATCH 5/5] include math.ceil to the localization --- Modules/Encoders/Base94.luau | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/Encoders/Base94.luau b/Modules/Encoders/Base94.luau index 95d8f54..286d01c 100644 --- a/Modules/Encoders/Base94.luau +++ b/Modules/Encoders/Base94.luau @@ -12,6 +12,7 @@ local bit32_bor=bit32.bor local bit32_lshift=bit32.lshift local bit32_rshift=bit32.rshift local bit32_band=bit32.band +local math_ceil=math.ceil local DIRECT_LOOKUP = {} local BIT_MASK = {} @@ -42,7 +43,7 @@ end function Base94.encode(input: buffer): buffer local length = buffer_len(input) - local output = buffer_create(math.ceil(length * 1.25) + 2) + local output = buffer_create(math_ceil(length * 1.25) + 2) local counter = 0 local bits = 0