--!native
--[[
	LZW Module

	Provides functions for compressing and decompressing complex Luau data
	using LZW algorithm after converting the data to a JSON-compatible string format.
	Handles various Roblox datatypes and nested tables. Includes detailed debugging.

	Author: Asta (@TheYusufGamer)
]]

local LZW = {}

-- Services and Constants
local HttpService = game:GetService("HttpService")
local Workspace = game:GetService("Workspace") -- Needed for Instance path resolution

local BINARY_HEADER = string.char(0x02) -- Header for LZW compressed data
local UNCOMPRESSED_HEADER = string.char(0x01) -- Header for data sent uncompressed

-- Debug flag (set to true to enable detailed console output)
local DEBUG_MODE = false -- Set to true for detailed logging

-- Forward declarations for recursive functions
local encodeValue
local decodeValue

--- Encodes various Roblox datatypes into JSON-serializable tables with __type tags.
encodeValue = function(value: any): any
	local vType = typeof(value)

	if DEBUG_MODE then -- Conditional debug print
		local valueSnippet = value
		if vType == "string" then valueSnippet = string.sub(value :: string, 1, 40) .. (#(value :: string) > 40 and "..." or "")
		elseif vType == "table" then valueSnippet = "[table]"
		elseif vType ~= "number" and vType ~= "boolean" and vType ~= "nil" then valueSnippet = "[" .. vType .. "]" end
		-- print("encodeValue processing type:", vType, " snippet:", valueSnippet) -- Can be very verbose
	end

	-- Base Types
	if vType == "string" or vType == "number" or vType == "boolean" or vType == "nil" then
		return value

		-- Roblox Datatypes
	elseif vType == "Vector3" then local v3 = value :: Vector3; return { __type = "Vector3", x = v3.X, y = v3.Y, z = v3.Z }
	elseif vType == "Vector2" then local v2 = value :: Vector2; return { __type = "Vector2", x = v2.X, y = v2.Y }
	elseif vType == "Color3" then local c3 = value :: Color3; return { __type = "Color3", r = c3.R, g = c3.G, b = c3.B }
	elseif vType == "CFrame" then return { __type = "CFrame", components = { (value :: CFrame):GetComponents() } }
	elseif vType == "UDim2" then local u2 = value :: UDim2; return { __type = "UDim2", xs = u2.X.Scale, xo = u2.X.Offset, ys = u2.Y.Scale, yo = u2.Y.Offset }
	elseif vType == "UDim" then local u1 = value :: UDim; return { __type = "UDim", s = u1.Scale, o = u1.Offset }
	elseif vType == "Rect" then local rt = value :: Rect; return { __type = "Rect", minx = rt.Min.X, miny = rt.Min.Y, maxx = rt.Max.X, maxy = rt.Max.Y }
	elseif vType == "EnumItem" then
		local en = value :: EnumItem
		local enumTypeName = string.match(tostring(en.EnumType), "^Enum%.(.*)")
		if enumTypeName then return { __type = "Enum", enumType = enumTypeName, name = en.Name, value = en.Value }
		else warn("LZW Encoder: Could not determine EnumType name for", en); return nil end -- Error: cannot reliably serialize
	elseif vType == "BrickColor" then local bc = value :: BrickColor; return { __type = "BrickColor", num = bc.Number }
	elseif vType == "ColorSequence" then
		local cs = value :: ColorSequence; local keypoints = {}; for _, kp in ipairs(cs.Keypoints) do table.insert(keypoints, { t = kp.Time, r = kp.Value.R, g = kp.Value.G, b = kp.Value.B }) end
		return { __type = "ColorSequence", keypoints = keypoints }
	elseif vType == "NumberSequence" then
		local ns = value :: NumberSequence; local keypoints = {}; for _, kp in ipairs(ns.Keypoints) do table.insert(keypoints, { t = kp.Time, v = kp.Value, e = kp.Envelope }) end
		return { __type = "NumberSequence", keypoints = keypoints }
	elseif vType == "NumberRange" then local nr = value :: NumberRange; return { __type = "NumberRange", min = nr.Min, max = nr.Max }
	elseif vType == "Instance" then
		local inst = value :: Instance; local path = inst:GetPath()
		if string.sub(path, 1, #("Workspace") + 1) == "Workspace." then path = string.sub(path, #("Workspace") + 2); return { __type = "InstancePath", path = path }
		else warn("LZW Enc: Can't get relative Instance path for:", inst:GetFullName(), "- Storing FullName (unreliable)."); return { __type = "InstanceName", name = inst:GetFullName() } end
		-- Implement More Data types in the future

		-- Tables
	elseif vType == "table" then
		local encodedTable
		local isPureArray = true
		local numElements = 0
		for k in pairs(value) do
			numElements += 1
			if typeof(k) ~= 'number' or k <= 0 or k > numElements or math.floor(k) ~= k then isPureArray = false end
		end
		if isPureArray and numElements ~= #value then isPureArray = false end -- Check sparseness

		if isPureArray then
			-- Encode as a JSON array
			encodedTable = {}
			if DEBUG_MODE then print("Encoding table as ARRAY, len:", #value) end
			for i = 1, #value do
				--print("--- Encoding array element at index:", i) -- Inner debug print
				local element = value[i]
				local encodeElementSuccess, encodedElementOrErr = pcall(encodeValue, element)

				if not encodeElementSuccess then
					warn(string.format("!!! FAILED encoding array element at index %d: %s", i, tostring(encodedElementOrErr)))
					-- Option 1: Store error marker (keeps structure, fails later potentially)
					encodedTable[i] = { __error = "Encoding failed: " .. tostring(encodedElementOrErr) }
					-- Option 2: Propagate failure (makes outer pcall fail)
					-- error(string.format("Failed encoding array element at index %d: %s", i, tostring(encodedElementOrErr))) -- This will be caught by pcall in convertDataToString
				elseif encodedElementOrErr == nil and element ~= nil then
					warn(string.format("!!! NIL result encoding non-nil array element at index %d (Original type: %s)", i, typeof(element)))
					encodedTable[i] = nil -- Explicitly insert nil
				else
					encodedTable[i] = encodedElementOrErr
				end
			end
			--	print("--- Reached END of array processing. Returning table. Type:", type(encodedTable))
			return encodedTable

		else
			-- Encode as a JSON object (dictionary), ensuring string keys
			encodedTable = {}
			if DEBUG_MODE then print("Encoding table as DICTIONARY") end
			for k, v in pairs(value) do
				local keyString: string
				if typeof(k) == "string" then keyString = k
				else keyString = tostring(k) end -- Convert non-string keys
				-- if DEBUG_MODE and keyString ~= k then warn("LZW Encoder: Converted key to string:", k, "->", keyString) end -- Optional warning

				--print("--- Encoding dictionary value for key:", keyString) -- Inner debug print
				local encodeValSuccess, encodedValOrErr = pcall(encodeValue, v)

				if not encodeValSuccess then
					warn(string.format("!!! FAILED encoding dictionary value for key '%s': %s", keyString, tostring(encodedValOrErr)))
					encodedTable[keyString] = { __error = "Encoding failed: " .. tostring(encodedValOrErr) }
					-- error(string.format("Failed encoding dictionary value for key '%s': %s", keyString, tostring(encodedValOrErr))) -- Option 2: Propagate failure
				elseif encodedValOrErr == nil and v ~= nil then
					warn(string.format("!!! NIL result encoding non-nil dictionary value for key '%s' (Original type: %s)", keyString, typeof(v)))
					encodedTable[keyString] = nil
				else
					encodedTable[keyString] = encodedValOrErr
				end
			end
			return encodedTable -- Return dictionary
		end

	else
		-- Type not explicitly handled by any previous block
		-- This indicates a missing type in the 'if/elseif' chain
		warn("LZW Encoder: Unhandled type automatically considered an error: " .. vType .. " for value:", value)
		error("LZW Encoder: Unhandled type reached end of encodeValue: " .. vType) -- Error out
	end
end

--- Decodes JSON-parsed data (with __type tags) back into Roblox objects.
decodeValue = function(value: any): any
	local vType = typeof(value)

	if vType == "table" and value.__type then
		local tag = value.__type
		if tag == "Vector3" then return Vector3.new(value.x, value.y, value.z) end
		if tag == "Vector2" then return Vector2.new(value.x, value.y) end
		if tag == "Color3" then return Color3.new(value.r, value.g, value.b) end
		if tag == "CFrame" then return CFrame.new(unpack(value.components)) end
		if tag == "UDim2" then return UDim2.new(value.xs, value.xo, value.ys, value.yo) end
		if tag == "UDim" then return UDim.new(value.s, value.o) end
		if tag == "Rect" then return Rect.new(value.minx, value.miny, value.maxx, value.maxy) end
		if tag == "Enum" then if Enum[value.enumType] then return Enum[value.enumType][value.name] else warn("LZW Decoder: Unknown EnumType:", value.enumType); return nil end end
		if tag == "BrickColor" then return BrickColor.new(value.num) end
		if tag == "ColorSequence" then local kp = {}; for _, d in ipairs(value.keypoints) do table.insert(kp, ColorSequenceKeypoint.new(d.t, Color3.new(d.r, d.g, d.b))) end; return ColorSequence.new(kp) end
		if tag == "NumberSequence" then local kp = {}; for _, d in ipairs(value.keypoints) do table.insert(kp, NumberSequenceKeypoint.new(d.t, d.v, d.e)) end; return NumberSequence.new(kp) end
		if tag == "NumberRange" then return NumberRange.new(value.min, value.max) end
		if tag == "InstancePath" then local s, f = pcall(Workspace.FindFirstChild, Workspace, value.path, true); if s and f then return f else warn("LZW Decoder: Instance path not found:", value.path); return nil end end
		if tag == "InstanceName" then warn("LZW Decoder: Cannot reconstruct Instance from FullName:", value.name); return nil end

		warn("LZW Decoder: Unhandled __type tag:", tag); return value -- Return table if tag unhandled

	elseif vType == "table" then
		-- Regular table, check if array or dictionary based on keys from JSON
		local isJsonArray = true
		local maxArrayIndex = 0
		local keyCount = 0
		for k in pairs(value) do
			keyCount += 1
			local numKey = tonumber(k)
			if typeof(k) ~= "string" or not numKey or numKey <= 0 or numKey > keyCount or math.floor(numKey) ~= numKey then isJsonArray = false; break end -- Break optimization
			maxArrayIndex = math.max(maxArrayIndex, numKey)
		end
		if keyCount ~= maxArrayIndex then isJsonArray = false end -- Check sequentiality

		local decodedTable = {}
		if isJsonArray then
			for i = 1, keyCount do decodedTable[i] = decodeValue(value[tostring(i)]) end
		else
			for kString, v in pairs(value) do
				local originalKey; local numKey = tonumber(kString)
				if numKey ~= nil and tostring(numKey) == kString then originalKey = numKey else originalKey = kString end
				decodedTable[originalKey] = decodeValue(v)
			end
		end
		return decodedTable
	else
		-- Base type
		return value
	end
end

--- Main serialization function using the custom encoder + JSON
local function convertDataToString(data: any): (string?)
	if DEBUG_MODE then print("--- convertDataToString: Encoding data ---") end

	local encodeSuccess, encodedDataOrErr = pcall(encodeValue, data)
	if not encodeSuccess then
		warn("LZW Serializer: pcall(encodeValue, data) FAILED. Error:", encodedDataOrErr)
		return nil
	elseif encodedDataOrErr == nil and data ~= nil then
		warn("LZW Serializer: encodeValue succeeded but returned nil for non-nil input.")
		return nil -- Treat this as an encoding failure if the input wasn't nil
	end

	local encodedData = encodedDataOrErr

	if DEBUG_MODE then print("--- convertDataToString: Structure type pre-JSON:", type(encodedData)) end

	if DEBUG_MODE then print("--- convertDataToString: Calling JSONEncode ---") end
	local jsonSuccess, jsonStringOrError = pcall(HttpService.JSONEncode, HttpService, encodedData)
	if not jsonSuccess then
		warn("LZW Serializer: HttpService:JSONEncode FAILED - ", jsonStringOrError)
		return nil
	end
	if DEBUG_MODE then print("--- convertDataToString: JSONEncode succeeded, length:", #jsonStringOrError) end
	return jsonStringOrError
end

--- Main deserialization function using JSON + the custom decoder
local function convertStringToData(jsonString: string): (any)
	if not jsonString or #jsonString == 0 then return nil end

	local success, decodedJson = pcall(HttpService.JSONDecode, HttpService, jsonString)
	if not success then
		warn("LZW Deserializer: HttpService:JSONDecode FAILED - ", decodedJson)
		return nil
	end

	local decodeSuccess, originalData = pcall(decodeValue, decodedJson)
	if not decodeSuccess then
		warn("LZW Deserializer: decodeValue FAILED -", originalData)
		return nil
	end

	return originalData
end

local function compressLZW(inputStr: string): {number}
	local dictSize = 256
	local dictionary = {}
	for i = 0, 255 do dictionary[string.char(i)] = i end
	local w = ""
	local result = {}
	local inputLength = #inputStr
	for i = 1, inputLength do
		local c = inputStr:sub(i, i)
		local wc = w .. c
		if dictionary[wc] then
			w = wc
		else
			table.insert(result, dictionary[w])
			dictionary[wc] = dictSize
			dictSize += 1
			w = c
		end
	end
	if w ~= "" then table.insert(result, dictionary[w]) end
	return result
end

local function decompressLZW(compressedCodes: {number}): (string?)
	if #compressedCodes == 0 then return "" end
	local dictSize = 256
	local dictionary = {}
	for i = 0, 255 do dictionary[i] = string.char(i) end
	local w = dictionary[compressedCodes[1]]
	if not w then warn("LZW Decomp: Invalid start code."); return nil end
	local result = {w}
	local entry = ""
	for i = 2, #compressedCodes do
		local k = compressedCodes[i]
		if dictionary[k] then
			entry = dictionary[k]
		elseif k == dictSize then
			entry = w .. w:sub(1, 1)
		else
			warn("LZW Decomp: Invalid code " .. tostring(k) .. " at index " .. i)
			return nil
		end
		table.insert(result, entry)
		dictionary[dictSize] = w .. entry:sub(1, 1)
		dictSize += 1
		w = entry
	end
	return table.concat(result)
end

local function encodeVarint(codes: {number}): string
	local bytes = {}
	for _, code in ipairs(codes) do
		local c = code
		repeat
			local byteValue = c % 128
			c = math.floor(c / 128)
			if c > 0 then byteValue = byteValue + 128 end
			table.insert(bytes, string.char(byteValue))
		until c == 0
	end
	return table.concat(bytes)
end

local function decodeVarint(compressedStr: string): ({number}?)
	local codes = {}
	local currentCode = 0
	local shift = 0
	local strLen = #compressedStr
	for i = 1, strLen do
		local byteValue = string.byte(compressedStr, i)
		if not byteValue then warn("Varint Decode: Bad byte read."); return nil end
		currentCode = currentCode + (byteValue % 128) * (2 ^ (shift * 7))
		shift = shift + 1
		if byteValue < 128 then
			table.insert(codes, currentCode)
			currentCode = 0
			shift = 0
		end
	end
	if shift > 0 then warn("Varint Decode: Truncated sequence."); return nil end
	return codes
end

--- Compresses data using the custom serializer and then LZW.
-- @param data The data to compress.
-- @return string? The compressed binary string (with header), or nil on failure.
function LZW:Compress(data: any): (string?)
	-- 1. Serialize using custom JSON approach
	local serializedStr = convertDataToString(data)
	if not serializedStr then
		warn("LZW Compress: Serialization failed.")
		return nil
	end
	if #serializedStr == 0 then -- Handle empty serialization result
		return UNCOMPRESSED_HEADER .. ""
	end

	-- 2. Perform LZW Compression (use pcall for safety)
	local lzwSuccess, codesOrErr = pcall(compressLZW, serializedStr)
	if not lzwSuccess or not codesOrErr then
		warn("LZW Compress: compressLZW failed - ", codesOrErr)
		return nil
	end
	local codes = codesOrErr :: {number} -- Cast after success check

	-- 3. Encode Varint (use pcall for safety)
	local varintSuccess, compressedVarintStrOrErr = pcall(encodeVarint, codes)
	if not varintSuccess or not compressedVarintStrOrErr then
		warn("LZW Compress: encodeVarint failed - ", compressedVarintStrOrErr)
		return nil
	end
	local compressedVarintStr = compressedVarintStrOrErr :: string -- Cast

	-- 4. Construct final compressed string and compare sizes
	local compressedDataWithHeader = BINARY_HEADER .. compressedVarintStr
	if #compressedDataWithHeader >= #serializedStr + 1 then
		if DEBUG_MODE then print("LZW Compress: Using uncompressed (LZW larger/equal)") end
		return UNCOMPRESSED_HEADER .. serializedStr
	else
		if DEBUG_MODE then print(string.format("LZW Compress: Using compressed (Original: %d, LZW+Hdr: %d)", #serializedStr, #compressedDataWithHeader)) end
		return compressedDataWithHeader
	end
end

--- Decompresses data that was compressed by LZW:Compress.
-- @param compressedString The binary string received (with header).
-- @return any The original data, or nil on failure.
function LZW:Decompress(compressedString: string): (any)
	if not compressedString or #compressedString < 1 then
		warn("LZW Decompress: Nil/Empty input.")
		return nil
	end

	local header = compressedString:sub(1, 1)
	local dataString = compressedString:sub(2)
	local deserializedData: any

	if header == UNCOMPRESSED_HEADER then
		if DEBUG_MODE then print("LZW Decompress: Handling UNCOMPRESSED data") end
		deserializedData = convertStringToData(dataString)

	elseif header == BINARY_HEADER then
		if DEBUG_MODE then print("LZW Decompress: Handling LZW data") end
		-- Decode Varint first
		local decodeSuccess, codesOrErr = pcall(decodeVarint, dataString)
		if not decodeSuccess or not codesOrErr then warn("LZW Decomp: Varint decode failed -", codesOrErr); return nil end
		local codes = codesOrErr :: {number}
		if #codes == 0 and #dataString > 0 then warn("LZW Decomp: Varint produced no codes from non-empty input."); return nil end

		-- Decompress LZW
		local decompSuccess, decompressedStrOrErr = pcall(decompressLZW, codes)
		if not decompSuccess or not decompressedStrOrErr then warn("LZW Decomp: Core LZW decompression failed -", decompressedStrOrErr); return nil end
		local decompressedStr = decompressedStrOrErr :: string

		-- Deserialize the resulting string
		deserializedData = convertStringToData(decompressedStr)
	else
		warn("LZW Decomp: Invalid header byte: " .. tostring(string.byte(header)))
		return nil
	end

	-- Check final result
	if deserializedData == nil and #compressedString > 1 then
		-- Only warn if we expected data but got nil (e.g., internal deserialization error)
		warn("LZW Decomp: Final deserialization resulted in nil.")
	end

	return deserializedData
end

return LZW


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

-- Varables
local read = {}			:: {[number]: (cursor: Cursor.Cursor) -> any}
local write = {}		:: {[string]: (cursor: Cursor.Cursor, value:any) -> ()}

-- Nil
read[0] = function(cursor: any)
	return nil
end

write["nil"] = function(cursor: any, value: nil)
	cursor:Allocate(1)
	buffer.writeu8(cursor.Buffer, cursor.Index, 0)
	cursor.Index += 1
end

-- Number
read[1] = function(cursor) return -cursor:ReadU1() end
read[2] = function(cursor) return -cursor:ReadU2() end
read[3] = function(cursor) return -cursor:ReadU3() end
read[4] = function(cursor) return -cursor:ReadU4() end
read[5] = function(cursor) return cursor:ReadU1() end
read[6] = function(cursor) return cursor:ReadU2() end
read[7] = function(cursor) return cursor:ReadU3() end
read[8] = function(cursor) return cursor:ReadU4() end
read[9] = function(cursor) return cursor:ReadF4() end

write.number = function(cursor, value: number)
	if value % 1 == 0 then
		if value < 0 then
			if value > -256 then
				cursor:Allocate(2) cursor:WriteU1(1) cursor:WriteU1(-value)
			elseif value > -65536 then
				cursor:Allocate(3) cursor:WriteU1(2) cursor:WriteU2(-value)
			elseif value > -16777216 then
				cursor:Allocate(4) cursor:WriteU1(3) cursor:WriteU3(-value)
			else
				cursor:Allocate(5) cursor:WriteU1(4) cursor:WriteU4(-value)
			end
		else
			if value < 256 then
				cursor:Allocate(2) cursor:WriteU1(5) cursor:WriteU1(value)
			elseif value < 65536 then
				cursor:Allocate(3) cursor:WriteU1(6) cursor:WriteU2(value)
			elseif value < 16777216 then
				cursor:Allocate(4) cursor:WriteU1(7) cursor:WriteU3(value)
			else
				cursor:Allocate(5) cursor:WriteU1(8) cursor:WriteU4(value)
			end
		end
	else
		cursor:Allocate(5) cursor:WriteU1(9) cursor:WriteF4(value)
	end
end

-- Boolean
read[10] = function(cursor)
	return cursor:ReadU1() == 1
end

write.boolean = function(cursor, value: boolean)
	cursor:Allocate(2)
	cursor:WriteU1(10)
	cursor:WriteU1(if value then 1 else 0)
end

-- String
read[11] = function(cursor)
	return cursor:ReadString(cursor:ReadU1())
end

write.string = function(cursor, value: string)
	cursor:Allocate(2 + #value)
	cursor:WriteU1(11)
	cursor:WriteU1(#value)
	cursor:WriteString(value)
end

-- Buffer
read[12] = function(cursor)
	return cursor:ReadBuffer(cursor:ReadU1())
end

write.buffer = function(cursor, value: buffer)
	local length = buffer.len(value)
	cursor:Allocate(2 + length)
	cursor:WriteU1(12)
	cursor:WriteU1(length)
	cursor:WriteBuffer(value)
end

-- Vector2
read[13] = function(cursor)
	return Vector2.new(cursor:ReadF4(), cursor:ReadF4())
end

write.Vector2 = function(cursor, value: Vector2)
	cursor:Allocate(9)
	cursor:WriteU1(13)
	cursor:WriteF4(value.X)
	cursor:WriteF4(value.Y)
end

-- Vector3
read[14] = function(cursor)
	return Vector3.new(cursor:ReadF4(), cursor:ReadF4(), cursor:ReadF4())
end

write.Vector3 = function(cursor, value: Vector3)
	cursor:Allocate(13)
	cursor:WriteU1(14)
	cursor:WriteF4(value.X)
	cursor:WriteF4(value.Y)
	cursor:WriteF4(value.Z)
end

-- CFrame
read[15] = function(cursor)
	return CFrame.fromEulerAnglesXYZ(
		cursor:ReadU2() / 10430.219195527361,
		cursor:ReadU2() / 10430.219195527361,
		cursor:ReadU2() / 10430.219195527361
	) + Vector3.new(
		cursor:ReadF4(),
		cursor:ReadF4(),
		cursor:ReadF4()
	)
end

write.CFrame = function(cursor, value: CFrame)
	cursor:Allocate(19)
	cursor:WriteU1(15)
	local rx, ry, rz = value:ToEulerAnglesXYZ()
	cursor:WriteU2(rx * 10430.219195527361 + 0.5)
	cursor:WriteU2(ry * 10430.219195527361 + 0.5)
	cursor:WriteU2(rz * 10430.219195527361 + 0.5)
	cursor:WriteF4(value.X)
	cursor:WriteF4(value.Y)
	cursor:WriteF4(value.Z)
end

-- NumberRange
read[16] = function(cursor)
	return NumberRange.new(cursor:ReadF4(), cursor:ReadF4())
end

write.NumberRange = function(cursor, value: NumberRange)
	cursor:Allocate(9)
	cursor:WriteU1(16)
	cursor:WriteF4(value.Min)
	cursor:WriteF4(value.Max)
end

-- NumberSequence
read[17] = function(cursor)
	local length = cursor:ReadU1()
	local keypoints = table.create(length)
	for index = 1, cursor:ReadU1() do
		table.insert(keypoints, NumberSequenceKeypoint.new(
			cursor:ReadU1() / 255,
			cursor:ReadU1() / 255,
			cursor:ReadU1() / 255
			))
	end
	return NumberSequence.new(keypoints)
end

write.NumberSequence = function(cursor, value: NumberSequence)
	local length = #value.Keypoints
	cursor:Allocate(2 + length * 3)
	cursor:WriteU1(17)
	cursor:WriteU1(length)
	for index, keypoint in value.Keypoints do
		cursor:WriteU1(keypoint.Time * 255 + 0.5)
		cursor:WriteU1(keypoint.Value * 255 + 0.5)
		cursor:WriteU1(keypoint.Envelope * 255 + 0.5)
	end
end

-- Color3
read[18] = function(cursor)
	return Color3.fromRGB(cursor:ReadU1(), cursor:ReadU1(), cursor:ReadU1())
end

write.Color3 = function(cursor, value: Color3)
	cursor:Allocate(4)
	cursor:WriteU1(18)
	cursor:WriteU1(value.R * 255 + 0.5)
	cursor:WriteU1(value.G * 255 + 0.5)
	cursor:WriteU1(value.B * 255 + 0.5)
end

-- ColorSequence
read[19] = function(cursor)
	local length = cursor:ReadU1()
	local keypoints = table.create(length)
	for index = 1, cursor:ReadU1() do
		table.insert(keypoints, ColorSequenceKeypoint.new(
			cursor:ReadU1() / 255,
			Color3.fromRGB(cursor:ReadU1(), cursor:ReadU1(), cursor:ReadU1())
			))
	end
	return ColorSequence.new(keypoints)
end

write.ColorSequence = function(cursor, value: ColorSequence)
	local length = #value.Keypoints
	cursor:Allocate(2 + length * 4)
	cursor:WriteU1(19)
	cursor:WriteU1(length)
	for index, keypoint in value.Keypoints do
		cursor:WriteU1(keypoint.Time * 255 + 0.5)
		cursor:WriteU1(keypoint.Value.R * 255 + 0.5)
		cursor:WriteU1(keypoint.Value.G * 255 + 0.5)
		cursor:WriteU1(keypoint.Value.B * 255 + 0.5)
	end
end

-- BrickColor
read[20] = function(cursor)
	return BrickColor.new(cursor:ReadU2())
end

write.BrickColor = function(cursor, value: BrickColor)
	cursor:Allocate(3)
	cursor:WriteU1(20)
	cursor:WriteU2(value.Number)
end

-- UDim
read[21] = function(cursor)
	return UDim.new(cursor:ReadS2() / 1000, cursor:ReadS2())
end

write.UDim = function(cursor, value: UDim)
	cursor:Allocate(5)
	cursor:WriteU1(21)
	cursor:WriteS2(value.Scale * 1000 + 0.5)
	cursor:WriteS2(value.Offset)
end

-- UDim2
read[22] = function(cursor)
	return UDim2.new(cursor:ReadS2() / 1000, cursor:ReadS2(), cursor:ReadS2() / 1000, cursor:ReadS2())
end

write.UDim2 = function(cursor, value: UDim2)
	cursor:Allocate(9)
	cursor:WriteU1(22)
	cursor:WriteS2(value.X.Scale * 1000 + 0.5)
	cursor:WriteS2(value.X.Offset)
	cursor:WriteS2(value.Y.Scale * 1000 + 0.5)
	cursor:WriteS2(value.Y.Offset)
end

-- Rect
read[23] = function(cursor)
	return Rect.new(cursor:ReadF4(), cursor:ReadF4(), cursor:ReadF4(), cursor:ReadF4())
end

write.Rect = function(cursor, value: Rect)
	cursor:Allocate(17)
	cursor:WriteU1(23)
	cursor:WriteF4(value.Min.X)
	cursor:WriteF4(value.Min.Y)
	cursor:WriteF4(value.Max.X)
	cursor:WriteF4(value.Max.Y)
end

-- Region3
read[24] = function(cursor)
	return Region3.new(
		Vector3.new(cursor:ReadF4(), cursor:ReadF4(), cursor:ReadF4()),
		Vector3.new(cursor:ReadF4(), cursor:ReadF4(), cursor:ReadF4())
	)
end

write.Region3 = function(cursor, value: Region3)
	cursor:Allocate(25)
	cursor:WriteU1(24)
	local halfSize = value.Size / 2
	local minimum = value.CFrame.Position - halfSize
	local maximum = value.CFrame.Position + halfSize
	cursor:WriteF4(minimum.X)
	cursor:WriteF4(minimum.Y)
	cursor:WriteF4(minimum.Z)
	cursor:WriteF4(maximum.X)
	cursor:WriteF4(maximum.Y)
	cursor:WriteF4(maximum.Z)
end

-- Instance
read[25] = function(cursor)
	return cursor:ReadInstance()
end

write.Instance = function(cursor, value: Instance)
	cursor:Allocate(1)
	cursor:WriteU1(25)
	cursor:WriteInstance(value)
end

return {

	Read = function(cursor: Cursor.Cursor)
		return read[cursor:ReadU1()](cursor)
	end,

	Write = function(cursor: Cursor.Cursor, value: any)
		write[typeof(value)](cursor, value)
	end,

}


--!strict
--!optimize 2
--!native

-- Binary data type for handling raw binary data such as compressed data
-- Optimized version
-- Binary Type Handler (U3 Length Prefix for >64KB support)
-- Author: Asta (@TheYusufGamer)

-- Requires
local CursorModule = require(script.Parent.Parent.Types.Cursor)
type Cursor = CursorModule.Cursor

return {
	Read = function(cursor: Cursor): string
		local length: number = cursor:ReadU3()
		local data: string = cursor:ReadString(length)

		return data
	end,

	Write = function(cursor: Cursor, value: string)
		local length: number = #value

		-- Validate length against U3 maximum if necessary
		if length > 16777215 then
			error(string.format("Binary data length (%d) exceeds 3-byte U24 limit (16777215).", length), 2)
		end

		cursor:WriteU3(length)
		cursor:WriteString(value)

	end,
}


--!strict

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		local offset = cursor.Index * 8
		local value1 = buffer.readbits(cursor.Buffer, offset + 0, 1) == 1
		local value2 = buffer.readbits(cursor.Buffer, offset + 1, 1) == 1
		local value3 = buffer.readbits(cursor.Buffer, offset + 2, 1) == 1
		local value4 = buffer.readbits(cursor.Buffer, offset + 3, 1) == 1
		local value5 = buffer.readbits(cursor.Buffer, offset + 4, 1) == 1
		local value6 = buffer.readbits(cursor.Buffer, offset + 5, 1) == 1
		local value7 = buffer.readbits(cursor.Buffer, offset + 6, 1) == 1
		local value8 = buffer.readbits(cursor.Buffer, offset + 7, 1) == 1
		cursor.Index += 1
		return {value1, value2, value3, value4, value5, value6, value7, value8}
	end,

	Write = function(cursor: Cursor.Cursor, value: {boolean})
		cursor:Allocate(1)
		local offset = cursor.Index * 8
		buffer.writebits(cursor.Buffer, offset + 0, 1, if value[1] then 1 else 0)
		buffer.writebits(cursor.Buffer, offset + 1, 1, if value[2] then 1 else 0)
		buffer.writebits(cursor.Buffer, offset + 2, 1, if value[3] then 1 else 0)
		buffer.writebits(cursor.Buffer, offset + 3, 1, if value[4] then 1 else 0)
		buffer.writebits(cursor.Buffer, offset + 4, 1, if value[5] then 1 else 0)
		buffer.writebits(cursor.Buffer, offset + 5, 1, if value[6] then 1 else 0)
		buffer.writebits(cursor.Buffer, offset + 6, 1, if value[7] then 1 else 0)
		buffer.writebits(cursor.Buffer, offset + 7, 1, if value[8] then 1 else 0)
		cursor.Index += 1
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return cursor:ReadU1() == 1
	end,

	Write = function(cursor: Cursor.Cursor, value: boolean)
		cursor:Allocate(1)
		cursor:WriteU1(if value then 1 else 0)
	end,

}


--!strict
--!optimize 2
--!native
-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return BrickColor.new(cursor:ReadU3())
	end,

	Write = function(cursor: Cursor.Cursor, value: BrickColor)
		cursor:Allocate(3)
		cursor:WriteU3(value.Number)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return cursor:ReadBuffer(cursor:ReadU1())
	end,

	Write = function(cursor: Cursor.Cursor, value: buffer)
		local length = buffer.len(value)
		cursor:Allocate(1 + length)
		cursor:WriteU1(length)
		cursor:WriteBuffer(value)
	end,

}


--!strict
--!optimize 2
--!native
-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return CFrame.fromEulerAnglesXYZ(
			cursor:ReadU1() / 40.58451048843331,
			cursor:ReadU1() / 40.58451048843331,
			cursor:ReadU1() / 40.58451048843331
		) + Vector3.new(
			cursor:ReadF3(),
			cursor:ReadF3(),
			cursor:ReadF3()
		)
	end,

	Write = function(cursor: Cursor.Cursor, value: CFrame)
		cursor:Allocate(12)
		local rx, ry, rz = value:ToEulerAnglesXYZ()
		cursor:WriteU1(rx * 40.58451048843331 + 0.5)
		cursor:WriteU1(ry * 40.58451048843331 + 0.5)
		cursor:WriteU1(rz * 40.58451048843331 + 0.5)
		cursor:WriteF3(value.X)
		cursor:WriteF3(value.Y)
		cursor:WriteF3(value.Z)
	end,

}


--!strict
--!optimize 2
--!native
-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return CFrame.fromEulerAnglesXYZ(
			cursor:ReadU2() / 10430.219195527361,
			cursor:ReadU2() / 10430.219195527361,
			cursor:ReadU2() / 10430.219195527361
		) + Vector3.new(
			cursor:ReadF4(),
			cursor:ReadF4(),
			cursor:ReadF4()
		)
	end,

	Write = function(cursor: Cursor.Cursor, value: CFrame)
		cursor:Allocate(18)
		local rx, ry, rz = value:ToEulerAnglesXYZ()
		cursor:WriteU2(rx * 10430.219195527361 + 0.5)
		cursor:WriteU2(ry * 10430.219195527361 + 0.5)
		cursor:WriteU2(rz * 10430.219195527361 + 0.5)
		cursor:WriteF4(value.X)
		cursor:WriteF4(value.Y)
		cursor:WriteF4(value.Z)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return CFrame.fromEulerAnglesXYZ(
			cursor:ReadU1() / 40.58451048843331,
			cursor:ReadU1() / 40.58451048843331,
			cursor:ReadU1() / 40.58451048843331
		) + Vector3.new(
			cursor:ReadF4(),
			cursor:ReadF4(),
			cursor:ReadF4()
		)
	end,

	Write = function(cursor: Cursor.Cursor, value: CFrame)
		cursor:Allocate(15)
		local rx, ry, rz = value:ToEulerAnglesXYZ()
		cursor:WriteU1(rx * 40.58451048843331 + 0.5)
		cursor:WriteU1(ry * 40.58451048843331 + 0.5)
		cursor:WriteU1(rz * 40.58451048843331 + 0.5)
		cursor:WriteF4(value.X)
		cursor:WriteF4(value.Y)
		cursor:WriteF4(value.Z)
	end,

}


--!strict
--!optimize 2
--!native
-- Recommended character array lengths: 2, 4, 8, 16, 32, 64, 128, 256

-- Requires
local Cursor = require(script.Parent.Cursor)

-- Varables
local indices = {}
local characters = {[0] =
	" ", ".", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D",
	"E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
	"U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
	"k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
}

-- Initialize
for index, value in characters do indices[value] = index end
local bits = math.ceil(math.log(#characters + 1, 2))
local bytes = bits / 8

return {

	Read = function(cursor: Cursor.Cursor)
		local length = cursor:ReadU1()
		local bytes = math.ceil(length * bytes)
		local characterArray = table.create(length)
		local offset = cursor.Index * 8
		for index = 1, length do
			table.insert(characterArray, characters[buffer.readbits(cursor.Buffer, offset, bits)])
			offset += bits
		end
		cursor.Index += bytes
		return table.concat(characterArray)
	end,

	Write = function(cursor: Cursor.Cursor, value: string)
		local bytes = math.ceil(#value * bytes)
		cursor:Allocate(1 + bytes)
		cursor:WriteU1(#value)
		local offset = cursor.Index * 8
		for index = 1, #value do
			buffer.writebits(cursor.Buffer, offset, bits, indices[value:sub(index, index)])
			offset += bits
		end
		cursor.Index += bytes
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return Color3.fromRGB(cursor:ReadU1(), cursor:ReadU1(), cursor:ReadU1())
	end,

	Write = function(cursor: Cursor.Cursor, value: Color3)
		cursor:Allocate(3)
		cursor:WriteU1(value.R * 255 + 0.5)
		cursor:WriteU1(value.G * 255 + 0.5)
		cursor:WriteU1(value.B * 255 + 0.5)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		local length = cursor:ReadU1()
		local keypoints = table.create(length)
		for index = 1, cursor:ReadU1() do
			table.insert(keypoints, ColorSequenceKeypoint.new(
				cursor:ReadU1() / 255,
				Color3.fromRGB(cursor:ReadU1(), cursor:ReadU1(), cursor:ReadU1())
			))
		end
		return ColorSequence.new(keypoints)
	end,

	Write = function(cursor: Cursor.Cursor, value: ColorSequence)
		local length = #value.Keypoints
		cursor:Allocate(1 + length * 4)
		cursor:WriteU1(length)
		for index, keypoint in value.Keypoints do
			cursor:WriteU1(keypoint.Time * 255 + 0.5)
			cursor:WriteU1(keypoint.Value.R * 255 + 0.5)
			cursor:WriteU1(keypoint.Value.G * 255 + 0.5)
			cursor:WriteU1(keypoint.Value.B * 255 + 0.5)
		end
	end,

}


--!strict
--!optimize 2
--!native

-- Types
export type Cursor = {
	Type:					"Cursor",
	Index:					number,
	InstanceIndex:			number,
	Length:					number,
	Buffer:					buffer,
	Instances:				{Instance},
	Allocate:				(self: Cursor, amount: number) -> (),
	Clear:					(self: Cursor) -> (),
	Truncate:				(self: Cursor) -> buffer,
	ReadS1:					(self: Cursor) -> number,
	WriteS1:				(self: Cursor, value: number) -> (),
	ReadS2:					(self: Cursor) -> number,
	WriteS2:				(self: Cursor, value: number) -> (),
	ReadS3:					(self: Cursor) -> number,
	WriteS3:				(self: Cursor, value: number) -> (),
	ReadS4:					(self: Cursor) -> number,
	WriteS4:				(self: Cursor, value: number) -> (),
	ReadU1:					(self: Cursor) -> number,
	WriteU1:				(self: Cursor, value: number) -> (),
	ReadU2:					(self: Cursor) -> number,
	WriteU2:				(self: Cursor, value: number) -> (),
	ReadU3:					(self: Cursor) -> number,
	WriteU3:				(self: Cursor, value: number) -> (),
	ReadU4:					(self: Cursor) -> number,
	WriteU4:				(self: Cursor, value: number) -> (),
	ReadF2:					(self: Cursor) -> number,
	WriteF2:				(self: Cursor, value: number) -> (),
	ReadF3:					(self: Cursor) -> number,
	WriteF3:				(self: Cursor, value: number) -> (),
	ReadF4:					(self: Cursor) -> number,
	WriteF4:				(self: Cursor, value: number) -> (),
	ReadF8:					(self: Cursor) -> number,
	WriteF8:				(self: Cursor, value: number) -> (),
	ReadString:				(self: Cursor, length: number) -> string,
	WriteString:			(self: Cursor, value: string) -> (),
	ReadBuffer:				(self: Cursor, length: number) -> buffer,
	WriteBuffer:			(self: Cursor, value: buffer) -> (),
	ReadInstance:			(self: Cursor) -> Instance,
	WriteInstance:			(self: Cursor, value: Instance) -> (),
}

-- Varables
local Cursor = {}			:: Cursor
Cursor["__index"] = Cursor
Cursor.Type = "Cursor"

-- Constructor
local function Constructor(initialBuffer: buffer?, instances: {Instance}?)
	local cursor = (setmetatable({}, Cursor) :: any) :: Cursor
	cursor.Index = 0
	cursor.InstanceIndex = 0
	cursor.Buffer = initialBuffer or buffer.create(1024)
	cursor.Length = buffer.len(cursor.Buffer)
	cursor.Instances = instances or {}
	return cursor
end

-- Cursor
function Cursor:Allocate(amount)
	local length = self.Index + amount
	if length <= self.Length then return end
	while self.Length < length do self.Length *= 1.5 end
	local newBuffer = buffer.create(self.Length)
	buffer.copy(newBuffer, 0, self.Buffer, 0, self.Index)
	self.Buffer = newBuffer
end

function Cursor:Clear()
	--buffer.fill(self.Buffer, 0, 0, self.Index)
	self.Index = 0
	self.InstanceIndex = 0
	table.clear(self.Instances)
end

function Cursor:Truncate()
	local truncatedBuffer = buffer.create(self.Index)
	buffer.copy(truncatedBuffer, 0, self.Buffer, 0, self.Index)
	return truncatedBuffer
end

-- Signed Integers
function Cursor:ReadS1()
	local value = buffer.readi8(self.Buffer, self.Index)
	self.Index += 1
	return value
end

function Cursor:WriteS1(value)
	local bytesToWrite = 1
	self:Allocate(bytesToWrite)
	buffer.writei8(self.Buffer, self.Index, value)
	self.Index += bytesToWrite
end

function Cursor:ReadS2()
	local value = buffer.readi16(self.Buffer, self.Index)
	self.Index += 2
	return value
end

function Cursor:WriteS2(value)
	local bytesToWrite = 2
	self:Allocate(bytesToWrite)
	buffer.writei16(self.Buffer, self.Index, value)
	self.Index += bytesToWrite
end

function Cursor:ReadS3()
	local value = buffer.readbits(self.Buffer, self.Index * 8, 24) - 8388608
	self.Index += 3
	return value
end

function Cursor:WriteS3(value)
	local bytesToWrite = 3
	self:Allocate(bytesToWrite)
	buffer.writebits(self.Buffer, self.Index * 8, 24, value + 8388608)
	self.Index += bytesToWrite
end

function Cursor:ReadS4()
	local value = buffer.readi32(self.Buffer, self.Index)
	self.Index += 4
	return value
end

function Cursor:WriteS4(value)
	local bytesToWrite = 4
	self:Allocate(bytesToWrite)
	buffer.writei32(self.Buffer, self.Index, value)
	self.Index += bytesToWrite
end

-- Unsigned Integers
function Cursor:ReadU1()
	local value = buffer.readu8(self.Buffer, self.Index)
	self.Index += 1
	return value
end

function Cursor:WriteU1(value)
	local bytesToWrite = 1
	self:Allocate(bytesToWrite)
	buffer.writeu8(self.Buffer, self.Index, value)
	self.Index += bytesToWrite
end

function Cursor:ReadU2()
	local value = buffer.readu16(self.Buffer, self.Index)
	self.Index += 2
	return value
end

function Cursor:WriteU2(value)
	local bytesToWrite = 2
	self:Allocate(bytesToWrite)
	buffer.writeu16(self.Buffer, self.Index, value)
	self.Index += bytesToWrite
end

function Cursor:ReadU3()
	local value = buffer.readbits(self.Buffer, self.Index * 8, 24)
	self.Index += 3
	return value
end

function Cursor:WriteU3(value)
	local bytesToWrite = 3
	self:Allocate(bytesToWrite)
	buffer.writebits(self.Buffer, self.Index * 8, 24, value)
	self.Index += bytesToWrite
end

function Cursor:ReadU4()
	local value = buffer.readu32(self.Buffer, self.Index)
	self.Index += 4
	return value
end

function Cursor:WriteU4(value)
	local bytesToWrite = 4
	self:Allocate(bytesToWrite)
	buffer.writeu32(self.Buffer, self.Index, value)
	self.Index += bytesToWrite
end

-- Floating Point Numbers
function Cursor:ReadF2()
	local offset = self.Index * 8
	local mantissa = buffer.readbits(self.Buffer, offset + 0, 10)
	local exponent = buffer.readbits(self.Buffer, offset + 10, 5)
	local sign = buffer.readbits(self.Buffer, offset + 15, 1)
	self.Index += 2
	if mantissa == 0b0000000000 then
		if exponent == 0b00000 then return 0 end
		if exponent == 0b11111 then return if sign == 0 then math.huge else -math.huge end
	elseif exponent == 0b11111 then return 0/0 end
	if sign == 0 then
		return (mantissa / 1024 + 1) * 2 ^ (exponent - 15)
	else
		return -(mantissa / 1024 + 1) * 2 ^ (exponent - 15)
	end
end

function Cursor:WriteF2(value)
	local bytesToWrite = 2
	self:Allocate(bytesToWrite)
	local offset = self.Index * 8
	if value == 0 then
		buffer.writebits(self.Buffer, offset, 16, 0b0_00000_0000000000)
	elseif value >= 65520 then
		buffer.writebits(self.Buffer, offset, 16, 0b0_11111_0000000000)
	elseif value <= -65520 then
		buffer.writebits(self.Buffer, offset, 16, 0b1_11111_0000000000)
	elseif value ~= value then
		buffer.writebits(self.Buffer, offset, 16, 0b0_11111_0000000001)
	else
		local sign = 0
		if value < 0 then sign = 1 value = -value end
		local mantissa, exponent = math.frexp(value)
		buffer.writebits(self.Buffer, offset + 0, 10, mantissa * 2048 - 1023.5)
		buffer.writebits(self.Buffer, offset + 10, 5, exponent + 14)
		buffer.writebits(self.Buffer, offset + 15, 1, sign)
	end
	self.Index += bytesToWrite
end

function Cursor:ReadF3()
	local offset = self.Index * 8
	local mantissa = buffer.readbits(self.Buffer, offset + 0, 17)
	local exponent = buffer.readbits(self.Buffer, offset + 17, 6)
	local sign = buffer.readbits(self.Buffer, offset + 23, 1)
	self.Index += 3
	if mantissa == 0b00000000000000000 then
		if exponent == 0b000000 then return 0 end
		if exponent == 0b111111 then return if sign == 0 then math.huge else -math.huge end
	elseif exponent == 0b111111 then return 0/0 end
	if sign == 0 then
		return (mantissa / 131072 + 1) * 2 ^ (exponent - 31)
	else
		return -(mantissa / 131072 + 1) * 2 ^ (exponent - 31)
	end
end

function Cursor:WriteF3(value)
	local bytesToWrite = 3
	self:Allocate(bytesToWrite)
	local offset = self.Index * 8
	if value == 0 then
		buffer.writebits(self.Buffer, offset, 24, 0b0_000000_00000000000000000)
	elseif value >= 4294959104 then
		buffer.writebits(self.Buffer, offset, 24, 0b0_111111_00000000000000000)
	elseif value <= -4294959104 then
		buffer.writebits(self.Buffer, offset, 24, 0b1_111111_00000000000000000)
	elseif value ~= value then
		buffer.writebits(self.Buffer, offset, 24, 0b0_111111_00000000000000001)
	else
		local sign = 0
		if value < 0 then sign = 1 value = -value end
		local mantissa, exponent = math.frexp(value)
		buffer.writebits(self.Buffer, offset + 0, 17, mantissa * 262144 - 131071.5)
		buffer.writebits(self.Buffer, offset + 17, 6, exponent + 30)
		buffer.writebits(self.Buffer, offset + 23, 1, sign)
	end
	self.Index += bytesToWrite
end

function Cursor:ReadF4()
	local value = buffer.readf32(self.Buffer, self.Index)
	self.Index += 4
	return value
end

function Cursor:WriteF4(value)
	local bytesToWrite = 4
	self:Allocate(bytesToWrite)
	buffer.writef32(self.Buffer, self.Index, value)
	self.Index += bytesToWrite
end

function Cursor:ReadF8()
	local value = buffer.readf64(self.Buffer, self.Index)
	self.Index += 8
	return value
end

function Cursor:WriteF8(value)
	local bytesToWrite = 8
	self:Allocate(bytesToWrite)
	buffer.writef64(self.Buffer, self.Index, value)
	self.Index += bytesToWrite
end

-- String
function Cursor:ReadString(length)
	local value = buffer.readstring(self.Buffer, self.Index, length)
	self.Index += length
	return value
end

function Cursor:WriteString(value)
	local bytesToWrite = #value
	self:Allocate(bytesToWrite)
	buffer.writestring(self.Buffer, self.Index, value, bytesToWrite) -- Explicitly add length
	self.Index += bytesToWrite
end

-- Buffer
function Cursor:ReadBuffer(length)
	local value = buffer.create(length)
	buffer.copy(value, 0, self.Buffer, self.Index, length)
	self.Index += length
	return value
end

function Cursor:WriteBuffer(value)
	local bytesToWrite = buffer.len(value)
	self:Allocate(bytesToWrite)
	buffer.copy(self.Buffer, self.Index, value, 0, bytesToWrite) -- Add srcOffset=0 and length
	self.Index += bytesToWrite
end

-- Instance
function Cursor:ReadInstance()
	self.InstanceIndex += 1
	return self.Instances[self.InstanceIndex]
end

function Cursor:WriteInstance(value)
	local bytesToWrite = 0 -- No direct buffer write, but ensure allocation logic is consistent
	self:Allocate(bytesToWrite)
	self.InstanceIndex += 1
	self.Instances[self.InstanceIndex] = value
end

return Constructor


--!strict
--!optimize 2
--!native

--[[
    EfficientTable.lua

    Optimized table serialization for NetRay
    Provides specialized handling for different table structures
    - Dense arrays (consecutive numeric keys)
    - String key maps (tables with string keys)
    - Mixed tables (optimized format for tables with mixed key types)
    - Homogeneous tables (tables with all values of the same type)

    Determines the most efficient encoding strategy based on table contents
    Author: Asta (@TheYusufGamer)
]]

-- Requires
local Cursor = require(script.Parent.Cursor)

-- Constants for encoding types
local ENCODING_TYPES = {
    DENSE_ARRAY = 1,     -- Array with consecutive integer keys
    STRING_KEY_MAP = 2,  -- Table with only string keys
    MIXED = 3,           -- Table with mixed key types
    HOMOGENEOUS = 4      -- Table with values of the same type
}

-- Type checking utilities
local function getValueType(value)
    local valueType = typeof(value)
    if valueType == "table" then
        -- Check if it's an array (all integer keys)
        local isArray = true
        local count = 0
        for k in pairs(value) do
            count += 1
            if type(k) ~= "number" or k % 1 ~= 0 or k < 1 then
                isArray = false
                break
            end
        end
        if isArray then
            return "array"
        end
        return "table"
    end
    return valueType
end

-- Check if a table is a dense array (all integer keys from 1 to n)
local function isDenseArray(tbl)
    local count = 0
    for _ in pairs(tbl) do
        count += 1
    end

    -- Quick check if #tbl matches the count
    if #tbl == count then
        -- Verify all keys are sequential
        for i = 1, count do
            if tbl[i] == nil then
                return false
            end
        end
        return true
    end
    return false
end

-- Check if all table values are of the same type
local function isHomogeneousTable(tbl)
    local firstType = nil

    for _, v in pairs(tbl) do
        local currentType = typeof(v)
        if firstType == nil then
            firstType = currentType
        elseif firstType ~= currentType then
            return false, nil
        end
    end

    return true, firstType
end

-- Check if all keys are strings
local function isStringKeyMap(tbl)
    for k in pairs(tbl) do
        if type(k) ~= "string" then
            return false
        end
    end
    return true
end

-- Get Types module safely (avoid circular dependency)
local function getTypesModule()
    return require(script.Parent.Types)
end

-- Read function for EfficientTable
local function Read(cursor)
    -- Read the encoding type
    local encodingType = cursor:ReadU1()

    -- Lazy-load Types module to avoid circular dependency
    local Types = getTypesModule()

    -- Handle based on encoding type
    if encodingType == ENCODING_TYPES.DENSE_ARRAY then
        local length = cursor:ReadU2()
        local valueType = cursor:ReadU1()
        local result = table.create(length)

        -- Homogeneous array optimization
        if valueType > 0 then
            -- All elements are the same type
            local readFunc = nil
            for typeName, typeId in pairs(Types.Types) do
                if typeId == valueType then
                    readFunc = Types.Reads[typeName]
                    break
                end
            end

            if readFunc then
                for i = 1, length do
                    result[i] = readFunc(cursor)
                end
            else
                -- Fallback to Any
                for i = 1, length do
                    result[i] = Types.Reads.Any(cursor)
                end
            end
        else
            -- Mixed types array
            for i = 1, length do
                result[i] = Types.Reads.Any(cursor)
            end
        end

        return result

    elseif encodingType == ENCODING_TYPES.STRING_KEY_MAP then
        local count = cursor:ReadU2()
        local result = {}

        for _ = 1, count do
            -- Read key length and key
            local keyLength = cursor:ReadU1()
            local key = cursor:ReadString(keyLength)

            -- Read value
            local value = Types.Reads.Any(cursor)
            result[key] = value
        end

        return result

    elseif encodingType == ENCODING_TYPES.MIXED then
        local count = cursor:ReadU2()
        local result = {}

        for _ = 1, count do
            -- Read key type and value
            local keyType = cursor:ReadU1()
            local key

            -- Parse key based on type
            if keyType == 1 then
                -- Number key
                key = cursor:ReadU4()
            elseif keyType == 2 then
                -- String key
                local keyLength = cursor:ReadU1()
                key = cursor:ReadString(keyLength)
            elseif keyType == 3 then
                -- Boolean key
                key = cursor:ReadU1() == 1
            else
                -- Use Any type for other key types
                key = Types.Reads.Any(cursor)
            end

            -- Read value
            local value = Types.Reads.Any(cursor)
            result[key] = value
        end

        return result

    elseif encodingType == ENCODING_TYPES.HOMOGENEOUS then
        local count = cursor:ReadU2()
        local valueType = cursor:ReadU1()
        local result = {}

        -- Read key-value pairs
        for _ = 1, count do
            -- Read key (always use Any for keys)
            local key = Types.Reads.Any(cursor)

            -- Read value using the specific type
            local value
            local readFunc = nil
            for typeName, typeId in pairs(Types.Types) do
                if typeId == valueType then
                    readFunc = Types.Reads[typeName]
                    break
                end
            end

            if readFunc then
                value = readFunc(cursor)
            else
                value = Types.Reads.Any(cursor)
            end

            result[key] = value
        end

        return result
    end

    -- Fallback to empty table if invalid encoding
    return {}
end

-- Write function for EfficientTable
local function Write(cursor, value)
    -- Lazy-load Types module to avoid circular dependency
    local Types = getTypesModule()

    -- Determine the most efficient encoding strategy
    local isDense = isDenseArray(value)
    local isStringMap = not isDense and isStringKeyMap(value)
    local isHomogeneous, commonType = isHomogeneousTable(value)

    -- Get table size
    local size = 0
    for _ in pairs(value) do
        size += 1
    end

    -- Choose encoding strategy
    if isDense then
        -- Dense array encoding
        cursor:Allocate(4) -- 1 byte for type, 2 bytes for length, 1 byte for value type
        cursor:WriteU1(ENCODING_TYPES.DENSE_ARRAY)
        cursor:WriteU2(size)

        -- Check if all values are of the same type for further optimization
        if isHomogeneous and commonType then
            local valueTypeId = 0
            for typeName, typeId in pairs(Types.Types) do
                if typeName == commonType then
                    valueTypeId = typeId
                    break
                end
            end

            cursor:WriteU1(valueTypeId)

            -- Write values with specialized writer if available
            local writeFunc = Types.Writes[commonType]
            if writeFunc then
                for i = 1, size do
                    writeFunc(cursor, value[i])
                end
            else
                for i = 1, size do
                    Types.Writes.Any(cursor, value[i])
                end
            end
        else
            -- Mixed types in array
            cursor:WriteU1(0) -- 0 indicates mixed types
            for i = 1, size do
                Types.Writes.Any(cursor, value[i])
            end
        end

    elseif isStringMap then
        -- String key map encoding
        cursor:Allocate(3) -- 1 byte for type, 2 bytes for count
        cursor:WriteU1(ENCODING_TYPES.STRING_KEY_MAP)
        cursor:WriteU2(size)

        -- Write key-value pairs
        for k, v in pairs(value) do
            -- Write key length and key
            cursor:Allocate(1 + #k)
            cursor:WriteU1(#k)
            cursor:WriteString(k)

            -- Write value
            Types.Writes.Any(cursor, v)
        end

    elseif isHomogeneous and commonType and size > 0 then
        -- Homogeneous table encoding
        cursor:Allocate(4) -- 1 byte for type, 2 bytes for count, 1 byte for value type
        cursor:WriteU1(ENCODING_TYPES.HOMOGENEOUS)
        cursor:WriteU2(size)

        local valueTypeId = 0
        for typeName, typeId in pairs(Types.Types) do
            if typeName == commonType then
                valueTypeId = typeId
                break
            end
        end

        cursor:WriteU1(valueTypeId)

        -- Get specialized write function if available
        local writeFunc = Types.Writes[commonType]

        -- Write key-value pairs
        for k, v in pairs(value) do
            -- Write key (always use Any for keys)
            Types.Writes.Any(cursor, k)

            -- Write value with specialized writer if available
            if writeFunc then
                writeFunc(cursor, v)
            else
                Types.Writes.Any(cursor, v)
            end
        end

    else
        -- Mixed table encoding (fallback)
        cursor:Allocate(3) -- 1 byte for type, 2 bytes for count
        cursor:WriteU1(ENCODING_TYPES.MIXED)
        cursor:WriteU2(size)

        -- Write key-value pairs
        for k, v in pairs(value) do
            local keyType = typeof(k)

            if keyType == "number" and k % 1 == 0 and k >= 0 and k <= 4294967295 then
                -- Number key
                cursor:Allocate(5) -- 1 byte for key type, 4 bytes for key value
                cursor:WriteU1(1)
                cursor:WriteU4(k)

            elseif keyType == "string" then
                -- String key
                cursor:Allocate(2 + #k) -- 1 byte for key type, 1 byte for length, n bytes for key
                cursor:WriteU1(2)
                cursor:WriteU1(#k)
                cursor:WriteString(k)

            elseif keyType == "boolean" then
                -- Boolean key
                cursor:Allocate(2) -- 1 byte for key type, 1 byte for value
                cursor:WriteU1(3)
                cursor:WriteU1(k and 1 or 0)

            else
                -- Other key type - use Any
                cursor:Allocate(1) -- 1 byte for key type
                cursor:WriteU1(0)
                Types.Writes.Any(cursor, k)
            end

            -- Write value using Any type
            Types.Writes.Any(cursor, v)
        end
    end
end

-- Return the module
return {
    Read = Read,
    Write = Write
}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

-- Varables
local indices = {}
local values = Enum:GetEnums() :: {any} -- fix type checking bug

-- Initialize
for index, value in values do indices[value] = index end

return {

	Read = function(cursor: Cursor.Cursor)
		local offset = cursor.Index * 8
		cursor.Index += 3
		return values[buffer.readbits(cursor.Buffer, offset + 0, 12)]:FromValue(buffer.readbits(cursor.Buffer, offset + 12, 12))
	end,

	Write = function(cursor: Cursor.Cursor, value: EnumItem)
		cursor:Allocate(3)
		local offset = cursor.Index * 8
		buffer.writebits(cursor.Buffer, offset + 0, 12, indices[value.EnumType])
		buffer.writebits(cursor.Buffer, offset + 12, 12, value.Value)
		cursor.Index += 3
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return cursor:ReadInstance()
	end,

	Write = function(cursor: Cursor.Cursor, value: Instance)
		cursor:WriteInstance(value)
	end,

}


--!strict
--!optimize 2
--!native

--[[
	Serializes/Deserializes JSON-compatible Luau values using a Cursor.

	Supports:
	- strings
	- numbers (stored as F8/double)
	- booleans
	- nil
	- tables (arrays: sequential integer keys 1..n)
	- tables (objects: string keys)

	Does NOT support:
	- Luau-specific types (userdata, functions, threads)
	- Mixed key types in tables (will treat as object)
	- Non-sequential integer keys (will treat as object)
	- Cycles/shared references (will duplicate data)

	Format:
	- Type identifier (U1):
		0: Null (nil)
		1: Boolean (false)
		2: Boolean (true)
		3: Number (F8)
		4: String (U4 length + data)
		5: Array (U4 count + elements)
		6: Object (U4 count + key/value pairs)
	- Data (dependent on type)

    Author: Asta (@TheYusufGamer)
]]

-- Requires
local Cursor = require(script.Parent.Cursor) -- Adjust path if needed

-- Type Definitions
type JsonValue = string | number | boolean | nil | JsonArray | JsonObject
type JsonArray = { [number]: JsonValue }
type JsonObject = { [string]: JsonValue }
type Cursor = Cursor.Cursor

-- Constants for Type Identification
local TYPE_NULL = 0
local TYPE_FALSE = 1
local TYPE_TRUE = 2
local TYPE_NUMBER = 3
local TYPE_STRING = 4
local TYPE_ARRAY = 5
local TYPE_OBJECT = 6

-- Forward declaration for recursive functions
local WriteInternal
local ReadInternal

-- Internal Write Function
WriteInternal = function(cursor: Cursor, value: JsonValue)
	local valueType = typeof(value)

	if value == nil then
		cursor:Allocate(1)
		cursor:WriteU1(TYPE_NULL)
	elseif valueType == "boolean" then
		cursor:Allocate(1)
		cursor:WriteU1(if value then TYPE_TRUE else TYPE_FALSE)
	elseif valueType == "number" then
		cursor:Allocate(1 + 8)
		cursor:WriteU1(TYPE_NUMBER)
		cursor:WriteF8(value)
	elseif valueType == "string" then
		local len = #value
		cursor:Allocate(1 + 4 + len)
		cursor:WriteU1(TYPE_STRING)
		cursor:WriteU4(len)
		cursor:WriteString(value)
	elseif valueType == "table" then
		-- Determine if it's an array or object
		local count = 0
		local isArray = true
		for key, _ in pairs(value) do
			count += 1
			if typeof(key) ~= "number" or key < 1 or key % 1 ~= 0 then
				isArray = false
				-- No need to check further if it's already not an array
			end
		end

		-- Check sequential nature for arrays more strictly
		if isArray and count > 0 then
			for i = 1, count do
				if (value :: JsonArray)[i] == nil then
					isArray = false
					break
				end
			end
		elseif count == 0 then
			-- Empty table could be array or object, default to array representation.
			isArray = true
		end

		if isArray then
			-- Treat as Array
			cursor:Allocate(1 + 4)
			cursor:WriteU1(TYPE_ARRAY)
			local arrayLen = #value -- Use # for potentially sparse arrays treated as arrays
			cursor:WriteU4(arrayLen)
			for i = 1, arrayLen do
				WriteInternal(cursor, (value :: JsonArray)[i]) -- Recursive call
			end
		else
			-- Treat as Object
			cursor:Allocate(1 + 4)
			cursor:WriteU1(TYPE_OBJECT)
			cursor:WriteU4(count) -- Use counted pairs for objects
			for key, val in pairs(value) do
				if typeof(key) ~= "string" then
					error(`JSON serialization error: Object keys must be strings. Got type '{typeof(key)}'`)
				end
				-- Write Key (as a string)
				local keyLen = #key
				cursor:Allocate(4 + keyLen)
				cursor:WriteU4(keyLen)
				cursor:WriteString(key)
				-- Write Value (recursive)
				WriteInternal(cursor, val)
			end
		end
	else
		error(`JSON serialization error: Unsupported type '{valueType}'`)
	end
end

-- Internal Read Function
ReadInternal = function(cursor: Cursor): JsonValue
	local typeId = cursor:ReadU1()

	if typeId == TYPE_NULL then
		return nil
	elseif typeId == TYPE_FALSE then
		return false
	elseif typeId == TYPE_TRUE then
		return true
	elseif typeId == TYPE_NUMBER then
		return cursor:ReadF8()
	elseif typeId == TYPE_STRING then
		local len = cursor:ReadU4()
		return cursor:ReadString(len)
	elseif typeId == TYPE_ARRAY then
		local count = cursor:ReadU4()
		local array = table.create(count)
		for i = 1, count do
			array[i] = ReadInternal(cursor) -- Recursive call
		end
		return array
	elseif typeId == TYPE_OBJECT then
		local count = cursor:ReadU4()
		local object = {}
		for _ = 1, count do
			-- Read Key
			local keyLen = cursor:ReadU4()
			local key = cursor:ReadString(keyLen)
			-- Read Value
			local value = ReadInternal(cursor) -- Recursive call
			object[key] = value
		end
		return object
	else
		error(`JSON deserialization error: Invalid type identifier '{typeId}' at index {cursor.Index - 1}`)
	end
end

-- Public Interface
return {
	Read = ReadInternal,
	Write = WriteInternal,
}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return nil
	end,

	Write = function(cursor: Cursor.Cursor, value: nil)

	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return cursor:ReadF2()
	end,

	Write = function(cursor: Cursor.Cursor, value: number)
		cursor:Allocate(2)
		cursor:WriteF2(value)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return cursor:ReadF3()
	end,

	Write = function(cursor: Cursor.Cursor, value: number)
		cursor:Allocate(3)
		cursor:WriteF3(value)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return cursor:ReadF4()
	end,

	Write = function(cursor: Cursor.Cursor, value: number)
		cursor:Allocate(4)
		cursor:WriteF4(value)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return cursor:ReadF8()
	end,

	Write = function(cursor: Cursor.Cursor, value: number)
		cursor:Allocate(8)
		cursor:WriteF8(value)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return NumberRange.new(cursor:ReadF4(), cursor:ReadF4())
	end,

	Write = function(cursor: Cursor.Cursor, value: NumberRange)
		cursor:Allocate(8)
		cursor:WriteF4(value.Min)
		cursor:WriteF4(value.Max)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return cursor:ReadS2()
	end,

	Write = function(cursor: Cursor.Cursor, value: number)
		cursor:Allocate(2)
		cursor:WriteS2(value)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return cursor:ReadS4()
	end,

	Write = function(cursor: Cursor.Cursor, value: number)
		cursor:Allocate(4)
		cursor:WriteS4(value)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return cursor:ReadS1()
	end,

	Write = function(cursor: Cursor.Cursor, value: number)
		cursor:Allocate(1)
		cursor:WriteS1(value)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		local length = cursor:ReadU1()
		local keypoints = table.create(length)
		for index = 1, cursor:ReadU1() do
			table.insert(keypoints, NumberSequenceKeypoint.new(
				cursor:ReadU1() / 255,
				cursor:ReadU1() / 255,
				cursor:ReadU1() / 255
			))
		end
		return NumberSequence.new(keypoints)
	end,

	Write = function(cursor: Cursor.Cursor, value: NumberSequence)
		local length = #value.Keypoints
		cursor:Allocate(1 + length * 3)
		cursor:WriteU1(length)
		for index, keypoint in value.Keypoints do
			cursor:WriteU1(keypoint.Time * 255 + 0.5)
			cursor:WriteU1(keypoint.Value * 255 + 0.5)
			cursor:WriteU1(keypoint.Envelope * 255 + 0.5)
		end
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return cursor:ReadU2()
	end,

	Write = function(cursor: Cursor.Cursor, value: number)
		cursor:Allocate(2)
		cursor:WriteU2(value)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return cursor:ReadU3()
	end,

	Write = function(cursor: Cursor.Cursor, value: number)
		cursor:Allocate(3)
		cursor:WriteU3(value)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return cursor:ReadU4()
	end,

	Write = function(cursor: Cursor.Cursor, value: number)
		cursor:Allocate(4)
		cursor:WriteU4(value)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		local offset = cursor.Index * 8
		local value1 = buffer.readbits(cursor.Buffer, offset + 0, 4)
		local value2 = buffer.readbits(cursor.Buffer, offset + 4, 4)
		cursor.Index += 1
		return {value1, value2}
	end,

	Write = function(cursor: Cursor.Cursor, value: {number})
		cursor:Allocate(1)
		local offset = cursor.Index * 8
		buffer.writebits(cursor.Buffer, offset + 0, 4, value[1])
		buffer.writebits(cursor.Buffer, offset + 4, 4, value[2])
		cursor.Index += 1
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return cursor:ReadU1()
	end,

	Write = function(cursor: Cursor.Cursor, value: number)
		cursor:Allocate(1)
		cursor:WriteU1(value)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return Rect.new(cursor:ReadF4(), cursor:ReadF4(), cursor:ReadF4(), cursor:ReadF4())
	end,

	Write = function(cursor: Cursor.Cursor, value: Rect)
		cursor:Allocate(16)
		cursor:WriteF4(value.Min.X)
		cursor:WriteF4(value.Min.Y)
		cursor:WriteF4(value.Max.X)
		cursor:WriteF4(value.Max.Y)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return Region3.new(
			Vector3.new(cursor:ReadF4(), cursor:ReadF4(), cursor:ReadF4()),
			Vector3.new(cursor:ReadF4(), cursor:ReadF4(), cursor:ReadF4())
		)
	end,

	Write = function(cursor: Cursor.Cursor, value: Region3)
		cursor:Allocate(24)
		local halfSize = value.Size / 2
		local minimum = value.CFrame.Position - halfSize
		local maximum = value.CFrame.Position + halfSize
		cursor:WriteF4(minimum.X)
		cursor:WriteF4(minimum.Y)
		cursor:WriteF4(minimum.Z)
		cursor:WriteF4(maximum.X)
		cursor:WriteF4(maximum.Y)
		cursor:WriteF4(maximum.Z)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

-- Varables
local indices = {}
local values: {[any]: any} = {
	"DataStore Failed To Load",
	"Another Static String",
	math.pi,
	123456789,
	Vector3.new(1, 2, 3),
	"You can have upto 255 static values of any type"
}

-- Initialize
for index, value in values do indices[value] = index end

return {

	Read = function(cursor: Cursor.Cursor)
		return values[cursor:ReadU1()]
	end,

	Write = function(cursor: Cursor.Cursor, value: any)
		cursor:Allocate(1)
		cursor:WriteU1(indices[value] or 0)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return cursor:ReadString(cursor:ReadU1())
	end,

	Write = function(cursor: Cursor.Cursor, value: string)
		cursor:Allocate(1 + #value)
		cursor:WriteU1(#value)
		cursor:WriteString(value)
	end,

}


--!strict

--[[
	S8		Minimum: -128			Maximum: 127
	S16		Minimum: -32768			Maximum: 32767
	S24		Minimum: -8388608		Maximum: 8388607
	S32		Minimum: -2147483648	Maximum: 2147483647

	U8		Minimum: 0				Maximum: 255
	U16		Minimum: 0				Maximum: 65535
	U24		Minimum: 0				Maximum: 16777215
	U32		Minimum: 0				Maximum: 4294967295

	F16		±2048					[65520]
	F24		±262144					[4294959104]
	F32		±16777216				[170141183460469231731687303715884105728]
	F64		±9007199254740992		[huge]
]]

-- Types
export type Types = {
	Any:					any,
	Static:					any,
	Nil:					nil,
	NumberS8:				number,
	NumberS16:				number,
	NumberS24:				number,
	NumberS32:				number,
	NumberU4:				{number},
	NumberU8:				number,
	NumberU16:				number,
	NumberU24:				number,
	NumberU32:				number,
	NumberF16:				number,
	NumberF24:				number,
	NumberF32:				number,
	NumberF64:				number,
	Boolean1:				{boolean},
	Boolean8:				boolean,
	Characters:				string,
	Binary:					string,
	String:					string,
	Buffer:					buffer,
	Vector2S16:				Vector2,
	Vector2F24:				Vector2,
	Vector2F32:				Vector2,
	Vector3S16:				Vector3,
	Vector3F24:				Vector3,
	Vector3F32:				Vector3,
	CFrameF24U8:			CFrame,
	CFrameF32U8:			CFrame,
	CFrameF32U16:			CFrame,
	NumberRange:			NumberRange,
	NumberSequence:			NumberSequence,
	Color3:					Color3,
	ColorSequence:			ColorSequence,
	BrickColor:				BrickColor,
	UDim:					UDim,
	UDim2:					UDim2,
	Rect:					Rect,
	Region3:				Region3,
	EnumItem:				EnumItem,
	Instance:				Instance,
}

-- Varables
local names = {}			:: Types
local reads = {}			:: {[string]: (cursor: any) -> any}
local writes = {}			:: {[string]: (cursor: any, value: any) -> ()}
local types = {
	Nil =					"nil",
	NumberS8 =				"number",
	NumberS16 =				"number",
	NumberS24 =				"number",
	NumberS32 =				"number",
	NumberU4 =				"table",
	NumberU8 =				"number",
	NumberU16 =				"number",
	NumberU24 =				"number",
	NumberU32 =				"number",
	NumberF16 =				"number",
	NumberF24 =				"number",
	NumberF32 =				"number",
	NumberF64 =				"number",
	Boolean1 =				"table",
	Boolean8 =				"boolean",
	Characters =			"string",
	Binary = 				"string",
	String =				"string",
	Buffer =				"buffer",
	Vector2S16 =			"Vector2",
	Vector2F24 =			"Vector2",
	Vector2F32 =			"Vector2",
	Vector3S16 =			"Vector3",
	Vector3F24 =			"Vector3",
	Vector3F32 =			"Vector3",
	CFrameF24U8 =			"CFrame",
	CFrameF32U8 =			"CFrame",
	CFrameF32U16 =			"CFrame",
	NumberRange =			"NumberRange",
	NumberSequence =		"NumberSequence",
	Color3 =				"Color3",
	ColorSequence =			"ColorSequence",
	BrickColor =			"BrickColor",
	UDim =					"UDim",
	UDim2 =					"UDim2",
	Rect =					"Rect",
	Region3 =				"Region3",
	EnumItem =				"EnumItem",
	Instance =				"Instance",
}

-- Initialize
for _, moduleScript in script.Parent:GetChildren() do
	if moduleScript == script then continue end
	if moduleScript == script.Parent.Cursor then continue end
	local typeFunctions = require(moduleScript) :: any
	names[moduleScript.Name] = moduleScript.Name
	reads[moduleScript.Name] = typeFunctions.Read
	writes[moduleScript.Name] = typeFunctions.Write
end

return {Names = names, Types = types, Reads = reads, Writes = writes}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return UDim.new(cursor:ReadS2() / 1000, cursor:ReadS2())
	end,

	Write = function(cursor: Cursor.Cursor, value: UDim)
		cursor:Allocate(4)
		cursor:WriteS2(value.Scale * 1000 + 0.5)
		cursor:WriteS2(value.Offset)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return UDim2.new(cursor:ReadS2() / 1000, cursor:ReadS2(), cursor:ReadS2() / 1000, cursor:ReadS2())
	end,

	Write = function(cursor: Cursor.Cursor, value: UDim2)
		cursor:Allocate(8)
		cursor:WriteS2(value.X.Scale * 1000 + 0.5)
		cursor:WriteS2(value.X.Offset)
		cursor:WriteS2(value.Y.Scale * 1000 + 0.5)
		cursor:WriteS2(value.Y.Offset)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return Vector2.new(cursor:ReadF3(), cursor:ReadF3())
	end,

	Write = function(cursor: Cursor.Cursor, value: Vector2)
		cursor:Allocate(6)
		cursor:WriteF3(value.X)
		cursor:WriteF3(value.Y)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return Vector2.new(cursor:ReadF4(), cursor:ReadF4())
	end,

	Write = function(cursor: Cursor.Cursor, value: Vector2)
		cursor:Allocate(8)
		cursor:WriteF4(value.X)
		cursor:WriteF4(value.Y)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return Vector2.new(cursor:ReadS2(), cursor:ReadS2())
	end,

	Write = function(cursor: Cursor.Cursor, value: Vector2)
		cursor:Allocate(4)
		cursor:WriteS2(value.X + 0.5)
		cursor:WriteS2(value.Y + 0.5)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return Vector3.new(cursor:ReadF3(), cursor:ReadF3(), cursor:ReadF3())
	end,

	Write = function(cursor: Cursor.Cursor, value: Vector3)
		cursor:Allocate(9)
		cursor:WriteF3(value.X)
		cursor:WriteF3(value.Y)
		cursor:WriteF3(value.Z)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return Vector3.new(cursor:ReadF4(), cursor:ReadF4(), cursor:ReadF4())
	end,

	Write = function(cursor: Cursor.Cursor, value: Vector3)
		cursor:Allocate(12)
		cursor:WriteF4(value.X)
		cursor:WriteF4(value.Y)
		cursor:WriteF4(value.Z)
	end,

}


--!strict
--!optimize 2
--!native

-- Requires
local Cursor = require(script.Parent.Cursor)

return {

	Read = function(cursor: Cursor.Cursor)
		return Vector3.new(cursor:ReadS2(), cursor:ReadS2(), cursor:ReadS2())
	end,

	Write = function(cursor: Cursor.Cursor, value: Vector3)
		cursor:Allocate(6)
		cursor:WriteS2(value.X + 0.5)
		cursor:WriteS2(value.Y + 0.5)
		cursor:WriteS2(value.Z + 0.5)
	end,

}


--!optimize 2

--[[
    NetRay - High Performance Roblox Networking Library (v1.1.2)

    Author: Asta (@TheYusufGamer)
    Created: 2025-03-22
]]

local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local PlayersService = game:GetService("Players")

--==============================================================================
-- Type Imports (Shared & Dependencies)
--==============================================================================

-- Third Party
local SignalPlusModule = require(script.ThirdParty.SignalPlus)
local PromiseModule = require(script.ThirdParty.Promise)
type SignalPlusFactory = () -> SignalPlus -- Type for the factory function itself
type SignalPlus = SignalPlusModule.SignalPlus -- Type for the instance returned
type Promise<T> = PromiseModule.Promise<T> -- Assuming Promise exports generic type

-- Shared Modules (Assume these export their Module types)
local UtilitiesModule = require(script.Shared.Utilities)
local ErrorsModule = require(script.Shared.Errors)
local SerializerModule = require(script.Shared.Serializer)
local TypeCheckerModule = require(script.Shared.TypeChecker)
local MiddlewareModule = require(script.Shared.Middleware)
local CircuitBreakerModule = require(script.Shared.CircuitBreaker)
type UtilitiesType = UtilitiesModule.UtilitiesModule
type ErrorsType = ErrorsModule.ErrorsModule
type SerializerType = SerializerModule.NetRaySerializerModule
type TypeCheckerType = TypeCheckerModule.TypeCheckerModule
type Middleware = MiddlewareModule.Middleware
type CircuitBreaker = CircuitBreakerModule.CircuitBreaker

--==============================================================================
-- Core Type Definitions (Needed Globally)
--==============================================================================

type Player = PlayersService.Player

-- Define Unified Event Type for API consistency
export type NetRayEvent = {
	Name: string,
	Options: {[string]: any}?,
	RemoteEvent: RemoteEvent,
	TypeDefinition: any?,
	Priority: number,
	OnEvent: (self: NetRayEvent, callback: ((player: Player, data: any) -> ()) | ((data: any) -> ())) -> NetRayEvent,
	FireServer: ((self: NetRayEvent, data: any) -> ())?,
	FireClient: ((self: NetRayEvent, player: Player, data: any) -> ())?,
	FireAllClients: ((self: NetRayEvent, data: any) -> ())?,
	FireAllClientsExcept: ((self: NetRayEvent, excludedPlayer: Player, data: any) -> ())?,
	FireFilteredClients: ((self: NetRayEvent, filter: (player: Player) -> boolean, data: any) -> ())?,
}

-- Define Debug structure type
type DebugType = {
	GlobalEvent: SignalPlus,
	Error: SignalPlus,
	NetworkTraffic: SignalPlus,
	EnableMonitoring: (options: {enabled: boolean}?) -> boolean,
}

-- Base type only includes shared *data* properties
type NetRayBaseModuleData = {
	Priority: {[string]: number},
	Version: string,
	Debug: DebugType,
	Utils: UtilitiesType,
	Errors: ErrorsType,
	Serializer: SerializerType,
	TypeChecker: TypeCheckerType,
}

--==============================================================================
-- Module Implementation Setup
--==============================================================================

local NetRay: any = {} -- Start as 'any', will be cast to specific type at the end
NetRay.__index = NetRay

local SignalPlusFactory: SignalPlusFactory = SignalPlusModule

-- Assign shared data properties
NetRay.Priority = {
	CRITICAL = 0,
	HIGH = 1,
	NORMAL = 2,
	LOW = 3,
	BACKGROUND = 4
}
NetRay.Version = "1.1.5"

NetRay.Debug = {
	GlobalEvent = SignalPlusFactory(),
	Error = SignalPlusFactory(),
	NetworkTraffic = SignalPlusFactory(),
	EnableMonitoring = function(options: {enabled: boolean}?)
		options = options or {}
		local isEnabled = options.enabled ~= false
		-- TODO: Implement actual manager signaling if needed
		print("[NetRay] Monitoring " .. (isEnabled and "Enabled" or "Disabled"))
		return isEnabled
	end,
} :: DebugType

NetRay.Utils = UtilitiesModule
NetRay.Errors = ErrorsModule
NetRay.Serializer = SerializerModule
NetRay.TypeChecker = TypeCheckerModule

--==============================================================================
-- Context-Specific Initialization & FINAL TYPE DEFINITION
--==============================================================================

if RunService:IsServer() then
	-- Server Context --

	-- Require Server Modules
	local ServerManager_Module = require(script.Server.ServerManager)
	local ServerEvent_Module = require(script.Server.ServerEvent)
	local RequestServer_Module = require(script.Server.RequestServer)

	-- Define Local Aliases for required module types
	type ServerManager = ServerManager_Module.ServerManager
	type ServerEventType = ServerEvent_Module.ServerEventType
	type RequestServerType = RequestServer_Module.RequestServerType
	type ServerEventOptions = ServerEvent_Module.ServerEventOptions -- Import options type
	type RequestServerOptions = RequestServer_Module.RequestServerOptions -- Import options type

	-- Define the COMPLETE Server Module Type HERE, using the specific types
	export type NetRayServerModule = NetRayBaseModuleData & {
		-- Properties
		Server: ServerManager,
		-- Method Signatures
		RegisterMiddleware: (self: NetRayServerModule, name: string, middlewareFn: any, priority: number?) -> (),
		GetCircuitBreaker: (self: NetRayServerModule, eventName: string) -> CircuitBreaker?,
		RegisterEvent: (self: NetRayServerModule, eventName: string, options: ServerEventOptions?) -> ServerEventType, -- Specific Return Type
		RegisterRequestEvent: (self: NetRayServerModule, eventName: string, options: RequestServerOptions?) -> RequestServerType, -- Specific Return Type
	}

	-- Implement Server API Methods on NetRay Table
	local serverManager = ServerManager_Module.new()
	NetRay.Server = serverManager :: ServerManager -- Assign typed manager instance

	-- Connect signals
	for signalName, signal in pairs(serverManager.Signals) do
		if signalName == "Error" then
			signal:Connect(function(...)
				NetRay.Debug.Error:Fire("Server", ...)
			end)
		else
			signal:Connect(function(...)
				NetRay.Debug.GlobalEvent:Fire("Server", signalName, ...)
			end)
		end
	end

	-- Implement shared/server methods
	function NetRay:RegisterMiddleware(name: string, middlewareFn: any, priority: number?) : Middleware?
		-- self.Server is guaranteed defined in this block
		if self.Server and self.Server.Middleware then
			self.Server.Middleware:Register(name, middlewareFn, priority)
		else
			warn("[NetRay S] Middleware registration failed - manager not ready?")
		end
	end

	function NetRay:GetCircuitBreaker(eventName: string): CircuitBreaker?
		return self.Server.CircuitBreakers[eventName]
	end

	function NetRay:RegisterEvent(eventName: string, options: ServerEventOptions?): ServerEventType
		-- Pass ServerManager instance (self.Server)
		return ServerEvent_Module.new(eventName, options, self.Server) :: ServerEventType
	end

	function NetRay:RegisterRequestEvent(eventName: string, options: RequestServerOptions?): RequestServerType
		-- Pass ServerManager instance (self.Server)
		local requestServer = RequestServer_Module.new(eventName, options, self.Server)
		return requestServer :: RequestServerType
	end

	-- Create remotes folder (deferred)
	task.defer(function()
		local remoteFolder = ReplicatedStorage:FindFirstChild("NetRayRemotes")
		if not remoteFolder then
			remoteFolder = Instance.new("Folder")
			remoteFolder.Name = "NetRayRemotes"
			remoteFolder.Parent = ReplicatedStorage
			print("[NetRay] Created NetRayRemotes folder.")
		end
	end)

	-- Initialize Server Manager Components
	serverManager:Initialize()

	-- Return the NetRay table, cast to the type DEFINED WITHIN THIS BLOCK
	return NetRay :: NetRayServerModule

else
	-- Client Context --

	-- Require Client Modules
	local ClientManager_Module = require(script.Client.ClientManager)
	local ClientEvent_Module = require(script.Client.ClientEvent)
	local RequestClient_Module = require(script.Client.RequestClient)

	-- Define Local Aliases for required module types
	type ClientManager = ClientManager_Module.ClientManager
	type ClientEventType = ClientEvent_Module.ClientEventType
	type RequestClientType = RequestClient_Module.RequestClientType
	type ClientEventOptions = ClientManager_Module.ClientEventOptions -- Import options type
	type RequestClientOptions = RequestClient_Module.RequestClientOptions -- Import options type

	-- Define the COMPLETE Client Module Type HERE, using the specific types
	export type NetRayClientModule = NetRayBaseModuleData & {
		-- Properties
		Client: ClientManager,
		-- Method Signatures
		RegisterMiddleware: (self: NetRayClientModule, name: string, middlewareFn: any, priority: number?) -> (),
		GetCircuitBreaker: (self: NetRayClientModule, eventName: string) -> CircuitBreaker?,
		GetEvent: (self: NetRayClientModule, eventName: string) -> NetRayEvent, -- Returns unified type
		RegisterRequestEvent: (self: NetRayClientModule, eventName: string, options: RequestClientOptions?) -> RequestClientType, -- Specific Return Type
	}

	-- Implement Client API Methods on NetRay Table
	local clientManager = ClientManager_Module.new()
	NetRay.Client = clientManager :: ClientManager -- Assign typed manager instance

	-- Connect signals
	for signalName, signal in pairs(clientManager.Signals) do
		if signalName == "Error" then
			signal:Connect(function(...)
				NetRay.Debug.Error:Fire("Client", ...)
			end)
		else
			signal:Connect(function(...)
				NetRay.Debug.GlobalEvent:Fire("Client", signalName, ...)
			end)
		end
	end

	-- Implement shared/client methods
	function NetRay:RegisterMiddleware(name: string, middlewareFn: any, priority: number?) : Middleware?
		if self.Client and self.Client.Middleware then
			self.Client.Middleware:Register(name, middlewareFn, priority)
		else
			warn("[NetRay C] Middleware registration failed - manager not ready?")
		end
	end

	function NetRay:GetCircuitBreaker(eventName: string): CircuitBreaker?
		return self.Client.CircuitBreakers[eventName]
	end

	function NetRay:GetEvent(eventName: string): NetRayEvent?
		local eventInfo = self.Client.Events[eventName]
		if eventInfo and eventInfo.Event then
			return eventInfo.Event :: NetRayEvent
		end

		local newEvent = ClientEvent_Module.new(eventName, nil, self.Client)

		if newEvent then
			return newEvent :: NetRayEvent
		else
			warn(("[NetRay] GetEvent: Failed create/find RemoteEvent for '%s'. Ensure registered on server & replicated."):format(eventName))
			return nil
		end
	end

	function NetRay:RegisterRequestEvent(eventName: string, options: RequestClientOptions?): RequestClientType?
		-- Pass ClientManager instance (self.Client)
		local requestClient = RequestClient_Module.new(eventName, options, self.Client)
		return requestClient :: RequestClientType?
	end

	-- Initialize Client Manager Components
	clientManager:Initialize()

	return NetRay :: NetRayClientModule
end


--!strict
--BindUtils.luau
--[[
  //////////////////////
 //   Bind Utility   //
//////////////////////
]]

local BindUtils = {}

local IAS = require("./TypeDefinition")

local REMOVED: IAS.Bind = {
	KeyCode = Enum.KeyCode.Unknown,
	PrimaryModifier = Enum.KeyCode.Unknown,
	SecondaryModifier = Enum.KeyCode.Unknown
}

@native local function convertStrToEnumKeycode(name: IAS.supposedKeyCode?): Enum.KeyCode
	return (if typeof(name) == "string" then Enum.KeyCode:FromName(name) else name) or Enum.KeyCode.Unknown
end

local function getBind(binds: IAS.Binds, name: string): IAS.Bind?
	return binds[name]
end

function BindUtils.AddBind(binds: IAS.Binds, name: string, keyCode: IAS.supposedKeyCode?, PrimaryModifier: IAS.supposedKeyCode?, SecondaryModifier: IAS.supposedKeyCode?): boolean
	if binds[name] ~= nil then return false end

	binds[name] = {
		KeyCode = convertStrToEnumKeycode(keyCode),
		PrimaryModifier = convertStrToEnumKeycode(PrimaryModifier),
		SecondaryModifier = convertStrToEnumKeycode(SecondaryModifier)
	}

	return true
end

function BindUtils.RemoveBind(binds: IAS.Binds, name: string): boolean
	if binds[name] == nil then return false end
	binds[name] = nil
	return true
end

local function setKeyCode(binds: IAS.Binds, name: string, keyCode: IAS.supposedKeyCode): boolean
	local bind = getBind(binds, name)
	if not bind then return false end
	bind.KeyCode = convertStrToEnumKeycode(keyCode)
	return true
end

local function setModifiers(binds: IAS.Binds, name: string, PrimaryModifier: IAS.supposedKeyCode?, SecondaryModifier: IAS.supposedKeyCode?): boolean
	local bind = getBind(binds, name)
	if not bind then return false end
	bind.PrimaryModifier = convertStrToEnumKeycode(PrimaryModifier)
	bind.SecondaryModifier = convertStrToEnumKeycode(SecondaryModifier)
	return true
end

function BindUtils.SetKeyCode(binds, name, keyCode)
	return setKeyCode(binds, name, keyCode)
end

function BindUtils.ResetKeyCode(binds, name)
	return setKeyCode(binds, name, Enum.KeyCode.Unknown)
end

function BindUtils.ClearModifiers(binds, name)
	return setModifiers(binds, name)
end

function BindUtils.ResetBinds(binds: IAS.Binds): boolean
	for name in binds do
		binds[name] = nil
	end

	binds.MainAction = {
		KeyCode = Enum.KeyCode.Unknown,
		PrimaryModifier = Enum.KeyCode.Unknown,
		SecondaryModifier = Enum.KeyCode.Unknown
	}

	return true
end

function BindUtils.SetModifiers(binds, name, PrimaryModifier, SecondaryModifier)
	return setModifiers(binds, name, PrimaryModifier, SecondaryModifier)
end

@native function BindUtils.Clone(binds: IAS.Binds): IAS.Binds
	local clone: IAS.Binds = {}

	for name, bind in binds do
		clone[name] = {
			KeyCode = bind.KeyCode,
			PrimaryModifier = bind.PrimaryModifier,
			SecondaryModifier = bind.SecondaryModifier
		}
	end

	return clone
end

@native function BindUtils.CheckDifferences(base: IAS.Binds, compare: IAS.Binds): (boolean, IAS.Binds?)
	local differences: IAS.Binds = {}
	local hasDifferences = false

	for name, compareBind in compare do
		local baseBind = base[name]

		if not baseBind then
			hasDifferences = true
			differences[name] = {
				KeyCode = compareBind.KeyCode,
				PrimaryModifier = compareBind.PrimaryModifier,
				SecondaryModifier = compareBind.SecondaryModifier
			}
			continue
		end

		local entryDiff = {}

		if baseBind.KeyCode ~= compareBind.KeyCode then
			entryDiff.KeyCode = compareBind.KeyCode
		end

		if baseBind.PrimaryModifier ~= compareBind.PrimaryModifier then
			entryDiff.PrimaryModifier = compareBind.PrimaryModifier
		end

		if baseBind.SecondaryModifier ~= compareBind.SecondaryModifier then
			entryDiff.SecondaryModifier = compareBind.SecondaryModifier
		end

		if next(entryDiff) ~= nil then
			hasDifferences = true
			differences[name] = entryDiff
		end
	end

	for name in base do
		if compare[name] == nil then
			hasDifferences = true
			differences[name] = REMOVED
		end
	end

	if not hasDifferences then
		return true, nil
	end

	return false, differences
end

@native function BindUtils.ApplyDifferences(target: IAS.Binds, differences: IAS.Binds): boolean
	local changed = false

	for name, diff in differences do
		if diff == REMOVED then
			if target[name] ~= nil then
				target[name] = nil
				changed = true
			end
			continue
		end

		local bind = target[name]
		if not bind then
			bind = {
				KeyCode = Enum.KeyCode.Unknown,
				PrimaryModifier = Enum.KeyCode.Unknown,
				SecondaryModifier = Enum.KeyCode.Unknown,
			}
			target[name] = bind
			changed = true
		end

		if diff.KeyCode ~= nil and bind.KeyCode ~= diff.KeyCode then
			bind.KeyCode = diff.KeyCode
			changed = true
		end

		if diff.PrimaryModifier ~= nil and bind.PrimaryModifier ~= diff.PrimaryModifier then
			bind.PrimaryModifier = diff.PrimaryModifier
			changed = true
		end

		if diff.SecondaryModifier ~= nil and bind.SecondaryModifier ~= diff.SecondaryModifier then
			bind.SecondaryModifier = diff.SecondaryModifier
			changed = true
		end

	end

	return changed
end

@native function BindUtils.GetDifference(base: IAS.Binds, differences: IAS.Binds)
	local results: { IAS.DiffList } = {}

	for bindName, diff in differences do
		local baseBind = base[bindName]

		if diff == REMOVED then
			if baseBind then
				table.insert(results, {
					BindName = bindName,
					ChangeType = "Removed",

					OldKeyCode = baseBind.KeyCode,
					NewKeyCode = nil,

					OldModifiers = {
						PrimaryModifier = baseBind.PrimaryModifier,
						SecondaryModifier = baseBind.SecondaryModifier,
					},

					NewModifiers = {
						PrimaryModifier = Enum.KeyCode.Unknown,
						SecondaryModifier = Enum.KeyCode.Unknown,
					},

					AddedModifiers = {
						PrimaryModifier = Enum.KeyCode.Unknown,
						SecondaryModifier = Enum.KeyCode.Unknown,
					},

					RemovedModifiers = {
						PrimaryModifier = baseBind.PrimaryModifier,
						SecondaryModifier = baseBind.SecondaryModifier,
					},
				})
			end
			continue
		end

		if not baseBind then
			table.insert(results, {
				BindName = bindName,
				ChangeType = "Added",

				OldKeyCode = nil,
				NewKeyCode = diff.KeyCode,

				OldModifiers = {
					PrimaryModifier = Enum.KeyCode.Unknown,
					SecondaryModifier = Enum.KeyCode.Unknown,
				},

				NewModifiers = {
					PrimaryModifier = diff.PrimaryModifier,
					SecondaryModifier = diff.SecondaryModifier,
				},

				AddedModifiers = {
					PrimaryModifier = diff.PrimaryModifier,
					SecondaryModifier = diff.SecondaryModifier,
				},

				RemovedModifiers = {
					PrimaryModifier = Enum.KeyCode.Unknown,
					SecondaryModifier = Enum.KeyCode.Unknown,
				},
			})
			continue
		end

		local oldPM = baseBind.PrimaryModifier
		local oldSM = baseBind.SecondaryModifier
		local newPM = diff.PrimaryModifier or oldPM
		local newSM = diff.SecondaryModifier or oldSM

		local addedPM = Enum.KeyCode.Unknown
		local addedSM = Enum.KeyCode.Unknown
		local removedPM = Enum.KeyCode.Unknown
		local removedSM = Enum.KeyCode.Unknown

		if oldPM ~= newPM then
			if newPM ~= Enum.KeyCode.Unknown then
				addedPM = newPM
			end
			if oldPM ~= Enum.KeyCode.Unknown then
				removedPM = oldPM
			end
		end

		if oldSM ~= newSM then
			if newSM ~= Enum.KeyCode.Unknown then
				addedSM = newSM
			end
			if oldSM ~= Enum.KeyCode.Unknown then
				removedSM = oldSM
			end
		end

		table.insert(results, {
			BindName = bindName,
			ChangeType = "Modified",

			OldKeyCode = baseBind.KeyCode,
			NewKeyCode = diff.KeyCode or baseBind.KeyCode,

			OldModifiers = {
				PrimaryModifier = oldPM,
				SecondaryModifier = oldSM,
			},

			NewModifiers = {
				PrimaryModifier = newPM,
				SecondaryModifier = newSM,
			},

			AddedModifiers = {
				PrimaryModifier = addedPM,
				SecondaryModifier = addedSM,
			},

			RemovedModifiers = {
				PrimaryModifier = removedPM,
				SecondaryModifier = removedSM,
			},
		})
	end

	return results
end

return BindUtils


--!strict
--IllusionSignal.luau
--[[
  //////////////////////
 //  Illusion Signal //
//////////////////////
]]

local IAS = require("./TypeDefinition")

return {
	new = function(): IAS.IAScriptSignal
		local head: IAS.Node? = nil

		local function createConnection(node: IAS.Node): IAS.IAScriptConnection
			local connected = true

			local conn: IAS.IAScriptConnection = {
				Connected = true,
				Disconnect = function(self)
					if not connected then return end
					connected = false
					self.Connected = false

					if head == node then
						head = node.next
						return
					end

					local current = head
					while current and current.next ~= node do
						current = current.next
					end

					if current then
						current.next = node.next
					end
				end,
			}

			return conn
		end

		local Signal: IAS.IAScriptSignal = {
			Connect = function(self: IAS.IAScriptSignal, fn: IAS.func): IAS.IAScriptConnection
				local node: IAS.Node = {
					fn = fn,
					next = head,
				}

				head = node
				return createConnection(node)
			end,

			Once = function(self: IAS.IAScriptSignal, fn: IAS.func): IAS.IAScriptConnection
				local fired = false
				local conn: IAS.IAScriptConnection

				conn = self:Connect(function(...)
					if fired then return end
					fired = true
					conn:Disconnect()
					fn(...)
				end)

				return conn
			end,

			Wait = function(self: IAS.IAScriptSignal): IAS.variant
				local thread = coroutine.running()
				local conn: IAS.IAScriptConnection
				local activeValue: IAS.variant? = nil

				conn = self:Connect(function(active: IAS.variant)
					conn:Disconnect()
					activeValue = active
					task.spawn(thread)
				end)

				coroutine.yield()
				return activeValue :: IAS.variant
			end,

			Fire = function(self: IAS.IAScriptSignal, active: IAS.variant, pressed: boolean?)
				local node = head
				while node do
					local fn = node.fn
					node = node.next
					if fn then
						fn(active, pressed)
					end
				end
			end,

			DisconnectAll = function(self)
				local node = head
				while node do
					node.fn = nil
					local nextNode = node.next
					node.next = nil
					node = nextNode
				end
				head = nil
			end

		}

		return Signal
	end
}


local fs = require('@lune/fs')
local process = require('@lune/process')

local function destroy_dot_git()
	if not fs.isDir('.git') then return end
	fs.removeDir('.git')
end

local function self_delete()
	if fs.isDir('.create') then fs.removeDir('.create') end
end

local function initiate_git_repository()
	process.exec('git', { 'init' }, { stdio = 'forward' })
	process.exec('git', { 'branch', '-M', 'main' }, { stdio = 'forward' })
end

destroy_dot_git()
initiate_git_repository()
self_delete()


-- MIT License

-- Copyright (c) 2022 centau

-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:

-- The above copyright notice and this permission notice shall be included in all
-- copies or substantial portions of the Software.

-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.
return function(obj: any, err_level: number?)
	return if typeof(obj) == 'RBXScriptConnection'
		then function()
			obj:Disconnect()
		end
		elseif type(obj) == 'thread' then function()
			task.cancel(obj)
		end
		elseif typeof(obj) == 'Instance' then function()
			obj:Destroy()
		end
		elseif obj.destroy then function()
			obj:destroy()
		end
		elseif obj.disconnect then function()
			obj:disconnect()
		end
		elseif obj.Destroy then function()
			obj:Destroy()
		end
		elseif obj.Disconnect then function()
			obj:Disconnect()
		end
		else error('cannot cleanup given object', err_level or 2)
end


local task = if game then task else require '@lune/task' :: typeof(task)

export type options = {
	base_delay: number?,
	max_retries: number?,
}

return function<Result>(fn: () -> Result)
	return function(opts: options): (boolean, Result?)
		local base = opts.base_delay or 2
		local max_retries = opts.max_retries or 5
		local ok, result = false, nil
		local attempts = 0

		repeat
			ok, result = pcall(fn)
			if ok then break end

			attempts += 1
			if attempts >= max_retries then break end

			local max_wait_ceiling = base ^ attempts
			local final_wait = math.random() * max_wait_ceiling
			task.wait(final_wait)
		until ok or attempts >= max_retries

		return ok, result
	end
end


local cleanup = if game
	then require(script.Parent.cleanup)
	else require './cleanup'

local function deleted_err()
	error('Cannot use pool after destroy.', 2)
end

return function<obj>()
	local strict = false
	local constructor = nil

	local stack = {} :: { obj }
	local exports = {}
	type self = typeof(exports)

	function exports.register_constructor<T>(self: self, fn: () -> obj)
		constructor = fn
		return exports
	end

	function exports.preallocate(self: self, size: number)
		assert(typeof(constructor) == 'function')

		for i = 1, size do
			table.insert(stack, constructor())
		end

		return exports
	end

	function exports.get(self: self): obj?
		if #stack > 0 then return table.remove(stack) end
		if not strict and constructor then return constructor() end
		return nil
	end

	function exports.release(self: self, obj: obj)
		table.insert(stack, obj)
	end

	function exports.size(self: self): number
		return #stack
	end

	function exports.destroy(self: self)
		for index in exports do
			exports[index] = nil
		end

		setmetatable(exports, { __index = deleted_err })

		for _, obj in stack do
			cleanup(obj, 3)
		end

		stack = nil :: any
		constructor = nil :: any
		strict = nil :: any
	end

	function exports.enable_strict(self: self)
		strict = true
		return self
	end

	return exports
end