Переменная привязанная в игроку

Версия Minecraft
1.16.4
API
Forge
Я хотел бы сделать так, чтобы для каждого игрока была создана переменная, к примеру мана/усталость и т.п.
Она не должна отображаться(только если в руке будет нужный предмет, но это позже)

Вопрос в чем, как для каждого пользователя создавать переменную, как с ней потом работать и как ее сохранить, чтобы после выхода она сохранила свое значение?
Идеально было бы сохранять для каждого юзера файл player.data или что-то в этом роде и туда записывать данные, а после входа в мир подгружать.
Проблема в том, что примерный алгоритм я себе выстроил, а как реализовать не знаю.


Так же интересует привязка переменой к определенному чанку. Тип 1 чанк содержит сколько-то энергии. Как это реализовать?
 
Последнее редактирование:

will0376

Токсичная личность
2,072
55
584
7,099
324
1,510
Зачем открывать дверь тараном, когда есть ключ?
чтобы для каждого игрока была создана переменная, к примеру мана/усталость
привязка переменой к определенному чанку. Тип 1 чанк содержит сколько-то энергии.
Лучший способ это сделать - капабилити
Пример капабилити игрока:
Пример капабилити чанка(1.12, но концептуально ниче не поменялось с тех пор):
В Ender'sMagic также есть мана в игроках, однако в пример привел Psi, ибо 1.16
 
Последнее редактирование:
Лучший способ это сделать - капабилити
Хах, я почитал, но так и не понял что это и для чего😅

Я ожидал что-то типа метода, который будет записывать данные переменой в файл, а потом считывать. А там реализуют интерфейсы, что-то с чем-то сравнивают, какие-то условия. Да и док на англ, что усложняет ее понимание(гугл переводчик не очень справляется).

Может я слепой, но я упор не вижу в том примере, как сохранить переменную и как потом к ней получить дотсуп(
 
Если с игроками все более-менее понятно. НУ там фалом действительно можно, я думаю, на оптимизацию это не повлияет. А вот с чанакми, уже сложнее.
я думал как-то сделать метод генерации "энергии" в чанке и при прогрузке, просто вызывать генератор, так мы получим значение энергии в чанке без переменный и файлов. Но я хочу добавить возможность траты энергии, по этому такой вариант не подходит. Им можно регулировать "базовый уровень" или же "стабильный". Но вот динамический не выйдет так(
А с этими капабилити и миксины я не разобрался, да и мне кажется что это слишком сложный метод для такой простой задачи)
 
Миксины - чрезвычайно мощный инструмент разработки, предназначенный для модификации и дополнения уже написанного скомпилированного код

Это костыли, а костыли код оптимизированным сделать не смогут)
Должны быть стандартные способы, типо этого:
Лучший способ это сделать - капабилити
Но почитав шо это, не понял ка использовать для сохранения переменных, да и вообще для чего это хрень
 
7,099
324
1,510
Это костыли, а костыли код оптимизированным сделать не смогут)
Костыльность и эффективность кода - вещи ортогональные
Но почитав шо это, не понял ка использовать для сохранения переменных, да и вообще для чего это хрень
Это способ прикрепить к произвольному объекту некоторую структуру данных
 
вещи ортогональные
Может в моддинге я новичок, но в программирование далеко не нуб. Лишняя библа, надстройка кода над обычной функцией и есть антоним оптимизации. Так что они не ортогональные, а как раз наоборот)
Я ходил в очень строгую школу для кодеров, где лишний байт, не нужная библа = -1 бал, два таких косяка и уже еле набираешь проходной бал, по этому я в первую очередь думаю об оптимизации, какой бы проект не был.
Даже для роботы с Json строками я писал свой класс, кстати, очень прикольно и удобно, ничего сложно в пару сотен строк, в отличие от нынешних библ.
Я не в коем случаи не хочу как-то по остроумничать, но факт есть фактом. Если есть возможность сделать все стандартными инструментами, почему нет, зачем нагружать код?


Это способ прикрепить к произвольному объекту некоторую структуру данных
В общем я понял алгоритм действий, наверное...

миксины - костыли?
Прямым языком сказано, самим автором - ЭТО КОТСЫЛЬ.
Миксины - чрезвычайно мощный инструмент разработки, предназначенный для модификации и дополнения уже написанного скомпилированного код


Оставлю пока тему без ответа, когда напишу код, который реализует, то что в моем вопросе, то выложу тут, чтобы следующий кто, захочет сделать подобное не разбирался в том дер той каше, что разбираюсь я.
 
7,099
324
1,510
Может в моддинге я новичок, но в программирование далеко не нуб.
Я ходил в очень строгую школу для кодеров
Это очень здорово, что ты пришел в моддинг имея начальные знания и навыки программирования!
Лишняя библа, надстройка кода над обычной функцией и есть антоним оптимизации.
Чтобы случайно не превратить дискуссию в спор о словах и их определениях, давай для начала определим, о чем мы спорим.
Насколько я понимаю, речь идет о быстродействии программы. Т.е. мы считаем программу оптимальной, если она решает задачу за минимальное возможное время. Стоит отметить, что время выполнения программы - объективная метрика. Если два программиста будут стоять рядом и смотреть на одну и ту же работающую программу, то она будет работать одинаково быстро с точки зрения обоих программистов.
Все верно?

