-- Creates the generic "ROBLOX" loading screen on startup
-- Written by ArceusInator & Ben Tkacheff, 2014
-- Updates by 0xBAADF00D, 2017
local AssetService = game:GetService("AssetService")
local MarketplaceService = game:GetService("MarketplaceService")
local UserInputService = game:GetService("UserInputService")
local VRService = game:GetService("VRService")
local GuiService = game:GetService("GuiService")
local ContextActionService = game:GetService("ContextActionService")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local ContentProvider = game:GetService("ContentProvider")
local CoreGui = game:GetService("CoreGui")
local ReplicatedFirst = game:GetService("ReplicatedFirst")
local ScriptContext = game:GetService("ScriptContext")

local RobloxGui = CoreGui:WaitForChild("RobloxGui")
local create = require(RobloxGui:WaitForChild("Modules"):WaitForChild("Common"):WaitForChild("Create"))
local PolicyService = require(RobloxGui.Modules.Common:WaitForChild("PolicyService"))

--FFlags
local FFlagLoadTheLoadingScreenFasterSuccess, FFlagLoadTheLoadingScreenFasterValue = pcall(function() return settings():GetFFlag("LoadTheLoadingScreenFaster") end)
local FFlagLoadTheLoadingScreenFaster = FFlagLoadTheLoadingScreenFasterSuccess and FFlagLoadTheLoadingScreenFasterValue
local FFlagLoadTheLoadingScreenEvenFaster = game:DefineFastFlag("LoadTheLoadingScreenEvenFaster", false)
local FFlagLoadingScreenDontBlockOnPolicyService = game:DefineFastFlag("LoadingScreenDontBlockOnPolicyService", false)
local FFlagBackButtonWhileLoadingGoesBackToApp = game:DefineFastFlag("BackButtonWhileLoadingGoesBackToApp", false)
local FFlagLoadingScreenShowBlankUntilPolicyServiceReturns = game:DefineFastFlag("LoadingScreenShowBlankUntilPolicyServiceReturns", false)

local FFlagShowConnectionErrorCode = settings():GetFFlag("ShowConnectionErrorCode")
local FFlagConnectionScriptEnabled = settings():GetFFlag("ConnectionScriptEnabled")

local antiAddictionNoticeStringEn = "Boycott bad games, refuse pirated games. Be aware of self-defense and being deceived. Playing games is good for your brain, but too much game play can harm your health. Manage your time well and enjoy a healthy lifestyle."
local FFlagConnectErrorHandlerInLoadingScript = require(RobloxGui.Modules.Flags.FFlagConnectErrorHandlerInLoadingScript)
local loadErrorHandlerFromEngine = game:GetEngineFeature("LoadErrorHandlerFromEngine")

local debugMode = false

local startTime = tick()
local loadingImageInputBeganConn = nil
local waitForGameIdConnection

local GAME_THUMBNAIL_URL = "rbxthumb://type=GameIcon&id=%d&w=256&h=256"
local MAX_ICON_SIZE = Vector2.new(256, 256)
local ICON_ASPECT_RATIO = 1

local COLORS = {
	BACKGROUND_COLOR = Color3.fromRGB(45, 45, 45),
	TEXT_COLOR = Color3.fromRGB(255, 255, 255),
	ERROR = Color3.fromRGB(253, 68, 72)
}
local spinnerImageId = "rbxasset://textures/loading/robloxTilt.png"

-- Variables
local GameAssetInfo -- loaded by InfoProvider:LoadAssets()
local currScreenGui
local renderSteppedConnection
local backButtonConnection
local destroyingBackground, destroyedLoadingGui = false, false
local isTenFootInterface = GuiService:IsTenFootInterface()

local placeLabel, creatorLabel = nil, nil
local backgroundFadeStarted = false
local tweenPlaceIcon = nil
local layoutIsReady = false

local connectionHealthShown = false
local connectionHealthCon

local InfoProvider = {}

local function WaitForPlaceId()
	local placeId = game.PlaceId
	if placeId == 0 then
		game:GetPropertyChangedSignal("PlaceId"):wait()
		placeId = game.PlaceId
	end
	return placeId
end

function InfoProvider:GetGameName()
	if GameAssetInfo ~= nil then
		return GameAssetInfo.Name
	else
		return ''
	end
end

function InfoProvider:GetCreatorName()
	if GameAssetInfo ~= nil then
		return GameAssetInfo.Creator.Name
	else
		return ''
	end
end

