--@EternityDev
--!strict
--!native
--!optimize 2
local Raycast = {}
Raycast.__index = Raycast

local RunService = game:GetService("RunService")
local Signal = require(script.Signal)

local PreSimulation = RunService.PreSimulation
local sampleActor = script:WaitForChild("Actor")

function Raycast.new(header: string, directory: Instance, raycastParam: RaycastParams?, actors: number?)
	if not header then
		header = tostring(math.random(99, 1999))
	end
	if not raycastParam then
		raycastParam = RaycastParams.new()
	end

	local meta = setmetatable({}, Raycast)
	meta.filter = raycastParam
	meta.origin = Vector3.new()
	meta.direction = Vector3.new()
	meta.state = "inactive"
	meta.inputs = {}
	meta.outputs = {}
	meta.connections = {}
	meta.task = {}
	meta._actors = {}

	meta.Signal = Signal(`Signal-{header}`)

	if not directory:FindFirstChild(`Actors-{header}`) then
		Instance.new("Folder", directory).Name = `Actors-{header}`
	end

	for idx=1,actors or 4 do
		local actor = sampleActor:Clone()
		meta.inputs[idx] = actor.Input
		meta.outputs[idx] = actor.Output.Event

		if RunService:IsClient() then
			actor.Client.Enabled = true
			actor.Server.Enabled = false
		else
			actor.Client.Enabled = false
			actor.Server.Enabled = true
		end
		table.insert(meta._actors, actor)
		actor.Parent = directory:FindFirstChild(`Actors-{header}`)
	end

	meta:_bind()
	return meta
end

function Raycast:updateFilter(raycastParam: RaycastParams)
	if not raycastParam then return end
	self.filter = raycastParam
end

function Raycast:setOrigin(origin: Vector3)
	if not origin then return end
	self.origin = origin
end

function Raycast:setDirection(direction: Vector3)
	if not direction then return end
	self.direction = direction
end

function Raycast:_unbind()
	for _, c in self.connections do
		c:Disconnect()
	end
	table.clear(self.connections)
end

function Raycast:_bind()
	for idx, output: RBXScriptSignal in self.outputs do
		self.connections[idx] = output:Connect(function(...)
			self.Signal:Fire(...)
		end)
	end
end

function Raycast:run(forceRun: boolean?)
	if not forceRun then
		if self.state == "active" then return end
	end
	self.state = "active"
	local newTask
	newTask = task.spawn(function()
		while PreSimulation:Wait() do
			for _, input: BindableEvent in self.inputs do
				input:Fire(self.origin, self.direction, self.filter)
			end
		end
	end)
	table.insert(self.task, newTask)
end

function Raycast:stop()
	if self.state == "inactive" then return end

	for _, t in self.task do
		task.cancel(t)
	end
	table.clear(self.task)

	self.state = "inactive"
end

function Raycast:destroy()
	self:stop()
	self:_unbind()
	self.Signal:Destroy()
	for _, actor in self._actors do
		actor.Parent:Destroy()
		break
	end
	table.clear(self)
	setmetatable(self, nil)
end

return Raycast :: typeof(Raycast)


--!optimize 2
--!native
local Surround = {}

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

local scheduler = require(ReplicatedStorage:WaitForChild("scheduler"))

local begin_profiler, end_profiler = scheduler.profiler.Begin, scheduler.profiler.End
local Player: Player = game:GetService("Players").LocalPlayer

type Object = {
	root: BasePart,
	sound: Sound,
	baseVolume: number,
	max_distance: number,
}

local default_max_distance: number = 15
local throttle: number = 1 / 24
local last_throttle: number = 0
local objects: { Object } = {}
local queued: { Object } = {}

Surround.addObject = function(_object: BasePart, sound: Sound, baseVolume: number?, max_distance: number?)
	if not _object or not sound then
		return
	end
	local object: BasePart = _object
	if _object:IsA("Model") and _object.PrimaryPart then
		object = _object.PrimaryPart
	end
	if not object:IsA("BasePart") then
		return
	end
	table.insert(objects, {
		root = object,
		sound = sound,
		baseVolume = baseVolume or sound.Volume,
		max_distance = max_distance or default_max_distance,
	})
end

Surround.removeObject = function(_object: BasePart, sound: Sound)
	if not _object or not sound then
		return
	end
	local object: BasePart = _object
	if _object:IsA("Model") and _object.PrimaryPart then
		object = _object.PrimaryPart
	end

	for idx: number, obj: Object in objects do
		if obj.root == object and obj.sound == sound then
			table.remove(objects, idx)
			return
		end
	end
end

RunService.PreRender:Connect(function(dt: number)
	if last_throttle <= throttle then
		last_throttle += dt
		return
	end
	last_throttle = 0
	local characterRoot: BasePart? = Player.Character and Player.Character.PrimaryPart
	if not characterRoot then
		return
	end
	begin_profiler("Surround.simulate")
	local characterPos: Vector3 = characterRoot.Position
	if next(queued) then
		local sounds: { [Sound]: number } = {}
		for _, object: Object in queued do
			local pos: Vector3 = object.root.Position
			local distance: number = (pos - characterPos).Magnitude
			local volume: number =
				math.clamp(object.baseVolume * (1 - distance / object.max_distance), 0, object.baseVolume)
			local highest_volume: number = sounds[object.sound] or -1
			if volume <= highest_volume then
				continue
			end
			sounds[object.sound] = volume
		end
		table.clear(queued)
		for sound: Sound, volume: number in sounds do
			sound.Volume = volume
		end
	else
		for idx: number, object: Object in objects do
			if not object.root or not object.sound or not object.root.Parent then
				objects[idx] = nil
				continue
			end
			if not object.root:IsDescendantOf(workspace) then
				continue
			end
			table.insert(queued, object)
		end
	end
	end_profiler("Surround.simulate")
end)

return Surround :: typeof(Surround)


--!optimize 2
local client = {}

local ContextActionService = game:GetService("ContextActionService")
local CollectionService = game:GetService("CollectionService")
local localPlayer = game:GetService("Players").LocalPlayer

local vide = require("../vide")
local Profiler = require("./profiler")
local client_performance = require("@self/performance")
local remotes = require("@self/remotes/Remotes")
local spawn_app = require("./ui/spawn_app")
local home_app = require("./ui/home")

local unbind_performance_socket = remotes.fromEvent("scheduler.unbind_performance_socket")
local profiler_state = remotes.fromEvent("scheduler.profiler_state")

local server_frame_value = vide.source({})

local version: string, rootScope, root_performance, home_app_ui
Profiler.store("server_frame_value", server_frame_value)

local function onStateChanged(instance: Instance?)
	if instance and instance ~= localPlayer then
		return
	end
	if localPlayer:HasTag("__allowedScheduler__") then
		script.Parent.profiler:SetAttribute("__state__", true)
	else
		script.Parent.profiler:SetAttribute("__state__", false)
	end
end

client.assign_ver = function(ver: string)
	version = ver
end

client.init = function()
	if script:HasTag("__init__") then
		return
	end
	script:AddTag("__init__")
	CollectionService:GetInstanceAddedSignal("__allowedScheduler__"):Connect(onStateChanged)
	CollectionService:GetInstanceRemovedSignal("__allowedScheduler__"):Connect(onStateChanged)
	task.defer(onStateChanged)
end

client.toggle_widget = function(_, state: Enum.UserInputState)
	if state and state ~= Enum.UserInputState.End then
		return
	end
	-- cleanup
	if root_performance then
		root_performance()
		root_performance = nil
	end
	if not localPlayer:HasTag("__allowedScheduler__") or rootScope then
		home_app_ui = nil
		profiler_state:FireServer(false)
		unbind_performance_socket:FireServer()
		Profiler.reset()
		Profiler.Pause()
		if rootScope then
			rootScope()
			rootScope = nil
		end
		spawn_app.unmount_all()
		return
	end
	if home_app_ui then
		return
	end
	root_performance = client_performance.run(Profiler)
	profiler_state:FireServer(true)
	Profiler.reset()
	Profiler.Resume()
	rootScope = vide.root(function()
		-- Server Scheduler
		home_app_ui = home_app({
			env = "Server",
			Profiler = Profiler,
			profiler_state = profiler_state,
			ProfilerState = Profiler.get_stored("server_frame_value"),
			Runtime = Profiler.get_stored("server_runtime"),
			version = version,

			onClose = function()
				if home_app_ui then
					home_app_ui.Enabled = false
				end
			end,
			onDestroy = function()
				home_app_ui = nil
				profiler_state:FireServer(false)
				unbind_performance_socket:FireServer()
				Profiler.reset()
				Profiler.Pause()
				if not rootScope then
					return
				end
				rootScope()
				rootScope = nil
				spawn_app.unmount_all()
			end,
		})
		home_app_ui.Parent = game.Players.LocalPlayer:WaitForChild("PlayerGui", 9e9)
	end)
end

client.bind_to = function(key_bind: Enum.KeyCode)
	if not client then
		warn(`please use .client() first.`)
		return
	end
	ContextActionService:UnbindAction("Toggle Scheduler Widget")
	ContextActionService:BindAction("Toggle Scheduler Widget", client.toggle_widget, false, key_bind or Enum.KeyCode.F3)
end

return client :: typeof(client)


--!optimize 2
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local Stats = game:GetService("Stats")

local vide = require("../../vide")
local remotes = require("./remotes/Remotes")

local get_performance_socket = remotes.fromFunction("scheduler.get_performance_socket")
local communicate_frame_time = remotes.fromEvent("scheduler.communicate_frame_time")
local server_frame_time = remotes.fromEvent("scheduler.server_frame_time")
local communicate_run_time = remotes.fromEvent("scheduler.communicate_run_time")

local root, source, cleanup = vide.root, vide.source, vide.cleanup
local open_connections: { [Player | string]: RBXScriptConnection } = {}

local Buffer = remotes.buffer
local localUser: Player = Players.LocalPlayer
local average_heartbeat: number = 0
local average_frames: number = 0
local render_gpu_frame_time: number = 0
local render_cpu_frame_time: number = 0
local counter: number = 0
local acccumulate: number = 0
local UPDATE_EVERY: number = 1

local scope_value = source("")
local store = source({})
local localSocketId = source("")

local memory_usage = source({} :: { number })
local heartbeat_time = source({} :: { number })
local frames_time = source({} :: { number })
local render_gpu_time = source({} :: { number })
local render_cpu_time = source({} :: { number })

