Skip to content

Commit c982a5d

Browse files
committed
It works!
1 parent 870fa89 commit c982a5d

14 files changed

Lines changed: 650 additions & 410 deletions

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@
4242
<version>1.16.5-R0.1-SNAPSHOT</version>
4343
<scope>provided</scope>
4444
</dependency>
45+
<dependency>
46+
<groupId>io.netty</groupId>
47+
<artifactId>netty-all</artifactId>
48+
<version>4.1.25.Final</version>
49+
<scope>provided</scope>
50+
</dependency>
4551
<dependency>
4652
<groupId>it.unimi.dsi</groupId>
4753
<artifactId>fastutil</artifactId>

src/main/java/it/feargames/tileculling/occlusionculling/BlockChangeListener.java renamed to src/main/java/it/feargames/tileculling/ChunkCache.java

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
package it.feargames.tileculling.occlusionculling;
1+
package it.feargames.tileculling;
22

3-
import it.feargames.tileculling.CullingPlugin;
43
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
54
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
6-
import org.bukkit.Bukkit;
75
import org.bukkit.Chunk;
86
import org.bukkit.ChunkSnapshot;
97
import org.bukkit.World;
@@ -19,20 +17,23 @@
1917
import org.bukkit.event.world.ChunkLoadEvent;
2018
import org.bukkit.event.world.ChunkUnloadEvent;
2119
import org.bukkit.event.world.WorldUnloadEvent;
20+
import org.bukkit.scheduler.BukkitRunnable;
2221

2322
import java.util.*;
2423
import java.util.concurrent.locks.ReentrantReadWriteLock;
2524

