diff --git a/src/main/java/net/minecraft/entity/player/EntityPlayerMP.java b/src/main/java/net/minecraft/entity/player/EntityPlayerMP.java index 2a649a9..5d72871 100644 --- a/src/main/java/net/minecraft/entity/player/EntityPlayerMP.java +++ b/src/main/java/net/minecraft/entity/player/EntityPlayerMP.java @@ -104,6 +104,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.ultramine.server.chunk.ChunkSendManager; +import org.ultramine.server.data.player.PlayerData; import net.minecraft.entity.item.EntityItem; import net.minecraftforge.common.ForgeHooks; @@ -989,6 +990,7 @@ private int renderDistance; private final ChunkSendManager chunkMgr = new ChunkSendManager(this); + private PlayerData playerData; @Override public boolean isEntityPlayerMP() @@ -1006,6 +1008,16 @@ return renderDistance; } + public PlayerData getData() + { + return playerData; + } + + public void setData(PlayerData playerData) + { + this.playerData = playerData; + } + /** * Переносит игрока в другой мир без использования порталов. Обратите * внимение: сначала нужно установить координаты назначения @@ -1018,4 +1030,4 @@ this.lastHealth = -1.0F; this.lastFoodLevel = -1; } -} \ No newline at end of file +} diff --git a/src/main/java/net/minecraft/server/management/ServerConfigurationManager.java b/src/main/java/net/minecraft/server/management/ServerConfigurationManager.java index ab19507..9d5ee24 100644 --- a/src/main/java/net/minecraft/server/management/ServerConfigurationManager.java +++ b/src/main/java/net/minecraft/server/management/ServerConfigurationManager.java @@ -113,9 +113,14 @@ this.bannedIPs.func_152686_a(false); this.maxPlayers = 8; } - + public void initializeConnectionToPlayer(NetworkManager p_72355_1_, EntityPlayerMP p_72355_2_, NetHandlerPlayServer nethandlerplayserver) { + serverDataLoader.initializeConnectionToPlayer(p_72355_1_, p_72355_2_, nethandlerplayserver); + } + + public void initializeConnectionToPlayer_body(NetworkManager p_72355_1_, EntityPlayerMP p_72355_2_, NetHandlerPlayServer nethandlerplayserver) + { GameProfile gameprofile = p_72355_2_.getGameProfile(); PlayerProfileCache playerprofilecache = this.mcServer.func_152358_ax(); GameProfile gameprofile1 = playerprofilecache.func_152652_a(gameprofile.getId()); @@ -280,7 +285,7 @@ { if (p_72391_1_.playerNetServerHandler == null) return; - this.playerNBTManagerObj.writePlayerData(p_72391_1_); + this.getDataLoader().savePlayer(p_72391_1_); StatisticsFile statisticsfile = (StatisticsFile)this.field_148547_k.get(p_72391_1_.getUniqueID()); if (statisticsfile != null) @@ -1130,4 +1135,18 @@ { return this.whiteListEnforced; } + + /* ======================================== ULTRAMINE START =====================================*/ + + private final ServerDataLoader serverDataLoader = new ServerDataLoader(this); + + public ServerDataLoader getDataLoader() + { + return serverDataLoader; + } + + public IPlayerFileData getPlayerNBTLoader() + { + return playerNBTManagerObj; + } } diff --git a/src/main/java/net/minecraft/stats/StatisticsFile.java b/src/main/java/net/minecraft/stats/StatisticsFile.java index 9174f1e..d96c955 100644 --- a/src/main/java/net/minecraft/stats/StatisticsFile.java +++ b/src/main/java/net/minecraft/stats/StatisticsFile.java @@ -6,6 +6,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonParser; + import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; @@ -15,6 +16,7 @@ import java.util.Map; import java.util.Set; import java.util.Map.Entry; + import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.network.play.server.S37PacketStatistics; @@ -22,9 +24,11 @@ import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.IJsonSerializable; import net.minecraft.util.TupleIntJsonSerializable; + import org.apache.commons.io.FileUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.ultramine.server.util.AsyncIOUtils; public class StatisticsFile extends StatFileWriter { @@ -64,14 +68,14 @@ public void func_150883_b() { - try - { - FileUtils.writeStringToFile(this.field_150887_d, func_150880_a(this.field_150875_a)); - } - catch (IOException ioexception) - { - logger.error("Couldn\'t save stats", ioexception); - } +// try +// { + AsyncIOUtils.writeString(this.field_150887_d, func_150880_a(this.field_150875_a)); +// } +// catch (IOException ioexception) +// { +// logger.error("Couldn\'t save stats", ioexception); +// } } public void func_150873_a(EntityPlayer p_150873_1_, StatBase p_150873_2_, int p_150873_3_) @@ -251,4 +255,4 @@ { return this.field_150886_g; } -} \ No newline at end of file +} diff --git a/src/main/java/net/minecraft/world/storage/SaveHandler.java b/src/main/java/net/minecraft/world/storage/SaveHandler.java index aa9b551..3a60995 100644 --- a/src/main/java/net/minecraft/world/storage/SaveHandler.java +++ b/src/main/java/net/minecraft/world/storage/SaveHandler.java @@ -320,4 +320,9 @@ { return this.saveDirectoryName; } -} \ No newline at end of file + + public File getPlayerSaveDir() + { + return playersDirectory; + } +} diff --git a/src/main/java/org/ultramine/server/UMEventHandler.java b/src/main/java/org/ultramine/server/UMEventHandler.java index b188ed6..c2052ed 100644 --- a/src/main/java/org/ultramine/server/UMEventHandler.java +++ b/src/main/java/org/ultramine/server/UMEventHandler.java @@ -1,6 +1,9 @@ package org.ultramine.server; +import org.ultramine.server.data.player.io.PlayerDataIOExecutor; + import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import cpw.mods.fml.common.gameevent.TickEvent; import net.minecraft.util.ChatComponentStyle; import net.minecraft.util.ChatComponentText; import net.minecraft.util.ChatComponentTranslation; @@ -41,4 +44,13 @@ e.component = new ChatComponentTranslation("%s%s%s\u00A77: %s", prefix, username, postfix, msg); } + + @SubscribeEvent + public void onServerTick(TickEvent.ServerTickEvent e) + { + if(e.phase == TickEvent.Phase.START) + { + PlayerDataIOExecutor.tick(); + } + } } diff --git a/src/main/java/org/ultramine/server/UltramineServerModContainer.java b/src/main/java/org/ultramine/server/UltramineServerModContainer.java index 64b18df..5811a6f 100644 --- a/src/main/java/org/ultramine/server/UltramineServerModContainer.java +++ b/src/main/java/org/ultramine/server/UltramineServerModContainer.java @@ -12,6 +12,7 @@ import com.google.common.eventbus.Subscribe; import cpw.mods.fml.common.DummyModContainer; +import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.LoadController; import cpw.mods.fml.common.ModMetadata; import cpw.mods.fml.common.event.FMLConstructionEvent; @@ -29,6 +30,7 @@ import org.ultramine.commands.syntax.DefaultCompleters; import org.ultramine.permission.commands.BasicPermissionCommands; import org.ultramine.permission.internal.OpPermissionProxySet; +import org.ultramine.server.data.player.PlayerCoreData; public class UltramineServerModContainer extends DummyModContainer { @@ -64,7 +66,9 @@ @Subscribe public void init(FMLInitializationEvent e) { - MinecraftForge.EVENT_BUS.register(new UMEventHandler()); + UMEventHandler handler = new UMEventHandler(); + MinecraftForge.EVENT_BUS.register(handler); + FMLCommonHandler.instance().bus().register(handler); } @Subscribe @@ -83,6 +87,7 @@ @Subscribe public void serverStarting(FMLServerStartingEvent e) { + e.getServer().getConfigurationManager().getDataLoader().registerPlayerDataExt(PlayerCoreData.class, "core"); e.registerArgumentHandlers(DefaultCompleters.class); e.registerCommands(BasicPermissionCommands.class); e.registerCommands(VanillaCommands.class); diff --git a/src/main/java/org/ultramine/server/data/IDataProvider.java b/src/main/java/org/ultramine/server/data/IDataProvider.java new file mode 100644 index 0000000..247d3ff --- /dev/null +++ b/src/main/java/org/ultramine/server/data/IDataProvider.java @@ -0,0 +1,22 @@ +package org.ultramine.server.data; + +import java.util.List; + +import org.ultramine.server.data.player.PlayerData; + +import com.mojang.authlib.GameProfile; + +import net.minecraft.nbt.NBTTagCompound; + +public interface IDataProvider +{ + NBTTagCompound loadPlayer(GameProfile player); + + void savePlayer(GameProfile player, NBTTagCompound nbt); + + PlayerData loadPlayerData(GameProfile player); + + List loadAllPlayerData(); + + void savePlayerData(PlayerData data); +} diff --git a/src/main/java/org/ultramine/server/data/NBTFileDataProvider.java b/src/main/java/org/ultramine/server/data/NBTFileDataProvider.java new file mode 100644 index 0000000..4c8b829 --- /dev/null +++ b/src/main/java/org/ultramine/server/data/NBTFileDataProvider.java @@ -0,0 +1,154 @@ +package org.ultramine.server.data; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.ultramine.server.data.player.PlayerData; +import org.ultramine.server.data.player.PlayerDataExtension; +import org.ultramine.server.data.player.PlayerDataExtensionInfo; + +import com.mojang.authlib.GameProfile; + +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.server.management.ServerConfigurationManager; +import net.minecraft.world.storage.SaveHandler; + +public class NBTFileDataProvider implements IDataProvider +{ + private static final Logger log = LogManager.getLogger(); + + private final ServerConfigurationManager mgr; + private File umPlayerDir; + + public NBTFileDataProvider(ServerConfigurationManager mgr) + { + this.mgr = mgr; + } + + @Override + public NBTTagCompound loadPlayer(GameProfile player) + { + return ((SaveHandler)mgr.getPlayerNBTLoader()).getPlayerData(player.getName()); + } + + @Override + public void savePlayer(GameProfile player, NBTTagCompound nbt) + { + safeWriteNBT(new File(((SaveHandler)mgr.getPlayerNBTLoader()).getPlayerSaveDir(), player.getName() + ".dat"), nbt); + } + + @Override + public PlayerData loadPlayerData(GameProfile player) + { + checkPlayerDir(); + + return readPlayerData(getPlayerDataNBT(player.getName())); + } + + public List loadAllPlayerData() + { + checkPlayerDir(); + + List list = new ArrayList(); + for(File file : umPlayerDir.listFiles()) + { + if(file.getName().endsWith(".dat")) + { + try + { + list.add(readPlayerData(CompressedStreamTools.readCompressed(new FileInputStream(file)))); + } + catch(IOException e) + { + log.warn("Failed to load ultramine player data from " + file.getName(), e); + } + } + } + + return list; + } + + @Override + public void savePlayerData(PlayerData data) + { + NBTTagCompound nbt = new NBTTagCompound(); + nbt.setString("id", data.getProfile().getId()); + nbt.setString("name", data.getProfile().getName()); + for(PlayerDataExtensionInfo info : mgr.getDataLoader().getDataExtProviders()) + { + NBTTagCompound extnbt = new NBTTagCompound(); + data.get(info.getExtClass()).writeToNBT(extnbt); + nbt.setTag(info.getTagName(), extnbt); + } + + safeWriteNBT(new File(umPlayerDir, data.getProfile() + ".dat"), nbt); + } + + private void checkPlayerDir() + { + if(umPlayerDir == null) + { + umPlayerDir = new File(((SaveHandler)mgr.getPlayerNBTLoader()).getPlayerSaveDir(), "ultramine"); + umPlayerDir.mkdir(); + } + } + + private NBTTagCompound getPlayerDataNBT(String username) + { + try + { + File file = new File(umPlayerDir, username + ".dat"); + + if (file.exists()) + { + return CompressedStreamTools.readCompressed(new FileInputStream(file)); + } + } + catch (IOException e) + { + log.warn("Failed to load ultramine player data for " + username, e); + } + + return null; + } + + private PlayerData readPlayerData(NBTTagCompound nbt) + { + String id = nbt.getString("id"); + String username = nbt.getString("name"); + + List infos = mgr.getDataLoader().getDataExtProviders(); + List data = new ArrayList(infos.size()); + + for(PlayerDataExtensionInfo info : infos) + { + data.add(info.createFromNBT(nbt)); + } + + return new PlayerData(new GameProfile(id, username), data); + } + + private void safeWriteNBT(File file, NBTTagCompound nbt) + { + try + { + File file1 = new File(file.getParentFile(), file.getName()+".tmp"); + CompressedStreamTools.writeCompressed(nbt, new FileOutputStream(file1)); + + if (file.exists()) + file.delete(); + file1.renameTo(file); + } + catch(IOException e) + { + log.warn("Failed to write file: "+file.getAbsolutePath(), e); + } + } +} diff --git a/src/main/java/org/ultramine/server/data/ServerDataLoader.java b/src/main/java/org/ultramine/server/data/ServerDataLoader.java new file mode 100644 index 0000000..8f904c2 --- /dev/null +++ b/src/main/java/org/ultramine/server/data/ServerDataLoader.java @@ -0,0 +1,86 @@ +package org.ultramine.server.data; + +import java.util.ArrayList; +import java.util.List; + +import org.ultramine.server.data.player.PlayerData; +import org.ultramine.server.data.player.PlayerDataExtension; +import org.ultramine.server.data.player.PlayerDataExtensionInfo; +import org.ultramine.server.data.player.io.PlayerDataIOExecutor; +import org.ultramine.server.util.GlobalExecutors; + +import cpw.mods.fml.common.FMLCommonHandler; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.NetHandlerPlayServer; +import net.minecraft.network.NetworkManager; +import net.minecraft.server.management.ServerConfigurationManager; +import net.minecraft.world.storage.SaveHandler; +import net.minecraftforge.event.ForgeEventFactory; + +public class ServerDataLoader +{ + private static final boolean isClient = FMLCommonHandler.instance().getSide().isClient(); + private final ServerConfigurationManager mgr; + private final IDataProvider dataProvider; + private final List dataExtinfos = new ArrayList(); + + public ServerDataLoader(ServerConfigurationManager mgr) + { + this.mgr = mgr; + dataProvider = new NBTFileDataProvider(mgr); + } + + public IDataProvider getDataProvider() + { + return dataProvider; + } + + public void initializeConnectionToPlayer(NetworkManager network, EntityPlayerMP player, NetHandlerPlayServer nethandler) + { + if(isClient) + { + NBTTagCompound nbt = mgr.readPlayerDataFromFile(player); + player.setData(getDataProvider().loadPlayerData(player.getGameProfile())); + mgr.initializeConnectionToPlayer_body(network, player, nethandler, nbt); + } + else + { + PlayerDataIOExecutor.requestData(getDataProvider(), network, player, nethandler, this, true); + } + } + + public void plyaerLoadCallback(NetworkManager network, EntityPlayerMP player, NetHandlerPlayServer nethandler, NBTTagCompound nbt, PlayerData data) + { + if(data != null) + player.setData(data); + ForgeEventFactory.firePlayerLoadingEvent(player, ((SaveHandler)mgr.getPlayerNBTLoader()).getPlayerSaveDir(), player.getUniqueID().toString()); + mgr.initializeConnectionToPlayer_body(network, player, nethandler, nbt); + } + + public void savePlayer(final EntityPlayerMP player) + { + ForgeEventFactory.firePlayerSavingEvent(player, ((SaveHandler)mgr.getPlayerNBTLoader()).getPlayerSaveDir(), player.getUniqueID().toString()); + final NBTTagCompound nbt = new NBTTagCompound(); + player.writeToNBT(nbt); + GlobalExecutors.writingIOExecutor().execute(new Runnable() + { + @Override + public void run() + { + getDataProvider().savePlayer(player.getGameProfile(), nbt); + getDataProvider().savePlayerData(player.getData()); + } + }); + } + + public void registerPlayerDataExt(Class clazz, String nbtTagName) + { + dataExtinfos.add(new PlayerDataExtensionInfo(clazz, nbtTagName)); + } + + public List getDataExtProviders() + { + return dataExtinfos; + } +} diff --git a/src/main/java/org/ultramine/server/data/player/PlayerCoreData.java b/src/main/java/org/ultramine/server/data/player/PlayerCoreData.java new file mode 100644 index 0000000..6afb2bd --- /dev/null +++ b/src/main/java/org/ultramine/server/data/player/PlayerCoreData.java @@ -0,0 +1,49 @@ +package org.ultramine.server.data.player; + +import java.util.HashMap; +import java.util.Map; + +import org.ultramine.server.util.WarpLocation; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; + +public class PlayerCoreData extends PlayerDataExtension +{ + private Map homes = new HashMap(); + + public WarpLocation getHome(String name) + { + return homes.get(name); + } + + public void setHome(String name, WarpLocation home) + { + homes.put(name, home); + } + + @Override + public void writeToNBT(NBTTagCompound nbt) + { + NBTTagList homeList = new NBTTagList(); + for(Map.Entry ent : homes.entrySet()) + { + NBTTagCompound nbt1 = new NBTTagCompound(); + nbt1.setString("k", ent.getKey()); + nbt1.setTag("v", ent.getValue().toNBT()); + homeList.appendTag(nbt1); + } + nbt.setTag("homes", homeList); + } + + @Override + public void readFromNBT(NBTTagCompound nbt) + { + NBTTagList homeList = nbt.getTagList("homes", 10); + for(int i = 0, s = homeList.tagCount(); i < s; i++) + { + NBTTagCompound nbt1 = homeList.getCompoundTagAt(i); + homes.put(nbt1.getString("k"), WarpLocation.getFromNBT(nbt1.getCompoundTag("v"))); + } + } +} diff --git a/src/main/java/org/ultramine/server/data/player/PlayerData.java b/src/main/java/org/ultramine/server/data/player/PlayerData.java new file mode 100644 index 0000000..56d4c7c --- /dev/null +++ b/src/main/java/org/ultramine/server/data/player/PlayerData.java @@ -0,0 +1,37 @@ +package org.ultramine.server.data.player; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.mojang.authlib.GameProfile; + +public class PlayerData +{ + private final GameProfile profile; + private final Map, PlayerDataExtension> data = new HashMap, PlayerDataExtension>(); + private final PlayerCoreData coreData; + + public PlayerData(GameProfile profile, List list) + { + this.profile = profile; + for(PlayerDataExtension o : list) + data.put(o.getClass(), o); + coreData = get(PlayerCoreData.class); + } + + public GameProfile getProfile() + { + return profile; + } + + public T get(Class clazz) + { + return clazz.cast(data.get(clazz)); + } + + public PlayerCoreData core() + { + return coreData; + } +} diff --git a/src/main/java/org/ultramine/server/data/player/PlayerDataExtension.java b/src/main/java/org/ultramine/server/data/player/PlayerDataExtension.java new file mode 100644 index 0000000..90e919d --- /dev/null +++ b/src/main/java/org/ultramine/server/data/player/PlayerDataExtension.java @@ -0,0 +1,10 @@ +package org.ultramine.server.data.player; + +import net.minecraft.nbt.NBTTagCompound; + +public abstract class PlayerDataExtension +{ + public abstract void writeToNBT(NBTTagCompound nbt); + + public abstract void readFromNBT(NBTTagCompound nbt); +} diff --git a/src/main/java/org/ultramine/server/data/player/PlayerDataExtensionInfo.java b/src/main/java/org/ultramine/server/data/player/PlayerDataExtensionInfo.java new file mode 100644 index 0000000..90aac1f --- /dev/null +++ b/src/main/java/org/ultramine/server/data/player/PlayerDataExtensionInfo.java @@ -0,0 +1,45 @@ +package org.ultramine.server.data.player; + +import net.minecraft.nbt.NBTTagCompound; + +public class PlayerDataExtensionInfo +{ + private Class clazz; + private String nbtTagName; + + public PlayerDataExtensionInfo(Class clazz, String nbtTagName) + { + this.clazz = clazz; + this.nbtTagName = nbtTagName; + } + + public Class getExtClass() + { + return clazz; + } + + public String getTagName() + { + return nbtTagName; + } + + private PlayerDataExtension makeNew() + { + try + { + return clazz.newInstance(); + } + catch(Exception e) + { + throw new RuntimeException(e); + } + } + + public PlayerDataExtension createFromNBT(NBTTagCompound nbt) + { + PlayerDataExtension data = makeNew(); + if(nbt != null) + data.readFromNBT(nbt.getCompoundTag(nbtTagName)); + return data; + } +} diff --git a/src/main/java/org/ultramine/server/data/player/io/LoadedDataStruct.java b/src/main/java/org/ultramine/server/data/player/io/LoadedDataStruct.java new file mode 100644 index 0000000..99db9a8 --- /dev/null +++ b/src/main/java/org/ultramine/server/data/player/io/LoadedDataStruct.java @@ -0,0 +1,27 @@ +package org.ultramine.server.data.player.io; + +import org.ultramine.server.data.player.PlayerData; + +import net.minecraft.nbt.NBTTagCompound; + +public class LoadedDataStruct +{ + private final NBTTagCompound nbt; + private final PlayerData data; + + public LoadedDataStruct(NBTTagCompound nbt, PlayerData data) + { + this.nbt = nbt; + this.data = data; + } + + public NBTTagCompound getNBT() + { + return nbt; + } + + public PlayerData getPlayerData() + { + return data; + } +} diff --git a/src/main/java/org/ultramine/server/data/player/io/PlayerDataIOExecutor.java b/src/main/java/org/ultramine/server/data/player/io/PlayerDataIOExecutor.java new file mode 100644 index 0000000..19eb620 --- /dev/null +++ b/src/main/java/org/ultramine/server/data/player/io/PlayerDataIOExecutor.java @@ -0,0 +1,26 @@ +package org.ultramine.server.data.player.io; + +import org.ultramine.server.data.IDataProvider; +import org.ultramine.server.data.ServerDataLoader; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.NetHandlerPlayServer; +import net.minecraft.network.NetworkManager; +import net.minecraftforge.common.util.AsynchronousExecutor; + +public class PlayerDataIOExecutor +{ + private static final AsynchronousExecutor instance + = new AsynchronousExecutor(new PlayerDataIOProvider(), 1); + + public static void requestData(IDataProvider provider, NetworkManager network, EntityPlayerMP player, NetHandlerPlayServer nethandler, + ServerDataLoader callback, boolean loadData) + { + instance.add(new QueuedPlayer(provider, network, player, nethandler, loadData), callback); + } + + public static void tick() + { + instance.finishActive(); + } +} diff --git a/src/main/java/org/ultramine/server/data/player/io/PlayerDataIOProvider.java b/src/main/java/org/ultramine/server/data/player/io/PlayerDataIOProvider.java new file mode 100644 index 0000000..ac804c9 --- /dev/null +++ b/src/main/java/org/ultramine/server/data/player/io/PlayerDataIOProvider.java @@ -0,0 +1,44 @@ +package org.ultramine.server.data.player.io; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.ultramine.server.data.ServerDataLoader; +import org.ultramine.server.data.player.PlayerData; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.util.AsynchronousExecutor; + +public class PlayerDataIOProvider implements AsynchronousExecutor.CallBackProvider +{ + private final AtomicInteger threadNumber = new AtomicInteger(1); + + @Override + public Thread newThread(Runnable runnable) + { + Thread thread = new Thread(runnable, "PlayerData I/O Executor Thread-" + threadNumber.getAndIncrement()); + thread.setDaemon(true); + return thread; + } + + @Override + public LoadedDataStruct callStage1(QueuedPlayer param) throws RuntimeException + { + NBTTagCompound nbt = param.getDataProvider().loadPlayer(param.getPlayer().getGameProfile()); + PlayerData data = param.shouldLoadData() ? param.getDataProvider().loadPlayerData(param.getPlayer().getGameProfile()) : null; + + return new LoadedDataStruct(nbt, data); + } + + @Override + public void callStage2(QueuedPlayer param, LoadedDataStruct data) throws RuntimeException + { + if(data.getNBT() != null) + param.getPlayer().readFromNBT(data.getNBT()); + } + + @Override + public void callStage3(QueuedPlayer param, LoadedDataStruct data, ServerDataLoader callback) throws RuntimeException + { + callback.plyaerLoadCallback(param.getNetwork(), param.getPlayer(), param.getNethandler(), data.getNBT(), data.getPlayerData()); + } +} diff --git a/src/main/java/org/ultramine/server/data/player/io/QueuedPlayer.java b/src/main/java/org/ultramine/server/data/player/io/QueuedPlayer.java new file mode 100644 index 0000000..940b361 --- /dev/null +++ b/src/main/java/org/ultramine/server/data/player/io/QueuedPlayer.java @@ -0,0 +1,50 @@ +package org.ultramine.server.data.player.io; + +import org.ultramine.server.data.IDataProvider; + +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.NetHandlerPlayServer; +import net.minecraft.network.NetworkManager; + +public class QueuedPlayer +{ + private final IDataProvider provider; + private final NetworkManager network; + private final EntityPlayerMP player; + private final NetHandlerPlayServer nethandler; + private final boolean loadData; + + public QueuedPlayer(IDataProvider provider, NetworkManager network, EntityPlayerMP player, NetHandlerPlayServer nethandler, boolean loadData) + { + this.provider = provider; + this.network = network; + this.player = player; + this.nethandler = nethandler; + this.loadData = loadData; + } + + public IDataProvider getDataProvider() + { + return provider; + } + + public NetworkManager getNetwork() + { + return network; + } + + public EntityPlayerMP getPlayer() + { + return player; + } + + public NetHandlerPlayServer getNethandler() + { + return nethandler; + } + + public boolean shouldLoadData() + { + return loadData; + } +} diff --git a/src/main/java/org/ultramine/server/util/AsyncIOUtils.java b/src/main/java/org/ultramine/server/util/AsyncIOUtils.java new file mode 100644 index 0000000..0e3a8fc --- /dev/null +++ b/src/main/java/org/ultramine/server/util/AsyncIOUtils.java @@ -0,0 +1,79 @@ +package org.ultramine.server.util; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + +import net.minecraft.nbt.CompressedStreamTools; +import net.minecraft.nbt.NBTTagCompound; + +import org.apache.commons.io.FileUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class AsyncIOUtils +{ + private static final Logger log = LogManager.getLogger(); + + public static void writeString(final File file, final String data) + { + GlobalExecutors.writingIOExecutor().execute(new Runnable() + { + @Override + public void run() + { + try + { + FileUtils.writeStringToFile(file, data); + } + catch(IOException e) + { + log.warn("Failed to write file: "+file.getAbsolutePath(), e); + } + } + }); + } + + public static void writeBytes(final File file, final byte[] data) + { + GlobalExecutors.writingIOExecutor().execute(new Runnable() + { + @Override + public void run() + { + try + { + FileUtils.writeByteArrayToFile(file, data); + } + catch(IOException e) + { + log.warn("Failed to write file: "+file.getAbsolutePath(), e); + } + } + }); + } + + public static void safeWriteNBT(final File file, final NBTTagCompound nbt) + { + GlobalExecutors.writingIOExecutor().execute(new Runnable() + { + @Override + public void run() + { + try + { + File file1 = new File(file.getParentFile(), file.getName()+".tmp"); + CompressedStreamTools.writeCompressed(nbt, new FileOutputStream(file1)); + + if (file.exists()) + file.delete(); + file1.renameTo(file); + } + catch(IOException e) + { + log.warn("Failed to write file: "+file.getAbsolutePath(), e); + } + } + }); + } +} diff --git a/src/main/java/org/ultramine/server/util/GlobalExecutors.java b/src/main/java/org/ultramine/server/util/GlobalExecutors.java new file mode 100644 index 0000000..1874e1b --- /dev/null +++ b/src/main/java/org/ultramine/server/util/GlobalExecutors.java @@ -0,0 +1,19 @@ +package org.ultramine.server.util; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class GlobalExecutors +{ + private static final ExecutorService io = Executors.newSingleThreadExecutor(); + + /** + * Обрабатывает задачи на сохранение чего-либо на диск/в БД. Используется + * единственный поток, т.к. при сохранениее не требуется наискорейшее + * выполнение задачи. + */ + public static ExecutorService writingIOExecutor() + { + return io; + } +} diff --git a/src/main/java/org/ultramine/server/util/WarpLocation.java b/src/main/java/org/ultramine/server/util/WarpLocation.java index 28b15a2..20ece9c 100644 --- a/src/main/java/org/ultramine/server/util/WarpLocation.java +++ b/src/main/java/org/ultramine/server/util/WarpLocation.java @@ -1,6 +1,7 @@ package org.ultramine.server.util; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.nbt.NBTTagCompound; public class WarpLocation { @@ -52,6 +53,36 @@ Math.abs(z - loc.z) < 0.1; } + public NBTTagCompound toNBT() + { + NBTTagCompound nbt = new NBTTagCompound(); + nbt.setDouble("x", x); + nbt.setDouble("y", y); + nbt.setDouble("z", z); + if(dimension != 0) + nbt.setInteger("d", dimension); + if(yaw != 0F) + nbt.setFloat("w", yaw); + if(pitch != 0F) + nbt.setFloat("p", pitch); + if(randomRadius != 0d) + nbt.setDouble("r", randomRadius); + return nbt; + } + + public static WarpLocation getFromNBT(NBTTagCompound nbt) + { + double x = nbt.getDouble("x"); + double y = nbt.getDouble("y"); + double z = nbt.getDouble("z"); + int dimension = nbt.hasKey("d") ? nbt.getInteger("d") : 0; + float yaw = nbt.hasKey("w") ? nbt.getFloat("w") : 0F; + float pitch = nbt.hasKey("p") ? nbt.getFloat("p") : 0F; + int randomRadius = nbt.hasKey("r") ? nbt.getInteger("r") : 0; + + return new WarpLocation(dimension, x, y, z, yaw, pitch, randomRadius); + } + public static WarpLocation getFromPlayer(EntityPlayer player) { WarpLocation s = new WarpLocation(player.dimension, player.posX, player.posY, player.posZ, player.rotationYaw, player.rotationPitch);