local function performance(Profiler)
	cleanup(function()
		for host: Player | string, socket: RBXScriptConnection in open_connections do
			socket:Disconnect()
		end
		table.clear(open_connections)
	end)

	local function obtain_store_data(host: Player | string)
		local data = host and get_performance_socket:Invoke(3, { to = host, id = 0 }, nil)
		if not data or not data.socket then
			return
		end
		localSocketId(data.socket)
		local memory = source(0)
		local heartbeat_time = source(0)
		local frames_time = source(0)
		local render_gpu_time = source(0)
		local render_cpu_time = source(0)

		store()[host] = {
			memory = memory,
			heartbeat_time = heartbeat_time,
			frames_time = frames_time,
			render_gpu_time = render_gpu_time,
			render_cpu_time = render_cpu_time,
		}
		store(store())
		local socket: RBXScriptConnection
		socket = communicate_frame_time.OnClientEvent:Connect(function(_host: Player | string, packet: buffer)
			if _host ~= host or type(packet) ~= "buffer" then
				return
			end
			-- socket:Disconnect()
			local packet_heartbeat_time: number, packet_frames_time: number, packet_render_gpu_time: number, packet_render_cpu_time: number, packet_memory: number =
				buffer.readf64(packet, 0),
				buffer.readf64(packet, 8),
				buffer.readf64(packet, 16),
				buffer.readf64(packet, 24),
				buffer.readf64(packet, 32)
			heartbeat_time(packet_heartbeat_time)
			frames_time(packet_frames_time)
			render_gpu_time(packet_render_gpu_time)
			render_cpu_time(packet_render_cpu_time)
			memory(packet_memory)
			Profiler.socket_assign(host, {
				memory = packet_memory,
				heartbeat_time = packet_heartbeat_time,
				frames_time = packet_frames_time,
				render_cpu_time = packet_render_cpu_time,
				render_gpu_time = packet_render_gpu_time,
			})
		end)
		open_connections[host] = socket
		if host == localUser then
			communicate_run_time.OnClientEvent:Connect(function(packet: buffer)
				if type(packet) ~= "buffer" then
					return
				end
				local parsed_runTime: string = buffer.tostring(packet)
				scope_value(parsed_runTime)
				local scope_value = Profiler.get_stored("server_runtime")
				if not scope_value then
					Profiler.store("server_runtime", scope_value)
				end
			end)
		end
	end

	obtain_store_data("server")
	for _, player: Player in Players:GetPlayers() do
		obtain_store_data(player)
	end

	cleanup(Players.PlayerAdded:Connect(obtain_store_data))
	cleanup(Players.PlayerRemoving:Connect(function(Player: Player)
		if open_connections[Player] then
			open_connections[Player]:Disconnect()
			open_connections[Player] = nil
		end
	end))
	cleanup(RunService.Heartbeat:Connect(function()
		for host: Player | string, socket: RBXScriptConnection in open_connections do
			if socket and socket.Connected then
				continue
			end
			store()[host] = nil
			store(store())
		end
	end))

	local server_frame_value = Profiler.get_stored("server_frame_value")

	cleanup(RunService.Heartbeat:Connect(function(dt: number)
		counter += 1
		acccumulate += dt

		average_heartbeat += Stats.HeartbeatTime
		average_frames += Stats.FrameTime
		render_gpu_frame_time += Stats.RenderGPUFrameTime
		render_cpu_frame_time += Stats.RenderCPUFrameTime

		if acccumulate > UPDATE_EVERY then
			if next(open_connections) then
				local b: buffer = buffer.create(40)
				local data_memory: number, data_heartbeat_time: number, data_frames_time: number, data_render_cpu_frame_time: number, data_render_gpu_frame_time: number =
					Stats:GetTotalMemoryUsageMb() * 1e3 * 1e3,
					average_heartbeat / counter,
					average_frames / counter,
					render_cpu_frame_time / counter,
					render_gpu_frame_time / counter

				buffer.writef64(b, 0, data_heartbeat_time)
				buffer.writef64(b, 8, data_frames_time)
				buffer.writef64(b, 16, data_render_gpu_frame_time)
				buffer.writef64(b, 24, data_render_cpu_frame_time)
				buffer.writef64(b, 32, data_memory)

				memory_usage()[localUser] = data_memory
				heartbeat_time()[localUser] = data_heartbeat_time
				frames_time()[localUser] = data_frames_time
				render_gpu_time()[localUser] = data_render_cpu_frame_time
				render_cpu_time()[localUser] = data_render_gpu_frame_time

				communicate_frame_time:FireServer(localSocketId(), b)

				Profiler.socket_assign(localUser, {
					memory = data_memory,
					heartbeat_time = data_heartbeat_time,
					frames_time = data_frames_time,
					render_cpu_time = data_render_cpu_frame_time,
					render_gpu_time = data_render_gpu_frame_time,
				})
			end

			average_heartbeat = 0
			average_frames = 0
			render_gpu_frame_time = 0
			render_cpu_frame_time = 0
			acccumulate -= UPDATE_EVERY
			counter = 0
		end
	end))

	cleanup(server_frame_time.OnClientEvent:Connect(function(packet: buffer)
		if type(packet) ~= "buffer" then
			return
		end
		local raw_exported_server_frames = Buffer.read(packet)
		if not raw_exported_server_frames then
			return
		end
		server_frame_value(Profiler.merge(raw_exported_server_frames, false))
	end))
end

return {
	run = function(Profiler)
		return root(function()
			performance(Profiler)
		end)
	end,
}


--!optimize 2
--!strict
--@EternityDev
local Function = {}
Function.__index = Function

local RunService = game:GetService("RunService")

function Function.new(remote: RemoteFunction)
	local meta = setmetatable({
		remote = remote,
	}, Function)
	remote.Destroying:Once(function()
		if not meta then return end
		Function.Remove(meta)
	end)
	return meta
end

function Function:Invoke(expireTime: number, ...: any): (...any?)
	if not RunService:IsClient() then
		return error("Cannot execute InvokeServer on the server.", 2)
	end
	if not expireTime then return end
	local timer, session
	local obj = { ... }
	local thread: thread = coroutine.running()
	timer = task.delay(expireTime or 3, function()
		task.cancel(session)
		task.spawn(thread, nil)
	end)
	session = task.spawn(function()
		local obj = self.remote:InvokeServer(table.unpack(obj))
		task.cancel(timer)
		task.spawn(thread, obj)
	end)
	return coroutine.yield()
end

function Function:Remove()
	setmetatable(self, nil)
end

function Function:Destroy()
	self.remote:Destroy()
end

return Function


--!optimize 2
--!strict
--@EternityDev
--Client
local Remotes = {}

if not script.Parent.Parent.Parent:FindFirstChild("Remotes") then
	Instance.new("Folder", script.Parent.Parent.Parent).Name = "Remotes"
end
local RemotesDir = script.Parent.Parent.Parent:WaitForChild("Remotes")

local Function = require("./Function")

function Remotes.fromEvent(remoteName: string): RemoteEvent
	if not RemotesDir:FindFirstChild(`Remote_{remoteName}`) then
		error(`Oops, cant find remote-event for "{remoteName}", make sure you have defined it on server-side.`)
	end
	return RemotesDir[`Remote_{remoteName}`]
end

function Remotes.fromFunction(remoteName: string)
	if not RemotesDir:FindFirstChild(`Remote_{remoteName}`) then
		error(`Oops, cant find remote-function for "{remoteName}", make sure you have defined it on server-side.`)
	end
	return Function.new(RemotesDir[`Remote_{remoteName}`])
end

Remotes.buffer = require("../../util/bufferencoder")

return Remotes


--!optimize 2
--!strict
local scheduler = {}

local profiler = require("@self/profiler")
local client = nil

scheduler.profiler = profiler

scheduler.server = function()
	local server = require("@self/server")
	return server
end

scheduler.client = function()
	if client then
		return client
	end
	client = require("@self/client")
	client.assign_ver("0.1.4")
	return client
end

return scheduler :: typeof(scheduler)


--!optimize 2
--!native
local Profiler = {}
Profiler.isRunning = false

local IsServer: boolean = game:GetService("RunService"):IsServer()
local vide = require("../vide")
local convert_scale = require("./util/convert_scale")
local Signal = require("./util/limesignal")

local socket_changed_signal = Signal.Signal.new()
local lastUpdated: number, profiler_state: boolean = os.clock(), IsServer

type socket_array_map = {
	memory: number,
	heartbeat_time: number,
	frames_time: number,
	render_gpu_time: number,
	render_cpu_time: number,
}
type socket_transcript_type_inner = {
	id: number,
	host: string,
	memory: string,
	cpu: string,
	frames: string,
	gpu: string,
	cpuRender: string,
}
type socket_transcript_type = { socket_transcript_type_inner }

type TimerInfo = {
	name: string,
	startTime: number,
	deductedTime: number,
}

local activeTimers: { [thread]: { TimerInfo } } = {}
local frameSystems: { { Name: string, Duration: number, Color: Color3 } } = {}
local colorCache: { [string]: Color3 } = {}
local sockets: { [Player | string]: socket_array_map } = {}
local stores: { [string]: any } = {}

local timerPool: { TimerInfo } = {}

local function allocTimer(name: string, startTime: number): TimerInfo
	local t = table.remove(timerPool)
	if t then
		t.name = name
		t.startTime = startTime
		t.deductedTime = 0
		return t
	end
	return {
		name = name,
		startTime = startTime,
		deductedTime = 0,
	}
end

local function freeTimer(t: TimerInfo)
	table.insert(timerPool, t)
end

local state: (any) -> { any } = vide.source({})

local function generateColor(str: string): Color3
	if colorCache[str] then
		return colorCache[str]
	end
	local hash: number = 0
	for i: number = 1, #str do
		local char: number = string.byte(str, i)
		hash = (bit32.lshift(hash, 5) - hash) + char
		hash = bit32.band(hash, hash) -- Convert to 32bit integer
	end
	local hue: number = (hash % 360) / 360
	local color: Color3 = Color3.fromHSV(hue, 0.8, 0.95)
	colorCache[str] = color
	return color
end

Profiler.reset = function()
	Profiler.isRunning = false
	table.clear(activeTimers)
	table.clear(frameSystems)
	Profiler.isRunning = true
end

-- Sockets

Profiler.all_sockets = function(): { [Player | string]: socket_array_map? }
	return sockets
end

Profiler.socket_assign = function(host: Player | string, new_map: socket_array_map)
	sockets[host] = new_map
	socket_changed_signal:fire()
end

Profiler.getSocket = function(host: Player | string): socket_array_map?
	return sockets[host]
end

-- { id = 1, host = "server", memory = "0.00GB", cpu = "0.0us", gpu = "0.0us", cpuRender = "0.00ms" },
Profiler.sockets_transcript = function(): socket_transcript_type
	local idx: number, transcript: socket_transcript_type = 1, {}
	for host: Player | string, map: socket_array_map in sockets do
		local frame_time: number = 1 / (map.frames_time or 1)
		if frame_time ~= frame_time or frame_time == math.huge then
			frame_time = 0
		end
		table.insert(transcript, {
			id = idx,
			host = type(host) == "string" and host or (host :: Player).Name,
			memory = convert_scale("B", map.memory),
			cpu = convert_scale("s", map.heartbeat_time or 0),
			frames = string.format("%.2f", frame_time),
			gpu = convert_scale("s", map.render_gpu_time or 0),
			cpuRender = convert_scale("s", map.render_cpu_time or 0),
		})
		idx += 1
	end
	table.sort(transcript, function(a: socket_transcript_type_inner, b: socket_transcript_type_inner): boolean
		return a.id ~= 1 and true or a.host < b.host
	end)
	return transcript
end

Profiler.onSocketChanged = socket_changed_signal

-- Profilers

Profiler.RunTime = function(): number
	local runTime: number = 0
	for _, frame: { Name: string, Duration: number, Color: Color3 } in frameSystems do
		runTime += frame.Duration
	end
	return runTime
end

Profiler.merge = function(map_value: { { Name: string, Duration: number, Color: Color3 } }?, update_state: boolean?)
	map_value = map_value or frameSystems
	local merged: { [string]: { Name: string, Duration: number, Count: number, Color: Color3 } } = {}
	for _, frameData: { Name: string, Duration: number, Color: Color3 } in map_value :: any do
		if not merged[frameData.Name] then
			merged[frameData.Name] = {
				Name = frameData.Name,
				Duration = frameData.Duration,
				Count = 1,
				Color = frameData.Color,
			}
		else
			local system = merged[frameData.Name]
			system.Duration += frameData.Duration
			system.Count += 1
		end
	end
	local sorted = {}
	for _, system in merged do
		system.Duration /= system.Count
		table.insert(sorted, system)
	end
	table.sort(sorted, function(a, b)
		return a.Name < b.Name
	end)
	if update_state == nil or update_state then
		state(sorted)
	end
	table.clear(frameSystems)
	return sorted
