如何更改特定坐标处的方块? 客户端服务器端

问题描述

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

#include <iostream>
#include <vector>

class base_main {
public:
  virtual ~base_main() {}
};

class base_1 : virtual public base_main {};

class base_2 : virtual public base_main {};

class base_3 : virtual public base_main {};

class object : public base_1,public base_2,public base_3 {};

template <typename DestT,typename SrcT,typename T>
void forward_if(SrcT obj,T *o,void (T::*f)(DestT)) noexcept {
  if (auto tmp = dynamic_cast<DestT>(obj); tmp != nullptr) {
    (o->*f)(tmp);
  }
}

struct listener_base {
  virtual void object_created(base_main *o) = 0;
};

struct specific_listener : public listener_base {
  void object_created(base_main *o) override {
    forward_if<base_1 *>(o,this,&specific_listener::object_created);
    forward_if<base_2 *>(o,&specific_listener::object_created);
  }

  void object_created(base_1 *o) {
    std::cout << "object created base_1" << std::endl;
  }

  void object_created(base_2 *o) {
    std::cout << "object created base_2" << std::endl;
  }
};

int main() {
  std::vector<listener_base *> listeners;
  listeners.push_back(new specific_listener());
  object o;

  for (auto listener : listeners) {
    listener->object_created(&o);
  }

  return 0;
}

解决方法

首先,我强烈建议您遵循 the standard Java naming conventions,因为它会让其他人更容易理解您的代码。

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

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

幸运的是,Fabric Wikia networking tutorial,因此您不必阅读所有 Javadoc。您感兴趣的部分是“向服务器发送数据包并在服务器上接收数据包”。

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

网络简介

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

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

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

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

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

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

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

Fabric 网络 API

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

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

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

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

(不要忘记用你的模组 ID 替换 modid

您通常希望通过数据包传递数据(例如块位置和要更改的块),您可以使用 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_PACKET ID 的数据包已经发送,但是我们还需要在服务器端监听和接收它。我们可以通过使用 ServerPlayNetworking.registerGlobalReceiver:

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

    });
}

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

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

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

ServerPlayNetworking.registerGlobalReceiver(SET_BLOCK_PACKET,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
    });
});

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

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...