﻿-- C:\Users\vendetta\Documents\moon\src\codegen.luau

local Codegen = {}
Codegen.__index = Codegen

function Codegen.new(tracer)
	local self = setmetatable({}, Codegen)
	self.tracer = tracer
	self.indentLevel = 0
	self.loopVarCounter = 0
	return self
end

function Codegen:generate()
	local lines = {}
	
	local mainTrace = self.tracer:getMainTrace()
	local processedIndices = {}
	
	local loopPatterns = self:detectLoops(mainTrace)
	
	for idx, entry in ipairs(mainTrace) do
		if not processedIndices[idx] then
			local inLoop = false
			
			for _, loop in ipairs(loopPatterns) do
				if idx >= loop.startIdx and idx <= loop.endIdx then
					if idx == loop.startIdx then
						local loopCode = self:generateLoopCode(loop)
						for _, line in ipairs(loopCode) do
							table.insert(lines, line)
						end
					end
					processedIndices[idx] = true
					inLoop = true
					break
				end
			end
			
			if not inLoop then
				local line = self:formatEntry(entry)
				if line then
					table.insert(lines, line)
				end
			end
		end
	end
	
	local output = table.concat(lines, "\n")
	output = self:expandCallbacks(output)
	output = self:cleanupOutput(output)
	
	return output
end

function Codegen:detectLoops(entries)
	local loops = {}
	local i = 1
	
	while i <= #entries do
		local sequence = self:findRepeatingSequence(entries, i)
		
		if sequence and #sequence.entries >= 2 then
			table.insert(loops, sequence)
			i = sequence.endIdx + 1
		else
			i = i + 1
		end
	end
	
	return loops
end

function Codegen:findRepeatingSequence(entries, startIdx)
	local patterns = {}
	local currentIdx = startIdx
	
	while currentIdx <= #entries do
		local entry = entries[currentIdx]
		local data = entry.data
		
		local normalized, varPart = self:normalizeForPattern(data)
		
		if normalized then
			if not patterns[normalized] then
				patterns[normalized] = {
					normalized = normalized,
					entries = {},
					indices = {},
					startIdx = currentIdx,
					iterVar = nil,
					iterSource = nil
				}
			end
			
			table.insert(patterns[normalized].entries, entry)
			table.insert(patterns[normalized].indices, varPart)
			patterns[normalized].endIdx = currentIdx
		end
		
		currentIdx = currentIdx + 1
		
		if currentIdx - startIdx > 50 then
			break
		end
	end
	
	local best = nil
	for _, pattern in pairs(patterns) do
		if #pattern.entries >= 2 then
			if not best or #pattern.entries > #best.entries then
				pattern.loopType = self:detectLoopType(pattern)
				best = pattern
			end
		end
	end
	
	return best
end

function Codegen:normalizeForPattern(data)
	local normalized = data
	local varPart = nil
	
	local childMatch = string.match(data, "_child%d+")
	if childMatch then
		varPart = childMatch
		normalized = string.gsub(data, "_child%d+", "_child{IDX}")
		return normalized, varPart
	end
	
	local descMatch = string.match(data, "_desc%d+")
	if descMatch then
		varPart = descMatch
		normalized = string.gsub(data, "_desc%d+", "_desc{IDX}")
		return normalized, varPart
	end
	
	local bracketNum = string.match(data, "%[(%d+)%]")
	if bracketNum then
		varPart = bracketNum
		normalized = string.gsub(data, "%[%d+%]", "[{IDX}]")
		return normalized, varPart
	end
	
	return nil, nil
end

function Codegen:detectLoopType(pattern)
	local firstEntry = pattern.entries[1]
	local data = firstEntry.data
	
	if string.find(data, "GetChildren") or string.find(data, "_child") then
		return "children"
	end
	
	if string.find(data, "GetDescendants") or string.find(data, "_desc") then
		return "descendants"
	end
	
	if string.find(data, "GetPlayers") then
		return "players"
	end
	
	local allNumeric = true
	for _, idx in ipairs(pattern.indices) do
		if not tonumber(string.match(idx, "%d+")) then
			allNumeric = false
			break
		end
	end
	
	if allNumeric then
		return "numeric"
	end
	
	return "ipairs"
end

