Пакетная система

Версия Minecraft
1.16.5
API
Forge
10
0
Здравствуйте товарищи, начал писать мод для одного прибора чтобы взаимодействие с миром майнкрафта было более красочным...
Нужно сделать чтобы при поломки блока данные отправлялись к персонажу поломавшему блок. Понимаю что нужно использовать пакетную систему, так как ивент поломки блока, видимо, находится на сервере и надо чтобы сервер отправлял месседж клиенту и потом эти данные я к примеру выведу для отладки в консоль. Но к сожалению пока в тупике. Причем реализацию для однопользовательского режима сделал, но все ломается как только персонаж теряет свою уникальность

Сам ивент
Event:
@SubscribeEvent
    public static void onBlockBreak(final BlockEvent.BreakEvent event)
    {
        String playerName = event.getPlayer().getName().getString();
        String block = event.getState().getBlock().asItem().toString();

        Packet.INSTANCE.send(PacketDistributor.TRACKING_CHUNK.with(()->event.getPlayer().getServer().), new ClientBoundBlock(event.getPos()));
    }

Класс пакета
Packet:
public final class Packet
{
    private static final String PROTOCOL_VERSION = "1";

    public static final SimpleChannel INSTANCE = NetworkRegistry.newSimpleChannel(
            new ResourceLocation(EmptyMod.MOD_ID, "main"), () -> PROTOCOL_VERSION,
            PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals);

    private Packet(){}

    public static void init()
    {
        int index = 0;
        INSTANCE.messageBuilder(ServerBoundBlock.class, index, NetworkDirection.PLAY_TO_SERVER)
                .encoder(ServerBoundBlock::encode).decoder(ServerBoundBlock::decode)
                .consumer(ServerBoundBlock::handle).add();
        INSTANCE.messageBuilder(ClientBoundBlock.class, index++, NetworkDirection.PLAY_TO_CLIENT)
                .encoder(ClientBoundBlock::encode).decoder(ClientBoundBlock::decode)
                .consumer(ClientBoundBlock::handle).add();
    }

}

Класс клиента
ClientBoundBlock:
public class ClientBoundBlock
{
    public final BlockPos blockPos;

    public ClientBoundBlock(BlockPos blockPos)
    {
        this.blockPos = blockPos;
    }

    public ClientBoundBlock(PacketBuffer buffer)
    {
        this(buffer.readBlockPos());
    }

    public void encode(PacketBuffer buffer)
    {
        buffer.writeBlockPos(this.blockPos);
    }

    public static ClientBoundBlock decode(PacketBuffer buffer)
    {
        return new ClientBoundBlock(buffer.readBlockPos());
    }

    public boolean handle(Supplier<NetworkEvent.Context> ctx)
    {
        final AtomicBoolean success = new AtomicBoolean(false);
        ctx.get().enqueueWork(()->{
            DistExecutor.unsafeRunWhenOn(Dist.CLIENT,
                    () -> () -> success.set(true));

            System.out.println("Server watch breaking block!!!");
        });

        ctx.get().setPacketHandled(true);
        return success.get();
    }
}

Класс сервера
ServerBoundBlock:
public class ServerBoundBlock
{
    public final BlockPos blockPos;

    public ServerBoundBlock(BlockPos blockPos)
    {
        this.blockPos = blockPos;
    }

    public void encode(PacketBuffer buffer)
    {
        buffer.writeBlockPos(this.blockPos);
    }

    public static ServerBoundBlock decode(PacketBuffer buffer)
    {
        return new ServerBoundBlock(buffer.readBlockPos());
    }

    public boolean handle(Supplier<NetworkEvent.Context> ctx)
    {
        final AtomicBoolean success = new AtomicBoolean(false);
        ctx.get().enqueueWork(()->{
            System.out.println("Server watch breaking block!!!");
            success.set(true);
        });

        ctx.get().setPacketHandled(true);
        return success.get();
    }
}
 
