[РЕШЕНО] Как получить seed на клиенте?

Версия Minecraft
1.16.5
API
Forge
1,356
109
233
В моём моде есть перемешивание списка в зависимости от seed'а мира. Однако, если я делаю это методом выше, то при подключении игрока к серверу, на клиенте (у игрока) не вызываются нужные мне методы (т.к. они вызываются исключительно на сервере).
Эти листы я использую в TileEntityRenderer, и они там играют ключевую роль, так что просто забить на это дело я не могу.
Сейчас код выглядит так:
Java:
@SubscribeEvent public static void onWorldLoaded(WorldEvent.Load e) {
            if(e.getWorld() instanceof ServerWorld) {
                long seed = ((ServerWorld)e.getWorld()).getSeed();
                randomizeFirstList(new Random(seed));
                createSecondList(new Random(seed));
                createThirdList(new Random(seed));
            }
        }
Однако, я пробовал и получать сид через сервер, записанный в клиентском мире (((ClientWorld)e.getWorld()).getServer().getWorldData().worldGenSettings().seed();), но у меня вылетал null.
Ещё я пробовал пакеты, но с ними у меня не задалось, и я просто не понял как их использовать. Да и кажется мне, что они в данном случае не нужны.
Собственно вопрос: как я могу передать seed с сервера на клиент при подгрузке мира на клиенте?
 
1,356
109
233
Только если запрашивать пакетами с сервера.
Хорошо, тогда как запрашивать? Я пробовал делать пакеты, но, честно говоря, не совсем понимаю как конкретно они работают. Доки forge читал, гайды смотрел, однако понимание меня не озарило. Сейчас пакет запроса выглядит так (init() вызывается в commonSetup(FMLCommonSetupEvent), который вызывается в конструкторе мода):
Сам пакет:
public class SeedPacket {
    public final long seed;
    public SeedPacket(long seed) { this.seed = seed; }
    public SeedPacket(PacketBuffer buffer){ this(buffer.readLong()); }
    public void save(PacketBuffer buffer) { buffer.writeLong(seed); }
    public static long handle(SeedPacket pkt, Supplier<NetworkEvent.Context> ctx) {
        ctx.get().enqueueWork(() -> {
            DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> SeedPacket.handlePacket(pkt, ctx));
        });
        ctx.get().setPacketHandled(true);
        return pkt.seed;
    }
    public static void handlePacket(SeedPacket msg, Supplier<NetworkEvent.Context> ctx) {
        //Здесь должна быть передача сида с сервера на клиент
    }
}

Регистрация пакетов:
public class PacketHandler {
    public static final SimpleChannel CHANNEL = NetworkRegistry.newSimpleChannel(
            new ResourceLocation(EnhancedEnchanting.MODID, "network"), () -> "1",
            version -> version.equals("1"), version -> version.equals("1"));
    public static void init() {
        CHANNEL.registerMessage(0, SeedPacket.class, SeedPacket::save, SeedPacket::new, SeedPacket::handle);
    }
}

Отправлять, если я правильно понял, мне нужно в WorldEvent.Load вот так? PacketHandler.CHANNEL.send(PacketDistributor.ALL.noArg(), new SeedPacket(seed));
Даже если я так его отправил, как мне его принять (или что там нужно сделать)?
 
1,074
72
372
1,356
109
233
Условный SeedCracker тоже существует (правда на Fabric, но не думаю, что будет сложно переписать на Forge).

Есть ещё вариант мешать список от чего-то другого, что привязано к миру? Я буду только рад, если найдётся что-то такое, ибо эти костыли (на мой взгляд) с сидом явно плохи.
 
