告别数字ID!手把手教你修复mcpi库以适配Minecraft 1.13+的Python自动化脚本

当你在Minecraft 1.13+版本中兴奋地运行Python自动化脚本时,突然发现 setBlock() 函数无法正常工作——这可能是由于官方mcpi库(1.2.0版本)尚未适配新版命名空间ID系统导致的。本文将带你深入问题本质,从定位库文件到修改关键代码,最终实现Python脚本与新版Minecraft的无缝协作。

1. 理解Minecraft 1.13的ID系统变革

2018年发布的Minecraft 1.13("水域更新")对游戏底层进行了重大调整,其中最影响自动化脚本的就是方块ID系统的重构:

  • 旧版(1.12及之前) :使用纯数字ID(如 1 表示石头, 48 表示苔石)
  • 新版(1.13+) :采用命名空间ID(如 minecraft:stone minecraft:mossy_cobblestone

这种变化导致依赖数字ID的旧版mcpi库在调用 setBlock() 等函数时会抛出类型错误。通过 pip show mcpi 命令可以确认当前安装的库版本:

$ pip show mcpi
Name: mcpi
Version: 1.2.0
Summary: Minecraft Pi edition Python library
...

注意:虽然JuicyRaspberryPie插件已更新支持1.16.5,但Python端的mcpi库自2017年后就未再更新

2. 定位并修改mcpi库文件

2.1 查找库安装位置

首先需要找到mcpi库的实际安装路径。在Python交互环境中执行:

>>> import mcpi
>>> print(mcpi.__file__)
/usr/local/lib/python3.9/site-packages/mcpi/__init__.py

得到的路径即为库的安装目录,关键文件 minecraft.py 通常位于同级目录下。

2.2 识别问题函数

打开 minecraft.py 文件,搜索 setBlock setBlocks 函数,会发现它们都调用了 intFloor() 方法进行类型转换:

def setBlock(self, *args):
    """Set block (x,y,z,id,[data])"""
    self.conn.send(b"world.setBlock", intFloor(args))

def setBlocks(self, *args):
    """Set a cuboid of blocks (x0,y0,z0,x1,y1,z1,id,[data])"""
    self.conn.send(b"world.setBlocks", intFloor(args))

intFloor() 会将所有参数强制转换为整数,这正是导致新版命名空间ID无法正常工作的罪魁祸首。

2.3 实施修复方案

修改这两个函数,移除 intFloor 调用,直接传递原始参数:

def setBlock(self, *args):
    """Set block (x,y,z,id,[data])"""
    self.conn.send(b"world.setBlock", args)

def setBlocks(self, *args):
    """Set a cuboid of blocks (x0,y0,z0,x1,y1,z1,id,[data])"""
    self.conn.send(b"world.setBlocks", args)

保存文件后,建议在同一目录创建 __init__.py 文件(如果不存在),确保修改能被正确加载:

# /usr/local/lib/python3.9/site-packages/mcpi/__init__.py
from .minecraft import Minecraft
from . import block
from . import entity
from . import event
from . import util
from . import vec3

3. 测试修复效果

3.1 基础功能验证

创建一个测试脚本 test_block.py

from mcpi.minecraft import Minecraft

mc = Minecraft.create()
pos = mc.player.getTilePos()

# 使用命名空间ID放置方块
mc.setBlock(pos.x+1, pos.y, pos.z, "minecraft:diamond_block")
mc.setBlocks(pos.x+2, pos.y, pos.z, 
             pos.x+4, pos.y+2, pos.z, 
             "minecraft:redstone_block")

运行后应在游戏中看到钻石块和红石块组成的结构,证明基础功能已修复。

3.2 边界情况测试

为确保修改的健壮性,建议测试以下场景:

  • 混合参数类型 :坐标使用浮点数,ID使用字符串
  • 特殊方块 :含NBT数据的方块(如带文字的告示牌)
  • 批量操作 :大规模 setBlocks 调用性能

示例测试用例:

# 测试浮点坐标和特殊方块
mc.setBlock(pos.x+1.5, pos.y+0.3, pos.z-2.8,
           "minecraft:oak_sign[rotation=8]{Text1:'Hello'}")

# 性能测试:生成100x100平台
import time
start = time.time()
mc.setBlocks(pos.x, pos.y-1, pos.z,
            pos.x+99, pos.y-1, pos.z+99,
            "minecraft:grass_block")
print(f"耗时:{time.time()-start:.2f}秒")

4. 高级应用与优化技巧

4.1 构建辅助函数库

为简化常用操作,可以创建自定义工具库。例如 mc_utils.py

from mcpi.minecraft import Minecraft
from mcpi import block

class Builder:
    def __init__(self, mc):
        self.mc = mc
    
    def pyramid(self, x, y, z, size, material):
        """生成金字塔结构"""
        for i in range(size):
            self.mc.setBlocks(x+i, y+i, z+i,
                            x+size*2-i, y+i, z+size*2-i,
                            material)
            self.mc.setBlocks(x+i+1, y+i, z+i+1,
                            x+size*2-i-1, y+i, z+size*2-i-1,
                            block.AIR.id)

# 使用示例
mc = Minecraft.create()
builder = Builder(mc)
pos = mc.player.getTilePos()
builder.pyramid(pos.x, pos.y, pos.z, 10, "minecraft:gold_block")

4.2 性能优化策略

当处理大型建筑时,可采用以下优化手段:

  • 批量操作 :优先使用 setBlocks 而非多次 setBlock
  • 异步执行 :将耗时操作放入单独线程
  • 区块预计算 :先在内存中构建结构数据,再一次性提交

示例异步生成器:

import threading

def async_build(mc, blueprint):
    def worker():
        for x, y, z, block_id in blueprint:
            mc.setBlock(x, y, z, block_id)
    threading.Thread(target=worker).start()

# 使用示例
blueprint = [(pos.x+i, pos.y, pos.z, "minecraft:glass") for i in range(100)]
async_build(mc, blueprint)

4.3 错误处理与日志记录

增强脚本的健壮性:

import logging
logging.basicConfig(filename='mc_script.log', level=logging.INFO)

def safe_set_block(mc, x, y, z, block_id):
    try:
        mc.setBlock(x, y, z, block_id)
        logging.info(f"成功放置 {block_id} 在 ({x}, {y}, {z})")
    except Exception as e:
        logging.error(f"放置方块失败: {str(e)}")
        # 自动重试或执行备用方案

5. 兼容性处理与未来维护

5.1 多版本兼容方案

如果需要同时支持新旧版本,可以实现版本检测和自动适配:

def smart_set_block(mc, x, y, z, block):
    """智能选择设置方块的方式"""
    if isinstance(block, int):  # 数字ID
        mc.setBlock(x, y, z, block)
    elif isinstance(block, str):  # 命名空间ID
        if hasattr(mc, '_modified_api'):  # 已修复的库
            mc.setBlock(x, y, z, block)
        else:  # 未修复的旧库
            raise RuntimeError("需要先修复mcpi库以支持命名空间ID")

5.2 创建补丁系统

为方便团队协作和后续更新,可以制作自动化补丁脚本 patch_mcpi.py

import os
import fileinput

def patch_mcpi_lib():
    lib_path = os.path.dirname(os.__file__)
    mcpi_path = os.path.join(lib_path, 'site-packages', 'mcpi', 'minecraft.py')
    
    replacements = [
        ('self.conn.send(b"world.setBlock", intFloor(args))',
         'self.conn.send(b"world.setBlock", args)'),
        ('self.conn.send(b"world.setBlocks", intFloor(args))',
         'self.conn.send(b"world.setBlocks", args)')
    ]
    
    for line in fileinput.input(mcpi_path, inplace=True):
        for old, new in replacements:
            line = line.replace(old, new)
        print(line, end='')

if __name__ == '__main__':
    patch_mcpi_lib()
    print("mcpi库已成功打补丁!")

运行此脚本将自动完成所有必要的修改,无需手动编辑库文件。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