package com.irtimaled.bbor.common;
import com.irtimaled.bbor.Logger;
-import com.irtimaled.bbor.config.ConfigManager;
-import net.minecraft.world.World;
-import net.minecraft.world.chunk.Chunk;
-import net.minecraft.world.chunk.IChunkProvider;
-import net.minecraft.world.dimension.DimensionType;
-import net.minecraft.world.gen.ChunkProviderServer;
+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.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) {
- dimensionCache.setWorldData(world.getSeed(), world.getWorldInfo().getSpawnX(), world.getWorldInfo().getSpawnZ());
- DimensionType dimensionType = world.dimension.getType();
- Logger.info("create world dimension: %s, %s (seed: %d)", dimensionType, world.getClass().toString(), world.getSeed());
- DimensionProcessor boundingBoxCache = new DimensionProcessor(dimensionType);
- 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, StructureProcessor> structureProcessors = 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() {
+ BoundingBoxType.registerTypes();
+ EventBus.subscribe(WorldLoaded.class, this::worldLoaded);
+ EventBus.subscribe(StructuresLoaded.class, this::structuresLoaded);
+ EventBus.subscribe(PlayerLoggedIn.class, this::playerLoggedIn);
+ EventBus.subscribe(PlayerLoggedOut.class, this::playerLoggedOut);
+ EventBus.subscribe(PlayerSubscribed.class, this::onPlayerSubscribed);
+ EventBus.subscribe(ServerTick.class, e -> serverTick());
+ }
+
+ 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);
+ }
+
+ private void structuresLoaded(StructuresLoaded event) {
+ int dimensionId = event.getDimensionId();
+ StructureProcessor structureProcessor = getStructureProcessor(dimensionId);
+ structureProcessor.process(event.getStructures());
}
- public void chunkLoaded(Chunk chunk) {
- DimensionType dimensionType = chunk.getWorld().dimension.getType();
- BoundingBoxCache cache = dimensionCache.get(dimensionType);
- if(cache instanceof DimensionProcessor) {
- ((DimensionProcessor)cache).processChunk(chunk);
+ private StructureProcessor getStructureProcessor(int dimensionId) {
+ StructureProcessor structureProcessor = structureProcessors.get(dimensionId);
+ if (structureProcessor == null) {
+ structureProcessor = new StructureProcessor(getOrCreateCache(dimensionId));
+ structureProcessors.put(dimensionId, structureProcessor);
}
+ return structureProcessor;
}
- public void tick() {
- villageProcessors.forEach(VillageProcessor::process);
+ private void playerLoggedIn(PlayerLoggedIn event) {
+ if (seed == null || spawnX == null || spawnZ == null) {
+ return;
+ }
+ ServerPlayer player = event.getPlayer();
+ player.sendPacket(InitializeClient.getPayload(seed, spawnX, spawnZ));
}
- public void init() {
- dimensionCache = new DimensionCache();
+ private void playerLoggedOut(PlayerLoggedOut event) {
+ int playerId = event.getPlayerId();
+ players.remove(playerId);
+ playerBoundingBoxesCache.remove(playerId);
+ }
+
+ private void onPlayerSubscribed(PlayerSubscribed event) {
+ int playerId = event.getPlayerId();
+ ServerPlayer player = event.getPlayer();
+ players.put(playerId, player);
+ sendToPlayer(playerId, player);
}
- public void setEventHandler(IVillageEventHandler eventHandler) {
- this.eventHandler = eventHandler;
+ 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 DimensionCache getDimensionCache() {
- return dimensionCache;
+ private void serverTick() {
+ for (Map.Entry<Integer, ServerPlayer> playerEntry : players.entrySet()) {
+ int playerId = playerEntry.getKey();
+ ServerPlayer player = playerEntry.getValue();
+
+ sendToPlayer(playerId, player);
+ }
+ }
+
+ protected BoundingBoxCache getCache(int dimensionId) {
+ return dimensionCache.get(dimensionId);
+ }
+
+ protected BoundingBoxCache getOrCreateCache(int dimensionId) {
+ return dimensionCache.computeIfAbsent(dimensionId, dt -> new BoundingBoxCache());
+ }
+
+ protected void clearCaches() {
+ structureProcessors.clear();
+ for (BoundingBoxCache cache : dimensionCache.values()) {
+ cache.clear();
+ }
+ dimensionCache.clear();
}
}