
print("Config Load")

local placeId = {PlaceId}
local port = {NetworkPort}
local gameId = {PlaceId}
local CreatorId = {CreatorId}
local CreatorType = {CreatorType}
local TempPlaceAccessKey = "{TempPlaceAccessKey}"
local sleeptime = 1
local access = "{AuthToken}"
local JobId = "{JobId}"
local BaseURL = "http://www.nexium.fit"
local BaseDomain = "nexium.fit"
local timeout = 15

local HttpService = game:GetService("HttpService")
local Players = game:GetService("Players")
local ScriptContext = game:GetService("ScriptContext")

print("Starting server for place "..tostring(placeId).." on port "..tostring(port).." and job id "..JobId)

------------------- UTILITY FUNCTIONS --------------------------

function waitForChild(parent, childName)
	while true do
		local child = parent:findFirstChild(childName)
		if child then
			return child
		end
		parent.ChildAdded:wait()
	end
end

function onDied(victim, humanoid)
	return
end

-----------------------------------END UTILITY FUNCTIONS -------------------------

-----------------------------------"CUSTOM" SHARED CODE----------------------------------

pcall(function() settings().Network.UseInstancePacketCache = true end)
pcall(function() settings().Network.UsePhysicsPacketCache = true end)
pcall(function() settings()["Task Scheduler"].PriorityMethod = Enum.PriorityMethod.AccumulatedError end)


settings().Network.PhysicsSend = Enum.PhysicsSendMethod.TopNErrors
settings().Network.ExperimentalPhysicsEnabled = true
settings().Network.WaitingForCharacterLogRate = 100
pcall(function() settings().Diagnostics:LegacyScriptMode() end)

-----------------------------------START GAME SHARED SCRIPT------------------------------

local RobloxPlacesList = {{
    4378
}}

function isPlaceOwnedByRoblox( place_id )
    for _, id in pairs( RobloxPlacesList ) do
        if id == place_id then
            return true
        end
    end
    return false
end

local assetId = placeId -- might be able to remove this now

local scriptContext = game:GetService('ScriptContext')
scriptContext.ScriptsDisabled = true

game:SetPlaceID(assetId, isPlaceOwnedByRoblox(assetId))
pcall(function () if universeId ~= nil then game:SetUniverseId(universeId) end end)
pcall(function() game.JobId = JobId end)
game:GetService("ChangeHistoryService"):SetEnabled(false)

if CreatorType == 1 then
    CreatorType = Enum.CreatorType.User
elseif CreatorType == 2 then
    CreatorType = Enum.CreatorType.Group
else
    CreatorType = Enum.CreatorType.User
end

pcall(function() game:SetCreatorID(CreatorId, CreatorType) end)

-- establish this peer as the Server
local ns = game:GetService("NetworkServer")

local badgeUrlFlagExists, badgeUrlFlagValue = pcall(function () return settings():GetFFlag("NewBadgeServiceUrlEnabled") end)
local newBadgeUrlEnabled = badgeUrlFlagExists and badgeUrlFlagValue
if BaseURL~=nil then
	local apiProxyUrl = string.gsub(BaseURL, "http://www", "https://api")    -- hack - passing domain (ie "sitetest1.robloxlabs.com") and appending "https://api." to it would be better

	pcall(function() game:GetService("Players"):SetAbuseReportUrl(BaseURL .. "/AbuseReport/InGameChatHandler.ashx") end)
	pcall(function() game:GetService("ScriptInformationProvider"):SetAssetUrl(BaseURL .. "/Asset/") end)
	pcall(function() game:GetService("ContentProvider"):SetBaseUrl(BaseURL .. "/") end)
	pcall(function() game:GetService("Players"):SetChatFilterUrl(BaseURL .. "/Game/ChatFilter.ashx") end)
	
	if gameCode then
		game:SetVIPServerId(tostring(gameCode))
	end

	game:GetService("BadgeService"):SetPlaceId(placeId)

	if access~=nil then
		game:GetService("BadgeService"):SetAwardBadgeUrl(BaseURL .. "/Game/Badge/AwardBadge.ashx?UserID=%d&BadgeID=%d&PlaceID=%d")
		game:GetService("BadgeService"):SetHasBadgeUrl(BaseURL .. "/Game/Badge/HasBadge.ashx?UserID=%d&BadgeID=%d")
		game:GetService("BadgeService"):SetIsBadgeDisabledUrl(BaseURL .. "/Game/Badge/IsBadgeDisabled.ashx?BadgeID=%d&PlaceID=%d")

		game:GetService("FriendService"):SetMakeFriendUrl(BaseURL .. "/Friend/CreateFriend?firstUserId=%d&secondUserId=%d")
		game:GetService("FriendService"):SetBreakFriendUrl(BaseURL .. "/Friend/BreakFriend?firstUserId=%d&secondUserId=%d")
		game:GetService("FriendService"):SetGetFriendsUrl(BaseURL .. "/Friend/AreFriends?userId=%d")
        game:GetService("FriendService"):SetCreateFriendRequestUrl(BaseURL .. "/Friend/CreateFriendRequest?requesterUserId=%d&requestedUserId=%d")
        game:GetService("FriendService"):SetDeleteFriendRequestUrl(BaseURL .. "/Friend/DeleteFriendRequest?requesterUserId=%d&requestedUserId=%d")
	end
	game:GetService("BadgeService"):SetIsBadgeLegalUrl("")
	game:GetService("InsertService"):SetBaseSetsUrl(BaseURL .. "/Game/Tools/InsertAsset.ashx?nsets=10&type=base")
	game:GetService("InsertService"):SetUserSetsUrl(BaseURL .. "/Game/Tools/InsertAsset.ashx?nsets=20&type=user&userid=%d")
	game:GetService("InsertService"):SetCollectionUrl(BaseURL .. "/Game/Tools/InsertAsset.ashx?sid=%d")
	game:GetService("InsertService"):SetAssetUrl(BaseURL .. "/Asset/?id=%d")
	game:GetService("InsertService"):SetAssetVersionUrl(BaseURL .. "/Asset/?assetversionid=%d")

    game:GetService("Players"):SetSaveDataUrl(BaseURL .. "/persistence/legacy/save?placeId=" .. tostring(placeId) .. "&userId=%d")
    game:GetService("Players"):SetLoadDataUrl(BaseURL .. "/persistence/legacy/load?placeId=" .. tostring(placeId) .. "&userId=%d")
	
	--pcall(function() loadfile(BaseURL .. "/Game/LoadPlaceInfo.ashx?PlaceId=" .. placeId)() end)
	
	--pcall(function() 
	--			if access then
	--				loadfile(BaseURL .. "/Game/PlaceSpecificScript.ashx?PlaceId=" .. placeId .. "&" .. access)()
	--			end
	--		end)