function InfoProvider:LoadAssets()
	if FFlagLoadTheLoadingScreenFaster then
		coroutine.wrap(function()
			local placeId = WaitForPlaceId()
			local success, result = pcall(function()
				GameAssetInfo = MarketplaceService:GetProductInfo(placeId)
			end)
			if not success then
				print("LoadingScript->InfoProvider:LoadAssets:", result)
			end
		end)()
	else
		--spawn() == slowpoke.jpg
		spawn(function()
			local PLACEID = game.PlaceId
			if PLACEID <= 0 then
				while game.PlaceId <= 0 do
					wait()
				end
				PLACEID = game.PlaceId
			end

			-- load game asset info
			coroutine.resume(coroutine.create(function()
				local success, result = pcall(function()
					GameAssetInfo = MarketplaceService:GetProductInfo(PLACEID)
				end)
				if not success then
					print("LoadingScript->InfoProvider:LoadAssets:", result)
				end
			end))
		end)
	end
end

-- create a cancel binding for console to be able to cancel anytime while loading
local function createTenfootCancelGui()
	local cancelLabel = create'ImageLabel'
	{
		Name = "CancelLabel",
		Size = UDim2.new(0, 83, 0, 83),
		Position = UDim2.new(1, -32 - 83, 0, 32),
		BackgroundTransparency = 1,
		Image = 'rbxasset://textures/ui/Shell/ButtonIcons/BButton.png'
	}
	local cancelText = create'TextLabel'
	{
		Name = "CancelText",
		Size = UDim2.new(0, 400, 0, 83),
		Position = UDim2.new(1, -131, 0, 32),
		AnchorPoint = Vector2.new(1, 0),
		BackgroundTransparency = 1,
		Font = Enum.Font.SourceSans,
		TextSize = 48,
		TextXAlignment = Enum.TextXAlignment.Right,
		TextColor3 = COLORS.TEXT_COLOR,
		Text = "Cancel"
	}

	if not ReplicatedFirst:IsFinishedReplicating() then
		local seenBButtonBegin = false
		ContextActionService:BindCoreAction("CancelGameLoad",
			function(actionName, inputState, inputObject)
				if inputState == Enum.UserInputState.Begin then
					seenBButtonBegin = true
				elseif inputState == Enum.UserInputState.End and seenBButtonBegin then
					cancelLabel:Destroy()
					cancelText.Text = "Canceling..."
					cancelText.Position = UDim2.new(1, -32, 0, 32)
					ContextActionService:UnbindCoreAction('CancelGameLoad')
					game:Shutdown()
				end
			end,
			false,
			Enum.KeyCode.ButtonB)
	end

	while cancelLabel.Parent == nil do
		if currScreenGui then
			local blackFrame = currScreenGui:FindFirstChild('BlackFrame')
			if blackFrame then
				cancelLabel.Parent = blackFrame
				cancelText.Parent = blackFrame
				break
			end
		end
		wait()
	end
end

