package com.irtimaled.bbor.common;
import com.irtimaled.bbor.Logger;
-import com.irtimaled.bbor.ReflectionHelper;
-import com.irtimaled.bbor.config.ConfigManager;
-import net.minecraft.world.DimensionType;
-import net.minecraft.world.World;
-import net.minecraft.world.chunk.Chunk;
-import net.minecraft.world.chunk.IChunkProvider;
-import net.minecraft.world.gen.ChunkProviderServer;
-import net.minecraft.world.gen.IChunkGenerator;
+import com.irtimaled.bbor.common.events.*;
+import com.irtimaled.bbor.common.messages.AddBoundingBox;
+import com.irtimaled.bbor.common.messages.InitializeClient;
+import com.irtimaled.bbor.common.messages.PayloadBuilder;
+import com.irtimaled.bbor.common.messages.RemoveBoundingBox;
+import com.irtimaled.bbor.common.models.AbstractBoundingBox;
+import com.irtimaled.bbor.common.models.ServerPlayer;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
public class CommonProxy {
- protected DimensionCache dimensionCache;
- protected Set<VillageProcessor> villageProcessors = new HashSet<>();
-
- private IVillageEventHandler eventHandler = null;
-
- public void worldLoaded(World world) {
- IChunkProvider chunkProvider = world.getChunkProvider();
- if (chunkProvider instanceof ChunkProviderServer) {
- IChunkGenerator chunkGenerator = ReflectionHelper.getPrivateValue(ChunkProviderServer.class, (ChunkProviderServer) chunkProvider, IChunkGenerator.class);
- dimensionCache.setWorldData(world.getSeed(), world.getWorldInfo().getSpawnX(), world.getWorldInfo().getSpawnZ());
- DimensionType dimensionType = world.provider.getDimensionType();
- Logger.info("create world dimension: %s, %s (chunkprovider: %s) (seed: %d)", dimensionType, world.getClass().toString(), chunkGenerator.getClass().toString(), world.getSeed());
- DimensionProcessor boundingBoxCache = new DimensionProcessor(dimensionType, chunkGenerator);
- dimensionCache.put(dimensionType, boundingBoxCache);
- if (ConfigManager.drawVillages.getBoolean()) {
- villageProcessors.add(new VillageProcessor(world, dimensionType, eventHandler, boundingBoxCache));
+ private final Map<Integer, ServerPlayer> players = new ConcurrentHashMap<>();
+ private final Map<Integer, Set<AbstractBoundingBox>> playerBoundingBoxesCache = new HashMap<>();
+ private final Map<Integer, VillageProcessor> villageProcessors = new HashMap<>();
+ private final Map<Integer, ChunkProcessor> chunkProcessors = new HashMap<>();
+ private final Map<Integer, BoundingBoxCache> dimensionCache = new ConcurrentHashMap<>();
+ private Long seed = null;
+ private Integer spawnX = null;
+ private Integer spawnZ = null;
+
+ public void init() {
+ EventBus.subscribe(WorldLoaded.class, this::worldLoaded);
+ EventBus.subscribe(ChunkLoaded.class, this::chunkLoaded);
+ EventBus.subscribe(PlayerLoggedIn.class, this::playerLoggedIn);
+ EventBus.subscribe(PlayerLoggedOut.class, this::playerLoggedOut);
+ EventBus.subscribe(PlayerSubscribed.class, this::onPlayerSubscribed);
+ EventBus.subscribe(ServerWorldTick.class, this::serverWorldTick);
+ EventBus.subscribe(ServerTick.class, e -> serverTick());
+ EventBus.subscribe(VillageRemoved.class, this::onVillageRemoved);
+ }
+
+ protected void setSeed(long seed) {
+ this.seed = seed;
+ }
+
+ protected void setWorldSpawn(int spawnX, int spawnZ) {
+ this.spawnX = spawnX;
+ this.spawnZ = spawnZ;
+ }
+
+ private void worldLoaded(WorldLoaded event) {
+ int dimensionId = event.getDimensionId();
+ long seed = event.getSeed();
+ if (dimensionId == Dimensions.OVERWORLD) {
+ setSeed(seed);
+ setWorldSpawn(event.getSpawnX(), event.getSpawnZ());
+ }
+ Logger.info("create world dimension: %s (seed: %d)", dimensionId, seed);
+ BoundingBoxCache boundingBoxCache = getOrCreateCache(dimensionId);
+ chunkProcessors.put(dimensionId, new ChunkProcessor(boundingBoxCache));
+ villageProcessors.put(dimensionId, new VillageProcessor(dimensionId, boundingBoxCache));
+ }
+
+ private void chunkLoaded(ChunkLoaded event) {
+ ChunkProcessor chunkProcessor = chunkProcessors.get(event.getDimensionId());
+ if (chunkProcessor == null) return;
+
+ chunkProcessor.process(event.getChunk());
+ }
+
+ private void playerLoggedIn(PlayerLoggedIn event) {
+ if (seed == null || spawnX == null || spawnZ == null) {
+ return;
+ }
+ ServerPlayer player = event.getPlayer();
+ player.sendPacket(InitializeClient.getPayload(seed, spawnX, spawnZ));
+ }
+
+ private void playerLoggedOut(PlayerLoggedOut event) {
+ int playerId = event.getPlayerId();
+ players.remove(playerId);
+ playerBoundingBoxesCache.remove(playerId);
+ }
+
+ private void onVillageRemoved(VillageRemoved event) {
+ sendRemoveBoundingBox(event.getDimensionId(), event.getVillage());
+ }
+
+ private void sendRemoveBoundingBox(int dimensionId, AbstractBoundingBox boundingBox) {
+ PayloadBuilder payload = RemoveBoundingBox.getPayload(dimensionId, boundingBox);
+ if (payload == null) return;
+
+ for (Map.Entry<Integer, ServerPlayer> playerEntry : players.entrySet()) {
+ int playerId = playerEntry.getKey();
+ ServerPlayer player = playerEntry.getValue();
+ if (player.getDimensionId() == dimensionId) {
+ player.sendPacket(payload);
+
+ if (playerBoundingBoxesCache.containsKey(playerId)) {
+ playerBoundingBoxesCache.get(playerId).remove(boundingBox);
+ }
}
}
}
- public void chunkLoaded(Chunk chunk) {
- DimensionType dimensionType = chunk.getWorld().provider.getDimensionType();
- dimensionCache.refresh(dimensionType);
+ private void onPlayerSubscribed(PlayerSubscribed event) {
+ int playerId = event.getPlayerId();
+ ServerPlayer player = event.getPlayer();
+ players.put(playerId, player);
+ sendToPlayer(playerId, player);
}
- public void tick() {
- villageProcessors.forEach(VillageProcessor::process);
+ private void sendToPlayer(int playerId, ServerPlayer player) {
+ for (Map.Entry<Integer, BoundingBoxCache> entry : dimensionCache.entrySet()) {
+ int dimensionId = entry.getKey();
+ BoundingBoxCache boundingBoxCache = entry.getValue();
+ if (boundingBoxCache == null) return;
+
+ Set<AbstractBoundingBox> playerBoundingBoxes = playerBoundingBoxesCache.computeIfAbsent(playerId, k -> new HashSet<>());
+
+ Map<AbstractBoundingBox, Set<AbstractBoundingBox>> boundingBoxMap = boundingBoxCache.getBoundingBoxes();
+ for (AbstractBoundingBox key : boundingBoxMap.keySet()) {
+ if (playerBoundingBoxes.contains(key)) {
+ continue;
+ }
+
+ Set<AbstractBoundingBox> boundingBoxes = boundingBoxMap.get(key);
+ PayloadBuilder payload = AddBoundingBox.getPayload(dimensionId, key, boundingBoxes);
+ if (payload != null)
+ player.sendPacket(payload);
+
+ playerBoundingBoxes.add(key);
+ }
+ }
}
- public void init() {
- dimensionCache = new DimensionCache();
+ protected void removeBoundingBox(int dimensionId, AbstractBoundingBox key) {
+ BoundingBoxCache cache = getCache(dimensionId);
+ if (cache == null) return;
+
+ cache.removeBoundingBox(key);
}
- public void setEventHandler(IVillageEventHandler eventHandler) {
- this.eventHandler = eventHandler;
+ private void serverTick() {
+ for (Map.Entry<Integer, ServerPlayer> playerEntry : players.entrySet()) {
+ int playerId = playerEntry.getKey();
+ ServerPlayer player = playerEntry.getValue();
+
+ sendToPlayer(playerId, player);
+ }
+ }
+
+ private void serverWorldTick(ServerWorldTick event) {
+ VillageProcessor villageProcessor = villageProcessors.get(event.getDimensionId());
+ if (villageProcessor == null) return;
+
+ villageProcessor.process(event.getWorld());
+ }
+
+ protected BoundingBoxCache getCache(int dimensionId) {
+ return dimensionCache.get(dimensionId);
+ }
+
+ protected BoundingBoxCache getOrCreateCache(int dimensionId) {
+ return dimensionCache.computeIfAbsent(dimensionId, dt -> new BoundingBoxCache());
}
- public DimensionCache getDimensionCache() {
- return dimensionCache;
+ protected void clearCaches() {
+ for (VillageProcessor villageProcessor : villageProcessors.values()) {
+ villageProcessor.clear();
+ }
+ villageProcessors.clear();
+ for (BoundingBoxCache cache : dimensionCache.values()) {
+ cache.clear();
+ }
+ dimensionCache.clear();
}
}