Надеюсь что так, поэтому продолжаю дальше.

Теперь определимся с костылями.
В инете можно найти много разных определений. Обычно имеется ввиду код, который выполняет функцию "подпорки", чтобы эта часть программы работала. Часто это попытка заставить программу работать любой ценой, игнорируя правила хорошего кода, не разбираясь в специфике ошибки и т.д. Стоит отметить, что костыльность кода зависит от конкретного наблюдателя: два программиста могут стоять рядом, смотреть на код одной и той же программы и оценивать его костыльность по разному.


Теперь вернемся к вопросу ортогональности этих двух вещей. Когда я говорю, что две вещи ортогональны, я подразумеваю, что эти две вещи могут сочетаться в разных взаимо-независимых пропорциях.
  • Код, который быстро работает и является костылем.
  • Код, который медленно работает и является костылем.
  • Код, который быстро работает и не является костылем.
  • Код, который медленно работает и не является костылем.
Почему все четыре кейса(а также их более тонкие градации) могут существовать?
Это может быть очевидно, исходя из того, что оптимальность по времени выполнения - объективная характеристика кода, а костыльность - субъективная.
Также можно найти живые примеры на каждый кейс. Так как ты в основном сомневаешься в первом и в последнем кейсах, то я приведу примеры для них.

Допустим, разработчик делает рендер графического интерфейса на чистом opengl. Разработчик правильно выровнял один из элементов гуи где-то в середине процесса. Ближе к концу работы он замечает, что этот элемент съезжает на 10 пикселей правее. Разработчик не видит очевидных причин этому явлению. Т.к. в остальном все работает нормально, то он просто добавляет этому элементу дополнительное смещение при помощи glTranslate(10,0,0). Опять тестирует и все работает хорошо. Эта дополнительная инструкция практически не замедлила рендер, в коде и без того куча операцией с матрицами трансформации. При этом такое решение является костылем, т.к. разработчик не выяснял о глубинных причинах бага.

Допустим, имеется список дробных чисел и стоит микро-задача применять ко всех элементам списка какие-то функции преобразования, типо, умножить на два. Формализуем ее как метод void transformList(Function<Double, Double> mapper)
При этом другой части программы может единовременно быть нужен только сегмент списка.
Как мы можем реализовать функцию transformList?
Самое простое, что можно сделать, это обойти список и обновить значения
Java:
private List<Double> list = new ArrayList<>();

public void transformList(Function<Double, Double> mapper){
    for(int i=0;i<list.size();i++)
        list.set(i, mapper.apply(list.get(i)));
}
Костыль? Вроде бы нет, вполне очевидный код, который делает то, что ожидается.
Он быстрый? Временная сложность алгоритма O(n): если у нас список длиной n, то нужно произвести n операций.
Можно ли сделать быстрее? Из специфики программы известно, что единовременно потребляется только некоторая часть списка. Если список длинный, а окно потребления маленькое, то имеет смысл применять преобразование только к элементам, которые нужны в текущий момент. Это позволит снизить временную сложность до O(k), где k - размер окна потребления.
Получается примерно так(детали, связанные с высвобождением более не используемых списков и некоторые другие вещи опущены для простоты):
Java:
private List<Double> list = new ArrayList<>();

public void transformList(Function<Double, Double> mapper){
    list = new LazyTransformList(list, mapper);
}

public static class LazyTransformList<A> extends List<A> {
    private List<A> original;
    private Function<A,A> mapper;
    private List<A> transformed;
    public LazyTransformList(List<A> original, Function<A,A> mapper) { ... }

    public A get(int index){
        A exists = transformed.get(index);
        if(exists!=null)
            return exists;
        else{
            A newValue = mapper.apply(original.get(index));
            transformed.set(index, newValue);
            return newValue;
        }   
    }

    public void set(int index, A newValue){
        transformed.set(index, newValue);
    }
}
Таким образом, первый вариант реализации не самый быстрый

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


Код, который медленно работает и не является костылем.
В этом я не сомневаюсь, ибо есть куча примера так называемого гов плохого кода.
У меня даже есть сомнение насчет реализации в своем коде, мне кажется то как я реализовал данную возможность костылем:
Java:
@SubscribeEvent
    public static void onCraft(ItemCraftedEvent event){
        CraftingInventory inv = (CraftingInventory) event.getInventory();
        boolean p = false, b = false, s=false, l = false;
        for (int i = 0; i < inv.getSizeInventory(); ++i) {
            ItemStack stack = inv.getStackInSlot(i);
            if (!stack.isEmpty()) {
                if (stack.getItem() == Items.BOWL) {
                    b = true;
                }else if(stack.getItem() == ItemInit.white_shard.get()) {
                    s = true;
                }else if(stack.getItem() == ItemInit.pounder.get()) {
                    p = true;
                }else{
                    l = true;
                }
            }
        }
        if(p && b && s && !l) {
            for (int i = 0; i < inv.getSizeInventory(); ++i) {
                ItemStack stack = inv.getStackInSlot(i);
                if (!stack.isEmpty()) {
                    if(stack.getItem() == Items.BOWL) {
                        int count = stack.getCount();
                        event.getInventory().setInventorySlotContents(i, new ItemStack(Items.BOWL, count+1));
                    }
                }
            }
        }
    }
