]> git.lizzy.rs Git - BoundingBoxOutlineReloaded.git/blob - src/main/java/com/irtimaled/bbor/common/CommonProxy.java
Stop set modification errors
[BoundingBoxOutlineReloaded.git] / src / main / java / com / irtimaled / bbor / common / CommonProxy.java
1 package com.irtimaled.bbor.common;
2
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;
17
18 import java.util.HashMap;
19 import java.util.HashSet;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.concurrent.ConcurrentHashMap;
23
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<>();
31
32     public void init() {
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);
42     }
43
44     protected void setWorldData(long seed, int spawnX, int spawnZ) {
45         worldData = new WorldData(seed, spawnX, spawnZ);
46     }
47
48     protected void setWorldSpawn(int spawnX, int spawnZ) {
49         worldData = new WorldData(worldData.getSeed(), spawnX, spawnZ);
50     }
51
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);
61                 break;
62             case Dimensions.NETHER:
63                 chunkProcessor = new NetherChunkProcessor(boundingBoxCache);
64                 break;
65             case Dimensions.THE_END:
66                 chunkProcessor = new EndChunkProcessor(boundingBoxCache);
67                 break;
68         }
69         Logger.info("create world dimension: %s (seed: %d)", dimensionId, seed);
70         chunkProcessors.put(dimensionId, chunkProcessor);
71         villageProcessors.put(dimensionId, new VillageProcessor(dimensionId, boundingBoxCache));
72     }
73
74     private void chunkLoaded(ChunkLoaded event) {
75         AbstractChunkProcessor chunkProcessor = chunkProcessors.get(event.getDimensionId());
76         if (chunkProcessor == null) return;
77
78         chunkProcessor.process(event.getChunk());
79     }
80
81     private void playerLoggedIn(PlayerLoggedIn event) {
82         ServerPlayer player = event.getPlayer();
83         player.sendPacket(InitializeClient.getPayload(worldData));
84     }
85
86     private void playerLoggedOut(PlayerLoggedOut event) {
87         ServerPlayer player = event.getPlayer();
88         players.remove(player);
89         playerBoundingBoxesCache.remove(player);
90     }
91
92     private void onVillageRemoved(VillageRemoved event) {
93         sendRemoveBoundingBox(event.getDimensionId(), event.getVillage());
94     }
95
96     private void sendRemoveBoundingBox(int dimensionId, AbstractBoundingBox boundingBox) {
97         PayloadBuilder payload = RemoveBoundingBox.getPayload(dimensionId, boundingBox);
98         if (payload == null) return;
99
100         for (ServerPlayer player : players) {
101             if (player.getDimensionId() == dimensionId) {
102                 player.sendPacket(payload);
103
104                 if (playerBoundingBoxesCache.containsKey(player)) {
105                     playerBoundingBoxesCache.get(player).remove(boundingBox);
106                 }
107             }
108         }
109     }
110
111     private void onPlayerSubscribed(PlayerSubscribed event) {
112         ServerPlayer player = event.getPlayer();
113         players.add(player);
114         sendToPlayer(player, getCache(player.getDimensionId()));
115     }
116
117     private void sendToPlayer(ServerPlayer player, BoundingBoxCache boundingBoxCache) {
118         if (boundingBoxCache == null) return;
119
120         Map<AbstractBoundingBox, Set<AbstractBoundingBox>> cacheSubset = getBoundingBoxMap(player, boundingBoxCache.getBoundingBoxes());
121
122         for (AbstractBoundingBox key : cacheSubset.keySet()) {
123             Set<AbstractBoundingBox> boundingBoxes = cacheSubset.get(key);
124             PayloadBuilder payload = AddBoundingBox.getPayload(player.getDimensionId(), key, boundingBoxes);
125             if (payload != null)
126                 player.sendPacket(payload);
127
128             if (!playerBoundingBoxesCache.containsKey(player)) {
129                 playerBoundingBoxesCache.put(player, new HashSet<>());
130             }
131             playerBoundingBoxesCache.get(player).add(key);
132         }
133     }
134
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));
140             }
141         }
142         return cacheSubset;
143     }
144
145     protected void removeBoundingBox(int dimensionId, AbstractBoundingBox key) {
146         BoundingBoxCache cache = getCache(dimensionId);
147         if (cache == null) return;
148
149         cache.removeBoundingBox(key);
150     }
151
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);
157     }
158
159     private void serverTick() {
160         for (ServerPlayer player : players) {
161             sendToPlayer(player, getCache(player.getDimensionId()));
162         }
163     }
164
165     private void serverWorldTick(ServerWorldTick event) {
166         VillageProcessor villageProcessor = villageProcessors.get(event.getDimensionId());
167         if (villageProcessor == null) return;
168
169         villageProcessor.process(event.getWorld());
170     }
171
172     protected BoundingBoxCache getCache(int dimensionId) {
173         return dimensionCache.get(dimensionId);
174     }
175
176     protected BoundingBoxCache getOrCreateCache(int dimensionId) {
177         return dimensionCache.computeIfAbsent(dimensionId, dt -> new BoundingBoxCache());
178     }
179
180     protected void clearCaches() {
181         for (VillageProcessor villageProcessor : villageProcessors.values()) {
182             villageProcessor.clear();
183         }
184         villageProcessors.clear();
185         for (BoundingBoxCache cache : dimensionCache.values()) {
186             cache.clear();
187         }
188         dimensionCache.clear();
189     }
190 }