ceshi 2 bug处理ok

This commit is contained in:
wenhe committed 2026-09-23 16:58:44 +08:00
1 parent b768ff1555
commit faa3a22631
8 files changed
+314 -52

No files matched your search

@@ -32,6 +32,7 @@
4. 在 modMain.py import 这些服务类即可(框架会自动实例化)
================================================================================
"""
import time
import mod.server.extraServerApi as serverApi
# ---- 网易API基础对象 ----
@@ -57,6 +58,15 @@ GAI_FAN_EVENT = "GaifanRequest"
# 相邻放置/厨具蘸油/寿司/裹粉/种水稻), 而非仅盖饭. ----
ITEM_USE_ON_REQUEST = "ItemUseOnRequest"
# ---- 通用"手持物品右键功能方块"的 BlockUse 转换请求(与客户端 BLOCK_USE_REQUEST 一致).
# 用于修复根因B: 手持可放置/可投掷物品(生肉/鱼/鸡蛋/胡萝卜/盘子/木条/寿司等)右键
# 功能方块(支杆/挂肉钩/鸡窝/饲料槽/丸子机/石磨/炉灶/寿司方块)时, 原版先播放"放置/
# 投掷"预览, 服务端 handleBlockUse 转换靠 ServerBlockUseEvent 触发, 但顺序不可保证,
# 导致"想转换却放置/投掷了"。客户端对该(物品,功能方块)组合 ret 消闪后显式 Notify
# 本事件, 服务端按被点方块 blockOwnerAll 路由到相应 handleBlockUse (与 TakeoutRequest
# 同机制). ----
BLOCK_USE_REQUEST = "BlockUseRequest"
# ---- 方块实体数据组件(计时持久化:记录开始时间戳,重启后继续计时) ----
blockEntityDataComp = comp.CreateBlockEntityData(LevelId)
@@ -71,6 +81,15 @@ FACE_OFFSET = {
5: (1, 0, 0),
}
# ---- 右键转换成功后"取消同一右键配套的放置/吃"的时间窗口(秒) ----
# 同一右键动作会同时触发 ServerBlockUseEvent(转换) 和 ServerItemUseOnEvent /
# ServerItemTryUseEvent(手持方块物品的放置前摇 / 喝药吃食)。OnBlockUse 转换成功后
# 会记录该玩家在此窗口内的后续 ItemUseOn/TryUse 应被取消(否则"转出了成品却又把
# 手持方块放出来")。超过窗口即视为过期残留, pop 后直接放行, 避免"做完一个操作
# 后任意位置放置都被吞"(统计: 空手右键只触发 BlockUse, 无配套 ItemUseOn 消费,
# 若标记无时限会永久残留吞掉玩家下一次真实放置)。
CANCEL_WINDOW = 0.5
class FeatureDispatcher(BlueFoxServerSystem):
"""
@@ -225,6 +244,10 @@ class FeatureDispatcher(BlueFoxServerSystem):
# (与 GaifanRequest 同机制, 统一覆盖全部 itemUseOn 转换)
self.ListenForEvent(MOD_NAME, "ClientFeatureDispatcher",
ITEM_USE_ON_REQUEST, self, self.OnItemUseOnRequest)
# 10. BlockUseRequest —— 客户端拦截(手持可放置/可投掷物品, 功能方块)放置预览后,
# 显式请求服务端执行对应 handleBlockUse(根因B: 防止"想转换却放置/投掷"了)。
self.ListenForEvent(MOD_NAME, "ClientFeatureDispatcher",
BLOCK_USE_REQUEST, self, self.OnBlockUseRequest)
def UnListenEvent(self):
self.UnListenForEvent(serverApi.GetEngineNamespace(),
@@ -251,6 +274,8 @@ class FeatureDispatcher(BlueFoxServerSystem):
GAI_FAN_EVENT, self, self.OnGaifanRequest)
self.UnListenForEvent(MOD_NAME, "ClientFeatureDispatcher",
ITEM_USE_ON_REQUEST, self, self.OnItemUseOnRequest)
self.UnListenForEvent(MOD_NAME, "ClientFeatureDispatcher",
BLOCK_USE_REQUEST, self, self.OnBlockUseRequest)
# ============================================================================
# 主入口1:玩家右键方块 → 按 blockOwner 路由到右键服务
@@ -284,8 +309,9 @@ class FeatureDispatcher(BlueFoxServerSystem):
handled = service.handleBlockUse(args)
if handled:
args['cancel'] = True
self._cancelItemUse[playerId] = True
print "[FD-BlockUse-DONE]", "player:", playerId, "hand:", _handItem, "block:", blockName, "svc:", service.__class__.__name__, "-> cancel=True, _cancelItemUse=True"
# 记录时间戳(带窗口), 只取消同一右键配套的放置/吃; 过期残留自动放行
self._cancelItemUse[playerId] = time.time()
print "[FD-BlockUse-DONE]", "player:", playerId, "hand:", _handItem, "block:", blockName, "svc:", service.__class__.__name__, "-> cancel=True, _cancelItemUse=ts"
return
print "[FD-BlockUse-NOMATCH]", "player:", playerId, "hand:", _handItem, "block:", blockName, "-> 无匹配规则, 放行"
@@ -306,11 +332,12 @@ class FeatureDispatcher(BlueFoxServerSystem):
"""
playerId = args.get("playerId") or args.get("entityId")
# 【关键】同一右键动作会同时触发 ServerBlockUseEvent(盛出/转换, 已由 OnBlockUse
# 处理并设置 _cancelItemUse) 和 ServerItemUseOnEvent(手持方块物品的放置前摇)。
# 若前一帧 OnBlockUse 已成功处理(盛出掉落/方块转换), 这里必须消费标记并
# ret=True 取消放置, 否则"盛出了成品"却又"把手持的木碗/盘子放了出来"。
# (与 OnItemTryUse 消费标记取消"吃/喝"是同一模式。)
if self._cancelItemUse.pop(playerId, False):
# 处理并设置 _cancelItemUse 时间戳) 和 ServerItemUseOnEvent(手持方块物品的放置前摇)。
# 若在 CANCEL_WINDOW 窗口内, 这里必须消费标记并 ret=True 取消放置, 否则"盛出了成品"
# 却又"把手持的木碗/盘子放了出来"。超窗口则视为过期残留, pop 后直接放行——绝不吞
# 玩家之后的真实放置(日志: 空手右键完成后残留标记吞掉任意位置放置, 已修复)。
_ts = self._cancelItemUse.pop(playerId, 0)
if _ts and (time.time() - _ts) < CANCEL_WINDOW:
args['ret'] = True
print "[FD-ItemUseOn-CONSUME-CANCEL]", "player:", playerId, "item:", args.get("itemName"), "block:", args.get("blockName"), "-> ret=True 取消放置"
return
@@ -361,7 +388,8 @@ class FeatureDispatcher(BlueFoxServerSystem):
handled = service.handleItemUseOn(args)
if handled:
args['ret'] = True
self._cancelItemUse[playerId] = True
# 时间戳(带窗口): itemUseOn 转换成功后, 同右手的后续 TryUse/放置也取消
self._cancelItemUse[playerId] = time.time()
return
# ============================================================================
@@ -513,6 +541,39 @@ class FeatureDispatcher(BlueFoxServerSystem):
return
print "[FD-ItemUseOnRequest-NO-HANDLER]", itemName
def OnBlockUseRequest(self, args):
"""
客户端 ClientFeatureDispatcher 在拦截(手持可放置/可投掷物品, 功能方块)放置预览后,
显式 NotifyToServer 发来的 BlockUse 转换请求(根因B). 因为客户端已 ret=True 把该
组合的 ServerBlockUseEvent 吞掉(消闪), 服务端原本的 handleBlockUse 转换在这里补齐.
按被点方块名路由到 blockOwnerAll 里的服务(可能多个, 逐个 fallback, 第一个返回 True
的处理成功, 与 OnBlockUse 相同). 各服务 handleBlockUse 内部自带 checkCooldown(同坐标/
同物品), 即使与真实 ServerBlockUseEvent 重复触发, 冷却也会拦截第二次, 不双重消耗.
"""
playerId = args.get("playerId")
x, y, z = args.get("x"), args.get("y"), args.get("z")
blockName = args.get("blockName")
if not (playerId and x is not None and y is not None and z is not None and blockName):
return
services = self.blockOwnerAll.get(blockName)
if not services:
print "[FD-BlockUseRequest-NO-SVC]", blockName
return
useArgs = {
"blockName": blockName,
"playerId": playerId,
"entityId": playerId,
"x": x, "y": y, "z": z,
"face": args.get("face", 1),
}
for service in services:
if hasattr(service, "handleBlockUse"):
handled = service.handleBlockUse(useArgs)
if handled:
return
print "[FD-BlockUseRequest-NO-HANDLER]", blockName
# ============================================================================
# 主入口3:拦截物品使用(防止喝药水/吃食物等默认行为)
# ============================================================================
@@ -526,7 +587,10 @@ class FeatureDispatcher(BlueFoxServerSystem):
"""
playerId = args.get("playerId")
print "[FD-ItemTryUse]", args.get("itemName"), "player", playerId
if self._cancelItemUse.pop(playerId, False):
# 仅在 CANCEL_WINDOW 窗口内消费(同一右键配套的吃/喝); 过期残留 pop 后放行,
# 不吞玩家之后再吃/喝物品(根因A: 无时限标记残留会吞掉后续任意物品使用)。
_ts = self._cancelItemUse.pop(playerId, 0)
if _ts and (time.time() - _ts) < CANCEL_WINDOW:
args['cancel'] = True
return
itemName = args.get("itemName")