local function GenerateGui()
	local screenGui = create 'ScreenGui' {
		Name = 'RobloxLoadingGui',
		DisplayOrder = 8,
	}

	--
	-- create descendant frames
	local mainBackgroundContainer

	local inGameGlobalGuiInset = settings():GetFVariable("InGameGlobalGuiInset")
	mainBackgroundContainer = create 'Frame' {
		Name = 'BlackFrame',
		BackgroundColor3 = COLORS.BACKGROUND_COLOR,
		BackgroundTransparency = 0,
		Size = UDim2.new(1, 0, 1, inGameGlobalGuiInset),
		Position = UDim2.new(0, 0, 0, -inGameGlobalGuiInset),
		Active = true,
		Parent = screenGui
	}

	local closeButton =	create 'ImageButton' {
		Name = 'CloseButton',
		Image = 'rbxasset://textures/loading/cancelButton.png',
		ImageTransparency = 1,
		BackgroundTransparency = 1,
		Position = UDim2.new(1, -37, 0, 5),
		Size = UDim2.new(0, 32, 0, 32),
		Active = false,
		ZIndex = 10,
		Parent = mainBackgroundContainer
	}

    closeButton.MouseButton1Click:connect(function()
        game:Shutdown()
    end)

	local graphicsFrame = create 'Frame' {
		Name = 'GraphicsFrame',
		BorderSizePixel = 0,
		BackgroundTransparency = 1,
		AnchorPoint = Vector2.new(1, 1),
		Position = UDim2.new(0.95, 0, 0.95, 0),
		Size = UDim2.new(0.15, 0, 0.15, 0),
		ZIndex = 2,
		Parent = mainBackgroundContainer,

		create("UIAspectRatioConstraint") {
			AspectRatio = 1
		},
		create("UISizeConstraint") {
			MaxSize = Vector2.new(100, 100)
		}
	}

	local loadingImage = create 'ImageLabel' {
		Name = 'LoadingImage',
		BackgroundTransparency = 1,
		Image = spinnerImageId,
		AnchorPoint = Vector2.new(0.5, 0.5),
		Position = UDim2.new(0.5, 0, 0.5, 0),
		Size = UDim2.new(1, 0, 1, 0),
		ZIndex = 2,
		Parent = graphicsFrame,
	}

    local numberOfTaps = 0
    local lastTapTime = math.huge
	local doubleTapTimeThreshold = 0.5

    loadingImageInputBeganConn = loadingImage.InputBegan:connect(function()
        if PolicyService:IsSubjectToChinaPolicies() then
            return
        end

        if numberOfTaps == 0 then
            numberOfTaps = 1
            lastTapTime = tick()
            return
        end

        if UserInputService.TouchEnabled == true and UserInputService.MouseEnabled == false then
            if tick() - lastTapTime <= doubleTapTimeThreshold then
                GuiService:ShowStatsBasedOnInputString("ConnectionHealth")
				connectionHealthShown = not connectionHealthShown
            end
        end

        numberOfTaps = 0
        lastTapTime = math.huge
	end)

	local infoFrame = create 'Frame' {
		Name = 'InfoFrame',
		BackgroundTransparency = 1,
		AnchorPoint = Vector2.new(0.5, 0.5),
		Position = UDim2.new(0.5, 0, 0.5, 0),
		Size = UDim2.new(0.75, 0, 1, 0),
		ZIndex = 2,
		Parent = mainBackgroundContainer,
		create 'UIPadding' {
			Name = 'UiMessagePadding',
			PaddingBottom = UDim.new(0, 25),
		} or nil
	}


	local uiMessageFrame = create 'Frame' {
		Name = 'UiMessageFrame',
		BackgroundTransparency = 1,
		Position = UDim2.new(0.25, 0, 1, -120),
		Size = UDim2.new(1, 0, 0, 35),
		ZIndex = 2,
		LayoutOrder = 5,
		Parent = infoFrame,

		create 'TextLabel' {
			Name = 'UiMessage',
			BackgroundTransparency = 1,
			Position = UDim2.new(0, 0, 0, 5),
			Size = UDim2.new(1, 0, 0, 25),
			Font = Enum.Font.SourceSansLight,
			TextSize = 18,
			TextScaled = true,
			TextWrapped = true,
			TextColor3 = COLORS.TEXT_COLOR,
			Text = "",
			TextTransparency = 1,
			ZIndex = 2,
		}
	}

	local infoFrameAspect = create("UIAspectRatioConstraint") {
		AspectRatio = 3 / 2,
		Parent = infoFrame
	}
	local infoFrameList = create("UIListLayout") {
		SortOrder = Enum.SortOrder.LayoutOrder,
		FillDirection = Enum.FillDirection.Vertical,
		VerticalAlignment = Enum.VerticalAlignment.Center,
		HorizontalAlignment = Enum.HorizontalAlignment.Center,
		Padding = UDim.new(0.05, 0),
		Parent = infoFrame
	}

	local textContainer = create("Frame") {
		BackgroundTransparency = 1,
		Size = UDim2.new(2/3, 0, 1, 0),
		LayoutOrder = 2,
		Parent = nil,

		create("UIListLayout") {
			FillDirection = Enum.FillDirection.Vertical,
			VerticalAlignment = Enum.VerticalAlignment.Center,
			HorizontalAlignment = Enum.HorizontalAlignment.Left,
			SortOrder = Enum.SortOrder.LayoutOrder
		}
	}
	local placeIcon = create("ImageLabel") {
		Name = "PlaceIcon",
		BackgroundTransparency = 1,
		Size = UDim2.new(1, 0, 1, 0),
		Position = UDim2.new(0.5, 0, 0, 0),
		AnchorPoint = Vector2.new(0.5, 0),
		LayoutOrder = 1,
		Parent = infoFrame,

		ImageTransparency = 1,
		Image = "",

		create("UIAspectRatioConstraint") {
			AspectRatio = FFlagLoadTheLoadingScreenEvenFaster and ICON_ASPECT_RATIO or 576 / 324,
			AspectType = Enum.AspectType.ScaleWithParentSize,
			DominantAxis = Enum.DominantAxis.Width
		},
		create("UISizeConstraint") {
			MaxSize = FFlagLoadTheLoadingScreenEvenFaster and MAX_ICON_SIZE or Vector2.new(400, 400)
		}
	}

	if FFlagLoadTheLoadingScreenEvenFaster then
		local function onGameId()
			local gameId = game.GameId
			local imageUrl = GAME_THUMBNAIL_URL:format(gameId)
			placeIcon.Image = imageUrl
			ContentProvider:PreloadAsync({ placeIcon })
			if not backgroundFadeStarted then
				placeIcon.ImageTransparency = 0
			end
		end
		if game.GameId > 0 then
			coroutine.wrap(onGameId)()
		else
			waitForGameIdConnection = game:GetPropertyChangedSignal("GameId"):Connect(function ()
				waitForGameIdConnection:Disconnect()
				onGameId()
			end)
		end
	else
		--Start trying to load the place icon image
		--Web might not have this icon size generated, so we can poll asset-thumbnail/json and check
		--the JSON result for thumbnailFinal/Final to see when it's done being generated so we never
		--show a N/A image. This is how the console AppShell does it!
		coroutine.wrap(function()
			local placeId = WaitForPlaceId()

			local function tryGetFinalAsync()
				local imageUrl = nil
				local isGenerated = false
				local success, msg = pcall(function()
					imageUrl, isGenerated = AssetService:GetAssetThumbnailAsync(placeId, Vector2.new(576, 324), 1)
				end)

				if success and isGenerated == true and imageUrl then
					ContentProvider:PreloadAsync { imageUrl }
					placeIcon.Image = imageUrl

					if not backgroundFadeStarted then
						placeIcon.ImageTransparency = 0
					end

					return true
				end

				return false
			end

			while not tryGetFinalAsync() do end
		end)()
	end

	placeLabel = create 'TextLabel' {
		Name = 'PlaceLabel',
		BackgroundTransparency = 1,
		Size = UDim2.new(1, 0, 0, 80),
		Position = UDim2.new(0, 0, 0, 0),
		Font = Enum.Font.SourceSans,
		TextSize = isTenFootInterface and 48 or 24,
		TextWrapped = true,
		TextScaled = true,
		TextColor3 = COLORS.TEXT_COLOR,
		TextStrokeTransparency = 1,
		TextTransparency = 1,
		Text = "",
		TextXAlignment = Enum.TextXAlignment.Center,
		TextYAlignment = Enum.TextYAlignment.Bottom,
		ZIndex = 2,
		LayoutOrder = 2,
		Parent = infoFrame
	}

	local creatorContainer = create 'Frame' {
		Name = "Creator",
		BackgroundTransparency = 1,
		Size = UDim2.new(1, 0, 0, 48),
		LayoutOrder = 3,

		create 'UIListLayout' {
			FillDirection = Enum.FillDirection.Horizontal,
			HorizontalAlignment = Enum.HorizontalAlignment.Center,
			VerticalAlignment = Enum.VerticalAlignment.Center,
			SortOrder = Enum.SortOrder.LayoutOrder,
			Padding = UDim.new(0, 5)
		}
	}

	local byLabel, creatorIcon = nil, nil
	if isTenFootInterface then
		byLabel = create'TextLabel' {
			Name = "ByLabel",
			BackgroundTransparency = 1,
			Size = UDim2.new(0, 36, 0, 30),
			Position = UDim2.new(0, 0, 0, 80),
			Font = Enum.Font.SourceSansLight,
			TextSize = 36,
			TextScaled = true,
			TextColor3 = COLORS.TEXT_COLOR,
			TextStrokeTransparency = 1,
			Text = "By",
			TextXAlignment = Enum.TextXAlignment.Left,
			TextYAlignment = Enum.TextYAlignment.Top,
			ZIndex = 2,
			Visible = true,
			Parent = infoFrame,
			LayoutOrder = 1
		}
		creatorIcon = create'ImageLabel' {
			Name = "CreatorIcon",
			BackgroundTransparency = 1,
			Size = UDim2.new(0, 30, 0, 30),
			Position = UDim2.new(0, 38, 0, 80),
			ImageTransparency = 0,
			Image = 'rbxasset://textures/ui/Shell/Icons/RobloxIcon24.png',
			ZIndex = 2,
			Visible = true,
			Parent = infoFrame,
			LayoutOrder = 2
		}
	end

	creatorLabel = create 'TextLabel' {
		Name = 'CreatorLabel',
		BackgroundTransparency = 1,
		Size = UDim2.new(1, 0, 0, 30),
		Position = UDim2.new(0, 0, 0, 80),
		Font = Enum.Font.SourceSansLight,
		TextSize = isTenFootInterface and 36 or 18,
		TextWrapped = true,
		TextScaled = true,
		TextColor3 = COLORS.TEXT_COLOR,
		TextStrokeTransparency = 1,
		Text = "",
		TextXAlignment = Enum.TextXAlignment.Center,
		TextYAlignment = Enum.TextYAlignment.Center,
		ZIndex = 2,
		LayoutOrder = 4,
		Parent = infoFrame
	}

	if isTenFootInterface then
		creatorContainer.Parent = infoFrame

		byLabel.TextScaled = false
		byLabel.Parent = creatorContainer
		byLabel.TextXAlignment = Enum.TextXAlignment.Center
		byLabel.TextYAlignment = Enum.TextYAlignment.Center

		creatorIcon.Parent = creatorContainer

		creatorLabel.Parent = creatorContainer
		creatorLabel.TextScaled = false
		creatorLabel.TextWrapped = false
		creatorLabel.Position = UDim2.new(0, 72, 0, 80)
		creatorLabel.Size = UDim2.new(0, creatorLabel.TextBounds.X, 1, 0)
	end

    coroutine.wrap(function()
        RunService.RenderStepped:wait()
        RunService.RenderStepped:wait()
        layoutIsReady = true

        placeLabel.TextTransparency = 0

        local uiMessage = uiMessageFrame.UiMessage
        if uiMessage.Text ~= "" then
            uiMessage.TextTransparency = 0
        end
    end)()

	if not FFlagConnectionScriptEnabled or isTenFootInterface then
		local errorFrame = create 'Frame' {
			Name = 'ErrorFrame',
			BackgroundColor3 = COLORS.ERROR,
			BorderSizePixel = 0,
			Position = UDim2.new(0.25,0,0,0),
			Size = UDim2.new(0.5, 0, 0, 80),
			ZIndex = 8,
			Visible = false,
			Parent = screenGui,

			create 'TextLabel' {
				Name = "ErrorText",
				BackgroundTransparency = 1,
				Size = UDim2.new(1, 0, 1, 0),
				Font = Enum.Font.SourceSansBold,
				TextSize = 14,
				TextWrapped = true,
				TextColor3 = COLORS.TEXT_COLOR,
				Text = "",
				ZIndex = 8
			}
		}
	else
		if not loadErrorHandlerFromEngine and FFlagConnectErrorHandlerInLoadingScript then
			ScriptContext:AddCoreScriptLocal("Connection", RobloxGui)
		end
	end

	while not game:GetService("CoreGui") do
		if FFlagLoadTheLoadingScreenFaster then
			RunService.RenderStepped:wait()
		else
			wait()
		end
	end

	screenGui.Parent = CoreGui

	infoFrame.RootLocalizationTable = CoreGui:FindFirstChild("CoreScriptLocalization")

	currScreenGui = screenGui

	local function onResized()
		local isPortrait = screenGui.AbsoluteSize.X < screenGui.AbsoluteSize.Y

		infoFrame.Position = UDim2.new(0.5, 0, 0.5, 0)
		infoFrame.AnchorPoint = Vector2.new(0.5, 0.5)
		infoFrame.Size = UDim2.new(0.75, 0, 1, 0)
		infoFrameAspect.AspectRatio = isPortrait and 2/3 or 3/2

		placeLabel.Size = UDim2.new(1, 0, 0, isTenFootInterface and 120 or 80)

		if isTenFootInterface then
			creatorLabel.Size = UDim2.new(0, creatorLabel.TextBounds.X, 1, 0)
		else
			creatorLabel.Size = UDim2.new(1, 0, 0, 30)
		end

		infoFrameList.FillDirection = Enum.FillDirection.Vertical
		infoFrameList.HorizontalAlignment = Enum.HorizontalAlignment.Center
		infoFrameList.Padding = UDim.new(0, 0)
		textContainer.Parent = nil
		infoFrameList:ApplyLayout()

		placeLabel.TextXAlignment = Enum.TextXAlignment.Center
		placeLabel.AutoLocalize = false
	end
	onResized()
	screenGui:GetPropertyChangedSignal("AbsoluteSize"):connect(onResized)
