Skip to content

Commit 24e2ceb

Browse files
committed
feat: wip - add obfuscation to core package
1 parent 8a2911b commit 24e2ceb

File tree

58 files changed

+1524
-99
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+1524
-99
lines changed

orebfuscator-compatibility/orebfuscator-compatibility-api/src/main/java/net/imprex/orebfuscator/OrebfuscatorCompatibility.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@
88
import org.bukkit.plugin.Plugin;
99

1010
import dev.imprex.orebfuscator.config.api.Config;
11+
import dev.imprex.orebfuscator.interop.ChunkAccessor;
1112
import dev.imprex.orebfuscator.logging.OfcLogger;
12-
import dev.imprex.orebfuscator.util.ChunkCacheKey;
1313
import net.imprex.orebfuscator.compatibility.CompatibilityLayer;
14-
import net.imprex.orebfuscator.nms.ReadOnlyChunk;
1514
import net.imprex.orebfuscator.util.ServerVersion;
1615

1716
public class OrebfuscatorCompatibility {
@@ -64,8 +63,8 @@ public static void cancelTasks() {
6463
instance.getScheduler().cancelTasks();
6564
}
6665

67-
public static CompletableFuture<ReadOnlyChunk[]> getNeighboringChunks(World world, ChunkCacheKey key) {
68-
return instance.getNeighboringChunks(world, key);
66+
public static CompletableFuture<ChunkAccessor[]> getNeighboringChunks(World world, int chunkX, int chunkZ) {
67+
return instance.getNeighboringChunks(world, chunkX, chunkZ);
6968
}
7069

7170
public static void close() {

orebfuscator-compatibility/orebfuscator-compatibility-api/src/main/java/net/imprex/orebfuscator/compatibility/CompatibilityLayer.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@
44

55
import org.bukkit.World;
66

7-
import dev.imprex.orebfuscator.util.ChunkCacheKey;
8-
import net.imprex.orebfuscator.nms.ReadOnlyChunk;
7+
import dev.imprex.orebfuscator.interop.ChunkAccessor;
98

109
public interface CompatibilityLayer {
1110

1211
boolean isGameThread();
1312

1413
CompatibilityScheduler getScheduler();
1514

16-
CompletableFuture<ReadOnlyChunk[]> getNeighboringChunks(World world, ChunkCacheKey key);
15+
CompletableFuture<ChunkAccessor[]> getNeighboringChunks(World world, int chunkX, int chunkZ);
1716
}

orebfuscator-compatibility/orebfuscator-compatibility-bukkit/src/main/java/net/imprex/orebfuscator/compatibility/bukkit/BukkitChunkLoader.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@
1010
import org.bukkit.plugin.Plugin;
1111

1212
import dev.imprex.orebfuscator.config.api.Config;
13-
import dev.imprex.orebfuscator.util.ChunkCacheKey;
13+
import dev.imprex.orebfuscator.interop.ChunkAccessor;
1414
import dev.imprex.orebfuscator.util.ChunkDirection;
1515
import net.imprex.orebfuscator.OrebfuscatorNms;
16-
import net.imprex.orebfuscator.nms.ReadOnlyChunk;
1716

1817
public class BukkitChunkLoader implements Runnable {
1918

@@ -27,8 +26,8 @@ public BukkitChunkLoader(Plugin plugin, Config config) {
2726
Bukkit.getScheduler().runTaskTimer(plugin, this, 0, 1);
2827
}
2928

30-
public CompletableFuture<ReadOnlyChunk[]> submitRequest(World world, ChunkCacheKey key) {
31-
Request request = new Request(world, key);
29+
public CompletableFuture<ChunkAccessor[]> submitRequest(World world, int chunkX, int chunkZ) {
30+
Request request = new Request(world, chunkX, chunkZ);
3231
this.requests.offer(request);
3332
return request.future;
3433
}
@@ -46,22 +45,24 @@ public void run() {
4645
private class Request implements Runnable {
4746

4847
private final World world;
49-
private final ChunkCacheKey key;
48+
private final int chunkX;
49+
private final int chunkZ;
5050

51-
private final CompletableFuture<ReadOnlyChunk[]> future = new CompletableFuture<>();
51+
private final CompletableFuture<ChunkAccessor[]> future = new CompletableFuture<>();
5252

53-
public Request(World world, ChunkCacheKey key) {
53+
public Request(World world, int chunkX, int chunkZ) {
5454
this.world = world;
55-
this.key = key;
55+
this.chunkX = chunkX;
56+
this.chunkZ = chunkZ;
5657
}
5758

5859
@Override
5960
public void run() {
60-
final ReadOnlyChunk[] neighboringChunks = new ReadOnlyChunk[4];
61+
final ChunkAccessor[] neighboringChunks = new ChunkAccessor[4];
6162

6263
for (ChunkDirection direction : ChunkDirection.values()) {
63-
int chunkX = key.x() + direction.getOffsetX();
64-
int chunkZ = key.z() + direction.getOffsetZ();
64+
int chunkX = this.chunkX + direction.getOffsetX();
65+
int chunkZ = this.chunkZ + direction.getOffsetZ();
6566

6667
neighboringChunks[direction.ordinal()] = OrebfuscatorNms.getReadOnlyChunk(world, chunkX, chunkZ);
6768
}

orebfuscator-compatibility/orebfuscator-compatibility-bukkit/src/main/java/net/imprex/orebfuscator/compatibility/bukkit/BukkitCompatibilityLayer.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@
66
import org.bukkit.plugin.Plugin;
77

88
import dev.imprex.orebfuscator.config.api.Config;
9-
import dev.imprex.orebfuscator.util.ChunkCacheKey;
9+
import dev.imprex.orebfuscator.interop.ChunkAccessor;
1010
import net.imprex.orebfuscator.compatibility.CompatibilityLayer;
1111
import net.imprex.orebfuscator.compatibility.CompatibilityScheduler;
12-
import net.imprex.orebfuscator.nms.ReadOnlyChunk;
1312

1413
public class BukkitCompatibilityLayer implements CompatibilityLayer {
1514

@@ -34,7 +33,7 @@ public CompatibilityScheduler getScheduler() {
3433
}
3534

3635
@Override
37-
public CompletableFuture<ReadOnlyChunk[]> getNeighboringChunks(World world, ChunkCacheKey key) {
38-
return this.chunkLoader.submitRequest(world, key);
36+
public CompletableFuture<ChunkAccessor[]> getNeighboringChunks(World world, int chunkX, int chunkZ) {
37+
return this.chunkLoader.submitRequest(world, chunkX, chunkZ);
3938
}
4039
}

orebfuscator-compatibility/orebfuscator-compatibility-paper/src/main/java/net/imprex/orebfuscator/compatibility/paper/AbstractPaperCompatibilityLayer.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,25 @@
44

55
import org.bukkit.World;
66

7-
import dev.imprex.orebfuscator.util.ChunkCacheKey;
7+
import dev.imprex.orebfuscator.interop.ChunkAccessor;
88
import dev.imprex.orebfuscator.util.ChunkDirection;
99
import net.imprex.orebfuscator.OrebfuscatorNms;
1010
import net.imprex.orebfuscator.compatibility.CompatibilityLayer;
11-
import net.imprex.orebfuscator.nms.ReadOnlyChunk;
1211

1312
public abstract class AbstractPaperCompatibilityLayer implements CompatibilityLayer {
1413

1514
@Override
16-
public CompletableFuture<ReadOnlyChunk[]> getNeighboringChunks(World world, ChunkCacheKey key) {
15+
public CompletableFuture<ChunkAccessor[]> getNeighboringChunks(World world, int chunkX, int chunkZ) {
1716
CompletableFuture<?>[] futures = new CompletableFuture<?>[4];
18-
ReadOnlyChunk[] neighboringChunks = new ReadOnlyChunk[4];
17+
ChunkAccessor[] neighboringChunks = new ChunkAccessor[4];
1918

2019
for (ChunkDirection direction : ChunkDirection.values()) {
21-
int chunkX = key.x() + direction.getOffsetX();
22-
int chunkZ = key.z() + direction.getOffsetZ();
20+
int x = chunkX + direction.getOffsetX();
21+
int z = chunkX + direction.getOffsetZ();
2322
int index = direction.ordinal();
2423

2524
futures[index] = world.getChunkAtAsync(chunkX, chunkZ).thenAccept(chunk -> {
26-
neighboringChunks[index] = OrebfuscatorNms.getReadOnlyChunk(world, chunkX, chunkZ);
25+
neighboringChunks[index] = OrebfuscatorNms.getReadOnlyChunk(world, x, z);
2726
});
2827
}
2928

orebfuscator-core/pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@
5353
<version>${dependency.joml.version}</version>
5454
<scope>provided</scope>
5555
</dependency>
56-
<!--<dependency>
56+
<dependency>
5757
<groupId>org.lz4</groupId>
5858
<artifactId>lz4-java</artifactId>
5959
<version>${dependency.lz4.version}</version>
60-
<scope>compile</scope>
61-
</dependency>-->
60+
<scope>provided</scope>
61+
</dependency>
6262
</dependencies>
6363

6464
<build>
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
package dev.imprex.orebfuscator.cache;
2+
3+
import java.io.IOException;
4+
import java.util.HashMap;
5+
import java.util.LinkedList;
6+
import java.util.Map;
7+
import java.util.Queue;
8+
import java.util.concurrent.CompletableFuture;
9+
import java.util.concurrent.locks.Condition;
10+
import java.util.concurrent.locks.Lock;
11+
import java.util.concurrent.locks.ReentrantLock;
12+
import org.jetbrains.annotations.NotNull;
13+
import dev.imprex.orebfuscator.interop.OrebfuscatorCore;
14+
import dev.imprex.orebfuscator.logging.OfcLogger;
15+
import dev.imprex.orebfuscator.util.ChunkCacheKey;
16+
17+
/**
18+
* This class works similar to a bounded buffer for cache read and write requests but also functions
19+
* as the only consumer of said buffer. All requests can get reorder similar to modern memory access
20+
* reordering in CPUs. If for example a write request is already in the buffer and a new read
21+
* request for the same position is created then the read request doesn't get put in the buffer and
22+
* gets completed with the content of the write request.
23+
*
24+
* @see <a href="https://en.wikipedia.org/wiki/Producer–consumer_problem">Bound buffer</a>
25+
* @see <a href="https://en.wikipedia.org/wiki/Memory_ordering">Memory ordering</a>
26+
*/
27+
public class AsyncChunkSerializer implements Runnable {
28+
29+
private final Lock lock = new ReentrantLock(true);
30+
private final Condition notFull = lock.newCondition();
31+
private final Condition notEmpty = lock.newCondition();
32+
33+
private final Map<ChunkCacheKey, Runnable> tasks = new HashMap<>();
34+
private final Queue<ChunkCacheKey> positions = new LinkedList<>();
35+
36+
private final int maxTaskQueueSize;
37+
private final ChunkSerializer serializer;
38+
39+
private final Thread thread;
40+
private volatile boolean running = true;
41+
42+
public AsyncChunkSerializer(OrebfuscatorCore orebfuscator, AbstractRegionFileCache<?> regionFileCache) {
43+
this.maxTaskQueueSize = orebfuscator.config().cache().maximumTaskQueueSize();
44+
this.serializer = new ChunkSerializer(regionFileCache);
45+
46+
this.thread = new Thread(OrebfuscatorCore.THREAD_GROUP, this, "ofc-chunk-serializer");
47+
this.thread.setDaemon(true);
48+
this.thread.start();
49+
50+
orebfuscator.statistics().cache.setDiskCacheQueueLength(this.tasks::size);
51+
}
52+
53+
@NotNull
54+
public CompletableFuture<CacheChunkEntry> read(@NotNull ChunkCacheKey key) {
55+
this.lock.lock();
56+
try {
57+
Runnable task = this.tasks.get(key);
58+
if (task instanceof WriteTask) {
59+
return CompletableFuture.completedFuture(((WriteTask) task).chunk);
60+
} else if (task instanceof ReadTask) {
61+
return ((ReadTask) task).future;
62+
} else {
63+
CompletableFuture<CacheChunkEntry> future = new CompletableFuture<>();
64+
this.queueTask(key, new ReadTask(key, future));
65+
return future;
66+
}
67+
} finally {
68+
this.lock.unlock();
69+
}
70+
}
71+
72+
public void write(@NotNull ChunkCacheKey key, @NotNull CacheChunkEntry chunk) {
73+
this.lock.lock();
74+
try {
75+
Runnable prevTask = this.queueTask(key, new WriteTask(key, chunk));
76+
if (prevTask instanceof ReadTask) {
77+
((ReadTask) prevTask).future.complete(chunk);
78+
}
79+
} finally {
80+
this.lock.unlock();
81+
}
82+
}
83+
84+
@NotNull
85+
private Runnable queueTask(@NotNull ChunkCacheKey key, @NotNull Runnable nextTask) {
86+
while (this.positions.size() >= this.maxTaskQueueSize) {
87+
this.notFull.awaitUninterruptibly();
88+
}
89+
90+
if (!this.running) {
91+
throw new IllegalStateException("AsyncChunkSerializer already closed");
92+
}
93+
94+
Runnable prevTask = this.tasks.put(key, nextTask);
95+
if (prevTask == null) {
96+
this.positions.offer(key);
97+
}
98+
99+
this.notEmpty.signal();
100+
return prevTask;
101+
}
102+
103+
@Override
104+
public void run() {
105+
while (this.running) {
106+
this.lock.lock();
107+
try {
108+
while (this.positions.isEmpty()) {
109+
this.notEmpty.await();
110+
}
111+
112+
this.tasks.remove(this.positions.poll()).run();
113+
114+
this.notFull.signal();
115+
} catch (InterruptedException e) {
116+
break;
117+
} finally {
118+
this.lock.unlock();
119+
}
120+
}
121+
}
122+
123+
public void close() {
124+
this.lock.lock();
125+
try {
126+
this.running = false;
127+
this.thread.interrupt();
128+
129+
while (!this.positions.isEmpty()) {
130+
Runnable task = this.tasks.remove(this.positions.poll());
131+
if (task instanceof WriteTask) {
132+
task.run();
133+
}
134+
}
135+
} finally {
136+
this.lock.unlock();
137+
}
138+
}
139+
140+
private class WriteTask implements Runnable {
141+
142+
private final ChunkCacheKey key;
143+
private final CacheChunkEntry chunk;
144+
145+
public WriteTask(ChunkCacheKey key, CacheChunkEntry chunk) {
146+
this.key = key;
147+
this.chunk = chunk;
148+
}
149+
150+
@Override
151+
public void run() {
152+
try {
153+
serializer.write(key, chunk);
154+
} catch (IOException e) {
155+
OfcLogger.error(e);
156+
}
157+
}
158+
}
159+
160+
private class ReadTask implements Runnable {
161+
162+
private final ChunkCacheKey key;
163+
private final CompletableFuture<CacheChunkEntry> future;
164+
165+
public ReadTask(ChunkCacheKey key, CompletableFuture<CacheChunkEntry> future) {
166+
this.key = key;
167+
this.future = future;
168+
}
169+
170+
@Override
171+
public void run() {
172+
try {
173+
future.complete(serializer.read(key));
174+
} catch (IOException e) {
175+
future.completeExceptionally(e);
176+
}
177+
}
178+
}
179+
}

0 commit comments

Comments
 (0)