Краш-лог
[18:32:31] [Server thread/FATAL] [minecraft/ThreadTaskExecutor]: Error executing task on Server
java.lang.NullPointerException: null
at net.minecraftforge.fml.network.PacketDistributor.lambda$playerConsumer$1(PacketDistributor.java:217) ~[forge-1.16.5-36.2.34_mapped_snapshot_20210309-1.16.5-recomp.jar:?] {re:classloading}
at net.minecraftforge.fml.network.PacketDistributor$PacketTarget.send(PacketDistributor.java:179) ~[forge-1.16.5-36.2.34_mapped_snapshot_20210309-1.16.5-recomp.jar:?] {re:classloading}
at net.minecraftforge.fml.network.simple.SimpleChannel.send(SimpleChannel.java:124) ~[forge-1.16.5-36.2.34_mapped_snapshot_20210309-1.16.5-recomp.jar:?] {re:classloading}
at com.PoNi.emptymod.core.events.AromaEvents.onBlockBreak(AromaEvents.java:30) ~[main/:?] {re:classloading}
at net.minecraftforge.eventbus.ASMEventHandler_1_AromaEvents_onBlockBreak_BreakEvent.invoke(.dynamic) ~[?:?] {}
at net.minecraftforge.eventbus.ASMEventHandler.invoke(ASMEventHandler.java:85) ~[eventbus-4.0.0.jar:?] {}
at net.minecraftforge.eventbus.EventBus.post(EventBus.java:302) ~[eventbus-4.0.0.jar:?] {}
at net.minecraftforge.eventbus.EventBus.post(EventBus.java:283) ~[eventbus-4.0.0.jar:?] {}
at net.minecraftforge.common.ForgeHooks.onBlockBreakEvent(ForgeHooks.java:609) ~[forge:?] {re:classloading}
at net.minecraft.server.management.PlayerInteractionManager.tryHarvestBlock(PlayerInteractionManager.java:239) ~[forge:?] {re:classloading}
at net.minecraft.server.management.PlayerInteractionManager.func_229860_a_(PlayerInteractionManager.java:226) ~[forge:?] {re:classloading}
at net.minecraft.server.management.PlayerInteractionManager.func_225416_a(PlayerInteractionManager.java:155) ~[forge:?] {re:classloading}
at net.minecraft.network.play.ServerPlayNetHandler.processPlayerDigging(ServerPlayNetHandler.java:957) ~[forge:?] {re:classloading}
at net.minecraft.network.play.client.CPlayerDiggingPacket.processPacket(CPlayerDiggingPacket.java:50) ~[forge:?] {re:classloading}
at net.minecraft.network.play.client.CPlayerDiggingPacket.processPacket(CPlayerDiggingPacket.java:12) ~[forge:?] {re:classloading}
at net.minecraft.network.PacketThreadUtil.lambda$checkThreadAndEnqueue$0(PacketThreadUtil.java:19) ~[forge:?] {re:classloading}
at net.minecraft.util.concurrent.TickDelayedTask.run(TickDelayedTask.java:20) ~[forge:?] {re:classloading}
at net.minecraft.util.concurrent.ThreadTaskExecutor.run(ThreadTaskExecutor.java:139) ~[forge:?] {re:classloading,pl:accesstransformer:B}
at net.minecraft.util.concurrent.RecursiveEventLoop.run(RecursiveEventLoop.java:22) ~[forge:?] {re:classloading}
at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:759) ~[forge:?] {re:classloading,pl:accesstransformer:B}
at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:159) ~[forge:?] {re:classloading,pl:accesstransformer:B}
at net.minecraft.util.concurrent.ThreadTaskExecutor.driveOne(ThreadTaskExecutor.java:109) ~[forge:?] {re:classloading,pl:accesstransformer:B}
at net.minecraft.server.MinecraftServer.driveOneInternal(MinecraftServer.java:742) ~[forge:?] {re:classloading,pl:accesstransformer:B}
at net.minecraft.server.MinecraftServer.driveOne(MinecraftServer.java:736) ~[forge:?] {re:classloading,pl:accesstransformer:B}
at net.minecraft.util.concurrent.ThreadTaskExecutor.drainTasks(ThreadTaskExecutor.java:97) ~[forge:?] {re:classloading,pl:accesstransformer:B}
at net.minecraft.server.MinecraftServer.runScheduledTasks(MinecraftServer.java:721) ~[forge:?] {re:classloading,pl:accesstransformer:B}
at net.minecraft.server.MinecraftServer.func_240802_v_(MinecraftServer.java:668) ~[forge:?] {re:classloading,pl:accesstransformer:B}
at net.minecraft.server.MinecraftServer.lambda$startServer$0(MinecraftServer.java:233) ~[forge:?] {re:classloading,pl:accesstransformer:B}
Краш-лог:
[18:32:31] [Server thread/FATAL] [minecraft/ThreadTaskExecutor]: Error executing task on Server
java.lang.NullPointerException: null
	at net.minecraftforge.fml.network.PacketDistributor.lambda$playerConsumer$1(PacketDistributor.java:217) ~[forge-1.16.5-36.2.34_mapped_snapshot_20210309-1.16.5-recomp.jar:?] {re:classloading}
	at net.minecraftforge.fml.network.PacketDistributor$PacketTarget.send(PacketDistributor.java:179) ~[forge-1.16.5-36.2.34_mapped_snapshot_20210309-1.16.5-recomp.jar:?] {re:classloading}
	at net.minecraftforge.fml.network.simple.SimpleChannel.send(SimpleChannel.java:124) ~[forge-1.16.5-36.2.34_mapped_snapshot_20210309-1.16.5-recomp.jar:?] {re:classloading}
	at com.PoNi.emptymod.core.events.AromaEvents.onBlockBreak(AromaEvents.java:30) ~[main/:?] {re:classloading}
	at net.minecraftforge.eventbus.ASMEventHandler_1_AromaEvents_onBlockBreak_BreakEvent.invoke(.dynamic) ~[?:?] {}
	at net.minecraftforge.eventbus.ASMEventHandler.invoke(ASMEventHandler.java:85) ~[eventbus-4.0.0.jar:?] {}
	at net.minecraftforge.eventbus.EventBus.post(EventBus.java:302) ~[eventbus-4.0.0.jar:?] {}
	at net.minecraftforge.eventbus.EventBus.post(EventBus.java:283) ~[eventbus-4.0.0.jar:?] {}
	at net.minecraftforge.common.ForgeHooks.onBlockBreakEvent(ForgeHooks.java:609) ~[forge:?] {re:classloading}
	at net.minecraft.server.management.PlayerInteractionManager.tryHarvestBlock(PlayerInteractionManager.java:239) ~[forge:?] {re:classloading}
	at net.minecraft.server.management.PlayerInteractionManager.func_229860_a_(PlayerInteractionManager.java:226) ~[forge:?] {re:classloading}
	at net.minecraft.server.management.PlayerInteractionManager.func_225416_a(PlayerInteractionManager.java:155) ~[forge:?] {re:classloading}
	at net.minecraft.network.play.ServerPlayNetHandler.processPlayerDigging(ServerPlayNetHandler.java:957) ~[forge:?] {re:classloading}
	at net.minecraft.network.play.client.CPlayerDiggingPacket.processPacket(CPlayerDiggingPacket.java:50) ~[forge:?] {re:classloading}
	at net.minecraft.network.play.client.CPlayerDiggingPacket.processPacket(CPlayerDiggingPacket.java:12) ~[forge:?] {re:classloading}
	at net.minecraft.network.PacketThreadUtil.lambda$checkThreadAndEnqueue$0(PacketThreadUtil.java:19) ~[forge:?] {re:classloading}
	at net.minecraft.util.concurrent.TickDelayedTask.run(TickDelayedTask.java:20) ~[forge:?] {re:classloading}
	at net.minecraft.util.concurrent.ThreadTaskExecutor.run(ThreadTaskExecutor.java:139) ~[forge:?] {re:classloading,pl:accesstransformer:B}
	at net.minecraft.util.concurrent.RecursiveEventLoop.run(RecursiveEventLoop.java:22) ~[forge:?] {re:classloading}
	at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:759) ~[forge:?] {re:classloading,pl:accesstransformer:B}
	at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:159) ~[forge:?] {re:classloading,pl:accesstransformer:B}
	at net.minecraft.util.concurrent.ThreadTaskExecutor.driveOne(ThreadTaskExecutor.java:109) ~[forge:?] {re:classloading,pl:accesstransformer:B}
	at net.minecraft.server.MinecraftServer.driveOneInternal(MinecraftServer.java:742) ~[forge:?] {re:classloading,pl:accesstransformer:B}
	at net.minecraft.server.MinecraftServer.driveOne(MinecraftServer.java:736) ~[forge:?] {re:classloading,pl:accesstransformer:B}
	at net.minecraft.util.concurrent.ThreadTaskExecutor.drainTasks(ThreadTaskExecutor.java:97) ~[forge:?] {re:classloading,pl:accesstransformer:B}
	at net.minecraft.server.MinecraftServer.runScheduledTasks(MinecraftServer.java:721) ~[forge:?] {re:classloading,pl:accesstransformer:B}
	at net.minecraft.server.MinecraftServer.func_240802_v_(MinecraftServer.java:668) ~[forge:?] {re:classloading,pl:accesstransformer:B}
	at net.minecraft.server.MinecraftServer.lambda$startServer$0(MinecraftServer.java:233) ~[forge:?] {re:classloading,pl:accesstransformer:B}