end

---------------------------------------------------------
-- Main Script (show something now + setup connections)

-- start loading assets asap
InfoProvider:LoadAssets()
GenerateGui()
if isTenFootInterface then
	createTenfootCancelGui()
end

local fadeCycleTime = 1.7
local turnCycleTime = 2

local function spinnerEasingFunc(a, b, t)
	t = t * 2
	if t < 1 then
		return b / 2 * t*t*t + a
	else
		t = t - 2
		return b / 2 * (t * t * t + 2) + b
	end
end

if FFlagLoadingScreenDontBlockOnPolicyService then
	local showAntiAddictionNoticeStringEn = false
	if FFlagLoadingScreenShowBlankUntilPolicyServiceReturns then
		showAntiAddictionNoticeStringEn = "waiting"
	end
	-- PolicyService requires LocalPlayer to exist, which doesn't happen until
	-- after we connect to the server. If the server is full, this can take a
	-- very long time (like several minutes) to return a value. As a result, we
	-- call it asynchronously and default to false.
	coroutine.wrap(function()
		showAntiAddictionNoticeStringEn = PolicyService:IsSubjectToChinaPolicies()
	end)()

	renderSteppedConnection = RunService.RenderStepped:connect(function(dt)
		if not currScreenGui then return end
		if not currScreenGui:FindFirstChild("BlackFrame") then return end

		local infoFrame = currScreenGui.BlackFrame:FindFirstChild('InfoFrame')
		if infoFrame then
			-- set place name
			if placeLabel and placeLabel.Text == "" then
				placeLabel.Text = InfoProvider:GetGameName()
			end

			-- set creator name
			if creatorLabel and creatorLabel.Text == "" then
				if FFlagLoadingScreenShowBlankUntilPolicyServiceReturns and showAntiAddictionNoticeStringEn == "waiting" then
					creatorLabel.Text = ""
				elseif showAntiAddictionNoticeStringEn then
					creatorLabel.Text = antiAddictionNoticeStringEn
				else
					local creatorName = InfoProvider:GetCreatorName()
					if creatorName ~= "" then
						if isTenFootInterface then
							creatorLabel.Text = creatorName
							creatorLabel.Size = UDim2.new(0, creatorLabel.TextBounds.X, 1, 0)
						else
							creatorLabel.Text = "By ".. creatorName
						end
					end
				end
			end
		end

		local currentTime = tick()
		local fadeAmount = dt * fadeCycleTime

		local spinnerImage = currScreenGui.BlackFrame.GraphicsFrame.LoadingImage
		local timeInCycle = currentTime % turnCycleTime
		local cycleAlpha = spinnerEasingFunc(0, 1, timeInCycle / turnCycleTime)
		spinnerImage.Rotation = cycleAlpha * 360


		if not isTenFootInterface then
			if currentTime - startTime > 5 and currScreenGui.BlackFrame.CloseButton.ImageTransparency > 0 then
				currScreenGui.BlackFrame.CloseButton.ImageTransparency = currScreenGui.BlackFrame.CloseButton.ImageTransparency - fadeAmount

				if currScreenGui.BlackFrame.CloseButton.ImageTransparency <= 0 then
					currScreenGui.BlackFrame.CloseButton.Active = true
				end
			end
		end
	end)
