Иконка ресурса

Реализация проводов и передачи энергии

Версия(и) Minecraft
1.12.2
Я снова с гайдом по энергии, но с обновленным. Этот гайд ни в коем случае не претендует на истину и не может, поэтому я прошу вас давать свою критику и помочь мне его допилить.

GitHub этого туториала.

То, что обновилось с прошлого раза:
  1. Теперь я сохраняю сети в world capability
  2. Если провод или другой участник сети находит в своем окружении сеть, то он просто ставит себе id сети и добавляет себя в сеть (Если это генератор или харнилище. Хранить провода не вижу смысла т.к. все равно придется пересчитывать, чтобы понять, что отсоединилось, а что нет).
  3. Если провод находит несколько сетей – он удаляет их и объеденяет в одну.
Я для проводов использовал TileEntity, но без метода update. Не отрицаю, что Id можно и в BlockState'ах хранить. Если это влияет на производительность, то я заменю tile просто на блок.

Почти все методы связанные с сетью я решил поместить в один класс и прокомментировал для удобства. Так же обозначил, что и в какой метод у блока/тайла нужно поместить, поэтому вы можете создавать своих участников сети
Java:
public class EnergyNetworkUtil {
    public static void buildNetwork(World world, BlockPos start) {
        HashSet<BlockPos> checked = new HashSet<>(); //список проверенных блоков
        HashSet<BlockPos> participants = new HashSet<>();//список генераторов, которые будут найдены в сети
        ArrayDeque<BlockPos> queue = new ArrayDeque<>(100);//Очередь, это особенность реализации алгоритма поиска в ширину.
    
    
        queue.offer(start);//Добавляем стартовую позицию в очередь
        checked.add(start);//И сразу добавляем в проверенные
        while (!queue.isEmpty()) {//Выполняем пока очередь не пуста
            BlockPos nPos = queue.poll();//Этот метод возвращает объект из головы очереди и сразу его удаляет.
            //Проверяем не генератор ли стартовая позиция.
            TileEntity tile = world.getTileEntity(nPos);
            boolean generator = false;
            boolean storage = false;
            //Здесь мы просто провереям, что полученная позиция - это генератор или хранилище и записываем результат в переменные
            //Это нам понадобиться позже
           // P.s. и естественно в участников сети
            if(tile != null) {
                generator = tile.hasCapability(EnergyGeneratorCapability.ENERGY_GENERATOR, null);
                storage = tile.hasCapability(EnergyStorageCapability.ENERGY_STORAGE, null);
                if(generator) {
                    participants.add(nPos);
                }
                if(storage) {
                    participants.add(nPos);
                }
            }
            for (EnumFacing face : EnumFacing.VALUES) {//Циклом прогоняемся по всем возможным "направлением"(не знаю как лучше перевести)
                BlockPos child = nPos.offset(face);//эта функция возвращает позицию со сдвигом в данном направлении
                TileEntity tileEntity = world.getTileEntity(child);//получаем тайл
                //Такая же проверка, как и выше только на соседние блоки
                boolean generator_child = false;
                boolean storage_child = false;
                //Проверяем нет ли в списке проверенных соседнего блока
                if(!checked.contains(child)) {
                    NetworkParticipantTile participant = null;
                    //Здесь мы проверяем, что соседние блоки - это генератор или хранилище
                    if(tileEntity != null) {
                        if(tileEntity instanceof NetworkParticipantTile) {
                            participant = (NetworkParticipantTile) tileEntity;
                        }
                        generator_child = tileEntity.hasCapability(EnergyGeneratorCapability.ENERGY_GENERATOR, null);
                        storage_child = tileEntity.hasCapability(EnergyStorageCapability.ENERGY_STORAGE, null);
                    }
                    //если провод - добавляем в очередь
                    if(world.getBlockState(child).getBlock() instanceof Wire) {
                        checked.add(child);
                        queue.addLast(child);
                    }
                    //Если соседний блок генератор и родительский(тот от которого мы "шагаем" в сторону) не генератор
                    if (generator_child && !generator) {
                        checked.add(child);
                        queue.addLast(child);
                    }
                    //Соседний блок - хранилище и родительский не хранилище
                    if(storage_child && !storage) {
                        checked.add(child);
                        queue.addLast(child);
                    }
                    //Если родительский блок генератор и соседний блок - хранилище
                    if(generator && storage_child) {
                        checked.add(child);
                        queue.addLast(child);
                    }
                }
            }
        }
        if(!participants.isEmpty()) { // Участники не пусты
            //Создаем экземпляр сети и записываем туда найденные генераторы и хранилища
            EnergyNetwork network = new EnergyNetwork();
            network.setParticipants(participants);
            //Получаем список сетей
            EnergyNetworkList list = getEnergyNetworkList(world);
            if(list != null) {
                //И добавляем туда сеть
                int id = list.addNetwork(network);
                //Потом запускаем метод который выставит id всей сети
                setNetworkId(world, start, id);
            }
        }else {
            setNetworkId(world, start, -1);
        }
    }

