From 2a8c7e832848f3122fcd6a7b7f5abc3b6d517630 Mon Sep 17 00:00:00 2001 From: wenhe <1046926120@qq.com> Date: Thu, 6 Aug 2026 11:37:28 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AE=AD=E6=A1=B6=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ceshi/service/BlockSwitchService.py | 306 +++++++++++------- 1 file changed, 190 insertions(+), 116 deletions(-) diff --git a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/ceshi/service/BlockSwitchService.py b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/ceshi/service/BlockSwitchService.py index 5a0d04c..5aac38a 100644 --- a/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/ceshi/service/BlockSwitchService.py +++ b/behavior_pack_v8jz0zSR/Script_NeteaseModousIY5aSF/ceshi/service/BlockSwitchService.py @@ -46,6 +46,10 @@ from ...BlueFoxTools.ToolsConfig import LevelId # GetBlockEntityData 返回的 BlockEntityData 对象支持 [] 操作,直接赋值即可持久化。 blockEntityDataComp = comp.CreateBlockEntityData(LevelId) +# ---- 游戏组件(用于 AddTimer 延迟写入 block entity) ----# +gameComp = comp.CreateGame(LevelId) + + # ---- 导入工具方块切换规则 ---- # BLOCK_SWITCHES 是一个列表,每条规则是一个 dict,格式见 BlockSwitchData.py 顶部注释 from ..BlockSwitchData import BLOCK_SWITCHES @@ -157,7 +161,7 @@ class BlockSwitchService(BlueFoxServerSystem): # ============================================================================ # 主入口:玩家右键方块 - # ============================================================================ + # ============================================================================ def OnBlockUse(self, args): """ @@ -307,7 +311,7 @@ class BlockSwitchService(BlueFoxServerSystem): return # ---- 切换型 ---- - # 内存中已有数据(_loadBlockDataToMemory 已加载),直接用 + # 内存中已有数据(_loaBlockDataToMemory 已加载),直接用 record = self.switchRecord.get(key) # ---- 正向操作:往方块里放东西(source == 被右键的方块) ---- @@ -425,13 +429,27 @@ class BlockSwitchService(BlueFoxServerSystem): """ try: blockData = blockEntityDataComp.GetBlockEntityData(dim, pos) + print "[BlockSwitchService] _writeBlockData GetBlockEntityData returned: %s (pos=%s dim=%s)" % (type(blockData).__name__ if blockData is not None else "None", pos, dim) if blockData is None: print "[BlockSwitchService] _writeBlockData blockData is None: pos=%s dim=%s" % (pos, dim) return for key, value in kwargs.items(): + print "[BlockSwitchService] _writeBlockData setting: [%s]=%s (type=%s)" % (key, value, type(value).__name__) blockData[key] = value + # 写入后立即读取验证 + verifyData = blockEntityDataComp.GetBlockEntityData(dim, pos) + if verifyData: + for key in kwargs: + try: + val = verifyData[key] + print "[BlockSwitchService] _writeBlockData verify: [%s]=%s" % (key, val) + except: + print "[BlockSwitchService] _writeBlockData verify: [%s]=" % key + else: + print "[BlockSwitchService] _writeBlockData verify FAILED: blockData is None after write!" except Exception as e: - print "[BlockSwitchService] _writeBlockData 写入失败: pos=%s dim=%s err=%s" % (pos, dim, str(e)) + import traceback + print "[BlockSwitchService] _writeBlockData 写入失败: pos=%s dim=%s err=%s trace=%s" % (pos, dim, str(e), traceback.format_exc()) def _saveRecord(self, key, playerId, pos, dim, record): """ @@ -471,6 +489,7 @@ class BlockSwitchService(BlueFoxServerSystem): 记录存在方块实体数据里,所以玩家退出游戏再重进, loadSwitchRecord 还能从方块里把物品读回来。 """ + print "[BlockSwitchService] saveSwitchRecord pos=%s dim=%s record=%s" % (pos, dim, record) if "count" in record: self._writeBlockData(dim, pos, rtdd_swt="count", @@ -492,18 +511,39 @@ class BlockSwitchService(BlueFoxServerSystem): """ try: blockData = blockEntityDataComp.GetBlockEntityData(dim, pos) + print "[BlockSwitchService] loadSwitchRecord GetBlockEntityData returned: %s (pos=%s dim=%s)" % (type(blockData).__name__ if blockData is not None else "None", pos, dim) + if blockData is None: + print "[BlockSwitchService] loadSwitchRecord blockData is None!" + return None + # 注意:BlockEntityData 对象不支持 .keys() 和 .get() 方法 + # 只支持 [] 操作符(__getitem__),key 不存在会抛 KeyError swt = blockData["rtdd_swt"] # 记录类型:"count" 或 "stack" + print "[BlockSwitchService] loadSwitchRecord rtdd_swt=%s" % swt if swt == "count": + count = blockData["rtdd_swc"] + itemName = blockData["rtdd_swi"] + try: + auxValue = blockData["rtdd_swa"] + except (KeyError, Exception): + auxValue = 0 + print "[BlockSwitchService] loadSwitchRecord count data: count=%s itemName=%s auxValue=%s" % (count, itemName, auxValue) return { - "count": blockData["rtdd_swc"], - "itemName": blockData["rtdd_swi"], - "auxValue": blockData.get("rtdd_swa", 0), + "count": count, + "itemName": itemName, + "auxValue": auxValue, } elif swt == "stack": + stack = blockData["rtdd_sws"] + print "[BlockSwitchService] loadSwitchRecord stack data: len=%s" % len(stack) return { - "stack": blockData["rtdd_sws"], + "stack": stack, } - except: + else: + print "[BlockSwitchService] loadSwitchRecord unknown swt: %s" % swt + return None + except Exception as e: + import traceback + print "[BlockSwitchService] loadSwitchRecord exception: %s trace=%s" % (str(e), traceback.format_exc()) return None def clearSwitchRecord(self, key, playerId, pos, dim): @@ -513,6 +553,147 @@ class BlockSwitchService(BlueFoxServerSystem): self.switchRecord.pop(key, None) self._writeBlockData(dim, pos, rtdd_swt="", rtdd_swc=0, rtdd_swi="", rtdd_swa=0, rtdd_sws=[]) + # ============================================================================ + # 箭桶模式(arrowMode):存箭 / 取箭 + # ============================================================================ + + def _handleArrowMode(self, arrowMode, recipe, playerId, pos, dim, itemDict, key, blockName): + """ + 处理箭桶的存箭/取箭逻辑。 + + 【存箭流程】 + 1. 检查手中是否为箭(_isArrow 已在 findRecipe 验证过) + 2. 检查方块是否有记录且类型匹配: + a. 已有记录 → 检查手中箭类型(itemName+auxValue)是否匹配 → 不匹配则 return + b. 无记录 → 新建记录 + 3. 计算实际可存数量 = min(手中数量, maxCount - 已有数量) + 4. 如果可存数量 <= 0 → return + 5. 从手中扣减,更新记录,替换方块 + + 【取箭流程】 + 1. 检查方块是否有记录且 count > 0 + 2. 给玩家手中/背包添加 count 只箭(itemName + auxValue 按记录) + 3. 清空记录,替换方块 + """ + record = self.switchRecord.get(key) + + if arrowMode == "store": + # ---- 存箭 ---- + handCount = itemDict.get("count", 0) + if handCount <= 0: + return + + handItemName = itemDict.get("newItemName", "") + handAuxValue = itemDict.get("newAuxValue", 0) + + # 检查方块是否有记录且类型匹配 + if record and record["count"] > 0: + # 已有记录 → 检查箭类型是否一致 + if record.get("itemName") != handItemName or record.get("auxValue", 0) != handAuxValue: + return # 类型不匹配,不能混存 + else: + # 无记录 → 新建 + record = { + "count": 0, + "itemName": handItemName, + "auxValue": handAuxValue, + } + + # 计算实际可存数量 + maxCount = recipe.get("maxCount", 32) + if record["count"] >= maxCount: + return # 已满 + storeCount = min(handCount, maxCount - record["count"]) + + # 从玩家手中扣减 + self._consumeItemCount(playerId, itemDict, storeCount) + + # 更新记录 + record["count"] += storeCount + + # 替换方块 + if recipe.get("target"): + self.replaceBlock(pos, dim, recipe["target"], playerId) + + # ★ 保存记录到新方块 + # 注意:这里不用延迟写入。虽然 SetBlockNew 替换方块后 block entity + # 可能尚未完全初始化,但 GetBlockEntityData 仍然能获取到对象并写入。 + # 延迟写入反而可能导致退出游戏后数据不持久化。 + self._saveRecord(key, playerId, pos, dim, record) + + elif arrowMode == "retrieve": + # ---- 取箭 ---- + if not record or record["count"] <= 0: + return + + retCount = record["count"] + retItemName = record["itemName"] + retAuxValue = record.get("auxValue", 0) + + # 给玩家箭 + self.giveItem(playerId, retItemName, retCount, retAuxValue) + + # 替换方块 + if recipe.get("target"): + self.replaceBlock(pos, dim, recipe["target"], playerId) + + # ★ 清空记录 + self.clearSwitchRecord(key, playerId, pos, dim) + + def _consumeItemCount(self, playerId, itemDict, amount): + """ + 从玩家主手批量扣减指定数量的物品。 + 如果手中数量刚好等于 amount → 清空主手。 + 如果手中数量大于 amount → 减少 amount。 + """ + if not itemDict or amount <= 0: + return + count = itemDict.get("count", 0) + if count <= amount: + # 数量不够或刚好用完 → 清空主手 + comp.CreateItem(playerId).SetEntityItem( + serverApi.GetMinecraftEnum().ItemPosType.CARRIED, + {"itemName": "minecraft:air", "count": 0, "auxValue": 0}) + else: + # 数量够 → 减少 amount 个 + itemDict["count"] = count - amount + comp.CreateItem(playerId).SetEntityItem( + serverApi.GetMinecraftEnum().ItemPosType.CARRIED, + {"itemName": itemDict["newItemName"], + "count": itemDict["count"], + "auxValue": itemDict.get("newAuxValue", 0)}) + + # ============================================================================ + # 延迟写入帮手(解决 SetBlockNew 替换方块后 block entity 未初始化的问题) + # ============================================================================ + + def _saveRecordDelayed(self, key, playerId, pos, dim, record): + """ + 延迟一帧保存记录到方块实体数据。 + + SetBlockNew 替换方块后,新方块的 block entity 需要引擎内部初始化时间, + 立即 GetBlockEntityData 可能返回 None 导致写入静默失败。 + + 先更新内存,再延迟写入方块实体。 + """ + print "[BlockSwitchService] _saveRecordDelayed key=%s record=%s" % (key, record) + self.switchRecord[key] = record + gameComp.AddTimer(0.1, self.saveSwitchRecord, playerId, pos, dim, record) + + def _clearRecordDelayed(self, key, playerId, pos, dim): + """ + 延迟一帧清除方块实体数据。 + """ + print "[BlockSwitchService] _clearRecordDelayed key=%s" % (key,) + self.switchRecord.pop(key, None) + gameComp.AddTimer(0.1, self._clearRecordWrite, playerId, pos, dim) + + def _clearRecordWrite(self, playerId, pos, dim): + """ + 实际执行清除写入(被 AddTimer 调用)。 + """ + self._writeBlockData(dim, pos, rtdd_swt="", rtdd_swc=0, rtdd_swi="", rtdd_swa=0, rtdd_sws=[]) + # ============================================================================ # 工具方法:获取玩家手中物品 # ============================================================================ @@ -587,111 +768,4 @@ class BlockSwitchService(BlueFoxServerSystem): 如果背包满了,物品会掉在地上。 """ comp.CreateItem(playerId).SpawnItemToPlayerInv( - {"itemName": itemName, "count": count, "auxValue": auxValue}, playerId) - - # ============================================================================ - # 箭桶模式(arrowMode):存箭 / 取箭 - # ============================================================================ - - def _handleArrowMode(self, arrowMode, recipe, playerId, pos, dim, itemDict, key, blockName): - """ - 处理箭桶的存箭/取箭逻辑。 - - 【存箭流程】 - 1. 检查手中是否为箭(_isArrow 已在 findRecipe 验证过) - 2. 检查方块是否有记录且类型匹配: - a. 已有记录 → 检查手中箭类型(itemName+auxValue)是否匹配 → 不匹配则 return - b. 无记录 → 新建记录 - 3. 计算实际可存数量 = min(手中数量, maxCount - 已有数量) - 4. 如果可存数量 <= 0 → return - 5. 从手中扣减,更新记录,替换方块 - - 【取箭流程】 - 1. 检查方块是否有记录且 count > 0 - 2. 给玩家手中/背包添加 count 只箭(itemName + auxValue 按记录) - 3. 清空记录,替换方块 - """ - record = self.switchRecord.get(key) - - if arrowMode == "store": - # ---- 存箭 ---- - handCount = itemDict.get("count", 0) - if handCount <= 0: - return - - handItemName = itemDict.get("newItemName", "") - handAuxValue = itemDict.get("newAuxValue", 0) - - # 检查方块是否有记录且类型匹配 - if record and record["count"] > 0: - # 已有记录 → 检查箭类型是否一致 - if record.get("itemName") != handItemName or record.get("auxValue", 0) != handAuxValue: - return # 类型不匹配,不能混存 - else: - # 无记录 → 新建 - record = { - "count": 0, - "itemName": handItemName, - "auxValue": handAuxValue, - } - - # 计算实际可存数量 - maxCount = recipe.get("maxCount", 32) - if record["count"] >= maxCount: - return # 已满 - storeCount = min(handCount, maxCount - record["count"]) - - # 从玩家手中扣减 - self._consumeItemCount(playerId, itemDict, storeCount) - - # 更新记录 - record["count"] += storeCount - - # 替换方块 - if recipe.get("target"): - self.replaceBlock(pos, dim, recipe["target"], playerId) - - # 保存记录到新方块 - self._saveRecord(key, playerId, pos, dim, record) - - elif arrowMode == "retrieve": - # ---- 取箭 ---- - if not record or record["count"] <= 0: - return - - retCount = record["count"] - retItemName = record["itemName"] - retAuxValue = record.get("auxValue", 0) - - # 给玩家箭 - self.giveItem(playerId, retItemName, retCount, retAuxValue) - - # 替换方块 - if recipe.get("target"): - self.replaceBlock(pos, dim, recipe["target"], playerId) - - # 清空记录 - self.clearSwitchRecord(key, playerId, pos, dim) - - def _consumeItemCount(self, playerId, itemDict, amount): - """ - 从玩家主手批量扣减指定数量的物品。 - 如果手中数量刚好等于 amount → 清空主手。 - 如果手中数量大于 amount → 减少 amount。 - """ - if not itemDict or amount <= 0: - return - count = itemDict.get("count", 0) - if count <= amount: - # 数量不够或刚好用完 → 清空主手 - comp.CreateItem(playerId).SetEntityItem( - serverApi.GetMinecraftEnum().ItemPosType.CARRIED, - {"itemName": "minecraft:air", "count": 0, "auxValue": 0}) - else: - # 数量够 → 减少 amount 个 - itemDict["count"] = count - amount - comp.CreateItem(playerId).SetEntityItem( - serverApi.GetMinecraftEnum().ItemPosType.CARRIED, - {"itemName": itemDict["newItemName"], - "count": itemDict["count"], - "auxValue": itemDict.get("newAuxValue", 0)}) \ No newline at end of file + {"itemName": itemName, "count": count, "auxValue": auxValue}, playerId) \ No newline at end of file