Skip to content

Commit 333515f

Browse files
committed
fix: multiple ConcurrentModificationException in plugin
1 parent 3ed71d1 commit 333515f

4 files changed

Lines changed: 42 additions & 33 deletions

File tree

orebfuscator-core/src/main/java/dev/imprex/orebfuscator/proximity/ProximitySystem.java

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -45,33 +45,38 @@ public void start() {
4545
public void run() {
4646
long processStart = System.nanoTime();
4747
invokeProcess().whenComplete((v, throwable) -> {
48-
if (throwable != null) {
49-
OfcLogger.error("An error occurred while running proximity worker", throwable);
50-
}
51-
52-
if (this.executor.isShutdown()) {
53-
return;
54-
}
55-
56-
long processTime = System.nanoTime() - processStart;
57-
this.statistics.proximityProcess.add(processTime);
58-
59-
// check if we have enough time to sleep
60-
long waitTime = Math.max(0, this.checkInterval - processTime);
61-
long waitMillis = TimeUnit.NANOSECONDS.toMillis(waitTime);
62-
63-
if (waitMillis > 0) {
64-
// measure wait time
65-
this.statistics.proximityWait.add(TimeUnit.MILLISECONDS.toNanos(waitMillis));
66-
this.executor.schedule(this, waitMillis, TimeUnit.MILLISECONDS);
67-
} else {
68-
// Schedule instead of executing directly to break reentrant execution chains of
69-
// OrebfuscatorExecutor and avoid potential StackOverflowError.
70-
this.executor.schedule(this, 0L, TimeUnit.NANOSECONDS);
71-
}
72-
}).whenComplete((v, throwable) -> {
73-
if (throwable != null) {
74-
OfcLogger.error("An error occurred while running proximity worker, can't recover!", throwable);
48+
try {
49+
if (throwable != null) {
50+
OfcLogger.error("An error occurred while running proximity worker", throwable);
51+
}
52+
53+
if (this.executor.isShutdown()) {
54+
return;
55+
}
56+
57+
long processTime = System.nanoTime() - processStart;
58+
this.statistics.proximityProcess.add(processTime);
59+
60+
// check if we have enough time to sleep
61+
long waitTime = Math.max(0, this.checkInterval - processTime);
62+
long waitMillis = TimeUnit.NANOSECONDS.toMillis(waitTime);
63+
64+
if (waitMillis > 0) {
65+
// measure wait time
66+
this.statistics.proximityWait.add(TimeUnit.MILLISECONDS.toNanos(waitMillis));
67+
this.executor.schedule(this, waitMillis, TimeUnit.MILLISECONDS);
68+
} else {
69+
// Schedule instead of executing directly to break reentrant execution chains of
70+
// OrebfuscatorExecutor and avoid potential StackOverflowError.
71+
this.statistics.proximityWait.add(0);
72+
this.executor.schedule(this, 0L, TimeUnit.NANOSECONDS);
73+
}
74+
} catch (Exception e) {
75+
OfcLogger.error("An error occurred while scheduling proximity worker", e);
76+
77+
// backoff for 1sec on reschedule failure
78+
this.statistics.proximityWait.add(TimeUnit.SECONDS.toNanos(1));
79+
this.executor.schedule(this, 1L, TimeUnit.SECONDS);
7580
}
7681
});
7782
}

orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/iterop/BukkitPlayerAccessorManager.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
package net.imprex.orebfuscator.iterop;
22

3-
import dev.imprex.orebfuscator.interop.PlayerAccessor;
4-
import dev.imprex.orebfuscator.logging.OfcLogger;
53
import java.util.HashMap;
64
import java.util.List;
75
import java.util.Map;
8-
import net.imprex.orebfuscator.Orebfuscator;
6+
import java.util.concurrent.ConcurrentHashMap;
97
import org.bukkit.Bukkit;
108
import org.bukkit.entity.Player;
119
import org.bukkit.event.EventHandler;
@@ -17,11 +15,13 @@
1715
import org.bukkit.event.server.PluginDisableEvent;
1816
import org.jspecify.annotations.NullMarked;
1917
import org.jspecify.annotations.Nullable;
18+
import dev.imprex.orebfuscator.interop.PlayerAccessor;
19+
import net.imprex.orebfuscator.Orebfuscator;
2020

2121
@NullMarked
2222
public class BukkitPlayerAccessorManager implements Listener {
2323

24-
private final Map<Player, BukkitPlayerAccessor> players = new HashMap<>();
24+
private final Map<Player, BukkitPlayerAccessor> players = new ConcurrentHashMap<>();
2525

2626
private final Orebfuscator orebfuscator;
2727
private final BukkitWorldAccessorManager worldManager;

orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/iterop/BukkitWorldAccessorManager.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.HashMap;
55
import java.util.List;
66
import java.util.Map;
7+
import java.util.concurrent.ConcurrentHashMap;
78
import net.imprex.orebfuscator.Orebfuscator;
89
import org.bukkit.Bukkit;
910
import org.bukkit.World;
@@ -17,7 +18,7 @@
1718
@NullMarked
1819
public class BukkitWorldAccessorManager implements Listener {
1920

20-
private final Map<World, BukkitWorldAccessor> worlds = new HashMap<>();
21+
private final Map<World, BukkitWorldAccessor> worlds = new ConcurrentHashMap<>();
2122

2223
private final Orebfuscator orebfuscator;
2324

orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/util/WrappedClientboundLevelChunkPacketData.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
66
import com.comphenix.protocol.utility.MinecraftReflection;
77
import dev.imprex.orebfuscator.util.BlockPos;
8+
import java.util.ArrayList;
89
import java.util.Iterator;
910
import java.util.List;
1011
import java.util.function.Predicate;
@@ -43,7 +44,8 @@ public void setBuffer(byte[] buffer) {
4344
}
4445

4546
public void removeBlockEntityIf(Predicate<BlockPos> predicate) {
46-
List<?> blockEntities = (List<?>) BLOCK_ENTITIES.get(this.handle);
47+
// work on copy only to prevent ConcurrentModificationException
48+
List<?> blockEntities = new ArrayList<>((List<?>) BLOCK_ENTITIES.get(this.handle));
4749
for (Iterator<?> iterator = blockEntities.iterator(); iterator.hasNext(); ) {
4850
Object blockEntityInfo = iterator.next();
4951
int packedXZ = (int) PACKED_XZ.get(blockEntityInfo);
@@ -56,5 +58,6 @@ public void removeBlockEntityIf(Predicate<BlockPos> predicate) {
5658
iterator.remove();
5759
}
5860
}
61+
BLOCK_ENTITIES.set(this.handle, blockEntities);
5962
}
6063
}

0 commit comments

Comments
 (0)