--// CodingAgent 2026

local Root = script.Parent.Parent

local Var = require(Root.Variables)
local Frontend = require(Root.Frontend)
local Utilities = require(Root.Utilities)

local Page
local Frames = Var.MainFrame.Welcome.SetupFull

local TweenService = Var.Services.TweenService

local function SwapPages(Page1, Page2, NewIcon, Spin)
	local TTC = 0.4

	local Tweem = TweenService:Create(
		Page1,
		TweenInfo.new(TTC * 1.3, Enum.EasingStyle.Cubic),
		{ Position = UDim2.new(-0.5, 0, 0, 0), GroupTransparency = 1 }
	)

	Tweem:Play()
	TweenService:Create(
		Frames.SideDecor.ImageLabel,
		TweenInfo.new(TTC, Enum.EasingStyle.Cubic),
		{ Position = UDim2.new(0, 0, 0.5, 0), ImageTransparency = 1 }
	):Play()
	TweenService:Create(
		Frames.SideDecor.AdmSpinner,
		TweenInfo.new(TTC, Enum.EasingStyle.Cubic),
		{ Position = UDim2.new(0, 0, 0.5, 0), GroupTransparency = 1 }
	):Play()

	Tweem.Completed:Wait()

	Page2.Position = UDim2.new(0.3, 0, 0, 0)
	Page2.GroupTransparency = 1

	Frames.SideDecor.ImageLabel.Position = UDim2.new(0.8, 0, 0.5, 0)
	Frames.SideDecor.AdmSpinner.Position = UDim2.new(0.8, 0, 0.5, 0)

	Frames.SideDecor.AdmSpinner.Visible = Spin
	Frames.SideDecor.ImageLabel.Image = NewIcon
	Frames.SideDecor.ImageLabel.Visible = not Spin

	Page2.Visible = true
	Page1.Visible = false

	local ET = TweenService:Create(
		Page2,
		TweenInfo.new(TTC * 1.2, Enum.EasingStyle.Cubic),
		{ Position = UDim2.new(0, 0, 0, 0), GroupTransparency = 0 }
	)
	TweenService:Create(
		Frames.SideDecor.ImageLabel,
		TweenInfo.new(TTC, Enum.EasingStyle.Cubic),
		{ Position = UDim2.new(0.5, 0, 0.5, 0), ImageTransparency = 0 }
	):Play()
	TweenService:Create(
		Frames.SideDecor.AdmSpinner,
		TweenInfo.new(TTC, Enum.EasingStyle.Cubic),
		{ Position = UDim2.new(0.5, 0, 0.5, 0), GroupTransparency = 0 }
	):Play()

	ET:Play()

	ET.Completed:Wait()
end

return function()
	local ConnectionsTable = {}
	local Metadata = {
		["HD_ADMIN"] = {
			PrettyName = "HD Admin",
			Icon = "",
		},

		["EXE_5"] = {
			PrettyName = "Exe 5",
			Icon = "",
		},

		["EXE_6"] = {
			PrettyName = "Exe 6",
			Icon = "",
		},

		["ADONIS"] = {
			PrettyName = "Adonis",
			Icon = "",
		},

		["BASIC_ADMIN_ESSENTIALS"] = {
			PrettyName = "Basic Admin Essentials",
			Icon = "",
		},

		["KOHLS_ADMIN"] = {
			PrettyName = "Kohl's Admin",
			Icon = "",
		},

		["KOHLS_ADMIN_2"] = {
			PrettyName = "Kohl's Admin 2",
			Icon = "",
		},
	}

	Frames.Page1.Body.Text = string.gsub(Frames.Page1.Body.Text, "CodingAgent", game.Players.LocalPlayer.Name)

	Frames.Page1.NextPage.MouseButton1Click:Connect(function()
		SwapPages(Frames.Page1, Frames.Loading, "", true)

		local ImportableSystems = Var.Remotes.GetImports:InvokeServer()

		print(ImportableSystems)

		for TechSystemName, System in ImportableSystems do
			if TechSystemName == "_AdmSystemsNotFound" then
				continue
			end

			local NewDisplay = Frames.Page2.Select.System:Clone()

			NewDisplay.Name = TechSystemName
			NewDisplay.SystemName.Text = Metadata[TechSystemName].PrettyName
			NewDisplay.SystemIcon.Image = Metadata[TechSystemName].Icon
			NewDisplay.DetectedStatus.Text =
				`Ready to import {System.Admins} admins, {System.Commands} commands, and {System.Ranks} ranks`
			NewDisplay.StatusIcon.Image = Utilities.Icon("check-plain")

			NewDisplay.Parent = Frames.Page2.Select
			NewDisplay.Visible = true
		end

		for _, TechSystemName in ImportableSystems._AdmSystemsNotFound do
			local NewDisplay = Frames.Page2.Select.System:Clone()

			NewDisplay.Name = TechSystemName
			NewDisplay.SystemName.Text = Metadata[TechSystemName].PrettyName
			NewDisplay.SystemIcon.Image = Metadata[TechSystemName].Icon
			NewDisplay.DetectedStatus.Text = "Not found"
			NewDisplay.StatusIcon.Image = Utilities.Icon("tear")

			NewDisplay.Parent = Frames.Page2.Select
			NewDisplay.Visible = true
		end

		Frontend.Toast({
			Icon = Utilities.Icon("warning-triangle"),
			Text = "This feature is not ready for use yet",
			Subtext = "Please do not report any errors.",
			Timeout = 3,
		})

		SwapPages(Frames.Loading, Frames.Page2, Utilities.Icon("route"), false)
	end)

	Frames.Page2.NextPage.MouseButton1Click:Connect(function()
		SwapPages(Frames.Page2, Frames.Loading, "", true)

		for _, Connection in ConnectionsTable do
			Connection:Disconnect()
		end

		local GatewayCard = Frames.Page3.Select.URL:Clone()
		GatewayCard.Name = "GatewayCard"
		GatewayCard.SystemName.Text = "Gateway (Automatic)"
		GatewayCard.DetectedStatus.Text = "Connected · Automatic failover · No configuration required"
		GatewayCard.Parent = Frames.Page3.Select
		GatewayCard.Visible = true

		Frames.Page3.CustomServer.Click.MouseButton1Click:Connect(function()
			Frontend.Popup.new(
				{
					Icon = Utilities.Icon("administer"),
					Name = "Administer",
					Subheader = "Custom AOS Server",
				},
				{
					Primary = Utilities.Icon("network"),
					SubIcon = Utilities.Icon("chain"),
				},

				"Custom AOS URL (Optional)",
				"Enter your own AOS instance URL. Leave blank to use the gateway, which automatically routes to the best available node.",

				{
					{
						Text = "Use gateway",
						Icon = Utilities.Icon("no-entry"),
						Callback = function(Close)
							Close()
						end,
					},
					{
						Text = "Save",
						Icon = Utilities.Icon("floppy-disk"),
						Callback = function(Close, TextInputs)
							local ServerURL = TextInputs[1]
							Close()

							if not ServerURL or ServerURL == "" then
								return
							end

							local Result = Var.Remotes.SetPrimaryAppServer:InvokeServer(ServerURL)

							Frontend.Popup.new(
								{
									Icon = Utilities.Icon("administer"),
									Name = "Administer",
									Subheader = "Custom AOS Server",
								},
								{
									Primary = Utilities.Icon("network"),
									SubIcon = Utilities.Icon(Result[1] and "check-plain" or "x"),
								},
								Result[1] and "Saved!" or "Something went wrong",
								Result[1]
										and "Your custom AOS URL has been saved. You can clear it later from the Marketplace settings."
									or "Failed to save the custom server URL. Check your game's output for details.",
								{
									{
										Text = "OK",
										Icon = Utilities.Icon("check-plain"),
										Callback = function(C)
											C()
										end,
									},
								}
							)
						end,
					},
				},
				{ {
					HeaderText = "Server URL (https://my-aos.example.com)",
				} }
			)
		end)

		SwapPages(Frames.Loading, Frames.Page3, Utilities.Icon("network"), false)
	end)

	Frames.Page3.NextPage.MouseButton1Click:Connect(function()
		SwapPages(Frames.Page3, Frames.Loading, "", true)

		SwapPages(Frames.Loading, Frames.Page4, Utilities.Icon("gear"), false)
	end)

	Frames.Page4.NextPage.MouseButton1Click:Connect(function()
		SwapPages(Frames.Page4, Frames.Page5, Utilities.Icon("student"), false)
	end)

	Frames.Page5.TourConfirm.MouseButton1Click:Connect(function()
		local tour = require(script.Tour)
		tour.BeginTour()

		SwapPages(Frames.Page5, Frames.Page6, Utilities.Icon("rbxassetid://check-plain"), false)
	end)

	Frames.Page5.Exit.MouseButton1Click:Connect(function()
		SwapPages(Frames.Page5, Frames.Page6, Utilities.Icon("check-plain"), false)
	end)

	Frames.Page6.Exit.MouseButton1Click:Connect(function()
		print(Var.Remotes.TutorialSeen:InvokeServer("FirstTimeSetup", true))

		Var.DoHeaderEffects = true
		Frontend.SwapAppBasic(Var.MainFrame.Home, {
			Icon = Utilities.Icon("home"),
			Name = "Home",
		})
	end)
end


local Types = {}

export type ButtonConfig = {
	Icon: string,
	AppName: string,
	Background: string,
	Description: string,

	MainButtonClick: () -> (),
	MainButtonFrame: Frame | CanvasGroup?,
	ButtonID: string,

	SubIcons: {
		{
			Icon: string,
			Name: string,
			Frame: Frame | CanvasGroup,
			Click: () -> (),

			ButtonID: string,
		}
	},
}

return Types


return {
	EditMode = false,
	MaxPeople = 50,

	EditModeMembers = {},
	EditModeApps = {},
	EditModeName = "",
	EditModeColor = "",
	EditModeReason = "",
	EditModeIsProtected = false,
	EditModeRank = 0,

	HasPopulatedApps = false,

	Strings = {
		["_locale"] = "en_US",

		["WelcHeaderNew"] = "Welcome to the Rank Creator",
		["WelcHeaderEdit"] = "Welcome to the Rank Editor",

		["WelcBodyNew"] = "The Rank Creator allows you to create new admin ranks and specify their permitted apps and users. New users you add will be given access to Administer instantly even if already in-game.\n\nYou'll only be able to add apps which you personally have access to. Only people with the permission will be able to enabled the option to permit all apps.",
		["WelcBodyEdit"] = "Any changes you make here will be saved to the index and will be logged to the rank to ensure safety of your game.\n\nPlease note the same rules apply with all access. Protected ranks may not be edited directly via UI.",

		["FilterStepHead"] = "Name",
		["FilterStepBody"] = "Pick this rank's name. Some apps will display it to the public so it'll be filtered.",

		["MembersStepHead"] = "Members",
		["MembersStepBody"] = "Choose who will be able to access Administer under this rank.",

		["AppsStepHead"] = "Apps",
		["AppsStepBody"] = "Choose the apps this rank will be able to use. You can't pick any apps you don't have yourself.",

		["FinBodyNew"] = "Great! Your rank has been successfully created.\nAnybody you just added will be given the panel instantly. No need to do anything from here.\n\nIf you would like to edit this rank in the future, come back to this page at any time and click the settings icon next to the name of the rank you just created.",
		["FinBodyEdit"] = "Done! This rank has been edited with your requested changes.\n\nNew members will get the panel and removed ones will lose it. No need to do anything else.",

		["FinBodyNewError"] = "Something unexpected happened, and the server code didn't handle it right. This is either a misconfiguration or Administer issue. Do your part and report it if you didn't cause it.\n\n%s",
		["FinBodyEditError"] = "Looks like we can't process your edit right now, sorry about that. This may be due to unexpected permissions or another error. Here's the support error for reporting:\n\n%s",

		["FinHeaderError"] = "Oops, that's not right.",
		["FinHeaderSucc"] = "All done!",

		["NavBarProgress"] = "Step %s/%s",
		["NavBarHeaderNewUnconf"] = "Creating a rank...",
		["NavBarHeaderNewNamed"] = "Creating %s...",

		["CannotGoBack"] = "You can't do that right now.",

		["RanksV2MigrationNudgeHdr"] = "Rank Deprecation Warning",
		["RanksV2MigrationNudgeBody"] = "Your game is currently using an old version of Administer ranks and it will need to update. By clicking yes, all ranks will be removed and you will need to remake them. This process may only be done in Studio to ensure you don't get locked out of Administer when the process is complete.",
	},
}


local module = {}
local Variables = require(script.Parent.Parent.Variables)
local settings = Variables.MainFrame.Configuration.Settings

type settingProperties = { title: string, subtitle: string, description: string, placeholder: string }
type category = { title: string, containers: { title: string, entries: { settingProperties } } }

function module.createCategory(input: category)
	local categoryTemplate = settings
end

return module


--// Administer
--// CodingAgent (2024-2026)

local Variables = {}

Variables.AdministerRoot = script.Parent.Parent.Parent
Variables.Remotes = game.ReplicatedStorage:WaitForChild("Administer", 15)

Variables.MainFrame = Variables.AdministerRoot:WaitForChild("Main", 10)
Variables.Mobile = false
Variables.InitErrored = false
Variables.IsOpen = false
Variables.IsPanelAnimating = false
Variables.EnableEditableImages = false
Variables.ActivePage = Variables.MainFrame.Home
Variables.DoHeaderEffects = true
Variables.HeartbeatDuration = 10
Variables.PingTicksLog = 60
Variables.AnimationSpeed = 1

Variables.CombinedButtonConfig = {}
Variables.Ping = {}

Variables.Panel = nil
Variables.OpenSound = script.Parent

Variables.Services = {
	Players = game:GetService("Players"),
	GUIService = game:GetService("GuiService"),
	HttpService = game:GetService("HttpService"),
	TweenService = game:GetService("TweenService"),
	AssetService = game:GetService("AssetService"),
	UserInputService = game:GetService("UserInputService"),
	ReplicatedStorage = game:GetService("ReplicatedStorage"),
	MarketplaceService = game:GetService("MarketplaceService"),
	LocalizationService = game:GetService("LocalizationService"),
}

Variables.Marketplace = {
	ActivePage = "AAFeatured",
	ActivePageType = "FEATURED",
}

Variables.LocaleModule = { ["_"] = "_init" }

function Variables.init() end

return Variables


