You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Meteor Client自动告示牌放置模块修复及扩展需求问询

Meteor Client 自定义告示牌自动放置模块修复及后续功能扩展方案

一、现有模块修复方案

1. 核心问题排查

常见故障原因包括:模块未正确注册、物品检测逻辑错误、放置位置判断失效、无冷却控制导致触发频率异常、未适配Meteor Client的事件监听机制。

2. 修复后代码实现

(1) Addon.java(模块注册类)

import meteordevelopment.meteorclient.addons.MeteorAddon;
import meteordevelopment.meteorclient.systems.modules.Category;
import meteordevelopment.meteorclient.systems.modules.Modules;

public class SignPlacerAddon extends MeteorAddon {
    public static final Category CATEGORY = new Category("Sign Placer");

    @Override
    public void onInitialize() {
        Modules.get().add(new ModuleSignPlacer());
    }

    @Override
    public String getPackage() {
        return "com.yourname.signplacer"; // 替换为你的实际包名
    }
}

(2) ModuleSignPlacer.java(核心功能类)

import meteordevelopment.meteorclient.events.world.TickEvent;
import meteordevelopment.meteorclient.settings.BoolSetting;
import meteordevelopment.meteorclient.settings.IntSetting;
import meteordevelopment.meteorclient.settings.SettingGroup;
import meteordevelopment.meteorclient.systems.modules.Module;
import meteordevelopment.orbit.EventHandler;
import net.minecraft.item.Items;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;

public class ModuleSignPlacer extends Module {
    private final SettingGroup sgGeneral = settings.getDefaultGroup();

    // 放置范围设置
    private final IntSetting range = sgGeneral.add(new IntSetting.Builder()
        .name("range")
        .description("放置告示牌的最大范围")
        .defaultValue(3)
        .min(1)
        .max(5)
        .build()
    );

    // 潜行触发开关
    private final BoolSetting onlyWhenSneaking = sgGeneral.add(new BoolSetting.Builder()
        .name("only-when-sneaking")
        .description("仅在潜行时执行放置")
        .defaultValue(false)
        .build()
    );

    // 放置延迟控制
    private final IntSetting delay = sgGeneral.add(new IntSetting.Builder()
        .name("delay")
        .description("两次放置的间隔(游戏刻)")
        .defaultValue(2)
        .min(1)
        .max(10)
        .build()
    );

    private int tickCounter = 0;

    public ModuleSignPlacer() {
        super(SignPlacerAddon.CATEGORY, "sign-placer", "自动在玩家周围放置告示牌");
    }

    @EventHandler
    private void onTick(TickEvent.Post event) {
        // 潜行条件检查
        if (onlyWhenSneaking.get() && !mc.player.isSneaking()) return;

        // 延迟控制
        if (tickCounter < delay.get()) {
            tickCounter++;
            return;
        }
        tickCounter = 0;

        // 手持物品检查
        Hand hand = mc.player.getMainHandStack().isOf(Items.OAK_SIGN) ? Hand.MAIN_HAND : Hand.OFF_HAND;
        if (!mc.player.getStackInHand(hand).isOf(Items.OAK_SIGN)) return;

        // 遍历周围可放置位置
        BlockPos playerPos = mc.player.getBlockPos();
        int maxRange = range.get();

        for (int x = -maxRange; x <= maxRange; x++) {
            for (int y = -maxRange; y <= maxRange; y++) {
                for (int z = -maxRange; z <= maxRange; z++) {
                    BlockPos basePos = playerPos.add(x, y, z);
                    // 检查基底是否为固体方块
                    if (!mc.world.getBlockState(basePos).isSolid()) continue;

                    // 尝试在方块六个面放置
                    for (Direction dir : Direction.values()) {
                        BlockPos placePos = basePos.offset(dir);
                        if (!mc.world.getBlockState(placePos).isAir()) continue;

                        // 构造命中结果并执行放置
                        Vec3d hitPos = Vec3d.ofCenter(basePos).add(Vec3d.of(dir.getVector()).multiply(0.5));
                        BlockHitResult hitResult = new BlockHitResult(hitPos, dir.getOpposite(), basePos, false);
                        mc.interactionManager.interactBlock(mc.player, hand, hitResult);
                        return; // 每tick只放置一个,避免触发反作弊
                    }
                }
            }
        }
    }
}

(3) build.gradle 依赖检查

确保依赖版本与你的Minecraft/Meteor Client版本匹配:

plugins {
    id 'java'
    id 'com.github.johnrengelman.shadow' version '7.1.2'
}

repositories {
    mavenCentral()
    maven { url 'https://maven.meteordev.org/releases' }
    maven { url 'https://maven.fabricmc.net/' }
}

dependencies {
    implementation 'meteordevelopment:meteor-client:0.5.4' // 替换为当前最新版本
    implementation 'net.fabricmc:fabric-loader:0.14.21'
    implementation 'net.minecraft:minecraft:1.20.1' // 替换为你的MC版本
}

