diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index cc04157..0652554 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -83,9 +83,9 @@ import org.ultramine.permission.IPermissionManager; import org.ultramine.server.BackupManager; import org.ultramine.server.ConfigurationHandler; -import org.ultramine.server.MultiWorld; import org.ultramine.server.WatchdogThread; import org.ultramine.server.util.GlobalExecutors; +import org.ultramine.server.world.MultiWorld; import net.minecraftforge.common.DimensionManager; import net.minecraftforge.common.MinecraftForge; diff --git a/src/main/java/net/minecraft/world/WorldProvider.java b/src/main/java/net/minecraft/world/WorldProvider.java index 43eeb91..0fe7b5a 100644 --- a/src/main/java/net/minecraft/world/WorldProvider.java +++ b/src/main/java/net/minecraft/world/WorldProvider.java @@ -226,7 +226,7 @@ */ public String getSaveFolder() { - return (worldObj instanceof WorldServerMulti || MinecraftServer.getServer() == null) ? + return (worldObj instanceof WorldServerMulti || MinecraftServer.getServer() == null || MinecraftServer.getServer().isSinglePlayer()) ? (dimensionId == 0 ? null : "DIM" + dimensionId) : "../" + MinecraftServer.getServer().getMultiWorld().getNameByID(dimensionId); } diff --git a/src/main/java/org/ultramine/commands/basic/TechCommands.java b/src/main/java/org/ultramine/commands/basic/TechCommands.java index 9c2940b..9a7a041 100644 --- a/src/main/java/org/ultramine/commands/basic/TechCommands.java +++ b/src/main/java/org/ultramine/commands/basic/TechCommands.java @@ -1,7 +1,5 @@ package org.ultramine.commands.basic; -import java.io.File; -import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; @@ -23,16 +21,13 @@ import static net.minecraft.util.EnumChatFormatting.*; import net.minecraft.world.WorldServer; import net.minecraft.world.chunk.Chunk; -import net.minecraftforge.common.DimensionManager; -import org.apache.commons.io.FileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.ultramine.commands.Command; import org.ultramine.commands.CommandContext; import org.ultramine.server.BackupManager; import org.ultramine.server.ConfigurationHandler; -import org.ultramine.server.MultiWorld; import org.ultramine.server.Restarter; import org.ultramine.server.Teleporter; import org.ultramine.server.UltramineServerConfig; @@ -42,7 +37,8 @@ import org.ultramine.server.chunk.ChunkProfiler; import org.ultramine.server.chunk.IChunkLoadCallback; import org.ultramine.server.util.BasicTypeParser; -import org.ultramine.server.util.WarpLocation; +import org.ultramine.server.world.MultiWorld; +import org.ultramine.server.world.WorldDescriptor; import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.eventhandler.SubscribeEvent; @@ -170,85 +166,62 @@ if(ctx.getAction().equals("list")) { ctx.sendMessage("command.multiworld.list.head"); - for(int dim : DimensionManager.getStaticDimensionIDs()) + for(WorldDescriptor desc : mw.getAllDescs()) { - String name = mw.getNameByID(dim); - ctx.sendMessage(GOLD, " - [%s](%s) - %s", dim, name != null ? name : "unnamed", mw.getWorldByID(dim) != null ? "loaded" : "unloaded"); + ctx.sendMessage(GOLD, " - [%s](%s) - %s", desc.getDimension(), desc.getName(), desc.getState()); } return; } - int dim = ctx.get("world").asInt(); - WorldServer world = mw.getWorldByID(dim); + WorldDescriptor desc = mw.getDescByNameOrID(ctx.get("world").asString()); - if(!DimensionManager.isDimensionRegistered(dim)) + if(desc == null) ctx.failure("command.multiworld.notregistered"); if(ctx.getAction().equals("load")) { - if(world != null) + if(desc.getState().isLoaded()) ctx.failure("command.multiworld.alreadyloaded"); - mw.unholdDimension(dim); //if was held - DimensionManager.initDimension(dim); + desc.forceLoad(); ctx.sendMessage("command.multiworld.load.success"); } - else if(ctx.getAction().equals("unload") || ctx.getAction().equals("hold")) + else if(ctx.getAction().equals("unload")) { - if(world == null) + if(!desc.getState().isLoaded()) ctx.failure("command.multiworld.notloaded"); - movePlayersOut(world); - if(ctx.getAction().equals("hold")) - mw.holdDimension(dim); - DimensionManager.unloadWorld(dim); - ctx.sendMessage("command.multiworld."+ctx.getAction()+".success"); + desc.unload(); + ctx.sendMessage("command.multiworld.unload.success"); + } + else if(ctx.getAction().equals("hold")) + { + desc.hold(); + ctx.sendMessage("command.multiworld.hold.success"); } else if(ctx.getAction().equals("goto")) { - if(world == null) + if(!desc.getState().isLoaded()) ctx.failure("command.multiworld.notloaded"); - Teleporter.tpNow(ctx.getSenderAsPlayer(), dim, world.getWorldInfo().getSpawnX(), world.getWorldInfo().getSpawnY(), world.getWorldInfo().getSpawnZ()); + WorldServer world = desc.getWorld(); + Teleporter.tpNow(ctx.getSenderAsPlayer(), desc.getDimension(), world.getWorldInfo().getSpawnX(), world.getWorldInfo().getSpawnY(), world.getWorldInfo().getSpawnZ()); } - else if(ctx.getAction().equals("destroy") || ctx.getAction().equals("delete")) + else if(ctx.getAction().equals("destroy")) { - if(world == null && !ctx.getAction().equals("delete")) + if(!desc.getState().isLoaded()) ctx.failure("command.multiworld.notloaded"); - if(world != null) - { - movePlayersOut(world); - mw.destroyWorld(world); - } - - mw.holdDimension(dim); - - if(ctx.getAction().equals("delete")) - { - try - { - FileUtils.cleanDirectory(new File(ctx.getServer().getWorldsDir(), world != null ? mw.getSaveDirName(world) : mw.getNameByID(dim))); - } - catch(IOException e) - { - throw new RuntimeException(e); - } - } - ctx.sendMessage("command.multiworld."+ctx.getAction()+".success"); + desc.destroyWorld(); + desc.hold(); + ctx.sendMessage("command.multiworld.destroy.success"); } - } - - private static void movePlayersOut(WorldServer world) - { - WarpLocation spawn = world.func_73046_m().getConfigurationManager().getDataLoader().getWarp("spawn"); - for(EntityPlayerMP player : GenericIterableFactory.newCastingIterable(world.playerEntities, EntityPlayerMP.class)) + else if(ctx.getAction().equals("delete")) { - if(player.dimension == spawn.dimension) - player.playerNetServerHandler.kickPlayerFromServer("The world has been unloaded"); - else - Teleporter.tpNow(player, spawn); + desc.deleteWorld(); + desc.hold(); + ctx.sendMessage("command.multiworld.delete.success"); } } @@ -561,7 +534,7 @@ if(MinecraftServer.getServer().getTickCounter() % Math.max(1,169/chunksPerTick) != 0) return; - WorldServer world = MinecraftServer.getServer().getMultiWorld().getOrLoadWorldByID(dim); + WorldServer world = MinecraftServer.getServer().getMultiWorld().getWorldByID(dim); if(world == null) return; Border[] borders = world.getConfig().borders; diff --git a/src/main/java/org/ultramine/server/BackupManager.java b/src/main/java/org/ultramine/server/BackupManager.java index 9516def..311ed92 100644 --- a/src/main/java/org/ultramine/server/BackupManager.java +++ b/src/main/java/org/ultramine/server/BackupManager.java @@ -38,6 +38,7 @@ import org.ultramine.server.data.ServerDataLoader; import org.ultramine.server.util.GlobalExecutors; import org.ultramine.server.util.ZipUtil; +import org.ultramine.server.world.WorldDescriptor; import com.google.common.base.Function; @@ -195,7 +196,7 @@ if(!zipFile.exists() || zipFile.isDirectory() || !zipFile.getName().endsWith(".zip")) throw new CommandException("command.backup.apply.fail.nofile", path); - final Set moveOnly; + Set moveOnly; try { Set available = ZipUtil.getRootFiles(zipFile); @@ -228,7 +229,9 @@ { if(!moveOnly.contains(server.getMultiWorld().getSaveDirName(world))) continue; - List players = server.getMultiWorld().destroyWorld(world); + WorldDescriptor desc = server.getMultiWorld().getDescFromWorld(world); + List players = desc.extractPlayer(); + desc.destroyWorld(); dimToPlayerMap.put(world.provider.dimensionId, players); } @@ -273,13 +276,16 @@ try { + final List moveOnlyPaths = new ArrayList(moveOnly.size()); + for(String s : moveOnly) + moveOnlyPaths.add(s + '/'); ZipUtil.unzip(zipFile, server.getWorldsDir(), new Function() { @Override public String apply(String name) { boolean contains = false; - for(String s : moveOnly) + for(String s : moveOnlyPaths) { if(name.startsWith(s)) { diff --git a/src/main/java/org/ultramine/server/MultiWorld.java b/src/main/java/org/ultramine/server/MultiWorld.java deleted file mode 100644 index 3e135d3..0000000 --- a/src/main/java/org/ultramine/server/MultiWorld.java +++ /dev/null @@ -1,569 +0,0 @@ -package org.ultramine.server; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.ultramine.server.WorldsConfig.WorldConfig; -import org.ultramine.server.WorldsConfig.WorldConfig.MobSpawn.MobSpawnEngine; -import org.ultramine.server.WorldsConfig.WorldConfig.Settings.WorldTime; -import org.ultramine.server.util.BasicTypeParser; - -import cpw.mods.fml.common.FMLCommonHandler; -import cpw.mods.fml.common.eventhandler.SubscribeEvent; -import cpw.mods.fml.common.network.FMLEmbeddedChannel; -import cpw.mods.fml.common.network.FMLNetworkEvent; -import cpw.mods.fml.common.network.FMLOutboundHandler; -import cpw.mods.fml.common.network.NetworkRegistry; -import cpw.mods.fml.common.network.handshake.NetworkDispatcher; -import cpw.mods.fml.relauncher.Side; -import cpw.mods.fml.relauncher.SideOnly; -import gnu.trove.TCollections; -import gnu.trove.map.TIntIntMap; -import gnu.trove.map.TIntObjectMap; -import gnu.trove.map.hash.TIntIntHashMap; -import gnu.trove.map.hash.TIntObjectHashMap; -import gnu.trove.set.TIntSet; -import gnu.trove.set.hash.TIntHashSet; -import net.minecraft.entity.player.EntityPlayerMP; -import net.minecraft.scoreboard.ScorePlayerTeam; -import net.minecraft.server.MinecraftServer; -import net.minecraft.tileentity.TileEntity; -import net.minecraft.world.WorldManager; -import net.minecraft.world.WorldServer; -import net.minecraft.world.WorldServerMulti; -import net.minecraft.world.WorldSettings; -import net.minecraft.world.WorldType; -import net.minecraft.world.chunk.storage.AnvilSaveHandler; -import net.minecraft.world.storage.ISaveFormat; -import net.minecraft.world.storage.ISaveHandler; -import net.minecraft.world.storage.WorldInfo; -import net.minecraftforge.common.DimensionManager; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.common.network.ForgeMessage; -import net.minecraftforge.event.world.WorldEvent; - -public class MultiWorld -{ - private static final Logger log = LogManager.getLogger(); - private final MinecraftServer server; - private final TIntIntMap provTranslt = new TIntIntHashMap(); - private final TIntObjectMap dimToNameMap = new TIntObjectHashMap(); - private final TIntObjectMap dimToWorldMap = new TIntObjectHashMap(); - private final Map nameToWorldMap = new HashMap(); - private final TIntObjectMap dimToConfigMap = new TIntObjectHashMap(); - private final Set backupDirs = new HashSet(); - private final TIntSet holdDims = new TIntHashSet(); - private TIntSet isolatedDataDims; - - public MultiWorld(MinecraftServer server) - { - this.server = server; - } - - public void registerProviderTranslation(int src, int dst) - { - provTranslt.put(src, dst); - } - - private void sendDimensionToAll(int dim, int pid) - { - FMLEmbeddedChannel channel = NetworkRegistry.INSTANCE.getChannel("FORGE", Side.SERVER); - channel.attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.ALL); - channel.writeAndFlush(new ForgeMessage.DimensionRegisterMessage(dim, provTranslt.containsKey(pid) ? provTranslt.get(pid) : pid)); - } - - @SubscribeEvent - public void onPlayerLoggedIn(FMLNetworkEvent.ServerConnectionFromClientEvent event) - { - FMLEmbeddedChannel channel = NetworkRegistry.INSTANCE.getChannel("FORGE", Side.SERVER); - channel.attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.DISPATCHER); - channel.attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(event.manager.channel().attr(NetworkDispatcher.FML_DISPATCHER).get()); - for (int dim : DimensionManager.getStaticDimensionIDs()) - { - int pid = DimensionManager.getProviderType(dim); - channel.writeAndFlush(new ForgeMessage.DimensionRegisterMessage(dim, provTranslt.containsKey(pid) ? provTranslt.get(pid) : pid)); - } - } - - @SubscribeEvent - public void onWorldUnload(WorldEvent.Unload event) - { - if(!event.world.isRemote) - { - dimToWorldMap.remove(event.world.provider.dimensionId); - nameToWorldMap.remove(resolveNameForDim(event.world.provider.dimensionId)); - ((WorldServer)event.world).theChunkProviderServer.setWorldUnloaded(); - } - } - - @SideOnly(Side.SERVER) - public void handleServerWorldsInit() - { - DimensionManager.registerProviderType(-10, org.ultramine.server.wempty.WorldProviderEmpty.class, false); - registerProviderTranslation(-10, 0); - DimensionManager.unregisterDimension(-1); - DimensionManager.unregisterDimension(0); - DimensionManager.unregisterDimension(1); - - Map worlds = ConfigurationHandler.getWorldsConfig().worlds; - TIntSet isolatedDataDimsSet = new TIntHashSet(); - - for(Map.Entry ent : worlds.entrySet()) - { - WorldConfig conf = ent.getValue(); - if(!DimensionManager.isDimensionRegistered(conf.dimension)) - DimensionManager.registerDimension(conf.dimension, conf.generation.providerID); - if(conf.settings.useIsolatedPlayerData) - isolatedDataDimsSet.add(conf.dimension); - - dimToNameMap.put(conf.dimension, ent.getKey()); - dimToConfigMap.put(conf.dimension, conf); - } - - isolatedDataDims = TCollections.unmodifiableSet(isolatedDataDimsSet); - - String mainWorldName = dimToNameMap.get(0); - if(mainWorldName == null) - mainWorldName = "world"; - server.setFolderName("worlds" + File.separator + mainWorldName); - - WorldConfig mainConf = worlds.get(mainWorldName); - if(mainConf == null) - mainConf = ConfigurationHandler.getWorldsConfig().global; - - if(mainConf.settings.useIsolatedPlayerData) - { - log.warn("Can't use isolated player data in main world! Ignoring it"); - mainConf.settings.useIsolatedPlayerData = false; - } - - boolean usingPlayerDir = server.getConfigurationManager().getDataLoader().getDataProvider().isUsingWorldPlayerDir(); - - ISaveFormat format = server.getActiveAnvilConverter(); - ISaveHandler mainSaveHandler = format.getSaveLoader(mainWorldName, true); - WorldInfo mainWorldInfo = mainSaveHandler.loadWorldInfo(); - WorldSettings mainSettings = makeSettings(mainWorldInfo, mainConf); - - WorldServer mainWorld = new WorldServer(server, mainSaveHandler, mainWorldName, mainConf.dimension, mainSettings, server.theProfiler); - - initWorld(mainWorld, mainConf); - - for (int dim : DimensionManager.getStaticDimensionIDs()) - { - if(dim == mainConf.dimension) continue; - - String name = resolveNameForDim(dim); - WorldConfig conf = worlds.get(name); - - if(conf == null) - { - log.warn("World with dimension id:{} was loaded bypass worlds configuration. Using global config", dim); - conf = ConfigurationHandler.getWorldsConfig().global; - } - - WorldServer world; - if(ConfigurationHandler.getServerConfig().settings.other.splitWorldDirs) - { - ISaveHandler save = format.getSaveLoader(name, usingPlayerDir && conf.settings.useIsolatedPlayerData); - ((AnvilSaveHandler)save).setSingleStorage(); - world = new WorldServer(server, save, name, dim, makeSettings(save.loadWorldInfo(), conf), server.theProfiler); - } - else - { - world = new WorldServerMulti(server, mainSaveHandler, mainWorldName, dim, makeSettings(mainWorldInfo, conf), mainWorld, server.theProfiler); - } - - initWorld(world, conf); - } - } - - @SideOnly(Side.SERVER) - public void reloadServerWorlds() - { - Map worlds = ConfigurationHandler.getWorldsConfig().worlds; - TIntSet isolatedDataDimsSet = new TIntHashSet(); - - for(Map.Entry ent : worlds.entrySet()) - { - WorldConfig conf = ent.getValue(); - if(!DimensionManager.isDimensionRegistered(conf.dimension)) - { - DimensionManager.registerDimension(conf.dimension, conf.generation.providerID); - sendDimensionToAll(conf.dimension, conf.generation.providerID); - dimToNameMap.put(conf.dimension, ent.getKey()); - } - else - { - WorldServer world = getWorldByID(conf.dimension); - if(world != null) - applyConfig(world, conf); - } - if(conf.settings.useIsolatedPlayerData) - isolatedDataDimsSet.add(conf.dimension); - dimToConfigMap.put(conf.dimension, conf); - } - - isolatedDataDims = TCollections.unmodifiableSet(isolatedDataDimsSet); - } - - @SideOnly(Side.CLIENT) - public void handleClientWorldsInit() - { - for(WorldServer world : server.worldServers) - { - WorldConfig conf = getDefaultClientConfig(world); - world.setConfig(conf); - String name = resolveNameForDim(world.provider.dimensionId); - dimToWorldMap.put(world.provider.dimensionId, world); - nameToWorldMap.put(name, world); - } - } - - @SideOnly(Side.SERVER) - public void initDimension(int dim) - { - if(holdDims.contains(dim)) - return; - ISaveFormat format = server.getActiveAnvilConverter(); - - String name = resolveNameForDim(dim); - WorldConfig conf = ConfigurationHandler.getWorldsConfig().worlds.get(name); - - if(conf == null) - { - log.warn("World with dimension id:{} was loaded bypass worlds configuration. Using global config", dim); - conf = ConfigurationHandler.getWorldsConfig().global; - } - - WorldServer world; - if(dim == 0) - { - ISaveHandler mainSaveHandler = format.getSaveLoader(name, true); - WorldInfo mainWorldInfo = mainSaveHandler.loadWorldInfo(); - WorldSettings mainSettings = makeSettings(mainWorldInfo, conf); - - world = new WorldServer(server, mainSaveHandler, name, dim, mainSettings, server.theProfiler); - } - else if(ConfigurationHandler.getServerConfig().settings.other.splitWorldDirs) - { - ISaveHandler save = format.getSaveLoader(name, false); - ((AnvilSaveHandler)save).setSingleStorage(); - world = new WorldServer(server, save, name, dim, makeSettings(save.loadWorldInfo(), conf), server.theProfiler); - } - else - { - WorldServer mainWorld = getWorldByID(0); - ISaveHandler mainSaveHandler = mainWorld.getSaveHandler(); - WorldInfo mainWorldInfo = mainWorld.getWorldInfo(); - world = new WorldServerMulti(server, mainSaveHandler, mainWorldInfo.getWorldName(), dim, makeSettings(mainWorldInfo, conf), mainWorld, server.theProfiler); - } - - initWorld(world, conf); - } - - @SideOnly(Side.CLIENT) - public void onClientInitDimension(WorldServer world) - { - WorldConfig conf = getDefaultClientConfig(world); - world.setConfig(conf); - String name = resolveNameForDim(world.provider.dimensionId); - dimToWorldMap.put(world.provider.dimensionId, world); - nameToWorldMap.put(name, world); - } - - private String resolveNameForDim(int dim) - { - String name = dimToNameMap.get(dim); - - if(name == null) - { - name = "world_unnamed" + dim; - dimToNameMap.put(dim, name); - } - - return name; - } - - @SideOnly(Side.CLIENT) - private WorldConfig getDefaultClientConfig(WorldServer world) - { - WorldConfig conf = new WorldConfig(); - conf.generation = new WorldConfig.Generation(); - conf.mobSpawn = new WorldConfig.MobSpawn(); - conf.settings = new WorldConfig.Settings(); - conf.chunkLoading = new WorldConfig.ChunkLoading(); - conf.portals = new WorldConfig.Portals(); - - conf.portals.netherLink = world.provider.dimensionId == -1 ? 0 : -1; - conf.portals.enderLink = world.provider.dimensionId == 1 ? 0 : 1; - - return conf; - } - - @SideOnly(Side.SERVER) - private WorldSettings makeSettings(WorldInfo wi, WorldConfig conf) - { - WorldSettings mainSettings; - - if (wi == null) - { - mainSettings = new WorldSettings(toSeed(conf.generation.seed), server.getGameType(), conf.generation.generateStructures, - server.isHardcore(), WorldType.parseWorldType(conf.generation.levelType)); - mainSettings.func_82750_a(conf.generation.generatorSettings); - } - else - { - mainSettings = new WorldSettings(wi); - } - - return mainSettings; - } - - private static long toSeed(String seedstr) - { - try - { - return Long.parseLong(seedstr); - } - catch (NumberFormatException e) - { - return seedstr.hashCode(); - } - } - - @SideOnly(Side.SERVER) - private void initWorld(WorldServer world, WorldConfig conf) - { - world.addWorldAccess(new WorldManager(server, world)); - - if (!server.isSinglePlayer()) - world.getWorldInfo().setGameType(server.getGameType()); - - applyConfig(world, conf); - - MinecraftForge.EVENT_BUS.post(new WorldEvent.Load(world)); - - String name = resolveNameForDim(world.provider.dimensionId); - dimToWorldMap.put(world.provider.dimensionId, world); - nameToWorldMap.put(name, world); - - if(!(world instanceof WorldServerMulti)) - backupDirs.add(name); - } - - @SideOnly(Side.SERVER) - private void applyConfig(WorldServer world, WorldConfig conf) - { - world.difficultySetting = BasicTypeParser.parseDifficulty(conf.settings.difficulty); - world.setAllowedSpawnTypes(conf.mobSpawn.spawnMonsters, conf.mobSpawn.spawnAnimals); - world.getGameRules().setOrCreateGameRule("doDaylightCycle", Boolean.toString(conf.settings.time != WorldTime.FIXED)); - world.getGameRules().setOrCreateGameRule("doMobSpawning", Boolean.toString(conf.mobSpawn.spawnEngine != MobSpawnEngine.NONE)); - world.setConfig(conf); - } - - @SideOnly(Side.SERVER) - public int allocTempDim() - { - return DimensionManager.getNextFreeDimId(); - } - - @SideOnly(Side.SERVER) - public WorldServer makeTempWorld() - { - int dim = allocTempDim(); - return makeTempWorld("temp_"+dim, dim); - } - - @SideOnly(Side.SERVER) - public WorldServer makeTempWorld(String name) - { - return makeTempWorld(name, allocTempDim()); - } - - @SideOnly(Side.SERVER) - public WorldServer makeTempWorld(String name, int dim) - { - if(DimensionManager.isDimensionRegistered(dim)) - throw new RuntimeException("Dimension "+dim+" already registered (on making temp world)"); - - DimensionManager.registerDimension(dim, 0); - dimToNameMap.put(dim, name); - ISaveFormat format = server.getActiveAnvilConverter(); - ISaveHandler save = format.getSaveLoader(name, false); - ((AnvilSaveHandler)save).setSingleStorage(); - WorldInfo wi = save.loadWorldInfo(); - if(wi != null) - wi.setWorldName(name); - WorldServer world = new WorldServer(server, save, name, dim, makeSettings(wi, ConfigurationHandler.getWorldsConfig().global), server.theProfiler); - initWorld(world, ConfigurationHandler.getWorldsConfig().global); - backupDirs.remove(name); - sendDimensionToAll(dim, 0); - return world; - } - - @SideOnly(Side.SERVER) - @SuppressWarnings("unchecked") - public List destroyWorld(WorldServer world) - { - if(world.provider.dimensionId == 0) - for(ScorePlayerTeam team : new ArrayList(world.getScoreboard().getTeams())) - world.getScoreboard().removeTeam(team); - - List players = new ArrayList(world.playerEntities); - for(EntityPlayerMP player : players) - { - world.removePlayerEntityDangerously(player); - player.isDead = false; - world.getEntityTracker().removePlayerFromTrackers(player); - world.getPlayerManager().removePlayer(player); - player.getChunkMgr().setWorldDestroyed(); - player.setWorld(null); - player.theItemInWorldManager.setWorld(null); - } - world.playerEntities.clear(); - - world.theChunkProviderServer.unloadAll(false); - world.forceUnloadTileEntities(); - world.theChunkProviderServer.setWorldUnloaded(); - world.theChunkProviderServer.unloadAll(false); - world.forceUnloadTileEntities(); - - MinecraftForge.EVENT_BUS.post(new WorldEvent.Unload(world)); - DimensionManager.setWorld(world.provider.dimensionId, null); - world.theChunkProviderServer.loadedChunkHashMap.clear(); - for(Object o : world.loadedTileEntityList) - ((TileEntity)o).setWorldObj(null); - world.loadedTileEntityList.clear(); - - return players; - } - - public void holdDimension(int dim) - { - holdDims.add(dim); - } - - public boolean unholdDimension(int dim) - { - return holdDims.remove(dim); - } - - public WorldServer getWorldByID(int dim) - { - return dimToWorldMap.get(dim); - } - - public WorldServer getOrLoadWorldByID(int dim) - { - WorldServer world = dimToWorldMap.get(dim); - if(world != null) - return world; - DimensionManager.initDimension(dim); - return dimToWorldMap.get(dim); - } - - public WorldServer getWorldByName(String name) - { - return nameToWorldMap.get(name); - } - - public WorldServer getWorldByNameOrID(String id) - { - return BasicTypeParser.isInt(id) ? dimToWorldMap.get(Integer.parseInt(id)) : nameToWorldMap.get(id); - } - - public Set getAllNames() - { - return nameToWorldMap.keySet(); - } - - public Collection getLoadedWorlds() - { - return dimToWorldMap.valueCollection(); - } - - public String getNameByID(int id) - { - return dimToNameMap.get(id); - } - - public WorldConfig getConfigByID(int dim) - { - WorldConfig cfg = dimToConfigMap.get(dim); - if(cfg == null) - return ConfigurationHandler.getWorldsConfig().global; - return cfg; - } - - public Set getDirsForBackup() - { - return backupDirs; - } - - public Collection resolveSaveDirs(Collection names) - { - if(backupDirs.size() == 1) - return backupDirs; - List dirs = new ArrayList(); - for(String name : names) - { - WorldServer world = getWorldByNameOrID(name); - if(world != null) - { - if(!(world instanceof WorldServerMulti)) - dirs.add(resolveNameForDim(world.provider.dimensionId)); - } - else - { - if(BasicTypeParser.isInt(name)) - { - int dim = Integer.parseInt(name); - if(DimensionManager.isDimensionRegistered(dim)) - dirs.add(resolveNameForDim(dim)); - } - else - { - if(new File(server.getWorldsDir(), name).isDirectory()) - dirs.add(name); - } - } - } - - return dirs; - } - - public String getSaveDirName(WorldServer world) - { - if(world instanceof WorldServerMulti) - return dimToNameMap.get(0); - - return dimToNameMap.get(world.provider.dimensionId); - } - - public TIntSet getIsolatedDataDims() - { - return isolatedDataDims; - } - - public void register() - { - FMLCommonHandler.instance().bus().register(this); - MinecraftForge.EVENT_BUS.register(this); - } - - public void unregister() - { - FMLCommonHandler.instance().bus().unregister(this); - MinecraftForge.EVENT_BUS.unregister(this); - dimToWorldMap.clear(); - nameToWorldMap.clear(); - } -} diff --git a/src/main/java/org/ultramine/server/WorldsConfig.java b/src/main/java/org/ultramine/server/WorldsConfig.java index 54bc508..965beac 100644 --- a/src/main/java/org/ultramine/server/WorldsConfig.java +++ b/src/main/java/org/ultramine/server/WorldsConfig.java @@ -1,17 +1,17 @@ package org.ultramine.server; -import java.util.HashMap; +import java.util.ArrayList; import java.util.List; -import java.util.Map; public class WorldsConfig { public WorldConfig global = new WorldConfig(); - public Map worlds = new HashMap(); + public List worlds = new ArrayList(); public static class WorldConfig { public int dimension; + public String name; public Generation generation; public MobSpawn mobSpawn; public Settings settings; diff --git a/src/main/java/org/ultramine/server/wempty/ChunkProviderEmpty.java b/src/main/java/org/ultramine/server/wempty/ChunkProviderEmpty.java deleted file mode 100644 index 42ce938..0000000 --- a/src/main/java/org/ultramine/server/wempty/ChunkProviderEmpty.java +++ /dev/null @@ -1,125 +0,0 @@ -package org.ultramine.server.wempty; - -import java.util.List; - -import net.minecraft.entity.EnumCreatureType; -import net.minecraft.init.Blocks; -import net.minecraft.util.IProgressUpdate; -import net.minecraft.world.ChunkPosition; -import net.minecraft.world.World; -import net.minecraft.world.biome.BiomeGenBase; -import net.minecraft.world.chunk.Chunk; -import net.minecraft.world.chunk.IChunkProvider; - -public class ChunkProviderEmpty implements IChunkProvider -{ - private World world; - - public ChunkProviderEmpty(World world) - { - this.world = world; - } - - @Override - public boolean chunkExists(int var1, int var2) - { - return true; - } - - @Override - public Chunk provideChunk(int par1, int par2) - { - Chunk chunk = new Chunk(this.world, par1, par2); - - byte[] arr = chunk.getBiomeArray(); - - for (int i = 0; i < arr.length; ++i) - { - arr[i] = (byte)BiomeGenBase.forest.biomeID; - } - - chunk.generateSkylightMap(); - - return chunk; - } - - @Override - public Chunk loadChunk(int var1, int var2) - { - return this.provideChunk(var1, var2); - } - - @Override - public void populate(IChunkProvider var1, int x, int z) - { - int bx = x << 4; - int bz = z << 4; - - if((x > -2 && x < 2) && (z > -2 && z < 2)) - { - for(int i = 0; i < 16; i++) - { - for(int j = 0; j < 16; j++) - { - world.setBlock(bx + i, 64, bz + j, Blocks.grass); - } - } - } - } - - @Override - public boolean saveChunks(boolean var1, IProgressUpdate var2) - { - return true; - } - - @Override - public boolean unloadQueuedChunks() - { - return false; - } - - @Override - public boolean canSave() - { - return true; - } - - @Override - public String makeString() - { - return "EmptyWorldSource"; - } - - @SuppressWarnings("rawtypes") - @Override - public List getPossibleCreatures(EnumCreatureType var1, int var2, int var3, int var4) - { - return null; - } - - @Override - public ChunkPosition func_147416_a(World var1, String var2, int var3, int var4, int var5) - { - return null; - } - - @Override - public int getLoadedChunkCount() - { - return 0; - } - - @Override - public void recreateStructures(int var1, int var2) - { - - } - - @Override - public void saveExtraData() - { - - } - -} diff --git a/src/main/java/org/ultramine/server/wempty/WorldProviderEmpty.java b/src/main/java/org/ultramine/server/wempty/WorldProviderEmpty.java deleted file mode 100644 index 9183131..0000000 --- a/src/main/java/org/ultramine/server/wempty/WorldProviderEmpty.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.ultramine.server.wempty; - -import net.minecraft.world.WorldProvider; -import net.minecraft.world.chunk.IChunkProvider; - -public class WorldProviderEmpty extends WorldProvider -{ - - public IChunkProvider createChunkGenerator() - { - return new ChunkProviderEmpty(worldObj); - } - - @Override - public String getDimensionName() - { - return "EmptyWorld"; - } - -} diff --git a/src/main/java/org/ultramine/server/world/ChunkProviderEmpty.java b/src/main/java/org/ultramine/server/world/ChunkProviderEmpty.java new file mode 100644 index 0000000..c66c0a2 --- /dev/null +++ b/src/main/java/org/ultramine/server/world/ChunkProviderEmpty.java @@ -0,0 +1,125 @@ +package org.ultramine.server.world; + +import java.util.List; + +import net.minecraft.entity.EnumCreatureType; +import net.minecraft.init.Blocks; +import net.minecraft.util.IProgressUpdate; +import net.minecraft.world.ChunkPosition; +import net.minecraft.world.World; +import net.minecraft.world.biome.BiomeGenBase; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.IChunkProvider; + +public class ChunkProviderEmpty implements IChunkProvider +{ + private World world; + + public ChunkProviderEmpty(World world) + { + this.world = world; + } + + @Override + public boolean chunkExists(int var1, int var2) + { + return true; + } + + @Override + public Chunk provideChunk(int par1, int par2) + { + Chunk chunk = new Chunk(this.world, par1, par2); + + byte[] arr = chunk.getBiomeArray(); + + for (int i = 0; i < arr.length; ++i) + { + arr[i] = (byte)BiomeGenBase.forest.biomeID; + } + + chunk.generateSkylightMap(); + + return chunk; + } + + @Override + public Chunk loadChunk(int var1, int var2) + { + return this.provideChunk(var1, var2); + } + + @Override + public void populate(IChunkProvider var1, int x, int z) + { + int bx = x << 4; + int bz = z << 4; + + if((x > -2 && x < 2) && (z > -2 && z < 2)) + { + for(int i = 0; i < 16; i++) + { + for(int j = 0; j < 16; j++) + { + world.setBlock(bx + i, 64, bz + j, Blocks.grass); + } + } + } + } + + @Override + public boolean saveChunks(boolean var1, IProgressUpdate var2) + { + return true; + } + + @Override + public boolean unloadQueuedChunks() + { + return false; + } + + @Override + public boolean canSave() + { + return true; + } + + @Override + public String makeString() + { + return "EmptyWorldSource"; + } + + @SuppressWarnings("rawtypes") + @Override + public List getPossibleCreatures(EnumCreatureType var1, int var2, int var3, int var4) + { + return null; + } + + @Override + public ChunkPosition func_147416_a(World var1, String var2, int var3, int var4, int var5) + { + return null; + } + + @Override + public int getLoadedChunkCount() + { + return 0; + } + + @Override + public void recreateStructures(int var1, int var2) + { + + } + + @Override + public void saveExtraData() + { + + } + +} diff --git a/src/main/java/org/ultramine/server/world/MultiWorld.java b/src/main/java/org/ultramine/server/world/MultiWorld.java new file mode 100644 index 0000000..d172c77 --- /dev/null +++ b/src/main/java/org/ultramine/server/world/MultiWorld.java @@ -0,0 +1,426 @@ +package org.ultramine.server.world; + +import gnu.trove.TCollections; +import gnu.trove.map.TIntIntMap; +import gnu.trove.map.TIntObjectMap; +import gnu.trove.map.hash.TIntIntHashMap; +import gnu.trove.map.hash.TIntObjectHashMap; +import gnu.trove.set.TIntSet; +import gnu.trove.set.hash.TIntHashSet; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.WorldServer; +import net.minecraft.world.WorldServerMulti; +import net.minecraftforge.common.DimensionManager; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.network.ForgeMessage; +import net.minecraftforge.event.world.WorldEvent; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.ultramine.server.ConfigurationHandler; +import org.ultramine.server.WorldsConfig.WorldConfig; +import org.ultramine.server.util.BasicTypeParser; + +import cpw.mods.fml.common.FMLCommonHandler; +import cpw.mods.fml.common.eventhandler.EventPriority; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.common.network.FMLEmbeddedChannel; +import cpw.mods.fml.common.network.FMLNetworkEvent; +import cpw.mods.fml.common.network.FMLOutboundHandler; +import cpw.mods.fml.common.network.NetworkRegistry; +import cpw.mods.fml.common.network.handshake.NetworkDispatcher; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +public class MultiWorld +{ + private static final Logger log = LogManager.getLogger(); + + private final MinecraftServer server; + private final TIntIntMap provTranslt = new TIntIntHashMap(); + private final TIntObjectMap dimToWorldMap = new TIntObjectHashMap(); + private final Map nameToWorldMap = new HashMap(); + private TIntSet isolatedDataDims; + private boolean serverLoaded; + + public MultiWorld(MinecraftServer server) + { + this.server = server; + } + + public void registerProviderTranslation(int src, int dst) + { + provTranslt.put(src, dst); + } + + void sendDimensionToAll(int dim, int pid) + { + if(!serverLoaded) + return; + FMLEmbeddedChannel channel = NetworkRegistry.INSTANCE.getChannel("FORGE", Side.SERVER); + channel.attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.ALL); + channel.writeAndFlush(new ForgeMessage.DimensionRegisterMessage(dim, provTranslt.containsKey(pid) ? provTranslt.get(pid) : pid)); + } + + @SubscribeEvent + public void onPlayerLoggedIn(FMLNetworkEvent.ServerConnectionFromClientEvent event) + { + FMLEmbeddedChannel channel = NetworkRegistry.INSTANCE.getChannel("FORGE", Side.SERVER); + channel.attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.DISPATCHER); + channel.attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(event.manager.channel().attr(NetworkDispatcher.FML_DISPATCHER).get()); + for (int dim : DimensionManager.getStaticDimensionIDs()) + { + int pid = DimensionManager.getProviderType(dim); + channel.writeAndFlush(new ForgeMessage.DimensionRegisterMessage(dim, provTranslt.containsKey(pid) ? provTranslt.get(pid) : pid)); + } + } + + @SubscribeEvent(priority = EventPriority.LOWEST) + public void onWorldUnload(WorldEvent.Unload e) + { + if(e.world.isRemote) + return; + WorldDescriptor desc = getDescByID(e.world.provider.dimensionId); + if(desc != null) + desc.onUnload(); + } + + @SideOnly(Side.SERVER) + public void handleServerWorldsInit() + { + DimensionManager.registerProviderType(-10, org.ultramine.server.world.WorldProviderEmpty.class, false); + registerProviderTranslation(-10, 0); + DimensionManager.unregisterDimension(-1); + DimensionManager.unregisterDimension(0); + DimensionManager.unregisterDimension(1); + + for(int dim : DimensionManager.getStaticDimensionIDs()) + { + WorldDescriptor desc = getOrCreateDescriptor(dim); + desc.setState(WorldState.UNLOADED); + desc.setConfig(ConfigurationHandler.getWorldsConfig().global); + } + + // + + TIntSet isolatedDataDimsSet = new TIntHashSet(); + checkDuplicates(ConfigurationHandler.getWorldsConfig().worlds); + for(WorldConfig config : ConfigurationHandler.getWorldsConfig().worlds) + { + WorldDescriptor desc = getOrCreateDescriptor(config.dimension); + if(config.name != null) + desc.setName(config.name); + + desc.setConfig(config); + if(desc.getState() == WorldState.UNREGISTERED) + desc.register(); + if(config.settings.useIsolatedPlayerData) + isolatedDataDimsSet.add(config.dimension); + } + isolatedDataDims = TCollections.unmodifiableSet(isolatedDataDimsSet); + + WorldDescriptor overDesc = getDescByID(0); + if(overDesc == null) + throw new RuntimeException("WorldDescriptor for OverWorld (dimension = 0) not found!"); + overDesc.weakLoad(); + + for(WorldDescriptor desc : nameToWorldMap.values()) + if(desc.getDimension() != 0) + desc.weakLoad(); + + serverLoaded = true; + } + + @SideOnly(Side.SERVER) + public void reloadServerWorlds() + { + TIntSet isolatedDataDimsSet = new TIntHashSet(isolatedDataDims); + checkDuplicates(ConfigurationHandler.getWorldsConfig().worlds); + for(WorldConfig config : ConfigurationHandler.getWorldsConfig().worlds) + { + WorldDescriptor desc = getOrCreateDescriptor(config.dimension); + if(config.name != null) + desc.setName(config.name); + + desc.setConfig(config); + if(desc.getState() == WorldState.UNREGISTERED) + desc.register(); + if(config.settings.useIsolatedPlayerData) + isolatedDataDimsSet.add(config.dimension); + else + isolatedDataDimsSet.remove(config.dimension); + } + isolatedDataDims = TCollections.unmodifiableSet(isolatedDataDimsSet); + } + + @SideOnly(Side.SERVER) + public void initDimension(int dim) + { + WorldDescriptor desc = getDescByID(dim); + if(desc == null && DimensionManager.isDimensionRegistered(dim)) + { + desc = getOrCreateDescriptor(dim); + desc.setState(WorldState.UNLOADED); + desc.setConfig(ConfigurationHandler.getWorldsConfig().global); + } + if(desc != null) + desc.weakLoad(); + } + + @SideOnly(Side.CLIENT) + public void handleClientWorldsInit() + { + for(WorldServer world : server.worldServers) + onClientInitDimension(world); + } + + @SideOnly(Side.CLIENT) + public void onClientInitDimension(WorldServer world) + { + WorldConfig conf = getDefaultClientConfig(world.provider.dimensionId); + world.setConfig(conf); + WorldDescriptor desc = getOrCreateDescriptor(world.provider.dimensionId); + desc.setConfig(conf); + desc.setState(WorldState.LOADED); + desc.setWorld(world); + } + + @SideOnly(Side.CLIENT) + private WorldConfig getDefaultClientConfig(int dim) + { + WorldConfig conf = new WorldConfig(); + conf.generation = new WorldConfig.Generation(); + conf.mobSpawn = new WorldConfig.MobSpawn(); + conf.settings = new WorldConfig.Settings(); + conf.chunkLoading = new WorldConfig.ChunkLoading(); + conf.portals = new WorldConfig.Portals(); + + conf.portals.netherLink = dim == -1 ? 0 : -1; + conf.portals.enderLink = dim == 1 ? 0 : 1; + + return conf; + } + + private void checkDuplicates(List list) + { + TIntObjectMap idMap = new TIntObjectHashMap(); + Map nameMap = new HashMap(); + + for(WorldConfig config : list) + { + { + WorldConfig prev = idMap.put(config.dimension, config); + if(prev != null) + throw new RuntimeException("Duplicate dimension ID in worlds.yml! Dimension: " + config.dimension + ", Names: "+prev.name + " " + config.name); + } + + if(config.name != null) + { + WorldConfig prev = nameMap.put(config.name, config); + if(prev != null) + throw new RuntimeException("Duplicate world names in worlds.yml! Name: " + prev.name + ", Dimensions: "+prev.dimension + " " + config.dimension); + } + } + } + + private WorldDescriptor getOrCreateDescriptor(int dim, String name) + { + WorldDescriptor desc = dimToWorldMap.get(dim); + if(desc == null) + { + desc = new WorldDescriptor(server, this, !server.isSinglePlayer() && ConfigurationHandler.getServerConfig().settings.other.splitWorldDirs, dim, name); + dimToWorldMap.put(dim, desc); + nameToWorldMap.put(desc.getName(), desc); + } + + return desc; + } + + private WorldDescriptor getOrCreateDescriptor(int dim) + { + return getOrCreateDescriptor(dim, "world_unnamed" + dim); + } + + void transitDescName(WorldDescriptor desc, String oldName, String newName) + { + if(desc.getState().isLoaded()) + throw new RuntimeException("Can not change name of loaded world"); + nameToWorldMap.remove(oldName); + nameToWorldMap.put(newName, desc); + } + + @SideOnly(Side.SERVER) + public int allocTempDim() + { + int dim = -2; + while(getDescByID(dim) != null || DimensionManager.isDimensionRegistered(dim)) + dim--; + if(dim < -128) + throw new RuntimeException("Not enough free dimension IDs"); + return dim; + } + + @SideOnly(Side.SERVER) + public WorldDescriptor makeTempWorld() + { + int dim = allocTempDim(); + return makeTempWorld("temp_"+dim, dim); + } + + @SideOnly(Side.SERVER) + public WorldDescriptor makeTempWorld(String name) + { + return makeTempWorld(name, allocTempDim()); + } + + @SideOnly(Side.SERVER) + public WorldDescriptor makeTempWorld(String name, int dim) + { + if(getDescByID(dim) != null || DimensionManager.isDimensionRegistered(dim)) + throw new RuntimeException("WorldDescriptor for dimension "+dim+" already registered (on making temp world)"); + + WorldDescriptor desc = getOrCreateDescriptor(dim, name); + desc.setConfig(ConfigurationHandler.getWorldsConfig().global); + + return desc; + } + + public WorldDescriptor getDescByID(int dim) + { + return dimToWorldMap.get(dim); + } + + public WorldDescriptor getDescByName(String name) + { + return nameToWorldMap.get(name); + } + + public WorldDescriptor getDescByNameOrID(String id) + { + return BasicTypeParser.isInt(id) ? dimToWorldMap.get(Integer.parseInt(id)) : nameToWorldMap.get(id); + } + + public WorldDescriptor getDescFromWorld(WorldServer world) + { + return getOrCreateDescriptor(world.provider.dimensionId); + } + + public WorldServer getWorldByID(int dim) + { + WorldDescriptor desc = getDescByID(dim); + return desc == null ? null : desc.getOrLoadWorld(); + } + + public WorldServer getWorldByName(String name) + { + WorldDescriptor desc = getDescByName(name); + return desc == null ? null : desc.getOrLoadWorld(); + } + + public WorldServer getWorldByNameOrID(String id) + { + WorldDescriptor desc = getDescByNameOrID(id); + return desc == null ? null : desc.getOrLoadWorld(); + } + + public Collection getAllNames() + { + return nameToWorldMap.keySet(); + } + + public Collection getAllDescs() + { + return nameToWorldMap.values(); + } + + public Collection getLoadedWorlds() + { + List worlds = new ArrayList(); + for(WorldDescriptor desc : nameToWorldMap.values()) + { + if(desc.getState().isLoaded()) + worlds.add(desc.getWorld()); + } + return worlds; + } + + public String getNameByID(int id) + { + return getDescByID(id).getName(); + } + + public WorldConfig getConfigByID(int dim) + { + WorldDescriptor desc = getDescByID(dim); + if(desc == null) + return server.isSinglePlayer() ? getDefaultClientConfig(dim) : ConfigurationHandler.getWorldsConfig().global; + return desc.getConfig(); + } + + public Collection getDirsForBackup() + { + if(!ConfigurationHandler.getServerConfig().settings.other.splitWorldDirs) + return Arrays.asList(getDescByID(0).getName()); + + List dirs = new ArrayList(); + for(WorldDescriptor desc : nameToWorldMap.values()) + { + if(desc.getState() == WorldState.LOADED || desc.getState() == WorldState.UNLOADED) + dirs.add(desc.getName()); + } + + return dirs; + } + + @SideOnly(Side.SERVER) + public Collection resolveSaveDirs(Collection names) + { + if(!ConfigurationHandler.getServerConfig().settings.other.splitWorldDirs) + return Arrays.asList(getDescByID(0).getName()); + + List dirs = new ArrayList(); + for(String name : names) + { + WorldDescriptor desc = getDescByNameOrID(name); + if(desc != null) + dirs.add(desc.getName()); + } + + return dirs; + } + + public String getSaveDirName(WorldServer world) + { + if(world instanceof WorldServerMulti) + return getDescByID(0).getName(); + + return getDescByID(world.provider.dimensionId).getName(); + } + + public TIntSet getIsolatedDataDims() + { + return isolatedDataDims; + } + + public void register() + { + FMLCommonHandler.instance().bus().register(this); + MinecraftForge.EVENT_BUS.register(this); + } + + public void unregister() + { + FMLCommonHandler.instance().bus().unregister(this); + MinecraftForge.EVENT_BUS.unregister(this); + dimToWorldMap.clear(); + nameToWorldMap.clear(); + } +} diff --git a/src/main/java/org/ultramine/server/world/WorldDescriptor.java b/src/main/java/org/ultramine/server/world/WorldDescriptor.java new file mode 100644 index 0000000..caa1495 --- /dev/null +++ b/src/main/java/org/ultramine/server/world/WorldDescriptor.java @@ -0,0 +1,364 @@ +package org.ultramine.server.world; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.scoreboard.ScorePlayerTeam; +import net.minecraft.server.MinecraftServer; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.WorldManager; +import net.minecraft.world.WorldServer; +import net.minecraft.world.WorldServerMulti; +import net.minecraft.world.WorldSettings; +import net.minecraft.world.WorldType; +import net.minecraft.world.chunk.storage.AnvilSaveHandler; +import net.minecraft.world.storage.ISaveFormat; +import net.minecraft.world.storage.ISaveHandler; +import net.minecraft.world.storage.WorldInfo; +import net.minecraftforge.common.DimensionManager; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.world.WorldEvent; + +import org.apache.commons.io.FileUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.ultramine.server.ConfigurationHandler; +import org.ultramine.server.Teleporter; +import org.ultramine.server.WorldsConfig.WorldConfig; +import org.ultramine.server.WorldsConfig.WorldConfig.MobSpawn.MobSpawnEngine; +import org.ultramine.server.WorldsConfig.WorldConfig.Settings.WorldTime; +import org.ultramine.server.util.BasicTypeParser; +import org.ultramine.server.util.WarpLocation; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +public class WorldDescriptor +{ + private static final Logger log = LogManager.getLogger(); + + private final MinecraftServer server; + private final MultiWorld mw; + private final boolean splitWorldDirs; + private final int dimension; + + private String name; + private File directory; + private WorldConfig config; + private WorldState state = WorldState.UNREGISTERED; + private WorldServer world; + + public WorldDescriptor(MinecraftServer server, MultiWorld mw, boolean splitWorldDirs, int dimension, String name) + { + this.server = server; + this.mw = mw; + this.splitWorldDirs = splitWorldDirs; + this.dimension = dimension; + this.name = name; + this.directory = new File(server.getWorldsDir(), name); + } + + public int getDimension() + { + return dimension; + } + + public String getName() + { + return name; + } + + void setName(String name) + { + if(!this.name.equals(name)) + { + this.mw.transitDescName(this, this.name, name); + this.name = name; + this.directory = new File(server.getWorldsDir(), this.name); + } + } + + public File getDirectory() + { + return this.directory; + } + + public WorldConfig getConfig() + { + return config; + } + + void setConfig(WorldConfig config) + { + this.config = config; + if(state.isLoaded()) + applyConfig(); + } + + public WorldState getState() + { + return state; + } + + void setState(WorldState state) + { + this.state = state; + } + + public WorldServer getWorld() + { + return world; + } + + void setWorld(WorldServer world) + { + this.world = world; + this.directory = world.getSaveHandler().getWorldDirectory(); + if(dimension != 0 && !splitWorldDirs) + this.directory = new File(this.directory, world.provider.getSaveFolder()); + } + + public WorldServer getOrLoadWorld() + { + if(state != WorldState.LOADED) + weakLoad(); + return world; + } + + public void register() + { + if(state != WorldState.UNREGISTERED) + throw new IllegalStateException("Dimension "+dimension+" already registered"); + if(config == null) + throw new IllegalStateException("Can not register dimension "+dimension+": world config == null!"); + if(DimensionManager.isDimensionRegistered(dimension)) + DimensionManager.unregisterDimension(dimension); + DimensionManager.registerDimension(dimension, config.generation.providerID); + setState(WorldState.UNLOADED); + mw.sendDimensionToAll(dimension, config.generation.providerID); + } + + @SideOnly(Side.SERVER) + public void forceLoad() + { + if(state == WorldState.UNREGISTERED) + register(); + load(); + } + + @SideOnly(Side.SERVER) + public void weakLoad() + { + if(state == WorldState.HELD || state == WorldState.UNREGISTERED) + return; + + load(); + } + + @SideOnly(Side.SERVER) + private void load() + { + if(state.isLoaded()) + throw new RuntimeException("Dimension ["+dimension+"] is already loaded"); + + ISaveFormat format = server.getActiveAnvilConverter(); + if(config == null) + { + log.warn("World with dimension id:{} was loaded bypass worlds configuration. Using global config", dimension); + config = ConfigurationHandler.getWorldsConfig().global; + } + + WorldServer world; + if(dimension == 0) + { + ISaveHandler mainSaveHandler = format.getSaveLoader(name, true); + WorldInfo mainWorldInfo = mainSaveHandler.loadWorldInfo(); + WorldSettings mainSettings = makeSettings(mainWorldInfo, config); + + world = new WorldServer(server, mainSaveHandler, name, dimension, mainSettings, server.theProfiler); + } + else if(splitWorldDirs) + { + ISaveHandler save = format.getSaveLoader(name, false); + ((AnvilSaveHandler)save).setSingleStorage(); + world = new WorldServer(server, save, name, dimension, makeSettings(save.loadWorldInfo(), config), server.theProfiler); + } + else + { + WorldServer mainWorld = mw.getWorldByID(0); + ISaveHandler mainSaveHandler = mainWorld.getSaveHandler(); + WorldInfo mainWorldInfo = mainWorld.getWorldInfo(); + world = new WorldServerMulti(server, mainSaveHandler, mainWorldInfo.getWorldName(), dimension, makeSettings(mainWorldInfo, config), mainWorld, server.theProfiler); + } + + setWorld(world); + initWorld(); + setState(WorldState.LOADED); + } + + @SideOnly(Side.SERVER) + private WorldSettings makeSettings(WorldInfo wi, WorldConfig conf) + { + WorldSettings mainSettings; + + if (wi == null) + { + mainSettings = new WorldSettings(toSeed(conf.generation.seed), server.getGameType(), conf.generation.generateStructures, + server.isHardcore(), WorldType.parseWorldType(conf.generation.levelType)); + mainSettings.func_82750_a(conf.generation.generatorSettings); + } + else + { + mainSettings = new WorldSettings(wi); + } + + return mainSettings; + } + + private static long toSeed(String seedstr) + { + try + { + return Long.parseLong(seedstr); + } + catch (NumberFormatException e) + { + return seedstr.hashCode(); + } + } + + @SideOnly(Side.SERVER) + private void initWorld() + { + world.addWorldAccess(new WorldManager(server, world)); + world.getWorldInfo().setGameType(server.getGameType()); + applyConfig(); + MinecraftForge.EVENT_BUS.post(new WorldEvent.Load(world)); + } + + @SideOnly(Side.SERVER) + private void applyConfig() + { + world.difficultySetting = BasicTypeParser.parseDifficulty(config.settings.difficulty); + world.setAllowedSpawnTypes(config.mobSpawn.spawnMonsters, config.mobSpawn.spawnAnimals); + world.getGameRules().setOrCreateGameRule("doDaylightCycle", Boolean.toString(config.settings.time != WorldTime.FIXED)); + world.getGameRules().setOrCreateGameRule("doMobSpawning", Boolean.toString(config.mobSpawn.spawnEngine != MobSpawnEngine.NONE)); + world.setConfig(config); + } + + void onUnload() + { + ((WorldServer)world).theChunkProviderServer.setWorldUnloaded(); + world = null; + if(getState().isLoaded()) + setState(WorldState.UNLOADED); + } + + public List extractPlayer() + { + if(!state.isLoaded()) + return Collections.emptyList(); + + @SuppressWarnings("unchecked") + List players = new ArrayList(world.playerEntities); + for(EntityPlayerMP player : players) + { + world.removePlayerEntityDangerously(player); + player.isDead = false; + world.getEntityTracker().removePlayerFromTrackers(player); + world.getPlayerManager().removePlayer(player); + player.getChunkMgr().setWorldDestroyed(); + player.setWorld(null); + player.theItemInWorldManager.setWorld(null); + } + world.playerEntities.clear(); + + return players; + } + + @SideOnly(Side.SERVER) + public void hold() + { + if(getState().isLoaded()) + unload(); + setState(WorldState.HELD); + } + + @SideOnly(Side.SERVER) + public void unload() + { + if(!getState().isLoaded()) + return; + if(!world.playerEntities.isEmpty()) + movePlayersOut(); + + DimensionManager.unloadWorld(dimension); + } + + @SuppressWarnings("unchecked") + public void destroyWorld() + { + if(!getState().isLoaded()) + return; + if(!world.playerEntities.isEmpty()) + movePlayersOut(); + + WorldServer world = this.world; + if(world.provider.dimensionId == 0) + for(ScorePlayerTeam team : new ArrayList(world.getScoreboard().getTeams())) + world.getScoreboard().removeTeam(team); + + world.theChunkProviderServer.setWorldUnloaded(); + world.theChunkProviderServer.unloadAll(false); + world.forceUnloadTileEntities(); + + MinecraftForge.EVENT_BUS.post(new WorldEvent.Unload(world)); + DimensionManager.setWorld(world.provider.dimensionId, null); + world.theChunkProviderServer.loadedChunkHashMap.clear(); + for(Object o : world.loadedTileEntityList) + ((TileEntity)o).setWorldObj(null); + world.loadedTileEntityList.clear(); + } + + @SideOnly(Side.SERVER) + public void deleteWorld() + { + if(state.isLoaded()) + destroyWorld(); + + try + { + FileUtils.cleanDirectory(getDirectory()); + } + catch(IOException e) + { + throw new RuntimeException(e); + } + } + + private void movePlayersOut() + { + WarpLocation spawn = server.getConfigurationManager().getDataLoader().getWarp("spawn"); + @SuppressWarnings("unchecked") + List players = new ArrayList(world.playerEntities); + for(EntityPlayerMP player : players) + { + if(player.dimension == spawn.dimension) + { + player.playerNetServerHandler.kickPlayerFromServer("The world has been unloaded"); + world.removePlayerEntityDangerously(player); + player.isDead = false; + world.getEntityTracker().removePlayerFromTrackers(player); + world.getPlayerManager().removePlayer(player); + } + else + { + Teleporter.tpNow(player, spawn); + } + } + } +} diff --git a/src/main/java/org/ultramine/server/world/WorldProviderEmpty.java b/src/main/java/org/ultramine/server/world/WorldProviderEmpty.java new file mode 100644 index 0000000..fe738bd --- /dev/null +++ b/src/main/java/org/ultramine/server/world/WorldProviderEmpty.java @@ -0,0 +1,20 @@ +package org.ultramine.server.world; + +import net.minecraft.world.WorldProvider; +import net.minecraft.world.chunk.IChunkProvider; + +public class WorldProviderEmpty extends WorldProvider +{ + + public IChunkProvider createChunkGenerator() + { + return new ChunkProviderEmpty(worldObj); + } + + @Override + public String getDimensionName() + { + return "EmptyWorld"; + } + +} diff --git a/src/main/java/org/ultramine/server/world/WorldState.java b/src/main/java/org/ultramine/server/world/WorldState.java new file mode 100644 index 0000000..3517371 --- /dev/null +++ b/src/main/java/org/ultramine/server/world/WorldState.java @@ -0,0 +1,18 @@ +package org.ultramine.server.world; + +public enum WorldState +{ + LOADED(true), UNLOADED(false), HELD(false), UNREGISTERED(false); + + private final boolean isLoaded; + + private WorldState(boolean isLoaded) + { + this.isLoaded = isLoaded; + } + + public boolean isLoaded() + { + return isLoaded; + } +} diff --git a/src/main/resources/org/ultramine/defaults/defaultworlds.yml b/src/main/resources/org/ultramine/defaults/defaultworlds.yml index 56ebb5e..95f3bfa 100644 --- a/src/main/resources/org/ultramine/defaults/defaultworlds.yml +++ b/src/main/resources/org/ultramine/defaults/defaultworlds.yml @@ -97,9 +97,9 @@ higherLimit: 16 worlds: - world: - <<: *global + - <<: *global dimension: 0 + name: 'world' generation: <<: *global_gen providerID: 0 @@ -109,17 +109,17 @@ portals: netherLink: -1 enderLink: 1 - world_nether: - <<: *global + - <<: *global dimension: -1 + name: 'world_nether' generation: <<: *global_gen providerID: -1 portals: netherLink: 0 - world_the_end: - <<: *global + - <<: *global dimension: 1 + name: 'world_the_end' generation: <<: *global_gen providerID: 1