--// Administer
--// CodingAgent 2024-2026

local Widgets = {
	Active = {},
}

local Modules = script.Parent

local Utilities = require(Modules.Utilities)
local Frontend = require(Modules.Frontend)
local Var = require(Modules.Variables)

function Widgets.GetAvailableWidgets(): { Small: { {} }, Large: { {} } }
	local Widgets: { Small: { {} }, Large: { {} } } = { Small = {}, Large = {} }

	for _, Widget in Var.MainFrame:GetChildren() do
		local WidgetFolder: Folder? = Widget:FindFirstChild(".widgets")
		if not WidgetFolder or not WidgetFolder:IsA("Folder") then
			continue
		end

		xpcall(function()
			local Module: Instance? = WidgetFolder:FindFirstChild(".widgetconfig")
			local Config

			if Module and Module:IsA("ModuleScript") then
				Config = require(Module) :: any
			end

			if not Config then
				Utilities.Logging.Error(
					`{Widget.Name}: Invalid Administer Widget folder (missing .widgetconfig, please read the docs!)`
				)
			end

			local SplitGenerator = string.split(Config._generator, "-")
			if SplitGenerator[1] ~= "AdministerWidgetConfig" then
				Utilities.Logging.Error(
					`{Widget.Name}: Not a valid Administer widget configuration file (bad .widgetconfig, please read the docs!)`
				)
			end

			for Key, FinalWidget in Config["Widgets"] do
				if FinalWidget["Type"] == "SMALL_LABEL" then
					table.insert(Widgets["Small"], FinalWidget)
				elseif FinalWidget["Type"] == "LARGE_BOX" then
					table.insert(Widgets["Large"], FinalWidget)
				else
					Utilities.Logging.Error(`{Widget.Name}: Bad widget type (not in predefined list)`)
				end
				FinalWidget["Identifier"] = `{Widget.Name}\\{Key}`
				FinalWidget["AppName"] = Widget.Name
			end
		end, function(z)
			Utilities.Logging.Warn(z)
			Utilities.Logging.Error(`{Widget.Name}: This does not appear to be a valid Administer widget (failure)`)
		end)
	end

	return Widgets
end

function Widgets.Edit(ContainingUI): ()
	if ContainingUI:GetAttribute("IsEditing") then
		Frontend.Toast({
			Icon = Utilities.Icon("triangle-warning"),
			Text = Utilities.Translate("generic.error.Generic"),
			Subtext = Utilities.Translate("widgets.concurrent.Body"),
			Timeout = 5,
		})

		return
	else
		ContainingUI:SetAttribute("IsEditing", true)
	end

	local Editing = ContainingUI.Editing
	local AnimSpeed = Var.AnimationSpeed * 1.2
	local Selected = ""
	local SelectedTable = {}
	local Tweens: { Tween } = {}

	Editing.Visible = true
	Editing.Preview.Select.Visible = true

	local NewPreviewContent: CanvasGroup = ContainingUI.Content:Clone()

	for _, Item in NewPreviewContent:GetChildren() do
		if
			Item:IsA("LocalScript") or Item:IsA("Script")
		then
			Item:Destroy()
		end
	end

	NewPreviewContent.Parent = Editing:FindFirstChild("Preview")

	Tweens = {
		Var.Services.TweenService:Create(Editing.Preview, TweenInfo.new(AnimSpeed, Enum.EasingStyle.Quart), {
			Size = UDim2.new(0.459, 0, 0.551, 0),
			Position = UDim2.new(0.271, 0, 0.057, 0),
			BackgroundTransparency = 0.4,
		}),
		Var.Services.TweenService:Create(
			NewPreviewContent,
			TweenInfo.new(AnimSpeed * 0.35),
			{ Size = UDim2.new(1, 0, 1, 0), Position = UDim2.new(0, 0, 0, 0) }
		),
		Var.Services.TweenService:Create(Editing.Background, TweenInfo.new(AnimSpeed), { ImageTransparency = 0 }),
		Var.Services.TweenService:Create(Editing.AppName, TweenInfo.new(AnimSpeed), { TextTransparency = 0 }),
		Var.Services.TweenService:Create(Editing.WidgetName, TweenInfo.new(AnimSpeed), { TextTransparency = 0 }),
		Var.Services.TweenService:Create(Editing.Last.ImageLabel, TweenInfo.new(AnimSpeed), { ImageTransparency = 0 }),
		Var.Services.TweenService:Create(Editing.Next.ImageLabel, TweenInfo.new(AnimSpeed), { ImageTransparency = 0 }),
		Var.Services.TweenService:Create(
			Editing.Preview.DefaultCorner_,
			TweenInfo.new(AnimSpeed),
			{ CornerRadius = UDim.new(0, 18) }
		),
	}

	xpcall(function()
		Editing.AppName.Text = ContainingUI:GetAttribute("AppName")
		Editing.WidgetName.Text = ContainingUI:GetAttribute("InitialWidgetName")
	end, function()
		Editing.AppName.Text = Utilities.Translate("widgets.uninstalled.Header")
		Editing.WidgetName.Text = Utilities.Translate("widgets.Corrupted")
	end)

	task.spawn(function()
		Tweens[1]:Play()
		Tweens[2]:Play()
		Tweens[3]:Play()
		Tweens[8]:Play()

		task.wait(AnimSpeed * 0.8)

		for _, Tween in Tweens do
			Tween:Play()
		end
	end)

	local HoverFX = {}
	local ShouldHover = true

	HoverFX[1] = Editing.Preview.Select.MouseEnter:Connect(function()
		if not ShouldHover then
			return
		end
		Var.Services.TweenService
			:Create(
				Editing.Preview,
				TweenInfo.new(AnimSpeed * 0.6, Enum.EasingStyle.Quart),
				{ Size = UDim2.new(0.472, 0, 0.614, 0), Position = UDim2.new(0.264, 0, 0.017, 0) }
			)
			:Play()
	end)

	HoverFX[2] = Editing.Preview.Select.MouseLeave:Connect(function()
		if not ShouldHover then
			return
		end
		Var.Services.TweenService
			:Create(
				Editing.Preview,
				TweenInfo.new(AnimSpeed * 0.6, Enum.EasingStyle.Quart),
				{ Size = UDim2.new(0.459, 0, 0.551, 0), Position = UDim2.new(0.271, 0, 0.057, 0) }
			)
			:Play()
	end)

	HoverFX["ClickEvent"] = Editing.Preview.Select.MouseButton1Click:Connect(function()
		for _, v in HoverFX do
			v:Disconnect()
		end

		Editing.Preview.Select.Visible = false

		AnimSpeed = ((tonumber(Utilities.GetSetting("AnimationSpeed")) :: number) or 1) * 0.4

		Tweens = {
			Var.Services.TweenService:Create(
				Editing.Preview,
				TweenInfo.new(
					((tonumber(Utilities.GetSetting("AnimationSpeed")) :: number) or 1) * 1.2,
					Enum.EasingStyle.Quart
				),
				{ Position = UDim2.new(0.264, 0, 0.189, 0) }
			),
			Var.Services.TweenService:Create(Editing.AppName, TweenInfo.new(AnimSpeed), { TextTransparency = 1 }),
			Var.Services.TweenService:Create(Editing.WidgetName, TweenInfo.new(AnimSpeed), { TextTransparency = 1 }),
			Var.Services.TweenService:Create(
				Editing.Last.ImageLabel,
				TweenInfo.new(AnimSpeed),
				{ ImageTransparency = 1 }
			),
			Var.Services.TweenService:Create(
				Editing.Next.ImageLabel,
				TweenInfo.new(AnimSpeed),
				{ ImageTransparency = 1 }
			),
		}

		for _, Tween in Tweens do
			Tween:Play()
		end

		Tweens[1].Completed:Wait()
		AnimSpeed = ((tonumber(Utilities.GetSetting("AnimationSpeed")) :: number) or 1) * 1.2

		Var.Services.TweenService
			:Create(
				Editing.Preview,
				TweenInfo.new(AnimSpeed, Enum.EasingStyle.Quart),
				{ Position = UDim2.new(0.043, 0, 0, 0), Size = UDim2.new(0.957, 0, 1, 0), BackgroundTransparency = 1 }
			)
			:Play()
		Var.Services.TweenService:Create(Editing.Background, TweenInfo.new(AnimSpeed), { ImageTransparency = 1 }):Play()
		Var.Services.TweenService
			:Create(Editing.Preview.DefaultCorner_, TweenInfo.new(AnimSpeed), { CornerRadius = UDim.new(0, 0) })
			:Play()

		if Selected == "" then
			Utilities.Logging.Print("Exiting because nothing was selected!")
			for _, Element in Editing.Preview:GetChildren() do
				if not table.find({ "DefaultCorner_", "Select" }, Element.Name) then
					Element.Parent = ContainingUI.Content
				end
			end

			Editing.Visible = false
			return
		end

		Var.Remotes.UpdateHomePage:InvokeServer({
			EventType = "UPDATE",
			EventID = `ChangeWidget-{ContainingUI.Name}`,
			WidgetID = ContainingUI.Name,
			NewIdentifier = Selected,
		})

		task.wait(AnimSpeed)

		ContainingUI.SideData.Banner.Text = SelectedTable["Name"]
		ContainingUI.SideData.BannerIcon.Image = SelectedTable["Icon"]
		ContainingUI.Content:ClearAllChildren()

		for _, Element in Editing.Preview:GetChildren() do
			if not table.find({ "DefaultCorner_", "Select" }, Element.Name) then
				Element.Parent = ContainingUI.Content
			end
		end

		Editing.Visible = false
		ContainingUI:SetAttribute("IsEditing", false)
	end)

	local Widgets = Widgets.GetAvailableWidgets()["Large"]
	local Count = 0
	local Buttons = {}

	local function ClickNext()
		ShouldHover = false
		Count += 1

		if Count > #Widgets then
			Count = 1
		end

		if Widgets[Count].CanDiscover == false then
			Count += 1
			return ClickNext()
		end

		AnimSpeed = Var.AnimationSpeed * 3
		Tweens = {
			Var.Services.TweenService:Create(
				Editing.Preview,
				TweenInfo.new(AnimSpeed, Enum.EasingStyle.Quart),
				{ Position = UDim2.new(-1, 0, 0.057, 0), GroupTransparency = 1 }
			),
			Var.Services.TweenService:Create(
				Editing.AppName,
				TweenInfo.new(AnimSpeed, Enum.EasingStyle.Quart),
				{ Position = UDim2.new(-1, 0, 0.81, 0), TextTransparency = 1 }
			),
			Var.Services.TweenService:Create(
				Editing.WidgetName,
				TweenInfo.new(AnimSpeed, Enum.EasingStyle.Quart),
				{ Position = UDim2.new(-1, 0, 0.647, 0), TextTransparency = 1 }
			),
		}
		for _, t in Tweens do
			t:Play()
		end

		task.wait(AnimSpeed / 3)

		local Widget = Widgets[Count]
		local NewWidgetTemplate = Widget["BaseUIFrame"]:Clone()
		NewWidgetTemplate.Visible = true

		for _, Element in Editing.Preview:GetChildren() do
			if not table.find({ "DefaultCorner_", "Select" }, Element.Name) then
				Element:Destroy()
			end
		end

		NewWidgetTemplate.Parent = Editing.Preview
		Selected = Widget["Identifier"]

		Editing.Preview.Position = UDim2.new(1, 0, 0.075, 0)
		Editing.AppName.Position = UDim2.new(1, 0, 0.81, 0)
		Editing.WidgetName.Position = UDim2.new(1, 0, 0.647, 0)
		Editing.WidgetName.Text = Widget["Name"]
		Editing.AppName.Text = Widget["AppName"]
		AnimSpeed = Var.AnimationSpeed * 2.45
		SelectedTable = Widget

		Tweens = {
			Var.Services.TweenService:Create(
				Editing.Preview,
				TweenInfo.new(AnimSpeed, Enum.EasingStyle.Quart),
				{ Position = UDim2.new(0.271, 0, 0.057, 0), GroupTransparency = 0 }
			),
			Var.Services.TweenService:Create(
				Editing.AppName,
				TweenInfo.new(AnimSpeed, Enum.EasingStyle.Quart),
				{ Position = UDim2.new(0.04, 0, 0.81, 0), TextTransparency = 0 }
			),
			Var.Services.TweenService:Create(
				Editing.WidgetName,
				TweenInfo.new(AnimSpeed, Enum.EasingStyle.Quart),
				{ Position = UDim2.new(0.04, 0, 0.647, 0), TextTransparency = 0 }
			),
		}
		for _, t in Tweens do
			t:Play()
		end

		Tweens[1].Completed:Wait()
		ShouldHover = true
	end

	Buttons[1] = Editing.Next.MouseButton1Click:Connect(ClickNext)

	Buttons[2] = Editing.Last.MouseButton1Click:Connect(function()
		ShouldHover = false
		Count -= 1

		if Count < 1 then
			Count = 0
			return
		end

		if Count > #Widgets then
			Count = 1
		end

		AnimSpeed = Var.AnimationSpeed * 2
		Tweens = {
			Var.Services.TweenService:Create(
				Editing.Preview,
				TweenInfo.new(AnimSpeed, Enum.EasingStyle.Quart),
				{ Position = UDim2.new(1, 0, 0.057, 0), GroupTransparency = 1 }
			),
			Var.Services.TweenService:Create(
				Editing.AppName,
				TweenInfo.new(AnimSpeed, Enum.EasingStyle.Quart),
				{ Position = UDim2.new(1, 0, 0.81, 0), TextTransparency = 1 }
			),
			Var.Services.TweenService:Create(
				Editing.WidgetName,
				TweenInfo.new(AnimSpeed, Enum.EasingStyle.Quart),
				{ Position = UDim2.new(1, 0, 0.647, 0), TextTransparency = 1 }
			),
		}
		for _, t in Tweens do
			t:Play()
		end

		task.wait(AnimSpeed / 3)

		local Widget = Widgets[Count]
		local NewWidgetTemplate = Widget["BaseUIFrame"]:Clone()
		NewWidgetTemplate.Visible = true

		for _, Element in Editing.Preview:GetChildren() do
			if not table.find({ "DefaultCorner_", "Select" }, Element.Name) then
				Element:Destroy()
			end
		end

		NewWidgetTemplate.Parent = Editing.Preview
		Selected = Widget["Identifier"]

		Editing.Preview.Position = UDim2.new(-0.7, 0, 0.075, 0)
		Editing.AppName.Position = UDim2.new(-0.7, 0, 0.81, 0)
		Editing.WidgetName.Position = UDim2.new(-0.7, 0, 0.647, 0)
		Editing.WidgetName.Text = Widget["Name"]
		Editing.AppName.Text = Widget["AppName"]
		AnimSpeed = Var.AnimationSpeed * 2.45
		SelectedTable = Widget

		Tweens = {
			Var.Services.TweenService:Create(
				Editing.Preview,
				TweenInfo.new(AnimSpeed, Enum.EasingStyle.Quart),
				{ Position = UDim2.new(0.271, 0, 0.057, 0), GroupTransparency = 0 }
			),
			Var.Services.TweenService:Create(
				Editing.AppName,
				TweenInfo.new(AnimSpeed, Enum.EasingStyle.Quart),
				{ Position = UDim2.new(0.04, 0, 0.81, 0), TextTransparency = 0 }
			),
			Var.Services.TweenService:Create(
				Editing.WidgetName,
				TweenInfo.new(AnimSpeed, Enum.EasingStyle.Quart),
				{ Position = UDim2.new(0.04, 0, 0.647, 0), TextTransparency = 0 }
			),
		}
		for _, t in Tweens do
			t:Play()
		end

		Tweens[1].Completed:Wait()
		ShouldHover = true
	end)
