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.models.AbstractBoundingBox;
9 import com.irtimaled.bbor.common.models.ServerPlayer;
11 import java.util.HashMap;
12 import java.util.HashSet;
15 import java.util.concurrent.ConcurrentHashMap;
17 public class CommonProxy {
18 private final Map<Integer, ServerPlayer> players = new ConcurrentHashMap<>();
19 private final Map<Integer, Set<AbstractBoundingBox>> playerBoundingBoxesCache = new HashMap<>();
20 private final Map<Integer, StructureProcessor> structureProcessors = new HashMap<>();
21 private final Map<Integer, BoundingBoxCache> dimensionCache = new ConcurrentHashMap<>();
22 private Long seed = null;
23 private Integer spawnX = null;
24 private Integer spawnZ = null;
27 BoundingBoxType.registerTypes();
28 EventBus.subscribe(WorldLoaded.class, this::worldLoaded);
29 EventBus.subscribe(StructuresLoaded.class, this::structuresLoaded);
30 EventBus.subscribe(PlayerLoggedIn.class, this::playerLoggedIn);
31 EventBus.subscribe(PlayerLoggedOut.class, this::playerLoggedOut);
32 EventBus.subscribe(PlayerSubscribed.class, this::onPlayerSubscribed);
33 EventBus.subscribe(ServerTick.class, e -> serverTick());
36 protected void setSeed(long seed) {
40 protected void setWorldSpawn(int spawnX, int spawnZ) {
45 private void worldLoaded(WorldLoaded event) {
46 int dimensionId = event.getDimensionId();
47 long seed = event.getSeed();
48 if (dimensionId == Dimensions.OVERWORLD) {
50 setWorldSpawn(event.getSpawnX(), event.getSpawnZ());
52 Logger.info("create world dimension: %s (seed: %d)", dimensionId, seed);
55 private void structuresLoaded(StructuresLoaded event) {
56 int dimensionId = event.getDimensionId();
57 StructureProcessor structureProcessor = getStructureProcessor(dimensionId);
58 structureProcessor.process(event.getStructures());
61 private StructureProcessor getStructureProcessor(int dimensionId) {
62 StructureProcessor structureProcessor = structureProcessors.get(dimensionId);
63 if (structureProcessor == null) {
64 structureProcessor = new StructureProcessor(getOrCreateCache(dimensionId));
65 structureProcessors.put(dimensionId, structureProcessor);
67 return structureProcessor;
70 private void playerLoggedIn(PlayerLoggedIn event) {
71 if (seed == null || spawnX == null || spawnZ == null) {
74 ServerPlayer player = event.getPlayer();
75 player.sendPacket(InitializeClient.getPayload(seed, spawnX, spawnZ));
78 private void playerLoggedOut(PlayerLoggedOut event) {
79 int playerId = event.getPlayerId();
80 players.remove(playerId);
81 playerBoundingBoxesCache.remove(playerId);
84 private void onPlayerSubscribed(PlayerSubscribed event) {
85 int playerId = event.getPlayerId();
86 ServerPlayer player = event.getPlayer();
87 players.put(playerId, player);
88 sendToPlayer(playerId, player);
91 private void sendToPlayer(int playerId, ServerPlayer player) {
92 for (Map.Entry<Integer, BoundingBoxCache> entry : dimensionCache.entrySet()) {
93 int dimensionId = entry.getKey();
94 BoundingBoxCache boundingBoxCache = entry.getValue();
95 if (boundingBoxCache == null) return;
97 Set<AbstractBoundingBox> playerBoundingBoxes = playerBoundingBoxesCache.computeIfAbsent(playerId, k -> new HashSet<>());
99 Map<AbstractBoundingBox, Set<AbstractBoundingBox>> boundingBoxMap = boundingBoxCache.getBoundingBoxes();
100 for (AbstractBoundingBox key : boundingBoxMap.keySet()) {
101 if (playerBoundingBoxes.contains(key)) {
105 Set<AbstractBoundingBox> boundingBoxes = boundingBoxMap.get(key);
106 PayloadBuilder payload = AddBoundingBox.getPayload(dimensionId, key, boundingBoxes);
108 player.sendPacket(payload);
110 playerBoundingBoxes.add(key);
115 private void serverTick() {
116 for (Map.Entry<Integer, ServerPlayer> playerEntry : players.entrySet()) {
117 int playerId = playerEntry.getKey();
118 ServerPlayer player = playerEntry.getValue();
120 sendToPlayer(playerId, player);
124 protected BoundingBoxCache getCache(int dimensionId) {
125 return dimensionCache.get(dimensionId);
128 protected BoundingBoxCache getOrCreateCache(int dimensionId) {
129 return dimensionCache.computeIfAbsent(dimensionId, dt -> new BoundingBoxCache());
132 protected void clearCaches() {
133 structureProcessors.clear();
134 for (BoundingBoxCache cache : dimensionCache.values()) {
137 dimensionCache.clear();