]> git.lizzy.rs Git - BoundingBoxOutlineReloaded.git/blob - src/main/java/com/irtimaled/bbor/common/CommonProxy.java
Separate chunk processing from caching
[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.ChunkProcessor;
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.RemoveBoundingBox;
12 import com.irtimaled.bbor.common.models.BoundingBox;
13 import com.irtimaled.bbor.common.models.BoundingBoxMobSpawner;
14 import com.irtimaled.bbor.common.models.BoundingBoxVillage;
15 import com.irtimaled.bbor.config.ConfigManager;
16 import net.minecraft.entity.player.EntityPlayerMP;
17 import net.minecraft.network.play.server.SPacketCustomPayload;
18 import net.minecraft.util.math.BlockPos;
19 import net.minecraft.village.Village;
20 import net.minecraft.world.World;
21 import net.minecraft.world.chunk.Chunk;
22 import net.minecraft.world.chunk.IChunkProvider;
23 import net.minecraft.world.dimension.DimensionType;
24 import net.minecraft.world.gen.ChunkProviderServer;
25
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.Map;
29 import java.util.Set;
30 import java.util.concurrent.ConcurrentHashMap;
31
32 public class CommonProxy {
33     private Map<EntityPlayerMP, DimensionType> playerDimensions = new ConcurrentHashMap<>();
34     private Map<EntityPlayerMP, Set<BoundingBox>> playerBoundingBoxesCache = new HashMap<>();
35     private Map<Integer, BoundingBoxVillage> villageCache = new HashMap<>();
36     private Map<DimensionType, ChunkProcessor> chunkProcessors = new HashMap<>();
37
38     protected DimensionCache dimensionCache = new DimensionCache();
39
40     public void init() {
41         EventBus.subscribe(WorldLoaded.class, e -> worldLoaded(e.getWorld()));
42         EventBus.subscribe(ChunkLoaded.class, e -> chunkLoaded(e.getChunk()));
43         EventBus.subscribe(MobSpawnerBroken.class, e -> mobSpawnerBroken(e.getDimensionType(), e.getPos()));
44         EventBus.subscribe(PlayerChangedDimension.class, e -> playerChangedDimension(e.getPlayer()));
45         EventBus.subscribe(PlayerLoggedIn.class, e -> playerLoggedIn(e.getPlayer()));
46         EventBus.subscribe(PlayerLoggedOut.class, e -> playerLoggedOut(e.getPlayer()));
47         EventBus.subscribe(PlayerSubscribed.class, e -> sendBoundingBoxes(e.getPlayer()));
48         EventBus.subscribe(Tick.class, e -> tick());
49         if (ConfigManager.drawVillages.getBoolean()) {
50             EventBus.subscribe(VillageUpdated.class, e -> villageUpdated(e.getDimensionType(), e.getVillage()));
51         }
52     }
53
54     protected void setWorldData(long seed, int spawnX, int spawnZ) {
55         dimensionCache.setWorldData(seed, spawnX, spawnZ);
56     }
57
58     private void worldLoaded(World world) {
59         IChunkProvider chunkProvider = world.getChunkProvider();
60         if (chunkProvider instanceof ChunkProviderServer) {
61             DimensionType dimensionType = world.dimension.getType();
62             BoundingBoxCache boundingBoxCache = dimensionCache.getOrCreateCache(dimensionType);
63             ChunkProcessor chunkProcessor = null;
64             if (dimensionType == DimensionType.OVERWORLD) {
65                 setWorldData(world.getSeed(), world.getWorldInfo().getSpawnX(), world.getWorldInfo().getSpawnZ());
66                 chunkProcessor = new OverworldChunkProcessor(boundingBoxCache, world.getSeed());
67             }
68             if (dimensionType == DimensionType.NETHER) {
69                 chunkProcessor = new NetherChunkProcessor(boundingBoxCache);
70             }
71             if (dimensionType == DimensionType.THE_END) {
72                 chunkProcessor = new EndChunkProcessor(boundingBoxCache);
73             }
74             Logger.info("create world dimension: %s, %s (seed: %d)", dimensionType, world.getClass().toString(), world.getSeed());
75             chunkProcessors.put(dimensionType, chunkProcessor);
76         }
77     }
78
79     private void chunkLoaded(Chunk chunk) {
80         DimensionType dimensionType = chunk.getWorld().dimension.getType();
81         ChunkProcessor chunkProcessor = chunkProcessors.get(dimensionType);
82         if (chunkProcessor != null) {
83             chunkProcessor.process(chunk);
84         }
85     }
86
87     private void playerChangedDimension(EntityPlayerMP player) {
88         if (playerDimensions.containsKey(player)) {
89             sendBoundingBoxes(player);
90         }
91     }
92
93     private void playerLoggedIn(EntityPlayerMP player) {
94         player.connection.sendPacket(InitializeClient.getPayload(dimensionCache.getWorldData()));
95     }
96
97     private void playerLoggedOut(EntityPlayerMP player) {
98         playerDimensions.remove(player);
99         playerBoundingBoxesCache.remove(player);
100     }
101
102     private void sendRemoveBoundingBox(DimensionType dimensionType, BoundingBox boundingBox) {
103         SPacketCustomPayload payload = RemoveBoundingBox.getPayload(dimensionType, boundingBox);
104         for (EntityPlayerMP player : playerDimensions.keySet()) {
105             if (DimensionType.getById(player.dimension) == dimensionType) {
106                 Logger.info("remove 1 entry from %s (%s)", player.getScoreboardName(), dimensionType);
107                 player.connection.sendPacket(payload);
108
109                 if (playerBoundingBoxesCache.containsKey(player)) {
110                     playerBoundingBoxesCache.get(player).remove(boundingBox);
111                 }
112             }
113         }
114     }
115
116     private void sendBoundingBoxes(EntityPlayerMP player) {
117         DimensionType dimensionType = DimensionType.getById(player.dimension);
118         playerDimensions.put(player, dimensionType);
119         sendToPlayer(player, dimensionCache.getCache(dimensionType));
120     }
121
122     private void sendToPlayer(EntityPlayerMP player, BoundingBoxCache boundingBoxCache) {
123         if (boundingBoxCache == null)
124             return;
125         Map<BoundingBox, Set<BoundingBox>> cacheSubset = getBoundingBoxMap(player, boundingBoxCache.getBoundingBoxes());
126
127         DimensionType dimensionType = DimensionType.getById(player.dimension);
128         if (cacheSubset.keySet().size() > 0) {
129             Logger.info("send %d entries to %s (%s)", cacheSubset.keySet().size(), player.getScoreboardName(), dimensionType);
130         }
131
132         for (BoundingBox key : cacheSubset.keySet()) {
133             Set<BoundingBox> boundingBoxes = cacheSubset.get(key);
134             player.connection.sendPacket(AddBoundingBox.getPayload(dimensionType, key, boundingBoxes));
135
136             if (!playerBoundingBoxesCache.containsKey(player)) {
137                 playerBoundingBoxesCache.put(player, new HashSet<>());
138             }
139             playerBoundingBoxesCache.get(player).add(key);
140         }
141     }
142
143     private Map<BoundingBox, Set<BoundingBox>> getBoundingBoxMap(EntityPlayerMP player, Map<BoundingBox, Set<BoundingBox>> boundingBoxMap) {
144         Map<BoundingBox, Set<BoundingBox>> cacheSubset = new HashMap<>();
145         for (BoundingBox key : boundingBoxMap.keySet()) {
146             if (!playerBoundingBoxesCache.containsKey(player) || !playerBoundingBoxesCache.get(player).contains(key)) {
147                 cacheSubset.put(key, boundingBoxMap.get(key));
148             }
149         }
150         return cacheSubset;
151     }
152
153     protected void removeBoundingBox(DimensionType dimensionType, BoundingBox key) {
154         BoundingBoxCache cache = dimensionCache.getCache(dimensionType);
155         if (cache != null) {
156             cache.removeBoundingBox(key);
157         }
158     }
159
160     private void mobSpawnerBroken(DimensionType dimensionType, BlockPos pos) {
161         BoundingBox boundingBox = BoundingBoxMobSpawner.from(pos);
162         removeBoundingBox(dimensionType, boundingBox);
163         sendRemoveBoundingBox(dimensionType, boundingBox);
164     }
165
166     private void tick() {
167         for (EntityPlayerMP player : playerDimensions.keySet()) {
168             DimensionType dimensionType = playerDimensions.get(player);
169             sendToPlayer(player, dimensionCache.getCache(dimensionType));
170         }
171     }
172
173     private void villageUpdated(DimensionType dimensionType, Village village) {
174         BoundingBoxCache cache = dimensionCache.getCache(dimensionType);
175         if (cache == null) return;
176
177         int villageId = village.hashCode();
178         BoundingBoxVillage oldVillage = villageCache.get(villageId);
179         if (oldVillage != null && !oldVillage.matches(village)) {
180             cache.removeBoundingBox(oldVillage);
181             sendRemoveBoundingBox(dimensionType, oldVillage);
182             oldVillage = null;
183         }
184         if (village.isAnnihilated()) {
185             villageCache.remove(villageId);
186         } else {
187             BoundingBoxVillage newVillage = oldVillage == null ? BoundingBoxVillage.from(village) : oldVillage;
188             cache.addBoundingBox(newVillage);
189             villageCache.put(villageId, newVillage);
190         }
191     }
192
193     protected void clearCaches() {
194         villageCache.clear();
195         dimensionCache.clear();
196     }
197 }