    public static void setNetworkId(World world, BlockPos start, int id) {
        HashSet<BlockPos> checked = new HashSet<>(); //список проверенных блоков
        ArrayDeque<BlockPos> queue = new ArrayDeque<>(100);//Очередь, это особенность реализации алгоритма поиска в ширину.
    
        queue.offer(start);//Добавляем стартовую позицию в очередь
        checked.add(start);//И сразу добавляем в проверенные
        while (!queue.isEmpty()) {
            /* <Так же как ив buildNetwork> */
            //Только вместо того, чтобы записывать в участники мы устанавливаем участникам id(проводам тоже)
            BlockPos nPos = queue.poll();
            TileEntity tile = world.getTileEntity(nPos);
            boolean generator = false;
            boolean storage = false;
            if(tile != null) {
                if(tile instanceof NetworkParticipantTile) {
                    NetworkParticipantTile participant = (NetworkParticipantTile) tile;
                    participant.setNetworkId(id);
                }
                storage = tile.hasCapability(EnergyStorageCapability.ENERGY_STORAGE, null);
                generator = tile.hasCapability(EnergyGeneratorCapability.ENERGY_GENERATOR, null);
            }
            for (EnumFacing face : EnumFacing.VALUES) {
                BlockPos child = nPos.offset(face);
                TileEntity tileEntity = world.getTileEntity(child);
                boolean generator_child = false;
                boolean storage_child = false;
                if(!checked.contains(child)) {
                    NetworkParticipantTile participant = null;
                    if(tileEntity != null) {
                        if(tileEntity instanceof NetworkParticipantTile) {
                            participant = (NetworkParticipantTile) tileEntity;
                        }
                        generator_child = tileEntity.hasCapability(EnergyGeneratorCapability.ENERGY_GENERATOR, null);
                        storage_child = tileEntity.hasCapability(EnergyStorageCapability.ENERGY_STORAGE, null);
                    }
                
                    if(world.getBlockState(child).getBlock() instanceof Wire) {
                        checked.add(child);
                        queue.addLast(child);
                    }
                    if (generator_child && !generator) {
                        if(participant != null) participant.setNetworkId(id);//Ставим id
                        checked.add(child);
                        queue.addLast(child);
                    }
                    if(storage_child && !storage) {
                        if(participant != null) participant.setNetworkId(id);
                        checked.add(child);
                        queue.addLast(child);
                    }
                    if(generator && storage_child) {
                        if(participant != null) participant.setNetworkId(id);
                        checked.add(child);
                        queue.addLast(child);
                    }
                }
            }
            /* </Так же как ив buildNetwork> */
        }
    }
    //В onLoad у NetworkParticipantTile
    public static void checkAround(World world, BlockPos pos) {
        HashSet<Integer> ids = new HashSet(1);// Список найденных id вокруг
        HashSet<NetworkParticipantTile> no_ids = new HashSet(1);// Список позиций у которых отсутствует id
        //Получаем tile и записываем в переменную генератор ли это или хранилище
        TileEntity start = world.getTileEntity(pos);
        boolean generator = start.hasCapability(EnergyGeneratorCapability.ENERGY_GENERATOR, null);
        boolean storage = start.hasCapability(EnergyStorageCapability.ENERGY_STORAGE, null);
        //Здесь будет храниться id сети
        int this_id = -1;// -1 значит, что сети нет
        EnergyNetworkList list = getEnergyNetworkList(world);
        //Если генератор то строим сеть т.к. генератор главный в сети.
        if(generator) {
            buildNetwork(world, pos);
        }
        // Проверяем все соседние блоки
        for(EnumFacing facing : EnumFacing.VALUES) {
            BlockPos child = pos.offset(facing);
            TileEntity tile = world.getTileEntity(child);
        
            if(tile != null && tile instanceof NetworkParticipantTile) {
                boolean tile_generator = tile.hasCapability(EnergyGeneratorCapability.ENERGY_GENERATOR, null);
                boolean tile_storage = tile.hasCapability(EnergyStorageCapability.ENERGY_STORAGE, null);
                NetworkParticipantTile participant = (NetworkParticipantTile) tile;
                /* Так же, как и в buildNetwork выполняем проверки и в зависимости от того
                 * есть ли у блока id сети или нет, добавляем в нужный список */
                if(generator && !tile_generator) {
                    if(participant.hasNetworkId()) {
                        ids.add(participant.getNetworkId());
                    }
                    else {
                        no_ids.add(participant);
                    }
                }
                if(storage && !tile_storage) {
                    if(participant.hasNetworkId()) {
                        ids.add(participant.getNetworkId());
                    }
                    else {
                        no_ids.add(participant);
                    }
                }
                if(world.getBlockState(pos).getBlock() instanceof Wire) {
                    if(participant.hasNetworkId()) {
                        ids.add(participant.getNetworkId());
                    }
                    else {
                        no_ids.add(participant);
                    }
                }
            
            }
        
        }
        //Конвертируем в массивы для удобства
        Integer[] ids_arr = ids.toArray(new Integer[ids.size()]);
        NetworkParticipantTile[] no_ids_arr = no_ids.toArray(new NetworkParticipantTile[no_ids.size()]);
    
        //Если мы нашли больше двух айдишников, значит у нас две сети и их нудно соеденить
        //HashSet поидее не позволит записать два одинаковых id
        if(ids_arr.length>1) {
            //Сюда мы соберем вместе все найденные сети
            HashSet<BlockPos> result = new HashSet<>();
            if(list != null) {
                for (int i = 0; i < ids_arr.length; i++) {
                    if(list.getNetwork(ids_arr[i]) != null) {
                        //Добавляем всех участников сети из списка
                        result.addAll(list.getNetwork(ids_arr[i]).getParticipants());
                        //И удаляем сеть т.к. мы будем объеденять и нам она будет ненужна
                        list.removeNetwork(ids_arr[i]);
                    }
                }
            }
            //Создаем сеть из собранных
            int new_id = list.addNetwork(new EnergyNetwork(result));
            //И выставляем id новой сети
            setNetworkId(world, pos, new_id);
        }
        //Если id только 1
        else if (!ids.isEmpty() && ids_arr.length == 1) {
            TileEntity tile = world.getTileEntity(pos);
            this_id = ids_arr[0];
            if(tile != null) {
                //Если это участник сети просто ставим ему id
                if(tile instanceof NetworkParticipantTile) {
                    NetworkParticipantTile participant = (NetworkParticipantTile) tile;
                    participant.setNetworkId(this_id);
                }
                //+Если это генератор или хранилище добавляем в найденную сеть
                if(tile.hasCapability(EnergyGeneratorCapability.ENERGY_GENERATOR, null) ||
                        tile.hasCapability(EnergyStorageCapability.ENERGY_STORAGE, null)) {
                    if(list != null && list.getNetwork(this_id) != null){
                        list.getNetwork(this_id).getParticipants().add(pos);
                    }
                }
            }
        }
        //Если мы нашли блоки без id и this_id(id сети, в которой состоит блок вызвавший checkAround) не -1
        if(!no_ids.isEmpty() && this_id != -1) {
            for (int i = 0; i < no_ids_arr.length; i++) {
                NetworkParticipantTile participant = no_ids_arr[i];
                boolean generator_child = participant.hasCapability(EnergyGeneratorCapability.ENERGY_GENERATOR, null);
                boolean storage_child = participant.hasCapability(EnergyStorageCapability.ENERGY_STORAGE, null);
            
                //Добавляем в сеть если генератор/хранилище
                if(generator_child || storage_child && list != null) {
                    list.getNetwork(this_id).add(participant.getPos());
                }
                //Запускаем установку id т.к. за этим блоком могут быть еще блоки с отсутствующим id
                setNetworkId(world, participant.getPos(), this_id);
                participant.setNetworkId(this_id);
            }
        }
    }
    /**
     *
     * @param world Мир
     * @param start Стартовая позиция
     * @return Возвращает найденных участников
     */
    public static HashSet<BlockPos> checkNetwork(World world, BlockPos start) {
    
        /* <Так же как и в buildNetwork>
         * (за исключением того, что мы просто возвращаем найденных участников, а не создаем из них сеть)  */
        HashSet<BlockPos> checked = new HashSet<>();
        HashSet<BlockPos> participants = new HashSet<>();
        ArrayDeque<BlockPos> queue = new ArrayDeque<>(100);
    
        queue.offer(start);
        checked.add(start);
        while (!queue.isEmpty()) {
            BlockPos nPos = queue.poll();
        
            TileEntity tile = world.getTileEntity(nPos);
            boolean generator = false;
            boolean storage = false;
            if(tile != null) {
                generator = tile.hasCapability(EnergyGeneratorCapability.ENERGY_GENERATOR, null);
                storage = tile.hasCapability(EnergyStorageCapability.ENERGY_STORAGE, null);
                if(generator) {
                    participants.add(nPos);
                }
                if(storage) {
                    participants.add(nPos);
                }
            }
            for (EnumFacing face : EnumFacing.VALUES) {//Циклом прогоняемся по всем возможным "направлением"(не знаю как лучше перевести
                BlockPos child = nPos.offset(face);//эта функция возвращает позицию со сдвигом в данном направлении
                TileEntity tileEntity = world.getTileEntity(child);//получаем тайл
                boolean generator_child = false;
                boolean storage_child = false;
                if(!checked.contains(child)) {
                    if(tileEntity != null) {
                        generator_child = tileEntity.hasCapability(EnergyGeneratorCapability.ENERGY_GENERATOR, null);
                        storage_child = tileEntity.hasCapability(EnergyStorageCapability.ENERGY_STORAGE, null);
                    }
                    if(world.getBlockState(child).getBlock() instanceof Wire) {
                        checked.add(child);
                        queue.addLast(child);
                    }
                    if (generator_child && !generator) {
                        checked.add(child);
                        queue.addLast(child);
                    }
                    if(storage_child && !storage) {
                        checked.add(child);
                        queue.addLast(child);
                    }
                    if(generator && storage_child) {
                        checked.add(child);
                        queue.addLast(child);
                    }
                }
            }
        }
        /* </Так же как и в buildNetwork> */
        return participants;
    }

