From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Aikar Date: Mon, 6 Jul 2020 18:36:41 -0400 Subject: [PATCH] Fix Concurrency issue in WeightedList 1.17 Update: Looks like whatever this patch is trying to fix might be already fixed upstream, needs to be investigated if multiple threads from worldgen sort at same time, it will crash. So make a copy of the list for sorting purposes. diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java b/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java index 0a65e442ddc31c06f3bb0ff5aa152daee7a210af..a81ad258b39b7472312ab1bedeeacaf26ffae4f7 100644 --- a/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java +++ b/src/main/java/net/minecraft/world/entity/ai/behavior/GateBehavior.java @@ -17,7 +17,7 @@ public class GateBehavior extends Behavior { private final Set> exitErasedMemories; private final GateBehavior.OrderPolicy orderPolicy; private final GateBehavior.RunningPolicy runningPolicy; - private final WeightedList> behaviors = new WeightedList<>(); + private final WeightedList> behaviors = new WeightedList<>(false); // Paper - don't use a clone public GateBehavior(Map, MemoryStatus> requiredMemoryState, Set> memoriesToForgetWhenStopped, GateBehavior.OrderPolicy order, GateBehavior.RunningPolicy runMode, List, Integer>> tasks) { super(requiredMemoryState); @@ -65,10 +65,9 @@ public class GateBehavior extends Behavior { }).forEach((behavior) -> { behavior.g(world, entity, time); }); - Set set = this.exitErasedMemories; Brain behaviorcontroller = entity.getBrain(); - set.forEach(behaviorcontroller::removeMemory); + this.exitErasedMemories.forEach(behaviorcontroller::eraseMemory); // Paper - decomp fix } @Override @@ -111,11 +110,11 @@ public class GateBehavior extends Behavior { static enum OrderPolicy { ORDERED((weightedlist) -> { - }), SHUFFLED(WeightedList::a); + }), SHUFFLED(WeightedList::shuffle); private final Consumer> consumer; - private OrderPolicy(Consumer consumer) { + private OrderPolicy(Consumer> consumer) { // Paper - decomp fix this.consumer = consumer; } diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/WeightedList.java b/src/main/java/net/minecraft/world/entity/ai/behavior/WeightedList.java index e4c1c58e9a9c744c7ebb9948a27766b84a081b9e..85df30ef7c03c2f8ae741a8cac8bf601490d2539 100644 --- a/src/main/java/net/minecraft/world/entity/ai/behavior/WeightedList.java +++ b/src/main/java/net/minecraft/world/entity/ai/behavior/WeightedList.java @@ -6,7 +6,7 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import com.mojang.serialization.Dynamic; import com.mojang.serialization.DynamicOps; -import com.mojang.serialization.OptionalDynamic; + import java.util.Comparator; import java.util.List; import java.util.Random; @@ -14,26 +14,32 @@ import java.util.stream.Stream; public class WeightedList { - protected final List> entries; + protected final List> list; // Paper - decompile conflict private final Random random; + private final boolean isUnsafe; // Paper - public WeightedList() { - this(Lists.newArrayList()); + // Paper start - add useClone option + public WeightedList() { this(true); } + public WeightedList(boolean isUnsafe) { + this(Lists.newArrayList(), isUnsafe); } - private WeightedList(List> entries) { + private WeightedList(List> entries) { this(entries, true); } + private WeightedList(List> list, boolean isUnsafe) { + this.isUnsafe = isUnsafe; + // Paper end this.random = new Random(); - this.entries = Lists.newArrayList(entries); + this.list = Lists.newArrayList(list); // Paper - decompile conflict } public static Codec> codec(Codec codec) { - return WeightedList.entries.a(codec).listOf().xmap(WeightedList::new, (weightedlist) -> { - return weightedlist.a; + return WeightedList.WeightedEntry.codec(codec).listOf().xmap(WeightedList::new, (weightedlist) -> { // Paper - decompile conflict + return weightedlist.list; // Paper - decompile conflict }); } public WeightedList add(U item, int weight) { - this.entries.add(new WeightedList.WeightedEntry<>(item, weight)); + this.list.add(new WeightedList.WeightedEntry<>(item, weight)); // Paper - decompile conflict return this; } @@ -42,21 +48,20 @@ public class WeightedList { } public WeightedList shuffle(Random random) { - this.entries.forEach((weightedlist_a) -> { - weightedlist_a.setRandom(random.nextFloat()); - }); - this.entries.sort(Comparator.comparingDouble((object) -> { - return ((WeightedList.WeightedEntry) object).getRandWeight(); - })); - return this; + // Paper start - make concurrent safe, work off a clone of the list + List> list = isUnsafe ? new java.util.ArrayList>(this.list) : this.list; + list.forEach((weightedlist_a) -> weightedlist_a.setRandom(random.nextFloat())); + list.sort(Comparator.comparingDouble(WeightedEntry::getRandWeight)); + return isUnsafe ? new WeightedList<>(list, isUnsafe) : this; + // Paper end } public boolean isEmpty() { - return this.entries.isEmpty(); + return this.list.isEmpty(); // Paper - decompile conflict } public Stream stream() { - return this.entries.stream().map(WeightedList.entries::a); + return this.list.stream().map(WeightedList.WeightedEntry::getData); // Paper - decompile conflict } public U getOne(Random random) { @@ -64,7 +69,7 @@ public class WeightedList { } public String toString() { - return "WeightedList[" + this.entries + "]"; + return "WeightedList[" + this.list + "]"; // Paper - decompile conflict } public static class WeightedEntry { @@ -98,11 +103,7 @@ public class WeightedList { return new Codec>() { public DataResult, T>> decode(DynamicOps dynamicops, T t0) { Dynamic dynamic = new Dynamic(dynamicops, t0); - OptionalDynamic optionaldynamic = dynamic.get("data"); - Codec codec1 = codec; - - codec.getClass(); - return optionaldynamic.flatMap(codec1::parse).map((object) -> { + return dynamic.get("data").flatMap(codec::parse).map((object) -> { // Paper - decompile error return new WeightedList.WeightedEntry<>(object, dynamic.get("weight").asInt(1)); }).map((weightedlist_a) -> { return Pair.of(weightedlist_a, dynamicops.empty());