Помогите с @API

Версия Minecraft
1.7.10
1,159
38
544
Добрый день. Есть у меня желание добавить в игру систему переносимого веса. И сделать я хочу это, как подобает - реализовав API, который позволит модерам самим определять что делать при перегрузе, самостоятельно определять веса для каждого итема, назначать максимальный переносимый вес для игроков... и все в таком духе.

Т.е. я не хочу сделать мод, я хочу сделать апи, который будет использоваться в других модах.

Дальше я немного теряюсь. Гугл говорит, что пакет, содержащий апи должен иметь класс package-info.java c аннотацией @API. Но эта самая аннотация требует такой параметр как "owner", что подразумевает, что должен быть мод, ДЛЯ КОТОРОГО был сделан данный апи. Но вся соль в том, что весь мой код - это и есть апи. Вот как выглядит класс моего "мода" :D

Java:
package ru.rarescrap.weightapi;

import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.event.FMLInitializationEvent;

@Mod(modid = APIContainer.MODID, version = APIContainer.VERSION)
public class APIContainer
{
    public static final String MODID = "weightapi";
    public static final String VERSION = "1.0.0";

    @EventHandler
    public void init(FMLInitializationEvent event) {}
}

Как вы поняли, это дело существует как раз чтобы заполнить "owner"а. Мне такое кажется излишним и я чувствую, что можно лучше.

Раз мой "мод" по факту не является модом, то может быть это библиотека? Быть может стоит просто собрать jar-ник с классами моего апи и затолкать в <forge_server>/libraries/?

Не знаю... Кроме того, я так же не могу найти среди исходников форжа код, который работает с аннотацией @API

Обращаюсь к вам, товарищи. Подскажите лучшее решение для моей задумки. Есть ли у кого опыт разработки апи для майнкрафта в том виде, в каком я его описал? Если вам не понятно что я вообще пытаюсь сделать, то быть может мой код расскажет об этом больше:
Код:
package ru.rarescrap;

import net.minecraft.entity.Entity;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;

/**
 * Интерфейс для объктов, способных расчитывать вес предметов
 */
public interface IWeightProvider {

    /**
     * Вычисляет вес стака
     * @param itemStack Стак, вес которого нужно вычислить
     * @param inventory Инвентарь, где в данный момент находится стак
     * @param owner Владелец инентаря
     * @return Вес стака
     */
    /*
     * Тут нет простых методов вроде getWeight(Item), т.к. расчет веса может основываться и на оссобенностях
     * игрока (вдруг у него есть способность на компенсацию веса?), и на оссобенностях инвентаря (вдруг руда в
     * этом инвентаре не имее веса?), в котором находится стак... Именно поэтому этот метод имеет так много
     * аргументов - ради гибкости использования.
     */
    double getWeight(ItemStack itemStack, IInventory inventory, Entity owner);


}


Код:
package ru.rarescrap;

import net.minecraft.entity.Entity;
import net.minecraft.inventory.IInventory;

/**
 * Интерфейс для отслеживания изменения веса в инвентарях
 */
public interface IWeightTracker {
    /**
     * @return Инвентарь, который отслеживает данный {@link IWeightTracker}
     */
    IInventory getInventory();

    /**
     * @return Максимальная вместимость инвентаря
     */
    int getMaxWeight();

    /**
     * @return Текущий вес инвентаря
     */
    int getCurrentWeight();

    /**
     * @return True, если инвентарь перегружен (обычно, когда вес инвентаря
     *         больше максимально допустимого). Иначе - false.
     */
    boolean isOverloaded();

    // TODO: Нужно ли?
    Entity getInventoryOwner();

    // TODO: Нужно ли?
//    void onCurrentWeightChanged();
}

Код:
package ru.rarescrap;

import net.minecraft.inventory.IInventory;
import net.minecraft.item.ItemStack;

import java.util.HashMap;
import java.util.Map;

public class WeightRegistry {

    private static IWeightProvider weightProvider;
    private static Map<IInventory, IWeightTracker> trackers = new HashMap<IInventory, IWeightTracker>();

    // Трекеры нужно регистрировать по большому счету только для того, чтобы иметь возможность получить их из других мест
    public static void register(IWeightTracker tracker) {
        trackers.put(tracker.getInventory(), tracker);
        // TODO: Event?
    }

    public static void registerWeightProvider(IWeightProvider provider) {
        weightProvider = provider;
        // TODO: Event?
    }

    public static IWeightTracker getTracker(IInventory inventory) {
        return trackers.get(inventory);
    }

    public static IWeightProvider getWeightProvider() {
        return weightProvider;
    }

    // TODO: Опасно пиздец. Стоит ли оставлять?
    public static IWeightTracker findTracker(ItemStack itemStack) {
        for (Map.Entry<IInventory, IWeightTracker> entry : trackers.entrySet()) {
            IInventory inventory = entry.getKey();
            for (int i = 0; i < inventory.getSizeInventory(); i++) {
                ItemStack itemStack1 = inventory.getStackInSlot(i);
                if (itemStack1 == itemStack) return trackers.get(inventory);
            }
        }

        return null;
    }
}