    public static EnergyNetworkList getEnergyNetworkList(World world) {
        if(world.hasCapability(EnergyNetworkListCapability.NETWORK_LIST, null)) {
            EnergyNetworkList list = world.getCapability(EnergyNetworkListCapability.NETWORK_LIST, null);
            return list;
        }
        return null;
    }

    public static int getNetworkIdFromBlock(World world, BlockPos pos) {
        TileEntity tile = world.getTileEntity(pos);
        if(tile != null && tile instanceof NetworkParticipantTile) {
            NetworkParticipantTile participant = (NetworkParticipantTile) tile;
            if(participant.hasNetworkId()) {
                return participant.getNetworkId();
            }
        }
        return -1;
    }
    /**
     * Этот метод проверяет есть ли в where элементы what
     * @param where HashSet, в котором будет проверяться наличие элементов what
     * @param what HashSet, в элементы которого будут проверяться на наличие в what
     * @return true/false
     */
    public static  boolean contains(HashSet<BlockPos> where, HashSet<BlockPos> what) {
        BlockPos[] we = where.toArray(new BlockPos[where.size()]);
        BlockPos[] wt = what.toArray(new BlockPos[where.size()]);
        if(we.length < wt.length) {
            return false;
        }
        for (int i = 0; i < wt.length; i++) {
            for (int j = 0; j < we.length; j++) {
                if (wt[i] == we[j]) {
                     continue;
                }
                if(j == we.length - 1) {
                    return false;
                }
            }
        }
        return true;
    }
    //В метод breakBlock у блока
    public static void breakBlock(World worldIn,BlockPos pos) {
        EnergyNetworkList list = EnergyNetworkUtil.getEnergyNetworkList(worldIn);
    
        if(list != null) {
            TileEntity tile = worldIn.getTileEntity(pos);
        
            if(tile != null && tile instanceof NetworkParticipantTile) {
                NetworkParticipantTile participant = (NetworkParticipantTile) tile;
            
                if(participant.hasNetworkId()) {
                    EnergyNetwork network = list.getNetwork(participant.getNetworkId());
                
                    if(network != null) {// не забыть
                        network.remove(participant.getPos());
                        if(!network.hasGenerators(worldIn)){
                            list.removeNetwork(participant.getNetworkId());
                            EnergyNetworkUtil.setNetworkId(worldIn, pos, -1);
                        }
                    
                    }
                }
        
            }
        }
    }
    // в breakBlock у провода
    public static void breakWire(World worldIn,BlockPos pos) {
        //Сюда мы сохраним подсети, получившиеся в результате того, что мы с ломали провод
        HashMap<BlockPos,EnergyNetwork> posses = new HashMap();
        int networkId = -1;
        EnergyNetwork network = null;
        EnergyNetworkList list = EnergyNetworkUtil.getEnergyNetworkList(worldIn);
        TileEntity tile = worldIn.getTileEntity(pos);
    
        if(tile != null && tile instanceof NetworkParticipantTile) {
            NetworkParticipantTile participantTile = (NetworkParticipantTile) tile;
            networkId = participantTile.getNetworkId();
            network = list.getNetwork(networkId);
        }
    
        for (EnumFacing face : EnumFacing.VALUES) {
            BlockPos p = pos.offset(face);
            TileEntity tileEntity = worldIn.getTileEntity(p);
        
            if(tileEntity != null && tileEntity instanceof NetworkParticipantTile) {
            
                NetworkParticipantTile participant = (NetworkParticipantTile) tileEntity;
            
                //Получив tile мы проверяем, что у него есть id
                if(participant.hasNetworkId()) {
                    boolean check = false;
                    if(network != null && network.getParticipants() != null) {
                    
                        //С помощью метода checkNetwork проверяем какие есть участники сети(генераторы/хранилища)
                        HashSet<BlockPos> sub_network = checkNetwork(worldIn, p);
                        //С помощью метода contains проверяем есть ли в этой подчети все участники родительской
                        check = contains(sub_network, network.getParticipants());
                    
                        //Если нет, то мы должны поместить эту сеть в список подсетей
                        //Перед этим проверив, есть ли подсети с такими же участниками
                        //Так мы избавимся от дубликатов
                        if(!check) {
                            boolean equal_check = false;
                            Iterator<Map.Entry<BlockPos, EnergyNetwork>> iterator = posses.entrySet().iterator();
                            while (iterator.hasNext()) {
                                Map.Entry<BlockPos, EnergyNetwork> entry = iterator.next();
                                EnergyNetwork nEnergyNetwork = entry.getValue();
                            
                                equal_check = Arrays.equals(nEnergyNetwork.getParticipants().toArray(new BlockPos[nEnergyNetwork.getParticipants().size()]),
                                        sub_network.toArray(new BlockPos[sub_network.size()]));
                            
                                if(equal_check) {
                                    break;
                                }
                            }
                            //Если сеть не совпадает ни с одной из ранее найденных, то добавляем ее
                            if(!equal_check) {
                                EnergyNetwork energyNetwork = new EnergyNetwork();
                                energyNetwork.setParticipants(sub_network);
                                posses.put(p, energyNetwork);
                            }
                        }else {
                            continue;
                        }
                    }
                }
            }
        }
    
        if(!posses.isEmpty() && networkId != -1) {
            //Удаляем главную сеть
            list.removeNetwork(networkId);
            Iterator<Map.Entry<BlockPos, EnergyNetwork>> iterator = posses.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<BlockPos, EnergyNetwork> entry = iterator.next();
                EnergyNetwork nEnergyNetwork = entry.getValue();
            
                //Если подсеть имеет генераторы, значит мы можем ее добавить в список сетей
                if(nEnergyNetwork.hasGenerators(worldIn)) {
                    int id = list.addNetwork(nEnergyNetwork);
                    //выставляем id от ранее найденной стартовой позиции
                    BlockPos start = entry.getKey();
                    EnergyNetworkUtil.setNetworkId(worldIn, start, id);
                }
                else {//Если сеть не имеет генераторов - мы просто ставим id -1(Значит сети нет)
                    BlockPos start = entry.getKey();
                    EnergyNetworkUtil.setNetworkId(worldIn, start, -1);
                }
            }
        }
    }
}

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