else
	coroutine.wrap(function()
		local showAntiAddictionNoticeStringEn = PolicyService:IsSubjectToChinaPolicies()

		renderSteppedConnection = RunService.RenderStepped:connect(function(dt)
			if not currScreenGui then return end
			if not currScreenGui:FindFirstChild("BlackFrame") then return end

			local infoFrame = currScreenGui.BlackFrame:FindFirstChild('InfoFrame')
			if infoFrame then
				-- set place name
				if placeLabel and placeLabel.Text == "" then
					placeLabel.Text = InfoProvider:GetGameName()
				end

				-- set creator name
				if creatorLabel and creatorLabel.Text == "" then
					if showAntiAddictionNoticeStringEn then
						creatorLabel.Text = antiAddictionNoticeStringEn
					else
						local creatorName = InfoProvider:GetCreatorName()
						if creatorName ~= "" then
							if isTenFootInterface then
								creatorLabel.Text = creatorName
								creatorLabel.Size = UDim2.new(0, creatorLabel.TextBounds.X, 1, 0)
							else
								creatorLabel.Text = "By ".. creatorName
							end
						end
					end
				end
			end

			local currentTime = tick()
			local fadeAmount = dt * fadeCycleTime

			local spinnerImage = currScreenGui.BlackFrame.GraphicsFrame.LoadingImage
			local timeInCycle = currentTime % turnCycleTime
			local cycleAlpha = spinnerEasingFunc(0, 1, timeInCycle / turnCycleTime)
			spinnerImage.Rotation = cycleAlpha * 360


			if not isTenFootInterface then
				if currentTime - startTime > 5 and currScreenGui.BlackFrame.CloseButton.ImageTransparency > 0 then
					currScreenGui.BlackFrame.CloseButton.ImageTransparency = currScreenGui.BlackFrame.CloseButton.ImageTransparency - fadeAmount

					if currScreenGui.BlackFrame.CloseButton.ImageTransparency <= 0 then
						currScreenGui.BlackFrame.CloseButton.Active = true
					end
				end
			end
		end)
	end)()
