package jk_5.nailed.server.world; import jk_5.nailed.api.world.WorldContext; import jk_5.nailed.api.world.WorldProvider; import jk_5.nailed.server.NailedEventFactory; import net.minecraft.server.MinecraftServer; import net.minecraft.world.*; import net.minecraft.world.storage.ISaveHandler; import net.minecraft.world.storage.WorldInfo; import org.apache.commons.lang3.ArrayUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.*; public class NailedDimensionManager { private static final NailedDimensionManager INSTANCE = new NailedDimensionManager(); private static final Logger logger = LogManager.getLogger(); private final Hashtable<Integer, WorldProvider> customProviders = new Hashtable<Integer, WorldProvider>(); private final Hashtable<Integer, WorldServer> vanillaWorlds = new Hashtable<Integer, WorldServer>(); private final Hashtable<Integer, NailedWorld> worlds = new Hashtable<Integer, NailedWorld>(); private final Hashtable<Integer, WorldContext> worldContext = new Hashtable<Integer, WorldContext>(); private final List<Integer> dimensions = new ArrayList<Integer>(); private final List<Integer> unloadQueue = new ArrayList<Integer>(); private final BitSet dimensionMap = new BitSet(java.lang.Long.SIZE << 4); private int[] vanillaWorldIdArray = new int[0]; private NailedDimensionManager() { this.dimensionMap.set(1); } public void registerDimension(int id, WorldProvider provider){ if(dimensions.contains(id)){ throw new IllegalArgumentException(String.format("Failed to register dimension for id %d, One is already registered", id)); } customProviders.put(id, provider); dimensions.add(id); if(id >= 0) dimensionMap.set(id); } public void unregisterDimension(int id){ if(!dimensions.contains(id)){ throw new IllegalArgumentException(String.format("Failed to unregister dimension for id %d; No provider registered", id)); } dimensions.remove(id); } public boolean isDimensionRegistered(int dim){ return this.dimensions.contains(dim); } public int[] getAllDimensionIds(){ return this.vanillaWorldIdArray; } public void setWorld(int id, WorldServer world){ WorldContext context = this.worldContext.get(id); if(world != null){ NailedWorld nworld = new NailedWorld(world, context); this.vanillaWorlds.put(id, world); this.vanillaWorldIdArray = ArrayUtils.toPrimitive(this.vanillaWorlds.keySet().toArray(new Integer[this.vanillaWorlds.size()])); this.worlds.put(id, nworld); MinecraftServer.getServer().worldTickTimes.put(id, new long[100]); logger.info("Loading dimension " + id + " (" + world.getWorldInfo().getWorldName() + ") (" + nworld.toString() + ")"); }else{ this.vanillaWorlds.remove(id); this.vanillaWorldIdArray = ArrayUtils.toPrimitive(this.vanillaWorlds.keySet().toArray(new Integer[this.vanillaWorlds.size()])); this.worlds.remove(id); MinecraftServer.getServer().worldTickTimes.remove(id); logger.info("Unloading dimension " + id); } List<WorldServer> builder = new ArrayList<WorldServer>(); if(this.vanillaWorlds.get(0) != null){ builder.add(vanillaWorlds.get(0)); } for(Map.Entry<Integer, WorldServer> e : this.vanillaWorlds.entrySet()){ int dim = e.getKey(); if(dim < -1 || dim > 1){ builder.add(e.getValue()); } } MinecraftServer.getServer().worldServers = builder.toArray(new WorldServer[builder.size()]); } public void initWorld(int dimension, WorldContext ctx){ if(!this.dimensions.contains(dimension) && !this.customProviders.containsKey(dimension)){ throw new IllegalArgumentException(String.format("Provider type for dimension %d does not exist!", dimension)); } MinecraftServer mcserver = MinecraftServer.getServer(); String name = ctx.getName() + "/" + ctx.getSubName(); ISaveHandler saveHandler = mcserver.getActiveAnvilConverter().getSaveLoader(name, true); WorldInfo worldInfo = saveHandler.loadWorldInfo(); //Attempt to load level.dat WorldSettings worldSettings; if(worldInfo == null){ //If the level.dat does not exist, create a new one //TODO: populate this from the mappack that may or may not exist //Arguments: seed, gameType, enable structures, hardcore mode, worldType worldSettings = new WorldSettings(0, WorldSettings.GameType.ADVENTURE, false, false, WorldType.DEFAULT); worldSettings.setWorldName(""); //Generator settings (for flat) worldInfo = new WorldInfo(worldSettings, name); }else{ worldSettings = new WorldSettings(worldInfo); } worldContext.put(dimension, ctx); WorldServer world = new WorldServer(mcserver, saveHandler, worldInfo, dimension, mcserver.theProfiler); world.init(); world.addWorldAccess(new WorldManager(mcserver, world)); NailedEventFactory.fireWorldLoad(world); world.getWorldInfo().setGameType(mcserver.getGameType()); } public WorldServer getVanillaWorld(int dimension){ return this.vanillaWorlds.get(dimension); } public NailedWorld getWorld(int dimension){ return this.worlds.get(dimension); } public WorldServer[] getVanillaWorlds(){ return this.vanillaWorlds.values().toArray(new WorldServer[this.vanillaWorlds.size()]); } public NailedWorld[] getWorlds(){ return this.worlds.values().toArray(new NailedWorld[this.worlds.size()]); } public net.minecraft.world.WorldProvider createProviderFor(int dim){ if(!this.customProviders.containsKey(dim)){ throw new RuntimeException(String.format("No WorldProvider bound for dimension %d", dim)); } DelegatingWorldProvider d = new DelegatingWorldProvider(this.customProviders.get(dim)); d.setDimension(dim); return d; } public void unloadWorld(int id){ this.unloadQueue.add(id); } public void unloadWorlds(Hashtable<Integer, long[]> times){ for(Integer id : this.unloadQueue){ WorldServer w = this.vanillaWorlds.get(id); try{ if(w != null){ w.saveAllChunks(true, null); }else{ logger.warn("Unexpected world unload. World " + id + " is already unloaded! Skipping it"); } }catch(MinecraftException e){ logger.warn("Error while unloading world " + id, e); }finally{ if(w != null){ NailedEventFactory.fireWorldUnload(w); w.flush(); this.setWorld(id, null); } } } this.unloadQueue.clear(); } public int getNextFreeDimensionId(){ int next = 0; while(true){ next = this.dimensionMap.nextClearBit(next); if(dimensions.contains(next)){ dimensionMap.set(next); }else{ return next; } } } public static NailedDimensionManager instance() { return INSTANCE; } }