Циклические методы

Версия Minecraft
1.10.2
4,167
68
693
Всем привет!
Отмечу переход в высшую лигу вопросом :)
Можете подсказать где ошибка?
Пытаюсь удалить озёра, соприкасающиеся с определённым биомом.
Делаю это через IWorldGenerator. Проверяю все соседние блоки, и соседние блоки соседних блоков ))) И так, пока вся вода не исчезнет... По логике должно работать, но по факту убирается только ближайший к биому блок воды...
Что не так?
Код:
Код:
public class RemoveWaterGenerator implements IWorldGenerator {
 
 private IBlockState waterUp = Blocks.WATER.getDefaultState();

 @Override
 public void generate(Random random, int chunkX, int chunkZ, World world, IChunkGenerator chunkGenerator, IChunkProvider chunkProvider) {
 if (world.provider.getDimension() == 69) removeLakes(world, chunkX*16, chunkZ*16);
 }
 
 public void removeLakes(World world, int chunkX, int chunkZ) {
 for (int x = 0; x < 16; ++x) {
 for (int z = 0; z < 16; ++z) {
 BlockPos pos = new BlockPos(chunkX + x, 63, chunkZ + z);
 Biome biome = world.getBiomeGenForCoords(pos);
 if (biome instanceof BiomeMistBorder) { 
    if (world.getBlockState(pos.east()) == waterUp) removeWater(world, pos.east());
    if (world.getBlockState(pos.north()) == waterUp) removeWater(world, pos.north());
    if (world.getBlockState(pos.south()) == waterUp) removeWater(world, pos.south());
    if (world.getBlockState(pos.west()) == waterUp) removeWater(world, pos.west());
 }
 }
 }
 }

 private void removeWater(World world, BlockPos pos) {
    world.setBlockToAir(pos);
     if (world.getBlockState(pos.down()) == waterUp) removeWater(world, pos.down());
     if (world.getBlockState(pos.east()) == waterUp) removeWater(world, pos.east());
     if (world.getBlockState(pos.north()) == waterUp) removeWater(world, pos.north());
     if (world.getBlockState(pos.south()) == waterUp) removeWater(world, pos.south());
     if (world.getBlockState(pos.west()) == waterUp) removeWater(world, pos.west());
 }
}

P.S. На счёт зависания данной системы не волнуюсь, океанов в мире нет.
 
Решение
Дак а в таком ключе не пробовал?
Код:
private void removeWater(World world, BlockPos pos) {
    BlockPos newPos;
    if (world.getBlockState(newPos = pos.down()) == waterUp) {
        world.setBlockState(newPos, Blocks.AIR.getDefaultState());
        // здесь можно войти в рекурсию. но можно и циклом сделать.
    }
    ...
}


Возможно, когда удаляешь блок воды, у окружающий водяных блоков не dafault state. Но это предположение, код не могу посмотреть. Еще могу посоветовать на всякий случай сравнивать equals'ом


А нельзя сравнивать просто блоки?
Эм... Меня тут игнорируют или я глупые вопросы задаю?


Может быть, есть предложения, как ещё можно убрать все прилегающие друг к другу однотипные блоки?
В WorldEdit, вроде, такое было, но что-то не могу найти нужное место в коде.
 
Рекурсия. Когда проверяешь блок, проверяешь еще соседние стороны
 
Ну так а я разве не то же самое делаю?


Метод removeWater повторяется столько раз, сколько соседних блоков воды ещё осталось... Но беда в том, что он не повторяется нифига... Срабатывает только 1-й раз.
 
Хз тогда. Попробуй подебажить, где останавливается выполнение
 
BlockPos pos = new BlockPos(chunkX + x, 63, chunkZ + z);
Возможно, из-за создания множества инстансов происходит переполнение стека, что и объясняет удаление только нескольких первых блоков воды.
 
Пардон, не то посмотрел, у тебя же новые позиции берутся как pos.east()... Посмотри код этих методов, там, наверное создают новые инстансы BlockPos.я на 1.7.10 сижу не знаю ,как там с коордами без BlockPos работать. В 1.7.10 через int передается.
 
В общем, так! Почему-то проверка не работала, если я перед ней удалял блок воды...
А вот если ставил удаление после проверки всё работало, но! сама проверка получалась циклической, так как блоки воды оставались на месте и их проверяли кучу раз...
Поступил следующим образом:
Код:
public class RemoveWaterGenerator implements IWorldGenerator {
 
 private IBlockState waterUp = Blocks.WATER.getDefaultState();

 @Override
 public void generate(Random random, int chunkX, int chunkZ, World world, IChunkGenerator chunkGenerator, IChunkProvider chunkProvider) {
    if (!world.isRemote && world.provider.getDimension() == 69) removeLakes(world, chunkX*16, chunkZ*16);
 }

 public void removeLakes(World world, int chunkX, int chunkZ) {
    for (int x = 0; x < 16; ++x) {
      for (int z = 0; z < 16; ++z) {
         BlockPos pos = new BlockPos(chunkX + x, 63, chunkZ + z);
         Biome biome = world.getBiomeGenForCoords(pos);
         if (biome instanceof BiomeMistBorder) {
            List<BlockPos> poses = new ArrayList<BlockPos>();
            if (!poses.contains(pos.east()) && world.getBlockState(pos.east()) == waterUp) {
               poses.add(pos.east());
               poses = removeWater(world, pos.east(), poses);
            } 
            if (!poses.contains(pos.north()) && world.getBlockState(pos.north()) == waterUp) {
               poses.add(pos.north());
               poses = removeWater(world, pos.north(), poses);
            }
            if (!poses.contains(pos.south()) && world.getBlockState(pos.south()) == waterUp) {
               poses.add(pos.south());
               poses = removeWater(world, pos.south(), poses);
            }
            if (!poses.contains(pos.west()) && world.getBlockState(pos.west()) == waterUp) {
               poses.add(pos.west());
               poses = removeWater(world, pos.west(), poses);
            }
            if (poses.size() > 0)
               for (int i = 0; i < poses.size(); i++) world.setBlockToAir(poses.get(i));
            poses.clear();
         }
      }
    }
 }