end

Profiler.Begin = function(name: string)
	if not Profiler.isRunning or not profiler_state then
		return
	end
	local startTime = os.clock()
	local currentThread: thread = coroutine.running()
	local threadStack = activeTimers[currentThread]
	if not threadStack then
		threadStack = {}
		activeTimers[currentThread] = threadStack
	end

	table.insert(threadStack, allocTimer(name, startTime))

	-- overhead compensation
	local endTime = os.clock()
	local overhead = endTime - startTime
	if #threadStack > 1 then
		threadStack[#threadStack - 1].deductedTime += overhead
	end
end

Profiler.End = function(name: string)
	if not Profiler.isRunning or not profiler_state then
		return
	end
	local endStart: number = os.clock()
	local currentThread: thread = coroutine.running()
	local threadTimers: { TimerInfo } = activeTimers[currentThread]
	if not threadTimers then
		return
	end

	local lastIndex = #threadTimers
	if lastIndex > 0 and threadTimers[lastIndex].name == name then
		local timer = threadTimers[lastIndex]
		table.remove(threadTimers, lastIndex)

		local duration: number = endStart - timer.startTime - timer.deductedTime
		if duration < 0 then
			duration = 0
		end

		table.insert(frameSystems, {
			Name = name,
			Duration = duration,
			Color = generateColor(name),
		})
		freeTimer(timer)
	else
		for i: number = lastIndex - 1, 1, -1 do
			local timer: TimerInfo = threadTimers[i]
			if timer.name == name then
				table.remove(threadTimers, i)

				local duration: number = endStart - timer.startTime - timer.deductedTime
				if duration < 0 then
					duration = 0
				end

				table.insert(frameSystems, {
					Name = name,
					Duration = duration,
					Color = generateColor(name),
				})
				freeTimer(timer)
				break
			end
		end
	end

	local endFinish = os.clock()
	local overhead = endFinish - endStart
	if #threadTimers > 0 then
		threadTimers[#threadTimers].deductedTime += overhead
	end

	if #threadTimers == 0 then
		activeTimers[currentThread] = nil
	end
	if endStart - lastUpdated >= 1 then
		lastUpdated = endStart
		Profiler.merge(nil, true)
	end
end

Profiler.Pause = function()
	Profiler.isRunning = false
end

Profiler.Resume = function()
	Profiler.isRunning = true
end

Profiler.dump_state = function()
	return state
end

Profiler.dump_frames = function(): { { Name: string, Duration: number, Color: Color3 } }
	return frameSystems
end

-- Misc

Profiler.store = function(key: string, value: any)
	stores[key] = value
end

Profiler.get_stored = function(key: string): any?
	return stores[key]
end

if not IsServer then
	script:GetAttributeChangedSignal("__state__"):Connect(function()
		profiler_state = script:GetAttribute("__state__")
	end)
	profiler_state = script:GetAttribute("__state__")
end

return Profiler :: typeof(Profiler)


--!optimize 2
local server = {}
local HttpService = game:GetService("HttpService")
local RunService = game:GetService("RunService")
local Stats = game:GetService("Stats")
local CollectionService = game:GetService("CollectionService")

local convert_units = require("./util/convert_units")
local remotes = require("@self/remotes/Remotes")

local Buffer = remotes.buffer
local get_performance_socket = remotes.fromFunction("scheduler.get_performance_socket")
local unbind_performance_socket = remotes.fromEvent("scheduler.unbind_performance_socket")
local communicate_frame_time = remotes.fromEvent("scheduler.communicate_frame_time")
local server_frame_time = remotes.fromEvent("scheduler.server_frame_time")
local communicate_run_time = remotes.fromEvent("scheduler.communicate_run_time")
local profiler_state = remotes.fromEvent("scheduler.profiler_state")

local SEND_EVERY: number = 1

server.allowed = function(): { Player }
	return CollectionService:GetTagged("__allowedScheduler__")
end

server.allow = function(player: Player)
	player:AddTag("__allowedScheduler__")
end

server.disallow = function(player: Player)
	player:RemoveTag("__allowedScheduler__")
end

server.init = function(Profiler)
	if script:HasTag("__init__") then
		return
	end
	script:AddTag("__init__")
	local active_sockets = {}
	local _queue = {}

	get_performance_socket.remote.OnServerInvoke = function(sender: Player)
		local socket_id: string = HttpService:GenerateGUID(false):gsub("-", "")
		active_sockets[sender] = socket_id
		return {
			socket = socket_id,
		}
	end

	unbind_performance_socket.OnServerEvent:Connect(function(sender: Player)
		active_sockets[sender] = nil
	end)

	communicate_frame_time.OnServerEvent:Connect(function(sender: Player, socketId: string, packet: buffer)
		if
			type(socketId) ~= "string"
			or type(packet) ~= "buffer"
			or buffer.len(packet) ~= 40
			or not active_sockets[sender]
			or active_sockets[sender] ~= socketId
		then
			return
		end
		_queue[sender] = packet
	end)

	profiler_state.OnServerEvent:Connect(function(sender: Player, state: boolean)
		if not active_sockets[sender] or not sender:HasTag("__allowedScheduler__") or type(state) ~= "boolean" then
			return
		end
		Profiler.reset()
		if state == true then
			Profiler.Resume()
		else
			Profiler.Pause()
		end
	end)

	local average_heartbeat: number = 0
	local average_frames: number = 0
	local render_gpu_frame_time: number = 0
	local render_cpu_frame_time: number = 0
	local counter: number = 0
	local acccumulate: number = 0

	RunService.Heartbeat:Connect(function(dt: number)
		counter += 1
		acccumulate += dt

		average_heartbeat += Stats.HeartbeatTime
		average_frames += dt
		render_gpu_frame_time += Stats.RenderGPUFrameTime
		render_cpu_frame_time += Stats.RenderCPUFrameTime

		if acccumulate > SEND_EVERY and Profiler.isRunning then
			if next(active_sockets) then
				local runTime: string = convert_units("s", Profiler.RunTime())
				local exported_scheduler: { { Name: string, Duration: number, Color: Color3 } } = Profiler.dump_frames()
				local b: buffer = buffer.create(40)
				buffer.writef64(b, 0, average_heartbeat / counter)
				buffer.writef64(b, 8, average_frames / counter)
				buffer.writef64(b, 16, render_gpu_frame_time / counter)
				buffer.writef64(b, 24, render_cpu_frame_time / counter)
				buffer.writef64(b, 32, Stats:GetTotalMemoryUsageMb() * 1e3 * 1e3)

				for socket: Player in active_sockets do
					if not socket or not socket.Parent then
						active_sockets[socket] = nil
						continue
					end
					communicate_frame_time:FireClient(socket, "server", b)
					server_frame_time:FireClient(socket, Buffer.write(exported_scheduler))
					communicate_run_time:FireClient(socket, buffer.fromstring(runTime))
				end
			end
			if next(_queue) then
				for host: Player, packet: buffer in _queue do
					if not host or not host.Parent or buffer.len(packet) ~= 40 then
						_queue[host] = nil
						continue
					end
					for socket: Player in active_sockets do
						if not socket or not socket.Parent then
							active_sockets[socket] = nil
							continue
						end
						if socket == host then
							continue
						end
						communicate_frame_time:FireClient(socket, host, packet)
					end
					_queue[host] = nil
				end
			end

			average_heartbeat = 0
			average_frames = 0
			render_gpu_frame_time = 0
			render_cpu_frame_time = 0
			acccumulate -= SEND_EVERY
			counter = 0
		end
	end)
end

return server :: typeof(server)


--!optimize 2
--!strict
--@EternityDev
--Server
local Remotes = {}

if not script.Parent.Parent.Parent:FindFirstChild("Remotes") then
	Instance.new("Folder", script.Parent.Parent.Parent).Name = "Remotes"
end
local RemotesDir = script.Parent.Parent.Parent:WaitForChild("Remotes")

local Function = require("./Function")

function Remotes.fromEvent(remoteName: string): RemoteEvent
	if not RemotesDir:FindFirstChild(`Remote_{remoteName}`) then
		Instance.new("RemoteEvent", RemotesDir).Name = `Remote_{remoteName}`
	end
	return RemotesDir[`Remote_{remoteName}`]
end

function Remotes.fromFunction(remoteName: string)
	if not RemotesDir:FindFirstChild(`Remote_{remoteName}`) then
		Instance.new("RemoteFunction", RemotesDir).Name = `Remote_{remoteName}`
	end
	return Function.new(RemotesDir[`Remote_{remoteName}`])
end

Remotes.buffer = require("../../util/bufferencoder")

return Remotes


--!optimize 2
local pebble = require("../../pebble")
local vide = require("../../vide")

local spawn_app = require("./spawn_app")
local scheduler_app = require("./scheduler")
local performance_app = require("./performance")

type HomeProps = {
	env: string,
	Profiler: any,
	profiler_state: RemoteEvent,
	version: string,

	onClose: () -> (),
	onDestroy: () -> (),
}

local create = vide.create
local source = vide.source
local values = vide.values
local cleanup = vide.cleanup

local env_apps = source({
	{
		env = "Server",
		Position = UDim2.fromScale(0.25, 0.5),
		menus = source({
			scheduler = scheduler_app,
		}),
	},
	{
		env = "Client",
		Position = UDim2.fromScale(0.5, 0.5),
		menus = source({
			scheduler = scheduler_app,
			performance = performance_app,
		}),
	},
})

local home_app_ui, performance_app_ui, server_app_ui, client_app_ui

return function(props: HomeProps)
	home_app_ui = pebble.widget({
		title = "Home",
		subtitle = `v{props.version}`,
		min_size = Vector2.new(230, 200),
		bind_to_close = props.onClose,

		pebble.container({
			create("UIListLayout")({
				Padding = UDim.new(0, 2),
				VerticalFlex = Enum.UIFlexAlignment.SpaceEvenly,
				HorizontalFlex = Enum.UIFlexAlignment.Fill,
			}),

			create("ScrollingFrame")({
				-- Size = UDim2.fromScale(1, 1),
				CanvasSize = UDim2.new(),
				AutomaticCanvasSize = Enum.AutomaticSize.Y,
				BackgroundTransparency = 1,
				ScrollBarThickness = 6,
				HorizontalScrollBarInset = Enum.ScrollBarInset.Always,

				create("UIFlexItem")({
					FlexMode = Enum.UIFlexMode.Fill,
				}),

				pebble.padding({
					x = UDim.new(0, 1),
					right = UDim.new(0, 8),
				}),

				create("UIListLayout")({
					FillDirection = Enum.FillDirection.Horizontal,
					HorizontalFlex = Enum.UIFlexAlignment.Fill,
					Padding = UDim.new(0, 8),
					Wraps = true,
				}),

				values(env_apps, function(value, key)
					return pebble.pane({
						name = "",
						size = UDim2.fromOffset(200, 0),
						automaticsize = Enum.AutomaticSize.Y,

						create("UIListLayout")({
							Padding = UDim.new(0, 8),
						}),

						pebble.typography({
							text = value.env,
							wrapped = true,
						}),

						values(value.menus, function(app_constructor, i)
							local menu: string = i()
							return pebble.button({
								size = UDim2.new(1, 0, 0, 30),
								text = menu,
								activated = function()
									if menu == "scheduler" then
										if value.env == "Client" and client_app_ui then
											pcall(client_app_ui)
											client_app_ui = nil
										elseif server_app_ui then
											pcall(server_app_ui)
											server_app_ui = nil
										end

										local newApp = spawn_app.spawn_app(app_constructor, {
											env = value.env,
											Position = value.Position,
											ProfilerState = value.env == "Server" and props.Profiler.get_stored(
												"server_frame_value"
											) or props.Profiler.dump_state(),
											Runtime = value.env == "Server"
												and props.Profiler.get_stored("server_runtime"),
											Pause = function()
												if value.env == "Server" then
													props.profiler_state:FireServer(false)
												else
													props.Profiler.Pause()
												end
											end,
											Resume = function()
												if value.env == "Server" then
													props.profiler_state:FireServer(true)
												else
													props.Profiler.Resume()
												end
											end,
											onClose = function()
												if value.env == "Client" and client_app_ui then
													pcall(client_app_ui)
													client_app_ui = nil
												elseif server_app_ui then
													pcall(server_app_ui)
													server_app_ui = nil
												end
												if
													not client_app_ui
													and not server_app_ui
													and not performance_app_ui
													and not home_app_ui
												then
													spawn_app.unmount_all()
													props.onDestroy()
												end
											end,
										})

										if value.env == "Client" then
											client_app_ui = newApp
										else
											server_app_ui = newApp
										end
									elseif menu == "performance" then
										if performance_app_ui then
											pcall(performance_app_ui)
											performance_app_ui = nil
										end

										performance_app_ui = spawn_app.spawn_app(app_constructor, {
											performanceData = props.Profiler.sockets_transcript(),
											IsVisible = true,
											onClose = function()
												if performance_app_ui then
													pcall(performance_app_ui)
													performance_app_ui = nil
												end
												if
													not client_app_ui
													and not server_app_ui
													and not performance_app_ui
													and not home_app_ui
												then
													spawn_app.unmount_all()
													props.onDestroy()
												end
											end,
										})
									end
								end,
							})
						end),
					})
				end),
			}),
		}),
	})
	cleanup(home_app_ui)
	return home_app_ui
end


--!optimize 2
local vide = require("../../vide")
local pebble = require("../../pebble")
local Profiler = require("../profiler")

local create = vide.create
local source = vide.source
local values = vide.values
local cleanup = vide.cleanup

local function parse(data)
	local columnDefinitions = {
		{ key = "host", displayName = "Host" },
		{ key = "memory", displayName = "Memory Usage" },
		{ key = "cpu", displayName = "CPU" },
		{ key = "frames", displayName = "FPS" },
		{ key = "gpu", displayName = "GPU" },
		{ key = "cpuRender", displayName = "CPU Render" },
	}
	local resultTable = {}
	local columnMap = {}
	for i, def in columnDefinitions do
		resultTable[i] = { def.displayName }
		columnMap[def.key] = i
	end
	for _, record in data do
		for _, def in columnDefinitions do
			local key: string = def.key
			local value = record[key]
			local columnIndex: number = columnMap[key]
			if columnIndex and value ~= nil then
				table.insert(resultTable[columnIndex], value)
			end
		end
	end
	return resultTable
end

return function(props)
	local performanceData = source(parse(props.performanceData))

	Profiler.onSocketChanged:disconnectAll()
	cleanup(Profiler.onSocketChanged:connect(function()
		local parse = parse(Profiler.sockets_transcript())
		performanceData(parse)
	end))

	return pebble.widget({
		title = "Performance",
		min_size = Vector2.new(450, 250),
		size = Vector2.new(550, 250),

		bind_to_close = props.onClose,

		pebble.container({
			Size = UDim2.fromScale(1, 1),

			create("UIFlexItem")({
				FlexMode = Enum.UIFlexMode.Fill,
			}),

			pebble.tablesheet({
				size = UDim2.fromScale(1, 1),
				column_sizes = source({ 100, 80, 100, 200 }),
				read_value = function(column, row)
					local value = performanceData()[column][row]
					return value and value or ""
				end,
				on_click = function() end,
				on_click2 = function() end,
				columns = performanceData,
			}),
		}),
	})
end


--!optimize 2
local vide = require("../../vide")
local pebble = require("../../pebble")
local stack_bar = require("./stack_bar")
local convert_scale = require("../util/convert_scale")

type SchedulerProps = {
	env: string,
	Position: UDim2,
	ProfilerState: () -> { any },
	Runtime: string?,

	Pause: () -> (),
	Resume: () -> (),
	onClose: () -> (),
}

local create = vide.create
local source = vide.source
local derive = vide.derive
local indexes = vide.indexes
local action = vide.action
local cleanup = vide.cleanup
local effect = vide.effect

local function changed(property: string, fn: (...any) -> ())
	return action(function(instance)
		local cn = instance:GetPropertyChangedSignal(property):Connect(function()
			fn(instance[property])
		end)
		cleanup(function()
			cn:Disconnect()
		end)
	end)
end

local sort_by_options = {
	"Name",
	"Frame Time",
}

return function(props: SchedulerProps)
	local selected: (number?) -> number = source(0)
	local sort_by: (number?) -> number = source(2)

	-- Derived state for total runtime
	local totalRuntime: () -> number = derive(function()
		local total: number = 0
		for _, system in props.ProfilerState() do
			total += system.Duration
		end
		return total
	end)

	local function assign_frame(value_accessor, index)
		local gui_state = source(Enum.GuiState.Idle)
		local _frame: Frame
		local b = create("ImageButton")({
			Name = function()
				return value_accessor().Name
			end,
			Size = UDim2.new(1, 0, 0, 32),
			LayoutOrder = function()
				return 1e9 - value_accessor().Duration * 1e8
			end,
			BackgroundColor3 = function()
				return if gui_state() == Enum.GuiState.Press
					then pebble.theme.bg[-1]()
					elseif gui_state() == Enum.GuiState.Hover then pebble.theme.bg[6]()
					else pebble.theme.bg[3]()
			end,
			Visible = true,
			changed("GuiState", gui_state),
			MouseButton2Click = function()
				props.Pause()
			end,
			create("Folder")({
				(function()
					_frame = create("Frame")({
						Position = UDim2.new(0, 0, 1, 4),
						AnchorPoint = Vector2.new(0, 1),
						Size = function()
							return UDim2.new(value_accessor().Duration / totalRuntime(), 0, 0, 1)
						end,
						BackgroundColor3 = pebble.theme.fg_on_bg_high[0],
					})
					return _frame
				end)(),
				effect(function()
					if not _frame then
						return
					end
					_frame.Size = UDim2.new(value_accessor().Duration / totalRuntime(), 0, 0, 1)
				end),
			}),

			create("UIStroke")({
				Color = pebble.theme.bg[-3],
			}),

			create("UICorner")({
				CornerRadius = UDim.new(0, 4),
			}),

			create("UIListLayout")({
				FillDirection = Enum.FillDirection.Horizontal,
				VerticalAlignment = Enum.VerticalAlignment.Center,
				HorizontalFlex = Enum.UIFlexAlignment.SpaceEvenly,
				Padding = UDim.new(0, 8),
			}),

			pebble.padding({
				x = UDim.new(0, 8),
			}),

			create("Frame")({
				Size = UDim2.fromOffset(16, 16),
				AnchorPoint = Vector2.new(0.5, 0.5),
				BackgroundColor3 = function()
					return value_accessor().Color
				end,
				create("UICorner")({
					CornerRadius = UDim.new(1, 0),
				}),
			}),

			pebble.typography({
				automaticsize = Enum.AutomaticSize.None,
				text = function()
					return value_accessor().Name
				end,

				truncate = Enum.TextTruncate.SplitWord,
				xalignment = Enum.TextXAlignment.Left,
				disabled = false,

				create("UIFlexItem")({
					FlexMode = Enum.UIFlexMode.Fill,
					GrowRatio = 1,
					ShrinkRatio = 1,
				}),
			}),

			pebble.typography({
				automaticsize = Enum.AutomaticSize.XY,
				text = function()
					return convert_scale("s", value_accessor().Duration)
				end,
				xalignment = Enum.TextXAlignment.Right,
				disabled = true,
			}),
		})
		cleanup(b)
		return b
	end

	return pebble.widget({
		title = "Scheduler",
		subtitle = `host: {props.env}`,

		min_size = Vector2.new(200, 300),

		bind_to_close = props.onClose,

		create("Frame")({
			Name = "Elements",
			Size = UDim2.fromScale(1, 1),
			AutomaticSize = Enum.AutomaticSize.Y,

			BackgroundTransparency = 1,

			create("UIListLayout")({
				VerticalAlignment = Enum.VerticalAlignment.Bottom,
				FillDirection = Enum.FillDirection.Vertical,
				VerticalFlex = Enum.UIFlexAlignment.SpaceBetween,
				Padding = UDim.new(0, 8),
			}),

			create("Frame")({
				Size = UDim2.fromScale(1, 0),
				AutomaticSize = Enum.AutomaticSize.Y,

				BackgroundTransparency = 1,

				pebble.pane({
					name = "Overview",
					size = UDim2.fromScale(1, 0),

					create("UIListLayout")({
						FillDirection = Enum.FillDirection.Vertical,
					}),

					pebble.typography({
						text = props.Runtime and function()
							return `Runtime: {props.Runtime}`
						end or function()
							local time: number = totalRuntime()
							return `Run time: {convert_scale("s", time)}`
						end,
					}),

					stack_bar({
						values = props.ProfilerState,
						selected = selected,
					}),

					pebble.row({
						justifycontent = Enum.UIFlexAlignment.Fill,
						pebble.button({
							text = "Pause all",
							activated = props.Pause,
						}),

						pebble.button({
							text = "Resume all",
							activated = props.Resume,
						}),
					}),
				}),
			}),

			pebble.select({
				size = UDim2.new(1, 0, 0, 30),

				options = sort_by_options,
				selected = sort_by,
				update_selected = function(new: number)
					sort_by(new)
				end,
			}),

			create("ScrollingFrame")({
				Name = "Systems",

				Size = UDim2.fromScale(1, 0),
				CanvasSize = UDim2.new(),
				AutomaticCanvasSize = Enum.AutomaticSize.Y,

				BackgroundTransparency = 1,

				ScrollBarThickness = 6,
				VerticalScrollBarInset = Enum.ScrollBarInset.Always,
				ScrollBarImageColor3 = pebble.theme.fg_on_bg_low[3],

				create("UIFlexItem")({
					FlexMode = Enum.UIFlexMode.Fill,
				}),

				create("UIListLayout")({
					FillDirection = Enum.FillDirection.Vertical,
					Padding = UDim.new(0, 8),

					SortOrder = function()
						return if sort_by() == 1 then Enum.SortOrder.Name else Enum.SortOrder.LayoutOrder
					end,
				}),

				pebble.padding({
					y = UDim.new(0, 1),
					x = UDim.new(0, 1),
				}),

				indexes(props.ProfilerState, assign_frame),
			}),
		}),
	})
end


-- by jabby
--!strict
--!optimize 2
local Players = game:GetService("Players")

local vide = require("../../vide")

local destroy_fn: { [() -> ()]: boolean } = {}

local function unmount_all()
	for destroy in destroy_fn do
		destroy()
	end
	table.clear(destroy_fn)
end

local function spawn_app<T>(app, props: T): () -> ()
	return vide.root(function(destroy)
		local destroy = function()
			destroy_fn[destroy] = nil
			pcall(destroy)
		end

		local application = app(props)
		application.Parent = Players.LocalPlayer.PlayerGui

		vide.cleanup(application)
		destroy_fn[destroy] = true

		return destroy
	end)
end

return {
	unmount_all = unmount_all,
	spawn_app = spawn_app,
}


--!optimize 2
local vide = require("../../vide")

local create = vide.create
local indexes = vide.indexes
local derive = vide.derive

type stack_bar = {
	values: () -> { { Name: string, Color: Color3, Size: UDim2, Duration: number } },
	selected: (index: number) -> (),
}

return function(props: stack_bar)
	local total: () -> number = derive(function()
		local total: number = 0
		for _, value in props.values() do
			total += value.Duration
		end
		return total
	end)
	return create("Frame")({
		Name = "Graph",
		Size = UDim2.new(1, 0, 0, 32),
		indexes(props.values, function(value, index)
			return create("Frame")({
				Size = function()
					local duration = value().Duration
					local totalDuration = total()
					if totalDuration == 0 then
						return UDim2.fromScale(0, 1)
					end
					return UDim2.fromScale(duration / totalDuration, 1)
				end,
				BackgroundColor3 = function()
					return value().Color
				end,
				MouseEnter = function()
					props.selected(index)
				end,
			})
		end),
		create("UIListLayout")({
			FillDirection = Enum.FillDirection.Horizontal,
		}),
	})
end


--!optimize 2

--[[
	## made by anexpia •…• ##

	assigns values to specific bytes so that they can be encoded
	this is mainly for sending values that cannot be encoded from server to client
	like newproxy() objects, or other unique values like tables that you want to be the same everytime
	they're sent in a remote.

	you probably don't need to use this most of the time, but the option exists and its helpful when
	its actually needed
]]

local RS = game:GetService("RunService")

local Settings = require(script.Parent.Settings)

local SyncingEnabled = Settings.serverclientsyncing
local IsSyncOrigin = if SyncingEnabled then RS:IsServer() else true

local nametovalue: {[string]: any} = {}
local bytetovalue: {[number]: any} = {
	[2] = true,
	[3] = false,
	[102] = "",
	[103] = 0,
	[104] = 1,
	[105] = -1,
	[106] = math.huge,
	[107] = -math.huge,
	[108] = 0 / 0,
}

local valuetobyte: {[any]: number} = {
	[true] = 2,
	[false] = 3,
	[""] = 102,
	[0] = 103,
	[1] = 104,
	[-1] = 105,
	[math.huge] = 106,
	[-math.huge] = 107,
	-- no nan since nan will error if in table
}

local freebytes
local currentposition = 0

local function sanitizename(name)
	if type(name) ~= "string" then
		error(`expected name to be a string, got {typeof(name)}`, 0)
	end
	if name == "" then
		error("name should not be an empty string", 0)
	end

	return string.gsub(name, "[^%w_]", "_") -- turn everything that isnt %w and _ into _
end

-- Removes the value associated with 'name' from byte registry.
local function removename(name: string)
	name = sanitizename(name)

	local value = nametovalue[name]

	if value then
		local oldbyte = valuetobyte[value]
		valuetobyte[value] = nil

		if bytetovalue[oldbyte] == value then
			bytetovalue[oldbyte] = nil
		end

		if IsSyncOrigin then
			table.insert(freebytes, oldbyte)
			nametovalue[name] = nil -- not done on client because this is called whenever the attribute is changed, and current value is needed

			if SyncingEnabled then
				script:SetAttribute(name, nil)
			end
		end
	end
end

if not IsSyncOrigin then
	local function attributechanged(name, byte)
		removename(name)

		byte = byte or script:GetAttribute(name)

		if byte then
			local value = nametovalue[name]

			if not value then
				value = newproxy()
				nametovalue[name] = value
			end

			valuetobyte[value] = byte
			bytetovalue[byte] = value
		else
			nametovalue[name] = nil
		end
	end

	script.AttributeChanged:Connect(attributechanged)
	for name, value in script:GetAttributes() do
		attributechanged(name, value)
	end
else
	freebytes = {}
end

return {
	bytetovalue = bytetovalue,
	nametovalue = nametovalue,

	valuetobyte = valuetobyte,

	--[[
	Assigns 2 bytes to the name and returns the value that is encoded as the byte.
	Registered names are synced from server to client.
	]]
	register = function(name: string, predefined: any?): any
		name = sanitizename(name)

		if valuetobyte[predefined] then
			error(`cannot define {name} with the value {predefined} as the value is already defined`, 0)
		end

		if nametovalue[name] then
			if predefined then
				local oldvalue = nametovalue[name]
				local byte = valuetobyte[oldvalue]

				nametovalue[name] = predefined

				if byte then
					valuetobyte[oldvalue] = nil
					valuetobyte[predefined] = byte
					bytetovalue[byte] = predefined
				end
			end

			return nametovalue[name]
		end

		local v = predefined or newproxy()

		if IsSyncOrigin then
			local byte = table.remove(freebytes)

			if not byte then
				byte = currentposition - 1
				currentposition = byte
			end

			nametovalue[name] = v
			valuetobyte[v] = byte
			bytetovalue[byte] = v

			if SyncingEnabled then
				script:SetAttribute(name, byte)
			end
		else
			nametovalue[name] = v
		end

		return v
	end,

	remove = removename,
}


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

-- read float16 number from buffer
local function readf16(buff: buffer, cursor: number): number
	local n: number = buffer.readu16(buff, cursor)
	local mantissa: number, exponent: number, sign: number = bit32.extract(n, 0, 10),
														 	 bit32.extract(n, 10, 5),
															 bit32.extract(n, 15, 1)

	if mantissa == 0b0000000000 then
		if exponent == 0b00000 then return 0
		elseif exponent == 0b11111 then
			return if sign == 1 then -math.huge else math.huge
		end
	elseif exponent == 0b11111 then return 0/0 end

	local value: number = ((mantissa / 1024) + 1) * 2 ^ (exponent - 15)

	if sign == 1 then return -value end
	return value
end

-- write number as float16 into buffer
local function writef16(buff: buffer, cursor: number, value: number): ()
	if value == 0 then buffer.writeu16(buff, cursor, 0)
	elseif value >= 65520 then buffer.writeu16(buff, cursor, 0b0_11111_0000000000)
	elseif value <= -65520 then buffer.writeu16(buff, cursor, 0b1_11111_0000000000)
	elseif value ~= value then buffer.writeu16(buff, cursor, 0b0_11111_0000000001)
	else
		local sign: number = 0

		if value < 0 then
			sign = 1
			value = -value
		end

		local mantissa: number, exponent: number = math.frexp(value)
		if exponent < -14 then -- safeguard against tiny exponents like -20
			buffer.writeu16(buff, cursor, 0)
			return
		end

		buffer.writeu16(buff, cursor, bit32.bor((mantissa * 2048 - 1023.5), (exponent + 14) * 2^10, (sign) * 2^15))
	end
end

return {
	writef16 = writef16;
	readf16 = readf16;
}


--!native
--!optimize 2

-- quaternion stuff from https://devforum.roblox.com/t/introducing-luau-buffer-type-beta/2724894/105?u=anexpia
-- modified a bit to be more performant when encoding

local Types = require(script.Parent.Types)
local floatencoder = require(script.floatencoder)

local FP_EPSILON = 1e-6
local I16_PRECISION = 32767                 -- int16 range { -32,786, 32,767 }
local BUFF_CFRAME_SIZE = (3*4) + (1 + 3*2)  -- i.e. 3x f32, 1x u8 and 3x i16

local cframe_ToAxisAngle = CFrame.identity.ToAxisAngle

local function getNormalisedQuaternion(cframe: CFrame)
	local axis_shadowed, angle = cframe_ToAxisAngle(cframe)
	local ha = angle / 2

	local axis: vector = if vector.magnitude(axis_shadowed) > FP_EPSILON then vector.normalize(axis_shadowed) else (Vector3.xAxis :: any)
	axis *= math.sin(ha)

	local w = math.cos(ha)
	local length = math.sqrt(vector.dot(axis, axis) + w*w)

	if length < FP_EPSILON then
		return 0, 0, 0, 1
	end

	axis /= length
	return axis.x, axis.y, axis.z, w / length
end

local function compressQuaternion(cframe)
	local qx, qy, qz, qw = getNormalisedQuaternion(cframe)

	local index = -1
	local value = -math.huge

	local sign
	for i = 1, 4, 1 do
		local val = select(i, qx, qy, qz, qw)
		local abs = math.abs(val)
		if abs > value then
			index = i
			value = abs
			sign = val
		end
	end
	sign = sign >= 0 and 1 or -1

	local v0, v1, v2
	if index == 1 then
		v0 = math.floor(qy * sign * I16_PRECISION + 0.5)
		v1 = math.floor(qz * sign * I16_PRECISION + 0.5)
		v2 = math.floor(qw * sign * I16_PRECISION + 0.5)
	elseif index == 2 then
		v0 = math.floor(qx * sign * I16_PRECISION + 0.5)
		v1 = math.floor(qz * sign * I16_PRECISION + 0.5)
		v2 = math.floor(qw * sign * I16_PRECISION + 0.5)
	elseif index == 3 then
		v0 = math.floor(qx * sign * I16_PRECISION + 0.5)
		v1 = math.floor(qy * sign * I16_PRECISION + 0.5)
		v2 = math.floor(qw * sign * I16_PRECISION + 0.5)
	elseif index == 4 then
		v0 = math.floor(qx * sign * I16_PRECISION + 0.5)
		v1 = math.floor(qy * sign * I16_PRECISION + 0.5)
		v2 = math.floor(qz * sign * I16_PRECISION + 0.5)
	end

	return index, v0, v1, v2
end

local function decompressQuaternion(index, v0, v1, v2)
	v0 /= I16_PRECISION
	v1 /= I16_PRECISION
	v2 /= I16_PRECISION

	local d = math.sqrt(1 - (v0*v0 + v1*v1 + v2*v2))
	if index == 1 then
		return d, v0, v1, v2
	elseif index == 2 then
		return v0, d, v1, v2
	elseif index == 3 then
		return v0, v1, d, v2
	end

	return v0, v1, v2, d
end

local function write(buf: buffer, offset: number, input: CFrame)
	local pos = input.Position

	buffer.writef32(buf, offset, pos.X)
	buffer.writef32(buf, offset + 4, pos.Y)
	buffer.writef32(buf, offset + 8, pos.Z)

	local qi, q0, q1, q2 = compressQuaternion(input)
	buffer.writeu8(buf, offset + 12, qi)
	buffer.writei16(buf, offset + 13, q0)
	buffer.writei16(buf, offset + 15, q1)
	buffer.writei16(buf, offset + 17, q2)
end

local function read(buf: buffer, byte: number, offset: number, info: Types.ReadSettings): (CFrame, number)
	local x = buffer.readf32(buf, offset)
	local y = buffer.readf32(buf, offset + 4)
	local z = buffer.readf32(buf, offset + 8)

	if info.sanitize_nanandinf then
		if (x / x) ~= 1 then x = 0 end
		if (y / y) ~= 1 then y = 0 end
		if (z / z) ~= 1 then z = 0 end
	end

	local qi = buffer.readu8(buf, offset + 12)
	local q0 = buffer.readi16(buf, offset + 13)
	local q1 = buffer.readi16(buf, offset + 15)
	local q2 = buffer.readi16(buf, offset + 17)

	local qx, qy, qz, qw = decompressQuaternion(qi, q0, q1, q2)
	return CFrame.new(x, y, z, qx, qy, qz, qw), offset + BUFF_CFRAME_SIZE
end

return {
	readCFrame = read,
	writeCFrame = write,

	cframesize = BUFF_CFRAME_SIZE,

	writef16 = floatencoder.writef16;
	readf16 = floatencoder.readf16;
}


--!optimize 2

--[[
	## made by anexpia •…• ##
]]

local RS = game:GetService("RunService")
local Settings = require(script.Parent.Settings)
local SyncingEnabled = Settings.serverclientsyncing

local enumitem_to_type = {}
local enumitem_to_value = {}

local exposed = {
	enumitem_to_type = enumitem_to_type;
	enumitem_to_value = enumitem_to_value;

	compact = {},
	full = {},
}

for _, enum in Enum:GetEnums() do
	local n = tostring(enum)

	enumitem_to_type[enum] = n

	for _, enumitem in enum:GetEnumItems() do
		enumitem_to_type[enumitem] = n
		enumitem_to_value[enumitem] = enumitem.Value
	end
end

do
	-- encode enums as <19> <u8 length> <string>
	-- encode enumitems as <20> <u8 length> <string> <u16 value>

	local full = exposed.full

	local nametoenum = {}
	for _, v in Enum:GetEnums() do
		nametoenum[tostring(v)] = v
	end
	-- this is to avoid erroring due to version mismatch when decoding

	local enum_FromValue = (Enum.Material :: any).FromValue

	@native
	function full.encodeEnum(buf: buffer, offset: number, enum: Enum): number
		local name = enumitem_to_type[enum]
		local length = #name

		buffer.writeu8(buf, offset, length); offset += 1
		buffer.writestring(buf, offset, name)

		return offset + length
	end

	@native
	function full.encodeEnumItem(buf: buffer, offset: number, enumitem: EnumItem): number
		offset = full.encodeEnum(buf, offset, enumitem :: any)
		buffer.writeu16(buf, offset, enumitem_to_value[enumitem])

		return offset + 2
	end

	@native
	function full.decodeEnum(buf: buffer, byte: number, cursor: number): (Enum, number, boolean?)
		local length = buffer.readu8(buf, cursor); cursor += 1
		local name = buffer.readstring(buf, cursor, length)

		return nametoenum[name], cursor + length, true
	end

	@native
	function full.decodeEnumItem(buf: buffer, byte: number, cursor: number): (EnumItem, number, boolean?)
		local enum, newcursor = full.decodeEnum(buf, byte, cursor)
		local value = buffer.readu16(buf, newcursor)

		return enum and enum_FromValue(enum, value), newcursor + 2, true
	end
end

do
	-- encode enums as <19> <u16 index>
	-- encode enumitems as <20> <u16 index>
	-- syncing table between client & server is necessary to avoid issues due to version mismatch

	local enumarray: { Enum } = {}
	local enumitemarray: { EnumItem } = {}
	local valuelookup: { [any]: number } = {}

	local compact = exposed.compact

	local IsSyncOrigin = if SyncingEnabled then RS:IsServer() else true

	if IsSyncOrigin then
		local tosend1, tosend2

		if SyncingEnabled then
			tosend1, tosend2 = {}, {}
			local request: RemoteFunction = script:FindFirstChild("request")

			if request == nil then
				request = Instance.new("RemoteFunction")
				request.Name = "request"
				request.Parent = script
			end

			request.OnServerInvoke = function(player, v)
				return tosend1, tosend2
			end
		end

		do
			-- using tostring on index because the enum/enumitem may not exist
			-- which will lead to gaps getting created when syncing

			local enum_i, enumitem_i = 0, 0
			for _, k in Enum:GetEnums() do
				enum_i += 1
				enumarray[enum_i] = k
				valuelookup[k] = enum_i

				if tosend1 then tosend1[tostring(enum_i)] = k end

				for _, v in k:GetEnumItems() do
					enumitem_i += 1
					enumitemarray[enumitem_i] = v
					valuelookup[v] = enumitem_i

					if tosend2 then tosend2[tostring(enumitem_i)] = v end
				end
			end
		end
	else
		task.spawn(function()
			local request = script:WaitForChild("request")

			local function addtoarray(toarray, fromdict)
				for k, v in fromdict do
					k = tonumber(k)
					toarray[k] = v
					valuelookup[v] = k
				end
			end

			while true do
				local success, r1, r2 = pcall(request.InvokeServer, request)

				if success and r1 and r2 then
					addtoarray(enumarray, r1)
					addtoarray(enumitemarray, r2)

					break
				else
					task.wait(3)
				end
			end
		end)
	end

	@native
	function compact.encodeEnum(buf: buffer, offset: number, value: Enum): number
		local position = valuelookup[value]
		buffer.writeu16(buf, offset, position or 0)

		return offset + 2
	end

	compact.encodeEnumItem = (compact.encodeEnum :: any) :: (buf: buffer, offset: number, value: EnumItem) -> number

	@native
	function compact.decodeEnum(buf: buffer, byte: number, cursor: number): (Enum, number)
		local position = buffer.readu16(buf, cursor)
		return enumarray[position], cursor + 2
	end

	@native
	function compact.decodeEnumItem(buf: buffer, byte: number, cursor: number): (EnumItem, number)
		local position = buffer.readu16(buf, cursor)
		return enumitemarray[position], cursor + 2
	end
end

function exposed.getBehaviorFromSetting(operationsettings): (typeof(exposed.full), "full" | "compact")
	local value = operationsettings.rbxenum_behavior or Settings.rbxenum_behavior
	local enumbehavior = exposed[value]

	if enumbehavior == nil then
		error(`{value} is an invalid value for rbx_enumbehavior, options are 'full' / 'compact'`, 0)
	end

	return enumbehavior, value
end

return exposed


--!native
--!optimize 2
--!nolint LocalShadow

--[[ ## made by anexpia •…• ## ]]

local Encoder = script.Parent

local Datatypes = require(Encoder.ReadDatatypes)
local RbxEnumEncoder = require(Encoder.RbxEnumEncoder)
local Settings = require(Encoder.Settings)
local Types = require(Encoder.Types)

type ReadSettings = Types.ReadSettings

local defaultwritingtable = {}

--[[
Decodes the buffer created by encoder.write()

param - buff : the buffer to decode
param - readstart : reading starts from the offset given.
param - readsettings : {
. allowdeduplication: boolean -- if the buffer was written with deduplication enabled
. references: {any} -- table of values that couldn't be encoded which is returned by encoder.write()
. shiftseed: number -- the type bytes of values are unshuffled using the seed.
. rbxenum_behavior: "full" | "compact" -- override for the default setting.
. sanitize_nanandinf: boolean -- override for the default setting.
}
]]
return function(buff: buffer, readstart: number?, readsettings: ReadSettings?): { [any]: any }
	local readsettings: ReadSettings = readsettings or Settings

	local shiftseed = readsettings.shiftseed
	local dedupenabled = readsettings.allowdeduplication

	local cursor = readstart or 0
	local isdoingdeduplication_old = false

	do
		local firstbyte = buffer.readu8(buff, cursor)
		if shiftseed then
			math.randomseed(shiftseed)
			firstbyte = (firstbyte - math.random(1, 127)) % 128
			math.randomseed(shiftseed)
		end

		if firstbyte == 101 then
			return {}
		elseif firstbyte > 1 then
			error(`expected '0', '1', or '101' for first byte, got {firstbyte}`)
		end

		isdoingdeduplication_old = firstbyte == 0
		if isdoingdeduplication_old then dedupenabled = false end
	end

	local enumbehavior = RbxEnumEncoder.getBehaviorFromSetting(readsettings)
	local sanitize_nanandinf = readsettings.sanitize_nanandinf
	if sanitize_nanandinf == nil then sanitize_nanandinf = Settings.sanitize_nanandinf end

	Datatypes.adjustforsettings(enumbehavior, sanitize_nanandinf)

	local deduplicationindex = 0
	local deduplicationtable = if dedupenabled then {} else nil
	local currenttable = {}
	local maintable = currenttable

	local writingtable = defaultwritingtable

	local formedtables = {currenttable}
	local formedcount = 0

	local lastwasdictkey = false
	local dictkey = nil
	local currentindex = 0

	local info: Types.decodeinfo = {
		sanitize_nanandinf = sanitize_nanandinf,
		references = readsettings.references,

		deduplicationtable = deduplicationtable,
		tables = formedtables,
	}

	while cursor <= (buffer.len(buff) - 1) do
		local byte = buffer.readu8(buff, cursor)
		cursor += 1

		local isdictkey = byte > 127
		if isdictkey then
			byte = (255 - byte)
		end

		if shiftseed then
			byte = (byte - math.random(1, 127)) % 128
		end

		local value: any, canbededuplicated: boolean?

		local func = Datatypes[byte]
		if func then
			value, cursor, canbededuplicated = func(buff, byte, cursor, info)
		elseif byte == 1 then
			formedcount += 1

			if currentindex > 0 then
				table.move(writingtable, 1, currentindex, 1, currenttable)
				currentindex = 0
			end

			currenttable = formedtables[formedcount]
			if currenttable == nil then
				currenttable = {}
				formedtables[formedcount] = currenttable
			end

			lastwasdictkey = false
			dictkey = nil

			continue
		elseif byte == 0 then
			if isdoingdeduplication_old then
				isdoingdeduplication_old = false

				currenttable = {}
				deduplicationtable = currenttable
				info.deduplicationtable = deduplicationtable

				continue
			else
				if shiftseed then math.randomseed(os.time()) end

				if currentindex > 0 then
					table.move(writingtable, 1, currentindex, 1, currenttable)
				end

				table.clear(writingtable)
				return maintable
			end
		elseif byte == 101 then value = {}
		elseif byte ~= 4 then -- not nil
			error(`{byte} is not a type byte`)
		end

		if dedupenabled and canbededuplicated then
			deduplicationindex += 1
			deduplicationtable[deduplicationindex] = value
		end

		if lastwasdictkey then
			lastwasdictkey = false

			if dictkey ~= nil then
				currenttable[dictkey] = value
				dictkey = nil
			end
		elseif isdictkey then
			dictkey = value
			lastwasdictkey = true
		else
			currentindex += 1
			writingtable[currentindex] = value
		end
	end

	if shiftseed then math.randomseed(os.time()) end

	error("buffer is not terminated with '0' byte", 0)
end


--!nolint LocalUnused
--!nolint ImportUnused
--!optimize 2

local Module = script.Parent.Parent

local Settings = require(Module.Settings)
local Types = require(Module.Types)

type datatypedecodinginfo = Types.datatypedecodinginfo

local brickcolorbyte = 30

return {
    [brickcolorbyte] = @native function(buff: buffer, byte: number, cursor: number): (BrickColor, number)
        return BrickColor.new(buffer.readu16(buff, cursor)), cursor + 2
    end;
} :: datatypedecodinginfo


--!nolint LocalUnused
--!nolint ImportUnused
--!optimize 2

local Module = script.Parent.Parent

local Settings = require(Module.Settings)
local Types = require(Module.Types)
local Miscellaneous = require(Module.Miscellaneous)

type datatypedecodinginfo = Types.datatypedecodinginfo

local cframebyte = 17

return {
    [cframebyte] = Miscellaneous.readCFrame

} :: datatypedecodinginfo


--!nolint LocalUnused
--!nolint ImportUnused
--!optimize 2

local Module = script.Parent.Parent

local Settings = require(Module.Settings)
local Types = require(Module.Types)
local Miscellaneous = require(Module.Miscellaneous)

local color3always6bytes = Settings.color3always6bytes
local readf16 = Miscellaneous.readf16

type datatypedecodinginfo = Types.datatypedecodinginfo

local u8colorbyte = 23
local f16colorbyte = 24

local writebytesign

return {
	[u8colorbyte] = @native function(buff: buffer, byte: number, cursor: number): (Color3, number)
		local r, g, b = buffer.readu8(buff, cursor), buffer.readu8(buff, cursor + 1), buffer.readu8(buff, cursor + 2)
		return Color3.fromRGB(r, g, b), cursor + 3
	end;

	[f16colorbyte] = @native function(buff: buffer, byte: number, cursor: number): (Color3, number)
		local r, g, b = readf16(buff, cursor), readf16(buff, cursor + 2), readf16(buff, cursor + 4)
		return Color3.new(r, g, b), cursor + 6
	end;
} :: datatypedecodinginfo


--!nolint LocalUnused
--!nolint ImportUnused
--!optimize 2

local Module = script.Parent.Parent

local Settings = require(Module.Settings)
local Types = require(Module.Types)

type datatypedecodinginfo = Types.datatypedecodinginfo

local colorbyte = 25

local uI16_max = (2 ^ 16) - 1

return {
    [colorbyte] = @native function(buff: buffer, byte: number, cursor: number): (ColorSequence, number)
		local length = buffer.readu8(buff, cursor); cursor += 1
		local tbl = table.create(length)

		for i = 1, length do
			local time = math.clamp(buffer.readu16(buff, cursor) / uI16_max, 0, 1)
			local r, g, b = buffer.readu8(buff, cursor + 2), buffer.readu8(buff, cursor + 3), buffer.readu8(buff, cursor + 4); cursor += 5

			tbl[i] = ColorSequenceKeypoint.new(time, Color3.fromRGB(r, g, b))
		end

		return ColorSequence.new(tbl), cursor
	end;
} :: datatypedecodinginfo


--!nolint LocalUnused
--!nolint ImportUnused
--!optimize 2

local Module = script.Parent.Parent

local Settings = require(Module.Settings)
local Types = require(Module.Types)

type datatypedecodinginfo = Types.datatypedecodinginfo

local contentbyte = 33
local fontbyte = 34
local writebytesign

local FromValue = (Enum.FontWeight :: any).FromValue -- localizing it to silence errors 😭

return {
    [contentbyte] = @native function(buff: buffer, byte: number, cursor: number): (Content, number)
		local length = buffer.readu8(buff, cursor); cursor += 1

		if length == 0 then
			return Content.none, cursor
		elseif length == 1 then
			local num = buffer.readf64(buff, cursor)
			return Content.fromAssetId(num), cursor + 8
		else
			length -= 1

			local uri = buffer.readstring(buff, cursor, length)
			return Content.fromUri(uri), cursor + length
		end
	end;

	[fontbyte] = @native function(buff: buffer, byte: number, cursor: number): (Font, number)
		local length = buffer.readu8(buff, cursor); cursor += 1

		if length == 0 then
			local num = buffer.readf64(buff, cursor); cursor += 8

			local weightv, stylev = buffer.readu16(buff, cursor), buffer.readu8(buff, cursor + 2)
			return Font.fromId(num, FromValue(Enum.FontWeight, weightv), FromValue(Enum.FontStyle, stylev)), cursor + 3
		else
			length -= 1
			local familyname = buffer.readstring(buff, cursor, length); cursor += length
			if familyname == "" then familyname = "Arimo" end

			local weightv, stylev = buffer.readu16(buff, cursor), buffer.readu8(buff, cursor + 2)
			-- not using FromName because familyname can be empty
			return Font.new(`rbxasset://fonts/families/{familyname}.json`, FromValue(Enum.FontWeight, weightv), FromValue(Enum.FontStyle, stylev)), cursor + 3
		end
	end;
} :: datatypedecodinginfo


--!nolint LocalUnused
--!nolint ImportUnused
--!optimize 2

local Module = script.Parent.Parent
local Types = require(Module.Types)

type datatypedecodinginfo = Types.datatypedecodinginfo

local datetimebyte = 35

return {
    [datetimebyte] = @native function(buff: buffer, byte: number, cursor: number): (DateTime, number)
        local unixmillis = buffer.readf64(buff, cursor)
        return DateTime.fromUnixTimestampMillis(unixmillis), cursor + 8
    end;

} :: datatypedecodinginfo


--!nolint LocalUnused
--!nolint ImportUnused
--!optimize 2

local Module = script.Parent.Parent

local Settings = require(Module.Settings)
local Types = require(Module.Types)

type datatypedecodinginfo = Types.datatypedecodinginfo

local numberbyte = 21
local writebytesign

return {
    [numberbyte] = @native function(buff: buffer, byte: number, cursor: number): (NumberRange, number)
        local min = buffer.readf32(buff, cursor)
        local max = buffer.readf32(buff, cursor + 4)

        return NumberRange.new(min, max), cursor + 8
    end;
} :: datatypedecodinginfo


--!nolint LocalUnused
--!nolint ImportUnused
--!optimize 2

local Module = script.Parent.Parent

local Settings = require(Module.Settings)
local Types = require(Module.Types)
local Miscellaneous = require(Module.Miscellaneous)
type datatypedecodinginfo = Types.datatypedecodinginfo

local readf16 = Miscellaneous.readf16

local numberbyte = 22
local writebytesign

local uI16_max = (2 ^ 16) - 1

return {
	[numberbyte] = @native function(buff: buffer, byte: number, cursor: number): (NumberSequence, number)
		local length = buffer.readu8(buff, cursor); cursor += 1
		local tbl = table.create(length)

		for i = 1, length do
			local time = math.clamp(buffer.readu16(buff, cursor) / uI16_max, 0, 1)
			local value = buffer.readf32(buff, cursor + 2)
			local envelope = readf16(buff, cursor + 6); cursor += 8

			tbl[i] = NumberSequenceKeypoint.new(time, value, envelope)
		end

		return NumberSequence.new(tbl), cursor
	end;
} :: datatypedecodinginfo


--!nolint LocalUnused
--!nolint ImportUnused
--!optimize 2

local Module = script.Parent.Parent

local Settings = require(Module.Settings)
local Types = require(Module.Types)

type datatypedecodinginfo = Types.datatypedecodinginfo

local physpropsbyte = 29
local writebytesign

return {
    [physpropsbyte] = @native function(buff: buffer, byte: number, cursor: number): (PhysicalProperties, number)
        local Density = buffer.readf32(buff, cursor)
        local Friction = buffer.readf32(buff, cursor + 4)
        local Elasticity = buffer.readf32(buff, cursor + 8)
        local FrictionWeight = buffer.readf32(buff, cursor + 12)
        local ElasticityWeight = buffer.readf32(buff, cursor + 16)

        return PhysicalProperties.new(Density, Friction, Elasticity, FrictionWeight, ElasticityWeight),
            cursor + 20
    end;
} :: datatypedecodinginfo


--!nolint LocalUnused
--!nolint ImportUnused
--!optimize 2

local Module = script.Parent.Parent

local Settings = require(Module.Settings)
local Types = require(Module.Types)

type datatypedecodinginfo = Types.datatypedecodinginfo

local raybyte = 18
local writebytesign

return {
	[raybyte] = @native function(buff: buffer, byte: number, cursor: number, info: Types.decodeinfo): (Ray, number)
		local ox = buffer.readf32(buff, cursor); cursor += 4
		local oy = buffer.readf32(buff, cursor); cursor += 4
		local oz = buffer.readf32(buff, cursor); cursor += 4
		local dx = buffer.readf32(buff, cursor); cursor += 4
		local dy = buffer.readf32(buff, cursor); cursor += 4
		local dz = buffer.readf32(buff, cursor); cursor += 4

		if info.sanitize_nanandinf then
            if (ox / ox) ~= 1 then ox = 0 end
            if (oy / oy) ~= 1 then oy = 0 end
            if (oz / oz) ~= 1 then oz = 0 end
			if (dx / dx) ~= 1 then dx = 0 end
            if (dy / dy) ~= 1 then dy = 0 end
            if (dz / dz) ~= 1 then dz = 0 end
        end

		return Ray.new(Vector3.new(ox, oy, oz), Vector3.new(dx, dy, dz)), cursor
	end;
} :: datatypedecodinginfo


--!nolint LocalUnused
--!nolint ImportUnused
--!optimize 2

local Module = script.Parent.Parent

local Settings = require(Module.Settings)
local Types = require(Module.Types)

type datatypedecodinginfo = Types.datatypedecodinginfo

local rectbyte = 28
local writebytesign

return {
    [rectbyte] = @native function(buff: buffer, byte: number, cursor: number): (Rect, number)
        local xmin = buffer.readf32(buff, cursor)
        local ymin = buffer.readf32(buff, cursor + 4)
        local xmax = buffer.readf32(buff, cursor + 8)
        local ymax = buffer.readf32(buff, cursor + 12)

        return Rect.new(xmin, ymin, xmax, ymax), cursor + 16
    end;
} :: datatypedecodinginfo


--!nolint LocalUnused
--!nolint ImportUnused
--!optimize 2

local Module = script.Parent.Parent

local Settings = require(Module.Settings)
local Types = require(Module.Types)

type datatypedecodinginfo = Types.datatypedecodinginfo

local udimbyte = 26
local writebytesign

return {
    [udimbyte] = @native function(buff: buffer, byte: number, cursor: number): (UDim, number)
        local scale = buffer.readf32(buff, cursor)
        local offset = buffer.readi32(buff, cursor + 4)

        return UDim.new(scale, offset), cursor + 8
    end;

} :: datatypedecodinginfo


--!nolint LocalUnused
--!nolint ImportUnused
--!optimize 2

local Module = script.Parent.Parent

local Settings = require(Module.Settings)
local Types = require(Module.Types)

type datatypedecodinginfo = Types.datatypedecodinginfo

local udim2byte = 27
local writebytesign

return {
    [udim2byte] = @native function(buff: buffer, byte: number, cursor: number): (UDim2, number)
        local xscale = buffer.readf32(buff, cursor)
        local xoffset = buffer.readi32(buff, cursor + 4)
        local yscale = buffer.readf32(buff, cursor + 8)
        local yoffset = buffer.readi32(buff, cursor + 12)

        return UDim2.new(xscale, xoffset, yscale, yoffset), cursor + 16
    end;

} :: datatypedecodinginfo


--!nolint LocalUnused
--!nolint ImportUnused
--!optimize 2

local Module = script.Parent.Parent

local Settings = require(Module.Settings)
local Types = require(Module.Types)

type datatypedecodinginfo = Types.datatypedecodinginfo

local vectorbyte = 16
local vectorint16byte = 32

local writebytesign

return {
    [vectorbyte] = @native function(buff: buffer, byte: number, cursor: number, info: Types.decodeinfo): (Vector2, number)
        local x = buffer.readf32(buff, cursor)
        local y = buffer.readf32(buff, cursor + 4)

        if info.sanitize_nanandinf then
            if (x / x) ~= 1 then x = 0 end
            if (y / y) ~= 1 then y = 0 end
        end

        return Vector2.new(x, y), cursor + 8
    end;

    [vectorint16byte] = @native function(buff: buffer, byte: number, cursor: number): (Vector2int16, number)
        local x = buffer.readi16(buff, cursor)
        local y = buffer.readi16(buff, cursor + 2)

        return Vector2int16.new(x, y), cursor + 4
    end;
} :: datatypedecodinginfo


--!nolint LocalUnused
--!nolint ImportUnused
--!optimize 2

local Module = script.Parent.Parent

local Settings = require(Module.Settings)
local Types = require(Module.Types)

type datatypedecodinginfo = Types.datatypedecodinginfo

local vectorbyte = 15
local vectorint16byte = 31

local writebytesign

return {
    [vectorbyte] = @native function(buff: buffer, byte: number, cursor: number, info: Types.decodeinfo): (vector, number, boolean?)
        local x = buffer.readf32(buff, cursor)
        local y = buffer.readf32(buff, cursor + 4)
        local z = buffer.readf32(buff, cursor + 8)

        -- need to check if deduplication is possible as if nan exists, the vector
        -- cannot be deduplicated during writing process, thus would cause value positions to get shifted
        local candeduplicate = x == x and y == y and z == z

		if info.sanitize_nanandinf then
			if (x / x) ~= 1 then x = 0 end
			if (y / y) ~= 1 then y = 0 end
			if (z / z) ~= 1 then z = 0 end
        end

        return vector.create(x, y, z), cursor + 12, candeduplicate
    end;

    [vectorint16byte] = @native function(buff: buffer, byte: number, cursor: number): (Vector3int16, number)
        local x = buffer.readi16(buff, cursor)
        local y = buffer.readi16(buff, cursor + 2)
        local z = buffer.readi16(buff, cursor + 4)

        return Vector3int16.new(x, y, z), cursor + 6
    end;
} :: datatypedecodinginfo


--!nolint LocalUnused
--!nolint ImportUnused
--!optimize 2

local Module = script.Parent.Parent

local Settings = require(Module.Settings)
local Types = require(Module.Types)

type datatypedecodinginfo = Types.datatypedecodinginfo

local bufferu8byte = 12
local bufferu16byte = 13
local bufferu32byte = 14

@native
local function readbuffer(buff: buffer, byte: number, cursor: number, info: Types.decodeinfo): (buffer, number)
    local length

    if byte == bufferu8byte then length = buffer.readu8(buff, cursor); cursor += 1
    elseif byte == bufferu16byte then length = buffer.readu16(buff, cursor); cursor += 2
    else length = buffer.readu32(buff, cursor); cursor += 4 end

    local value = buffer.create(length)
    buffer.copy(value, 0, buff, cursor, length)

    return value, cursor + length
end

return {
    [bufferu8byte] = readbuffer;
    [bufferu16byte] = readbuffer;
    [bufferu32byte] = readbuffer;
} :: datatypedecodinginfo


--[[
	## made by anexpia •…• ##

	originally this also had write functions for each datatype
	but i found that it was slower than having if conditions

	so now it only contains read functions and thats it
]]

local bytetofunction = {}
local bytetodatatype = {}

for _, t in script:GetChildren() do
	local name = t.Name
	if t.Name == "template" then continue end

	for num, func in require(t) do
		if bytetodatatype[num] then
			warn(`The modules {name} and {bytetodatatype[num]} are using the same byte {num}`)
			continue
		end

		bytetofunction[num] = func
		bytetodatatype[num] = name
	end
end

table.clear(bytetodatatype)
bytetodatatype = nil :: any

local enumbyte = 19
local enumitembyte = 20

--                            math.huge           -math.huge             nan
local f106, f107, f108 = bytetofunction[106], bytetofunction[107], bytetofunction[108]
local f103 = bytetofunction[103] -- returns 0

function bytetofunction.adjustforsettings(enumbehavior, sanitize_nanandinf: boolean?)
	bytetofunction[enumbyte] = enumbehavior.decodeEnum
	bytetofunction[enumitembyte] = enumbehavior.decodeEnumItem

	if sanitize_nanandinf then
		for i = 106, 108 do
			bytetofunction[i] = f103
		end
	else
		bytetofunction[106], bytetofunction[107], bytetofunction[108] = f106, f107, f108
	end

end

return bytetofunction


--!nolint LocalUnused
--!nolint ImportUnused
--!optimize 2

local Module = script.Parent.Parent

local Settings = require(Module.Settings)
local Types = require(Module.Types)
local Enums = require(Module.Enums)

type datatypedecodinginfo = Types.datatypedecodinginfo

local u8numbyte = 5
local n_u8numbyte = 8

local u16numbyte = 6
local n_u16numbyte = 9

local u32numbyte = 7
local n_u32numbyte = 10

local floatnumbyte = 11

local t = {
	[u8numbyte] = @native function(buff: buffer, byte: number, cursor: number): (number, number, boolean?)
		return buffer.readu8(buff, cursor), cursor + 1
	end;
	[n_u8numbyte] = @native function(buff: buffer, byte: number, cursor: number): (number, number, boolean?)
		return -buffer.readu8(buff, cursor), cursor + 1
	end;

	[u16numbyte] = @native function(buff: buffer, byte: number, cursor: number): (number, number, boolean?)
		return buffer.readu16(buff, cursor), cursor + 2, true
	end;
	[n_u16numbyte] = @native function(buff: buffer, byte: number, cursor: number): (number, number, boolean?)
		return -buffer.readu16(buff, cursor), cursor + 2, true
	end;

	[u32numbyte] = @native function(buff: buffer, byte: number, cursor: number): (number, number, boolean?)
		return buffer.readu32(buff, cursor), cursor + 4, true
	end;
	[n_u32numbyte] = @native function(buff: buffer, byte: number, cursor: number): (number, number, boolean?)
		return -buffer.readu32(buff, cursor), cursor + 4, true
	end;

	[floatnumbyte] = @native function(buff: buffer, byte: number, cursor: number): (number, number, boolean?)
		return buffer.readf64(buff, cursor), cursor + 8, true
	end
}

return t


--!nolint LocalUnused
--!nolint ImportUnused
--!optimize 2

local Module = script.Parent.Parent

local Settings = require(Module.Settings)
local Types = require(Module.Types)

type datatypedecodinginfo = Types.datatypedecodinginfo

local stringu8byte = 54
local stringu16byte = 55
local stringu32byte = 56

local writebytesign

return {
	[stringu8byte] = @native function(buff: buffer, byte: number, cursor: number): (string, number, boolean?)
		local length = buffer.readu8(buff, cursor); cursor += 1
		return buffer.readstring(buff, cursor, length), cursor + length, true
	end;

	[stringu16byte] = @native function(buff: buffer, byte: number, cursor: number): (string, number, boolean?)
		local length = buffer.readu16(buff, cursor); cursor += 2
		return buffer.readstring(buff, cursor, length), cursor + length, true
	end;

	[stringu32byte] = @native function(buff: buffer, byte: number, cursor: number): (string, number, boolean?)
		local length = buffer.readu32(buff, cursor); cursor += 4
		return buffer.readstring(buff, cursor, length), cursor + length, true
	end;

} :: datatypedecodinginfo


--!nolint LocalUnused
--!nolint ImportUnused
--!optimize 2

local Module = script.Parent.Parent
local ByteToValue = require(Module.Enums).bytetovalue
local Types = require(Module.Types)

type datatypedecodinginfo = Types.datatypedecodinginfo

local tblu8byte = 51
local tblu16byte = 52
local tblu32byte = 53
local dedupu8byte = 57
local dedupu16byte = 58
local dedupu32byte = 59
local referenceu8byte = 60
local referenceu16byte = 61
local referenceu32byte = 62

local customvaluebyte = 99

@native
local function readtable(buff: buffer, byte: number, cursor: number, info: Types.decodeinfo)
    local index

    if byte == tblu8byte then index = buffer.readu8(buff, cursor); cursor += 1
    elseif byte == tblu16byte then index = buffer.readu16(buff, cursor); cursor += 2
    else index = buffer.readu32(buff, cursor); cursor += 4 end

    local formedtables = info.tables
    local f = formedtables[index]

    if f then return f, cursor
    else
        local value = {}
        formedtables[index] = value

        return value, cursor
    end
end

@native
local function readdeduplicate(buff: buffer, byte: number, cursor: number, info: Types.decodeinfo)
    local index

    if byte == dedupu8byte then index = buffer.readu8(buff, cursor); cursor += 1
    elseif byte == dedupu16byte then index = buffer.readu16(buff, cursor); cursor += 2
    else index = buffer.readu32(buff, cursor); cursor += 4 end

    return info.deduplicationtable[index], cursor
end

@native
local function readreference(buff: buffer, byte: number, cursor: number, info: Types.decodeinfo)
    local index

    if byte == referenceu8byte then index = buffer.readu8(buff, cursor); cursor += 1
    elseif byte == referenceu16byte then index = buffer.readu16(buff, cursor); cursor += 2
    else index = buffer.readu32(buff, cursor); cursor += 4 end

    local references = info.references

    if references then
        return references[index], cursor
    else
        error('Missing references table when decoding buffer that contains value references.')
    end
end

local t = {
    [tblu8byte] = readtable;
    [tblu16byte] = readtable;
    [tblu32byte] = readtable;

    [dedupu8byte] = readdeduplicate;
    [dedupu16byte] = readdeduplicate;
    [dedupu32byte] = readdeduplicate;

    [referenceu8byte] = readreference;
    [referenceu16byte] = readreference;
    [referenceu32byte] = readreference;

    [customvaluebyte] = @native function(buff: buffer, byte: number, cursor: number)
		return ByteToValue[-buffer.readu8(buff, cursor)], cursor + 1
    end,

} :: datatypedecodinginfo

-- doing those here so theres no cost for indexing ByteToValue
for byte, v in ByteToValue do
    if byte > 0 then
        -- this runs somehow faster with native .... ok i guess
        t[byte] = @native function(buff, byte, cursor) return v, cursor end
    end
end

return t


--!nolint LocalUnused
--!nolint ImportUnused
--!optimize 2

local Module = script.Parent.Parent

local Settings = require(Module.Settings)
local Types = require(Module.Types)

type datatypedecodinginfo = Types.datatypedecodinginfo

local templatebyte = -1
local writebytesign

return {
    [templatebyte] = @native function(buff: buffer, byte: number, cursor: number): (any, number, boolean?)
        error('Byte read function should be changed for the template.', 0)
    end;

} :: datatypedecodinginfo


local Encoder = script.Parent

-- explanation for each of the settings exists in the main module
local t = {
    color3always6bytes = false;
    rbxenum_behavior = "full" :: "full" | "compact";

    serverclientsyncing = false;
    sanitize_nanandinf = false;
}

for att, value in Encoder:GetAttributes() do
    if t[att] ~= nil then
        if type(value) == type(t[att]) then t[att] = value
        else warn(`[BUFFERENCODER] {typeof(value)} is not a valid type for the setting {att}`) end
    end
end

return t


type anyTable = { [any]: any }

export type encodeinfo = {
	valuepositionlookup: anyTable;

	settings: WriteSettings,
	scanlist: { anyTable },
	referencelist: { any },

	allocatedsize: number;
}

export type decodeinfo = {
	sanitize_nanandinf: boolean?,

	references: { any }?;

	deduplicationtable: { any };
	tables: { anyTable };
}

export type WriteSettings = {
	allowdeduplication: boolean?,
	allowreferences: boolean?,
	shiftseed: number?,

	rbxenum_behavior: "full" | "compact" | nil,
	color3always6bytes: boolean?,
}

export type ReadSettings = {
	allowdeduplication: boolean?,
	references: { any }?,
	shiftseed: number?,

	rbxenum_behavior: "full" | "compact" | nil,
	sanitize_nanandinf: boolean?,
}

export type datatypedecodinginfo = {
	[number]: (buff: buffer, byte: number, cursor: number, info: decodeinfo) -> (any, number, boolean?),
}

return 0