Game crashes sometimes when getting in range of a waterfall
Camo651 opened this issue ยท 0 comments
I'm running on 1.18 with optifabric and a sildurs shaders. Sometimes when I load in a waterfall in my server it crashes the whole game. It seems to be an issue at WaterfallCloudGenerators.tick(WaterfallCloudGenerators.java:30)
as seen in the crash log below.
This mod is super awesome and have been looking for something like it for a long time and would love to see it be polished up nicely.
[17:03:16] [Render thread/FATAL]: Unreported exception thrown!
java.util.ConcurrentModificationException: null
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1631) ~[?:?]
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) ~[?:?]
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[?:?]
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921) ~[?:?]
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[?:?]
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682) ~[?:?]
at ladysnake.effective.client.world.WaterfallCloudGenerators.tick(WaterfallCloudGenerators.java:30) ~[effective-1.0.jar:?]
at ladysnake.effective.client.Effective.lambda$onInitializeClient$2(Effective.java:121) ~[effective-1.0.jar:?]
at net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents.lambda$static$6(ClientTickEvents.java:111) ~[fabric-lifecycle-events-v1-1.4.10+c15ca33514-d830d5ce9751719.jar:?]
at net.minecraft.class_638.handler$zki000$tickWorldAfterBlockEntities(class_638.java:3100) ~[intermediary-fabric-loader-0.12.8-1.18.jar:?]
at net.minecraft.class_1937.method_18471(class_1937.java:483) ~[intermediary-fabric-loader-0.12.8-1.18.jar:?]
at net.minecraft.class_638.method_18116(class_638.java:251) ~[intermediary-fabric-loader-0.12.8-1.18.jar:?]
at net.minecraft.class_310.method_1574(class_310.java:1751) ~[intermediary-fabric-loader-0.12.8-1.18.jar:?]
at net.minecraft.class_310.method_1523(class_310.java:1086) ~[intermediary-fabric-loader-0.12.8-1.18.jar:?]
at net.minecraft.class_310.method_1514(class_310.java:733) [intermediary-fabric-loader-0.12.8-1.18.jar:?]
at net.minecraft.client.main.Main.main(Main.java:236) [intermediary-fabric-loader-0.12.8-1.18.jar:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[?:?]
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
at java.lang.reflect.Method.invoke(Method.java:568) ~[?:?]
at net.fabricmc.loader.impl.game.minecraft.MinecraftGameProvider.launch(MinecraftGameProvider.java:599) [fabric-loader-0.12.8.jar:?]
at net.fabricmc.loader.impl.launch.knot.Knot.launch(Knot.java:77) [fabric-loader-0.12.8.jar:?]
at net.fabricmc.loader.impl.launch.knot.KnotClient.main(KnotClient.java:23) [fabric-loader-0.12.8.jar:?]
[17:03:17] [Render thread/INFO]: Clearing audio channels
[17:03:17] [Render thread/INFO]: Stopping microphone thread
[17:03:17] [Render thread/INFO]: Disconnecting voicechat
[17:03:17] [Render thread/INFO]: [STDOUT]: Minimap session finalized.
[17:03:17] [Render thread/INFO]: Removing the schematic world...
[17:03:18] [Thread-1/INFO]: Minecraft instance shutting down, starting the Illuminations uninstaller
Edit: Looking at the source code at that error, I think I see what's going on. A waterfallCloudGenerator is being modified while the lambda expression on line 30 is being executed. This could probably be fixed by using a standard for loop instead such as:
List<WaterfallCloudGenerator> generatorsInDistance = new List<WaterfallCloudGenerator>();
for(int i=0; i<generator.size(); i++){
if(generators[i].world == MinecraftClient.getInstance().player.world &&
Math.sqrt(generators[i].blockPos.getSquaredDistance(MinecraftClient.getInstance().player.getBlockPos()))
<= MinecraftClient.getInstance().options.viewDistance * 8f){
generatorsInDistance.add(generators[i]);
}
}
There may be a good reason for it, but this looks sort of inefficient at first glance. All the generators in the view distance are being collected into a list, then that list is looped over as well to add the cloud effects to. Would it not be faster to simply loop over all the WaterfallGenerators in the main list 'generators' in one loop and check if it is within render distance all in one go rather than having multiple loops and lists to take up extra resources with. Something like this:
for (WaterfallCloudGenerator waterfallCloudGenerator : generatorsInDistance) {
if(generators[i].world == MinecraftClient.getInstance().player.world &&
Math.sqrt(generators[i].blockPos.getSquaredDistance(MinecraftClient.getInstance().player.getBlockPos()))
<= MinecraftClient.getInstance().options.viewDistance * 8f){
World world = waterfallCloudGenerator.world;
BlockPos blockPos = waterfallCloudGenerator.blockPos;
if (!(world.getBlockState(blockPos).getBlock() == Blocks.WATER && world.getBlockState(blockPos).getFluidState().isStill() && world.getBlockState(blockPos.add(0, 1, 0)).getBlock() == Blocks.WATER && !world.getBlockState(blockPos.add(0, 1, 0)).getFluidState().isStill() && world.getBlockState(blockPos.add(0, 1, 0)).getFluidState().getHeight() >= 0.77f)) {
generatorsToRemove.add(waterfallCloudGenerator);
}
if (world.random.nextInt(1) == 0 && world.getBlockState(blockPos).getBlock() == Blocks.WATER && world.getBlockState(blockPos).getFluidState().isStill() && world.getBlockState(blockPos.add(0, 1, 0)).getBlock() == Blocks.WATER && !world.getBlockState(blockPos.add(0, 1, 0)).getFluidState().isStill() && world.getBlockState(blockPos.add(0, 1, 0)).getFluidState().getHeight() >= 0.77f) {
double offsetX = world.random.nextGaussian() / 5f;
double offsetZ = world.random.nextGaussian() / 5f;
world.playSound(blockPos.getX(), blockPos.getY(), blockPos.getZ(), Effective.AMBIENCE_WATERFALL, SoundCategory.AMBIENT, 2.5f, 1.2f + world.random.nextFloat() / 10f, true);
world.addParticle(Effective.WATERFALL_CLOUD, blockPos.getX() + .5 + offsetX, blockPos.getY() + 1 + world.random.nextFloat(), blockPos.getZ() + .5 + offsetZ, world.random.nextFloat() / 5f * Math.signum(offsetX), world.random.nextFloat() / 5f, world.random.nextFloat() / 5f * Math.signum(offsetZ));
}
}
}