烧烤 动物养殖 分类 转换功能 计时转换 退出重新判断tick 解耦

This commit is contained in:
wenhe committed 2026-09-02 01:08:43 +08:00
1 parent f359008ac3
commit 991239bc26
4 files changed
+264 -432

No files matched your search

@@ -3,33 +3,33 @@
================================================================================
统一事件分发器(服务端)—— 方案A核心
================================================================================
【为什么需要它】
以前每个功能(做菜/工具切换)都各自监听 ServerBlockUseEvent,
玩家每次右键,所有系统都会被触发一遍、各自跑一次数据库拦截判断,
功能越多损耗越大。
【事件监听在哪?—— 全模组只有这一个类监听游戏事件】
所有右键/计时相关事件都由本类(FeatureDispatcher)统一监听:
- ServerBlockUseEvent 玩家右键方块 → 按 blockOwner 路由
- ServerItemUseOnEvent 手持物品右键方块表面 → 按 itemUseOnOwner 路由
- ServerItemTryUseEvent 拦截喝药水/吃食物等默认行为(配合右键转换)
- ServerBlockEntityTickEvent 计时方块每tick → 按 timerOwner 路由
【它做什么】
FeatureDispatcher 是"唯一"监听右键相关事件的服务端系统:
- ServerBlockUseEvent 玩家右键方块
- ServerItemUseOnEvent 手持物品右键方块表面(如放置生鸡肉)
- ServerItemTryUseEvent 拦截喝药水/吃食物等默认行为
其他所有功能服务(AnimalService/BarbecueService 及各自的 TimerService)
【都不监听事件】。它们只是"逻辑处理类",被本类调用:
- 右键服务实现 handleBlockUse / handleItemUseOn,由本类转发
- 计时服务实现 handleBlockEntityTick / startTimer,由本类转发
各功能服务在初始化时通过 FeatureDispatcher.register(self) 上报:
- 自己管辖的方块名集合 → 构建 方块名→服务 的 O(1) 路由表
- 自己处理的手持物品 → 构建 物品名→服务 的 O(1) 路由表
事件进来后,分发器先用 blockName / itemName 在路由表里 O(1) 定位到
唯一的那个功能服务,再调用它的 handleBlockUse / handleItemUseOn。
→ 每个右键事件只命中一个数据库,其余功能的数据库根本不会被查询。
【为什么要它】
以前每个功能都各自监听 ServerBlockUseEvent,玩家每次右键所有系统都被触发
一遍、各自跑一次数据库,功能越多损耗越大。
现在分发器用"方块名→服务"的 O(1) dict 路由,每个事件只命中一个服务。
【如何新增一个功能】
1. 新建 xxx/xxxData.py 写转换规则
2. 新建 xxx/service/XxxService.py(继承 BlueFoxServerSystem):
- __init__ 里构建自己的规则字典
- 实现 getFeatureBlocks() / getItemUseOnRules()
- 实现 handleBlockUse(args) / handleItemUseOn(args)
- 调用 FeatureDispatcher.register(self)
3. 在 modMain.py import 这个服务类即可
2. 右键转换 → 新建 xxx/service/XxxService.py(继承 BlueFoxServerSystem):
- __init__ 里构建规则字典 + 调 FeatureDispatcher.register(self)
- 实现 getFeatureBlocks() / getItemUseOnRules() / handleBlockUse(...)
3. 计时转换 → 新建 xxx/service/XxxTimerService.py(继承 BlueFoxServerSystem):
- __init__ 里构建计时规则字典 + 调 FeatureDispatcher.registerTimer(self)
- 实现 getTimerBlocks() / handleBlockEntityTick(...) / startTimer(...)
- 右键服务要"启动计时"时,调 FeatureDispatcher.startTimerFor(...) 转发过来
4. 在 modMain.py import 这些服务类即可(框架会自动实例化)
================================================================================
"""
import mod.server.extraServerApi as serverApi
@@ -48,13 +48,12 @@ blockEntityDataComp = comp.CreateBlockEntityData(LevelId)
class FeatureDispatcher(BlueFoxServerSystem):
"""
统一事件分发器
统一事件分发器(全模组唯一监听事件的地方)
【类级注册表(所有功能服务共享同一份路由表)】
blockOwner = {方块名: 功能服务实例}
blockSet = {方块名} # 全部功能方块集合
itemUseOnOwner = {物品名: 功能服务实例}
itemUseOnSet = {物品名} # 全部"手持物品右键方块表面"集合
【类级注册表(所有功能服务共享同一份路由表,O(1) 定位)】
blockOwner = {方块名: 右键服务实例} # ServerBlockUseEvent 用
itemUseOnOwner = {物品名: 右键服务实例} # ServerItemUseOnEvent 用
timerOwner = {方块名: 计时服务实例} # ServerBlockEntityTickEvent 用
"""
# ---- 类级共享路由表(用类变量,任何实例都能读写) ----
@@ -62,11 +61,17 @@ class FeatureDispatcher(BlueFoxServerSystem):
blockSet = set()
itemUseOnOwner = {}
itemUseOnSet = set()
timerOwner = {}
timerSet = set()
# ============================================================================
# 注册接口(功能服务在 __init__ 里调用)
# ============================================================================
@classmethod
def register(cls, service):
"""
功能服务初始化时调用,把管辖的方块和物品注册进路由表。
右键服务初始化时调用:把管辖的"右键方块"和"手持物品右键方块表面"注册进路由表。
同一方块/物品只会归属最后一个注册的服务(一般不会冲突)。
"""
# 右键方块路由:方块名 → 服务
@@ -79,9 +84,33 @@ class FeatureDispatcher(BlueFoxServerSystem):
cls.itemUseOnOwner[itemName] = service
cls.itemUseOnSet.add(itemName)
@classmethod
def registerTimer(cls, service):
"""
计时服务初始化时调用:把管辖的"计时源方块"注册进计时路由表。
ServerBlockEntityTickEvent 会按此表把 tick 事件路由到对应的计时服务。
"""
for block in service.getTimerBlocks():
cls.timerOwner[block] = service
cls.timerSet.add(block)
@classmethod
def startTimerFor(cls, blockName, dim, pos, playerId=None):
"""
右键服务调用:把"计时源方块"的计时启动任务转发给对应的计时服务。
- 例如:烧烤右键"烤鸡(生)"开始烤制;动物装满饲料盆后自动开始变空计时
- 内部通过 timerOwner 路由表 O(1) 找到计时服务,调它的 startTimer
返回 True=找到了计时服务并处理;False=该方块不属于任何计时服务
"""
service = cls.timerOwner.get(blockName)
if not service:
return False
service.startTimer(dim, pos, blockName, playerId)
return True
def __init__(self, namespace, systemName):
"""
初始化:注册三个事件监听。
初始化:注册事件监听(全模组唯一监听点)。
注意:本类自身不注册任何功能方块,只负责把事件分发给已注册的服务。
"""
super(FeatureDispatcher, self).__init__(namespace, systemName)
@@ -94,7 +123,7 @@ class FeatureDispatcher(BlueFoxServerSystem):
self.UnListenEvent()
# ============================================================================
# 事件注册与注销
# 事件注册与注销(这是全模组唯一监听事件的地方)
# ============================================================================
def ListenEvent(self):
@@ -133,7 +162,7 @@ class FeatureDispatcher(BlueFoxServerSystem):
"ServerBlockEntityTickEvent", self, self.OnBlockEntityTick)
# ============================================================================
# 主入口1:玩家右键方块
# 主入口1:玩家右键方块 → 按 blockOwner 路由到右键服务
# ============================================================================
def OnBlockUse(self, args):
@@ -141,7 +170,7 @@ class FeatureDispatcher(BlueFoxServerSystem):
玩家右键方块时触发。
用被右键的方块名 O(1) 定位到唯一的功能服务,转发给它的 handleBlockUse。
服务返回 True = 处理成功 → 取消默认行为(如防止喝药水),并标记拦截下一次物品使用
服务返回 True = 处理成功 → 取消默认行为,并标记拦截下一次物品使用
服务返回 False = 当前组合无匹配规则 → 交还默认行为
"""
blockName = args.get("blockName")
@@ -157,7 +186,7 @@ class FeatureDispatcher(BlueFoxServerSystem):
self._cancelItemUse[playerId] = True
# ============================================================================
# 主入口2:手持物品右键方块表面
# 主入口2:手持物品右键方块表面 → 按 itemUseOnOwner 路由到右键服务
# ============================================================================
def OnItemUseOn(self, args):
@@ -165,10 +194,8 @@ class FeatureDispatcher(BlueFoxServerSystem):
手持物品右键某个方块表面时触发(如手持原版生鸡肉右键地面)。
用手持物品名 O(1) 定位到唯一的功能服务,转发给它的 handleItemUseOn。
注意:取消"使用物品于方块"的字段是 ret(不是 cancel),
官方要求双端设置才能真正取消默认行为。本分发器已在服务端设 ret=True,
服务端不会执行默认放置/使用逻辑;客户端如需完美取消动画,
可再增加一个监听 ClientItemUseOnEvent 的客户端配对系统。
取消"使用物品于方块"的字段是 ret(不是 cancel),官方要求双端设置才能
真正取消默认行为。本分发器已在服务端设 ret=True。
"""
itemName = args.get("itemName")
service = self.itemUseOnOwner.get(itemName)
@@ -196,7 +223,7 @@ class FeatureDispatcher(BlueFoxServerSystem):
args['cancel'] = True
# ============================================================================
# 主入口4:计时方块 tick(持久化计时)
# 主入口4:计时方块 tick → 按 timerOwner 路由到计时服务(持久化计时)
# ============================================================================
def OnBlockEntityTick(self, args):
@@ -207,8 +234,9 @@ class FeatureDispatcher(BlueFoxServerSystem):
- 服务器重启/玩家重进后,tick 恢复,读回时间戳继续计时,
到点即转换(例如烤鸡烤了40秒退出,回来只剩20秒)
注意:该事件参数是 blockName/dimension/posX/posY/posZ(不是 posDim!)
直接用事件自带的 blockName 路由,省掉每帧读方块,性能更好。
路由:用事件自带的 blockName 在 timerOwner 里 O(1) 找到计时服务,
转发给它的 handleBlockEntityTick(省掉每帧读方块,性能更好)。
注意:事件参数是 blockName/dimension/posX/posY/posZ(不是 posDim!)
"""
posX = args.get("posX")
posY = args.get("posY")
@@ -217,7 +245,7 @@ class FeatureDispatcher(BlueFoxServerSystem):
if posX is None or posY is None or posZ is None or dimension is None:
return
blockName = args.get("blockName", "")
service = self.blockOwner.get(blockName) # 计时源方块一定注册在路由表里
service = self.timerOwner.get(blockName) # 计时源方块一定注册在 timerOwner
if not service:
return
service.handleBlockEntityTick(args)