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;
16 import com.irtimaled.bbor.common.models.WorldData;
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 = ConcurrentHashMap.newKeySet();
26 private Map<ServerPlayer, Set<AbstractBoundingBox>> playerBoundingBoxesCache = new HashMap<>();
27 private Map<Integer, VillageProcessor> villageProcessors = new HashMap<>();
28 private Map<Integer, AbstractChunkProcessor> chunkProcessors = new HashMap<>();
29 private WorldData worldData = null;
30 private final Map<Integer, BoundingBoxCache> dimensionCache = new ConcurrentHashMap<>();
33 EventBus.subscribe(WorldLoaded.class, this::worldLoaded);
34 EventBus.subscribe(ChunkLoaded.class, this::chunkLoaded);
35 EventBus.subscribe(MobSpawnerBroken.class, this::mobSpawnerBroken);
36 EventBus.subscribe(PlayerLoggedIn.class, this::playerLoggedIn);
37 EventBus.subscribe(PlayerLoggedOut.class, this::playerLoggedOut);
38 EventBus.subscribe(PlayerSubscribed.class, this::onPlayerSubscribed);
39 EventBus.subscribe(ServerWorldTick.class, this::serverWorldTick);
40 EventBus.subscribe(ServerTick.class, e -> serverTick());
41 EventBus.subscribe(VillageRemoved.class, this::onVillageRemoved);
44 protected void setWorldData(long seed, int spawnX, int spawnZ) {
45 worldData = new WorldData(seed, spawnX, spawnZ);
48 protected void setWorldSpawn(int spawnX, int spawnZ) {
49 worldData = new WorldData(worldData.getSeed(), spawnX, spawnZ);
52 private void worldLoaded(WorldLoaded event) {
53 int dimensionId = event.getDimensionId();
54 long seed = event.getSeed();
55 BoundingBoxCache boundingBoxCache = getOrCreateCache(dimensionId);
56 AbstractChunkProcessor chunkProcessor = null;
57 switch (dimensionId) {
58 case Dimensions.OVERWORLD:
59 setWorldData(seed, 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 ServerPlayer player = event.getPlayer();
83 player.sendPacket(InitializeClient.getPayload(worldData));
86 private void playerLoggedOut(PlayerLoggedOut event) {
87 ServerPlayer player = event.getPlayer();
88 players.remove(player);
89 playerBoundingBoxesCache.remove(player);
92 private void onVillageRemoved(VillageRemoved event) {
93 sendRemoveBoundingBox(event.getDimensionId(), event.getVillage());
96 private void sendRemoveBoundingBox(int dimensionId, AbstractBoundingBox boundingBox) {
97 PayloadBuilder payload = RemoveBoundingBox.getPayload(dimensionId, boundingBox);
98 if (payload == null) return;
100 for (ServerPlayer player : players) {
101 if (player.getDimensionId() == dimensionId) {
102 player.sendPacket(payload);
104 if (playerBoundingBoxesCache.containsKey(player)) {
105 playerBoundingBoxesCache.get(player).remove(boundingBox);
111 private void onPlayerSubscribed(PlayerSubscribed event) {
112 ServerPlayer player = event.getPlayer();
114 sendToPlayer(player, getCache(player.getDimensionId()));
117 private void sendToPlayer(ServerPlayer player, BoundingBoxCache boundingBoxCache) {
118 if (boundingBoxCache == null) return;
120 Map<AbstractBoundingBox, Set<AbstractBoundingBox>> cacheSubset = getBoundingBoxMap(player, boundingBoxCache.getBoundingBoxes());
122 for (AbstractBoundingBox key : cacheSubset.keySet()) {
123 Set<AbstractBoundingBox> boundingBoxes = cacheSubset.get(key);
124 PayloadBuilder payload = AddBoundingBox.getPayload(player.getDimensionId(), key, boundingBoxes);
126 player.sendPacket(payload);
128 if (!playerBoundingBoxesCache.containsKey(player)) {
129 playerBoundingBoxesCache.put(player, new HashSet<>());
131 playerBoundingBoxesCache.get(player).add(key);
135 private Map<AbstractBoundingBox, Set<AbstractBoundingBox>> getBoundingBoxMap(ServerPlayer player, Map<AbstractBoundingBox, Set<AbstractBoundingBox>> boundingBoxMap) {
136 Map<AbstractBoundingBox, Set<AbstractBoundingBox>> cacheSubset = new HashMap<>();
137 for (AbstractBoundingBox key : boundingBoxMap.keySet()) {
138 if (!playerBoundingBoxesCache.containsKey(player) || !playerBoundingBoxesCache.get(player).contains(key)) {
139 cacheSubset.put(key, boundingBoxMap.get(key));
145 protected void removeBoundingBox(int dimensionId, AbstractBoundingBox key) {
146 BoundingBoxCache cache = getCache(dimensionId);
147 if (cache == null) return;
149 cache.removeBoundingBox(key);
152 private void mobSpawnerBroken(MobSpawnerBroken event) {
153 int dimensionId = event.getDimensionId();
154 AbstractBoundingBox boundingBox = BoundingBoxMobSpawner.from(event.getPos());
155 removeBoundingBox(dimensionId, boundingBox);
156 sendRemoveBoundingBox(dimensionId, boundingBox);
159 private void serverTick() {
160 for (ServerPlayer player : players) {
161 sendToPlayer(player, getCache(player.getDimensionId()));
165 private void serverWorldTick(ServerWorldTick event) {
166 VillageProcessor villageProcessor = villageProcessors.get(event.getDimensionId());
167 if (villageProcessor == null) return;
169 villageProcessor.process(event.getWorld());
172 protected BoundingBoxCache getCache(int dimensionId) {
173 return dimensionCache.get(dimensionId);
176 protected BoundingBoxCache getOrCreateCache(int dimensionId) {
177 return dimensionCache.computeIfAbsent(dimensionId, dt -> new BoundingBoxCache());
180 protected void clearCaches() {
181 for (VillageProcessor villageProcessor : villageProcessors.values()) {
182 villageProcessor.clear();
184 villageProcessors.clear();
185 for (BoundingBoxCache cache : dimensionCache.values()) {
188 dimensionCache.clear();