Skip to content

第一个服务端脚本

在前文中,我们完成了服务端脚本注册,本篇将讲解服务端系统类的基本用法,并完成一个简易功能。

什么是系统类?

BaseSystem 是 ModSdk 提供的基础系统类,开发者可以通过它实现游戏中的核心脚本逻辑,例如注册和监听游戏事件广播事件等。结合 serverApi,可以轻松(?)开发出丰富多样的玩法功能。

什么是游戏事件?

在 Minecraft 的 Mod 开发中,游戏事件是游戏运行过程中触发的特定行为或状态变化。例如,玩家加入游戏、方块被破坏、生物受到伤害等,都是常见的游戏事件。

通过监听这些事件,开发者可以在特定的时机执行自定义逻辑,从而实现丰富的游戏功能。例如:

  • 在玩家加入时发送欢迎消息。
  • 当特定方块被破坏时生成奖励物品。
  • 在生物受到伤害时触发特殊效果。

事件监听的基本流程

  1. 注册事件监听器
    使用系统类方法 ListenForEvent 注册一个事件监听器,指定需要监听的事件类型以及回调函数

  2. 实现事件处理逻辑
    在事件触发时,回调函数 将被自动触发,并拿到相关的事件信息(如果存在),例如生物死亡事件可以拿到谁死亡了,是谁击杀的。

示例代码

以下是一个简单的服务端事件监听示例,监听玩家攻击生物的事件并输出日志:

python
# -*- coding: utf-8 -*-
import mod.server.extraServerApi as serverApi
ServerSystem = serverApi.GetServerSystemCls()   # 获取服务端系统基类

# 定义MyServer类
class MyServer(ServerSystem):
    def __init__(self, namespace, systemName):
        # ServerSystem基类接收两个参数 描述当前系统被注册的命名空间以及系统名(modMain.py中的注册)
        ServerSystem.__init__(self, namespace, systemName)
        # 获取原版MC引擎的命名空间和系统名
        mcNamespace, mcSystemName = serverApi.GetEngineNamespace(), serverApi.GetEngineSystemName()
        # 注册PlayerAttackEntityEvent事件监听(游戏关闭时所有的监听会被自动取消,因此不需要开发者考虑回收问题)
        self.ListenForEvent(mcNamespace, mcSystemName, "PlayerAttackEntityEvent", self, self.PlayerAttackEntityEvent)
    
    def PlayerAttackEntityEvent(self, args={}):
        playerId = args["playerId"]
        victimId = args["victimId"]
        print("玩家: {} 命中了 {}".format(playerId, victimId))

接口讲解

接口描述返回类型
serverApi.GetEngineNamespace获取引擎命名空间(原版命名空间)str
serverApi.GetEngineSystemName获取引擎系统名(原版系统名)str
ServerSystem::ListenForEvent监听特定游戏事件,注册回调方法None

ListenForEvent参数

描述类型
监听的命名空间str
监听的系统名str
监听的事件名str
系统来源(固定self)BaseSystem
绑定回调方法,必须是系统来源(及self)下的方法function

快速查阅:游戏API与事件,您可以在此处看到所有开放的游戏事件以及回调参数。

关于监听

事件监听除了监听原版游戏事件外还能监听其他MOD自定义的事件,因此需要命名空间+系统名区分指定监听谁。

编写服务端脚本

运行上述示例代码,游戏中玩家命中生物将在mcs控制台中输出对应的日志 (请确保按照上文modMain.py中注册它)。

为示例代码添加具体功能

通过事件触发的时机以及事件参数结合serverApi实现具体游戏功能。

python
# -*- coding: utf-8 -*-
import mod.server.extraServerApi as serverApi
ServerSystem = serverApi.GetServerSystemCls()

class MyServer(ServerSystem):
    def __init__(self, namespace, systemName):
        ServerSystem.__init__(self, namespace, systemName)
        mcNamespace, mcSystemName = serverApi.GetEngineNamespace(), serverApi.GetEngineSystemName()
        self.ListenForEvent(mcNamespace, mcSystemName, "PlayerAttackEntityEvent", self, self.PlayerAttackEntityEvent)
    
    # PlayerAttackEntityEvent事件的参数且看下文
    def PlayerAttackEntityEvent(self, args={}):
        playerId = args["playerId"]
        victimId = args["victimId"]
        # GetEngineTypeStr接口 获取实体英文标识符(与JSON中的定义一致)
        comp = serverApi.GetEngineCompFactory().CreateEngineType(victimId)
        if comp.GetEngineTypeStr() == "minecraft:zombie":
            print("玩家: {} 命中了僵尸({})".format(playerId, victimId))
            # Hurt接口 对指定实体造成指定伤害
            # 此处将对玩家造成3点伤害 实现玩家攻击僵尸自动受到反伤
            comp = serverApi.GetEngineCompFactory().CreateHurt(playerId)
            comp.Hurt(3, serverApi.GetMinecraftEnum().ActorDamageCause.EntityAttack, victimId, None, False)

自此,玩家在攻击到僵尸时将受到反伤害,攻击其他生物则无事发生。

补充

部分API接口可能需要用到levelId,该数据可通过 serverApi.GetLevelId() 获取。

引用接口/事件

接口描述
GetEngineTypeStr获取实体的类型名称(英文标识符)
Hurt设置实体伤害(造成伤害)
PlayerAttackEntityEvent【事件】当玩家攻击生物时触发

关于实体ID

请注意区分运行时entityId与JSON标识符的区别,每个生物在游戏中都有唯一的实体ID作为描述信息(描述CPP实体对象引用)对生物的操作离不开实体ID。

说明

由于网易ModSdk早年的工厂设计滥用,导致接口极难记忆,建议只记住有什么,需要用到时再去官网API文档搜索并CV。

总结

基本MOD开发逻辑如下:

  1. 注册游戏事件监听,在您需要的时机执行代码。
  2. 判断事件参数,筛选条件。
  3. 结合游戏API作出处理。

Released under the BSD3 License