1,356
109
233
Сейчас в двух списках, генерирующихся через сид, 810 значений String аля "oku_oku_oku", "oku_dor_zet" и т.д. Мне кажется пакета просто не хватит.
Я просто пакет отправить/прочитать(?) не могу. Текущий код:
Java:
public class SeedPacket {
    public long seed;
    public SeedPacket(long seed) { this.seed = seed; }
    public SeedPacket(PacketBuffer buffer){ this(buffer.readLong()); }
    public void save(PacketBuffer buffer) { buffer.writeLong(seed); }
    public static void handle(SeedPacket pkt, Supplier<NetworkEvent.Context> ctx) {
        ctx.get().enqueueWork(() -> {
            DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> SeedPacket.handlePacket(pkt, ctx));
        });
        ctx.get().setPacketHandled(true);
    }
    public static void handlePacket(SeedPacket msg, Supplier<NetworkEvent.Context> ctx) {
        WorldEnchantmentsEvents.randomizeFirstList(new Random(msg.seed));
        WorldEnchantmentsEvents.createSecondList(new Random(msg.seed));
        WorldEnchantmentsEvents.createThirdList(new Random(msg.seed));
    }
}
Java:
public class PacketHandler {
    public static final SimpleChannel CHANNEL = NetworkRegistry.newSimpleChannel(
            new ResourceLocation(EnhancedEnchanting.MODID, "network"), () -> "1",
            version -> version.equals("1"), version -> version.equals("1"));
    public static void init() {
        CHANNEL.registerMessage(0, SeedPacket.class, SeedPacket::save, SeedPacket::new, SeedPacket::handle,
                Optional.of(NetworkDirection.PLAY_TO_CLIENT));
    }
}
Java:
@SubscribeEvent public static void onWorldLoaded(WorldEvent.Load e) {
        if(e.getWorld() instanceof ServerWorld) {
            long seed = ((ServerWorld)e.getWorld()).getSeed();
            PacketHandler.CHANNEL.send(PacketDistributor.ALL.noArg(), new SeedPacket(seed));
            randomizeFirstList(new Random(seed));
            createSecondList(new Random(seed));
            createThirdList(new Random(seed));
        }
    }

Регистрация пакета не изменилась.
 
1,356
109
233
Посидел, потыркал пакеты, и, как я понял, WorldEvent.Load не отправляет пакет на клиент, точнее отправляет, но по завершении загрузки данных мира, однако перед Preparing Spawn Area. Поэтому игрок не получает Пакет.

Вопрос следующий: какой эвент использовать для отслеживания входа игрока на сервер?
 
1,356
109
233
А, точно. Я чёт зациклился на пакетах и искал вход там. Ща попробуем, отпишу.

UPD. Заработало. Передаю сейчас сид, но посмотрим, может в будущем поменяю на единственный лист.
Огромное спасибо всем в помощи!
 
Последнее редактирование:
1,356
109
233
Я переделал пакет так, чтобы он отправлял не сид, а лист с String. Для будущих поколений (или будущего тупого меня) передаю сей код сюда. Вызываю также в PlayerLoggedInEvent.
Java:
public class T2ListPacket {
    public final List<String> list; //Хранимый пакетом лист (хотя, по факту, на самом деле пакет хранит тучу String'ов)
    public T2ListPacket(List<String> list) { this.list = list; } //Конструктор для отправки пакета

    //Запись пакета
    public void save(PacketBuffer buffer) {
        buffer.writeShort(list.size()); //Записываем длину листа, чтобы в будущем по нему пробежаться
        for(String str : list) //Простецкий foreach
            buffer.writeUtf(str, 7); //Записываем значение в буфер.
        //Также я делаю ограничение на размер текста (7 символов), но это совсем необязательно.
    }

    //Считывание пакета
    public static T2ListPacket load(PacketBuffer buffer){
        List<String> list = new ArrayList<>(); //Объявление листа, который позже будет записан в пакет
        short length = buffer.readShort(); //Считывание длины листа, которую мы записали выше
        for(short = 0; i < length; i++)
            list.add(buffer.readUtf(7)); //Пробегаемся по листу и записываем все значения в буфер.
         //Текст ограничен 7 символами с начала строки, тоже необязательно
        return new T2ListPacket(list); //Возвращаем новый экземпляр пакета с листом, полученным из буфера
    }