Код:
package ru.rarescrap.event;

import net.minecraft.entity.Entity;
import net.minecraft.inventory.IInventory;
import net.minecraftforge.event.entity.EntityEvent;

public class WeightChangedEvent extends EntityEvent {
    public IInventory inventory;
    public double prevWeight;
    public double currentWeight;
    public boolean isOverloaded; // Это лучше, чем делать отдельный евент на перегрузку
    // TODO: Итемстак, который привел к изменению веса?
    // TODO: Трекер тут?

    public WeightChangedEvent(IInventory inventory, double prevWeight, double currentWeight, boolean isOverloaded, Entity owner) { //TODO: Зачем инвентарь если есть овнер который его содержит? Или может быть ситуация что овнер может не содержать инвентаря, которым же управляет?
        super(owner);
        this.inventory = inventory;
        this.prevWeight = prevWeight;
        this.currentWeight = currentWeight;
        this.isOverloaded = isOverloaded;
    }
}
 
1,159
38
544
В любом случае должен быть мод. Какой смысл от этих классов, если они нигде не будут задействоваться?
Ну конечно нужен мод. Только не мой - чужой. Смысл то в том, чтобы конкретную логику реализовывал другой мод.
 

tox1cozZ

aka Agravaine
8,455
598
2,892
Зачем тогда ему нужен твой апи, если он все равно будет реализовывать ВСЮ ЛОГИКУ САМ С НУЛЯ?
Я когда-то объяснял за эту тему, почитай, может поймешь. Как сделать Api?
Апи - это по сути расширяемость. В твоем случае у тебя должна быть реализована база: где-то это должен считать вес всех предметов в инвентаре игрока, детектить перевес(вызывать соответствующие методы) и все этом роде.
 
1,159
38
544
где-то это должен считать вес всех предметов в инвентаре игрока, детектить перевес(вызывать соответствующие методы) и все этом роде
Откуда я знаю как модер захочет считать вес? Один захочет читать веса из конфига, второй захочет чтобы у всего был вес 1, а третий вообще прикрутит туда вычисление через какоие-нибудь РПГ статы.

Смысл в том что моддер сам определяет как ему расчитывать вес и как и в каких инвенторях его детектить, если он того хочет.

Зачем тогда ему нужен твой апи, если он все равно будет реализовывать ВСЮ ЛОГИКУ САМ С НУЛЯ?
По моей задумке, ему не нужно реализовавыть ВСЮ ЛОГИКУ. А лишь те части, которые ему нужны. Поэтому я запилил WeightRegistry. Он будет будет хранить один и только один механизм определения веса вещей и трекеры к разным инвентарям. А моды, реализующие мой апи, смогут создавать свои собственные механизмы отслеживания изменения веса в инвентарях (трекеры), которые будут выбрасывать евенты при перегрузе, изменении веса, etc (эти самые эвенты определены в моем апи).

Т.е. я вижу применение моего апи так - Предположим, есть какой-то чувак решивший добавить в свой "мод на рюкзаки" (к примеру) поддержку веса. Ему не нужно определять вес вещей, ему не нужно думать что нужно делать если рюкзак переружен. Он просто пишет трекер для своего инвентаря и посылает евенты. А как на них отреагирует игра - дело еще одного чувака (о нем ниже)

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

Ну а третий вообще хочет самостоятельно расчитывать вес каждого предмета. Ему хочется чтобы игра работала с таким же поведением, но с другим весом вещей. Ему пофиг до эвентов, трекеров... Ему нужно лишь сделать свой IWeightProvider и регануть его в WeightRegistry.

При помощи моего апи я хочу удовлетворить нужны всех этих чуваков. Ты уж извини меня за возможное сумбурное изложение мыслей. Наверное, я что-то упускаю.
 
Последнее редактирование:
2,505
81
397
WeightProvider лучше регать для конкретного айтема. И иметь ещё WeightProvider по-умолчанию на случай, если айтем не зареган. Тогда будет меньше коллизий между чуваками.

И я очень сомневаюсь, что ты сможешь трекать вес в кастомном IInventory.
 
1,159
38
544
Товарищи, вы упускаете основной вопрос - как сделать апи? Как вообще работает аннотация @API (я не нашел никакого упоминания ее в коде форжа). Или может для моей задачи просто сделать либу и затолкать ее в libraries? Обсуждение же самого апи - дело вторичное.
 
7,099
324
1,510
917
22
332
api в данном случае - это просто набор классов, к которому ты открываешь общий доступ для всех модеров, и обещаешь его не изменять (полезно ещё в том случае, если проект закрытый). package-info.java сообщает форджу о том, что данные пакеты должны быть загружены первыми, чтобы быть доступными для всех модов (должен быть включён в каждый пакет апи). Вот розыскал один топик на форджфоруме: Клик
 
Последнее редактирование:
7,099
324
1,510
package-info.ru сообщает форджу о том, что данные пакеты должны быть загружены первыми, чтобы быть доступными для всех модов
Так ведь есть @Mod#dependency-after, зачем дублируется функционал?
 
Сверху