end

--pcall(function() game:GetService("NetworkServer"):SetIsPlayerAuthenticationRequired(true) end)
settings().Diagnostics.LuaRamLimit = 0
print("Configured Server")
local StartTime = tick()
local StoppingServer = false

local function GetPlayerByUserId( userId )
    for _, player in pairs( Players:GetPlayers() ) do
        if player.userId == userId then
            return player
        end
    end
    return nil
end

local function ReportServerPlayers(IgnoreThisPlayer)
    --if StoppingServer then return end
    local success, message = pcall(function()
        local PlayerList = {{}}
        for _, player in pairs(Players:GetChildren()) do
            if player:IsA("Player") and player ~= IgnoreThisPlayer then
                table.insert(PlayerList, {{
                    ["UserId"] = player.userId,
                    ["Name"] = player.Name
                }})
            end
        end
        local MessagePayload = HttpService:JSONEncode({{
            ["AuthToken"] = access,
            ["JobId"] = JobId,
            ["Players"] = PlayerList
        }})
        local ResponseData = game:HttpPost(BaseURL.."/internal/gameserver/reportplayers", MessagePayload, true, "application/json")
        local ResponseJSON = HttpService:JSONDecode(ResponseData)
        for _, player in pairs(ResponseJSON["bad"]) do -- This is a list of players that need to be kicked from the server
            local TargetPlayer = GetPlayerByUserId(player)
            if TargetPlayer ~= nil then
                print("Kicking Player", tostring(player), "because was requested by backend")
                TargetPlayer:Kick("There was an issue authenticating you, please contact support.")
                TargetPlayer:Destroy()
            end
        end
    end)
    if not success then
        print("ReportServerPlayers failed:", message)
    end
end

local function ReportServerStats()
    if StoppingServer then return end
    local success, message = pcall(function()
        local MessagePayload = HttpService:JSONEncode({{
            ["AuthToken"] = access,
            ["JobId"] = JobId,
            ["PlaceId"] = placeId,
            ["ServerAliveTime"] = (tick() - StartTime) + 1
        }})
        game:HttpPost(BaseURL.."/internal/gameserver/reportstats", MessagePayload, false, "application/json")
    end)
    if not success then
        print("ReportServerStats failed:", message)
    end
end

local function ReportServerShutdown()
    local success, message = pcall(function()
        local MessagePayload = HttpService:JSONEncode({{
            ["AuthToken"] = access,
            ["JobId"] = JobId,
            ["PlaceId"] = placeId,
            ["ServerAliveTime"] = tick() - StartTime
        }})
        game:HttpPost(BaseURL.."/internal/gameserver/reportshutdown", MessagePayload, false, "application/json")
    end)
    if not success then
        print("ReportServerShutdown failed:", message)
    end
end