26-
public class BlockChangeListener implements Listener {
25+
public class ChunkCache implements Listener {
2726

28-
private final Map<World, Long2ObjectMap<ChunkEntry>> cachedChunks = new HashMap<>();
27+
private final CullingPlugin plugin;
2928

29+
private final Map<World, Long2ObjectMap<ChunkEntry>> cachedChunks = new HashMap<>();
3030
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
3131
private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
3232
private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
3333

34-
public BlockChangeListener() {
35-
for (World world : Bukkit.getWorlds()) {
34+
public ChunkCache(CullingPlugin plugin) {
35+
this.plugin = plugin;
36+
for (World world : plugin.getServer().getWorlds()) {
3637
for (Chunk chunk : world.getLoadedChunks()) {
3738
updateCachedChunkSync(chunk.getWorld(), chunk.getChunkKey(), chunk);
3839
}
@@ -53,14 +54,24 @@ public void onWorldUnload(WorldUnloadEvent event) {
5354
public void onBlockPlace(BlockPlaceEvent e) {
5455
Chunk chunk = e.getBlock().getChunk();
5556
// We have to delay as tile entities are updated after the block has changed
56-
CullingPlugin.runTask(() -> updateCachedChunkSync(chunk.getWorld(), chunk.getChunkKey(), chunk));
57+
new BukkitRunnable() {
58+
@Override
59+
public void run() {
60+
updateCachedChunkSync(chunk.getWorld(), chunk.getChunkKey(), chunk);
61+
}
62+
}.runTask(plugin);
5763
}
5864

5965
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
6066
public void onBlockBreak(BlockBreakEvent e) {
6167
Chunk chunk = e.getBlock().getChunk();
6268
// We have to delay as tile entities are updated after the block has changed
63-
CullingPlugin.runTask(() -> updateCachedChunkSync(chunk.getWorld(), chunk.getChunkKey(), chunk));
69+
new BukkitRunnable() {
70+
@Override
71+
public void run() {
72+
updateCachedChunkSync(chunk.getWorld(), chunk.getChunkKey(), chunk);
73+
}
74+
}.runTask(plugin);
6475
}
6576

6677
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
@@ -120,11 +131,6 @@ private void updateCachedChunkSync(World world, long chunkKey, final Chunk chunk
120131
} finally {
121132
writeLock.unlock();
122133
}
123-
// Must be done on the main thread
124-
/*
125-
CullingPlugin.runTask(() -> {
126-
});
127-
*/
128134
}
129135

130136
private List<BlockState> filterTiles(BlockState[] tiles) {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package it.feargames.tileculling;
2+
3+
import org.bukkit.entity.Player;
4+
5+
import java.util.concurrent.ExecutorService;
6+
import java.util.concurrent.Executors;
7+
import java.util.concurrent.TimeUnit;
8+
9+
public class ChunkSeeder {
10+
11+
private final PlayerChunkTracker playerTracker;
12+
private final ChunkTileVisibilityManager chunkTileVisibilityManager;
13+
14+
private final ExecutorService seederExecutor = Executors.newFixedThreadPool(10);
15+
16+
public ChunkSeeder(PlayerChunkTracker playerTracker, ChunkTileVisibilityManager chunkTileVisibilityManager) {
17+
this.playerTracker = playerTracker;
18+
this.chunkTileVisibilityManager = chunkTileVisibilityManager;
19+
}
20+
21+
public void shutdown() {
22+
seederExecutor.shutdownNow();
23+
try {
24+
seederExecutor.awaitTermination(5, TimeUnit.SECONDS);
25+
} catch (InterruptedException ignored) {
26+
}
27+
}
28+
29+
public void seedChunk(Player player, long chunkKey) {
30+
seederExecutor.submit(() -> {
31+
chunkTileVisibilityManager.seedChunk(player, chunkKey);
32+
playerTracker.trackChunk(player, chunkKey);
33+
});
34+
}
35+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package it.feargames.tileculling;
2+
3+
import it.feargames.tileculling.adapter.IAdapter;
4+
import it.feargames.tileculling.occlusionculling.AxisAlignedBB;
5+
import it.feargames.tileculling.occlusionculling.OcclusionCulling;
6+
import org.bukkit.Chunk;
7+
import org.bukkit.Location;
8+
import org.bukkit.Material;
9+
import org.bukkit.World;
10+
import org.bukkit.block.BlockState;
11+
import org.bukkit.entity.Player;
12+
import org.bukkit.util.Vector;
13+
14+
import java.util.List;
15+
16+
public class ChunkTileVisibilityManager {
17+
18+
public static final int CHUNK_RANGE = 6; // TODO: makes no sense to have a chunk range, not this way at least
19+
public static final AxisAlignedBB BLOCK_AABB = new AxisAlignedBB(0d, 0d, 0d, 1d, 1d, 1d);
20+
21+
private final IAdapter adapter;
22+
private final PlayerChunkTracker playerTracker;
23+
private final VisibilityCache visibilityCache;
24+
private final ChunkCache chunkCache;
25+
26+
private final OcclusionCulling culling;
27+
28+
public ChunkTileVisibilityManager(IAdapter adapter, PlayerChunkTracker playerTracker, VisibilityCache visibilityCache, ChunkCache chunkCache) {
29+
this.adapter = adapter;
30+
this.playerTracker = playerTracker;
31+
this.visibilityCache = visibilityCache;
32+
this.chunkCache = chunkCache;
33+
34+
this.culling = new OcclusionCulling(chunkCache);
35+
}
36+
37+
public void updateVisibility(Player player) {
38+
World world = player.getWorld();
39+
Location playerLocation = player.getLocation();
40+
41+
Vector playerEyeLocation = player.getEyeLocation().toVector();
42+
43+
int playerChunkX = playerLocation.getBlockX() >> 4;
44+
int playerChunkZ = playerLocation.getBlockZ() >> 4;
45+
46+
culling.resetCache();
47+
48+
for (int x = -CHUNK_RANGE; x <= CHUNK_RANGE; x++) {
49+
for (int z = -CHUNK_RANGE; z <= CHUNK_RANGE; z++) {
50+
int chunkX = playerChunkX + x;
51+
int chunkZ = playerChunkZ + z;
52+
long chunkKey = Chunk.getChunkKey(chunkX, chunkZ);
53+
54+
if (!playerTracker.isChunkTracked(player, chunkKey)) {
55+
continue;
56+
}
57+
58+
List<BlockState> tiles = chunkCache.getChunkTiles(world, chunkKey);
59+
if (tiles == null) {
60+
continue;
61+
}
62+
for (BlockState block : tiles) {
63+
Location bloc = block.getLocation();
64+
boolean canSee = culling.isAABBVisible(player, playerEyeLocation, bloc, BLOCK_AABB);
65+
boolean hidden = visibilityCache.isHidden(player, block.getLocation());
66+
if (hidden && canSee) {
67+
visibilityCache.setHidden(player, block.getLocation(), false);
68+
adapter.updateBlockState(player, block.getLocation(), block.getBlockData());
69+
} else if (!hidden && !canSee) {
70+
visibilityCache.setHidden(player, block.getLocation(), true);
71+
adapter.updateBlockState(player, block.getLocation(), Material.AIR, (byte) 0);
72+
}
73+
}
74+
}
75+
}
76+
}
77+
78+
public void seedChunk(Player player, long chunkKey) {
79+
World world = player.getWorld();
80+
Vector playerEyeLocation = player.getEyeLocation().toVector();
81+
82+
List<BlockState> tiles = chunkCache.getChunkTiles(world, chunkKey);
83+
if (tiles == null) {
84+
// Should never happen, just in case
85+
return;
86+
}
87+
88+
for (BlockState block : tiles) {
89+
Location bloc = block.getLocation();
90+
boolean canSee = culling.isAABBVisible(player, playerEyeLocation, bloc, BLOCK_AABB);
91+
if (canSee) {
92+
visibilityCache.setHidden(player, block.getLocation(), false);
93+
player.sendBlockChange(block.getLocation(), block.getBlockData());
94+
} else {
95+
visibilityCache.setHidden(player, block.getLocation(), true);
96+
}
97+
}
98+
}
99+
}

src/main/java/it/feargames/tileculling/CullTask.java

Lines changed: 0 additions & 100 deletions
This file was deleted.

0 commit comments

Comments
 (0)