0

我正在尝试在 YouTuber TommyInnit 的视频“ Minecraft's Laser Eye Mod Is Hilarious”中重新创建该 mod,因为我和我的朋友希望使用它,但我们在互联网上找不到它,我已经从这里获取了用于 raycasting 和还设置了一个键绑定,但我不知道如何设置您正在查看的块。我试图设法获取块并设置它,但我只能找到如何制作尚不存在的新块。我的代码如下,块代码在第 142 行:

package net.laser.eyes;

import org.lwjgl.glfw.GLFW;
import net.minecraft.client.options.KeyBinding;
import net.minecraft.client.util.InputUtil;
import net.minecraft.text.LiteralText;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
import net.fabricmc.fabric.api.event.client.ClientTickCallback;
import net.fabricmc.api.*;
import net.fabricmc.fabric.api.client.rendering.v1.*;
import net.minecraft.block.*;
import net.minecraft.client.*;
import net.minecraft.client.gui.*;
import net.minecraft.client.util.math.*;
import net.minecraft.entity.*;
import net.minecraft.entity.decoration.*;
import net.minecraft.entity.projectile.*;
import net.minecraft.text.*;
import net.minecraft.util.hit.*;
import net.minecraft.util.math.*;
import net.minecraft.world.*;

public class main implements ModInitializer {
    @Override
    public void onInitialize() {
        KeyBinding binding1 = KeyBindingHelper.registerKeyBinding(new KeyBinding("key.laser-eyes.shoot", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_R, "key.category.laser.eyes"));
        HudRenderCallback.EVENT.register(main::displayBoundingBox);
        ClientTickCallback.EVENT.register(client -> {
            while (binding1.wasPressed()) {
                client.player.sendMessage(new LiteralText("Key 1 was pressed!"), false);
            }
        });
    }

    private static long lastCalculationTime = 0;
    private static boolean lastCalculationExists = false;
    private static int lastCalculationMinX = 0;
    private static int lastCalculationMinY = 0;
    private static int lastCalculationWidth = 0;
    private static int lastCalculationHeight = 0;

    private static void displayBoundingBox(MatrixStack matrixStack, float tickDelta) {
        long currentTime = System.currentTimeMillis();
        if(lastCalculationExists && currentTime - lastCalculationTime < 1000/45) {
            drawHollowFill(matrixStack, lastCalculationMinX, lastCalculationMinY,
                    lastCalculationWidth, lastCalculationHeight, 2, 0xffff0000);
            return;
        }

        lastCalculationTime = currentTime;

        MinecraftClient client = MinecraftClient.getInstance();
        int width = client.getWindow().getScaledWidth();
        int height = client.getWindow().getScaledHeight();
        Vec3d cameraDirection = client.cameraEntity.getRotationVec(tickDelta);
        double fov = client.options.fov;
        double angleSize = fov/height;
        Vector3f verticalRotationAxis = new Vector3f(cameraDirection);
        verticalRotationAxis.cross(Vector3f.POSITIVE_Y);
        if(!verticalRotationAxis.normalize()) {
            lastCalculationExists = false;
            return;
        }

        Vector3f horizontalRotationAxis = new Vector3f(cameraDirection);
        horizontalRotationAxis.cross(verticalRotationAxis);
        horizontalRotationAxis.normalize();

        verticalRotationAxis = new Vector3f(cameraDirection);
        verticalRotationAxis.cross(horizontalRotationAxis);

        HitResult hit = client.crosshairTarget;

        if (hit.getType() == HitResult.Type.MISS) {
            lastCalculationExists = false;
            return;
        }

        int minX = width;
        int maxX = 0;
        int minY = height;
        int maxY = 0;

        for(int y = 0; y < height; y +=2) {
            for(int x = 0; x < width; x+=2) {
                if(minX < x && x < maxX && minY < y && y < maxY) {
                    continue;
                }

                Vec3d direction = map(
                        (float) angleSize,
                        cameraDirection,
                        horizontalRotationAxis,
                        verticalRotationAxis,
                        x,
                        y,
                        width,
                        height
                );
                HitResult nextHit = rayTraceInDirection(client, tickDelta, direction);//TODO make less expensive

                if(nextHit == null) {
                    continue;
                }

                if(nextHit.getType() == HitResult.Type.MISS) {
                    continue;
                }

                if(nextHit.getType() != hit.getType()) {
                    continue;
                }

                if (nextHit.getType() == HitResult.Type.BLOCK) {
                    if(!((BlockHitResult) nextHit).getBlockPos().equals(((BlockHitResult) hit).getBlockPos())) {
                        continue;
                    }
                } else if(nextHit.getType() == HitResult.Type.ENTITY) {
                    if(!((EntityHitResult) nextHit).getEntity().equals(((EntityHitResult) hit).getEntity())) {
                        continue;
                    }
                }

                if(minX > x) minX = x;
                if(minY > y) minY = y;
                if(maxX < x) maxX = x;
                if(maxY < y) maxY = y;
            }
        }


        lastCalculationExists = true;
        lastCalculationMinX = minX;
        lastCalculationMinY = minY;
        lastCalculationWidth = maxX - minX;
        lastCalculationHeight = maxY - minY;

        drawHollowFill(matrixStack, minX, minY, maxX - minX, maxY - minY, 2, 0xffff0000);
        LiteralText text = new LiteralText("Bounding " + minX + " " + minY + " " + width + " " + height + ": ");
        client.player.sendMessage(text.append(getLabel(hit)), true);
        //SET THE BLOCK (maybe use hit.getPos(); to find it??)
        
    }