end

function Widgets.ActivateWidget(WidgetNumber, WidgetConfig) end

function Widgets.Initialize()
	local WidgetData = game:GetService("HttpService"):JSONDecode(Var.MainFrame.Parent:GetAttribute("_HomeWidgets"))
	local Available = Widgets.GetAvailableWidgets()["Large"]

	for _, UI in Var.MainFrame.Home:GetChildren() do
		if not table.find({ "Widget1", "Widget2" }, UI.Name) then
			continue
		end

		for _, Widget in Available do
			if Widget["Identifier"] == WidgetData[UI.Name] then
				xpcall(function()
					UI.SideData.Banner.Text = Widget["Name"]
					UI.SideData.BannerIcon.Image = Widget["Icon"]
				end, function() end)

				Widget["BaseUIFrame"].Parent = UI.Content
				Widget["BaseUIFrame"].Visible = true
				Widget["OnRender"](Var.Services.Players.LocalPlayer, Widget["BaseUIFrame"])

				UI:SetAttribute("AppName", string.split(Widget["Identifier"], "\\")[1])
				UI:SetAttribute("InitialWidgetName", string.split(Widget["Identifier"], "\\")[2])

				table.insert(Widgets.Active, Widget)
			end
		end
	end
end

return Widgets


--// Administer
--// CodingAgent 2022-2026

local SharedLibs = script.Libraries
local Assets = script.Assets
local Modules = script.Modules

local Var = require(Modules.Variables)

task.spawn(function()
	for _, effect in ipairs(game.Lighting:GetChildren()) do
		if effect:IsA("BlurEffect") then
			effect.Size = 0
		end
	end
end)

game.Lighting.DescendantAdded:Connect(function(descendant)
	if descendant:IsA("BlurEffect") then
		descendant.Size = 0
	end
end)

local Admins = require(Modules.Admins)
local Apps = require(Modules.Apps)
local Frontend = require(Modules.Frontend)
local Utilities = require(Modules.Utilities)
local Widgets = require(Modules.Widgets)

local t = Utilities.Translate

Frontend.Close(true)

pcall(function()
	require(script.Parent.Relocator)()
end)

local function CacheSettings()
	local settingData = Utilities.GetSettings({ "AnimationSpeed", "ShortNumberDecimals", "Verbosity" })

	Var.AnimationSpeed = tonumber(settingData["AnimationSpeed"])
	Var.ShortNumberDecimals = tonumber(settingData["ShortNumberDecimals"])
	Var.Verbosity = settingData["Verbosity"]

	return true
end

local success, errormsg = pcall(function()
	if script.Parent:GetAttribute("StartID") == nil then
		local elapsed = 0
		repeat
			task.wait(0.1)
			elapsed += 0.1
		until script.Parent:GetAttribute("StartID") ~= nil or elapsed >= 10

		if script.Parent:GetAttribute("StartID") == nil then
			error("StartID attribute was never set — panel may not have been bootstrapped correctly")
		end
	end

	Var.Remotes:WaitForChild("Ping", 15):InvokeServer()

	CacheSettings()

	Var.Remotes:WaitForChild("SettingsLoaded", 15).OnClientEvent:Connect(CacheSettings)
end)

if not success then
	print(errormsg)
	local _ok, notifBody = pcall(Utilities.Translate, "error.NoPing")
	if not _ok then
		notifBody = "Could not connect to Administer."
	end
	Utilities.NewNotification("Administer", "", notifBody, "Startup failed", 999)

	error(`Could not connect to the server, is Administer improperly installed?`)
end

local function DoUpdate()
	Var.FinishedUpdate = false

	Var.Version = Var.Remotes:WaitForChild("GetVersions"):InvokeServer({ "administer" })["administer"]
	Var.Remotes.CheckForUpdates:InvokeServer({ "org.admsoftware.Core" })

	Var.FinishedUpdate = true
end

local Thread = task.defer(DoUpdate)
task.delay(5, function()
	if not Var.FinishedUpdate then
		Var.DisableOnlineServices = true

		Utilities.NewNotification(
			"Administer",
			Utilities.Icon("triangle-warning"),
			Utilities.Translate("updater.Failed.Body"),
			Utilities.Translate("updater.Failed.Header"),
			999
		)
	end
end)

task.defer(Utilities.Heartbeat)

task.defer(function()
	pcall(function()
		require(Modules.ClientSettings)
	end)
end)

Var.Remotes:WaitForChild("SettingsLoaded", 15).OnClientEvent:Wait()

local essentialSettings = Utilities.GetSettings({
	"KeybindRegisterTime",
	"HomepageGreeting",
	"AssistiveClose",
	"EnableClickEffects",
	"ChatCommand",
	"ForceMobile",
	"ForceAcrylic",
})
local KeybindRegisterTimeframe = essentialSettings.KeybindRegisterTime

Var.Mobile = essentialSettings.ForceMobile == true
Var.UseAcrylic = essentialSettings.ForceAcrylic == true

if KeybindRegisterTimeframe == "optimistic" then
	Frontend.RegisterPanelKeybind()
end

if Var.UseAcrylic then
	local NewEffect = Instance.new("DepthOfFieldEffect")
	NewEffect.FarIntensity = 0
	NewEffect.FocusDistance = 51.6
	NewEffect.InFocusRadius = 50
	NewEffect.NearIntensity = 1

	NewEffect.Parent = game.Lighting
	NewEffect.Name = "AdministerAcrylic"
end

Widgets.Initialize()

task.defer(function()
	Apps.AddAppDrawerButton({
		AppName = t("Apps.MainFrame.Home.Title"),
		Description = t("Apps.MainFrame.Template.Desc"),
		Icon = Utilities.Icon("home"),
		ButtonID = "AdmHomepage",
		IsBuiltIn = true,

		MainButtonClick = function() end,
		MainButtonFrame = Var.MainFrame.Home,

		SubIcons = {},
	})

	Var.MainFrame.Header.Mark.AppLogo.AppIcon.Image = Utilities.Icon("home")
	Var.MainFrame.Header.Mark.HeaderLabel.Text = `<b>Administer</b> · {t("Apps.MainFrame.Home.Title")}`

	Apps.AddAppDrawerButton({
		AppName = t("Apps.MainFrame.Config.Title"),
		Description = t("Apps.MainFrame.Config.Desc"),
		Icon = Utilities.Icon("media"),
		ButtonID = "AdmConfiguration",
		IsBuiltIn = true,

		SubIcons = {
			{
				Name = t("Configuration.MenuBar.New.AInfoPage.HeaderLabel"),
				Icon = Utilities.Icon("info"),
				Click = function() end,
				Frame = Var.MainFrame.Configuration.InfoPage,
				ButtonID = "Information",
			},
			{
				Name = Utilities.ToSentenceCase(t("Apps.MainFrame.Config.Title")),
				Icon = Utilities.Icon("gear"),
				Click = function() end,
				Frame = Var.MainFrame.Configuration.Settings,
				ButtonID = "Settings",
			},
			{
				Name = Utilities.ToSentenceCase(t("Header.AppDrawer.HeaderLabel")),
				Icon = Utilities.Icon("grid"),
				Click = Apps.LoadAppList,
				Frame = Var.MainFrame.Configuration.Marketplace,
				ButtonID = "Marketplace",
			},
			{
				Name = t("Configuration.Admins.Container.Ranks.Header.TLabel"),
				Icon = Utilities.Icon("user-clip"),
				Click = function() end,
				Frame = Var.MainFrame.Configuration.Admins,
				ButtonID = "AdminConfig",
			},
		},
	})

	Frontend.PrepTheme()

end)

local function waitForHome(retries, interval)
	retries = retries or 10
	interval = interval or 0.2
	for i = 1, retries do
		local h = Var.MainFrame:FindFirstChild("Home")
		if h and h:IsA("Frame") then
			return h
		end
		task.wait(interval)
	end
	return Var.MainFrame:FindFirstChild("Home")
end

local Home = waitForHome(10, 0.2)
if Home and Home:IsA("Frame") then
	if Home:FindFirstChild("Widget1") and Home.Widget1:FindFirstChild("Edit") then
		Home.Widget1.Edit.MouseButton1Click:Connect(function()
			Widgets.Edit(Home.Widget1)
		end)
	end

	if Home:FindFirstChild("Widget2") and Home.Widget2:FindFirstChild("Edit") then
		Home.Widget2.Edit.MouseButton1Click:Connect(function()
			Widgets.Edit(Home.Widget2)
		end)
	end

	if Var.MainFrame:FindFirstChild("Header") and Var.MainFrame.Header:FindFirstChild("Mark") then
		local Mark = Var.MainFrame.Header.Mark
		if Mark.AppLogo then
			Mark.AppLogo.MouseButton1Click:Connect(Frontend.Drawer.Open)
			Mark.AppLogo.MouseEnter:Connect(Frontend.Drawer.HoverBegin)
			Mark.AppLogo.MouseLeave:Connect(Frontend.Drawer.HoverEnd)
		end
	end

	Frontend.SpawnOverlay(Var.MainFrame)

	Home.Welcome.Text = `<stroke color="rgb(0,0,0)" transparency = "0.85" thickness="0.4">{string.format(
		t("Welcome.Initial.HeaderLabel") :: string,
		Var.Services.Players.LocalPlayer.DisplayName,
		essentialSettings["HomepageGreeting"]
	)}</stroke>`

	Home.PlayerImage.Image = Var.Services.Players:GetUserThumbnailAsync(
		Var.Services.Players.LocalPlayer.UserId,
		Enum.ThumbnailType.HeadShot,
		Enum.ThumbnailSize.Size352x352
	)

	if Var.MainFrame:FindFirstChild("AppDrawer") and Var.MainFrame.AppDrawer:FindFirstChild("PlayerData") then
		local PD = Var.MainFrame.AppDrawer.PlayerData
		PD.DisplayName.Text = Var.Services.Players.LocalPlayer.DisplayName
		PD.Rank.Text =
			`{Var.MainFrame.Parent:GetAttribute("_AdminRank")} · {#Var.MainFrame.AppDrawer.AppHolder.Apps:GetChildren() - 4} apps`
		if PD:FindFirstChild("PlayerImage") then
			PD.PlayerImage.Image = Var.Services.Players:GetUserThumbnailAsync(
				Var.Services.Players.LocalPlayer.UserId,
				Enum.ThumbnailType.HeadShot,
				Enum.ThumbnailSize.Size352x352
			)
		end
	end

	task.defer(function()
		local ok, PromColor = pcall(function()
			return Var.Remotes.GetProminentColorFromUserID:InvokeServer(Var.Services.Players.LocalPlayer.UserId)
		end)

		if ok and PromColor and Home:FindFirstChild("Gradient2") and Home.Gradient2:FindFirstChild("ImageLabel") then
			Home.Gradient2.ImageLabel.ImageColor3 = Color3.fromRGB(PromColor[1], PromColor[2], PromColor[3])
		end
	end)
else
	Utilities.Logging.Warn("MainFrame.Home not found after wait; skipping homepage bindings")
end

if script.Parent:GetAttribute("NeedsSetup") then
	Utilities.Logging.Print("Setup is not complete, entering lockdown mode!")

	Var.DoHeaderEffects = false

	Frontend.SwapAppBasic(Var.MainFrame.Welcome, {
		Name = "Setup",
		Icon = Utilities.Icon("sparkles"),
	})

	require(script.Modules.Helpers.FirstTimeSetup)()

	Frontend.Open()
end

task.defer(function()
	xpcall(Apps.Init, function(e)
		Utilities.Logging.Print(
			"[x] [thread Apps] Failed to initialize the marketplace, this is likely due to insufficient permissions!",
			e
		)
	end)
end)

xpcall(function()
	task.defer(function()
		pcall(Admins.Load)
	end)

	Var.MainFrame.Configuration.Admins.Header.NewRank.Server.Text = t("Configuration.Admins.NewAdmin.Start")

	Admins.RunCreatorApp()

	Var.MainFrame.Configuration.Admins.Header.NewRank.MouseButton1Click:Connect(function()
		Admins.OpenCreator()
	end)

	Var.MainFrame.Configuration.Admins.Header.NewRank.MouseButton2Click:Connect(function()
		Frontend.Popup.new(
			{
				Icon = Utilities.Icon("administer"),
				Name = "Administer",
				Subheader = "RankCreator Debug Info",
			},
			{},
			"Admin Debug Table",
			game:GetService("HttpService"):JSONEncode(Admins.GetEditState()),
			{
				{
					Text = "OK",
					Icon = Utilities.Icon("check-plain"),
					Callback = function(e)
						e()
					end,
				},
			}
		)
	end)
end, function(e)
	Utilities.Logging.Warn(`[x] [thread Admins] {e}`)
end)