Потому что, я ловлю событие крафта, проверяю, какой это крафт(криво, но как умею пока) и дальше делаю, то что мне нужно. При этом это событие будет постоянно вызываться и проверяться на карфт при любом крафте, что и может повлиять на саму игру, хоть даже и на 0,01 сек, но если таких крафтов с проверками будет около 100, то это уже 1 сек, при том что это лишь крафты...
Так же я вижу что выделяю лишнюю память, для переменных, которые по факту не нужны.
Мне не нравиться моя реализация, хотя может я что-то выдумываю себе.

А про чанки, у меня появилась идея, как можно присваивать им базовое значение энергии без переменных и т.п.
В основном теле я напишу метод, который будет брать ид чанка(если такой есть), биом, теги блоков, которые находятся в чанке(некоторые блоки повышают базовое значение энергии, по типу цветочки, в пустыне же нет их и там базовое значение должно быть меньше), считать сколько каких блоков, пару математических операций и на выходе мы получим значение для этого чанка.

Вопрос в том, как взять ИД чанка и массив блоков в этом чанке?
Я уже нашел хендлер onChunkLoad, который в теории можно использовать для вызова генерации базового значения.
Но вопрос в том, как потом сохранить значение энергии в чанке, после того как игрок подействовал на него. Мне кажется для такой простой операции не нужны всякие миксины, капабилити. Я заметил тут тему про NBT, как там ответили, то NBT это
Способ хранения информации. Умеет хранить примитивы.
Можно ли его использовать для хранения переменной в чанке с его колвом энергии? Мне кажется это проще, хотя я не уверен))
 

will0376

Токсичная личность
2,072
55
584
подействовал на него.
Как вариант - открыть исходники майна и посмотреть, как сохраняется инфа в чанках...
Не зря же говорят, что 90% задумок уже есть в ванили.
 
7,099
324
1,510
Прикол в том, что ваниль писали моджанги, редактируя сам майн непосредственно. В рамках моддинга это почти всегда не применимо.
Поэтому форж и предлагает капабилити.

При этом это событие будет постоянно вызываться и проверяться на карфт при любом крафте, что и может повлиять на саму игру
А ты напиши предикат по уму. Сначала чекни, какой результат крафта. Если это не твой предмет - можно дальше не чекать.

брать ид чанка(если такой есть), биом, теги блоков, которые находятся в чанке(некоторые блоки повышают базовое значение энергии
Хочешь хранить соответствие id->energy в своей мапе? Приготовься делать саппорт кейсам, типо, отгрузка-загрузка чанков/миров

Нашел в форже минимальный пример капабилити чанка:
Там просто хранится одно значение int для каждого чанка
 
А ты напиши предикат по уму. Сначала чекни, какой результат крафта. Если это не твой предмет - можно дальше не чекать.
Спасибо, реально годный совет, так код стал в два раза меньше)
Даже и не думал про проверку по результату крафта)
Java:
@SubscribeEvent
    public static void onCraft(ItemCraftedEvent event) {
        if (event.getCrafting().getItem() == ItemInit.white_dust.get()) {
            CraftingInventory inv = (CraftingInventory) event.getInventory();
            for (int i = 0; i < inv.getSizeInventory(); ++i) {
                ItemStack stack = inv.getStackInSlot(i);
                if (!stack.isEmpty()) {
                    if (stack.getItem() == Items.BOWL) {
                        int count = stack.getCount();
                        event.getInventory().setInventorySlotContents(i, new ItemStack(Items.BOWL, count + 1));
                    }
                }
            }
        }
    }


Хочешь хранить соответствие id->energy в своей мапе? Приготовься делать саппорт кейсам, типо, отгрузка-загрузка чанков/миров
Нет, генерировать значение энергии по ид чанка, постоянно. Т.е не сложный метод, который будет возвращать флот значение.
Просто при потребности получить базовое значение энергии в чанке мы его просто вычисляем заново, как по мне пару математических подсчетов будут эффективней чем где-то хранить эту инфу, она по факту меняться же не будет, если не менять логику генератора, то и значение будет постоянно одно и то же)

Нашел в форже минимальный пример капабилити чанка:
Там просто хранится одно значение int для каждого чанка
Оп, идеально, а мне большего не нужно, пока что)
Пойду читать и изучать
 
Сверху