commit 9cae8e9d5a2e81aad8c025e9ac466eaaeb61c147 Author: OmerBenGera Date: Sat Aug 15 01:39:18 2020 +0300 Initial Commit diff --git a/API/src/main/java/com/bgsoftware/wildloaders/api/WildLoaders.java b/API/src/main/java/com/bgsoftware/wildloaders/api/WildLoaders.java new file mode 100644 index 0000000..b894569 --- /dev/null +++ b/API/src/main/java/com/bgsoftware/wildloaders/api/WildLoaders.java @@ -0,0 +1,18 @@ +package com.bgsoftware.wildloaders.api; + +import com.bgsoftware.wildloaders.api.managers.LoadersManager; +import com.bgsoftware.wildloaders.api.managers.NPCManager; + +public interface WildLoaders { + + /** + * Get the chunk loaders manager. + */ + LoadersManager getLoaders(); + + /** + * Get the npcs manager. + */ + NPCManager getNPCs(); + +} diff --git a/API/src/main/java/com/bgsoftware/wildloaders/api/WildLoadersAPI.java b/API/src/main/java/com/bgsoftware/wildloaders/api/WildLoadersAPI.java new file mode 100644 index 0000000..d51650a --- /dev/null +++ b/API/src/main/java/com/bgsoftware/wildloaders/api/WildLoadersAPI.java @@ -0,0 +1,7 @@ +package com.bgsoftware.wildloaders.api; + +public final class WildLoadersAPI { + + + +} diff --git a/API/src/main/java/com/bgsoftware/wildloaders/api/loaders/ChunkLoader.java b/API/src/main/java/com/bgsoftware/wildloaders/api/loaders/ChunkLoader.java new file mode 100644 index 0000000..dd13115 --- /dev/null +++ b/API/src/main/java/com/bgsoftware/wildloaders/api/loaders/ChunkLoader.java @@ -0,0 +1,47 @@ +package com.bgsoftware.wildloaders.api.loaders; + +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import org.bukkit.Location; +import org.bukkit.OfflinePlayer; +import org.bukkit.inventory.ItemStack; + +import java.util.Optional; + +public interface ChunkLoader { + + /** + * Get the loader data from of chunk loader. + */ + LoaderData getLoaderData(); + + /** + * Get the player who placed the chunk loader. + */ + OfflinePlayer getWhoPlaced(); + + /** + * Get the amount of time that is left until the chunk loader finishes, in ticks. + */ + long getTimeLeft(); + + /** + * Get the location of the chunk loader. + */ + Location getLocation(); + + /** + * Get the NPC of this chunk loader. + */ + Optional getNPC(); + + /** + * Remove this chunk loader. + */ + void remove(); + + /** + * Get the drop item of this chunk loader. + */ + ItemStack getLoaderItem(); + +} diff --git a/API/src/main/java/com/bgsoftware/wildloaders/api/loaders/LoaderData.java b/API/src/main/java/com/bgsoftware/wildloaders/api/loaders/LoaderData.java new file mode 100644 index 0000000..ab58e82 --- /dev/null +++ b/API/src/main/java/com/bgsoftware/wildloaders/api/loaders/LoaderData.java @@ -0,0 +1,22 @@ +package com.bgsoftware.wildloaders.api.loaders; + +import org.bukkit.inventory.ItemStack; + +public interface LoaderData { + + /** + * Get the name of the data. + */ + String getName(); + + /** + * Get the default amount of time the chunk loader will run for. + */ + long getTimeLeft(); + + /** + * Get the drop item of the chunk loader, with default time. + */ + ItemStack getLoaderItem(); + +} diff --git a/API/src/main/java/com/bgsoftware/wildloaders/api/managers/LoadersManager.java b/API/src/main/java/com/bgsoftware/wildloaders/api/managers/LoadersManager.java new file mode 100644 index 0000000..b06fbc1 --- /dev/null +++ b/API/src/main/java/com/bgsoftware/wildloaders/api/managers/LoadersManager.java @@ -0,0 +1,77 @@ +package com.bgsoftware.wildloaders.api.managers; + +import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; +import com.bgsoftware.wildloaders.api.loaders.LoaderData; +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.List; +import java.util.Optional; + +public interface LoadersManager { + + /** + * Get an active chunk loader from a chunk. + * @param chunk The chunk to check. + */ + Optional getChunkLoader(Chunk chunk); + + /** + * Get a chunk loader by it's location. + * @param location The location of the chunk loader. + */ + Optional getChunkLoader(Location location); + + /** + * Get all the chunk loaders on the server. + */ + List getChunkLoaders(); + + /** + * Get chunk-loader data by it's name. + * @param name The name of the data. + */ + Optional getLoaderData(String name); + + /** + * Get all the available chunk-loaders data. + */ + List getLoaderDatas(); + + /** + * Create a new chunk loader at a specific location. + * @param loaderData The data of the chunk loader. + * @param whoPlaced The player who placed the chunk loader. + * @param location The location of the chunk loader. + * @param timeLeft The amount of time left for the chunk loader to run. + * @return The new chunk loader object. + */ + ChunkLoader addChunkLoader(LoaderData loaderData, Player whoPlaced, Location location, long timeLeft); + + /** + * Remove a chunk loader from the database. + * It's recommended to use ChunkLoader#remove instead! + */ + void removeChunkLoader(ChunkLoader chunkLoader); + + /** + * Create a new chunk-loader data. + * @param name The name of the data. + * @param timeLeft The default amount of time to run. + * @param itemStack The item stack to drop upon break. + */ + void createLoaderData(String name, long timeLeft, ItemStack itemStack); + + /** + * Remove all the chunk loaders data from cache. + */ + void removeLoadersData(); + + /** + * Remove all chunk loaders from cache. + */ + void removeChunkLoaders(); + +} diff --git a/API/src/main/java/com/bgsoftware/wildloaders/api/managers/NPCManager.java b/API/src/main/java/com/bgsoftware/wildloaders/api/managers/NPCManager.java new file mode 100644 index 0000000..077857d --- /dev/null +++ b/API/src/main/java/com/bgsoftware/wildloaders/api/managers/NPCManager.java @@ -0,0 +1,40 @@ +package com.bgsoftware.wildloaders.api.managers; + +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import org.bukkit.Location; +import org.bukkit.entity.LivingEntity; + +import java.util.Optional; + +public interface NPCManager { + + /** + * Get a NPC by it's location. + * @param location The location of the npc. + */ + Optional getNPC(Location location); + + /** + * Create a NPC in a specific location. + * @param location The location of the npc. + */ + ChunkLoaderNPC createNPC(Location location); + + /** + * Check whether or not an entity is an NPC. + * @param livingEntity The entity to check. + */ + boolean isNPC(LivingEntity livingEntity); + + /** + * Kill a NPC. + * @param npc The NPC to kill. + */ + void killNPC(ChunkLoaderNPC npc); + + /** + * Kill all the npcs on the server. + */ + void killAllNPCs(); + +} diff --git a/API/src/main/java/com/bgsoftware/wildloaders/api/npc/ChunkLoaderNPC.java b/API/src/main/java/com/bgsoftware/wildloaders/api/npc/ChunkLoaderNPC.java new file mode 100644 index 0000000..2d6d9ea --- /dev/null +++ b/API/src/main/java/com/bgsoftware/wildloaders/api/npc/ChunkLoaderNPC.java @@ -0,0 +1,28 @@ +package com.bgsoftware.wildloaders.api.npc; + +import org.bukkit.Location; + +import java.util.UUID; + +/** + * NPCs are used to make spawners work in active chunks even without nearby players, and make crops to grow in newer versions. + */ +public interface ChunkLoaderNPC { + + /** + * Get the unique id of the npc. + */ + UUID getUniqueId(); + + /** + * Kill the npc. + * It's recommended to use NPCManager#killNPC instead! + */ + void die(); + + /** + * Get the location of the NPC. + */ + Location getLocation(); + +} diff --git a/libs/HolographicDisplays.jar b/libs/HolographicDisplays.jar new file mode 100644 index 0000000..f469b8f Binary files /dev/null and b/libs/HolographicDisplays.jar differ diff --git a/src/main/java/com/bgsoftware/wildloaders/Locale.java b/src/main/java/com/bgsoftware/wildloaders/Locale.java new file mode 100644 index 0000000..2bab5f7 --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/Locale.java @@ -0,0 +1,86 @@ +package com.bgsoftware.wildloaders; + +import com.bgsoftware.wildloaders.utils.config.CommentedConfiguration; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +@SuppressWarnings("WeakerAccess") +public final class Locale { + + private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + private static final Map localeMap = new HashMap<>(); + + public static Locale ALREADY_LOADED = new Locale("ALREADY_LOADED"); + public static Locale BROKE_LOADER = new Locale("BROKE_LOADER"); + public static Locale COMMAND_USAGE = new Locale("COMMAND_USAGE"); + public static Locale GIVE_SUCCESS = new Locale("GIVE_SUCCESS"); + public static Locale HELP_COMMAND_HEADER = new Locale("HELP_COMMAND_HEADER"); + public static Locale HELP_COMMAND_LINE = new Locale("HELP_COMMAND_LINE"); + public static Locale HELP_COMMAND_FOOTER = new Locale("HELP_COMMAND_FOOTER"); + public static Locale INVALID_AMOUNT = new Locale("INVALID_AMOUNT"); + public static Locale INVALID_LOADER = new Locale("INVALID_LOADER"); + public static Locale INVALID_PLAYER = new Locale("INVALID_PLAYER"); + public static Locale NO_PERMISSION = new Locale("NO_PERMISSION"); + public static Locale NO_PLACE_PERMISSION = new Locale("NO_PLACE_PERMISSION"); + public static Locale PLACED_LOADER = new Locale("PLACED_LOADER"); + public static Locale RECEIVE_SUCCESS = new Locale("RECEIVE_SUCCESS"); + + private Locale(String identifier){ + localeMap.put(identifier, this); + } + + private String message; + + public String getMessage(Object... objects){ + if(message != null && !message.equals("")) { + String msg = message; + + for (int i = 0; i < objects.length; i++) + msg = msg.replace("{" + i + "}", objects[i].toString()); + + return msg; + } + + return null; + } + + public void send(CommandSender sender, Object... objects){ + String message = getMessage(objects); + if(message != null && sender != null) + sender.sendMessage(message); + } + + private void setMessage(String message){ + this.message = message; + } + + public static void reload(){ + WildLoadersPlugin.log("Loading messages started..."); + long startTime = System.currentTimeMillis(); + int messagesAmount = 0; + File file = new File(plugin.getDataFolder(), "lang.yml"); + + if(!file.exists()) + plugin.saveResource("lang.yml", false); + + CommentedConfiguration cfg = CommentedConfiguration.loadConfiguration(file); + cfg.syncWithConfig(file, plugin.getResource("lang.yml")); + + for(String identifier : localeMap.keySet()){ + localeMap.get(identifier).setMessage(ChatColor.translateAlternateColorCodes('&', cfg.getString(identifier, ""))); + messagesAmount++; + } + + WildLoadersPlugin.log(" - Found " + messagesAmount + " messages in lang.yml."); + WildLoadersPlugin.log("Loading messages done (Took " + (System.currentTimeMillis() - startTime) + "ms)"); + } + + public static void sendMessage(CommandSender sender, String message){ + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', message)); + } + +} diff --git a/src/main/java/com/bgsoftware/wildloaders/WildLoadersPlugin.java b/src/main/java/com/bgsoftware/wildloaders/WildLoadersPlugin.java new file mode 100644 index 0000000..9412969 --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/WildLoadersPlugin.java @@ -0,0 +1,97 @@ +package com.bgsoftware.wildloaders; + +import com.bgsoftware.wildloaders.api.WildLoaders; +import com.bgsoftware.wildloaders.command.CommandsHandler; +import com.bgsoftware.wildloaders.handlers.LoadersHandler; +import com.bgsoftware.wildloaders.handlers.NPCHandler; +import com.bgsoftware.wildloaders.handlers.SettingsHandler; +import com.bgsoftware.wildloaders.listeners.BlocksListener; +import com.bgsoftware.wildloaders.listeners.ChunksListener; +import com.bgsoftware.wildloaders.metrics.Metrics; +import com.bgsoftware.wildloaders.nms.NMSAdapter; +import org.bukkit.plugin.java.JavaPlugin; + +public final class WildLoadersPlugin extends JavaPlugin implements WildLoaders { + + private static WildLoadersPlugin plugin; + + private SettingsHandler settingsHandler; + private LoadersHandler loadersHandler; + private NPCHandler npcHandler; + + private NMSAdapter nmsAdapter; + + @Override + public void onEnable() { + plugin = this; + new Metrics(this); + + log("******** ENABLE START ********"); + + loadNMSAdapter(); + + loadersHandler = new LoadersHandler(this); + npcHandler = new NPCHandler(this); + settingsHandler = new SettingsHandler(this); + + getServer().getPluginManager().registerEvents(new BlocksListener(this), this); + getServer().getPluginManager().registerEvents(new ChunksListener(this), this); + + CommandsHandler commandsHandler = new CommandsHandler(this); + getCommand("loader").setExecutor(commandsHandler); + getCommand("loader").setTabCompleter(commandsHandler); + + Locale.reload(); + +// if(Updater.isOutdated()) { +// log(""); +// log("A new version is available (v" + Updater.getLatestVersion() + ")!"); +// log("Version's description: \"" + Updater.getVersionDescription() + "\""); +// log(""); +// } + + log("******** ENABLE DONE ********"); + } + + @Override + public void onDisable() { + loadersHandler.removeChunkLoaders(); + npcHandler.killAllNPCs(); + } + + private void loadNMSAdapter(){ + String version = getServer().getClass().getPackage().getName().split("\\.")[3]; + try{ + nmsAdapter = (NMSAdapter) Class.forName("com.bgsoftware.wildloaders.nms.NMSAdapter_" + version).newInstance(); + } catch(ClassNotFoundException | InstantiationException | IllegalAccessException ex){ + log("Couldn't load up with an adapter " + version + ". Please contact @Ome_R"); + getServer().getPluginManager().disablePlugin(this); + } + } + + public SettingsHandler getSettings() { + return settingsHandler; + } + + @Override + public LoadersHandler getLoaders() { + return loadersHandler; + } + + public NPCHandler getNPCs() { + return npcHandler; + } + + public NMSAdapter getNMSAdapter() { + return nmsAdapter; + } + + public static void log(String message){ + plugin.getLogger().info(message); + } + + public static WildLoadersPlugin getPlugin(){ + return plugin; + } + +} diff --git a/src/main/java/com/bgsoftware/wildloaders/command/CommandsHandler.java b/src/main/java/com/bgsoftware/wildloaders/command/CommandsHandler.java new file mode 100644 index 0000000..b3919a0 --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/command/CommandsHandler.java @@ -0,0 +1,74 @@ +package com.bgsoftware.wildloaders.command; + +import com.bgsoftware.wildloaders.Locale; +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.command.commands.CmdGive; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; + +import java.util.ArrayList; +import java.util.List; + +public final class CommandsHandler implements CommandExecutor, TabCompleter { + + private final List subCommands = new ArrayList<>(); + private final WildLoadersPlugin plugin; + + public CommandsHandler(WildLoadersPlugin plugin){ + this.plugin = plugin; + subCommands.add(new CmdGive()); + } + + @Override + public boolean onCommand(CommandSender sender, org.bukkit.command.Command cmd, String label, String[] args) { + if(args.length > 0){ + for(ICommand subCommand : subCommands) { + if (subCommand.getLabel().equalsIgnoreCase(args[0])){ + if(subCommand.getPermission() != null && !sender.hasPermission(subCommand.getPermission())){ + Locale.NO_PERMISSION.send(sender); + return false; + } + if(args.length < subCommand.getMinArgs() || args.length > subCommand.getMaxArgs()){ + Locale.COMMAND_USAGE.send(sender, subCommand.getUsage()); + return false; + } + subCommand.perform(plugin, sender, args); + return true; + } + } + } + + + Locale.HELP_COMMAND_HEADER.send(sender); + for(ICommand subCommand : subCommands) + if(subCommand.getPermission() == null || sender.hasPermission(subCommand.getPermission())) + Locale.HELP_COMMAND_LINE.send(sender, subCommand.getUsage(), subCommand.getDescription()); + Locale.HELP_COMMAND_FOOTER.send(sender); + + return false; + } + + @Override + public List onTabComplete(CommandSender sender, org.bukkit.command.Command cmd, String label, String[] args) { + if(args.length > 0){ + for(ICommand subCommand : subCommands) { + if (subCommand.getLabel().equalsIgnoreCase(args[0])){ + if(subCommand.getPermission() != null && !sender.hasPermission(subCommand.getPermission())){ + return new ArrayList<>(); + } + return subCommand.tabComplete(plugin, sender, args); + } + } + } + + List list = new ArrayList<>(); + + for(ICommand subCommand : subCommands) + if(subCommand.getPermission() == null || sender.hasPermission(subCommand.getPermission())) + if(subCommand.getLabel().startsWith(args[0])) + list.add(subCommand.getLabel()); + + return list; + } +} diff --git a/src/main/java/com/bgsoftware/wildloaders/command/ICommand.java b/src/main/java/com/bgsoftware/wildloaders/command/ICommand.java new file mode 100644 index 0000000..5d8ea86 --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/command/ICommand.java @@ -0,0 +1,26 @@ +package com.bgsoftware.wildloaders.command; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import org.bukkit.command.CommandSender; + +import java.util.List; + +public interface ICommand { + + String getLabel(); + + String getUsage(); + + String getPermission(); + + String getDescription(); + + int getMinArgs(); + + int getMaxArgs(); + + void perform(WildLoadersPlugin plugin, CommandSender sender, String[] args); + + List tabComplete(WildLoadersPlugin plugin, CommandSender sender, String[] args); + +} diff --git a/src/main/java/com/bgsoftware/wildloaders/command/commands/CmdGive.java b/src/main/java/com/bgsoftware/wildloaders/command/commands/CmdGive.java new file mode 100644 index 0000000..c0c5d54 --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/command/commands/CmdGive.java @@ -0,0 +1,122 @@ +package com.bgsoftware.wildloaders.command.commands; + +import com.bgsoftware.wildloaders.Locale; +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.loaders.LoaderData; +import com.bgsoftware.wildloaders.command.ICommand; +import com.bgsoftware.wildloaders.utils.TimeUtils; +import com.bgsoftware.wildloaders.utils.items.ItemUtils; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public final class CmdGive implements ICommand { + + @Override + public String getLabel() { + return "give"; + } + + @Override + public String getUsage() { + return "loader give [amount] [time]"; + } + + @Override + public String getPermission() { + return "wildloaders.give"; + } + + @Override + public String getDescription() { + return "Give a chunk-loader item to a specific player."; + } + + @Override + public int getMinArgs() { + return 3; + } + + @Override + public int getMaxArgs() { + return 5; + } + + @Override + public void perform(WildLoadersPlugin plugin, CommandSender sender, String[] args) { + Player target = Bukkit.getPlayer(args[1]); + + if(target == null){ + Locale.INVALID_PLAYER.send(sender, args[1]); + return; + } + + Optional optionalLoaderData = plugin.getLoaders().getLoaderData(args[2]); + + if(!optionalLoaderData.isPresent()){ + Locale.INVALID_LOADER.send(sender, args[2]); + return; + } + + if(!target.equals(sender) && !sender.hasPermission(getPermission() + ".other")){ + Locale.NO_PERMISSION.send(sender); + return; + } + + if(target.equals(sender) && !(sender instanceof Player)){ + sender.sendMessage(ChatColor.RED + "You must give a chunk-loader item to a valid player."); + return; + } + + int amount = 1; + + if(args.length == 4){ + try{ + amount = Integer.parseInt(args[3]); + } catch (IllegalArgumentException e){ + Locale.INVALID_AMOUNT.send(sender, args[3]); + return; + } + } + + LoaderData loaderData = optionalLoaderData.get(); + + ItemStack itemStack = loaderData.getLoaderItem(); + itemStack.setAmount(amount); + + if(args.length == 5){ + itemStack = plugin.getNMSAdapter().setTag(itemStack, "loader-time", TimeUtils.fromString(args[4])); + } + + ItemUtils.addItems(target.getInventory(), target.getLocation(), itemStack); + + Locale.GIVE_SUCCESS.send(sender, amount, loaderData.getName(), target.getName()); + Locale.RECEIVE_SUCCESS.send(target, amount, loaderData.getName(), sender.getName()); + } + + @Override + public List tabComplete(WildLoadersPlugin plugin, CommandSender sender, String[] args) { + if(!sender.hasPermission(getPermission())) + return new ArrayList<>(); + + if (args.length == 3) { + List list = new ArrayList<>(); + for(LoaderData loaderData : plugin.getLoaders().getLoaderDatas()) + if(loaderData.getName().startsWith(args[2])) + list.add(loaderData.getName()); + return list; + } + + if (args.length >= 4) { + return new ArrayList<>(); + } + + return null; + } +} diff --git a/src/main/java/com/bgsoftware/wildloaders/handlers/LoadersHandler.java b/src/main/java/com/bgsoftware/wildloaders/handlers/LoadersHandler.java new file mode 100644 index 0000000..5e90118 --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/handlers/LoadersHandler.java @@ -0,0 +1,91 @@ +package com.bgsoftware.wildloaders.handlers; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.managers.LoadersManager; +import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; +import com.bgsoftware.wildloaders.api.loaders.LoaderData; +import com.bgsoftware.wildloaders.loaders.WChunkLoader; +import com.bgsoftware.wildloaders.loaders.WLoaderData; +import com.bgsoftware.wildloaders.utils.chunks.ChunkPosition; +import com.google.common.collect.Maps; +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public final class LoadersHandler implements LoadersManager { + + private final Map chunkLoaders = Maps.newConcurrentMap(); + private final Map chunkLoadersByChunks = Maps.newConcurrentMap(); + private final Map loadersData = Maps.newConcurrentMap(); + private final WildLoadersPlugin plugin; + + public LoadersHandler(WildLoadersPlugin plugin){ + this.plugin = plugin; + } + + @Override + public Optional getChunkLoader(Chunk chunk) { + return Optional.ofNullable(chunkLoadersByChunks.get(ChunkPosition.of(chunk))); + } + + @Override + public Optional getChunkLoader(Location location) { + return Optional.ofNullable(chunkLoaders.get(location)); + } + + @Override + public List getChunkLoaders() { + return Collections.unmodifiableList(new ArrayList<>(chunkLoaders.values())); + } + + @Override + public Optional getLoaderData(String name) { + return Optional.ofNullable(loadersData.get(name)); + } + + @Override + public List getLoaderDatas() { + return new ArrayList<>(loadersData.values()); + } + + @Override + public ChunkLoader addChunkLoader(LoaderData loaderData, Player whoPlaced, Location location, long timeLeft) { + ChunkLoader chunkLoader = new WChunkLoader(loaderData.getName(), whoPlaced, location, timeLeft); + chunkLoaders.put(location, chunkLoader); + chunkLoadersByChunks.put(ChunkPosition.of(location), chunkLoader); + plugin.getNPCs().createNPC(location); + return chunkLoader; + } + + @Override + public void removeChunkLoader(ChunkLoader chunkLoader) { + Location location = chunkLoader.getLocation(); + chunkLoaders.remove(location); + chunkLoadersByChunks.remove(ChunkPosition.of(location)); + chunkLoader.getNPC().ifPresent(npc -> plugin.getNPCs().killNPC(npc)); + } + + @Override + public void createLoaderData(String name, long timeLeft, ItemStack itemStack) { + LoaderData loaderData = new WLoaderData(name, timeLeft, itemStack); + loadersData.put(name, loaderData); + } + + @Override + public void removeLoadersData() { + loadersData.clear(); + } + + @Override + public void removeChunkLoaders() { + chunkLoaders.clear(); + chunkLoadersByChunks.clear(); + } +} diff --git a/src/main/java/com/bgsoftware/wildloaders/handlers/NPCHandler.java b/src/main/java/com/bgsoftware/wildloaders/handlers/NPCHandler.java new file mode 100644 index 0000000..f2d25ba --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/handlers/NPCHandler.java @@ -0,0 +1,163 @@ +package com.bgsoftware.wildloaders.handlers; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.managers.NPCManager; +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.utils.ServerVersion; +import com.bgsoftware.wildloaders.utils.locations.LocationUtils; +import com.google.common.collect.Maps; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.LivingEntity; + +import java.io.File; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; + +public final class NPCHandler implements NPCManager { + + private static final boolean PER_WORLD_NPCS = ServerVersion.isLessThan(ServerVersion.v1_14); + + private final WildLoadersPlugin plugin; + private final Map npcs = Maps.newConcurrentMap(); + private final Map npcUUIDs = Maps.newConcurrentMap(); + + public NPCHandler(WildLoadersPlugin plugin){ + this.plugin = plugin; + loadUUIDs(); + } + + @Override + public Optional getNPC(Location location) { + return Optional.ofNullable(npcs.get(new NPCIdentifier(location))); + } + + @Override + public ChunkLoaderNPC createNPC(Location location) { + return npcs.computeIfAbsent(new NPCIdentifier(location), i -> plugin.getNMSAdapter().createNPC(i.getSpawnLocation(), getUUID(i))); + } + + @Override + public boolean isNPC(LivingEntity livingEntity) { + ChunkLoaderNPC npc = getNPC(livingEntity.getLocation()).orElse(null); + return npc != null && npc.getUniqueId().equals(livingEntity.getUniqueId()); + } + + @Override + public void killNPC(ChunkLoaderNPC npc) { + if(!PER_WORLD_NPCS){ + NPCIdentifier identifier = new NPCIdentifier(npc.getLocation()); + npcs.remove(identifier); + + npcUUIDs.remove(identifier); + saveUUIDs(); + + npc.die(); + } + } + + @Override + public void killAllNPCs() { + for(ChunkLoaderNPC npc : npcs.values()){ + npc.die(); + } + + npcs.clear(); + } + + private void loadUUIDs(){ + File file = new File(plugin.getDataFolder(), "uuids.yml"); + + if(!file.exists()) + return; + + YamlConfiguration cfg = YamlConfiguration.loadConfiguration(file); + + for(String location : cfg.getConfigurationSection("").getKeys(false)){ + try{ + Location _location = LocationUtils.getLocation(location); + npcUUIDs.put(new NPCIdentifier(_location), UUID.fromString(cfg.getString(location))); + }catch(Exception ignored){} + } + } + + private UUID getUUID(NPCIdentifier identifier){ + if(npcUUIDs.containsKey(identifier)) + return npcUUIDs.get(identifier); + + UUID uuid; + + do{ + uuid = UUID.randomUUID(); + }while(npcUUIDs.containsValue(uuid)); + + npcUUIDs.put(identifier, uuid); + + saveUUIDs(); + + return uuid; + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + private void saveUUIDs(){ + if(Bukkit.isPrimaryThread()){ + Bukkit.getScheduler().runTaskAsynchronously(plugin, this::saveUUIDs); + return; + } + + File file = new File(plugin.getDataFolder(), "uuids.yml"); + + if(!file.exists()) + file.delete(); + + YamlConfiguration cfg = new YamlConfiguration(); + + for(Map.Entry entry : npcUUIDs.entrySet()) + cfg.set(LocationUtils.getLocation(entry.getKey().getSpawnLocation()), entry.getValue() + ""); + + try{ + file.getParentFile().mkdirs(); + file.createNewFile(); + cfg.save(file); + }catch(Exception ex){ + ex.printStackTrace(); + } + } + + private static final class NPCIdentifier{ + + private final Object identifier; + + NPCIdentifier(Location location){ + this.identifier = PER_WORLD_NPCS ? location.getWorld() : location; + } + + Location getSpawnLocation(){ + return PER_WORLD_NPCS ? ((World) identifier).getSpawnLocation() : (Location) identifier; + } + + @Override + public String toString() { + return PER_WORLD_NPCS ? ((World) identifier).getName() : identifier.toString(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NPCIdentifier that = (NPCIdentifier) o; + return Objects.equals(identifier, that.identifier); + } + + @Override + public int hashCode() { + return Objects.hash(identifier); + } + + } + +} diff --git a/src/main/java/com/bgsoftware/wildloaders/handlers/SettingsHandler.java b/src/main/java/com/bgsoftware/wildloaders/handlers/SettingsHandler.java new file mode 100644 index 0000000..45df3ca --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/handlers/SettingsHandler.java @@ -0,0 +1,97 @@ +package com.bgsoftware.wildloaders.handlers; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.utils.items.ItemBuilder; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.enchantments.Enchantment; + +import java.io.File; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public final class SettingsHandler { + + public List hologramLines; + + public SettingsHandler(WildLoadersPlugin plugin){ + WildLoadersPlugin.log("Loading configuration started..."); + long startTime = System.currentTimeMillis(); + int loadersAmount = 0; + File file = new File(plugin.getDataFolder(), "config.yml"); + + if(!file.exists()) + plugin.saveResource("config.yml", false); + + YamlConfiguration cfg = YamlConfiguration.loadConfiguration(file); + + hologramLines = cfg.getStringList("hologram-lines").stream() + .map(line -> ChatColor.translateAlternateColorCodes('&', line)).collect(Collectors.toList()); + + plugin.getLoaders().removeLoadersData(); + + for (String name : cfg.getConfigurationSection("chunkloaders").getKeys(false)) { + long timeLeft = cfg.getLong("chunkloaders." + name + ".time", 0); + + ItemBuilder itemBuilder = null; + + try{ + Material type = Material.valueOf(cfg.getString("chunkloaders." + name + ".type", "")); + short data = (short) cfg.getInt("chunkloaders." + name + ".data", 0); + + itemBuilder = new ItemBuilder(type, data); + + if(cfg.contains("chunkloaders." + name + ".name")) + itemBuilder.setDisplayName(ChatColor.translateAlternateColorCodes('&', + cfg.getString("chunkloaders." + name + ".name"))); + + if(cfg.contains("chunkloaders." + name + ".lore")) { + List lore = new ArrayList<>(); + + cfg.getStringList("chunkloaders." + name + ".lore") + .forEach(line -> lore.add(ChatColor.translateAlternateColorCodes('&', line))); + + itemBuilder.setLore(lore); + } + + if(cfg.contains("chunkloaders." + name + ".enchants")) { + for(String line : cfg.getStringList("chunkloaders." + name + ".enchants")){ + Enchantment enchantment = Enchantment.getByName(line.split(":")[0]); + int level = Integer.parseInt(line.split(":")[1]); + itemBuilder.addEnchant(enchantment, level); + } + } + + if(cfg.contains("chunkloaders." + name + ".skull")) { + itemBuilder.setTexture(cfg.getString("chunkloaders." + name + ".skull")); + } + } catch(Exception ignored){} + + if (timeLeft <= 0 || itemBuilder == null) { + WildLoadersPlugin.log("Something went wrong while loading chunk-loader '" + name + "'."); + continue; + } + + plugin.getLoaders().createLoaderData(name, timeLeft, itemBuilder.build()); + loadersAmount++; + } + + WildLoadersPlugin.log(" - Found " + loadersAmount + " chunk-loaders in config.yml."); + WildLoadersPlugin.log("Loading configuration done (Took " + (System.currentTimeMillis() - startTime) + "ms)"); + } + + public static void reload(){ + try{ + WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + Field settings = WildLoadersPlugin.class.getDeclaredField("settingsHandler"); + settings.setAccessible(true); + settings.set(plugin, new SettingsHandler(plugin)); + } catch(NoSuchFieldException | IllegalAccessException ex){ + ex.printStackTrace(); + } + } + +} diff --git a/src/main/java/com/bgsoftware/wildloaders/listeners/BlocksListener.java b/src/main/java/com/bgsoftware/wildloaders/listeners/BlocksListener.java new file mode 100644 index 0000000..6511b27 --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/listeners/BlocksListener.java @@ -0,0 +1,84 @@ +package com.bgsoftware.wildloaders.listeners; + +import com.bgsoftware.wildloaders.Locale; +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; +import com.bgsoftware.wildloaders.api.loaders.LoaderData; +import com.bgsoftware.wildloaders.utils.chunks.ChunkPosition; +import com.bgsoftware.wildloaders.utils.legacy.Materials; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; + +import java.util.Optional; + +@SuppressWarnings("unused") +public final class BlocksListener implements Listener { + + private final WildLoadersPlugin plugin; + + public BlocksListener(WildLoadersPlugin plugin){ + this.plugin = plugin; + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onLoaderPlace(BlockPlaceEvent e){ + String loaderName = plugin.getNMSAdapter().getTag(e.getItemInHand(), "loader-name", ""); + Optional optionalLoaderData = plugin.getLoaders().getLoaderData(loaderName); + + if(!optionalLoaderData.isPresent()) + return; + + if(!e.getPlayer().hasPermission("wildloaders.use")) { + Locale.NO_PLACE_PERMISSION.send(e.getPlayer()); + return; + } + + if(plugin.getLoaders().getChunkLoader(e.getBlock().getLocation().getChunk()).isPresent()){ + e.setCancelled(true); + Locale.ALREADY_LOADED.send(e.getPlayer()); + return; + } + + LoaderData loaderData = optionalLoaderData.get(); + + long timeLeft = plugin.getNMSAdapter().getTag(e.getItemInHand(), "loader-time", loaderData.getTimeLeft()); + + plugin.getLoaders().addChunkLoader(loaderData, e.getPlayer(), e.getBlock().getLocation(), timeLeft); + + Locale.PLACED_LOADER.send(e.getPlayer(), ChunkPosition.of(e.getBlock().getLocation())); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onLoaderBreak(BlockBreakEvent e){ + Location blockLoc = e.getBlock().getLocation(); + Optional optionalChunkLoader = plugin.getLoaders().getChunkLoader(blockLoc); + + if(!optionalChunkLoader.isPresent()) + return; + + ChunkLoader chunkLoader = optionalChunkLoader.get(); + chunkLoader.remove(); + + if(e.getPlayer().getGameMode() != GameMode.CREATIVE) + blockLoc.getWorld().dropItemNaturally(blockLoc, chunkLoader.getLoaderItem()); + + Locale.BROKE_LOADER.send(e.getPlayer()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onSpawnerPlace(BlockPlaceEvent e){ + if(e.getBlock().getType() != Materials.SPAWNER.toBukkitType()) + return; + + if(!plugin.getLoaders().getChunkLoader(e.getBlock().getChunk()).isPresent()) + return; + + plugin.getNMSAdapter().updateSpawner(e.getBlock().getLocation(), false); + } + +} diff --git a/src/main/java/com/bgsoftware/wildloaders/listeners/ChunksListener.java b/src/main/java/com/bgsoftware/wildloaders/listeners/ChunksListener.java new file mode 100644 index 0000000..660423b --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/listeners/ChunksListener.java @@ -0,0 +1,26 @@ +package com.bgsoftware.wildloaders.listeners; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.world.ChunkUnloadEvent; + +@SuppressWarnings("unused") +public final class ChunksListener implements Listener { + + private final WildLoadersPlugin plugin; + + public ChunksListener(WildLoadersPlugin plugin){ + this.plugin = plugin; + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onChunkUnload(ChunkUnloadEvent e){ + try { + if (plugin.getLoaders().getChunkLoader(e.getChunk()).isPresent()) + e.setCancelled(true); + }catch (Throwable ignored){} + } + +} diff --git a/src/main/java/com/bgsoftware/wildloaders/listeners/PlayersListener.java b/src/main/java/com/bgsoftware/wildloaders/listeners/PlayersListener.java new file mode 100644 index 0000000..0afb75a --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/listeners/PlayersListener.java @@ -0,0 +1,35 @@ +package com.bgsoftware.wildloaders.listeners; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import org.bukkit.Bukkit; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityDamageEvent; + +public final class PlayersListener implements Listener { + + private WildLoadersPlugin plugin; + + public PlayersListener(WildLoadersPlugin plugin){ + this.plugin = plugin; + } + + /** + * In some versions, the loaders can die even if they are in creative. + * This should fix the issue. + */ + + @EventHandler(priority = EventPriority.LOWEST) + public void onLoaderDamage(EntityDamageEvent e){ + if(!(e.getEntity() instanceof LivingEntity)) + return; + + if(plugin.getNPCs().isNPC((LivingEntity) e.getEntity())) { + e.setCancelled(true); + e.setDamage(0D); + } + } + +} diff --git a/src/main/java/com/bgsoftware/wildloaders/loaders/WChunkLoader.java b/src/main/java/com/bgsoftware/wildloaders/loaders/WChunkLoader.java new file mode 100644 index 0000000..6b8f5c6 --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/loaders/WChunkLoader.java @@ -0,0 +1,93 @@ +package com.bgsoftware.wildloaders.loaders; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; +import com.bgsoftware.wildloaders.api.loaders.LoaderData; +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.Optional; +import java.util.UUID; + +public final class WChunkLoader implements ChunkLoader { + + private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + + private final UUID whoPlaced; + private final Location location; + private final String loaderName; + + private boolean active = true; + private long timeLeft; + + public WChunkLoader(String loaderName, Player whoPlaced, Location location, long timeLeft){ + this.loaderName = loaderName; + this.whoPlaced = whoPlaced.getUniqueId(); + this.location = location.clone(); + this.timeLeft = timeLeft; + plugin.getNMSAdapter().createLoader(this); + } + + @Override + public LoaderData getLoaderData() { + return plugin.getLoaders().getLoaderData(loaderName).orElse(null); + } + + @Override + public OfflinePlayer getWhoPlaced() { + return Bukkit.getOfflinePlayer(whoPlaced); + } + + public boolean isNotActive(){ + if(active) + active = plugin.getLoaders().getChunkLoader(getLocation()).orElse(null) == this; + return !active; + } + + @Override + public long getTimeLeft() { + return timeLeft; + } + + public void tick(){ + timeLeft--; + if(timeLeft < 0) { + remove(); + } + } + + @Override + public Location getLocation() { + return location.clone(); + } + + @Override + public Optional getNPC() { + return plugin.getNPCs().getNPC(location); + } + + @Override + public void remove() { + if(!Bukkit.isPrimaryThread()){ + Bukkit.getScheduler().runTask(plugin, this::remove); + return; + } + + plugin.getNMSAdapter().removeLoader(this, timeLeft <= 0 || isNotActive()); + plugin.getLoaders().removeChunkLoader(this); + + getLocation().getBlock().setType(Material.AIR); + } + + @Override + public ItemStack getLoaderItem() { + ItemStack itemStack = getLoaderData().getLoaderItem(); + return plugin.getNMSAdapter().setTag(itemStack, "loader-time", getTimeLeft()); + } + +} diff --git a/src/main/java/com/bgsoftware/wildloaders/loaders/WLoaderData.java b/src/main/java/com/bgsoftware/wildloaders/loaders/WLoaderData.java new file mode 100644 index 0000000..d79c461 --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/loaders/WLoaderData.java @@ -0,0 +1,35 @@ +package com.bgsoftware.wildloaders.loaders; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.loaders.LoaderData; +import org.bukkit.inventory.ItemStack; + +public final class WLoaderData implements LoaderData { + + private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + + private final String name; + private final long timeLeft; + private final ItemStack loaderItem; + + public WLoaderData(String name, long timeLeft, ItemStack loaderItem){ + this.name = name; + this.timeLeft = timeLeft; + this.loaderItem = plugin.getNMSAdapter().setTag(loaderItem, "loader-name", name); + } + + @Override + public String getName() { + return name; + } + + @Override + public long getTimeLeft() { + return timeLeft; + } + + @Override + public ItemStack getLoaderItem() { + return loaderItem.clone(); + } +} diff --git a/src/main/java/com/bgsoftware/wildloaders/metrics/Metrics.java b/src/main/java/com/bgsoftware/wildloaders/metrics/Metrics.java new file mode 100644 index 0000000..3e078d4 --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/metrics/Metrics.java @@ -0,0 +1,707 @@ +package com.bgsoftware.wildloaders.metrics; + +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.RegisteredServiceProvider; +import org.bukkit.plugin.ServicePriority; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; + +import javax.net.ssl.HttpsURLConnection; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.logging.Level; +import java.util.zip.GZIPOutputStream; + +/** + * bStats collects some data for plugin authors. + *

+ * Check out https://bStats.org/ to learn more about bStats! + */ +@SuppressWarnings("all") +public class Metrics { + + static { + // You can use the property to disable the check in your test environment + if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) { + // Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D + final String defaultPackage = new String( + new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't'}); + final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); + // We want to make sure nobody just copy & pastes the example and use the wrong package names + if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) { + throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); + } + } + } + + // The version of this bStats class + public static final int B_STATS_VERSION = 1; + + // The url to which the data is sent + private static final String URL = "https://bStats.org/submitData/bukkit"; + + // Is bStats enabled on this server? + private boolean enabled; + + // Should failed requests be logged? + private static boolean logFailedRequests; + + // Should the sent data be logged? + private static boolean logSentData; + + // Should the response text be logged? + private static boolean logResponseStatusText; + + // The uuid of the server + private static String serverUUID; + + // The plugin + private final Plugin plugin; + + // A list with all custom charts + private final List charts = new ArrayList<>(); + + /** + * Class constructor. + * + * @param plugin The plugin which stats should be submitted. + */ + public Metrics(Plugin plugin) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null!"); + } + this.plugin = plugin; + + // Get the config file + File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); + File configFile = new File(bStatsFolder, "config.yml"); + YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); + + // Check if the config file exists + if (!config.isSet("serverUuid")) { + + // Add default values + config.addDefault("enabled", true); + // Every server gets it's unique random id. + config.addDefault("serverUuid", UUID.randomUUID().toString()); + // Should failed request be logged? + config.addDefault("logFailedRequests", false); + // Should the sent data be logged? + config.addDefault("logSentData", false); + // Should the response text be logged? + config.addDefault("logResponseStatusText", false); + + // Inform the server owners about bStats + config.options().header( + "bStats collects some data for plugin authors like how many servers are using their plugins.\n" + + "To honor their work, you should not disable it.\n" + + "This has nearly no effect on the server performance!\n" + + "Check out https://bStats.org/ to learn more :)" + ).copyDefaults(true); + try { + config.save(configFile); + } catch (IOException ignored) { } + } + + // Load the data + enabled = config.getBoolean("enabled", true); + serverUUID = config.getString("serverUuid"); + logFailedRequests = config.getBoolean("logFailedRequests", false); + logSentData = config.getBoolean("logSentData", false); + logResponseStatusText = config.getBoolean("logResponseStatusText", false); + + if (enabled) { + boolean found = false; + // Search for all other bStats Metrics classes to see if we are the first one + for (Class service : Bukkit.getServicesManager().getKnownServices()) { + try { + service.getField("B_STATS_VERSION"); // Our identifier :) + found = true; // We aren't the first + break; + } catch (NoSuchFieldException ignored) { } + } + // Register our service + Bukkit.getServicesManager().register(Metrics.class, this, plugin, ServicePriority.Normal); + if (!found) { + // We are the first! + startSubmitting(); + } + } + } + + /** + * Checks if bStats is enabled. + * + * @return Whether bStats is enabled or not. + */ + public boolean isEnabled() { + return enabled; + } + + /** + * Adds a custom chart. + * + * @param chart The chart to add. + */ + public void addCustomChart(CustomChart chart) { + if (chart == null) { + throw new IllegalArgumentException("Chart cannot be null!"); + } + charts.add(chart); + } + + /** + * Starts the Scheduler which submits our data every 30 minutes. + */ + private void startSubmitting() { + final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags + timer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + if (!plugin.isEnabled()) { // Plugin was disabled + timer.cancel(); + return; + } + // Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler + // Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;) + Bukkit.getScheduler().runTask(plugin, () -> submitData()); + } + }, 1000 * 60 * 5, 1000 * 60 * 30); + // Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start + // WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted! + // WARNING: Just don't do it! + } + + /** + * Gets the plugin specific data. + * This method is called using Reflection. + * + * @return The plugin specific data. + */ + public JSONObject getPluginData() { + JSONObject data = new JSONObject(); + + String pluginName = plugin.getDescription().getName(); + String pluginVersion = plugin.getDescription().getVersion(); + + data.put("pluginName", pluginName); // Append the name of the plugin + data.put("pluginVersion", pluginVersion); // Append the version of the plugin + JSONArray customCharts = new JSONArray(); + for (CustomChart customChart : charts) { + // Add the data of the custom charts + JSONObject chart = customChart.getRequestJsonObject(); + if (chart == null) { // If the chart is null, we skip it + continue; + } + customCharts.add(chart); + } + data.put("customCharts", customCharts); + + return data; + } + + /** + * Gets the server specific data. + * + * @return The server specific data. + */ + private JSONObject getServerData() { + // Minecraft specific data + int playerAmount; + try { + // Around MC 1.8 the return type was changed to a collection from an array, + // This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection; + Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers"); + playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class) + ? ((Collection) onlinePlayersMethod.invoke(Bukkit.getServer())).size() + : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length; + } catch (Exception e) { + playerAmount = Bukkit.getOnlinePlayers().length; // Just use the new method if the Reflection failed + } + int onlineMode = Bukkit.getOnlineMode() ? 1 : 0; + String bukkitVersion = Bukkit.getVersion(); + + // OS/Java specific data + String javaVersion = System.getProperty("java.version"); + String osName = System.getProperty("os.name"); + String osArch = System.getProperty("os.arch"); + String osVersion = System.getProperty("os.version"); + int coreCount = Runtime.getRuntime().availableProcessors(); + + JSONObject data = new JSONObject(); + + data.put("serverUUID", serverUUID); + + data.put("playerAmount", playerAmount); + data.put("onlineMode", onlineMode); + data.put("bukkitVersion", bukkitVersion); + + data.put("javaVersion", javaVersion); + data.put("osName", osName); + data.put("osArch", osArch); + data.put("osVersion", osVersion); + data.put("coreCount", coreCount); + + return data; + } + + /** + * Collects the data and sends it afterwards. + */ + private void submitData() { + final JSONObject data = getServerData(); + + JSONArray pluginData = new JSONArray(); + // Search for all other bStats Metrics classes to get their plugin data + for (Class service : Bukkit.getServicesManager().getKnownServices()) { + try { + service.getField("B_STATS_VERSION"); // Our identifier :) + + for (RegisteredServiceProvider provider : Bukkit.getServicesManager().getRegistrations(service)) { + try { + pluginData.add(provider.getService().getMethod("getPluginData").invoke(provider.getProvider())); + } catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { } + } + } catch (NoSuchFieldException ignored) { } + } + + data.put("plugins", pluginData); + + // Create a new thread for the connection to the bStats server + new Thread(new Runnable() { + @Override + public void run() { + try { + // Send the data + sendData(plugin, data); + } catch (Exception e) { + // Something went wrong! :( + if (logFailedRequests) { + plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e); + } + } + } + }).start(); + } + + /** + * Sends the data to the bStats server. + * + * @param plugin Any plugin. It's just used to get a logger instance. + * @param data The data to send. + * @throws Exception If the request failed. + */ + private static void sendData(Plugin plugin, JSONObject data) throws Exception { + if (data == null) { + throw new IllegalArgumentException("Data cannot be null!"); + } + if (Bukkit.isPrimaryThread()) { + throw new IllegalAccessException("This method must not be called from the main thread!"); + } + if (logSentData) { + plugin.getLogger().info("Sending data to bStats: " + data.toString()); + } + HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection(); + + // Compress the data to save bandwidth + byte[] compressedData = compress(data.toString()); + + // Add headers + connection.setRequestMethod("POST"); + connection.addRequestProperty("Accept", "application/json"); + connection.addRequestProperty("Connection", "close"); + connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request + connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); + connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format + connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION); + + // Send data + connection.setDoOutput(true); + DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream()); + outputStream.write(compressedData); + outputStream.flush(); + outputStream.close(); + + InputStream inputStream = connection.getInputStream(); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + + StringBuilder builder = new StringBuilder(); + String line; + while ((line = bufferedReader.readLine()) != null) { + builder.append(line); + } + bufferedReader.close(); + if (logResponseStatusText) { + plugin.getLogger().info("Sent data to bStats and received response: " + builder.toString()); + } + } + + /** + * Gzips the given String. + * + * @param str The string to gzip. + * @return The gzipped String. + * @throws IOException If the compression failed. + */ + private static byte[] compress(final String str) throws IOException { + if (str == null) { + return null; + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + GZIPOutputStream gzip = new GZIPOutputStream(outputStream); + gzip.write(str.getBytes(StandardCharsets.UTF_8)); + gzip.close(); + return outputStream.toByteArray(); + } + + /** + * Represents a custom chart. + */ + public static abstract class CustomChart { + + // The id of the chart + final String chartId; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + */ + CustomChart(String chartId) { + if (chartId == null || chartId.isEmpty()) { + throw new IllegalArgumentException("ChartId cannot be null or empty!"); + } + this.chartId = chartId; + } + + private JSONObject getRequestJsonObject() { + JSONObject chart = new JSONObject(); + chart.put("chartId", chartId); + try { + JSONObject data = getChartData(); + if (data == null) { + // If the data is null we don't send the chart. + return null; + } + chart.put("data", data); + } catch (Throwable t) { + if (logFailedRequests) { + Bukkit.getLogger().log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t); + } + return null; + } + return chart; + } + + protected abstract JSONObject getChartData() throws Exception; + + } + + /** + * Represents a custom simple pie. + */ + public static class SimplePie extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimplePie(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + String value = callable.call(); + if (value == null || value.isEmpty()) { + // Null = skip the chart + return null; + } + data.put("value", value); + return data; + } + } + + /** + * Represents a custom advanced pie. + */ + public static class AdvancedPie extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedPie(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + continue; // Skip this invalid + } + allSkipped = false; + values.put(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + } + + /** + * Represents a custom drilldown pie. + */ + public static class DrilldownPie extends CustomChart { + + private final Callable>> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public DrilldownPie(String chartId, Callable>> callable) { + super(chartId); + this.callable = callable; + } + + @Override + public JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map> map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean reallyAllSkipped = true; + for (Map.Entry> entryValues : map.entrySet()) { + JSONObject value = new JSONObject(); + boolean allSkipped = true; + for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) { + value.put(valueEntry.getKey(), valueEntry.getValue()); + allSkipped = false; + } + if (!allSkipped) { + reallyAllSkipped = false; + values.put(entryValues.getKey(), value); + } + } + if (reallyAllSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + } + + /** + * Represents a custom single line chart. + */ + public static class SingleLineChart extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SingleLineChart(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + int value = callable.call(); + if (value == 0) { + // Null = skip the chart + return null; + } + data.put("value", value); + return data; + } + + } + + /** + * Represents a custom multi line chart. + */ + public static class MultiLineChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public MultiLineChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() == 0) { + continue; // Skip this invalid + } + allSkipped = false; + values.put(entry.getKey(), entry.getValue()); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + + } + + /** + * Represents a custom simple bar chart. + */ + public static class SimpleBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimpleBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + for (Map.Entry entry : map.entrySet()) { + JSONArray categoryValues = new JSONArray(); + categoryValues.add(entry.getValue()); + values.put(entry.getKey(), categoryValues); + } + data.put("values", values); + return data; + } + + } + + /** + * Represents a custom advanced bar chart. + */ + public static class AdvancedBarChart extends CustomChart { + + private final Callable> callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public AdvancedBarChart(String chartId, Callable> callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JSONObject getChartData() throws Exception { + JSONObject data = new JSONObject(); + JSONObject values = new JSONObject(); + Map map = callable.call(); + if (map == null || map.isEmpty()) { + // Null = skip the chart + return null; + } + boolean allSkipped = true; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue().length == 0) { + continue; // Skip this invalid + } + allSkipped = false; + JSONArray categoryValues = new JSONArray(); + for (int categoryValue : entry.getValue()) { + categoryValues.add(categoryValue); + } + values.put(entry.getKey(), categoryValues); + } + if (allSkipped) { + // Null = skip the chart + return null; + } + data.put("values", values); + return data; + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter.java b/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter.java new file mode 100644 index 0000000..78b254c --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter.java @@ -0,0 +1,31 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.inventory.ItemStack; + +import java.util.UUID; + +public interface NMSAdapter { + + String getTag(ItemStack itemStack, String key, String def); + + ItemStack setTag(ItemStack itemStack, String key, String value); + + long getTag(ItemStack itemStack, String key, long def); + + ItemStack setTag(ItemStack itemStack, String key, long value); + + ItemStack getPlayerSkull(ItemStack itemStack, String texture); + + ChunkLoaderNPC createNPC(Location location, UUID uuid); + + void createLoader(ChunkLoader chunkLoader); + + void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle); + + void updateSpawner(Location location, boolean reset); + +} diff --git a/src/main/java/com/bgsoftware/wildloaders/npc/DummyChannel.java b/src/main/java/com/bgsoftware/wildloaders/npc/DummyChannel.java new file mode 100644 index 0000000..44afb0c --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/npc/DummyChannel.java @@ -0,0 +1,81 @@ +package com.bgsoftware.wildloaders.npc; + +import io.netty.channel.AbstractChannel; +import io.netty.channel.ChannelConfig; +import io.netty.channel.ChannelMetadata; +import io.netty.channel.ChannelOutboundBuffer; +import io.netty.channel.EventLoop; + +import java.net.SocketAddress; + +public final class DummyChannel extends AbstractChannel { + + public DummyChannel(){ + super(null); + } + + @Override + protected AbstractUnsafe newUnsafe() { + return null; + } + + @Override + protected boolean isCompatible(EventLoop eventLoop) { + return false; + } + + @Override + protected SocketAddress localAddress0() { + return null; + } + + @Override + protected SocketAddress remoteAddress0() { + return null; + } + + @Override + protected void doBind(SocketAddress socketAddress){ + + } + + @Override + protected void doDisconnect(){ + + } + + @Override + protected void doClose(){ + + } + + @Override + protected void doBeginRead(){ + + } + + @Override + protected void doWrite(ChannelOutboundBuffer channelOutboundBuffer){ + + } + + @Override + public ChannelConfig config() { + return null; + } + + @Override + public boolean isOpen() { + return false; + } + + @Override + public boolean isActive() { + return false; + } + + @Override + public ChannelMetadata metadata() { + return null; + } +} diff --git a/src/main/java/com/bgsoftware/wildloaders/utils/ServerVersion.java b/src/main/java/com/bgsoftware/wildloaders/utils/ServerVersion.java new file mode 100644 index 0000000..71f9cf4 --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/utils/ServerVersion.java @@ -0,0 +1,56 @@ +package com.bgsoftware.wildloaders.utils; + +import org.bukkit.Bukkit; + +public enum ServerVersion { + + v1_7(17), + v1_8(18), + v1_9(19), + v1_10(110), + v1_11(111), + v1_12(112), + v1_13(113), + v1_14(114), + v1_15(115), + v1_16(116); + + private static final ServerVersion currentVersion; + private static final String bukkitVersion; + private static final boolean legacy; + + static { + bukkitVersion = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; + String[] sections = bukkitVersion.split("_"); + currentVersion = ServerVersion.valueOf(sections[0] + "_" + sections[1]); + legacy = isLessThan(ServerVersion.v1_13); + } + + private final int code; + + ServerVersion(int code){ + this.code = code; + } + + + public static boolean isAtLeast(ServerVersion serverVersion){ + return currentVersion.code >= serverVersion.code; + } + + public static boolean isLessThan(ServerVersion serverVersion){ + return currentVersion.code < serverVersion.code; + } + + public static boolean isEquals(ServerVersion serverVersion){ + return currentVersion.code == serverVersion.code; + } + + public static boolean isLegacy(){ + return legacy; + } + + public static String getBukkitVersion(){ + return bukkitVersion; + } + +} diff --git a/src/main/java/com/bgsoftware/wildloaders/utils/TimeUtils.java b/src/main/java/com/bgsoftware/wildloaders/utils/TimeUtils.java new file mode 100644 index 0000000..3d2d168 --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/utils/TimeUtils.java @@ -0,0 +1,101 @@ +package com.bgsoftware.wildloaders.utils; + +import com.google.common.collect.Maps; + +import java.util.Map; +import java.util.concurrent.TimeUnit; + +public final class TimeUtils { + + private static final int MINUTES = 60, HOURS = 3600, DAYS = 86400; + + public static long fromString(String time){ + Map timeUnits = parseString(time); + long timeValue = 0; + + for(Map.Entry entry : timeUnits.entrySet()){ + switch (entry.getKey()){ + case SECONDS: + timeValue += entry.getValue(); + break; + case MINUTES: + timeValue += entry.getValue() * MINUTES; + break; + case HOURS: + timeValue += entry.getValue() * HOURS; + break; + case DAYS: + timeValue += entry.getValue() * DAYS; + break; + } + } + + return timeValue; + } + + private static Map parseString(String str){ + Map timeUnits = Maps.newHashMap(); + + TimeUnit timeUnit; + StringBuilder value = new StringBuilder(); + + for(int i = 0; i < str.length(); i++){ + char ch = str.charAt(i); + if(Character.isDigit(ch)){ + value.append(ch); + } + + if(!Character.isDigit(ch) || i == str.length() - 1){ + int timeValue = -1; + + try { + timeValue = Integer.parseInt(value.toString()); + }catch(Exception ignored){} + + switch (ch){ + case 'm': + case 'M': + timeUnit = TimeUnit.MINUTES; + break; + case 'h': + case 'H': + timeUnit = TimeUnit.HOURS; + break; + case 'd': + case 'D': + timeUnit = TimeUnit.DAYS; + break; + default: + timeUnit = TimeUnit.SECONDS; + break; + } + + if(timeValue != -1 && !timeUnits.containsKey(timeUnit)){ + timeUnits.put(timeUnit, timeValue); + } + + value = new StringBuilder(); + } + } + + return timeUnits; + } + + public static String[] formatTime(long time){ + String[] result = new String[4]; + + result[0] = String.valueOf(time / DAYS); + time = time % DAYS; + + result[1] = String.valueOf(time / HOURS); + time = time % HOURS; + + result[2] = String.valueOf(time / MINUTES); + time = time % MINUTES; + + result[3] = String.valueOf(time); + + return result; + } + +} diff --git a/src/main/java/com/bgsoftware/wildloaders/utils/chunks/ChunkPosition.java b/src/main/java/com/bgsoftware/wildloaders/utils/chunks/ChunkPosition.java new file mode 100644 index 0000000..c97ef41 --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/utils/chunks/ChunkPosition.java @@ -0,0 +1,59 @@ +package com.bgsoftware.wildloaders.utils.chunks; + +import org.bukkit.Chunk; +import org.bukkit.Location; + +import java.util.Objects; + +public final class ChunkPosition { + + private final String world; + private final int x, z; + + private ChunkPosition(String world, int x, int z){ + this.world = world; + this.x = x; + this.z = z; + } + + public String getWorld() { + return world; + } + + public int getX() { + return x; + } + + public int getZ() { + return z; + } + + @Override + public String toString() { + return world + ", " + x + ", " + z; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ChunkPosition that = (ChunkPosition) o; + return x == that.x && + z == that.z && + world.equals(that.world); + } + + @Override + public int hashCode() { + return Objects.hash(world, x, z); + } + + public static ChunkPosition of(Location location){ + return new ChunkPosition(location.getWorld().getName(), location.getBlockX() >> 4, location.getBlockZ() >> 4); + } + + public static ChunkPosition of(Chunk chunk){ + return new ChunkPosition(chunk.getWorld().getName(), chunk.getX(), chunk.getZ()); + } + +} diff --git a/src/main/java/com/bgsoftware/wildloaders/utils/config/CommentedConfiguration.java b/src/main/java/com/bgsoftware/wildloaders/utils/config/CommentedConfiguration.java new file mode 100644 index 0000000..185602a --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/utils/config/CommentedConfiguration.java @@ -0,0 +1,462 @@ +/******************************************************************************* + * + * CommentedConfiguration + * Developed by Ome_R + * + * You may use the resource and/or modify it - but not + * claiming it as your own work. You are not allowed + * to remove this message, unless being permitted by + * the developer of the resource. + * + * Spigot: https://www.spigotmc.org/resources/authors/ome_r.25629/ + * MC-Market: https://www.mc-market.org/resources/authors/40228/ + * Github: https://github.com/OmerBenGera?tab=repositories + * Website: https://bg-software.com/ + * + *******************************************************************************/ + +package com.bgsoftware.wildloaders.utils.config; + +import org.bukkit.Bukkit; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public final class CommentedConfiguration extends YamlConfiguration { + + /** + * Holds all comments for the config. + * Can be accessed by using the methods that are provided + * by the class. + */ + private final Map configComments = new HashMap<>(); + + /** + * Sync the config with another resource. + * This method can be used as an auto updater for your config files. + * @param file The file to save changes into if there are any. + * @param resource The resource to sync with. Can be provided by JavaPlugin#getResource + * @param ignoredSections An array of sections that will be ignored, and won't get updated + * if they are already exist in the config. If they are not in the + * config, they will be synced with the resource's config. + */ + public void syncWithConfig(File file, InputStream resource, String... ignoredSections){ + CommentedConfiguration cfg = loadConfiguration(resource); + if(syncConfigurationSection(cfg, cfg.getConfigurationSection(""), Arrays.asList(ignoredSections)) && file != null) { + try { + save(file); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + } + + /** + * Set a new comment to a path. + * You can remove comments by providing a null as a comment argument. + * @param path The path to set the comment to. + * @param comment The comment to set. Supports multiple lines (use \n as a spacer). + */ + public void setComment(String path, String comment){ + if(comment == null) + configComments.remove(path); + else + configComments.put(path, comment); + } + + /** + * Get a comment of a path. + * @param path The path to get the comment of. + * @return Returns a string that contains the comment. If no comment exists, null will be returned. + */ + public String getComment(String path){ + return getComment(path, null); + } + + /** + * Get a comment of a path with a default value if no comment exists. + * @param path The path to get the comment of. + * @param def A default comment that will be returned if no comment exists for the path. + * @return Returns a string that contains the comment. If no comment exists, the def value will be returned. + */ + public String getComment(String path, String def){ + return configComments.getOrDefault(path, def); + } + + /** + * Checks whether or not a path has a comment. + * @param path The path to check. + * @return Returns true if there's an existing comment, otherwise false. + */ + public boolean containsComment(String path){ + return getComment(path) != null; + } + + /** + * Load all data related to the config file - keys, values and comments. + * @param contents The contents of the file. + * @throws InvalidConfigurationException if the contents are invalid. + */ + @Override + public void loadFromString(String contents) throws InvalidConfigurationException { + //Load data of the base yaml (keys and values). + super.loadFromString(contents); + + //Parse the contents into lines. + String[] lines = contents.split("\n"); + int currentIndex = 0; + + //Variables that are used to track progress. + StringBuilder comments = new StringBuilder(); + String currentSection = ""; + + while(currentIndex < lines.length){ + //Checking if the current line is a comment. + if(isComment(lines[currentIndex])){ + //Adding the comment to the builder with an enter at the end. + comments.append(lines[currentIndex]).append("\n"); + } + + //Checking if the current line is a valid new section. + else if(isNewSection(lines[currentIndex])){ + //Parsing the line into a full-path. + currentSection = getSectionPath(this, lines[currentIndex], currentSection); + + //If there is a valid comment for the section. + if(comments.length() > 0) + //Adding the comment. + setComment(currentSection, comments.toString().substring(0, comments.length() - 1)); + + //Reseting the comment variable for further usage. + comments = new StringBuilder(); + } + + //Skipping to the next line. + currentIndex++; + } + } + + @Override + public void save(File file){ + try { + super.save(file); + }catch(Exception ex){ + ex.printStackTrace(); + } + } + + /** + * Parsing all the data (keys, values and comments) into a valid string, that will be written into a file later. + * @return A string that contains all the data, ready to be written into a file. + */ + @Override + public String saveToString() { + //First, we set headers to null - as we will handle all comments, including headers, in this method. + this.options().header(null); + //Get the string of the data (keys and values) and parse it into an array of lines. + List lines = new ArrayList<>(Arrays.asList(super.saveToString().split("\n"))); + + //Variables that are used to track progress. + int currentIndex = 0; + String currentSection = ""; + + while(currentIndex < lines.size()){ + String line = lines.get(currentIndex); + + //Checking if the line is a new section. + if(isNewSection(line)){ + //Parsing the line into a full-path. + currentSection = getSectionPath(this, line, currentSection); + //Checking if that path contains a comment + if(containsComment(currentSection)) { + //Adding the comment to the lines array at that index (adding it one line before the actual line) + lines.add(currentIndex, getComment(currentSection)); + //Skipping one line so the pointer will point to the line we checked again. + currentIndex++; + } + } + + //Skipping to the next line. + currentIndex++; + } + + //Parsing the array of lines into a one single string. + StringBuilder contents = new StringBuilder(); + for(String line : lines) + contents.append("\n").append(line); + + return contents.length() == 0 ? "" : contents.substring(1); + } + + /** + * Sync a specific configuration section with another one, recursively. + * @param commentedConfig The config that contains the data we need to sync with. + * @param section The current section that we sync. + * @param ignoredSections A list of ignored sections that won't be synced (unless not found in the file). + * @return Returns true if there were any changes, otherwise false. + */ + private boolean syncConfigurationSection(CommentedConfiguration commentedConfig, ConfigurationSection section, List ignoredSections){ + //Variables that are used to track progress. + boolean changed = false; + + //Going through all the keys of the section. + for (String key : section.getKeys(false)) { + //Parsing the key into a full-path. + String path = section.getCurrentPath().isEmpty() ? key : section.getCurrentPath() + "." + key; + + //Checking if the key is also a section. + if (section.isConfigurationSection(key)) { + //Checking if the section is ignored. + boolean isIgnored = ignoredSections.stream().anyMatch(path::contains); + //Checking if the config contains the section. + boolean containsSection = contains(path); + //If the config doesn't contain the section, or it's not ignored - we will sync data. + if(!containsSection || !isIgnored) { + //Syncing data and updating the changed variable. + changed = syncConfigurationSection(commentedConfig, section.getConfigurationSection(key), ignoredSections) || changed; + } + } + + //Checking if the config contains the path (not a section). + else if (!contains(path)) { + //Saving the value of the key into the config. + set(path, section.get(key)); + //Updating variables. + changed = true; + } + + //Checking if there is a valid comment for the path, and also making sure the comments are not the same. + if (commentedConfig.containsComment(path) && !commentedConfig.getComment(path).equals(getComment(path))) { + //Saving the comment of the key into the config. + setComment(path, commentedConfig.getComment(path)); + //Updating variable. + changed = true; + } + + } + + /*Keys cannot be ordered easily, so we need to do some tricks to make sure + all of the keys are ordered correctly (and the new config will look the same + as the resource that was provided).*/ + + //Checking if there was a value that had been added into the config + if(changed) + correctIndexes(section, getConfigurationSection(section.getCurrentPath())); + + return changed; + } + + /** + * Load a config from a file. + * @param file The file to load the config from. + * @return A new instance of CommentedConfiguration contains all the data (keys, values and comments). + */ + public static CommentedConfiguration loadConfiguration(File file) { + try { + FileInputStream stream = new FileInputStream(file); + return loadConfiguration(new InputStreamReader(stream, StandardCharsets.UTF_8)); + }catch(FileNotFoundException ex){ + Bukkit.getLogger().warning("File " + file.getName() + " doesn't exist."); + return new CommentedConfiguration(); + } + } + + /** + * Load a config from an input-stream, which is used for resources that are obtained using JavaPlugin#getResource. + * @param inputStream The input-stream to load the config from. + * @return A new instance of CommentedConfiguration contains all the data (keys, values and comments). + */ + public static CommentedConfiguration loadConfiguration(InputStream inputStream) { + return loadConfiguration(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + } + + /** + * Load a config from a reader (used for files and streams together). + * @param reader The reader to load the config from. + * @return A new instance of CommentedConfiguration contains all the data (keys, values and comments). + */ + public static CommentedConfiguration loadConfiguration(Reader reader) { + //Creating a blank instance of the config. + CommentedConfiguration config = new CommentedConfiguration(); + + //Parsing the reader into a BufferedReader for an easy reading of it. + try(BufferedReader bufferedReader = reader instanceof BufferedReader ? (BufferedReader)reader : new BufferedReader(reader)){ + StringBuilder contents = new StringBuilder(); + + String line; + while((line = bufferedReader.readLine()) != null) { + contents.append(line).append('\n'); + } + + config.loadFromString(contents.toString()); + } catch (IOException | InvalidConfigurationException ex) { + ex.printStackTrace(); + } + + return config; + } + + /** + * Checks if a line is a new section or not. + * Sadly, that's not possible to use ":" as a spacer between key and value, and ": " must be used. + * @param line The line to check. + * @return True if the line is a new section, otherwise false. + */ + private static boolean isNewSection(String line){ + String trimLine = line.trim(); + return trimLine.contains(": ") || trimLine.endsWith(":"); + } + + /** + * Creates a full path of a line. + * @param commentedConfig The config to get the path from. + * @param line The line to get the path of. + * @param currentSection The last known section. + * @return The full path of the line. + */ + private static String getSectionPath(CommentedConfiguration commentedConfig, String line, String currentSection){ + //Removing all spaces and getting the name of the section. + String newSection = line.trim().split(": ")[0]; + + //Parsing some formats to make sure having a plain name. + if(newSection.endsWith(":")) + newSection = newSection.substring(0, newSection.length() - 1); + if(newSection.startsWith(".")) + newSection = newSection.substring(1); + if(newSection.startsWith("'") && newSection.endsWith("'")) + newSection = newSection.substring(1, newSection.length() - 1); + + //Checking if the new section is a child-section of the currentSection. + if(!currentSection.isEmpty() && commentedConfig.contains(currentSection + "." + newSection)){ + newSection = currentSection + "." + newSection; + } + + //Looking for the parent of the the new section. + else{ + String parentSection = currentSection; + + /*Getting the parent of the new section. The loop will stop in one of the following situations: + 1) The parent is empty - which means we have no where to go, as that's the root section. + 2) The config contains a valid path that was built with ..*/ + while(!parentSection.isEmpty() && + !commentedConfig.contains((parentSection = getParentPath(parentSection)) + "." + newSection)); + + //Parsing and building the new full path. + newSection = parentSection.trim().isEmpty() ? newSection : parentSection + "." + newSection; + } + + return newSection; + } + + /** + * Checks if a line represents a comment or not. + * @param line The line to check. + * @return True if the line is a comment (stars with # or it's empty), otherwise false. + */ + private static boolean isComment(String line){ + String trimLine = line.trim(); + return trimLine.startsWith("#") || trimLine.isEmpty(); + } + + /** + * Get the parent path of the provided path, by removing the last '.' from the path. + * @param path The path to check. + * @return The parent path of the provided path. + */ + private static String getParentPath(String path){ + return path.contains(".") ? path.substring(0, path.lastIndexOf('.')) : ""; + } + + /** + * Convert key-indexes of a section into the same key-indexes that another section has. + * @param section The section that will be used as a way to get the correct indexes. + * @param target The target section that will be ordered again. + */ + private static void correctIndexes(ConfigurationSection section, ConfigurationSection target){ + //Parsing the sections into ArrayLists with their keys and values. + List> sectionMap = getSectionMap(section), correctOrder = new ArrayList<>(); + + //Going through the sectionMap, which is in the correct order. + for (Pair entry : sectionMap) { + //Adding the entry into a new list with the correct value from the target section. + correctOrder.add(new Pair<>(entry.key, target.get(entry.key))); + } + + /*The only way to change key-indexes is to add them one-by-one again, in the correct order. + In order to do so, the section needs to be cleared so the indexes will be reset.*/ + + //Clearing the configuration. + clearConfiguration(target); + + //Adding the entries again in the correct order. + for(Pair entry : correctOrder) + target.set(entry.key, entry.value); + } + + /** + * Parsing a section into a list that contains all of it's keys and their values without changing their order. + * @param section The section to convert. + * @return A list that contains all the keys and their values. + */ + private static List> getSectionMap(ConfigurationSection section){ + //Creating an empty ArrayList. + List> list = new ArrayList<>(); + + //Going through all the keys and adding them in the same order. + for(String key : section.getKeys(false)) { + list.add(new Pair<>(key, section.get(key))); + } + + return list; + } + + /** + * Clear a configuration section from all of it's keys. + * This can be done by setting all the keys' values to null. + * @param section The section to clear. + */ + private static void clearConfiguration(ConfigurationSection section){ + for(String key : section.getKeys(false)) + section.set(key, null); + } + + /** + * A class that is used as a way of representing a map's entry (which is not implemented). + */ + private static class Pair{ + + private final K key; + private final V value; + + Pair(K key, V value){ + this.key = key; + this.value = value; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof Pair && key.equals(((Pair) obj).key) && value.equals(((Pair) obj).value); + } + + @Override + public int hashCode() { + return key.hashCode(); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/bgsoftware/wildloaders/utils/items/ItemBuilder.java b/src/main/java/com/bgsoftware/wildloaders/utils/items/ItemBuilder.java new file mode 100644 index 0000000..763438d --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/utils/items/ItemBuilder.java @@ -0,0 +1,79 @@ +package com.bgsoftware.wildloaders.utils.items; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; + +import java.util.Arrays; +import java.util.List; + +@SuppressWarnings("WeakerAccess") +public final class ItemBuilder { + + private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + + private final ItemStack itemStack; + private final ItemMeta itemMeta; + private String texture = ""; + + public ItemBuilder(Material type){ + this(type, 1); + } + + public ItemBuilder(Material type, int amount){ + this(type, amount, (short) 0); + } + + public ItemBuilder(Material type, short damage){ + this(type, 1, damage); + } + + public ItemBuilder(Material type, int amount, short damage){ + this(new ItemStack(type, amount, damage)); + } + + public ItemBuilder(ItemStack itemStack){ + this.itemStack = itemStack; + this.itemMeta = itemStack.getItemMeta(); + } + + public ItemBuilder setDisplayName(String name){ + itemMeta.setDisplayName(name); + return this; + } + + public ItemBuilder setLore(String... lore){ + return setLore(Arrays.asList(lore)); + } + + public ItemBuilder setLore(List lore){ + itemMeta.setLore(lore); + return this; + } + + public ItemBuilder setOwner(String playerName){ + if(itemMeta instanceof SkullMeta){ + ((SkullMeta) itemMeta).setOwner(playerName); + } + return this; + } + + public ItemBuilder setTexture(String texture){ + this.texture = texture; + return this; + } + + public ItemBuilder addEnchant(Enchantment ench, int level){ + itemMeta.addEnchant(ench, level, true); + return this; + } + + public ItemStack build(){ + itemStack.setItemMeta(itemMeta); + return texture.isEmpty() ? itemStack : plugin.getNMSAdapter().getPlayerSkull(itemStack, texture); + } + +} diff --git a/src/main/java/com/bgsoftware/wildloaders/utils/items/ItemUtils.java b/src/main/java/com/bgsoftware/wildloaders/utils/items/ItemUtils.java new file mode 100644 index 0000000..1b39da8 --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/utils/items/ItemUtils.java @@ -0,0 +1,21 @@ +package com.bgsoftware.wildloaders.utils.items; + +import org.bukkit.Location; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.HashMap; + +public final class ItemUtils { + + private ItemUtils(){ + + } + + public static void addItems(Inventory inventory, Location location, ItemStack... itemStacks){ + HashMap leftOvers = inventory.addItem(itemStacks); + if(!leftOvers.isEmpty() && location != null) + leftOvers.values().forEach(itemStack -> location.getWorld().dropItemNaturally(location, itemStack)); + } + +} diff --git a/src/main/java/com/bgsoftware/wildloaders/utils/legacy/Materials.java b/src/main/java/com/bgsoftware/wildloaders/utils/legacy/Materials.java new file mode 100644 index 0000000..30f5bff --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/utils/legacy/Materials.java @@ -0,0 +1,42 @@ +package com.bgsoftware.wildloaders.utils.legacy; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; + +public enum Materials { + + SPAWNER("MOB_SPAWNER"); + + Materials(String bukkitType){ + this(bukkitType, 0); + } + + Materials(String bukkitType, int bukkitData){ + this.bukkitType = bukkitType; + this.bukkitData = (short) bukkitData; + } + + private final String bukkitType; + private final short bukkitData; + + public Material toBukkitType(){ + try { + try { + return Material.valueOf(bukkitType); + } catch (IllegalArgumentException ex) { + return Material.valueOf(name()); + } + }catch(Exception ex){ + throw new IllegalArgumentException("Couldn't cast " + name() + " into a bukkit enum. Contact Ome_R!"); + } + } + + public ItemStack toBukkitItem(){ + return toBukkitItem(1); + } + + public ItemStack toBukkitItem(int amount){ + return bukkitData == 0 ? new ItemStack(toBukkitType(), amount) : new ItemStack(toBukkitType(), amount, bukkitData); + } + +} diff --git a/src/main/java/com/bgsoftware/wildloaders/utils/locations/LocationUtils.java b/src/main/java/com/bgsoftware/wildloaders/utils/locations/LocationUtils.java new file mode 100644 index 0000000..05ef976 --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/utils/locations/LocationUtils.java @@ -0,0 +1,36 @@ +package com.bgsoftware.wildloaders.utils.locations; + +import org.bukkit.Bukkit; +import org.bukkit.Location; + +public final class LocationUtils { + + private LocationUtils(){} + + public static String getLocation(Location location){ + return location.getWorld().getName() + "," + location.getBlockX() + "," + location.getBlockY() + "," + location.getBlockZ(); + } + + public static Location getLocation(String location){ + String[] locationSections = location.split(","); + + if(locationSections.length != 4) + throw new IllegalArgumentException("Cannot parse location " + location); + + String worldName = locationSections[0]; + double x = parseDouble(locationSections[1]); + double y = parseDouble(locationSections[2]); + double z = parseDouble(locationSections[3]); + + return new Location(Bukkit.getWorld(worldName), x, y, z); + } + + private static double parseDouble(String str){ + try{ + return Double.parseDouble(str); + }catch (Exception ex){ + throw new IllegalArgumentException("Cannot parse double " + str); + } + } + +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..452883e --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,22 @@ +####################################################### +## ## +## WildLoaders Configuration ## +## Developed by Ome_R ## +## ## +####################################################### + +hologram-lines: +- '&b{0}''s Loader' +- '&bTime Left: &f{1} days, {2} hours, {3} minutes, {4} seconds' + +# List of all the chunk-loaders +# Do not name 2 chunk-loaders the same +chunkloaders: + normal_loader: + time: 1 #1 stands for 1x1 chunks, 3 for 3x3 and so on. + type: SPONGE + data: 1 + name: '&6Chunk Buster &7(Place to bust)' + lore: + - '&7Place this chunk buster' + - '&7to bust the chunk!' \ No newline at end of file diff --git a/src/main/resources/lang.yml b/src/main/resources/lang.yml new file mode 100644 index 0000000..a33a7a3 --- /dev/null +++ b/src/main/resources/lang.yml @@ -0,0 +1,21 @@ +####################################################### +## ## +## WildLoaders Configuration ## +## Developed by Ome_R ## +## ## +####################################################### + +ALREADY_LOADED: '&a&lWildLoaders &7This chunk is already loaded.' +BROKE_LOADER: '&a&lWildLoaders &7Successfully broken this chunk-loader.' +COMMAND_USAGE: '&cUsage: /{0}' +GIVE_SUCCESS: '&a&lWildLoaders &7Successfully gave x{0} {1} to {2}.' +HELP_COMMAND_HEADER: '&a&lWildLoaders &7Plugin Commands:' +HELP_COMMAND_LINE: '&a/{0} &7- {1}' +HELP_COMMAND_FOOTER: '' +INVALID_AMOUNT: '&a&lWildLoaders &7Invalid amount {0}.' +INVALID_LOADER: '&a&lWildLoaders &7Couldn''t find a valid chunk-loader with the name {0}.' +INVALID_PLAYER: '&a&lWildLoaders &7Couldn''t find a player with the name {0}.' +NO_PERMISSION: '&fUnknown command. Type "/help" for help.' +NO_PLACE_PERMISSION: '&a&lWildLoaders &7You are lacking the permission to place chunk loaders.' +PLACED_LOADER: '&a&lWildLoaders &7Successfully placed a new chunk-loader at {0}.' +RECEIVE_SUCCESS: '&a&lWildLoaders &7You received x{0} {1} from {2}.' \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..ab5116b --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,18 @@ +name: WildLoaders +version: 0.0.1 +api-version: 1.13 +main: com.bgsoftware.wildloaders.WildLoadersPlugin +description: Highly configurable and optimized chunk-loaders plugin. +website: https://bg-software.com/ +authors: [Ome_R] +commands: + loader: + description: Main command for wild loaders. +permissions: + wildloaders.*: + description: Gives access to all the commands. + default: op + children: + wildloaders.use: true + wildloaders.use: + description: Gives access to place a chunkloader. \ No newline at end of file diff --git a/v1_10_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_10_R1.java b/v1_10_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_10_R1.java new file mode 100644 index 0000000..640ab02 --- /dev/null +++ b/v1_10_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_10_R1.java @@ -0,0 +1,145 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.npc.DummyChannel; +import com.mojang.authlib.GameProfile; +import net.minecraft.server.v1_10_R1.DamageSource; +import net.minecraft.server.v1_10_R1.EntityPlayer; +import net.minecraft.server.v1_10_R1.EnumGamemode; +import net.minecraft.server.v1_10_R1.EnumProtocolDirection; +import net.minecraft.server.v1_10_R1.MinecraftServer; +import net.minecraft.server.v1_10_R1.NetworkManager; +import net.minecraft.server.v1_10_R1.Packet; +import net.minecraft.server.v1_10_R1.PacketPlayInBlockDig; +import net.minecraft.server.v1_10_R1.PacketPlayInBlockPlace; +import net.minecraft.server.v1_10_R1.PacketPlayInChat; +import net.minecraft.server.v1_10_R1.PacketPlayInFlying; +import net.minecraft.server.v1_10_R1.PacketPlayInHeldItemSlot; +import net.minecraft.server.v1_10_R1.PacketPlayInTransaction; +import net.minecraft.server.v1_10_R1.PacketPlayInUpdateSign; +import net.minecraft.server.v1_10_R1.PacketPlayInWindowClick; +import net.minecraft.server.v1_10_R1.PlayerConnection; +import net.minecraft.server.v1_10_R1.PlayerInteractManager; +import net.minecraft.server.v1_10_R1.WorldServer; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_10_R1.CraftServer; +import org.bukkit.craftbukkit.v1_10_R1.CraftWorld; + +import java.lang.reflect.Field; +import java.util.UUID; + +public final class ChunkLoaderNPC_v1_10_R1 extends EntityPlayer implements ChunkLoaderNPC { + + public ChunkLoaderNPC_v1_10_R1(Location location, UUID uuid){ + super(((CraftServer) Bukkit.getServer()).getServer(), ((CraftWorld) location.getWorld()).getHandle(), + new GameProfile(uuid, "Loader-" + location.getWorld().getName()), new PlayerInteractManager(((CraftWorld) location.getWorld()).getHandle())); + + playerConnection = new DummyPlayerConnection(server, this); + + playerInteractManager.setGameMode(EnumGamemode.CREATIVE); + fallDistance = 0.0F; + + fauxSleeping = true; + + spawnIn(world); + setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + world.players.add(this); + ((WorldServer) world).getPlayerChunkMap().addPlayer(this); + } + + @Override + public UUID getUniqueId() { + return super.getUniqueID(); + } + + @Override + public void die() { + super.die(); + } + + @Override + public Location getLocation() { + return getBukkitEntity().getLocation(); + } + + @Override + protected boolean damageEntity0(DamageSource damagesource, float f) { + return false; + } + + private static class DummyNetworkManager extends NetworkManager{ + + DummyNetworkManager(){ + super(EnumProtocolDirection.SERVERBOUND); + updateFields(); + } + + private void updateFields() { + try { + Field channelField = NetworkManager.class.getDeclaredField("channel"); + channelField.setAccessible(true); + channelField.set(this, new DummyChannel()); + channelField.setAccessible(false); + + Field socketAddressField = NetworkManager.class.getDeclaredField("l"); + socketAddressField.setAccessible(true); + socketAddressField.set(this, null); + } + catch (NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException e) { + e.printStackTrace(); + } + } + + } + + private static class DummyPlayerConnection extends PlayerConnection { + + DummyPlayerConnection(MinecraftServer minecraftServer, EntityPlayer entityPlayer) { + super(minecraftServer, new DummyNetworkManager(), entityPlayer); + } + + public void a(PacketPlayInWindowClick packetPlayInWindowClick) { + + } + + public void a(PacketPlayInTransaction packetPlayInTransaction) { + + } + + public void a(PacketPlayInFlying packetPlayInFlying) { + + } + + public void a(PacketPlayInUpdateSign packetPlayInUpdateSign) { + + } + + public void a(PacketPlayInBlockDig packetPlayInBlockDig) { + + } + + public void a(PacketPlayInBlockPlace packetPlayInBlockPlace) { + + } + + public void disconnect(String s) { + + } + + public void a(PacketPlayInHeldItemSlot packetPlayInHeldItemSlot) { + + } + + public void a(PacketPlayInChat packetPlayInChat) { + + } + + public void sendPacket(Packet packet) { + + } + + } + +} diff --git a/v1_10_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_10_R1.java b/v1_10_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_10_R1.java new file mode 100644 index 0000000..2f057cd --- /dev/null +++ b/v1_10_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_10_R1.java @@ -0,0 +1,282 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.loaders.WChunkLoader; +import net.minecraft.server.v1_10_R1.AxisAlignedBB; +import net.minecraft.server.v1_10_R1.Block; +import net.minecraft.server.v1_10_R1.BlockPosition; +import net.minecraft.server.v1_10_R1.Chunk; +import net.minecraft.server.v1_10_R1.Entity; +import net.minecraft.server.v1_10_R1.EntityArmorStand; +import net.minecraft.server.v1_10_R1.ITickable; +import net.minecraft.server.v1_10_R1.ItemStack; +import net.minecraft.server.v1_10_R1.NBTTagCompound; +import net.minecraft.server.v1_10_R1.NBTTagList; +import net.minecraft.server.v1_10_R1.NBTTagLong; +import net.minecraft.server.v1_10_R1.NBTTagString; +import net.minecraft.server.v1_10_R1.TileEntity; +import net.minecraft.server.v1_10_R1.TileEntityMobSpawner; +import net.minecraft.server.v1_10_R1.World; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_10_R1.CraftChunk; +import org.bukkit.craftbukkit.v1_10_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_10_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_10_R1.util.LongHash; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@SuppressWarnings({"ConstantConditions", "unused"}) +public final class NMSAdapter_v1_10_R1 implements NMSAdapter { + + private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + + @Override + public String getTag(org.bukkit.inventory.ItemStack itemStack, String key, String def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + if(!tagCompound.hasKeyOfType(key, 8)) + return def; + + return tagCompound.getString(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, String value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + tagCompound.set(key, new NBTTagString(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public long getTag(org.bukkit.inventory.ItemStack itemStack, String key, long def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + if(!tagCompound.hasKeyOfType(key, 4)) + return def; + + return tagCompound.getLong(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, long value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + tagCompound.set(key, new NBTTagLong(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack itemStack, String texture) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + + NBTTagCompound nbtTagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + NBTTagCompound skullOwner = nbtTagCompound.hasKey("SkullOwner") ? nbtTagCompound.getCompound("SkullOwner") : new NBTTagCompound(); + + NBTTagCompound properties = new NBTTagCompound(); + + NBTTagList textures = new NBTTagList(); + NBTTagCompound signature = new NBTTagCompound(); + signature.setString("Value", texture); + textures.add(signature); + + properties.set("textures", textures); + + skullOwner.set("Properties", properties); + skullOwner.setString("Id", UUID.randomUUID().toString()); + + nbtTagCompound.set("SkullOwner", skullOwner); + + nmsItem.setTag(nbtTagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public ChunkLoaderNPC createNPC(Location location, UUID uuid) { + return new ChunkLoaderNPC_v1_10_R1(location, uuid); + } + + @Override + public void createLoader(ChunkLoader chunkLoader) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + TileEntityChunkLoader tileEntityChunkLoader = new TileEntityChunkLoader(chunkLoader, world, blockPosition); + world.tileEntityListTick.add(tileEntityChunkLoader); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner).forEach(tileEntity -> { + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + tileEntity.save(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) -1); + tileEntity.a(nbtTagCompound); + }); + } + + @Override + public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + long tileEntityLong = LongHash.toLong(blockPosition.getX() >> 4, blockPosition.getZ() >> 4); + TileEntityChunkLoader tileEntityChunkLoader = TileEntityChunkLoader.tileEntityChunkLoaderMap.remove(tileEntityLong); + if(tileEntityChunkLoader != null) { + tileEntityChunkLoader.holograms.forEach(Entity::die); + tileEntityChunkLoader.removed = true; + world.tileEntityListTick.remove(tileEntityChunkLoader); + } + + if(spawnParticle) + world.a(null, 2001, blockPosition, Block.getCombinedId(world.getType(blockPosition))); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner).forEach(tileEntity -> { + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + tileEntity.save(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) 16); + tileEntity.a(nbtTagCompound); + }); + } + + @Override + public void updateSpawner(Location location, boolean reset) { + World world = ((CraftWorld) location.getWorld()).getHandle(); + + BlockPosition blockPosition = new BlockPosition(location.getX(), location.getY(), location.getZ()); + TileEntityMobSpawner mobSpawner = (TileEntityMobSpawner) world.getTileEntity(blockPosition); + + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + mobSpawner.save(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) (reset ? 16 : -1)); + mobSpawner.a(nbtTagCompound); + } + + private static final class TileEntityChunkLoader extends TileEntity implements ITickable { + + private static final Map tileEntityChunkLoaderMap = new HashMap<>(); + + private final List holograms = new ArrayList<>(); + private final ChunkLoader chunkLoader; + + private short currentTick = 20; + private short daysAmount, hoursAmount, minutesAmount, secondsAmount; + private boolean removed = false; + + TileEntityChunkLoader(ChunkLoader chunkLoader, World world, BlockPosition blockPosition){ + this.chunkLoader = chunkLoader; + + setPosition(blockPosition); + a(world); + + long timeLeft = chunkLoader.getTimeLeft(); + + daysAmount = (short) (timeLeft / 86400); + timeLeft = timeLeft % 86400; + + hoursAmount = (short) (timeLeft / 3600); + timeLeft = timeLeft % 3600; + + minutesAmount = (short) (timeLeft / 60); + timeLeft = timeLeft % 60; + + secondsAmount = (short) timeLeft; + + tileEntityChunkLoaderMap.put(LongHash.toLong(blockPosition.getX() >> 4, blockPosition.getZ() >> 4), this); + + double currentY = position.getY() + 1; + for(int i = plugin.getSettings().hologramLines.size(); i > 0; i--){ + EntityHologram hologram = new EntityHologram(world, position.getX() + 0.5, currentY, position.getZ() + 0.5); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + world.addEntity(hologram); + currentY += 0.23; + holograms.add(hologram); + } + } + + @Override + public void E_() { + if(removed || ++currentTick <= 20) + return; + + currentTick = 0; + + if(((WChunkLoader) chunkLoader).isNotActive()){ + chunkLoader.remove(); + return; + } + + int hologramsAmount = holograms.size(); + for (int i = hologramsAmount; i > 0; i--) { + EntityHologram hologram = holograms.get(hologramsAmount - i); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + } + + ((WChunkLoader) chunkLoader).tick(); + + if(!removed) { + secondsAmount--; + if (secondsAmount < 0) { + secondsAmount = 59; + minutesAmount--; + if (minutesAmount < 0) { + minutesAmount = 59; + hoursAmount--; + if (hoursAmount < 0) { + hoursAmount = 23; + daysAmount--; + } + } + } + } + } + + private void updateName(EntityHologram hologram, String line){ + hologram.setCustomName(line + .replace("{0}", chunkLoader.getWhoPlaced().getName()) + .replace("{1}", daysAmount + "") + .replace("{2}", hoursAmount + "") + .replace("{3}", minutesAmount + "") + .replace("{4}", secondsAmount + "") + ); + } + + } + + private static class EntityHologram extends EntityArmorStand { + + EntityHologram(World world, double x, double y, double z){ + super(world); + setPosition(x, y, z); + setInvisible(true); + setSmall(true); + setArms(false); + setNoGravity(true); + setBasePlate(true); + setMarker(true); + setCustomNameVisible(true); + a(new AxisAlignedBB(0D, 0D, 0D, 0D, 0D, 0D)); + } + + } + +} diff --git a/v1_11_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_11_R1.java b/v1_11_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_11_R1.java new file mode 100644 index 0000000..f67118a --- /dev/null +++ b/v1_11_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_11_R1.java @@ -0,0 +1,145 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.npc.DummyChannel; +import com.mojang.authlib.GameProfile; +import net.minecraft.server.v1_11_R1.DamageSource; +import net.minecraft.server.v1_11_R1.EntityPlayer; +import net.minecraft.server.v1_11_R1.EnumGamemode; +import net.minecraft.server.v1_11_R1.EnumProtocolDirection; +import net.minecraft.server.v1_11_R1.MinecraftServer; +import net.minecraft.server.v1_11_R1.NetworkManager; +import net.minecraft.server.v1_11_R1.Packet; +import net.minecraft.server.v1_11_R1.PacketPlayInBlockDig; +import net.minecraft.server.v1_11_R1.PacketPlayInBlockPlace; +import net.minecraft.server.v1_11_R1.PacketPlayInChat; +import net.minecraft.server.v1_11_R1.PacketPlayInFlying; +import net.minecraft.server.v1_11_R1.PacketPlayInHeldItemSlot; +import net.minecraft.server.v1_11_R1.PacketPlayInTransaction; +import net.minecraft.server.v1_11_R1.PacketPlayInUpdateSign; +import net.minecraft.server.v1_11_R1.PacketPlayInWindowClick; +import net.minecraft.server.v1_11_R1.PlayerConnection; +import net.minecraft.server.v1_11_R1.PlayerInteractManager; +import net.minecraft.server.v1_11_R1.WorldServer; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_11_R1.CraftServer; +import org.bukkit.craftbukkit.v1_11_R1.CraftWorld; + +import java.lang.reflect.Field; +import java.util.UUID; + +public final class ChunkLoaderNPC_v1_11_R1 extends EntityPlayer implements ChunkLoaderNPC { + + public ChunkLoaderNPC_v1_11_R1(Location location, UUID uuid){ + super(((CraftServer) Bukkit.getServer()).getServer(), ((CraftWorld) location.getWorld()).getHandle(), + new GameProfile(uuid, "Loader-" + location.getWorld().getName()), new PlayerInteractManager(((CraftWorld) location.getWorld()).getHandle())); + + playerConnection = new DummyPlayerConnection(server, this); + + playerInteractManager.setGameMode(EnumGamemode.CREATIVE); + fallDistance = 0.0F; + + fauxSleeping = true; + + spawnIn(world); + setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + world.players.add(this); + ((WorldServer) world).getPlayerChunkMap().addPlayer(this); + } + + @Override + public UUID getUniqueId() { + return super.getUniqueID(); + } + + @Override + public void die() { + super.die(); + } + + @Override + public Location getLocation() { + return getBukkitEntity().getLocation(); + } + + @Override + protected boolean damageEntity0(DamageSource damagesource, float f) { + return false; + } + + private static class DummyNetworkManager extends NetworkManager{ + + DummyNetworkManager(){ + super(EnumProtocolDirection.SERVERBOUND); + updateFields(); + } + + private void updateFields() { + try { + Field channelField = NetworkManager.class.getDeclaredField("channel"); + channelField.setAccessible(true); + channelField.set(this, new DummyChannel()); + channelField.setAccessible(false); + + Field socketAddressField = NetworkManager.class.getDeclaredField("l"); + socketAddressField.setAccessible(true); + socketAddressField.set(this, null); + } + catch (NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException e) { + e.printStackTrace(); + } + } + + } + + private static class DummyPlayerConnection extends PlayerConnection { + + DummyPlayerConnection(MinecraftServer minecraftServer, EntityPlayer entityPlayer) { + super(minecraftServer, new DummyNetworkManager(), entityPlayer); + } + + public void a(PacketPlayInWindowClick packetPlayInWindowClick) { + + } + + public void a(PacketPlayInTransaction packetPlayInTransaction) { + + } + + public void a(PacketPlayInFlying packetPlayInFlying) { + + } + + public void a(PacketPlayInUpdateSign packetPlayInUpdateSign) { + + } + + public void a(PacketPlayInBlockDig packetPlayInBlockDig) { + + } + + public void a(PacketPlayInBlockPlace packetPlayInBlockPlace) { + + } + + public void disconnect(String s) { + + } + + public void a(PacketPlayInHeldItemSlot packetPlayInHeldItemSlot) { + + } + + public void a(PacketPlayInChat packetPlayInChat) { + + } + + public void sendPacket(Packet packet) { + + } + + } + +} diff --git a/v1_11_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_11_R1.java b/v1_11_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_11_R1.java new file mode 100644 index 0000000..4c449f5 --- /dev/null +++ b/v1_11_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_11_R1.java @@ -0,0 +1,282 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.loaders.WChunkLoader; +import net.minecraft.server.v1_11_R1.AxisAlignedBB; +import net.minecraft.server.v1_11_R1.Block; +import net.minecraft.server.v1_11_R1.BlockPosition; +import net.minecraft.server.v1_11_R1.Chunk; +import net.minecraft.server.v1_11_R1.Entity; +import net.minecraft.server.v1_11_R1.EntityArmorStand; +import net.minecraft.server.v1_11_R1.ITickable; +import net.minecraft.server.v1_11_R1.ItemStack; +import net.minecraft.server.v1_11_R1.NBTTagCompound; +import net.minecraft.server.v1_11_R1.NBTTagList; +import net.minecraft.server.v1_11_R1.NBTTagLong; +import net.minecraft.server.v1_11_R1.NBTTagString; +import net.minecraft.server.v1_11_R1.TileEntity; +import net.minecraft.server.v1_11_R1.TileEntityMobSpawner; +import net.minecraft.server.v1_11_R1.World; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_11_R1.CraftChunk; +import org.bukkit.craftbukkit.v1_11_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_11_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_11_R1.util.LongHash; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@SuppressWarnings({"ConstantConditions", "unused"}) +public final class NMSAdapter_v1_11_R1 implements NMSAdapter { + + private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + + @Override + public String getTag(org.bukkit.inventory.ItemStack itemStack, String key, String def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + if(!tagCompound.hasKeyOfType(key, 8)) + return def; + + return tagCompound.getString(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, String value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + tagCompound.set(key, new NBTTagString(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public long getTag(org.bukkit.inventory.ItemStack itemStack, String key, long def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + if(!tagCompound.hasKeyOfType(key, 4)) + return def; + + return tagCompound.getLong(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, long value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + tagCompound.set(key, new NBTTagLong(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack itemStack, String texture) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + + NBTTagCompound nbtTagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + NBTTagCompound skullOwner = nbtTagCompound.hasKey("SkullOwner") ? nbtTagCompound.getCompound("SkullOwner") : new NBTTagCompound(); + + NBTTagCompound properties = new NBTTagCompound(); + + NBTTagList textures = new NBTTagList(); + NBTTagCompound signature = new NBTTagCompound(); + signature.setString("Value", texture); + textures.add(signature); + + properties.set("textures", textures); + + skullOwner.set("Properties", properties); + skullOwner.setString("Id", UUID.randomUUID().toString()); + + nbtTagCompound.set("SkullOwner", skullOwner); + + nmsItem.setTag(nbtTagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public ChunkLoaderNPC createNPC(Location location, UUID uuid) { + return new ChunkLoaderNPC_v1_11_R1(location, uuid); + } + + @Override + public void createLoader(ChunkLoader chunkLoader) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + TileEntityChunkLoader tileEntityChunkLoader = new TileEntityChunkLoader(chunkLoader, world, blockPosition); + world.tileEntityListTick.add(tileEntityChunkLoader); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner).forEach(tileEntity -> { + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + tileEntity.save(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) -1); + tileEntity.a(nbtTagCompound); + }); + } + + @Override + public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + long tileEntityLong = LongHash.toLong(blockPosition.getX() >> 4, blockPosition.getZ() >> 4); + TileEntityChunkLoader tileEntityChunkLoader = TileEntityChunkLoader.tileEntityChunkLoaderMap.remove(tileEntityLong); + if(tileEntityChunkLoader != null) { + tileEntityChunkLoader.holograms.forEach(Entity::die); + tileEntityChunkLoader.removed = true; + world.tileEntityListTick.remove(tileEntityChunkLoader); + } + + if(spawnParticle) + world.a(null, 2001, blockPosition, Block.getCombinedId(world.getType(blockPosition))); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner).forEach(tileEntity -> { + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + tileEntity.save(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) 16); + tileEntity.a(nbtTagCompound); + }); + } + + @Override + public void updateSpawner(Location location, boolean reset) { + World world = ((CraftWorld) location.getWorld()).getHandle(); + + BlockPosition blockPosition = new BlockPosition(location.getX(), location.getY(), location.getZ()); + TileEntityMobSpawner mobSpawner = (TileEntityMobSpawner) world.getTileEntity(blockPosition); + + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + mobSpawner.save(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) (reset ? 16 : -1)); + mobSpawner.a(nbtTagCompound); + } + + private static final class TileEntityChunkLoader extends TileEntity implements ITickable { + + private static final Map tileEntityChunkLoaderMap = new HashMap<>(); + + private final List holograms = new ArrayList<>(); + private final ChunkLoader chunkLoader; + + private short currentTick = 20; + private short daysAmount, hoursAmount, minutesAmount, secondsAmount; + private boolean removed = false; + + TileEntityChunkLoader(ChunkLoader chunkLoader, World world, BlockPosition blockPosition){ + this.chunkLoader = chunkLoader; + + setPosition(blockPosition); + a(world); + + long timeLeft = chunkLoader.getTimeLeft(); + + daysAmount = (short) (timeLeft / 86400); + timeLeft = timeLeft % 86400; + + hoursAmount = (short) (timeLeft / 3600); + timeLeft = timeLeft % 3600; + + minutesAmount = (short) (timeLeft / 60); + timeLeft = timeLeft % 60; + + secondsAmount = (short) timeLeft; + + tileEntityChunkLoaderMap.put(LongHash.toLong(blockPosition.getX() >> 4, blockPosition.getZ() >> 4), this); + + double currentY = position.getY() + 1; + for(int i = plugin.getSettings().hologramLines.size(); i > 0; i--){ + EntityHologram hologram = new EntityHologram(world, position.getX() + 0.5, currentY, position.getZ() + 0.5); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + world.addEntity(hologram); + currentY += 0.23; + holograms.add(hologram); + } + } + + @Override + public void F_() { + if(removed || ++currentTick <= 20) + return; + + currentTick = 0; + + if(((WChunkLoader) chunkLoader).isNotActive()){ + chunkLoader.remove(); + return; + } + + int hologramsAmount = holograms.size(); + for (int i = hologramsAmount; i > 0; i--) { + EntityHologram hologram = holograms.get(hologramsAmount - i); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + } + + ((WChunkLoader) chunkLoader).tick(); + + if(!removed) { + secondsAmount--; + if (secondsAmount < 0) { + secondsAmount = 59; + minutesAmount--; + if (minutesAmount < 0) { + minutesAmount = 59; + hoursAmount--; + if (hoursAmount < 0) { + hoursAmount = 23; + daysAmount--; + } + } + } + } + } + + private void updateName(EntityHologram hologram, String line){ + hologram.setCustomName(line + .replace("{0}", chunkLoader.getWhoPlaced().getName()) + .replace("{1}", daysAmount + "") + .replace("{2}", hoursAmount + "") + .replace("{3}", minutesAmount + "") + .replace("{4}", secondsAmount + "") + ); + } + + } + + private static class EntityHologram extends EntityArmorStand { + + EntityHologram(World world, double x, double y, double z){ + super(world); + setPosition(x, y, z); + setInvisible(true); + setSmall(true); + setArms(false); + setNoGravity(true); + setBasePlate(true); + setMarker(true); + setCustomNameVisible(true); + a(new AxisAlignedBB(0D, 0D, 0D, 0D, 0D, 0D)); + } + + } + +} diff --git a/v1_12_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_12_R1.java b/v1_12_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_12_R1.java new file mode 100644 index 0000000..eee75cf --- /dev/null +++ b/v1_12_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_12_R1.java @@ -0,0 +1,143 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.npc.DummyChannel; +import com.mojang.authlib.GameProfile; +import net.minecraft.server.v1_12_R1.DamageSource; +import net.minecraft.server.v1_12_R1.EntityPlayer; +import net.minecraft.server.v1_12_R1.EnumGamemode; +import net.minecraft.server.v1_12_R1.EnumProtocolDirection; +import net.minecraft.server.v1_12_R1.MinecraftServer; +import net.minecraft.server.v1_12_R1.NetworkManager; +import net.minecraft.server.v1_12_R1.Packet; +import net.minecraft.server.v1_12_R1.PacketPlayInBlockDig; +import net.minecraft.server.v1_12_R1.PacketPlayInBlockPlace; +import net.minecraft.server.v1_12_R1.PacketPlayInChat; +import net.minecraft.server.v1_12_R1.PacketPlayInFlying; +import net.minecraft.server.v1_12_R1.PacketPlayInHeldItemSlot; +import net.minecraft.server.v1_12_R1.PacketPlayInTransaction; +import net.minecraft.server.v1_12_R1.PacketPlayInUpdateSign; +import net.minecraft.server.v1_12_R1.PacketPlayInWindowClick; +import net.minecraft.server.v1_12_R1.PlayerConnection; +import net.minecraft.server.v1_12_R1.PlayerInteractManager; +import net.minecraft.server.v1_12_R1.WorldServer; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_12_R1.CraftServer; +import org.bukkit.craftbukkit.v1_12_R1.CraftWorld; + +import java.lang.reflect.Field; +import java.util.UUID; + +public final class ChunkLoaderNPC_v1_12_R1 extends EntityPlayer implements ChunkLoaderNPC { + + public ChunkLoaderNPC_v1_12_R1(Location location, UUID uuid){ + super(((CraftServer) Bukkit.getServer()).getServer(), ((CraftWorld) location.getWorld()).getHandle(), + new GameProfile(uuid, "Loader-" + location.getWorld().getName()), new PlayerInteractManager(((CraftWorld) location.getWorld()).getHandle())); + + playerConnection = new DummyPlayerConnection(server, this); + + a(EnumGamemode.CREATIVE); + fauxSleeping = true; + + spawnIn(world); + setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + world.players.add(this); + ((WorldServer) world).getPlayerChunkMap().addPlayer(this); + } + + @Override + public UUID getUniqueId() { + return super.getUniqueID(); + } + + @Override + public void die() { + super.die(); + } + + @Override + public Location getLocation() { + return getBukkitEntity().getLocation(); + } + + @Override + protected boolean damageEntity0(DamageSource damagesource, float f) { + return false; + } + + private static class DummyNetworkManager extends NetworkManager{ + + DummyNetworkManager(){ + super(EnumProtocolDirection.SERVERBOUND); + updateFields(); + } + + private void updateFields() { + try { + Field channelField = NetworkManager.class.getDeclaredField("channel"); + channelField.setAccessible(true); + channelField.set(this, new DummyChannel()); + channelField.setAccessible(false); + + Field socketAddressField = NetworkManager.class.getDeclaredField("l"); + socketAddressField.setAccessible(true); + socketAddressField.set(this, null); + } + catch (NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException e) { + e.printStackTrace(); + } + } + + } + + private static class DummyPlayerConnection extends PlayerConnection { + + DummyPlayerConnection(MinecraftServer minecraftServer, EntityPlayer entityPlayer) { + super(minecraftServer, new DummyNetworkManager(), entityPlayer); + } + + public void a(PacketPlayInWindowClick packetPlayInWindowClick) { + + } + + public void a(PacketPlayInTransaction packetPlayInTransaction) { + + } + + public void a(PacketPlayInFlying packetPlayInFlying) { + + } + + public void a(PacketPlayInUpdateSign packetPlayInUpdateSign) { + + } + + public void a(PacketPlayInBlockDig packetPlayInBlockDig) { + + } + + public void a(PacketPlayInBlockPlace packetPlayInBlockPlace) { + + } + + public void disconnect(String s) { + + } + + public void a(PacketPlayInHeldItemSlot packetPlayInHeldItemSlot) { + + } + + public void a(PacketPlayInChat packetPlayInChat) { + + } + + public void sendPacket(Packet packet) { + + } + + } + +} diff --git a/v1_12_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_12_R1.java b/v1_12_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_12_R1.java new file mode 100644 index 0000000..8e1d692 --- /dev/null +++ b/v1_12_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_12_R1.java @@ -0,0 +1,305 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.loaders.WChunkLoader; +import net.minecraft.server.v1_12_R1.AxisAlignedBB; +import net.minecraft.server.v1_12_R1.Block; +import net.minecraft.server.v1_12_R1.BlockPosition; +import net.minecraft.server.v1_12_R1.Chunk; +import net.minecraft.server.v1_12_R1.Entity; +import net.minecraft.server.v1_12_R1.EntityArmorStand; +import net.minecraft.server.v1_12_R1.ITickable; +import net.minecraft.server.v1_12_R1.ItemStack; +import net.minecraft.server.v1_12_R1.NBTTagCompound; +import net.minecraft.server.v1_12_R1.NBTTagList; +import net.minecraft.server.v1_12_R1.NBTTagLong; +import net.minecraft.server.v1_12_R1.NBTTagString; +import net.minecraft.server.v1_12_R1.TileEntity; +import net.minecraft.server.v1_12_R1.TileEntityMobSpawner; +import net.minecraft.server.v1_12_R1.World; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_12_R1.CraftChunk; +import org.bukkit.craftbukkit.v1_12_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_12_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_12_R1.util.LongHash; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@SuppressWarnings({"ConstantConditions", "unused"}) +public final class NMSAdapter_v1_12_R1 implements NMSAdapter { + + private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + private static Method compoundLoadMethod = null; + + static { + try{ + //noinspection JavaReflectionMemberAccess + compoundLoadMethod = TileEntity.class.getMethod("load", NBTTagCompound.class); + }catch (Exception ignored){} + } + + @Override + public String getTag(org.bukkit.inventory.ItemStack itemStack, String key, String def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + if(!tagCompound.hasKeyOfType(key, 8)) + return def; + + return tagCompound.getString(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, String value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + tagCompound.set(key, new NBTTagString(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public long getTag(org.bukkit.inventory.ItemStack itemStack, String key, long def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + if(!tagCompound.hasKeyOfType(key, 4)) + return def; + + return tagCompound.getLong(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, long value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + tagCompound.set(key, new NBTTagLong(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack itemStack, String texture) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + + NBTTagCompound nbtTagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + NBTTagCompound skullOwner = nbtTagCompound.hasKey("SkullOwner") ? nbtTagCompound.getCompound("SkullOwner") : new NBTTagCompound(); + + NBTTagCompound properties = new NBTTagCompound(); + + NBTTagList textures = new NBTTagList(); + NBTTagCompound signature = new NBTTagCompound(); + signature.setString("Value", texture); + textures.add(signature); + + properties.set("textures", textures); + + skullOwner.set("Properties", properties); + skullOwner.setString("Id", UUID.randomUUID().toString()); + + nbtTagCompound.set("SkullOwner", skullOwner); + + nmsItem.setTag(nbtTagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public ChunkLoaderNPC createNPC(Location location, UUID uuid) { + return new ChunkLoaderNPC_v1_12_R1(location, uuid); + } + + @Override + public void createLoader(ChunkLoader chunkLoader) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + TileEntityChunkLoader tileEntityChunkLoader = new TileEntityChunkLoader(chunkLoader, world, blockPosition); + world.tileEntityListTick.add(tileEntityChunkLoader); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner).forEach(tileEntity -> { + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + tileEntity.save(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) -1); + + try { + compoundLoadMethod.invoke(tileEntity, nbtTagCompound); + }catch (Throwable ex){ + tileEntity.a(nbtTagCompound); + } + }); + } + + @Override + public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + long tileEntityLong = LongHash.toLong(blockPosition.getX() >> 4, blockPosition.getZ() >> 4); + TileEntityChunkLoader tileEntityChunkLoader = TileEntityChunkLoader.tileEntityChunkLoaderMap.remove(tileEntityLong); + if(tileEntityChunkLoader != null) { + tileEntityChunkLoader.holograms.forEach(Entity::die); + tileEntityChunkLoader.removed = true; + world.tileEntityListTick.remove(tileEntityChunkLoader); + } + + if(spawnParticle) + world.a(null, 2001, blockPosition, Block.getCombinedId(world.getType(blockPosition))); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner).forEach(tileEntity -> { + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + tileEntity.save(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) 16); + + try { + compoundLoadMethod.invoke(tileEntity, nbtTagCompound); + }catch (Throwable ex){ + tileEntity.a(nbtTagCompound); + } + }); + } + + @Override + public void updateSpawner(Location location, boolean reset) { + World world = ((CraftWorld) location.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(location.getX(), location.getY(), location.getZ()); + TileEntityMobSpawner mobSpawner = (TileEntityMobSpawner) world.getTileEntity(blockPosition); + + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + mobSpawner.save(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) (reset ? 16 : -1)); + + try { + compoundLoadMethod.invoke(mobSpawner, nbtTagCompound); + }catch (Throwable ex){ + mobSpawner.a(nbtTagCompound); + } + } + + private static final class TileEntityChunkLoader extends TileEntity implements ITickable { + + private static final Map tileEntityChunkLoaderMap = new HashMap<>(); + + private final List holograms = new ArrayList<>(); + private final ChunkLoader chunkLoader; + + private short currentTick = 20; + private short daysAmount, hoursAmount, minutesAmount, secondsAmount; + private boolean removed = false; + + TileEntityChunkLoader(ChunkLoader chunkLoader, World world, BlockPosition blockPosition){ + this.chunkLoader = chunkLoader; + + setPosition(blockPosition); + a(world); + + long timeLeft = chunkLoader.getTimeLeft(); + + daysAmount = (short) (timeLeft / 86400); + timeLeft = timeLeft % 86400; + + hoursAmount = (short) (timeLeft / 3600); + timeLeft = timeLeft % 3600; + + minutesAmount = (short) (timeLeft / 60); + timeLeft = timeLeft % 60; + + secondsAmount = (short) timeLeft; + + tileEntityChunkLoaderMap.put(LongHash.toLong(blockPosition.getX() >> 4, blockPosition.getZ() >> 4), this); + + double currentY = position.getY() + 1; + for(int i = plugin.getSettings().hologramLines.size(); i > 0; i--){ + EntityHologram hologram = new EntityHologram(world, position.getX() + 0.5, currentY, position.getZ() + 0.5); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + world.addEntity(hologram); + currentY += 0.23; + holograms.add(hologram); + } + } + + @Override + public void e() { + if(removed || ++currentTick <= 20) + return; + + currentTick = 0; + + if(((WChunkLoader) chunkLoader).isNotActive()){ + chunkLoader.remove(); + return; + } + + int hologramsAmount = holograms.size(); + for (int i = hologramsAmount; i > 0; i--) { + EntityHologram hologram = holograms.get(hologramsAmount - i); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + } + + ((WChunkLoader) chunkLoader).tick(); + + if(!removed) { + secondsAmount--; + if (secondsAmount < 0) { + secondsAmount = 59; + minutesAmount--; + if (minutesAmount < 0) { + minutesAmount = 59; + hoursAmount--; + if (hoursAmount < 0) { + hoursAmount = 23; + daysAmount--; + } + } + } + } + } + + private void updateName(EntityHologram hologram, String line){ + hologram.setCustomName(line + .replace("{0}", chunkLoader.getWhoPlaced().getName()) + .replace("{1}", daysAmount + "") + .replace("{2}", hoursAmount + "") + .replace("{3}", minutesAmount + "") + .replace("{4}", secondsAmount + "") + ); + } + + } + + private static class EntityHologram extends EntityArmorStand{ + + EntityHologram(World world, double x, double y, double z){ + super(world); + setPosition(x, y, z); + setInvisible(true); + setSmall(true); + setArms(false); + setNoGravity(true); + setBasePlate(true); + setMarker(true); + setCustomNameVisible(true); + a(new AxisAlignedBB(0D, 0D, 0D, 0D, 0D, 0D)); + } + + } + +} diff --git a/v1_13_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_13_R1.java b/v1_13_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_13_R1.java new file mode 100644 index 0000000..79896bc --- /dev/null +++ b/v1_13_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_13_R1.java @@ -0,0 +1,146 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.npc.DummyChannel; +import com.mojang.authlib.GameProfile; +import net.minecraft.server.v1_13_R1.DamageSource; +import net.minecraft.server.v1_13_R1.EntityPlayer; +import net.minecraft.server.v1_13_R1.EnumGamemode; +import net.minecraft.server.v1_13_R1.EnumProtocolDirection; +import net.minecraft.server.v1_13_R1.MinecraftServer; +import net.minecraft.server.v1_13_R1.NetworkManager; +import net.minecraft.server.v1_13_R1.Packet; +import net.minecraft.server.v1_13_R1.PacketPlayInBlockDig; +import net.minecraft.server.v1_13_R1.PacketPlayInBlockPlace; +import net.minecraft.server.v1_13_R1.PacketPlayInChat; +import net.minecraft.server.v1_13_R1.PacketPlayInFlying; +import net.minecraft.server.v1_13_R1.PacketPlayInHeldItemSlot; +import net.minecraft.server.v1_13_R1.PacketPlayInTransaction; +import net.minecraft.server.v1_13_R1.PacketPlayInUpdateSign; +import net.minecraft.server.v1_13_R1.PacketPlayInWindowClick; +import net.minecraft.server.v1_13_R1.PlayerConnection; +import net.minecraft.server.v1_13_R1.PlayerInteractManager; +import net.minecraft.server.v1_13_R1.WorldServer; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_13_R1.CraftServer; +import org.bukkit.craftbukkit.v1_13_R1.CraftWorld; + +import java.lang.reflect.Field; +import java.util.UUID; + +public final class ChunkLoaderNPC_v1_13_R1 extends EntityPlayer implements ChunkLoaderNPC { + + public ChunkLoaderNPC_v1_13_R1(Location location, UUID uuid){ + super(((CraftServer) Bukkit.getServer()).getServer(), ((CraftWorld) location.getWorld()).getHandle(), + new GameProfile(uuid, "Loader-" + location.getWorld().getName()), new PlayerInteractManager(((CraftWorld) location.getWorld()).getHandle())); + MinecraftServer minecraftServer = ((CraftServer) Bukkit.getServer()).getServer(); + WorldServer worldServer = ((CraftWorld) location.getWorld()).getHandle(); + + playerConnection = new DummyPlayerConnection(minecraftServer, this); + + a(EnumGamemode.CREATIVE); + + fauxSleeping = true; + + spawnIn(worldServer); + setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + worldServer.players.add(this); + worldServer.getPlayerChunkMap().addPlayer(this); + } + + @Override + public UUID getUniqueId() { + return super.getUniqueID(); + } + + @Override + public void die() { + super.die(); + } + + @Override + public Location getLocation() { + return getBukkitEntity().getLocation(); + } + + @Override + protected boolean damageEntity0(DamageSource damagesource, float f) { + return false; + } + + private static class DummyNetworkManager extends NetworkManager{ + + DummyNetworkManager(){ + super(EnumProtocolDirection.SERVERBOUND); + updateFields(); + } + + private void updateFields() { + try { + Field channelField = NetworkManager.class.getDeclaredField("channel"); + channelField.setAccessible(true); + channelField.set(this, new DummyChannel()); + channelField.setAccessible(false); + + Field socketAddressField = NetworkManager.class.getDeclaredField("l"); + socketAddressField.setAccessible(true); + socketAddressField.set(this, null); + } + catch (NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException e) { + e.printStackTrace(); + } + } + + } + + private static class DummyPlayerConnection extends PlayerConnection { + + DummyPlayerConnection(MinecraftServer minecraftServer, EntityPlayer entityPlayer) { + super(minecraftServer, new DummyNetworkManager(), entityPlayer); + } + + public void a(PacketPlayInWindowClick packetPlayInWindowClick) { + + } + + public void a(PacketPlayInTransaction packetPlayInTransaction) { + + } + + public void a(PacketPlayInFlying packetPlayInFlying) { + + } + + public void a(PacketPlayInUpdateSign packetPlayInUpdateSign) { + + } + + public void a(PacketPlayInBlockDig packetPlayInBlockDig) { + + } + + public void a(PacketPlayInBlockPlace packetPlayInBlockPlace) { + + } + + public void disconnect(String s) { + + } + + public void a(PacketPlayInHeldItemSlot packetPlayInHeldItemSlot) { + + } + + public void a(PacketPlayInChat packetPlayInChat) { + + } + + public void sendPacket(Packet packet) { + + } + + } + +} diff --git a/v1_13_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_13_R1.java b/v1_13_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_13_R1.java new file mode 100644 index 0000000..ff899c7 --- /dev/null +++ b/v1_13_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_13_R1.java @@ -0,0 +1,278 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.loaders.WChunkLoader; +import net.minecraft.server.v1_13_R1.AxisAlignedBB; +import net.minecraft.server.v1_13_R1.Block; +import net.minecraft.server.v1_13_R1.BlockPosition; +import net.minecraft.server.v1_13_R1.Chunk; +import net.minecraft.server.v1_13_R1.Entity; +import net.minecraft.server.v1_13_R1.EntityArmorStand; +import net.minecraft.server.v1_13_R1.ITickable; +import net.minecraft.server.v1_13_R1.ItemStack; +import net.minecraft.server.v1_13_R1.NBTTagCompound; +import net.minecraft.server.v1_13_R1.NBTTagList; +import net.minecraft.server.v1_13_R1.NBTTagLong; +import net.minecraft.server.v1_13_R1.NBTTagString; +import net.minecraft.server.v1_13_R1.TileEntity; +import net.minecraft.server.v1_13_R1.TileEntityMobSpawner; +import net.minecraft.server.v1_13_R1.TileEntityTypes; +import net.minecraft.server.v1_13_R1.World; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_13_R1.CraftChunk; +import org.bukkit.craftbukkit.v1_13_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_13_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_13_R1.util.CraftChatMessage; +import org.bukkit.craftbukkit.v1_13_R1.util.LongHash; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@SuppressWarnings("unused") +public final class NMSAdapter_v1_13_R1 implements NMSAdapter { + + private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + + @Override + public String getTag(org.bukkit.inventory.ItemStack itemStack, String key, String def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + if(!tagCompound.hasKeyOfType(key, 8)) + return def; + + return tagCompound.getString(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, String value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + tagCompound.set(key, new NBTTagString(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public long getTag(org.bukkit.inventory.ItemStack itemStack, String key, long def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + if(!tagCompound.hasKeyOfType(key, 4)) + return def; + + return tagCompound.getLong(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, long value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + tagCompound.set(key, new NBTTagLong(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack itemStack, String texture) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + + NBTTagCompound nbtTagCompound = nmsItem.getOrCreateTag(); + + NBTTagCompound skullOwner = nbtTagCompound.hasKey("SkullOwner") ? nbtTagCompound.getCompound("SkullOwner") : new NBTTagCompound(); + + NBTTagCompound properties = new NBTTagCompound(); + + NBTTagList textures = new NBTTagList(); + NBTTagCompound signature = new NBTTagCompound(); + signature.setString("Value", texture); + textures.add(signature); + + properties.set("textures", textures); + + skullOwner.set("Properties", properties); + skullOwner.setString("Id", UUID.randomUUID().toString()); + + nbtTagCompound.set("SkullOwner", skullOwner); + + nmsItem.setTag(nbtTagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public ChunkLoaderNPC createNPC(Location location, UUID uuid) { + return new ChunkLoaderNPC_v1_13_R1(location, uuid); + } + + @Override + public void createLoader(ChunkLoader chunkLoader) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + TileEntityChunkLoader tileEntityChunkLoader = new TileEntityChunkLoader(chunkLoader, world, blockPosition); + world.tileEntityListTick.add(tileEntityChunkLoader); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner) + .forEach(tileEntity -> ((TileEntityMobSpawner) tileEntity).getSpawner().requiredPlayerRange = -1); + } + + @Override + public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + long tileEntityLong = LongHash.toLong(blockPosition.getX() >> 4, blockPosition.getZ() >> 4); + TileEntityChunkLoader tileEntityChunkLoader = TileEntityChunkLoader.tileEntityChunkLoaderMap.remove(tileEntityLong); + if(tileEntityChunkLoader != null) { + tileEntityChunkLoader.holograms.forEach(Entity::die); + tileEntityChunkLoader.removed = true; + world.tileEntityListTick.remove(tileEntityChunkLoader); + } + + if(spawnParticle) + world.a(null, 2001, blockPosition, Block.getCombinedId(world.getType(blockPosition))); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner) + .forEach(tileEntity -> ((TileEntityMobSpawner) tileEntity).getSpawner().requiredPlayerRange = 16); + } + + @Override + public void updateSpawner(Location location, boolean reset) { + World world = ((CraftWorld) location.getWorld()).getHandle(); + + BlockPosition blockPosition = new BlockPosition(location.getX(), location.getY(), location.getZ()); + TileEntityMobSpawner mobSpawner = (TileEntityMobSpawner) world.getTileEntity(blockPosition); + + if(mobSpawner == null) + return; + + mobSpawner.getSpawner().requiredPlayerRange = reset ? 16 : -1; + } + + private static final class TileEntityChunkLoader extends TileEntity implements ITickable { + + private static final Map tileEntityChunkLoaderMap = new HashMap<>(); + + private final List holograms = new ArrayList<>(); + private final ChunkLoader chunkLoader; + + private short currentTick = 20; + private short daysAmount, hoursAmount, minutesAmount, secondsAmount; + private boolean removed = false; + + TileEntityChunkLoader(ChunkLoader chunkLoader, World world, BlockPosition blockPosition){ + super(TileEntityTypes.w); + + this.chunkLoader = chunkLoader; + + setPosition(blockPosition); + setWorld(world); + + long timeLeft = chunkLoader.getTimeLeft(); + + daysAmount = (short) (timeLeft / 86400); + timeLeft = timeLeft % 86400; + + hoursAmount = (short) (timeLeft / 3600); + timeLeft = timeLeft % 3600; + + minutesAmount = (short) (timeLeft / 60); + timeLeft = timeLeft % 60; + + secondsAmount = (short) timeLeft; + + tileEntityChunkLoaderMap.put(LongHash.toLong(blockPosition.getX() >> 4, blockPosition.getZ() >> 4), this); + + double currentY = position.getY() + 1; + for(int i = plugin.getSettings().hologramLines.size(); i > 0; i--){ + EntityHologram hologram = new EntityHologram(world, position.getX() + 0.5, currentY, position.getZ() + 0.5); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + world.addEntity(hologram); + currentY += 0.23; + holograms.add(hologram); + } + } + + @Override + public void Y_() { + if(removed || ++currentTick <= 20) + return; + + currentTick = 0; + + if(((WChunkLoader) chunkLoader).isNotActive()){ + chunkLoader.remove(); + return; + } + + int hologramsAmount = holograms.size(); + for (int i = hologramsAmount; i > 0; i--) { + EntityHologram hologram = holograms.get(hologramsAmount - i); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + } + + ((WChunkLoader) chunkLoader).tick(); + + if(!removed) { + secondsAmount--; + if (secondsAmount < 0) { + secondsAmount = 59; + minutesAmount--; + if (minutesAmount < 0) { + minutesAmount = 59; + hoursAmount--; + if (hoursAmount < 0) { + hoursAmount = 23; + daysAmount--; + } + } + } + } + } + + private void updateName(EntityHologram hologram, String line){ + hologram.setCustomName(CraftChatMessage.fromStringOrNull(line + .replace("{0}", chunkLoader.getWhoPlaced().getName()) + .replace("{1}", daysAmount + "") + .replace("{2}", hoursAmount + "") + .replace("{3}", minutesAmount + "") + .replace("{4}", secondsAmount + "")) + ); + } + + } + + private static class EntityHologram extends EntityArmorStand { + + EntityHologram(World world, double x, double y, double z){ + super(world); + setPosition(x, y, z); + setInvisible(true); + setSmall(true); + setArms(false); + setNoGravity(true); + setBasePlate(true); + setMarker(true); + setCustomNameVisible(true); + a(new AxisAlignedBB(0D, 0D, 0D, 0D, 0D, 0D)); + } + + } + +} diff --git a/v1_13_R2/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_13_R2.java b/v1_13_R2/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_13_R2.java new file mode 100644 index 0000000..6e5b61e --- /dev/null +++ b/v1_13_R2/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_13_R2.java @@ -0,0 +1,148 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.npc.DummyChannel; +import com.mojang.authlib.GameProfile; +import net.minecraft.server.v1_13_R2.DamageSource; +import net.minecraft.server.v1_13_R2.EntityPlayer; +import net.minecraft.server.v1_13_R2.EnumGamemode; +import net.minecraft.server.v1_13_R2.EnumProtocolDirection; +import net.minecraft.server.v1_13_R2.MinecraftServer; +import net.minecraft.server.v1_13_R2.NetworkManager; +import net.minecraft.server.v1_13_R2.Packet; +import net.minecraft.server.v1_13_R2.PacketPlayInBlockDig; +import net.minecraft.server.v1_13_R2.PacketPlayInBlockPlace; +import net.minecraft.server.v1_13_R2.PacketPlayInChat; +import net.minecraft.server.v1_13_R2.PacketPlayInFlying; +import net.minecraft.server.v1_13_R2.PacketPlayInHeldItemSlot; +import net.minecraft.server.v1_13_R2.PacketPlayInTransaction; +import net.minecraft.server.v1_13_R2.PacketPlayInUpdateSign; +import net.minecraft.server.v1_13_R2.PacketPlayInWindowClick; +import net.minecraft.server.v1_13_R2.PlayerConnection; +import net.minecraft.server.v1_13_R2.PlayerInteractManager; +import net.minecraft.server.v1_13_R2.WorldServer; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_13_R2.CraftServer; +import org.bukkit.craftbukkit.v1_13_R2.CraftWorld; + +import java.lang.reflect.Field; +import java.util.UUID; + +public final class ChunkLoaderNPC_v1_13_R2 extends EntityPlayer implements ChunkLoaderNPC { + + public ChunkLoaderNPC_v1_13_R2(Location location, UUID uuid){ + super(((CraftServer) Bukkit.getServer()).getServer(), ((CraftWorld) location.getWorld()).getHandle(), + new GameProfile(uuid, "Loader-" + location.getWorld().getName()), new PlayerInteractManager(((CraftWorld) location.getWorld()).getHandle())); + + playerConnection = new DummyPlayerConnection(server, this); + + a(EnumGamemode.CREATIVE); + clientViewDistance = 1; + + fauxSleeping = true; + + spawnIn(world); + setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + world.players.add(this); + ((WorldServer) world).getPlayerChunkMap().addPlayer(this); + } + + @Override + public UUID getUniqueId() { + return super.getUniqueID(); + } + + @Override + public void die() { + WorldServer worldServer = getWorldServer(); + worldServer.players.remove(this); + worldServer.getPlayerChunkMap().removePlayer(this); + super.die(); + } + + @Override + public Location getLocation() { + return getBukkitEntity().getLocation(); + } + + @Override + protected boolean damageEntity0(DamageSource damagesource, float f) { + return false; + } + + private static class DummyNetworkManager extends NetworkManager{ + + DummyNetworkManager(){ + super(EnumProtocolDirection.SERVERBOUND); + updateFields(); + } + + private void updateFields() { + try { + Field channelField = NetworkManager.class.getDeclaredField("channel"); + channelField.setAccessible(true); + channelField.set(this, new DummyChannel()); + channelField.setAccessible(false); + + Field socketAddressField = NetworkManager.class.getDeclaredField("socketAddress"); + socketAddressField.setAccessible(true); + socketAddressField.set(this, null); + } + catch (NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException e) { + e.printStackTrace(); + } + } + + } + + private static class DummyPlayerConnection extends PlayerConnection { + + DummyPlayerConnection(MinecraftServer minecraftServer, EntityPlayer entityPlayer) { + super(minecraftServer, new DummyNetworkManager(), entityPlayer); + } + + public void a(PacketPlayInWindowClick packetPlayInWindowClick) { + + } + + public void a(PacketPlayInTransaction packetPlayInTransaction) { + + } + + public void a(PacketPlayInFlying packetPlayInFlying) { + + } + + public void a(PacketPlayInUpdateSign packetPlayInUpdateSign) { + + } + + public void a(PacketPlayInBlockDig packetPlayInBlockDig) { + + } + + public void a(PacketPlayInBlockPlace packetPlayInBlockPlace) { + + } + + public void disconnect(String s) { + + } + + public void a(PacketPlayInHeldItemSlot packetPlayInHeldItemSlot) { + + } + + public void a(PacketPlayInChat packetPlayInChat) { + + } + + public void sendPacket(Packet packet) { + + } + + } + +} diff --git a/v1_13_R2/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_13_R2.java b/v1_13_R2/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_13_R2.java new file mode 100644 index 0000000..9df1fab --- /dev/null +++ b/v1_13_R2/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_13_R2.java @@ -0,0 +1,278 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.loaders.WChunkLoader; +import net.minecraft.server.v1_13_R2.AxisAlignedBB; +import net.minecraft.server.v1_13_R2.Block; +import net.minecraft.server.v1_13_R2.BlockPosition; +import net.minecraft.server.v1_13_R2.Chunk; +import net.minecraft.server.v1_13_R2.Entity; +import net.minecraft.server.v1_13_R2.EntityArmorStand; +import net.minecraft.server.v1_13_R2.ITickable; +import net.minecraft.server.v1_13_R2.ItemStack; +import net.minecraft.server.v1_13_R2.NBTTagCompound; +import net.minecraft.server.v1_13_R2.NBTTagList; +import net.minecraft.server.v1_13_R2.NBTTagLong; +import net.minecraft.server.v1_13_R2.NBTTagString; +import net.minecraft.server.v1_13_R2.TileEntity; +import net.minecraft.server.v1_13_R2.TileEntityMobSpawner; +import net.minecraft.server.v1_13_R2.TileEntityTypes; +import net.minecraft.server.v1_13_R2.World; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_13_R2.CraftChunk; +import org.bukkit.craftbukkit.v1_13_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_13_R2.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_13_R2.util.CraftChatMessage; +import org.bukkit.craftbukkit.v1_13_R2.util.LongHash; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@SuppressWarnings("unused") +public final class NMSAdapter_v1_13_R2 implements NMSAdapter { + + private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + + @Override + public String getTag(org.bukkit.inventory.ItemStack itemStack, String key, String def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + if(!tagCompound.hasKeyOfType(key, 8)) + return def; + + return tagCompound.getString(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, String value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + tagCompound.set(key, new NBTTagString(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public long getTag(org.bukkit.inventory.ItemStack itemStack, String key, long def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + if(!tagCompound.hasKeyOfType(key, 4)) + return def; + + return tagCompound.getLong(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, long value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + tagCompound.set(key, new NBTTagLong(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack itemStack, String texture) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + + NBTTagCompound nbtTagCompound = nmsItem.getOrCreateTag(); + + NBTTagCompound skullOwner = nbtTagCompound.hasKey("SkullOwner") ? nbtTagCompound.getCompound("SkullOwner") : new NBTTagCompound(); + + NBTTagCompound properties = new NBTTagCompound(); + + NBTTagList textures = new NBTTagList(); + NBTTagCompound signature = new NBTTagCompound(); + signature.setString("Value", texture); + textures.add(signature); + + properties.set("textures", textures); + + skullOwner.set("Properties", properties); + skullOwner.setString("Id", UUID.randomUUID().toString()); + + nbtTagCompound.set("SkullOwner", skullOwner); + + nmsItem.setTag(nbtTagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public ChunkLoaderNPC createNPC(Location location, UUID uuid) { + return new ChunkLoaderNPC_v1_13_R2(location, uuid); + } + + @Override + public void createLoader(ChunkLoader chunkLoader) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + TileEntityChunkLoader tileEntityChunkLoader = new TileEntityChunkLoader(chunkLoader, world, blockPosition); + world.tileEntityListTick.add(tileEntityChunkLoader); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner) + .forEach(tileEntity -> ((TileEntityMobSpawner) tileEntity).getSpawner().requiredPlayerRange = -1); + } + + @Override + public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + long tileEntityLong = LongHash.toLong(blockPosition.getX() >> 4, blockPosition.getZ() >> 4); + TileEntityChunkLoader tileEntityChunkLoader = TileEntityChunkLoader.tileEntityChunkLoaderMap.remove(tileEntityLong); + if(tileEntityChunkLoader != null) { + tileEntityChunkLoader.holograms.forEach(Entity::die); + tileEntityChunkLoader.removed = true; + world.tileEntityListTick.remove(tileEntityChunkLoader); + } + + if(spawnParticle) + world.a(null, 2001, blockPosition, Block.getCombinedId(world.getType(blockPosition))); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner) + .forEach(tileEntity -> ((TileEntityMobSpawner) tileEntity).getSpawner().requiredPlayerRange = 16); + } + + @Override + public void updateSpawner(Location location, boolean reset) { + World world = ((CraftWorld) location.getWorld()).getHandle(); + + BlockPosition blockPosition = new BlockPosition(location.getX(), location.getY(), location.getZ()); + TileEntityMobSpawner mobSpawner = (TileEntityMobSpawner) world.getTileEntity(blockPosition); + + if(mobSpawner == null) + return; + + mobSpawner.getSpawner().requiredPlayerRange = reset ? 16 : -1; + } + + private static final class TileEntityChunkLoader extends TileEntity implements ITickable { + + private static final Map tileEntityChunkLoaderMap = new HashMap<>(); + + private final List holograms = new ArrayList<>(); + private final ChunkLoader chunkLoader; + + private short currentTick = 20; + private short daysAmount, hoursAmount, minutesAmount, secondsAmount; + private boolean removed = false; + + TileEntityChunkLoader(ChunkLoader chunkLoader, World world, BlockPosition blockPosition){ + super(TileEntityTypes.COMMAND_BLOCK); + + this.chunkLoader = chunkLoader; + + setPosition(blockPosition); + setWorld(world); + + long timeLeft = chunkLoader.getTimeLeft(); + + daysAmount = (short) (timeLeft / 86400); + timeLeft = timeLeft % 86400; + + hoursAmount = (short) (timeLeft / 3600); + timeLeft = timeLeft % 3600; + + minutesAmount = (short) (timeLeft / 60); + timeLeft = timeLeft % 60; + + secondsAmount = (short) timeLeft; + + tileEntityChunkLoaderMap.put(LongHash.toLong(blockPosition.getX() >> 4, blockPosition.getZ() >> 4), this); + + double currentY = position.getY() + 1; + for(int i = plugin.getSettings().hologramLines.size(); i > 0; i--){ + EntityHologram hologram = new EntityHologram(world, position.getX() + 0.5, currentY, position.getZ() + 0.5); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + world.addEntity(hologram); + currentY += 0.23; + holograms.add(hologram); + } + } + + @Override + public void tick() { + if(removed || ++currentTick <= 20) + return; + + currentTick = 0; + + if(((WChunkLoader) chunkLoader).isNotActive()){ + chunkLoader.remove(); + return; + } + + int hologramsAmount = holograms.size(); + for (int i = hologramsAmount; i > 0; i--) { + EntityHologram hologram = holograms.get(hologramsAmount - i); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + } + + ((WChunkLoader) chunkLoader).tick(); + + if(!removed) { + secondsAmount--; + if (secondsAmount < 0) { + secondsAmount = 59; + minutesAmount--; + if (minutesAmount < 0) { + minutesAmount = 59; + hoursAmount--; + if (hoursAmount < 0) { + hoursAmount = 23; + daysAmount--; + } + } + } + } + } + + private void updateName(EntityHologram hologram, String line){ + hologram.setCustomName(CraftChatMessage.fromStringOrNull(line + .replace("{0}", chunkLoader.getWhoPlaced().getName()) + .replace("{1}", daysAmount + "") + .replace("{2}", hoursAmount + "") + .replace("{3}", minutesAmount + "") + .replace("{4}", secondsAmount + "")) + ); + } + + } + + private static class EntityHologram extends EntityArmorStand { + + EntityHologram(World world, double x, double y, double z){ + super(world); + setPosition(x, y, z); + setInvisible(true); + setSmall(true); + setArms(false); + setNoGravity(true); + setBasePlate(true); + setMarker(true); + setCustomNameVisible(true); + a(new AxisAlignedBB(0D, 0D, 0D, 0D, 0D, 0D)); + } + + } + +} diff --git a/v1_14_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_14_R1.java b/v1_14_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_14_R1.java new file mode 100644 index 0000000..e190884 --- /dev/null +++ b/v1_14_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_14_R1.java @@ -0,0 +1,147 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.npc.DummyChannel; +import com.mojang.authlib.GameProfile; +import net.minecraft.server.v1_14_R1.EntityPlayer; +import net.minecraft.server.v1_14_R1.EnumGamemode; +import net.minecraft.server.v1_14_R1.EnumProtocolDirection; +import net.minecraft.server.v1_14_R1.MinecraftServer; +import net.minecraft.server.v1_14_R1.NetworkManager; +import net.minecraft.server.v1_14_R1.Packet; +import net.minecraft.server.v1_14_R1.PacketPlayInBlockDig; +import net.minecraft.server.v1_14_R1.PacketPlayInBlockPlace; +import net.minecraft.server.v1_14_R1.PacketPlayInChat; +import net.minecraft.server.v1_14_R1.PacketPlayInFlying; +import net.minecraft.server.v1_14_R1.PacketPlayInHeldItemSlot; +import net.minecraft.server.v1_14_R1.PacketPlayInTransaction; +import net.minecraft.server.v1_14_R1.PacketPlayInUpdateSign; +import net.minecraft.server.v1_14_R1.PacketPlayInWindowClick; +import net.minecraft.server.v1_14_R1.PlayerConnection; +import net.minecraft.server.v1_14_R1.PlayerInteractManager; +import net.minecraft.server.v1_14_R1.WorldServer; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_14_R1.CraftServer; +import org.bukkit.craftbukkit.v1_14_R1.CraftWorld; + +import java.lang.reflect.Field; +import java.util.UUID; + +public final class ChunkLoaderNPC_v1_14_R1 extends EntityPlayer implements ChunkLoaderNPC { + + private boolean dieCall = false; + + public ChunkLoaderNPC_v1_14_R1(Location location, UUID uuid){ + super(((CraftServer) Bukkit.getServer()).getServer(), ((CraftWorld) location.getWorld()).getHandle(), + new GameProfile(uuid, "Loader-" + location.getWorld().getName()), new PlayerInteractManager(((CraftWorld) location.getWorld()).getHandle())); + + playerConnection = new DummyPlayerConnection(server, this); + + a(EnumGamemode.CREATIVE); + clientViewDistance = 1; + + fauxSleeping = true; + + spawnIn(world); + setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + ((WorldServer) world).addPlayerJoin(this); + } + + @Override + public UUID getUniqueId() { + return super.getUniqueID(); + } + + @Override + public void die() { + if(!dieCall) { + dieCall = true; + getWorldServer().removePlayer(this); + dieCall = false; + } + else { + super.die(); + } + } + + @Override + public Location getLocation() { + return getBukkitEntity().getLocation(); + } + + private static class DummyNetworkManager extends NetworkManager{ + + DummyNetworkManager(){ + super(EnumProtocolDirection.SERVERBOUND); + updateFields(); + } + + private void updateFields() { + try { + Field channelField = NetworkManager.class.getDeclaredField("channel"); + channelField.setAccessible(true); + channelField.set(this, new DummyChannel()); + channelField.setAccessible(false); + + Field socketAddressField = NetworkManager.class.getDeclaredField("socketAddress"); + socketAddressField.setAccessible(true); + socketAddressField.set(this, null); + } + catch (NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException e) { + e.printStackTrace(); + } + } + + } + + private static class DummyPlayerConnection extends PlayerConnection { + + DummyPlayerConnection(MinecraftServer minecraftServer, EntityPlayer entityPlayer) { + super(minecraftServer, new DummyNetworkManager(), entityPlayer); + } + + public void a(PacketPlayInWindowClick packetPlayInWindowClick) { + + } + + public void a(PacketPlayInTransaction packetPlayInTransaction) { + + } + + public void a(PacketPlayInFlying packetPlayInFlying) { + + } + + public void a(PacketPlayInUpdateSign packetPlayInUpdateSign) { + + } + + public void a(PacketPlayInBlockDig packetPlayInBlockDig) { + + } + + public void a(PacketPlayInBlockPlace packetPlayInBlockPlace) { + + } + + public void disconnect(String s) { + + } + + public void a(PacketPlayInHeldItemSlot packetPlayInHeldItemSlot) { + + } + + public void a(PacketPlayInChat packetPlayInChat) { + + } + + public void sendPacket(Packet packet) { + + } + + } + +} diff --git a/v1_14_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_14_R1.java b/v1_14_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_14_R1.java new file mode 100644 index 0000000..0de2f28 --- /dev/null +++ b/v1_14_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_14_R1.java @@ -0,0 +1,293 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.loaders.WChunkLoader; +import net.minecraft.server.v1_14_R1.AxisAlignedBB; +import net.minecraft.server.v1_14_R1.Block; +import net.minecraft.server.v1_14_R1.BlockPosition; +import net.minecraft.server.v1_14_R1.Chunk; +import net.minecraft.server.v1_14_R1.ChunkCoordIntPair; +import net.minecraft.server.v1_14_R1.Entity; +import net.minecraft.server.v1_14_R1.EntityArmorStand; +import net.minecraft.server.v1_14_R1.EntityTypes; +import net.minecraft.server.v1_14_R1.ITickable; +import net.minecraft.server.v1_14_R1.ItemStack; +import net.minecraft.server.v1_14_R1.NBTTagCompound; +import net.minecraft.server.v1_14_R1.NBTTagList; +import net.minecraft.server.v1_14_R1.NBTTagLong; +import net.minecraft.server.v1_14_R1.NBTTagString; +import net.minecraft.server.v1_14_R1.TileEntity; +import net.minecraft.server.v1_14_R1.TileEntityMobSpawner; +import net.minecraft.server.v1_14_R1.TileEntityTypes; +import net.minecraft.server.v1_14_R1.World; +import net.minecraft.server.v1_14_R1.WorldServer; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_14_R1.CraftChunk; +import org.bukkit.craftbukkit.v1_14_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_14_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_14_R1.util.CraftChatMessage; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@SuppressWarnings("unused") +public final class NMSAdapter_v1_14_R1 implements NMSAdapter { + + private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + + @Override + public String getTag(org.bukkit.inventory.ItemStack itemStack, String key, String def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + if(!tagCompound.hasKeyOfType(key, 8)) + return def; + + return tagCompound.getString(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, String value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + tagCompound.set(key, new NBTTagString(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public long getTag(org.bukkit.inventory.ItemStack itemStack, String key, long def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + if(!tagCompound.hasKeyOfType(key, 4)) + return def; + + return tagCompound.getLong(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, long value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + tagCompound.set(key, new NBTTagLong(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack itemStack, String texture) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + + NBTTagCompound nbtTagCompound = nmsItem.getOrCreateTag(); + + NBTTagCompound skullOwner = nbtTagCompound.hasKey("SkullOwner") ? nbtTagCompound.getCompound("SkullOwner") : new NBTTagCompound(); + + NBTTagCompound properties = new NBTTagCompound(); + + NBTTagList textures = new NBTTagList(); + NBTTagCompound signature = new NBTTagCompound(); + signature.setString("Value", texture); + textures.add(signature); + + properties.set("textures", textures); + + skullOwner.set("Properties", properties); + skullOwner.setString("Id", UUID.randomUUID().toString()); + + nbtTagCompound.set("SkullOwner", skullOwner); + + nmsItem.setTag(nbtTagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public ChunkLoaderNPC createNPC(Location location, UUID uuid) { + return new ChunkLoaderNPC_v1_14_R1(location, uuid); + } + + @Override + public void createLoader(ChunkLoader chunkLoader) { + Location loaderLoc = chunkLoader.getLocation(); + assert loaderLoc.getWorld() != null; + WorldServer world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + TileEntityChunkLoader tileEntityChunkLoader = new TileEntityChunkLoader(chunkLoader, world, blockPosition); + world.tileEntityListTick.add(tileEntityChunkLoader); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner) + .forEach(tileEntity -> ((TileEntityMobSpawner) tileEntity).getSpawner().requiredPlayerRange = -1); + + world.setForceLoaded(chunk.getPos().x, chunk.getPos().z, true); + } + + @Override + public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) { + Location loaderLoc = chunkLoader.getLocation(); + assert loaderLoc.getWorld() != null; + WorldServer world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + long tileEntityLong = ChunkCoordIntPair.pair(blockPosition.getX() >> 4, blockPosition.getZ() >> 4); + TileEntityChunkLoader tileEntityChunkLoader = TileEntityChunkLoader.tileEntityChunkLoaderMap.remove(tileEntityLong); + if(tileEntityChunkLoader != null) { + tileEntityChunkLoader.holograms.forEach(Entity::die); + tileEntityChunkLoader.removed = true; + world.tileEntityListTick.remove(tileEntityChunkLoader); + } + + if(spawnParticle) + world.a(null, 2001, blockPosition, Block.getCombinedId(world.getType(blockPosition))); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner) + .forEach(tileEntity -> ((TileEntityMobSpawner) tileEntity).getSpawner().requiredPlayerRange = 16); + + world.setForceLoaded(chunk.getPos().x, chunk.getPos().z, true); + } + + @Override + public void updateSpawner(Location location, boolean reset) { + assert location.getWorld() != null; + World world = ((CraftWorld) location.getWorld()).getHandle(); + + BlockPosition blockPosition = new BlockPosition(location.getX(), location.getY(), location.getZ()); + TileEntityMobSpawner mobSpawner = (TileEntityMobSpawner) world.getTileEntity(blockPosition); + + if(mobSpawner == null) + return; + + mobSpawner.getSpawner().requiredPlayerRange = reset ? 16 : -1; + } + + private static final class TileEntityChunkLoader extends TileEntity implements ITickable { + + private static final Map tileEntityChunkLoaderMap = new HashMap<>(); + + private final List holograms = new ArrayList<>(); + private final ChunkLoader chunkLoader; + + private short currentTick = 20; + private short daysAmount, hoursAmount, minutesAmount, secondsAmount; + private boolean removed = false; + + TileEntityChunkLoader(ChunkLoader chunkLoader, World world, BlockPosition blockPosition){ + super(TileEntityTypes.COMMAND_BLOCK); + + this.chunkLoader = chunkLoader; + + setPosition(blockPosition); + setWorld(world); + + long timeLeft = chunkLoader.getTimeLeft(); + + daysAmount = (short) (timeLeft / 86400); + timeLeft = timeLeft % 86400; + + hoursAmount = (short) (timeLeft / 3600); + timeLeft = timeLeft % 3600; + + minutesAmount = (short) (timeLeft / 60); + timeLeft = timeLeft % 60; + + secondsAmount = (short) timeLeft; + + tileEntityChunkLoaderMap.put(ChunkCoordIntPair.pair(blockPosition.getX() >> 4, blockPosition.getZ() >> 4), this); + + double currentY = position.getY() + 1; + for(int i = plugin.getSettings().hologramLines.size(); i > 0; i--){ + EntityHologram hologram = new EntityHologram(world, position.getX() + 0.5, currentY, position.getZ() + 0.5); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + world.addEntity(hologram); + currentY += 0.23; + holograms.add(hologram); + } + } + + @Override + public void r() { + tick(); + } + + @Override + public void tick() { + if(removed || ++currentTick <= 20) + return; + + currentTick = 0; + + if(((WChunkLoader) chunkLoader).isNotActive()){ + chunkLoader.remove(); + return; + } + + int hologramsAmount = holograms.size(); + for (int i = hologramsAmount; i > 0; i--) { + EntityHologram hologram = holograms.get(hologramsAmount - i); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + } + + ((WChunkLoader) chunkLoader).tick(); + + if(!removed) { + secondsAmount--; + if (secondsAmount < 0) { + secondsAmount = 59; + minutesAmount--; + if (minutesAmount < 0) { + minutesAmount = 59; + hoursAmount--; + if (hoursAmount < 0) { + hoursAmount = 23; + daysAmount--; + } + } + } + } + } + + private void updateName(EntityHologram hologram, String line){ + assert chunkLoader.getWhoPlaced().getName() != null; + hologram.setCustomName(CraftChatMessage.fromStringOrNull(line + .replace("{0}", chunkLoader.getWhoPlaced().getName()) + .replace("{1}", daysAmount + "") + .replace("{2}", hoursAmount + "") + .replace("{3}", minutesAmount + "") + .replace("{4}", secondsAmount + "")) + ); + } + + } + + private static class EntityHologram extends EntityArmorStand { + + EntityHologram(World world, double x, double y, double z){ + super(EntityTypes.ARMOR_STAND, world); + setPosition(x, y, z); + setInvisible(true); + setSmall(true); + setArms(false); + setNoGravity(true); + setBasePlate(true); + setMarker(true); + setCustomNameVisible(true); + a(new AxisAlignedBB(0D, 0D, 0D, 0D, 0D, 0D)); + } + + } + +} diff --git a/v1_15_R1/src/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_15_R1.java b/v1_15_R1/src/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_15_R1.java new file mode 100644 index 0000000..db9a669 --- /dev/null +++ b/v1_15_R1/src/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_15_R1.java @@ -0,0 +1,147 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.npc.DummyChannel; +import com.mojang.authlib.GameProfile; +import net.minecraft.server.v1_15_R1.EntityPlayer; +import net.minecraft.server.v1_15_R1.EnumGamemode; +import net.minecraft.server.v1_15_R1.EnumProtocolDirection; +import net.minecraft.server.v1_15_R1.MinecraftServer; +import net.minecraft.server.v1_15_R1.NetworkManager; +import net.minecraft.server.v1_15_R1.Packet; +import net.minecraft.server.v1_15_R1.PacketPlayInBlockDig; +import net.minecraft.server.v1_15_R1.PacketPlayInBlockPlace; +import net.minecraft.server.v1_15_R1.PacketPlayInChat; +import net.minecraft.server.v1_15_R1.PacketPlayInFlying; +import net.minecraft.server.v1_15_R1.PacketPlayInHeldItemSlot; +import net.minecraft.server.v1_15_R1.PacketPlayInTransaction; +import net.minecraft.server.v1_15_R1.PacketPlayInUpdateSign; +import net.minecraft.server.v1_15_R1.PacketPlayInWindowClick; +import net.minecraft.server.v1_15_R1.PlayerConnection; +import net.minecraft.server.v1_15_R1.PlayerInteractManager; +import net.minecraft.server.v1_15_R1.WorldServer; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_15_R1.CraftServer; +import org.bukkit.craftbukkit.v1_15_R1.CraftWorld; + +import java.lang.reflect.Field; +import java.util.UUID; + +public final class ChunkLoaderNPC_v1_15_R1 extends EntityPlayer implements ChunkLoaderNPC { + + private boolean dieCall = false; + + public ChunkLoaderNPC_v1_15_R1(Location location, UUID uuid){ + super(((CraftServer) Bukkit.getServer()).getServer(), ((CraftWorld) location.getWorld()).getHandle(), + new GameProfile(uuid, "Loader-" + location.getWorld().getName()), new PlayerInteractManager(((CraftWorld) location.getWorld()).getHandle())); + + playerConnection = new DummyPlayerConnection(server, this); + + a(EnumGamemode.CREATIVE); + clientViewDistance = 1; + + fauxSleeping = true; + + spawnIn(world); + setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + ((WorldServer) world).addPlayerJoin(this); + } + + @Override + public UUID getUniqueId() { + return super.getUniqueID(); + } + + @Override + public void die() { + if(!dieCall) { + dieCall = true; + getWorldServer().removePlayer(this); + dieCall = false; + } + else { + super.die(); + } + } + + @Override + public Location getLocation() { + return getBukkitEntity().getLocation(); + } + + private static class DummyNetworkManager extends NetworkManager{ + + DummyNetworkManager(){ + super(EnumProtocolDirection.SERVERBOUND); + updateFields(); + } + + private void updateFields() { + try { + Field channelField = NetworkManager.class.getDeclaredField("channel"); + channelField.setAccessible(true); + channelField.set(this, new DummyChannel()); + channelField.setAccessible(false); + + Field socketAddressField = NetworkManager.class.getDeclaredField("socketAddress"); + socketAddressField.setAccessible(true); + socketAddressField.set(this, null); + } + catch (NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException e) { + e.printStackTrace(); + } + } + + } + + private static class DummyPlayerConnection extends PlayerConnection { + + DummyPlayerConnection(MinecraftServer minecraftServer, EntityPlayer entityPlayer) { + super(minecraftServer, new DummyNetworkManager(), entityPlayer); + } + + public void a(PacketPlayInWindowClick packetPlayInWindowClick) { + + } + + public void a(PacketPlayInTransaction packetPlayInTransaction) { + + } + + public void a(PacketPlayInFlying packetPlayInFlying) { + + } + + public void a(PacketPlayInUpdateSign packetPlayInUpdateSign) { + + } + + public void a(PacketPlayInBlockDig packetPlayInBlockDig) { + + } + + public void a(PacketPlayInBlockPlace packetPlayInBlockPlace) { + + } + + public void disconnect(String s) { + + } + + public void a(PacketPlayInHeldItemSlot packetPlayInHeldItemSlot) { + + } + + public void a(PacketPlayInChat packetPlayInChat) { + + } + + public void sendPacket(Packet packet) { + + } + + } + +} diff --git a/v1_15_R1/src/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_15_R1.java b/v1_15_R1/src/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_15_R1.java new file mode 100644 index 0000000..4f6bb89 --- /dev/null +++ b/v1_15_R1/src/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_15_R1.java @@ -0,0 +1,302 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.loaders.WChunkLoader; +import net.minecraft.server.v1_15_R1.AxisAlignedBB; +import net.minecraft.server.v1_15_R1.Block; +import net.minecraft.server.v1_15_R1.BlockPosition; +import net.minecraft.server.v1_15_R1.Chunk; +import net.minecraft.server.v1_15_R1.ChunkCoordIntPair; +import net.minecraft.server.v1_15_R1.Entity; +import net.minecraft.server.v1_15_R1.EntityArmorStand; +import net.minecraft.server.v1_15_R1.EntityTypes; +import net.minecraft.server.v1_15_R1.ITickable; +import net.minecraft.server.v1_15_R1.ItemStack; +import net.minecraft.server.v1_15_R1.NBTTagCompound; +import net.minecraft.server.v1_15_R1.NBTTagList; +import net.minecraft.server.v1_15_R1.NBTTagLong; +import net.minecraft.server.v1_15_R1.NBTTagString; +import net.minecraft.server.v1_15_R1.TileEntity; +import net.minecraft.server.v1_15_R1.TileEntityMobSpawner; +import net.minecraft.server.v1_15_R1.TileEntityTypes; +import net.minecraft.server.v1_15_R1.World; +import net.minecraft.server.v1_15_R1.WorldServer; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_15_R1.CraftChunk; +import org.bukkit.craftbukkit.v1_15_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_15_R1.util.CraftChatMessage; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@SuppressWarnings("unused") +public final class NMSAdapter_v1_15_R1 implements NMSAdapter { + + private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + + @Override + public String getTag(org.bukkit.inventory.ItemStack itemStack, String key, String def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + if(!tagCompound.hasKeyOfType(key, 8)) + return def; + + return tagCompound.getString(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, String value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + tagCompound.set(key, NBTTagString.a(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public long getTag(org.bukkit.inventory.ItemStack itemStack, String key, long def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + if(!tagCompound.hasKeyOfType(key, 4)) + return def; + + return tagCompound.getLong(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, long value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + tagCompound.set(key, NBTTagLong.a(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack itemStack, String texture) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + + NBTTagCompound nbtTagCompound = nmsItem.getOrCreateTag(); + + NBTTagCompound skullOwner = nbtTagCompound.hasKey("SkullOwner") ? nbtTagCompound.getCompound("SkullOwner") : new NBTTagCompound(); + + NBTTagCompound properties = new NBTTagCompound(); + + NBTTagList textures = new NBTTagList(); + NBTTagCompound signature = new NBTTagCompound(); + signature.setString("Value", texture); + textures.add(signature); + + properties.set("textures", textures); + + skullOwner.set("Properties", properties); + skullOwner.setString("Id", UUID.randomUUID().toString()); + + nbtTagCompound.set("SkullOwner", skullOwner); + + nmsItem.setTag(nbtTagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public ChunkLoaderNPC createNPC(Location location, UUID uuid) { + return new ChunkLoaderNPC_v1_15_R1(location, uuid); + } + + @Override + public void createLoader(ChunkLoader chunkLoader) { + Location loaderLoc = chunkLoader.getLocation(); + assert loaderLoc.getWorld() != null; + WorldServer world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + TileEntityChunkLoader tileEntityChunkLoader = new TileEntityChunkLoader(chunkLoader, world, blockPosition); + world.tileEntityListTick.add(tileEntityChunkLoader); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner) + .forEach(tileEntity -> ((TileEntityMobSpawner) tileEntity).getSpawner().requiredPlayerRange = -1); + + world.setForceLoaded(chunk.getPos().x, chunk.getPos().z, true); + } + + @Override + public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) { + Location loaderLoc = chunkLoader.getLocation(); + assert loaderLoc.getWorld() != null; + WorldServer world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + long tileEntityLong = ChunkCoordIntPair.pair(blockPosition.getX() >> 4, blockPosition.getZ() >> 4); + TileEntityChunkLoader tileEntityChunkLoader = TileEntityChunkLoader.tileEntityChunkLoaderMap.remove(tileEntityLong); + if(tileEntityChunkLoader != null) { + tileEntityChunkLoader.holograms.forEach(Entity::die); + tileEntityChunkLoader.removed = true; + world.tileEntityListTick.remove(tileEntityChunkLoader); + } + + if(spawnParticle) + world.a(null, 2001, blockPosition, Block.getCombinedId(world.getType(blockPosition))); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner) + .forEach(tileEntity -> ((TileEntityMobSpawner) tileEntity).getSpawner().requiredPlayerRange = 16); + + world.setForceLoaded(chunk.getPos().x, chunk.getPos().z, false); + } + + @Override + public void updateSpawner(Location location, boolean reset) { + assert location.getWorld() != null; + World world = ((CraftWorld) location.getWorld()).getHandle(); + + BlockPosition blockPosition = new BlockPosition(location.getX(), location.getY(), location.getZ()); + TileEntityMobSpawner mobSpawner = (TileEntityMobSpawner) world.getTileEntity(blockPosition); + + if(mobSpawner == null) + return; + + mobSpawner.getSpawner().requiredPlayerRange = reset ? 16 : -1; + } + + private static final class TileEntityChunkLoader extends TileEntity implements ITickable { + + private static final Map tileEntityChunkLoaderMap = new HashMap<>(); + + private final List holograms = new ArrayList<>(); + private final ChunkLoader chunkLoader; + + private short currentTick = 20; + private short daysAmount, hoursAmount, minutesAmount, secondsAmount; + private boolean removed = false; + + TileEntityChunkLoader(ChunkLoader chunkLoader, World world, BlockPosition blockPosition){ + super(TileEntityTypes.COMMAND_BLOCK); + + this.chunkLoader = chunkLoader; + + setLocation(world, blockPosition); + + long timeLeft = chunkLoader.getTimeLeft(); + + daysAmount = (short) (timeLeft / 86400); + timeLeft = timeLeft % 86400; + + hoursAmount = (short) (timeLeft / 3600); + timeLeft = timeLeft % 3600; + + minutesAmount = (short) (timeLeft / 60); + timeLeft = timeLeft % 60; + + secondsAmount = (short) timeLeft; + + tileEntityChunkLoaderMap.put(ChunkCoordIntPair.pair(blockPosition.getX() >> 4, blockPosition.getZ() >> 4), this); + + double currentY = position.getY() + 1; + for(int i = plugin.getSettings().hologramLines.size(); i > 0; i--){ + EntityHologram hologram = new EntityHologram(world, position.getX() + 0.5, currentY, position.getZ() + 0.5); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + world.addEntity(hologram); + currentY += 0.23; + holograms.add(hologram); + } + } + + @Override + public void v() { + tick(); + } + + @Override + public void tick() { + if(removed || ++currentTick <= 20) + return; + + currentTick = 0; + + if(((WChunkLoader) chunkLoader).isNotActive()){ + chunkLoader.remove(); + return; + } + + int hologramsAmount = holograms.size(); + for (int i = hologramsAmount; i > 0; i--) { + EntityHologram hologram = holograms.get(hologramsAmount - i); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + } + + ((WChunkLoader) chunkLoader).tick(); + + if(!removed) { + secondsAmount--; + if (secondsAmount < 0) { + secondsAmount = 59; + minutesAmount--; + if (minutesAmount < 0) { + minutesAmount = 59; + hoursAmount--; + if (hoursAmount < 0) { + hoursAmount = 23; + daysAmount--; + } + } + } + } + } + + private void updateName(EntityHologram hologram, String line){ + assert chunkLoader.getWhoPlaced().getName() != null; + hologram.setCustomName(CraftChatMessage.fromStringOrNull(line + .replace("{0}", chunkLoader.getWhoPlaced().getName()) + .replace("{1}", daysAmount + "") + .replace("{2}", hoursAmount + "") + .replace("{3}", minutesAmount + "") + .replace("{4}", secondsAmount + "")) + ); + } + + } + + private static class EntityHologram extends EntityArmorStand { + + EntityHologram(World world, double x, double y, double z){ + super(EntityTypes.ARMOR_STAND, world); + setPosition(x, y, z); + setInvisible(true); + setSmall(true); + setArms(false); + setNoGravity(true); + setBasePlate(true); + setMarker(true); + setCustomNameVisible(true); + a(new AxisAlignedBB(0D, 0D, 0D, 0D, 0D, 0D)); + } + + @Override + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + return new NBTTagCompound(); + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + return false; + } + + } + +} diff --git a/v1_16_R1/src/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_16_R1.java b/v1_16_R1/src/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_16_R1.java new file mode 100644 index 0000000..0cf878f --- /dev/null +++ b/v1_16_R1/src/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_16_R1.java @@ -0,0 +1,147 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.npc.DummyChannel; +import com.mojang.authlib.GameProfile; +import net.minecraft.server.v1_16_R1.EntityPlayer; +import net.minecraft.server.v1_16_R1.EnumGamemode; +import net.minecraft.server.v1_16_R1.EnumProtocolDirection; +import net.minecraft.server.v1_16_R1.MinecraftServer; +import net.minecraft.server.v1_16_R1.NetworkManager; +import net.minecraft.server.v1_16_R1.Packet; +import net.minecraft.server.v1_16_R1.PacketPlayInBlockDig; +import net.minecraft.server.v1_16_R1.PacketPlayInBlockPlace; +import net.minecraft.server.v1_16_R1.PacketPlayInChat; +import net.minecraft.server.v1_16_R1.PacketPlayInFlying; +import net.minecraft.server.v1_16_R1.PacketPlayInHeldItemSlot; +import net.minecraft.server.v1_16_R1.PacketPlayInTransaction; +import net.minecraft.server.v1_16_R1.PacketPlayInUpdateSign; +import net.minecraft.server.v1_16_R1.PacketPlayInWindowClick; +import net.minecraft.server.v1_16_R1.PlayerConnection; +import net.minecraft.server.v1_16_R1.PlayerInteractManager; +import net.minecraft.server.v1_16_R1.WorldServer; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_16_R1.CraftServer; +import org.bukkit.craftbukkit.v1_16_R1.CraftWorld; + +import java.lang.reflect.Field; +import java.util.UUID; + +public final class ChunkLoaderNPC_v1_16_R1 extends EntityPlayer implements ChunkLoaderNPC { + + private boolean dieCall = false; + + public ChunkLoaderNPC_v1_16_R1(Location location, UUID uuid){ + super(((CraftServer) Bukkit.getServer()).getServer(), ((CraftWorld) location.getWorld()).getHandle(), + new GameProfile(uuid, "Loader-" + location.getWorld().getName()), new PlayerInteractManager(((CraftWorld) location.getWorld()).getHandle())); + + playerConnection = new DummyPlayerConnection(server, this); + + a(EnumGamemode.CREATIVE); + clientViewDistance = 1; + + fauxSleeping = true; + + spawnIn(world); + setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + ((WorldServer) world).addPlayerJoin(this); + } + + @Override + public UUID getUniqueId() { + return super.getUniqueID(); + } + + @Override + public void die() { + if(!dieCall) { + dieCall = true; + getWorldServer().removePlayer(this); + dieCall = false; + } + else { + super.die(); + } + } + + @Override + public Location getLocation() { + return getBukkitEntity().getLocation(); + } + + private static class DummyNetworkManager extends NetworkManager{ + + DummyNetworkManager(){ + super(EnumProtocolDirection.SERVERBOUND); + updateFields(); + } + + private void updateFields() { + try { + Field channelField = NetworkManager.class.getDeclaredField("channel"); + channelField.setAccessible(true); + channelField.set(this, new DummyChannel()); + channelField.setAccessible(false); + + Field socketAddressField = NetworkManager.class.getDeclaredField("socketAddress"); + socketAddressField.setAccessible(true); + socketAddressField.set(this, null); + } + catch (NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException e) { + e.printStackTrace(); + } + } + + } + + private static class DummyPlayerConnection extends PlayerConnection { + + DummyPlayerConnection(MinecraftServer minecraftServer, EntityPlayer entityPlayer) { + super(minecraftServer, new DummyNetworkManager(), entityPlayer); + } + + public void a(PacketPlayInWindowClick packetPlayInWindowClick) { + + } + + public void a(PacketPlayInTransaction packetPlayInTransaction) { + + } + + public void a(PacketPlayInFlying packetPlayInFlying) { + + } + + public void a(PacketPlayInUpdateSign packetPlayInUpdateSign) { + + } + + public void a(PacketPlayInBlockDig packetPlayInBlockDig) { + + } + + public void a(PacketPlayInBlockPlace packetPlayInBlockPlace) { + + } + + public void disconnect(String s) { + + } + + public void a(PacketPlayInHeldItemSlot packetPlayInHeldItemSlot) { + + } + + public void a(PacketPlayInChat packetPlayInChat) { + + } + + public void sendPacket(Packet packet) { + + } + + } + +} diff --git a/v1_16_R1/src/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_16_R1.java b/v1_16_R1/src/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_16_R1.java new file mode 100644 index 0000000..ca76f9c --- /dev/null +++ b/v1_16_R1/src/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_16_R1.java @@ -0,0 +1,294 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.loaders.WChunkLoader; +import net.minecraft.server.v1_16_R1.AxisAlignedBB; +import net.minecraft.server.v1_16_R1.Block; +import net.minecraft.server.v1_16_R1.BlockPosition; +import net.minecraft.server.v1_16_R1.Chunk; +import net.minecraft.server.v1_16_R1.ChunkCoordIntPair; +import net.minecraft.server.v1_16_R1.Entity; +import net.minecraft.server.v1_16_R1.EntityArmorStand; +import net.minecraft.server.v1_16_R1.EntityTypes; +import net.minecraft.server.v1_16_R1.IBlockData; +import net.minecraft.server.v1_16_R1.ITickable; +import net.minecraft.server.v1_16_R1.ItemStack; +import net.minecraft.server.v1_16_R1.NBTTagCompound; +import net.minecraft.server.v1_16_R1.NBTTagList; +import net.minecraft.server.v1_16_R1.NBTTagLong; +import net.minecraft.server.v1_16_R1.NBTTagString; +import net.minecraft.server.v1_16_R1.TileEntity; +import net.minecraft.server.v1_16_R1.TileEntityMobSpawner; +import net.minecraft.server.v1_16_R1.TileEntityTypes; +import net.minecraft.server.v1_16_R1.World; +import net.minecraft.server.v1_16_R1.WorldServer; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_16_R1.CraftChunk; +import org.bukkit.craftbukkit.v1_16_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_16_R1.util.CraftChatMessage; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@SuppressWarnings("unused") +public final class NMSAdapter_v1_16_R1 implements NMSAdapter { + + private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + + @Override + public String getTag(org.bukkit.inventory.ItemStack itemStack, String key, String def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + if(!tagCompound.hasKeyOfType(key, 8)) + return def; + + return tagCompound.getString(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, String value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + tagCompound.set(key, NBTTagString.a(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public long getTag(org.bukkit.inventory.ItemStack itemStack, String key, long def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + if(!tagCompound.hasKeyOfType(key, 4)) + return def; + + return tagCompound.getLong(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, long value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + tagCompound.set(key, NBTTagLong.a(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack itemStack, String texture) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + + NBTTagCompound nbtTagCompound = nmsItem.getOrCreateTag(); + + NBTTagCompound skullOwner = nbtTagCompound.hasKey("SkullOwner") ? nbtTagCompound.getCompound("SkullOwner") : new NBTTagCompound(); + + NBTTagCompound properties = new NBTTagCompound(); + + NBTTagList textures = new NBTTagList(); + NBTTagCompound signature = new NBTTagCompound(); + signature.setString("Value", texture); + textures.add(signature); + + properties.set("textures", textures); + + skullOwner.set("Properties", properties); + skullOwner.setString("Id", UUID.randomUUID().toString()); + + nbtTagCompound.set("SkullOwner", skullOwner); + + nmsItem.setTag(nbtTagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public ChunkLoaderNPC createNPC(Location location, UUID uuid) { + return new ChunkLoaderNPC_v1_16_R1(location, uuid); + } + + @Override + public void createLoader(ChunkLoader chunkLoader) { + Location loaderLoc = chunkLoader.getLocation(); + assert loaderLoc.getWorld() != null; + WorldServer world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + TileEntityChunkLoader tileEntityChunkLoader = new TileEntityChunkLoader(chunkLoader, world, blockPosition); + world.tileEntityListTick.add(tileEntityChunkLoader); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner) + .forEach(tileEntity -> ((TileEntityMobSpawner) tileEntity).getSpawner().requiredPlayerRange = -1); + + world.setForceLoaded(chunk.getPos().x, chunk.getPos().z, true); + } + + @Override + public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) { + Location loaderLoc = chunkLoader.getLocation(); + assert loaderLoc.getWorld() != null; + WorldServer world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + long tileEntityLong = ChunkCoordIntPair.pair(blockPosition.getX() >> 4, blockPosition.getZ() >> 4); + TileEntityChunkLoader tileEntityChunkLoader = TileEntityChunkLoader.tileEntityChunkLoaderMap.remove(tileEntityLong); + if(tileEntityChunkLoader != null) { + tileEntityChunkLoader.holograms.forEach(Entity::die); + tileEntityChunkLoader.removed = true; + world.tileEntityListTick.remove(tileEntityChunkLoader); + } + + if(spawnParticle) + world.a(null, 2001, blockPosition, Block.getCombinedId(world.getType(blockPosition))); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner) + .forEach(tileEntity -> ((TileEntityMobSpawner) tileEntity).getSpawner().requiredPlayerRange = 16); + + world.setForceLoaded(chunk.getPos().x, chunk.getPos().z, false); + } + + @Override + public void updateSpawner(Location location, boolean reset) { + assert location.getWorld() != null; + World world = ((CraftWorld) location.getWorld()).getHandle(); + + BlockPosition blockPosition = new BlockPosition(location.getX(), location.getY(), location.getZ()); + IBlockData blockData = world.getType(blockPosition); + TileEntityMobSpawner mobSpawner = (TileEntityMobSpawner) world.getTileEntity(blockPosition); + + if(mobSpawner == null) + return; + + mobSpawner.getSpawner().requiredPlayerRange = reset ? 16 : -1; + } + + private static final class TileEntityChunkLoader extends TileEntity implements ITickable { + + private static final Map tileEntityChunkLoaderMap = new HashMap<>(); + + private final List holograms = new ArrayList<>(); + private final ChunkLoader chunkLoader; + + private short currentTick = 20; + private short daysAmount, hoursAmount, minutesAmount, secondsAmount; + private boolean removed = false; + + TileEntityChunkLoader(ChunkLoader chunkLoader, World world, BlockPosition blockPosition){ + super(TileEntityTypes.COMMAND_BLOCK); + + this.chunkLoader = chunkLoader; + + setLocation(world, blockPosition); + + long timeLeft = chunkLoader.getTimeLeft(); + + daysAmount = (short) (timeLeft / 86400); + timeLeft = timeLeft % 86400; + + hoursAmount = (short) (timeLeft / 3600); + timeLeft = timeLeft % 3600; + + minutesAmount = (short) (timeLeft / 60); + timeLeft = timeLeft % 60; + + secondsAmount = (short) timeLeft; + + tileEntityChunkLoaderMap.put(ChunkCoordIntPair.pair(blockPosition.getX() >> 4, blockPosition.getZ() >> 4), this); + + double currentY = position.getY() + 1; + for(int i = plugin.getSettings().hologramLines.size(); i > 0; i--){ + EntityHologram hologram = new EntityHologram(world, position.getX() + 0.5, currentY, position.getZ() + 0.5); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + world.addEntity(hologram); + currentY += 0.23; + holograms.add(hologram); + } + } + + @Override + public void w() { + tick(); + } + + @Override + public void tick() { + if(removed || ++currentTick <= 20) + return; + + currentTick = 0; + + if(((WChunkLoader) chunkLoader).isNotActive()){ + chunkLoader.remove(); + return; + } + + int hologramsAmount = holograms.size(); + for (int i = hologramsAmount; i > 0; i--) { + EntityHologram hologram = holograms.get(hologramsAmount - i); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + } + + ((WChunkLoader) chunkLoader).tick(); + + if(!removed) { + secondsAmount--; + if (secondsAmount < 0) { + secondsAmount = 59; + minutesAmount--; + if (minutesAmount < 0) { + minutesAmount = 59; + hoursAmount--; + if (hoursAmount < 0) { + hoursAmount = 23; + daysAmount--; + } + } + } + } + } + + private void updateName(EntityHologram hologram, String line){ + assert chunkLoader.getWhoPlaced().getName() != null; + hologram.setCustomName(CraftChatMessage.fromStringOrNull(line + .replace("{0}", chunkLoader.getWhoPlaced().getName()) + .replace("{1}", daysAmount + "") + .replace("{2}", hoursAmount + "") + .replace("{3}", minutesAmount + "") + .replace("{4}", secondsAmount + "")) + ); + } + + } + + private static class EntityHologram extends EntityArmorStand { + + EntityHologram(World world, double x, double y, double z){ + super(EntityTypes.ARMOR_STAND, world); + setPosition(x, y, z); + setInvisible(true); + setSmall(true); + setArms(false); + setNoGravity(true); + setBasePlate(true); + setMarker(true); + setCustomNameVisible(true); + a(new AxisAlignedBB(0D, 0D, 0D, 0D, 0D, 0D)); + } + + } + +} diff --git a/v1_16_R2/src/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_16_R2.java b/v1_16_R2/src/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_16_R2.java new file mode 100644 index 0000000..2814ec2 --- /dev/null +++ b/v1_16_R2/src/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_16_R2.java @@ -0,0 +1,147 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.npc.DummyChannel; +import com.mojang.authlib.GameProfile; +import net.minecraft.server.v1_16_R2.EntityPlayer; +import net.minecraft.server.v1_16_R2.EnumGamemode; +import net.minecraft.server.v1_16_R2.EnumProtocolDirection; +import net.minecraft.server.v1_16_R2.MinecraftServer; +import net.minecraft.server.v1_16_R2.NetworkManager; +import net.minecraft.server.v1_16_R2.Packet; +import net.minecraft.server.v1_16_R2.PacketPlayInBlockDig; +import net.minecraft.server.v1_16_R2.PacketPlayInBlockPlace; +import net.minecraft.server.v1_16_R2.PacketPlayInChat; +import net.minecraft.server.v1_16_R2.PacketPlayInFlying; +import net.minecraft.server.v1_16_R2.PacketPlayInHeldItemSlot; +import net.minecraft.server.v1_16_R2.PacketPlayInTransaction; +import net.minecraft.server.v1_16_R2.PacketPlayInUpdateSign; +import net.minecraft.server.v1_16_R2.PacketPlayInWindowClick; +import net.minecraft.server.v1_16_R2.PlayerConnection; +import net.minecraft.server.v1_16_R2.PlayerInteractManager; +import net.minecraft.server.v1_16_R2.WorldServer; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_16_R2.CraftServer; +import org.bukkit.craftbukkit.v1_16_R2.CraftWorld; + +import java.lang.reflect.Field; +import java.util.UUID; + +public final class ChunkLoaderNPC_v1_16_R2 extends EntityPlayer implements ChunkLoaderNPC { + + private boolean dieCall = false; + + public ChunkLoaderNPC_v1_16_R2(Location location, UUID uuid){ + super(((CraftServer) Bukkit.getServer()).getServer(), ((CraftWorld) location.getWorld()).getHandle(), + new GameProfile(uuid, "Loader-" + location.getWorld().getName()), new PlayerInteractManager(((CraftWorld) location.getWorld()).getHandle())); + + playerConnection = new DummyPlayerConnection(server, this); + + a(EnumGamemode.CREATIVE); + clientViewDistance = 1; + + fauxSleeping = true; + + spawnIn(world); + setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + ((WorldServer) world).addPlayerJoin(this); + } + + @Override + public UUID getUniqueId() { + return super.getUniqueID(); + } + + @Override + public void die() { + if(!dieCall) { + dieCall = true; + getWorldServer().removePlayer(this); + dieCall = false; + } + else { + super.die(); + } + } + + @Override + public Location getLocation() { + return getBukkitEntity().getLocation(); + } + + private static class DummyNetworkManager extends NetworkManager{ + + DummyNetworkManager(){ + super(EnumProtocolDirection.SERVERBOUND); + updateFields(); + } + + private void updateFields() { + try { + Field channelField = NetworkManager.class.getDeclaredField("channel"); + channelField.setAccessible(true); + channelField.set(this, new DummyChannel()); + channelField.setAccessible(false); + + Field socketAddressField = NetworkManager.class.getDeclaredField("socketAddress"); + socketAddressField.setAccessible(true); + socketAddressField.set(this, null); + } + catch (NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException e) { + e.printStackTrace(); + } + } + + } + + private static class DummyPlayerConnection extends PlayerConnection { + + DummyPlayerConnection(MinecraftServer minecraftServer, EntityPlayer entityPlayer) { + super(minecraftServer, new DummyNetworkManager(), entityPlayer); + } + + public void a(PacketPlayInWindowClick packetPlayInWindowClick) { + + } + + public void a(PacketPlayInTransaction packetPlayInTransaction) { + + } + + public void a(PacketPlayInFlying packetPlayInFlying) { + + } + + public void a(PacketPlayInUpdateSign packetPlayInUpdateSign) { + + } + + public void a(PacketPlayInBlockDig packetPlayInBlockDig) { + + } + + public void a(PacketPlayInBlockPlace packetPlayInBlockPlace) { + + } + + public void disconnect(String s) { + + } + + public void a(PacketPlayInHeldItemSlot packetPlayInHeldItemSlot) { + + } + + public void a(PacketPlayInChat packetPlayInChat) { + + } + + public void sendPacket(Packet packet) { + + } + + } + +} diff --git a/v1_16_R2/src/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_16_R2.java b/v1_16_R2/src/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_16_R2.java new file mode 100644 index 0000000..dc79b12 --- /dev/null +++ b/v1_16_R2/src/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_16_R2.java @@ -0,0 +1,294 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.loaders.WChunkLoader; +import net.minecraft.server.v1_16_R2.AxisAlignedBB; +import net.minecraft.server.v1_16_R2.Block; +import net.minecraft.server.v1_16_R2.BlockPosition; +import net.minecraft.server.v1_16_R2.Chunk; +import net.minecraft.server.v1_16_R2.ChunkCoordIntPair; +import net.minecraft.server.v1_16_R2.Entity; +import net.minecraft.server.v1_16_R2.EntityArmorStand; +import net.minecraft.server.v1_16_R2.EntityTypes; +import net.minecraft.server.v1_16_R2.IBlockData; +import net.minecraft.server.v1_16_R2.ITickable; +import net.minecraft.server.v1_16_R2.ItemStack; +import net.minecraft.server.v1_16_R2.NBTTagCompound; +import net.minecraft.server.v1_16_R2.NBTTagList; +import net.minecraft.server.v1_16_R2.NBTTagLong; +import net.minecraft.server.v1_16_R2.NBTTagString; +import net.minecraft.server.v1_16_R2.TileEntity; +import net.minecraft.server.v1_16_R2.TileEntityMobSpawner; +import net.minecraft.server.v1_16_R2.TileEntityTypes; +import net.minecraft.server.v1_16_R2.World; +import net.minecraft.server.v1_16_R2.WorldServer; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_16_R2.CraftChunk; +import org.bukkit.craftbukkit.v1_16_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_16_R2.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_16_R2.util.CraftChatMessage; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@SuppressWarnings("unused") +public final class NMSAdapter_v1_16_R2 implements NMSAdapter { + + private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + + @Override + public String getTag(org.bukkit.inventory.ItemStack itemStack, String key, String def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + if(!tagCompound.hasKeyOfType(key, 8)) + return def; + + return tagCompound.getString(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, String value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + tagCompound.set(key, NBTTagString.a(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public long getTag(org.bukkit.inventory.ItemStack itemStack, String key, long def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + if(!tagCompound.hasKeyOfType(key, 4)) + return def; + + return tagCompound.getLong(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, long value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.getOrCreateTag(); + + tagCompound.set(key, NBTTagLong.a(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack itemStack, String texture) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + + NBTTagCompound nbtTagCompound = nmsItem.getOrCreateTag(); + + NBTTagCompound skullOwner = nbtTagCompound.hasKey("SkullOwner") ? nbtTagCompound.getCompound("SkullOwner") : new NBTTagCompound(); + + NBTTagCompound properties = new NBTTagCompound(); + + NBTTagList textures = new NBTTagList(); + NBTTagCompound signature = new NBTTagCompound(); + signature.setString("Value", texture); + textures.add(signature); + + properties.set("textures", textures); + + skullOwner.set("Properties", properties); + skullOwner.setString("Id", UUID.randomUUID().toString()); + + nbtTagCompound.set("SkullOwner", skullOwner); + + nmsItem.setTag(nbtTagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public ChunkLoaderNPC createNPC(Location location, UUID uuid) { + return new ChunkLoaderNPC_v1_16_R2(location, uuid); + } + + @Override + public void createLoader(ChunkLoader chunkLoader) { + Location loaderLoc = chunkLoader.getLocation(); + assert loaderLoc.getWorld() != null; + WorldServer world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + TileEntityChunkLoader tileEntityChunkLoader = new TileEntityChunkLoader(chunkLoader, world, blockPosition); + world.tileEntityListTick.add(tileEntityChunkLoader); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner) + .forEach(tileEntity -> ((TileEntityMobSpawner) tileEntity).getSpawner().requiredPlayerRange = -1); + + world.setForceLoaded(chunk.getPos().x, chunk.getPos().z, true); + } + + @Override + public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) { + Location loaderLoc = chunkLoader.getLocation(); + assert loaderLoc.getWorld() != null; + WorldServer world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + long tileEntityLong = ChunkCoordIntPair.pair(blockPosition.getX() >> 4, blockPosition.getZ() >> 4); + TileEntityChunkLoader tileEntityChunkLoader = TileEntityChunkLoader.tileEntityChunkLoaderMap.remove(tileEntityLong); + if(tileEntityChunkLoader != null) { + tileEntityChunkLoader.holograms.forEach(Entity::die); + tileEntityChunkLoader.removed = true; + world.tileEntityListTick.remove(tileEntityChunkLoader); + } + + if(spawnParticle) + world.a(null, 2001, blockPosition, Block.getCombinedId(world.getType(blockPosition))); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner) + .forEach(tileEntity -> ((TileEntityMobSpawner) tileEntity).getSpawner().requiredPlayerRange = 16); + + world.setForceLoaded(chunk.getPos().x, chunk.getPos().z, true); + } + + @Override + public void updateSpawner(Location location, boolean reset) { + assert location.getWorld() != null; + World world = ((CraftWorld) location.getWorld()).getHandle(); + + BlockPosition blockPosition = new BlockPosition(location.getX(), location.getY(), location.getZ()); + IBlockData blockData = world.getType(blockPosition); + TileEntityMobSpawner mobSpawner = (TileEntityMobSpawner) world.getTileEntity(blockPosition); + + if(mobSpawner == null) + return; + + mobSpawner.getSpawner().requiredPlayerRange = reset ? 16 : -1; + } + + private static final class TileEntityChunkLoader extends TileEntity implements ITickable { + + private static final Map tileEntityChunkLoaderMap = new HashMap<>(); + + private final List holograms = new ArrayList<>(); + private final ChunkLoader chunkLoader; + + private short currentTick = 20; + private short daysAmount, hoursAmount, minutesAmount, secondsAmount; + private boolean removed = false; + + TileEntityChunkLoader(ChunkLoader chunkLoader, World world, BlockPosition blockPosition){ + super(TileEntityTypes.COMMAND_BLOCK); + + this.chunkLoader = chunkLoader; + + setLocation(world, blockPosition); + + long timeLeft = chunkLoader.getTimeLeft(); + + daysAmount = (short) (timeLeft / 86400); + timeLeft = timeLeft % 86400; + + hoursAmount = (short) (timeLeft / 3600); + timeLeft = timeLeft % 3600; + + minutesAmount = (short) (timeLeft / 60); + timeLeft = timeLeft % 60; + + secondsAmount = (short) timeLeft; + + tileEntityChunkLoaderMap.put(ChunkCoordIntPair.pair(blockPosition.getX() >> 4, blockPosition.getZ() >> 4), this); + + double currentY = position.getY() + 1; + for(int i = plugin.getSettings().hologramLines.size(); i > 0; i--){ + EntityHologram hologram = new EntityHologram(world, position.getX() + 0.5, currentY, position.getZ() + 0.5); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + world.addEntity(hologram); + currentY += 0.23; + holograms.add(hologram); + } + } + + @Override + public void w() { + tick(); + } + + @Override + public void tick() { + if(removed || ++currentTick <= 20) + return; + + currentTick = 0; + + if(((WChunkLoader) chunkLoader).isNotActive()){ + chunkLoader.remove(); + return; + } + + int hologramsAmount = holograms.size(); + for (int i = hologramsAmount; i > 0; i--) { + EntityHologram hologram = holograms.get(hologramsAmount - i); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + } + + ((WChunkLoader) chunkLoader).tick(); + + if(!removed) { + secondsAmount--; + if (secondsAmount < 0) { + secondsAmount = 59; + minutesAmount--; + if (minutesAmount < 0) { + minutesAmount = 59; + hoursAmount--; + if (hoursAmount < 0) { + hoursAmount = 23; + daysAmount--; + } + } + } + } + } + + private void updateName(EntityHologram hologram, String line){ + assert chunkLoader.getWhoPlaced().getName() != null; + hologram.setCustomName(CraftChatMessage.fromStringOrNull(line + .replace("{0}", chunkLoader.getWhoPlaced().getName()) + .replace("{1}", daysAmount + "") + .replace("{2}", hoursAmount + "") + .replace("{3}", minutesAmount + "") + .replace("{4}", secondsAmount + "")) + ); + } + + } + + private static class EntityHologram extends EntityArmorStand { + + EntityHologram(World world, double x, double y, double z){ + super(EntityTypes.ARMOR_STAND, world); + setPosition(x, y, z); + setInvisible(true); + setSmall(true); + setArms(false); + setNoGravity(true); + setBasePlate(true); + setMarker(true); + setCustomNameVisible(true); + a(new AxisAlignedBB(0D, 0D, 0D, 0D, 0D, 0D)); + } + + } + +} diff --git a/v1_7_R3/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_7_R3.java b/v1_7_R3/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_7_R3.java new file mode 100644 index 0000000..0a4f7f2 --- /dev/null +++ b/v1_7_R3/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_7_R3.java @@ -0,0 +1,146 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.npc.DummyChannel; +import net.minecraft.server.v1_7_R3.DamageSource; +import net.minecraft.server.v1_7_R3.EntityPlayer; +import net.minecraft.server.v1_7_R3.EnumGamemode; +import net.minecraft.server.v1_7_R3.MinecraftServer; +import net.minecraft.server.v1_7_R3.NetworkManager; +import net.minecraft.server.v1_7_R3.Packet; +import net.minecraft.server.v1_7_R3.PacketPlayInBlockDig; +import net.minecraft.server.v1_7_R3.PacketPlayInBlockPlace; +import net.minecraft.server.v1_7_R3.PacketPlayInChat; +import net.minecraft.server.v1_7_R3.PacketPlayInFlying; +import net.minecraft.server.v1_7_R3.PacketPlayInHeldItemSlot; +import net.minecraft.server.v1_7_R3.PacketPlayInTransaction; +import net.minecraft.server.v1_7_R3.PacketPlayInUpdateSign; +import net.minecraft.server.v1_7_R3.PacketPlayInWindowClick; +import net.minecraft.server.v1_7_R3.PlayerConnection; +import net.minecraft.server.v1_7_R3.PlayerInteractManager; +import net.minecraft.server.v1_7_R3.WorldServer; +import net.minecraft.util.com.mojang.authlib.GameProfile; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_7_R3.CraftServer; +import org.bukkit.craftbukkit.v1_7_R3.CraftWorld; + +import java.lang.reflect.Field; +import java.util.UUID; + +public final class ChunkLoaderNPC_v1_7_R3 extends EntityPlayer implements ChunkLoaderNPC { + + public ChunkLoaderNPC_v1_7_R3(Location location, UUID uuid){ + super(((CraftServer) Bukkit.getServer()).getServer(), ((CraftWorld) location.getWorld()).getHandle(), + new GameProfile(uuid, "Loader-" + location.getWorld().getName()), new PlayerInteractManager(((CraftWorld) location.getWorld()).getHandle())); + + playerConnection = new DummyPlayerConnection(server, this); + + playerInteractManager.setGameMode(EnumGamemode.CREATIVE); + fallDistance = 0.0F; + + fauxSleeping = true; + + spawnIn(world); + setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + //noinspection unchecked + world.players.add(this); + ((WorldServer) world).getPlayerChunkMap().addPlayer(this); + } + + + @Override + public UUID getUniqueId() { + return super.getUniqueID(); + } + + @Override + public void die() { + super.die(); + } + + @Override + public Location getLocation() { + return getBukkitEntity().getLocation(); + } + + @Override + public boolean damageEntity(DamageSource damagesource, float f) { + return false; + } + + private static class DummyNetworkManager extends NetworkManager{ + + DummyNetworkManager(){ + super(false); + updateFields(); + } + + private void updateFields() { + try { + Field channelField = NetworkManager.class.getDeclaredField("m"); + channelField.setAccessible(true); + channelField.set(this, new DummyChannel()); + channelField.setAccessible(false); + + Field socketAddressField = NetworkManager.class.getDeclaredField("n"); + socketAddressField.setAccessible(true); + socketAddressField.set(this, null); + } + catch (NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException e) { + e.printStackTrace(); + } + } + + } + + private static class DummyPlayerConnection extends PlayerConnection { + + DummyPlayerConnection(MinecraftServer minecraftServer, EntityPlayer entityPlayer) { + super(minecraftServer, new DummyNetworkManager(), entityPlayer); + } + + public void a(PacketPlayInWindowClick packetPlayInWindowClick) { + + } + + public void a(PacketPlayInTransaction packetPlayInTransaction) { + + } + + public void a(PacketPlayInFlying packetPlayInFlying) { + + } + + public void a(PacketPlayInUpdateSign packetPlayInUpdateSign) { + + } + + public void a(PacketPlayInBlockDig packetPlayInBlockDig) { + + } + + public void a(PacketPlayInBlockPlace packetPlayInBlockPlace) { + + } + + public void disconnect(String s) { + + } + + public void a(PacketPlayInHeldItemSlot packetPlayInHeldItemSlot) { + + } + + public void a(PacketPlayInChat packetPlayInChat) { + + } + + public void sendPacket(Packet packet) { + + } + + } + +} diff --git a/v1_7_R3/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_7_R3.java b/v1_7_R3/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_7_R3.java new file mode 100644 index 0000000..4ac7c61 --- /dev/null +++ b/v1_7_R3/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_7_R3.java @@ -0,0 +1,207 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.loaders.WChunkLoader; +import net.minecraft.server.v1_7_R3.Block; +import net.minecraft.server.v1_7_R3.Chunk; +import net.minecraft.server.v1_7_R3.IUpdatePlayerListBox; +import net.minecraft.server.v1_7_R3.ItemStack; +import net.minecraft.server.v1_7_R3.NBTTagCompound; +import net.minecraft.server.v1_7_R3.NBTTagList; +import net.minecraft.server.v1_7_R3.NBTTagLong; +import net.minecraft.server.v1_7_R3.NBTTagString; +import net.minecraft.server.v1_7_R3.TileEntity; +import net.minecraft.server.v1_7_R3.TileEntityMobSpawner; +import net.minecraft.server.v1_7_R3.World; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_7_R3.CraftChunk; +import org.bukkit.craftbukkit.v1_7_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_7_R3.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_7_R3.util.LongHash; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@SuppressWarnings("unused") +public final class NMSAdapter_v1_7_R3 implements NMSAdapter { + + private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + + @Override + public String getTag(org.bukkit.inventory.ItemStack itemStack, String key, String def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + if(!tagCompound.hasKeyOfType(key, 8)) + return def; + + return tagCompound.getString(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, String value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + tagCompound.set(key, new NBTTagString(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public long getTag(org.bukkit.inventory.ItemStack itemStack, String key, long def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + if(!tagCompound.hasKeyOfType(key, 4)) + return def; + + return tagCompound.getLong(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, long value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + tagCompound.set(key, new NBTTagLong(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack itemStack, String texture) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + + NBTTagCompound nbtTagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + NBTTagCompound skullOwner = nbtTagCompound.hasKey("SkullOwner") ? nbtTagCompound.getCompound("SkullOwner") : new NBTTagCompound(); + + NBTTagCompound properties = new NBTTagCompound(); + + NBTTagList textures = new NBTTagList(); + NBTTagCompound signature = new NBTTagCompound(); + signature.setString("Value", texture); + textures.add(signature); + + properties.set("textures", textures); + + skullOwner.set("Properties", properties); + skullOwner.setString("Id", UUID.randomUUID().toString()); + + nbtTagCompound.set("SkullOwner", skullOwner); + + nmsItem.setTag(nbtTagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public ChunkLoaderNPC createNPC(Location location, UUID uuid) { + return new ChunkLoaderNPC_v1_7_R3(location, uuid); + } + + @Override + public void createLoader(ChunkLoader chunkLoader) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + int x = loaderLoc.getBlockX(), y = loaderLoc.getBlockY(), z = loaderLoc.getBlockZ(); + + TileEntityChunkLoader tileEntityChunkLoader = new TileEntityChunkLoader(chunkLoader, world, x, y, z); + //noinspection unchecked + world.tileEntityList.add(tileEntityChunkLoader); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + //noinspection unchecked + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner).forEach(tileEntity -> { + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + ((TileEntity) tileEntity).b(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) -1); + ((TileEntity) tileEntity).a(nbtTagCompound); + }); + } + + @Override + public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + int x = loaderLoc.getBlockX(), y = loaderLoc.getBlockY(), z = loaderLoc.getBlockZ(); + + long tileEntityLong = LongHash.toLong(x >> 4, z >> 4); + TileEntityChunkLoader tileEntityChunkLoader = TileEntityChunkLoader.tileEntityChunkLoaderMap.remove(tileEntityLong); + if(tileEntityChunkLoader != null) { + tileEntityChunkLoader.removed = true; + world.tileEntityList.remove(tileEntityChunkLoader); + } + + if(spawnParticle) + world.a(null, 2001, x, y, z, Block.b(world.getType(x, y, z)) + (world.getData(x, y, z) << 12)); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + //noinspection unchecked + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner).forEach(tileEntity -> { + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + ((TileEntity) tileEntity).b(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) 16); + ((TileEntity) tileEntity).a(nbtTagCompound); + }); + } + + @Override + public void updateSpawner(Location location, boolean reset) { + World world = ((CraftWorld) location.getWorld()).getHandle(); + + TileEntityMobSpawner mobSpawner = (TileEntityMobSpawner) + world.getTileEntity(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + mobSpawner.b(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) (reset ? 16 : -1)); + mobSpawner.a(nbtTagCompound); + } + + private static final class TileEntityChunkLoader extends TileEntity implements IUpdatePlayerListBox { + + private static final Map tileEntityChunkLoaderMap = new HashMap<>(); + + private final ChunkLoader chunkLoader; + + private short currentTick = 20; + private boolean removed = false; + + TileEntityChunkLoader(ChunkLoader chunkLoader, World world, int x, int y, int z){ + this.chunkLoader = chunkLoader; + + this.x = x; + this.y = y; + this.z = z; + a(world); + + tileEntityChunkLoaderMap.put(LongHash.toLong(x >> 4, z >> 4), this); + } + + @Override + public void a() { + if(removed || ++currentTick <= 20) + return; + + currentTick = 0; + + if(((WChunkLoader) chunkLoader).isNotActive()){ + chunkLoader.remove(); + return; + } + + ((WChunkLoader) chunkLoader).tick(); + } + + } + +} diff --git a/v1_7_R4/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_7_R4.java b/v1_7_R4/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_7_R4.java new file mode 100644 index 0000000..3f11214 --- /dev/null +++ b/v1_7_R4/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_7_R4.java @@ -0,0 +1,145 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.npc.DummyChannel; +import net.minecraft.server.v1_7_R4.DamageSource; +import net.minecraft.server.v1_7_R4.EntityPlayer; +import net.minecraft.server.v1_7_R4.EnumGamemode; +import net.minecraft.server.v1_7_R4.MinecraftServer; +import net.minecraft.server.v1_7_R4.NetworkManager; +import net.minecraft.server.v1_7_R4.Packet; +import net.minecraft.server.v1_7_R4.PacketPlayInBlockDig; +import net.minecraft.server.v1_7_R4.PacketPlayInBlockPlace; +import net.minecraft.server.v1_7_R4.PacketPlayInChat; +import net.minecraft.server.v1_7_R4.PacketPlayInFlying; +import net.minecraft.server.v1_7_R4.PacketPlayInHeldItemSlot; +import net.minecraft.server.v1_7_R4.PacketPlayInTransaction; +import net.minecraft.server.v1_7_R4.PacketPlayInUpdateSign; +import net.minecraft.server.v1_7_R4.PacketPlayInWindowClick; +import net.minecraft.server.v1_7_R4.PlayerConnection; +import net.minecraft.server.v1_7_R4.PlayerInteractManager; +import net.minecraft.server.v1_7_R4.WorldServer; +import net.minecraft.util.com.mojang.authlib.GameProfile; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_7_R4.CraftServer; +import org.bukkit.craftbukkit.v1_7_R4.CraftWorld; + +import java.lang.reflect.Field; +import java.util.UUID; + +public final class ChunkLoaderNPC_v1_7_R4 extends EntityPlayer implements ChunkLoaderNPC { + + public ChunkLoaderNPC_v1_7_R4(Location location, UUID uuid){ + super(((CraftServer) Bukkit.getServer()).getServer(), ((CraftWorld) location.getWorld()).getHandle(), + new GameProfile(uuid, "Loader-" + location.getWorld().getName()), new PlayerInteractManager(((CraftWorld) location.getWorld()).getHandle())); + + playerConnection = new DummyPlayerConnection(server, this); + + playerInteractManager.setGameMode(EnumGamemode.CREATIVE); + fallDistance = 0.0F; + + fauxSleeping = true; + + spawnIn(world); + setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + //noinspection unchecked + world.players.add(this); + ((WorldServer) world).getPlayerChunkMap().addPlayer(this); + } + + @Override + public UUID getUniqueId() { + return super.getUniqueID(); + } + + @Override + public void die() { + super.die(); + } + + @Override + public Location getLocation() { + return getBukkitEntity().getLocation(); + } + + @Override + public boolean damageEntity(DamageSource damagesource, float f) { + return false; + } + + private static class DummyNetworkManager extends NetworkManager { + + DummyNetworkManager(){ + super(false); + updateFields(); + } + + private void updateFields() { + try { + Field channelField = NetworkManager.class.getDeclaredField("m"); + channelField.setAccessible(true); + channelField.set(this, new DummyChannel()); + channelField.setAccessible(false); + + Field socketAddressField = NetworkManager.class.getDeclaredField("n"); + socketAddressField.setAccessible(true); + socketAddressField.set(this, null); + } + catch (NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException e) { + e.printStackTrace(); + } + } + + } + + private static class DummyPlayerConnection extends PlayerConnection { + + DummyPlayerConnection(MinecraftServer minecraftServer, EntityPlayer entityPlayer) { + super(minecraftServer, new DummyNetworkManager(), entityPlayer); + } + + public void a(PacketPlayInWindowClick packetPlayInWindowClick) { + + } + + public void a(PacketPlayInTransaction packetPlayInTransaction) { + + } + + public void a(PacketPlayInFlying packetPlayInFlying) { + + } + + public void a(PacketPlayInUpdateSign packetPlayInUpdateSign) { + + } + + public void a(PacketPlayInBlockDig packetPlayInBlockDig) { + + } + + public void a(PacketPlayInBlockPlace packetPlayInBlockPlace) { + + } + + public void disconnect(String s) { + + } + + public void a(PacketPlayInHeldItemSlot packetPlayInHeldItemSlot) { + + } + + public void a(PacketPlayInChat packetPlayInChat) { + + } + + public void sendPacket(Packet packet) { + + } + + } + +} diff --git a/v1_7_R4/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_7_R4.java b/v1_7_R4/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_7_R4.java new file mode 100644 index 0000000..d9512cc --- /dev/null +++ b/v1_7_R4/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_7_R4.java @@ -0,0 +1,207 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.loaders.WChunkLoader; +import net.minecraft.server.v1_7_R4.Block; +import net.minecraft.server.v1_7_R4.Chunk; +import net.minecraft.server.v1_7_R4.IUpdatePlayerListBox; +import net.minecraft.server.v1_7_R4.ItemStack; +import net.minecraft.server.v1_7_R4.NBTTagCompound; +import net.minecraft.server.v1_7_R4.NBTTagList; +import net.minecraft.server.v1_7_R4.NBTTagLong; +import net.minecraft.server.v1_7_R4.NBTTagString; +import net.minecraft.server.v1_7_R4.TileEntity; +import net.minecraft.server.v1_7_R4.TileEntityMobSpawner; +import net.minecraft.server.v1_7_R4.World; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_7_R4.CraftChunk; +import org.bukkit.craftbukkit.v1_7_R4.CraftWorld; +import org.bukkit.craftbukkit.v1_7_R4.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_7_R4.util.LongHash; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +@SuppressWarnings("unused") +public final class NMSAdapter_v1_7_R4 implements NMSAdapter { + + private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + + @Override + public String getTag(org.bukkit.inventory.ItemStack itemStack, String key, String def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + if(!tagCompound.hasKeyOfType(key, 8)) + return def; + + return tagCompound.getString(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, String value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + tagCompound.set(key, new NBTTagString(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public long getTag(org.bukkit.inventory.ItemStack itemStack, String key, long def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + if(!tagCompound.hasKeyOfType(key, 4)) + return def; + + return tagCompound.getLong(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, long value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + tagCompound.set(key, new NBTTagLong(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack itemStack, String texture) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + + NBTTagCompound nbtTagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + NBTTagCompound skullOwner = nbtTagCompound.hasKey("SkullOwner") ? nbtTagCompound.getCompound("SkullOwner") : new NBTTagCompound(); + + NBTTagCompound properties = new NBTTagCompound(); + + NBTTagList textures = new NBTTagList(); + NBTTagCompound signature = new NBTTagCompound(); + signature.setString("Value", texture); + textures.add(signature); + + properties.set("textures", textures); + + skullOwner.set("Properties", properties); + skullOwner.setString("Id", UUID.randomUUID().toString()); + + nbtTagCompound.set("SkullOwner", skullOwner); + + nmsItem.setTag(nbtTagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public ChunkLoaderNPC createNPC(Location location, UUID uuid) { + return new ChunkLoaderNPC_v1_7_R4(location, uuid); + } + + @Override + public void createLoader(ChunkLoader chunkLoader) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + int x = loaderLoc.getBlockX(), y = loaderLoc.getBlockY(), z = loaderLoc.getBlockZ(); + + TileEntityChunkLoader tileEntityChunkLoader = new TileEntityChunkLoader(chunkLoader, world, x, y, z); + //noinspection unchecked + world.tileEntityList.add(tileEntityChunkLoader); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + //noinspection unchecked + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner).forEach(tileEntity -> { + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + ((TileEntity) tileEntity).b(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) -1); + ((TileEntity) tileEntity).a(nbtTagCompound); + }); + } + + @Override + public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + int x = loaderLoc.getBlockX(), y = loaderLoc.getBlockY(), z = loaderLoc.getBlockZ(); + + long tileEntityLong = LongHash.toLong(x >> 4, z >> 4); + TileEntityChunkLoader tileEntityChunkLoader = TileEntityChunkLoader.tileEntityChunkLoaderMap.remove(tileEntityLong); + if(tileEntityChunkLoader != null) { + tileEntityChunkLoader.removed = true; + world.tileEntityList.remove(tileEntityChunkLoader); + } + + if(spawnParticle) + world.a(null, 2001, x, y, z, Block.getId(world.getType(x, y, z)) + (world.getData(x, y, z) << 12)); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + //noinspection unchecked + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner).forEach(tileEntity -> { + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + ((TileEntity) tileEntity).b(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) 16); + ((TileEntity) tileEntity).a(nbtTagCompound); + }); + } + + @Override + public void updateSpawner(Location location, boolean reset) { + World world = ((CraftWorld) location.getWorld()).getHandle(); + + TileEntityMobSpawner mobSpawner = (TileEntityMobSpawner) + world.getTileEntity(location.getBlockX(), location.getBlockY(), location.getBlockZ()); + + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + mobSpawner.b(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) (reset ? 16 : -1)); + mobSpawner.a(nbtTagCompound); + } + + private static final class TileEntityChunkLoader extends TileEntity implements IUpdatePlayerListBox { + + private static final Map tileEntityChunkLoaderMap = new HashMap<>(); + + private final ChunkLoader chunkLoader; + + private short currentTick = 20; + private boolean removed = false; + + TileEntityChunkLoader(ChunkLoader chunkLoader, World world, int x, int y, int z){ + this.chunkLoader = chunkLoader; + + this.x = x; + this.y = y; + this.z = z; + a(world); + + tileEntityChunkLoaderMap.put(LongHash.toLong(x >> 4, z >> 4), this); + } + + @Override + public void a() { + if(removed || ++currentTick <= 20) + return; + + currentTick = 0; + + if(((WChunkLoader) chunkLoader).isNotActive()){ + chunkLoader.remove(); + return; + } + + ((WChunkLoader) chunkLoader).tick(); + } + + } + +} diff --git a/v1_8_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_8_R1.java b/v1_8_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_8_R1.java new file mode 100644 index 0000000..3a00e3e --- /dev/null +++ b/v1_8_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_8_R1.java @@ -0,0 +1,146 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.npc.DummyChannel; +import com.mojang.authlib.GameProfile; +import net.minecraft.server.v1_8_R1.DamageSource; +import net.minecraft.server.v1_8_R1.EntityPlayer; +import net.minecraft.server.v1_8_R1.EnumGamemode; +import net.minecraft.server.v1_8_R1.EnumProtocolDirection; +import net.minecraft.server.v1_8_R1.MinecraftServer; +import net.minecraft.server.v1_8_R1.NetworkManager; +import net.minecraft.server.v1_8_R1.Packet; +import net.minecraft.server.v1_8_R1.PacketPlayInBlockDig; +import net.minecraft.server.v1_8_R1.PacketPlayInBlockPlace; +import net.minecraft.server.v1_8_R1.PacketPlayInChat; +import net.minecraft.server.v1_8_R1.PacketPlayInFlying; +import net.minecraft.server.v1_8_R1.PacketPlayInHeldItemSlot; +import net.minecraft.server.v1_8_R1.PacketPlayInTransaction; +import net.minecraft.server.v1_8_R1.PacketPlayInUpdateSign; +import net.minecraft.server.v1_8_R1.PacketPlayInWindowClick; +import net.minecraft.server.v1_8_R1.PlayerConnection; +import net.minecraft.server.v1_8_R1.PlayerInteractManager; +import net.minecraft.server.v1_8_R1.WorldServer; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_8_R1.CraftServer; +import org.bukkit.craftbukkit.v1_8_R1.CraftWorld; + +import java.lang.reflect.Field; +import java.util.UUID; + +public final class ChunkLoaderNPC_v1_8_R1 extends EntityPlayer implements ChunkLoaderNPC { + + public ChunkLoaderNPC_v1_8_R1(Location location, UUID uuid){ + super(((CraftServer) Bukkit.getServer()).getServer(), ((CraftWorld) location.getWorld()).getHandle(), + new GameProfile(uuid, "Loader-" + location.getWorld().getName()), new PlayerInteractManager(((CraftWorld) location.getWorld()).getHandle())); + + playerConnection = new DummyPlayerConnection(server, this); + + playerInteractManager.setGameMode(EnumGamemode.CREATIVE); + fallDistance = 0.0F; + + fauxSleeping = true; + + spawnIn(world); + setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + //noinspection unchecked + world.players.add(this); + ((WorldServer) world).getPlayerChunkMap().addPlayer(this); + } + + @Override + public UUID getUniqueId() { + return super.getUniqueID(); + } + + @Override + public void die() { + super.die(); + } + + @Override + public Location getLocation() { + return getBukkitEntity().getLocation(); + } + + @Override + public boolean damageEntity(DamageSource damagesource, float f) { + return false; + } + + private static class DummyNetworkManager extends NetworkManager{ + + DummyNetworkManager(){ + super(EnumProtocolDirection.SERVERBOUND); + updateFields(); + } + + private void updateFields() { + try { + Field channelField = NetworkManager.class.getDeclaredField("i"); + channelField.setAccessible(true); + channelField.set(this, new DummyChannel()); + channelField.setAccessible(false); + + Field socketAddressField = NetworkManager.class.getDeclaredField("j"); + socketAddressField.setAccessible(true); + socketAddressField.set(this, null); + } + catch (NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException e) { + e.printStackTrace(); + } + } + + } + + private static class DummyPlayerConnection extends PlayerConnection { + + DummyPlayerConnection(MinecraftServer minecraftServer, EntityPlayer entityPlayer) { + super(minecraftServer, new DummyNetworkManager(), entityPlayer); + } + + public void a(PacketPlayInWindowClick packetPlayInWindowClick) { + + } + + public void a(PacketPlayInTransaction packetPlayInTransaction) { + + } + + public void a(PacketPlayInFlying packetPlayInFlying) { + + } + + public void a(PacketPlayInUpdateSign packetPlayInUpdateSign) { + + } + + public void a(PacketPlayInBlockDig packetPlayInBlockDig) { + + } + + public void a(PacketPlayInBlockPlace packetPlayInBlockPlace) { + + } + + public void disconnect(String s) { + + } + + public void a(PacketPlayInHeldItemSlot packetPlayInHeldItemSlot) { + + } + + public void a(PacketPlayInChat packetPlayInChat) { + + } + + public void sendPacket(Packet packet) { + + } + + } + +} diff --git a/v1_8_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_8_R1.java b/v1_8_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_8_R1.java new file mode 100644 index 0000000..4a5d8af --- /dev/null +++ b/v1_8_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_8_R1.java @@ -0,0 +1,284 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.loaders.WChunkLoader; +import net.minecraft.server.v1_8_R1.AxisAlignedBB; +import net.minecraft.server.v1_8_R1.Block; +import net.minecraft.server.v1_8_R1.BlockPosition; +import net.minecraft.server.v1_8_R1.Chunk; +import net.minecraft.server.v1_8_R1.Entity; +import net.minecraft.server.v1_8_R1.EntityArmorStand; +import net.minecraft.server.v1_8_R1.IUpdatePlayerListBox; +import net.minecraft.server.v1_8_R1.ItemStack; +import net.minecraft.server.v1_8_R1.NBTTagCompound; +import net.minecraft.server.v1_8_R1.NBTTagList; +import net.minecraft.server.v1_8_R1.NBTTagLong; +import net.minecraft.server.v1_8_R1.NBTTagString; +import net.minecraft.server.v1_8_R1.TileEntity; +import net.minecraft.server.v1_8_R1.TileEntityMobSpawner; +import net.minecraft.server.v1_8_R1.World; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_8_R1.CraftChunk; +import org.bukkit.craftbukkit.v1_8_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_8_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_8_R1.util.LongHash; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@SuppressWarnings("unused") +public final class NMSAdapter_v1_8_R1 implements NMSAdapter { + + private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + + @Override + public String getTag(org.bukkit.inventory.ItemStack itemStack, String key, String def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + if(!tagCompound.hasKeyOfType(key, 8)) + return def; + + return tagCompound.getString(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, String value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + tagCompound.set(key, new NBTTagString(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public long getTag(org.bukkit.inventory.ItemStack itemStack, String key, long def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + if(!tagCompound.hasKeyOfType(key, 4)) + return def; + + return tagCompound.getLong(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, long value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + tagCompound.set(key, new NBTTagLong(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack itemStack, String texture) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + + NBTTagCompound nbtTagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + NBTTagCompound skullOwner = nbtTagCompound.hasKey("SkullOwner") ? nbtTagCompound.getCompound("SkullOwner") : new NBTTagCompound(); + + NBTTagCompound properties = new NBTTagCompound(); + + NBTTagList textures = new NBTTagList(); + NBTTagCompound signature = new NBTTagCompound(); + signature.setString("Value", texture); + textures.add(signature); + + properties.set("textures", textures); + + skullOwner.set("Properties", properties); + skullOwner.setString("Id", UUID.randomUUID().toString()); + + nbtTagCompound.set("SkullOwner", skullOwner); + + nmsItem.setTag(nbtTagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public ChunkLoaderNPC createNPC(Location location, UUID uuid) { + return new ChunkLoaderNPC_v1_8_R1(location, uuid); + } + + @Override + public void createLoader(ChunkLoader chunkLoader) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + TileEntityChunkLoader tileEntityChunkLoader = new TileEntityChunkLoader(chunkLoader, world, blockPosition); + //noinspection unchecked + world.tileEntityList.add(tileEntityChunkLoader); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + //noinspection unchecked + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner).forEach(tileEntity -> { + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + ((TileEntity) tileEntity).b(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) -1); + ((TileEntity) tileEntity).a(nbtTagCompound); + }); + } + + @Override + public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + long tileEntityLong = LongHash.toLong(blockPosition.getX() >> 4, blockPosition.getZ() >> 4); + TileEntityChunkLoader tileEntityChunkLoader = TileEntityChunkLoader.tileEntityChunkLoaderMap.remove(tileEntityLong); + if(tileEntityChunkLoader != null) { + tileEntityChunkLoader.holograms.forEach(Entity::die); + tileEntityChunkLoader.removed = true; + world.tileEntityList.remove(tileEntityChunkLoader); + } + + if(spawnParticle) + world.a(null, 2001, blockPosition, Block.getCombinedId(world.getType(blockPosition))); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + //noinspection unchecked + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner).forEach(tileEntity -> { + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + ((TileEntity) tileEntity).b(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) 16); + ((TileEntity) tileEntity).a(nbtTagCompound); + }); + } + + @Override + public void updateSpawner(Location location, boolean reset) { + World world = ((CraftWorld) location.getWorld()).getHandle(); + + BlockPosition blockPosition = new BlockPosition(location.getX(), location.getY(), location.getZ()); + TileEntityMobSpawner mobSpawner = (TileEntityMobSpawner) world.getTileEntity(blockPosition); + + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + mobSpawner.b(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) (reset ? 16 : -1)); + mobSpawner.a(nbtTagCompound); + } + + private static final class TileEntityChunkLoader extends TileEntity implements IUpdatePlayerListBox { + + private static final Map tileEntityChunkLoaderMap = new HashMap<>(); + + private final List holograms = new ArrayList<>(); + private final ChunkLoader chunkLoader; + + private short currentTick = 20; + private short daysAmount, hoursAmount, minutesAmount, secondsAmount; + private boolean removed = false; + + TileEntityChunkLoader(ChunkLoader chunkLoader, World world, BlockPosition blockPosition){ + this.chunkLoader = chunkLoader; + + a(blockPosition); + a(world); + + long timeLeft = chunkLoader.getTimeLeft(); + + daysAmount = (short) (timeLeft / 86400); + timeLeft = timeLeft % 86400; + + hoursAmount = (short) (timeLeft / 3600); + timeLeft = timeLeft % 3600; + + minutesAmount = (short) (timeLeft / 60); + timeLeft = timeLeft % 60; + + secondsAmount = (short) timeLeft; + + tileEntityChunkLoaderMap.put(LongHash.toLong(blockPosition.getX() >> 4, blockPosition.getZ() >> 4), this); + + double currentY = position.getY() + 1; + for(int i = plugin.getSettings().hologramLines.size(); i > 0; i--){ + EntityHologram hologram = new EntityHologram(world, position.getX() + 0.5, currentY, position.getZ() + 0.5); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + world.addEntity(hologram); + currentY += 0.23; + holograms.add(hologram); + } + } + + @Override + public void c() { + if(removed || ++currentTick <= 20) + return; + + currentTick = 0; + + if(((WChunkLoader) chunkLoader).isNotActive()){ + chunkLoader.remove(); + return; + } + + int hologramsAmount = holograms.size(); + for (int i = hologramsAmount; i > 0; i--) { + EntityHologram hologram = holograms.get(hologramsAmount - i); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + } + + ((WChunkLoader) chunkLoader).tick(); + + if(!removed) { + secondsAmount--; + if (secondsAmount < 0) { + secondsAmount = 59; + minutesAmount--; + if (minutesAmount < 0) { + minutesAmount = 59; + hoursAmount--; + if (hoursAmount < 0) { + hoursAmount = 23; + daysAmount--; + } + } + } + } + } + + private void updateName(EntityHologram hologram, String line){ + hologram.setCustomName(line + .replace("{0}", chunkLoader.getWhoPlaced().getName()) + .replace("{1}", daysAmount + "") + .replace("{2}", hoursAmount + "") + .replace("{3}", minutesAmount + "") + .replace("{4}", secondsAmount + "") + ); + } + + } + + private static class EntityHologram extends EntityArmorStand { + + EntityHologram(World world, double x, double y, double z){ + super(world); + setPosition(x, y, z); + setInvisible(true); + setSmall(true); + setArms(false); + setGravity(false); + setBasePlate(true); + setCustomNameVisible(true); + a(new AxisAlignedBB(0D, 0D, 0D, 0D, 0D, 0D)); + } + + } + +} diff --git a/v1_8_R2/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_8_R2.java b/v1_8_R2/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_8_R2.java new file mode 100644 index 0000000..cc4f8ac --- /dev/null +++ b/v1_8_R2/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_8_R2.java @@ -0,0 +1,145 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.npc.DummyChannel; +import com.mojang.authlib.GameProfile; +import net.minecraft.server.v1_8_R2.DamageSource; +import net.minecraft.server.v1_8_R2.EntityPlayer; +import net.minecraft.server.v1_8_R2.EnumProtocolDirection; +import net.minecraft.server.v1_8_R2.MinecraftServer; +import net.minecraft.server.v1_8_R2.NetworkManager; +import net.minecraft.server.v1_8_R2.Packet; +import net.minecraft.server.v1_8_R2.PacketPlayInBlockDig; +import net.minecraft.server.v1_8_R2.PacketPlayInBlockPlace; +import net.minecraft.server.v1_8_R2.PacketPlayInChat; +import net.minecraft.server.v1_8_R2.PacketPlayInFlying; +import net.minecraft.server.v1_8_R2.PacketPlayInHeldItemSlot; +import net.minecraft.server.v1_8_R2.PacketPlayInTransaction; +import net.minecraft.server.v1_8_R2.PacketPlayInUpdateSign; +import net.minecraft.server.v1_8_R2.PacketPlayInWindowClick; +import net.minecraft.server.v1_8_R2.PlayerConnection; +import net.minecraft.server.v1_8_R2.PlayerInteractManager; +import net.minecraft.server.v1_8_R2.WorldServer; +import net.minecraft.server.v1_8_R2.WorldSettings; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_8_R2.CraftServer; +import org.bukkit.craftbukkit.v1_8_R2.CraftWorld; + +import java.lang.reflect.Field; +import java.util.UUID; + +public final class ChunkLoaderNPC_v1_8_R2 extends EntityPlayer implements ChunkLoaderNPC { + + public ChunkLoaderNPC_v1_8_R2(Location location, UUID uuid){ + super(((CraftServer) Bukkit.getServer()).getServer(), ((CraftWorld) location.getWorld()).getHandle(), + new GameProfile(uuid, "Loader-" + location.getWorld().getName()), new PlayerInteractManager(((CraftWorld) location.getWorld()).getHandle())); + + playerConnection = new DummyPlayerConnection(server, this); + + playerInteractManager.setGameMode(WorldSettings.EnumGamemode.CREATIVE); + fallDistance = 0.0F; + + fauxSleeping = true; + + spawnIn(world); + setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + world.players.add(this); + ((WorldServer) world).getPlayerChunkMap().addPlayer(this); + } + + @Override + public UUID getUniqueId() { + return super.getUniqueID(); + } + + @Override + public void die() { + super.die(); + } + + @Override + public Location getLocation() { + return getBukkitEntity().getLocation(); + } + + @Override + public boolean damageEntity(DamageSource damagesource, float f) { + return false; + } + + private static class DummyNetworkManager extends NetworkManager{ + + DummyNetworkManager(){ + super(EnumProtocolDirection.SERVERBOUND); + updateFields(); + } + + private void updateFields() { + try { + Field channelField = NetworkManager.class.getDeclaredField("k"); + channelField.setAccessible(true); + channelField.set(this, new DummyChannel()); + channelField.setAccessible(false); + + Field socketAddressField = NetworkManager.class.getDeclaredField("l"); + socketAddressField.setAccessible(true); + socketAddressField.set(this, null); + } + catch (NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException e) { + e.printStackTrace(); + } + } + + } + + private static class DummyPlayerConnection extends PlayerConnection { + + DummyPlayerConnection(MinecraftServer minecraftServer, EntityPlayer entityPlayer) { + super(minecraftServer, new DummyNetworkManager(), entityPlayer); + } + + public void a(PacketPlayInWindowClick packetPlayInWindowClick) { + + } + + public void a(PacketPlayInTransaction packetPlayInTransaction) { + + } + + public void a(PacketPlayInFlying packetPlayInFlying) { + + } + + public void a(PacketPlayInUpdateSign packetPlayInUpdateSign) { + + } + + public void a(PacketPlayInBlockDig packetPlayInBlockDig) { + + } + + public void a(PacketPlayInBlockPlace packetPlayInBlockPlace) { + + } + + public void disconnect(String s) { + + } + + public void a(PacketPlayInHeldItemSlot packetPlayInHeldItemSlot) { + + } + + public void a(PacketPlayInChat packetPlayInChat) { + + } + + public void sendPacket(Packet packet) { + + } + + } + +} diff --git a/v1_8_R2/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_8_R2.java b/v1_8_R2/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_8_R2.java new file mode 100644 index 0000000..95fd847 --- /dev/null +++ b/v1_8_R2/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_8_R2.java @@ -0,0 +1,281 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.loaders.WChunkLoader; +import net.minecraft.server.v1_8_R2.AxisAlignedBB; +import net.minecraft.server.v1_8_R2.Block; +import net.minecraft.server.v1_8_R2.BlockPosition; +import net.minecraft.server.v1_8_R2.Chunk; +import net.minecraft.server.v1_8_R2.Entity; +import net.minecraft.server.v1_8_R2.EntityArmorStand; +import net.minecraft.server.v1_8_R2.IUpdatePlayerListBox; +import net.minecraft.server.v1_8_R2.ItemStack; +import net.minecraft.server.v1_8_R2.NBTTagCompound; +import net.minecraft.server.v1_8_R2.NBTTagList; +import net.minecraft.server.v1_8_R2.NBTTagLong; +import net.minecraft.server.v1_8_R2.NBTTagString; +import net.minecraft.server.v1_8_R2.TileEntity; +import net.minecraft.server.v1_8_R2.TileEntityMobSpawner; +import net.minecraft.server.v1_8_R2.World; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_8_R2.CraftChunk; +import org.bukkit.craftbukkit.v1_8_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_8_R2.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_8_R2.util.LongHash; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@SuppressWarnings("unused") +public final class NMSAdapter_v1_8_R2 implements NMSAdapter { + + private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + + @Override + public String getTag(org.bukkit.inventory.ItemStack itemStack, String key, String def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + if(!tagCompound.hasKeyOfType(key, 8)) + return def; + + return tagCompound.getString(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, String value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + tagCompound.set(key, new NBTTagString(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public long getTag(org.bukkit.inventory.ItemStack itemStack, String key, long def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + if(!tagCompound.hasKeyOfType(key, 4)) + return def; + + return tagCompound.getLong(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, long value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + tagCompound.set(key, new NBTTagLong(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack itemStack, String texture) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + + NBTTagCompound nbtTagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + NBTTagCompound skullOwner = nbtTagCompound.hasKey("SkullOwner") ? nbtTagCompound.getCompound("SkullOwner") : new NBTTagCompound(); + + NBTTagCompound properties = new NBTTagCompound(); + + NBTTagList textures = new NBTTagList(); + NBTTagCompound signature = new NBTTagCompound(); + signature.setString("Value", texture); + textures.add(signature); + + properties.set("textures", textures); + + skullOwner.set("Properties", properties); + skullOwner.setString("Id", UUID.randomUUID().toString()); + + nbtTagCompound.set("SkullOwner", skullOwner); + + nmsItem.setTag(nbtTagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public ChunkLoaderNPC createNPC(Location location, UUID uuid) { + return new ChunkLoaderNPC_v1_8_R2(location, uuid); + } + + @Override + public void createLoader(ChunkLoader chunkLoader) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + TileEntityChunkLoader tileEntityChunkLoader = new TileEntityChunkLoader(chunkLoader, world, blockPosition); + world.tileEntityList.add(tileEntityChunkLoader); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner).forEach(tileEntity -> { + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + tileEntity.b(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) -1); + tileEntity.a(nbtTagCompound); + }); + } + + @Override + public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + long tileEntityLong = LongHash.toLong(blockPosition.getX() >> 4, blockPosition.getZ() >> 4); + TileEntityChunkLoader tileEntityChunkLoader = TileEntityChunkLoader.tileEntityChunkLoaderMap.remove(tileEntityLong); + if(tileEntityChunkLoader != null) { + tileEntityChunkLoader.holograms.forEach(Entity::die); + tileEntityChunkLoader.removed = true; + world.tileEntityList.remove(tileEntityChunkLoader); + } + + if(spawnParticle) + world.a(null, 2001, blockPosition, Block.getCombinedId(world.getType(blockPosition))); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner).forEach(tileEntity -> { + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + tileEntity.b(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) 16); + tileEntity.a(nbtTagCompound); + }); + } + + @Override + public void updateSpawner(Location location, boolean reset) { + World world = ((CraftWorld) location.getWorld()).getHandle(); + + BlockPosition blockPosition = new BlockPosition(location.getX(), location.getY(), location.getZ()); + TileEntityMobSpawner mobSpawner = (TileEntityMobSpawner) world.getTileEntity(blockPosition); + + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + mobSpawner.b(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) (reset ? 16 : -1)); + mobSpawner.a(nbtTagCompound); + } + + private static final class TileEntityChunkLoader extends TileEntity implements IUpdatePlayerListBox { + + private static final Map tileEntityChunkLoaderMap = new HashMap<>(); + + private final List holograms = new ArrayList<>(); + private final ChunkLoader chunkLoader; + + private short currentTick = 20; + private short daysAmount, hoursAmount, minutesAmount, secondsAmount; + private boolean removed = false; + + TileEntityChunkLoader(ChunkLoader chunkLoader, World world, BlockPosition blockPosition){ + this.chunkLoader = chunkLoader; + + a(blockPosition); + a(world); + + long timeLeft = chunkLoader.getTimeLeft(); + + daysAmount = (short) (timeLeft / 86400); + timeLeft = timeLeft % 86400; + + hoursAmount = (short) (timeLeft / 3600); + timeLeft = timeLeft % 3600; + + minutesAmount = (short) (timeLeft / 60); + timeLeft = timeLeft % 60; + + secondsAmount = (short) timeLeft; + + tileEntityChunkLoaderMap.put(LongHash.toLong(blockPosition.getX() >> 4, blockPosition.getZ() >> 4), this); + + double currentY = position.getY() + 1; + for(int i = plugin.getSettings().hologramLines.size(); i > 0; i--){ + EntityHologram hologram = new EntityHologram(world, position.getX() + 0.5, currentY, position.getZ() + 0.5); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + world.addEntity(hologram); + currentY += 0.23; + holograms.add(hologram); + } + } + + @Override + public void c() { + if(removed || ++currentTick <= 20) + return; + + currentTick = 0; + + if(((WChunkLoader) chunkLoader).isNotActive()){ + chunkLoader.remove(); + return; + } + + int hologramsAmount = holograms.size(); + for (int i = hologramsAmount; i > 0; i--) { + EntityHologram hologram = holograms.get(hologramsAmount - i); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + } + + ((WChunkLoader) chunkLoader).tick(); + + if(!removed) { + secondsAmount--; + if (secondsAmount < 0) { + secondsAmount = 59; + minutesAmount--; + if (minutesAmount < 0) { + minutesAmount = 59; + hoursAmount--; + if (hoursAmount < 0) { + hoursAmount = 23; + daysAmount--; + } + } + } + } + } + + private void updateName(EntityHologram hologram, String line){ + hologram.setCustomName(line + .replace("{0}", chunkLoader.getWhoPlaced().getName()) + .replace("{1}", daysAmount + "") + .replace("{2}", hoursAmount + "") + .replace("{3}", minutesAmount + "") + .replace("{4}", secondsAmount + "") + ); + } + + } + + private static class EntityHologram extends EntityArmorStand { + + EntityHologram(World world, double x, double y, double z){ + super(world); + setPosition(x, y, z); + setInvisible(true); + setSmall(true); + setArms(false); + setGravity(false); + setBasePlate(true); + setCustomNameVisible(true); + a(new AxisAlignedBB(0D, 0D, 0D, 0D, 0D, 0D)); + } + + } + +} diff --git a/v1_8_R3/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_8_R3.java b/v1_8_R3/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_8_R3.java new file mode 100644 index 0000000..f1bb21d --- /dev/null +++ b/v1_8_R3/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_8_R3.java @@ -0,0 +1,145 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.npc.DummyChannel; +import com.mojang.authlib.GameProfile; +import net.minecraft.server.v1_8_R3.DamageSource; +import net.minecraft.server.v1_8_R3.EntityPlayer; +import net.minecraft.server.v1_8_R3.EnumProtocolDirection; +import net.minecraft.server.v1_8_R3.MinecraftServer; +import net.minecraft.server.v1_8_R3.NetworkManager; +import net.minecraft.server.v1_8_R3.Packet; +import net.minecraft.server.v1_8_R3.PacketPlayInBlockDig; +import net.minecraft.server.v1_8_R3.PacketPlayInBlockPlace; +import net.minecraft.server.v1_8_R3.PacketPlayInChat; +import net.minecraft.server.v1_8_R3.PacketPlayInFlying; +import net.minecraft.server.v1_8_R3.PacketPlayInHeldItemSlot; +import net.minecraft.server.v1_8_R3.PacketPlayInTransaction; +import net.minecraft.server.v1_8_R3.PacketPlayInUpdateSign; +import net.minecraft.server.v1_8_R3.PacketPlayInWindowClick; +import net.minecraft.server.v1_8_R3.PlayerConnection; +import net.minecraft.server.v1_8_R3.PlayerInteractManager; +import net.minecraft.server.v1_8_R3.WorldServer; +import net.minecraft.server.v1_8_R3.WorldSettings; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_8_R3.CraftServer; +import org.bukkit.craftbukkit.v1_8_R3.CraftWorld; + +import java.lang.reflect.Field; +import java.util.UUID; + +public final class ChunkLoaderNPC_v1_8_R3 extends EntityPlayer implements ChunkLoaderNPC { + + public ChunkLoaderNPC_v1_8_R3(Location location, UUID uuid){ + super(((CraftServer) Bukkit.getServer()).getServer(), ((CraftWorld) location.getWorld()).getHandle(), + new GameProfile(uuid, "Loader-" + location.getWorld().getName()), new PlayerInteractManager(((CraftWorld) location.getWorld()).getHandle())); + + playerConnection = new DummyPlayerConnection(server, this); + + playerInteractManager.setGameMode(WorldSettings.EnumGamemode.CREATIVE); + fallDistance = 0.0F; + + fauxSleeping = true; + + spawnIn(world); + setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + world.players.add(this); + ((WorldServer) world).getPlayerChunkMap().addPlayer(this); + } + + @Override + public UUID getUniqueId() { + return super.getUniqueID(); + } + + @Override + public void die() { + super.die(); + } + + @Override + public Location getLocation() { + return getBukkitEntity().getLocation(); + } + + @Override + public boolean damageEntity(DamageSource damagesource, float f) { + return false; + } + + private static class DummyNetworkManager extends NetworkManager{ + + DummyNetworkManager(){ + super(EnumProtocolDirection.SERVERBOUND); + updateFields(); + } + + private void updateFields() { + try { + Field channelField = NetworkManager.class.getDeclaredField("channel"); + channelField.setAccessible(true); + channelField.set(this, new DummyChannel()); + channelField.setAccessible(false); + + Field socketAddressField = NetworkManager.class.getDeclaredField("l"); + socketAddressField.setAccessible(true); + socketAddressField.set(this, null); + } + catch (NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException e) { + e.printStackTrace(); + } + } + + } + + private static class DummyPlayerConnection extends PlayerConnection { + + DummyPlayerConnection(MinecraftServer minecraftServer, EntityPlayer entityPlayer) { + super(minecraftServer, new DummyNetworkManager(), entityPlayer); + } + + public void a(PacketPlayInWindowClick packetPlayInWindowClick) { + + } + + public void a(PacketPlayInTransaction packetPlayInTransaction) { + + } + + public void a(PacketPlayInFlying packetPlayInFlying) { + + } + + public void a(PacketPlayInUpdateSign packetPlayInUpdateSign) { + + } + + public void a(PacketPlayInBlockDig packetPlayInBlockDig) { + + } + + public void a(PacketPlayInBlockPlace packetPlayInBlockPlace) { + + } + + public void disconnect(String s) { + + } + + public void a(PacketPlayInHeldItemSlot packetPlayInHeldItemSlot) { + + } + + public void a(PacketPlayInChat packetPlayInChat) { + + } + + public void sendPacket(Packet packet) { + + } + + } + +} diff --git a/v1_8_R3/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_8_R3.java b/v1_8_R3/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_8_R3.java new file mode 100644 index 0000000..0d77bbb --- /dev/null +++ b/v1_8_R3/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_8_R3.java @@ -0,0 +1,281 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.loaders.WChunkLoader; +import net.minecraft.server.v1_8_R3.AxisAlignedBB; +import net.minecraft.server.v1_8_R3.Block; +import net.minecraft.server.v1_8_R3.BlockPosition; +import net.minecraft.server.v1_8_R3.Chunk; +import net.minecraft.server.v1_8_R3.Entity; +import net.minecraft.server.v1_8_R3.EntityArmorStand; +import net.minecraft.server.v1_8_R3.IUpdatePlayerListBox; +import net.minecraft.server.v1_8_R3.ItemStack; +import net.minecraft.server.v1_8_R3.NBTTagCompound; +import net.minecraft.server.v1_8_R3.NBTTagList; +import net.minecraft.server.v1_8_R3.NBTTagLong; +import net.minecraft.server.v1_8_R3.NBTTagString; +import net.minecraft.server.v1_8_R3.TileEntity; +import net.minecraft.server.v1_8_R3.TileEntityMobSpawner; +import net.minecraft.server.v1_8_R3.World; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_8_R3.CraftChunk; +import org.bukkit.craftbukkit.v1_8_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_8_R3.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_8_R3.util.LongHash; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@SuppressWarnings("unused") +public final class NMSAdapter_v1_8_R3 implements NMSAdapter { + + private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + + @Override + public String getTag(org.bukkit.inventory.ItemStack itemStack, String key, String def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + if(!tagCompound.hasKeyOfType(key, 8)) + return def; + + return tagCompound.getString(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, String value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + tagCompound.set(key, new NBTTagString(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public long getTag(org.bukkit.inventory.ItemStack itemStack, String key, long def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + if(!tagCompound.hasKeyOfType(key, 4)) + return def; + + return tagCompound.getLong(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, long value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + tagCompound.set(key, new NBTTagLong(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack itemStack, String texture) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + + NBTTagCompound nbtTagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + NBTTagCompound skullOwner = nbtTagCompound.hasKey("SkullOwner") ? nbtTagCompound.getCompound("SkullOwner") : new NBTTagCompound(); + + NBTTagCompound properties = new NBTTagCompound(); + + NBTTagList textures = new NBTTagList(); + NBTTagCompound signature = new NBTTagCompound(); + signature.setString("Value", texture); + textures.add(signature); + + properties.set("textures", textures); + + skullOwner.set("Properties", properties); + skullOwner.setString("Id", UUID.randomUUID().toString()); + + nbtTagCompound.set("SkullOwner", skullOwner); + + nmsItem.setTag(nbtTagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public ChunkLoaderNPC createNPC(Location location, UUID uuid) { + return new ChunkLoaderNPC_v1_8_R3(location, uuid); + } + + @Override + public void createLoader(ChunkLoader chunkLoader) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + TileEntityChunkLoader tileEntityChunkLoader = new TileEntityChunkLoader(chunkLoader, world, blockPosition); + world.tileEntityList.add(tileEntityChunkLoader); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner).forEach(tileEntity -> { + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + tileEntity.b(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) -1); + tileEntity.a(nbtTagCompound); + }); + } + + @Override + public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + long tileEntityLong = LongHash.toLong(blockPosition.getX() >> 4, blockPosition.getZ() >> 4); + TileEntityChunkLoader tileEntityChunkLoader = TileEntityChunkLoader.tileEntityChunkLoaderMap.remove(tileEntityLong); + if(tileEntityChunkLoader != null) { + tileEntityChunkLoader.holograms.forEach(Entity::die); + tileEntityChunkLoader.removed = true; + world.tileEntityList.remove(tileEntityChunkLoader); + } + + if(spawnParticle) + world.a(null, 2001, blockPosition, Block.getCombinedId(world.getType(blockPosition))); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner).forEach(tileEntity -> { + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + tileEntity.b(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) 16); + tileEntity.a(nbtTagCompound); + }); + } + + @Override + public void updateSpawner(Location location, boolean reset) { + World world = ((CraftWorld) location.getWorld()).getHandle(); + + BlockPosition blockPosition = new BlockPosition(location.getX(), location.getY(), location.getZ()); + TileEntityMobSpawner mobSpawner = (TileEntityMobSpawner) world.getTileEntity(blockPosition); + + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + mobSpawner.b(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) (reset ? 16 : -1)); + mobSpawner.a(nbtTagCompound); + } + + private static final class TileEntityChunkLoader extends TileEntity implements IUpdatePlayerListBox { + + private static final Map tileEntityChunkLoaderMap = new HashMap<>(); + + private final List holograms = new ArrayList<>(); + private final ChunkLoader chunkLoader; + + private short currentTick = 20; + private short daysAmount, hoursAmount, minutesAmount, secondsAmount; + private boolean removed = false; + + TileEntityChunkLoader(ChunkLoader chunkLoader, World world, BlockPosition blockPosition){ + this.chunkLoader = chunkLoader; + + a(blockPosition); + a(world); + + long timeLeft = chunkLoader.getTimeLeft(); + + daysAmount = (short) (timeLeft / 86400); + timeLeft = timeLeft % 86400; + + hoursAmount = (short) (timeLeft / 3600); + timeLeft = timeLeft % 3600; + + minutesAmount = (short) (timeLeft / 60); + timeLeft = timeLeft % 60; + + secondsAmount = (short) timeLeft; + + tileEntityChunkLoaderMap.put(LongHash.toLong(blockPosition.getX() >> 4, blockPosition.getZ() >> 4), this); + + double currentY = position.getY() + 1; + for(int i = plugin.getSettings().hologramLines.size(); i > 0; i--){ + EntityHologram hologram = new EntityHologram(world, position.getX() + 0.5, currentY, position.getZ() + 0.5); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + world.addEntity(hologram); + currentY += 0.23; + holograms.add(hologram); + } + } + + @Override + public void c() { + if(removed || ++currentTick <= 20) + return; + + currentTick = 0; + + if(((WChunkLoader) chunkLoader).isNotActive()){ + chunkLoader.remove(); + return; + } + + int hologramsAmount = holograms.size(); + for (int i = hologramsAmount; i > 0; i--) { + EntityHologram hologram = holograms.get(hologramsAmount - i); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + } + + ((WChunkLoader) chunkLoader).tick(); + + if(!removed) { + secondsAmount--; + if (secondsAmount < 0) { + secondsAmount = 59; + minutesAmount--; + if (minutesAmount < 0) { + minutesAmount = 59; + hoursAmount--; + if (hoursAmount < 0) { + hoursAmount = 23; + daysAmount--; + } + } + } + } + } + + private void updateName(EntityHologram hologram, String line){ + hologram.setCustomName(line + .replace("{0}", chunkLoader.getWhoPlaced().getName()) + .replace("{1}", daysAmount + "") + .replace("{2}", hoursAmount + "") + .replace("{3}", minutesAmount + "") + .replace("{4}", secondsAmount + "") + ); + } + + } + + private static class EntityHologram extends EntityArmorStand { + + EntityHologram(World world, double x, double y, double z){ + super(world); + setPosition(x, y, z); + setInvisible(true); + setSmall(true); + setArms(false); + setGravity(false); + setBasePlate(true); + setCustomNameVisible(true); + a(new AxisAlignedBB(0D, 0D, 0D, 0D, 0D, 0D)); + } + + } + +} diff --git a/v1_9_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_9_R1.java b/v1_9_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_9_R1.java new file mode 100644 index 0000000..2b98a30 --- /dev/null +++ b/v1_9_R1/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_9_R1.java @@ -0,0 +1,145 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.npc.DummyChannel; +import com.mojang.authlib.GameProfile; +import net.minecraft.server.v1_9_R1.DamageSource; +import net.minecraft.server.v1_9_R1.EntityPlayer; +import net.minecraft.server.v1_9_R1.EnumProtocolDirection; +import net.minecraft.server.v1_9_R1.MinecraftServer; +import net.minecraft.server.v1_9_R1.NetworkManager; +import net.minecraft.server.v1_9_R1.Packet; +import net.minecraft.server.v1_9_R1.PacketPlayInBlockDig; +import net.minecraft.server.v1_9_R1.PacketPlayInBlockPlace; +import net.minecraft.server.v1_9_R1.PacketPlayInChat; +import net.minecraft.server.v1_9_R1.PacketPlayInFlying; +import net.minecraft.server.v1_9_R1.PacketPlayInHeldItemSlot; +import net.minecraft.server.v1_9_R1.PacketPlayInTransaction; +import net.minecraft.server.v1_9_R1.PacketPlayInUpdateSign; +import net.minecraft.server.v1_9_R1.PacketPlayInWindowClick; +import net.minecraft.server.v1_9_R1.PlayerConnection; +import net.minecraft.server.v1_9_R1.PlayerInteractManager; +import net.minecraft.server.v1_9_R1.WorldServer; +import net.minecraft.server.v1_9_R1.WorldSettings; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_9_R1.CraftServer; +import org.bukkit.craftbukkit.v1_9_R1.CraftWorld; + +import java.lang.reflect.Field; +import java.util.UUID; + +public final class ChunkLoaderNPC_v1_9_R1 extends EntityPlayer implements ChunkLoaderNPC { + + public ChunkLoaderNPC_v1_9_R1(Location location, UUID uuid){ + super(((CraftServer) Bukkit.getServer()).getServer(), ((CraftWorld) location.getWorld()).getHandle(), + new GameProfile(uuid, "Loader-" + location.getWorld().getName()), new PlayerInteractManager(((CraftWorld) location.getWorld()).getHandle())); + + playerConnection = new DummyPlayerConnection(server, this); + + playerInteractManager.setGameMode(WorldSettings.EnumGamemode.CREATIVE); + fallDistance = 0.0F; + + fauxSleeping = true; + + spawnIn(world); + setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + world.players.add(this); + ((WorldServer) world).getPlayerChunkMap().addPlayer(this); + } + + @Override + public UUID getUniqueId() { + return super.getUniqueID(); + } + + @Override + public void die() { + super.die(); + } + + @Override + public Location getLocation() { + return getBukkitEntity().getLocation(); + } + + @Override + protected boolean damageEntity0(DamageSource damagesource, float f) { + return false; + } + + private static class DummyNetworkManager extends NetworkManager{ + + DummyNetworkManager(){ + super(EnumProtocolDirection.SERVERBOUND); + updateFields(); + } + + private void updateFields() { + try { + Field channelField = NetworkManager.class.getDeclaredField("channel"); + channelField.setAccessible(true); + channelField.set(this, new DummyChannel()); + channelField.setAccessible(false); + + Field socketAddressField = NetworkManager.class.getDeclaredField("l"); + socketAddressField.setAccessible(true); + socketAddressField.set(this, null); + } + catch (NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException e) { + e.printStackTrace(); + } + } + + } + + private static class DummyPlayerConnection extends PlayerConnection { + + DummyPlayerConnection(MinecraftServer minecraftServer, EntityPlayer entityPlayer) { + super(minecraftServer, new DummyNetworkManager(), entityPlayer); + } + + public void a(PacketPlayInWindowClick packetPlayInWindowClick) { + + } + + public void a(PacketPlayInTransaction packetPlayInTransaction) { + + } + + public void a(PacketPlayInFlying packetPlayInFlying) { + + } + + public void a(PacketPlayInUpdateSign packetPlayInUpdateSign) { + + } + + public void a(PacketPlayInBlockDig packetPlayInBlockDig) { + + } + + public void a(PacketPlayInBlockPlace packetPlayInBlockPlace) { + + } + + public void disconnect(String s) { + + } + + public void a(PacketPlayInHeldItemSlot packetPlayInHeldItemSlot) { + + } + + public void a(PacketPlayInChat packetPlayInChat) { + + } + + public void sendPacket(Packet packet) { + + } + + } + +} diff --git a/v1_9_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_9_R1.java b/v1_9_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_9_R1.java new file mode 100644 index 0000000..b56f183 --- /dev/null +++ b/v1_9_R1/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_9_R1.java @@ -0,0 +1,282 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.loaders.WChunkLoader; +import net.minecraft.server.v1_9_R1.AxisAlignedBB; +import net.minecraft.server.v1_9_R1.Block; +import net.minecraft.server.v1_9_R1.BlockPosition; +import net.minecraft.server.v1_9_R1.Chunk; +import net.minecraft.server.v1_9_R1.Entity; +import net.minecraft.server.v1_9_R1.EntityArmorStand; +import net.minecraft.server.v1_9_R1.ITickable; +import net.minecraft.server.v1_9_R1.ItemStack; +import net.minecraft.server.v1_9_R1.NBTTagCompound; +import net.minecraft.server.v1_9_R1.NBTTagList; +import net.minecraft.server.v1_9_R1.NBTTagLong; +import net.minecraft.server.v1_9_R1.NBTTagString; +import net.minecraft.server.v1_9_R1.TileEntity; +import net.minecraft.server.v1_9_R1.TileEntityMobSpawner; +import net.minecraft.server.v1_9_R1.World; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_9_R1.CraftChunk; +import org.bukkit.craftbukkit.v1_9_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_9_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_9_R1.util.LongHash; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@SuppressWarnings("unused") +public final class NMSAdapter_v1_9_R1 implements NMSAdapter { + + private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + + @Override + public String getTag(org.bukkit.inventory.ItemStack itemStack, String key, String def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + if(!tagCompound.hasKeyOfType(key, 8)) + return def; + + return tagCompound.getString(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, String value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + tagCompound.set(key, new NBTTagString(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public long getTag(org.bukkit.inventory.ItemStack itemStack, String key, long def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + if(!tagCompound.hasKeyOfType(key, 4)) + return def; + + return tagCompound.getLong(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, long value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + tagCompound.set(key, new NBTTagLong(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack itemStack, String texture) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + + NBTTagCompound nbtTagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + NBTTagCompound skullOwner = nbtTagCompound.hasKey("SkullOwner") ? nbtTagCompound.getCompound("SkullOwner") : new NBTTagCompound(); + + NBTTagCompound properties = new NBTTagCompound(); + + NBTTagList textures = new NBTTagList(); + NBTTagCompound signature = new NBTTagCompound(); + signature.setString("Value", texture); + textures.add(signature); + + properties.set("textures", textures); + + skullOwner.set("Properties", properties); + skullOwner.setString("Id", UUID.randomUUID().toString()); + + nbtTagCompound.set("SkullOwner", skullOwner); + + nmsItem.setTag(nbtTagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public ChunkLoaderNPC createNPC(Location location, UUID uuid) { + return new ChunkLoaderNPC_v1_9_R1(location, uuid); + } + + @Override + public void createLoader(ChunkLoader chunkLoader) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + TileEntityChunkLoader tileEntityChunkLoader = new TileEntityChunkLoader(chunkLoader, world, blockPosition); + world.tileEntityListTick.add(tileEntityChunkLoader); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner).forEach(tileEntity -> { + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + tileEntity.save(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) -1); + tileEntity.a(nbtTagCompound); + }); + } + + @Override + public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + long tileEntityLong = LongHash.toLong(blockPosition.getX() >> 4, blockPosition.getZ() >> 4); + TileEntityChunkLoader tileEntityChunkLoader = TileEntityChunkLoader.tileEntityChunkLoaderMap.remove(tileEntityLong); + if(tileEntityChunkLoader != null) { + tileEntityChunkLoader.holograms.forEach(Entity::die); + tileEntityChunkLoader.removed = true; + world.tileEntityListTick.remove(tileEntityChunkLoader); + } + + if(spawnParticle) + world.a(null, 2001, blockPosition, Block.getCombinedId(world.getType(blockPosition))); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner).forEach(tileEntity -> { + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + tileEntity.save(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) 16); + tileEntity.a(nbtTagCompound); + }); + } + + @Override + public void updateSpawner(Location location, boolean reset) { + World world = ((CraftWorld) location.getWorld()).getHandle(); + + BlockPosition blockPosition = new BlockPosition(location.getX(), location.getY(), location.getZ()); + TileEntityMobSpawner mobSpawner = (TileEntityMobSpawner) world.getTileEntity(blockPosition); + + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + mobSpawner.save(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) (reset ? 16 : -1)); + mobSpawner.a(nbtTagCompound); + } + + private static final class TileEntityChunkLoader extends TileEntity implements ITickable { + + private static final Map tileEntityChunkLoaderMap = new HashMap<>(); + + private final List holograms = new ArrayList<>(); + private final ChunkLoader chunkLoader; + + private short currentTick = 20; + private short daysAmount, hoursAmount, minutesAmount, secondsAmount; + private boolean removed = false; + + TileEntityChunkLoader(ChunkLoader chunkLoader, World world, BlockPosition blockPosition){ + this.chunkLoader = chunkLoader; + + a(blockPosition); + a(world); + + long timeLeft = chunkLoader.getTimeLeft(); + + daysAmount = (short) (timeLeft / 86400); + timeLeft = timeLeft % 86400; + + hoursAmount = (short) (timeLeft / 3600); + timeLeft = timeLeft % 3600; + + minutesAmount = (short) (timeLeft / 60); + timeLeft = timeLeft % 60; + + secondsAmount = (short) timeLeft; + + tileEntityChunkLoaderMap.put(LongHash.toLong(blockPosition.getX() >> 4, blockPosition.getZ() >> 4), this); + + double currentY = position.getY() + 1; + for(int i = plugin.getSettings().hologramLines.size(); i > 0; i--){ + EntityHologram hologram = new EntityHologram(world, position.getX() + 0.5, currentY, position.getZ() + 0.5); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + world.addEntity(hologram); + currentY += 0.23; + holograms.add(hologram); + } + } + + @Override + public void c() { + if(removed || ++currentTick <= 20) + return; + + currentTick = 0; + + if(((WChunkLoader) chunkLoader).isNotActive()){ + chunkLoader.remove(); + return; + } + + int hologramsAmount = holograms.size(); + for (int i = hologramsAmount; i > 0; i--) { + EntityHologram hologram = holograms.get(hologramsAmount - i); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + } + + ((WChunkLoader) chunkLoader).tick(); + + if(!removed) { + secondsAmount--; + if (secondsAmount < 0) { + secondsAmount = 59; + minutesAmount--; + if (minutesAmount < 0) { + minutesAmount = 59; + hoursAmount--; + if (hoursAmount < 0) { + hoursAmount = 23; + daysAmount--; + } + } + } + } + } + + private void updateName(EntityHologram hologram, String line){ + hologram.setCustomName(line + .replace("{0}", chunkLoader.getWhoPlaced().getName()) + .replace("{1}", daysAmount + "") + .replace("{2}", hoursAmount + "") + .replace("{3}", minutesAmount + "") + .replace("{4}", secondsAmount + "") + ); + } + + } + + private static class EntityHologram extends EntityArmorStand { + + EntityHologram(World world, double x, double y, double z){ + super(world); + setPosition(x, y, z); + setInvisible(true); + setSmall(true); + setArms(false); + setGravity(false); + setBasePlate(true); + setMarker(true); + setCustomNameVisible(true); + a(new AxisAlignedBB(0D, 0D, 0D, 0D, 0D, 0D)); + } + + } + +} diff --git a/v1_9_R2/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_9_R2.java b/v1_9_R2/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_9_R2.java new file mode 100644 index 0000000..e184e52 --- /dev/null +++ b/v1_9_R2/src/main/java/com/bgsoftware/wildloaders/nms/ChunkLoaderNPC_v1_9_R2.java @@ -0,0 +1,145 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.npc.DummyChannel; +import com.mojang.authlib.GameProfile; +import net.minecraft.server.v1_9_R2.DamageSource; +import net.minecraft.server.v1_9_R2.EntityPlayer; +import net.minecraft.server.v1_9_R2.EnumProtocolDirection; +import net.minecraft.server.v1_9_R2.MinecraftServer; +import net.minecraft.server.v1_9_R2.NetworkManager; +import net.minecraft.server.v1_9_R2.Packet; +import net.minecraft.server.v1_9_R2.PacketPlayInBlockDig; +import net.minecraft.server.v1_9_R2.PacketPlayInBlockPlace; +import net.minecraft.server.v1_9_R2.PacketPlayInChat; +import net.minecraft.server.v1_9_R2.PacketPlayInFlying; +import net.minecraft.server.v1_9_R2.PacketPlayInHeldItemSlot; +import net.minecraft.server.v1_9_R2.PacketPlayInTransaction; +import net.minecraft.server.v1_9_R2.PacketPlayInUpdateSign; +import net.minecraft.server.v1_9_R2.PacketPlayInWindowClick; +import net.minecraft.server.v1_9_R2.PlayerConnection; +import net.minecraft.server.v1_9_R2.PlayerInteractManager; +import net.minecraft.server.v1_9_R2.WorldServer; +import net.minecraft.server.v1_9_R2.WorldSettings; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_9_R2.CraftServer; +import org.bukkit.craftbukkit.v1_9_R2.CraftWorld; + +import java.lang.reflect.Field; +import java.util.UUID; + +public final class ChunkLoaderNPC_v1_9_R2 extends EntityPlayer implements ChunkLoaderNPC { + + public ChunkLoaderNPC_v1_9_R2(Location location, UUID uuid){ + super(((CraftServer) Bukkit.getServer()).getServer(), ((CraftWorld) location.getWorld()).getHandle(), + new GameProfile(uuid, "Loader-" + location.getWorld().getName()), new PlayerInteractManager(((CraftWorld) location.getWorld()).getHandle())); + + playerConnection = new DummyPlayerConnection(server, this); + + playerInteractManager.setGameMode(WorldSettings.EnumGamemode.CREATIVE); + fallDistance = 0.0F; + + fauxSleeping = true; + + spawnIn(world); + setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + + world.players.add(this); + ((WorldServer) world).getPlayerChunkMap().addPlayer(this); + } + + @Override + public UUID getUniqueId() { + return super.getUniqueID(); + } + + @Override + public void die() { + super.die(); + } + + @Override + public Location getLocation() { + return getBukkitEntity().getLocation(); + } + + @Override + protected boolean damageEntity0(DamageSource damagesource, float f) { + return false; + } + + private static class DummyNetworkManager extends NetworkManager{ + + DummyNetworkManager(){ + super(EnumProtocolDirection.SERVERBOUND); + updateFields(); + } + + private void updateFields() { + try { + Field channelField = NetworkManager.class.getDeclaredField("channel"); + channelField.setAccessible(true); + channelField.set(this, new DummyChannel()); + channelField.setAccessible(false); + + Field socketAddressField = NetworkManager.class.getDeclaredField("l"); + socketAddressField.setAccessible(true); + socketAddressField.set(this, null); + } + catch (NoSuchFieldException|SecurityException|IllegalArgumentException|IllegalAccessException e) { + e.printStackTrace(); + } + } + + } + + private static class DummyPlayerConnection extends PlayerConnection { + + DummyPlayerConnection(MinecraftServer minecraftServer, EntityPlayer entityPlayer) { + super(minecraftServer, new DummyNetworkManager(), entityPlayer); + } + + public void a(PacketPlayInWindowClick packetPlayInWindowClick) { + + } + + public void a(PacketPlayInTransaction packetPlayInTransaction) { + + } + + public void a(PacketPlayInFlying packetPlayInFlying) { + + } + + public void a(PacketPlayInUpdateSign packetPlayInUpdateSign) { + + } + + public void a(PacketPlayInBlockDig packetPlayInBlockDig) { + + } + + public void a(PacketPlayInBlockPlace packetPlayInBlockPlace) { + + } + + public void disconnect(String s) { + + } + + public void a(PacketPlayInHeldItemSlot packetPlayInHeldItemSlot) { + + } + + public void a(PacketPlayInChat packetPlayInChat) { + + } + + public void sendPacket(Packet packet) { + + } + + } + +} diff --git a/v1_9_R2/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_9_R2.java b/v1_9_R2/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_9_R2.java new file mode 100644 index 0000000..88d5e56 --- /dev/null +++ b/v1_9_R2/src/main/java/com/bgsoftware/wildloaders/nms/NMSAdapter_v1_9_R2.java @@ -0,0 +1,282 @@ +package com.bgsoftware.wildloaders.nms; + +import com.bgsoftware.wildloaders.WildLoadersPlugin; +import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; +import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; +import com.bgsoftware.wildloaders.loaders.WChunkLoader; +import net.minecraft.server.v1_9_R2.AxisAlignedBB; +import net.minecraft.server.v1_9_R2.Block; +import net.minecraft.server.v1_9_R2.BlockPosition; +import net.minecraft.server.v1_9_R2.Chunk; +import net.minecraft.server.v1_9_R2.Entity; +import net.minecraft.server.v1_9_R2.EntityArmorStand; +import net.minecraft.server.v1_9_R2.ITickable; +import net.minecraft.server.v1_9_R2.ItemStack; +import net.minecraft.server.v1_9_R2.NBTTagCompound; +import net.minecraft.server.v1_9_R2.NBTTagList; +import net.minecraft.server.v1_9_R2.NBTTagLong; +import net.minecraft.server.v1_9_R2.NBTTagString; +import net.minecraft.server.v1_9_R2.TileEntity; +import net.minecraft.server.v1_9_R2.TileEntityMobSpawner; +import net.minecraft.server.v1_9_R2.World; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_9_R2.CraftChunk; +import org.bukkit.craftbukkit.v1_9_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_9_R2.inventory.CraftItemStack; +import org.bukkit.craftbukkit.v1_9_R2.util.LongHash; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@SuppressWarnings({"ConstantConditions", "unused"}) +public final class NMSAdapter_v1_9_R2 implements NMSAdapter { + + private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); + + @Override + public String getTag(org.bukkit.inventory.ItemStack itemStack, String key, String def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + if(!tagCompound.hasKeyOfType(key, 8)) + return def; + + return tagCompound.getString(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, String value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + tagCompound.set(key, new NBTTagString(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public long getTag(org.bukkit.inventory.ItemStack itemStack, String key, long def) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + if(!tagCompound.hasKeyOfType(key, 4)) + return def; + + return tagCompound.getLong(key); + } + + @Override + public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack itemStack, String key, long value) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + tagCompound.set(key, new NBTTagLong(value)); + + nmsItem.setTag(tagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack itemStack, String texture) { + ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack); + + NBTTagCompound nbtTagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound(); + + NBTTagCompound skullOwner = nbtTagCompound.hasKey("SkullOwner") ? nbtTagCompound.getCompound("SkullOwner") : new NBTTagCompound(); + + NBTTagCompound properties = new NBTTagCompound(); + + NBTTagList textures = new NBTTagList(); + NBTTagCompound signature = new NBTTagCompound(); + signature.setString("Value", texture); + textures.add(signature); + + properties.set("textures", textures); + + skullOwner.set("Properties", properties); + skullOwner.setString("Id", UUID.randomUUID().toString()); + + nbtTagCompound.set("SkullOwner", skullOwner); + + nmsItem.setTag(nbtTagCompound); + + return CraftItemStack.asBukkitCopy(nmsItem); + } + + @Override + public ChunkLoaderNPC createNPC(Location location, UUID uuid) { + return new ChunkLoaderNPC_v1_9_R2(location, uuid); + } + + @Override + public void createLoader(ChunkLoader chunkLoader) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + TileEntityChunkLoader tileEntityChunkLoader = new TileEntityChunkLoader(chunkLoader, world, blockPosition); + world.tileEntityListTick.add(tileEntityChunkLoader); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner).forEach(tileEntity -> { + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + tileEntity.save(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) -1); + tileEntity.a(nbtTagCompound); + }); + } + + @Override + public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) { + Location loaderLoc = chunkLoader.getLocation(); + World world = ((CraftWorld) loaderLoc.getWorld()).getHandle(); + BlockPosition blockPosition = new BlockPosition(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ()); + + long tileEntityLong = LongHash.toLong(blockPosition.getX() >> 4, blockPosition.getZ() >> 4); + TileEntityChunkLoader tileEntityChunkLoader = TileEntityChunkLoader.tileEntityChunkLoaderMap.remove(tileEntityLong); + if(tileEntityChunkLoader != null) { + tileEntityChunkLoader.holograms.forEach(Entity::die); + tileEntityChunkLoader.removed = true; + world.tileEntityListTick.remove(tileEntityChunkLoader); + } + + if(spawnParticle) + world.a(null, 2001, blockPosition, Block.getCombinedId(world.getType(blockPosition))); + + Chunk chunk = ((CraftChunk) loaderLoc.getChunk()).getHandle(); + chunk.tileEntities.values().stream().filter(tileEntity -> tileEntity instanceof TileEntityMobSpawner).forEach(tileEntity -> { + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + tileEntity.save(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) 16); + tileEntity.a(nbtTagCompound); + }); + } + + @Override + public void updateSpawner(Location location, boolean reset) { + World world = ((CraftWorld) location.getWorld()).getHandle(); + + BlockPosition blockPosition = new BlockPosition(location.getX(), location.getY(), location.getZ()); + TileEntityMobSpawner mobSpawner = (TileEntityMobSpawner) world.getTileEntity(blockPosition); + + NBTTagCompound nbtTagCompound = new NBTTagCompound(); + mobSpawner.save(nbtTagCompound); + nbtTagCompound.setShort("RequiredPlayerRange", (short) (reset ? 16 : -1)); + mobSpawner.a(nbtTagCompound); + } + + private static final class TileEntityChunkLoader extends TileEntity implements ITickable { + + private static final Map tileEntityChunkLoaderMap = new HashMap<>(); + + private final List holograms = new ArrayList<>(); + private final ChunkLoader chunkLoader; + + private short currentTick = 20; + private short daysAmount, hoursAmount, minutesAmount, secondsAmount; + private boolean removed = false; + + TileEntityChunkLoader(ChunkLoader chunkLoader, World world, BlockPosition blockPosition){ + this.chunkLoader = chunkLoader; + + a(blockPosition); + a(world); + + long timeLeft = chunkLoader.getTimeLeft(); + + daysAmount = (short) (timeLeft / 86400); + timeLeft = timeLeft % 86400; + + hoursAmount = (short) (timeLeft / 3600); + timeLeft = timeLeft % 3600; + + minutesAmount = (short) (timeLeft / 60); + timeLeft = timeLeft % 60; + + secondsAmount = (short) timeLeft; + + tileEntityChunkLoaderMap.put(LongHash.toLong(blockPosition.getX() >> 4, blockPosition.getZ() >> 4), this); + + double currentY = position.getY() + 1; + for(int i = plugin.getSettings().hologramLines.size(); i > 0; i--){ + EntityHologram hologram = new EntityHologram(world, position.getX() + 0.5, currentY, position.getZ() + 0.5); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + world.addEntity(hologram); + currentY += 0.23; + holograms.add(hologram); + } + } + + @Override + public void c() { + if(removed || ++currentTick <= 20) + return; + + currentTick = 0; + + if(((WChunkLoader) chunkLoader).isNotActive()){ + chunkLoader.remove(); + return; + } + + int hologramsAmount = holograms.size(); + for (int i = hologramsAmount; i > 0; i--) { + EntityHologram hologram = holograms.get(hologramsAmount - i); + updateName(hologram, plugin.getSettings().hologramLines.get(i - 1)); + } + + ((WChunkLoader) chunkLoader).tick(); + + if(!removed) { + secondsAmount--; + if (secondsAmount < 0) { + secondsAmount = 59; + minutesAmount--; + if (minutesAmount < 0) { + minutesAmount = 59; + hoursAmount--; + if (hoursAmount < 0) { + hoursAmount = 23; + daysAmount--; + } + } + } + } + } + + private void updateName(EntityHologram hologram, String line){ + hologram.setCustomName(line + .replace("{0}", chunkLoader.getWhoPlaced().getName()) + .replace("{1}", daysAmount + "") + .replace("{2}", hoursAmount + "") + .replace("{3}", minutesAmount + "") + .replace("{4}", secondsAmount + "") + ); + } + + } + + private static class EntityHologram extends EntityArmorStand { + + EntityHologram(World world, double x, double y, double z){ + super(world); + setPosition(x, y, z); + setInvisible(true); + setSmall(true); + setArms(false); + setGravity(false); + setBasePlate(true); + setMarker(true); + setCustomNameVisible(true); + a(new AxisAlignedBB(0D, 0D, 0D, 0D, 0D, 0D)); + } + + } + +}