Я не делал для capability интерфейс, поэтому сразу реализация. Этот класс будет ответственен за хранение сетей
Java:
public class EnergyNetworkList {
    public HashMap<Integer, EnergyNetwork> networks = new HashMap<>();

    public EnergyNetwork getNetwork(int id){
        return networks.get(id);
    }

    public Set<Integer> getIds(){
        return networks.keySet();
    }

    public int getNextId() {
        Set<Integer> keys = networks.keySet();
        int j = 0;
    
        for(Integer i : keys) {
            if(i > j) {
                j = i;
            }
        }
        int res = ++j;
        return res;
    }

    public void removeNetwork(int id){
        networks.remove(id);
    }

    public int addNetwork(EnergyNetwork network){
        int id = getNextId();
        networks.put(id, network);
        return id;
    }

    public void setNetworks(HashMap<Integer, EnergyNetwork> networks) {
        this.networks = networks;
    }

    public HashMap<Integer, EnergyNetwork> getNetworks() {
        return networks;
    }

}

Теперь создадим класс, который хранит экземпляр нашего списка сетей и имеет удобный метод для регистрации capability
Java:
public class EnergyNetworkListCapability {
    @CapabilityInject(EnergyNetworkList.class)
    public static Capability<EnergyNetworkList> NETWORK_LIST = null;