function Codegen:generateLoopCode(loop)
	local lines = {}
	self.loopVarCounter = self.loopVarCounter + 1
	
	local iterVar = "i" .. self.loopVarCounter
	local valVar = "v" .. self.loopVarCounter .. "_item"
	
	if loop.loopType == "numeric" then
		local nums = {}
		for _, idx in ipairs(loop.indices) do
			local n = tonumber(string.match(idx, "%d+"))
			if n then table.insert(nums, n) end
		end
		
		if #nums >= 2 then
			table.sort(nums)
			local minN = nums[1]
			local maxN = nums[#nums]
			
			table.insert(lines, "for " .. iterVar .. " = " .. minN .. ", " .. maxN .. " do")
			
			local sampleEntry = loop.entries[1]
			local sampleData = sampleEntry.data
			local loopBody = string.gsub(sampleData, "%d+", iterVar, 1)
			loopBody = string.gsub(loopBody, "local v%d+=", "local " .. valVar .. "=")
			
			table.insert(lines, "\t" .. loopBody)
			table.insert(lines, "end")
		end
	elseif loop.loopType == "children" or loop.loopType == "descendants" or loop.loopType == "ipairs" then
		local sourceVar = self:extractSourceVariable(loop.entries[1].data)
		
		if sourceVar then
			table.insert(lines, "for " .. iterVar .. ", " .. valVar .. " in ipairs(" .. sourceVar .. ") do")
			
			local sampleEntry = loop.entries[1]
			local sampleData = sampleEntry.data
			
			local loopBody = self:convertToLoopBody(sampleData, valVar)
			table.insert(lines, "\t" .. loopBody)
			
			table.insert(lines, "end")
		else
			for _, entry in ipairs(loop.entries) do
				local line = self:formatEntry(entry)
				if line then
					table.insert(lines, line)
				end
			end
		end
	else
		for _, entry in ipairs(loop.entries) do
			local line = self:formatEntry(entry)
			if line then
				table.insert(lines, line)
			end
		end
	end
	
	return lines
end

function Codegen:extractSourceVariable(data)
	local varMatch = string.match(data, "(v%d+)%[")
	if varMatch then
		return varMatch
	end
	
	varMatch = string.match(data, "(v%d+)_child")
	if varMatch then
		return varMatch
	end
	
	varMatch = string.match(data, "(v%d+)_desc")
	if varMatch then
		return varMatch
	end
	
	return nil
end

function Codegen:convertToLoopBody(data, valVar)
	local result = data
	
	result = string.gsub(result, "v%d+_child%d+", valVar)
	result = string.gsub(result, "v%d+_desc%d+", valVar)
	result = string.gsub(result, "v%d+%[%d+%]", valVar)
	
	result = string.gsub(result, "local v%d+=", "local result=")
	
	return result
end

function Codegen:formatEntry(entry)
	local op = entry.op
	local data = entry.data
	
	if op == "call" then
		if string.find(data, "error:") or string.find(data, "timeout") then
	return data
end
		return data
	elseif op == "newindex" then
		return data
	elseif op == "assign" then
		return data
	elseif op == "print" then
		return data
	elseif op == "warn" then
		return data
	elseif op == "branch_start" then
		return "if " .. data .. " then"
	elseif op == "branch_else" then
		if data == "" then
			return "else"
		else
			return data .. " then"
		end
	elseif op == "branch_end" then
		return "end"
	elseif op == "comment" then
		return "-- " .. data
	elseif op == "loop_start" then
		return data
	elseif op == "loop_end" then
		return "end"
	end
	
	return nil
end

function Codegen:expandCallbacks(code)
	local result = code
	local maxIterations = 100
	local iteration = 0
	
	while iteration < maxIterations do
		iteration = iteration + 1
		
		local startPos, endPos, cbIdStr = string.find(result, "function%(%)%-%-%[%[CB:(%d+)%]%]end")
		
		if not startPos then
			startPos, endPos, cbIdStr = string.find(result, "function%(%)[^e]*%-%-%[%[CB:(%d+)%]%][^e]*end")
		end
		
		if not startPos then
			local pattern = "function%(%)[%s]*%-%-%[%[CB:(%d+)%]%][%s]*end"
			startPos, endPos, cbIdStr = string.find(result, pattern)
		end
		
		if not startPos then
			local s, e = string.find(result, "%-%-%[%[CB:%d+%]%]")
			if s then
				local cbId = string.match(string.sub(result, s, e), "CB:(%d+)")
				if cbId then
					cbIdStr = cbId
					local funcStart = nil
					for i = s - 1, math.max(1, s - 50), -1 do
						local substr = string.sub(result, i, s - 1)
						if string.match(substr, "function%([^)]*%)[%s]*$") then
							funcStart = i
							break
						end
					end
					
					if funcStart then
						local funcEnd = string.find(result, "end", e + 1)
						if funcEnd then
							startPos = funcStart
							endPos = funcEnd + 2
						end
					end
				end
			end
		end
		
		if not startPos or not cbIdStr then
			break
		end
		
		local callbackId = tonumber(cbIdStr)
		local callbackBody = self:buildCallbackBody(callbackId)
		
		local before = string.sub(result, 1, startPos - 1)
		local after = string.sub(result, endPos + 1)
		
		local replacement
		if callbackBody == "" then
			replacement = "function()end"
		else
			replacement = "function()\n" .. callbackBody .. "\nend"
		end
		
		result = before .. replacement .. after
	end
	
	result = string.gsub(result, "%-%-%[%[CB:%d+%]%]", "")
	
	return result
end

function Codegen:buildCallbackBody(callbackId)
	local cbTrace = self.tracer:getCallbackTrace(callbackId)
	local lines = {}
	local processedIndices = {}
	
	local loopPatterns = self:detectLoops(cbTrace)
	
	for idx, entry in ipairs(cbTrace) do
		if not processedIndices[idx] then
			local inLoop = false
			
			for _, loop in ipairs(loopPatterns) do
				if idx >= loop.startIdx and idx <= loop.endIdx then
					if idx == loop.startIdx then
						local loopCode = self:generateLoopCode(loop)
						for _, line in ipairs(loopCode) do
							table.insert(lines, "\t" .. line)
						end
					end
					processedIndices[idx] = true
					inLoop = true
					break
				end
			end
			
			if not inLoop then
				local line = self:formatEntry(entry)
				if line then
					table.insert(lines, "\t" .. line)
				end
			end
		end
	end
	
	local body = table.concat(lines, "\n")
	body = self:expandCallbacks(body)
	
	return body
end

function Codegen:cleanupOutput(code)
	local result = code
	
	result = string.gsub(result, "\n\n\n+", "\n\n")
	
	result = string.gsub(result, "local (v%d+)=([^\n]+)\n[^\n]-%(\\1%)", function(var, expr)
		return "-- inlined: " .. expr
	end)
	
	return result
end

return Codegen


-- C:\Users\vendetta\Documents\moon\src\null.luau

local Tracer = require("./tracer")
local Sandbox = require("./sandbox")
local Codegen = require("./codegen")

local function cleanError(err)
	local msg = tostring(err)
	
	-- Remove file paths like C:\Users\...\file.luau:123:
	msg = string.gsub(msg, "[A-Za-z]:\\[^:]+:%d+:%s*", "")
	
	-- Remove Unix paths like /home/.../file.luau:123:
	msg = string.gsub(msg, "/[^:]+:%d+:%s*", "")
	
	-- Remove [string "..."]:123: patterns
	msg = string.gsub(msg, '%[string "[^"]*"%]:%d+:%s*', "")
	
	-- Remove chunk name patterns like chunk:123:
	msg = string.gsub(msg, "[%w_]+%.luau?:%d+:%s*", "")
	
	-- Trim whitespace
	msg = string.gsub(msg, "^%s+", "")
	msg = string.gsub(msg, "%s+$", "")
	
	return msg
end

local Null = {}
Null.__index = Null

function Null.new(config)
	local self = setmetatable({}, Null)
	self.config = config or {}
	self.config.callbackMaxDepth = self.config.callbackMaxDepth or 5
	self.config.callbackMaxIterations = self.config.callbackMaxIterations or 3
	self.config.maxTraceSize = self.config.maxTraceSize or 500000
	self.config.executionLimit = self.config.executionLimit or 1000000
	self.tracer = nil
	self.sandbox = nil
	self.executionDepth = 0
	return self
end

function Null:process(source, chunkName)
	chunkName = chunkName or "chunk"
	
	self.tracer = Tracer.new(self.config)
	self.sandbox = Sandbox.new(self.tracer, self.config)
	
	local success, err = self.sandbox:execute(source, chunkName)
	
	if not success then
		self:executeCallbacks()
		return false, err
	end
	
	self:executeCallbacks()
	
	return true, nil
end

function Null:executeCallbacks()
	local maxIterations = self.config.callbackMaxIterations
	local maxDepth = self.config.callbackMaxDepth
	local loopIterations = self.config.loopCallbackIterations or 3
	
	for iteration = 1, maxIterations do
		local executedAny = false
		
		local callbacksToRun = {}
		for _, cb in ipairs(self.tracer:getCallbacks()) do
			if not cb.executed and self.executionDepth < maxDepth then
				table.insert(callbacksToRun, cb)
			end
		end
		
		for _, cb in ipairs(callbacksToRun) do
	local isLoop = self.tracer:isLoopCallback(cb.id)
	local runs = isLoop and loopIterations or 1
	
	for run = 1, runs do
		if run > 1 then
			self.tracer:setCallbackContext(cb.id)
			self.tracer:log("call", "-- loop iteration " .. run)
		end
		self:executeCallback(cb, run)
	end
	
	executedAny = true
end
		
		if not executedAny then
			break
		end
	end
	
	if self.config.dualPathExecution then
		self:executeDualPathCallbacks()
	end
	
	self:executeHooks()
end

function Null:executeCallback(callback, iteration)
	if iteration == 1 then
		callback.executed = true
	end
	
	self.executionDepth = self.executionDepth + 1
	self.tracer:setCallbackContext(callback.id)
	
	local args = self.sandbox:buildMockArgs(callback.context, iteration)
	
	self.sandbox.executionCount = 0
	
	local startTime = os.clock()
	local maxTime = 2
	local operationsBefore = self.tracer:getOperationCount()
	
	local ok, err = pcall(function()
		callback.fn(table.unpack(args))
	end)
	
	local operationsAfter = self.tracer:getOperationCount()
	local operationsDelta = operationsAfter - operationsBefore
	
	if not ok then
	local errStr = cleanError(err)
	if string.find(errStr, "early_return") then
		self.tracer:log("call", "-- early return detected")
	elseif string.find(errStr, "break") then
		self.tracer:log("call", "-- break detected")
	elseif string.find(errStr, "Time limit") then
		self.tracer:log("call", "-- timeout")
	elseif string.find(errStr, "Execution limit") then
		self.tracer:log("call", "-- execution limit reached")
	else
		self.tracer:log("call", "-- error: " .. errStr:sub(1, 80))
	end
end
	
	if os.clock() - startTime > maxTime then
		self.tracer:log("call", "-- callback timeout")
	end
	
	if operationsDelta == 0 and ok then
		self.tracer:log("call", "-- callback had no traced operations")
	end
	
	self.tracer:clearCallbackContext()
	self.executionDepth = self.executionDepth - 1
end

function Null:getOutput()
	local codegen = Codegen.new(self.tracer)
	return codegen:generate()
end

function Null:getStats()
	return {
		operations = self.tracer:getOperationCount(),
		callbacks = self.tracer:getExecutedCallbackCount()
	}
end

function Null:getTrace()
	return self.tracer:getTrace()
end

function Null:getCallbacks()
	return self.tracer:getCallbacks()
end

function Null:executeDualPathCallbacks()
	local conditionalCallbacks = {}
	
	for _, cb in ipairs(self.tracer:getCallbacks()) do
		local ctx = string.lower(cb.context)
		if string.find(ctx, "findfirst") or 
		   string.find(ctx, "waitforchild") or
		   string.find(ctx, "getservice") or
		   string.find(ctx, "invoke") then
			table.insert(conditionalCallbacks, cb)
		end
	end
	
	for _, cb in ipairs(conditionalCallbacks) do
		if cb.executed and self.executionDepth < self.config.callbackMaxDepth then
			self.tracer:log("call", "-- alternate path execution")
			self.sandbox:setReturnMode("nil")
			self:executeCallback(cb, 1)
			self.sandbox:setReturnMode("normal")
		end
	end
end

function Null:executeHooks()
	local hooks = self.tracer.hooks
	
	for _, hook in ipairs(hooks) do
		if hook.active and not hook.executed then
			hook.executed = true
			
			self.tracer:log("call", "-- executing hook for: " .. hook.originalName)
			self.tracer:setCallbackContext(nil)
			
			local mockOriginal = function(...)
				return self.sandbox:createMock("original_result", "any")
			end
			
			local ok, err = pcall(function()
				hook.hookFn(mockOriginal)
			end)
			
			if not ok then
				self.tracer:log("call", "-- hook error: " .. cleanError(err):sub(1, 50))
			end
		end
	end
end

return Null


-- C:\Users\vendetta\Documents\moon\src\sandbox.luau

local Utils = require("./utils")
local Types = require("./types")

local Sandbox = {}
Sandbox.__index = Sandbox

local TRACED_METATABLE = nil

function Sandbox.new(tracer, config)
	local self = setmetatable({}, Sandbox)
	self.tracer = tracer
	self.config = config or {}
	self.env = {}
	self.services = {}
	self.executionCount = 0
	self.executionLimit = tracer.config.executionLimit or 1000000
    self.startTime = os.clock()
	self.recentLogs = {}
    self.spamDetected = {}
    self.callPatternCount = {}
    self.maxPatternRepeat = 3
	self:initMetatable()
	self:buildEnvironment()
	self.returnMode = "normal"
	self.methodReturns = {}
	self.propertyValues = {}
	self:initSmartReturns()
	return self
end

function Sandbox:initSmartReturns()
	local sandbox = self
	
	self.methodReturns = {
		SetAttribute = function(name, args)
			return nil
		end,
		GetAttribute = function(name, args)
			local attrName = args[1] or "attr"
			return sandbox:createMock(name .. ":GetAttribute(" .. Utils.serialize(attrName, sandbox) .. ")", "any")
		end,
		GetAttributes = function(name)
			return {}
		end,
		SetPrimaryPartCFrame = function(name, args)
			return nil
		end,
		MoveTo = function(name, args)
			return nil
		end,
		PivotTo = function(name, args)
			return nil
		end,
		GetPivot = function(name)
			return sandbox:createMock(name .. ":GetPivot()", "CFrame")
		end,
		GetBoundingBox = function(name)
			return sandbox:createMock(name .. "_cf", "CFrame"), sandbox:createMock(name .. "_size", "Vector3")
		end,
		GetExtentsSize = function(name)
			return sandbox:createMock(name .. ":GetExtentsSize()", "Vector3")
		end,
		GetChildren = function(name)
			local children = {}
			for i = 1, 3 do
				local childName = name .. "_child" .. i
				local child = sandbox:createMock(childName, "Instance")
				children[i] = child
			end
			setmetatable(children, {
				__index = function(t, k)
					if type(k) == "number" then
						if not rawget(t, k) then
							local childName = name .. "_child" .. k
							local child = sandbox:createMock(childName, "Instance")
							rawset(t, k, child)
						end
						return rawget(t, k)
					end
					return nil
				end
			})
			return children
		end,
		GetDescendants = function(name)
			local descendants = {}
			for i = 1, 5 do
				local descName = name .. "_desc" .. i
				local desc = sandbox:createMock(descName, "Instance")
				descendants[i] = desc
			end
			setmetatable(descendants, {
				__index = function(t, k)
					if type(k) == "number" then
						if not rawget(t, k) then
							local descName = name .. "_desc" .. k
							local desc = sandbox:createMock(descName, "Instance")
							rawset(t, k, desc)
						end
						return rawget(t, k)
					end
					return nil
				end
			})
			return descendants
		end,
		Clone = function(name, mockType)
			local varName = sandbox.tracer:nextVar()
			sandbox:log("call", "local " .. varName .. "=" .. name .. ":Clone()")
			return sandbox:createMock(varName, mockType)
		end,
		GetPlayers = function(name)
			local p1 = sandbox:createMock("player1", "Player")
			local p2 = sandbox:createMock("player2", "Player")
			return {p1, p2}
		end,
		Cross = function(name, args)
			local other = args[1]
			local otherStr = sandbox:isTraced(other) and sandbox:getName(other) or Utils.serialize(other, sandbox)
			return sandbox:createMock(name .. ":Cross(" .. otherStr .. ")", "Vector3")
		end,
		Dot = function(name, args)
			return 0
		end,
		FuzzyEq = function()
			return true
		end,
		Lerp = function(name, args)
			local goal = args[1]
			local alpha = args[2]
			local goalStr = sandbox:isTraced(goal) and sandbox:getName(goal) or Utils.serialize(goal, sandbox)
			local alphaStr = sandbox:isTraced(alpha) and sandbox:getName(alpha) or tostring(alpha)
			return sandbox:createMock(name .. ":Lerp(" .. goalStr .. "," .. alphaStr .. ")", "CFrame")
		end,
		ToWorldSpace = function(name, args)
			local cf = args[1]
			local cfStr = sandbox:isTraced(cf) and sandbox:getName(cf) or Utils.serialize(cf, sandbox)
			return sandbox:createMock(name .. ":ToWorldSpace(" .. cfStr .. ")", "CFrame")
		end,
		ToObjectSpace = function(name, args)
			local cf = args[1]
			local cfStr = sandbox:isTraced(cf) and sandbox:getName(cf) or Utils.serialize(cf, sandbox)
			return sandbox:createMock(name .. ":ToObjectSpace(" .. cfStr .. ")", "CFrame")
		end,
		PointToWorldSpace = function(name, args)
			local point = args[1]
			local pointStr = sandbox:isTraced(point) and sandbox:getName(point) or Utils.serialize(point, sandbox)
			return sandbox:createMock(name .. ":PointToWorldSpace(" .. pointStr .. ")", "Vector3")
		end,
		PointToObjectSpace = function(name, args)
			local point = args[1]
			local pointStr = sandbox:isTraced(point) and sandbox:getName(point) or Utils.serialize(point, sandbox)
			return sandbox:createMock(name .. ":PointToObjectSpace(" .. pointStr .. ")", "Vector3")
		end,
		VectorToWorldSpace = function(name, args)
			local vec = args[1]
			local vecStr = sandbox:isTraced(vec) and sandbox:getName(vec) or Utils.serialize(vec, sandbox)
			return sandbox:createMock(name .. ":VectorToWorldSpace(" .. vecStr .. ")", "Vector3")
		end,
		VectorToObjectSpace = function(name, args)
			local vec = args[1]
			local vecStr = sandbox:isTraced(vec) and sandbox:getName(vec) or Utils.serialize(vec, sandbox)
			return sandbox:createMock(name .. ":VectorToObjectSpace(" .. vecStr .. ")", "Vector3")
		end,
		GetComponents = function(name)
			return 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1
		end,
		ToEulerAnglesXYZ = function(name)
			return 0, 0, 0
		end,
		ToEulerAnglesYXZ = function(name)
			return 0, 0, 0
		end,
		ToOrientation = function(name)
			return 0, 0, 0
		end,
		ToAxisAngle = function(name)
			return sandbox:createMock(name .. "_axis", "Vector3"), 0
		end,
		GetPlayerFromCharacter = function(name)
			return sandbox:createMock("player", "Player")
		end,
		FindFirstChildOfClass = function(name, args)
			local className = args[1] or "Instance"
			local varName = sandbox.tracer:nextVar()
			return sandbox:createMock(varName, className)
		end,
		FindFirstChildWhichIsA = function(name, args)
			local className = args[1] or "Instance"
			local varName = sandbox.tracer:nextVar()
			return sandbox:createMock(varName, className)
		end,
		IsA = function(name, args)
			return true
		end,
		IsDescendantOf = function()
			return true
		end,
		IsAncestorOf = function()
			return false
		end,
		GetFullName = function(name)
			return name
		end,
		InvokeServer = function(name, args)
			local varName = sandbox.tracer:nextVar()
			return sandbox:createMock(varName, "any")
		end,
		Raycast = function(name)
			local varName = sandbox.tracer:nextVar()
			return sandbox:createMock(varName, "RaycastResult")
		end
	}
	
	self.propertyValues = {
		Name = function(mockName) 
			return mockName 
		end,
		ClassName = function(_, mockType) 
			return mockType or "Instance" 
		end,
		UserId = function(mockName)
	if string.find(mockName, "LocalPlayer") then
		return self.config.userId or 1234567890
	end
	return 0
end,
Name = function(mockName, mockType)
	if string.find(mockName, "LocalPlayer") then
		return self.config.username or "Player"
	end
	local parts = {}
	for part in string.gmatch(mockName, "[^%.]+") do
		table.insert(parts, part)
	end
	return parts[#parts] or mockName
end,
DisplayName = function(mockName)
	if string.find(mockName, "LocalPlayer") then
		return self.config.displayName or "Player"
	end
	return "Player"
end,
AccountAge = function()
	return 365
end,
		Parent = function(mockName)
			local parts = {}
			for part in string.gmatch(mockName, "[^%.]+") do
				table.insert(parts, part)
			end
			if #parts > 1 then
				table.remove(parts)
				return sandbox:createMock(table.concat(parts, "."), "Instance")
			end
			return sandbox:createMock("game", "DataModel")
		end,
		Position = function(mockName)
			return sandbox:createMock(mockName .. ".Position", "Vector3")
		end,
		CFrame = function(mockName)
			return sandbox:createMock(mockName .. ".CFrame", "CFrame")
		end,
		Character = function(mockName)
			return sandbox:createMock(mockName .. ".Character", "Model")
		end,
		PrimaryPart = function(mockName)
			return sandbox:createMock(mockName .. ".PrimaryPart", "BasePart")
		end,
		HumanoidRootPart = function(mockName)
			return sandbox:createMock(mockName .. ".HumanoidRootPart", "BasePart")
		end,
		Humanoid = function(mockName)
			return sandbox:createMock(mockName .. ".Humanoid", "Humanoid")
		end,
		Health = function()
			return 100
		end,
		MaxHealth = function()
			return 100
		end,
		WalkSpeed = function()
			return 16
		end,
		JumpPower = function()
			return 50
		end,
		Transparency = function()
			return 0
		end,
		Visible = function()
			return true
		end,
		Enabled = function()
			return true
		end,
		Value = function(mockName)
			return sandbox:createMock(mockName .. ".Value", "any")
		end,
		Text = function()
			return ""
		end,
		Size = function(mockName)
			return sandbox:createMock(mockName .. ".Size", "Vector3")
		end,
		Anchored = function()
			return false
		end,
		X = function()
			return 0
		end,
		Y = function()
			return 0
		end,
		Z = function()
			return 0
		end,
		Magnitude = function()
			return 0
		end,
		Unit = function(mockName)
			return sandbox:createMock(mockName .. ".Unit", "Vector3")
		end,
		Scale = function()
			return 0
		end,
		Offset = function()
			return 0
		end,
		Width = function()
			return 0
		end,
		Height = function()
			return 0
		end,
		R = function()
			return 0
		end,
		G = function()
			return 0
		end,
		B = function()
			return 0
		end,
		AbsolutePosition = function(mockName)
			return sandbox:createMock(mockName .. ".AbsolutePosition", "Vector2")
		end,
		AbsoluteSize = function(mockName)
			return sandbox:createMock(mockName .. ".AbsoluteSize", "Vector2")
		end,
		AbsoluteRotation = function()
			return 0
		end,
		CanCollide = function()
			return true
		end
	}
end

function Sandbox:setReturnMode(mode)
	self.returnMode = mode
end

function Sandbox:getSmartReturn(methodName, baseName, mockType, args)
	if self.returnMode == "nil" then
		local nilMethods = {
			"FindFirstChild",
			"FindFirstChildOfClass", 
			"FindFirstChildWhichIsA",
			"FindFirstAncestor",
			"FindFirstAncestorOfClass",
			"FindFirstAncestorWhichIsA",
			"FindFirstDescendant"
		}
		
		for _, method in ipairs(nilMethods) do
			if methodName == method then
				self.tracer:markNilCheck(baseName)
				return nil, true
			end
		end
	end
	
	local handler = self.methodReturns[methodName]
	if handler then
		local result = handler(baseName, args)
		return result, true
	end
	
	if methodName == "WaitForChild" then
		local childName = args[1]
		if type(childName) == "string" then
			local varName = self.tracer:nextVar()
			local mock = self:createMock(varName, "Instance")
			return mock, true
		elseif self:isTraced(childName) then
			local varName = self.tracer:nextVar()
			local mock = self:createMock(varName, "Instance")
			return mock, true
		end
	end
	
	if methodName == "FindFirstChild" then
		local childName = args[1]
		local recursive = args[2]
		
		local varName = self.tracer:nextVar()
		self.tracer:markConditionalPoint(varName, "nil_possible")
		
		local mock = self:createMock(varName, "Instance")
		return mock, true
	end
	
	return nil, false
end

function Sandbox:getSmartProperty(propName, mockName, mockType)
	local handler = self.propertyValues[propName]
	if handler then
		return handler(mockName, mockType), true
	end
	return nil, false
end

function Sandbox:createMock(name, mockType)
	return setmetatable({
		_name = name,
		_type = mockType or "Instance",
		_cache = {}
	}, TRACED_METATABLE)
end

function Sandbox:isTraced(value)
	if type(value) ~= "table" then
		return false
	end
	return rawget(value, "_name") ~= nil
end

function Sandbox:getName(value)
	if self:isTraced(value) then
		return rawget(value, "_name")
	end
	return nil
end

function Sandbox:getType(value)
	if self:isTraced(value) then
		return rawget(value, "_type")
	end
	return nil
end

function Sandbox:checkExecution()
	self.executionCount = self.executionCount + 1
	if self.executionCount > self.executionLimit then
		error("Null: Execution limit exceeded")
	end
	if self.executionCount % 10000 == 0 then
		if os.clock() - (self.startTime or os.clock()) > 5 then
			error("Null: Time limit exceeded")
		end
	end
end

function Sandbox:shouldLog(content)
	local normalized = content
	normalized = string.gsub(normalized, "v%d+", "vN")
	normalized = string.gsub(normalized, "%d+%.%d+", "N")
	normalized = string.gsub(normalized, "%d+", "N")
	normalized = string.gsub(normalized, '"[^"]*"', '"S"')
	normalized = string.gsub(normalized, "%s+", "")
	
	if self.callPatternCount[normalized] then
		self.callPatternCount[normalized] = self.callPatternCount[normalized] + 1
		if self.callPatternCount[normalized] > self.maxPatternRepeat then
			return false
		end
	else
		self.callPatternCount[normalized] = 1
	end
	
	table.insert(self.recentLogs, normalized)
	if #self.recentLogs > 10 then
		table.remove(self.recentLogs, 1)
	end
	
	local count = 0
	for _, log in ipairs(self.recentLogs) do
		if log == normalized then
			count = count + 1
		end
	end
	
	if count >= 3 then
		return false
	end
	
	return true
end

function Sandbox:log(op, data)
	self:checkExecution()
	if self:shouldLog(data) then
		self.tracer:log(op, data)
	end
end

function Sandbox:initMetatable()
	if TRACED_METATABLE then
		return
	end
	
	local sandbox = self
	local tracer = self.tracer
	
	TRACED_METATABLE = {}
	
	TRACED_METATABLE.__index = function(t, key)
	if type(key) == "string" and string.sub(key, 1, 1) == "_" then
		return rawget(t, key)
	end
	
	local cache = rawget(t, "_cache")
	if cache and cache[key] then
		return cache[key]
	end
	
	local name = rawget(t, "_name")
	local mockType = rawget(t, "_type")
	
	local smartValue, hasSmartValue = sandbox:getSmartProperty(key, name, mockType)
	if hasSmartValue then
		if type(smartValue) ~= "table" then
			return smartValue
		end
		if not sandbox:isTraced(smartValue) then
			return smartValue
		end
		if cache then
			cache[key] = smartValue
		end
		return smartValue
	end
	
	if sandbox:isNumericProperty(key, mockType) then
		return 0
	end
	
	if sandbox:isBooleanProperty(key) then
		return true
	end
	
	if sandbox:isStringProperty(key) then
		if key == "Name" then
			local parts = {}
			for part in string.gmatch(name, "[^%.]+") do
				table.insert(parts, part)
			end
			return parts[#parts] or name
		end
		if key == "ClassName" then
			return mockType or "Instance"
		end
		return ""
	end
	
	local newName
	if type(key) == "string" and string.match(key, "^[%a_][%w_]*$") then
		newName = name .. "." .. key
	else
		newName = name .. "[" .. Utils.serialize(key, sandbox) .. "]"
	end
	
	local inferredType = sandbox:inferType(key, mockType)
	local mock = sandbox:createMock(newName, inferredType)
	
	if cache then
		cache[key] = mock
	end
	
	return mock
end

function Sandbox:inferType(key, parentType)
	local typeMap = {
		Character = "Model",
		PrimaryPart = "BasePart",
		HumanoidRootPart = "BasePart",
		Humanoid = "Humanoid",
		Head = "BasePart",
		Torso = "BasePart",
		Parent = "Instance",
		Position = "Vector3",
		CFrame = "CFrame",
		Size = "Vector3",
		Color = "Color3",
		BrickColor = "BrickColor",
		Transparency = "number",
		CanCollide = "boolean",
		Anchored = "boolean",
		Value = "any",
		Text = "string",
		Visible = "boolean",
		Enabled = "boolean",
		LocalPlayer = "Player",
		PlayerGui = "PlayerGui",
		Backpack = "Backpack",
		CurrentCamera = "Camera",
		Terrain = "Terrain",
		Mouse = "Mouse",
		Changed = "RBXScriptSignal",
		ChildAdded = "RBXScriptSignal",
		ChildRemoved = "RBXScriptSignal",
		DescendantAdded = "RBXScriptSignal",
		DescendantRemoving = "RBXScriptSignal",
		Touched = "RBXScriptSignal",
		TouchEnded = "RBXScriptSignal",
		PlayerAdded = "RBXScriptSignal",
		PlayerRemoving = "RBXScriptSignal",
		CharacterAdded = "RBXScriptSignal",
		CharacterRemoving = "RBXScriptSignal",
		Heartbeat = "RBXScriptSignal",
		RenderStepped = "RBXScriptSignal",
		Stepped = "RBXScriptSignal",
		InputBegan = "RBXScriptSignal",
		InputEnded = "RBXScriptSignal",
		InputChanged = "RBXScriptSignal",
		OnServerEvent = "RBXScriptSignal",
		OnClientEvent = "RBXScriptSignal",
		OnInvoke = "RBXScriptSignal",
		MouseButton1Click = "RBXScriptSignal",
		MouseButton1Down = "RBXScriptSignal",
		MouseButton1Up = "RBXScriptSignal",
		MouseButton2Click = "RBXScriptSignal",
		MouseEnter = "RBXScriptSignal",
		MouseLeave = "RBXScriptSignal",
		FocusLost = "RBXScriptSignal",
		Activated = "RBXScriptSignal"
	}
	
	if type(key) == "string" then
		return typeMap[key] or "any"
	end
	
	return "any"
end

function Sandbox:wrapWithConditional(value, varName)
	if not self:isTraced(value) then
		return value
	end
	
	local wrapper = {
		_name = varName,
		_type = "conditional",
		_cache = {},
		_originalValue = value,
		_isConditional = true
	}
	
	return setmetatable(wrapper, getmetatable(value))
end

function Sandbox:trackBranch(condition, branchType)
	local condStr = "unknown"
	
	if self:isTraced(condition) then
		condStr = self:getName(condition)
	elseif type(condition) == "string" then
		condStr = condition
	elseif type(condition) == "boolean" then
		condStr = tostring(condition)
	end
	
	if branchType == "if" then
		self.tracer:pushBranch("if", condStr)
		self.tracer:log("branch_start", condStr)
	elseif branchType == "elseif" then
		self.tracer:log("branch_else", "elseif " .. condStr)
	elseif branchType == "else" then
		self.tracer:log("branch_else", "")
	elseif branchType == "end" then
		self.tracer:popBranch()
		self.tracer:log("branch_end", "")
	end
end

function Sandbox:getCurrentBranch()
	return self.tracer.currentBranch
end

function Sandbox:inferReturnType(methodName, parentType)
	local returnTypes = {
		Clone = parentType or "Instance",
		FindFirstChild = "Instance",
		FindFirstChildOfClass = "Instance",
		FindFirstChildWhichIsA = "Instance",
		FindFirstAncestor = "Instance",
		FindFirstAncestorOfClass = "Instance",
		FindFirstAncestorWhichIsA = "Instance",
		WaitForChild = "Instance",
		GetChildren = "table",
		GetDescendants = "table",
		GetPlayers = "table",
		GetPlayerFromCharacter = "Player",
		GetService = "Instance",
		Create = "Tween",
		Play = "nil",
		Stop = "nil",
		Raycast = "RaycastResult",
		InvokeServer = "any",
		InvokeClient = "any",
		FireServer = "nil",
		FireClient = "nil",
		FireAllClients = "nil",
		Connect = "RBXScriptConnection",
		Wait = "any",
		Destroy = "nil",
		ClearAllChildren = "nil",
		GetAttribute = "any",
		SetAttribute = "nil",
		GetAttributes = "table",
		GetTouchingParts = "table",
		GetPartsInPart = "table",
		GetPartBoundsInBox = "table",
		GetPartBoundsInRadius = "table",
		Kick = "nil",
		LoadCharacter = "nil",
		GetMouse = "Mouse",
		GetPropertyChangedSignal = "RBXScriptSignal"
	}
	
	return returnTypes[methodName] or "any"
end

function Sandbox:isNumericProperty(propName, mockType)
	local numericProps = {
		X = true, Y = true, Z = true,
		W = true,
		Magnitude = true,
		R = true, G = true, B = true,
		Scale = true, Offset = true,
		Width = true, Height = true,
		Health = true, MaxHealth = true,
		WalkSpeed = true, JumpPower = true, JumpHeight = true,
		Transparency = true,
		AbsoluteRotation = true,
		LayoutOrder = true, ZIndex = true,
		TextSize = true,
		Mass = true,
		TimePosition = true, TimeLength = true,
		Volume = true, PlaybackSpeed = true,
		FieldOfView = true,
		Brightness = true,
		ClockTime = true, GeographicLatitude = true
	}
	
	if numericProps[propName] then
		return true
	end
	
	local numericTypes = {
		number = true,
		float = true,
		int = true,
		double = true
	}
	
	if numericTypes[mockType] then
		return true
	end
	
	return false
end

function Sandbox:isBooleanProperty(propName)
	local boolProps = {
		Visible = true, Enabled = true,
		Anchored = true, CanCollide = true, CanTouch = true, CanQuery = true,
		Locked = true, Archivable = true,
		Active = true, Draggable = true,
		Modal = true, ClipsDescendants = true,
		Looped = true, Playing = true,
		AutoRotate = true,
		CastShadow = true
	}
	
	return boolProps[propName] == true
end

function Sandbox:isStringProperty(propName)
	local stringProps = {
		Name = true, ClassName = true,
		Text = true, PlaceholderText = true,
		Value = true,
		DisplayName = true,
		JobId = true
	}
	
	return stringProps[propName] == true
end
	
	TRACED_METATABLE.__newindex = function(t, key, value)
		local name = rawget(t, "_name")
		local propName
		
		if type(key) == "string" and string.match(key, "^[%a_][%w_]*$") then
			propName = name .. "." .. key
		else
			propName = name .. "[" .. Utils.serialize(key, sandbox) .. "]"
		end
		
		sandbox:log("newindex", propName .. "=" .. Utils.serialize(value, sandbox))
	end
	
	TRACED_METATABLE.__call = function(t, firstArg, ...)
	local name = rawget(t, "_name")
	local mockType = rawget(t, "_type")
	local allArgs = {firstArg, ...}
	
	local isMethod = false
	local methodBase = nil
	local methodName = nil
	local args = allArgs
	
	local dotPos = string.find(name, "%.[^.]+$")
	local colonPos = string.find(name, ":[^:]+$")
	
	if dotPos and firstArg ~= nil and sandbox:isTraced(firstArg) then
		local parentName = string.sub(name, 1, dotPos - 1)
		local firstName = sandbox:getName(firstArg)
		if firstName == parentName then
			isMethod = true
			methodBase = parentName
			methodName = string.sub(name, dotPos + 1)
			args = {...}
		end
	end
	
	if not methodName then
		local lastDot = string.find(name, "[%.:]([^%.:]+)$")
		if lastDot then
			methodName = string.sub(name, lastDot + 1)
		else
			methodName = name
		end
	end
	
	if methodName == "Connect" or methodName == "connect" then
		local callback = args[1]
		if type(callback) == "function" then
			local signalName = methodBase or name
			local cb = tracer:registerCallback(callback, signalName)
			
			if string.find(string.lower(signalName), "heartbeat") or
			   string.find(string.lower(signalName), "renderstepped") or
			   string.find(string.lower(signalName), "stepped") then
				tracer:markAsLoopCallback(cb.id)
			end
			
			local callStr
			if isMethod then
				callStr = methodBase .. ":" .. methodName .. "(function()--[[CB:" .. cb.id .. "]]end)"
			else
				callStr = name .. "(function()--[[CB:" .. cb.id .. "]]end)"
			end
			
			local resultVar = tracer:nextVar()
			sandbox:log("call", "local " .. resultVar .. "=" .. callStr)
			return sandbox:createMock(resultVar, "RBXScriptConnection")
		end
	end
	
	local smartResult, hasSmartResult = sandbox:getSmartReturn(methodName, methodBase or name, mockType, args)
	if hasSmartResult then
		local argStrs = {}
		for _, arg in ipairs(args) do
			if type(arg) == "function" then
				local cb = tracer:registerCallback(arg, name)
				table.insert(argStrs, "function()--[[CB:" .. cb.id .. "]]end")
			else
				table.insert(argStrs, Utils.serialize(arg, sandbox))
			end
		end
		
		local callStr
		if isMethod then
			callStr = methodBase .. ":" .. methodName .. "(" .. table.concat(argStrs, ",") .. ")"
		else
			callStr = name .. "(" .. table.concat(argStrs, ",") .. ")"
		end
		
		if smartResult == nil then
			sandbox:log("call", callStr .. " -- returns nil")
			return nil
		end
		
		if type(smartResult) == "table" and not sandbox:isTraced(smartResult) then
			local resultVar = tracer:nextVar()
			sandbox:log("call", "local " .. resultVar .. "=" .. callStr)
			return smartResult
		end
		
		if type(smartResult) ~= "table" then
			local resultVar = tracer:nextVar()
			sandbox:log("call", "local " .. resultVar .. "=" .. callStr)
			return smartResult
		end
		
		local resultVar = tracer:nextVar()
		sandbox:log("call", "local " .. resultVar .. "=" .. callStr)
		return smartResult
	end
	
	local argStrs = {}
	for i, arg in ipairs(args) do
		if type(arg) == "function" then
			local cb = tracer:registerCallback(arg, name)
			table.insert(argStrs, "function()--[[CB:" .. cb.id .. "]]end")
		else
			table.insert(argStrs, Utils.serialize(arg, sandbox))
		end
	end
	
	local callStr
	if isMethod then
		callStr = methodBase .. ":" .. methodName .. "(" .. table.concat(argStrs, ",") .. ")"
	else
		callStr = name .. "(" .. table.concat(argStrs, ",") .. ")"
	end
	
	local resultVar = tracer:nextVar()
	sandbox:log("call", "local " .. resultVar .. "=" .. callStr)
	
	local resultType = sandbox:inferReturnType(methodName, mockType)
	return sandbox:createMock(resultVar, resultType)
end
	
	TRACED_METATABLE.__tostring = function(t)
		return rawget(t, "_name")
	end
	
	TRACED_METATABLE.__concat = function(a, b)
	local aStr, bStr
	
	if sandbox:isTraced(a) then
		aStr = sandbox:getName(a)
	elseif type(a) == "string" then
		aStr = Utils.serialize(a, sandbox)
	else
		aStr = tostring(a)
	end
	
	if sandbox:isTraced(b) then
		bStr = sandbox:getName(b)
	elseif type(b) == "string" then
		bStr = Utils.serialize(b, sandbox)
	else
		bStr = tostring(b)
	end
	
	return sandbox:createMock("(" .. aStr .. ".." .. bStr .. ")", "string")
end
	
	TRACED_METATABLE.__len = function(t)
		return 0
	end
	
	TRACED_METATABLE.__add = function(a, b)
	local aIsNum = type(a) == "number"
	local bIsNum = type(b) == "number"
	
	if aIsNum and sandbox:isTraced(b) then
		return sandbox:createMock("(" .. tostring(a) .. "+" .. sandbox:getName(b) .. ")", "number")
	elseif bIsNum and sandbox:isTraced(a) then
		return sandbox:createMock("(" .. sandbox:getName(a) .. "+" .. tostring(b) .. ")", "number")
	elseif sandbox:isTraced(a) and sandbox:isTraced(b) then
		local aType = sandbox:getType(a)
		local bType = sandbox:getType(b)
		
		if aType == "Vector3" or bType == "Vector3" then
			return sandbox:createMock("(" .. sandbox:getName(a) .. "+" .. sandbox:getName(b) .. ")", "Vector3")
		elseif aType == "Vector2" or bType == "Vector2" then
			return sandbox:createMock("(" .. sandbox:getName(a) .. "+" .. sandbox:getName(b) .. ")", "Vector2")
		elseif aType == "CFrame" or bType == "CFrame" then
			return sandbox:createMock("(" .. sandbox:getName(a) .. "+" .. sandbox:getName(b) .. ")", "CFrame")
		end
		
		return sandbox:createMock("(" .. sandbox:getName(a) .. "+" .. sandbox:getName(b) .. ")", "number")
	end
	
	local aStr = Utils.serialize(a, sandbox)
	local bStr = Utils.serialize(b, sandbox)
	return sandbox:createMock("(" .. aStr .. "+" .. bStr .. ")", "number")
end
	
	TRACED_METATABLE.__sub = function(a, b)
	local aIsNum = type(a) == "number"
	local bIsNum = type(b) == "number"
	
	if aIsNum and sandbox:isTraced(b) then
		return sandbox:createMock("(" .. tostring(a) .. "-" .. sandbox:getName(b) .. ")", "number")
	elseif bIsNum and sandbox:isTraced(a) then
		return sandbox:createMock("(" .. sandbox:getName(a) .. "-" .. tostring(b) .. ")", "number")
	elseif sandbox:isTraced(a) and sandbox:isTraced(b) then
		local aType = sandbox:getType(a)
		local bType = sandbox:getType(b)
		
		if aType == "Vector3" or bType == "Vector3" then
			return sandbox:createMock("(" .. sandbox:getName(a) .. "-" .. sandbox:getName(b) .. ")", "Vector3")
		elseif aType == "Vector2" or bType == "Vector2" then
			return sandbox:createMock("(" .. sandbox:getName(a) .. "-" .. sandbox:getName(b) .. ")", "Vector2")
		end
		
		return sandbox:createMock("(" .. sandbox:getName(a) .. "-" .. sandbox:getName(b) .. ")", "number")
	end
	
	local aStr = Utils.serialize(a, sandbox)
	local bStr = Utils.serialize(b, sandbox)
	return sandbox:createMock("(" .. aStr .. "-" .. bStr .. ")", "number")
end

TRACED_METATABLE.__mul = function(a, b)
	local aType = sandbox:isTraced(a) and sandbox:getType(a)
	local bType = sandbox:isTraced(b) and sandbox:getType(b)
	
	local resultType = "number"
	if aType == "Vector3" or bType == "Vector3" then
		resultType = "Vector3"
	elseif aType == "Vector2" or bType == "Vector2" then
		resultType = "Vector2"
	elseif aType == "CFrame" or bType == "CFrame" then
		resultType = "CFrame"
	end
	
	local aStr = sandbox:isTraced(a) and sandbox:getName(a) or Utils.serialize(a, sandbox)
	local bStr = sandbox:isTraced(b) and sandbox:getName(b) or Utils.serialize(b, sandbox)
	
	return sandbox:createMock("(" .. aStr .. "*" .. bStr .. ")", resultType)
end

TRACED_METATABLE.__div = function(a, b)
	local aType = sandbox:isTraced(a) and sandbox:getType(a)
	local bType = sandbox:isTraced(b) and sandbox:getType(b)
	
	local resultType = "number"
	if aType == "Vector3" or bType == "Vector3" then
		resultType = "Vector3"
	elseif aType == "Vector2" or bType == "Vector2" then
		resultType = "Vector2"
	end
	
	local aStr = sandbox:isTraced(a) and sandbox:getName(a) or Utils.serialize(a, sandbox)
	local bStr = sandbox:isTraced(b) and sandbox:getName(b) or Utils.serialize(b, sandbox)
	
	return sandbox:createMock("(" .. aStr .. "/" .. bStr .. ")", resultType)
end

TRACED_METATABLE.__mod = function(a, b)
	local aStr = sandbox:isTraced(a) and sandbox:getName(a) or Utils.serialize(a, sandbox)
	local bStr = sandbox:isTraced(b) and sandbox:getName(b) or Utils.serialize(b, sandbox)
	return sandbox:createMock("(" .. aStr .. "%" .. bStr .. ")", "number")
end

TRACED_METATABLE.__pow = function(a, b)
	local aStr = sandbox:isTraced(a) and sandbox:getName(a) or Utils.serialize(a, sandbox)
	local bStr = sandbox:isTraced(b) and sandbox:getName(b) or Utils.serialize(b, sandbox)
	return sandbox:createMock("(" .. aStr .. "^" .. bStr .. ")", "number")
end

TRACED_METATABLE.__unm = function(a)
	local aType = sandbox:isTraced(a) and sandbox:getType(a)
	local resultType = aType or "number"
	local aStr = sandbox:isTraced(a) and sandbox:getName(a) or Utils.serialize(a, sandbox)
	return sandbox:createMock("(-" .. aStr .. ")", resultType)
end
	
	TRACED_METATABLE.__eq = function(a, b)
		return true
	end
	
	TRACED_METATABLE.__lt = function(a, b)
		return false
	end
	
	TRACED_METATABLE.__le = function(a, b)
		return true
	end
end

function Sandbox:buildEnvironment()
	self:addStandardLibrary()
	self:addRobloxGlobals()
	self:addRobloxServices()
	self:addExecutorFunctions()
	Types.addToEnvironment(self.env, self)
end

function Sandbox:addStandardLibrary()
	local env = self.env
	local sandbox = self
	local tracer = self.tracer
	
	env.type = type
	env.type = function(v)
	if sandbox:isTraced(v) then
		local name = sandbox:getName(v)
		tracer:markConditionalPoint(name, "type_check")
		return sandbox:getType(v) or "userdata"
	end
	return type(v)
end
	
	env.typeof = function(v)
	if sandbox:isTraced(v) then
		local name = sandbox:getName(v)
		tracer:markConditionalPoint(name, "typeof_check")
		return sandbox:getType(v) or "Instance"
	end
	return type(v)
end
	
	env.tostring = function(v)
	if sandbox:isTraced(v) then
		local name = sandbox:getName(v)
		local mockType = sandbox:getType(v)
		
		if mockType == "Vector3" then
			return "0, 0, 0"
		elseif mockType == "Vector2" then
			return "0, 0"
		elseif mockType == "CFrame" then
			return "0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1"
		elseif mockType == "Color3" then
			return "0, 0, 0"
		elseif mockType == "UDim2" then
			return "{0, 0}, {0, 0}"
		elseif mockType == "UDim" then
			return "0, 0"
		end
		
		return name
	end
	return tostring(v)
end
	
	env.tonumber = function(v, base)
	if sandbox:isTraced(v) then
		local mockType = sandbox:getType(v)
		if mockType == "number" then
			return 0
		end
		return nil
	end
	return tonumber(v, base)
end
	env.pairs = function(t)
	if sandbox:isTraced(t) then
		local name = sandbox:getName(t)
		tracer:log("call", "-- iterating pairs(" .. name .. ")")
		
		local children = {}
		for i = 1, 3 do
			local childName = name .. "_item" .. i
			local child = sandbox:createMock(childName, "any")
			table.insert(children, {i, child})
		end
		
		local idx = 0
		return function()
			idx = idx + 1
			if idx <= #children then
				return children[idx][1], children[idx][2]
			end
			return nil
		end
	end
	return pairs(t)
end

env.ipairs = function(t)
	if sandbox:isTraced(t) then
		local name = sandbox:getName(t)
		tracer:log("call", "-- iterating ipairs(" .. name .. ")")
		
		local children = {}
		for i = 1, 3 do
			local childName = name .. "_item" .. i
			local child = sandbox:createMock(childName, "any")
			table.insert(children, child)
		end
		
		local idx = 0
		return function()
			idx = idx + 1
			if idx <= #children then
				return idx, children[idx]
			end
			return nil
		end
	end
	return ipairs(t)
end
	env.next = next
	env.next = function(t, k)
	if sandbox:isTraced(t) then
		local name = sandbox:getName(t)
		
		if k == nil then
			local firstKey = name .. "_key1"
			local firstVal = sandbox:createMock(name .. "_val1", "any")
			return firstKey, firstVal
		else
			return nil
		end
	end
	return next(t, k)
end
	env.select = select
	env.unpack = table.unpack
	env.pcall = pcall
	env.xpcall = xpcall
	env.error = error
	env.assert = assert
	
	env.if_ = function(cond)
	return {
		_isIfChain = true,
		_condition = cond,
		Then = function(self, thenValue)
			self._thenValue = thenValue
			return self
		end,
		Else = function(self, elseValue)
			if self._condition then
				return self._thenValue
			else
				return elseValue
			end
		end
	}
end
	
	env.rawget = function(t, k)
		if type(t) ~= "table" then
			return nil
		end
		return rawget(t, k)
	end
	
	env.rawset = function(t, k, v)
		if type(t) ~= "table" then
			return t
		end
		return rawset(t, k, v)
	end
	
	env.rawequal = rawequal
	
	env.rawlen = function(v)
		if type(v) ~= "table" and type(v) ~= "string" then
			return 0
		end
		return rawlen(v)
	end
	
	env.setmetatable = setmetatable
	env.getmetatable = getmetatable
	env.collectgarbage = function() end
	
	env.newproxy = function(addMeta)
		local proxy = {}
		if addMeta == true then
			setmetatable(proxy, {})
		end
		return proxy
	end
	
	env.print = function(...)
		local args = {...}
		local strs = {}
		for _, v in ipairs(args) do
			table.insert(strs, Utils.serialize(v, sandbox))
		end
		tracer:log("print", "print(" .. table.concat(strs, ",") .. ")")
	end
	
	env.warn = function(...)
		local args = {...}
		local strs = {}
		for _, v in ipairs(args) do
			table.insert(strs, Utils.serialize(v, sandbox))
		end
		tracer:log("warn", "warn(" .. table.concat(strs, ",") .. ")")
	end
	
	env.loadstring = function(src, name)
	-- Block if source is a traced value (could be reading files)
	if sandbox:isTraced(src) then
		local srcName = sandbox:getName(src)
		
		-- Check if it looks like file content
		if string.find(srcName, "readFile") or 
		   string.find(srcName, "readfile") or
		   string.find(srcName, "blocked_") then
			sandbox:log("call", "-- blocked loadstring from file read")
			return function()
				return sandbox:createMock("blocked_loadstring_result", "any")
			end
		end
		
		return function()
			local varName = tracer:nextVar()
			sandbox:log("call", "local " .. varName .. "=loadstring(" .. srcName .. ")()")
			return sandbox:createMock(varName, "any")
		end
	end
	
	-- If actual string source, sandbox it
	if type(src) == "string" then
		-- Block if source contains dangerous patterns
		if string.find(src, "@lune") or 
		   string.find(src, "require%(") or
		   string.find(src, "getfenv") then
			sandbox:log("call", "-- blocked suspicious loadstring")
			return function()
				return sandbox:createMock("blocked_result", "any")
			end
		end
		
		local fn, err = loadstring(src, name)
		if fn then
			setfenv(fn, env)
		end
		return fn, err
	end
	
	return nil, "invalid source"
end

env.load = env.loadstring

	env.setfenv = setfenv
	
	env.getfenv = function(f)
	-- Always return sandbox env, never real environment
	if f == nil or f == 0 or f == 1 then
		return env
	end
	if type(f) == "function" then
		return env
	end
	if type(f) == "number" then
		return env
	end
	return env
end
	
	env.math = {}
	for k, v in pairs(math) do
		env.math[k] = v
	end
	env.math.clamp = math.clamp or function(x, min, max)
		return math.max(min, math.min(max, x))
	end
	env.math.sign = math.sign or function(x)
		if x > 0 then return 1 elseif x < 0 then return -1 else return 0 end
	end
	env.math.round = math.round or function(x)
		return math.floor(x + 0.5)
	end
	env.math.noise = math.noise or function()
		return 0
	end

	local realMath = {}
for k, v in pairs(math) do
	realMath[k] = v
end

env.math.floor = function(x)
	if sandbox:isTraced(x) then
		return sandbox:createMock("math.floor(" .. sandbox:getName(x) .. ")", "number")
	end
	return realMath.floor(x)
end

env.math.ceil = function(x)
	if sandbox:isTraced(x) then
		return sandbox:createMock("math.ceil(" .. sandbox:getName(x) .. ")", "number")
	end
	return realMath.ceil(x)
end

env.math.abs = function(x)
	if sandbox:isTraced(x) then
		return sandbox:createMock("math.abs(" .. sandbox:getName(x) .. ")", "number")
	end
	return realMath.abs(x)
end

env.math.sqrt = function(x)
	if sandbox:isTraced(x) then
		return sandbox:createMock("math.sqrt(" .. sandbox:getName(x) .. ")", "number")
	end
	return realMath.sqrt(x)
end

env.math.sin = function(x)
	if sandbox:isTraced(x) then
		return sandbox:createMock("math.sin(" .. sandbox:getName(x) .. ")", "number")
	end
	return realMath.sin(x)
end

env.math.cos = function(x)
	if sandbox:isTraced(x) then
		return sandbox:createMock("math.cos(" .. sandbox:getName(x) .. ")", "number")
	end
	return realMath.cos(x)
end

env.math.tan = function(x)
	if sandbox:isTraced(x) then
		return sandbox:createMock("math.tan(" .. sandbox:getName(x) .. ")", "number")
	end
	return realMath.tan(x)
end

env.math.rad = function(x)
	if sandbox:isTraced(x) then
		return sandbox:createMock("math.rad(" .. sandbox:getName(x) .. ")", "number")
	end
	return realMath.rad(x)
end

env.math.deg = function(x)
	if sandbox:isTraced(x) then
		return sandbox:createMock("math.deg(" .. sandbox:getName(x) .. ")", "number")
	end
	return realMath.deg(x)
end

env.math.min = function(...)
	local args = {...}
	local hasTraced = false
	local argStrs = {}
	
	for _, arg in ipairs(args) do
		if sandbox:isTraced(arg) then
			hasTraced = true
			table.insert(argStrs, sandbox:getName(arg))
		else
			table.insert(argStrs, tostring(arg))
		end
	end
	
	if hasTraced then
		return sandbox:createMock("math.min(" .. table.concat(argStrs, ",") .. ")", "number")
	end
	return realMath.min(...)
end

env.math.max = function(...)
	local args = {...}
	local hasTraced = false
	local argStrs = {}
	
	for _, arg in ipairs(args) do
		if sandbox:isTraced(arg) then
			hasTraced = true
			table.insert(argStrs, sandbox:getName(arg))
		else
			table.insert(argStrs, tostring(arg))
		end
	end
	
	if hasTraced then
		return sandbox:createMock("math.max(" .. table.concat(argStrs, ",") .. ")", "number")
	end
	return realMath.max(...)
end

env.math.clamp = function(x, min, max)
	local hasTraced = sandbox:isTraced(x) or sandbox:isTraced(min) or sandbox:isTraced(max)
	
	if hasTraced then
		local xStr = sandbox:isTraced(x) and sandbox:getName(x) or tostring(x)
		local minStr = sandbox:isTraced(min) and sandbox:getName(min) or tostring(min)
		local maxStr = sandbox:isTraced(max) and sandbox:getName(max) or tostring(max)
		return sandbox:createMock("math.clamp(" .. xStr .. "," .. minStr .. "," .. maxStr .. ")", "number")
	end
	
	return realMath.max(min, realMath.min(max, x))
end

env.math.random = function(m, n)
	if m and n then
		return realMath.random(m, n)
	elseif m then
		return realMath.random(m)
	end
	return realMath.random()
end

env.math.randomseed = function(seed)
	if sandbox:isTraced(seed) then
		return
	end
	return realMath.randomseed(seed)
end
	
	env.string = {}
	for k, v in pairs(string) do
		env.string[k] = v
	end
	env.string.format = function(fmt, ...)
	local args = {...}
	local realArgs = {}
	
	for i, arg in ipairs(args) do
		if sandbox:isTraced(arg) then
			local name = sandbox:getName(arg)
			local mockType = sandbox:getType(arg)
			
			if mockType == "number" or string.find(name, "%.X$") or string.find(name, "%.Y$") or string.find(name, "%.Z$") then
				table.insert(realArgs, 0)
			elseif mockType == "string" then
				table.insert(realArgs, name)
			else
				table.insert(realArgs, tostring(name))
			end
		else
			table.insert(realArgs, arg)
		end
	end
	
	local ok, result = pcall(string.format, fmt, table.unpack(realArgs))
	if ok then
		return result
	else
		local argStrs = {}
		for _, arg in ipairs(args) do
			if sandbox:isTraced(arg) then
				table.insert(argStrs, sandbox:getName(arg))
			else
				table.insert(argStrs, tostring(arg))
			end
		end
		return sandbox:createMock("string.format(" .. Utils.serialize(fmt, sandbox) .. "," .. table.concat(argStrs, ",") .. ")", "string")
	end
end
	env.string.split = string.split
	
	env.table = {}
	for k, v in pairs(table) do
		env.table[k] = v
	end
	env.table.unpack = table.unpack
	env.table.pack = table.pack
	env.table.create = table.create
	env.table.find = table.find
	env.table.clear = table.clear
	env.table.clone = table.clone
	env.table.freeze = table.freeze
	env.table.isfrozen = table.isfrozen
	env.table.move = table.move
	
	env.coroutine = {}
	for k, v in pairs(coroutine) do
		env.coroutine[k] = v
	end
	
	env.os = {
	time = os.time,
	date = os.date,
	clock = os.clock,
	difftime = os.difftime,
	-- Block these
	execute = function()
		return nil
	end,
	exit = function() end,
	getenv = function()
		return nil
	end,
	remove = function()
		return nil
	end,
	rename = function()
		return nil
	end,
	tmpname = function()
		return "/tmp/blocked"
	end,
	setlocale = function()
		return nil
	end
}
	
	env.bit32 = bit32
	env.utf8 = utf8
	env.buffer = buffer
	
	env.debug = {
		traceback = debug.traceback,
		traceback = function(msg, level)
		-- Return sanitized traceback without file paths
		return "stack traceback:\n\t[C]: in function 'error'\n\t[sandbox]: in main chunk"
	end,
	info = function(...)
		-- Return fake info without real paths
		return "sandbox", 0, "main"
	end,
		info = debug.info,
		getinfo = function()
		return {
			source = "=[sandbox]",
			short_src = "[sandbox]",
			linedefined = -1,
			lastlinedefined = -1,
			what = "Lua",
			currentline = -1,
			nups = 0,
			nparams = 0,
			isvararg = true,
			name = nil,
			namewhat = "",
			func = nil
		}
	end,
		getmetatable = function(obj)
			if type(obj) == "table" or type(obj) == "userdata" then
				return getmetatable(obj)
			end
			return nil
		end,
		setmetatable = function(obj, mt)
			if type(obj) == "table" then
				return setmetatable(obj, mt)
			end
			return obj
		end,
		getinfo = function()
			return {
				source = "=[C]",
				short_src = "[C]",
				linedefined = -1,
				lastlinedefined = -1,
				what = "C",
				currentline = -1,
				nups = 0,
				nparams = 0,
				isvararg = true,
				name = nil,
				namewhat = "",
				func = nil
			}
		end,
		getlocal = function() return nil end,
		setlocal = function() return nil end,
		getupvalue = function() return nil end,
		setupvalue = function() return nil end,
		sethook = function() end,
		gethook = function() return nil, nil, 0 end,
		getregistry = function() return {} end
	}
	
	env._VERSION = "Luau"
	env._G = env
end

function Sandbox:addRobloxGlobals()
	local env = self.env
	local sandbox = self
	local tracer = self.tracer
	
	local gameMeta = {
		__index = function(t, key)
			if type(key) == "string" and string.sub(key, 1, 1) == "_" then
				return rawget(t, key)
			end
			
			if key == "GetService" then
				return function(_, serviceName)
					if sandbox.services[serviceName] then
						return sandbox.services[serviceName]
					end
					
					local varName = tracer:nextVar()
					sandbox:log("call", "local " .. varName .. "=game:GetService(\"" .. serviceName .. "\")")
					
					local service = sandbox:createMock(varName, serviceName)
					sandbox.services[serviceName] = service
					return service
				end
			end
			
			if key == "FindService" then
				return function(_, serviceName)
					if sandbox.services[serviceName] then
						return sandbox.services[serviceName]
					end
					return nil
				end
			end
			
			if key == "PlaceId" then
	return sandbox.config.placeId or 0
end

if key == "GameId" then
	return sandbox.config.gameId or 0
end

if key == "JobId" then
	return sandbox.config.jobId or ""
end

if key == "CreatorId" then
	return sandbox.config.creatorId or 0
end

if key == "CreatorType" then
	return sandbox:createMock("Enum.CreatorType.User", "EnumItem")
end

if key == "PrivateServerId" then
	return ""
end

if key == "PrivateServerOwnerId" then
	return 0
end
			
			if sandbox.services[key] then
				return sandbox.services[key]
			end
			
			local varName = tracer:nextVar()
			sandbox:log("call", "local " .. varName .. "=game." .. key)
			return sandbox:createMock(varName, "Instance")
		end,
		
		__newindex = function(t, key, value)
			sandbox:log("newindex", "game." .. tostring(key) .. "=" .. Utils.serialize(value, sandbox))
		end,
		
		__tostring = function()
			return "game"
		end
	}
	
	env.game = setmetatable({
		_name = "game",
		_type = "DataModel",
		_cache = {}
	}, gameMeta)
	
	env.Game = env.game
	
	env.workspace = sandbox:createMock("workspace", "Workspace")
	env.Workspace = env.workspace
	sandbox.services["Workspace"] = env.workspace
	
	env.script = sandbox:createMock("script", "LocalScript")
	
	-- Create LocalPlayer with spoofed values
local localPlayerMeta = {
	__index = function(t, key)
		if type(key) == "string" and string.sub(key, 1, 1) == "_" then
			return rawget(t, key)
		end
		
		local cache = rawget(t, "_cache")
		if cache and cache[key] then
			return cache[key]
		end
		
		-- Spoofed values
		if key == "UserId" then
			return sandbox.config.userId or 1234567890
		end
		if key == "Name" then
			return sandbox.config.username or "Player"
		end
		if key == "DisplayName" then
			return sandbox.config.displayName or "Player"
		end
		if key == "AccountAge" then
			return 365
		end
		if key == "MembershipType" then
			return sandbox:createMock("Enum.MembershipType.None", "EnumItem")
		end
		if key == "Character" then
			local char = sandbox:createMock("Players.LocalPlayer.Character", "Model")
			if cache then cache[key] = char end
			return char
		end
		if key == "CharacterAdded" then
			return sandbox:createMock("Players.LocalPlayer.CharacterAdded", "RBXScriptSignal")
		end
		if key == "CharacterRemoving" then
			return sandbox:createMock("Players.LocalPlayer.CharacterRemoving", "RBXScriptSignal")
		end
		if key == "PlayerGui" then
			local gui = sandbox:createMock("Players.LocalPlayer.PlayerGui", "PlayerGui")
			if cache then cache[key] = gui end
			return gui
		end
		if key == "Backpack" then
			local bp = sandbox:createMock("Players.LocalPlayer.Backpack", "Backpack")
			if cache then cache[key] = bp end
			return bp
		end
		if key == "PlayerScripts" then
			local ps = sandbox:createMock("Players.LocalPlayer.PlayerScripts", "PlayerScripts")
			if cache then cache[key] = ps end
			return ps
		end
		if key == "Team" then
			return nil
		end
		if key == "TeamColor" then
			return sandbox:createMock("BrickColor.new(\"White\")", "BrickColor")
		end
		if key == "Neutral" then
			return true
		end
		
		-- Methods
		if key == "GetMouse" then
			return function()
				return sandbox:createMock("mouse", "Mouse")
			end
		end
		if key == "Kick" then
			return function()
				sandbox:log("call", "Players.LocalPlayer:Kick()")
			end
		end
		if key == "ClearCharacterAppearance" then
			return function()
				sandbox:log("call", "Players.LocalPlayer:ClearCharacterAppearance()")
			end
		end
		if key == "LoadCharacter" then
			return function()
				sandbox:log("call", "Players.LocalPlayer:LoadCharacter()")
			end
		end
		if key == "HasAppearanceLoaded" then
			return function()
				return true
			end
		end
		if key == "GetFriendsOnline" then
			return function()
				local varName = tracer:nextVar()
				sandbox:log("call", "local " .. varName .. "=Players.LocalPlayer:GetFriendsOnline()")
				return {}
			end
		end
		if key == "IsFriendsWith" then
			return function(_, userId)
				local varName = tracer:nextVar()
				sandbox:log("call", "local " .. varName .. "=Players.LocalPlayer:IsFriendsWith(" .. tostring(userId) .. ")")
				return false
			end
		end
		if key == "GetRankInGroup" then
			return function(_, groupId)
				local varName = tracer:nextVar()
				sandbox:log("call", "local " .. varName .. "=Players.LocalPlayer:GetRankInGroup(" .. tostring(groupId) .. ")")
				return 0
			end
		end
		if key == "IsInGroup" then
			return function(_, groupId)
				local varName = tracer:nextVar()
				sandbox:log("call", "local " .. varName .. "=Players.LocalPlayer:IsInGroup(" .. tostring(groupId) .. ")")
				return false
			end
		end
		if key == "GetRoleInGroup" then
			return function(_, groupId)
				local varName = tracer:nextVar()
				sandbox:log("call", "local " .. varName .. "=Players.LocalPlayer:GetRoleInGroup(" .. tostring(groupId) .. ")")
				return "Guest"
			end
		end
		
		-- Default property access
		local name = rawget(t, "_name")
		local newName
		if type(key) == "string" and string.match(key, "^[%a_][%w_]*$") then
			newName = name .. "." .. key
		else
			newName = name .. "[" .. Utils.serialize(key, sandbox) .. "]"
		end
		
		local mock = sandbox:createMock(newName, sandbox:inferType(key, "Player"))
		if cache then cache[key] = mock end
		return mock
	end,
	
	__newindex = function(t, key, value)
		sandbox:log("newindex", "Players.LocalPlayer." .. tostring(key) .. "=" .. Utils.serialize(value, sandbox))
	end,
	
	__tostring = function()
		return sandbox.config.username or "Player"
	end
}

sandbox.localPlayer = setmetatable({
	_name = "Players.LocalPlayer",
	_type = "Player",
	_cache = {}
}, localPlayerMeta)

	env.wait = function(t)
		t = t or 0
		sandbox:log("call", "wait(" .. tostring(t) .. ")")
		return t, t
	end
	
	env.delay = function(t, f)
		local cb = tracer:registerCallback(f, "delay")
		sandbox:log("call", "delay(" .. tostring(t) .. ",function()--[[CB:" .. cb.id .. "]]end)")
	end
	
	env.spawn = function(f)
		local cb = tracer:registerCallback(f, "spawn")
		sandbox:log("call", "spawn(function()--[[CB:" .. cb.id .. "]]end)")
	end
	
	env.task = {
		wait = function(t)
			t = t or 0
			sandbox:log("call", "task.wait(" .. tostring(t) .. ")")
			return t
		end,
		spawn = function(f, ...)
			local cb = tracer:registerCallback(f, "task.spawn")
			local args = {...}
			local argStr = ""
			if #args > 0 then
				local argStrs = {}
				for _, arg in ipairs(args) do
					table.insert(argStrs, Utils.serialize(arg, sandbox))
				end
				argStr = "," .. table.concat(argStrs, ",")
			end
			sandbox:log("call", "task.spawn(function()--[[CB:" .. cb.id .. "]]end" .. argStr .. ")")
		end,
		defer = function(f, ...)
			local cb = tracer:registerCallback(f, "task.defer")
			sandbox:log("call", "task.defer(function()--[[CB:" .. cb.id .. "]]end)")
		end,
		delay = function(t, f, ...)
			local cb = tracer:registerCallback(f, "task.delay")
			sandbox:log("call", "task.delay(" .. tostring(t) .. ",function()--[[CB:" .. cb.id .. "]]end)")
		end,
		cancel = function() end,
		synchronize = function() end,
		desynchronize = function() end
	}
	
	env.tick = function()
		return os.clock()
	end
	
	env.time = function()
		return os.clock()
	end
	
	env.elapsedTime = function()
		return os.clock()
	end
	
	env.elapsed_time = env.elapsedTime
	
	env.require = function(module)
	-- Block Lune-specific requires
	if type(module) == "string" then
		if string.sub(module, 1, 1) == "@" then
			local blockedModules = {
				["@lune/fs"] = true,
				["@lune/process"] = true,
				["@lune/net"] = true,
				["@lune/stdio"] = true,
				["@lune/task"] = true,
				["@lune/serde"] = true,
				["@lune/roblox"] = true,
				["@lune/regex"] = true,
				["@lune/datetime"] = true,
				["@lune/luau"] = true,
			}
			
			if blockedModules[module] then
				sandbox:log("call", "-- blocked require: " .. module)
				return sandbox:createMock("blocked_" .. string.gsub(module, "@lune/", ""), "table")
			end
			
			-- Block any @lune/* pattern
			if string.sub(module, 1, 6) == "@lune/" then
				sandbox:log("call", "-- blocked require: " .. module)
				return sandbox:createMock("blocked_lune_module", "table")
			end
			
			-- Block other potentially dangerous patterns
			sandbox:log("call", "-- blocked require: " .. module)
			return sandbox:createMock("blocked_module", "table")
		end
		
		-- Block relative path requires that might access source
		if string.find(module, "%.%.") or string.find(module, "^%.%/") then
			sandbox:log("call", "-- blocked require: " .. module)
			return sandbox:createMock("blocked_path", "table")
		end
		
		-- Block absolute paths
		if string.find(module, "^/") or string.find(module, "^[A-Za-z]:") then
			sandbox:log("call", "-- blocked require: " .. module)
			return sandbox:createMock("blocked_path", "table")
		end
		
		-- Block src/ requires
		if string.find(module, "^src/") or string.find(module, "^%./src/") then
			sandbox:log("call", "-- blocked require: " .. module)
			return sandbox:createMock("blocked_src", "table")
		end
	end
	
	-- Normal Roblox-style require (ModuleScript instances)
	local varName = tracer:nextVar()
	sandbox:log("call", "local " .. varName .. "=require(" .. Utils.serialize(module, sandbox) .. ")")
	return sandbox:createMock(varName, "ModuleResult")
end
	
	env.shared = setmetatable({}, {
		__index = function(t, key)
			return rawget(t, key)
		end,
		__newindex = function(t, key, value)
			rawset(t, key, value)
			sandbox:log("assign", "shared." .. key .. "=" .. Utils.serialize(value, sandbox))
		end
	})
end

function Sandbox:addRobloxServices()
	local serviceList = {
		"Players",
		"ReplicatedStorage",
		"ReplicatedFirst",
		"ServerStorage",
		"ServerScriptService",
		"StarterGui",
		"StarterPack",
		"StarterPlayer",
		"Lighting",
		"SoundService",
		"Chat",
		"LocalizationService",
		"TestService",
		"RunService",
		"UserInputService",
		"ContextActionService",
		"GuiService",
		"HapticService",
		"VRService",
		"TweenService",
		"ContentProvider",
		"Debris",
		"PhysicsService",
		"CollectionService",
		"BadgeService",
		"GamePassService",
		"InsertService",
		"MarketplaceService",
		"TeleportService",
		"SocialService",
		"PolicyService",
		"HttpService",
		"DataStoreService",
		"MemoryStoreService",
		"MessagingService",
		"TextService",
		"TextChatService",
		"PathfindingService",
		"ProximityPromptService",
		"CoreGui",
		"Stats",
		"VirtualInputManager",
		"VirtualUser",
		"KeyframeSequenceProvider",
		"AvatarEditorService",
		"GroupService",
		"LogService",
		"AnalyticsService",
		"LocalizationService",
		"NotificationService",
		"PermissionsService",
		"Players",
		"PointsService",
		"ScriptContext",
		"Selection",
		"StudioService",
		"TimerService"
	}
	
	for _, name in ipairs(serviceList) do
		if not self.services[name] then
			self.services[name] = self:createMock(name, name)
		end
	end

	local sandbox = self
	local tracer = self.tracer
	local playersService = self.services["Players"]
	
	if playersService then
		local playersMeta = {
			__index = function(t, key)
				if type(key) == "string" and string.sub(key, 1, 1) == "_" then
					return rawget(t, key)
				end
				
				if key == "LocalPlayer" then
					return sandbox.localPlayer
				end
				
				if key == "PlayerAdded" then
					return sandbox:createMock("Players.PlayerAdded", "RBXScriptSignal")
				end
				
				if key == "PlayerRemoving" then
					return sandbox:createMock("Players.PlayerRemoving", "RBXScriptSignal")
				end
				
				if key == "GetPlayers" then
					return function()
						local varName = tracer:nextVar()
						sandbox:log("call", "local " .. varName .. "=Players:GetPlayers()")
						return {sandbox.localPlayer}
					end
				end
				
				if key == "GetPlayerFromCharacter" then
					return function(_, char)
						local varName = tracer:nextVar()
						sandbox:log("call", "local " .. varName .. "=Players:GetPlayerFromCharacter(" .. Utils.serialize(char, sandbox) .. ")")
						return sandbox.localPlayer
					end
				end
				
				if key == "GetPlayerByUserId" then
					return function(_, userId)
						local varName = tracer:nextVar()
						sandbox:log("call", "local " .. varName .. "=Players:GetPlayerByUserId(" .. tostring(userId) .. ")")
						if userId == (sandbox.config.userId or 1234567890) then
							return sandbox.localPlayer
						end
						return nil
					end
				end
				
				if key == "GetUserIdFromNameAsync" then
					return function(_, name)
						local varName = tracer:nextVar()
						sandbox:log("call", "local " .. varName .. "=Players:GetUserIdFromNameAsync(" .. Utils.serialize(name, sandbox) .. ")")
						if name == (sandbox.config.username or "Player") then
							return sandbox.config.userId or 1234567890
						end
						return 0
					end
				end
				
				if key == "GetNameFromUserIdAsync" then
					return function(_, userId)
						local varName = tracer:nextVar()
						sandbox:log("call", "local " .. varName .. "=Players:GetNameFromUserIdAsync(" .. tostring(userId) .. ")")
						if userId == (sandbox.config.userId or 1234567890) then
							return sandbox.config.username or "Player"
						end
						return "Unknown"
					end
				end
				
				if key == "MaxPlayers" then
					return 50
				end
				
				if key == "NumPlayers" then
					return 1
				end
				
				if key == "PreferredPlayers" then
					return 20
				end
				
				local name = rawget(t, "_name")
				local newName = name .. "." .. tostring(key)
				return sandbox:createMock(newName, "any")
			end,
			
			__tostring = function()
				return "Players"
			end
		}
		
		self.services["Players"] = setmetatable({
			_name = "Players",
			_type = "Players",
			_cache = {}
		}, playersMeta)
	end
end

function Sandbox:addExecutorFunctions()
	local env = self.env
	local sandbox = self
	local tracer = self.tracer
	
	local genvStorage = {}
	
	env.getgenv = function()
		return setmetatable({}, {
			__index = function(_, key)
				return genvStorage[key] or env[key]
			end,
			__newindex = function(_, key, value)
				genvStorage[key] = value
				sandbox:log("assign", "getgenv()." .. key .. "=" .. Utils.serialize(value, sandbox))
			end
		})
	end
	
	env.getrenv = function()
		return env
	end
	
	env.getrawmetatable = function(obj)
		return getmetatable(obj) or {}
	end
	
	env.setrawmetatable = function(obj, mt)
		pcall(setmetatable, obj, mt)
		return obj
	end
	
	env.isreadonly = function()
		return false
	end
	
	env.setreadonly = function() end
	
	env.getnamecallmethod = function()
		return ""
	end
	
	env.setnamecallmethod = function() end
	
	env.hookfunction = function(target, hook)
	local targetName = "unknown"
	if sandbox:isTraced(target) then
		targetName = sandbox:getName(target)
	end
	
	if type(hook) == "function" then
		tracer:registerHook(targetName, hook, "function")
	end
	
	local varName = tracer:nextVar()
	sandbox:log("call", "local " .. varName .. "=hookfunction(" .. Utils.serialize(target, sandbox) .. "," .. Utils.serialize(hook, sandbox) .. ")")
	return target
end
	
	env.replaceclosure = env.hookfunction
	
	env.hookmetamethod = function(obj, method, hook)
	local objName = "unknown"
	if sandbox:isTraced(obj) then
		objName = sandbox:getName(obj)
	end
	
	local hookName = objName .. ".__" .. tostring(method)
	
	if type(hook) == "function" then
		tracer:registerHook(hookName, hook, "metamethod")
		local cb = tracer:registerCallback(hook, "hookmetamethod_" .. method)
		local varName = tracer:nextVar()
		sandbox:log("call", "local " .. varName .. "=hookmetamethod(" .. Utils.serialize(obj, sandbox) .. ",\"" .. method .. "\",function()--[[CB:" .. cb.id .. "]]end)")
		return function(...) 
			return sandbox:createMock("original_" .. method .. "_result", "any")
		end
	end
	
	local varName = tracer:nextVar()
	sandbox:log("call", "local " .. varName .. "=hookmetamethod(" .. Utils.serialize(obj, sandbox) .. ",\"" .. method .. "\"," .. Utils.serialize(hook, sandbox) .. ")")
	return function() end
end
	
	env.restorefunction = function() end
	
	env.isexecutorclosure = function()
		return false
	end
	
	env.checkclosure = env.isexecutorclosure
	env.isourclosure = env.isexecutorclosure
	
	env.islclosure = function(f)
		return type(f) == "function"
	end
	
	env.iscclosure = function()
		return false
	end
	
	env.clonefunction = function(f)
		return f
	end
	
	env.newcclosure = function(f)
		return f
	end

	env.assert = function(cond, msg)
	if sandbox:isTraced(cond) then
		local name = sandbox:getName(cond)
		tracer:markConditionalPoint(name, "assert")
		tracer:log("call", "assert(" .. name .. ")")
	end
	return cond
end
	
	env.checkcaller = function()
		return true
	end
	
	env.getfunctionhash = function()
		return string.rep("0", 64)
	end
	
	env.getgc = function()
		return {}
	end
	
	env.filtergc = function(_, _, returnOne)
		if returnOne then
			return nil
		end
		return {}
	end
	
	env.getreg = function()
		return {}
	end
	
	env.cloneref = function(obj)
		local varName = tracer:nextVar()
		sandbox:log("call", "local " .. varName .. "=cloneref(" .. Utils.serialize(obj, sandbox) .. ")")
		return sandbox:createMock(varName, "Instance")
	end
	
	env.compareinstances = function()
		return false
	end
	
	env.getinstances = function()
		return {}
	end
	
	env.getnilinstances = function()
		return {}
	end
	
	env.gethui = function()
		local varName = tracer:nextVar()
		sandbox:log("call", "local " .. varName .. "=gethui()")
		return sandbox:createMock(varName, "ScreenGui")
	end
	
	env.getconnections = function(signal)
		sandbox:log("call", "getconnections(" .. Utils.serialize(signal, sandbox) .. ")")
		return {}
	end
	
	env.firesignal = function(signal, ...)
		local args = {signal, ...}
		local argStrs = {}
		for _, arg in ipairs(args) do
			table.insert(argStrs, Utils.serialize(arg, sandbox))
		end
		sandbox:log("call", "firesignal(" .. table.concat(argStrs, ",") .. ")")
	end
	
	env.replicatesignal = function(signal, ...)
		local args = {signal, ...}
		local argStrs = {}
		for _, arg in ipairs(args) do
			table.insert(argStrs, Utils.serialize(arg, sandbox))
		end
		sandbox:log("call", "replicatesignal(" .. table.concat(argStrs, ",") .. ")")
	end
	
	env.fireclickdetector = function(detector, distance)
		sandbox:log("call", "fireclickdetector(" .. Utils.serialize(detector, sandbox) .. "," .. tostring(distance or 0) .. ")")
	end
	
	env.fireproximityprompt = function(prompt)
		sandbox:log("call", "fireproximityprompt(" .. Utils.serialize(prompt, sandbox) .. ")")
	end
	
	env.firetouchinterest = function(part1, part2, toggle)
		sandbox:log("call", "firetouchinterest(" .. Utils.serialize(part1, sandbox) .. "," .. Utils.serialize(part2, sandbox) .. "," .. tostring(toggle) .. ")")
	end
	
	env.getscripts = function()
		return {}
	end
	
	env.getrunningscripts = function()
		return {}
	end
	
	env.getloadedmodules = function()
		return {}
	end
	
	env.getcallingscript = function()
		return sandbox:createMock("script", "LocalScript")
	end
	
	env.getscriptbytecode = function()
		return ""
	end
	
	env.dumpstring = env.getscriptbytecode
	
	env.getscripthash = function()
		return ""
	end
	
	env.getscriptclosure = function()
		return function() end
	end
	
	env.getscriptfunction = env.getscriptclosure
	
	env.getsenv = function()
		return {}
	end
	
	env.decompile = function()
		return ""
	end
	
	env.gethiddenproperty = function()
		return nil, false
	end
	
	env.sethiddenproperty = function()
		return false
	end
	
	env.isscriptable = function()
		return true
	end
	
	env.setscriptable = function()
		return true
	end
	
	env.getthreadidentity = function()
		return 2
	end
	
	env.getidentity = env.getthreadidentity
	env.getthreadcontext = env.getthreadidentity
	
	env.setthreadidentity = function() end
	env.setidentity = env.setthreadidentity
	env.setthreadcontext = env.setthreadidentity
	
	env.getcallbackvalue = function()
		return nil
	end
	
	local b64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
	
	env.base64encode = function(data)
		if type(data) ~= "string" then
			return ""
		end
		local result = {}
		local padding = (3 - #data % 3) % 3
		data = data .. string.rep("\0", padding)
		for i = 1, #data, 3 do
			local b1, b2, b3 = string.byte(data, i, i + 2)
			local n = b1 * 65536 + b2 * 256 + b3
			table.insert(result, string.sub(b64chars, math.floor(n / 262144) % 64 + 1, math.floor(n / 262144) % 64 + 1))
			table.insert(result, string.sub(b64chars, math.floor(n / 4096) % 64 + 1, math.floor(n / 4096) % 64 + 1))
			table.insert(result, string.sub(b64chars, math.floor(n / 64) % 64 + 1, math.floor(n / 64) % 64 + 1))
			table.insert(result, string.sub(b64chars, n % 64 + 1, n % 64 + 1))
		end
		for i = 1, padding do
			result[#result - i + 1] = "="
		end
		return table.concat(result)
	end
	
	env.base64_encode = env.base64encode
	
	env.base64decode = function(data)
		if type(data) ~= "string" then
			return ""
		end
		data = string.gsub(data, "[^" .. b64chars .. "=]", "")
		local result = {}
		for i = 1, #data, 4 do
			local c1, c2, c3, c4 = string.byte(data, i, i + 3)
			if not c1 then break end
			local n1 = (string.find(b64chars, string.char(c1)) or 1) - 1
			local n2 = (string.find(b64chars, string.char(c2 or 65)) or 1) - 1
			local n3 = (c3 == 61 or not c3) and 0 or (string.find(b64chars, string.char(c3)) or 1) - 1
			local n4 = (c4 == 61 or not c4) and 0 or (string.find(b64chars, string.char(c4)) or 1) - 1
			local n = n1 * 262144 + n2 * 4096 + n3 * 64 + n4
			table.insert(result, string.char(math.floor(n / 65536) % 256))
			if c3 and c3 ~= 61 then
				table.insert(result, string.char(math.floor(n / 256) % 256))
			end
			if c4 and c4 ~= 61 then
				table.insert(result, string.char(n % 256))
			end
		end
		return table.concat(result)
	end
	
	env.base64_decode = env.base64decode
	
	env.lz4compress = function(data)
		return data
	end
	
	env.lz4decompress = function(data)
		return data
	end
	
	env.crypt = {
		base64encode = env.base64encode,
		base64decode = env.base64decode,
		base64_encode = env.base64encode,
		base64_decode = env.base64decode,
		encrypt = function(data)
			return data
		end,
		decrypt = function(data)
			return data
		end,
		generatekey = function()
			return string.rep("0", 32)
		end,
		generatebytes = function(len)
			return string.rep("\0", len or 16)
		end,
		hash = function()
			return string.rep("0", 64)
		end
	}
	
	env.readfile = function(path)
	-- Block access to source files
	if type(path) == "string" then
		local blocked = {
			"main", "sandbox", "tracer", "codegen", "null", "types", "utils"
		}
		local lowerPath = string.lower(path)
		
		for _, name in ipairs(blocked) do
			if string.find(lowerPath, name) then
				sandbox:log("call", "-- blocked readfile: " .. path)
				return ""
			end
		end
		
		if string.find(lowerPath, "%.luau?$") or string.find(lowerPath, "^src/") then
			sandbox:log("call", "-- blocked readfile: " .. path)
			return ""
		end
	end
	
	local varName = tracer:nextVar()
	sandbox:log("call", "local " .. varName .. "=readfile(" .. Utils.serialize(path, sandbox) .. ")")
	return ""
end

env.writefile = function(path, data)
	sandbox:log("call", "writefile(" .. Utils.serialize(path, sandbox) .. "," .. Utils.serialize(data, sandbox) .. ")")
	-- Don't actually write anything
end

env.appendfile = function(path, data)
	sandbox:log("call", "appendfile(" .. Utils.serialize(path, sandbox) .. "," .. Utils.serialize(data, sandbox) .. ")")
end

env.loadfile = function(path)
	sandbox:log("call", "-- blocked loadfile: " .. Utils.serialize(path, sandbox))
	return function() end, nil
end

env.listfiles = function(path)
	sandbox:log("call", "listfiles(" .. Utils.serialize(path, sandbox) .. ")")
	return {} -- Return empty, don't expose real filesystem
end

env.isfile = function(path)
	sandbox:log("call", "isfile(" .. Utils.serialize(path, sandbox) .. ")")
	return false -- Always false, don't reveal real files
end

env.isfolder = function(path)
	sandbox:log("call", "isfolder(" .. Utils.serialize(path, sandbox) .. ")")
	return false
end

env.dofile = function(path)
	sandbox:log("call", "-- blocked dofile: " .. Utils.serialize(path, sandbox))
	return nil
end
	
	env.makefolder = function(path)
		sandbox:log("call", "makefolder(" .. Utils.serialize(path, sandbox) .. ")")
	end
	
	env.delfolder = function(path)
		sandbox:log("call", "delfolder(" .. Utils.serialize(path, sandbox) .. ")")
	end
	
	env.delfile = function(path)
		sandbox:log("call", "delfile(" .. Utils.serialize(path, sandbox) .. ")")
	end
	
	env.getcustomasset = function(path)
		return "rbxasset://" .. tostring(path)
	end
	
	env.identifyexecutor = function()
		return "Null", "1.0.0"
	end
	
	env.getexecutorname = env.identifyexecutor
	
	env.request = function(options)
		local varName = tracer:nextVar()
		sandbox:log("call", "local " .. varName .. "=request(" .. Utils.serialize(options, sandbox) .. ")")
		return {
			Success = true,
			StatusCode = 200,
			StatusMessage = "OK",
			Headers = {},
			Body = "{}"
		}
	end
	
	env.http_request = env.request
	env.http = {request = env.request}
	env.syn = {request = env.request}
	
	env.setclipboard = function(text)
		sandbox:log("call", "setclipboard(" .. Utils.serialize(text, sandbox) .. ")")
	end
	
	env.toclipboard = env.setclipboard
	
	env.getclipboard = function()
		return ""
	end
	
	env.rconsolecreate = function()
		sandbox:log("call", "rconsolecreate()")
	end
	
	env.consolecreate = env.rconsolecreate
	
	env.rconsoleprint = function(text)
		sandbox:log("call", "rconsoleprint(" .. Utils.serialize(text, sandbox) .. ")")
	end
	
	env.consoleprint = env.rconsoleprint
	env.rconsoleclear = function() end
	env.consoleclear = env.rconsoleclear
	env.rconsoledestroy = function() end
	env.consoledestroy = env.rconsoledestroy
	
	env.rconsoleinput = function()
		return ""
	end
	
	env.consoleinput = env.rconsoleinput
	
	env.rconsoleinfo = function(text)
		sandbox:log("call", "rconsoleinfo(" .. Utils.serialize(text, sandbox) .. ")")
	end
	
	env.rconsolewarn = function(text)
		sandbox:log("call", "rconsolewarn(" .. Utils.serialize(text, sandbox) .. ")")
	end
	
	env.rconsoleerr = function(text)
		sandbox:log("call", "rconsoleerr(" .. Utils.serialize(text, sandbox) .. ")")
	end
	
	env.rconsolename = function(name)
		sandbox:log("call", "rconsolename(" .. Utils.serialize(name, sandbox) .. ")")
	end
	
	env.Drawing = {
		new = function(drawingType)
			local varName = tracer:nextVar()
			sandbox:log("call", "local " .. varName .. "=Drawing.new(" .. Utils.serialize(drawingType, sandbox) .. ")")
			return sandbox:createMock(varName, "Drawing")
		end,
		Fonts = {
			UI = 0,
			System = 1,
			Plex = 2,
			Monospace = 3
		}
	}
	
	env.isrenderobj = function()
		return false
	end
	
	env.getrenderproperty = function()
		return nil
	end
	
	env.setrenderproperty = function() end
	env.cleardrawcache = function() end
	
	env.WebSocket = {
		connect = function(url)
			local varName = tracer:nextVar()
			sandbox:log("call", "local " .. varName .. "=WebSocket.connect(" .. Utils.serialize(url, sandbox) .. ")")
			return sandbox:createMock(varName, "WebSocket")
		end
	}
	
	env.keypress = function(key)
		sandbox:log("call", "keypress(" .. Utils.serialize(key, sandbox) .. ")")
	end
	
	env.keyrelease = function(key)
		sandbox:log("call", "keyrelease(" .. Utils.serialize(key, sandbox) .. ")")
	end
	
	env.mouse1click = function()
		sandbox:log("call", "mouse1click()")
	end
	
	env.mouse1press = function()
		sandbox:log("call", "mouse1press()")
	end
	
	env.mouse1release = function()
		sandbox:log("call", "mouse1release()")
	end
	
	env.mouse2click = function()
		sandbox:log("call", "mouse2click()")
	end
	
	env.mouse2press = function()
		sandbox:log("call", "mouse2press()")
	end
	
	env.mouse2release = function()
		sandbox:log("call", "mouse2release()")
	end
	
	env.mousescroll = function(amount)
		sandbox:log("call", "mousescroll(" .. tostring(amount) .. ")")
	end
	
	env.mousemoverel = function(x, y)
		sandbox:log("call", "mousemoverel(" .. tostring(x) .. "," .. tostring(y) .. ")")
	end
	
	env.mousemoveabs = function(x, y)
		sandbox:log("call", "mousemoveabs(" .. tostring(x) .. "," .. tostring(y) .. ")")
	end
	
	env.iswindowactive = function()
		return true
	end
	
	env.getmousestate = function()
		return true
	end
	
	env.setfpscap = function(fps)
		sandbox:log("call", "setfpscap(" .. tostring(fps) .. ")")
	end
	
	env.getfpscap = function()
		return 60
	end
	
	env.isrbxactive = function()
		return true
	end
	
	env.isgameactive = env.isrbxactive
	
	env.getping = function()
		return 50
	end
	
	env.getfps = function()
		return 60
	end
	
	env.queue_on_teleport = function(code)
		sandbox:log("call", "queue_on_teleport(" .. Utils.serialize(code, sandbox) .. ")")
	end
	
	env.queueonteleport = env.queue_on_teleport
	
	env.protect_gui = function(gui)
		sandbox:log("call", "protect_gui(" .. Utils.serialize(gui, sandbox) .. ")")
	end
	
	env.protectgui = env.protect_gui
	
	env.unprotect_gui = function(gui)
		sandbox:log("call", "unprotect_gui(" .. Utils.serialize(gui, sandbox) .. ")")
	end
	
	env.unprotectgui = env.unprotect_gui
	
	env.setfflag = function(flag, value)
		sandbox:log("call", "setfflag(\"" .. tostring(flag) .. "\",\"" .. tostring(value) .. "\")")
	end
	
	env.getfflag = function()
		return ""
	end
end

function Sandbox:buildMockArgs(context, iteration)
	iteration = iteration or 1
	local ctx = string.lower(context)
	
	if string.find(ctx, "playeradded") or string.find(ctx, "playerremoving") then
		return {self:createMock("player", "Player")}
	end
	
	if string.find(ctx, "characteradded") or string.find(ctx, "characterremoving") then
		return {self:createMock("character", "Model")}
	end
	
	if string.find(ctx, "inputbegan") or string.find(ctx, "inputended") or string.find(ctx, "inputchanged") then
		local input = self:createMock("input", "InputObject")
		return {input, false}
	end
	
	if string.find(ctx, "renderstepped") then
		return {0.016 * iteration}
	end
	
	if string.find(ctx, "heartbeat") or string.find(ctx, "stepped") then
		return {0.016 * iteration}
	end
	
	if string.find(ctx, "touched") then
		return {self:createMock("hitPart", "BasePart")}
	end
	
	if string.find(ctx, "changed") and not string.find(ctx, "input") then
		return {"Value"}
	end
	
	if string.find(ctx, "childadded") or string.find(ctx, "childremoved") then
		return {self:createMock("child", "Instance")}
	end
	
	if string.find(ctx, "descendantadded") or string.find(ctx, "descendantremoving") then
		return {self:createMock("descendant", "Instance")}
	end
	
	if string.find(ctx, "mousebutton") or string.find(ctx, "click") or string.find(ctx, "activated") then
		return {}
	end
	
	if string.find(ctx, "mousemove") or string.find(ctx, "mouseenter") or string.find(ctx, "mouseleave") then
		return {100 * iteration, 100 * iteration}
	end
	
	if string.find(ctx, "textchanged") then
		return {}
	end
	
	if string.find(ctx, "focuslost") then
		return {false, self:createMock("inputObject", "InputObject")}
	end
	
	if string.find(ctx, "onserverevent") or string.find(ctx, "onclientevent") then
		return {self:createMock("arg1", "any"), self:createMock("arg2", "any")}
	end
	
	if string.find(ctx, "oninvoke") then
		return {self:createMock("arg1", "any")}
	end
	
	if string.find(ctx, "getpropertychangedsignal") then
		return {}
	end
	
	if string.find(ctx, "attributechanged") then
		return {"AttributeName"}
	end
	
	if string.find(ctx, "callback") or string.find(ctx, "inline") then
		return {self:createMock("arg1", "any")}
	end
	
	if string.find(ctx, "dropdown") or string.find(ctx, "toggle") or string.find(ctx, "slider") then
		return {self:createMock("value", "any")}
	end
	
	if string.find(ctx, "button") then
		return {}
	end
	
	if string.find(ctx, "textbox") then
		return {"text_input"}
	end
	
	if ctx == "" or ctx == "unknown" then
		return {self:createMock("arg1", "any")}
	end
	
	return {}
end

function Sandbox:execute(source, chunkName)
	local chunk, loadErr = loadstring(source, chunkName)
	if not chunk then
		return false, "Load error: " .. tostring(loadErr)
	end
	
	setfenv(chunk, self.env)
	
	local co = coroutine.create(chunk)
	local ok, err = coroutine.resume(co)
	
	if not ok then
		return false, tostring(err)
	end
	
	return true, nil
end

return Sandbox


-- C:\Users\vendetta\Documents\moon\src\tracer.luau

local Tracer = {}
Tracer.__index = Tracer

function Tracer.new(config)
	local self = setmetatable({}, Tracer)
	self.config = config or {}
	self.trace = {}
	self.callbacks = {}
	self.varCounter = 0
	self.callbackCounter = 0
	self.currentCallbackId = nil
	self.maxTraceSize = config.maxTraceSize or 500000
	self.traceLimitHit = false
	self.loopCallbacks = {}
	self.hooks = {}
	self.branchStack = {}
	self.currentBranch = nil
	self.loopPatterns = {}
	self.conditionalPoints = {}
	self.nilCheckVars = {}
	self.knownValues = {}
	return self
end

function Tracer:log(op, data)
	if self.traceLimitHit then
		return
	end
	
	if #self.trace >= self.maxTraceSize then
		self.traceLimitHit = true
		return
	end
	
	local entry = {
		op = op,
		data = data,
		callbackId = self.currentCallbackId
	}
	
	table.insert(self.trace, entry)
end

function Tracer:nextVar()
	self.varCounter = self.varCounter + 1
	return "v" .. self.varCounter
end

function Tracer:registerCallback(fn, context)
	for _, cb in ipairs(self.callbacks) do
		if cb.fn == fn then
			return cb
		end
	end
	
	self.callbackCounter = self.callbackCounter + 1
	
	local callback = {
		id = self.callbackCounter,
		fn = fn,
		context = context or "unknown",
		executed = false
	}
	
	table.insert(self.callbacks, callback)
	return callback
end

function Tracer:setCallbackContext(id)
	self.currentCallbackId = id
end

function Tracer:clearCallbackContext()
	self.currentCallbackId = nil
end

function Tracer:getTrace()
	return self.trace
end

function Tracer:getCallbacks()
	return self.callbacks
end

function Tracer:getCallbackById(id)
	for _, cb in ipairs(self.callbacks) do
		if cb.id == id then
			return cb
		end
	end
	return nil
end

function Tracer:getMainTrace()
	local mainTrace = {}
	for _, entry in ipairs(self.trace) do
		if entry.callbackId == nil then
			table.insert(mainTrace, entry)
		end
	end
	return mainTrace
end

function Tracer:getCallbackTrace(callbackId)
	local cbTrace = {}
	for _, entry in ipairs(self.trace) do
		if entry.callbackId == callbackId then
			table.insert(cbTrace, entry)
		end
	end
	return cbTrace
end

function Tracer:getOperationCount()
	return #self.trace
end

function Tracer:getExecutedCallbackCount()
	local count = 0
	for _, cb in ipairs(self.callbacks) do
		if cb.executed then
			count = count + 1
		end
	end
	return count
end

function Tracer:markAsLoopCallback(callbackId)
	self.loopCallbacks[callbackId] = true
end

function Tracer:isLoopCallback(callbackId)
	return self.loopCallbacks[callbackId] == true
end

function Tracer:registerHook(originalName, hookFn, hookType)
	local hookId = #self.hooks + 1
	local hook = {
		id = hookId,
		originalName = originalName,
		hookFn = hookFn,
		hookType = hookType,
		active = true
	}
	table.insert(self.hooks, hook)
	return hook
end

function Tracer:getHooksFor(name)
	local matches = {}
	for _, hook in ipairs(self.hooks) do
		if hook.active and string.find(name, hook.originalName, 1, true) then
			table.insert(matches, hook)
		end
	end
	return matches
end

function Tracer:pushBranch(branchType, condition)
	local branch = {
		type = branchType,
		condition = condition,
		entries = {},
		parent = self.currentBranch
	}
	table.insert(self.branchStack, branch)
	self.currentBranch = branch
	return branch
end

function Tracer:popBranch()
	local branch = self.currentBranch
	if branch then
		self.currentBranch = branch.parent
		table.remove(self.branchStack)
	end
	return branch
end

function Tracer:setKnownValue(name, value, valueType)
	self.knownValues[name] = {
		value = value,
		valueType = valueType
	}
end

function Tracer:getKnownValue(name)
	return self.knownValues[name]
end

function Tracer:getCallbacksByContext(pattern)
	local matches = {}
	for _, cb in ipairs(self.callbacks) do
		if string.find(string.lower(cb.context), pattern, 1, true) then
			table.insert(matches, cb)
		end
	end
	return matches
end

function Tracer:detectLoopPattern(entries)
	if #entries < 3 then
		return nil
	end
	
	local patterns = {}
	local i = 1
	
	while i <= #entries do
		local entry = entries[i]
		local data = entry.data
		
		local basePattern, indexPattern = self:extractPattern(data)
		
		if basePattern then
			if not patterns[basePattern] then
				patterns[basePattern] = {
					base = basePattern,
					indices = {},
					entries = {},
					startIdx = i
				}
			end
			
			table.insert(patterns[basePattern].indices, indexPattern)
			table.insert(patterns[basePattern].entries, entry)
		end
		
		i = i + 1
	end
	
	local validPatterns = {}
	for base, pattern in pairs(patterns) do
		if #pattern.entries >= 3 then
			pattern.endIdx = pattern.startIdx + #pattern.entries - 1
			table.insert(validPatterns, pattern)
		end
	end
	
	return validPatterns
end

function Tracer:extractPattern(data)
	local numPattern = string.match(data, "(.-)_child(%d+)")
	if numPattern then
		return numPattern .. "_child{N}", string.match(data, "_child(%d+)")
	end
	
	numPattern = string.match(data, "(.-)_desc(%d+)")
	if numPattern then
		return numPattern .. "_desc{N}", string.match(data, "_desc(%d+)")
	end
	
	numPattern = string.match(data, "(.-)%[(%d+)%]")
	if numPattern then
		return numPattern .. "[{N}]", string.match(data, "%[(%d+)%]")
	end
	
	local iterPattern = string.match(data, "(.-v%d+)%[")
	if iterPattern then
		local idx = string.match(data, "%[(.-)%]")
		if idx and string.match(idx, "^%d+$") then
			return iterPattern .. "[{N}]", idx
		end
	end
	
	return nil, nil
end

function Tracer:markConditionalPoint(varName, checkType)
	table.insert(self.conditionalPoints, {
		var = varName,
		checkType = checkType,
		traceIndex = #self.trace
	})
end

function Tracer:markNilCheck(varName)
	self.nilCheckVars[varName] = true
end

function Tracer:isNilChecked(varName)
	return self.nilCheckVars[varName] == true
end

function Tracer:getConditionalPoints()
	return self.conditionalPoints
end

return Tracer


-- C:\Users\vendetta\Documents\moon\src\types.luau

local Utils = require("./utils")

local Types = {}

function Types.addToEnvironment(env, sandbox)
	local tracer = sandbox.tracer
	
	env.Instance = {
		new = function(className, parent)
			local varName = tracer:nextVar()
			local callStr
			if parent then
				callStr = "local " .. varName .. "=Instance.new(\"" .. className .. "\"," .. Utils.serialize(parent, sandbox) .. ")"
			else
				callStr = "local " .. varName .. "=Instance.new(\"" .. className .. "\")"
			end
			sandbox:log("call", callStr)
			return sandbox:createMock(varName, className)
		end
	}
	
	local typeConstructors = {
		"Vector2",
		"Vector3",
		"CFrame",
		"Color3",
		"UDim",
		"UDim2",
		"Rect",
		"Ray",
		"Region3",
		"Region3int16",
		"BrickColor",
		"NumberSequence",
		"ColorSequence",
		"NumberRange",
		"TweenInfo",
		"PhysicalProperties",
		"Faces",
		"Axes",
		"Random",
		"NumberSequenceKeypoint",
		"ColorSequenceKeypoint",
		"PathWaypoint",
		"OverlapParams",
		"RaycastParams",
		"Font",
		"Vector2int16",
		"Vector3int16",
		"CatalogSearchParams",
		"FloatCurveKey",
		"RotationCurveKey",
		"DockWidgetPluginGuiInfo",
		"UDim2",
		"Rect"
	}
	
	for _, typeName in ipairs(typeConstructors) do
		env[typeName] = Types.createTypeConstructor(typeName, sandbox)
	end
	
	env.Color3 = Types.createColor3(sandbox)
	env.BrickColor = Types.createBrickColor(sandbox)
	env.CFrame = Types.createCFrame(sandbox)
	env.Vector3 = Types.createVector3(sandbox)
	env.Vector2 = Types.createVector2(sandbox)
	env.UDim2 = Types.createUDim2(sandbox)
	env.UDim = Types.createUDim(sandbox)
	env.TweenInfo = Types.createTweenInfo(sandbox)
	env.NumberSequence = Types.createNumberSequence(sandbox)
	env.ColorSequence = Types.createColorSequence(sandbox)
	env.NumberRange = Types.createNumberRange(sandbox)
	env.Ray = Types.createRay(sandbox)
	env.Rect = Types.createRect(sandbox)
	env.Region3 = Types.createRegion3(sandbox)
	env.RaycastParams = Types.createRaycastParams(sandbox)
	env.OverlapParams = Types.createOverlapParams(sandbox)
	env.Random = Types.createRandom(sandbox)
	env.DateTime = Types.createDateTime(sandbox)
	env.Enum = Types.createEnum(sandbox)
end

function Types.createTypeConstructor(typeName, sandbox)
	local tracer = sandbox.tracer
	
	return setmetatable({}, {
		__index = function(_, key)
			return function(...)
				local args = {...}
				local argStrs = {}
				for _, a in ipairs(args) do
					table.insert(argStrs, Utils.serialize(a, sandbox))
				end
				local expr = typeName .. "." .. key .. "(" .. table.concat(argStrs, ",") .. ")"
				return sandbox:createMock(expr, typeName)
			end
		end,
		__call = function(_, ...)
			local args = {...}
			local argStrs = {}
			for _, a in ipairs(args) do
				table.insert(argStrs, Utils.serialize(a, sandbox))
			end
			local expr = typeName .. ".new(" .. table.concat(argStrs, ",") .. ")"
			return sandbox:createMock(expr, typeName)
		end,
		__tostring = function()
			return typeName
		end
	})
end

function Types.createVector3(sandbox)
	return setmetatable({}, {
		__index = function(_, key)
			if key == "new" then
				return function(x, y, z)
					x = x or 0
					y = y or 0
					z = z or 0
					local expr = "Vector3.new(" .. Utils.serialize(x, sandbox) .. "," .. Utils.serialize(y, sandbox) .. "," .. Utils.serialize(z, sandbox) .. ")"
					return sandbox:createMock(expr, "Vector3")
				end
			elseif key == "zero" then
				return sandbox:createMock("Vector3.zero", "Vector3")
			elseif key == "one" then
				return sandbox:createMock("Vector3.one", "Vector3")
			elseif key == "xAxis" then
				return sandbox:createMock("Vector3.xAxis", "Vector3")
			elseif key == "yAxis" then
				return sandbox:createMock("Vector3.yAxis", "Vector3")
			elseif key == "zAxis" then
				return sandbox:createMock("Vector3.zAxis", "Vector3")
			elseif key == "FromNormalId" then
				return function(normalId)
					local expr = "Vector3.FromNormalId(" .. Utils.serialize(normalId, sandbox) .. ")"
					return sandbox:createMock(expr, "Vector3")
				end
			elseif key == "FromAxis" then
				return function(axis)
					local expr = "Vector3.FromAxis(" .. Utils.serialize(axis, sandbox) .. ")"
					return sandbox:createMock(expr, "Vector3")
				end
			end
			return nil
		end,
		__call = function(_, x, y, z)
			x = x or 0
			y = y or 0
			z = z or 0
			local expr = "Vector3.new(" .. Utils.serialize(x, sandbox) .. "," .. Utils.serialize(y, sandbox) .. "," .. Utils.serialize(z, sandbox) .. ")"
			return sandbox:createMock(expr, "Vector3")
		end,
		__tostring = function()
			return "Vector3"
		end
	})
end

function Types.createVector2(sandbox)
	return setmetatable({}, {
		__index = function(_, key)
			if key == "new" then
				return function(x, y)
					x = x or 0
					y = y or 0
					local expr = "Vector2.new(" .. Utils.serialize(x, sandbox) .. "," .. Utils.serialize(y, sandbox) .. ")"
					return sandbox:createMock(expr, "Vector2")
				end
			elseif key == "zero" then
				return sandbox:createMock("Vector2.zero", "Vector2")
			elseif key == "one" then
				return sandbox:createMock("Vector2.one", "Vector2")
			elseif key == "xAxis" then
				return sandbox:createMock("Vector2.xAxis", "Vector2")
			elseif key == "yAxis" then
				return sandbox:createMock("Vector2.yAxis", "Vector2")
			end
			return nil
		end,
		__call = function(_, x, y)
			x = x or 0
			y = y or 0
			local expr = "Vector2.new(" .. Utils.serialize(x, sandbox) .. "," .. Utils.serialize(y, sandbox) .. ")"
			return sandbox:createMock(expr, "Vector2")
		end,
		__tostring = function()
			return "Vector2"
		end
	})
end

function Types.createCFrame(sandbox)
	return setmetatable({}, {
		__index = function(_, key)
			if key == "new" then
				return function(...)
					local args = {...}
					local argStrs = {}
					for _, a in ipairs(args) do
						table.insert(argStrs, Utils.serialize(a, sandbox))
					end
					local expr = "CFrame.new(" .. table.concat(argStrs, ",") .. ")"
					return sandbox:createMock(expr, "CFrame")
				end
			elseif key == "identity" then
				return sandbox:createMock("CFrame.identity", "CFrame")
			elseif key == "Angles" then
				return function(x, y, z)
					local expr = "CFrame.Angles(" .. Utils.serialize(x, sandbox) .. "," .. Utils.serialize(y, sandbox) .. "," .. Utils.serialize(z, sandbox) .. ")"
					return sandbox:createMock(expr, "CFrame")
				end
			elseif key == "fromEulerAnglesXYZ" then
				return function(x, y, z)
					local expr = "CFrame.fromEulerAnglesXYZ(" .. Utils.serialize(x, sandbox) .. "," .. Utils.serialize(y, sandbox) .. "," .. Utils.serialize(z, sandbox) .. ")"
					return sandbox:createMock(expr, "CFrame")
				end
			elseif key == "fromEulerAnglesYXZ" then
				return function(x, y, z)
					local expr = "CFrame.fromEulerAnglesYXZ(" .. Utils.serialize(x, sandbox) .. "," .. Utils.serialize(y, sandbox) .. "," .. Utils.serialize(z, sandbox) .. ")"
					return sandbox:createMock(expr, "CFrame")
				end
			elseif key == "fromOrientation" then
				return function(x, y, z)
					local expr = "CFrame.fromOrientation(" .. Utils.serialize(x, sandbox) .. "," .. Utils.serialize(y, sandbox) .. "," .. Utils.serialize(z, sandbox) .. ")"
					return sandbox:createMock(expr, "CFrame")
				end
			elseif key == "fromAxisAngle" then
				return function(axis, angle)
					local expr = "CFrame.fromAxisAngle(" .. Utils.serialize(axis, sandbox) .. "," .. Utils.serialize(angle, sandbox) .. ")"
					return sandbox:createMock(expr, "CFrame")
				end
			elseif key == "fromMatrix" then
				return function(pos, vX, vY, vZ)
					local expr = "CFrame.fromMatrix(" .. Utils.serialize(pos, sandbox) .. "," .. Utils.serialize(vX, sandbox) .. "," .. Utils.serialize(vY, sandbox) .. "," .. Utils.serialize(vZ, sandbox) .. ")"
					return sandbox:createMock(expr, "CFrame")
				end
			elseif key == "lookAt" then
				return function(at, lookAt, up)
					local argStrs = {Utils.serialize(at, sandbox), Utils.serialize(lookAt, sandbox)}
					if up then
						table.insert(argStrs, Utils.serialize(up, sandbox))
					end
					local expr = "CFrame.lookAt(" .. table.concat(argStrs, ",") .. ")"
					return sandbox:createMock(expr, "CFrame")
				end
			end
			return nil
		end,
		__call = function(_, ...)
			local args = {...}
			local argStrs = {}
			for _, a in ipairs(args) do
				table.insert(argStrs, Utils.serialize(a, sandbox))
			end
			local expr = "CFrame.new(" .. table.concat(argStrs, ",") .. ")"
			return sandbox:createMock(expr, "CFrame")
		end,
		__tostring = function()
			return "CFrame"
		end
	})
end

function Types.createColor3(sandbox)
	return setmetatable({}, {
		__index = function(_, key)
			if key == "new" then
				return function(r, g, b)
					r = r or 0
					g = g or 0
					b = b or 0
					local expr = "Color3.new(" .. Utils.serialize(r, sandbox) .. "," .. Utils.serialize(g, sandbox) .. "," .. Utils.serialize(b, sandbox) .. ")"
					return sandbox:createMock(expr, "Color3")
				end
			elseif key == "fromRGB" then
				return function(r, g, b)
					r = r or 0
					g = g or 0
					b = b or 0
					local expr = "Color3.fromRGB(" .. Utils.serialize(r, sandbox) .. "," .. Utils.serialize(g, sandbox) .. "," .. Utils.serialize(b, sandbox) .. ")"
					return sandbox:createMock(expr, "Color3")
				end
			elseif key == "fromHSV" then
				return function(h, s, v)
					local expr = "Color3.fromHSV(" .. Utils.serialize(h, sandbox) .. "," .. Utils.serialize(s, sandbox) .. "," .. Utils.serialize(v, sandbox) .. ")"
					return sandbox:createMock(expr, "Color3")
				end
			elseif key == "fromHex" then
				return function(hex)
					local expr = "Color3.fromHex(" .. Utils.serialize(hex, sandbox) .. ")"
					return sandbox:createMock(expr, "Color3")
				end
			end
			return nil
		end,
		__call = function(_, r, g, b)
			r = r or 0
			g = g or 0
			b = b or 0
			local expr = "Color3.new(" .. Utils.serialize(r, sandbox) .. "," .. Utils.serialize(g, sandbox) .. "," .. Utils.serialize(b, sandbox) .. ")"
			return sandbox:createMock(expr, "Color3")
		end,
		__tostring = function()
			return "Color3"
		end
	})
end

function Types.createBrickColor(sandbox)
	return setmetatable({}, {
		__index = function(_, key)
			if key == "new" then
				return function(val)
					local expr = "BrickColor.new(" .. Utils.serialize(val, sandbox) .. ")"
					return sandbox:createMock(expr, "BrickColor")
				end
			elseif key == "palette" then
				return function(index)
					local expr = "BrickColor.palette(" .. Utils.serialize(index, sandbox) .. ")"
					return sandbox:createMock(expr, "BrickColor")
				end
			elseif key == "random" then
				return function()
					return sandbox:createMock("BrickColor.random()", "BrickColor")
				end
			elseif key == "White" then
				return function()
					return sandbox:createMock("BrickColor.White()", "BrickColor")
				end
			elseif key == "Black" then
				return function()
					return sandbox:createMock("BrickColor.Black()", "BrickColor")
				end
			elseif key == "Red" then
				return function()
					return sandbox:createMock("BrickColor.Red()", "BrickColor")
				end
			elseif key == "Green" then
				return function()
					return sandbox:createMock("BrickColor.Green()", "BrickColor")
				end
			elseif key == "Blue" then
				return function()
					return sandbox:createMock("BrickColor.Blue()", "BrickColor")
				end
			elseif key == "Yellow" then
				return function()
					return sandbox:createMock("BrickColor.Yellow()", "BrickColor")
				end
			end
			return nil
		end,
		__call = function(_, val)
			local expr = "BrickColor.new(" .. Utils.serialize(val, sandbox) .. ")"
			return sandbox:createMock(expr, "BrickColor")
		end,
		__tostring = function()
			return "BrickColor"
		end
	})
end

function Types.createUDim2(sandbox)
	return setmetatable({}, {
		__index = function(_, key)
			if key == "new" then
				return function(xScale, xOffset, yScale, yOffset)
					xScale = xScale or 0
					xOffset = xOffset or 0
					yScale = yScale or 0
					yOffset = yOffset or 0
					local expr = "UDim2.new(" .. Utils.serialize(xScale, sandbox) .. "," .. Utils.serialize(xOffset, sandbox) .. "," .. Utils.serialize(yScale, sandbox) .. "," .. Utils.serialize(yOffset, sandbox) .. ")"
					return sandbox:createMock(expr, "UDim2")
				end
			elseif key == "fromScale" then
				return function(xScale, yScale)
					local expr = "UDim2.fromScale(" .. Utils.serialize(xScale, sandbox) .. "," .. Utils.serialize(yScale, sandbox) .. ")"
					return sandbox:createMock(expr, "UDim2")
				end
			elseif key == "fromOffset" then
				return function(xOffset, yOffset)
					local expr = "UDim2.fromOffset(" .. Utils.serialize(xOffset, sandbox) .. "," .. Utils.serialize(yOffset, sandbox) .. ")"
					return sandbox:createMock(expr, "UDim2")
				end
			end
			return nil
		end,
		__call = function(_, xScale, xOffset, yScale, yOffset)
			xScale = xScale or 0
			xOffset = xOffset or 0
			yScale = yScale or 0
			yOffset = yOffset or 0
			local expr = "UDim2.new(" .. Utils.serialize(xScale, sandbox) .. "," .. Utils.serialize(xOffset, sandbox) .. "," .. Utils.serialize(yScale, sandbox) .. "," .. Utils.serialize(yOffset, sandbox) .. ")"
			return sandbox:createMock(expr, "UDim2")
		end,
		__tostring = function()
			return "UDim2"
		end
	})
end

function Types.createUDim(sandbox)
	return setmetatable({}, {
		__index = function(_, key)
			if key == "new" then
				return function(scale, offset)
					scale = scale or 0
					offset = offset or 0
					local expr = "UDim.new(" .. Utils.serialize(scale, sandbox) .. "," .. Utils.serialize(offset, sandbox) .. ")"
					return sandbox:createMock(expr, "UDim")
				end
			end
			return nil
		end,
		__call = function(_, scale, offset)
			scale = scale or 0
			offset = offset or 0
			local expr = "UDim.new(" .. Utils.serialize(scale, sandbox) .. "," .. Utils.serialize(offset, sandbox) .. ")"
			return sandbox:createMock(expr, "UDim")
		end,
		__tostring = function()
			return "UDim"
		end
	})
end

function Types.createTweenInfo(sandbox)
	return setmetatable({}, {
		__index = function(_, key)
			if key == "new" then
				return function(time, easingStyle, easingDirection, repeatCount, reverses, delayTime)
					local args = {}
					table.insert(args, Utils.serialize(time or 1, sandbox))
					if easingStyle then
						table.insert(args, Utils.serialize(easingStyle, sandbox))
					end
					if easingDirection then
						table.insert(args, Utils.serialize(easingDirection, sandbox))
					end
					if repeatCount then
						table.insert(args, Utils.serialize(repeatCount, sandbox))
					end
					if reverses ~= nil then
						table.insert(args, Utils.serialize(reverses, sandbox))
					end
					if delayTime then
						table.insert(args, Utils.serialize(delayTime, sandbox))
					end
					local expr = "TweenInfo.new(" .. table.concat(args, ",") .. ")"
					return sandbox:createMock(expr, "TweenInfo")
				end
			end
			return nil
		end,
		__call = function(_, time, easingStyle, easingDirection, repeatCount, reverses, delayTime)
			local args = {}
			table.insert(args, Utils.serialize(time or 1, sandbox))
			if easingStyle then
				table.insert(args, Utils.serialize(easingStyle, sandbox))
			end
			if easingDirection then
				table.insert(args, Utils.serialize(easingDirection, sandbox))
			end
			if repeatCount then
				table.insert(args, Utils.serialize(repeatCount, sandbox))
			end
			if reverses ~= nil then
				table.insert(args, Utils.serialize(reverses, sandbox))
			end
			if delayTime then
				table.insert(args, Utils.serialize(delayTime, sandbox))
			end
			local expr = "TweenInfo.new(" .. table.concat(args, ",") .. ")"
			return sandbox:createMock(expr, "TweenInfo")
		end,
		__tostring = function()
			return "TweenInfo"
		end
	})
end

function Types.createNumberSequence(sandbox)
	return setmetatable({}, {
		__index = function(_, key)
			if key == "new" then
				return function(...)
					local args = {...}
					local argStrs = {}
					for _, a in ipairs(args) do
						table.insert(argStrs, Utils.serialize(a, sandbox))
					end
					local expr = "NumberSequence.new(" .. table.concat(argStrs, ",") .. ")"
					return sandbox:createMock(expr, "NumberSequence")
				end
			end
			return nil
		end,
		__call = function(_, ...)
			local args = {...}
			local argStrs = {}
			for _, a in ipairs(args) do
				table.insert(argStrs, Utils.serialize(a, sandbox))
			end
			local expr = "NumberSequence.new(" .. table.concat(argStrs, ",") .. ")"
			return sandbox:createMock(expr, "NumberSequence")
		end,
		__tostring = function()
			return "NumberSequence"
		end
	})
end

function Types.createColorSequence(sandbox)
	return setmetatable({}, {
		__index = function(_, key)
			if key == "new" then
				return function(...)
					local args = {...}
					local argStrs = {}
					for _, a in ipairs(args) do
						table.insert(argStrs, Utils.serialize(a, sandbox))
					end
					local expr = "ColorSequence.new(" .. table.concat(argStrs, ",") .. ")"
					return sandbox:createMock(expr, "ColorSequence")
				end
			end
			return nil
		end,
		__call = function(_, ...)
			local args = {...}
			local argStrs = {}
			for _, a in ipairs(args) do
				table.insert(argStrs, Utils.serialize(a, sandbox))
			end
			local expr = "ColorSequence.new(" .. table.concat(argStrs, ",") .. ")"
			return sandbox:createMock(expr, "ColorSequence")
		end,
		__tostring = function()
			return "ColorSequence"
		end
	})
end

function Types.createNumberRange(sandbox)
	return setmetatable({}, {
		__index = function(_, key)
			if key == "new" then
				return function(min, max)
					local expr
					if max then
						expr = "NumberRange.new(" .. Utils.serialize(min, sandbox) .. "," .. Utils.serialize(max, sandbox) .. ")"
					else
						expr = "NumberRange.new(" .. Utils.serialize(min, sandbox) .. ")"
					end
					return sandbox:createMock(expr, "NumberRange")
				end
			end
			return nil
		end,
		__call = function(_, min, max)
			local expr
			if max then
				expr = "NumberRange.new(" .. Utils.serialize(min, sandbox) .. "," .. Utils.serialize(max, sandbox) .. ")"
			else
				expr = "NumberRange.new(" .. Utils.serialize(min, sandbox) .. ")"
			end
			return sandbox:createMock(expr, "NumberRange")
		end,
		__tostring = function()
			return "NumberRange"
		end
	})
end

function Types.createRay(sandbox)
	return setmetatable({}, {
		__index = function(_, key)
			if key == "new" then
				return function(origin, direction)
					local expr = "Ray.new(" .. Utils.serialize(origin, sandbox) .. "," .. Utils.serialize(direction, sandbox) .. ")"
					return sandbox:createMock(expr, "Ray")
				end
			end
			return nil
		end,
		__call = function(_, origin, direction)
			local expr = "Ray.new(" .. Utils.serialize(origin, sandbox) .. "," .. Utils.serialize(direction, sandbox) .. ")"
			return sandbox:createMock(expr, "Ray")
		end,
		__tostring = function()
			return "Ray"
		end
	})
end

function Types.createRect(sandbox)
	return setmetatable({}, {
		__index = function(_, key)
			if key == "new" then
				return function(...)
					local args = {...}
					local argStrs = {}
					for _, a in ipairs(args) do
						table.insert(argStrs, Utils.serialize(a, sandbox))
					end
					local expr = "Rect.new(" .. table.concat(argStrs, ",") .. ")"
					return sandbox:createMock(expr, "Rect")
				end
			end
			return nil
		end,
		__call = function(_, ...)
			local args = {...}
			local argStrs = {}
			for _, a in ipairs(args) do
				table.insert(argStrs, Utils.serialize(a, sandbox))
			end
			local expr = "Rect.new(" .. table.concat(argStrs, ",") .. ")"
			return sandbox:createMock(expr, "Rect")
		end,
		__tostring = function()
			return "Rect"
		end
	})
end

function Types.createRegion3(sandbox)
	return setmetatable({}, {
		__index = function(_, key)
			if key == "new" then
				return function(min, max)
					local expr = "Region3.new(" .. Utils.serialize(min, sandbox) .. "," .. Utils.serialize(max, sandbox) .. ")"
					return sandbox:createMock(expr, "Region3")
				end
			end
			return nil
		end,
		__call = function(_, min, max)
			local expr = "Region3.new(" .. Utils.serialize(min, sandbox) .. "," .. Utils.serialize(max, sandbox) .. ")"
			return sandbox:createMock(expr, "Region3")
		end,
		__tostring = function()
			return "Region3"
		end
	})
end

function Types.createRaycastParams(sandbox)
	return setmetatable({}, {
		__index = function(_, key)
			if key == "new" then
				return function()
					local varName = sandbox.tracer:nextVar()
					sandbox:log("call", "local " .. varName .. "=RaycastParams.new()")
					return sandbox:createMock(varName, "RaycastParams")
				end
			end
			return nil
		end,
		__call = function(_)
			local varName = sandbox.tracer:nextVar()
			sandbox:log("call", "local " .. varName .. "=RaycastParams.new()")
			return sandbox:createMock(varName, "RaycastParams")
		end,
		__tostring = function()
			return "RaycastParams"
		end
	})
end

function Types.createOverlapParams(sandbox)
	return setmetatable({}, {
		__index = function(_, key)
			if key == "new" then
				return function()
					local varName = sandbox.tracer:nextVar()
					sandbox:log("call", "local " .. varName .. "=OverlapParams.new()")
					return sandbox:createMock(varName, "OverlapParams")
				end
			end
			return nil
		end,
		__call = function(_)
			local varName = sandbox.tracer:nextVar()
			sandbox:log("call", "local " .. varName .. "=OverlapParams.new()")
			return sandbox:createMock(varName, "OverlapParams")
		end,
		__tostring = function()
			return "OverlapParams"
		end
	})
end

function Types.createRandom(sandbox)
	return setmetatable({}, {
		__index = function(_, key)
			if key == "new" then
				return function(seed)
					local varName = sandbox.tracer:nextVar()
					if seed then
						sandbox:log("call", "local " .. varName .. "=Random.new(" .. Utils.serialize(seed, sandbox) .. ")")
					else
						sandbox:log("call", "local " .. varName .. "=Random.new()")
					end
					return sandbox:createMock(varName, "Random")
				end
			end
			return nil
		end,
		__call = function(_, seed)
			local varName = sandbox.tracer:nextVar()
			if seed then
				sandbox:log("call", "local " .. varName .. "=Random.new(" .. Utils.serialize(seed, sandbox) .. ")")
			else
				sandbox:log("call", "local " .. varName .. "=Random.new()")
			end
			return sandbox:createMock(varName, "Random")
		end,
		__tostring = function()
			return "Random"
		end
	})
end

function Types.createDateTime(sandbox)
	return {
		now = function()
			return sandbox:createMock("DateTime.now()", "DateTime")
		end,
		fromUnixTimestamp = function(ts)
			return sandbox:createMock("DateTime.fromUnixTimestamp(" .. Utils.serialize(ts, sandbox) .. ")", "DateTime")
		end,
		fromUnixTimestampMillis = function(ts)
			return sandbox:createMock("DateTime.fromUnixTimestampMillis(" .. Utils.serialize(ts, sandbox) .. ")", "DateTime")
		end,
		fromIsoDate = function(str)
			return sandbox:createMock("DateTime.fromIsoDate(" .. Utils.serialize(str, sandbox) .. ")", "DateTime")
		end,
		fromLocalTime = function(tbl)
			return sandbox:createMock("DateTime.fromLocalTime(" .. Utils.serialize(tbl, sandbox) .. ")", "DateTime")
		end,
		fromUniversalTime = function(tbl)
			return sandbox:createMock("DateTime.fromUniversalTime(" .. Utils.serialize(tbl, sandbox) .. ")", "DateTime")
		end
	}
end

function Types.createEnum(sandbox)
	local enumCache = {}
	
	return setmetatable({}, {
		__index = function(_, enumType)
			if enumCache[enumType] then
				return enumCache[enumType]
			end
			
			local enumTypeProxy = setmetatable({}, {
				__index = function(_, enumValue)
					return sandbox:createMock("Enum." .. enumType .. "." .. enumValue, "EnumItem")
				end,
				__tostring = function()
					return "Enum." .. enumType
				end
			})
			
			enumCache[enumType] = enumTypeProxy
			return enumTypeProxy
		end,
		__tostring = function()
			return "Enum"
		end
	})
end

return Types


-- C:\Users\vendetta\Documents\moon\src\utils.luau

local Utils = {}

function Utils.serialize(value, sandbox, depth, seen)
	depth = depth or 0
	seen = seen or {}
	
	if depth > 30 then
		return "{}"
	end
	
	local t = type(value)
	
	if t == "nil" then
		return "nil"
	end
	
	if t == "boolean" then
		return tostring(value)
	end
	
	if t == "number" then
		return Utils.serializeNumber(value)
	end
	
	if t == "string" then
		return Utils.serializeString(value)
	end
	
	if t == "table" then
		if sandbox and sandbox.isTraced and sandbox:isTraced(value) then
			return rawget(value, "_name")
		end
		
		if seen[value] then
			return "{}"
		end
		
		return Utils.serializeTable(value, sandbox, depth, seen)
	end
	
	if t == "function" then
    if sandbox and sandbox.tracer then
        local ctx = sandbox.currentCallContext or "callback"
        local cb = sandbox.tracer:registerCallback(value, ctx)
        return "function()--[[CB:" .. cb.id .. "]]end"
    end
    return "function()end"
end
	
	return tostring(value)
end

function Utils.serializeNumber(n)
	if n ~= n then
		return "0/0"
	end
	
	if n == math.huge then
		return "math.huge"
	end
	
	if n == -math.huge then
		return "-math.huge"
	end
	
	if n == math.floor(n) then
		return tostring(n)
	end
	
	return string.format("%.14g", n)
end

function Utils.serializeString(s, maxLen)
	maxLen = maxLen or 10000
	
	if #s > maxLen then
		s = string.sub(s, 1, maxLen)
	end
	
	local result = {}
	table.insert(result, "\"")
	
	for i = 1, #s do
		local c = string.sub(s, i, i)
		local b = string.byte(c)
		
		if c == "\\" then
			table.insert(result, "\\\\")
		elseif c == "\"" then
			table.insert(result, "\\\"")
		elseif c == "\n" then
			table.insert(result, "\\n")
		elseif c == "\r" then
			table.insert(result, "\\r")
		elseif c == "\t" then
			table.insert(result, "\\t")
		elseif c == "\0" then
			table.insert(result, "\\0")
		elseif b < 32 or b > 126 then
			table.insert(result, string.format("\\x%02x", b))
		else
			table.insert(result, c)
		end
	end
	
	table.insert(result, "\"")
	return table.concat(result)
end

function Utils.serializeTable(tbl, sandbox, depth, seen)
	seen = seen or {}
	depth = depth or 0
	
	seen[tbl] = true
	
	local parts = {}
	
	local arrayLen = 0
	for i = 1, math.huge do
		if tbl[i] == nil then
			break
		end
		arrayLen = i
	end
	
	local usedKeys = {}
	
	for i = 1, arrayLen do
		usedKeys[i] = true
		table.insert(parts, Utils.serialize(tbl[i], sandbox, depth + 1, seen))
	end
	
	local hashKeys = {}
	for k, _ in pairs(tbl) do
		if not usedKeys[k] then
			if type(k) == "string" and string.sub(k, 1, 1) ~= "_" then
				table.insert(hashKeys, k)
			elseif type(k) == "number" then
				table.insert(hashKeys, k)
			end
		end
	end
	
	table.sort(hashKeys, function(a, b)
		local ta = type(a)
		local tb = type(b)
		if ta ~= tb then
			return ta < tb
		end
		if ta == "number" or ta == "string" then
			return a < b
		end
		return tostring(a) < tostring(b)
	end)
	
	for _, k in ipairs(hashKeys) do
		local v = tbl[k]
		local keyStr
		
		if type(k) == "string" and Utils.isValidIdentifier(k) then
			keyStr = k
		else
			keyStr = "[" .. Utils.serialize(k, sandbox, depth + 1, seen) .. "]"
		end
		
		local valueStr = Utils.serialize(v, sandbox, depth + 1, seen)
		table.insert(parts, keyStr .. "=" .. valueStr)
	end
	
	if #parts == 0 then
		return "{}"
	end
	
	return "{" .. table.concat(parts, ",") .. "}"
end

function Utils.isValidIdentifier(s)
	if type(s) ~= "string" then
		return false
	end
	
	if #s == 0 then
		return false
	end
	
	local first = string.sub(s, 1, 1)
	if not string.match(first, "[%a_]") then
		return false
	end
	
	if not string.match(s, "^[%a_][%w_]*$") then
		return false
	end
	
	local keywords = {
		["and"] = true,
		["break"] = true,
		["do"] = true,
		["else"] = true,
		["elseif"] = true,
		["end"] = true,
		["false"] = true,
		["for"] = true,
		["function"] = true,
		["if"] = true,
		["in"] = true,
		["local"] = true,
		["nil"] = true,
		["not"] = true,
		["or"] = true,
		["repeat"] = true,
		["return"] = true,
		["then"] = true,
		["true"] = true,
		["until"] = true,
		["while"] = true,
		["continue"] = true
	}
	
	if keywords[s] then
		return false
	end
	
	return true
end

function Utils.randomLetters(n)
	local chars = "abcdefghijklmnopqrstuvwxyz"
	local result = ""
	for _ = 1, n do
		local idx = math.random(1, #chars)
		result = result .. string.sub(chars, idx, idx)
	end
	return result
end

function Utils.trim(s)
	return string.match(s, "^%s*(.-)%s*$")
end

function Utils.startsWith(s, prefix)
	return string.sub(s, 1, #prefix) == prefix
end

function Utils.endsWith(s, suffix)
	return string.sub(s, -#suffix) == suffix
end

function Utils.split(s, delimiter)
	local result = {}
	local pattern = "([^" .. delimiter .. "]+)"
	for match in string.gmatch(s, pattern) do
		table.insert(result, match)
	end
	return result
end

function Utils.escapePattern(s)
	return string.gsub(s, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1")
end

function Utils.deepCopy(t, seen)
	if type(t) ~= "table" then
		return t
	end
	
	seen = seen or {}
	
	if seen[t] then
		return seen[t]
	end
	
	local copy = {}
	seen[t] = copy
	
	for k, v in pairs(t) do
		copy[Utils.deepCopy(k, seen)] = Utils.deepCopy(v, seen)
	end
	
	local mt = getmetatable(t)
	if mt then
		setmetatable(copy, Utils.deepCopy(mt, seen))
	end
	
	return copy
end

return Utils


-- C:\Users\vendetta\Documents\moon\main.luau

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

local Null = require("./src/null")

local VERSION = "1.1.0"

local function generateOutputName()
	local chars = "abcdefghijklmnopqrstuvwxyz"
	local suffix = ""
	for _ = 1, 3 do
		local idx = math.random(1, #chars)
		suffix = suffix .. string.sub(chars, idx, idx)
	end
	return "null_output_" .. suffix .. ".lua"
end

local function printUsage()
	print("Null Environment Logger v" .. VERSION)
	print("")
	print("Usage: lune run main <input.lua> [options]")
	print("")
	print("Options:")
	print("  -o, --output <file>     Output file path")
	print("  --placeid <id>          Spoof PlaceId (default: 0)")
	print("  --gameid <id>           Spoof GameId (default: 0)")
	print("  --userid <id>           Spoof UserId (default: 1234567890)")
	print("  --username <name>       Spoof Username (default: Player)")
	print("  --displayname <name>    Spoof DisplayName (default: Player)")
	print("  -h, --help              Show this help")
	print("")
	print("Examples:")
	print("  lune run main script.lua")
	print("  lune run main script.lua -o output.lua")
	print("  lune run main script.lua --placeid 123456 --userid 987654")
end

local function parseArgs(args)
	local parsed = {
		input = nil,
		output = nil,
		placeId = 0,
		gameId = 0,
		userId = 1234567890,
		username = "Player",
		displayName = "Player"
	}
	
	local i = 1
	while i <= #args do
		local arg = args[i]
		
		if arg == "-o" or arg == "--output" then
			i = i + 1
			if i <= #args then
				parsed.output = args[i]
			else
				print("Error: -o requires a file path")
				return nil
			end
		elseif arg == "--placeid" then
			i = i + 1
			if i <= #args then
				parsed.placeId = tonumber(args[i]) or 0
			end
		elseif arg == "--gameid" then
			i = i + 1
			if i <= #args then
				parsed.gameId = tonumber(args[i]) or 0
			end
		elseif arg == "--userid" then
			i = i + 1
			if i <= #args then
				parsed.userId = tonumber(args[i]) or 1234567890
			end
		elseif arg == "--username" then
			i = i + 1
			if i <= #args then
				parsed.username = args[i]
			end
		elseif arg == "--displayname" then
			i = i + 1
			if i <= #args then
				parsed.displayName = args[i]
			end
		elseif arg == "-h" or arg == "--help" then
			printUsage()
			process.exit(0)
			return nil
		elseif string.sub(arg, 1, 1) == "-" then
			print("Error: Unknown option: " .. arg)
			printUsage()
			return nil
		else
			if not parsed.input then
				parsed.input = arg
			end
		end
		
		i = i + 1
	end
	
	return parsed
end

local function main()
	local args = process.args
	
	if #args < 1 then
		printUsage()
		process.exit(1)
		return
	end
	
	local parsed = parseArgs(args)
	if not parsed then
		process.exit(1)
		return
	end
	
	if not parsed.input then
		print("Error: No input file specified")
		printUsage()
		process.exit(1)
		return
	end
	
	local inputPath = parsed.input
	
	local readSuccess, source = pcall(fs.readFile, inputPath)
	if not readSuccess then
		print("Error: Cannot read file: " .. inputPath)
		process.exit(1)
		return
	end
	
	math.randomseed(os.clock() * 1000000 + os.time())
	local outputPath = parsed.output or generateOutputName()
	
	print("Null Environment Logger v" .. VERSION)
	print("Input: " .. inputPath)
	print("Output: " .. outputPath)
	print("")
	
	if parsed.placeId ~= 0 or parsed.gameId ~= 0 or parsed.userId ~= 1234567890 then
		print("Spoofing:")
		if parsed.placeId ~= 0 then
			print("  PlaceId: " .. parsed.placeId)
		end
		if parsed.gameId ~= 0 then
			print("  GameId: " .. parsed.gameId)
		end
		if parsed.userId ~= 1234567890 then
			print("  UserId: " .. parsed.userId)
		end
		if parsed.username ~= "Player" then
			print("  Username: " .. parsed.username)
		end
		if parsed.displayName ~= "Player" then
			print("  DisplayName: " .. parsed.displayName)
		end
		print("")
	end
	
	local config = {
		callbackMaxDepth = 10,
		callbackMaxIterations = 5,
		loopCallbackIterations = 3,
		dualPathExecution = true,
		maxTraceSize = 100000,
		executionLimit = 500000,
		
		-- Spoof values
		placeId = parsed.placeId,
		gameId = parsed.gameId,
		jobId = "",
		userId = parsed.userId,
		username = parsed.username,
		displayName = parsed.displayName
	}
	
	local null = Null.new(config)
	
	local success, err = null:process(source, inputPath)
	
	if not success then
		print("Execution error: " .. tostring(err))
		print("Generating partial output...")
	end
	
	local output = null:getOutput()
	
	local writeSuccess = pcall(fs.writeFile, outputPath, output)
	if not writeSuccess then
		print("Error: Cannot write to: " .. outputPath)
		process.exit(1)
		return
	end
	
	local stats = null:getStats()
	print("Saved: " .. outputPath)
	print("Operations: " .. stats.operations)
	print("Callbacks: " .. stats.callbacks)
end

main()