end

-- use the old error frame when on XBox
if not FFlagConnectionScriptEnabled or isTenFootInterface then
	local errorImage

	GuiService.ErrorMessageChanged:connect(function()
		if GuiService:GetErrorMessage() ~= '' then
			--TODO: Remove this reference to Utility
			local utility = require(RobloxGui.Modules.Settings.Utility)
			if isTenFootInterface then
				currScreenGui.ErrorFrame.Size = UDim2.new(1, 0, 0, 144)
				currScreenGui.ErrorFrame.Position = UDim2.new(0, 0, 0, 0)
				currScreenGui.ErrorFrame.BackgroundColor3 = COLORS.BACKGROUND_COLOR
				currScreenGui.ErrorFrame.BackgroundTransparency = 0.5
				currScreenGui.ErrorFrame.ErrorText.TextSize = 36
				currScreenGui.ErrorFrame.ErrorText.Position = UDim2.new(.3, 0, 0, 0)
				currScreenGui.ErrorFrame.ErrorText.Size = UDim2.new(.4, 0, 0, 144)
				if errorImage == nil then
					errorImage = Instance.new("ImageLabel")
					errorImage.Image = "rbxasset://textures/ui/ErrorIconSmall.png"
					errorImage.Size = UDim2.new(0, 96, 0, 79)
					errorImage.Position = UDim2.new(0.228125, 0, 0, 32)
					errorImage.ZIndex = 9
					errorImage.BackgroundTransparency = 1
					errorImage.Parent = currScreenGui.ErrorFrame
				end
			elseif utility:IsSmallTouchScreen() then
				currScreenGui.ErrorFrame.Size = UDim2.new(0.5, 0, 0, 40)
			end

			local errorCode = GuiService:GetErrorCode()
			local errorMessage = GuiService:GetErrorMessage()
			if not FFlagShowConnectionErrorCode then
				currScreenGui.ErrorFrame.ErrorText.Text = errorMessage
			else
				if not errorCode then
					currScreenGui.ErrorFrame.ErrorText.Text = ("%s (Error Code: -1)"):format(errorMessage)
				else
					currScreenGui.ErrorFrame.ErrorText.Text = ("%s (Error Code: %d)"):format(errorMessage, errorCode.Value)
				end
			end

			currScreenGui.ErrorFrame.Visible = true
			local blackFrame = currScreenGui:FindFirstChild('BlackFrame')
			if blackFrame then
				blackFrame.CloseButton.ImageTransparency = 0
				blackFrame.CloseButton.Active = true
			end
		else
			currScreenGui.ErrorFrame.Visible = false
		end
	end)