    public static void register() {
        CapabilityManager.INSTANCE.register(EnergyNetworkList.class, new Capability.IStorage<EnergyNetworkList>()
        {

            @Override
            public NBTBase writeNBT(Capability<EnergyNetworkList> capability, EnergyNetworkList instance, EnumFacing side) {
            
                NBTTagList mainList = new NBTTagList();
            
                Iterator<Map.Entry<Integer, EnergyNetwork>> iterator = instance.getNetworks().entrySet().iterator();
                while (iterator.hasNext()) {
                    Map.Entry<Integer, EnergyNetwork> network = iterator.next();
                
                    NBTTagList list = new NBTTagList();
                    NBTTagCompound compound = new NBTTagCompound();
                
                    compound.setInteger("id", network.getKey());
                    for(BlockPos pos : network.getValue().getParticipants()) {
                        list.appendTag(NBTUtil.createPosTag(pos));
                    }
                    compound.setTag("participants", list);
                
                    mainList.appendTag(compound);
                }
            
                return mainList;
            }

            @Override
            public void readNBT(Capability<EnergyNetworkList> capability, EnergyNetworkList instance, EnumFacing side,
                    NBTBase nbt) {
                NBTTagList mainList = (NBTTagList) nbt;
                HashMap<Integer, EnergyNetwork> networks = new HashMap<>();
            
                for (int i = 0; i < mainList.tagCount(); i++) {
                    NBTTagCompound compound = mainList.getCompoundTagAt(i);
                    EnergyNetwork network = new EnergyNetwork();
                    HashSet<BlockPos> participants = new HashSet<>();
                
                    NBTTagList list = compound.getTagList("participants", Constants.NBT.TAG_COMPOUND);
                    for (int j = 0; j < list.tagCount(); j++) {
                        NBTTagCompound tag = list.getCompoundTagAt(j);
                        participants.add(NBTUtil.getPosFromTag(tag));
                    }
                    network.setParticipants(participants);
                
                    networks.put(compound.getInteger("id"), network);
                
                }
                instance.setNetworks(networks);
            }
        
        }, EnergyNetworkList::new);
    }
}