Решение
Да канал я регистрировал, но я просто не совсем могу понять именно как распределять пакеты если не сложно можете ли подсказать как его правильнее устроить.
При регистрации пакета я обычно указываю чётко с какой куда он будет отправляться.
Java:
CHANNEL.registerMessage(index++, CodexScreenPacket.class, CodexScreenPacket::save, CodexScreenPacket::load,
        CodexScreenPacket::handle, Optional.of(NetworkDirection.PLAY_TO_CLIENT));
Далее, проверка на!world.isClientSide(), дабы отправилось точно с сервера, а не с клиента.
Java:
if(!world.isClientSide) {
            CompoundNBT tag = player.getItemInHand(hand).getOrCreateTag();
            PacketHandler.CHANNEL.sendTo(new CodexScreenPacket(tag)...
427
41
108
Нужно сделать чтобы при поломки блока данные отправлялись к персонажу поломавшему блок
BlockEvent.BreakEvent, если мне не изменяет память, бросается как и на серверной стороне, так и на клиентской. (когда от сервера прилетает пакет, что блок сломан). Не совсем понял, что ты хочешь сделать, но полагаю, твою проблему решит проверка на World#isRemote
 
  • Like
Реакции: PoNi
10
0
Дружище, вот ситуация, локальная сеть, мне нужно чтобы каждый игрок получал (к примеру в консоль свой поломанный блок). Можешь подсказать как сделать? и как реализовать World#isRemote буду очень благодарен
 
427
41
108
Тогда тем более зачем на стороне сервера какой-либо код? world.isRemore возвращает - удаленный мир или серверный. когда событие бросается и сторона клиент - делай что нужно (upd исправил, опять путаю стороны)
 
Последнее редактирование:
  • Like
Реакции: PoNi
10
0
Только, что проверил. и все же в консоль выводится только у хоста, а нужно чтобы у каждого подключенного игрока условно в консоли появлялся Hello world. Я написал вот так, может я как то ошибся?
event:
@SubscribeEvent
    public static void onBlockBreak(final BlockEvent.BreakEvent event)
    {
        if (!event.getWorld().isRemote())
        {
            System.out.println("Hello world");
        }
    }
 
10
0
Инвертировать условие?
А иначе не работает, я уже совсем не понимаю, вроде в теории прочитал и должно было заработать...

event:
@Mod.EventBusSubscriber(modid =  EmptyMod.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE, value = Dist.CLIENT)
public class AromaEvents
{
    @SubscribeEvent
    public static void onBlockBreak(final BlockEvent.BreakEvent event)
    {
        if (event.getWorld().isRemote())
        {
            System.out.println("Hello world");
        }
    }

}
 
345
25
94
Свой обработчик событий ты регистрируешь на стороне клиента, то есть тут уже никогда не сработает условие !World#isRemote, метод isRemote работает очень просто, если возвращается истина, то это клиент, в противном случае (возвращается ложь) это сервер.
 
10
0
Дружище, а как тогда у меня срабатывает !World.isRemote и не срабатывает World.isRemote
Event:
@Mod.EventBusSubscriber(modid =  EmptyMod.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE, value = Dist.CLIENT)
public class AromaEvents
{
    @SubscribeEvent
    public static void onBlockBreak(final BlockEvent.BreakEvent event)
    {
        if (event.getWorld().isRemote())
        {
            System.out.println("Hello world");
        }
    }

}


И здесь ничего не происхрдит
 
345
25
94
Возможно на новых версиях что-то поменяли, прочитай JavaDoc к методу, но очень сомневаюсь, что это действительно так.

UPD: Проверь регистрацию, возможно пытаешься зарегистрировать только на сервере.
 
Последнее редактирование:
1,369
112
241
В BlockEvent.BreakEvent поступает только серверный мир. Пройдясь по цепочке вызовов (Ctrl+B или Ctrl+ЛКМ) это легко понять. Так, ты пытался отправить с сервера клиентский пакет обратно на сервер. И да, регистрировал ли ты канал пакетов в моде?
 
  • Like
Реакции: PoNi
427
41
108
Специально посмотрел откуда вызывается событие ломания блока. Оно действительно вызывается только на стороне сервера/ (Проверять надо было, я сразу говорил что не уверен)
На стороне клиента отправляются серверу лишь состояния ,,процесса копания,, и координаты. (Если только попробовать предугадать, когда блок будет сломан)
1675078865108.png
1675079313428.png
 
Последнее редактирование:
10
0
В BlockEvent.BreakEvent поступает только серверный мир. Пройдясь по цепочке вызовов (Ctrl+B или Ctrl+ЛКМ) это легко понять. Так, ты пытался отправить с сервера клиентский пакет обратно на сервер. И да, регистрировал ли ты канал пакетов в моде?
Да канал я регистрировал, но я просто не совсем могу понять именно как распределять пакеты если не сложно можете ли подсказать как его правильнее устроить.
 
10
0
а нафига тебе пакеты... у игрока есть метод sendMessage, отправляющий ему сообщение с серверной части.
хреначь его в брейк эвент свой и всё, не нужны никакие пакеты

а нафига тебе пакеты... у игрока есть метод sendMessage, отправляющий ему сообщение с серверной части.
хреначь его в брейк эвент свой и всё, не нужны никакие пакеты
Sendmessage отравляет сообщение в мат, мне же нужно чтобы игрок не знал о внутренности мода, а данные сразу отправлялись на прибор.
 
1,369
112
241
Да канал я регистрировал, но я просто не совсем могу понять именно как распределять пакеты если не сложно можете ли подсказать как его правильнее устроить.
При регистрации пакета я обычно указываю чётко с какой куда он будет отправляться.
Java:
CHANNEL.registerMessage(index++, CodexScreenPacket.class, CodexScreenPacket::save, CodexScreenPacket::load,
        CodexScreenPacket::handle, Optional.of(NetworkDirection.PLAY_TO_CLIENT));
Далее, проверка на!world.isClientSide(), дабы отправилось точно с сервера, а не с клиента.
Java:
if(!world.isClientSide) {
            CompoundNBT tag = player.getItemInHand(hand).getOrCreateTag();
            PacketHandler.CHANNEL.sendTo(new CodexScreenPacket(tag),
                    ((ServerPlayerEntity) player).connection.connection,
                    NetworkDirection.PLAY_TO_CLIENT);
        }
И, самое важное, определить кому ты отправляешь данные. В случае выше, я отправляю их одному конкретному игроку, т.к. у меня открывается гуишка. Если я поставлю вместо player.connection.connection PacketDistributor.ALL.noArg(), то пакет станет отправляться ВСЕМ игрокам, что в данном случае нам не нужно, ибо тогда гуишка откроется у ВСЕХ игроков.
В PacketDistributor все доступные случае описаны, если вдруг что не понятно, переходишь по вызову правой части и смотришь кому и как отправляется пакет.
Мой пакет выглядит след. образом (а то, как я отправляю пакет было выше):
Java:
public class CodexScreenPacket {
    public final CompoundNBT tag;
   
    public CodexScreenPacket(CompoundNBT tag) {
        this.tag = tag;
    }
   
    public static CodexScreenPacket load(PacketBuffer buffer){
        return new CodexScreenPacket(buffer.readNbt());
    }
    public void save(PacketBuffer buffer) {
        buffer.writeNbt(tag);
    }
  
    public static void handle(CodexScreenPacket msg, Supplier<NetworkEvent.Context> ctx) {
        ctx.get().enqueueWork(() -> openScreen(msg));
        ctx.get().setPacketHandled(true);
    }
   
    @OnlyIn(Dist.CLIENT) //Во избежание краша сервера, т.к. класс Minecraft есть _только_ на клиенте
    public static void openScreen(CodexScreenPacket msg) {
        Minecraft.getInstance().setScreen(new CodexScreen(msg.tag.getList("Revealed", 8)));
    }
}
 
  • Like
Реакции: PoNi
10
0
При регистрации пакета я обычно указываю чётко с какой куда он будет отправляться
а где именно регистрировать, и можете ли ресурсы посоветовать где почитать про пакетную систему, а то я неделю просидел и не смог придумать только запутался больше.
 
1,369
112
241
Тривиально:
//В гл. классе мода
//В конструктор мода FMLJavaModLoadingContext.get().getModEventBus().register(this);
public void commonSetup(FMLCommonSetupEvent e) {
    PacketHandler.init();
}

//Далее код из PacketHandler
public static void init() {
        CHANNEL.registerMessage(0, CodexScreenPacket.class, CodexScreenPacket::save, CodexScreenPacket::load,
                CodexScreenPacket::handle, Optional.of(NetworkDirection.PLAY_TO_CLIENT));
    }
 
Сверху