local function AuthenticatePlayer( player )
    local success, message = pcall(function()
        local VerificationTicket = string.match( player.CharacterAppearance, BaseDomain.."/Asset/CharacterFetch.ashx%?userId=%d+%&t=(%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x)%&legacy=1$")
        if VerificationTicket == nil then
            print("Failed to get VerificationTicket for player", player.Name)
            return false
        end

        local MessagePayload = HttpService:JSONEncode({{
            ["AuthToken"] = access,
            ["JobId"] = JobId,
            ["PlaceId"] = placeId,
            ["ServerAliveTime"] = tick() - StartTime,
            ["UserId"] = player.userId,
            ["VerificationTicket"] = VerificationTicket,
            ["CharacterAppearance"] = player.CharacterAppearance,
            ["Username"] = player.Name
        }})
        local ResponseData = game:HttpPost(BaseURL.."/internal/gameserver/verifyplayer", MessagePayload, true, "application/json")
        local ResponseJSON = HttpService:JSONDecode(ResponseData)
        return ResponseJSON["authenticated"]
    end)
    if not success then
        print("AuthenticatePlayer failed:", message)
        return false
    end
    return message
end

local function ShutdownServer()
    StoppingServer = true
    ReportServerShutdown()
    ScriptContext.ScriptsDisabled = true
    ns:Stop(1000)
    game:Shutdown()
end

local TotalPlayersJoined = 0
game:GetService("Players").PlayerAdded:connect(function(player)
    local StartTime = tick()
    local CharacterURL
    repeat
        if string.find(player.CharacterAppearance, BaseDomain.."/Asset/CharacterFetch.ashx%?userId=%d+") then
            CharacterURL = player.CharacterAppearance
        end
        wait(0.1)
    until CharacterURL ~= nil or tick() - StartTime > 1
    if CharacterURL == nil then
        player:Kick("There was an issue authenticating you, please contact support.")
        print("Failed to get UserId for player", player.Name, "because CharacterURL was nil")
        return
    end

    local UserId = tonumber(string.match(CharacterURL, BaseDomain.."/Asset/CharacterFetch.ashx%?userId=(%d+)%&t=%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x%&legacy=1$"))

    if UserId ~= nil then
        player.userId = UserId
    else
        player:Kick("There was an issue authenticating you, please contact support.")
        print("Failed to get UserId for player", player.Name, CharacterURL)
        return
    end

    local IsPlayerAuthenticated = AuthenticatePlayer(player)
    if IsPlayerAuthenticated then
        player.DataComplexityLimit = 1024 * 1024 * 1
        player.CharacterAppearance = BaseURL.."/Asset/CharacterFetch.ashx?userId="..tostring(player.userId).."&legacy=1"
        ReportServerPlayers()
        player:LoadData()
        TotalPlayersJoined = TotalPlayersJoined + 1

        local PlayerChangedConnection
        PlayerChangedConnection = player.Changed:connect(function(property)
            if property == "Name" then
                ReportServerPlayers()
            end
        end)

        coroutine.wrap(function()
            while true do
                wait(120)
                if StoppingServer then break end
                if player.Parent == nil then break end
                pcall(function() player:SaveData() end)
            end
        end)()
    else
        player:Kick("There was an issue authenticating you, please contact support.")
        print("Failed to authenticate player", player.Name)
        return
    end
end)


game:GetService("Players").PlayerRemoving:connect(function(player)
	ReportServerPlayers(player)
    pcall(function() player:SaveData() end)
    local PlayerCount = #Players:GetPlayers()
    if PlayerCount == 0 then
        wait(10) -- Wait 10 seconds to see if anyone rejoins
        PlayerCount = #Players:GetPlayers()
        if PlayerCount == 0 then
            ShutdownServer()
        end
    end
end)

local onlyCallGameLoadWhenInRccWithAccessKey = newBadgeUrlEnabled
wait()
-- load the game
print("Loading game")

local success, result = pcall(function()
    game:Load(BaseURL .. "/asset/?id=" .. placeId.."&access=".. TempPlaceAccessKey)
end)
if not success then
    print("Failed to Load Place File, unsupported file format")
    local ErrorMessage = Instance.new("Message", workspace)
    ErrorMessage.Text = "Failed to Load Place File, unsupported file format"
end

--Players:SetChatStyle(Enum.ChatStyle.ClassicAndBubble)
-- Now start the connection
ns:Start(port, sleeptime) 

if timeout then
	scriptContext:SetTimeout(timeout)
end
scriptContext.ScriptsDisabled = false

-- StartGame --
Game:GetService("RunService"):Run()
ReportServerStats()

coroutine.wrap(function()
    while true do
        wait(10)
        if StoppingServer then break end
        ReportServerStats()
        ReportServerPlayers()
    end
end)()

coroutine.wrap(function()
    wait(120) -- Wait 2 minutes to check if anyone has joined
    if TotalPlayersJoined == 0 then
        print("Stopping server, no players joined past 2 minutes.")
        ShutdownServer()
    end
end)()

pcall(function() Game:GetService("ScriptContext"):AddStarterScript(37801172) end)