"Прикрепляем" capability к миру в EventHandler:

Java:
@Mod.EventBusSubscriber(modid = BaseClass.MODID)
public class RedEventHandler {

    private static final ResourceLocation WORLD_CAP = new ResourceLocation(BaseClass.MODID, "EnergyNetworkList");

    @SubscribeEvent
    public static void attachCapability(AttachCapabilitiesEvent<World> event) {
        event.addCapability(WORLD_CAP, new ICapabilitySerializable<NBTTagList>() {
        
            private final EnergyNetworkList energyNetworkList = new EnergyNetworkList();
        
            @Override
            public boolean hasCapability(final Capability<?> capability, final EnumFacing facing) {
                return capability == EnergyNetworkListCapability.NETWORK_LIST;
            }

            @Override
            public <T> T getCapability(final Capability<T> capability, final EnumFacing facing) {
                if (capability == EnergyNetworkListCapability.NETWORK_LIST) {
                    return (T) this.energyNetworkList;
                }
                return null;
            }

            @Override
            public NBTTagList serializeNBT() {
                return (NBTTagList) EnergyNetworkListCapability.NETWORK_LIST.getStorage()
                .writeNBT(EnergyNetworkListCapability.NETWORK_LIST, energyNetworkList, null);
            }

            @Override
            public void deserializeNBT(NBTTagList nbt) {
                EnergyNetworkListCapability.NETWORK_LIST.getStorage()
                .readNBT(EnergyNetworkListCapability.NETWORK_LIST, energyNetworkList, null, nbt);
            }
        });
    }
}

Примеры реализаций блоков:

NetworkParticipantTile(Просто вместо TileEntity наследуйте блоки, которые должны участвовать в сети от него). Это для того, чтобы каждому блоку не писать один и тотже writeNBT и readNBT, а так же это очень упрощает проверку
Java:
public class NetworkParticipantTile extends TileEntity{

    private int networkId = -1;

    @Override
    public void onLoad() {
        EnergyNetworkUtil.checkAround(getWorld(), getPos());
        super.onLoad();
    }

    public boolean hasNetworkId() {
        if(networkId == -1) {
            return false;
        }
        return true;
    }

    public void setNetworkId(int networkId) {
        this.networkId = networkId;
        markDirty();
    }

    public int getNetworkId() {
        return networkId;
    }

    @Override
    public void readFromNBT(NBTTagCompound compound) {
        this.networkId = compound.getInteger("networkId");
        super.readFromNBT(compound);
    }

    @Override
    public NBTTagCompound writeToNBT(NBTTagCompound compound) {
        compound.setInteger("networkId", networkId);
        return super.writeToNBT(compound);
    }

}

NetworkParticipant(Тоже самое, как и с NetworkParticipantTile, только это блок)
Java:
public class NetworkParticipant extends Block{

    public NetworkParticipant(Material materialIn) {
        super(materialIn);
    }

    @Override
    public void breakBlock(World worldIn, BlockPos pos, IBlockState state) {
        EnergyNetworkUtil.breakBlock(worldIn, pos);
        super.breakBlock(worldIn, pos, state);
    }


}

Wire(Соединения проводов визуально взял у @iconst_1 )
Java:
public class Wire extends BlockEnergyTileEntity<WireTile>{
    //Каждое свойство отвечает за то, подключен ли к этой стороне провод или другой участник цепи(true/false)
    public static final PropertyBool DOWN = PropertyBool.create("down");
    public static final PropertyBool UP = PropertyBool.create("up");
    public static final PropertyBool NORTH = PropertyBool.create("north");
    public static final PropertyBool SOUTH = PropertyBool.create("south");
    public static final PropertyBool WEST = PropertyBool.create("west");
    public static final PropertyBool EAST = PropertyBool.create("east");

