MENU

网易我的世界大型结构控制生成群系与生成范围

2025 年 11 月 05 日 • 阅读: 38 • Minecraft,开发日常

壹;前言

这篇文章不是为纯新手准备的,你至少了解或掌握以下技能才适合查看。

  • Python
  • (BP) 特征与特征生成规则
  • (BP) 结构与大型结构
  • Molang

贰;实现原理

1. 纯 Python 实现

已知网易大型结构可拆分为不同大小的子结构(16,32,64),并且提供了初始化时的结构生成事件 PlaceNeteaseStructureFeatureEvent,我们可以通过事件的参数来得到当前结构生成区块群系与坐标,也可以通过设定参数 cancel 来取消结构的生成,但是网易的结构生成事件是每个子结构都会触发,所以我们需要一个唯一标识来标记当前大型结构,我这里使用的方式是通过简单的计算取到第一个结构的起始坐标来做为唯一标识,代码如下。

# structureManager.py

# -*- coding: utf-8 -*-
import mod.server.extraServerApi as serverApi

from abyssogenesisScripts.modServer.storage import serverStorage

comp_factory = serverApi.GetEngineCompFactory()


class StructureManager(object):
    structures = {}

    @staticmethod
    def init():
        # 延迟导入避免循环依赖
        from abyssogenesisScripts.modServer import serverSystem
        serverStorage.ServerSystemObj.ListenForEvent(
            serverSystem.engine_namespace,
            serverSystem.engine_system_name,
            "PlaceNeteaseStructureFeatureEvent",
            StructureManager,
            StructureManager.on_dispose
        )

    @staticmethod
    def register(structure_id, xc, zc, size, chance, biomes, radius=0):
        # type: (str, int, int, int, int, list, int) -> None
        """
        注册大型结构到结构控制器中, 用于控制群系生成

        :param structure_id: 结构标识名
        :param xc: 区块 x 宽度
        :param zc: 区块 z 宽度
        :param size: 单区块大小(16,32)
        :param chance: 刷新概率
        :param biomes: 群系列表
        :param radius: xz 超过 (0,0) 坐标半径多少才刷新
        """
        # 计算最大区块数
        maxc = xc * zc
        # 记录结构数据
        StructureManager.structures[structure_id] = {
            'structure_id': structure_id,
            'xc': xc,
            'zc': zc,
            'size': size,
            'chance': chance,
            'biomes': biomes,
            'radius': radius
        }
        # 注册子区块
        comp = comp_factory.CreateFeature(serverApi.GetLevelId())
        for i in xrange(1, maxc + 1):
            comp.AddNeteaseFeatureWhiteList('{}_{}'.format(structure_id, i))

    @staticmethod
    def on_dispose(params):
        """
        当结构开始生成的时候触发, 此处会判断是否为目标结构
        """
        # 判断结构是否存在于列表
        structure_name = params['structureName'].rsplit('_', 1)
        if structure_name[0] not in StructureManager.structures:
            return

        # 获取结构信息
        structure_data = StructureManager.structures[structure_name[0]]
        # 获取当前索引
        index = int(structure_name[1]) - 1
        # 计算当前原点坐标
        current_x = index / 5
        current_z = index % 5
        origin_x = params['x'] - current_x * 16
        origin_z = params['z'] - current_z * 16
        structure_key = "{}_{}_{}_{}".format(
            params['dimensionId'],
            structure_name[0],
            origin_x,
            origin_z
        )

        # 判断当前遗迹在当前存档中是否处理过
        extra_data_comp = comp_factory.CreateExtraData(serverApi.GetLevelId())
        extra_data = extra_data_comp.GetExtraData('abyss_structure_state')
        # 如果为空则创建新数据
        if extra_data is None:
            extra_data = {}
            extra_data_comp.SetExtraData('abyss_structure_state', {}, True)

        # 判断当前特征是否存在于数据中
        if structure_key in extra_data:
            # 判断群系是否符合条件
            params['cancel'] = extra_data[structure_key]
            return

        # 计算条件是否符合
        if not check_conditions(structure_data, params):
            # 取消事件并记录原点坐标
            params['cancel'] = True
            extra_data[structure_key] = True
            extra_data_comp.SetExtraData('abyss_structure_state', extra_data)
            return

        # 标记为符合条件
        extra_data[structure_key] = False
        extra_data_comp.SetExtraData('abyss_structure_state', extra_data)

    def on_tick(self, params):
        pass