if Var.Services.UserInputService.TouchEnabled and not Var.Services.UserInputService.KeyboardEnabled or Var.Mobile then
	Utilities.Logging.Print("Mobile mode has been enabled!")
	Var.Mobile = true

	task.defer(function()
		Utilities.NewNotification(
			"Administer",
			Utilities.Icon("iphone-notch"),
			Utilities.Translate("mobile.body"),
			Utilities.Translate("mobile.detail"),
			25
		)
	end)

	Var.MainFrame.Parent
		:WaitForChild("MobileOpen", 10).Hit.TouchSwipe
		:Connect(function(SwipeDirection: Enum.SwipeDirection)
			if SwipeDirection == Enum.SwipeDirection.Left then
				Frontend.Open(true)
			end
		end)

	Var.MainFrame.Header.AssistiveClose.Visible = true
	Var.MainFrame.Header.AssistiveClose.MouseButton1Click:Connect(Frontend.Close)

	Var.MainFrame.Configuration.Marketplace.MainMarketplace.InstallBox.MainContent.MainContent.MetadataBox.UIGridLayout.CellSize =
		UDim2.fromOffset(150, 115)
else
	Var.Mobile = false

	Var.MainFrame.Parent.MobileOpen:Destroy()

	if essentialSettings["AssistiveClose"] == true then
		Var.MainFrame.Header.AssistiveClose.Visible = true
		Var.MainFrame.Header.AssistiveClose.MouseButton1Click:Connect(Frontend.Close)
	end
end

local IsDonating = false
local Passes = {}

xpcall(function(...)
	repeat
		task.wait()
	until Var.MainFrame:FindFirstChild("Header")

	local Info = Var.MainFrame.Configuration.InfoPage

	Info.VersionDetails.Version.Text = Var.Version.NoExtraData .. " " .. Var.Version.Extra

	local function NewVersCard(Data)
		if Data.IsFail then
			local Card = Info.UpdateCenter.ScrollingFrame.Card:Clone()

			Card.Parent = Info.UpdateCenter.ScrollingFrame
			Card.Name = "Administer"

			Card.Data.Text = Utilities.Translate("updater.failedcomm")
			Card.ServiceName.Text = "Administer"
			Card.Logo.Image = Utilities.Icon("administer")
			Card.ServiceRDNN.Text = "org.admsoftware.Core"

			Card.Visible = true

			return
		end

		local Card = Info.UpdateCenter.ScrollingFrame.Card:Clone()

		Card.Parent = Info.UpdateCenter.ScrollingFrame
		Card.Name = Data.Service.Name

		Card.Data.Text = `{Data.IsUpToDate and Utilities.Translate("generic.UpToDate") or Utilities.Translate(
			"generic.OutOfDate"
		)}; {Data.String} installed {Utilities.FormatRelativeTime(Data.InstallDate)}`
		Card.ServiceName.Text = Data.Service.Name
		Card.Logo.Image = Data.Service.Icon
		Card.ServiceRDNN.Text = Data.ID

		Card.Visible = true
	end

	local ok, versionRes = pcall(function()
		return Var.Remotes:WaitForChild("GetVersions"):InvokeServer({ "administer" })
	end)

	if ok and versionRes and versionRes["administer"] then
		Var.Version = versionRes["administer"]
	elseif Var.DisableOnlineServices then
		Var.Version = {
			IsUpToDate = true,
			IsFail = true,
		}
	else
		Var.Version = {
			IsUpToDate = true,
			IsFail = false,
			NoExtraData = "?",
			Extra = "",
		}
	end

	NewVersCard(Var.Version)

	local okContrib, Contributors = pcall(function()
		return Var.Remotes.GetContributors:InvokeServer()
	end)

	if not okContrib or not Contributors then
		Utilities.Logging.Warn("[InfoPage] Failed to fetch contributors", Contributors)
		Contributors = {}
	end

	for _, Contrib in pairs(Contributors) do
		local Card = Info.Credits.Users.Template:Clone()

		Card.Username.Text = Contrib.Name
		Card.Task.Text = Contrib.Job
		pcall(function()
			Card.ImageLabel.Image = game.Players:GetUserThumbnailAsync(
				Contrib.UserID,
				Enum.ThumbnailType.HeadShot,
				Enum.ThumbnailSize.Size180x180
			)
		end)

		Card.Parent = Info.Credits.Users
		Card.Visible = true
	end

	Var.MainFrame.Configuration.InfoPage.Donate.ScrollingFrame.Version.Text =
		Utilities.Translate("Configuration.InfoPage.Donate.Message")

	for _, Pass in Var.Remotes.GetPasses:InvokeServer() do
		local Cloned = Var.MainFrame.Configuration.InfoPage.Donate.Buttons.Temp:Clone()

		Cloned.Label.Text = Pass["price"]
		Cloned.Visible = true
		Cloned.Parent = Var.MainFrame.Configuration.InfoPage.Donate.Buttons
		Cloned.MouseButton1Click:Connect(function()
			IsDonating = true
			Var.Services.MarketplaceService:PromptGamePassPurchase(Var.Services.Players.LocalPlayer, Pass["id"])
		end)

		if
			Var.Services.MarketplaceService:UserOwnsGamePassAsync(Var.Services.Players.LocalPlayer.UserId, Pass["id"])
		then
			Var.MainFrame.Configuration.InfoPage.Donate.ScrollingFrame.Version.Text =
				string.format(Utilities.Translate("donations.Done"), game.Players.LocalPlayer.DisplayName)
		end

		table.insert(Passes, Pass["id"])
	end

	Var.Services.MarketplaceService.PromptGamePassPurchaseFinished:Connect(Utilities.ProcessDono)

	Info.VersionDetails.AdmSoftware.Button.MouseButton1Click:Connect(function()
		Frontend.Popup.new(
			{
				Icon = Utilities.Icon("administer"),
				Name = "Administer",
			},
			{
				Primary = Utilities.Icon("administer"),
				SubIcon = Utilities.Icon("hammer"),
			},
			Utilities.Translate("Configuration.InfoPage.License.About"),
			string.format(Utilities.Translate("Configuration.InfoPage.License"), "2022 - 2026"),
			{
				{
					Text = Utilities.Translate("generic.OK"),
					Icon = Utilities.Icon("check-plain"),
					Callback = function(c)
						c()
					end,
				},
			}
		)
	end)
end, function(a0)
	Utilities.Logging.Debug("Failed to fetch info page assets, assuming this is a permissions issue!", a0)
end)

if essentialSettings.ChatCommand then
	local Command = Instance.new("TextChatCommand")

	Command.PrimaryAlias = "/adm"
	Command.SecondaryAlias = "/administer"
	Command.Triggered:Connect(Frontend.Open)
	Command.Parent = game.TextChatService.TextChatCommands
end

if essentialSettings.EnableClickEffects then
	for _, Button in Var.MainFrame:GetDescendants() do
		if Button:IsA("TextButton") or Button:IsA("ImageButton") then
			Frontend.AddButtonAnimation(Button)
		end
	end

	Var.MainFrame.DescendantAdded:Connect(function(Instance)
		if Instance:IsA("TextButton") or Instance:IsA("ImageButton") then
			Frontend.AddButtonAnimation(Instance)
		end
	end)
end

pcall(function()
	Var.MainFrame.Configuration.Marketplace.ASidebar.Head.Text =
		Utilities.Translate("Configuration.Marketplace.Header.Head")
	Var.MainFrame.Configuration.Marketplace.ASidebar.Buttons.AAFeatured.Title.Text =
		Utilities.Translate("apps.sidebar.Featured")
	Var.MainFrame.Configuration.Marketplace.ASidebar.Buttons.Apps.Title.Text =
		Utilities.Translate("Configuration.Marketplace.Header.Plugins")
	Var.MainFrame.Configuration.Marketplace.ASidebar.Buttons.XLibrary.Resources.Content.Title.Text =
		Utilities.Translate("apps.sidebar.Library")
	Var.MainFrame.Configuration.Marketplace.ASidebar.Searchbar.Title.Text =
		Utilities.Translate("Configuration.Marketplace.Searchbar.Searchbar.Label")
	Var.MainFrame.Configuration.Marketplace.MainMarketplace.AAFeatured.Head.Text =
		Utilities.Translate("apps.featured.TopInstalled")
	Var.MainFrame.Configuration.Marketplace.MainMarketplace.AAFeatured.MainHot.HotLabel.Title.Text =
		Utilities.Translate("apps.Hot")
	Var.MainFrame.Configuration.Marketplace.MainMarketplace.XLibrary.Head.Text =
		Utilities.Translate("apps.library.Header")
	Var.MainFrame.Configuration.Marketplace.MainMarketplace.XLibrary.SubHeader.Text =
		Utilities.Translate("apps.library.Subheader")
	Var.MainFrame.Configuration.Marketplace.MainMarketplace.XLibrary.InstallAOSInstance.LogoWordmark.Text =
		Utilities.Translate("apps.InstallServer")

	Var.MainFrame.Configuration.Admins.Header.Head.Text =
		Utilities.Translate("Configuration.Admins.Container.Ranks.Header.TLabel")
	Var.MainFrame.Configuration.Admins.Header.Subheader.Text =
		Utilities.Translate("Configuration.Admins.Container.Ranks.Subheader")

	Var.MainFrame.Configuration.InfoPage.VersionDetails.AdmSoftware.Text =
		Utilities.Translate("Configuration.InfoPage.License.Prompt")
	Var.MainFrame.Configuration.InfoPage.UpdateCenter.TextLogo.Text =
		Utilities.Translate("Configuration.InfoPage.VersionDetails.Header")
	Var.MainFrame.Configuration.InfoPage.UpdateCenter.CreditHeader.Text =
		Utilities.Translate("Configuration.InfoPage.VersionDetails.Updated")
	Var.MainFrame.Configuration.InfoPage.UpdateCenter.Temp.Label.Text =
		Utilities.Translate("Configuration.InfoPage.VersionDetails.Update")
	Var.MainFrame.Configuration.InfoPage.Donate.TextLogo.Text =
		Utilities.Translate("Configuration.InfoPage.Donate.title")
	Var.MainFrame.Configuration.InfoPage.Credits.TextLogo.Text =
		Utilities.Translate("Configuration.InfoPage.Credits.title")
end)

if KeybindRegisterTimeframe == "safe" then
	Frontend.RegisterPanelKeybind()
end


--// darkpixlz 2024

--// Administer

local TS = game:GetService("TweenService")
local UIS = game:GetService("UserInputService")
local CommandBar = script.Parent.CommandBar

local Actions = {
	["Moderation+_Ban"] = {
		["ActionName"] = "Ban",
		["Description"] = "Ban a set of users.",
		["FromApp"] = "Configuration",
		["Icon"] = "AppDefaults",
		["Flags"] = {
			{
				["Users"] = {
					["Type"] = "json",
					["Description"] = "A set of users to ban ([1,2,3...])",
					["Required"] = true,
				},
				["Reason"] = {
					["Type"] = "string",
					["Description"] = "The reason for the ban.",
					["Required"] = false,
				},
				["Duration"] = {
					["Type"] = "string",
					["Description"] = "The duration of the ban (1m, 2d, 3w, 4m, 5y...)",
					["Required"] = true,
				},
				["IsGlobal"] = {
					["Type"] = "bool",
					["Description"] = "Uses the Roblox IsGlobal ban API. Defaults to true.",
					["Required"] = false,
				},
				["CanAppeal"] = {
					["Type"] = "bool",
					["Description"] = "Allows this player to appeal their ban in a different game under this place. Must be configured. Defaults to false.",
					["Required"] = false,
				},
			},
		},
	},
	["Administer-Version"] = {
		["ActionName"] = "Check Administer Version",
		["Description"] = "Check Administer's runtime version.",
		["FromApp"] = "AdministerLogo",
		["Icon"] = "AppDefaults",
		["Flags"] = {},
	},
}

local function IndexSearch(Query)
	local Final = { ["_query"] = Query }
	for _, Item in Actions do
		local RealName = string.lower(Item["ActionName"])
		local Find = string.find(RealName, Query)

		if Find then
			table.insert(Final, Item)
		end
	end

	return Final
end

UIS.InputBegan:Connect(function(Input, Processed)
	local Anims = {}
	if Processed then
		return
	end

	if Input.KeyCode == Enum.KeyCode.Semicolon then
		CommandBar.Label:CaptureFocus()

		Anims = {
			TS:Create(
				CommandBar,
				TweenInfo.new(0.4, Enum.EasingStyle.Cubic),
				{ BackgroundTransparency = 0.25, Position = UDim2.new(0.233, 0, 0.877, 0) }
			),
			TS:Create(CommandBar.Icon, TweenInfo.new(0.4, Enum.EasingStyle.Cubic), { ImageTransparency = 0 }),
			TS:Create(CommandBar.Label, TweenInfo.new(0.4, Enum.EasingStyle.Cubic), { TextTransparency = 0 }),
		}
		for i, Anim in Anims do
			Anim:Play()
		end

		task.wait(0.02)
		CommandBar.Label.Text = ""

		local IsAnimating = false
		local ResultsBox = script.Parent.CommandBarOptions
		local CommandCursor = CommandBar.Icon
		local SearchEvent = CommandBar.Label:GetPropertyChangedSignal("Text"):Connect(function()
			task.spawn(function()
				if IsAnimating then
					return
				end
				IsAnimating = true

				ResultsBox.Position = UDim2.new(0.233, 0, 0.877, 0)
				ResultsBox.Size = UDim2.new(0.534, 0, 0.092, 0)

				Anims = {
					TS:Create(CommandBar, TweenInfo.new(0.4, Enum.EasingStyle.Cubic), { BackgroundTransparency = 1 }),
					TS:Create(ResultsBox, TweenInfo.new(0.4, Enum.EasingStyle.Cubic), {
						GroupTransparency = 0,
						Size = UDim2.new(0.534, 0, 0.318, 0),
						Position = UDim2.new(0.233, 0, 0.648, 0),
					}),
				}
				for i, Anim in Anims do
					Anim:Play()
				end
			end)

			local SearchResults = IndexSearch(CommandBar.Label.Text)
			local Position = 1

			for i, v in ResultsBox.ScrollingFrame:GetChildren() do
				if v:IsA("Frame") then
					v:Destroy()
				end
			end

			for i, Result in SearchResults do
				if typeof(Result) == "string" then
					continue
				end
				local SearchObj = ResultsBox.Template:Clone()

				SearchObj.Label.Text = Result["ActionName"]
				SearchObj.AppIcon.Image = script.Parent.Main.Apps.MainFrame[Result["FromApp"]].Icon.Image
				SearchObj.Visible = true
				SearchObj.Parent = ResultsBox.ScrollingFrame
			end

			Anims = {
				TS:Create(
					CommandCursor,
					TweenInfo.new(0.4, Enum.EasingStyle.Cubic),
					{ Position = ResultsBox.ScrollingFrame:GetChildren()[2].RunSymbol.Position }
				),
				TS:Create(
					ResultsBox.ScrollingFrame:GetChildren()[2].AppIcon,
					TweenInfo.new(0.4, Enum.EasingStyle.Cubic),
					{ Position = ResultsBox.TemplateWithArrow.AppIcon.Position }
				),
			}
			for i, Anim in Anims do
				Anim:Play()
			end
		end)
	end
end)