    private static void drawHollowFill(MatrixStack matrixStack, int x, int y, int width, int height, int stroke, int color) {
        matrixStack.push();
        matrixStack.translate(x-stroke, y-stroke, 0);
        width += stroke *2;
        height += stroke *2;
        DrawableHelper.fill(matrixStack, 0, 0, width, stroke, color);
        DrawableHelper.fill(matrixStack, width - stroke, 0, width, height, color);
        DrawableHelper.fill(matrixStack, 0, height - stroke, width, height, color);
        DrawableHelper.fill(matrixStack, 0, 0, stroke, height, color);
        matrixStack.pop();
    }

    private static Text getLabel(HitResult hit) {
        if(hit == null) return new LiteralText("null");

        switch (hit.getType()) {
            case BLOCK:
                return getLabelBlock((BlockHitResult) hit);
            case ENTITY:
                return getLabelEntity((EntityHitResult) hit);
            case MISS:
            default:
                return new LiteralText("null");
        }
    }

    private static Text getLabelEntity(EntityHitResult hit) {
        return hit.getEntity().getDisplayName();
    }

    private static Text getLabelBlock(BlockHitResult hit) {
        BlockPos blockPos = hit.getBlockPos();
        BlockState blockState = MinecraftClient.getInstance().world.getBlockState(blockPos);
        Block block = blockState.getBlock();
        return block.getName();
    }

    private static Vec3d map(float anglePerPixel, Vec3d center, Vector3f horizontalRotationAxis,
                             Vector3f verticalRotationAxis, int x, int y, int width, int height) {
        float horizontalRotation = (x - width/2f) * anglePerPixel;
        float verticalRotation = (y - height/2f) * anglePerPixel;

        final Vector3f temp2 = new Vector3f(center);
        temp2.rotate(verticalRotationAxis.getDegreesQuaternion(verticalRotation));
        temp2.rotate(horizontalRotationAxis.getDegreesQuaternion(horizontalRotation));
        return new Vec3d(temp2);
    }

    private static HitResult rayTraceInDirection(MinecraftClient client, float tickDelta, Vec3d direction) {
        Entity entity = client.getCameraEntity();
        if (entity == null || client.world == null) {
            return null;
        }

        double reachDistance = 5.0F;
        HitResult target = rayTrace(entity, reachDistance, tickDelta, false, direction);
        boolean tooFar = false;
        double extendedReach = 6.0D;
        reachDistance = extendedReach;

        Vec3d cameraPos = entity.getCameraPosVec(tickDelta);

        extendedReach = extendedReach * extendedReach;
        if (target != null) {
            extendedReach = target.getPos().squaredDistanceTo(cameraPos);
        }

        Vec3d vec3d3 = cameraPos.add(direction.multiply(reachDistance));
        Box box = entity
                .getBoundingBox()
                .stretch(entity.getRotationVec(1.0F).multiply(reachDistance))
                .expand(1.0D, 1.0D, 1.0D);
        EntityHitResult entityHitResult = ProjectileUtil.raycast(
                entity,
                cameraPos,
                vec3d3,
                box,
                (entityx) -> !entityx.isSpectator() && entityx.collides(),
                extendedReach
        );

        if (entityHitResult == null) {
            return target;
        }

        Entity entity2 = entityHitResult.getEntity();
        Vec3d hitPos = entityHitResult.getPos();
        if (cameraPos.squaredDistanceTo(hitPos) < extendedReach || target == null) {
            target = entityHitResult;
            if (entity2 instanceof LivingEntity || entity2 instanceof ItemFrameEntity) {
                client.targetedEntity = entity2;
            }
        }

        return target;
    }

