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.DimensionId;
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<DimensionId, StructureProcessor> structureProcessors = new HashMap<>();
22 private final Map<DimensionId, BoundingBoxCache> dimensionCache = new ConcurrentHashMap<>();
23 private Long seed = null;
24 private Integer spawnX = null;
25 private Integer spawnZ = null;
28 BoundingBoxType.registerTypes();
29 EventBus.subscribe(WorldLoaded.class, this::worldLoaded);
30 EventBus.subscribe(StructuresLoaded.class, this::structuresLoaded);
31 EventBus.subscribe(PlayerLoggedIn.class, this::playerLoggedIn);
32 EventBus.subscribe(PlayerLoggedOut.class, this::playerLoggedOut);
33 EventBus.subscribe(PlayerSubscribed.class, this::onPlayerSubscribed);
34 EventBus.subscribe(ServerTick.class, e -> serverTick());
37 protected void setSeed(long seed) {
41 protected void setWorldSpawn(int spawnX, int spawnZ) {
46 private void worldLoaded(WorldLoaded event) {
47 DimensionId dimensionId = event.getDimensionId();
48 long seed = event.getSeed();
49 if (dimensionId == DimensionId.OVERWORLD) {
51 setWorldSpawn(event.getSpawnX(), event.getSpawnZ());
53 Logger.info("create world dimension: %s (seed: %d)", dimensionId, seed);
56 private void structuresLoaded(StructuresLoaded event) {
57 DimensionId dimensionId = event.getDimensionId();
58 StructureProcessor structureProcessor = getStructureProcessor(dimensionId);
59 structureProcessor.process(event.getStructures());
62 private StructureProcessor getStructureProcessor(DimensionId dimensionId) {
63 StructureProcessor structureProcessor = structureProcessors.get(dimensionId);
64 if (structureProcessor == null) {
65 structureProcessor = new StructureProcessor(getOrCreateCache(dimensionId));
66 structureProcessors.put(dimensionId, structureProcessor);
68 return structureProcessor;
71 private void playerLoggedIn(PlayerLoggedIn event) {
72 if (seed == null || spawnX == null || spawnZ == null) {
75 ServerPlayer player = event.getPlayer();
76 player.sendPacket(InitializeClient.getPayload(seed, spawnX, spawnZ));
79 private void playerLoggedOut(PlayerLoggedOut event) {
80 int playerId = event.getPlayerId();
81 players.remove(playerId);
82 playerBoundingBoxesCache.remove(playerId);
85 private void onPlayerSubscribed(PlayerSubscribed event) {
86 int playerId = event.getPlayerId();
87 ServerPlayer player = event.getPlayer();
88 players.put(playerId, player);
89 sendToPlayer(playerId, player);
92 private void sendToPlayer(int playerId, ServerPlayer player) {
93 for (Map.Entry<DimensionId, BoundingBoxCache> entry : dimensionCache.entrySet()) {
94 DimensionId dimensionId = entry.getKey();
95 BoundingBoxCache boundingBoxCache = entry.getValue();
96 if (boundingBoxCache == null) return;
98 Set<AbstractBoundingBox> playerBoundingBoxes = playerBoundingBoxesCache.computeIfAbsent(playerId, k -> new HashSet<>());
100 Map<AbstractBoundingBox, Set<AbstractBoundingBox>> boundingBoxMap = boundingBoxCache.getBoundingBoxes();
101 for (AbstractBoundingBox key : boundingBoxMap.keySet()) {
102 if (playerBoundingBoxes.contains(key)) {
106 Set<AbstractBoundingBox> boundingBoxes = boundingBoxMap.get(key);
107 PayloadBuilder payload = AddBoundingBox.getPayload(dimensionId, key, boundingBoxes);
109 player.sendPacket(payload);
111 playerBoundingBoxes.add(key);
116 private void serverTick() {
117 for (Map.Entry<Integer, ServerPlayer> playerEntry : players.entrySet()) {
118 int playerId = playerEntry.getKey();
119 ServerPlayer player = playerEntry.getValue();
121 sendToPlayer(playerId, player);
125 protected BoundingBoxCache getCache(DimensionId dimensionId) {
126 return dimensionCache.get(dimensionId);
129 protected BoundingBoxCache getOrCreateCache(DimensionId dimensionId) {
130 return dimensionCache.computeIfAbsent(dimensionId, dt -> new BoundingBoxCache());
133 protected void clearCaches() {
134 structureProcessors.clear();
135 for (BoundingBoxCache cache : dimensionCache.values()) {
138 dimensionCache.clear();