 private List<BlockPos> removeWater(World world, BlockPos pos, List<BlockPos> poses) {
   if (!poses.contains(pos.down()) && world.getBlockState(pos.down()) == waterUp) {
      poses.add(pos.down());
       poses = removeWater(world, pos.down(), poses);
   }
   if (!poses.contains(pos.east()) && world.getBlockState(pos.east()) == waterUp) {
       poses.add(pos.east());
       poses = removeWater(world, pos.east(), poses);
   }
   if (!poses.contains(pos.north()) && world.getBlockState(pos.north()) == waterUp) {
      poses.add(pos.north());
       poses = removeWater(world, pos.north(), poses);
   }
   if (!poses.contains(pos.south()) && world.getBlockState(pos.south()) == waterUp) {
      poses.add(pos.south());
      poses = removeWater(world, pos.south(), poses);
   }
    if (!poses.contains(pos.west()) && world.getBlockState(pos.west()) == waterUp) {
      poses.add(pos.west());
      poses = removeWater(world, pos.west(), poses);
   }
   return poses;
 }
}
В целом, проблема решена, но если кто-нибудь объяснит мне, отчего же так получалось и как это можно исправить более простым путём, буду рад )))
 
И всё-таки так игра жёстко зависает и вылетает...
Пишет: java.lang.StackOverflowError: Exception ticking world.
Я так понял, из-за переполнения массива данными... А может и нет...
Как бы так сразу удалять блоки воды?
 
Я же написал, про то, что не надо в рекурсии создавать новые. Сгенерируй их и сохрани до вызова рекурсии


А еще ты вызываешь одни и те же методы несколько раз для разных проверок.
 
Проблема тут принципиальная. Нельзя делать такие вещи через рекурсию. Стек очень-очень сильно ограничен, а у тебя тут на первой же реке глубина вызовов будет шестизначная, если не больше. Ищи решение без рекурсии (или руками ограничивай количество рекурсивных вызовов).
 
а если бы к блокам происходило обращение через int x,y,z, рекурсивный метод переполнил стек позже?
 
Даже немного быстрее наверное.
В стеке хранятся локальные переменные и место возврата из метода. При каждом рекурсивном вызове эти переменные будут добавлятся в стек.
Поэтому рекурсия всегда не эффективна по использованию памяти, если конечно компилятор не развернет рекурсию в цикл.
 
Ну ладно... Ясно, что объём маленький... Но вот почему он не ищет воду, если я сразу убираю центральный блок? Это бы сразу решило проблему. Или нет?


Да и потом, нет у меня там рек... Там просто озерца средних размеров...
Я так понял, что зависания происходят из-за создания не одного, а множества массивов в каждом новом загруженном чанке. И происходит это именно из-за того, что вода не убирается сразу, а только после нахождения всех блоков... Таким образом он снова и снова ищет те же самые блоки...
А вот как её убрать до того?


Тут беда немножко в другом, если пробовать найти иные способы решения:
Беда в том, что проверка НЕ выполняется, если перед этим удалять блок воды.
Чтобы вы поняли, о чём я говорю...
Вот так проверка не выполняется. Даже БЕЗ рекурсии.
Код:
private void removeWater(World world, BlockPos pos) {
world.setBlockState(pos, Blocks.AIR.getDefaultState()); //удаляю в начале
     if (world.getBlockState(pos.down()) == waterUp) {
     System.out.println(pos.down());
     //removeWater(world, pos.down());
     }
     if (world.getBlockState(pos.east()) == waterUp) {
     System.out.println(pos.east());
     //removeWater(world, pos.east());
     }
     if (world.getBlockState(pos.north()) == waterUp) {
     System.out.println(pos.north());
     //removeWater(world, pos.north());
     }
     if (world.getBlockState(pos.south()) == waterUp) {
     System.out.println(pos.south());
     //removeWater(world, pos.south());
     }
     if (world.getBlockState(pos.west()) == waterUp) {
     System.out.println(pos.west());
     //removeWater(world, pos.west());
     }
}

А вот так срабатывает... Просто переместил одну строчку вниз:
Код:
private void removeWater(World world, BlockPos pos) {
     if (world.getBlockState(pos.down()) == waterUp) {
     System.out.println(pos.down());
     //removeWater(world, pos.down());
     }
     if (world.getBlockState(pos.east()) == waterUp) {
     System.out.println(pos.east());
     //removeWater(world, pos.east());
     }
     if (world.getBlockState(pos.north()) == waterUp) {
     System.out.println(pos.north());
     //removeWater(world, pos.north());
     }
     if (world.getBlockState(pos.south()) == waterUp) {
     System.out.println(pos.south());
     //removeWater(world, pos.south());
     }
     if (world.getBlockState(pos.west()) == waterUp) {
     System.out.println(pos.west());
     //removeWater(world, pos.west());
     }
 world.setBlockState(pos, Blocks.AIR.getDefaultState()); //удаляю в конце
 }

Что это может быть?
 
Назад
Сверху