---@class UGCGameState_C:BP_UGCGameState_C ---@field WrapperClass UClass ---@field ShootWeaponClass UClass --Edit Below-- UGCGameSystem.UGCRequire('Script.Common.ue_enum_custom') require('Script.Global.Global'); local UGCGameState = {}; UGCGameState.ServerTime = 0; --- 服务器时间 UGCGameState.ServerTimeDiff = 0; --- 服务器时间 - 客户端时间的差值;如果想在服务器求客户端时间:当前时间 - 该值; UGCGameState.MapIndex = 0; --- 地图数据 ---@type table UGCGameState.ArchiveData = {}; --- 玩家存档数据 ---@type table UGCGameState.AccountInfo = {}; --- 玩家账户信息 UGCGameState.CountDownTime = 0; --- 游戏进程数据 UGCGameState.MiniInitData = {}; --- 一些在最前面的数据需要特定传输的 UGCGameState.PlayerDisconnect = {}; --- 玩家是否掉线 UGCGameState.EnableShovel = false; --- 是否可以滑铲 UGCGameState.MiniState = nil; function UGCGameState:ReceiveBeginPlay() GlobalInit.InitGlobalVar(); self:InitServerTime(); self.bIsOpenShovelingAbility = DefaultSettings.OpenShovel and self.EnableShovel; if GlobalBeginTool then GlobalBeginTool:ReceiveBeginPlay(); end table.func(MiniManager, "Init", self); end function UGCGameState:ReceiveTick(DeltaTime) if GlobalTickTool then GlobalTickTool:ReceiveTick(DeltaTime, self:GetServerTime()); end end function UGCGameState:InitServerTime() if IsServer then self.ServerTime = 1; -- 设置好 DOREPONCE(self, "ServerTime"); end end function UGCGameState:ReceiveEndPlay() DefaultSettings = nil; EventTypes = nil; GameState = nil; LocalPlayerKey = nil; LocalPlayerController = nil; LocalTeamPlayers = nil; EnemyTeamPlayers = nil; UnableTable(); UnableTool(); UnableWidgetManager(); UGCGameSystem.GameState = nil; ---@type table ArchiveTable = nil; AccountTable = nil; end function UGCGameState:GetAvailableServerRPCs() return "LoadMap" , "CheckServerTime" , "UIAlready" , "MiniGameReceiveRPC" end function UGCGameState:GetReplicatedProperties() return { "MapIndex", "Lazy" } , { "ArchiveData", "Lazy" } , { "AccountInfo", "Lazy" } , "CountDownTime" , { "ServerTime", "Lazy" } , { "LoadMapName", "Lazy" } , { "CurrMiniType", "Lazy" } , { "MiniInfo", "Lazy" } , { "MiniInitData", "Lazy" } , { "MiniState", "Lazy" } , "EnableShovel" end ----------------------------------------- 玩家属性 ----------------------------------------- ---@param InPlayerKey PlayerKey function UGCGameState:HandleAccountInfo(InPlayerKey) if InPlayerKey == nil then self.AccountInfo = TableHelper.DeepCopyTable(AccountTable); else self.AccountInfo[InPlayerKey] = TableHelper.DeepCopyTable(AccountTable[InPlayerKey]); end DOREPONCE(self, "AccountInfo"); end function UGCGameState:HandleArchiveData(InPlayerKey, IgnoreList) if IgnoreList == nil then IgnoreList = {}; end UGCLogSystem.LogTree(string.format("[UGCGameState:HandleArchiveData] PlayerKey = %s, IgnoreList =", tostring(InPlayerKey)), IgnoreList); local List = {}; for i, v in pairs(IgnoreList) do List[v] = 1; end if InPlayerKey == nil then self.ArchiveData = {}; for i, v in pairs(ArchiveTable) do self:HandleOnePlayerArchiveData(i, List, IgnoreList); end UGCLogSystem.Log("[UGCGameState:HandleArchiveData] 同步结束"); else self.ArchiveData[InPlayerKey] = {}; for i, v in pairs(ArchiveTable[InPlayerKey]) do if List[i] == nil then self.ArchiveData[InPlayerKey][i] = TableHelper.DeepCopyTable(v); end end end DOREPONCE(self, "ArchiveData"); end function UGCGameState:HandleOnePlayerArchiveData(InPlayerKey, List, IgnoreList) self.ArchiveData[InPlayerKey] = {}; for i, v in pairs(ArchiveTable[InPlayerKey]) do if List[i] == nil then self.ArchiveData[InPlayerKey][i] = TableHelper.DeepCopyTable(v); end end end function UGCGameState:OnRep_ArchiveData() if LocalPlayerKey == nil then return ; end --- 保证就算复制一个也没问题~ for i, v in pairs(self.ArchiveData) do ArchiveTable[i] = v; end UGCLogSystem.LogTree(string.format("[UGCGameState:OnRep_ArchiveData] ArchiveTable ="), ArchiveTable) UGCEventSystem.SendEvent(EventTypes.UpdateArchiveData, ArchiveTable); end function UGCGameState:OnRep_AccountInfo() -- 检查当前玩了多少局 if LocalPlayerKey == nil then return end -- 显示拍脸图 AccountTable = self.AccountInfo; UGCLogSystem.LogTree(string.format("[UGCGameState:OnRep_AccountInfo] AccountTable ="), AccountTable) UGCEventSystem.SendEvent(EventTypes.UpdateAccountInfo, AccountTable); end ----------------------------------------- 地图加载 ----------------------------------------- --- 已经加载了的地图名称 UGCGameState.LoadMapName = nil; --- 加载地图(服务器/客户端都可以执行) ---@param Name string 地图索引 function UGCGameState:LoadMap(Name) -- 找到对应地图,然后进行加载 if self.LoadMapName ~= nil then self:UnloadMap({ self.LoadMapName }); end self.LoadMapName = Name; for i, v in pairs(LevelTable.LevelInfo) do if v.MapName == Name then self.MapIndex = i; DOREPONCE(self, "MapIndex"); break; end end UGCLogSystem.Log("[UGCGameState:LoadMap] 加载 %s", Name) LevelStreamTool.LoadStreamLevels({ Name }, { Object = self, Func = self.LoadMapFinish }, false); end function UGCGameState:OnRep_LoadMapName() if self.LoadMapName == nil then return; end UGCLogSystem.Log("[UGCGameState:OnRep_LoadMapName] LoadMapName = %s", self.LoadMapName); UGCEventSystem.SendEvent(EventTypes.OnMapLoaded, self.LoadMapName); table.func(MiniManager, "OnMapLoadComplete") end --- 随机加载地图 ---@param function UGCGameState:LoadRandomMap(InIndex) if IsServer then self:UnloadMap(); -- 判断当前是不是随即关卡 UGCLogSystem.LogTree(string.format("[UGCGameState:LoadMap] LevelTable.LevelInfo ="), LevelTable.LevelInfo) if InIndex == nil or InIndex == LevelTable.ELevelType.Random then self.MapIndex = LevelTable.GetRandomLevel(true, 1)[1]; else self.MapIndex = InIndex; end local MapNames = {}; table.insert(MapNames, LevelTable.LevelInfo[self.MapIndex].MapName); LevelStreamTool.LoadStreamLevels(MapNames, { Object = self, Func = self.LoadMapFinish }, false); DOREPONCE(self, "MapIndex"); else -- 发送 RPC 到服务器 UnrealNetwork.CallUnrealRPC(LocalPlayerController, self, "LoadMap", InIndex); end end function UGCGameState:OnRep_MapIndex() if self.MapIndex == 0 then return; end UGCLogSystem.Log("[UGCGameState:OnRep_MapIndex] 开始加载小地图 MapIndex = %d", self.MapIndex); -- 加载小地图 self:LoadMinimap(self.MapIndex); end --- 卸载地图 function UGCGameState:UnloadMap() if self.MapIndex ~= 0 then local MapNames = {}; table.insert(MapNames, LevelTable.LevelInfo[self.MapIndex].MapName) LevelStreamTool.UnLoadStreamLevels(MapNames, { Object = self, Func = self.UnLoadMapFinish }, false); self.MapIndex = 0; self.LoadMapName = nil; -- 重置 ClientAlready self.bInitOnce = false; DOREPONCE(self, "MapIndex"); end end --- 地图加载完成 function UGCGameState:LoadMapFinish() UGCLogSystem.Log("[UGCGameState:LoadMapFinish] 地图加载完成"); self:OnMapLoaded(); end function UGCGameState:UnLoadMapFinish() UGCLogSystem.Log("[UGCGameState:UnLoadMapFinish] 地图卸载成功") table.func(MiniManager, "OnMapUnLoadedComplete") DOREPONCE(self, "LoadMapName"); end --- 加载小地图 function UGCGameState:LoadMinimap(InIndex) UGCLogSystem.Log("[UGCGameState:LoadMinimap] 开始加载 %s 的小地图", LevelTable.LevelInfo[InIndex].MapName); local MinimapInfo = LevelTable.LevelInfo[InIndex].MiniMapInfo; if not table.isEmpty(MinimapInfo) then UGCLogSystem.Log("[UGCGameState:LoadMinimap] 开始添加"); UGCWidgetManagerSystem.ChangeMap(MinimapInfo.MapPath, MinimapInfo.MapCentre, MinimapInfo.MapSize, MinimapInfo.MapScale); end end function UGCGameState:OnMapLoaded() --- 加载地图上的东西 UGCLogSystem.Log("[UGCGameState:OnMapLoaded] 执行") -- 检测一下是否有默认地图 table.func(MiniManager, "OnMapLoadComplete") -- 此时同步一下即可,已经加载完毕了 DOREPONCE(self, "LoadMapName"); end ----------------------------------------- 游戏进程 ----------------------------------------- function UGCGameState:OnBeforeGameStart(InTime) if MiniManager then table.func(MiniManager, "OnBeforeGameStart", InTime); end end --- 游戏结束 function UGCGameState:OnGameEnded() -- 打开结算界面 if IsServer then UGCLogSystem.Log("[UGCGameState:OnGameEnded] 执行结束"); UGCEventSystem.SetTimer(self, function() for _, PC in pairs(UGCGameSystem.GetAllPlayerController(false)) do local Success = UGCGameSystem.SendPlayerSettlement(PC.PlayerKey); UGCLogSystem.Log("[UGCGameState:OnGameEnded] 执行是否成功:%s", tostring(Success)) local Pawn = UGCGameSystem.GetPlayerPawnByPlayerKey(PC.PlayerKey) if Pawn and UE.IsValid(Pawn) then UGCPawnSystem.LeavePawnState(Pawn, EPawnState.Move); Pawn:K2_DestroyActor(); end end end, 0.2); UE.CloseServer(); else if not WidgetManager:IsVisiblePanel(WidgetConfig.EUIType.GameEnd) then UGCLogSystem.Log("[UGCGameState:OnGameEnded] 显示 GameEnd 界面") WidgetManager:ShowPanel(WidgetConfig.EUIType.GameEnd, false, self.MiniInfo.GameWinner, self.MiniInfo.RoundWinners) WidgetManager:ClosePanel(WidgetConfig.EUIType.Main); end UGCLogSystem.LogTree(string.format("[UGCGameState:OnGameEnded] AllPanel ="), WidgetManager.AllPanel); -- 清空一些资源 WidgetManager:DestroyPanel(WidgetConfig.EUIType.AllWeapon); WidgetManager:DestroyPanel(WidgetConfig.EUIType.AllWeapon_Spectator); WidgetManager:DestroyPanel(WidgetConfig.EUIType.CustomSelectWeapon); --WidgetManager:DestroyPanel(WidgetConfig.EUIType.ShowCustomSelectWeaponBtn); WidgetManager:DestroyPanel(WidgetConfig.EUIType.ShowPK); WidgetManager:DestroyPanel(WidgetConfig.EUIType.ShowRankInheritance); end end --- 倒计时 function UGCGameState:OnGameProgress_Tick(InTime) UGCLogSystem.Log("[UGCGameState:OnGameProgress_Tick] 执行,InTime = %s", tostring(InTime)); self.CountDownTime = InTime; end function UGCGameState:OnRep_CountDownTime() UGCEventSystem.SendEvent(EventTypes.GameCountDownTimeChange, self.CountDownTime); table.func(MiniManager, "OnMiniGameTimeCount", self.CountDownTime) end --- 当前游戏时间,以服务器为基准(因为服务器不会改变) ---客户端准备好之后会发送一个 RPC 过来,之后就是 function UGCGameState:CheckServerTime(InPlayerKey, InTime) if IsServer then local PC = UGCGameSystem.GetPlayerControllerByPlayerKey(InPlayerKey) UnrealNetwork.CallUnrealRPC(PC, self, "CheckClientTime", UE.GetCurrentTime()); self:OnClientAlready(InPlayerKey); else UnrealNetwork.CallUnrealRPC(LocalPlayerController, self, "CheckServerTime", InPlayerKey, InTime); end end function UGCGameState:OnRep_ServerTime() -- 接收到之后就立刻发送 RPC 到服务器,或者也可以等一会 if self.ServerTime == 1 then if LocalPlayerKey == nil or LocalPlayerController == nil then UGCEventSystem.SetTimer(self, function() self:CheckServerTime(LocalPlayerKey, UE.GetCurrentTime()) end, 1.); return ; end self:CheckServerTime(LocalPlayerKey, UE.GetCurrentTime()) elseif self.ServerTime == 0 then else local Diff = self.ServerTime - UE.GetCurrentTime(); UGCLogSystem.Log("[UGCGameState:OnRep_ServerTime] Diff = %f", Diff) self:OnClientAlready(LocalPlayerKey); end end function UGCGameState:CheckClientTime(InTime) self.ServerTimeDiff = InTime - UE.GetCurrentTime(); UGCLogSystem.Log("[UGCGameState:CheckClientTime] self.ServerTimeDiff = %f", self.ServerTimeDiff); self:OnClientAlready(LocalPlayerKey); end UGCGameState.bInitOnce = {}; function UGCGameState:OnClientAlready(PlayerKey) if self.bInitOnce[PlayerKey] then return end self.bInitOnce[PlayerKey] = true; UGCEventSystem.SendEvent(EventTypes.ClientAlready); local ServerTime = self:GetServerTime() UGCLogSystem.Log("[UGCGameState:OnClientAlready] 执行 ServerTime = %f", ServerTime); if GlobalBeginTool then GlobalBeginTool:ReceiveClientAlready(); GlobalBeginTool = nil; end table.func(MiniManager, "OnClientAlready"); local PC = UGCGameSystem.GetPlayerControllerByPlayerKey(PlayerKey); if PC and UE.IsValid(PC) and (not PC:IsPureSpectator()) then PC:SendLast10Games(); PC:SendSelectWeapons(); end end UGCGameState.bInitOnce_UIAlready = false; function UGCGameState:UIAlready() if self.bInitOnce_UIAlready then return ; end self.bInitOnce_UIAlready = true; UGCLogSystem.Log("[UGCGameState:UIAlready] 执行") table.func(MiniManager, "OnUIAlready"); if IsClient then UnrealNetwork.CallUnrealRPC(LocalPlayerController, self, "UIAlready"); end end --- S & C : 获取服务器时间 ---@return float|nil function UGCGameState:GetServerTime() if IsServer then return UE.GetCurrentTime(); else if self.ServerTimeDiff == 0 then return nil; else return self.ServerTimeDiff + UE.GetCurrentTime(); end end end --- 提前重置操作 function UGCGameState:BeforeReset(InTime) for i, Pawn in pairs(UGCGameSystem.GetAllPlayerPawn()) do if Pawn and UE.IsValid(Pawn) then if Pawn then Pawn:K2_DestroyActor(); end UE.RespawnPlayer(d, InTime); end end self:ResetKill(); end function UGCGameState:ResetKill() -- 重置其他的 for i, v in pairs(UGCGameSystem.GetAllPlayerController(false)) do v:ResetGame(); end end --- 重置游戏 function UGCGameState:ResetGame() if IsServer then UnrealNetwork.CallUnrealRPC_Multicast(self, "ResetGame"); -- 重置击杀数 for i, v in pairs(UGCGameSystem.GetAllPlayerState(false)) do v:ResetKillNum(); end else WidgetManager:GetPanel(WidgetConfig.EUIType.Main, function(Widget) table.func(Widget, "OnGameStart"); end) end end ----------------------------------------- 界面 UI ----------------------------------------- --- 显示 UI ---@param InUIType EUIType ---@param IsShow boolean function UGCGameState:ShowUIByType(InUIType, IsShow, ...) if IsServer then UnrealNetwork.CallUnrealRPC_Multicast(self, "ShowUIByType", InUIType, IsShow, ...); else if IsShow then WidgetManager:ShowPanel(InUIType, false, ...); else WidgetManager:ClosePanel(InUIType); end end end ----------------------------------------- 场景物品 ----------------------------------------- ---@type BP_ResourceBase_C UGCGameState.ResourceActor = nil; --- 加载资源类,服务器,客户端都需要加载一次 ---@return BP_ResourceBase_C function UGCGameState:LoadResource() if self.ResourceActor == nil then self.ResourceActor = UE.FindActorByClass(ObjectPath.BP_ResourceBase); end UGCLogSystem.Log("[UGCGameState:LoadResource] %s", UGCGameSystem.GetUGCResourcesFullPath('Asset/Blueprint/UGCPlayerPawn.UGCPlayerPawn_C')) return self.ResourceActor; end -- 清除场景上的 Wrappers function UGCGameState:ClearWrappers() local InTable = {}; UE.FindActorsByClass(self:GetWrapperClass(), InTable, function(InIndex, InActor) return InActor:GetOwner() == nil; end); for i, v in pairs(InTable) do v:K2_DestroyActor(); end end ----------------------------------------- BUFF ----------------------------------------- ---@type AActor UGCGameState.MapCenterActor = nil; --- 获取中心点 ---@return AActor function UGCGameState:GetCenterPointLocation() if self.MapCenterActor ~= nil then return self.MapCenterActor:K2_GetActorLocation() end local CenterActors = {}; UE.FindActorsByClass(ObjectPath.BP_ActorStart, CenterActors, function(InIndex, InActor) return InActor:ActorHasTag("MapCenter"); end); UGCLogSystem.LogTree(string.format("[UGCGameState:GetCenterPointLocation] CenterActors ="), CenterActors) self.MapCenterActor = CenterActors[1]; if self.MapCenterActor ~= nil then return self.MapCenterActor:K2_GetActorLocation(); end return nil; end ----------------------------------------- 武器 ----------------------------------------- --- 获取武器基类 ---@return UClass 武器基类 function UGCGameState:GetShootWeaponClass() return self.ShootWeaponClass; end ---@return UClass function UGCGameState:GetWrapperClass() return self.WrapperClass; end ----------------------------------------- FUNCTIONAL ----------------------------------------- --- 发送自定义事件 function UGCGameState:SendEvent(IsSend, InPlayerKey, InEvent, ...) if IsServer then if IsSend then if InPlayerKey == nil then UnrealNetwork.CallUnrealRPC_Multicast(self, "SendEvent", false, nil, InEvent, ...); else UnrealNetwork.CallUnrealRPC(UGCGameSystem.GetPlayerControllerByPlayerKey(InPlayerKey), UGCGameSystem.GameState, "SendEvent", false, InPlayerKey, InEvent, ...); end else UGCEventSystem.SendEvent(InEvent, InPlayerKey, ...) end else -- 从客户端发送出去 if IsSend then UnrealNetwork.CallUnrealRPC(DefaultSettings.LocalPlayerController, UGCGameSystem.GameState, "SendEvent", false, DefaultSettings.LocalPlayerKey, InEvent, ...); else UGCEventSystem.SendEvent(InEvent, InPlayerKey, ...); end end end ----------------------------------------- MINI ----------------------------------------- --- 当前选择的 MiniType UGCGameState.CurrMiniType = -1; ---@param InMiniType int32 function UGCGameState:SelectMiniType(InMiniType) self.CurrMiniType = InMiniType; DOREPONCE(self, "CurrMiniType"); end function UGCGameState:OnRep_CurrMiniType() if self.CurrMiniType < 0 then return ; end table.func(MiniManager, "LoadMiniGame", self.CurrMiniType) end --- 发送 MiniGame RPC ---@param InFuncName string 函数名称 ---@vararg any function UGCGameState:SendMiniGameRPC(InFuncName, ...) if IsServer then UnrealNetwork.CallUnrealRPC_Multicast(self, "MiniGameReceiveRPC", InFuncName, true, ...); else UnrealNetwork.CallUnrealRPC(LocalPlayerController, self, "MiniGameReceiveRPC", InFuncName, false, ...); end end --- Mini Game 接收到 RPC ---@param InFuncName string 函数名称 ---@param SendFromServer bool 是否发自服务器 function UGCGameState:MiniGameReceiveRPC(InFuncName, SendFromServer, ...) if table.hasFunc(MiniManager, InFuncName) then table.func(MiniManager, InFuncName, ...) else if MiniManager ~= nil and MiniManager.CurrMiniMode then table.func(MiniManager.CurrMiniMode, InFuncName, ...) end end end --- 小游戏需要同步的数据 UGCGameState.MiniInfo = {}; function UGCGameState:SetMiniInfo(InMiniInfo) self.MiniInfo = InMiniInfo; DOREPONCE(self, "MiniInfo"); end function UGCGameState:OnRep_MiniInfo(InOld) if table.isEmpty(self.MiniInfo) then return ; end -- 发送出去 UGCLogSystem.LogTree(string.format("[UGCGameState:OnRep_MiniInfo] MiniInfo"), self.MiniInfo) if MiniManager then MiniManager.MiniInfo = self.MiniInfo; table.func(MiniManager, "OnRep_MiniInfo", InOld) end end --- 设置初始化数据,初始化数据不会更改,可以直接获取 function UGCGameState:SetMiniInitData(InKey, InVal) self.MiniInitData[InKey] = InVal; DOREPONCE(self, "MiniInitData"); end function UGCGameState:GetMiniInitData(InKey) return self.MiniInitData[InKey]; end --- 获取 MiniGame 的数据 function UGCGameState:GetMiniInfo(Name) if MiniManager then return table.func(MiniManager, "GetMiniInfo", Name); end return nil; end ----------------------------------------- 自定义函数 ----------------------------------------- --- 滑铲 function UGCGameState:OnRep_EnableShovel() if UGCEventSystem then UGCEventSystem.SendEvent(EventTypes.ChangeEnableShovel, self.EnableShovel); end end function UGCGameState:SetMiniState(InState) self.MiniState = InState; UGCLogSystem.Log("[UGCGameState:SetMiniState] InState = %s", InState); DOREPONCE(self, "MiniState"); end UGCGameState.UITimer = nil; function UGCGameState:OnRep_MiniState() if UGCEventSystem == nil then return ; end if self.UITimer ~= nil then UGCEventSystem.StopTimer(self.UITimer); self.UITimer = nil; end if self.MiniState <= MiniGameState.MINI_PREPARE then self.UITimer = UGCEventSystem.SetTimerLoop(self, function() if LocalPlayerController and LocalPlayerController:IsPureSpectator() then if not WidgetManager:IsVisiblePanel(WidgetConfig.EUIType.AllWeapon_Spectator) then UGCLogSystem.Log("[UGCGameState:OnRep_MiniState] 尝试显示 AllWeapon_Spectator"); WidgetManager:ShowPanel(WidgetConfig.EUIType.AllWeapon_Spectator, false); else if self.UITimer then UGCEventSystem.StopTimer(self.UITimer); self.UITimer = nil; end end else if not WidgetManager:IsVisiblePanel(WidgetConfig.EUIType.AllWeapon) then UGCLogSystem.Log("[UGCGameState:OnRep_MiniState] 尝试显示 AllWeapon"); WidgetManager:ShowPanel(WidgetConfig.EUIType.AllWeapon, false); else if self.UITimer then UGCEventSystem.StopTimer(self.UITimer); self.UITimer = nil; end end end end, 2); elseif self.MiniState > MiniGameState.MINI_END then self.UITimer = UGCEventSystem.SetTimerLoop(self, function() if not WidgetManager:IsVisiblePanel(WidgetConfig.EUIType.GameEnd) then UGCLogSystem.Log("[UGCGameState:OnRep_MiniState] 尝试显示 GameEnd"); WidgetManager:ShowPanel(WidgetConfig.EUIType.GameEnd, false, self.MiniInfo.GameWinner, self.MiniInfo.RoundWinners); else if self.UITimer then UGCEventSystem.StopTimer(self.UITimer); self.UITimer = nil; end end end, 2); end end return UGCGameState;