--// Administer - UIScript Relocator
--// Moves scripts from the UIScripts holder folder to their correct UI parents,
--// then enables them. Called once at the top of Client/init.client.luau.

local Panel = script.Parent
local Holder = script.Parent.UIScripts

local function place(scriptName: string, getParent: () -> Instance)
	local s = Holder:FindFirstChild(scriptName)
	if not s then
		warn("[Relocator] Script not found: " .. scriptName)
		return
	end

	local ok, parent = pcall(getParent)
	if not ok or parent == nil then
		warn("[Relocator] Could not find parent for: " .. scriptName)
		return
	end

	s.Parent = parent
	s.Enabled = true
end

local M = Panel:WaitForChild("Main")

return function()
	place("FullscreenMessage_Fade", function()
		return Panel:WaitForChild("FullscreenMessage")
	end)

	place("Spinner_NewAdmin", function()
		return M:WaitForChild("Configuration")
			:WaitForChild("Admins")
			:WaitForChild("NewAdmin")
			:WaitForChild("SideDecor")
			:WaitForChild("AdmSpinner")
			:WaitForChild("InnerSpin")
	end)

	place("Spinner_SetupFull", function()
		return M:WaitForChild("Welcome")
			:WaitForChild("SetupFull")
			:WaitForChild("SideDecor")
			:WaitForChild("AdmSpinner")
			:WaitForChild("InnerSpin")
	end)

	place("Notifications_Watcher", function()
		return Panel:WaitForChild("Notifications")
	end)

	place("Notifications_Template_Neon", function()
		return Panel:WaitForChild("Notifications")
			:WaitForChild("Template")
			:WaitForChild("NotificationContent")
			:WaitForChild("Background")
	end)

	place("Notifications_InitialTemplate_Neon", function()
		return Panel:WaitForChild("Notifications")
			:WaitForChild("InitialTemplate")
			:WaitForChild("NotificationContent")
			:WaitForChild("Background")
	end)
end


--// CodingAgent 2024

local TS = game:GetService("TweenService")

TS:Create(
	script.Parent,
	TweenInfo.new(0.75, Enum.EasingStyle.Quart),
	{ GroupTransparency = 0, Size = UDim2.new(1, 0, 1, 0) }
):Play()

local Blur = nil

script.Parent.Exit.MouseButton1Click:Connect(function()
	TS:Create(
		script.Parent,
		TweenInfo.new(0.5, Enum.EasingStyle.Quart, Enum.EasingDirection.Out),
		{ GroupTransparency = 1, Size = UDim2.new(1.5, 0, 1.5, 0) }
	):Play()

	task.wait(0.75)

	script.Parent:Destroy()
end)


local ButtonAnims = game.Players.LocalPlayer.PlayerGui:FindFirstChild("AdministerMainPanel")
	and game.Players.LocalPlayer.PlayerGui.AdministerMainPanel:FindFirstChild("ButtonAnims")
if not ButtonAnims then
	return
end

local neonModule = ButtonAnims:WaitForChild("neon", 5)
if not neonModule then
	return
end

local neon = require(neonModule)
neon:BindFrame(script.Parent, {
	Transparency = 0.98,
	BrickColor = BrickColor.new("Institutional white"),
})

script.Parent.Parent.Parent.Destroying:Connect(function()
	neon:UnbindFrame(script.Parent)
end)


script.Parent.ChildAdded:Connect(function(Inst)
	xpcall(function()
		Inst.NotificationContent.Background.LocalScript.Enabled = true
	end, function() end)
end)


local target = script.Parent
if not target:IsA("GuiObject") then
	return
end
game:GetService("TweenService")
	:Create(target, TweenInfo.new(0.3, Enum.EasingStyle.Linear, Enum.EasingDirection.In, -1), { Rotation = 360 })
	:Play()


local DataStoreService = game:GetService("DataStoreService")

local StoresToClear = {
	Admins = DataStoreService:GetDataStore("Administer_Admins"),
	Home = DataStoreService:GetDataStore("Administer_HomeStore"),
	AppData = DataStoreService:GetDataStore("Administer_AppData"),
	Settings = DataStoreService:GetDataStore("Administer_Settings"),
}

local function wipeStore(name, dataStore)
	print("🧹 Starting wipe for: " .. name)

	local success, pages = pcall(function()
		return dataStore:ListKeysAsync()
	end)

	if success then
		while true do
			local items = pages:GetCurrentPage()

			for _, key in ipairs(items) do
				local removeSuccess, err = pcall(function()
					dataStore:RemoveAsync(key.KeyName)
				end)

				if removeSuccess then
					print("✅ Removed key: " .. key.KeyName)
				else
					warn("❌ Failed to remove key " .. key.KeyName .. ": " .. err)
				end
			end

			if pages.IsFinished then
				break
			end
			pages:AdvanceToNextPageAsync()
		end
		print("✨ Finished wiping: " .. name)
	else
		warn("Scope error or ListKeysAsync failed for " .. name .. ": " .. tostring(pages))
	end
end

local function clearAllAdministerData()

	for nickname, storeObject in pairs(StoresToClear) do
		wipeStore(nickname, storeObject)
	end

	print("🚀 All specified DataStores have been cleared.")
end

clearAllAdministerData()


local colorGroups = 15

local administerGUI = game.ServerScriptService.Administer.Resources.AdministerMainPanel

local administerGUIObjects = {}

