This commit is contained in:
2026-10-07 05:28:36 +08:00
parent 1321ed6c01
commit 4ac6d8309c
8 changed files with 112 additions and 20 deletions
@@ -19,6 +19,7 @@ using Assets.MyGame.Scripts.Net.APIResponses.GameConfig_SubClass;
using Assets.MyGame.Scripts.Net.WebResponses;
using cfg;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -173,6 +174,16 @@ public class CreateFishManager : MonoSingleton<CreateFishManager>
/// <summary>特殊 Boss</summary>
public Dictionary<string, MonsterContral> fishDatas_hashCodekey_TS_Boss = new Dictionary<string, MonsterContral>();
// ----------------------------------------------------------------
// 击杀消息幂等去重(fishId|killtime → 处理时刻)
// 服务端可能对同一次击杀下发多条 RoomKillNotice(掉落推送之外的重复/重发),
// 同 fishId+killtime 只处理第一条,防止重复克隆爆奖特效/重复出 bjEndUI;
// MultiDeath(星际巡航等多次死亡玩法)每段 killtime 不同,不受影响。
// ----------------------------------------------------------------
private readonly Dictionary<string, float> _handledKillKeys = new Dictionary<string, float>();
private readonly List<string> _expiredKillKeys = new List<string>();
private const float KillDedupKeepSeconds = 60f; // 键保鲜期(秒),超时清理防字典增长
#if UNITY_EDITOR
// ⭐ 编辑器调试 List:与上方字典同步,方便在 Inspector 查看当前场上活鱼
public List<MonsterContral> fishList_All = new List<MonsterContral>();
@@ -1961,15 +1972,40 @@ public class CreateFishManager : MonoSingleton<CreateFishManager>
// 第二条与击杀消息同 fishId/killtime/gold,但只有 drop、没有 param。
// Boss 鱼 DieFish 是延迟回收(字典未即时移除),第二条若按击杀再处理一次:
// ① not-self 模式下 _pending[key] 被覆盖 → 前一个"爆奖中"BJEndUI 失去引用、永久残留在座位上;
// ② 重复克隆爆奖特效、重复走 DieFish → 特效/音效/爆奖双份。
// ② 重复克隆爆奖特效、重复走 DieFish → 特效/音效/爆奖双份(两个 Event 实例各出一次 bjEndUI)。
// 客户端目前没有掉落弹窗逻辑(见 RoomKillNotice.drop 注释),直接跳过;将来做掉落 UI 时在此处理。
if (killFish.drop != null && killFish.param == null)
// ⚠️ param 是 JToken:JSON "param":null 会被反序列化成 JValue(Null)(Type==JTokenType.Null),
// 不能只判 C# null(与 BossKillParam.cs 的判空写法保持一致),否则第二条漏拦 → 重复爆奖。
if (killFish.drop != null
&& (killFish.param == null || killFish.param.Type == JTokenType.Null))
{
NetLog.Log($"[CreateFishManager] 掉落道具推送(跳过击杀处理):fishId={killFish.fishId} " +
$"prop={killFish.drop.propNumber}×{killFish.drop.count}");
return;
}
// ⭐ 击杀消息幂等去重(第二道锁):同 fishId+killtime+beishu+gold 已处理过 → 判为同一次击杀的
// 重复/重发消息,直接跳过,防止重复走击杀流程(重复克隆爆奖特效 → 两个 Event 实例各出一次
// bjEndUI = 重复显示爆奖标志)。
// ⚠️ 掉落推送(param 为空)已在上面被拦、不会记录键,避免"掉落先到"时误挡真正的击杀消息;
// MultiDeath(星际巡航等多次死亡玩法)同一秒内多段的 killtime 可能相同,但各段 beishu/gold
// 不同,键里带上二者后正常放行。
string killKey = killFish.fishId + "|" + killFish.killtime + "|" + killFish.beishu + "|" + killFish.gold;
if (_handledKillKeys.ContainsKey(killKey))
{
NetLog.Log($"[CreateFishManager] 重复击杀消息(跳过):key={killKey}");
return;
}
_handledKillKeys[killKey] = Time.unscaledTime;
if (_handledKillKeys.Count >= 64) // 顺手清理过期键,防字典增长
{
float nowTime = Time.unscaledTime;
_expiredKillKeys.Clear();
foreach (var kv in _handledKillKeys)
if (nowTime - kv.Value > KillDedupKeepSeconds) _expiredKillKeys.Add(kv.Key);
foreach (var k in _expiredKillKeys) _handledKillKeys.Remove(k);
}
NetLog.Log($"[CreateFishManager] 杀鱼延迟:{DateTimeOffset.Now.ToUnixTimeSeconds() - killFish.timespan}s");
SitPosData sitData = null; //击杀玩家座位数据
foreach (var item in currGameType.list_Sits)