    private static HitResult rayTrace(
            Entity entity,
            double maxDistance,
            float tickDelta,
            boolean includeFluids,
            Vec3d direction
    ) {
        Vec3d end = entity.getCameraPosVec(tickDelta).add(direction.multiply(maxDistance));
        return entity.world.raycast(new RaycastContext(
                entity.getCameraPosVec(tickDelta),
                end,
                RaycastContext.ShapeType.OUTLINE,
                includeFluids ? RaycastContext.FluidHandling.ANY : RaycastContext.FluidHandling.NONE,
                entity
        ));
    }



    }


4

1 回答 1

1

首先,我强烈建议遵循标准的 Java 命名约定,因为它会使您的代码更容易被其他人理解。

存在于世界中特定位置的块的技术名称是“块状态”,由BlockState类表示。

您只能在服务器端的特定位置更改块状态。您的光线投射代码在客户端运行,因此您需要使用 Fabric Networking API。您可以在此处查看服务器端 Javadoc ,在此处查看客户端 Javadoc 。

幸运的是,Fabric Wiki一个网络教程,因此您不必阅读所有 Javadoc。您感兴趣的部分是“向服务器发送数据包并在服务器上接收数据包”。

以下是针对您的用例的指南:

网络简介

Minecraft 在两个不同的组件中运行;客户端和服务器。

客户端负责渲染和GUI等工作,而服务器负责处理世界存储、实体AI等(这里说的是逻辑客户端和服务器)

物理服务器和物理客户端是实际运行的 JAR 文件。

物理(专用)服务器仅包含逻辑服务器,而物理客户端包含逻辑(集成)服务器和逻辑客户端。

可以在此处找到解释它的图表。

因此,逻辑客户端无法更改逻辑服务器的状态(例如,世界中的块状态),因此必须将数据包从客户端发送到服务器,以便服务器响应。

以下代码仅为示例代码,请勿复制!您应该考虑安全预防措施,例如防止作弊客户端更改每个块。可能是网络中最重要的规则之一:假设客户在撒谎。

Fabric 网络 API

您的起点是ServerPlayNetworkingClientPlayNetworking。它们是帮助您发送和接收数据包的类。

使用 注册监听器registerGlobalReceiver,并使用 发送数据包send

您首先需要一个Identifier以便将您的数据包与其他数据包分开并确保它被正确解释。建议将Identifier这样的一个放在您的ModInitializer或实用程序类的静态字段中。

public class MyMod implements ModInitializer {
    public static final Identifier SET_BLOCK_PACKET = new Identifier("modid", "setblock");
}

(不要忘记替换modid为您的模组 ID)

您通常希望通过数据包传递数据(例如,块位置和要更改的块),您可以使用PacketByteBuf.

让我们把这一切拼凑起来

所以,我们有一个Identifier. 让我们发送一个数据包!

客户端

我们将首先创建一个PacketByteBuf并写入正确的数据:

private static void displayBoundingBox(MatrixStack matrixStack, float tickDelta) {
// ...
    PacketByteBuf data = PacketByteBufs.create();
    buf.writeBlockPos(hit.getPos());
    // Minecraft doesn't have a way to write a Block to a packet, so we will write the registry name instead
    buf.writeIdentifier(new Identifier("minecraft", "someblock" /*for example, "stone"*/));
}

现在发送数据包

// ...
ClientPlayNetworking.send(SET_BLOCK_PACKET, buf);

服务器端

一个带有SET_BLOCK_PACKETID 的数据包已经发送,但是我们还需要在服务器端监听和接收它。我们可以通过使用来做到这一点ServerPlayNetworking.registerGlobalReceiver

@Override
public void onInitialize() {
    // ...
    // This code MUST be in onInitialize
    ServerPlayNetworking.registerGlobalReceiver(SET_BLOCK_PACKET, (server, player, handler, buf, sender) -> {

    });
}

我们在这里使用 lambda 表达式。有关 lambdas 的更多信息,Google 是您的朋友。

接收数据包时,您的 lambda 代码将在网络线程上执行。此代码不允许修改任何与游戏内逻辑(即世界)相关的内容。为此,我们将使用server.execute(Runnable).

不过,您应该阅读网络线程上的 buf。

ServerPlayNetworking.registerGlobalReceiver(SET_BLOCK_PACKET, (server, player, handler, buf, sender) -> {
    BlockPos pos = buf.readBlockPos(); // reads must be done in the same order
    Block blockToSet = Registry.BLOCK.get(buf.readIdentifier()); // reading using the identifier

    server.execute(() -> { // We are now on the main thread
        // In a normal mod, checks will be done here to prevent the client from setting blocks outside the world etc. but this is only example code

        player.getServerWorld().setBlockState(pos, blockToSet.getDefaultState()); // changing the block state
    });
});

再一次,您应该防止客户端发送无效位置

于 2021-01-28T10:01:25.500 回答