def check_conditions(structure_data, params):
    # type: (dict, dict) -> bool
    """
    检测结构生成条件是否达标
    """

    # 判断群系条件是否符合
    if params['biomeName'] not in structure_data['biomes']:
        return False

    # 判断是否在限制范围内
    radius = structure_data['radius']
    if radius > 0 and abs(params['x']) < radius and abs(params['z']) < radius:
        return False

    return True
# serverSystem.py

# -*- coding: utf-8 -*-

class AbyssogenesisServerSystem(ServerSystem):

    def __init__(self, namespace, system_name):
        # 初始化结构管理器
        StructureManager.init()
        # 记录要监听的结构类型
        StructureManager.register(
            structure_id='vsydn:abyss_stone_giant_reforged',
            xc=5,
            zc=5,
            size=16,
            chance=100,
            biomes=['jungle', 'bamboo_jungle'],
            radius=500
        )

代码的原理就是通过对大型结构首个生成的子结构计算原点(拆分的第一个结构的起始坐标)位置来做为唯一标识,不管条件符合不符合都写入到自定义数据中存储起来,之后相同大型结构的子结构生成时取到相同原点做判断时,只需要检测原点是否被记录了,如果记录了直接返回记录的状态来达到限制刷新的群系、范围等条件。

2. 特征规则 Molang 实现

通过纯代码实现的特征规则如果被 PlaceNeteaseStructureFeatureEvent 事件 cancel 之后,无法通过 LocateNeteaseFeatureRule 获取到坐标,如果你需要使用 LocateNeteaseFeatureRule 来定位特征规则坐标那么就需要通过 Molang query.is_biome(x, z, args..) 来实现,如下。

{
  "format_version": "1.14.0",
  "minecraft:feature_rules": {
    "conditions": {
      "minecraft:biome_filter": [
        {
          "all_of": [
            {
              "any_of": [
                {
                  "operator": "==",
                  "test": "has_biome_tag",
                  "value": "overworld"
                }
              ]
            }
          ]
        }
      ],
      "placement_pass": "surface_pass"
    },
    "description": {
      "identifier": "vsydn:abyss_stone_giant_reforged",
      "places_feature": "vsydn:abyss_stone_giant_feature"
    },
    "distribution": {
      "coordinate_eval_order": "xzy",
      "iterations": "query.is_biome(variable.originx+0,variable.originz+0,1)&&math.mod(variable.originx+0,336)==0&&math.mod(variable.originz+0,336)==0&&math.abs(query.noise(variable.originx+0,variable.originz+0))<1.0?1:0",
      "scatter_chance": 100,
      "x": 0,
      "y": "query.get_height_at(variable.worldx,variable.worldz)-20",
      "z": 0
    }
  }
}

其中 distribution.iterations 开头的 query.is_biome(variable.originx+0,variable.originz+0,1) 就是群系判断的语句,假如你是大型结构,我的建议是通过判断第一个特征结构的位置的群系来决定是否生成,如果要更合理可以取中心子结构的位置,下方是两个子结构的坐标计算语句例子。

query.is_biome(variable.originx+0,variable.originz+0,1)
query.is_biome(variable.originx+0,variable.originz-16,1)

叁;结尾

实现逻辑很简单也写上了相应注释,这里不做过多赘述,如果你有更好的方式可以在下方评论留言互相探讨。

最后编辑于: 2025 年 11 月 07 日