1 package com.irtimaled.bbor.common;
3 import com.irtimaled.bbor.Logger;
4 import com.irtimaled.bbor.common.events.*;
5 import com.irtimaled.bbor.common.messages.AddBoundingBox;
6 import com.irtimaled.bbor.common.messages.InitializeClient;
7 import com.irtimaled.bbor.common.messages.PayloadBuilder;
8 import com.irtimaled.bbor.common.messages.RemoveBoundingBox;
9 import com.irtimaled.bbor.common.models.AbstractBoundingBox;
10 import com.irtimaled.bbor.common.models.ServerPlayer;
12 import java.util.HashMap;
13 import java.util.HashSet;
16 import java.util.concurrent.ConcurrentHashMap;
18 public class CommonProxy {
19 private final Map<Integer, ServerPlayer> players = new ConcurrentHashMap<>();
20 private final Map<Integer, Set<AbstractBoundingBox>> playerBoundingBoxesCache = new HashMap<>();
21 private final Map<Integer, VillageProcessor> villageProcessors = new HashMap<>();
22 private final Map<Integer, StructureProcessor> structureProcessors = new HashMap<>();
23 private final Map<Integer, BoundingBoxCache> dimensionCache = new ConcurrentHashMap<>();
24 private Long seed = null;
25 private Integer spawnX = null;
26 private Integer spawnZ = null;
29 BoundingBoxType.registerTypes();
30 EventBus.subscribe(WorldLoaded.class, this::worldLoaded);
31 EventBus.subscribe(StructuresLoaded.class, this::structuresLoaded);
32 EventBus.subscribe(PlayerLoggedIn.class, this::playerLoggedIn);
33 EventBus.subscribe(PlayerLoggedOut.class, this::playerLoggedOut);
34 EventBus.subscribe(PlayerSubscribed.class, this::onPlayerSubscribed);
35 EventBus.subscribe(ServerWorldTick.class, this::serverWorldTick);
36 EventBus.subscribe(ServerTick.class, e -> serverTick());
37 EventBus.subscribe(VillageRemoved.class, this::onVillageRemoved);
40 protected void setSeed(long seed) {
44 protected void setWorldSpawn(int spawnX, int spawnZ) {
49 private void worldLoaded(WorldLoaded event) {
50 int dimensionId = event.getDimensionId();
51 long seed = event.getSeed();
52 if (dimensionId == Dimensions.OVERWORLD) {
54 setWorldSpawn(event.getSpawnX(), event.getSpawnZ());
56 Logger.info("create world dimension: %s (seed: %d)", dimensionId, seed);
59 private void structuresLoaded(StructuresLoaded event) {
60 int dimensionId = event.getDimensionId();
61 StructureProcessor structureProcessor = getStructureProcessor(dimensionId);
62 structureProcessor.process(event.getStructures());
65 private StructureProcessor getStructureProcessor(int dimensionId) {
66 StructureProcessor structureProcessor = structureProcessors.get(dimensionId);
67 if (structureProcessor == null) {
68 structureProcessor = new StructureProcessor(getOrCreateCache(dimensionId));
69 structureProcessors.put(dimensionId, structureProcessor);
71 return structureProcessor;
74 private void playerLoggedIn(PlayerLoggedIn event) {
75 if (seed == null || spawnX == null || spawnZ == null) {
78 ServerPlayer player = event.getPlayer();
79 player.sendPacket(InitializeClient.getPayload(seed, spawnX, spawnZ));
82 private void playerLoggedOut(PlayerLoggedOut event) {
83 int playerId = event.getPlayerId();
84 players.remove(playerId);
85 playerBoundingBoxesCache.remove(playerId);
88 private void onVillageRemoved(VillageRemoved event) {
89 sendRemoveBoundingBox(event.getDimensionId(), event.getVillage());
92 private void sendRemoveBoundingBox(int dimensionId, AbstractBoundingBox boundingBox) {
93 PayloadBuilder payload = RemoveBoundingBox.getPayload(dimensionId, boundingBox);
94 if (payload == null) return;
96 for (Map.Entry<Integer, ServerPlayer> playerEntry : players.entrySet()) {
97 int playerId = playerEntry.getKey();
98 ServerPlayer player = playerEntry.getValue();
99 if (player.getDimensionId() == dimensionId) {
100 player.sendPacket(payload);
102 if (playerBoundingBoxesCache.containsKey(playerId)) {
103 playerBoundingBoxesCache.get(playerId).remove(boundingBox);
109 private void onPlayerSubscribed(PlayerSubscribed event) {
110 int playerId = event.getPlayerId();
111 ServerPlayer player = event.getPlayer();
112 players.put(playerId, player);
113 sendToPlayer(playerId, player);
116 private void sendToPlayer(int playerId, ServerPlayer player) {
117 for (Map.Entry<Integer, BoundingBoxCache> entry : dimensionCache.entrySet()) {
118 int dimensionId = entry.getKey();
119 BoundingBoxCache boundingBoxCache = entry.getValue();
120 if (boundingBoxCache == null) return;
122 Set<AbstractBoundingBox> playerBoundingBoxes = playerBoundingBoxesCache.computeIfAbsent(playerId, k -> new HashSet<>());
124 Map<AbstractBoundingBox, Set<AbstractBoundingBox>> boundingBoxMap = boundingBoxCache.getBoundingBoxes();
125 for (AbstractBoundingBox key : boundingBoxMap.keySet()) {
126 if (playerBoundingBoxes.contains(key)) {
130 Set<AbstractBoundingBox> boundingBoxes = boundingBoxMap.get(key);
131 PayloadBuilder payload = AddBoundingBox.getPayload(dimensionId, key, boundingBoxes);
133 player.sendPacket(payload);
135 playerBoundingBoxes.add(key);
140 protected void removeBoundingBox(int dimensionId, AbstractBoundingBox key) {
141 BoundingBoxCache cache = getCache(dimensionId);
142 if (cache == null) return;
144 cache.removeBoundingBox(key);
147 private void serverTick() {
148 for (Map.Entry<Integer, ServerPlayer> playerEntry : players.entrySet()) {
149 int playerId = playerEntry.getKey();
150 ServerPlayer player = playerEntry.getValue();
152 sendToPlayer(playerId, player);
156 private void serverWorldTick(ServerWorldTick event) {
157 int dimensionId = event.getDimensionId();
158 VillageProcessor villageProcessor = villageProcessors.get(dimensionId);
159 if (villageProcessor == null) {
160 villageProcessor = new VillageProcessor(dimensionId, getOrCreateCache(dimensionId));
161 villageProcessors.put(dimensionId, villageProcessor);
164 villageProcessor.process(event.getWorld());
167 protected BoundingBoxCache getCache(int dimensionId) {
168 return dimensionCache.get(dimensionId);
171 protected BoundingBoxCache getOrCreateCache(int dimensionId) {
172 return dimensionCache.computeIfAbsent(dimensionId, dt -> new BoundingBoxCache());
175 protected void clearCaches() {
176 for (VillageProcessor villageProcessor : villageProcessors.values()) {
177 villageProcessor.clear();
179 villageProcessors.clear();
180 structureProcessors.clear();
181 for (BoundingBoxCache cache : dimensionCache.values()) {
184 dimensionCache.clear();