package com.irtimaled.bbor.common;
import com.irtimaled.bbor.Logger;
-import com.irtimaled.bbor.common.chunkProcessors.ChunkProcessor;
-import com.irtimaled.bbor.common.chunkProcessors.EndChunkProcessor;
-import com.irtimaled.bbor.common.chunkProcessors.NetherChunkProcessor;
-import com.irtimaled.bbor.common.chunkProcessors.OverworldChunkProcessor;
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.RemoveBoundingBox;
-import com.irtimaled.bbor.common.models.BoundingBox;
-import com.irtimaled.bbor.common.models.BoundingBoxMobSpawner;
-import com.irtimaled.bbor.common.models.BoundingBoxVillage;
-import com.irtimaled.bbor.common.models.WorldData;
-import com.irtimaled.bbor.config.ConfigManager;
-import io.netty.channel.local.LocalAddress;
-import net.minecraft.entity.player.EntityPlayerMP;
-import net.minecraft.network.play.server.SPacketCustomPayload;
-import net.minecraft.util.math.BlockPos;
-import net.minecraft.village.Village;
-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.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;
-import java.util.function.Consumer;
public class CommonProxy {
- private Set<EntityPlayerMP> players = new HashSet<>();
- private Map<EntityPlayerMP, Set<BoundingBox>> playerBoundingBoxesCache = new HashMap<>();
- private Map<Integer, BoundingBoxVillage> villageCache = new HashMap<>();
- private Map<DimensionType, ChunkProcessor> chunkProcessors = new HashMap<>();
- private WorldData worldData = null;
- private final Map<DimensionType, BoundingBoxCache> dimensionCache = new ConcurrentHashMap<>();
+ 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() {
- EventBus.subscribe(WorldLoaded.class, e -> worldLoaded(e.getWorld()));
- EventBus.subscribe(ChunkLoaded.class, e -> chunkLoaded(e.getChunk()));
- EventBus.subscribe(MobSpawnerBroken.class, e -> mobSpawnerBroken(e.getDimensionType(), e.getPos()));
- EventBus.subscribe(PlayerLoggedIn.class, e -> playerLoggedIn(e.getPlayer()));
- EventBus.subscribe(PlayerLoggedOut.class, e -> playerLoggedOut(e.getPlayer()));
- EventBus.subscribe(PlayerSubscribed.class, e -> sendBoundingBoxes(e.getPlayer()));
- EventBus.subscribe(Tick.class, e -> tick());
- if (ConfigManager.drawVillages.get()) {
- EventBus.subscribe(VillageUpdated.class, e -> villageUpdated(e.getDimensionType(), e.getVillage()));
- }
+ 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 setWorldData(long seed, int spawnX, int spawnZ) {
- worldData = new WorldData(seed, spawnX, spawnZ);
+ protected void setSeed(long seed) {
+ this.seed = seed;
}
- private void worldLoaded(World world) {
- IChunkProvider chunkProvider = world.getChunkProvider();
- if (chunkProvider instanceof ChunkProviderServer) {
- DimensionType dimensionType = world.dimension.getType();
- BoundingBoxCache boundingBoxCache = getOrCreateCache(dimensionType);
- ChunkProcessor chunkProcessor = null;
- if (dimensionType == DimensionType.OVERWORLD) {
- setWorldData(world.getSeed(), world.getWorldInfo().getSpawnX(), world.getWorldInfo().getSpawnZ());
- chunkProcessor = new OverworldChunkProcessor(boundingBoxCache);
- }
- if (dimensionType == DimensionType.NETHER) {
- chunkProcessor = new NetherChunkProcessor(boundingBoxCache);
- }
- if (dimensionType == DimensionType.THE_END) {
- chunkProcessor = new EndChunkProcessor(boundingBoxCache);
- }
- Logger.info("create world dimension: %s, %s (seed: %d)", dimensionType, world.getClass().toString(), world.getSeed());
- chunkProcessors.put(dimensionType, chunkProcessor);
- }
+ protected void setWorldSpawn(int spawnX, int spawnZ) {
+ this.spawnX = spawnX;
+ this.spawnZ = spawnZ;
}
- private void chunkLoaded(Chunk chunk) {
- DimensionType dimensionType = chunk.getWorld().dimension.getType();
- ChunkProcessor chunkProcessor = chunkProcessors.get(dimensionType);
- if (chunkProcessor != null) {
- chunkProcessor.process(chunk);
+ 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 playerLoggedIn(EntityPlayerMP player) {
- if (player.connection.netManager.getRemoteAddress() instanceof LocalAddress) return;
- player.connection.sendPacket(InitializeClient.getPayload(worldData));
+ private void structuresLoaded(StructuresLoaded event) {
+ int dimensionId = event.getDimensionId();
+ StructureProcessor structureProcessor = getStructureProcessor(dimensionId);
+ structureProcessor.process(event.getStructures());
}
- private void playerLoggedOut(EntityPlayerMP player) {
- players.remove(player);
- playerBoundingBoxesCache.remove(player);
+ private StructureProcessor getStructureProcessor(int dimensionId) {
+ StructureProcessor structureProcessor = structureProcessors.get(dimensionId);
+ if (structureProcessor == null) {
+ structureProcessor = new StructureProcessor(getOrCreateCache(dimensionId));
+ structureProcessors.put(dimensionId, structureProcessor);
+ }
+ return structureProcessor;
}
- private void sendRemoveBoundingBox(DimensionType dimensionType, BoundingBox boundingBox) {
- SPacketCustomPayload payload = RemoveBoundingBox.getPayload(dimensionType, boundingBox);
- if (payload == null) return;
-
- for (EntityPlayerMP player : players) {
- if (DimensionType.getById(player.dimension) == dimensionType) {
- player.connection.sendPacket(payload);
-
- if (playerBoundingBoxesCache.containsKey(player)) {
- playerBoundingBoxesCache.get(player).remove(boundingBox);
- }
- }
+ 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 sendBoundingBoxes(EntityPlayerMP player) {
- DimensionType dimensionType = DimensionType.getById(player.dimension);
- players.add(player);
- sendToPlayer(player, getCache(dimensionType));
+ private void playerLoggedOut(PlayerLoggedOut event) {
+ int playerId = event.getPlayerId();
+ players.remove(playerId);
+ playerBoundingBoxesCache.remove(playerId);
}
- private void sendToPlayer(EntityPlayerMP player, BoundingBoxCache boundingBoxCache) {
- if (boundingBoxCache == null) return;
-
- Map<BoundingBox, Set<BoundingBox>> cacheSubset = getBoundingBoxMap(player, boundingBoxCache.getBoundingBoxes());
-
- DimensionType dimensionType = DimensionType.getById(player.dimension);
-
- for (BoundingBox key : cacheSubset.keySet()) {
- Set<BoundingBox> boundingBoxes = cacheSubset.get(key);
- SPacketCustomPayload payload = AddBoundingBox.getPayload(dimensionType, key, boundingBoxes);
- if (payload != null)
- player.connection.sendPacket(payload);
-
- if (!playerBoundingBoxesCache.containsKey(player)) {
- playerBoundingBoxesCache.put(player, new HashSet<>());
- }
- playerBoundingBoxesCache.get(player).add(key);
- }
+ private void onPlayerSubscribed(PlayerSubscribed event) {
+ int playerId = event.getPlayerId();
+ ServerPlayer player = event.getPlayer();
+ players.put(playerId, player);
+ sendToPlayer(playerId, player);
}
- private Map<BoundingBox, Set<BoundingBox>> getBoundingBoxMap(EntityPlayerMP player, Map<BoundingBox, Set<BoundingBox>> boundingBoxMap) {
- Map<BoundingBox, Set<BoundingBox>> cacheSubset = new HashMap<>();
- for (BoundingBox key : boundingBoxMap.keySet()) {
- if (!playerBoundingBoxesCache.containsKey(player) || !playerBoundingBoxesCache.get(player).contains(key)) {
- cacheSubset.put(key, boundingBoxMap.get(key));
- }
- }
- return cacheSubset;
- }
+ 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;
- protected void removeBoundingBox(DimensionType dimensionType, BoundingBox key) {
- BoundingBoxCache cache = getCache(dimensionType);
- if (cache == null) return;
+ Set<AbstractBoundingBox> playerBoundingBoxes = playerBoundingBoxesCache.computeIfAbsent(playerId, k -> new HashSet<>());
- cache.removeBoundingBox(key);
- }
+ Map<AbstractBoundingBox, Set<AbstractBoundingBox>> boundingBoxMap = boundingBoxCache.getBoundingBoxes();
+ for (AbstractBoundingBox key : boundingBoxMap.keySet()) {
+ if (playerBoundingBoxes.contains(key)) {
+ continue;
+ }
- private void mobSpawnerBroken(DimensionType dimensionType, BlockPos pos) {
- BoundingBox boundingBox = BoundingBoxMobSpawner.from(pos);
- removeBoundingBox(dimensionType, boundingBox);
- sendRemoveBoundingBox(dimensionType, boundingBox);
- }
+ Set<AbstractBoundingBox> boundingBoxes = boundingBoxMap.get(key);
+ PayloadBuilder payload = AddBoundingBox.getPayload(dimensionId, key, boundingBoxes);
+ if (payload != null)
+ player.sendPacket(payload);
- private void tick() {
- for (EntityPlayerMP player : players) {
- DimensionType dimensionType = DimensionType.getById(player.dimension);
- sendToPlayer(player, getCache(dimensionType));
+ playerBoundingBoxes.add(key);
+ }
}
}
- private void villageUpdated(DimensionType dimensionType, Village village) {
- BoundingBoxCache cache = getCache(dimensionType);
- if (cache == null) return;
+ private void serverTick() {
+ for (Map.Entry<Integer, ServerPlayer> playerEntry : players.entrySet()) {
+ int playerId = playerEntry.getKey();
+ ServerPlayer player = playerEntry.getValue();
- int villageId = village.hashCode();
- BoundingBoxVillage oldVillage = villageCache.get(villageId);
- if (oldVillage != null && !oldVillage.matches(village)) {
- cache.removeBoundingBox(oldVillage);
- sendRemoveBoundingBox(dimensionType, oldVillage);
- oldVillage = null;
+ sendToPlayer(playerId, player);
}
- if (village.isAnnihilated()) {
- villageCache.remove(villageId);
- } else {
- BoundingBoxVillage newVillage = oldVillage == null ? BoundingBoxVillage.from(village) : oldVillage;
- cache.addBoundingBox(newVillage);
- villageCache.put(villageId, newVillage);
- }
- }
-
- protected BoundingBoxCache getCache(DimensionType dimensionType) {
- return dimensionCache.get(dimensionType);
}
- protected BoundingBoxCache getOrCreateCache(DimensionType dimensionType) {
- return dimensionCache.computeIfAbsent(dimensionType, dt -> new BoundingBoxCache());
+ protected BoundingBoxCache getCache(int dimensionId) {
+ return dimensionCache.get(dimensionId);
}
- protected void runOnCache(DimensionType dimensionType, Consumer<BoundingBoxCache> action) {
- action.accept(getOrCreateCache(dimensionType));
+ protected BoundingBoxCache getOrCreateCache(int dimensionId) {
+ return dimensionCache.computeIfAbsent(dimensionId, dt -> new BoundingBoxCache());
}
protected void clearCaches() {
- villageCache.clear();
+ structureProcessors.clear();
for (BoundingBoxCache cache : dimensionCache.values()) {
- cache.close();
+ cache.clear();
}
dimensionCache.clear();
}