    //Собственно то, что этот пакет делает
    public static void handle(T2ListPacket msg, Supplier<NetworkEvent.Context> ctx) {
        ctx.get().enqueueWork(() -> {
            WordMachineTileRenderer.T2_LIST.addAll(msg.list); //Делаем с листами, полученными из пакета, что хотим
            WordForgeTileRenderer.T2_LIST.addAll(msg.list); //Тут тоже
        });
        ctx.get().setPacketHandled(true);
    }
}
 
Последнее редактирование:
1,074
72
372
Лучше не стало. К чему такая жёсткая экономия строк в ущерб читаемости? Конструктор и поля остались по-прежнему кашей. Код не представляет ценности, такое более-менее разобравшийся начинающий способен написать. Запись длины списка на int крайне избыточна, short в большинстве случаев хватит за глаза, а может даже и byte.
 
Последнее редактирование:
1,356
109
233
С short понял-принял, исправлю. То что код для начинающих я не спорю, однако может помочь людям, которые в пакетах совсем не шарят (я на момент этой темы, про буфер слышал лишь в кастомных типах рецептов, например). Про экономию ничего не скажу, на мой взгляд там всё более чем понятно. Скажу так: зачем разворачивать тело конструктора, где лишь 1 строка?
 

tox1cozZ

aka Agravaine
8,455
598
2,892
Запись длины списка на int крайне избыточна, short в большинстве случаев хватит за глаза, а может даже и byte.
Есть утилитарный метод write/readVarInt, который упаковывает инт в нужное количество байт в зависимости от значения.
 

necauqua

когда-то был anti344
Администратор
1,216
27
172
Тю ты блин.
Не знаю как там было раньше, но сейчас (за исключением конструктора) всё прекрасно читаемо, не знаю что тут можно вообще по-другому написать, алгоритм описан элементарный и однозначный, лол
Комментарии которые дословно повторяют то что написано кодом как раз таки лишние, там один комментарий на весь код нужен, где ты поясняешь зачем там волшебная цифра 7 - чтобы потом через месяц это понять сразу, когда будешь читать свой код.

Умный человек посоветовал varint - для таких вещей как длины листов/строк - идеальная вещь.

зачем разворачивать тело конструктора, где лишь 1 строка?
А зачем их сворачивать? Ща напишу эссе опять..
Тратишь на это лишнее время, и ide-шка и так это сделает, визуально, если настроишь.
Затем, если читать много-много методов, то методы в которых кто-то выпендрился с нестандартным синтаксисом, зачем-то, цепляют глаз, заставляют потратить лишнее мысленное усилие на понимание того, что ты собственно глазами увидел.

И ещё хуже если кто-то банально не следит, не считает важным, за нормальностью синтаксиса: тут for со скобочками, тут без, тут так метод отформатирован, тут эдак, там перед = не стоит пробела, тут переменные с большой буквы, хотя в джаве повсемесно придерживаются camelCase - да ладно, всё же работает, компилятору же по барабану - вот только тебе через месяц, или, что наверное более важно, вообще другим людям (ну вроде нас, кстати, привет) читать такое банально неприятно (у тебя ещё всё ок, господин выше докопался по полной, и риторика у данного невероятного программиста эдакая свысока).

Нормальный кодстайл, и желательно вообще буквально форсить форматирование автоматизированными тулами - типа включить format on save, так вот, нормальный кодстайл он как нормальная грамматика - отступы там, параграфы, писать с большой буквы и всё такое. Попробуй найди какой-нибудь безграмотный фанфик, почитай его, и скажи что глаза не спотыкаются и тебе комфортно такое читать.
 
1,356
109
233
Умный человек посоветовал varint
С этой штукой разобрался, сейчас её и использую. Комментарий в этом коде я добавлял на сайте, т.е. в коде у меня его нет. И да, в коде только пояснение за цифорку 7.
Код я стараюсь держать в одном стиле, даже если где-то фрагмент скопирован, скажем, из той же ботании. С "эссе" в целом согласен, по поводу тулз возьму на заметку.
 
Сверху