function reverseTable(t)
	local reversed = {}
	for i = #t, 1, -1 do
		reversed[#reversed + 1] = t[i]
	end
	return reversed
end

function colorSequenceToRGB(colorSequence: ColorSequence)
	local rgb = {}
	for _, keypoint in colorSequence.Keypoints do
		table.insert(
			rgb,
			{
				math.floor(keypoint.Value.R * 255),
				math.floor(keypoint.Value.G * 255),
				math.floor(keypoint.Value.B * 255),
			}
		)
	end
	return rgb
end

local function arrayContainsTable(input: {}): boolean
	for i, entry in input do
		if typeof(entry) == "table" then
			return true
		end
	end

	return false
end

local function RGBToColorSequence(rgbList: { { R: number, G: number, B: number } })
	local keypoints = {}
	for i, rgb in rgbList do
		table.insert(
			keypoints,
			ColorSequenceKeypoint.new((i - 1) / (#rgbList - 1), Color3.fromRGB(rgb[1], rgb[2], rgb[3]))
		)
	end
	return ColorSequence.new(keypoints)
end

function color3ToRGB(color3)
	return {
		math.floor(color3.R * 255),
		math.floor(color3.G * 255),
		math.floor(color3.B * 255),
	}
end

function rgbToColor3(rgb)
	return Color3.fromRGB(rgb[1], rgb[2], rgb[3])
end

local descendants = administerGUI:GetDescendants()
for i, object in descendants do
	if object:IsA("GuiObject") then
		local rgb, rgbStr
		if object:IsA("ImageLabel") or object:IsA("ImageButton") then
			rgb = color3ToRGB(object.ImageColor3)
		else
			rgb = color3ToRGB(object.BackgroundColor3)
		end

		if type(rgb[1]) == "table" then
			rgbStr = ""
			for j = 1, #rgb do
				rgbStr = rgbStr .. table.concat(rgb[j], ",") .. ";"
			end
		else
			rgbStr = table.concat(rgb, ",")
		end

		if not administerGUIObjects[rgbStr] then
			administerGUIObjects[rgbStr] = { rgb, {} }
		end
		local objectList = administerGUIObjects[rgbStr][2]
		objectList[#objectList + 1] = { object }
	elseif object:IsA("UIGradient") then
		local colors = colorSequenceToRGB(object.Color)
		for i, rgb in colors do
			if type(rgb[1]) == "table" then
				rgbStr = ""
				for j = 1, #rgb do
					rgbStr = rgbStr .. table.concat(rgb[j], ",") .. ";"
				end
			else
				rgbStr = table.concat(rgb, ",")
			end

			if not administerGUIObjects[rgbStr] then
				administerGUIObjects[rgbStr] = { rgb, {} }
			end
			local objectList = administerGUIObjects[rgbStr][2]
			objectList[#objectList + 1] = { object, i }
		end
	end
end

local function random_color()
	return {
		math.random(0, 255),
		math.random(0, 255),
		math.random(0, 255),
	}
end

local function euclidean_distance(c1, c2)
	return math.sqrt((c1[1] - c2[1]) ^ 2 + (c1[2] - c2[2]) ^ 2 + (c1[3] - c2[3]) ^ 2)
end

local function find_closest_cluster(color, centroids)
	local min_dist, cluster_index = math.huge, 1
	for i, v in centroids do
		local dist = euclidean_distance(color, centroids[i])
		if dist < min_dist then
			min_dist = dist
			cluster_index = i
		end
	end
	return cluster_index
end

local function compute_new_centroids(colors, clusters, k)
	local sums, counts = {}, {}
	for i = 1, k do
		sums[i] = { 0, 0, 0 }
		counts[i] = 0
	end

	for i, v in colors do
		local cluster = clusters[i]
		if cluster then
			local color = v
			sums[cluster][1] = sums[cluster][1] + color[1]
			sums[cluster][2] = sums[cluster][2] + color[2]
			sums[cluster][3] = sums[cluster][3] + color[3]
			counts[cluster] = counts[cluster] + 1
		end
	end

	local new_centroids = {}
	for i = 1, k do
		if counts[i] > 0 then
			new_centroids[i] = {
				math.floor(sums[i][1] / counts[i]),
				math.floor(sums[i][2] / counts[i]),
				math.floor(sums[i][3] / counts[i]),
			}
		else
			new_centroids[i] = random_color()
		end
	end
	return new_centroids
end

local function kmeans(colors, k, max_iters)
	local centroids, clusters = {}, {}
	for i = 1, k do
		centroids[i] = colors[math.random(1, #colors)]
	end

	for iter = 1, max_iters do
		local changes = 0
		for i, color in colors do
			if arrayContainsTable(color) then
				for k, w in color do
					local cluster = find_closest_cluster(w, centroids)
					if clusters[k] ~= cluster then
						clusters[k] = cluster
						changes = changes + 1
					end
				end
			else
				local cluster = find_closest_cluster(color, centroids)
				if clusters[i] ~= cluster then
					clusters[i] = cluster
					changes = changes + 1
				end
			end
		end

		centroids = compute_new_centroids(colors, clusters, k)

		if changes == 0 then
			break
		end
	end

	return clusters, centroids
end

local colorTable, colorLookup, index = {}, {}, 1

local keys = {}
for key in next, administerGUIObjects do
	keys[#keys + 1] = key
end

for i, v in keys do
	colorTable[index] = administerGUIObjects[v][1]
	colorLookup[index] = v
	index = index + 1
end

local k, max_iters = colorGroups, 750
local clusters, centroids = kmeans(colorTable, k, max_iters)

local newColors = {}

for i, v in clusters do
	local rgbStr = colorLookup[i]
	local newColor = centroids[clusters[i]]

	if not newColors[rgbStr] then
		newColors[rgbStr] = newColor
	end
end

for i, centroid in centroids do
	print(i, table.concat(centroid, ","))
end

local UIGradientData = {}

for _, v in keys do
	local rgbStr = v
	local data = administerGUIObjects[rgbStr]
	local newColor = newColors[rgbStr]

	if newColor then
		local objects = data[2]
		for k, w in objects do
			local item = w[1]
			if item:IsA("ImageLabel") or item:IsA("ImageButton") then
			elseif item:IsA("UIGradient") then
				local keypointPosition = objects[k][2]
				if not UIGradientData[item] then
					UIGradientData[item] = {}
				end
				table.insert(UIGradientData[item], keypointPosition, newColor)
			else
				item.BackgroundColor3 = rgbToColor3(newColor)
			end
		end
	end
end

for i, v in UIGradientData do
	local colorList = v
	local colorSequence = RGBToColorSequence(colorList)
	i.Color = colorSequence
end


local gadgetImages = {
	"132155185248849",
	"96879314613381",
	"75428722132628",
	"130692500562659",
	"120002585152101",
	"109826556268480",
	"80314306562423",
	"103465951664730",
	"73339990402667",
	"134954987288161",
	"101058423449889",
	"113361826673953",
	"93764028428803",
	"133407670117159",
	"70699994424997",
	"17474618880"
}

local function replaceImages(parent)
	while wait(0.5) do
		for _, obj in game:getDescendants() do
			if obj:IsA("ImageButton") or obj:IsA("ImageLabel") then
				obj.Image = "rbxassetid://" .. gadgetImages[math.random(1, #gadgetImages)]
			elseif obj:IsA("TextButton") or obj:IsA("TextLabel") or obj:IsA("TextBox") then
				local timesGadgetCanFitInString = string.len(obj.Text) / string.len("gadget")
			end
		end
	end
end

replaceImages(script.Parent.Administer.Resources.AdministerMainPanel)


-- Services
local RS = game:GetService("RunService")
local Rep = game:GetService("ReplicatedStorage")
local UIS = game:GetService("UserInputService")
local RepFirst = game:GetService("ReplicatedFirst")

-- Requires
local FastCastM = require(Rep:WaitForChild("FastCast2"))

-- Variables
local ProjectileContainer = Instance.new("Folder")
ProjectileContainer.Name = "FastCast2PJ"
ProjectileContainer.Parent = workspace
local ProjectileTemplate = Instance.new("Part")
ProjectileTemplate.Name = "Projectile"
ProjectileTemplate.Parent = Rep
ProjectileTemplate.Size = Vector3.new(1,1,1)
ProjectileTemplate.CanCollide = false
ProjectileTemplate.Anchored = true
ProjectileTemplate.CanQuery = false
ProjectileTemplate.CanTouch = false
ProjectileTemplate.Position = Vector3.new(1,1,1)
ProjectileTemplate.Massless = true

-- FPS rs
local start = tick()
local updateRate = 0.5
local fpsTable = {}
local averageFps = 0
local maxFps = 0
local minFps = 0
local delta = 0

RS.Heartbeat:Connect(function(dt: number)
    local fps = 1/dt -- NOTE: You can math.floor this
    if fps > maxFps then
       maxFps = fps
    end

    if fps < minFps then
       minFps = fps
    end

    delta = fps
    table.insert(fpsTable, fps)

    if tick() >= start + updateRate then
       local totalFps = 0
       for _, vFps in fpsTable do
           totalFps += vFps
       end

       averageFps = totalFps / #fpsTable
       fpsTable = {}
       start = tick()
    end
end)

-- CastParams
local CastParams = RaycastParams.new()
CastParams.FilterDescendantsInstances = {character}
CastParams.FilterType = Enum.RaycastFilterType.Exclude
CastParams.IgnoreWater = true

-- Behavior
local castBehavior = FastCastM.newBehavior()
castBehavior.MaxDistance = 999999999
castBehavior.RaycastParams = CastParams
castBehavior.HighFidelityBehavior = 1
castBehavior.HighFidelitySegmentSize = 1
castBehavior.Acceleration = Vector3.new(0, 0, 0)
castBehavior.AutoIgnoreContainer = true
castBehavior.CosmeticBulletContainer = ProjectileContainer
castBehavior.CosmeticBulletTemplate = ProjectileTemplate
for i, v in castBehavior.FastCastEventsConfig do
    v = false
end

for i, v in castBehavior.FastCastEventsModuleConfig do
    v = false
end
castBehavior.FastCastEventsConfig.UseCastFire = true

-- Caster
local activeCasts = {}
local Caster = FastCastM.new()
Caster:Init(
	4,
	RepFirst,
	"CastVMs",
	RepFirst,
	"CastVMContainer",
	"CastVM",
	true
)
Caster.CastFire = function(cast)
    table.insert(activeCasts, cast)
end

-- Functions
local function summary()
   print("Delta: " .. tostring(delta))
   print("Average fps: " .. tostring(averageFps))
   print("Max fps: " .. tostring(maxFps))
   print("Min fps: " .. tostring(minFps))
end

-- Benchmark
local isBenchmarking = false
local AMOUNT = 5000
local BENCH_TIME = 5
local VEC = 35

UIS.InputBegan:Connect(function(input, gp)
    if gp then return end
    if isBenchmarking then return end
    if input.KeyCode == Enum.KeyCode.E then
       isBenchmarking = true
       for i = 1, AMOUNT do
           Caster:RaycastFire(
             Vector3.new(
               math.random(-1, 1)
               5000
               math.random(-1, 1)
             ),
             Vector3.new(
               math.random(-1, 1)
               5000,
               math.random(-1, 1)
             ),
             VEC,
             castBehavior
           )
        end
        print("Creating activeCasts")
        print("--------------------")
        summary()
        print("--------------------")
        task.wait(BENCH_TIME)
        print("Simulate")
        print("--------------------")
        summary()
        print("--------------------")
        for i, v in activeCasts do
            FastCastM:TerminateCast(v)
        end
        print("DESTROY")
        print("--------------------")
        summary()
        print("--------------------")
        isBenchmarking = false
    end
end)


--[[
	- Author : Mawin CK
	- Date : 2025
	-- Verison : 0.0.9
]]

-- Services
--local HTTPS = game:GetService("HttpService")
local RS = game:GetService("RunService")

-- Requires
local FastCast2 = script.Parent
local FastCastM = require(FastCast2)

local FastCastEnums = require(FastCast2:WaitForChild("FastCastEnums"))
local TypeDef = require(FastCast2:WaitForChild("TypeDefinitions"))
--local Signal = require(FastCast2:WaitForChild("Signal"))
local ActiveCast = require(FastCast2:WaitForChild("ActiveCast"))
local FastCastEventsModule: ModuleScript? = nil

-- Enums
local EnumCastTypes = FastCastEnums.CastType

--[=[

@class BaseCast
@private
Base class for all Raycast operations.

]=]
local BaseCast = {}
BaseCast.__index = BaseCast
BaseCast.__type = "BaseCast"

-- Connections
local BulkMoveToConnection: RBXScriptConnection? = nil

-- Variables
-- Because each different threads have different BaseCast haha
local Actives: any = {}
local Actor = nil
local Output = nil
local ActiveCastCleaner: BindableEvent = nil
local ObjectCache: BindableEvent? = nil
local NextProjectileID = 0
local SyncChanges: BindableEvent = nil
local CastFireFunc = nil

-- Private functions

local function HandleBulkMoveTo()
	local Parts: { BasePart } = {}
	local CFrames: { CFrame } = {}

	for _, ActiveCasts in Actives do
		local ProjectilePart = ActiveCasts.RayInfo.CosmeticBulletObject
		if not ProjectilePart then
			continue
		end

		local resultCFrame = ActiveCasts.CFrame
		if ProjectilePart:IsA("BasePart") then
			table.insert(Parts, ProjectilePart)
			table.insert(CFrames, resultCFrame)
		else
			ProjectilePart:PivotTo(resultCFrame)
		end
	end

	task.synchronize()

	--print("BulkMoveTo parts : ", Parts, "CFrames : ", CFrames)
	workspace:BulkMoveTo(Parts, CFrames, Enum.BulkMoveMode.FireCFrameChanged)
end

local function SendCastFire(
	cast: TypeDef.ActiveCastData,
	origin: Vector3,
	direction: Vector3,
	velocity: Vector3 | number,
	behavior: TypeDef.FastCastBehavior
)
	cast.Caster.Output:Fire("CastFire", cast, origin, direction, velocity, behavior)
end

--[=[

@function Init
@within BaseCast

@param BindableOutput BindableEvent -- The BindableEvent used for outputting events.
@param Data any -- Configuration data for the BaseCast.
@return BaseCast -- The initialized BaseCast instance.

To initialize a new BaseCast instance.

]=]
function BaseCast.Init(BindableOutput: BindableEvent, Data: any)
	local self = setmetatable({}, BaseCast)
	-- Others
	--print(BindableOutput.Parent)
	Actor = BindableOutput.Parent
	Actives = setmetatable({}, { __mode = "v" })
	-- Bindable
	Output = BindableOutput

	local BindableCleaner = Instance.new("BindableEvent")
	BindableCleaner.Name = "ActiveCastDestroyer"
	BindableCleaner.Parent = Actor

	if Data.useObjectCache then
		local BindableObjectCache = Instance.new("BindableFunction")
		BindableObjectCache.Parent = Actor
		BindableObjectCache.Name = "ActiveCastObjectCache"
		ObjectCache = BindableObjectCache
	end

	if Data.useBulkMoveTo then
		--print("Connecting PreRender")
		BulkMoveToConnection = RS.PreRender:ConnectParallel(HandleBulkMoveTo)
	end

	ActiveCastCleaner = BindableCleaner

	ActiveCastCleaner.Event:Connect(function(activeCastID: number)
		if Actives[activeCastID] then
			Actives[activeCastID] = nil
			Actor:SetAttribute("Tasks", Actor:GetAttribute("Tasks") - 1)
		end
	end)

	SyncChanges = Instance.new("BindableEvent")
	SyncChanges.Name = "SyncChanges"
	SyncChanges.Parent = Actor

	SyncChanges.Event:Connect(function(cast: TypeDef.ActiveCastData)
		local ID = cast.ID
		local TargetCast = Actives[ID]

		if TargetCast then
			for i, v in cast do
				TargetCast[i] = v
			end
		end

	end)

	return self
end

--[=[

@method Raycast
@within BaseCast

@param Origin Vector3 -- The origin of the raycast.
@param Direction Vector3 -- The direction of the raycast.
@param Velocity Vector3 | number -- The velocity of the raycast.
@param Behavior FastCastBehavior -- The behavior data for the raycast.
@param GUID string -- The unique identifier for the raycast.

Create a raycast.

]=]
function BaseCast:Raycast(
	Origin: Vector3,
	Direction: Vector3,
	Velocity: Vector3 | number,
	Behavior: TypeDef.FastCastBehavior
)
	--table.insert(self.Actives, ActiveCast.new(self, Origin, Direction, Velocity, Behavior))
	Actor:SetAttribute("Tasks", Actor:GetAttribute("Tasks") + 1)

	NextProjectileID += 1

	Actives[NextProjectileID] = ActiveCast.createCastData({
		Output = Output,
		ActiveCastCleaner = ActiveCastCleaner,
		ObjectCache = ObjectCache,
		SyncChange = SyncChanges
	}, NextProjectileID, Origin, Direction, Velocity, Behavior, FastCastEventsModule, {
		CastType = EnumCastTypes.Raycast
	} :: any)

	if Behavior.FastCastEventsConfig.UseCastFire then
		SendCastFire(Actives[NextProjectileID], Origin, Direction, Velocity, Behavior)
	end
	if Behavior.FastCastEventsModuleConfig.UseCastFire then
		if CastFireFunc then
			CastFireFunc(Actives[NextProjectileID], Origin, Direction, Velocity, Behavior)
		end
	end
end

--[=[

@method SetFastCastEventsModule
@within BaseCast

@param moduleScript ModuleScript -- The FastCastEventsModule to set.

]=]
function BaseCast:SetFastCastEventsModule(moduleScript: ModuleScript)
	FastCastEventsModule = moduleScript
	if moduleScript and typeof(moduleScript) == "Instance" and moduleScript:IsA("ModuleScript") then
		CastFireFunc = require(moduleScript)
		if CastFireFunc.CastFire then
			CastFireFunc = CastFireFunc.CastFire
		else
			CastFireFunc = nil
		end
	end
end

--[=[

@method Blockcast
@within BaseCast

@param Origin Vector3 -- The origin of the blockcast.
@param Size Vector3 -- The size of the blockcast.
@param Direction Vector3 -- The direction of the blockcast.
@param Velocity Vector3 | number -- The velocity of the blockcast.
@param Behavior FastCastBehavior -- The behavior data for the blockcast.

Create a Blockcast.

]=]
function BaseCast:Blockcast(
	Origin: Vector3,
	Size: Vector3,
	Direction: Vector3,
	Velocity: Vector3 | number,
	Behavior: TypeDef.FastCastBehavior
)
	Actor:SetAttribute("Tasks", Actor:GetAttribute("Tasks") + 1)
	NextProjectileID += 1

	Actives[NextProjectileID] = ActiveCast.createCastData({
		Output = Output,
		ActiveCastCleaner = ActiveCastCleaner,
		ObjectCache = ObjectCache,
		SyncChange = SyncChanges
	}, NextProjectileID, Origin, Direction, Velocity, Behavior, FastCastEventsModule, {
		CastType = EnumCastTypes.Blockcast,
		Size = Size
	} :: any)

	if Behavior.FastCastEventsConfig.UseCastFire then
		SendCastFire(Actives[NextProjectileID], Origin, Direction, Velocity, Behavior)
	end
	if Behavior.FastCastEventsModuleConfig.UseCastFire then
		if CastFireFunc then
			CastFireFunc(Actives[NextProjectileID], Origin, Direction, Velocity, Behavior)
		end
	end
end

--[=[

@method Spherecast
@within BaseCast

@param Origin Vector3 -- The origin of the spherecast.
@param Radius number -- The radius of the spherecast.
@param Direction Vector3 -- The direction of the spherecast.
@param Velocity Vector3 | number -- The velocity of the spherecast.
@param Behavior FastCastBehavior -- The behavior data for the spherecast.

Create a Spherecast.

]=]
function BaseCast:Spherecast(
	Origin: Vector3,
	Radius: number,
	Direction: Vector3,
	Velocity: Vector3 | number,
	Behavior: TypeDef.FastCastBehavior
)
	Actor:SetAttribute("Tasks", Actor:GetAttribute("Tasks") + 1)
	NextProjectileID += 1

	Actives[NextProjectileID] = ActiveCast.createCastData({
		Output = Output,
		ActiveCastCleaner = ActiveCastCleaner,
		ObjectCache = ObjectCache,
		SyncChange = SyncChanges
	}, NextProjectileID, Origin, Direction, Velocity, Behavior, FastCastEventsModule, {
		CastType = EnumCastTypes.Spherecast,
		Radius = Radius
	} :: any)

	if Behavior.FastCastEventsConfig.UseCastFire then
		SendCastFire(Actives[NextProjectileID], Origin, Direction, Velocity, Behavior)
	end
	if Behavior.FastCastEventsModuleConfig.UseCastFire then
		if CastFireFunc then
			CastFireFunc(Actives[NextProjectileID], Origin, Direction, Velocity, Behavior)
		end
	end
end

--[=[

@method BindBulkMoveTo
@within BaseCast

@param bool boolean -- Whether to enable or disable BulkMoveTo.

Enables or disables the BulkMoveTo feature.

]=]
function BaseCast:BindBulkMoveTo(bool: boolean)
	if bool then
		if not BulkMoveToConnection then
    		BulkMoveToConnection = RS.PreRender:ConnectParallel(HandleBulkMoveTo)
		end
	else
		if BulkMoveToConnection then
			BulkMoveToConnection:Disconnect()
			BulkMoveToConnection = nil
		end
	end
end

--[=[

@method BindObjectCache
@within BaseCast

@param bool boolean -- Whether to enable or disable ObjectCache.

Enables or disables the ObjectCache feature.

]=]
function BaseCast:BindObjectCache(bool: boolean)
	if bool then
		if ObjectCache then
			return
		end
		local BindableObjectCache = Instance.new("BindableFunction")
		BindableObjectCache.Parent = Actor
		BindableObjectCache.Name = "ActiveCastObjectCache"
		ObjectCache = BindableObjectCache
	else
		if ObjectCache then
			ObjectCache:Destroy()
			ObjectCache = nil
		end
	end
end

--[=[

@method Destroy
@within BaseCast

Destroys the BaseCast instance and cleans up resources.

]=]
function BaseCast:Destroy()
	if BulkMoveToConnection then
		BulkMoveToConnection:Disconnect()
		BulkMoveToConnection = nil
	end

	FastCastEventsModule = nil

	for _, v in Actives do
		FastCastM:TerminateCast(v)
	end

	Actives = {}
	setmetatable(self, nil)
end

return BaseCast


--[[
	- Author : Mawin CK
	- Date : 2025
	-- Verison : 0.0.3
]]
-- Haha, noob

local Configs = {}

Configs.DebugLogging = {
	Casting = false,
	Segment = false,
	Hit = false,
	RayPierce = false,
	Calculation = false,
}
Configs.VisualizeCasts = true

return Configs


--[[
	- Author : Mawin_CK
	- Date : 2025
	-- Verison : 0.0.6
]]

--!strict

-- Requires

local TypeDefinitions = require(script.Parent.TypeDefinitions)
local FastCastEnums = require(script.Parent:WaitForChild("FastCastEnums"))

-- Defaults

local Defaults = {}

Defaults.VisualizationFolderName = "FastCastVisualizationObjects"

-- Behavior

Defaults.FastCastBehavior = {
	RaycastParams = nil,
	Acceleration = Vector3.new(),
	MaxDistance = 1000,
	CanPierceFunction = nil,
	HighFidelityBehavior = FastCastEnums.HighFidelityBehavior.Default,
	HighFidelitySegmentSize = 0.5,

	CosmeticBulletTemplate = nil,
	CosmeticBulletProvider = nil,
	CosmeticBulletContainer = nil,

	AutoIgnoreContainer = true,

	SimulateAfterPhysic = true,

	-- Performance
	AutomaticPerformance = true,
	AdaptivePerformance = {
		HighFidelitySegmentSizeIncrease = 0.5,
		LowerHighFidelityBehavior = true
	},

	-- Debug
	VisualizeCasts = false,
	VisualizeCastSettings = {
		-- Segment
		Debug_SegmentColor = Color3.new(),
		Debug_SegmentTransparency = 0.75,
		Debug_SegmentSize = 0.10,

		-- Hit
		Debug_HitColor = Color3.new(0.2, 1, 0.5),
		Debug_HitTransparency = 0.5,
		Debug_HitSize = 0.25,

		-- Raypierce
		Debug_RayPierceColor = Color3.new(1, 0.2, 0.2),
		Debug_RayPierceTransparency = 0.25,
		Debug_RayPierceSize = 0.4,

		-- Lifetime
		Debug_RayLifetime = 1,
		Debug_HitLifetime = 1
	},

	FastCastEventsModuleConfig = {
		UseLengthChanged = false,
		UseHit = true,
		UsePierced = true,
		UseCastTerminating = true,
		UseCanPierce = true,
		UseCastFire = true
	},

	FastCastEventsConfig = {
		UseLengthChanged = false,
		UseHit = true,
		UsePierced = true,
		UseCastTerminating = true,
		UseCastFire = true
	}
} :: TypeDefinitions.FastCastBehavior

return Defaults


--[[
	- Author : Mawin CK
	- Date : 2025
	-- Verison : 0.0.9
]]

--!strict

--[=[

@class FastCastEnums
Enums for FastCast2.

]=]

local Enums = {}

--[=[

How High-Fidelity the cast simulation should be.
@type HighFidelityBehavior {Default, Automatic, Always}
@within FastCastEnums

]=]
Enums.HighFidelityBehavior = {
	Default = 1,
	Automatic = 2,
	Always = 3
}

Enums.CastType = {
	Raycast = 1,
	Blockcast = 2,
	Spherecast = 3
}

return Enums


--[[
	- Author : Mawin CK
	- Date : 11/03/2025
]]

-- Modules

-- REPLACE WITH ACTUAL PATH (Just use ObjectValue lol)
--local Rep = game:GetService("ReplicatedStorage")
--local FastCast2Module = Rep:WaitForChild("FastCast2")

local FastCast2Module: ModuleScript = script:WaitForChild("FastCast2").Value :: ModuleScript

-- Requires
local TypeDefinitions = require(FastCast2Module:WaitForChild("TypeDefinitions"))

local BaseCast = require(FastCast2Module:WaitForChild("BaseCast"))

-- Variables
local actor = script:GetActor()
if actor == nil then
	error("The script must placed inside of actor")
end

-- Listeners

actor:BindToMessage("Init", function(Data: any?)
	BaseCast = BaseCast.Init(script.Parent:WaitForChild("Output"), Data)
end)

actor:BindToMessage("Raycast", function(
	origin: Vector3,
	direction: Vector3,
	velocity: Vector3 | number,
	behavior: TypeDefinitions.FastCastBehavior
)
	--print(behavior)
	--print(SharedCasters[casterID])
	--StoredCasts[casterID][ID] = ActiveCast.new(bindableEvent, origin, direction, velocity, behavior)

	BaseCast:Raycast(origin, direction, velocity, behavior)
end)

actor:BindToMessage(
	"SetFastCastEventsModule",
	function(moduleScript: ModuleScript?)
		BaseCast:SetFastCastEventsModule(moduleScript)
	end
)

--[[actor:BindToMessage("Blockcast", function(
	casterID : string,
	caster : TypeDefinitions.ActiveBlockCast,
	ID : string,
	origin : Vector3,
	size : Vector3,
	direction : Vector3 | number,
	velocity : Vector3,
	behavior : TypeDefinitions.FastCastBehavior?
)
	StoredCasts[casterID][ID] = ActiveBlockcast.new(caster, origin, size, direction, velocity, behavior)
end)]]

actor:BindToMessage("Blockcast", function(
	origin: Vector3,
	size: Vector3,
	direction: Vector3,
	velocity: Vector3 | number,
	behavior: TypeDefinitions.FastCastBehavior
)
	BaseCast:Blockcast(origin, size, direction, velocity, behavior)
end)

actor:BindToMessage("Spherecast", function(
	origin : Vector3,
	radius : number,
	direction : Vector3,
	velocity : Vector3 | number,
	behavior : TypeDefinitions.FastCastBehavior
)
	BaseCast:Spherecast(origin, radius, direction, velocity, behavior)
end)

actor:BindToMessage("BindBulkMoveTo", function(bool: boolean)
	BaseCast:BindBulkMoveTo(bool)
end)

actor:BindToMessage("BindObjectCache", function(bool: boolean)
	BaseCast:BindObjectCache(bool)
end)

-- CleanUp

actor:BindToMessage("Destroy", function()
	BaseCast:Destroy()
	script.Parent:Destroy()
end)


--[[
	- Author : Mawin CK
	- Date : 11/03/2025
]]

-- Modules

-- REPLACE WITH ACTUAL PATH (Just use ObjectValue lol)
--local Rep = game:GetService("ReplicatedStorage")
--local FastCast2Module = Rep:WaitForChild("FastCast2")

local FastCast2Module: ModuleScript = script:WaitForChild("FastCast2").Value :: ModuleScript

local TypeDefinitions = require(FastCast2Module:WaitForChild("TypeDefinitions"))

local BaseCast = require(FastCast2Module:WaitForChild("BaseCast"))

-- Variables
local actor = script:GetActor()
if actor == nil then
	error("The script must placed inside of actor")
end

-- Listeners

actor:BindToMessage("Init", function(Data : any?)
	BaseCast = BaseCast.Init(
		script.Parent:WaitForChild("Output"),
		Data
	)
end)

actor:BindToMessage("Raycast", function(
	origin : Vector3,
	direction : Vector3,
	velocity : Vector3 | number,
	behavior : TypeDefinitions.FastCastBehavior
)
	--print(behavior)
	--print(SharedCasters[casterID])
	--StoredCasts[casterID][ID] = ActiveCast.new(bindableEvent, origin, direction, velocity, behavior)

	BaseCast:Raycast(origin, direction, velocity, behavior)
end)

--[[actor:BindToMessage("Blockcast", function(
	casterID : string,
	caster : TypeDefinitions.ActiveBlockCast,
	ID : string,
	origin : Vector3,
	size : Vector3,
	direction : Vector3 | number,
	velocity : Vector3,
	behavior : TypeDefinitions.FastCastBehavior?
)
	StoredCasts[casterID][ID] = ActiveBlockcast.new(caster, origin, size, direction, velocity, behavior)
end)]]

actor:BindToMessage(
	"SetFastCastEventsModule",
	function(moduleScript: ModuleScript?)
		BaseCast:SetFastCastEventsModule(moduleScript)
	end
)

actor:BindToMessage("Blockcast", function(
	origin : Vector3,
	size : Vector3,
	direction : Vector3,
	velocity : Vector3 | number,
	behavior : TypeDefinitions.FastCastBehavior
)
	--print(behavior)
	--print(SharedCasters[casterID])
	--StoredCasts[casterID][ID] = ActiveCast.new(bindableEvent, origin, direction, velocity, behavior)

	BaseCast:Blockcast(origin, size, direction, velocity, behavior)
end)

actor:BindToMessage("Spherecast", function(
	origin : Vector3,
	radius : number,
	direction : Vector3,
	velocity : Vector3 | number,
	behavior : TypeDefinitions.FastCastBehavior
)
	BaseCast:Spherecast(origin, radius, direction, velocity, behavior)
end)

actor:BindToMessage("BindBulkMoveTo", function(bool : boolean)
	BaseCast:BindBulkMoveTo(bool)
end)

actor:BindToMessage("BindObjectCache", function(bool : boolean)
	BaseCast:BindObjectCache(bool)
end)

-- CleanUp

actor:BindToMessage("Destroy", function()
	BaseCast:Destroy()
	script.Parent:Destroy()
end)


-- ******************************* --
-- 			AX3NX / AXEN		   --
-- ******************************* --

-- Modded by Mawin_CK
-- Desc : I make it more customizable and more easy to use :P

-- Services

local ReplicatedFirst = game:GetService("ReplicatedFirst")
local ServerScriptService = game:GetService("ServerScriptService")
local RunService = game:GetService("RunService")

-- Types

local IS_SERVER = RunService:IsServer()

export type Dispatcher = {
	Init : (newContainerParent : Instance, VMContainerName : string, VMname : string) -> (),
	new : (Threads: number, Data: any?, Callback: (...any) -> ()?) -> Dispatcher,

	Threads: {Actor},

	Dispatch: (Dispatcher, Message : string?, ...any) -> (),
	Allocate: (Dispatcher, Threads: number, Data: any?, Callback: (...any) -> ()?) -> (),
	DispatchAll: (Dispatcher, Message : string?, ...any) -> (),

	Destroy : (Dispatcher) -> ()
}

-- Paths

local ServerScript : Script = script:FindFirstChild("ServerVM")

local LocalScript : LocalScript = script:FindFirstChild("ClientVM")

-- Default settings

local ClientContainerParent = ReplicatedFirst
local ServerContainerParent = ServerScriptService

-- Constants

local Dispatcher = {}
Dispatcher.__index = Dispatcher
Dispatcher.__type = "Dispatcher"

local Template;
local Container;

local ControllerName = ""
local ContainerName = ""
local ContainerParent = (IS_SERVER and ServerContainerParent or ClientContainerParent)

-- Variables

local AlreadyInit = false

-- Public Functions

--[[

	Initialize the dispatcher

	NOTE : Only once in a client/server

	Parameters :
		- newContainerParent : The parent of the VM container
		- VMContainerName : The name of the VM container
		- VMContainer : The VM container
		- VMname : The name of the VM

]]
function Dispatcher.Init(newContainerParent : Instance, VMContainerName : string, VMname : string)
	if AlreadyInit then
		warn("Dispatcher already initialized")
		return
	end

	-- Init

	local Actor = Instance.new("Actor")
	Actor:SetAttribute("Tasks", 0)

	local Controller
	if IS_SERVER then
		assert(ServerScript, "ServerScript path not set")
		Controller = ServerScript:Clone()
	else
		assert(LocalScript, "LocalScript path not set")
		Controller = LocalScript:Clone()
	end

	-- Setup

	ControllerName = VMname
	ContainerName = VMContainerName
	ContainerParent = newContainerParent

	-- Start

	assert(Controller, "Controller script not found or not valid")

	Controller.Name = ControllerName or "Controller"
	Controller.Parent = Actor
	Actor.Parent = script

	Template = Actor :: any

	Container = Instance.new("Folder")
	Container.Name = ContainerName or "DISPATCHER_THREADS"
	Container.Parent = ContainerParent

	AlreadyInit = true
end

--[[
	Create a new dispatcher that can be used to dispatch messages to the actors

Parameters :
		Threads: number - The number of threads to use
		Data: any? - The data when actors Init
		Callback: (...any) -> () - The callback to use for the actors

	Example :
		local dispatcher = Dispatcher.new(10, ModuleScript, function(...)
			print(...)
		end)

	@return Dispatcher
]]
function Dispatcher.new(Threads: number, Data : any?, Callback: (...any) -> ()?): Dispatcher
	--assert(typeof(Module) == "Instance" and Module:IsA("ModuleScript"), "Invalid argument #1 to 'Dispatcher.new', module must be a module script.")
	assert(type(Threads) == "number" and Threads > 0, "Invalid argument #2 to 'Dispatcher.new', threads must be a positive integer.")

	if not AlreadyInit then
		error("Please Init dispatcher, RunContext : " .. IS_SERVER and "Server"or "Client")
	end

	local self: Dispatcher = setmetatable({
		Threads = {}
	} :: any, Dispatcher)

	--> Allocate initial threads
	self:Allocate(Threads, Data, Callback)

	return self
end

function Dispatcher:Allocate(Threads: number, Data: any?, Callback: (...any) -> (...any)?)
	assert(type(Threads) == "number" and Threads > 0, "Invalid argument #2 to 'Dispatcher.new', threads must be a positive integer.")

	local Actors = {}

	--> Create actors
	for _ = 1, Threads do
		local Actor = Template:Clone()
		Actor.Parent = Container

		local controller = Actor:FindFirstChild(ControllerName)

		if Callback then
			local Output = Instance.new("BindableEvent")
			Output.Name = "Output"
			Output.Parent = Actor

			Actor.Output.Event:Connect(Callback)
		end

		if controller then
			controller.Enabled = true
		end
		table.insert(Actors, Actor)
	end

	--> Allow actors to start
	RunService.PostSimulation:Wait()

	--> Initialize actors
	for _, Actor in Actors do
		Actor:SendMessage("Init", Data)
	end

	--> Merge actors into threads
	table.move(Actors, 1, #Actors, #self.Threads + 1, self.Threads)
end

--[[
	Dispatch a message to the actors

Parameters :
		Message: string? - The message to send to the actors
		...: any - The arguments to send to the actors

		if the Message is nil, then the actors will be called with the "Dispatch" message

		Example :

		local dispatcher = Dispatcher.new(10, nil)
		dispatcher:Dispatch("Hello from client", "Hello from client")

]]
function Dispatcher:Dispatch(Message : string?, ...)
	local Threads: {Actor} = table.clone(self.Threads)
	table.sort(Threads, function(a: Actor, b: Actor)
		local aTasks = a:GetAttribute("Tasks") or 0
		local bTasks = b:GetAttribute("Tasks") or 0
		return aTasks < bTasks
	end)
	Threads[1]:SendMessage(Message or "Dispatch", ...)
end

function Dispatcher:Destroy(destroySource: boolean)
	for _, Thread in self.Threads do
		Thread:SendMessage("Destroy")
	end
	self.Threads = {}

	task.spawn(function()
		while #Container:GetChildren() ~= 0 do
			task.wait()
		end
		Container:Destroy()
		if destroySource then
			script:Destroy()
		end
	end)
end

function Dispatcher:DispatchAll(Message : string?, ...)
	for _, Thread in self.Threads do
		Thread:SendMessage(Message or "Dispatch", ...)
	end
end

return Dispatcher


--[[
	- Modded By Mawin_CK
	Desc :  i added __type = "ObjectCache" to letting FastCast Recongize that this is ObjectCache
]]

--[=[

@class ObjectCache
@private
@external ObjectCache https://devforum.roblox.com/t/objectcache-a-modern-blazing-fast-model-and-part-cache/3104112
ObjectCache usage should be derived from their DevForum post:

https://devforum.roblox.com/t/objectcache-a-modern-blazing-fast-model-and-part-cache/3104112

]=]

--!strict
--!native
local HTTPS = game:GetService("HttpService")

local FAR_AWAY_CFRAME = CFrame.new(2^24, 2^24, 2^24)
local EXPAND_BY_AMOUNT = 50

local MovingParts = table.create(10_000)
local MovingCFrames = table.create(10_000)

local ScheduledUpdate = false
local function UpdateMovement()
	while true do
		workspace:BulkMoveTo(MovingParts, MovingCFrames, Enum.BulkMoveMode.FireCFrameChanged)

		table.clear(MovingParts)
		table.clear(MovingCFrames)

		ScheduledUpdate = false
		coroutine.yield()
	end
end
local UpdateMovementThread = coroutine.create(UpdateMovement)

local Cache = {}
Cache.__index = Cache
Cache.__type = "ObjectCache"

function Cache:_GetNew(Amount: number, Warn: boolean)
	if Warn then
		warn(`ObjectCache: Cache retrieval exceeded preallocated amount! expanding by {Amount}...`)
	end

	local FreeObjectsContainer = self._FreeObjects
	local InitialLength = #self._FreeObjects
	local CacheHolder = self.CacheHolder

	local IsTemplateModel = self._IsTemplateModel
	local Template: Model | BasePart = self._Template

	local TargetParts = table.create(Amount)
	local TargetCFrames = table.create(Amount)
	local AddedObjects = table.create(Amount)
	for Index = InitialLength + 1, InitialLength + Amount do
		local Object = Template:Clone()
		local ObjectRoot: BasePart = if IsTemplateModel then (Object:: Model).PrimaryPart:: BasePart else Object:: BasePart

		FreeObjectsContainer[Index] = ObjectRoot

		local OffsetIndex = Index - InitialLength
		TargetParts[OffsetIndex] = ObjectRoot
		TargetCFrames[OffsetIndex] = FAR_AWAY_CFRAME
		AddedObjects[OffsetIndex] = Object
	end

	workspace:BulkMoveTo(TargetParts, TargetCFrames, Enum.BulkMoveMode.FireCFrameChanged)

	for _, Object in AddedObjects do
		(Object:: Instance).Parent = CacheHolder
	end

	return table.remove(FreeObjectsContainer)
end

function Cache:GetPart(PartCFrame: CFrame?): BasePart
	local Part = table.remove(self._FreeObjects) or self:_GetNew(self._ExpandAmount, true)

	--local ID = HTTPS:GenerateGUID(false)
	self._Objects[Part] = nil
	if PartCFrame then
		table.insert(MovingParts, Part)
		table.insert(MovingCFrames, PartCFrame)

		if not ScheduledUpdate then
			ScheduledUpdate = true
			task.defer(UpdateMovementThread)
		end
	end

	--Part:SetAttribute("ID", ID)
	--print("GET " .. ID)
	return Part
end
function Cache:ReturnPart(Part: BasePart)
	--print("RET " .. Part:GetAttribute("ID"))
	if self._Objects[Part] then
		return
	end
	--print("RETURNED")

	self._Objects[Part] = true

	table.insert(self._FreeObjects, Part)
	table.insert(MovingParts, Part)
	table.insert(MovingCFrames, FAR_AWAY_CFRAME)

	if not ScheduledUpdate then
		ScheduledUpdate = true
		task.defer(UpdateMovementThread)
	end
end

function Cache:Update()
	task.spawn(UpdateMovementThread)
end

function Cache:ExpandCache(Amount: number)
	assert(typeof(Amount) ~= "number" or Amount >= 0, `Invalid argument #1 to 'ObjectCache:ExpandCache' (positive number expected, got {typeof(Amount)})`)
	self:_GetNew(Amount, false)
end
function Cache:SetExpandAmount(Amount: number)
	assert(typeof(Amount) ~= "number" or Amount > 0, `Invalid argument #1 to 'ObjectCache:SetExpandAmount' (positive number expected, got {typeof(Amount)})`)
	self._ExpandAmount = Amount
end

function Cache:IsInUse(Object: BasePart): boolean
	return self._Objects[Object] == nil
end

function Cache:Destroy()
	self.CacheHolder:Destroy()
end

local function GetCacheContainer()
	local CacheHolder = Instance.new("Folder")
	CacheHolder.Name = "ObjectCache " .. HTTPS:GenerateGUID(false)

	return CacheHolder
end

local Constructor = {}
function Constructor.new(Template: BasePart | Model, CacheSize: number?, CachesContainer: Instance?)
	local TemplateType = typeof(Template)
	assert(TemplateType == "Instance", `Invalid argument #1 to 'ObjectCache.new' (BasePart expected, got {TemplateType})`)

	assert(Template:IsA("BasePart") or Template:IsA("Model"), `Invalid argument #1 to 'ObjectCache.new' (BasePart or Model expected, got {Template.ClassName})`)
	assert(Template.Archivable, `ObjectCache: Cannot use template object provided, as it has Archivable set to false.`)
	if Template:IsA("Model") then
		assert(Template.PrimaryPart ~= nil, `Invalid Template provided to 'ObjectCache.new': Model has no PrimaryPart set!`)
	end

	local CacheSizeType = typeof(CacheSize)
	assert(CacheSize == nil or CacheSizeType == "number", `Invalid argument #2 to 'ObjectCache.new' (number expected, got {CacheSizeType})`)
	assert(CacheSize == nil or CacheSize >= 0, `Invalid argument #2 to 'ObjectCache.new' (positive number expected, got {CacheSize})`)

	local ContainerType = typeof(CachesContainer)
	assert(CachesContainer == nil or ContainerType == "Instance", `Invalid argument #3 to 'ObjectCache.new' (Instance expected, got {ContainerType})`)

	local PreallocAmount = CacheSize or 10
	local CacheParent = GetCacheContainer()

	local Objects: {[BasePart]: boolean} = {}
	local FreeObjects: {BasePart | Model} = table.create(PreallocAmount)

	local TargetParts = table.create(PreallocAmount)

	local IsTemplateModel = Template:IsA("Model")
	for Index = 1, PreallocAmount do
		local Object = Template:Clone()
		local ObjectRoot: BasePart = if IsTemplateModel then (Object:: Model).PrimaryPart:: BasePart else Object:: BasePart

		FreeObjects[Index] = Object
		TargetParts[Index] = ObjectRoot

		ObjectRoot.CFrame = FAR_AWAY_CFRAME;
		(Object:: Instance).Parent = CacheParent
	end

	CacheParent.Parent = CachesContainer or workspace

	return setmetatable({
		CacheHolder = CacheParent,
		_ExpandAmount = EXPAND_BY_AMOUNT,
		_Template = Template,
		_FreeObjects = TargetParts,
		_Objects = Objects,
		_IsTemplateModel = IsTemplateModel,
		_PreallocatedAmount = PreallocAmount,
		Type = "ObjectCache"
	}, Cache)
end

return Constructor


--!optimize 2
--!nocheck
--!native

export type Connection<U...> = {
	Connected: boolean,

	Disconnect: (self: Connection<U...>) -> (),
	Reconnect: (self: Connection<U...>) -> (),
}

export type Signal<T...> = {
	RBXScriptConnection: RBXScriptConnection?,

	Connect: <U...>(self: Signal<T...>, fn: (...any) -> (), U...) -> Connection<U...>,
	Once: <U...>(self: Signal<T...>, fn: (...any) -> (), U...) -> Connection<U...>,
	Wait: (self: Signal<T...>) -> T...,
	Fire: (self: Signal<T...>, T...) -> (),
	DisconnectAll: (self: Signal<T...>) -> (),
	Destroy: (self: Signal<T...>) -> (),
}

local freeThreads: { thread } = {}

local function runCallback(callback, thread, ...)
	callback(...)
	table.insert(freeThreads, thread)
end

local function yielder()
	while true do
		runCallback(coroutine.yield())
	end
end

local Connection = {}
Connection.__index = Connection

local function disconnect<U...>(self: Connection<U...>)
	if not self.Connected then
		return
	end
	self.Connected = false

	local next = self._next
	local prev = self._prev

	if next then
		next._prev = prev
	end
	if prev then
		prev._next = next
	end

	local signal = self._signal
	if signal._head == self then
		signal._head = next
	end
end

local function reconnect<U...>(self: Connection<U...>)
	if self.Connected then
		return
	end
	self.Connected = true

	local signal = self._signal
	local head = signal._head
	if head then
		head._prev = self
	end
	signal._head = self

	self._next = head
	self._prev = false
end

Connection.Disconnect = disconnect
Connection.Reconnect = reconnect

--\\ Signal //--
local Signal = {}
Signal.__index = Signal

-- stylua: ignore
local rbxConnect, rbxDisconnect do
	if task then
		local bindable = Instance.new("BindableEvent")
		rbxConnect = bindable.Event.Connect
		rbxDisconnect = bindable.Event:Connect(function() end).Disconnect
		bindable:Destroy()
	end
end

local function connect<T..., U...>(self: Signal<T...>, fn: (...any) -> (), ...: U...): Connection<U...>
	local head = self._head
	local varargs = { ... }
	local cn = setmetatable({
		Connected = true,
		_signal = self,
		_fn = fn,
		_varargs = if #varargs == 0 then false else varargs,
		_next = head,
		_prev = false,
	}, Connection)

	if head then
		head._prev = cn
	end
	self._head = cn

	return cn
end

local function once<T..., U...>(self: Signal<T...>, fn: (...any) -> (), ...: U...)
	local cn
	cn = connect(self, function(...)
		disconnect(cn)
		fn(...)
	end, ...)
	return cn
end

local wait = if task
	then function<T...>(self: Signal<T...>): ...any
		local thread = coroutine.running()
		local cn
		cn = connect(self, function(...)
			disconnect(cn)
			if coroutine.status(thread) == "suspended" then
				task.spawn(thread, ...)
			end
		end)
		return coroutine.yield()
	end
	else function<T...>(self: Signal<T...>): ...any
		local thread = coroutine.running()
		local cn
		cn = connect(self, function(...)
			disconnect(cn)
			local passed, message = coroutine.resume(thread, ...)
			if not passed then
				error(message, 0)
			end
		end)
		return coroutine.yield()
	end

local fire = if task
	then function<T...>(self: Signal<T...>, ...: any)
		local cn = self._head
		while cn do
			local thread
			if #freeThreads > 0 then
				thread = freeThreads[#freeThreads]
				freeThreads[#freeThreads] = nil
				else
				thread = coroutine.create(yielder)
				coroutine.resume(thread)
			end

			if not cn._varargs then
				task.spawn(thread, cn._fn, thread, ...)
			else
				local args = cn._varargs
				local len = #args
				local count = len
				for _, value in { ... } do
					count += 1
					args[count] = value
				end

				task.spawn(thread, cn._fn, thread, table.unpack(args))

				for i = count, len + 1, -1 do
					args[i] = nil
				end
			end

			cn = cn._next
		end
	end
else function<T...>(self: Signal<T...>, ...: any)
		local cn = self._head
		while cn do
			local thread
			if #freeThreads > 0 then
				thread = freeThreads[#freeThreads]
				freeThreads[#freeThreads] = nil
			else
				thread = coroutine.create(yielder)
				coroutine.resume(thread)
			end

			if not cn._varargs then
				local passed, message = coroutine.resume(thread, cn._fn, thread, ...)
				if not passed then
					print(string.format("%s\nstacktrace:\n%s", message, debug.traceback()))
				end
			else
				local args = cn._varargs
				local len = #args
				local count = len
				for _, value in { ... } do
					count += 1
					args[count] = value
				end

				local passed, message = coroutine.resume(thread, cn._fn, thread, table.unpack(args))
				if not passed then
					print(string.format("%s\nstacktrace:\n%s", message, debug.traceback()))
				end

				for i = count, len + 1, -1 do
					args[i] = nil
				end
			end

			cn = cn._next
		end
	end

	local function disconnectAll<T...>(self: Signal<T...>)
		local cn = self._head
		while cn do
			disconnect(cn)
			cn = cn._next
		end
	end

	local function destroy<T...>(self: Signal<T...>)
		disconnectAll(self)
		local cn = self.RBXScriptConnection
		if cn then
			rbxDisconnect(cn)
			self.RBXScriptConnection = nil
		end
	end

	--\\ Constructors
	function Signal.new<T...>(): Signal<T...>
		return setmetatable({ _head = false }, Signal)
	end

	function Signal.wrap<T...>(signal: RBXScriptSignal): Signal<T...>
		local wrapper = setmetatable({ _head = false }, Signal)
		wrapper.RBXScriptConnection = rbxConnect(signal, function(...)
			fire(wrapper, ...)
		end)
		return wrapper
	end

	--\\ Methods
	Signal.Connect = connect
	Signal.Once = once
	Signal.Wait = wait
	Signal.Fire = fire
	Signal.DisconnectAll = disconnectAll
	Signal.Destroy = destroy

	return { new = Signal.new, wrap = Signal.wrap }