diff --git a/src/main/java/net/minecraft/server/management/ServerConfigurationManager.java b/src/main/java/net/minecraft/server/management/ServerConfigurationManager.java index a3ca810..9fa3884 100644 --- a/src/main/java/net/minecraft/server/management/ServerConfigurationManager.java +++ b/src/main/java/net/minecraft/server/management/ServerConfigurationManager.java @@ -420,6 +420,11 @@ public EntityPlayerMP respawnPlayer(EntityPlayerMP p_72368_1_, int p_72368_2_, boolean p_72368_3_) { + int oldDim = p_72368_1_.dimension; + boolean respawnOnBed = (getServerInstance().isSinglePlayer() || ConfigurationHandler.getServerConfig().settings.spawnLocations.respawnOnBed); + WarpLocation spawnWarp = getDataLoader().getWarp(getServerInstance().isSinglePlayer() ? "spawn" : ConfigurationHandler.getServerConfig().settings.spawnLocations.deathSpawn); + WarpLocation spawn = (spawnWarp != null ? spawnWarp : getDataLoader().getWarp("spawn")).randomize(); + World world = mcServer.worldServerForDimension(p_72368_2_); if (world == null) { @@ -435,7 +440,9 @@ p_72368_1_.getServerForPlayer().getPlayerManager().removePlayer(p_72368_1_); this.playerEntityList.remove(p_72368_1_); this.mcServer.worldServerForDimension(p_72368_1_.dimension).removePlayerEntityDangerously(p_72368_1_); - ChunkCoordinates chunkcoordinates = p_72368_1_.getBedLocation(p_72368_2_); + ChunkCoordinates chunkcoordinates = respawnOnBed ? p_72368_1_.getBedLocation(p_72368_2_) : null; + if(chunkcoordinates == null) + p_72368_2_ = spawn.dimension; boolean flag1 = p_72368_1_.isSpawnForced(p_72368_2_); p_72368_1_.dimension = p_72368_2_; Object object; @@ -450,8 +457,6 @@ } EntityPlayerMP entityplayermp1 = new EntityPlayerMP(this.mcServer, this.mcServer.worldServerForDimension(p_72368_1_.dimension), p_72368_1_.getGameProfile(), (ItemInWorldManager)object); - WarpLocation spawnWarp = getDataLoader().getWarp(getServerInstance().isSinglePlayer() ? "spawn" : ConfigurationHandler.getServerConfig().settings.spawnLocations.deathSpawn); - WarpLocation spawn = (spawnWarp != null ? spawnWarp : getDataLoader().getWarp("spawn")).randomize(); entityplayermp1.setLocationAndAngles(spawn.x, spawn.y, spawn.z, spawn.yaw, spawn.pitch); entityplayermp1.playerNetServerHandler = p_72368_1_.playerNetServerHandler; entityplayermp1.clonePlayer(p_72368_1_, p_72368_3_); @@ -459,9 +464,10 @@ entityplayermp1.setEntityId(p_72368_1_.getEntityId()); WorldServer worldserver = this.mcServer.worldServerForDimension(p_72368_1_.dimension); this.func_72381_a(entityplayermp1, p_72368_1_, worldserver); + getDataLoader().handleRespawn(p_72368_1_, entityplayermp1, oldDim, p_72368_2_); ChunkCoordinates chunkcoordinates1; - if (chunkcoordinates != null && (getServerInstance().isSinglePlayer() || ConfigurationHandler.getServerConfig().settings.spawnLocations.respawnOnBed)) + if (chunkcoordinates != null) { chunkcoordinates1 = EntityPlayer.verifyRespawnCoordinates(this.mcServer.worldServerForDimension(p_72368_1_.dimension), chunkcoordinates, flag1); diff --git a/src/main/java/org/ultramine/server/MultiWorld.java b/src/main/java/org/ultramine/server/MultiWorld.java index 50d2a22..577a155 100644 --- a/src/main/java/org/ultramine/server/MultiWorld.java +++ b/src/main/java/org/ultramine/server/MultiWorld.java @@ -19,8 +19,11 @@ 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.TIntObjectMap; import gnu.trove.map.hash.TIntObjectHashMap; +import gnu.trove.set.TIntSet; +import gnu.trove.set.hash.TIntHashSet; import net.minecraft.server.MinecraftServer; import net.minecraft.world.WorldManager; import net.minecraft.world.WorldServer; @@ -39,10 +42,11 @@ public class MultiWorld { private static final Logger log = LogManager.getLogger(); - private final TIntObjectHashMap dimToNameMap = new TIntObjectHashMap(); + private final MinecraftServer server; + private final TIntObjectMap dimToNameMap = new TIntObjectHashMap(); private final TIntObjectMap dimToWorldMap = new TIntObjectHashMap(); private final Map nameToWorldMap = new HashMap(); - private final MinecraftServer server; + private TIntSet isolatedDataDims; public MultiWorld(MinecraftServer server) { @@ -78,15 +82,20 @@ DimensionManager.unregisterDimension(1); Map worlds = ConfigurationHandler.getWorldsConfig().worlds; + TIntSet isolatedDataDimsSet = new TIntHashSet(); for(Map.Entry ent : worlds.entrySet()) { WorldConfig conf = ent.getValue(); DimensionManager.registerDimension(conf.dimension, conf.generation.providerID); + if(conf.settings.useIsolatedPlayerData) + isolatedDataDimsSet.add(conf.dimension); dimToNameMap.put(conf.dimension, ent.getKey()); } + isolatedDataDims = TCollections.unmodifiableSet(isolatedDataDimsSet); + String mainWorldName = dimToNameMap.get(0); if(mainWorldName == null) mainWorldName = "world"; @@ -95,6 +104,14 @@ 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); WorldSettings mainSettings = makeSettings(mainSaveHandler, mainConf); @@ -118,7 +135,7 @@ } else { - ISaveHandler save = format.getSaveLoader(name, false); + ISaveHandler save = format.getSaveLoader(name, usingPlayerDir && conf.settings.useIsolatedPlayerData); ((AnvilSaveHandler)save).setSingleStorage(); world = new WorldServer(server, save, name, dim, makeSettings(save, conf), server.theProfiler); } @@ -248,6 +265,11 @@ return nameToWorldMap.keySet(); } + public TIntSet getIsolatedDataDims() + { + return isolatedDataDims; + } + public void register() { FMLCommonHandler.instance().bus().register(this); diff --git a/src/main/java/org/ultramine/server/UMEventHandler.java b/src/main/java/org/ultramine/server/UMEventHandler.java index 96ee954..dc79a63 100644 --- a/src/main/java/org/ultramine/server/UMEventHandler.java +++ b/src/main/java/org/ultramine/server/UMEventHandler.java @@ -3,9 +3,12 @@ import org.ultramine.server.util.BasicTypeParser; import org.ultramine.server.util.WarpLocation; +import cpw.mods.fml.common.eventhandler.EventPriority; import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.common.gameevent.PlayerEvent.PlayerChangedDimensionEvent; import cpw.mods.fml.common.gameevent.TickEvent; import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.server.MinecraftServer; import net.minecraft.util.ChatComponentStyle; import net.minecraft.util.ChatComponentText; import net.minecraft.util.ChatComponentTranslation; @@ -47,6 +50,7 @@ if(e.entityPlayer.isEntityPlayerMP()) { ((EntityPlayerMP)e.entityPlayer).setData(((EntityPlayerMP)e.original).getData()); + ((EntityPlayerMP)e.entityPlayer).setStatisticsFile(MinecraftServer.getServer().getConfigurationManager().func_152602_a(e.entityPlayer)); } } @@ -62,4 +66,10 @@ player.getData().core().setLastLocation(WarpLocation.getFromPlayer(player)); } } + + @SubscribeEvent(priority = EventPriority.HIGH) + public void onPlayerChangedDimension(PlayerChangedDimensionEvent e) + { + MinecraftServer.getServer().getConfigurationManager().getDataLoader().handlePlayerDimensionChange((EntityPlayerMP)e.player, e.fromDim, e.toDim); + } } diff --git a/src/main/java/org/ultramine/server/WorldsConfig.java b/src/main/java/org/ultramine/server/WorldsConfig.java index 96699d0..9e30f57 100644 --- a/src/main/java/org/ultramine/server/WorldsConfig.java +++ b/src/main/java/org/ultramine/server/WorldsConfig.java @@ -40,6 +40,7 @@ public int maxBuildHeight = 256; public WorldTime time = WorldTime.NORMAL; public Weather weather = Weather.NORMAL; + public boolean useIsolatedPlayerData = false; public enum WorldTime { diff --git a/src/main/java/org/ultramine/server/data/Databases.java b/src/main/java/org/ultramine/server/data/Databases.java index 2a71538..f1e9101 100644 --- a/src/main/java/org/ultramine/server/data/Databases.java +++ b/src/main/java/org/ultramine/server/data/Databases.java @@ -29,8 +29,8 @@ ds.setUrl(info.url); ds.setUsername(info.username); ds.setPassword(info.password); - - ds.setMaxActive(info.maxConnections); + if(info.maxConnections > 0) + ds.setMaxActive(info.maxConnections); databases.put(ent.getKey(), ds); } diff --git a/src/main/java/org/ultramine/server/data/IDataProvider.java b/src/main/java/org/ultramine/server/data/IDataProvider.java index 6681d55..cd786b2 100644 --- a/src/main/java/org/ultramine/server/data/IDataProvider.java +++ b/src/main/java/org/ultramine/server/data/IDataProvider.java @@ -18,10 +18,16 @@ { void init(); + boolean isUsingWorldPlayerDir(); + NBTTagCompound loadPlayer(GameProfile player); + NBTTagCompound loadPlayer(int dim, GameProfile player); + void savePlayer(GameProfile player, NBTTagCompound nbt); + void savePlayer(int dim, GameProfile player, NBTTagCompound nbt); + PlayerData loadPlayerData(GameProfile player); List loadAllPlayerData(); diff --git a/src/main/java/org/ultramine/server/data/JDBCDataProvider.java b/src/main/java/org/ultramine/server/data/JDBCDataProvider.java index 568d522..15285ee 100644 --- a/src/main/java/org/ultramine/server/data/JDBCDataProvider.java +++ b/src/main/java/org/ultramine/server/data/JDBCDataProvider.java @@ -83,8 +83,10 @@ s.execute("CREATE TABLE IF NOT EXISTS `"+tab_player_gamedata+"` (" + "`pid` int(11) unsigned NOT NULL AUTO_INCREMENT," + + "`forDim` int(11) NOT NULL," + + "`curDim` int(11) NOT NULL," + "`data` blob NOT NULL," - + "PRIMARY KEY (`pid`)" + + "PRIMARY KEY (`pid`, `forDim`)" + ") ENGINE=InnoDB"); s.execute("CREATE TABLE IF NOT EXISTS `"+tab_player_data+"` (" @@ -141,10 +143,22 @@ close(conn, ps, rs); } } - + + @Override + public boolean isUsingWorldPlayerDir() + { + return false; + } + @Override public NBTTagCompound loadPlayer(GameProfile player) { + return loadPlayer(0, player); + } + + @Override + public NBTTagCompound loadPlayer(int dim, GameProfile player) + { int id = playerIDs.get(player.getId()); if(id == -1) return null; @@ -155,12 +169,15 @@ try { conn = ds.getConnection(); - ps = conn.prepareStatement("SELECT `data` FROM `"+tab_player_gamedata+"` WHERE `pid`=?"); + ps = conn.prepareStatement("SELECT `data`, `curDim` FROM `"+tab_player_gamedata+"` WHERE `pid`=? AND `forDim`=?"); ps.setInt(1, id); + ps.setInt(2, dim); rs = ps.executeQuery(); if(!rs.next()) return null; - return CompressedStreamTools.readCompressed(new ByteArrayInputStream(rs.getBytes("data"))); + NBTTagCompound nbt = CompressedStreamTools.readCompressed(new ByteArrayInputStream(rs.getBytes("data"))); + nbt.setInteger("Dimension", rs.getInt("curDim")); + return nbt; } catch(Exception e) { @@ -177,6 +194,12 @@ @Override public void savePlayer(final GameProfile player, final NBTTagCompound nbt) { + savePlayer(0, player, nbt); + } + + @Override + public void savePlayer(final int dim, final GameProfile player, final NBTTagCompound nbt) + { GlobalExecutors.writingIOExecutor().execute(new Runnable() { @Override @@ -191,11 +214,12 @@ conn = ds.getConnection(); if(id == -1) id = createPlayerID(conn, player); - ps = conn.prepareStatement("INSERT INTO `"+tab_player_gamedata+"` (`pid`, `data`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `data`=?"); + ps = conn.prepareStatement("INSERT INTO `"+tab_player_gamedata+"` (`pid`, `data`, `forDim`, `curDim`) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE " + + "`data`=values(data), `forDim`=values(forDim), `curDim`=values(curDim)"); ps.setInt(1, id); - byte[] data = CompressedStreamTools.compress(nbt); - ps.setBytes(2, data); - ps.setBytes(3, data); + ps.setBytes(2, CompressedStreamTools.compress(nbt)); + ps.setInt(3, dim); + ps.setInt(4, nbt.getInteger("Dimension")); ps.executeUpdate(); } catch(Exception e) @@ -510,7 +534,7 @@ } }); } - + private int createPlayerID(Connection conn, GameProfile player) throws SQLException { PreparedStatement ps = null; diff --git a/src/main/java/org/ultramine/server/data/NBTFileDataProvider.java b/src/main/java/org/ultramine/server/data/NBTFileDataProvider.java index c7d6428..1f73f5a 100644 --- a/src/main/java/org/ultramine/server/data/NBTFileDataProvider.java +++ b/src/main/java/org/ultramine/server/data/NBTFileDataProvider.java @@ -50,29 +50,33 @@ } @Override + public boolean isUsingWorldPlayerDir() + { + return true; + } + + @Override public NBTTagCompound loadPlayer(GameProfile player) { - File dir = ((SaveHandler)mgr.getPlayerNBTLoader()).getPlayerSaveDir(); - File file = new File(dir, player.getId().toString() + ".dat"); - if(file.exists()) - { - try - { - return CompressedStreamTools.readCompressed(new FileInputStream(file)); - } - catch(IOException e) - { - log.warn("Failed to load player data for " + player.getName(), e); - } - } - - return null; + return loadPlayer((SaveHandler)mgr.getPlayerNBTLoader(), player); } - + + @Override + public NBTTagCompound loadPlayer(int dim, GameProfile player) + { + return loadPlayer((SaveHandler)mgr.getServerInstance().getMultiWorld().getWorldByID(dim).getSaveHandler(), player); + } + @Override public void savePlayer(GameProfile player, NBTTagCompound nbt) { - AsyncIOUtils.safeWriteNBT(new File(((SaveHandler)mgr.getPlayerNBTLoader()).getPlayerSaveDir(), player.getId().toString() + ".dat"), nbt); + savePlayer((SaveHandler)mgr.getPlayerNBTLoader(), player, nbt); + } + + @Override + public void savePlayer(int dim, GameProfile player, NBTTagCompound nbt) + { + savePlayer((SaveHandler)mgr.getServerInstance().getMultiWorld().getWorldByID(dim).getSaveHandler(), player, nbt); } @Override @@ -162,6 +166,30 @@ writeWarpList(); } + public NBTTagCompound loadPlayer(SaveHandler sh, GameProfile player) + { + File dir = sh.getPlayerSaveDir(); + File file = new File(dir, player.getId().toString() + ".dat"); + if(file.exists()) + { + try + { + return CompressedStreamTools.readCompressed(new FileInputStream(file)); + } + catch(IOException e) + { + log.warn("Failed to load player data for " + player.getName(), e); + } + } + + return null; + } + + public void savePlayer(SaveHandler sh, GameProfile player, NBTTagCompound nbt) + { + AsyncIOUtils.safeWriteNBT(new File(sh.getPlayerSaveDir(), player.getId().toString() + ".dat"), nbt); + } + private NBTTagCompound getPlayerDataNBT(String username) { try diff --git a/src/main/java/org/ultramine/server/data/ServerDataLoader.java b/src/main/java/org/ultramine/server/data/ServerDataLoader.java index 1c64820..fbcc0a3 100644 --- a/src/main/java/org/ultramine/server/data/ServerDataLoader.java +++ b/src/main/java/org/ultramine/server/data/ServerDataLoader.java @@ -1,5 +1,7 @@ package org.ultramine.server.data; +import gnu.trove.set.TIntSet; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -25,6 +27,7 @@ import net.minecraft.network.NetworkManager; import net.minecraft.server.management.ServerConfigurationManager; import net.minecraft.stats.StatisticsFile; +import net.minecraft.world.WorldServer; import net.minecraft.world.storage.SaveHandler; import net.minecraft.world.storage.WorldInfo; import net.minecraftforge.event.ForgeEventFactory; @@ -34,7 +37,7 @@ private static final boolean isClient = FMLCommonHandler.instance().getSide().isClient(); private final TwoStepsExecutor executor = isClient ? null : new TwoStepsExecutor("PlayerData loader #%d"); private final ServerConfigurationManager mgr; - private IDataProvider dataProvider; + private final IDataProvider dataProvider; private final List dataExtinfos = new ArrayList(); private final Map playerDataCache = new HashMap(); private final Map namedPlayerDataCache = new HashMap(); @@ -44,6 +47,7 @@ public ServerDataLoader(ServerConfigurationManager mgr) { this.mgr = mgr; + dataProvider = isClient || !ConfigurationHandler.getServerConfig().settings.inSQLServerStorage.enabled ? new NBTFileDataProvider(mgr) : new JDBCDataProvider(mgr); } public IDataProvider getDataProvider() @@ -119,7 +123,6 @@ public void loadCache() { - dataProvider = isClient || !ConfigurationHandler.getServerConfig().settings.inSQLServerStorage.enabled ? new NBTFileDataProvider(mgr) : new JDBCDataProvider(mgr); dataProvider.init(); if(!isClient) executor.register(); @@ -166,12 +169,19 @@ final GameProfile profile = player.getGameProfile(); final boolean loadData = !playerDataCache.containsKey(player.getGameProfile().getId()); final StatisticsFile loadedStats = mgr.func_152602_a(player); + final TIntSet isolatedDataDims = mgr.getServerInstance().getMultiWorld().getIsolatedDataDims(); executor.execute(new Function() { @Override public LoadedDataStruct apply(Void input) //async { - NBTTagCompound nbt = getDataProvider().loadPlayer(profile); + NBTTagCompound nbt = getDataProvider().loadPlayer(profile); + if(nbt != null) + { + int dim = nbt.getInteger("Dimension"); + if(dim != 0 && isolatedDataDims.contains(dim)) + nbt = getDataProvider().loadPlayer(dim, profile); + } PlayerData data = loadData ? getDataProvider().loadPlayerData(profile) : null; StatisticsFile stats = loadedStats != null ? loadedStats : mgr.loadStatisticsFile_Async(profile); return new LoadedDataStruct(nbt, data, stats); @@ -221,10 +231,109 @@ NBTTagCompound nbt = new NBTTagCompound(); player.writeToNBT(nbt); - getDataProvider().savePlayer(player.getGameProfile(), nbt); + if(player.getServerForPlayer().getConfig().settings.useIsolatedPlayerData) + getDataProvider().savePlayer(player.worldObj.provider.dimensionId, player.getGameProfile(), nbt); + else + getDataProvider().savePlayer(player.getGameProfile(), nbt); getDataProvider().savePlayerData(player.getData()); } + public void handlePlayerDimensionChange(EntityPlayerMP player, int fromDim, int toDim) + { + WorldServer from = mgr.getServerInstance().getMultiWorld().getWorldByID(fromDim); + WorldServer to = mgr.getServerInstance().getMultiWorld().getWorldByID(toDim); + + boolean fromIs = from.getConfig().settings.useIsolatedPlayerData; + boolean toIs = to.getConfig().settings.useIsolatedPlayerData; + + if(fromIs || toIs) + { + NBTTagCompound nbt = new NBTTagCompound(); + player.writeToNBT(nbt); + if(fromIs) + dataProvider.savePlayer(fromDim, player.getGameProfile(), nbt); + else// if(toIs) + dataProvider.savePlayer(player.getGameProfile(), nbt); + + loadIsolatedData(player, toDim, toIs, true); + } + } + + public void handleRespawn(EntityPlayerMP dead, EntityPlayerMP created, int oldDim, int newDim) + { + WorldServer from = mgr.getServerInstance().getMultiWorld().getWorldByID(oldDim); + WorldServer to = mgr.getServerInstance().getMultiWorld().getWorldByID(newDim); + + boolean fromIs = from.getConfig().settings.useIsolatedPlayerData; + boolean toIs = to.getConfig().settings.useIsolatedPlayerData; + + if(fromIs || toIs) + { + if(fromIs) + dataProvider.savePlayer(oldDim, dead.getGameProfile(), new NBTTagCompound()); + + loadIsolatedData(created, newDim, toIs, false); + } + } + + private void loadIsolatedData(final EntityPlayerMP player, final int toDim, final boolean toIs, boolean replaceToNull) + { + final GameProfile profile = player.getGameProfile(); + if(isClient) + { + NBTTagCompound nbt; + if(toIs) + nbt = dataProvider.loadPlayer(toDim, profile); + else// if(fromIs) + nbt = dataProvider.loadPlayer(profile); + applyIsolatedData(player, nbt); + } + else + { + if(replaceToNull) + applyIsolatedData(player, null); + + executor.execute(new Function() + { + @Override + public NBTTagCompound apply(Void input) //async + { + if(toIs) + return dataProvider.loadPlayer(toDim, profile); + else// if(fromIs) + return dataProvider.loadPlayer(profile); + } + }, new Function() + { + @Override + public Void apply(NBTTagCompound nbt) //sync + { + player.inventory.dropAllItems(); + applyIsolatedData(player, nbt); + return null; + } + }); + } + } + + private void applyIsolatedData(EntityPlayerMP player, NBTTagCompound nbt) + { + double x = player.posX; + double y = player.posY; + double z = player.posZ; + float yaw = player.rotationYaw; + float pitch = player.rotationPitch; + + player.readFromNBT(nbt != null ? nbt : new NBTTagCompound()); + + player.dimension = player.worldObj.provider.dimensionId; + player.prevPosX = player.lastTickPosX = player.posX = x; + player.prevPosY = player.lastTickPosY = player.posY = y; + player.prevPosZ = player.lastTickPosZ = player.posZ = z; + player.prevRotationYaw = player.rotationYaw = yaw; + player.prevRotationPitch = player.rotationPitch = pitch; + } + public void registerPlayerDataExt(Class clazz, String nbtTagName) { dataExtinfos.add(new PlayerDataExtensionInfo(clazz, nbtTagName)); diff --git a/src/main/resources/org/ultramine/defaults/defaultworlds.yml b/src/main/resources/org/ultramine/defaults/defaultworlds.yml index 39ba8ea..f386829 100644 --- a/src/main/resources/org/ultramine/defaults/defaultworlds.yml +++ b/src/main/resources/org/ultramine/defaults/defaultworlds.yml @@ -16,6 +16,7 @@ pvp: true time: NORMAL weather: NORMAL + useIsolatedPlayerData: false chunkLoading: &global_cl viewDistance: 10 chunkUpdateRadius: 7