end

if FFlagBackButtonWhileLoadingGoesBackToApp then
	backButtonConnection = GuiService.ShowLeaveConfirmation:Connect(function()
		-- When OS back button is pressed during loading screen, exit
		-- immediately. Behaves the same as the close button.
		game:Shutdown()
	end)
end

GuiService.UiMessageChanged:connect(function(type, newMessage)
	if type == Enum.UiMessageType.UiMessageInfo then
		local blackFrame = currScreenGui and currScreenGui:FindFirstChild('BlackFrame')
		if blackFrame then
			local infoFrame = blackFrame:FindFirstChild("InfoFrame")
            if infoFrame then
                local uiMessage = infoFrame.UiMessageFrame.UiMessage
                uiMessage.Text = newMessage
                if newMessage ~= '' and layoutIsReady then
                    uiMessage.TextTransparency = 0
                else
                    uiMessage.TextTransparency = 1
                end
            end
		end
	end
end)

if not FFlagConnectionScriptEnabled and GuiService:GetErrorMessage() ~= '' then
	currScreenGui.ErrorFrame.ErrorText.Text = GuiService:GetErrorMessage()
	currScreenGui.ErrorFrame.Visible = true
end


local function stopListeningToRenderingStep()
	if renderSteppedConnection then
		renderSteppedConnection:disconnect()
		renderSteppedConnection = nil
	end
end

local function disconnectAndCloseHealthStat()
	if connectionHealthCon then
		connectionHealthCon:disconnect()
		connectionHealthCon = nil
		GuiService:CloseStatsBasedOnInputString("ConnectionHealth")
	end
end

