- Версия(и) Minecraft
- 1.12.2
Я снова с гайдом по энергии, но с обновленным. Этот гайд ни в коем случае не претендует на истину и не может, поэтому я прошу вас давать свою критику и помочь мне его допилить.
GitHub этого туториала.
То, что обновилось с прошлого раза:
Почти все методы связанные с сетью я решил поместить в один класс и прокомментировал для удобства. Так же обозначил, что и в какой метод у блока/тайла нужно поместить, поэтому вы можете создавать своих участников сети
Теперь перейдем к capability
Тут я не комментировал код т.к. думаю что все предельно ясно, а если нет, то я обязательно прокомментирую
Я не делал для capability интерфейс, поэтому сразу реализация. Этот класс будет ответственен за хранение сетей
Теперь создадим класс, который хранит экземпляр нашего списка сетей и имеет удобный метод для регистрации capability
"Прикрепляем" capability к миру в EventHandler:
Примеры реализаций блоков:
NetworkParticipantTile(Просто вместо TileEntity наследуйте блоки, которые должны участвовать в сети от него). Это для того, чтобы каждому блоку не писать один и тотже writeNBT и readNBT, а так же это очень упрощает проверку
NetworkParticipant(Тоже самое, как и с NetworkParticipantTile, только это блок)
Wire(Соединения проводов визуально взял у @iconst_1 )
P.s. Не знаю, как лучше было поступить, поэтому создал новую тему. Если нужно будет, то я перенесу или удалю предыдущую.
GitHub этого туториала.
То, что обновилось с прошлого раза:
- Теперь я сохраняю сети в world capability
- Если провод или другой участник сети находит в своем окружении сеть, то он просто ставит себе id сети и добавляет себя в сеть (Если это генератор или харнилище. Хранить провода не вижу смысла т.к. все равно придется пересчитывать, чтобы понять, что отсоединилось, а что нет).
- Если провод находит несколько сетей – он удаляет их и объеденяет в одну.
Почти все методы связанные с сетью я решил поместить в один класс и прокомментировал для удобства. Так же обозначил, что и в какой метод у блока/тайла нужно поместить, поэтому вы можете создавать своих участников сети
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. Не знаю, как лучше было поступить, поэтому создал новую тему. Если нужно будет, то я перенесу или удалю предыдущую.