    public Wire() {
        super(Material.CIRCUITS);
        this.setRegistryName("wire");
        this.setUnlocalizedName("wire");
        this.setCreativeTab(CommonProxy.redstone_things_tab);
    }

    //Метод "строящий" сеть, передаем в него мир и стартовую позицию
    @Deprecated
    public ArrayList<BlockPos> buildNetwork(IBlockAccess worldIn, BlockPos start) {
        HashSet<BlockPos> checked = new HashSet<>(); //список проверенных блоков
        ArrayList<BlockPos> generators = new ArrayList<>();//список генераторов, которые будут найдены в сети
        ArrayList<BlockPos> storages = new ArrayList<>();//список проводов, которые будут найдены в сети
        ArrayDeque<BlockPos> queue = new ArrayDeque<>(100);//Очередь, это особенность реализации алгоритма поиска в ширину.
    
        queue.offer(start);//Добавляем стартовую позицию в очередь
        checked.add(start);//И сразу добавляем в проверенные
        while (!queue.isEmpty()) {//Выполняем пока очередь не пуста
            BlockPos nPos = queue.poll();//Этот метод возвращает объект из головы очереди и сразу его удаляет.
            //Проверяем не генератор ли стартовая позиция.
            TileEntity tile = worldIn.getTileEntity(nPos);
            if(tile != null) {
                if(tile.hasCapability(EnergyGeneratorCapability.ENERGY_GENERATOR, null)) {// У меня свои капы на генератор и хранилище
                    generators.add(nPos);
                }
            }
            for (EnumFacing face : EnumFacing.VALUES) {//Циклом прогоняемся по всем возможным "направлением"(не знаю как лучше перевести
                BlockPos child = nPos.offset(face);//эта функция возвращает позицию со сдвигом в данном направлении
                TileEntity tileEntity = worldIn.getTileEntity(child);//получаем тайл
                if(tileEntity != null) {
                    //проверяем, что это, генератор или хранилище
                    if(tileEntity.hasCapability(EnergyGeneratorCapability.ENERGY_GENERATOR, null)) {
                        generators.add(child); // и добовлеям их в соответственные списки
                    }
                    if(tileEntity.hasCapability(EnergyStorageCapability.ENERGY_STORAGE, null)) {
                        storages.add(child);// и добовлеям их в соответственные списки
                    }
                }
                //Если в списке проверенных нету этой позиции и блок на этой позиции - это провод, добавляем в список
                //проверенных + в очередь, чтобы с этой позиции проверить уже другие блоки
                if(!checked.contains(child) && worldIn.getBlockState(child).getBlock() instanceof Wire) {
                    checked.add(child);
                    queue.addLast(child);//Добавляем в низ очереди
                }
            }
        }
    
        if(!generators.isEmpty()) {//Если есть в сети  генераторы
            for (int i = 0; i < generators.size(); i++) {//прогоняемся по ним циклом
                BlockPos gPos = generators.get(i);
                TileEntity tileEntity = worldIn.getTileEntity(gPos);
                if(tileEntity != null && tileEntity instanceof GeneratorTile) {
                    //Проверяем, что они наследают спец класс GeneratorTile, нижке под спойлером есть этот класс
                    GeneratorTile generator = (GeneratorTile) tileEntity;
                    generator.setStorages(storages);//устонавливаем в этот генератор список найденных в сети хранилищ
                }
            }
        }
        return generators;
    }

    @Override
    public void breakBlock(World worldIn, BlockPos pos, IBlockState state) {
        // TODO Auto-generated method stub
        EnergyNetworkUtil.breakWire(worldIn, pos);
        super.breakBlock(worldIn, pos, state);
    }

    //Это метод нужен для проверки можем ли мы текстуркой "соедениться" с другим блоком
    public boolean canConnectTo(IBlockAccess world, BlockPos pos, EnumFacing facing){
        BlockPos child = pos.offset(facing);
        TileEntity tileEntity = world.getTileEntity(child);
        if(tileEntity != null) {
            if(tileEntity.hasCapability(EnergyStorageCapability.ENERGY_STORAGE, null)) {
                //если генератор - да
                return true;
            }
            if(tileEntity.hasCapability(EnergyGeneratorCapability.ENERGY_GENERATOR, null)) {
                //если хранилище - да
                return true;
            }
        }
        if(world.getBlockState(child).getBlock() instanceof Wire) {
            //если провод - да
            return true;
        }
        return false;
    }


