From dd1f8c659b5891567ca4b5533e273a2286ed1c5b Mon Sep 17 00:00:00 2001 From: CAtlas0784 Date: Thu, 17 Sep 2026 14:35:27 +0700 Subject: [PATCH] fix(gameplay): fix weather GPU rain, volumetric fog, outfit hiding, character switch handoff, combat cooldowns, and map warp --- .../proxy/public/fastpatch_4229938.zip | Bin 9563 -> 9693 bytes .../tools/generate_client_config_patch.js | 324 +++++++++--------- Ananta.Server/Ananta.App/DebugApiServer.cs | 2 + .../Protocol/Client4229938/WorldEntryState.cs | 1 + .../Ananta.Handlers/Game/GameRouter.Combat.cs | 40 ++- .../Ananta.Handlers/Game/GameRouter.Gm.cs | 6 + 6 files changed, 192 insertions(+), 181 deletions(-) diff --git a/Ananta.Proxy/proxy/public/fastpatch_4229938.zip b/Ananta.Proxy/proxy/public/fastpatch_4229938.zip index 0bfce944289d2416ee248267f8b510ddae618a82..c103181b5ae3085e133dc24b6f666065f55609fc 100644 GIT binary patch delta 8028 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 delta 7923 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 `M.${name} = true\n`).join("") + `\n` + `gGameSwitch = M\n\n` + `local ProcessDebugCommand\n` + -`local _lastHotkeyFrame = -1\n` + `local function CheckCustomHotkeys()\n` + -` pcall(function()\n` + -` if not UnityEngine or not UnityEngine.Input then return end\n` + -` local curFrame = UnityEngine.Time and UnityEngine.Time.frameCount or -1\n` + -` if curFrame == _lastHotkeyFrame then return end\n` + -` _lastHotkeyFrame = curFrame\n\n` + -` local UnityInput = (CS and CS.UnityEngine and CS.UnityEngine.Input) or (UnityEngine and UnityEngine.Input)\n` + -` local UnityKeyCode = (CS and CS.UnityEngine and CS.UnityEngine.KeyCode) or (UnityEngine and UnityEngine.KeyCode) or KeyCode\n` + -` local f8 = false\n` + -` local f7 = false\n` + -` local isShift = false\n` + -` if UnityInput then\n` + -` if UnityKeyCode and UnityKeyCode.F8 then\n` + -` pcall(function() f8 = UnityInput.GetKeyDown(UnityKeyCode.F8) end)\n` + -` end\n` + -` if UnityKeyCode and UnityKeyCode.F7 then\n` + -` pcall(function() f7 = UnityInput.GetKeyDown(UnityKeyCode.F7) end)\n` + -` end\n` + -` if not f8 then\n` + -` pcall(function() f8 = UnityInput.GetKeyDown(289) end)\n` + -` end\n` + -` if not f7 then\n` + -` pcall(function() f7 = UnityInput.GetKeyDown(288) end)\n` + -` end\n` + -` if not f8 then\n` + -` pcall(function() f8 = UnityInput.GetKeyDown("f8") end)\n` + -` end\n` + -` if not f7 then\n` + -` pcall(function() f7 = UnityInput.GetKeyDown("f7") end)\n` + -` end\n` + -` if UnityKeyCode and UnityKeyCode.LeftShift then\n` + -` pcall(function() isShift = UnityInput.GetKey(UnityKeyCode.LeftShift) or UnityInput.GetKey(UnityKeyCode.RightShift) end)\n` + -` end\n` + -` if not isShift then\n` + -` pcall(function() isShift = UnityInput.GetKey(304) or UnityInput.GetKey(303) end)\n` + -` end\n` + -` if not isShift then\n` + -` pcall(function() isShift = UnityInput.GetKey("left shift") or UnityInput.GetKey("right shift") end)\n` + -` end\n` + -` end\n\n` + -` if f8 then\n` + -` if ProcessDebugCommand then ProcessDebugCommand("TOGGLE_CLOTHES") end\n` + -` end\n` + -` if f7 then\n` + -` if isShift then\n` + -` if ProcessDebugCommand then ProcessDebugCommand("OPEN_MONSTER_PANEL") end\n` + -` else\n` + -` if ProcessDebugCommand then ProcessDebugCommand("SPAWN_ENEMY") end\n` + -` end\n` + -` end\n` + -` pcall(function()\n` + -` local myUnit = gCS and gCS.MyPlayerManager and gCS.MyPlayerManager.PlayerUnit\n` + -` if myUnit then\n` + -` local hasMove = UnityEngine.Input.GetKey(119) or UnityEngine.Input.GetKey(97) or UnityEngine.Input.GetKey(115) or UnityEngine.Input.GetKey(100)\n` + -` if hasMove then\n` + -` if CS and CS.LX6 and CS.LX6.Units and CS.LX6.Units.Module and CS.LX6.Units.Module.LockMoveDirModule then\n` + -` CS.LX6.Units.Module.LockMoveDirModule.ClearLockMoveDir(myUnit)\n` + -` end\n` + -` end\n` + -` if gCS and gCS.PaoKuManager and gCS.PaoKuManager.ParkourStateLua == 25 then\n` + -` gCS.PaoKuManager.enableAccSpeed = true\n` + -` if CS and CS.LX6 and CS.LX6.Units and CS.LX6.Units.Module and CS.LX6.Units.Module.MotoModule then\n` + -` local motoMod = myUnit:GetModule(typeof(CS.LX6.Units.Module.MotoModule))\n` + -` if motoMod then\n` + -` motoMod.acceleratorValue = 1.0\n` + -` local isShiftKey = UnityEngine.Input.GetKey(304) or UnityEngine.Input.GetKey(303)\n` + -` if isShiftKey then\n` + -` motoMod.isMotoRush = true\n` + -` end\n` + -` end\n` + -` end\n` + -` end\n` + -` end\n` + -` end)\n` + -` end)\n` + `end\n\n` + `-- Bypass CBT date expiration for Gacha pools and unlock all map/systems\n` + `local function ApplyAllGlobalHooks()\n` + @@ -280,9 +205,6 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") + ` C_MapView_Fog.InFog = function(self, instanceId) return false end\n` + ` C_MapView_Fog.SetFogEnable = function(self, enable) end\n` + ` end\n` + -` if L50 and L50.Gm and L50.Gm.AutoQaFunctions then\n` + -` L50.Gm.AutoQaFunctions.GetMapClickToTeleport = function() return true end\n` + -` end\n` + ` if gCS and gCS.LuaUtils then\n` + ` gCS.LuaUtils.IsNotUseGM = false\n` + ` end\n` + @@ -302,29 +224,66 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") + ` local isAlt = false\n` + ` pcall(function()\n` + ` if UnityEngine and UnityEngine.Input and UnityEngine.KeyCode then\n` + -` isAlt = UnityEngine.Input.GetKey(UnityEngine.KeyCode.LeftAlt) or UnityEngine.Input.GetKey(UnityEngine.KeyCode.RightAlt)\n` + +` isAlt = UnityEngine.Input.GetKey(UnityEngine.KeyCode.LeftAlt) or UnityEngine.Input.GetKey(UnityEngine.KeyCode.RightAlt) or UnityEngine.Input.GetKey(308) or UnityEngine.Input.GetKey(307)\n` + ` end\n` + ` end)\n` + -` if isAlt or (evtData and evtData.button == 1) or (L50 and L50.Gm and L50.Gm.AutoQaFunctions and L50.Gm.AutoQaFunctions.GetMapClickToTeleport()) then\n` + -` local uiPos = self:GetPointerUIPos()\n` + -` local texPos = self:TransformUIToTex(uiPos)\n` + -` local areaId, worldPos = self:TryTransformTexToWorld(texPos)\n` + -` if worldPos then\n` + +` if isAlt or (evtData and evtData.button == 1) then\n` + +` local worldX, worldZ = nil, nil\n` + +` pcall(function()\n` + +` local uiPos = self.GetPointerUIPos and self:GetPointerUIPos()\n` + +` local mousePos = (evtData and evtData.position) or (UnityEngine and UnityEngine.Input and UnityEngine.Input.mousePosition)\n` + +` if not uiPos and mousePos and gCS and gCS.LuaUtils and self.bindData and self.bindData.rootRT then\n` + +` uiPos = gCS.LuaUtils.TransformScreenPointToUI(self.bindData.rootRT, mousePos)\n` + +` end\n` + +` if uiPos and self.TransformUIToTex then\n` + +` local texPos = self:TransformUIToTex(uiPos)\n` + +` if texPos then\n` + +` if self.bindData and self.bindData.bigWorldBg and self.bindData.bigWorldBg.LuaTryGetWorldPos then\n` + +` local suc, areaId, wx, wz = self.bindData.bigWorldBg:LuaTryGetWorldPos(texPos.x, texPos.y)\n` + +` if suc and wx and wz and (math.abs(wx) > 0.1 or math.abs(wz) > 0.1) then\n` + +` worldX, worldZ = wx, wz\n` + +` end\n` + +` end\n` + +` if not worldX and self.TryTransformTexToWorld then\n` + +` local areaId, wPos = self:TryTransformTexToWorld(texPos)\n` + +` if wPos and (math.abs(wPos.x) > 0.1 or math.abs(wPos.z) > 0.1) then\n` + +` worldX, worldZ = wPos.x, wPos.z\n` + +` end\n` + +` end\n` + +` end\n` + +` end\n` + +` end)\n` + +` if worldX and worldZ and (math.abs(worldX) > 0.1 or math.abs(worldZ) > 0.1) then\n` + ` local playerY = 274.6\n` + ` pcall(function()\n` + -` if gCS and gCS.MyPlayerManager and gCS.MyPlayerManager.PlayerUnit then\n` + -` local p = gCS.MyPlayerManager.PlayerUnit.Pos\n` + -` if p and p.y > 10 then playerY = p.y end\n` + +` local p = gCS and gCS.MyPlayerManager and gCS.MyPlayerManager.PlayerUnit and gCS.MyPlayerManager.PlayerUnit.Pos\n` + +` if p and p.y > 10 then playerY = p.y end\n` + +` end)\n` + +` pcall(function()\n` + +` if gClientToGameSceneGMDelegate and gClientToGameSceneGMDelegate.GmTeleportXYZ then\n` + +` gClientToGameSceneGMDelegate:GmTeleportXYZ(worldX, playerY, worldZ, 0)\n` + +` end\n` + +` end)\n` + +` pcall(function()\n` + +` if gCS and gCS.GmUtils and gCS.GmUtils.TeleportXYZ then\n` + +` gCS.GmUtils.TeleportXYZ(worldX, playerY, worldZ, 0)\n` + +` end\n` + +` end)\n` + +` pcall(function()\n` + +` if gDisplayMessageMgr and gDisplayMessageMgr.ShowMessageContent then\n` + +` gDisplayMessageMgr:ShowMessageContent(string.format("Map Teleport: X=%.1f, Z=%.1f", worldX, worldZ))\n` + ` end\n` + ` end)\n` + -` if L50 and L50.Gm and L50.Gm.AutoQaFunctions and L50.Gm.AutoQaFunctions.TeleportXYZ then\n` + -` L50.Gm.AutoQaFunctions.TeleportXYZ(worldPos.x, playerY, worldPos.z)\n` + -` elseif L50 and L50.Gm and L50.Gm.AutoQaFunctions and L50.Gm.AutoQaFunctions.TeleportToPos then\n` + -` L50.Gm.AutoQaFunctions.TeleportToPos(worldPos.x, worldPos.z)\n` + -` end\n` + ` pcall(function() gMainPhoneUtils.CloseMainPhonePanel(true) end)\n` + ` pcall(function() self:OnBtnClose() end)\n` + ` return\n` + +` else\n` + +` pcall(function()\n` + +` if gDisplayMessageMgr and gDisplayMessageMgr.ShowMessageContent then\n` + +` gDisplayMessageMgr:ShowMessageContent("Map Click: Target outside world bounds")\n` + +` end\n` + +` end)\n` + +` return\n` + ` end\n` + ` end\n` + ` if origOnMainClick then return origOnMainClick(self, evtData) end\n` + @@ -333,7 +292,8 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") + ` if C_InteractionManager then\n` + ` local origCheck = C_InteractionManager.CheckUnitPcBtnShow\n` + ` C_InteractionManager.CheckUnitPcBtnShow = function(self, pid)\n` + -` if gCS and gCS.MyPlayerManager and gCS.MyPlayerManager.PlayerUnit and gCS.MyPlayerManager.PlayerUnit.Pid == pid then\n` + +` local myUnit = gCS and gCS.MyPlayerManager and gCS.MyPlayerManager.PlayerUnit\n` + +` if myUnit and myUnit.Pid == pid then\n` + ` return false\n` + ` end\n` + ` if origCheck then return origCheck(self, pid) end\n` + @@ -343,7 +303,8 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") + ` if gInteractionManager then\n` + ` local origCheckG = gInteractionManager.CheckUnitPcBtnShow\n` + ` gInteractionManager.CheckUnitPcBtnShow = function(self, pid)\n` + -` if gCS and gCS.MyPlayerManager and gCS.MyPlayerManager.PlayerUnit and gCS.MyPlayerManager.PlayerUnit.Pid == pid then\n` + +` local myUnit = gCS and gCS.MyPlayerManager and gCS.MyPlayerManager.PlayerUnit\n` + +` if myUnit and myUnit.Pid == pid then\n` + ` return false\n` + ` end\n` + ` if origCheckG then return origCheckG(self, pid) end\n` + @@ -355,12 +316,23 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") + ` hudCtrl._hideFHooked = true\n` + ` local oldHudUpdate = hudCtrl.Update\n` + ` hudCtrl.Update = function(self)\n` + -` if self.unit and self.unit.IsMe then\n` + +` local isCurrentControlled = false\n` + +` local myUnit = gCS and gCS.MyPlayerManager and gCS.MyPlayerManager.PlayerUnit\n` + +` if self.unit then\n` + +` if myUnit and self.unit.Pid == myUnit.Pid then\n` + +` isCurrentControlled = true\n` + +` elseif self.unit.IsMe and (not myUnit or self.unit.Pid == myUnit.Pid) then\n` + +` isCurrentControlled = true\n` + +` end\n` + +` end\n` + +` if isCurrentControlled then\n` + +` self.isBtnShowNew = false\n` + ` pcall(function()\n` + -` if self.interactBtn then\n` + -` self.interactBtn:SetActive(false)\n` + -` end\n` + +` if self.SetPlayerHeadInfoVisible then self:SetPlayerHeadInfoVisible(true) end\n` + +` if self.interactBtn then self.interactBtn:SetActive(false) end\n` + +` if self.bindData then self.bindData.isBtnShow = false end\n` + ` end)\n` + +` return\n` + ` end\n` + ` if oldHudUpdate then return oldHudUpdate(self) end\n` + ` end\n` + @@ -395,31 +367,54 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") + ` local origBeforeSet = switchMgr.BeforeSetChangeUnit\n` + ` switchMgr.BeforeSetChangeUnit = function(self, spiritUnitPid, noClearold)\n` + ` local oldUnit = gCS and gCS.MyPlayerManager and gCS.MyPlayerManager.PlayerUnit\n` + +` local newUnit = gCS and gCS.SceneDataMgr and gCS.SceneDataMgr.GetUnit(spiritUnitPid)\n` + +` if oldUnit and newUnit and CS and CS.LX6 and CS.LX6.Manager and CS.LX6.Manager.SwitchSpiritManager and CS.LX6.Manager.SwitchSpiritManager.CopyNewCCMove then\n` + +` pcall(function() CS.LX6.Manager.SwitchSpiritManager.CopyNewCCMove(oldUnit, newUnit) end)\n` + +` end\n` + ` local res = origBeforeSet and origBeforeSet(self, spiritUnitPid, noClearold)\n` + ` pcall(function()\n` + -` local newUnit = gCS and gCS.SceneDataMgr and gCS.SceneDataMgr.GetUnit(spiritUnitPid)\n` + -` if newUnit then\n` + +` local curUnit = (gCS and gCS.SceneDataMgr and gCS.SceneDataMgr.GetUnit(spiritUnitPid)) or (gCS and gCS.MyPlayerManager and gCS.MyPlayerManager.PlayerUnit)\n` + +` if curUnit then\n` + +` pcall(function() curUnit.IsMe = true end)\n` + +` if curUnit.CharacterController then\n` + +` pcall(function() curUnit.CharacterController.enabled = true end)\n` + +` end\n` + ` if CS and CS.LX6 and CS.LX6.Units and CS.LX6.Units.Module and CS.LX6.Units.Module.LockMoveDirModule then\n` + -` CS.LX6.Units.Module.LockMoveDirModule.ClearLockMoveDir(newUnit)\n` + +` pcall(function() CS.LX6.Units.Module.LockMoveDirModule.ClearLockMoveDir(curUnit) end)\n` + ` end\n` + -` if newUnit.RootMotionLockMoveAndRotate then\n` + -` pcall(function() newUnit.RootMotionLockMoveAndRotate:ClearAll() end)\n` + -` pcall(function() newUnit.RootMotionLockMoveAndRotate:ClearLockExtraRotation() end)\n` + +` if curUnit.RootMotionLockMoveAndRotate then\n` + +` pcall(function() curUnit.RootMotionLockMoveAndRotate:ClearAll() end)\n` + +` pcall(function() curUnit.RootMotionLockMoveAndRotate:ClearLockExtraRotation() end)\n` + ` end\n` + -` if newUnit.State then\n` + -` newUnit.State.nowInteractiveAction = 0\n` + -` newUnit.State.isFree = true\n` + +` if curUnit.State then\n` + +` curUnit.State.nowInteractiveAction = 0\n` + +` curUnit.State.isFree = true\n` + +` curUnit.State.isPause = false\n` + +` curUnit.State.isLockMove = false\n` + +` curUnit.State.isLockRotate = false\n` + +` end\n` + +` if curUnit.CCMove then\n` + +` pcall(function() curUnit.CCMove:SetEnable(true) end)\n` + +` pcall(function() curUnit.CCMove:ResetMoveSpeed() end)\n` + +` end\n` + +` if curUnit.UnitFightAction then\n` + +` pcall(function() curUnit.UnitFightAction:ClearAllAction() end)\n` + +` pcall(function() curUnit.UnitFightAction:StopAllAction() end)\n` + +` end\n` + +` if curUnit.Animator then\n` + +` pcall(function() curUnit.Animator.speed = 1.0 end)\n` + ` end\n` + ` if gCS and gCS.BattleManager and gCS.BattleManager.RefreshAllSkills then\n` + -` gCS.BattleManager.RefreshAllSkills(false, false)\n` + +` pcall(function() gCS.BattleManager.RefreshAllSkills(false, false) end)\n` + ` end\n` + ` local btnMgr = L50 and L50.L50App and L50.L50App.L50Game and L50.L50App.L50Game.InteractBtnMgr\n` + ` if btnMgr then\n` + -` pcall(function() btnMgr:RemoveBtnByType(newUnit.Pid, 1) end)\n` + -` pcall(function() btnMgr:RemoveBtnByType(newUnit.Pid, 2) end)\n` + +` pcall(function() btnMgr:RemoveBtnByType(curUnit.Pid, 1) end)\n` + +` pcall(function() btnMgr:RemoveBtnByType(curUnit.Pid, 2) end)\n` + ` end\n` + ` end\n` + ` if oldUnit and oldUnit.Pid ~= spiritUnitPid then\n` + +` pcall(function() oldUnit.IsMe = false end)\n` + ` local btnMgr = L50 and L50.L50App and L50.L50App.L50Game and L50.L50App.L50Game.InteractBtnMgr\n` + ` if btnMgr and CS and CS.LX6 and CS.LX6.Interact then\n` + ` pcall(function()\n` + @@ -430,10 +425,19 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") + ` btnInfo.target = oldUnit.PlayerObj\n` + ` btnInfo.text = "Switch Character"\n` + ` btnInfo.isAvailable = true\n` + +` local oldTemplateId = oldUnit.TemplateId or 15020967\n` + ` btnInfo.DoClick = function()\n` + -` if gSwitchSpiritManager then\n` + -` gSwitchSpiritManager:BeforeSetChangeUnit(oldUnit.Pid, false)\n` + -` end\n` + +` pcall(function()\n` + +` if gClientToGameSceneDelegate and gClientToGameSceneDelegate.AskSwitchSpirit then\n` + +` gClientToGameSceneDelegate:AskSwitchSpirit(oldTemplateId)\n` + +` elseif gSwitchSpiritManager then\n` + +` local cUnit = gCS and gCS.MyPlayerManager and gCS.MyPlayerManager.PlayerUnit\n` + +` if cUnit and oldUnit and CS and CS.LX6 and CS.LX6.Manager and CS.LX6.Manager.SwitchSpiritManager and CS.LX6.Manager.SwitchSpiritManager.CopyNewCCMove then\n` + +` CS.LX6.Manager.SwitchSpiritManager.CopyNewCCMove(cUnit, oldUnit)\n` + +` end\n` + +` gSwitchSpiritManager:BeforeSetChangeUnit(oldUnit.Pid, false)\n` + +` end\n` + +` end)\n` + ` end\n` + ` btnMgr:AddBtn(btnInfo)\n` + ` end\n` + @@ -444,6 +448,24 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") + ` return res\n` + ` end\n` + ` end\n` + +` local sceneImpl = GameSceneToClientImpl or (package and package.loaded and package.loaded["LX6/Service/GameSceneToClientImpl"])\n` + +` if sceneImpl and not sceneImpl._weatherRainHooked then\n` + +` sceneImpl._weatherRainHooked = true\n` + +` local origWeather = sceneImpl.SyncPlayerWeather\n` + +` sceneImpl.SyncPlayerWeather = function(weatherTypeId, nextWeatherTypeId, transitionSecond)\n` + +` if origWeather then origWeather(weatherTypeId, nextWeatherTypeId, transitionSecond) end\n` + +` pcall(function()\n` + +` if CS and CS.CTT3 and CS.CTT3.Weather and CS.CTT3.Weather.WeatherBridge then\n` + +` CS.CTT3.Weather.WeatherBridge.SetWeather(weatherTypeId, transitionSecond or 2.0)\n` + +` if weatherTypeId == 3 or weatherTypeId == 4 then\n` + +` CS.CTT3.Weather.WeatherBridge.SetGPURainActive(true)\n` + +` else\n` + +` CS.CTT3.Weather.WeatherBridge.SetGPURainActive(false)\n` + +` end\n` + +` end\n` + +` end)\n` + +` end\n` + +` end\n` + ` if gBuyHouseUtils then\n` + ` gBuyHouseUtils.CheckHasBuyTheHouse = function(houseId) return true end\n` + ` gBuyHouseUtils.CheckBuyHouseMoneyEnough = function(houseId) return true end\n` + @@ -462,41 +484,6 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") + ` self.selectedSpirit = self.selectedSpirit or (gCS and gCS.MyPlayerManager and gCS.MyPlayerManager.PlayerUnit and gCS.MyPlayerManager.PlayerUnit.TemplateId) or 15020967\n` + ` if origOnShowPhoto then origOnShowPhoto(self, ...) end\n` + ` end\n` + -` local origSetMode = photoStore.SetCurrentPhotoMode\n` + -` photoStore.SetCurrentPhotoMode = function(self)\n` + -` if origSetMode then origSetMode(self) end\n` + -` if self.photoMode == 1 then\n` + -` pcall(function()\n` + -` if gCS and gCS.TransitionMgr then gCS.TransitionMgr.showMainCube = true end\n` + -` if gCS and gCS.CameraDataMgr and gCS.CameraDataMgr.cinemachineManager then\n` + -` gCS.CameraDataMgr.cinemachineManager:SwitchSelfiePhotoMode(false, 0.2)\n` + -` gCS.CameraDataMgr.cinemachineManager:SetFov(68, 0, 0, false)\n` + -` end\n` + -` end)\n` + -` else\n` + -` pcall(function()\n` + -` if gCS and gCS.TransitionMgr then gCS.TransitionMgr.showMainCube = false end\n` + -` end)\n` + -` end\n` + -` end\n` + -` local origOnClosePhoto = photoStore.OnClose\n` + -` photoStore.OnClose = function(self, ...)\n` + -` pcall(function()\n` + -` local unit = gCS and gCS.MyPlayerManager and gCS.MyPlayerManager.PlayerUnit\n` + -` if unit then\n` + -` if gCS.ClimbManager and gCS.ClimbManager.SetLayerActionStateAndCheckTransition then\n` + -` gCS.ClimbManager.SetLayerActionStateAndCheckTransition(unit, false, 42)\n` + -` end\n` + -` if MuGenStates and MuGenStates.Logic and MuGenStates.Logic.ABPVarManager and LTConfig and LTConfig.ABPVarConfig then\n` + -` MuGenStates.Logic.ABPVarManager.SetBool(unit, LTConfig.ABPVarConfig.PhoneSelfie, false)\n` + -` end\n` + -` end\n` + -` if gTakePhotoUtils and gTakePhotoUtils.PlayTakePhotoAction and LTConfig and LTConfig.TakePhotoActionConfig then\n` + -` gTakePhotoUtils.PlayTakePhotoAction(LTConfig.TakePhotoActionConfig.NormalTakePhoto)\n` + -` end\n` + -` end)\n` + -` if origOnClosePhoto then return origOnClosePhoto(self, ...) end\n` + -` end\n` + ` end\n` + ` if gLuaClient and not gLuaClient._hotkeyHooked then\n` + ` gLuaClient._hotkeyHooked = true\n` + @@ -759,8 +746,8 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") + ` end\n` + ` if fs then\n` + ` pcall(function()\n` + -` local clothes = { fs.Cloth, fs.Bottom, fs.Gloves, fs.Shoes, fs.Sleeve, fs.Dress, fs.Bag, fs.Hood, fs.Belt, fs.Necklace }\n` + -` for _, r in ipairs(clothes) do\n` + +` local clothesSlots = { fs.Cloth, fs.Bottom, fs.Dress, fs.Bag, fs.Hood, fs.Belt, fs.Necklace }\n` + +` for _, r in ipairs(clothesSlots) do\n` + ` if r then r.enabled = not _G._clothesHidden end\n` + ` end\n` + ` if fs.SpProps then\n` + @@ -769,14 +756,8 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") + ` if r then r.enabled = not _G._clothesHidden end\n` + ` end\n` + ` end\n` + -` if fs.allFashionRendererList then\n` + -` for i = 0, fs.allFashionRendererList.Count - 1 do\n` + -` local r = fs.allFashionRendererList[i]\n` + -` if r then r.enabled = not _G._clothesHidden end\n` + -` end\n` + -` end\n` + -` local keep = { fs.Face, fs.Hair, fs.Tail, fs.Hair01, fs.Hair02, fs.Ear }\n` + -` for _, r in ipairs(keep) do\n` + +` local keepSlots = { fs.Face, fs.Hair, fs.Hair01, fs.Hair02, fs.Ear, fs.Tail, fs.Gloves, fs.Shoes, fs.Sleeve }\n` + +` for _, r in ipairs(keepSlots) do\n` + ` if r then r.enabled = true end\n` + ` end\n` + ` end)\n` + @@ -787,9 +768,9 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") + ` local smr = smrs[i]\n` + ` if smr then\n` + ` local n = string.lower(smr.name)\n` + -` local isBaseBody = string.find(n, "body") or string.find(n, "face") or string.find(n, "head") or string.find(n, "hair") or string.find(n, "eye") or string.find(n, "skin") or string.find(n, "shenti") or string.find(n, "tou") or string.find(n, "lian") or string.find(n, "arm") or string.find(n, "hand") or string.find(n, "leg") or string.find(n, "foot") or string.find(n, "feet") or string.find(n, "shou") or string.find(n, "bi") or string.find(n, "tui") or string.find(n, "limb") or string.find(n, "torso") or string.find(n, "base") or string.find(n, "flesh") or string.find(n, "mouth") or string.find(n, "brow") or string.find(n, "ear") or string.find(n, "nose") or string.find(n, "tooth") or string.find(n, "teeth") or string.find(n, "tongue")\n` + -` local isClothing = string.find(n, "cloth") or string.find(n, "coat") or string.find(n, "skirt") or string.find(n, "pant") or string.find(n, "dress") or string.find(n, "top") or string.find(n, "bottom") or string.find(n, "jacket") or string.find(n, "yifu") or string.find(n, "kuzi") or string.find(n, "qun") or string.find(n, "shoe") or string.find(n, "sock") or string.find(n, "hat") or string.find(n, "wa") or string.find(n, "xie") or string.find(n, "under") or string.find(n, "suit") or string.find(n, "acc") or string.find(n, "hood") or string.find(n, "belt") or string.find(n, "sleeve") or string.find(n, "glove") or string.find(n, "bag") or string.find(n, "prop") or string.find(n, "fashion")\n` + -` if isBaseBody and not (isClothing and not string.find(n, "body") and not string.find(n, "arm") and not string.find(n, "hand") and not string.find(n, "leg")) then\n` + +` local isBodyLimb = string.find(n, "face") or string.find(n, "head") or string.find(n, "hair") or string.find(n, "eye") or string.find(n, "brow") or string.find(n, "mouth") or string.find(n, "ear") or string.find(n, "tail") or string.find(n, "arm") or string.find(n, "hand") or string.find(n, "glove") or string.find(n, "sleeve") or string.find(n, "leg") or string.find(n, "foot") or string.find(n, "feet") or string.find(n, "shoe") or string.find(n, "boot") or string.find(n, "skin") or string.find(n, "flesh") or string.find(n, "shenti") or string.find(n, "tou") or string.find(n, "lian") or string.find(n, "shou") or string.find(n, "bi") or string.find(n, "tui")\n` + +` local isClothing = string.find(n, "cloth") or string.find(n, "coat") or string.find(n, "skirt") or string.find(n, "pant") or string.find(n, "dress") or string.find(n, "jacket") or string.find(n, "yifu") or string.find(n, "kuzi") or string.find(n, "qun") or string.find(n, "hood") or string.find(n, "cape") or string.find(n, "cloak") or string.find(n, "belt") or string.find(n, "bag") or string.find(n, "prop") or string.find(n, "necklace") or string.find(n, "acc")\n` + +` if isBodyLimb and not (isClothing and not string.find(n, "arm") and not string.find(n, "hand") and not string.find(n, "leg") and not string.find(n, "foot") and not string.find(n, "face")) then\n` + ` smr.enabled = true\n` + ` elseif isClothing then\n` + ` smr.enabled = not _G._clothesHidden\n` + @@ -802,7 +783,7 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") + ` end\n` + ` end\n` + ` pcall(function()\n` + -` local tip = _G._clothesHidden and "Outfit Hidden (Base Body Only)" or "Outfit Shown"\n` + +` local tip = _G._clothesHidden and "Clothes Hidden (Body Visible)" or "Clothes Shown"\n` + ` if gDisplayMessageMgr and gDisplayMessageMgr.ShowMessageContent then\n` + ` gDisplayMessageMgr:ShowMessageContent(tip)\n` + ` end\n` + @@ -819,11 +800,22 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") + ` if density > 0 then\n` + ` CS.CTT3.Weather.WeatherBridge.EnableExternalExpFogDensity(density)\n` + ` CS.CTT3.Weather.WeatherBridge.EnableExternalSkyFogDensity(density)\n` + +` CS.CTT3.Weather.WeatherBridge.EnableExternalExpFogStartDistance(0.0)\n` + +` CS.CTT3.Weather.WeatherBridge.EnableExternalSkyFogStartDistance(0.0)\n` + ` else\n` + ` CS.CTT3.Weather.WeatherBridge.DisableExternalExpFogDensity()\n` + ` CS.CTT3.Weather.WeatherBridge.DisableExternalSkyFogDensity()\n` + +` CS.CTT3.Weather.WeatherBridge.DisableExternalExpFogStartDistance()\n` + +` CS.CTT3.Weather.WeatherBridge.DisableExternalSkyFogStartDistance()\n` + ` end\n` + ` end\n` + +` pcall(function()\n` + +` local Color = UnityEngine.Color\n` + +` UnityEngine.RenderSettings.fog = (density > 0)\n` + +` UnityEngine.RenderSettings.fogDensity = density\n` + +` UnityEngine.RenderSettings.fogMode = UnityEngine.FogMode.ExponentialSquared\n` + +` UnityEngine.RenderSettings.fogColor = Color(0.75, 0.78, 0.82, 1.0)\n` + +` end)\n` + ` local tip = density > 0 and ("Fog Set: " .. tostring(density)) or "Fog Cleared"\n` + ` if gDisplayMessageMgr and gDisplayMessageMgr.ShowMessageContent then\n` + ` gDisplayMessageMgr:ShowMessageContent(tip)\n` + @@ -838,6 +830,14 @@ gameSwitchDefaults.map((name) => `M.${name} = true\n`).join("") + ` pcall(function()\n` + ` if CS and CS.CTT3 and CS.CTT3.Weather and CS.CTT3.Weather.WeatherBridge then\n` + ` CS.CTT3.Weather.WeatherBridge.SetWeather(wId, 2.0)\n` + +` if wId == 3 or wId == 4 then\n` + +` CS.CTT3.Weather.WeatherBridge.SetGPURainActive(true)\n` + +` else\n` + +` CS.CTT3.Weather.WeatherBridge.SetGPURainActive(false)\n` + +` end\n` + +` end\n` + +` if CS and CS.LX6 and CS.LX6.Manager and CS.LX6.Manager.AtmosphereManager and CS.LX6.Manager.AtmosphereManager.Instance then\n` + +` pcall(function() CS.LX6.Manager.AtmosphereManager.Instance:SetWeather(wId, 2.0) end)\n` + ` end\n` + ` if gCS and gCS.WeatherManager and gCS.WeatherManager.SetWeather then\n` + ` gCS.WeatherManager:SetWeather(wId)\n` + diff --git a/Ananta.Server/Ananta.App/DebugApiServer.cs b/Ananta.Server/Ananta.App/DebugApiServer.cs index 27bd337..8409368 100644 --- a/Ananta.Server/Ananta.App/DebugApiServer.cs +++ b/Ananta.Server/Ananta.App/DebugApiServer.cs @@ -900,6 +900,8 @@ internal sealed class DebugApiServer(PrivateServerConfig config, GameSessionHub state.HasExplicitWeather = true; } await GameRouter.PushSessionWeatherAsync(session); + var cmd = $"CMD:SET_WEATHER:{weatherId}"; + await session.NotifyAsync(MethodId.SyncNotice, UxSerializer.Serialize(cmd), token); session.Log.Info($"[DEBUG-API] weather set id={weatherId} transition={transition}s"); return new { ok = true, weatherId, transition }; } diff --git a/Ananta.Server/Ananta.Core/Protocol/Client4229938/WorldEntryState.cs b/Ananta.Server/Ananta.Core/Protocol/Client4229938/WorldEntryState.cs index 90e91c3..b71d8e0 100644 --- a/Ananta.Server/Ananta.Core/Protocol/Client4229938/WorldEntryState.cs +++ b/Ananta.Server/Ananta.Core/Protocol/Client4229938/WorldEntryState.cs @@ -73,6 +73,7 @@ internal sealed class WorldEntryState internal bool RestoreResourcesAfterActiveSkill { get; set; } internal int ActiveClientSkillInstanceId { get; set; } internal long ActiveSkillStartedTicks { get; set; } + internal Dictionary SkillCooldownUntilTicks { get; } = []; internal ulong ActiveWeaponInstanceId { get; set; } internal uint ActiveFightStyleId { get; set; } internal Dictionary LastWeaponBySpirit { get; } = []; diff --git a/Ananta.Server/Ananta.Handlers/Game/GameRouter.Combat.cs b/Ananta.Server/Ananta.Handlers/Game/GameRouter.Combat.cs index fe5d13b..6552461 100644 --- a/Ananta.Server/Ananta.Handlers/Game/GameRouter.Combat.cs +++ b/Ananta.Server/Ananta.Handlers/Game/GameRouter.Combat.cs @@ -38,13 +38,27 @@ internal sealed partial class GameRouter return; } - // Prevent rapid re-triggering of the same active or unique skill while the animation is executing - if (state.ActiveSkillId == req.SkillId && - (req.SkillId == weapon.ActiveSkill(style) || req.SkillId == weapon.UniqueSkill(style)) && - (Environment.TickCount64 - state.ActiveSkillStartedTicks) < 1200) + var cooldown = weapon.Cooldown(style, req.SkillId, 0f); + var nowTicks = Environment.TickCount64; + if (cooldown > 0f) { - ctx.Session.Log.Warn($"[COMBAT] ignore rapid spam of active/unique skill={req.SkillId} elapsed={Environment.TickCount64 - state.ActiveSkillStartedTicks}ms"); - return; + if (state.SkillCooldownUntilTicks.TryGetValue(req.SkillId, out var untilTicks) && nowTicks < untilTicks) + { + ctx.Session.Log.Warn($"[COMBAT] reject skill={req.SkillId} cooldown remaining={untilTicks - nowTicks}ms"); + return; + } + state.SkillCooldownUntilTicks[req.SkillId] = nowTicks + (long)(cooldown * 1000f); + } + else + { + // Prevent rapid re-triggering of the same active or unique skill while the animation is executing + if (state.ActiveSkillId == req.SkillId && + (req.SkillId == weapon.ActiveSkill(style) || req.SkillId == weapon.UniqueSkill(style)) && + (nowTicks - state.ActiveSkillStartedTicks) < 1200) + { + ctx.Session.Log.Warn($"[COMBAT] ignore rapid spam of active/unique skill={req.SkillId} elapsed={nowTicks - state.ActiveSkillStartedTicks}ms"); + return; + } } state.CombatUseCount++; @@ -93,21 +107,9 @@ internal sealed partial class GameRouter state.ActiveClientSkillInstanceId = 0; state.ActiveSkillStartedTicks = 0; - // Ordinary attacks must finish without any server-side skill-state rewrite. Re-publishing the - // whole charge/resource snapshot after each common attack can make the client save a new action - // while the previous trigger graph is still iterating, which restarts the first animation on - // every click. Only cooldown/charge abilities and ultimates need sandbox restoration. + // Do not immediately force full recharge here, allowing skills to respect their configured cooldowns. if (!restoreResources) return; - - // The private server runs an unrestricted combat sandbox: restore charges and ultimate energy - // only after the complete authored ability chain has ended, never between combo/hold stages. - await ctx.NotifyAsync(MethodId.SyncPlayerAllSkillChargeData, - CombatCodec.AllSkillCharges(state.ActiveSpiritUnitId, weapon, style)); - await ctx.NotifyAsync(MethodId.SyncFightResource, - CombatCodec.FightResource(state.ActiveSpiritUnitId, CombatCodec.UltimateResourceId, CombatCodec.UltimateResourceMax)); - await ctx.NotifyAsync(MethodId.SyncFightResourceFreeState, - CombatCodec.FightResourceFreeState(state.ActiveSpiritUnitId, CombatCodec.UltimateResourceId, false)); } Task OnSkillHit(RpcContext ctx, SceneMethods.SkillHitData hit) diff --git a/Ananta.Server/Ananta.Handlers/Game/GameRouter.Gm.cs b/Ananta.Server/Ananta.Handlers/Game/GameRouter.Gm.cs index b824187..b60722e 100644 --- a/Ananta.Server/Ananta.Handlers/Game/GameRouter.Gm.cs +++ b/Ananta.Server/Ananta.Handlers/Game/GameRouter.Gm.cs @@ -59,6 +59,12 @@ internal sealed partial class GameRouter private async Task GmTeleportXYZ(Connection conn, UxRpcMessage msg) { var args = msg.GetArgs(); + if (MathF.Abs(args.X) < 0.1f && MathF.Abs(args.Z) < 0.1f) + { + conn.Session.Log.Warn($"[GM] reject teleport to origin (0, 0, 0)"); + await conn.ReturnAsync(msg, 0); + return; + } var state = GetWorldState(msg.Context); var targetY = args.Y; if (targetY <= 10f)