1 package com.irtimaled.bbor.common;
3 import com.irtimaled.bbor.Logger;
4 import com.irtimaled.bbor.common.chunkProcessors.ChunkProcessor;
5 import com.irtimaled.bbor.common.chunkProcessors.EndChunkProcessor;
6 import com.irtimaled.bbor.common.chunkProcessors.NetherChunkProcessor;
7 import com.irtimaled.bbor.common.chunkProcessors.OverworldChunkProcessor;
8 import com.irtimaled.bbor.common.events.*;
9 import com.irtimaled.bbor.common.messages.AddBoundingBox;
10 import com.irtimaled.bbor.common.messages.InitializeClient;
11 import com.irtimaled.bbor.common.messages.PayloadBuilder;
12 import com.irtimaled.bbor.common.messages.RemoveBoundingBox;
13 import com.irtimaled.bbor.common.models.*;
14 import net.minecraft.world.World;
15 import net.minecraft.world.WorldServer;
16 import net.minecraft.world.chunk.Chunk;
18 import java.util.HashMap;
19 import java.util.HashSet;
22 import java.util.concurrent.ConcurrentHashMap;
24 public class CommonProxy {
25 private Set<ServerPlayer> players = new HashSet<>();
26 private Map<ServerPlayer, Set<BoundingBox>> playerBoundingBoxesCache = new HashMap<>();
27 private Map<Integer, VillageProcessor> villageProcessors = new HashMap<>();
28 private Map<Integer, ChunkProcessor> chunkProcessors = new HashMap<>();
29 private WorldData worldData = null;
30 private final Map<Integer, BoundingBoxCache> dimensionCache = new ConcurrentHashMap<>();
33 EventBus.subscribe(WorldLoaded.class, e -> worldLoaded(e.getWorld()));
34 EventBus.subscribe(ChunkLoaded.class, e -> chunkLoaded(e.getChunk()));
35 EventBus.subscribe(MobSpawnerBroken.class, e -> mobSpawnerBroken(e.getDimensionId(), e.getPos()));
36 EventBus.subscribe(PlayerLoggedIn.class, e -> playerLoggedIn(e.getPlayer()));
37 EventBus.subscribe(PlayerLoggedOut.class, e -> playerLoggedOut(e.getPlayer()));
38 EventBus.subscribe(PlayerSubscribed.class, e -> sendBoundingBoxes(e.getPlayer()));
39 EventBus.subscribe(ServerWorldTick.class, e -> serverWorldTick(e.getWorld()));
40 EventBus.subscribe(ServerTick.class, e -> serverTick());
41 EventBus.subscribe(VillageRemoved.class, e -> sendRemoveBoundingBox(e.getDimensionId(), e.getVillage()));
44 protected void setWorldData(long seed, int spawnX, int spawnZ) {
45 worldData = new WorldData(seed, spawnX, spawnZ);
48 private void worldLoaded(World world) {
49 if (world instanceof WorldServer) {
50 int dimensionId = world.dimension.getType().getId();
51 BoundingBoxCache boundingBoxCache = getOrCreateCache(dimensionId);
52 ChunkProcessor chunkProcessor = null;
53 if (dimensionId == Dimensions.OVERWORLD) {
54 setWorldData(world.getSeed(), world.getWorldInfo().getSpawnX(), world.getWorldInfo().getSpawnZ());
55 chunkProcessor = new OverworldChunkProcessor(boundingBoxCache);
57 if (dimensionId == Dimensions.NETHER) {
58 chunkProcessor = new NetherChunkProcessor(boundingBoxCache);
60 if (dimensionId == Dimensions.THE_END) {
61 chunkProcessor = new EndChunkProcessor(boundingBoxCache);
63 Logger.info("create world dimension: %s, %s (seed: %d)", dimensionId, world.getClass().toString(), world.getSeed());
64 chunkProcessors.put(dimensionId, chunkProcessor);
65 villageProcessors.put(dimensionId, new VillageProcessor(dimensionId, boundingBoxCache));
69 private void chunkLoaded(Chunk chunk) {
70 int dimensionId = chunk.getWorld().dimension.getType().getId();
71 ChunkProcessor chunkProcessor = chunkProcessors.get(dimensionId);
72 if (chunkProcessor != null) {
73 chunkProcessor.process(chunk);
77 private void playerLoggedIn(ServerPlayer player) {
78 player.sendPacket(InitializeClient.getPayload(worldData));
81 private void playerLoggedOut(ServerPlayer player) {
82 players.remove(player);
83 playerBoundingBoxesCache.remove(player);
86 private void sendRemoveBoundingBox(int dimensionId, BoundingBox boundingBox) {
87 PayloadBuilder payload = RemoveBoundingBox.getPayload(dimensionId, boundingBox);
88 if (payload == null) return;
90 for (ServerPlayer player : players) {
91 if (player.getDimensionId() == dimensionId) {
92 player.sendPacket(payload);
94 if (playerBoundingBoxesCache.containsKey(player)) {
95 playerBoundingBoxesCache.get(player).remove(boundingBox);
101 private void sendBoundingBoxes(ServerPlayer player) {
103 sendToPlayer(player, getCache(player.getDimensionId()));
106 private void sendToPlayer(ServerPlayer player, BoundingBoxCache boundingBoxCache) {
107 if (boundingBoxCache == null) return;
109 Map<BoundingBox, Set<BoundingBox>> cacheSubset = getBoundingBoxMap(player, boundingBoxCache.getBoundingBoxes());
111 for (BoundingBox key : cacheSubset.keySet()) {
112 Set<BoundingBox> boundingBoxes = cacheSubset.get(key);
113 PayloadBuilder payload = AddBoundingBox.getPayload(player.getDimensionId(), key, boundingBoxes);
115 player.sendPacket(payload);
117 if (!playerBoundingBoxesCache.containsKey(player)) {
118 playerBoundingBoxesCache.put(player, new HashSet<>());
120 playerBoundingBoxesCache.get(player).add(key);
124 private Map<BoundingBox, Set<BoundingBox>> getBoundingBoxMap(ServerPlayer player, Map<BoundingBox, Set<BoundingBox>> boundingBoxMap) {
125 Map<BoundingBox, Set<BoundingBox>> cacheSubset = new HashMap<>();
126 for (BoundingBox key : boundingBoxMap.keySet()) {
127 if (!playerBoundingBoxesCache.containsKey(player) || !playerBoundingBoxesCache.get(player).contains(key)) {
128 cacheSubset.put(key, boundingBoxMap.get(key));
134 protected void addBoundingBox(int dimensionId, BoundingBox key, Set<BoundingBox> boundingBoxes) {
135 BoundingBoxCache cache = getCache(dimensionId);
136 if (cache == null) return;
138 cache.addBoundingBoxes(key, boundingBoxes);
141 protected void removeBoundingBox(int dimensionId, BoundingBox key) {
142 BoundingBoxCache cache = getCache(dimensionId);
143 if (cache == null) return;
145 cache.removeBoundingBox(key);
148 private void mobSpawnerBroken(int dimensionId, Coords pos) {
149 BoundingBox boundingBox = BoundingBoxMobSpawner.from(pos);
150 removeBoundingBox(dimensionId, boundingBox);
151 sendRemoveBoundingBox(dimensionId, boundingBox);
154 private void serverTick() {
155 for (ServerPlayer player : players) {
156 sendToPlayer(player, getCache(player.getDimensionId()));
160 private void serverWorldTick(WorldServer world) {
161 int dimensionId = world.dimension.getType().getId();
162 VillageProcessor villageProcessor = villageProcessors.get(dimensionId);
163 if(villageProcessor == null) return;
165 villageProcessor.process(world.getVillageCollection());
168 protected BoundingBoxCache getCache(int dimensionId) {
169 return dimensionCache.get(dimensionId);
172 protected BoundingBoxCache getOrCreateCache(int dimensionId) {
173 return dimensionCache.computeIfAbsent(dimensionId, dt -> new BoundingBoxCache());
176 protected void clearCaches() {
177 for(VillageProcessor villageProcessor : villageProcessors.values()) {
178 villageProcessor.clear();
180 villageProcessors.clear();
181 for (BoundingBoxCache cache : dimensionCache.values()) {
184 dimensionCache.clear();