local function fadeAndDestroyBlackFrame(blackFrame)
	if destroyingBackground then return end
	destroyingBackground = true
	spawn(function()
		local infoFrame = blackFrame:FindFirstChild("InfoFrame")
		local graphicsFrame = blackFrame:FindFirstChild("GraphicsFrame")

		local infoFrameDescendants = infoFrame:GetDescendants()
		local transparency = 0
		local rateChange = 1.8
		local lastUpdateTime = nil

		--Notify everything else to stop messing with transparency to avoid ugly fighting effects
		backgroundFadeStarted = true
		if tweenPlaceIcon then
			tweenPlaceIcon:Cancel()
			tweenPlaceIcon = nil
		end

		while transparency < 1 do
			RunService.RenderStepped:wait()
			if not lastUpdateTime then
				lastUpdateTime = tick()
			else
				local newTime = tick()
				transparency = transparency + rateChange * (newTime - lastUpdateTime)
				for i = 1, #infoFrameDescendants do
					local child = infoFrameDescendants[i]
					if child:IsA('TextLabel') then
						child.TextTransparency = transparency
					elseif child:IsA('ImageLabel') then
						child.ImageTransparency = transparency
					end
				end
				graphicsFrame.LoadingImage.ImageTransparency = transparency
				blackFrame.BackgroundTransparency = transparency

				lastUpdateTime = newTime
			end
		end
		if blackFrame ~= nil then
			stopListeningToRenderingStep()
			blackFrame:Destroy()
		end

		if waitForGameIdConnection then
			waitForGameIdConnection:Disconnect()
		end

		if loadingImageInputBeganConn then
			loadingImageInputBeganConn:disconnect()
		end

		if backButtonConnection then
			backButtonConnection:Disconnect()
		end

        if connectionHealthShown then
			if UserInputService.TouchEnabled == true and UserInputService.MouseEnabled == false then
				connectionHealthCon = game:GetService("UserInputService").InputBegan:connect(function()
					disconnectAndCloseHealthStat()
				end)
			else
				GuiService:CloseStatsBasedOnInputString("ConnectionHealth")
			end
		else
			GuiService:CloseStatsBasedOnInputString("ConnectionHealth")
		end
    end)
end

local function destroyLoadingElements(instant)
	if not currScreenGui then return end
	if destroyedLoadingGui then return end
	destroyedLoadingGui = true

	local guiChildren = currScreenGui:GetChildren()
	for i=1, #guiChildren do
		-- need to keep this around in case we get a connection error later
		if guiChildren[i].Name ~= "ErrorFrame" then
			if guiChildren[i].Name == "BlackFrame" and not instant then
				fadeAndDestroyBlackFrame(guiChildren[i])
			else
				guiChildren[i]:Destroy()
			end
		end
	end
end

local function waitForCharacterLoaded()
	if Players.CharacterAutoLoads then
		local localPlayer = Players.LocalPlayer
		if not localPlayer then
			Players:GetPropertyChangedSignal("LocalPlayer"):wait()
			localPlayer = Players.LocalPlayer
		end
		if not localPlayer.Character then
			localPlayer.CharacterAdded:wait()
		end
	end
end

local function handleRemoveDefaultLoadingGui(instant)
	if isTenFootInterface then
		ContextActionService:UnbindCoreAction('CancelGameLoad')
	end
	destroyLoadingElements(instant)
	ReplicatedFirst:SetDefaultLoadingGuiRemoved()
end

local function handleFinishedReplicating()
	if #ReplicatedFirst:GetChildren() == 0 then
		if game:IsLoaded() then
			waitForCharacterLoaded()
			handleRemoveDefaultLoadingGui()
		else
			local gameLoadedCon = nil
			gameLoadedCon = game.Loaded:connect(function()
				gameLoadedCon:disconnect()
				gameLoadedCon = nil
				waitForCharacterLoaded()
				handleRemoveDefaultLoadingGui()
			end)
		end
	else
		wait(5) -- make sure after 5 seconds we remove the default gui, even if the user doesn't
		handleRemoveDefaultLoadingGui()
	end
end

if debugMode then
	warn("Not destroying loading screen because debugMode is true")
	return
end
ReplicatedFirst.FinishedReplicating:connect(handleFinishedReplicating)
if ReplicatedFirst:IsFinishedReplicating() then
	handleFinishedReplicating()
end

ReplicatedFirst.RemoveDefaultLoadingGuiSignal:connect(handleRemoveDefaultLoadingGui)
if ReplicatedFirst:IsDefaultLoadingGuiRemoved() then
	handleRemoveDefaultLoadingGui()
end

coroutine.wrap(function()
	if not VRService.VREnabled then
		VRService:GetPropertyChangedSignal("VREnabled"):Wait()
	end
	handleRemoveDefaultLoadingGui(true)
	require(RobloxGui.Modules.LoadingScreen3D)
end)()
