1 package com.irtimaled.bbor.common;
3 import com.irtimaled.bbor.Logger;
4 import com.irtimaled.bbor.common.events.PlayerLoggedIn;
5 import com.irtimaled.bbor.common.events.PlayerLoggedOut;
6 import com.irtimaled.bbor.common.events.PlayerSubscribed;
7 import com.irtimaled.bbor.common.events.ServerTick;
8 import com.irtimaled.bbor.common.events.StructuresLoaded;
9 import com.irtimaled.bbor.common.events.WorldLoaded;
10 import com.irtimaled.bbor.common.messages.AddBoundingBox;
11 import com.irtimaled.bbor.common.messages.InitializeClient;
12 import com.irtimaled.bbor.common.messages.PayloadBuilder;
13 import com.irtimaled.bbor.common.models.AbstractBoundingBox;
14 import com.irtimaled.bbor.common.models.DimensionId;
15 import com.irtimaled.bbor.common.models.ServerPlayer;
17 import java.util.HashMap;
18 import java.util.HashSet;
21 import java.util.concurrent.ConcurrentHashMap;
23 public class CommonProxy {
24 private final Map<Integer, ServerPlayer> players = new ConcurrentHashMap<>();
25 private final Map<Integer, Set<AbstractBoundingBox>> playerBoundingBoxesCache = new HashMap<>();
26 private final Map<DimensionId, StructureProcessor> structureProcessors = new HashMap<>();
27 private final Map<DimensionId, BoundingBoxCache> dimensionCache = new ConcurrentHashMap<>();
28 private Long seed = null;
29 private Integer spawnX = null;
30 private Integer spawnZ = null;
33 BoundingBoxType.registerTypes();
34 EventBus.subscribe(WorldLoaded.class, this::worldLoaded);
35 EventBus.subscribe(StructuresLoaded.class, this::structuresLoaded);
36 EventBus.subscribe(PlayerLoggedIn.class, this::playerLoggedIn);
37 EventBus.subscribe(PlayerLoggedOut.class, this::playerLoggedOut);
38 EventBus.subscribe(PlayerSubscribed.class, this::onPlayerSubscribed);
39 EventBus.subscribe(ServerTick.class, e -> serverTick());
42 protected void setSeed(long seed) {
46 protected void setWorldSpawn(int spawnX, int spawnZ) {
51 private void worldLoaded(WorldLoaded event) {
52 DimensionId dimensionId = event.getDimensionId();
53 long seed = event.getSeed();
54 if (dimensionId == DimensionId.OVERWORLD) {
56 setWorldSpawn(event.getSpawnX(), event.getSpawnZ());
58 Logger.info("create world dimension: %s (seed: %d)", dimensionId, seed);
61 private void structuresLoaded(StructuresLoaded event) {
62 DimensionId dimensionId = event.getDimensionId();
63 StructureProcessor structureProcessor = getStructureProcessor(dimensionId);
64 structureProcessor.process(event.getStructures());
67 private StructureProcessor getStructureProcessor(DimensionId dimensionId) {
68 StructureProcessor structureProcessor = structureProcessors.get(dimensionId);
69 if (structureProcessor == null) {
70 structureProcessor = new StructureProcessor(getOrCreateCache(dimensionId));
71 structureProcessors.put(dimensionId, structureProcessor);
73 return structureProcessor;
76 private void playerLoggedIn(PlayerLoggedIn event) {
77 if (seed == null || spawnX == null || spawnZ == null) {
80 ServerPlayer player = event.getPlayer();
81 player.sendPacket(InitializeClient.getPayload(seed, spawnX, spawnZ));
84 private void playerLoggedOut(PlayerLoggedOut event) {
85 int playerId = event.getPlayerId();
86 players.remove(playerId);
87 playerBoundingBoxesCache.remove(playerId);
90 private void onPlayerSubscribed(PlayerSubscribed event) {
91 int playerId = event.getPlayerId();
92 ServerPlayer player = event.getPlayer();
93 players.put(playerId, player);
94 sendToPlayer(playerId, player);
97 private void sendToPlayer(int playerId, ServerPlayer player) {
98 for (Map.Entry<DimensionId, BoundingBoxCache> entry : dimensionCache.entrySet()) {
99 DimensionId dimensionId = entry.getKey();
100 BoundingBoxCache boundingBoxCache = entry.getValue();
101 if (boundingBoxCache == null) return;
103 Set<AbstractBoundingBox> playerBoundingBoxes = playerBoundingBoxesCache.computeIfAbsent(playerId, k -> new HashSet<>());
105 Map<AbstractBoundingBox, Set<AbstractBoundingBox>> boundingBoxMap = boundingBoxCache.getBoundingBoxes();
106 for (AbstractBoundingBox key : boundingBoxMap.keySet()) {
107 if (playerBoundingBoxes.contains(key)) {
111 Set<AbstractBoundingBox> boundingBoxes = boundingBoxMap.get(key);
112 PayloadBuilder payload = AddBoundingBox.getPayload(dimensionId, key, boundingBoxes);
114 player.sendPacket(payload);
116 playerBoundingBoxes.add(key);
121 private void serverTick() {
122 for (Map.Entry<Integer, ServerPlayer> playerEntry : players.entrySet()) {
123 int playerId = playerEntry.getKey();
124 ServerPlayer player = playerEntry.getValue();
126 sendToPlayer(playerId, player);
130 protected BoundingBoxCache getCache(DimensionId dimensionId) {
131 return dimensionCache.get(dimensionId);
134 protected BoundingBoxCache getOrCreateCache(DimensionId dimensionId) {
135 return dimensionCache.computeIfAbsent(dimensionId, dt -> new BoundingBoxCache());
138 protected void clearCaches() {
139 structureProcessors.clear();
140 for (BoundingBoxCache cache : dimensionCache.values()) {
143 dimensionCache.clear();