    @Override
    public void addCollisionBoxToList(IBlockState state, World world, BlockPos pos, AxisAlignedBB entityBox,
            List<AxisAlignedBB> collidingBoxes, Entity entityIn, boolean isActualState) {
        //В зависимости от того, какие стороны подключены, выставляем определенную коллизию
        if (canConnectTo(world, pos, EnumFacing.UP)) {
            addCollisionBoxToList(pos, entityBox, collidingBoxes,
                    new AxisAlignedBB(0.377, 0, 0.377, 0.623D, 0.623D, 0.623D));
        }
        if (canConnectTo(world, pos, EnumFacing.UP)) {
            addCollisionBoxToList(pos, entityBox, collidingBoxes,
                    new AxisAlignedBB(0.377, 0.377, 0.377, 0.623D, 1, 0.623D));
        }
        if (canConnectTo(world, pos, EnumFacing.NORTH)) {
            addCollisionBoxToList(pos, entityBox, collidingBoxes,
                    new AxisAlignedBB(0.377, 0.377, 0, 0.623D, 0.623D, 0.623D));
        }
        if (canConnectTo(world, pos, EnumFacing.SOUTH)) {
            addCollisionBoxToList(pos, entityBox, collidingBoxes,
                    new AxisAlignedBB(0.377, 0.377, 0.377, 0.623D, 0.623D, 1));
        }
        if (canConnectTo(world, pos, EnumFacing.EAST)) {
            addCollisionBoxToList(pos, entityBox, collidingBoxes,
                    new AxisAlignedBB(0.377, 0.377, 0.377, 1, 0.623D, 0.623D));
        }
        if (canConnectTo(world, pos, EnumFacing.WEST)) {
            addCollisionBoxToList(pos, entityBox, collidingBoxes,
                    new AxisAlignedBB(0, 0.377, 0.377, 0.623D, 0.623D, 0.623D));
        }
    }

    @Override
    public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess world, BlockPos pos) {
        // Тоже самое, что и с коллизией
        double[] sidebound = new double[6];
        sidebound[0]=sidebound[1]=sidebound[2]=0.377D;
        sidebound[3]=sidebound[4]=sidebound[5]=0.6231D;
    
        if (canConnectTo(world, pos, EnumFacing.DOWN) == true) {
            sidebound[1]=0;
        }
        if (canConnectTo(world, pos, EnumFacing.UP) == true) {
            sidebound[4]=1;
        }
        if (canConnectTo(world, pos, EnumFacing.NORTH) == true) {
            sidebound[2]=0;
        }
        if (canConnectTo(world, pos, EnumFacing.SOUTH) == true) {
            sidebound[5]=1;
        }
        if (canConnectTo(world, pos, EnumFacing.WEST) == true) {
            sidebound[0]=0;
        }
        if (canConnectTo(world, pos, EnumFacing.EAST) == true) {
            sidebound[3]=1;
        }
    
        return new AxisAlignedBB(sidebound[0], sidebound[1], sidebound[2], sidebound[3], sidebound[4], sidebound[5]);
    }

    @Override
    protected BlockStateContainer createBlockState() {
        //Создаем контейнер для наших "свойств"
        return new BlockStateContainer(this, UP, DOWN, NORTH, SOUTH, WEST, EAST);
    }

    @Override
    public IBlockState getActualState(IBlockState state, IBlockAccess world, BlockPos pos) {
        //Пользуясь методом просто устанавливаем true/false свойствам
        // С помощью этого мы будем рендерить(или нет) частичку кабеля с определенной стороны.
        return state
        .withProperty(DOWN, canConnectTo(world, pos, EnumFacing.DOWN))
        .withProperty(UP, canConnectTo(world, pos, EnumFacing.UP))
        .withProperty(NORTH, canConnectTo(world, pos, EnumFacing.NORTH))
        .withProperty(SOUTH, canConnectTo(world, pos, EnumFacing.SOUTH))
        .withProperty(WEST, canConnectTo(world, pos, EnumFacing.WEST))
        .withProperty(EAST, canConnectTo(world, pos, EnumFacing.EAST));
    }

    @Override
    public int getMetaFromState(IBlockState state) {
        return 0;
    }

    @Override
    public boolean isOpaqueCube(IBlockState state) {
        return false;
    }

    @Override
    public boolean isFullCube(IBlockState state) {
        return false;
    }

    @Override
    public Class<WireTile> getTileEntityClass() {
        // TODO Auto-generated method stub
        return WireTile.class;
    }

    @Override
    public WireTile createTileEntity(World world, IBlockState blockState) {
        // TODO Auto-generated method stub
        return new WireTile();
    }

}

P.s. Не знаю, как лучше было поступить, поэтому создал новую тему. Если нужно будет, то я перенесу или удалю предыдущую.
Автор
GreedyCat
Просмотры
1,232
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок

Другие ресурсы пользователя GreedyCat

Сверху