shadowJar {
    archiveBaseName.set('SignPlacerAddon')
    archiveVersion.set('1.0.0')
    archiveClassifier.set('')
    relocate 'com.yourname.signplacer', 'meteordevelopment.addons.signplacer' // 替换为你的包名
}

tasks.withType(JavaCompile) {
    options.encoding = 'UTF-8'
    options.release = 17 // MC 1.18+ 需Java 17
}

(4) 项目目录结构验证

SignPlacerAddon/
├── src/
│   └── main/
│       ├── java/
│       │   └── com/
│       │       └── yourname/
│       │           └── signplacer/
│       │               ├── SignPlacerAddon.java
│       │               └── ModuleSignPlacer.java
│       └── resources/
│           └── fabric.mod.json
└── build.gradle

二、后续功能扩展:画框与地图放置

1. 功能设计思路

  • 新增枚举类型区分放置目标:告示牌、画框、带地图的画框
  • 复用现有位置遍历逻辑,针对不同物品调整检测与放置逻辑
  • 适配2B2T下界环境,保留延迟控制避免反作弊触发

2. 核心扩展代码

(1) 添加类型选择设置

在ModuleSignPlacer.java中新增枚举与设置项:

import meteordevelopment.meteorclient.settings.EnumSetting;

public enum PlaceTarget {
    Sign,
    PaintingFrame,
    MapFrame
}

// 在sgGeneral设置组中添加
private final EnumSetting<PlaceTarget> placeTarget = sgGeneral.add(new EnumSetting.Builder<PlaceTarget>()
    .name("place-target")
    .description("选择要放置的对象类型")
    .defaultValue(PlaceTarget.Sign)
    .build()
);

(2) 修改物品检测与放置逻辑

替换原有onTick方法中的物品检查与放置代码:

// 手持物品检查
Hand hand = Hand.MAIN_HAND;
boolean hasValidItem = false;

switch (placeTarget.get()) {
    case Sign:
        hasValidItem = mc.player.getMainHandStack().isOf(Items.OAK_SIGN) || mc.player.getOffHandStack().isOf(Items.OAK_SIGN);
        hand = mc.player.getMainHandStack().isOf(Items.OAK_SIGN) ? Hand.MAIN_HAND : Hand.OFF_HAND;
        break;
    case PaintingFrame:
        hasValidItem = mc.player.getMainHandStack().isOf(Items.PAINTING) || mc.player.getOffHandStack().isOf(Items.PAINTING);
        hand = mc.player.getMainHandStack().isOf(Items.PAINTING) ? Hand.MAIN_HAND : Hand.OFF_HAND;
        break;
    case MapFrame:
        hasValidItem = mc.player.getMainHandStack().isOf(Items.FILLED_MAP) || mc.player.getOffHandStack().isOf(Items.FILLED_MAP);
        hand = mc.player.getMainHandStack().isOf(Items.FILLED_MAP) ? Hand.MAIN_HAND : Hand.OFF_HAND;
        break;
}

if (!hasValidItem) return;

// 放置逻辑分支
switch (placeTarget.get()) {
    case Sign:
    case PaintingFrame:
        // 复用原有告示牌的放置遍历逻辑
        BlockPos playerPos = mc.player.getBlockPos();
        int maxRange = range.get();

        for (int x = -maxRange; x <= maxRange; x++) {
            for (int y = -maxRange; y <= maxRange; y++) {
                for (int z = -maxRange; z <= maxRange; z++) {
                    BlockPos basePos = playerPos.add(x, y, z);
                    if (!mc.world.getBlockState(basePos).isSolid()) continue;

                    for (Direction dir : Direction.values()) {
                        BlockPos placePos = basePos.offset(dir);
                        if (!mc.world.getBlockState(placePos).isAir()) continue;

                        Vec3d hitPos = Vec3d.ofCenter(basePos).add(Vec3d.of(dir.getVector()).multiply(0.5));
                        BlockHitResult hitResult = new BlockHitResult(hitPos, dir.getOpposite(), basePos, false);
                        mc.interactionManager.interactBlock(mc.player, hand, hitResult);
                        return;
                    }
                }
            }
        }
        break;
    case MapFrame:
        // 寻找周围已放置的画框并放置地图
        BlockPos playerPosMap = mc.player.getBlockPos();
        int maxRangeMap = range.get();

        for (int x = -maxRangeMap; x <= maxRangeMap; x++) {
            for (int y = -maxRangeMap; y <= maxRangeMap; y++) {
                for (int z = -maxRangeMap; z <= maxRangeMap; z++) {
                    BlockPos framePos = playerPosMap.add(x, y, z);
                    if (!mc.world.getBlockState(framePos).isOf(Blocks.PAINTING)) continue;

                    Vec3d hitPos = Vec3d.ofCenter(framePos);
                    BlockHitResult hitResult = new BlockHitResult(hitPos, Direction.UP, framePos, false);
                    mc.interactionManager.interactBlock(mc.player, hand, hitResult);
                    return;
                }
            }
        }
        break;
}

内容的提问来源于stack exchange,提问作者Andrex Youtube

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 07:05:11