1 package com.irtimaled.bbor.common;
3 import com.irtimaled.bbor.Logger;
4 import com.irtimaled.bbor.common.chunkProcessors.AbstractChunkProcessor;
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.AbstractBoundingBox;
14 import com.irtimaled.bbor.common.models.BoundingBoxMobSpawner;
15 import com.irtimaled.bbor.common.models.ServerPlayer;
18 import java.util.concurrent.ConcurrentHashMap;
20 public class CommonProxy {
21 private Set<ServerPlayer> players = ConcurrentHashMap.newKeySet();
22 private Map<ServerPlayer, Set<AbstractBoundingBox>> playerBoundingBoxesCache = new HashMap<>();
23 private Map<Integer, VillageProcessor> villageProcessors = new HashMap<>();
24 private Map<Integer, AbstractChunkProcessor> chunkProcessors = new HashMap<>();
25 private final Map<Integer, BoundingBoxCache> dimensionCache = new ConcurrentHashMap<>();
26 private Long seed = null;
27 private Integer spawnX = null;
28 private Integer spawnZ = null;
31 EventBus.subscribe(WorldLoaded.class, this::worldLoaded);
32 EventBus.subscribe(ChunkLoaded.class, this::chunkLoaded);
33 EventBus.subscribe(MobSpawnerBroken.class, this::mobSpawnerBroken);
34 EventBus.subscribe(PlayerLoggedIn.class, this::playerLoggedIn);
35 EventBus.subscribe(PlayerLoggedOut.class, this::playerLoggedOut);
36 EventBus.subscribe(PlayerSubscribed.class, this::onPlayerSubscribed);
37 EventBus.subscribe(ServerWorldTick.class, this::serverWorldTick);
38 EventBus.subscribe(ServerTick.class, e -> serverTick());
39 EventBus.subscribe(VillageRemoved.class, this::onVillageRemoved);
42 protected void setSeed(long seed) {
46 protected void setWorldSpawn(int spawnX, int spawnZ) {
51 private void worldLoaded(WorldLoaded event) {
52 int dimensionId = event.getDimensionId();
53 long seed = event.getSeed();
54 BoundingBoxCache boundingBoxCache = getOrCreateCache(dimensionId);
55 AbstractChunkProcessor chunkProcessor = null;
56 switch (dimensionId) {
57 case Dimensions.OVERWORLD:
59 setWorldSpawn(event.getSpawnX(), event.getSpawnZ());
60 chunkProcessor = new OverworldChunkProcessor(boundingBoxCache);
62 case Dimensions.NETHER:
63 chunkProcessor = new NetherChunkProcessor(boundingBoxCache);
65 case Dimensions.THE_END:
66 chunkProcessor = new EndChunkProcessor(boundingBoxCache);
69 Logger.info("create world dimension: %s (seed: %d)", dimensionId, seed);
70 chunkProcessors.put(dimensionId, chunkProcessor);
71 villageProcessors.put(dimensionId, new VillageProcessor(dimensionId, boundingBoxCache));
74 private void chunkLoaded(ChunkLoaded event) {
75 AbstractChunkProcessor chunkProcessor = chunkProcessors.get(event.getDimensionId());
76 if (chunkProcessor == null) return;
78 chunkProcessor.process(event.getChunk());
81 private void playerLoggedIn(PlayerLoggedIn event) {
82 if (seed == null || spawnX == null || spawnZ == null) {
85 ServerPlayer player = event.getPlayer();
86 player.sendPacket(InitializeClient.getPayload(seed, spawnX, spawnZ));
89 private void playerLoggedOut(PlayerLoggedOut event) {
90 ServerPlayer player = event.getPlayer();
91 players.remove(player);
92 playerBoundingBoxesCache.remove(player);
95 private void onVillageRemoved(VillageRemoved event) {
96 sendRemoveBoundingBox(event.getDimensionId(), event.getVillage());
99 private void sendRemoveBoundingBox(int dimensionId, AbstractBoundingBox boundingBox) {
100 PayloadBuilder payload = RemoveBoundingBox.getPayload(dimensionId, boundingBox);
101 if (payload == null) return;
103 for (ServerPlayer player : players) {
104 if (player.getDimensionId() == dimensionId) {
105 player.sendPacket(payload);
107 if (playerBoundingBoxesCache.containsKey(player)) {
108 playerBoundingBoxesCache.get(player).remove(boundingBox);
114 private void onPlayerSubscribed(PlayerSubscribed event) {
115 ServerPlayer player = event.getPlayer();
117 sendToPlayer(player, getCache(player.getDimensionId()));
120 private void sendToPlayer(ServerPlayer player, BoundingBoxCache boundingBoxCache) {
121 if (boundingBoxCache == null) return;
123 Map<AbstractBoundingBox, Set<AbstractBoundingBox>> cacheSubset = getBoundingBoxMap(player, boundingBoxCache.getBoundingBoxes());
125 for (AbstractBoundingBox key : cacheSubset.keySet()) {
126 Set<AbstractBoundingBox> boundingBoxes = cacheSubset.get(key);
127 PayloadBuilder payload = AddBoundingBox.getPayload(player.getDimensionId(), key, boundingBoxes);
129 player.sendPacket(payload);
131 if (!playerBoundingBoxesCache.containsKey(player)) {
132 playerBoundingBoxesCache.put(player, new HashSet<>());
134 playerBoundingBoxesCache.get(player).add(key);
138 private Map<AbstractBoundingBox, Set<AbstractBoundingBox>> getBoundingBoxMap(ServerPlayer player, Map<AbstractBoundingBox, Set<AbstractBoundingBox>> boundingBoxMap) {
139 Map<AbstractBoundingBox, Set<AbstractBoundingBox>> cacheSubset = new HashMap<>();
140 for (AbstractBoundingBox key : boundingBoxMap.keySet()) {
141 if (!playerBoundingBoxesCache.containsKey(player) || !playerBoundingBoxesCache.get(player).contains(key)) {
142 cacheSubset.put(key, boundingBoxMap.get(key));
148 protected void removeBoundingBox(int dimensionId, AbstractBoundingBox key) {
149 BoundingBoxCache cache = getCache(dimensionId);
150 if (cache == null) return;
152 cache.removeBoundingBox(key);
155 private void mobSpawnerBroken(MobSpawnerBroken event) {
156 int dimensionId = event.getDimensionId();
157 AbstractBoundingBox boundingBox = BoundingBoxMobSpawner.from(event.getPos());
158 removeBoundingBox(dimensionId, boundingBox);
159 sendRemoveBoundingBox(dimensionId, boundingBox);
162 private void serverTick() {
163 for (ServerPlayer player : players) {
164 sendToPlayer(player, getCache(player.getDimensionId()));
168 private void serverWorldTick(ServerWorldTick event) {
169 VillageProcessor villageProcessor = villageProcessors.get(event.getDimensionId());
170 if (villageProcessor == null) return;
172 villageProcessor.process(event.getWorld());
175 protected BoundingBoxCache getCache(int dimensionId) {
176 return dimensionCache.get(dimensionId);
179 protected BoundingBoxCache getOrCreateCache(int dimensionId) {
180 return dimensionCache.computeIfAbsent(dimensionId, dt -> new BoundingBoxCache());
183 protected void clearCaches() {
184 for (VillageProcessor villageProcessor : villageProcessors.values()) {
185 villageProcessor.clear();
187 villageProcessors.clear();
188 for (BoundingBoxCache cache : dimensionCache.values()) {
191 dimensionCache.clear();