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

@@ -331,8 +331,8 @@ class FurnitureService(BlueFoxServerSystem):
retItemName = record["itemName"]
retAux = record.get("aux", 0)
# 给玩家箭
self.giveItem(playerId, retItemName, retCount, retAux)
# 掉落物形式返回箭(根因C)
self._dropItemDict(dim, pos, {"itemName": retItemName, "count": retCount, "auxValue": retAux})
# 替换方块
if recipe.get("target"):
@@ -371,9 +371,10 @@ class FurnitureService(BlueFoxServerSystem):
poppedName = stackNames.pop() # 后放先取, 弹最后一件
if stack:
topItem = stack.pop()
self.giveItemObj(playerId, topItem)
self._dropItemObj(dim, pos, topItem) # 掉落物返还(根因C)
else:
self.giveItem(playerId, poppedName)
self._dropItemDict(dim, pos,
{"itemName": poppedName, "count": 1, "auxValue": 0})
# 计算剩余 -> 目标方块, 并保存剩余记录(保证后续取回一致)
target = self._rackTarget(stackNames)
self.replaceBlock(pos, dim, target, playerId)
@@ -477,9 +478,15 @@ class FurnitureService(BlueFoxServerSystem):
if recipe.get("dropItem"):
self.dropItems(dim, pos, recipe["dropItem"])
# ---- 给玩家背包物品(水槽取水/取脏盘子/盘子架取回) ----
# ---- 掉落物形式(水槽取水/取脏盘子/盘子架取回等) ----
# 【根因C】获得物品一律改掉落物形式, 不再直接塞进背包
if recipe.get("giveItem"):
self.giveItem(playerId, recipe["giveItem"])
_gItem = recipe["giveItem"]
if isinstance(_gItem, dict):
self._dropItemDict(dim, pos, _gItem)
else:
self._dropItemDict(dim, pos,
{"itemName": _gItem, "count": recipe.get("giveCount", 1), "auxValue": 0})
# ---- 转换成功提示 ----
if recipe.get("tip"):
@@ -348,22 +348,22 @@ ITEM_USE_ON_RULES = [
"replace": "rtdd:steamerdumplings_0", "consume": 4,
"startTimer": "rtdd:steamerdumplings_0",
"needBelow": LIT_STOVE, "belowTip": "§c蒸笼需要放在点燃的炉灶上",
"tip": "§a四个生饺子放进蒸笼, 开始蒸制(共60秒); 计时结束变成 熟饺子"},
"tip": "§a四个生饺子放进蒸笼, 开始蒸制; 计时结束变成 熟饺子"},
{"item": "rtdd:roubaozi_3", "surface": STEAMER,
"replace": "rtdd:baozilong_1", "consume": 3,
"startTimer": "rtdd:baozilong_1",
"needBelow": LIT_STOVE, "belowTip": "§c蒸笼需要放在点燃的炉灶上",
"tip": "§a三个生肉包子放进蒸笼, 开始蒸制(共60秒); 计时结束变成 熟包子"},
"tip": "§a三个生肉包子放进蒸笼, 开始蒸制; 计时结束变成 熟包子"},
{"item": "rtdd:bamboo_4", "surface": STEAMER,
"replace": "rtdd:bamboo_6", "consume": 1,
"startTimer": "rtdd:bamboo_6",
"needBelow": LIT_STOVE, "belowTip": "§c蒸笼需要放在点燃的炉灶上",
"tip": "§a竹筒饭放进蒸笼, 开始蒸制(共60秒); 计时结束变成 熟竹筒饭"},
"tip": "§a竹筒饭放进蒸笼, 开始蒸制; 计时结束变成 熟竹筒饭"},
{"item": "rtdd:plate_34", "surface": STEAMER,
"replace": "rtdd:steamermilkandegg", "consume": 1,
"startTimer": "rtdd:steamermilkandegg",
"needBelow": LIT_STOVE, "belowTip": "§c蒸笼需要放在点燃的炉灶上",
"tip": "§a蛋液半成品放进蒸笼, 开始蒸制(共60秒); 计时结束变成 蒸牛奶鸡蛋"},
"tip": "§a蛋液半成品放进蒸笼, 开始蒸制; 计时结束变成 蒸牛奶鸡蛋"},
]
# ============================================================================
@@ -372,34 +372,34 @@ ITEM_USE_ON_RULES = [
# ============================================================================
TIMER_RULES = [
# 发酵(60秒, autoStart)
{"source": "rtdd:rawdough_4", "target": "rtdd:dough_0", "time": 60,
{"source": "rtdd:rawdough_4", "target": "rtdd:dough_0", "time": 2,
"autoStart": True, "doneTip": "§e面团发酵好了"},
{"source": "rtdd:breaddough_1", "target": "rtdd:breaddough_1a", "time": 60,
{"source": "rtdd:breaddough_1", "target": "rtdd:breaddough_1a", "time": 2,
"autoStart": True, "doneTip": "§e法棍面胚发好了"},
# 蒸笼: 手持生食右键蒸笼 -> 蒸X_生(见 ITEM_USE_ON_RULES); 蒸X_生下方是
# 点燃的炉灶(stove_2)才计时, 60秒蒸熟
{"source": "rtdd:steamerdumplings_0", "target": "rtdd:steamerdumplings_1", "time": 60,
{"source": "rtdd:steamerdumplings_0", "target": "rtdd:steamerdumplings_1", "time": 2,
"below": [LIT_STOVE], "autoStart": True, "doneTip": "§e饺子蒸好了"},
{"source": "rtdd:baozilong_1", "target": "rtdd:baozilong_2", "time": 60,
{"source": "rtdd:baozilong_1", "target": "rtdd:baozilong_2", "time": 2,
"below": [LIT_STOVE], "autoStart": True, "doneTip": "§e包子蒸好了"},
{"source": "rtdd:bamboo_6", "target": "rtdd:bamboo_7", "time": 60,
{"source": "rtdd:bamboo_6", "target": "rtdd:bamboo_7", "time": 2,
"below": [LIT_STOVE], "autoStart": True, "doneTip": "§e竹筒饭蒸好了"},
{"source": "rtdd:steamermilkandegg", "target": "rtdd:steamermilkandegg_1", "time": 60,
{"source": "rtdd:steamermilkandegg", "target": "rtdd:steamermilkandegg_1", "time": 2,
"below": [LIT_STOVE], "autoStart": True, "doneTip": "§e蒸牛奶鸡蛋好了"},
# 烤炉烘焙(60秒, 下方判定烤炉; 烤炉被打碎 -> 该方块掉落+提示)
{"source": "rtdd:breaddough_1a", "target": "rtdd:bread_1", "time": 60,
{"source": "rtdd:breaddough_1a", "target": "rtdd:bread_1", "time": 2,
"below": [OVEN], "autoStart": True, "dropOnBreak": True, "doneTip": "§e法棍烤好了"},
{"source": "rtdd:breaddough_2", "target": "rtdd:bread_2", "time": 60,
{"source": "rtdd:breaddough_2", "target": "rtdd:bread_2", "time": 2,
"below": [OVEN], "autoStart": True, "dropOnBreak": True, "doneTip": "§e吐司烤好了"},
{"source": "rtdd:breaddough_4d", "target": "rtdd:bread_4c", "time": 60,
{"source": "rtdd:breaddough_4d", "target": "rtdd:bread_4c", "time": 2,
"below": [OVEN], "autoStart": True, "dropOnBreak": True, "doneTip": "§e甜甜圈胚烤好了"},
{"source": "rtdd:dought_9", "target": "rtdd:bread_3", "time": 60,
{"source": "rtdd:dought_9", "target": "rtdd:bread_3", "time": 2,
"below": [OVEN], "autoStart": True, "dropOnBreak": True, "doneTip": "§e可颂烤好了"},
{"source": "rtdd:cakebatterblock_0", "target": "rtdd:cakebatterblock", "time": 60,
{"source": "rtdd:cakebatterblock_0", "target": "rtdd:cakebatterblock", "time": 2,
"below": [OVEN], "autoStart": True, "dropOnBreak": True, "doneTip": "§e蛋糕面糊烤好了"},
{"source": "rtdd:plate_34", "target": "rtdd:plate_35", "time": 60,
{"source": "rtdd:plate_34", "target": "rtdd:plate_35", "time": 2,
"below": [OVEN], "autoStart": True, "dropOnBreak": True, "doneTip": "§e布丁半成品好了"},
{"source": "rtdd:creampastry_0", "target": "rtdd:souffle_0", "time": 60,
{"source": "rtdd:creampastry_0", "target": "rtdd:souffle_0", "time": 2,
"below": [OVEN], "autoStart": True, "dropOnBreak": True, "doneTip": "§e舒芙蕾烤好了"},
]
@@ -105,10 +105,9 @@ class MianshiService(BlueFoxServerSystem):
self.timerSources.add(src)
self.featureBlocks.add(src)
if src not in self.startTimerTips:
sec = r.get("time", 60)
tgt = NAME_MAP.get(r["target"], r["target"])
self.startTimerTips[src] = "§a开始蒸制: 还需%d秒, 计时结束变成 %s" % (sec, tgt)
self.timingTips[src] = "§e正在蒸制中: 还需%d秒, 计时结束变成 %s" % (sec, tgt)
self.startTimerTips[src] = "§a开始蒸制, 计时结束变成 %s" % tgt
self.timingTips[src] = "§e正在蒸制中, 计时结束变成 %s" % tgt
# ---- 结果方块/物品 -> "下一步"可读提示(供转换/入笼提示追加) ----
# 例子: 饺子皮 -> "加入 猪肉馅、青菜碎"; 可颂面胚 -> "放在烤炉上等计时(60秒)结束变成 可颂"
@@ -140,9 +139,9 @@ class MianshiService(BlueFoxServerSystem):
sec = r.get("time", 60)
tgt = NAME_MAP.get(r["target"], r["target"])
if r.get("below") == [OVEN]:
_addTip(src, "放在烤炉上等计时(%d秒)结束变成 %s" % (sec, tgt))
_addTip(src, "放在烤炉上等计时结束变成 %s" % tgt)
else:
_addTip(src, "等计时(%d秒)结束变成 %s" % (sec, tgt))
_addTip(src, "等计时结束变成 %s" % tgt)
# ---- 计时源方块 -> 计时结束后的成品名(烤炉固定开始烤制提示用) ----
self.timerTargetName = {}
@@ -191,13 +190,11 @@ class MianshiService(BlueFoxServerSystem):
if blockName in KNEAD_COUNTERS and (not itemName or itemName == "minecraft:air"):
return self._handleKnead(blockName, playerId, pos, dim, key)
# ---- 烤炉固定: 胚/面糊/蛋液等放在烤炉上 -> 禁止右键转换, 固定开始烤制 ----
if blockName in OVEN_FIX_BLOCKS and self._belowIs(dim, pos, OVEN):
FeatureDispatcher.startTimerFor(blockName, dim, pos, playerId)
if self.checkCooldown(key, 0.4):
comp.CreateMsg(playerId).NotifyOneMessage(
playerId, "§a已启动计时(共60秒)")
return True
# ---- 烤炉固定: 胚/面糊/蛋液等放在烤炉上 → 不再通过右键启动计时。
# 【根因D】计时改自动开始: MianshiTimerService 的 tick 会按下方是否为烤炉
# 自动启动计时(OVEN_FIX_BLOCKS 的 autoStart, 参照烤乳猪机制)。这里不再拦截
# 右键启动, 也不挡玩家正常放置。空手右键成品取走逻辑保留在下方。
# (移除了原"startTimerFor + 提示已启动计时"分支)
# ---- 烤制成品取走: 空手右键成品(下方是烤炉) -> 成品掉落物, 烤炉不动 ----
if blockName in OVEN_RESULT_BLOCKS and self._belowIs(dim, pos, OVEN) \
@@ -210,12 +207,8 @@ class MianshiService(BlueFoxServerSystem):
playerId, "§a取走" + self._nameOf(blockName))
return True
# ---- 空手右键"蒸笼计时源"(蒸X_生): 下方是点燃炉灶 -> 开始/继续计时 ----
if (not itemName or itemName == "minecraft:air") and blockName in self.timerSources:
if not self.checkCooldown(key, 0.4):
return True
self._handleStartTimer(blockName, playerId, pos, dim)
return True
# ---- 空手右键"蒸笼计时源"(蒸X_生): 计时已改自动(tick 判定下方点燃炉灶),
# 不再通过空手右键启动; 此处不再拦截, 避免空手右键被吞影响后续操作。----
# ---- 常规转换 ----
recipe = self.findRecipe(blockName, itemDict)
@@ -28,7 +28,8 @@ clientComp = clientApi.GetEngineCompFactory()
from ...BlueFoxTools.SpecialFunciton.BlueFoxSystem import BlueFoxClientSystem
from .InterceptData import (ITEM_USE_ON_CANCEL, ITEM_USE_ON_CANCEL_BY_ITEM,
SUSHI_BLOCKS, SUSHI_CANCEL, COATING_ITEMS,
TAKEOUT_CANCEL, GAI_FAN_CANCEL)
TAKEOUT_CANCEL, GAI_FAN_CANCEL, BLOCKUSE_CANCEL,
BLOCKUSE_CANCEL_ITEMS)
# 自定义事件: 客户端拦(碗/盘,汤锅/炒锅/油锅成品)放置预览后, 显式通知服务端执行盛出
# 服务端在 FeatureDispatcher 用同一事件名监听(见 service/FeatureDispatcher.py)
@@ -45,6 +46,13 @@ GAI_FAN_EVENT = "GaifanRequest"
# 种水稻. 若不显式通知, 客户端 ret=True 会吞掉 ServerItemUseOnEvent, 这些转换"点无反应"。
ITEM_USE_ON_REQUEST = "ItemUseOnRequest"
# 自定义事件: 客户端拦(手持可放置/可投掷物品, 功能方块)放置预览后, 显式通知服务端执行
# BlockUse 转换(根因B). 例如手持 生鸡肉/鳕鱼/鲑鱼 右键 支杆 -> 上杆; 死鱼右键挂肉钩 ->
# 挂放; 鸡蛋右键鸡窝 -> 放蛋; 胡萝卜右键饲料槽 -> 加料; 盘子右键丸子机/石磨 -> 盛取;
# 木条右键炉灶 -> 点火. 客户端 ret 掉放置/投掷预览后 NotifyToServer, 服务端按被点方块
# blockOwnerAll 路由到 handleBlockUse(与 TakeoutRequest 同机制).
BLOCK_USE_REQUEST = "BlockUseRequest"
class ClientFeatureDispatcher(BlueFoxClientSystem):
"""客户端右键拦截: 白名单提前 cancel/ret, 消除闪烁"""
@@ -56,6 +64,14 @@ class ClientFeatureDispatcher(BlueFoxClientSystem):
# 转换完成后一段时间内, 该位置再被当作"放置"会直接 ret 吞掉, 消除"转换后
# 由于引擎重置右键导致多放置一次"的闪一下。时长 1.5 秒(用户要求明显可见)。
self._afterCd = {}
# BlockUse 转换(手持食材/鸡蛋右键功能方块)的独立短冷却: 回调方块后的 0.2s 内
# 该坐标再被当作右键会直接 ret 吞掉, 防止"放蛋后连点被误当投掷扔出去"。
# 时长 0.2 秒(用户指定"稍微停一下0.1~0.2秒"); 翻炒(0.3s 间隔)不受影响。
self._blockCd = {}
# 玩家级"刚做过 BlockUse 转换(放蛋/放料)"短标记: key=playerId, value=时间戳.
# 用于 OnItemTryUse(投掷/使用轨道)在 0.2s 内取消该玩家的投掷动画(如鸡蛋右键鸡窝
# 放蛋后引擎重置右键立即触发投掷 -> 扔蛋)。cd 到即放行, 对地扔蛋正常。
self._blockCdPlayer = {}
self.ListenForEvent(clientApi.GetEngineNamespace(),
clientApi.GetEngineSystemName(),
"ClientItemUseOnEvent", self, self.OnItemUseOn)
@@ -95,6 +111,31 @@ class ClientFeatureDispatcher(BlueFoxClientSystem):
args["ret"] = True
return
# BlockUse 转换短冷却: 放蛋/放料后 0.2s 内同一坐标再被当作右键(引擎重置导致的
# 二次使用/投掷)直接吞掉, 防止"连点鸡窝被误当扔蛋"。时长 0.2s(用户指定, 不卡翻炒)。
_bcd = self._blockCd.get(cdKey)
if _bcd is not None and (time.time() - _bcd) < 0.2:
args["ret"] = True
return
# 【根因B】手持可放置/可投掷物品 右键功能方块(支杆/挂肉钩/鸡窝/饲料槽/丸子机/石磨/
# 炉灶/寿司/炒锅放料 等) -> 此组合是 BlockUse 转换(服务端 handleBlockUse), 不能走下面
# ITEM_USE_ON_CANCEL_BY_ITEM 的"相邻放置"(那会绕开 owners 检查把死鱼/死鸡放到地面)。
# 此处 ret 掉放置/投掷预览 + 显式 BlockUseRequest, 由服务端按被点方块 handleBlockUse。
# 【注意】这里设 _blockCd(0.2s 短冷却): 放蛋/放料转换后引擎重置右键会再触发一次
# "使用/投掷", 若这次也被当正常右键会扔蛋/放着。0.2s 内同坐标直接吞掉, 既消扔蛋又
# 不卡翻炒(翻炒 0.3s 间隔 > 0.2s)。去重同时靠服务端 handleBlockUse 自带 checkCooldown。
if blockName and (itemName, blockName) in BLOCKUSE_CANCEL:
args["ret"] = True
self._blockCd[cdKey] = time.time()
self._blockCdPlayer[pId] = time.time() # 玩家级短标记, 取消随后 0.2s 内投掷动画
self.NotifyToServer(BLOCK_USE_REQUEST, {
"playerId": pId,
"x": args.get("x"), "y": args.get("y"), "z": args.get("z"),
"blockName": blockName, "itemName": itemName,
})
return
# 纯手持物品取消 (相邻放置/手持吃物品右键表面): 只看 item, 服务端走 handleItemUseOn
# (Yuanban 相邻放置/装盘、Natural 种水稻、Shiyongfood 直接吃)。这些组合一旦客户端
# ret=True 会吞掉 ServerItemUseOnEvent, 故必须显式通知服务端补齐(盖饭同款机制)。
@@ -187,6 +228,15 @@ class ClientFeatureDispatcher(BlueFoxClientSystem):
# 饭团/水煮蛋/豆腐 在 ITEM_USE_ON_CANCEL_BY_ITEM 里(EAT_ITEMS)
if itemName in ITEM_USE_ON_CANCEL_BY_ITEM:
args["cancel"] = True
# 投掷/使用抑制: 玩家刚做过 BlockUse 转换(0.2s 内, 如放蛋/放料), 引擎重置右键会立即
# 再触发一次"投掷/使用"动画(鸡蛋右键鸡窝放蛋 -> 扔蛋)。此窗口内若手持 BlockUse 可投掷/
# 可用物品, 直接 cancel 掉投掷; 窗口过后放行(对地扔蛋正常)。
# 注意: 玩家级标记(pId)而非坐标——投掷事件无目标方块坐标, 只有玩家。0.2s 极短且仅在
# 刚做完方块转换后生效, 不会影响玩家正常对地面/空中扔蛋。
_pId = args.get("playerId") or args.get("entityId")
_pc = self._blockCdPlayer.get(_pId, 0)
if _pc and (time.time() - _pc) < 0.2 and itemName in BLOCKUSE_CANCEL_ITEMS:
args["cancel"] = True
# 注: 客户端系统需在客户端进程被实例化。modMain 只能在服务端 import 服务端
@@ -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")
@@ -103,6 +103,72 @@ try:
except Exception:
YOUGUO_SWITCH, YOUGUO_PLATE = None, None
# ---- 根因B: 各"手持可放置/可投掷物品右键功能方块"走 ServerBlockUseEvent 的转换规则表.
# 字段用 source/block + item(块字段统一为 source; 不同服务可能用 source 或 block, 取兼容). ----
_BLOCK_USE_TABLES = []
# 动物养殖(鸡窝放蛋/饲料槽/水槽 等): AnimalData.ANIMAL_SWITCHES
try:
from ..dongwuyangzhi.AnimalData import ANIMAL_SWITCHES as ANIMAL_RS
_BLOCK_USE_TABLES.append(ANIMAL_RS)
except Exception:
pass
# 烧烤(上杆: 支杆+生肉/鱼): BarbecueData.BARBECUE_SWITCHES
try:
from ..shaokao.BarbecueData import BARBECUE_SWITCHES as BBQ_RS
_BLOCK_USE_TABLES.append(BBQ_RS)
except Exception:
pass
# 死亡生物(挂肉钩: 死鱼+钩/尸体挂放): DeadmobData.DEADMOB_SWITCHES
try:
from ..siwangshengwu.DeadmobData import DEADMOB_SWITCHES as DEADMOB_RS
_BLOCK_USE_TABLES.append(DEADMOB_RS)
except Exception:
pass
# 厨具(炉灶/丸子机/石磨: 木条+炉灶 等): ChujuData.STOVE_RULES / MEATBALLMAKER_RULES
try:
from ..chuju.ChujuData import (STOVE_RULES as CHUJU_STOVE,
MEATBALLMAKER_RULES as CHUJU_MEAT,
STEAMER_RULES as CHUJU_STEAM)
_BLOCK_USE_TABLES.extend([CHUJU_STOVE, CHUJU_MEAT, CHUJU_STEAM])
except Exception:
pass
# 面食(面粉加水/揉面 等): MianshiData.SWITCH_RULES
try:
from ..mianshi.MianshiData import SWITCH_RULES as MIANSHI_SWITCH
_BLOCK_USE_TABLES.append(MIANSHI_SWITCH)
except Exception:
pass
# 炒锅(放料: 手持食材右键炒锅等): ChaoguoData.SWITCH_RULES
# (放料/翻炒走 handleBlockUse; 其中 action==take_out 的"盘子出锅"已由 TAKEOUT_CANCEL
# 专用通道处理, 派生时过滤掉, 避免与盛出走 TakeoutRequest 冲突)
try:
from ..chaoguo.ChaoguoData import SWITCH_RULES as CHAOGUO_BLOCKUSE
_BLOCK_USE_TABLES.append(CHAOGUO_BLOCKUSE)
except Exception:
pass
# 汤锅(放料: 手持食材/锅态汤右键汤锅等): TangguoData.SWITCH_RULES
# 同样过滤 action==take_out(盛出走 TakeoutRequest)
try:
from ..tangguo.TangguoData import SWITCH_RULES as TANGGUO_BLOCKUSE
_BLOCK_USE_TABLES.append(TANGGUO_BLOCKUSE)
except Exception:
pass
# 油锅(放料: 手持食材右键油锅等): YouguoData.SWITCH_RULES
# 同样过滤 action==take_out(出锅走 TakeoutRequest)
try:
from ..youguo.YouguoData import SWITCH_RULES as YOUGUO_BLOCKUSE
_BLOCK_USE_TABLES.append(YOUGUO_BLOCKUSE)
except Exception:
pass
# ---- 原版放置/盘子转换 (itemUseOn, surface=相邻放置 / source=盘子) ----
try:
from ..yuanbanfangzhi.YuanbanData import (
@@ -186,6 +252,37 @@ if CHAOGUO_USON:
if isinstance(_it, basestring) and _it and isinstance(_bk, basestring) and _bk:
GAI_FAN_CANCEL.add((_it, _bk))
# ============================================================================
# 三之C、根因B "手持可放置/可投掷物品右键功能方块" -> BlockUse 显式事件拦截
# 客户端拦(手持物品, 功能方块)的放置/投掷预览后, NotifyToServer(BLOCK_USE_REQUEST),
# 服务端 OnBlockUseRequest 按被点方块路由到对应服务的 handleBlockUse。
# 仅取"手持物品非空"(item != None 且非 * 通配) 的转换对 —— 空手右键(item=None)
# 不涉及放置预览, 不需要拦截, 仍走正常 ServerBlockUseEvent。
# ============================================================================
BLOCKUSE_CANCEL = set()
for _tbl in _BLOCK_USE_TABLES:
for _r in _as_list(_tbl):
if not isinstance(_r, dict):
continue
_it = _r.get("item")
if not _it or not isinstance(_it, basestring) or _it.startswith("*"):
continue # 只看"手持具体物品"的转换; 空手/通配不拦
# 炒锅 SWITCH_RULES 的 action==take_out(盘子出锅) 已由 TAKEOUT_CANCEL 专用通道处理,
# 这里跳过, 避免与盛出走 TakeoutRequest 冲突; 其余(放料/翻炒)走 BlockUse。
if _r.get("action") == "take_out":
continue
# 被点方块: 兼容 source(主流) 与 block(个别)
_bk = _r.get("source") or _r.get("block")
if isinstance(_bk, basestring) and _bk:
BLOCKUSE_CANCEL.add((_it, _bk))
# BlockUse 转换涉及的全部手持物品集合(客户端 OnItemTryUse 也需取消默认"使用/投掷/倒水",
# 否则手持鸡蛋右键鸡窝会被当成投掷扔出去; 水桶倒水同理)。
# 只取原版/可"使用"的物品(minecraft: 前缀)进入 TryUse 取消; rtdd 方块物品不触发 TryUse。
BLOCKUSE_CANCEL_ITEMS = set(
_it for (_it, _bk) in BLOCKUSE_CANCEL if _it.startswith("minecraft:")
)
# ---- Chuju 锅铲/油罐 inline 转换(在 ChujuService.handleItemUseOn 内联, 不在数据表) ----
# 手持 锅铲/带油锅铲 右键 油罐(oilcan)/炒锅(wok) 蘸油/还油/上油 -> 取消默认
CHUJU_INLINE_CANCEL = {
@@ -31,7 +31,7 @@ ITEM_USE_ON_RULES = [
"item": "minecraft:chicken", # 原版生鸡肉
"target": "rtdd:roastchickenplace_0", # 生鸡肉(放置后)
"consume": 1,
"tip": "§a已放置生鸡肉,可右键支杆上杆烤制",
"tip": "§a已放置生鸡肉",
},
{
"item": "minecraft:salmon", # 原版生鲑鱼