diff --git a/README.md b/README.md index 8e73904..4968b71 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ You can open, close or destroy the door only if you have the corresponding key i ## Documentation A documentation is available [here](https://dev.bukkit.org/projects/serial-key/pages/documentation). -Keep in mind that this documentation is for the **v0.4** of this plugin ! +Keep in mind that this documentation is for the **v1.0** of this plugin ! ## Videos You can find all videos [here](https://dev.bukkit.org/projects/serial-key/pages/videos). diff --git a/build.gradle b/build.gradle index 3572933..907b1ee 100644 --- a/build.gradle +++ b/build.gradle @@ -1,107 +1,3 @@ -plugins { - id 'com.github.johnrengelman.shadow' version '2.0.4' - id 'net.minecrell.plugin-yml.bukkit' version '0.3.0' - id 'java' - id 'maven' -} - -group = 'fr.skyost.serialkey' -version = '0.4.1' - -sourceCompatibility = 1.8 - -bukkit { - name = 'SerialKey' - main = 'fr.skyost.serialkey.SerialKey' - description = 'Lock your chests and doors.' - author = 'Skyost' - website = 'http://dev.bukkit.org/bukkit-plugins/serial-key/' - apiVersion = '1.13' - - commands { - serialkey { - description = 'Main command of SerialKey.' - aliases = ['serial-key', 'sk'] - usage = '/serialkey getkey.' - } - } - - permissions { - 'serialkey.*' { - setDefault('OP') - description = 'Gives you all plugin\'s permissions' - children = [ - 'serialkey.craft.key', - 'serialkey.craft.masterkey', - 'serialkey.craft.keyclone', - 'serialkey.craft.bunchofkeys', - 'serialkey.craft.padlockfinder', - 'serialkey.use.key', - 'serialkey.use.masterkey', - 'serialkey.use.bunchofkeys', - 'serialkey.use.padlockfinder', - 'serialkey.command.getkey', - ] - } - 'serialkey.craft.key' { - setDefault('TRUE') - description = 'Allows you to craft a key.' - } - 'serialkey.craft.masterkey' { - setDefault('OP') - description = 'Allows you to craft a master key.' - } - 'serialkey.craft.keyclone' { - setDefault('TRUE') - description = 'Allows you to clone a key.' - } - 'serialkey.craft.bunchofkeys' { - setDefault('TRUE') - description = 'Allows you to bunch of keys.' - } - 'serialkey.craft.padlockfinder' { - setDefault('TRUE') - description = 'Allows you to craft a padlock finder.' - } - 'serialkey.use.key' { - setDefault('TRUE') - description = 'Allows you to use a key.' - } - 'serialkey.use.masterkey' { - setDefault('OP') - description = 'Allows you to use a master key.' - } - 'serialkey.use.bunchofkeys' { - setDefault('TRUE') - description = 'Allows you to use a bunch of keys.' - } - 'serialkey.use.padlockfinder' { - setDefault('TRUE') - description = 'Allows you to use a padlock finder.' - } - 'serialkey.command.getkey' { - setDefault('OP') - description = 'Allows you to use /serialkey getkey.' - } - } -} - -repositories { - mavenCentral() - maven { url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' } - maven { url = 'http://nexus.hc.to/content/repositories/pub_releases' } - maven { url = 'http://repo.bstats.org/content/repositories/releases/' } -} - -dependencies { - compileOnly 'org.bukkit:bukkit:1.13.1-R0.1-SNAPSHOT' - compile 'org.bstats:bstats-bukkit-lite:1.2' -} - -shadowJar { - destinationDir = new File(rootProject.projectDir, 'build/release/') - version = project.version - configurations = [project.configurations.compile] - mergeServiceFiles() - relocate 'org.bstats.bukkit', 'fr.skyost.serialkey.util.bstats' +subprojects { + group = 'fr.skyost.serialkey' } \ No newline at end of file diff --git a/bukkit/build.gradle b/bukkit/build.gradle new file mode 100644 index 0000000..6f35bb5 --- /dev/null +++ b/bukkit/build.gradle @@ -0,0 +1,105 @@ +plugins { + id 'com.github.johnrengelman.shadow' version '2.0.4' + id 'net.minecrell.plugin-yml.bukkit' version '0.3.0' + id 'java' + id 'maven' +} + +version = '1.0' +sourceCompatibility = 1.8 + +bukkit { + name = 'SerialKey' + main = 'fr.skyost.serialkey.bukkit.SerialKey' + description = 'Lock your chests and doors.' + author = 'Skyost' + website = 'http://dev.bukkit.org/bukkit-plugins/serial-key/' + apiVersion = '1.13' + + commands { + serialkey { + description = 'Main command of SerialKey.' + aliases = ['serial-key', 'sk'] + usage = '/serialkey getkey.' + } + } + + permissions { + 'serialkey.*' { + setDefault('OP') + description = 'Gives you all plugin\'s permissions' + children = [ + 'serialkey.craft.key', + 'serialkey.craft.masterkey', + 'serialkey.craft.keyclone', + 'serialkey.craft.bunchofkeys', + 'serialkey.craft.padlockfinder', + 'serialkey.use.key', + 'serialkey.use.masterkey', + 'serialkey.use.bunchofkeys', + 'serialkey.use.padlockfinder', + 'serialkey.command.getkey', + ] + } + 'serialkey.craft.key' { + setDefault('TRUE') + description = 'Allows you to craft a key.' + } + 'serialkey.craft.masterkey' { + setDefault('OP') + description = 'Allows you to craft a master key.' + } + 'serialkey.craft.keyclone' { + setDefault('TRUE') + description = 'Allows you to clone a key.' + } + 'serialkey.craft.bunchofkeys' { + setDefault('TRUE') + description = 'Allows you to bunch of keys.' + } + 'serialkey.craft.padlockfinder' { + setDefault('TRUE') + description = 'Allows you to craft a padlock finder.' + } + 'serialkey.use.key' { + setDefault('TRUE') + description = 'Allows you to use a key.' + } + 'serialkey.use.masterkey' { + setDefault('OP') + description = 'Allows you to use a master key.' + } + 'serialkey.use.bunchofkeys' { + setDefault('TRUE') + description = 'Allows you to use a bunch of keys.' + } + 'serialkey.use.padlockfinder' { + setDefault('TRUE') + description = 'Allows you to use a padlock finder.' + } + 'serialkey.command.getkey' { + setDefault('OP') + description = 'Allows you to use /serialkey getkey.' + } + } +} + +repositories { + mavenCentral() + maven { url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' } + maven { url = 'http://nexus.hc.to/content/repositories/pub_releases' } + maven { url = 'http://repo.bstats.org/content/repositories/releases/' } +} + +dependencies { + implementation project(':core') + compileOnly 'org.bukkit:bukkit:1.13.2-R0.1-SNAPSHOT' + implementation 'org.bstats:bstats-bukkit-lite:1.2' +} + +shadowJar { + destinationDir = new File(rootProject.projectDir, 'build/release/') + version = project.version + mergeServiceFiles() + relocate 'org.bstats.bukkit', 'fr.skyost.serialkey.bukkit.util.bstats' +} \ No newline at end of file diff --git a/bukkit/src/main/java/fr/skyost/serialkey/bukkit/BukkitTypeConverter.java b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/BukkitTypeConverter.java new file mode 100644 index 0000000..1515b82 --- /dev/null +++ b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/BukkitTypeConverter.java @@ -0,0 +1,116 @@ +package fr.skyost.serialkey.bukkit; + +import fr.skyost.serialkey.core.object.PersonIdentity; +import fr.skyost.serialkey.core.object.SerialKeyLocation; +import fr.skyost.serialkey.core.object.SerialKeyPerson; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +/** + * A class that allows to convert core objects to Bukkit objects (and vice-versa). + */ + +public class BukkitTypeConverter { + + /** + * Converts a SerialKey location to a Bukkit location. + * + * @param location The SerialKey location. + * + * @return The Bukkit location. + */ + + public static Location toBukkitLocation(final SerialKeyLocation location) { + return new Location(Bukkit.getWorld(location.getWorld()), location.getX(), location.getY(), location.getZ()); + } + + /** + * Converts a Bukkit location to a SerialKey location. + * + * @param location The Bukkit location. + * + * @return The SerialKey location. + */ + + public static SerialKeyLocation toSerialKeyLocation(final Location location) { + return new SerialKeyLocation(location.getWorld().getName(), location.getBlockX(), location.getBlockY(), location.getBlockZ()); + } + + /** + * Converts a SerialKey person to a Bukkit command sender. + * + * @param person The SerialKey person. + * + * @return The Bukkit command sender. + */ + + public static CommandSender toBukkitPerson(final SerialKeyPerson person) { + final PersonIdentity identity = person.getIdentity(); + if(identity.getType() == PersonIdentity.Type.CONSOLE) { + return Bukkit.getConsoleSender(); + } + + return Bukkit.getPlayer(identity.getName()); + } + + /** + * Converts a Bukkit command sender to a SerialKey person. + * + * @param sender Bukkit command sender. + * + * @return The SerialKey person. + */ + + public static SerialKeyPerson toSerialKeyPerson(final CommandSender sender) { + return new BukkitPerson(sender); + } + + /** + * Represents a Bukkit person. + */ + + private static class BukkitPerson implements SerialKeyPerson { + + /** + * The sender instance. + */ + + private final CommandSender sender; + + /** + * The identity. + */ + + private final PersonIdentity identity; + + /** + * Creates a new Bukkit person instance. + * + * @param sender The sender instance. + */ + + BukkitPerson(final CommandSender sender) { + this.sender = sender; + this.identity = new PersonIdentity(sender instanceof Player ? PersonIdentity.Type.PLAYER : PersonIdentity.Type.CONSOLE, sender.getName()); + } + + @Override + public PersonIdentity getIdentity() { + return identity; + } + + @Override + public void sendMessage(final String message) { + sender.sendMessage(message); + } + + @Override + public boolean hasPermission(final String permission) { + return sender.hasPermission(permission); + } + + } + +} \ No newline at end of file diff --git a/bukkit/src/main/java/fr/skyost/serialkey/bukkit/SerialKey.java b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/SerialKey.java new file mode 100644 index 0000000..fbb74c2 --- /dev/null +++ b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/SerialKey.java @@ -0,0 +1,169 @@ +package fr.skyost.serialkey.bukkit; + +import fr.skyost.serialkey.bukkit.command.BukkitGetKeyCommand; +import fr.skyost.serialkey.bukkit.config.BukkitPluginConfig; +import fr.skyost.serialkey.bukkit.config.BukkitPluginData; +import fr.skyost.serialkey.bukkit.config.BukkitPluginMessages; +import fr.skyost.serialkey.bukkit.item.BukkitItemManager; +import fr.skyost.serialkey.bukkit.listener.*; +import fr.skyost.serialkey.bukkit.padlock.BukkitPadlockManager; +import fr.skyost.serialkey.bukkit.unlocker.BukkitUnlocker; +import fr.skyost.serialkey.bukkit.util.Skyupdater; +import fr.skyost.serialkey.core.SerialKeyPlugin; +import org.bstats.bukkit.MetricsLite; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.command.CommandSender; +import org.bukkit.command.PluginCommand; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.File; + +/** + * The SerialKey plugin class. + */ + +public class SerialKey extends JavaPlugin implements SerialKeyPlugin { + + /** + * The plugin config. + */ + + private BukkitPluginConfig config; + + /** + * The plugin messages. + */ + + private BukkitPluginMessages messages; + + /** + * The item manager. + */ + + private BukkitItemManager itemManager; + + /** + * The unlocker. + */ + + private BukkitUnlocker unlocker; + + /** + * The padlock manager. + */ + + private BukkitPadlockManager padlockManager; + + @Override + public final void onEnable() { + try { + final File dataFolder = this.getDataFolder(); + + // Configuration : + + config = new BukkitPluginConfig(dataFolder); + config.load(); + messages = new BukkitPluginMessages(dataFolder); + messages.load(); + final BukkitPluginData data = new BukkitPluginData(dataFolder); + data.load(); + + // Core object : + + itemManager = new BukkitItemManager(this); + itemManager.createRecipes(); + unlocker = new BukkitUnlocker(this); + padlockManager = new BukkitPadlockManager(this); + padlockManager.load(data); + + // Events : + + final PluginManager manager = Bukkit.getPluginManager(); + manager.registerEvents(new BukkitGlobalListener(this), this); + manager.registerEvents(new BukkitBlocksListener(this), this); + manager.registerEvents(new BukkitBunchOfKeysListener(this), this); + manager.registerEvents(new BukkitPadlockFinderListener(this), this); + if(config.disableHoppers) { + manager.registerEvents(new BukkitHopperListener(this), this); + } + if(!config.allowLostChests) { + manager.registerEvents(new BukkitLostChestsListener(this), this); + } + + // Commands : + + final BukkitGetKeyCommand executor = new BukkitGetKeyCommand(this); + final PluginCommand command = this.getCommand("serialkey"); + command.setUsage(executor.getUsage()); + command.setExecutor(executor); + + // Services : + + if(config.enableUpdater) { + new Skyupdater(this, 84423, this.getFile(), true, true); + } + if(config.enableMetrics) { + new MetricsLite(this); + } + } + catch(final NullPointerException ex) { + sendMessage(Bukkit.getConsoleSender(), ChatColor.RED + "Null pointer exception ! Maybe you have misconfigured one (or more) item recipe."); + } + catch(final Exception ex) { + ex.printStackTrace(); + } + } + + @Override + public final void onDisable() { + try { + final BukkitPluginData data = new BukkitPluginData(getDataFolder()); + padlockManager.save(data); + data.save(); + } + catch(final Exception ex) { + ex.printStackTrace(); + } + } + + @Override + public BukkitItemManager getItemManager() { + return itemManager; + } + + @Override + public BukkitUnlocker getUnlocker() { + return unlocker; + } + + @Override + public BukkitPadlockManager getPadlockManager() { + return padlockManager; + } + + @Override + public BukkitPluginConfig getPluginConfig() { + return config; + } + + @Override + public BukkitPluginMessages getPluginMessages() { + return messages; + } + + /** + * Sends a message with the plugin prefix. + * + * @param sender Who receives the message. + * @param message The message. + */ + + public void sendMessage(final CommandSender sender, final String message) { + sender.sendMessage(messages.prefix + " " + message); + } + +} \ No newline at end of file diff --git a/bukkit/src/main/java/fr/skyost/serialkey/bukkit/command/BukkitGetKeyCommand.java b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/command/BukkitGetKeyCommand.java new file mode 100644 index 0000000..8c41160 --- /dev/null +++ b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/command/BukkitGetKeyCommand.java @@ -0,0 +1,78 @@ +package fr.skyost.serialkey.bukkit.command; + +import com.google.common.base.Joiner; +import fr.skyost.serialkey.bukkit.BukkitTypeConverter; +import fr.skyost.serialkey.bukkit.SerialKey; +import fr.skyost.serialkey.core.command.GetKeyCommand; +import org.bukkit.ChatColor; +import org.bukkit.block.Block; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.PluginDescriptionFile; + +/** + * Represents the
/serialkey getkey
command executor. + */ + +public class BukkitGetKeyCommand extends GetKeyCommand implements CommandExecutor { + + /** + * Creates a new
/serialkey getkey
command executor instance. + * + * @param plugin The plugin instance. + */ + + public BukkitGetKeyCommand(final SerialKey plugin) { + super(plugin); + } + + @Override + public final boolean onCommand(final CommandSender sender, final Command command, final String label, final String[] args) { + if(args.length < 1) { + return false; + } + + if(args[0].equalsIgnoreCase("getkey")) { + final Player player = sender instanceof Player ? (Player) sender : null; + final Block targeted = player == null ? null : player.getTargetBlock(null, 100); + + super.execute( + BukkitTypeConverter.toSerialKeyPerson(sender), + targeted == null ? null : BukkitTypeConverter.toSerialKeyLocation(targeted.getLocation()), + player == null ? item -> {} : (item -> player.getWorld().dropItemNaturally(player.getEyeLocation(), item)) + ); + return true; + } + + return false; + } + + @Override + protected ItemStack copyItem(final ItemStack item) { + return item.clone(); + } + + /** + * Returns the command's usage. + * + * @return The command's usage. + */ + + public final String getUsage() { + final PluginDescriptionFile description = ((SerialKey)getPlugin()).getDescription(); + final StringBuilder builder = new StringBuilder(); + builder.append(ChatColor.GOLD).append("------------------------------------\n"); + builder.append(ChatColor.GOLD).append(description.getName()); + builder.append(ChatColor.RESET).append(" v").append(description.getVersion()).append("\n"); + builder.append("By ").append(ChatColor.GOLD).append(Joiner.on(' ').join(description.getAuthors())).append("\n"); + builder.append(ChatColor.GOLD).append("------------------------------------\n"); + builder.append(ChatColor.RESET).append(ChatColor.BOLD).append("Commands :\n"); + builder.append(ChatColor.RESET).append(ChatColor.GOLD).append("/serialkey getkey"); + builder.append(ChatColor.RESET ).append(" - Returns the key corresponding to your facing block."); + return builder.toString(); + } + +} \ No newline at end of file diff --git a/src/main/java/fr/skyost/serialkey/config/PluginConfig.java b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/config/BukkitPluginConfig.java similarity index 63% rename from src/main/java/fr/skyost/serialkey/config/PluginConfig.java rename to bukkit/src/main/java/fr/skyost/serialkey/bukkit/config/BukkitPluginConfig.java index cc6bc90..192a306 100644 --- a/src/main/java/fr/skyost/serialkey/config/PluginConfig.java +++ b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/config/BukkitPluginConfig.java @@ -1,21 +1,18 @@ -package fr.skyost.serialkey.config; - -import java.io.File; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; +package fr.skyost.serialkey.bukkit.config; +import fr.skyost.serialkey.bukkit.util.Skyoconfig; +import fr.skyost.serialkey.core.config.SerialKeyConfig; import org.bukkit.ChatColor; import org.bukkit.Material; -import fr.skyost.serialkey.util.Skyoconfig; +import java.io.File; +import java.util.*; /** * The plugin configuration class. */ -public class PluginConfig extends Skyoconfig { +public class BukkitPluginConfig extends Skyoconfig implements SerialKeyConfig { @ConfigOptions(name = "enable.updater") public boolean enableUpdater = true; @@ -37,27 +34,27 @@ public class PluginConfig extends Skyoconfig { public Material keyMaterial = Material.TRIPWIRE_HOOK; @ConfigOptions(name = "key.name") public String keyName = ChatColor.GOLD + "Key"; - @ConfigOptions(name = "key.shape") + @ConfigOptions(name = "key.recipe") public List keyShape = Arrays.asList("A", "B"); @ConfigOptions(name = "master-key.material") public Material masterKeyMaterial = Material.NAME_TAG; @ConfigOptions(name = "master-key.name") public String masterKeyName = ChatColor.DARK_PURPLE + "Master Key"; - @ConfigOptions(name = "master-key.shape") + @ConfigOptions(name = "master-key.recipe") public List masterKeyShape = Arrays.asList("C", "B"); @ConfigOptions(name = "bunch-of-keys.material") public Material bunchOfKeysMaterial = Material.NAME_TAG; @ConfigOptions(name = "bunch-of-keys.name") public String bunchOfKeysName = ChatColor.BLUE + "Bunch of keys"; - @ConfigOptions(name = "bunch-of-keys.shape") + @ConfigOptions(name = "bunch-of-keys.recipe") public List bunchOfKeysShape = Arrays.asList(" D ", "DBD", " D "); @ConfigOptions(name = "padlock-finder.name") public String padlockFinderName = ChatColor.RED + "Padlock finder"; - @ConfigOptions(name = "shape-materials-v1") + @ConfigOptions(name = "recipe-materials-v1") public LinkedHashMap shapeMaterials = new LinkedHashMap<>(); /** @@ -66,8 +63,8 @@ public class PluginConfig extends Skyoconfig { * @param dataFolder The plugin data folder. */ - public PluginConfig(final File dataFolder) { - super(new File(dataFolder, "config.yml"), Collections.singletonList("SerialKey configuration")); + public BukkitPluginConfig(final File dataFolder) { + super(new File(dataFolder, "config.yml"), Collections.singletonList("SerialKey Configuration")); shapeMaterials.put("A", Material.IRON_INGOT.name()); shapeMaterials.put("B", Material.LEVER.name()); @@ -75,4 +72,49 @@ public PluginConfig(final File dataFolder) { shapeMaterials.put("D", Material.STRING.name()); } + @Override + public boolean areKeysReusable() { + return reusableKeys; + } + + @Override + public boolean areLoresEncrypted() { + return encryptLore; + } + + @Override + public boolean canRenameItems() { + return canRenameItems; + } + + @Override + public List getKeyShape() { + return keyShape; + } + + @Override + public List getMasterKeyShape() { + return masterKeyShape; + } + + @Override + public List getBunchOfKeysShape() { + return bunchOfKeysShape; + } + + @Override + public Map getShapeMaterials() { + return shapeMaterials; + } + + @Override + public String getKeyMaterialID() { + return keyMaterial.name(); + } + + @Override + public String getPadlockFinderMaterialID() { + return Material.COMPASS.name(); + } + } diff --git a/bukkit/src/main/java/fr/skyost/serialkey/bukkit/config/BukkitPluginData.java b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/config/BukkitPluginData.java new file mode 100644 index 0000000..a3574db --- /dev/null +++ b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/config/BukkitPluginData.java @@ -0,0 +1,36 @@ +package fr.skyost.serialkey.bukkit.config; + +import fr.skyost.serialkey.bukkit.util.Skyoconfig; +import fr.skyost.serialkey.core.config.SerialKeyData; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * The plugin data class. + */ + +public class BukkitPluginData extends Skyoconfig implements SerialKeyData { + + @ConfigOptions(name = "padlocks") + public List padlocks = new ArrayList<>(); + + /** + * Creates a new plugin data instance. + * + * @param dataFolder The plugin data folder. + */ + + public BukkitPluginData(final File dataFolder) { + super(new File(dataFolder, "data.yml"), Collections.singletonList("SerialKey Data")); + } + + @Override + public Collection getData() { + return padlocks; + } + +} diff --git a/bukkit/src/main/java/fr/skyost/serialkey/bukkit/config/BukkitPluginMessages.java b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/config/BukkitPluginMessages.java new file mode 100644 index 0000000..fe88ee6 --- /dev/null +++ b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/config/BukkitPluginMessages.java @@ -0,0 +1,83 @@ +package fr.skyost.serialkey.bukkit.config; + +import fr.skyost.serialkey.bukkit.util.Skyoconfig; +import fr.skyost.serialkey.core.config.SerialKeyMessages; +import org.bukkit.ChatColor; + +import java.io.File; +import java.util.Collections; + +/** + * The plugin messages class. + */ + +public class BukkitPluginMessages extends Skyoconfig implements SerialKeyMessages { + + @ConfigOptions(name = "messages.prefix") + public String prefix = ChatColor.AQUA + "[SerialKey]"; + @ConfigOptions(name = "messages.permission") + public String messagePermission = ChatColor.RED + "You do not have the permission to perform this action."; + @ConfigOptions(name = "messages.padlock-placed") + public String messagePadlockPlaced = ChatColor.GREEN + "Padlock placed ! If you want to remove it, you have to break this block."; + @ConfigOptions(name = "messages.padlock-removed") + public String messagePadlockRemoved = ChatColor.GOLD + "Padlock removed."; + @ConfigOptions(name = "messages.block-has-padlock") + public String messageBlockHasPadlock = ChatColor.RED + "This block has a padlock."; + @ConfigOptions(name = "messages.padlock-finder-enabled") + public String messagePadlockFinderEnabled = ChatColor.GREEN + "Padlock finder enabled ! Your compasses will now point to its object. You can reset it back to the spawn by doing another right click with any padlock finder."; + @ConfigOptions(name = "messages.padlock-finder-disabled") + public String messagePadlockFinderDisabled = ChatColor.RED + "Padlock finder has been disabled."; + @ConfigOptions(name = "messages.chest-protection") + public String messageChestProtection = ChatColor.RED + "You can't place this key in this chest."; + + /** + * Creates a new plugin messages instance. + * + * @param dataFolder The plugin data folder. + */ + + public BukkitPluginMessages(final File dataFolder) { + super(new File(dataFolder, "messages.yml"), Collections.singletonList("SerialKey Messages")); + } + + @Override + public String getPrefix() { + return prefix; + } + + @Override + public String getPermissionMessage() { + return messagePermission; + } + + @Override + public String getPadlockPlacedMessage() { + return messagePadlockPlaced; + } + + @Override + public String getPadlockRemovedMessage() { + return messagePadlockRemoved; + } + + @Override + public String getBlockHasPadlockMessage() { + return messageBlockHasPadlock; + } + + @Override + public String getPadlockFinderEnabledMessage() { + return messagePadlockFinderEnabled; + } + + @Override + public String getPadlockFinderDisabledMessage() { + return messagePadlockFinderDisabled; + } + + @Override + public String getChestProtectionMessage() { + return messageChestProtection; + } + +} diff --git a/bukkit/src/main/java/fr/skyost/serialkey/bukkit/item/BukkitItemManager.java b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/item/BukkitItemManager.java new file mode 100644 index 0000000..00ea34c --- /dev/null +++ b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/item/BukkitItemManager.java @@ -0,0 +1,162 @@ +package fr.skyost.serialkey.bukkit.item; + +import fr.skyost.serialkey.bukkit.SerialKey; +import fr.skyost.serialkey.bukkit.config.BukkitPluginConfig; +import fr.skyost.serialkey.bukkit.util.Util; +import fr.skyost.serialkey.core.item.PluginItemManager; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.ShapedRecipe; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * The Bukkit item manager class. + */ + +public class BukkitItemManager extends PluginItemManager { + + /** + * The plugin instance. + */ + + private final SerialKey plugin; + + /** + * Creates a new Bukkit item manager instance. + * + * @param plugin The plugin instance. + */ + + public BukkitItemManager(final SerialKey plugin) { + this(plugin, plugin.getPluginConfig(), Util.createItem(plugin.getPluginConfig().keyName, plugin.getPluginConfig().keyMaterial)); + } + + /** + * Creates a new Bukkit item manager instance. + * + * @param plugin The plugin instance. + * @param key The key item. + */ + + private BukkitItemManager(final SerialKey plugin, final BukkitPluginConfig config, final ItemStack key) { + super(plugin.getPluginConfig(), key, Util.createItem(config.masterKeyName, config.masterKeyMaterial), key.clone(), Util.createItem(config.bunchOfKeysName, config.bunchOfKeysMaterial), Util.createItem(config.padlockFinderName, Material.COMPASS)); + + this.plugin = plugin; + getKeyCloneItem().setAmount(2); + } + + @Override + public void createRecipe(final String id, final ItemStack result, final List shape, Map ingredients) { + final ShapedRecipe recipe = new ShapedRecipe(new NamespacedKey(plugin, id), result); + recipe.shape(shape.toArray(new String[0])); + if(ingredients.equals(config.getShapeMaterials())) { + ingredients = fr.skyost.serialkey.core.util.Util.keepAll(ingredients, shape); + } + for(final Map.Entry entry : ingredients.entrySet()) { + recipe.setIngredient(entry.getKey().charAt(0), Material.valueOf(entry.getValue())); + } + Bukkit.addRecipe(recipe); + } + + @Override + protected boolean isItemValid(final ItemStack item) { + return item != null && item.hasItemMeta() && item.getItemMeta().hasDisplayName(); + } + + @Override + protected boolean compareItemsName(final ItemStack item1, final ItemStack item2) { + return item1.getItemMeta().getDisplayName().equals(item2.getItemMeta().getDisplayName()); + } + + @Override + protected boolean compareItemsType(final ItemStack item1, final ItemStack item2) { + return item1.getType() == item2.getType(); + } + + @Override + public List getLore(final ItemStack object) { + if(object == null) { + return new ArrayList<>(); + } + + final ItemMeta meta = object.getItemMeta(); + if(meta == null) { + return new ArrayList<>(); + } + + final List lore = meta.getLore(); + return lore == null ? new ArrayList<>() : lore; + } + + @Override + public void setLore(final ItemStack object, final List lore) { + final ItemMeta meta = object.getItemMeta(); + meta.setLore(lore); + object.setItemMeta(meta); + } + + /** + * Checks if the specified inventory is a bunch of keys (blank or used). + * + * @param inventory The inventory. + * + * @return true : yes. + *
false : no. + */ + + public boolean isBunchOfKeys(final Inventory inventory) { + final ItemStack bunchOfKeys = getBunchOfKeysItem(); + return inventory.getName().equals(bunchOfKeys.getItemMeta().getDisplayName()) && inventory.getSize() == 9; + } + + /** + * Creates an inventory for the specified bunch of keys and opens it for the specified players. + * + * @param bunchOfKeys The bunch of keys item. + * @param players The players. + * + * @return The inventory. + */ + + public Inventory createInventory(final ItemStack bunchOfKeys, final Player... players) { + final Inventory inventory = Bukkit.createInventory(null, 9, bunchOfKeys.getItemMeta().getDisplayName()); + inventory.setMaxStackSize(1); + + final List lore = getLore(bunchOfKeys); + final int n = lore.size(); + for(int i = 0; i < n; i++) { + final ItemStack key = getKeyItem().clone(); + final ItemMeta meta = key.getItemMeta(); + meta.setLore(Arrays.asList(lore.get(i), lore.get(++i))); + key.setItemMeta(meta); + inventory.addItem(key); + } + + /*final List locations = unlocker.getLocations(bunchOfKeys); + if(!locations.isEmpty()) { + for(final SerialKeyLocation object : locations) { + final ItemStack key = getKeyItem().clone(); + unlocker.addLocation(key, object); + inventory.addItem(key); + } + }*/ + + if(players != null) { + for(final Player player : players) { + player.openInventory(inventory); + } + } + + return inventory; + } + +} \ No newline at end of file diff --git a/bukkit/src/main/java/fr/skyost/serialkey/bukkit/listener/BukkitBlocksListener.java b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/listener/BukkitBlocksListener.java new file mode 100644 index 0000000..5d30281 --- /dev/null +++ b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/listener/BukkitBlocksListener.java @@ -0,0 +1,105 @@ +package fr.skyost.serialkey.bukkit.listener; + +import fr.skyost.serialkey.bukkit.BukkitTypeConverter; +import fr.skyost.serialkey.bukkit.util.DoorUtil; +import fr.skyost.serialkey.core.SerialKeyPlugin; +import fr.skyost.serialkey.core.listener.BlocksListener; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.event.Cancellable; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.*; +import org.bukkit.event.entity.EntityBreakDoorEvent; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.ArrayList; +import java.util.List; + +/** + * A listener that allows to listen blocks related events. + */ + +public class BukkitBlocksListener extends BlocksListener implements Listener { + + /** + * Creates a new blocks listener instance. + * + * @param plugin The plugin. + */ + + public BukkitBlocksListener(final SerialKeyPlugin plugin) { + super(plugin); + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + private void onBlockPlace(final BlockPlaceEvent event) { + super.onBlockPlace(event.getItemInHand(), () -> event.setCancelled(true)); + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + private void onEntityBreakDoor(final EntityBreakDoorEvent event) { + super.onBlockBreakByCreature(event.getBlock().getLocation(), () -> event.setCancelled(true)); + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + private void onEntityExplode(final EntityExplodeEvent event) { + final List blocks = event.blockList(); + for(final Block block : new ArrayList<>(blocks)) { + super.onBlockExplode(block.getLocation(), () -> blocks.remove(block)); + protectUpperDoor(block, () -> blocks.remove(block)); + } + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + private void onBlockRedstone(final BlockRedstoneEvent event) { + super.onBlockPoweredByRedstone(event.getBlock().getLocation(), () -> event.setNewCurrent(0)); + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + private void onBlockBreak(final BlockBreakEvent event) { + if(protectUpperDoor(event)) { + plugin.sendMessage(BukkitTypeConverter.toSerialKeyPerson(event.getPlayer()), plugin.getPluginMessages().getBlockHasPadlockMessage()); + } + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + private void onBlockIgnite(final BlockIgniteEvent event) { + protectUpperDoor(event); + } + + /** + * Protects the door that is up to the specified block. + * + * @param event The block event. + * @param The event type. + * + * @return Whether the event has been cancelled or not. + */ + + private boolean protectUpperDoor(final T event) { + return protectUpperDoor(event.getBlock(), () -> event.setCancelled(true)); + } + + /** + * Protects the door that is up to the specified block. + * + * @param event The block event. + * @param cancelEvent The runnable that allows to cancel the event. + * + * @return Whether the event has been cancelled or not. + */ + + private boolean protectUpperDoor(final Block event, final Runnable cancelEvent) { + final Block up = event.getRelative(BlockFace.UP); + if(DoorUtil.getInstance(up.getBlockData()) == null) { + return false; + } + + return super.cancelIfHasPadlock(up.getLocation(), cancelEvent); + } + +} diff --git a/bukkit/src/main/java/fr/skyost/serialkey/bukkit/listener/BukkitBunchOfKeysListener.java b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/listener/BukkitBunchOfKeysListener.java new file mode 100644 index 0000000..4227ede --- /dev/null +++ b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/listener/BukkitBunchOfKeysListener.java @@ -0,0 +1,130 @@ +package fr.skyost.serialkey.bukkit.listener; + +import fr.skyost.serialkey.bukkit.item.BukkitItemManager; +import fr.skyost.serialkey.core.SerialKeyPlugin; +import fr.skyost.serialkey.core.listener.BunchOfKeysListener; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +/** + * A listener that allows to listen bunch of keys related events. + */ + +public class BukkitBunchOfKeysListener extends BunchOfKeysListener implements Listener { + + /** + * The item manager. + */ + + private BukkitItemManager itemManager; + + /** + * Creates a new bunch of keys listener instance. + * + * @param plugin The plugin. + */ + + public BukkitBunchOfKeysListener(final SerialKeyPlugin plugin) { + super(plugin); + } + + @Override + public void setPlugin(final SerialKeyPlugin plugin) { + if(!(plugin.getItemManager() instanceof BukkitItemManager)) { + throw new IllegalArgumentException("Invalid item manager provided."); + } + + super.setPlugin(plugin); + itemManager = (BukkitItemManager)plugin.getItemManager(); + } + + @EventHandler(priority = EventPriority.HIGHEST) + private void onPlayerInteract(final PlayerInteractEvent event) { + switch(event.getAction()) { + case RIGHT_CLICK_BLOCK: + super.onPlayerRightClickOnBlock(event.getClickedBlock().getLocation(), event.getItem(), () -> cancelIfCreateInventory(event)); + break; + case RIGHT_CLICK_AIR: + super.onPlayerRightClickOnAir(event.getItem(), () -> cancelIfCreateInventory(event)); + break; + } + } + + @EventHandler + private void onInventoryClick(final InventoryClickEvent event) { + if(!itemManager.isBunchOfKeys(event.getInventory())) { + return; + } + + final ItemStack item = event.getCurrentItem(); + if(item.getType() != Material.AIR && !itemManager.isUsedKey(item)) { + event.setCancelled(true); + } + } + + @EventHandler + private void onInventoryClose(final InventoryCloseEvent event) { + final Inventory inventory = event.getInventory(); + if(!itemManager.isBunchOfKeys(inventory)) { + return; + } + + final HumanEntity player = event.getPlayer(); + ItemStack bunchOfKeys = player.getInventory().getItemInOffHand(); + if(!itemManager.isBunchOfKeys(bunchOfKeys)) { + bunchOfKeys = player.getInventory().getItemInMainHand(); + } + + if(bunchOfKeys.getAmount() > 1) { + final ItemStack clone = bunchOfKeys.clone(); + clone.setAmount(bunchOfKeys.getAmount() - 1); + player.getWorld().dropItemNaturally(player.getEyeLocation(), clone); + + bunchOfKeys.setAmount(1); + } + + unlocker.clearLocations(bunchOfKeys); + + final int n = inventory.getSize(); + for(int i = 0; i < n; i++) { + final ItemStack item = inventory.getItem(i); + if(item == null) { + continue; + } + + if(!itemManager.isUsedKey(item)) { + player.getWorld().dropItemNaturally(player.getLocation(), item); + continue; + } + + unlocker.addLocation(bunchOfKeys, item); + if(item.getAmount() > 1) { + final ItemStack clone = item.clone(); + clone.setAmount(item.getAmount() - 1); + player.getWorld().dropItemNaturally(player.getEyeLocation(), clone); + } + } + } + + /** + * Cancels the specified event if the inventory has been successfully created. + * + * @param event The event. + */ + + private void cancelIfCreateInventory(final PlayerInteractEvent event) { + if(itemManager.createInventory(event.getItem(), event.getPlayer()) != null) { + event.setCancelled(true); + } + } + +} diff --git a/bukkit/src/main/java/fr/skyost/serialkey/bukkit/listener/BukkitGlobalListener.java b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/listener/BukkitGlobalListener.java new file mode 100644 index 0000000..43725e3 --- /dev/null +++ b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/listener/BukkitGlobalListener.java @@ -0,0 +1,107 @@ +package fr.skyost.serialkey.bukkit.listener; + +import fr.skyost.serialkey.bukkit.BukkitTypeConverter; +import fr.skyost.serialkey.bukkit.util.DoorUtil; +import fr.skyost.serialkey.core.SerialKeyPlugin; +import fr.skyost.serialkey.core.listener.GlobalListener; +import fr.skyost.serialkey.core.object.SerialKeyLocation; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.block.Block; +import org.bukkit.block.Chest; +import org.bukkit.block.data.type.TrapDoor; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.inventory.PrepareItemCraftEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.CraftingInventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.ShapedRecipe; + +/** + * A listener that allows to globally listen plugin events. + */ + +public class BukkitGlobalListener extends GlobalListener implements Listener { + + /** + * Creates a new global listener instance. + * + * @param plugin The plugin. + */ + + public BukkitGlobalListener(final SerialKeyPlugin plugin) { + super(plugin); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + private void onPrepareItemCraft(final PrepareItemCraftEvent event) { + final CraftingInventory craftingTable = event.getInventory(); + final ItemStack result = craftingTable.getResult(); + final String key = event.getRecipe() instanceof ShapedRecipe ? ((ShapedRecipe)event.getRecipe()).getKey().getNamespace() : null; + super.onPreviewItemCraft( + BukkitTypeConverter.toSerialKeyPerson(event.getView().getPlayer()), + craftingTable.getMatrix(), + key, + result, + itemManager.getKeyCloneItem().equals(result) ? (item -> itemManager.isBlankKey(item)) : (item -> item.getType() == Material.COMPASS), + item -> {}, + () -> craftingTable.setResult(null) + ); + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + private void onPlayerInteract(final PlayerInteractEvent event) { + final Player player = event.getPlayer(); + + if(event.getAction() == Action.LEFT_CLICK_BLOCK) { + super.onPlayerLeftClick( + event.getItem(), + BukkitTypeConverter.toSerialKeyLocation(event.getClickedBlock().getLocation()), + BukkitTypeConverter.toSerialKeyPerson(event.getPlayer()), + BukkitTypeConverter.toSerialKeyLocation(event.getPlayer().getLocation()), + item -> player.getWorld().dropItemNaturally(player.getLocation(), item), + () -> player.getInventory().setItemInMainHand(new ItemStack(Material.AIR)), + () -> player.playSound(event.getClickedBlock().getLocation(), Sound.ENTITY_ITEM_BREAK, 1f, 1f), + () -> event.setCancelled(true) + ); + return; + } + + if(event.getAction() == Action.RIGHT_CLICK_BLOCK) { + super.onPlayerRightClick( + event.getItem(), + BukkitTypeConverter.toSerialKeyLocation(event.getClickedBlock().getLocation()), + BukkitTypeConverter.toSerialKeyPerson(event.getPlayer()), + () -> event.setCancelled(true) + ); + } + } + + + @Override + protected ItemStack copy(final ItemStack item) { + return item.clone(); + } + + @Override + protected int getAmount(final ItemStack item) { + return item.getAmount(); + } + + @Override + protected void setAmount(final ItemStack item, final int amount) { + item.setAmount(amount); + } + + @Override + protected boolean isPadlockLocationValid(final SerialKeyLocation location) { + final Block block = BukkitTypeConverter.toBukkitLocation(location).getBlock(); + return block.getState() instanceof Chest || DoorUtil.getInstance(block.getBlockData()) != null || block.getBlockData() instanceof TrapDoor; + } + +} \ No newline at end of file diff --git a/bukkit/src/main/java/fr/skyost/serialkey/bukkit/listener/BukkitHopperListener.java b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/listener/BukkitHopperListener.java new file mode 100644 index 0000000..b7a44f7 --- /dev/null +++ b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/listener/BukkitHopperListener.java @@ -0,0 +1,50 @@ +package fr.skyost.serialkey.bukkit.listener; + +import fr.skyost.serialkey.bukkit.BukkitTypeConverter; +import fr.skyost.serialkey.core.SerialKeyPlugin; +import fr.skyost.serialkey.core.listener.HopperListener; +import fr.skyost.serialkey.core.object.SerialKeyLocation; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Chest; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.inventory.ItemStack; + +/** + * A listener that allows to listen hoppers related events. + */ + +public class BukkitHopperListener extends HopperListener implements Listener { + + /** + * Creates a new hoppers listener instance. + * + * @param plugin The plugin. + */ + + public BukkitHopperListener(final SerialKeyPlugin plugin) { + super(plugin); + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + private void onBlockPlace(final BlockPlaceEvent event) { + final Block block = event.getBlockPlaced(); + if(block.getType() == Material.HOPPER) { + super.onBlockPlace( + BukkitTypeConverter.toSerialKeyLocation(event.getBlockPlaced().getLocation()), + BukkitTypeConverter.toSerialKeyPerson(event.getPlayer()), + () -> event.setCancelled(true) + ); + } + } + + @Override + protected boolean isChest(final SerialKeyLocation location) { + return BukkitTypeConverter.toBukkitLocation(location).getBlock().getState() instanceof Chest; + } + +} diff --git a/bukkit/src/main/java/fr/skyost/serialkey/bukkit/listener/BukkitLostChestsListener.java b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/listener/BukkitLostChestsListener.java new file mode 100644 index 0000000..2aa269c --- /dev/null +++ b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/listener/BukkitLostChestsListener.java @@ -0,0 +1,56 @@ +package fr.skyost.serialkey.bukkit.listener; + +import fr.skyost.serialkey.bukkit.BukkitTypeConverter; +import fr.skyost.serialkey.core.SerialKeyPlugin; +import fr.skyost.serialkey.core.listener.LostChestsListener; +import org.bukkit.Location; +import org.bukkit.block.Chest; +import org.bukkit.block.DoubleChest; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; + +/** + * A listener that allows to listen chests related events. + */ + +public class BukkitLostChestsListener extends LostChestsListener implements Listener { + + /** + * Creates a new lost chests listener instance. + * + * @param plugin The plugin. + */ + + public BukkitLostChestsListener(final SerialKeyPlugin plugin) { + super(plugin); + } + + @EventHandler(priority = EventPriority.HIGHEST) + private void onInventoryClick(final InventoryClickEvent event) { + final InventoryHolder holder = event.getInventory().getHolder(); + + Location location = null; + if(holder instanceof Chest) { + location = ((Chest)holder).getLocation(); + } + else if(holder instanceof DoubleChest) { + location = ((DoubleChest)holder).getLocation(); + } + + if(location == null) { + return; + } + + super.onInventoryClick( + BukkitTypeConverter.toSerialKeyPerson(event.getWhoClicked()), + BukkitTypeConverter.toSerialKeyLocation(location), + event.getCurrentItem(), + () -> event.setCancelled(true) + ); + } + +} \ No newline at end of file diff --git a/bukkit/src/main/java/fr/skyost/serialkey/bukkit/listener/BukkitPadlockFinderListener.java b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/listener/BukkitPadlockFinderListener.java new file mode 100644 index 0000000..bd8c274 --- /dev/null +++ b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/listener/BukkitPadlockFinderListener.java @@ -0,0 +1,48 @@ +package fr.skyost.serialkey.bukkit.listener; + +import fr.skyost.serialkey.bukkit.BukkitTypeConverter; +import fr.skyost.serialkey.core.SerialKeyPlugin; +import fr.skyost.serialkey.core.listener.PadlockFinderListener; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; + +/** + * A listener that allows to listen padlock finder related events. + */ + +public class BukkitPadlockFinderListener extends PadlockFinderListener implements Listener { + + /** + * Creates a new padlock finder listener instance. + * + * @param plugin The plugin. + */ + + public BukkitPadlockFinderListener(final SerialKeyPlugin plugin) { + super(plugin); + } + + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + private void onPlayerInteract(final PlayerInteractEvent event) { + if(event.getAction() != Action.RIGHT_CLICK_AIR && event.getAction() != Action.RIGHT_CLICK_BLOCK) { + return; + } + + final Player player = event.getPlayer(); + super.onPlayerRightClick( + event.getItem(), + BukkitTypeConverter.toSerialKeyPerson(player), + BukkitTypeConverter.toSerialKeyLocation(player.getWorld().getSpawnLocation()), + BukkitTypeConverter.toSerialKeyLocation(player.getCompassTarget()), + location -> player.setCompassTarget(BukkitTypeConverter.toBukkitLocation(location)), + () -> event.setCancelled(true) + ); + } + +} diff --git a/bukkit/src/main/java/fr/skyost/serialkey/bukkit/padlock/BukkitPadlockManager.java b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/padlock/BukkitPadlockManager.java new file mode 100644 index 0000000..184e5fb --- /dev/null +++ b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/padlock/BukkitPadlockManager.java @@ -0,0 +1,80 @@ +package fr.skyost.serialkey.bukkit.padlock; + +import fr.skyost.serialkey.bukkit.BukkitTypeConverter; +import fr.skyost.serialkey.bukkit.SerialKey; +import fr.skyost.serialkey.bukkit.util.DoorUtil; +import fr.skyost.serialkey.core.object.SerialKeyLocation; +import fr.skyost.serialkey.core.padlock.PluginPadlockManager; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.block.Chest; +import org.bukkit.block.DoubleChest; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; + +/** + * The Bukkit padlock manager. + */ + +public class BukkitPadlockManager extends PluginPadlockManager { + + /** + * Creates a new Bukkit padlock manager instance. + * + * @param plugin The plugin. + */ + + public BukkitPadlockManager(final SerialKey plugin) { + super(plugin); + } + + @Override + public boolean fixLocation(final SerialKeyLocation location) { + if(hasPadlock(location, false)) { + return false; + } + + final Block block = BukkitTypeConverter.toBukkitLocation(location).getBlock(); + if(block.getState() instanceof Chest) { + final InventoryHolder holder = ((org.bukkit.block.Chest)block.getState()).getInventory().getHolder(); + if(holder instanceof DoubleChest) { + final SerialKeyLocation left = pluginLocationToSerialKeyLocation(((org.bukkit.block.Chest)((DoubleChest)holder).getLeftSide()).getLocation()); + if(hasPadlock(left, false)) { + location.setX(left.getX()); + location.setZ(left.getZ()); + return true; + } + + final SerialKeyLocation right = pluginLocationToSerialKeyLocation(((org.bukkit.block.Chest)((DoubleChest)holder).getRightSide()).getLocation()); + if(hasPadlock(right, false)) { + location.setX(right.getX()); + location.setZ(right.getZ()); + return true; + } + } + return false; + } + + if(DoorUtil.getInstance(block.getBlockData()) != null) { + location.setY(DoorUtil.getBlockBelow(block).getY()); + if(hasPadlock(location, false)) { + return true; + } + final Block doubleDoor = DoorUtil.getDoubleDoor(block); + if(doubleDoor != null) { + final Location doubleDoorLocation = doubleDoor.getLocation(); + location.setX(doubleDoorLocation.getBlockX()); + location.setZ(doubleDoorLocation.getBlockZ()); + } + return true; + } + + return false; + } + + @Override + protected SerialKeyLocation pluginLocationToSerialKeyLocation(final Location location) { + return BukkitTypeConverter.toSerialKeyLocation(location); + } + +} \ No newline at end of file diff --git a/bukkit/src/main/java/fr/skyost/serialkey/bukkit/unlocker/BukkitUnlocker.java b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/unlocker/BukkitUnlocker.java new file mode 100644 index 0000000..678e303 --- /dev/null +++ b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/unlocker/BukkitUnlocker.java @@ -0,0 +1,35 @@ +package fr.skyost.serialkey.bukkit.unlocker; + +import fr.skyost.serialkey.bukkit.SerialKey; +import fr.skyost.serialkey.bukkit.util.Util; +import fr.skyost.serialkey.core.unlocker.PluginUnlocker; +import org.bukkit.ChatColor; +import org.bukkit.inventory.ItemStack; + +/** + * The Bukkit unlocker class. + */ + +public class BukkitUnlocker extends PluginUnlocker { + + /** + * Creates a new Bukkit unlocker instance. + * + * @param plugin The plugin instance. + */ + + public BukkitUnlocker(final SerialKey plugin) { + super(plugin); + } + + @Override + protected String randomColor() { + return Util.randomChatColor(ChatColor.BOLD, ChatColor.ITALIC, ChatColor.UNDERLINE, ChatColor.STRIKETHROUGH, ChatColor.MAGIC, ChatColor.BLACK).toString(); + } + + @Override + protected String stripColor(final String string) { + return ChatColor.stripColor(string); + } + +} \ No newline at end of file diff --git a/src/main/java/fr/skyost/serialkey/util/DoorUtil.java b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/util/DoorUtil.java similarity index 91% rename from src/main/java/fr/skyost/serialkey/util/DoorUtil.java rename to bukkit/src/main/java/fr/skyost/serialkey/bukkit/util/DoorUtil.java index 985d8a1..55c4f63 100644 --- a/src/main/java/fr/skyost/serialkey/util/DoorUtil.java +++ b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/util/DoorUtil.java @@ -1,4 +1,4 @@ -package fr.skyost.serialkey.util; +package fr.skyost.serialkey.bukkit.util; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; @@ -24,11 +24,11 @@ public class DoorUtil { }; /** - * Checks if the specified MaterialData is a Door. + * Checks if the specified BlockData is a Door. * - * @param data The MaterialData. + * @param data The BlockData. * - * @return true : If the specified MaterialData. + * @return true : If the specified BlockData is a door. *
false : Otherwise. */ diff --git a/src/main/java/fr/skyost/serialkey/util/Skyoconfig.java b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/util/Skyoconfig.java similarity index 98% rename from src/main/java/fr/skyost/serialkey/util/Skyoconfig.java rename to bukkit/src/main/java/fr/skyost/serialkey/bukkit/util/Skyoconfig.java index 9c6317f..a7af81a 100644 --- a/src/main/java/fr/skyost/serialkey/util/Skyoconfig.java +++ b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/util/Skyoconfig.java @@ -1,8 +1,7 @@ -package fr.skyost.serialkey.util; +package fr.skyost.serialkey.bukkit.util; import com.google.common.base.Joiner; import com.google.common.primitives.Primitives; - import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; @@ -174,7 +173,7 @@ private void saveConfig(final YamlConfiguration config) throws IOException { * @param name The Field's name. Will be the path. * @param config The YamlConfiguration. * - * @throws ParseException If the JSON parser fails to parse a Location or a Vector. + * @throws ParseException If the JSON parser fails to parse a SerialKeyLocation or a Vector. * @throws IllegalAccessException If Skyoconfig does not have access to the Field or the Method valueOf of a Primitive. * @throws InvocationTargetException Invoked if the Skyoconfig fails to use valueOf for a Primitive. * @throws NoSuchMethodException Same as InvocationTargetException. @@ -219,7 +218,7 @@ private void saveField(final Field field, final String name, final YamlConfigura * * @return The deserialized value of the specified Object. * - * @throws ParseException If the JSON parser fails to parse a Location or a Vector. + * @throws ParseException If the JSON parser fails to parse a SerialKeyLocation or a Vector. * @throws IllegalAccessException If Skyoconfig does not have access to the Field or the Method valueOf of a Primitive. * @throws InvocationTargetException Invoked if the Skyoconfig fails to use valueOf for a Primitive. * @throws NoSuchMethodException Same as InvocationTargetException. diff --git a/src/main/java/fr/skyost/serialkey/util/Skyupdater.java b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/util/Skyupdater.java similarity index 97% rename from src/main/java/fr/skyost/serialkey/util/Skyupdater.java rename to bukkit/src/main/java/fr/skyost/serialkey/bukkit/util/Skyupdater.java index 8c9cab6..8edaa5f 100644 --- a/src/main/java/fr/skyost/serialkey/util/Skyupdater.java +++ b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/util/Skyupdater.java @@ -1,7 +1,6 @@ -package fr.skyost.serialkey.util; +package fr.skyost.serialkey.bukkit.util; import com.google.common.base.Joiner; - import org.bukkit.Bukkit; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.YamlConfiguration; @@ -10,13 +9,7 @@ import org.json.simple.JSONObject; import org.json.simple.JSONValue; -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; +import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.util.logging.Level; @@ -323,7 +316,7 @@ public static boolean compareVersions(final String versionTo, final String versi * Gets the formatted name of a version. *
Used for the method compareVersions(...) of this class. * - * @param version The version you want to format. + * @param version The version you want to addLocation. * @param separator The separator between the numbers of this version. * @param maxWidth The max width of the formatted version. * diff --git a/bukkit/src/main/java/fr/skyost/serialkey/bukkit/util/Util.java b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/util/Util.java new file mode 100644 index 0000000..8f7a35f --- /dev/null +++ b/bukkit/src/main/java/fr/skyost/serialkey/bukkit/util/Util.java @@ -0,0 +1,56 @@ +package fr.skyost.serialkey.bukkit.util; + +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +/** + * Contains some useful methods. + */ + +public class Util { + + /** + * Picks a random ChatColor. + * + * @param exclude If you want to exclude some colors. + * + * @return A random ChatColor. + */ + + public static ChatColor randomChatColor(final ChatColor... exclude) { + final List excludeList = Arrays.asList(exclude); + final ChatColor[] values = ChatColor.values(); + final Random random = new Random(); + + ChatColor result; + do { + result = values[random.nextInt(values.length)]; + } + while(excludeList.contains(result)); + return result; + } + + /** + * Creates an item with a custom name. + * + * @param name The name. + * @param material The item's material. + * + * @return The item. + */ + + public static ItemStack createItem(final String name, final Material material) { + final ItemStack item = new ItemStack(material); + final ItemMeta meta = item.getItemMeta(); + meta.setDisplayName(name); + item.setItemMeta(meta); + return item; + } + +} \ No newline at end of file diff --git a/core/build.gradle b/core/build.gradle new file mode 100644 index 0000000..46fe2cf --- /dev/null +++ b/core/build.gradle @@ -0,0 +1,22 @@ +plugins { + id 'com.github.johnrengelman.shadow' version '2.0.4' + id 'java' + id 'maven' +} + +version = '1.0' +sourceCompatibility = 1.8 + +repositories { + mavenCentral() +} + +dependencies { + compileOnly 'com.google.code.gson:gson:2.8.0' +} + +shadowJar { + destinationDir = new File(rootProject.projectDir, 'build/release/') + version = project.version + mergeServiceFiles() +} diff --git a/core/src/main/java/fr/skyost/serialkey/core/SerialKeyPlugin.java b/core/src/main/java/fr/skyost/serialkey/core/SerialKeyPlugin.java new file mode 100644 index 0000000..6fb9aa5 --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/SerialKeyPlugin.java @@ -0,0 +1,70 @@ +package fr.skyost.serialkey.core; + +import fr.skyost.serialkey.core.config.SerialKeyConfig; +import fr.skyost.serialkey.core.config.SerialKeyMessages; +import fr.skyost.serialkey.core.item.PluginItemManager; +import fr.skyost.serialkey.core.object.SerialKeyPerson; +import fr.skyost.serialkey.core.padlock.PluginPadlockManager; +import fr.skyost.serialkey.core.unlocker.PluginUnlocker; + +/** + * Represents a SerialKey plugin. + * + * @param The ItemStack class. + * @param The Location class. + */ + +public interface SerialKeyPlugin { + + /** + * Returns the item manager. + * + * @return The item manager. + */ + + PluginItemManager getItemManager(); + + /** + * Returns the unlocker. + * + * @return The unlocker. + */ + + PluginUnlocker getUnlocker(); + + /** + * Returns the padlock manager. + * + * @return The padlock manager. + */ + + PluginPadlockManager getPadlockManager(); + + /** + * Returns the plugin configuration. + * + * @return The plugin configuration. + */ + + SerialKeyConfig getPluginConfig(); + + /** + * Returns the plugin messages. + * + * @return The plugin messages. + */ + + SerialKeyMessages getPluginMessages(); + + /** + * Sends a message with the plugin prefix. + * + * @param person Who receives the message. + * @param message The message. + */ + + default void sendMessage(final SerialKeyPerson person, final String message) { + person.sendMessage(getPluginMessages().getPrefix() + " " + message); + } + +} \ No newline at end of file diff --git a/core/src/main/java/fr/skyost/serialkey/core/command/GetKeyCommand.java b/core/src/main/java/fr/skyost/serialkey/core/command/GetKeyCommand.java new file mode 100644 index 0000000..17c8446 --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/command/GetKeyCommand.java @@ -0,0 +1,84 @@ +package fr.skyost.serialkey.core.command; + +import fr.skyost.serialkey.core.SerialKeyPlugin; +import fr.skyost.serialkey.core.object.PersonIdentity; +import fr.skyost.serialkey.core.object.SerialKeyLocation; +import fr.skyost.serialkey.core.object.SerialKeyPerson; + +import java.util.function.Consumer; + +/** + * Represents the
/serialkey getkey
command executor. + */ + +public abstract class GetKeyCommand { + + /** + * The plugin instance. + */ + + private final SerialKeyPlugin plugin; + + /** + * Creates a new
/serialkey getkey
command executor instance. + * + * @param plugin The plugin instance. + */ + + public GetKeyCommand(final SerialKeyPlugin plugin) { + this.plugin = plugin; + } + + /** + * Executes the command. + * + * @param executor The executor. + * @param targetedBlock The targeted block. + * @param dropItem The function that allows to drop an item. + * + * @return Number of affected entities. + */ + + protected int execute(final SerialKeyPerson executor, final SerialKeyLocation targetedBlock, final Consumer dropItem) { + if(executor.getIdentity().getType() != PersonIdentity.Type.PLAYER) { + plugin.sendMessage(executor, "&cYou must be a player to run this command."); + return 0; + } + + if(!executor.hasPermission("serialkey.command.getkey")) { + plugin.sendMessage(executor, plugin.getPluginMessages().getPermissionMessage()); + return 0; + } + + if(targetedBlock != null && plugin.getPadlockManager().hasPadlock(targetedBlock)) { + final I key = copyItem(plugin.getItemManager().getKeyItem()); + plugin.getUnlocker().addLocation(key, targetedBlock); + dropItem.accept(key); + return 1; + } + + plugin.sendMessage(executor, "&cYou must be looking at a valid block."); + return 0; + } + + /** + * Returns the plugin instance. + * + * @return The plugin instance. + */ + + public SerialKeyPlugin getPlugin() { + return plugin; + } + + /** + * Copies an item. + * + * @param item The item. + * + * @return The copied item. + */ + + protected abstract I copyItem(final I item); + +} \ No newline at end of file diff --git a/core/src/main/java/fr/skyost/serialkey/core/config/SerialKeyConfig.java b/core/src/main/java/fr/skyost/serialkey/core/config/SerialKeyConfig.java new file mode 100644 index 0000000..d531df4 --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/config/SerialKeyConfig.java @@ -0,0 +1,87 @@ +package fr.skyost.serialkey.core.config; + +import java.util.List; +import java.util.Map; + +/** + * Represents a SerialKey configuration. + */ + +public interface SerialKeyConfig { + + /** + * Whether keys are reusable or not. + * + * @return true Yes. + *
false Otherwise. + */ + + boolean areKeysReusable(); + + /** + * Whether lores are encrypted or not. + * + * @return true Yes. + *
false Otherwise. + */ + + boolean areLoresEncrypted(); + + /** + * Whether players can rename items or not. + * + * @return true Yes. + *
false Otherwise. + */ + + boolean canRenameItems(); + + /** + * The key shape. + * + * @return The key shape. + */ + + List getKeyShape(); + + /** + * The master key shape. + * + * @return The master key shape. + */ + + List getMasterKeyShape(); + + /** + * The bunch of keys shape. + * + * @return The bunch of keys shape. + */ + + List getBunchOfKeysShape(); + + /** + * Returns the shape materials. + * + * @return The shape materials. + */ + + Map getShapeMaterials(); + + /** + * Returns the key material ID. + * + * @return The key material ID. + */ + + String getKeyMaterialID(); + + /** + * Returns the padlock finder material ID. + * + * @return The padlock finder material ID. + */ + + String getPadlockFinderMaterialID(); + +} \ No newline at end of file diff --git a/core/src/main/java/fr/skyost/serialkey/core/config/SerialKeyData.java b/core/src/main/java/fr/skyost/serialkey/core/config/SerialKeyData.java new file mode 100644 index 0000000..80ef4f7 --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/config/SerialKeyData.java @@ -0,0 +1,100 @@ +package fr.skyost.serialkey.core.config; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; +import fr.skyost.serialkey.core.object.SerialKeyLocation; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; + +/** + * Allows to collect and save padlocks. + */ + +public interface SerialKeyData { + + /** + * Returns the raw data. + * + * @return The raw data. + */ + + Collection getData(); + + /** + * Returns the padlocks. + * + * @return The padlocks. + */ + + default Collection getPadlocks() { + final HashSet result = new HashSet<>(); + for(final String padlock : getData()) { + result.add(locationFromJson(padlock)); + } + return result; + } + + /** + * Adds a padlock. + * + * @param location The padlock location. + */ + + default void addPadlock(final SerialKeyLocation location) { + getData().add(locationToJson(location)); + } + + /** + * Removes a padlock. + * + * @param location The padlock location. + * + * @return Whether a padlock has been removed. + */ + + default boolean removePadlock(final SerialKeyLocation location) { + final Collection data = getData(); + for(final String padlock : new ArrayList<>(data)) { + if(locationFromJson(padlock).equals(location)) { + data.remove(padlock); + return true; + } + } + + return false; + } + + /** + * Converts a JSON String to a SerialKey location. + * + * @param json The JSON String. + * + * @return The SerialKey location. + */ + + default SerialKeyLocation locationFromJson(final String json) { + final JsonObject representation = new JsonParser().parse(json).getAsJsonObject(); + return new SerialKeyLocation(representation.get("world").getAsString(), representation.get("x").getAsInt(), representation.get("y").getAsInt(), representation.get("z").getAsInt()); + } + + /** + * Converts a SerialKey location to a JSON String. + * + * @param location The SerialKey location. + * + * @return The JSON String. + */ + + default String locationToJson(final SerialKeyLocation location) { + final JsonObject representation = new JsonObject(); + representation.add("world", new JsonPrimitive(location.getWorld())); + representation.add("x", new JsonPrimitive(location.getX())); + representation.add("y", new JsonPrimitive(location.getY())); + representation.add("z", new JsonPrimitive(location.getZ())); + return representation.toString(); + } + +} \ No newline at end of file diff --git a/core/src/main/java/fr/skyost/serialkey/core/config/SerialKeyMessages.java b/core/src/main/java/fr/skyost/serialkey/core/config/SerialKeyMessages.java new file mode 100644 index 0000000..1371a4d --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/config/SerialKeyMessages.java @@ -0,0 +1,73 @@ +package fr.skyost.serialkey.core.config; + +/** + * Allows to configure SerialKey messages. + */ + +public interface SerialKeyMessages { + + /** + * The plugin messages prefix. + * + * @return The plugin messages prefix. + */ + + String getPrefix(); + + /** + * Returns the permission message. + * + * @return The permission message. + */ + + String getPermissionMessage(); + + /** + * Returns the "padlock placed" message. + * + * @return The "padlock placed" message. + */ + + String getPadlockPlacedMessage(); + + /** + * Returns the "padlock removed" message. + * + * @return The "padlock removed" message. + */ + + String getPadlockRemovedMessage(); + + /** + * Returns the "block has a padlock" message. + * + * @return The "block has a padlock" message. + */ + + String getBlockHasPadlockMessage(); + + /** + * Returns the "padlock finder enabled" message. + * + * @return The "padlock finder enabled" message. + */ + + String getPadlockFinderEnabledMessage(); + + /** + * Returns the "padlock finder disabled" message. + * + * @return The "padlock finder disabled" message. + */ + + String getPadlockFinderDisabledMessage(); + + /** + * Returns the "chest protected" message. + * + * @return The "chest protected" message. + */ + + String getChestProtectionMessage(); + +} \ No newline at end of file diff --git a/core/src/main/java/fr/skyost/serialkey/core/item/ItemManager.java b/core/src/main/java/fr/skyost/serialkey/core/item/ItemManager.java new file mode 100644 index 0000000..12c8a7f --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/item/ItemManager.java @@ -0,0 +1,316 @@ +package fr.skyost.serialkey.core.item; + +import java.util.List; + +/** + * The class that allows to manage plugin items. + * + * @param ItemStack class. + */ + +public abstract class ItemManager { + + /** + * The key item. + */ + + private T key; + + /** + * The master key item. + */ + + private T masterKey; + + /** + * The key clone item. + */ + + private T keyClone; + + /** + * The bunch of keys item. + */ + + private T bunchOfKeys; + + /** + * The padlock finder item. + */ + + private T padlockFinder; + + /** + * Creates a new item manager instance. + * + * @param key The key item. + * @param masterKey The master key item. + * @param keyClone The key clone item. + * @param bunchOfKeys The bunch of keys item. + * @param padlockFinder The padlock finder item. + */ + + protected ItemManager(final T key, final T masterKey, final T keyClone, final T bunchOfKeys, final T padlockFinder) { + this.key = key; + this.masterKey = masterKey; + this.keyClone = keyClone; + this.bunchOfKeys = bunchOfKeys; + this.padlockFinder = padlockFinder; + } + + /** + * Returns the key item. + * + * @return The key item. + */ + + public T getKeyItem() { + return key; + } + + /** + * Sets the key item. + * + * @param key The key item. + */ + + public void setKeyItem(final T key) { + this.key = key; + } + + /** + * Returns the master key item. + * + * @return The master key item. + */ + + public T getMasterKeyItem() { + return masterKey; + } + + /** + * Sets the master key item. + * + * @param masterKey The master key item. + */ + + public void setMasterKeyItem(final T masterKey) { + this.masterKey = masterKey; + } + + /** + * Returns the key clone item. + * + * @return The key clone item. + */ + + public T getKeyCloneItem() { + return keyClone; + } + + /** + * Sets the key clone item. + * + * @param keyClone The key clone item. + */ + + public void setKeyCloneItem(final T keyClone) { + this.keyClone = keyClone; + } + + /** + * Returns the bunch of keys item. + * + * @return The bunch of keys item. + */ + + public T getBunchOfKeysItem() { + return bunchOfKeys; + } + + /** + * Sets the bunch of keys item. + * + * @param bunchOfKeys The bunch of keys item. + */ + + public void setBunchOfKeysItem(final T bunchOfKeys) { + this.bunchOfKeys = bunchOfKeys; + } + + /** + * Returns the padlock finder item. + * + * @return The padlock finder item. + */ + + public T getPadlockFinderItem() { + return padlockFinder; + } + + /** + * Sets the padlock finder item. + * + * @param padlockFinder The padlock finder item. + */ + + public void setPadlockFinderItem(final T padlockFinder) { + this.padlockFinder = padlockFinder; + } + + /** + * Checks if the specified item is a key (blank or used). + * + * @param item The item. + * + * @return true : yes. + *
false : no. + */ + + public abstract boolean isKey(final T item); + + /** + * Checks if the specified item is a blank key. + * + * @param item The item. + * + * @return true : yes. + *
false : no. + */ + + public boolean isBlankKey(final T item) { + return isKey(item) && !isUsedKey(item); + } + + /** + * Checks if the specified item is an used key. + * + * @param item The item. + * + * @return true : yes. + *
false : no. + */ + + public boolean isUsedKey(final T item) { + return isKey(item) && isLoreValid(item); + } + + /** + * Checks if the specified item is a master key. + * + * @param item The item. + * + * @return true : yes. + *
false : no. + */ + + public abstract boolean isMasterKey(final T item); + + /** + * Checks if the specified item is a bunch of keys (blank or used). + * + * @param item The item. + * + * @return true : yes. + *
false : no. + */ + + public abstract boolean isBunchOfKeys(final T item); + + /** + * Checks if the specified item is a blank bunch of keys. + * + * @param item The item. + * + * @return true : yes. + *
false : no. + */ + + public boolean isBlankBunchOfKeys(final T item) { + return isBunchOfKeys(item) && !isUsedBunchOfKeys(item); + } + + /** + * Checks if the specified item is an used bunch of keys. + * + * @param item The item. + * + * @return true : yes. + *
false : no. + */ + + public boolean isUsedBunchOfKeys(final T item) { + return isBunchOfKeys(item) && isLoreValid(item); + } + + /** + * Checks if the specified item is a padlock finder (blank or used). + * + * @param item The item. + * + * @return true : yes. + *
false : no. + */ + + public abstract boolean isPadlockFinder(final T item); + + /** + * Checks if the specified item is a blank padlock finder. + * + * @param item The item. + * + * @return true : yes. + *
false : no. + */ + + public boolean isBlankPadlockFinder(final T item) { + return isPadlockFinder(item) && !isUsedPadlockFinder(item); + } + + /** + * Checks if the specified item is an used padlock finder. + * + * @param item The item. + * + * @return true : yes. + *
false : no. + */ + + public boolean isUsedPadlockFinder(final T item) { + return isPadlockFinder(item) && isLoreValid(item); + } + + /** + * Returns the specified item lore (must not be null). + * + * @param object The item. + * + * @return The specified item lore. + */ + + public abstract List getLore(final T object); + + /** + * Applies the lore to the specified item. + * + * @param object The item. + * @param lore The lore. + */ + + public abstract void setLore(final T object, final List lore); + + /** + * Returns whether the lore of the specified item is valid or not. + * + * @param object The iteù. + * + * @return true Yes. + *
false Otherwise. + */ + + public boolean isLoreValid(final T object) { + final List lore = getLore(object); + return !lore.isEmpty() && lore.size() % 2 == 0; + } + +} \ No newline at end of file diff --git a/core/src/main/java/fr/skyost/serialkey/core/item/PluginItemManager.java b/core/src/main/java/fr/skyost/serialkey/core/item/PluginItemManager.java new file mode 100644 index 0000000..719174b --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/item/PluginItemManager.java @@ -0,0 +1,176 @@ +package fr.skyost.serialkey.core.item; + +import fr.skyost.serialkey.core.config.SerialKeyConfig; +import fr.skyost.serialkey.core.util.Util; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * An item manager that depends on a plugin. + * + * @param ItemStack class. + */ + +public abstract class PluginItemManager extends ItemManager { + + /** + * The key recipe ID. + */ + + public static final String KEY_RECIPE_ID = "key"; + + /** + * The master key recipe ID. + */ + + public static final String MASTER_KEY_RECIPE_ID = "master_key"; + + /** + * The key clone recipe ID. + */ + + public static final String KEY_CLONE_RECIPE_ID = "key_clone"; + + /** + * The bunch of keys recipe ID. + */ + + public static final String BUNCH_OF_KEYS_RECIPE_ID = "bunch_of_keys"; + + /** + * The padlock finder recipe ID. + */ + + public static final String PADLOCK_FINDER_RECIPE_ID = "padlock_finder"; + + /** + * The SerialKey configuration instance. + */ + + protected final SerialKeyConfig config; + + /** + * Creates a new plugin item manager instance. + * + * @param config The SerialKey configuration instance. + * @param key The key item. + * @param masterKey The master key item. + * @param keyClone The key clone item. + * @param bunchOfKeys The bunch of keys item. + * @param padlockFinder The padlock finder item. + */ + + public PluginItemManager(final SerialKeyConfig config, final T key, final T masterKey, final T keyClone, final T bunchOfKeys, final T padlockFinder) { + super(key, masterKey, keyClone, bunchOfKeys, padlockFinder); + + this.config = config; + } + + /** + * Creates all required recipes. + */ + + public void createRecipes() { + createRecipe(KEY_RECIPE_ID, getKeyItem(), config.getKeyShape()); + createRecipe(MASTER_KEY_RECIPE_ID, getMasterKeyItem(), config.getMasterKeyShape()); + createRecipe(KEY_CLONE_RECIPE_ID, getKeyCloneItem(), Collections.singletonList("YY"), Objects.requireNonNull(Util.createMap(new String[]{"Y"}, new String[]{config.getKeyMaterialID()}))); + createRecipe(BUNCH_OF_KEYS_RECIPE_ID, getBunchOfKeysItem(), config.getBunchOfKeysShape()); + createRecipe(PADLOCK_FINDER_RECIPE_ID, getPadlockFinderItem(), Collections.singletonList("ZY"), Objects.requireNonNull(Util.createMap(new String[]{"Z", "Y"}, new String[]{config.getPadlockFinderMaterialID(), config.getKeyMaterialID()}))); + } + + /** + * Creates a recipe for an item. + * + * @param id The recipe ID. + * @param result The item. + * @param shape The shape. + */ + + private void createRecipe(final String id, final T result, final List shape) { + createRecipe(id, result, shape, config.getShapeMaterials()); + } + + /** + * Creates a recipe for an item. + * + * @param id The recipe ID. + * @param result The item. + * @param shape The shape. + * @param ingredients The ingredients needed for the craft. + */ + + protected abstract void createRecipe(final String id, final T result, final List shape, final Map ingredients); + + @Override + public boolean isKey(final T item) { + return areItemsEqual(getKeyItem(), item); + } + + @Override + public boolean isMasterKey(final T item) { + return areItemsEqual(getMasterKeyItem(), item); + } + + @Override + public boolean isBunchOfKeys(final T item) { + return areItemsEqual(getBunchOfKeysItem(), item); + } + + @Override + public boolean isPadlockFinder(final T item) { + return areItemsEqual(getPadlockFinderItem(), item); + } + + /** + * Returns whether the specified items are equal. + * + * @param item1 The first item. + * @param item2 The second item. + * + * @return true Yes. + *
false Otherwise. + */ + + private boolean areItemsEqual(final T item1, final T item2) { + return isItemValid(item2) && compareItemsType(item1, item2) && (config.canRenameItems() || compareItemsName(item1, item2)); + } + + /** + * Checks if the specified item is valid. + * + * @param item The item. + * + * @return true : yes. + *
false : no. + */ + + protected abstract boolean isItemValid(final T item); + + /** + * Compare the specified items name. + * + * @param item1 The first item. + * @param item2 The second item. + * + * @return true : they are equal. + *
false : they are not equal. + */ + + protected abstract boolean compareItemsName(final T item1, final T item2); + + /** + * Compare the specified items type. + * + * @param item1 The first item. + * @param item2 The second item. + * + * @return true : they are equal. + *
false : they are not equal. + */ + + protected abstract boolean compareItemsType(final T item1, final T item2); + +} \ No newline at end of file diff --git a/core/src/main/java/fr/skyost/serialkey/core/listener/BlocksListener.java b/core/src/main/java/fr/skyost/serialkey/core/listener/BlocksListener.java new file mode 100644 index 0000000..18503e6 --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/listener/BlocksListener.java @@ -0,0 +1,83 @@ +package fr.skyost.serialkey.core.listener; + +import fr.skyost.serialkey.core.SerialKeyPlugin; + +/** + * A listener that allows to listen blocks related events. + */ + +public class BlocksListener extends SerialKeyListener { + + /** + * Creates a new blocks listener instance. + * + * @param plugin The plugin. + */ + + public BlocksListener(final SerialKeyPlugin plugin) { + super(plugin); + } + + /** + * Triggered when a block is placed. + * + * @param item The item that was in player's hand. + * @param cancelEvent The runnable that cancels the event. + */ + + protected void onBlockPlace(final I item, final Runnable cancelEvent) { + if(itemManager.isKey(item) || itemManager.isMasterKey(item) || itemManager.isBunchOfKeys(item)/* || itemManager.isPadlockFinder(item)*/) { + cancelEvent.run(); + } + } + + /** + * Triggered when a block has been broken by a creature. + * + * @param location The block location. + * @param cancelEvent The runnable that cancels the event. + */ + + protected boolean onBlockBreakByCreature(final L location, final Runnable cancelEvent) { + return cancelIfHasPadlock(location, cancelEvent); + } + + /** + * Triggered when a block has been exploded. + * + * @param location The block location. + * @param cancelEvent The runnable that cancels the event. + */ + + protected boolean onBlockExplode(final L location, final Runnable cancelEvent) { + return cancelIfHasPadlock(location, cancelEvent); + } + + /** + * Triggered when a block has been powered by redstone. + * + * @param location The block location. + * @param cancelEvent The runnable that cancels the event. + */ + + protected boolean onBlockPoweredByRedstone(final L location, final Runnable cancelEvent) { + return cancelIfHasPadlock(location, cancelEvent); + } + + /** + * Cancels an event if the specified location has a padlock. + * + * @param location The block location. + * @param cancelEvent The runnable that cancels the event. + */ + + protected boolean cancelIfHasPadlock(final L location, final Runnable cancelEvent) { + if(location != null && padlockManager.hasPadlock(location)) { + cancelEvent.run(); + return true; + } + + return false; + } + +} \ No newline at end of file diff --git a/core/src/main/java/fr/skyost/serialkey/core/listener/BunchOfKeysListener.java b/core/src/main/java/fr/skyost/serialkey/core/listener/BunchOfKeysListener.java new file mode 100644 index 0000000..10a7148 --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/listener/BunchOfKeysListener.java @@ -0,0 +1,52 @@ +package fr.skyost.serialkey.core.listener; + +import fr.skyost.serialkey.core.SerialKeyPlugin; + +/** + * A listener that allows to listen bunch of keys related events. + */ + +public class BunchOfKeysListener extends SerialKeyListener { + + /** + * Creates a new bunch of keys listener instance. + * + * @param plugin The plugin. + */ + + public BunchOfKeysListener(final SerialKeyPlugin plugin) { + super(plugin); + } + + /** + * Triggered when a player right clicks on air (no block). + * + * @param item The item that was used. + * @param cancelIfCreateInventory Cancels the event if the bunch of keys inventory has been successfully created. + */ + + protected void onPlayerRightClickOnAir(final I item, final Runnable cancelIfCreateInventory) { + if(!itemManager.isBunchOfKeys(item)) { + return; + } + + cancelIfCreateInventory.run(); + } + + /** + * Triggered when a player right clicks on a block. + * + * @param location The block location. + * @param item The item that was used. + * @param cancelIfCreateInventory Cancels the event if the bunch of keys inventory has been successfully created. + */ + + protected void onPlayerRightClickOnBlock(final L location, final I item, final Runnable cancelIfCreateInventory) { + if(padlockManager.hasPadlock(location)) { + return; + } + + onPlayerRightClickOnAir(item, cancelIfCreateInventory); + } + +} \ No newline at end of file diff --git a/core/src/main/java/fr/skyost/serialkey/core/listener/GlobalListener.java b/core/src/main/java/fr/skyost/serialkey/core/listener/GlobalListener.java new file mode 100644 index 0000000..303b711 --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/listener/GlobalListener.java @@ -0,0 +1,255 @@ +package fr.skyost.serialkey.core.listener; + +import fr.skyost.serialkey.core.SerialKeyPlugin; +import fr.skyost.serialkey.core.item.PluginItemManager; +import fr.skyost.serialkey.core.object.SerialKeyLocation; +import fr.skyost.serialkey.core.object.SerialKeyPerson; + +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * A listener that allows to globally listen plugin events. + */ + +public abstract class GlobalListener extends SerialKeyListener { + + /** + * Creates a new global listener instance. + * + * @param plugin The plugin. + */ + + public GlobalListener(final SerialKeyPlugin plugin) { + super(plugin); + } + + /** + * Triggered when a craft is previewed. + * + * @param player The involved player. + * @param craftingTable The crafting table items. + * @param shapeId The shape ID. + * @param result The result item. + * @param isIngredient Function to check whether an item is an ingredient. + * @param setPreview Function to change the preview. + * @param cancelEvent The runnable that cancels the event. + */ + + protected void onPreviewItemCraft( + final SerialKeyPerson player, + final I[] craftingTable, + final String shapeId, + final I result, + final Function isIngredient, + final Consumer setPreview, + final Runnable cancelEvent + ) { + final Boolean permission = hasPermission(player, result); + if(permission == null) { + return; + } + + if(!permission) { + getPlugin().sendMessage(player, getPlugin().getPluginMessages().getPermissionMessage()); + cancelEvent.run(); + return; + } + + if(shapeId != null && (shapeId.equals(PluginItemManager.KEY_CLONE_RECIPE_ID) || shapeId.equals(PluginItemManager.PADLOCK_FINDER_RECIPE_ID))) { + cloneLore(craftingTable, result, isIngredient, setPreview, cancelEvent); + } + } + + /** + * Triggered when a player left clicks. + * + * @param item The item that was used. + * @param location The click location. + * @param player The involved player. + * @param dropItem Function that allows to drop an item. + * @param clearHand Clears player's hand. + * @param playBreakSound Play an item break sound. + * @param cancelEvent The runnable that cancels the event. + */ + + protected void onPlayerLeftClick(final I item, final SerialKeyLocation location, final SerialKeyPerson player, final SerialKeyLocation playerLocation, final Consumer dropItem, final Runnable clearHand, final Runnable playBreakSound, final Runnable cancelEvent) { + if(padlockManager.hasPadlock(location)) { + if(unlocker.canUnlock(item, location, player)) { + if(!itemManager.isMasterKey(item)) { + int amount = 0; + if(itemManager.isUsedKey(item)) { + amount = getAmount(item); + clearHand.run(); + } + else if(itemManager.isBunchOfKeys(item)) { + amount = unlocker.removeLocation(item, location); + } + if(amount > 0 && plugin.getPluginConfig().areKeysReusable()) { + final I copy = copy(itemManager.getKeyItem()); + setAmount(copy, amount); + dropItem.accept(copy); + } + else { + playBreakSound.run(); + } + } + padlockManager.unregisterPadlock(location); + plugin.sendMessage(player, plugin.getPluginMessages().getPadlockRemovedMessage()); + } + else { + plugin.sendMessage(player, plugin.getPluginMessages().getBlockHasPadlockMessage()); + } + cancelEvent.run(); + return; + } + + boolean isMasterKey = itemManager.isMasterKey(item); + if(!isPadlockLocationValid(location) || (!itemManager.isBlankKey(item) && !isMasterKey)) { + return; + } + + cancelEvent.run(); + if(!isMasterKey && getAmount(item) > 1) { + final I copy = copy(item); + setAmount(copy, getAmount(item) - 1); + dropItem.accept(copy); + setAmount(item, 1); + } + + padlockManager.registerPadlock(location, item); + plugin.sendMessage(player, plugin.getPluginMessages().getPadlockPlacedMessage()); + } + + /** + * Triggered when a player right clicks. + * + * @param item The item that was used. + * @param location The click location. + * @param player The involved player. + * @param cancelEvent The runnable that cancels the event. + */ + + protected void onPlayerRightClick(final I item, final SerialKeyLocation location, final SerialKeyPerson player, final Runnable cancelEvent) { + if(!isPadlockLocationValid(location) || !padlockManager.hasPadlock(location)) { + return; + } + + if(!unlocker.canUnlock(item, location, player)) { + plugin.sendMessage(player, plugin.getPluginMessages().getBlockHasPadlockMessage()); + cancelEvent.run(); + } + } + + /** + * Checks whether the specified player has the permission to craft the specified item. + * + * @param player The player. + * @param result The item. + * + * @return Whether the specified player has the permission to craft the specified item (can be null if the item does not correspond to a plugin item). + */ + + private Boolean hasPermission(final SerialKeyPerson player, final I result) { + if(itemManager.isKey(result)) { + return player.hasPermission(itemManager.getKeyCloneItem().equals(result) ? "serialkey.craft.keyclone" : "serialkey.craft.key"); + } + + if(itemManager.isMasterKey(result)) { + return player.hasPermission("serialkey.craft.masterkey"); + } + + if(itemManager.isBlankBunchOfKeys(result)) { + return player.hasPermission("serialkey.craft.bunchofkeys"); + } + + if(itemManager.isBlankPadlockFinder(result)) { + return player.hasPermission("serialkey.craft.padlockfinder"); + } + + return null; + } + + /** + * Applies the lore of an used key to the craft result. + * + * @param craftingTable The crafting table items. + * @param result The result item. + * @param isIngredient Function to check whether an item is an ingredient. + * @param setPreview Function to change the preview. + * @param cancelEvent The runnable that cancels the event. + */ + + private void cloneLore( + final I[] craftingTable, + final I result, + final Function isIngredient, + final Consumer setPreview, + final Runnable cancelEvent + ) { + I key = null; + I ingredient = null; + for(final I item : craftingTable) { + if(item == null || getAmount(item) >= 2) { + continue; + } + + if(itemManager.isUsedKey(item)) { + key = item; + continue; + } + + if(isIngredient.apply(item)) { + ingredient = item; + } + } + + if(key == null || ingredient == null) { + cancelEvent.run(); + return; + } + + itemManager.setLore(result, itemManager.getLore(key)); + setPreview.accept(result); + } + + /** + * Copies an item. + * + * @param item The item. + * + * @return The copy. + */ + + protected abstract I copy(final I item); + + /** + * Returns the amount of items in the specified stack. + * + * @param itemStack The item stack. + * + * @return The amount of items in the specified stack. + */ + + protected abstract int getAmount(final I itemStack); + + /** + * Sets the amount of items in the specified stack. + * + * @param itemStack The item stack. + * @param amount The amount. + */ + + protected abstract void setAmount(final I itemStack, final int amount); + + /** + * Returns whether a padlock can be placed at the specified location. + * + * @param location The location. + * + * @return Whether a padlock can be placed at the specified location. + */ + + protected abstract boolean isPadlockLocationValid(final SerialKeyLocation location); + +} \ No newline at end of file diff --git a/core/src/main/java/fr/skyost/serialkey/core/listener/HopperListener.java b/core/src/main/java/fr/skyost/serialkey/core/listener/HopperListener.java new file mode 100644 index 0000000..8f1acb8 --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/listener/HopperListener.java @@ -0,0 +1,67 @@ +package fr.skyost.serialkey.core.listener; + +import fr.skyost.serialkey.core.SerialKeyPlugin; +import fr.skyost.serialkey.core.object.SerialKeyLocation; +import fr.skyost.serialkey.core.object.SerialKeyPerson; + +/** + * A listener that allows to listen hoppers related events. + */ + +public abstract class HopperListener extends SerialKeyListener { + + /** + * Directions to check for chests. + */ + + private static final SerialKeyLocation[] DIRECTIONS = new SerialKeyLocation[]{ + new SerialKeyLocation(null, 0, 1, 0), + new SerialKeyLocation(null, 0, -1, 0), + new SerialKeyLocation(null, 0, 0, -1), + new SerialKeyLocation(null, 1, 0, 0), + new SerialKeyLocation(null, 0, 0, 1), + new SerialKeyLocation(null, -1, 0, 0) + }; + + /** + * Creates a new hoppers listener instance. + * + * @param plugin The plugin. + */ + + public HopperListener(final SerialKeyPlugin plugin) { + super(plugin); + } + + /** + * Triggered when a block has been placed. + * + * @param location The location. + * @param player The involved player. + * @param cancelEvent The runnable that cancels the event. + */ + + protected void onBlockPlace(final SerialKeyLocation location, final SerialKeyPerson player, final Runnable cancelEvent) { + for(final SerialKeyLocation direction : DIRECTIONS) { + final SerialKeyLocation relative = location.getRelative(direction); + if(!(isChest(relative)) || !padlockManager.hasPadlock(relative)) { + continue; + } + + plugin.sendMessage(player, plugin.getPluginMessages().getBlockHasPadlockMessage()); + cancelEvent.run(); + return; + } + } + + /** + * Returns whether a chest is located at the specified location. + * + * @param location The location. + * + * @return Whether a chest is located at the specified location. + */ + + protected abstract boolean isChest(final SerialKeyLocation location); + +} \ No newline at end of file diff --git a/core/src/main/java/fr/skyost/serialkey/core/listener/LostChestsListener.java b/core/src/main/java/fr/skyost/serialkey/core/listener/LostChestsListener.java new file mode 100644 index 0000000..a0d5590 --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/listener/LostChestsListener.java @@ -0,0 +1,53 @@ +package fr.skyost.serialkey.core.listener; + +import fr.skyost.serialkey.core.SerialKeyPlugin; +import fr.skyost.serialkey.core.object.SerialKeyLocation; +import fr.skyost.serialkey.core.object.SerialKeyPerson; + +/** + * A listener that allows to listen chests related events. + */ + +public class LostChestsListener extends SerialKeyListener { + + /** + * Creates a new lost chests listener instance. + * + * @param plugin The plugin. + */ + + public LostChestsListener(final SerialKeyPlugin plugin) { + super(plugin); + } + + /** + * Triggered when a click occurs in an inventory. + * + * @param player The involved player. + * @param chestLocation The chest location. + * @param item The clicked item. + * @param cancelEvent The runnable that cancels the event. + */ + + protected void onInventoryClick(final SerialKeyPerson player, final SerialKeyLocation chestLocation, final I item, final Runnable cancelEvent) { + if(!itemManager.isUsedKey(item) && !itemManager.isUsedBunchOfKeys(item)) { + return; + } + + + padlockManager.fixLocation(chestLocation); + if(!padlockManager.hasPadlock(chestLocation, false)) { + return; + } + + for(final SerialKeyLocation location : unlocker.getLocations(item)) { + padlockManager.fixLocation(location); + if(location.equals(chestLocation)) { + cancelEvent.run(); + plugin.sendMessage(player, plugin.getPluginMessages().getChestProtectionMessage()); + return; + } + } + } + +} \ No newline at end of file diff --git a/core/src/main/java/fr/skyost/serialkey/core/listener/PadlockFinderListener.java b/core/src/main/java/fr/skyost/serialkey/core/listener/PadlockFinderListener.java new file mode 100644 index 0000000..2cd3cd1 --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/listener/PadlockFinderListener.java @@ -0,0 +1,54 @@ +package fr.skyost.serialkey.core.listener; + +import fr.skyost.serialkey.core.SerialKeyPlugin; +import fr.skyost.serialkey.core.object.SerialKeyLocation; +import fr.skyost.serialkey.core.object.SerialKeyPerson; + +import java.util.function.Consumer; + +/** + * A listener that allows to listen padlock finder related events. + */ + +public class PadlockFinderListener extends SerialKeyListener { + + /** + * Creates a new padlock finder listener instance. + * + * @param plugin The plugin. + */ + + public PadlockFinderListener(final SerialKeyPlugin plugin) { + super(plugin); + } + + /** + * Triggered when a player right clicks. + * + * @param item The item. + * @param player The involved player. + * @param spawn The spawn location. + * @param currentCompassesTarget The current compasses target. + * @param setCompassesTarget Function that allows to change the current compasses target. + * @param cancelEvent The runnable that cancels the event. + */ + + protected void onPlayerRightClick(final I item, final SerialKeyPerson player, final SerialKeyLocation spawn, final SerialKeyLocation currentCompassesTarget, final Consumer setCompassesTarget, final Runnable cancelEvent) { + if(!itemManager.isUsedPadlockFinder(item)) { + return; + } + + final SerialKeyLocation padlockLocation = unlocker.getLocations(item).get(0); + if(currentCompassesTarget.equals(spawn)) { + setCompassesTarget.accept(padlockLocation); + plugin.sendMessage(player, plugin.getPluginMessages().getPadlockFinderEnabledMessage()); + } + else { + setCompassesTarget.accept(spawn); + plugin.sendMessage(player, plugin.getPluginMessages().getPadlockFinderDisabledMessage()); + } + + cancelEvent.run(); + } + +} \ No newline at end of file diff --git a/core/src/main/java/fr/skyost/serialkey/core/listener/SerialKeyListener.java b/core/src/main/java/fr/skyost/serialkey/core/listener/SerialKeyListener.java new file mode 100644 index 0000000..f3b0e34 --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/listener/SerialKeyListener.java @@ -0,0 +1,75 @@ +package fr.skyost.serialkey.core.listener; + +import fr.skyost.serialkey.core.SerialKeyPlugin; +import fr.skyost.serialkey.core.item.PluginItemManager; +import fr.skyost.serialkey.core.padlock.PluginPadlockManager; +import fr.skyost.serialkey.core.unlocker.PluginUnlocker; + +/** + * Represents a SerialKey listener. + * + * @param ItemStack class. + * @param Location class. + */ + +public class SerialKeyListener { + + /** + * The plugin. + */ + + protected SerialKeyPlugin plugin; + + /** + * The item manager. + */ + + protected PluginItemManager itemManager; + + /** + * The padlock manager. + */ + + protected PluginPadlockManager padlockManager; + + /** + * The unlocker. + */ + + protected PluginUnlocker unlocker; + + /** + * Creates a new SerialKey listener instance. + * + * @param plugin The plugin. + */ + + SerialKeyListener(final SerialKeyPlugin plugin) { + setPlugin(plugin); + } + + /** + * Returns the plugin instance. + * + * @return The plugin instance. + */ + + public SerialKeyPlugin getPlugin() { + return plugin; + } + + /** + * Sets the plugin instance. + * + * @param plugin The plugin instance. + */ + + public void setPlugin(final SerialKeyPlugin plugin) { + this.plugin = plugin; + + itemManager = plugin.getItemManager(); + padlockManager = plugin.getPadlockManager(); + unlocker = plugin.getUnlocker(); + } + +} \ No newline at end of file diff --git a/core/src/main/java/fr/skyost/serialkey/core/object/PersonIdentity.java b/core/src/main/java/fr/skyost/serialkey/core/object/PersonIdentity.java new file mode 100644 index 0000000..4284264 --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/object/PersonIdentity.java @@ -0,0 +1,73 @@ +package fr.skyost.serialkey.core.object; + +/** + * Represents a SerialKey person identity. + */ + +public class PersonIdentity { + + /** + * Identity types. + */ + + public enum Type { + + /** + * Console type. + */ + + CONSOLE, + + /** + * Player type. + */ + + PLAYER + + } + + /** + * The type of the person. + */ + + private final Type type; + + /** + * The name of the person. + */ + + private final String name; + + /** + * Creates a new person identity instance. + * + * @param type The type of the person. + * @param name The name of the person. + */ + + public PersonIdentity(final Type type, final String name) { + this.type = type; + this.name = name; + } + + /** + * Returns the type of the person. + * + * @return The type of the person. + */ + + public Type getType() { + return type; + } + + /** + * Returns the name of the person. + * + * @return The name of the person. + */ + + public String getName() { + return name; + } + +} \ No newline at end of file diff --git a/core/src/main/java/fr/skyost/serialkey/core/object/SerialKeyLocation.java b/core/src/main/java/fr/skyost/serialkey/core/object/SerialKeyLocation.java new file mode 100644 index 0000000..cc124ae --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/object/SerialKeyLocation.java @@ -0,0 +1,220 @@ +package fr.skyost.serialkey.core.object; + +import java.util.Objects; + +/** + * Represents a point in a 3D-Space located by a world and three coordinates. + */ + +public class SerialKeyLocation { + + /** + * The world. + */ + + private String world; + + /** + * X coordinate. + */ + + private int x; + + /** + * Y coordinate. + */ + + private int y; + + /** + * Z coordinate. + */ + + private int z; + + /** + * Creates a new SerialKey location. + * + * @param world The world. + * @param position The String position (x, y, z). + */ + + public SerialKeyLocation(final String world, final String position) { + this.world = world; + + final String[] rawLocation = position.split(", "); + x = Integer.parseInt(rawLocation[0]); + y = Integer.parseInt(rawLocation[1]); + z = Integer.parseInt(rawLocation[2]); + } + + /** + * Creates a new SerialKey location. + * + * @param world The world. + * @param x The X coordinate. + * @param y The Y coordinate. + * @param z The Z coordinate. + */ + + public SerialKeyLocation(final String world, final int x, final int y, final int z) { + this.world = world; + this.x = x; + this.y = y; + this.z = z; + } + + /** + * Returns the world name. + * + * @return The world name. + */ + + public String getWorld() { + return world; + } + + /** + * Sets the world name. + * + * @param world The world name. + */ + + public void setWorld(final String world) { + this.world = world; + } + + /** + * Returns the X coordinate. + * + * @return The X coordinate. + */ + + public int getX() { + return x; + } + + /** + * Sets the X coordinate. + * + * @param x The X coordinate. + */ + + public void setX(final int x) { + this.x = x; + } + + /** + * Returns the Y coordinate. + * + * @return The Y coordinate. + */ + + public int getY() { + return y; + } + + /** + * Sets the Y coordinate. + * + * @param y The Y coordinate. + */ + + public void setY(final int y) { + this.y = y; + } + + /** + * Returns the Z coordinate. + * + * @return The Z coordinate. + */ + + public int getZ() { + return z; + } + + /** + * Sets the Z coordinate. + * + * @param z The Z coordinate. + */ + + public void setZ(final int z) { + this.z = z; + } + + /** + * Returns the position (x, y, z). + * + * @return The position. + */ + + public String getPosition() { + return x + ", " + y + ", " + z; + } + + /** + * Returns a location where its coordinates are (
this.x + location.x
,
this.y + location.y
,
this.z + location.z
) + * where (
this.x
,
this.y
,
this.z
) are the coordinates of this location + * and (
location.x
,
location.y
,
location.z
) are the coordinates of the specified location. + * + * @param location The location. + * + * @return The calculated location. + */ + + public SerialKeyLocation getRelative(final SerialKeyLocation location) { + return getRelative(location.x, location.y, location.z); + } + + /** + * Returns a location where its coordinates are (
this.x + x
,
this.y + y
,
this.z + z
) + * where (
this.x
,
this.y
,
this.z
) are the coordinates of this location + * and (
x
,
y
,
z
) are the specified coordinates. + * + * @param x The X. + * @param y The Y. + * @param z The Z. + * + * @return The calculated location. + */ + + public SerialKeyLocation getRelative(final int x, final int y, final int z) { + return new SerialKeyLocation(world, this.x + x, this.y + y, this.z + z); + } + + /** + * Creates a copy of the current location. + * + * @return The copy. + */ + + public SerialKeyLocation copy() { + return new SerialKeyLocation(world, x, y, z); + } + + @Override + public String toString() { + return "{\"" + world.replace("\"", "\\\"") + "\", " + getPosition() + "}"; + } + + @Override + public int hashCode() { + return Objects.hash(world, x, y ,z); + } + + public boolean equals(final SerialKeyLocation location) { + return world.equals(location.world) && x == location.x && y == location.y && z == location.z; + } + + @Override + public boolean equals(final Object object) { + if(object instanceof SerialKeyLocation) { + return equals((SerialKeyLocation)object); + } + + return super.equals(object); + } + +} \ No newline at end of file diff --git a/core/src/main/java/fr/skyost/serialkey/core/object/SerialKeyPerson.java b/core/src/main/java/fr/skyost/serialkey/core/object/SerialKeyPerson.java new file mode 100644 index 0000000..e26087b --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/object/SerialKeyPerson.java @@ -0,0 +1,35 @@ +package fr.skyost.serialkey.core.object; + +/** + * Represents a person. + */ + +public interface SerialKeyPerson { + + /** + * Returns the person identity. + * + * @return The identity. + */ + + PersonIdentity getIdentity(); + + /** + * Sends a message to the current person. + * + * @param message The message. + */ + + void sendMessage(final String message); + + /** + * Returns whether the person has the specified permission. + * + * @param permission The permission. + * + * @return Whether the person has the specified permission. + */ + + boolean hasPermission(final String permission); + +} \ No newline at end of file diff --git a/core/src/main/java/fr/skyost/serialkey/core/padlock/PadlockManager.java b/core/src/main/java/fr/skyost/serialkey/core/padlock/PadlockManager.java new file mode 100644 index 0000000..b417237 --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/padlock/PadlockManager.java @@ -0,0 +1,70 @@ +package fr.skyost.serialkey.core.padlock; + +import fr.skyost.serialkey.core.object.SerialKeyLocation; + +import java.util.HashSet; +import java.util.Set; + +/** + * The class that allows to manage padlocks. + */ + +public class PadlockManager { + + /** + * The padlocks. + */ + + private final HashSet padlocks = new HashSet<>(); + + /** + * Registers a padlock at the specified location. + * + * @param location The location. + */ + + public void registerPadlock(final SerialKeyLocation location) { + padlocks.add(location.copy()); + } + + /** + * Unregisters a padlock located at the specified location. + * + * @param location The location. + */ + + public void unregisterPadlock(final SerialKeyLocation location) { + padlocks.remove(location); + } + + /** + * Returns whether or no there is a padlock at the specified location. + * + * @param location The location. + * + * @return Whether or no there is a padlock at the specified location. + */ + + public boolean hasPadlock(final SerialKeyLocation location) { + return padlocks.contains(location); + } + + /** + * Clears all padlocks. + */ + + public void clearPadlocks() { + padlocks.clear(); + } + + /** + * Returns all padlocks. + * + * @return All padlocks. + */ + + public Set getPadlocks() { + return padlocks; + } + +} \ No newline at end of file diff --git a/core/src/main/java/fr/skyost/serialkey/core/padlock/PluginPadlockManager.java b/core/src/main/java/fr/skyost/serialkey/core/padlock/PluginPadlockManager.java new file mode 100644 index 0000000..74ba375 --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/padlock/PluginPadlockManager.java @@ -0,0 +1,194 @@ +package fr.skyost.serialkey.core.padlock; + +import fr.skyost.serialkey.core.SerialKeyPlugin; +import fr.skyost.serialkey.core.config.SerialKeyData; +import fr.skyost.serialkey.core.object.SerialKeyLocation; + +import java.util.Collection; + +/** + * A padlock manager that depends on a plugin. + * + * @param ItemStack class. + * @param Location class. + */ + +public abstract class PluginPadlockManager extends PadlockManager { + + /** + * The plugin instance. + */ + + private SerialKeyPlugin plugin; + + /** + * Creates a new plugin padlock manager instance. + * + * @param plugin The plugin. + */ + + public PluginPadlockManager(final SerialKeyPlugin plugin) { + this.plugin = plugin; + } + + /** + * Loads the padlocks from the specified data. + * + * @param data The data. + */ + + public void load(final SerialKeyData data) { + final Collection locations = data.getPadlocks(); + for(final SerialKeyLocation location : locations) { + registerPadlock(location); + } + } + + /** + * Saves the padlocks to the specified data. + * + * @param data The data. + */ + + public void save(final SerialKeyData data) { + for(final SerialKeyLocation location : getPadlocks()) { + data.addPadlock(location); + } + } + + /** + * Registers a padlock at the specified location. + * + * @param location The location. + */ + + public void registerPadlock(final L location) { + registerPadlock(location, null); + } + + /** + * Registers a padlock at the specified location. + * + * @param location The location. + * @param item The item to format. + */ + + public void registerPadlock(final L location, final I item) { + registerPadlock(pluginLocationToSerialKeyLocation(location), item); + } + + /** + * Registers a padlock at the specified location. + * + * @param location The location. + * @param item The item to format. + */ + + public void registerPadlock(final SerialKeyLocation location, final I item) { + super.registerPadlock(location); + if(item != null && plugin.getItemManager().isBlankKey(item)) { + plugin.getUnlocker().addLocation(item, location); + } + } + + /** + * Returns whether or no there is a padlock at the specified location. + * + * @param location The location. + * + * @return Whether or no there is a padlock at the specified location. + */ + + public boolean hasPadlock(final L location) { + return hasPadlock(pluginLocationToSerialKeyLocation(location)); + } + + @Override + public boolean hasPadlock(final SerialKeyLocation location) { + return hasPadlock(location, true); + } + + /** + * Returns whether or no there is a padlock at the specified location. + * + * @param location The location. + * @param fixLocation Whether the location should be fixed. + * + * @return Whether or no there is a padlock at the specified location. + */ + + public boolean hasPadlock(final SerialKeyLocation location, final boolean fixLocation) { + if(fixLocation) { + fixLocation(location); + } + return super.hasPadlock(location); + } + + /** + * Unregisters a padlock located at the specified location. + * + * @param location The location. + */ + + private void unregisterPadlock(final L location) { + unregisterPadlock(location, null); + } + + @Override + public void unregisterPadlock(final SerialKeyLocation location) { + unregisterPadlock(location, null); + } + + /** + * Unregisters a padlock located at the specified location. + * + * @param location The location. + * @param item The item to format. + */ + + private void unregisterPadlock(final L location, final I item) { + unregisterPadlock(pluginLocationToSerialKeyLocation(location), item); + } + + /** + * Unregisters a padlock located at the specified location. + * + * @param location The location. + * @param item The item to format. + */ + + private void unregisterPadlock(final SerialKeyLocation location, final I item) { + fixLocation(location); + super.unregisterPadlock(location); + if(item != null) { + if(plugin.getItemManager().isUsedKey(item)) { + plugin.getUnlocker().clearLocations(item); + } + if(plugin.getItemManager().isUsedBunchOfKeys(item)) { + plugin.getUnlocker().removeLocation(item, location); + } + } + } + + /** + * Corrects a object (used to handle doors because they are composed from two blocks and double chests too). + * + * @param location The object. + * + * @return true : If the object has been corrected. + *
false : Otherwise. + */ + + public abstract boolean fixLocation(final SerialKeyLocation location); + + /** + * Converts a plugin location to a SerialKey location. + * + * @param location The location. + * + * @return The SerialKey location. + */ + + protected abstract SerialKeyLocation pluginLocationToSerialKeyLocation(final L location); + +} \ No newline at end of file diff --git a/core/src/main/java/fr/skyost/serialkey/core/unlocker/LoreUnlocker.java b/core/src/main/java/fr/skyost/serialkey/core/unlocker/LoreUnlocker.java new file mode 100644 index 0000000..a5b7d72 --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/unlocker/LoreUnlocker.java @@ -0,0 +1,228 @@ +package fr.skyost.serialkey.core.unlocker; + +import fr.skyost.serialkey.core.item.ItemManager; +import fr.skyost.serialkey.core.object.SerialKeyLocation; +import fr.skyost.serialkey.core.util.ROT47; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +/** + * Represents an unlocker that depends on items lore. + * + * @param ItemStack class. + */ + +public abstract class LoreUnlocker implements Unlocker { + + /** + * The item manager. + */ + + final ItemManager itemManager; + + /** + * Creates a new lore unlocker instance. + * + * @param itemManager The item manager. + */ + + public LoreUnlocker(final ItemManager itemManager) { + this.itemManager = itemManager; + } + + @Override + public List getLocations(final T item) { + return getLocations(item, isCipheringEnabled() ? ROT47::rotate : string -> string); + } + + /** + * Returns the locations located in the item lore. + * + * @param item The item. + * @param stringProcessor The string processor. + * + * @return All locations located in the item lore. + */ + + public List getLocations(final T item, final Function stringProcessor) { + final List result = new ArrayList<>(); + if(!itemManager.isLoreValid(item)) { + return result; + } + + final List lore = itemManager.getLore(item); + final int n = lore.size(); + + for(int i = 0; i < n; i++) { + result.add(new SerialKeyLocation(stringProcessor.apply(lore.get(i)), stringProcessor.apply(lore.get(++i)))); + } + + return result; + } + + @Override + public boolean canUnlock(final T item, final String world, final String position) { + return canUnlock(item, world, position, isCipheringEnabled() ? ROT47::rotate : string -> string); + } + + /** + * Returns whether the item can unlock the padlock located at the specified location. + * + * @param item The item. + * @param world The world. + * @param position The position. + * @param stringProcessor The string processor. + * + * @return Whether the item can unlock the padlock located at the specified location. + */ + + protected boolean canUnlock(final T item, final String world, final String position, final Function stringProcessor) { + if(itemManager.isMasterKey(item)) { + return true; + } + + if(!itemManager.isLoreValid(item)) { + return false; + } + + final List lore = itemManager.getLore(item); + final int n = lore.size(); + for(int i = 0; i < n; i++) { + if(stringProcessor.apply(lore.get(i)).equals(world) && stringProcessor.apply(lore.get(++i)).equals(position)) { + return true; + } + } + + return false; + } + + /** + * Adds a location to the specified collection item. + * + * @param collection The collection (bunch of keys for example). + * @param item The item. + */ + + public void addLocation(final T collection, final T item) { + if(!itemManager.isLoreValid(item)) { + return; + } + + final List lore = itemManager.getLore(collection); + lore.addAll(itemManager.getLore(item)); + itemManager.setLore(collection, lore); + } + + /** + * Adds a location to the specified item. + * + * @param item The item. + * @param location The location. + */ + + public void addLocation(final T item, final SerialKeyLocation location) { + addLocation(item, location.getWorld(), location.getPosition()); + } + + /** + * Adds a location to the specified item. + * + * @param item The item. + * @param world The world. + * @param position The position. + */ + + protected void addLocation(final T item, final String world, final String position) { + addLocation(item, world, position, isCipheringEnabled() ? ROT47::rotate : string -> string); + } + + /** + * Adds a location to the specified item. + * + * @param item The item. + * @param world The world. + * @param position The position. + * @param stringProcessor The string processor. + */ + + protected void addLocation(final T item, final String world, final String position, final Function stringProcessor) { + final List lore = itemManager.getLore(item); + lore.add(stringProcessor.apply(world)); + lore.add(stringProcessor.apply(position)); + itemManager.setLore(item, lore); + } + + /** + * Removes a location from the specified item. + * + * @param item The item. + * @param location The location. + */ + + public short removeLocation(final T item, final SerialKeyLocation location) { + return removeLocation(item, location.getWorld(), location.getPosition()); + } + + /** + * Removes a location from the specified item. + * + * @param item The item. + * @param world The world. + * @param position The position. + */ + + protected short removeLocation(final T item, final String world, final String position) { + return removeLocation(item, world, position, isCipheringEnabled() ? ROT47::rotate : string -> string); + } + + /** + * Removes a location from the specified item. + * + * @param item The item. + * @param world The world. + * @param position The position. + * @param stringProcessor The string processor. + */ + + protected short removeLocation(final T item, final String world, final String position, final Function stringProcessor) { + if(!itemManager.isLoreValid(item)) { + return 0; + } + + final List lore = itemManager.getLore(item); + short count = 0; + for(int i = 0; i < lore.size(); i++) { + if(world.equals(stringProcessor.apply(lore.get(i))) && position.equals(stringProcessor.apply(lore.get(++i)))) { + lore.remove(i); + lore.remove(--i); + i--; + + count++; + } + } + itemManager.setLore(item, lore.isEmpty() ? null : lore); + + return count; + } + + /** + * Clears all locations of the specified item. + * + * @param item The item. + */ + + public void clearLocations(final T item) { + itemManager.setLore(item, null); + } + + /** + * Returns whether ROT47 ciphering should be enabled. + * + * @return Whether ROT47 ciphering should be enabled. + */ + + public abstract boolean isCipheringEnabled(); + +} \ No newline at end of file diff --git a/core/src/main/java/fr/skyost/serialkey/core/unlocker/PluginUnlocker.java b/core/src/main/java/fr/skyost/serialkey/core/unlocker/PluginUnlocker.java new file mode 100644 index 0000000..20d4388 --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/unlocker/PluginUnlocker.java @@ -0,0 +1,141 @@ +package fr.skyost.serialkey.core.unlocker; + +import fr.skyost.serialkey.core.SerialKeyPlugin; +import fr.skyost.serialkey.core.object.SerialKeyLocation; +import fr.skyost.serialkey.core.object.SerialKeyPerson; +import fr.skyost.serialkey.core.util.ROT47; + +import java.util.List; +import java.util.function.Function; + +/** + * An unlocker that depends on a plugin. + * + * @param ItemStack class. + */ + +public abstract class PluginUnlocker extends LoreUnlocker { + + /** + * The plugin instance. + */ + + private final SerialKeyPlugin plugin; + + /** + * Creates a new plugin unlocker instance. + * + * @param plugin The plugin. + */ + + public PluginUnlocker(final SerialKeyPlugin plugin) { + super(plugin.getItemManager()); + + this.plugin = plugin; + } + + @Override + public List getLocations(final T item) { + final Function stringProcessor = isCipheringEnabled() ? string -> ROT47.rotate(stripColor(string)) : this::stripColor; + return getLocations(item, stringProcessor); + } + + @Override + public void addLocation(final T item, final SerialKeyLocation location) { + plugin.getPadlockManager().fixLocation(location); + super.addLocation(item, location); + } + + @Override + public void addLocation(final T item, final String world, final String position) { + final String color = randomColor(); + addLocation(item, world, position, isCipheringEnabled() ? string -> color + ROT47.rotate(string) : string -> color + string); + } + + @Override + public short removeLocation(final T item, final SerialKeyLocation location) { + plugin.getPadlockManager().fixLocation(location); + return super.removeLocation(item, location); + } + + @Override + public short removeLocation(final T item, final String world, final String position) { + final Function stringProcessor = isCipheringEnabled() ? string -> ROT47.rotate(stripColor(string)) : this::stripColor; + return removeLocation(item, world, position, stringProcessor); + } + + @Override + public boolean canUnlock(final T item, final SerialKeyLocation location) { + plugin.getPadlockManager().fixLocation(location); + return super.canUnlock(item, location); + } + + @Override + public boolean canUnlock(final T item, final String world, final String position) { + final Function stringProcessor = isCipheringEnabled() ? string -> ROT47.rotate(stripColor(string)) : this::stripColor; + return super.canUnlock(item, world, position, stringProcessor); + } + + @Override + public boolean isCipheringEnabled() { + return plugin.getPluginConfig().areLoresEncrypted(); + } + + /** + * Checks if the specified item is a valid key for the specified object. + * + * @param item The object. + * @param location The location. + * @param player If you want to check if the player has the right permission. Will send a message if he doesn't. + * + * @return true : yes. + *
false : no. + */ + + public boolean canUnlock(final T item, final SerialKeyLocation location, final SerialKeyPerson player) { + if(itemManager.isMasterKey(item)) { + if(player != null && !player.hasPermission("serialkey.use.masterkey")) { + plugin.sendMessage(player, plugin.getPluginMessages().getPermissionMessage()); + } + return true; + } + + plugin.getPadlockManager().fixLocation(location); + if(itemManager.isUsedKey(item)) { + if(player != null && !player.hasPermission("serialkey.use.key")) { + plugin.sendMessage(player, plugin.getPluginMessages().getPermissionMessage()); + return false; + } + return canUnlock(item, location.getWorld(), location.getPosition()); + } + + if(itemManager.isUsedBunchOfKeys(item)) { + if(player != null && !player.hasPermission("serialkey.use.bunchofkeys")) { + plugin.sendMessage(player, plugin.getPluginMessages().getPermissionMessage()); + return false; + } + return canUnlock(item, location.getWorld(), location.getPosition()); + } + + return false; + } + + /** + * Returns a random chat color. + * + * @return A random chat color. + */ + + protected abstract String randomColor(); + + /** + * Strips all colors from the specified string. + * + * @param string The string. + * + * @return The handled string. + */ + + protected abstract String stripColor(final String string); + +} \ No newline at end of file diff --git a/core/src/main/java/fr/skyost/serialkey/core/unlocker/Unlocker.java b/core/src/main/java/fr/skyost/serialkey/core/unlocker/Unlocker.java new file mode 100644 index 0000000..22043a2 --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/unlocker/Unlocker.java @@ -0,0 +1,50 @@ +package fr.skyost.serialkey.core.unlocker; + +import fr.skyost.serialkey.core.object.SerialKeyLocation; + +import java.util.Collection; + +/** + * Represents an unlocker. + * + * @param Location class. + */ + +public interface Unlocker { + + /** + * Returns the locations located in the item lore. + * + * @param item The item. + * + * @return All locations located in the item lore. + */ + + Collection getLocations(final T item); + + /** + * Returns whether the item can unlock the padlock located at the specified location. + * + * @param item The item. + * @param location The location. + * + * @return Whether the item can unlock the padlock located at the specified location. + */ + + default boolean canUnlock(final T item, final SerialKeyLocation location) { + return canUnlock(item, location.getWorld(), location.getPosition()); + } + + /** + * Returns whether the item can unlock the padlock located at the specified location. + * + * @param item The item. + * @param world The world. + * @param position The position. + * + * @return Whether the item can unlock the padlock located at the specified location. + */ + + boolean canUnlock(final T item, final String world, final String position); + +} \ No newline at end of file diff --git a/src/main/java/fr/skyost/serialkey/util/ROT47.java b/core/src/main/java/fr/skyost/serialkey/core/util/ROT47.java similarity index 93% rename from src/main/java/fr/skyost/serialkey/util/ROT47.java rename to core/src/main/java/fr/skyost/serialkey/core/util/ROT47.java index 481e636..0d97b01 100644 --- a/src/main/java/fr/skyost/serialkey/util/ROT47.java +++ b/core/src/main/java/fr/skyost/serialkey/core/util/ROT47.java @@ -1,4 +1,4 @@ -package fr.skyost.serialkey.util; +package fr.skyost.serialkey.core.util; /** * ROT47 algorithm implementation. diff --git a/core/src/main/java/fr/skyost/serialkey/core/util/Util.java b/core/src/main/java/fr/skyost/serialkey/core/util/Util.java new file mode 100644 index 0000000..1b1eeac --- /dev/null +++ b/core/src/main/java/fr/skyost/serialkey/core/util/Util.java @@ -0,0 +1,60 @@ +package fr.skyost.serialkey.core.util; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * Contains some useful methods. + */ + +public class Util { + + /** + * Returns the map which contains only the specified object. + * + * @param map The map. + * @param objects The object. + * + * @return A map which contains only the object (as keys). + */ + + public static Map keepAll(final Map map, final Collection objects) { + final Map result = new HashMap<>(); + for(final String object : objects) { + final char[] chars = new char[object.length()]; + object.getChars(0, object.length() > 3 ? 3 : object.length(), chars, 0); + for(final char c : chars) { + if(c == ' ') { + continue; + } + final String character = String.valueOf(c); + if(map.containsKey(character)) { + result.put(character, map.get(character)); + } + } + } + return result; + } + + /** + * Creates a map. + * + * @param keys The keys. + * @param values The values. + * + * @return The map. + */ + + public static Map createMap(final K[] keys, final V[] values) { + if(keys.length != values.length) { + return null; + } + final Map map = new HashMap<>(); + for(int i = 0; i != keys.length; i++) { + map.put(keys[i], values[i]); + } + return map; + } + +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..5fa2a85 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,3 @@ +signing.keyId=1E1199FD +signing.password=Pavilion +signing.secretKeyRingFile=C:/Users/Hugo/.gnupg/secring.gpg diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d2c45a4..fbca28a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Sun Nov 25 13:02:53 CET 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..a378a4f --- /dev/null +++ b/settings.gradle @@ -0,0 +1,3 @@ +include 'core' +include 'bukkit' +include 'sponge' diff --git a/sponge/build.gradle b/sponge/build.gradle new file mode 100644 index 0000000..99888f9 --- /dev/null +++ b/sponge/build.gradle @@ -0,0 +1,63 @@ +plugins { + id 'idea' + id 'de.fuerstenau.buildconfig' version '1.1.8' + id 'com.github.johnrengelman.shadow' version '2.0.4' + id 'org.spongepowered.plugin' version '0.9.0' + id 'java' + id 'signing' +} + +version = '1.0' +sourceCompatibility = 1.8 + +repositories { + mavenCentral() +} + +repositories { + maven { url = 'http://repo.bstats.org/content/repositories/releases/' } +} + +dependencies { + implementation project(':core') + compileOnly 'org.spongepowered:spongeapi:7.1.0' + annotationProcessor 'org.spongepowered:spongeapi:7.1.0' + implementation 'org.bstats:bstats-sponge-lite:1.2' +} + +signing { + sign configurations.archives +} + +shadowJar { + destinationDir = new File(rootProject.projectDir, 'build/release/') + version = project.version + mergeServiceFiles() + relocate 'org.bstats.sponge', 'fr.skyost.serialkey.sponge.util.bstats' +} + +def properties = new Properties() +file("plugin.properties").withInputStream { properties.load(it) } + +sponge { + plugin { + id = properties.id + meta { + name = properties.name + version = project.version + description = properties.description + url = properties.url + authors = Arrays.asList(String.valueOf(properties.authors).split(", ")) + } + } +} + +buildConfig { + packageName = project.group + '.sponge' + + buildConfigField 'String', 'PLUGIN_ID', String.valueOf(properties.id) + buildConfigField 'String', 'PLUGIN_NAME', String.valueOf(properties.name) + buildConfigField 'String', 'PLUGIN_DESCRIPTION', String.valueOf(properties.description) + buildConfigField 'String', 'PLUGIN_URL', String.valueOf(properties.url) + buildConfigField 'String', 'PLUGIN_AUTHORS', String.valueOf(properties.authors) +} \ No newline at end of file diff --git a/sponge/plugin.properties b/sponge/plugin.properties new file mode 100644 index 0000000..74708de --- /dev/null +++ b/sponge/plugin.properties @@ -0,0 +1,5 @@ +id=serial-key +name=SerialKey +description=Lock your doors and chests ! +url=https://ore.spongepowered.org/Skyost/SerialKey +authors=Skyost \ No newline at end of file diff --git a/sponge/src/main/java/fr/skyost/serialkey/sponge/SerialKey.java b/sponge/src/main/java/fr/skyost/serialkey/sponge/SerialKey.java new file mode 100644 index 0000000..a09823b --- /dev/null +++ b/sponge/src/main/java/fr/skyost/serialkey/sponge/SerialKey.java @@ -0,0 +1,229 @@ +package fr.skyost.serialkey.sponge; + +import com.google.inject.Inject; +import fr.skyost.serialkey.core.SerialKeyPlugin; +import fr.skyost.serialkey.sponge.command.SpongeGetKeyCommand; +import fr.skyost.serialkey.sponge.config.SpongeConfig; +import fr.skyost.serialkey.sponge.config.SpongePluginConfig; +import fr.skyost.serialkey.sponge.config.SpongePluginData; +import fr.skyost.serialkey.sponge.config.SpongePluginMessages; +import fr.skyost.serialkey.sponge.item.SpongeItemManager; +import fr.skyost.serialkey.sponge.listener.*; +import fr.skyost.serialkey.sponge.padlock.SpongePadlockManager; +import fr.skyost.serialkey.sponge.unlocker.SpongeUnlocker; +import fr.skyost.serialkey.sponge.util.OreUpdater; +import fr.skyost.serialkey.sponge.util.Permission; +import fr.skyost.serialkey.sponge.util.Util; +import org.bstats.sponge.MetricsLite; +import org.slf4j.Logger; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.command.CommandSource; +import org.spongepowered.api.command.spec.CommandSpec; +import org.spongepowered.api.config.ConfigDir; +import org.spongepowered.api.event.EventManager; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.game.state.GamePreInitializationEvent; +import org.spongepowered.api.event.game.state.GameStartedServerEvent; +import org.spongepowered.api.event.game.state.GameStoppingEvent; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.plugin.Plugin; +import org.spongepowered.api.service.permission.PermissionDescription; +import org.spongepowered.api.service.permission.PermissionService; +import org.spongepowered.api.text.Text; +import org.spongepowered.api.world.Location; +import org.spongepowered.api.world.World; + +import java.nio.file.Path; + +/** + * The SerialKey plugin class. + */ + +@Plugin(id = BuildConfig.PLUGIN_ID, name = BuildConfig.PLUGIN_NAME, description = BuildConfig.PLUGIN_DESCRIPTION, version = BuildConfig.VERSION, url = BuildConfig.PLUGIN_URL, authors = BuildConfig.PLUGIN_AUTHORS) +public class SerialKey implements SerialKeyPlugin> { + + /** + * The data folder. + */ + + @Inject + @ConfigDir(sharedRoot = false) + private Path dataFolder; + + /** + * bStats metrics. + */ + + @Inject + private MetricsLite metrics; + + /** + * The plugin's logger. + */ + + @Inject + private Logger logger; + + /** + * The plugin config. + */ + + private SpongePluginConfig config; + + /** + * The plugin messages. + */ + + private SpongePluginMessages messages; + + /** + * The item manager. + */ + + private SpongeItemManager itemManager; + + /** + * The unlocker. + */ + + private SpongeUnlocker unlocker; + + /** + * The padlock manager. + */ + + private SpongePadlockManager padlockManager; + + @Listener + public void onGamePreInitialize(final GamePreInitializationEvent event) throws SpongeConfig.InvalidConfigurationException { + // TODO: Needs proper error handling + // Configuration : + + config = new SpongePluginConfig(dataFolder.resolve("config.conf")); + config.load(); + messages = new SpongePluginMessages(dataFolder.resolve("messages.conf")); + messages.load(); + + // Core object : + + itemManager = new SpongeItemManager(this); + itemManager.createRecipes(); + unlocker = new SpongeUnlocker(this); + padlockManager = new SpongePadlockManager(this); + + // Events : + + final EventManager eventManager = Sponge.getEventManager(); + eventManager.registerListeners(this, new SpongeGlobalListener(this)); + eventManager.registerListeners(this, new SpongeBlocksListener(this)); + eventManager.registerListeners(this, new SpongeBunchOfKeysListener(this)); + eventManager.registerListeners(this, new SpongePadlockFinderListener(this)); + if(config.disableHoppers) { + eventManager.registerListeners(this, new SpongeHopperListener(this)); + } + if(!config.allowLostChests) { + eventManager.registerListeners(this, new SpongeLostChestsListener(this)); + } + + // Commands : + + Sponge.getServiceManager().getRegistration(PermissionService.class).ifPresent(registration -> registerPermissions(registration.getProvider())); + Sponge.getCommandManager().register(this, CommandSpec.builder() + .description(Text.of("Main command of SerialKey.")) + .child( + CommandSpec.builder() + .description(Text.of("Returns the key of the targeted block.")) + .permission("serialkey.command.getkey") + .executor(new SpongeGetKeyCommand(this)) + .build(), "getkey" + ) + .build(), "serial-key", "sk"); + + // Services : + + if(config.enableUpdater) { + new OreUpdater(logger).start(); + } + } + + @Listener + public void onGameStarted(final GameStartedServerEvent event) throws SpongeConfig.InvalidConfigurationException { + final SpongePluginData data = new SpongePluginData(dataFolder.resolve("data.conf")); + data.load(); + padlockManager.load(data); + } + + @Listener + public void onGameStop(final GameStoppingEvent event) throws SpongeConfig.InvalidConfigurationException { + final SpongePluginData data = new SpongePluginData(dataFolder.resolve("data.conf")); + padlockManager.save(data); + data.save(); + } + + @Override + public SpongeItemManager getItemManager() { + return itemManager; + } + + @Override + public SpongeUnlocker getUnlocker() { + return unlocker; + } + + @Override + public SpongePadlockManager getPadlockManager() { + return padlockManager; + } + + @Override + public SpongePluginConfig getPluginConfig() { + return config; + } + + @Override + public SpongePluginMessages getPluginMessages() { + return messages; + } + + /** + * Sends a message with the plugin's prefix. + * + * @param receiver Who receives the message. + * @param message The message. + */ + + public void sendMessage(final CommandSource receiver, final String message) { + receiver.sendMessage(Util.parseString(messages.prefix + " " + message)); + } + + /** + * Registers all plugin permissions. + * + * @param service The permission service. + */ + + private void registerPermissions(final PermissionService service) { + final Permission[] permissions = new Permission[]{ + new Permission("serialkey.craft.key", PermissionDescription.ROLE_USER, "Allows you to craft a key."), + new Permission("serialkey.craft.masterkey", PermissionDescription.ROLE_STAFF, "Allows you to craft a master key."), + new Permission("serialkey.craft.keyclone", PermissionDescription.ROLE_USER, "Allows you to craft a key clone."), + new Permission("serialkey.craft.bunchofkeys", PermissionDescription.ROLE_USER, "Allows you to craft a bunch of keys."), + new Permission("serialkey.craft.padlockfinder", PermissionDescription.ROLE_USER, "Allows you to craft a padlock finder."), + + new Permission("serialkey.use.key", PermissionDescription.ROLE_USER, "Allows you to use a key."), + new Permission("serialkey.use.masterkey", PermissionDescription.ROLE_STAFF, "Allows you to use a master key."), + new Permission("serialkey.use.bunchofkeys", PermissionDescription.ROLE_USER, "Allows you to use a bunch of keys."), + new Permission("serialkey.use.padlockfinder", PermissionDescription.ROLE_USER, "Allows you to use a padlock finder."), + + new Permission("serialkey.command.getkey", PermissionDescription.ROLE_STAFF, "Allows you to use /serialkey getkey.") + }; + for(final Permission permission : permissions) { + final PermissionDescription.Builder builder = service.newDescriptionBuilder(this); + builder.id(permission.getPermission()); + builder.assign(permission.getRole(), true); + builder.description(permission.getDescription()); + builder.register(); + } + } + +} \ No newline at end of file diff --git a/sponge/src/main/java/fr/skyost/serialkey/sponge/SpongeTypeConverter.java b/sponge/src/main/java/fr/skyost/serialkey/sponge/SpongeTypeConverter.java new file mode 100644 index 0000000..74ffead --- /dev/null +++ b/sponge/src/main/java/fr/skyost/serialkey/sponge/SpongeTypeConverter.java @@ -0,0 +1,121 @@ +package fr.skyost.serialkey.sponge; + +import fr.skyost.serialkey.core.object.PersonIdentity; +import fr.skyost.serialkey.core.object.SerialKeyLocation; +import fr.skyost.serialkey.core.object.SerialKeyPerson; +import fr.skyost.serialkey.sponge.util.Util; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.command.CommandSource; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.world.Location; +import org.spongepowered.api.world.World; + +import java.util.Optional; + +/** + * A class that allows to convert core objects to Sponge objects (and vice-versa). + */ + +public class SpongeTypeConverter { + + /** + * Converts a SerialKey location to a Sponge location. + * + * @param location The SerialKey location. + * + * @return The Sponge location. + */ + + public static Location toSpongeLocation(final SerialKeyLocation location) { + final Optional optional = Sponge.getServer().getWorld(location.getWorld()); + return optional.map(world -> new Location<>(world, location.getX(), location.getY(), location.getZ())).orElse(null); + } + + /** + * Converts a Sponge location to a SerialKey location. + * + * @param location The Sponge location. + * + * @return The SerialKey location. + */ + + public static SerialKeyLocation toSerialKeyLocation(final Location location) { + return new SerialKeyLocation(location.getExtent().getName(), location.getBlockX(), location.getBlockY(), location.getBlockZ()); + } + + /** + * Converts a SerialKey person to a Sponge command sender. + * + * @param person The SerialKey person. + * + * @return The Sponge command sender. + */ + + public static CommandSource toSpongePerson(final SerialKeyPerson person) { + final PersonIdentity identity = person.getIdentity(); + if(identity.getType() == PersonIdentity.Type.CONSOLE) { + return Sponge.getServer().getConsole(); + } + + return Sponge.getServer().getPlayer(identity.getName()).orElse(null); + } + + /** + * Converts a Sponge command sender to a SerialKey person. + * + * @param sender Sponge command sender. + * + * @return The SerialKey person. + */ + + public static SerialKeyPerson toSerialKeyPerson(final CommandSource sender) { + return new SpongePerson(sender); + } + + /** + * Represents a Sponge person. + */ + + private static class SpongePerson implements SerialKeyPerson { + + /** + * The command source instance. + */ + + private final CommandSource sender; + + /** + * The identity. + */ + + private final PersonIdentity identity; + + /** + * Creates a new Sponge person instance. + * + * @param sender The command source instance. + */ + + public SpongePerson(final CommandSource sender) { + this.sender = sender; + this.identity = new PersonIdentity(sender instanceof Player ? PersonIdentity.Type.PLAYER : PersonIdentity.Type.CONSOLE, sender.getName()); + } + + @Override + public PersonIdentity getIdentity() { + return identity; + } + + @Override + public void sendMessage(final String message) { + sender.sendMessage(Util.parseString(message)); + } + + @Override + public boolean hasPermission(final String permission) { + return sender.hasPermission(permission); + } + + } + +} \ No newline at end of file diff --git a/sponge/src/main/java/fr/skyost/serialkey/sponge/command/SpongeGetKeyCommand.java b/sponge/src/main/java/fr/skyost/serialkey/sponge/command/SpongeGetKeyCommand.java new file mode 100644 index 0000000..09c37b0 --- /dev/null +++ b/sponge/src/main/java/fr/skyost/serialkey/sponge/command/SpongeGetKeyCommand.java @@ -0,0 +1,56 @@ +package fr.skyost.serialkey.sponge.command; + +import fr.skyost.serialkey.core.SerialKeyPlugin; +import fr.skyost.serialkey.core.command.GetKeyCommand; +import fr.skyost.serialkey.sponge.SpongeTypeConverter; +import fr.skyost.serialkey.sponge.util.Util; +import org.spongepowered.api.command.CommandResult; +import org.spongepowered.api.command.CommandSource; +import org.spongepowered.api.command.args.CommandContext; +import org.spongepowered.api.command.spec.CommandExecutor; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.util.blockray.BlockRay; +import org.spongepowered.api.util.blockray.BlockRayHit; +import org.spongepowered.api.world.World; + +import javax.annotation.Nonnull; +import java.util.Optional; + +/** + * Represents the
/serialkey getkey
command executor. + */ + +public class SpongeGetKeyCommand extends GetKeyCommand implements CommandExecutor { + + /** + * Creates a new
/serialkey getkey
command executor instance. + * + * @param plugin The plugin instance. + */ + + public SpongeGetKeyCommand(final SerialKeyPlugin plugin) { + super(plugin); + } + + @Override + @Nonnull + public CommandResult execute(@Nonnull final CommandSource source, @Nonnull final CommandContext args) { + final Player player = source instanceof Player ? (Player)source : null; + final Optional> hit = player == null ? Optional.empty() : BlockRay.from(player).distanceLimit(100d).stopFilter(BlockRay.continueAfterFilter(BlockRay.onlyAirFilter(), 1)).build().end(); + + final int affectedEntities = super.execute( + SpongeTypeConverter.toSerialKeyPerson(source), + hit.map(hitBlock -> SpongeTypeConverter.toSerialKeyLocation(hitBlock.getLocation())).orElse(null), + player == null ? item -> {} : (item -> Util.dropItemAt(item, player.getLocation())) + ); + + return affectedEntities > 0 ? CommandResult.affectedEntities(affectedEntities) : CommandResult.empty(); + } + + @Override + protected ItemStack copyItem(final ItemStack item) { + return item.copy(); + } + +} \ No newline at end of file diff --git a/sponge/src/main/java/fr/skyost/serialkey/sponge/config/SpongeConfig.java b/sponge/src/main/java/fr/skyost/serialkey/sponge/config/SpongeConfig.java new file mode 100644 index 0000000..95d0315 --- /dev/null +++ b/sponge/src/main/java/fr/skyost/serialkey/sponge/config/SpongeConfig.java @@ -0,0 +1,285 @@ +package fr.skyost.serialkey.sponge.config; + +import com.google.common.base.Joiner; +import com.google.common.primitives.Primitives; +import ninja.leaping.configurate.ConfigurationNode; +import ninja.leaping.configurate.commented.CommentedConfigurationNode; +import ninja.leaping.configurate.hocon.HoconConfigurationLoader; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.item.ItemType; + +import java.io.File; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * A useful class that allows to serialize (and deserialize) its inheriting classes to a Sponge configuration. + */ + +public class SpongeConfig { + + /** + * The node separator. + */ + + private static final String SEPARATOR = "."; + + /** + * The file. + */ + + private final File file; + + /** + * The comments. + */ + + private final String[] comments; + + /** + * The configuration loader. + */ + + private final HoconConfigurationLoader loader; + + /** + * Creates a new Sponge config instance. + * + * @param file The file. + * @param comments The comments. + */ + + SpongeConfig(final Path file, final String... comments) { + this.file = file.toFile(); + this.comments = comments; + loader = HoconConfigurationLoader.builder().setPath(file).build(); + } + + /** + * Loads the fields from the config file. + * + * @throws InvalidConfigurationException If any error occurs. + */ + + public void load() throws InvalidConfigurationException { + try { + if(file.exists()) { + final CommentedConfigurationNode node = loader.load(); + node.setComment(Joiner.on(System.lineSeparator()).join(comments)); + + final Field[] fields = getClass().getDeclaredFields(); + for(final Field field : fields) { + field.setAccessible(true); + field.set(this, deserializeField(field, field.get(this), node)); + } + } + save(); + } + catch(final Exception ex) { + throw new InvalidConfigurationException(ex); + } + } + + /** + * Saves the fields to the config file. + * + * @throws InvalidConfigurationException If any error occurs. + */ + + public void save() throws InvalidConfigurationException { + try { + if(!file.exists()) { + file.getParentFile().mkdirs(); + file.createNewFile(); + } + + final ConfigurationNode node = loader.load(); + final Field[] fields = getClass().getDeclaredFields(); + for(final Field field : fields) { + field.setAccessible(true); + getFieldNode(field, node).setValue(serializeField(field.get(this))); + } + loader.save(node); + } + catch(final Exception ex) { + throw new InvalidConfigurationException(ex); + } + } + + /** + * Deserializes a field. + * + * @param field The field. + * @param defaultValue The default value. + * @param node The target node. + * + * @return The value. + * + * @throws IllegalAccessException If field access is not permitted. + * @throws InstantiationException If an object cannot be instantiated. + * @throws NoSuchMethodException If a specified method doesn't exist. + * @throws InvocationTargetException If a method cannot be invoked for the specified arguments. + */ + + private Object deserializeField(final Field field, final Object defaultValue, final ConfigurationNode node) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { + final ConfigurationNode fieldNode = getFieldNode(field, node); + + if(defaultValue instanceof Map) { + final Map result = (Map)defaultValue.getClass().newInstance(); + final Map childrenMap = fieldNode.getChildrenMap(); + for(final Map.Entry child : childrenMap.entrySet()) { + result.put(child.getKey(), child.getValue().getValue()); + } + + return result; + } + + if(defaultValue instanceof List) { + final List result = (List)defaultValue.getClass().newInstance(); + result.addAll(fieldNode.getList(object -> object, (List)defaultValue)); + return result; + } + + final Class clazz = defaultValue.getClass(); + if(clazz.isPrimitive()) { + return Primitives.wrap(clazz).getMethod("valueOf", String.class).invoke(this, fieldNode.getValue(defaultValue).toString()); + } + + if(Primitives.isWrapperType(clazz)) { + return clazz.getMethod("valueOf", String.class).invoke(this, fieldNode.getValue(defaultValue).toString()); + } + + if(defaultValue instanceof ItemType) { + return Sponge.getRegistry().getType(ItemType.class, fieldNode.getString(((ItemType)defaultValue).getId())).orElse(null); + } + + if(clazz.isEnum()) { + return Enum.valueOf((Class)clazz, fieldNode.getValue(defaultValue).toString()); + } + + if(defaultValue instanceof String) { + return fieldNode.getString((String)defaultValue); + } + + return defaultValue; + } + + /** + * Serializes a field. + * + * @param value The field value. + * + * @return The serialized value. + */ + + private Object serializeField(final Object value) { + if(value instanceof Map || value instanceof List || value instanceof String) { + return value; + } + + if(value instanceof ItemType) { + return ((ItemType)value).getId(); + } + + final Class clazz = value.getClass(); + if(clazz.isPrimitive() || Primitives.isWrapperType(clazz)) { + return value; + } + + if(clazz.isEnum()) { + return ((Enum)value).name(); + } + + return value; + } + + /** + * Returns the config node that corresponds to the specified field. + * + * @param field The field. + * @param parent The node parent. + * + * @return The config node that corresponds to the specified field. + */ + + private ConfigurationNode getFieldNode(final Field field, final ConfigurationNode parent) { + ConfigurationNode fieldNode = parent; + final String[] parts = getFieldName(field).split(Pattern.quote(SEPARATOR)); + for(final String part : parts) { + fieldNode = fieldNode.getNode(part); + } + + return fieldNode; + } + + /** + * Returns the string identifier that corresponds to the specified field. + * + * @param field The field. + * + * @return The string identifier that corresponds to the specified field. + */ + + private String getFieldName(final Field field) { + final ConfigOptions options = field.getAnnotation(ConfigOptions.class); + if(options == null || options.name().isEmpty()) { + return field.getName(); + } + return options.name(); + } + + /** + * Represents some config options that can be applied to a field. + */ + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + protected @interface ConfigOptions { + + /** + * The key's name. + * + * @return The key's name. + */ + + String name() default ""; + + } + + /** + * Represents an invalid configuration exception. + */ + + public class InvalidConfigurationException extends Exception { + + /** + * Creates a new invalid configuration exception instance. + * + * @param message The message. + */ + + private InvalidConfigurationException(final String message) { + super(message); + } + + /** + * Creates a new invalid configuration exception instance. + * + * @param throwable The throwable child. + */ + + private InvalidConfigurationException(final Throwable throwable) { + super(throwable); + } + + } + +} \ No newline at end of file diff --git a/sponge/src/main/java/fr/skyost/serialkey/sponge/config/SpongePluginConfig.java b/sponge/src/main/java/fr/skyost/serialkey/sponge/config/SpongePluginConfig.java new file mode 100644 index 0000000..05c2da7 --- /dev/null +++ b/sponge/src/main/java/fr/skyost/serialkey/sponge/config/SpongePluginConfig.java @@ -0,0 +1,117 @@ +package fr.skyost.serialkey.sponge.config; + +import fr.skyost.serialkey.core.config.SerialKeyConfig; +import org.spongepowered.api.item.ItemType; +import org.spongepowered.api.item.ItemTypes; + +import java.nio.file.Path; +import java.util.*; + +/** + * The plugin configuration class. + */ + +public class SpongePluginConfig extends SpongeConfig implements SerialKeyConfig { + + @ConfigOptions(name = "enable.updater") + public boolean enableUpdater = true; + + @ConfigOptions(name = "options.reusable-keys") + public boolean reusableKeys = true; + @ConfigOptions(name = "options.disable-hoppers") + public boolean disableHoppers = true; + @ConfigOptions(name = "options.encrypt-lore") + public boolean encryptLore = false; + @ConfigOptions(name = "options.can-rename-items") + public boolean canRenameItems = false; + @ConfigOptions(name = "options.allow-lost-chests") + public boolean allowLostChests = true; + + @ConfigOptions(name = "key.material") + public ItemType keyMaterial = ItemTypes.TRIPWIRE_HOOK; + @ConfigOptions(name = "key.name") + public String keyName = "&6Key"; + @ConfigOptions(name = "key.recipe") + public List keyShape = new ArrayList<>(Arrays.asList("A", "B")); + + @ConfigOptions(name = "master-key.material") + public ItemType masterKeyMaterial = ItemTypes.NAME_TAG; + @ConfigOptions(name = "master-key.name") + public String masterKeyName = "&5Master Key"; + @ConfigOptions(name = "master-key.recipe") + public List masterKeyShape = new ArrayList<>(Arrays.asList("C", "B")); + + @ConfigOptions(name = "bunch-of-keys.material") + public ItemType bunchOfKeysMaterial = ItemTypes.NAME_TAG; + @ConfigOptions(name = "bunch-of-keys.name") + public String bunchOfKeysName = "&9Bunch of keys"; + @ConfigOptions(name = "bunch-of-keys.recipe") + public List bunchOfKeysShape = new ArrayList<>(Arrays.asList(" D ", "DBD", " D ")); + + @ConfigOptions(name = "padlock-finder.name") + public String padlockFinderName = "&cPadlock finder"; + + @ConfigOptions(name = "recipe-materials-v1") + public LinkedHashMap shapeMaterials = new LinkedHashMap<>(); + + /** + * Creates a new plugin configuration instance. + * + * @param file The config file. + */ + + public SpongePluginConfig(final Path file) { + super(file, "SerialKey Configuration"); + + shapeMaterials.put("A", ItemTypes.IRON_INGOT.getId()); + shapeMaterials.put("B", ItemTypes.LEVER.getId()); + shapeMaterials.put("C", ItemTypes.COMMAND_BLOCK.getId()); + shapeMaterials.put("D", ItemTypes.STRING.getId()); + } + + @Override + public boolean areKeysReusable() { + return reusableKeys; + } + + @Override + public boolean areLoresEncrypted() { + return encryptLore; + } + + @Override + public boolean canRenameItems() { + return canRenameItems; + } + + @Override + public List getKeyShape() { + return keyShape; + } + + @Override + public List getMasterKeyShape() { + return masterKeyShape; + } + + @Override + public List getBunchOfKeysShape() { + return bunchOfKeysShape; + } + + @Override + public Map getShapeMaterials() { + return shapeMaterials; + } + + @Override + public String getKeyMaterialID() { + return keyMaterial.getId(); + } + + @Override + public String getPadlockFinderMaterialID() { + return ItemTypes.COMPASS.getId(); + } + +} \ No newline at end of file diff --git a/sponge/src/main/java/fr/skyost/serialkey/sponge/config/SpongePluginData.java b/sponge/src/main/java/fr/skyost/serialkey/sponge/config/SpongePluginData.java new file mode 100644 index 0000000..d2692b6 --- /dev/null +++ b/sponge/src/main/java/fr/skyost/serialkey/sponge/config/SpongePluginData.java @@ -0,0 +1,34 @@ +package fr.skyost.serialkey.sponge.config; + +import fr.skyost.serialkey.core.config.SerialKeyData; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * The plugin data class. + */ + +public class SpongePluginData extends SpongeConfig implements SerialKeyData { + + @ConfigOptions(name = "padlocks") + public List padlocks = new ArrayList<>(); + + /** + * Creates a new plugin data instance. + * + * @param file The config file. + */ + + public SpongePluginData(final Path file) { + super(file, "SerialKey Data"); + } + + @Override + public Collection getData() { + return padlocks; + } + +} \ No newline at end of file diff --git a/sponge/src/main/java/fr/skyost/serialkey/sponge/config/SpongePluginMessages.java b/sponge/src/main/java/fr/skyost/serialkey/sponge/config/SpongePluginMessages.java new file mode 100644 index 0000000..1a9c695 --- /dev/null +++ b/sponge/src/main/java/fr/skyost/serialkey/sponge/config/SpongePluginMessages.java @@ -0,0 +1,80 @@ +package fr.skyost.serialkey.sponge.config; + +import fr.skyost.serialkey.core.config.SerialKeyMessages; + +import java.nio.file.Path; + +/** + * The plugin messages class. + */ + +public class SpongePluginMessages extends SpongeConfig implements SerialKeyMessages { + + @ConfigOptions(name = "messages.prefix") + public String prefix = "&b[SerialKey]"; + @ConfigOptions(name = "messages.permission") + public String messagePermission = "&cYou do not have the permission to perform this action."; + @ConfigOptions(name = "messages.padlock-placed") + public String messagePadlockPlaced = "&aPadlock placed ! If you want to remove it, you have to break this block."; + @ConfigOptions(name = "messages.padlock-removed") + public String messagePadlockRemoved = "&6Padlock removed."; + @ConfigOptions(name = "messages.block-has-padlock") + public String messageBlockHasPadlock = "&cThis block has a padlock."; + @ConfigOptions(name = "messages.padlock-finder-enabled") + public String messagePadlockFinderEnabled = "&aPadlock finder enabled ! Your compasses will now point to its object. You can reset it back to the spawn by doing another right click with any padlock finder."; + @ConfigOptions(name = "messages.padlock-finder-disabled") + public String messagePadlockFinderDisabled = "&cPadlock finder has been disabled."; + @ConfigOptions(name = "messages.chest-protection") + public String messageChestProtection = "&cYou can't place this key in this chest."; + + /** + * Creates a new plugin messages instance. + * + * @param file The config file. + */ + + public SpongePluginMessages(final Path file) { + super(file, "SerialKey Messages"); + } + + @Override + public String getPrefix() { + return prefix; + } + + @Override + public String getPermissionMessage() { + return messagePermission; + } + + @Override + public String getPadlockPlacedMessage() { + return messagePadlockPlaced; + } + + @Override + public String getPadlockRemovedMessage() { + return messagePadlockRemoved; + } + + @Override + public String getBlockHasPadlockMessage() { + return messageBlockHasPadlock; + } + + @Override + public String getPadlockFinderEnabledMessage() { + return messagePadlockFinderEnabled; + } + + @Override + public String getPadlockFinderDisabledMessage() { + return messagePadlockFinderDisabled; + } + + @Override + public String getChestProtectionMessage() { + return messageChestProtection; + } + +} \ No newline at end of file diff --git a/sponge/src/main/java/fr/skyost/serialkey/sponge/item/SpongeItemManager.java b/sponge/src/main/java/fr/skyost/serialkey/sponge/item/SpongeItemManager.java new file mode 100644 index 0000000..e373b33 --- /dev/null +++ b/sponge/src/main/java/fr/skyost/serialkey/sponge/item/SpongeItemManager.java @@ -0,0 +1,195 @@ +package fr.skyost.serialkey.sponge.item; + +import fr.skyost.serialkey.core.item.PluginItemManager; +import fr.skyost.serialkey.core.unlocker.LoreUnlocker; +import fr.skyost.serialkey.sponge.BuildConfig; +import fr.skyost.serialkey.sponge.SerialKey; +import fr.skyost.serialkey.sponge.config.SpongePluginConfig; +import fr.skyost.serialkey.sponge.util.Util; +import org.spongepowered.api.GameRegistry; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.data.key.Keys; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.event.item.inventory.InteractInventoryEvent; +import org.spongepowered.api.item.ItemType; +import org.spongepowered.api.item.ItemTypes; +import org.spongepowered.api.item.inventory.Inventory; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.item.inventory.property.InventoryDimension; +import org.spongepowered.api.item.inventory.property.InventoryTitle; +import org.spongepowered.api.item.recipe.crafting.CraftingRecipe; +import org.spongepowered.api.item.recipe.crafting.Ingredient; +import org.spongepowered.api.item.recipe.crafting.ShapedCraftingRecipe; +import org.spongepowered.api.text.Text; +import org.spongepowered.api.text.serializer.TextSerializers; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * The Sponge item manager class. + */ + +public class SpongeItemManager extends PluginItemManager { + + /** + * The plugin instance. + */ + + private final SerialKey plugin; + + /** + * Creates a new Sponge item manager instance. + * + * @param plugin The plugin instance. + */ + + public SpongeItemManager(final SerialKey plugin) { + this(plugin, plugin.getPluginConfig(), Util.createItem(plugin.getPluginConfig().keyName, plugin.getPluginConfig().keyMaterial)); + } + + /** + * Creates a new Bukkit item manager instance. + * + * @param plugin The plugin instance. + * @param key The key item. + */ + + private SpongeItemManager(final SerialKey plugin, final SpongePluginConfig config, final ItemStack key) { + super(plugin.getPluginConfig(), key, Util.createItem(config.masterKeyName, config.masterKeyMaterial), key.copy(), Util.createItem(config.bunchOfKeysName, config.bunchOfKeysMaterial), Util.createItem(config.padlockFinderName, ItemTypes.COMPASS)); + + this.plugin = plugin; + getKeyCloneItem().setQuantity(2); + } + + /** + * Creates a recipe for an item. + * + * @param id The recipe ID. + * @param result The item. + * @param shape The shape. + * @param ingredients The ingredients needed for the craft. + */ + + @Override + public void createRecipe(final String id, final ItemStack result, final List shape, Map ingredients) { + if(ingredients.equals(plugin.getPluginConfig().shapeMaterials)) { + ingredients = fr.skyost.serialkey.core.util.Util.keepAll(ingredients, shape); + } + + final GameRegistry registry = Sponge.getRegistry(); + final Map where = new HashMap<>(); + ingredients.forEach((character, type) -> registry.getType(ItemType.class, type).ifPresent(itemType -> where.put(character.charAt(0), Ingredient.of(itemType)))); + + final CraftingRecipe recipe = ShapedCraftingRecipe.builder() + .aisle(shape.toArray(new String[0])) + .where(where) + .result(result) + .build(id, plugin); + + registry.getCraftingRecipeRegistry().register(recipe); + } + + @Override + protected boolean isItemValid(final ItemStack item) { + return item != null && item.get(Keys.DISPLAY_NAME).isPresent(); + } + + @Override + protected boolean compareItemsName(final ItemStack item1, final ItemStack item2) { + return item1.get(Keys.DISPLAY_NAME).equals(item2.get(Keys.DISPLAY_NAME)); + } + + @Override + protected boolean compareItemsType(final ItemStack item1, final ItemStack item2) { + return item1.getType() == item2.getType(); + } + + @Override + public List getLore(final ItemStack object) { + if(object == null) { + return new ArrayList<>(); + } + + final Optional> lore = object.get(Keys.ITEM_LORE); + return lore.map(texts -> texts.stream().map(TextSerializers.FORMATTING_CODE::serialize).collect(Collectors.toList())).orElseGet(ArrayList::new); + } + + @Override + public void setLore(final ItemStack object, final List lore) { + object.offer(Keys.ITEM_LORE, lore == null ? new ArrayList<>() : lore.stream().map(Util::parseString).collect(Collectors.toList())); + } + + /** + * Creates an inventory for the specified bunch of keys and opens it for the specified players. + * + * @param unlocker The unlocker. + * @param bunchOfKeys The bunch of keys item. + * @param players The players. + * + * @return The inventory. + */ + + public Inventory createInventory(final LoreUnlocker unlocker, final ItemStack bunchOfKeys, final Player... players) { + final Inventory inventory = Inventory.builder() + .property(InventoryTitle.of(bunchOfKeys.get(Keys.DISPLAY_NAME).orElse(Util.parseString(BuildConfig.PLUGIN_NAME)))) + .property(InventoryDimension.of(9, 1)) + .listener(InteractInventoryEvent.Close.class, event -> event.getCause().first(Player.class).ifPresent(player -> onBunchOfKeysInventoryClose(unlocker, event.getTargetInventory().first(), bunchOfKeys, player))) + .build(plugin); + + final List lore = bunchOfKeys.get(Keys.ITEM_LORE).orElse(new ArrayList<>()); + final int n = lore.size(); + for(int i = 0; i < n; i++) { + final ItemStack key = getKeyItem().copy(); + key.offer(Keys.ITEM_LORE, Arrays.asList(lore.get(i), lore.get(++i))); + inventory.offer(key); + } + + if(players != null) { + for(final Player player : players) { + player.openInventory(inventory); + } + } + + return inventory; + } + + /** + * Triggered when a bunch of keys inventory is closed. + * + * @param unlocker The unlocker. + * @param inventory The inventory. + * @param bunchOfKeys The bunch of keys item. + * @param player The involved player. + */ + + private void onBunchOfKeysInventoryClose(final LoreUnlocker unlocker, final Inventory inventory, final ItemStack bunchOfKeys, final Player player) { + if(bunchOfKeys.getQuantity() > 1) { + final ItemStack clone = bunchOfKeys.copy(); + clone.setQuantity(bunchOfKeys.getQuantity() - 1); + Util.dropItemAt(clone, player.getLocation()); + bunchOfKeys.setQuantity(1); + } + + unlocker.clearLocations(bunchOfKeys); + + while(inventory.size() > 0) { + inventory.poll().ifPresent( + item -> { + if(!isUsedKey(item)) { + Util.dropItemAt(item, player.getLocation()); + return; + } + + unlocker.addLocation(bunchOfKeys, item); + if(item.getQuantity() > 1) { + final ItemStack clone = item.copy(); + clone.setQuantity(item.getQuantity() - 1); + Util.dropItemAt(clone, player.getLocation()); + } + } + ); + } + } + +} diff --git a/sponge/src/main/java/fr/skyost/serialkey/sponge/listener/SpongeBlocksListener.java b/sponge/src/main/java/fr/skyost/serialkey/sponge/listener/SpongeBlocksListener.java new file mode 100644 index 0000000..1eaa6ae --- /dev/null +++ b/sponge/src/main/java/fr/skyost/serialkey/sponge/listener/SpongeBlocksListener.java @@ -0,0 +1,77 @@ +package fr.skyost.serialkey.sponge.listener; + +import fr.skyost.serialkey.core.SerialKeyPlugin; +import fr.skyost.serialkey.core.listener.BlocksListener; +import org.spongepowered.api.block.BlockSnapshot; +import org.spongepowered.api.data.Transaction; +import org.spongepowered.api.data.type.HandTypes; +import org.spongepowered.api.entity.living.Creature; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.Order; +import org.spongepowered.api.event.block.ChangeBlockEvent; +import org.spongepowered.api.event.block.NotifyNeighborBlockEvent; +import org.spongepowered.api.event.filter.cause.First; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.util.Direction; +import org.spongepowered.api.world.LocatableBlock; +import org.spongepowered.api.world.Location; +import org.spongepowered.api.world.World; +import org.spongepowered.api.world.explosion.Explosion; + +import java.util.List; +import java.util.Set; + +/** + * A listener that allows to listen blocks related events. + */ + +public class SpongeBlocksListener extends BlocksListener> { + + /** + * Creates a new blocks listener instance. + * + * @param plugin The plugin. + */ + + public SpongeBlocksListener(final SerialKeyPlugin> plugin) { + super(plugin); + } + + @Listener(order = Order.LATE) + public void onBlockPlace(final ChangeBlockEvent.Place event, final @First Player player) { + player.getItemInHand(HandTypes.MAIN_HAND).ifPresent(item -> super.onBlockPlace(item, () -> event.setCancelled(true))); + } + + @Listener(order = Order.LATE) + public void onBlockBreak(final ChangeBlockEvent event, @First final Creature creature) { + final List> transactions = event.getTransactions(); + for(final Transaction transaction : transactions) { + if(super.onBlockBreakByCreature(transaction.getOriginal().getLocation().orElse(null), () -> event.setCancelled(true))) { + break; + } + } + } + + @Listener(order = Order.LATE) + public void onBlockExplode(final ChangeBlockEvent event, @First final Explosion explosion) { + final List> transactions = event.getTransactions(); + for(final Transaction transaction : transactions) { + if(super.onBlockExplode(transaction.getOriginal().getLocation().orElse(null), () -> event.setCancelled(true))) { + break; + } + } + } + + @Listener(order = Order.LATE) + public void onNotifyNeighborBlock(final NotifyNeighborBlockEvent event, @First final LocatableBlock source) { + final Location location = source.getLocation(); + final Set directions = event.getNeighbors().keySet(); + for(final Direction direction : directions) { + if(super.onBlockPoweredByRedstone(location.getBlockRelative(direction), () -> event.setCancelled(true))) { + break; + } + } + } + +} \ No newline at end of file diff --git a/sponge/src/main/java/fr/skyost/serialkey/sponge/listener/SpongeBunchOfKeysListener.java b/sponge/src/main/java/fr/skyost/serialkey/sponge/listener/SpongeBunchOfKeysListener.java new file mode 100644 index 0000000..4dcca9b --- /dev/null +++ b/sponge/src/main/java/fr/skyost/serialkey/sponge/listener/SpongeBunchOfKeysListener.java @@ -0,0 +1,81 @@ +package fr.skyost.serialkey.sponge.listener; + +import fr.skyost.serialkey.core.SerialKeyPlugin; +import fr.skyost.serialkey.core.listener.BunchOfKeysListener; +import fr.skyost.serialkey.sponge.item.SpongeItemManager; +import org.spongepowered.api.block.BlockSnapshot; +import org.spongepowered.api.block.BlockTypes; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.event.Cancellable; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.Order; +import org.spongepowered.api.event.block.InteractBlockEvent; +import org.spongepowered.api.event.filter.cause.First; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.world.Location; +import org.spongepowered.api.world.World; + +/** + * A listener that allows to listen bunch of keys related events. + */ + +public class SpongeBunchOfKeysListener extends BunchOfKeysListener> { + + /** + * The item manager. + */ + + private SpongeItemManager itemManager; + + /** + * Creates a new bunch of keys listener instance. + * + * @param plugin The plugin. + */ + + public SpongeBunchOfKeysListener(final SerialKeyPlugin> plugin) { + super(plugin); + } + + @Override + public void setPlugin(final SerialKeyPlugin> plugin) { + if(!(plugin.getItemManager() instanceof SpongeItemManager)) { + throw new IllegalArgumentException("Invalid item manager provided."); + } + + super.setPlugin(plugin); + itemManager = (SpongeItemManager)plugin.getItemManager(); + } + + @Listener(order = Order.EARLY) + public void onPlayerRightClick(final InteractBlockEvent.Secondary event, @First final Player player) { + final ItemStack item = player.getItemInHand(event.getHandType()).orElse(null); + if(item == null) { + return; + } + + final BlockSnapshot block = event.getTargetBlock(); + block.getLocation().ifPresent(location -> { + final Runnable cancelIfCreateInventory = () -> cancelIfCreateInventory(event, item, player); + if(location.getBlockType() == BlockTypes.AIR) { + super.onPlayerRightClickOnAir(item, cancelIfCreateInventory); + return; + } + + super.onPlayerRightClickOnBlock(location, item, cancelIfCreateInventory); + }); + } + + /** + * Cancels the specified event if the inventory has been successfully created. + * + * @param event The event. + */ + + private void cancelIfCreateInventory(final Cancellable event, final ItemStack item, final Player player) { + if(itemManager.createInventory(unlocker, item, player) != null) { + event.setCancelled(true); + } + } + +} \ No newline at end of file diff --git a/sponge/src/main/java/fr/skyost/serialkey/sponge/listener/SpongeGlobalListener.java b/sponge/src/main/java/fr/skyost/serialkey/sponge/listener/SpongeGlobalListener.java new file mode 100644 index 0000000..605bca2 --- /dev/null +++ b/sponge/src/main/java/fr/skyost/serialkey/sponge/listener/SpongeGlobalListener.java @@ -0,0 +1,111 @@ +package fr.skyost.serialkey.sponge.listener; + +import fr.skyost.serialkey.core.SerialKeyPlugin; +import fr.skyost.serialkey.core.listener.GlobalListener; +import fr.skyost.serialkey.core.object.SerialKeyLocation; +import fr.skyost.serialkey.sponge.BuildConfig; +import fr.skyost.serialkey.sponge.SpongeTypeConverter; +import fr.skyost.serialkey.sponge.util.ChestUtil; +import fr.skyost.serialkey.sponge.util.DoorUtil; +import fr.skyost.serialkey.sponge.util.Util; +import org.spongepowered.api.block.BlockType; +import org.spongepowered.api.effect.sound.SoundTypes; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.Order; +import org.spongepowered.api.event.block.InteractBlockEvent; +import org.spongepowered.api.event.filter.cause.First; +import org.spongepowered.api.event.item.inventory.CraftItemEvent; +import org.spongepowered.api.item.ItemTypes; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.item.inventory.transaction.SlotTransaction; +import org.spongepowered.api.world.Location; +import org.spongepowered.api.world.World; + +import java.util.ArrayList; +import java.util.List; + +/** + * A listener that allows to globally listen plugin events. + */ + +public class SpongeGlobalListener extends GlobalListener> { + + /** + * Creates a new global listener instance. + * + * @param plugin The plugin. + */ + + public SpongeGlobalListener(final SerialKeyPlugin> plugin) { + super(plugin); + } + + @Listener(order = Order.EARLY) + public void onPreviewItemCraft(final CraftItemEvent.Preview event, @First final Player player) { + final SlotTransaction preview = event.getPreview(); + final ItemStack result = preview.getFinal().createStack(); + + final List items = new ArrayList<>(); + event.getCraftingInventory().slots().forEach(slot -> items.add(slot.peek().orElse(null))); + + final String id = event.getRecipe().isPresent() ? event.getRecipe().get().getId().replaceFirst(BuildConfig.PLUGIN_ID + ":", "") : null; + super.onPreviewItemCraft( + SpongeTypeConverter.toSerialKeyPerson(player), + items.toArray(new ItemStack[0]), + id, + result, + itemManager.isBlankKey(result) ? (item -> itemManager.isBlankKey(item)) : (item -> item.getType() == ItemTypes.COMPASS), + item -> event.getPreview().setCustom(item), + () -> preview.setCustom(Util.blankItem()) + ); + } + + @Listener(order = Order.LATE) + public void onPlayerLeftClick(final InteractBlockEvent.Primary event, @First final Player player) { + event.getTargetBlock().getLocation().ifPresent( + location -> super.onPlayerLeftClick( + player.getItemInHand(event.getHandType()).orElse(Util.blankItem()), + SpongeTypeConverter.toSerialKeyLocation(location), + SpongeTypeConverter.toSerialKeyPerson(player), + SpongeTypeConverter.toSerialKeyLocation(player.getLocation()), + item -> Util.dropItemAt(item, player.getLocation()), + () -> player.setItemInHand(event.getHandType(), Util.blankItem()), + () -> location.getExtent().playSound(SoundTypes.ENTITY_ITEM_BREAK, location.getPosition(), 1f), + () -> event.setCancelled(true) + ) + ); + } + + @Listener(order = Order.LATE) + public void onPlayerRightClick(final InteractBlockEvent.Secondary event, @First final Player player) { + event.getTargetBlock().getLocation().ifPresent(location -> super.onPlayerRightClick( + player.getItemInHand(event.getHandType()).orElse(Util.blankItem()), + SpongeTypeConverter.toSerialKeyLocation(location), + SpongeTypeConverter.toSerialKeyPerson(player), + () -> event.setCancelled(true) + )); + } + + @Override + protected ItemStack copy(final ItemStack item) { + return item.copy(); + } + + @Override + protected int getAmount(final ItemStack item) { + return item.getQuantity(); + } + + @Override + protected void setAmount(final ItemStack item, final int amount) { + item.setQuantity(amount); + } + + @Override + protected boolean isPadlockLocationValid(final SerialKeyLocation location) { + final BlockType type = SpongeTypeConverter.toSpongeLocation(location).getBlockType(); + return ChestUtil.isChest(type) || DoorUtil.isDoor(type) || DoorUtil.isTrapdoor(type); + } + +} \ No newline at end of file diff --git a/sponge/src/main/java/fr/skyost/serialkey/sponge/listener/SpongeHopperListener.java b/sponge/src/main/java/fr/skyost/serialkey/sponge/listener/SpongeHopperListener.java new file mode 100644 index 0000000..d489478 --- /dev/null +++ b/sponge/src/main/java/fr/skyost/serialkey/sponge/listener/SpongeHopperListener.java @@ -0,0 +1,54 @@ +package fr.skyost.serialkey.sponge.listener; + +import fr.skyost.serialkey.core.SerialKeyPlugin; +import fr.skyost.serialkey.core.listener.HopperListener; +import fr.skyost.serialkey.core.object.SerialKeyLocation; +import fr.skyost.serialkey.core.object.SerialKeyPerson; +import fr.skyost.serialkey.sponge.SpongeTypeConverter; +import fr.skyost.serialkey.sponge.util.ChestUtil; +import org.spongepowered.api.block.BlockSnapshot; +import org.spongepowered.api.block.BlockTypes; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.Order; +import org.spongepowered.api.event.block.ChangeBlockEvent; +import org.spongepowered.api.event.filter.cause.First; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.world.Location; +import org.spongepowered.api.world.World; + +/** + * A listener that allows to listen hoppers related events. + */ + +public class SpongeHopperListener extends HopperListener> { + + /** + * Creates a new hoppers listener instance. + * + * @param plugin The plugin. + */ + + public SpongeHopperListener(final SerialKeyPlugin> plugin) { + super(plugin); + } + + @Listener(order = Order.LATE) + public void onBlockPlace(final ChangeBlockEvent.Place event, final @First Player player) { + final SerialKeyPerson person = SpongeTypeConverter.toSerialKeyPerson(player); + event.getTransactions().forEach(transaction -> { + final BlockSnapshot block = transaction.getFinal(); + if(block.getState().getType() != BlockTypes.HOPPER) { + return; + } + + block.getLocation().ifPresent(location -> super.onBlockPlace(SpongeTypeConverter.toSerialKeyLocation(location), person, () -> transaction.setValid(false))); + }); + } + + @Override + protected boolean isChest(final SerialKeyLocation location) { + return ChestUtil.isChest(SpongeTypeConverter.toSpongeLocation(location).getBlockType()); + } + +} \ No newline at end of file diff --git a/sponge/src/main/java/fr/skyost/serialkey/sponge/listener/SpongeLostChestsListener.java b/sponge/src/main/java/fr/skyost/serialkey/sponge/listener/SpongeLostChestsListener.java new file mode 100644 index 0000000..edf617f --- /dev/null +++ b/sponge/src/main/java/fr/skyost/serialkey/sponge/listener/SpongeLostChestsListener.java @@ -0,0 +1,55 @@ +package fr.skyost.serialkey.sponge.listener; + +import fr.skyost.serialkey.core.SerialKeyPlugin; +import fr.skyost.serialkey.core.listener.LostChestsListener; +import fr.skyost.serialkey.sponge.SpongeTypeConverter; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.Order; +import org.spongepowered.api.event.filter.cause.First; +import org.spongepowered.api.event.item.inventory.ClickInventoryEvent; +import org.spongepowered.api.item.inventory.BlockCarrier; +import org.spongepowered.api.item.inventory.Inventory; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.item.inventory.type.CarriedInventory; +import org.spongepowered.api.world.Location; +import org.spongepowered.api.world.World; + +/** + * A listener that allows to listen chests related events. + */ + +public class SpongeLostChestsListener extends LostChestsListener> { + + /** + * Creates a new lost chests listener instance. + * + * @param plugin The plugin. + */ + + public SpongeLostChestsListener(final SerialKeyPlugin> plugin) { + super(plugin); + } + + @Listener(order = Order.EARLY) + public void onInventoryClick(final ClickInventoryEvent event, @First final Player player) { + final Inventory holder = event.getTargetInventory(); + if(!(holder instanceof CarriedInventory)) { + return; + } + + ((CarriedInventory)holder).getCarrier().ifPresent(carrier -> { + if(!(carrier instanceof BlockCarrier)) { + return; + } + + super.onInventoryClick( + SpongeTypeConverter.toSerialKeyPerson(player), + SpongeTypeConverter.toSerialKeyLocation(((BlockCarrier)carrier).getLocation()), + event.getCursorTransaction().getFinal().createStack(), + () -> event.setCancelled(true) + ); + }); + } + +} \ No newline at end of file diff --git a/sponge/src/main/java/fr/skyost/serialkey/sponge/listener/SpongePadlockFinderListener.java b/sponge/src/main/java/fr/skyost/serialkey/sponge/listener/SpongePadlockFinderListener.java new file mode 100644 index 0000000..d8aebfe --- /dev/null +++ b/sponge/src/main/java/fr/skyost/serialkey/sponge/listener/SpongePadlockFinderListener.java @@ -0,0 +1,48 @@ +package fr.skyost.serialkey.sponge.listener; + +import com.flowpowered.math.vector.Vector3d; +import fr.skyost.serialkey.core.SerialKeyPlugin; +import fr.skyost.serialkey.core.listener.PadlockFinderListener; +import fr.skyost.serialkey.core.object.SerialKeyLocation; +import fr.skyost.serialkey.sponge.SpongeTypeConverter; +import fr.skyost.serialkey.sponge.util.Util; +import org.spongepowered.api.data.key.Keys; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.Order; +import org.spongepowered.api.event.block.InteractBlockEvent; +import org.spongepowered.api.event.filter.cause.First; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.world.Location; +import org.spongepowered.api.world.World; + +/** + * A listener that allows to listen padlock finder related events. + */ + +public class SpongePadlockFinderListener extends PadlockFinderListener> { + + /** + * Creates a new padlock finder listener instance. + * + * @param plugin The plugin. + */ + + public SpongePadlockFinderListener(final SerialKeyPlugin> plugin) { + super(plugin); + } + + @Listener(order = Order.LATE) + public void onPlayerRightClick(final InteractBlockEvent.Secondary event, @First final Player player) { + final SerialKeyLocation spawn = SpongeTypeConverter.toSerialKeyLocation(player.getWorld().getSpawnLocation()); + super.onPlayerRightClick( + player.getItemInHand(event.getHandType()).orElse(Util.blankItem()), + SpongeTypeConverter.toSerialKeyPerson(player), + spawn, + player.get(Keys.TARGETED_LOCATION).map(position -> new SerialKeyLocation(player.getWorld().getName(), (int)position.getX(), (int)position.getY(), (int)position.getZ())).orElse(spawn), + location -> player.offer(Keys.TARGETED_LOCATION, new Vector3d(location.getX(), location.getY(), location.getZ())), + () -> event.setCancelled(true) + ); + } + +} \ No newline at end of file diff --git a/sponge/src/main/java/fr/skyost/serialkey/sponge/padlock/SpongePadlockManager.java b/sponge/src/main/java/fr/skyost/serialkey/sponge/padlock/SpongePadlockManager.java new file mode 100644 index 0000000..0841ed0 --- /dev/null +++ b/sponge/src/main/java/fr/skyost/serialkey/sponge/padlock/SpongePadlockManager.java @@ -0,0 +1,72 @@ +package fr.skyost.serialkey.sponge.padlock; + +import fr.skyost.serialkey.core.object.SerialKeyLocation; +import fr.skyost.serialkey.core.padlock.PluginPadlockManager; +import fr.skyost.serialkey.sponge.SerialKey; +import fr.skyost.serialkey.sponge.SpongeTypeConverter; +import fr.skyost.serialkey.sponge.util.ChestUtil; +import fr.skyost.serialkey.sponge.util.DoorUtil; +import org.spongepowered.api.block.tileentity.carrier.Chest; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.world.Location; +import org.spongepowered.api.world.World; + +import java.util.Set; + +/** + * The Sponge padlock manager. + */ + +public class SpongePadlockManager extends PluginPadlockManager> { + + /** + * Creates a new Sponge padlock manager instance. + * + * @param plugin The plugin. + */ + + public SpongePadlockManager(final SerialKey plugin) { + super(plugin); + } + + @Override + public boolean fixLocation(final SerialKeyLocation location) { + if(hasPadlock(location, false)) { + return false; + } + + final Location spongeLocation = SpongeTypeConverter.toSpongeLocation(location); + final Chest chest; + if((chest = ChestUtil.getChest(spongeLocation)) != null) { + final Set connectedChests = chest.getConnectedChests(); + for(final Chest connectedChest : connectedChests) { + final Location connectedLocation = connectedChest.getLocation(); + if(hasPadlock(pluginLocationToSerialKeyLocation(connectedLocation), false)) { + location.setX(connectedLocation.getBlockX()); + location.setZ(connectedLocation.getBlockZ()); + return true; + } + } + } + else if(DoorUtil.isDoor(spongeLocation)) { + location.setY(DoorUtil.getBlockBelow(spongeLocation).getBlockY()); + if(hasPadlock(location, false)) { + return true; + } + final Location doubleDoor = DoorUtil.getDoubleDoor(SpongeTypeConverter.toSpongeLocation(location)); + if(doubleDoor != null) { + location.setX(doubleDoor.getBlockX()); + location.setZ(doubleDoor.getBlockZ()); + return true; + } + } + + return false; + } + + @Override + protected SerialKeyLocation pluginLocationToSerialKeyLocation(final Location location) { + return SpongeTypeConverter.toSerialKeyLocation(location); + } + +} \ No newline at end of file diff --git a/sponge/src/main/java/fr/skyost/serialkey/sponge/unlocker/SpongeUnlocker.java b/sponge/src/main/java/fr/skyost/serialkey/sponge/unlocker/SpongeUnlocker.java new file mode 100644 index 0000000..ba46590 --- /dev/null +++ b/sponge/src/main/java/fr/skyost/serialkey/sponge/unlocker/SpongeUnlocker.java @@ -0,0 +1,34 @@ +package fr.skyost.serialkey.sponge.unlocker; + +import fr.skyost.serialkey.core.unlocker.PluginUnlocker; +import fr.skyost.serialkey.sponge.SerialKey; +import fr.skyost.serialkey.sponge.util.Util; +import org.spongepowered.api.item.inventory.ItemStack; + +/** + * The Sponge unlocker class. + */ + +public class SpongeUnlocker extends PluginUnlocker { + + /** + * Creates a new Sponge unlocker instance. + * + * @param plugin The plugin instance. + */ + + public SpongeUnlocker(final SerialKey plugin) { + super(plugin); + } + + @Override + protected String randomColor() { + return Util.randomTextColor(); + } + + @Override + protected String stripColor(final String string) { + return Util.stripColor(string); + } + +} \ No newline at end of file diff --git a/sponge/src/main/java/fr/skyost/serialkey/sponge/util/ChestUtil.java b/sponge/src/main/java/fr/skyost/serialkey/sponge/util/ChestUtil.java new file mode 100644 index 0000000..f43c063 --- /dev/null +++ b/sponge/src/main/java/fr/skyost/serialkey/sponge/util/ChestUtil.java @@ -0,0 +1,43 @@ +package fr.skyost.serialkey.sponge.util; + +import org.spongepowered.api.block.BlockType; +import org.spongepowered.api.block.BlockTypes; +import org.spongepowered.api.block.tileentity.TileEntity; +import org.spongepowered.api.block.tileentity.carrier.Chest; +import org.spongepowered.api.world.Location; +import org.spongepowered.api.world.World; + +import java.util.Optional; + +/** + * Allows to perform some operations on chests. + */ + +public class ChestUtil { + + /** + * Returns the chest instance at the specified location. + * + * @param location The location. + * + * @return The chest instance. + */ + + public static Chest getChest(final Location location) { + final Optional chest = location.getTileEntity(); + return (Chest)chest.filter(entity -> entity instanceof Chest).orElse(null); + } + + /** + * Returns whether the specified block type is a chest. + * + * @param type The block type. + * + * @return Whether the specified block type is a chest. + */ + + public static boolean isChest(final BlockType type) { + return type.equals(BlockTypes.CHEST) || type.equals(BlockTypes.TRAPPED_CHEST); + } + +} \ No newline at end of file diff --git a/sponge/src/main/java/fr/skyost/serialkey/sponge/util/DoorUtil.java b/sponge/src/main/java/fr/skyost/serialkey/sponge/util/DoorUtil.java new file mode 100644 index 0000000..d17885a --- /dev/null +++ b/sponge/src/main/java/fr/skyost/serialkey/sponge/util/DoorUtil.java @@ -0,0 +1,127 @@ +package fr.skyost.serialkey.sponge.util; + +import org.spongepowered.api.block.BlockType; +import org.spongepowered.api.block.BlockTypes; +import org.spongepowered.api.data.key.Keys; +import org.spongepowered.api.util.Direction; +import org.spongepowered.api.world.Location; +import org.spongepowered.api.world.World; + +/** + * Allows to perform some operations on doors. + */ + +public class DoorUtil { + + /** + * Doors faces. + */ + + private static final Direction[] DIRECTIONS = new Direction[]{ + Direction.NORTH, + Direction.EAST, + Direction.SOUTH, + Direction.WEST + }; + + /** + * Checks if the block located at specified location is a Door. + * + * @param location The location. + * + * @return true : If there is a door. + *
false : Otherwise. + */ + + public static boolean isDoor(final Location location) { + return isDoor(location.getBlockType()); + } + + /** + * Checks if the specified block type is a door. + * + * @param type The block type. + * + * @return true : If it's a door. + *
false : Otherwise. + */ + + public static boolean isDoor(final BlockType type) { + return type.equals(BlockTypes.ACACIA_DOOR) + || type.equals(BlockTypes.BIRCH_DOOR) + || type.equals(BlockTypes.DARK_OAK_DOOR) + || type.equals(BlockTypes.IRON_DOOR) + || type.equals(BlockTypes.JUNGLE_DOOR) + || type.equals(BlockTypes.SPRUCE_DOOR) + || type.equals(BlockTypes.WOODEN_DOOR); + } + + /** + * Checks if the specified block type is a trapdoor. + * + * @param type The block type. + * + * @return true : If it's a trapdoor. + *
false : Otherwise. + */ + + public static boolean isTrapdoor(final BlockType type) { + return type.equals(BlockTypes.TRAPDOOR) || type.equals(BlockTypes.IRON_TRAPDOOR); + } + + /** + * Returns the block below the specified block. + * + * @param block The block. + * + * @return The block below (if needed). + */ + + public static Location getBlockBelow(final Location block) { + if(!isDoor(block)) { + return null; + } + + // TODO: Check with two doors : one below the other. + final Location below = block.getRelative(Direction.DOWN); + return isDoor(below) ? below : block; + } + + /** + * Returns the other door of a double door. + * + * @param door The door. + * + * @return The other door. + */ + + public static Location getDoubleDoor(final Location door) { + for(final Direction direction : DIRECTIONS) { + + final Location relative = door.getRelative(direction); + if(areConnected(door, relative)) { + return getBlockBelow(relative); + } + } + return null; + } + + /** + * Check if two doors are connected. + * + * @param block1 The block one. + * @param block2 The block two. + * + * @return true : If the doors are connected. + *
false : Otherwise. + */ + + public static boolean areConnected(final Location block1, final Location block2) { + if(!isDoor(block1) || !isDoor(block2)) { + return false; + } + + return block1.getBlock().get(Keys.DIRECTION).equals(block2.getBlock().get(Keys.DIRECTION)) && block1.getBlock().get(Keys.HINGE_POSITION).equals(block2.getBlock().get(Keys.HINGE_POSITION)); + } + +} \ No newline at end of file diff --git a/sponge/src/main/java/fr/skyost/serialkey/sponge/util/OreUpdater.java b/sponge/src/main/java/fr/skyost/serialkey/sponge/util/OreUpdater.java new file mode 100644 index 0000000..f58e368 --- /dev/null +++ b/sponge/src/main/java/fr/skyost/serialkey/sponge/util/OreUpdater.java @@ -0,0 +1,137 @@ +package fr.skyost.serialkey.sponge.util; + +import com.google.gson.JsonParser; +import fr.skyost.serialkey.sponge.BuildConfig; +import org.slf4j.Logger; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.plugin.PluginContainer; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Optional; + +/** + * Ore update checker. + */ + +public class OreUpdater extends Thread { + + /** + * Plugin ID. + */ + + private static final String PLUGIN_ID = BuildConfig.PLUGIN_ID; + + /** + * The plugin's logger instance. + */ + + private final Logger logger; + + /** + * Creates a new updater instance. + * + * @param logger The plugin's logger. + */ + + public OreUpdater(final Logger logger) { + this.logger = logger; + } + + @Override + public final void run() { + try { + logger.info("Checking for updates..."); + + // Let's get the plugin. + final Optional optionalPluginContainer = Sponge.getPluginManager().getPlugin(PLUGIN_ID); + if(!optionalPluginContainer.isPresent()) { + throw new NullPointerException("No plugin found for \"" + PLUGIN_ID + "\"."); + } + + // We have to get the plugin's version. + final Optional optionalLocalVersion = optionalPluginContainer.get().getVersion(); + if(!optionalLocalVersion.isPresent()) { + return; + } + + // Now we can request the remote version. + final URL url = new URL(String.format("https://ore.spongepowered.org/api/v1/projects/%s", PLUGIN_ID)); + final String version = new JsonParser().parse(httpGet(url)).getAsJsonObject().get("recommended").getAsJsonObject().get("name").getAsString(); + + // And we can compare them. + if(versionCompare(optionalLocalVersion.get(), version) >= 0) { + logger.info("No update found."); + return; + } + + logger.info("Found an update !"); + logger.info("Head to \"https://ore.spongepowered.org/api/projects/" + PLUGIN_ID + "/versions/recommended/download\" to download " + version + "..."); + } + catch(final Exception ex) { + logger.error("Cannot check for updates :", ex); + } + } + + /** + * Compares two version strings. + * + * Use this instead of String.compareTo() for a non-lexicographical + * comparison that works for version strings. e.g. "1.10".compareTo("1.6"). + * + * @param str1 a string of ordinal numbers separated by decimal points. + * @param str2 a string of ordinal numbers separated by decimal points. + * @return The result is a negative integer if str1 is _numerically_ less than str2. + * The result is a positive integer if str1 is _numerically_ greater than str2. + * The result is zero if the strings are _numerically_ equal. + * + * @author Alex Gitelman. + */ + + private static int versionCompare(final String str1, final String str2) { + final String[] vals1 = str1.split("\\."); + final String[] vals2 = str2.split("\\."); + int i = 0; + // set index to first non-equal ordinal or length of shortest version string + while(i < vals1.length && i < vals2.length && vals1[i].equals(vals2[i])) { + i++; + } + // compare first non-equal ordinal number + if(i < vals1.length && i < vals2.length) { + int diff = Integer.valueOf(vals1[i]).compareTo(Integer.valueOf(vals2[i])); + return Integer.signum(diff); + } + // the strings are equal or one string is a substring of the other + // e.g. "1.2.3" = "1.2.3" or "1.2.3" < "1.2.3.4" + return Integer.signum(vals1.length - vals2.length); + } + + /** + * Sends an HTTP GET request. + * + * @param url The URL. + * + * @return The response body. + * + * @throws IOException If any I/O exception occurs. + */ + + private static String httpGet(final URL url) throws IOException { + final StringBuilder result = new StringBuilder(); + final HttpURLConnection connection = (HttpURLConnection)url.openConnection(); + connection.setRequestMethod("GET"); + + final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + String line; + while((line = reader.readLine()) != null) { + result.append(line); + } + + reader.close(); + return result.toString(); + } + +} \ No newline at end of file diff --git a/sponge/src/main/java/fr/skyost/serialkey/sponge/util/Permission.java b/sponge/src/main/java/fr/skyost/serialkey/sponge/util/Permission.java new file mode 100644 index 0000000..b704920 --- /dev/null +++ b/sponge/src/main/java/fr/skyost/serialkey/sponge/util/Permission.java @@ -0,0 +1,73 @@ +package fr.skyost.serialkey.sponge.util; + +import org.spongepowered.api.text.Text; + +/** + * Represents a simple permission. + */ + +public class Permission { + + /** + * The permission. + */ + + private final String permission; + + /** + * The role. + */ + + private final String role; + + /** + * The description. + */ + + private final Text description; + + /** + * Creates a new permission instance. + * + * @param permission The permission. + * @param role The role. + * @param description The description. + */ + + public Permission(final String permission, final String role, final String description) { + this.permission = permission; + this.role = role; + this.description = Util.parseString(description); + } + + /** + * Returns the permission. + * + * @return The permission. + */ + + public String getPermission() { + return permission; + } + + /** + * Returns the role. + * + * @return The role. + */ + + public String getRole() { + return role; + } + + /** + * Returns the description. + * + * @return The description. + */ + + public Text getDescription() { + return description; + } + +} \ No newline at end of file diff --git a/sponge/src/main/java/fr/skyost/serialkey/sponge/util/Util.java b/sponge/src/main/java/fr/skyost/serialkey/sponge/util/Util.java new file mode 100644 index 0000000..232e728 --- /dev/null +++ b/sponge/src/main/java/fr/skyost/serialkey/sponge/util/Util.java @@ -0,0 +1,118 @@ +package fr.skyost.serialkey.sponge.util; + +import org.spongepowered.api.data.key.Keys; +import org.spongepowered.api.entity.Entity; +import org.spongepowered.api.entity.EntityTypes; +import org.spongepowered.api.item.ItemType; +import org.spongepowered.api.item.ItemTypes; +import org.spongepowered.api.item.inventory.ItemStack; +import org.spongepowered.api.text.Text; +import org.spongepowered.api.text.serializer.TextSerializers; +import org.spongepowered.api.world.Location; +import org.spongepowered.api.world.World; + +import java.util.Random; + +/** + * Contains some useful methods. + */ + +public class Util { + + /** + * The available color codes. + */ + + private static final String[] COLORS = new String[]{ + "&1", + "&2", + "&3", + "&4", + "&5", + "&6", + "&7", + "&8", + "&9", + "&a", + "&b", + "&c", + "&d", + "&e", + "&f" + }; + + /** + * Strips colors from the specified string. + * + * @param string The string. + * + * @return The handled string. + */ + + public static String stripColor(final String string) { + return TextSerializers.FORMATTING_CODE.deserialize(string).toPlain(); + } + + /** + * Returns a random text color. + * + * @return A random text color. + */ + + public static String randomTextColor() { + final Random random = new Random(); + return COLORS[random.nextInt(COLORS.length)]; + } + + /** + * Parses a string with color codes. + * + * @param string The string. + * + * @return The parsed string (as a Text). + */ + + public static Text parseString(final String string) { + return TextSerializers.FORMATTING_CODE.deserialize(string); + } + + /** + * Creates an item with a custom name. + * + * @param name The name. + * @param material The item's material. + * + * @return The item. + */ + + public static ItemStack createItem(final String name, final ItemType material) { + return ItemStack.builder() + .add(Keys.DISPLAY_NAME, Util.parseString(name)) + .itemType(material) + .build(); + } + + /** + * Drops an item at the specified location. + * + * @param item The item. + * @param location The location. + */ + + public static void dropItemAt(final ItemStack item, final Location location) { + final Entity drop = location.getExtent().createEntity(EntityTypes.ITEM, location.getPosition()); + drop.offer(Keys.REPRESENTED_ITEM, item.createSnapshot()); + location.getExtent().spawnEntity(drop); + } + + /** + * Creates a blank item. + * + * @return The blank item. + */ + + public static ItemStack blankItem() { + return ItemStack.builder().itemType(ItemTypes.AIR).build(); + } + +} \ No newline at end of file diff --git a/src/main/java/fr/skyost/serialkey/SerialKey.java b/src/main/java/fr/skyost/serialkey/SerialKey.java deleted file mode 100644 index 0575ba3..0000000 --- a/src/main/java/fr/skyost/serialkey/SerialKey.java +++ /dev/null @@ -1,198 +0,0 @@ -package fr.skyost.serialkey; - -import org.bstats.bukkit.MetricsLite; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.command.CommandSender; -import org.bukkit.command.PluginCommand; -import org.bukkit.plugin.PluginManager; -import org.bukkit.plugin.java.JavaPlugin; -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Objects; - -import fr.skyost.serialkey.command.SerialKeyCommand; -import fr.skyost.serialkey.config.PluginConfig; -import fr.skyost.serialkey.config.PluginData; -import fr.skyost.serialkey.config.PluginMessages; -import fr.skyost.serialkey.listener.BlocksListener; -import fr.skyost.serialkey.listener.BunchOfKeysListener; -import fr.skyost.serialkey.listener.GlobalListener; -import fr.skyost.serialkey.listener.HopperListener; -import fr.skyost.serialkey.listener.LostChestsListener; -import fr.skyost.serialkey.listener.PadlockFinderListener; -import fr.skyost.serialkey.util.Skyupdater; -import fr.skyost.serialkey.util.Util; - -/** - * The SerialKey plugin class. - */ - -public class SerialKey extends JavaPlugin { - - /** - * The plugin config. - */ - - private PluginConfig config; - - /** - * The plugin messages. - */ - - private PluginMessages messages; - - /** - * The plugin data. - */ - - private PluginData data; - - /** - * The plugin API. - */ - - private SerialKeyAPI api; - - @Override - public final void onEnable() { - try { - final File dataFolder = this.getDataFolder(); - - // Configuration : - - config = new PluginConfig(dataFolder); - config.load(); - messages = new PluginMessages(dataFolder); - messages.load(); - data = new PluginData(dataFolder); - data.load(); - handleLocations(); - - // Items : - - api = new SerialKeyAPI(this); - api.createRecipe(api.getKeyItem(), config.keyShape); - api.createRecipe(api.getMasterKeyItem(), config.masterKeyShape); - api.createRecipe(api.getKeyCloneItem(), Collections.singletonList("YY"), Objects.requireNonNull(Util.createMap(new String[]{"Y"}, new String[]{config.keyMaterial.name()}))); - api.createRecipe(api.getBunchOfKeysItem(), config.bunchOfKeysShape); - api.createRecipe(api.getPadlockFinderItem(), Collections.singletonList("ZY"), Objects.requireNonNull(Util.createMap(new String[]{"Z", "Y"}, new String[]{Material.COMPASS.name(), config.keyMaterial.name()}))); - - // Events : - - final PluginManager manager = Bukkit.getPluginManager(); - manager.registerEvents(new GlobalListener(this), this); - manager.registerEvents(new BlocksListener(this), this); - manager.registerEvents(new BunchOfKeysListener(this), this); - manager.registerEvents(new PadlockFinderListener(this), this); - if(config.disableHoppers) { - manager.registerEvents(new HopperListener(this), this); - } - if(!config.allowLostChests) { - manager.registerEvents(new LostChestsListener(this), this); - } - - // Commands : - - final SerialKeyCommand executor = new SerialKeyCommand(this); - final PluginCommand command = this.getCommand("serialkey"); - command.setUsage(executor.getUsage()); - command.setExecutor(executor); - - // Services : - - if(config.enableUpdater) { - new Skyupdater(this, 84423, this.getFile(), true, true); - } - if(config.enableMetrics) { - new MetricsLite(this); - } - } - catch(final NullPointerException ex) { - sendMessage(Bukkit.getConsoleSender(), ChatColor.RED + "Null pointer exception ! Maybe you have misconfigured one (or more) item shape."); - } - catch(final Exception ex) { - ex.printStackTrace(); - } - } - - @Override - public final void onDisable() { - try { - data.save(); - } - catch(final Exception ex) { - ex.printStackTrace(); - } - } - - /** - * Returns the plugin's config. - * - * @return The plugin's config. - */ - - public PluginConfig getPluginConfig() { - return config; - } - - /** - * Returns the plugin's messages. - * - * @return The plugin's messages. - */ - - public PluginMessages getPluginMessages() { - return messages; - } - - /** - * Returns the plugin's data. - * - * @return The plugin's data. - */ - - public PluginData getPluginData() { - return data; - } - - /** - * Returns the plugin's API. - * - * @return The plugin's API. - */ - - public SerialKeyAPI getAPI() { - return api; - } - - /** - * Sends a message with the plugin's prefix. - * - * @param sender Who receives the message. - * @param message The message. - */ - - public void sendMessage(final CommandSender sender, final String message) { - sender.sendMessage(messages.prefix + " " + message); - } - - /** - * A little trick for the Skyoconfig because it cannot handle parameterized types. - */ - - private void handleLocations() { - for(final Object object : new ArrayList(data.padlocks)) { - final JSONObject json = (JSONObject)JSONValue.parse(object.toString()); - data.padlocks.add(new Location(Bukkit.getWorld(json.get("world").toString()), Double.parseDouble(json.get("x").toString()), Double.parseDouble(json.get("y").toString()), Double.parseDouble(json.get("z").toString()), Float.parseFloat(json.get("yaw").toString()), Float.parseFloat(json.get("pitch").toString()))); - data.padlocks.remove(object); - } - } - -} \ No newline at end of file diff --git a/src/main/java/fr/skyost/serialkey/SerialKeyAPI.java b/src/main/java/fr/skyost/serialkey/SerialKeyAPI.java deleted file mode 100644 index 6a85675..0000000 --- a/src/main/java/fr/skyost/serialkey/SerialKeyAPI.java +++ /dev/null @@ -1,781 +0,0 @@ -package fr.skyost.serialkey; - -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.NamespacedKey; -import org.bukkit.World; -import org.bukkit.block.Block; -import org.bukkit.block.DoubleChest; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.Chest; -import org.bukkit.entity.Player; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryHolder; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.ShapedRecipe; -import org.bukkit.inventory.meta.ItemMeta; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import fr.skyost.serialkey.config.PluginConfig; -import fr.skyost.serialkey.util.DoorUtil; -import fr.skyost.serialkey.util.ROT47; -import fr.skyost.serialkey.util.Util; - -/** - * The SerialKey API. - */ - -public class SerialKeyAPI { - - /** - * The plugin instance. - */ - - private final SerialKey plugin; - - /** - * The key item. - */ - - private ItemStack key; - - /** - * The master key item. - */ - - private ItemStack masterKey; - - /** - * The key clone item. - */ - - private ItemStack keyClone; - - /** - * The bunch of keys item. - */ - - private ItemStack bunchOfKeys; - - /** - * The padlock finder item. - */ - - private ItemStack padlockFinder; - - /** - * Creates a new SerialKey API instance. - * - * @param plugin The plugin instance. - */ - - SerialKeyAPI(final SerialKey plugin) { - this.plugin = plugin; - - final PluginConfig config = plugin.getPluginConfig(); - key = Util.createItem(config.keyName, config.keyMaterial); - masterKey = Util.createItem(config.masterKeyName, config.masterKeyMaterial); - keyClone = key.clone(); - keyClone.setAmount(2); - bunchOfKeys = Util.createItem(config.bunchOfKeysName, config.bunchOfKeysMaterial); - padlockFinder = Util.createItem(config.padlockFinderName, Material.COMPASS); - } - - /** - * Creates a recipe for an item with defaults ingredients. - * - * @param result The item. - * @param shape The shape. - */ - - public void createRecipe(final ItemStack result, final List shape) { - createRecipe(result, shape, plugin.getPluginConfig().shapeMaterials); - } - - /** - * Creates a recipe for an item. - * - * @param result The item. - * @param shape The shape. - * @param ingredients The ingredients needed for the craft. - */ - - public void createRecipe(final ItemStack result, final List shape, Map ingredients) { - final ShapedRecipe recipe = new ShapedRecipe(new NamespacedKey(plugin, ChatColor.stripColor(result.getItemMeta().getDisplayName()).toLowerCase().replace(' ', '_') + "_" + result.getAmount()), result); - recipe.shape(shape.toArray(new String[0])); - if(ingredients.equals(plugin.getPluginConfig().shapeMaterials)) { - ingredients = Util.keepAll(ingredients, shape); - } - for(final Map.Entry entry : ingredients.entrySet()) { - recipe.setIngredient(entry.getKey().charAt(0), Material.valueOf(entry.getValue())); - } - Bukkit.addRecipe(recipe); - } - - /** - * Returns a clone of the key item. - * - * @return A clone of the key item. - */ - - public ItemStack getKeyItem() { - return key.clone(); - } - - /** - * Sets the key item. - * - * @param key The key item. - */ - - public void setKeyItem(final ItemStack key) { - this.key = key; - } - - /** - * Returns a clone of the master key item. - * - * @return A clone of the master key item. - */ - - public ItemStack getMasterKeyItem() { - return masterKey.clone(); - } - - /** - * Sets the master key item. - * - * @param masterKey The master key item. - */ - - public void setMasterKeyItem(final ItemStack masterKey) { - this.masterKey = masterKey; - } - - /** - * Returns the key clone item. - * - * @return The key clone item. - */ - - public ItemStack getKeyCloneItem() { - return keyClone.clone(); - } - - /** - * Sets the key clone item. - * - * @param keyClone The key clone item. - */ - - public void setKeyCloneItem(final ItemStack keyClone) { - this.keyClone = keyClone; - } - - /** - * Returns the bunch of keys item. - * - * @return The bunch of keys item. - */ - - public ItemStack getBunchOfKeysItem() { - return bunchOfKeys; - } - - /** - * Sets the bunch of keys item. - * - * @param bunchOfKeys The bunch of keys item. - */ - - public void setBunchOfKeysItem(final ItemStack bunchOfKeys) { - this.bunchOfKeys = bunchOfKeys; - } - - /** - * Returns the padlock finder item. - * - * @return The padlock finder item. - */ - - public ItemStack getPadlockFinderItem() { - return padlockFinder.clone(); - } - - /** - * Sets the padlock finder item. - * - * @param padlockFinder The padlock finder item. - */ - - public void setPadlockFinderItem(final ItemStack padlockFinder) { - this.padlockFinder = padlockFinder; - } - - /** - * Creates a padlock for the selected location. - * - * @param location The location. - */ - - public void createPadlock(final Location location) { - createPadlock(location, null); - } - - /** - * Creates a padlock for the selected location. - *
It will format a key too. - * - * @param location The location (can be corrected if needed). - * @param key The key. - */ - - public void createPadlock(final Location location, final ItemStack key) { - correctLocation(location); - plugin.getPluginData().padlocks.add(location); - if(isBlankKey(key)) { - formatItem(location, key); - } - } - - /** - * Removes a padlock. - * - * @param location The location (can be corrected if needed). - */ - - public void removePadlock(final Location location) { - removePadlock(location, null); - } - - /** - * Removes a padlock. - *
It will format a key too. - * - * @param location The location (can be corrected if needed). - * @param key The key. - */ - - public void removePadlock(final Location location, final ItemStack key) { - correctLocation(location); - plugin.getPluginData().padlocks.remove(location); - if(isUsedKey(key)) { - final ItemMeta meta = key.getItemMeta(); - meta.setLore(null); - key.setItemMeta(meta); - } - } - - /** - * Checks if the specified location has a padlock. - * - * @param location The location (can be corrected if needed). - * - * @return true : yes. - *
false : no. - */ - - public boolean hasPadlock(final Location location) { - return hasPadlock(location, true); - } - - /** - * Checks if the specified location has a padlock. - * - * @param location The location. - * @param correctLocation If you want to automatically correct the location. - * - * @return true : yes. - *
false : no. - */ - - public boolean hasPadlock(final Location location, final boolean correctLocation) { - if(correctLocation) { - correctLocation(location); - } - return plugin.getPluginData().padlocks.contains(location); - } - - /** - * Checks if the specified item is a key (blank or used). - * - * @param item The item. - * - * @return true : yes. - *
false : no. - */ - - public boolean isKey(final ItemStack item) { - return Util.isValidItem(item) && item.getType() == key.getType() && (plugin.getPluginConfig().canRenameItems || item.getItemMeta().getDisplayName().equals(key.getItemMeta().getDisplayName())); - } - - /** - * Checks if the specified item is a blank key. - * - * @param item The item. - * - * @return true : yes. - *
false : no. - */ - - public boolean isBlankKey(final ItemStack item) { - return isKey(item) && !item.getItemMeta().hasLore(); - } - - /** - * Checks if the specified item is an used key. - * - * @param item The item. - * - * @return true : yes. - *
false : no. - */ - - public boolean isUsedKey(final ItemStack item) { - if(!isKey(item)) { - return false; - } - final List lore = item.getItemMeta().getLore(); - return lore != null && lore.size() == 2; - } - - /** - * Checks if the specified item is a master key. - * - * @param item The item. - * - * @return true : yes. - *
false : no. - */ - - public boolean isMasterKey(final ItemStack item) { - return Util.isValidItem(item) && item.getType() == masterKey.getType() && (plugin.getPluginConfig().canRenameItems || item.getItemMeta().getDisplayName().equals(masterKey.getItemMeta().getDisplayName())); - } - - /** - * Checks if the specified item is a bunch of keys (blank or used). - * - * @param item The item. - * - * @return true : yes. - *
false : no. - */ - - public boolean isBunchOfKeys(final ItemStack item) { - return Util.isValidItem(item) && item.getType() == bunchOfKeys.getType() && (plugin.getPluginConfig().canRenameItems || item.getItemMeta().getDisplayName().equals(bunchOfKeys.getItemMeta().getDisplayName())); - } - - /** - * Checks if the specified inventory is a bunch of keys (blank or used). - * - * @param inventory The inventory. - * - * @return true : yes. - *
false : no. - */ - - public boolean isBunchOfKeys(final Inventory inventory) { - return inventory.getName().equals(bunchOfKeys.getItemMeta().getDisplayName()) && inventory.getSize() == 9; - } - - /** - * Checks if the specified item is a blank bunch of keys. - * - * @param item The item. - * - * @return true : yes. - *
false : no. - */ - - public boolean isBlankBunchOfKeys(final ItemStack item) { - return isBunchOfKeys(item) && !item.getItemMeta().hasLore(); - } - - /** - * Checks if the specified item is an used bunch of keys. - * - * @param item The item. - * - * @return true : yes. - *
false : no. - */ - - public boolean isUsedBunchOfKeys(final ItemStack item) { - int size; - return isBunchOfKeys(item) && item.getItemMeta().hasLore() && (size = item.getItemMeta().getLore().size()) > 1 && size % 2 == 0; - } - - /** - * Checks if the specified item is a padlock finder (blank or used). - * - * @param item The item. - * - * @return true : yes. - *
false : no. - */ - - public boolean isPadlockFinder(final ItemStack item) { - return Util.isValidItem(item) && item.getType() == Material.COMPASS && item.getItemMeta().getDisplayName().equals(padlockFinder.getItemMeta().getDisplayName()); - } - - /** - * Checks if the specified item is a blank padlock finder. - * - * @param item The item. - * - * @return true : yes. - *
false : no. - */ - - public boolean isBlankPadlockFinder(final ItemStack item) { - return isPadlockFinder(item) && !item.getItemMeta().hasLore(); - } - - /** - * Checks if the specified item is an used padlock finder. - * - * @param item The item. - * - * @return true : yes. - *
false : no. - */ - - public boolean isUsedPadlockFinder(final ItemStack item) { - return isPadlockFinder(item) && item.getItemMeta().hasLore(); - } - - /** - * Checks if the specified item is a valid key for the specified location. - * - * @param key The key. - * @param location The location (will not be corrected). - * - * @return true : yes. - *
false : no. - */ - - public boolean isValidKey(final ItemStack key, final Location location) { - return isValidKey(key, location, null); - } - - /** - * Checks if the specified item is a valid key for the specified location. - * - * @param key The key. - * @param location The location (can be corrected if needed). - * @param player If you want to check if the player has the right permission. Will send a message otherwise. - * - * @return true : yes. - *
false : no. - */ - - public boolean isValidKey(final ItemStack key, final Location location, final Player player) { - if(isMasterKey(key)) { - if(player != null && !player.hasPermission("serialkey.use.masterkey")) { - plugin.sendMessage(player, plugin.getPluginMessages().messagePermission); - } - return true; - } - correctLocation(location); - try { - final Location keyLocation = extractLocation(key); - if(keyLocation != null && keyLocation.equals(location)) { - if(player != null && !player.hasPermission("serialkey.use.key")) { - plugin.sendMessage(player, plugin.getPluginMessages().messagePermission); - } - return true; - } - final ItemStack[] extractedKeys = extractKeys(key); - if(extractedKeys != null) { - if(player != null && !player.hasPermission("serialkey.use.bunchofkeys")) { - plugin.sendMessage(player, plugin.getPluginMessages().messagePermission); - return true; - } - for(final ItemStack extractedKey : extractedKeys) { - if(isValidKey(extractedKey, location, null)) { - return true; - } - } - } - } - catch(final Exception ex) { - ex.printStackTrace(); - } - return false; - } - - /** - * Extracts a location from a key or a padlock finder. - * - * @param item The item. - * - * @return The location. - */ - - public Location extractLocation(final ItemStack item) { - boolean isKey = isUsedKey(item); - if(!isKey && !isUsedPadlockFinder(item)) { - return null; - } - final List lore = item.getItemMeta().getLore(); - String loreWorld = ChatColor.stripColor(lore.get(0)); - String loreLocation = ChatColor.stripColor(lore.get(1)); - if(plugin.getPluginConfig().encryptLore) { - loreWorld = ROT47.rotate(loreWorld); - loreLocation = ROT47.rotate(loreLocation); - } - final World world = Bukkit.getWorld(loreWorld); - if(world == null) { - return null; - } - final String[] rawLocation = loreLocation.split(", "); - if(rawLocation.length != 3) { - return null; - } - final Location itemLocation = new Location(world, Integer.parseInt(rawLocation[0]), Integer.parseInt(rawLocation[1]), Integer.parseInt(rawLocation[2])); - if(isKey && correctLocation(itemLocation)) { - formatItem(itemLocation, item); - } - return itemLocation; - } - - /** - * Returns a key for the specified location. - * - * @param location The location (will not be corrected). - * - * @return The key. - */ - - public ItemStack getKey(final Location location) { - final ItemStack key = getKeyItem(); - formatItem(location, key); - return key; - } - - /** - * Returns a padlock finder for the specified location. - * - * @param location The location (will not be corrected). - * - * @return The padlock finder. - */ - - public ItemStack getPadlockFinder(final Location location) { - final ItemStack padlockFinder = getPadlockFinderItem(); - formatItem(location, padlockFinder); - return padlockFinder; - } - - /** - * Formats a key for the specified location. - * - * @param location The location (will not be corrected). - * @param item The key. - */ - - public void formatItem(final Location location, final ItemStack item) { - if(!isKey(item) && !isPadlockFinder(item)) { - return; - } - final ChatColor color = Util.randomChatColor(ChatColor.BOLD, ChatColor.ITALIC, ChatColor.UNDERLINE, ChatColor.STRIKETHROUGH, ChatColor.MAGIC, ChatColor.BLACK); - final ItemMeta meta = item.getItemMeta(); - String loreWorld = location.getWorld().getName(); - String loreLocation = location.getBlockX() + ", " + location.getBlockY() + ", " + location.getBlockZ(); - if(plugin.getPluginConfig().encryptLore) { - loreWorld = ROT47.rotate(loreWorld); - loreLocation = ROT47.rotate(loreLocation); - } - meta.setLore(Arrays.asList(color + loreWorld, color + loreLocation)); - item.setItemMeta(meta); - } - - /** - * Adds a key to a bunch of keys. - * - * @param bunchOfKeys The bunch of keys. - * @param key The key. - */ - - public void addKey(final ItemStack bunchOfKeys, final ItemStack key) { - if(!isBunchOfKeys(bunchOfKeys) || !isUsedKey(key)) { - return; - } - final ItemMeta meta = bunchOfKeys.getItemMeta(); - final List lore = meta.hasLore() ? new ArrayList<>(meta.getLore()) : new ArrayList<>(); - lore.addAll(key.getItemMeta().getLore()); - meta.setLore(lore); - bunchOfKeys.setItemMeta(meta); - } - - /** - * Removes a key from a bunch of keys. - * - * @param bunchOfKeys The bunch of keys. - * @param key The key. - * - * @return The number of deleted keys. - */ - - public short removeKey(final ItemStack bunchOfKeys, final ItemStack key) { - if(!isUsedBunchOfKeys(bunchOfKeys) || !isUsedKey(key)) { - return 0; - } - - final ItemMeta meta = bunchOfKeys.getItemMeta(); - final List lore = meta.getLore(); - final List keyLore = key.getItemMeta().getLore(); - - for(final String line : new ArrayList<>(keyLore)) { - keyLore.remove(line); - keyLore.add(ChatColor.stripColor(line)); - } - - short deleted = 0; - final int n = lore.size(); - for(int i = 0; i != n; i++) { - final String world = lore.get(i); - final String location = lore.get(++i); - if(keyLore.equals(Arrays.asList(ChatColor.stripColor(world), ChatColor.stripColor(location))) && lore.removeAll(Arrays.asList(world, location))) { - deleted++; - } - } - - meta.setLore(lore.size() == 0 ? null : lore); - bunchOfKeys.setItemMeta(meta); - return deleted; - } - - /** - * Clears a bunch of keys. - * - * @param bunchOfKeys The bunch of keys. - */ - - public void clearKeys(final ItemStack bunchOfKeys) { - if(!isUsedBunchOfKeys(bunchOfKeys)) { - return; - } - final ItemMeta meta = bunchOfKeys.getItemMeta(); - meta.setLore(null); - bunchOfKeys.setItemMeta(meta); - } - - /** - * Extracts all keys from a bunch of keys. - * - * @param bunchOfKeys The bunch of keys. - * - * @return The keys. - */ - - public ItemStack[] extractKeys(final ItemStack bunchOfKeys) { - if(!isUsedBunchOfKeys(bunchOfKeys)) { - return null; - } - final List lore = bunchOfKeys.getItemMeta().getLore(); - final List keys = new ArrayList<>(); - - final int n = lore.size(); - for(int i = 0; i != n; i++) { - final ItemStack blankKey = getKeyItem(); - final ItemMeta blankMeta = blankKey.getItemMeta(); - blankMeta.setLore(Arrays.asList(lore.get(i), lore.get(++i))); - blankKey.setItemMeta(blankMeta); - keys.add(blankKey); - } - - return keys.toArray(new ItemStack[0]); - } - - /** - * Creates an inventory for the specified bunch of keys and opens it for the specified players. - * - * @param bunchOfKeys The bunch of keys item. - * @param players The players. - * - * @return The inventory. - */ - - public Inventory createInventory(final ItemStack bunchOfKeys, final Player... players) { - if(!isBunchOfKeys(bunchOfKeys)) { - return null; - } - - final Inventory inventory = Bukkit.createInventory(null, 9, bunchOfKeys.getItemMeta().getDisplayName()); - inventory.setMaxStackSize(1); - - final ItemStack[] keys = extractKeys(bunchOfKeys); - if(keys != null && keys.length != 0) { - inventory.addItem(keys); - } - - if(players != null) { - for(final Player player : players) { - player.openInventory(inventory); - } - } - - return inventory; - } - - /** - * Corrects a location (used to handle doors because they are composed from two blocks and double chests too). - * - * @param location The location. - * - * @return true : If the location has been corrected. - *
false : Otherwise. - */ - - public boolean correctLocation(final Location location) { - if(hasPadlock(location, false)) { - return false; - } - - final Block block = location.getBlock(); - final BlockData data = block.getBlockData(); - if(data instanceof Chest) { - final InventoryHolder holder = ((org.bukkit.block.Chest)block.getState()).getInventory().getHolder(); - if(holder instanceof DoubleChest) { - final Location left = ((org.bukkit.block.Chest)((DoubleChest)holder).getLeftSide()).getLocation(); - if(hasPadlock(left, false)) { - location.setX(left.getX()); - location.setZ(left.getZ()); - return true; - } - - final Location right = ((org.bukkit.block.Chest)((DoubleChest)holder).getRightSide()).getLocation(); - if(hasPadlock(right, false)) { - location.setX(right.getX()); - location.setZ(right.getZ()); - return true; - } - } - return false; - } - - if(DoorUtil.getInstance(data) != null) { - location.setY(DoorUtil.getBlockBelow(block).getY()); - if(hasPadlock(location, false)) { - return true; - } - final Block doubleDoor = DoorUtil.getDoubleDoor(block); - if(doubleDoor != null) { - final Location doubleDoorLocation = doubleDoor.getLocation(); - location.setX(doubleDoorLocation.getX()); - location.setZ(doubleDoorLocation.getZ()); - } - return true; - } - - return false; - } - -} \ No newline at end of file diff --git a/src/main/java/fr/skyost/serialkey/command/SerialKeyCommand.java b/src/main/java/fr/skyost/serialkey/command/SerialKeyCommand.java deleted file mode 100644 index 0eb50c3..0000000 --- a/src/main/java/fr/skyost/serialkey/command/SerialKeyCommand.java +++ /dev/null @@ -1,110 +0,0 @@ -package fr.skyost.serialkey.command; - -import com.google.common.base.Joiner; - -import org.bukkit.ChatColor; -import org.bukkit.Location; -import org.bukkit.block.Block; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.plugin.PluginDescriptionFile; - -import fr.skyost.serialkey.SerialKey; - -/** - * Represents the
/serialkey
command executor. - */ - -public class SerialKeyCommand implements CommandExecutor { - - /** - * The plugin instance. - */ - - private final SerialKey plugin; - - /** - * Creates a new
/serialkey
command executor instance. - * - * @param plugin The plugin instance. - */ - - public SerialKeyCommand(final SerialKey plugin) { - this.plugin = plugin; - } - - @Override - public final boolean onCommand(final CommandSender sender, final Command command, final String label, final String[] args) { - if(!(sender instanceof Player)) { - plugin.sendMessage(sender, ChatColor.RED + "You must be a player to run this command."); - return true; - } - - if(args.length < 1) { - return false; - } - args[0] = args[0].toLowerCase(); - - if(!sender.hasPermission("serialkey.command." + args[0])) { - plugin.sendMessage(sender, plugin.getPluginMessages().messagePermission); - return true; - } - - switch(args[0]) { - case "getkey": - final Block block = ((Player)sender).getTargetBlock(null, 100); - if(block != null) { - final Location location = block.getLocation(); - if(plugin.getAPI().hasPadlock(location)) { - try { - final Player player = (Player)sender; - player.getWorld().dropItemNaturally(player.getEyeLocation(), plugin.getAPI().getKey(location)); - } - catch(final Exception ex) { - ex.printStackTrace(); - plugin.sendMessage(sender, ChatColor.RED + ex.getClass().getName()); - } - break; - } - } - - plugin.sendMessage(sender, ChatColor.RED + "You must be targeting to a valid block."); - break; - default: - return false; - } - - return true; - } - - /** - * Returns the plugin instance. - * - * @return The plugin instance. - */ - - public SerialKey getPlugin() { - return plugin; - } - - /** - * Returns the command's usage. - * - * @return The command's usage. - */ - - public final String getUsage() { - final PluginDescriptionFile description = plugin.getDescription(); - final StringBuilder builder = new StringBuilder(); - builder.append(ChatColor.GOLD).append("------------------------------------\n"); - builder.append(description.getName()).append(" v").append(description.getVersion()).append("\n"); - builder.append("By ").append(Joiner.on(' ').join(description.getAuthors())).append("\n"); - builder.append("------------------------------------\n"); - builder.append(ChatColor.RESET).append(ChatColor.BOLD).append("Commands :\n"); - builder.append(ChatColor.RESET).append("/serialkey getkey - Returns the key corresponding to your facing block."); - return builder.toString(); - } - -} \ No newline at end of file diff --git a/src/main/java/fr/skyost/serialkey/config/PluginData.java b/src/main/java/fr/skyost/serialkey/config/PluginData.java deleted file mode 100644 index 12afb22..0000000 --- a/src/main/java/fr/skyost/serialkey/config/PluginData.java +++ /dev/null @@ -1,31 +0,0 @@ -package fr.skyost.serialkey.config; - -import org.bukkit.Location; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import fr.skyost.serialkey.util.Skyoconfig; - -/** - * The plugin data class. - */ - -public class PluginData extends Skyoconfig { - - @ConfigOptions(name = "padlocks") - public List padlocks = new ArrayList<>(); - - /** - * Creates a new plugin data instance. - * - * @param dataFolder The plugin data folder. - */ - - public PluginData(final File dataFolder) { - super(new File(dataFolder, "data.yml"), Collections.singletonList("SerialKey Data")); - } - -} diff --git a/src/main/java/fr/skyost/serialkey/config/PluginMessages.java b/src/main/java/fr/skyost/serialkey/config/PluginMessages.java deleted file mode 100644 index 14bd70c..0000000 --- a/src/main/java/fr/skyost/serialkey/config/PluginMessages.java +++ /dev/null @@ -1,43 +0,0 @@ -package fr.skyost.serialkey.config; - -import java.io.File; -import java.util.Collections; - -import org.bukkit.ChatColor; - -import fr.skyost.serialkey.util.Skyoconfig; - -/** - * The plugin messages class. - */ - -public class PluginMessages extends Skyoconfig { - - @ConfigOptions(name = "messages.prefix") - public String prefix = ChatColor.AQUA + "[SerialKey]"; - @ConfigOptions(name = "messages.permission") - public String messagePermission = ChatColor.RED + "You do not have the permission to perform this action."; - @ConfigOptions(name = "messages.1") - public String message1 = ChatColor.GREEN + "Padlock placed ! If you want to remove it, you have to break this block."; - @ConfigOptions(name = "messages.2") - public String message2 = ChatColor.GOLD + "Padlock removed."; - @ConfigOptions(name = "messages.3") - public String message3 = ChatColor.RED + "This block has a padlock."; - @ConfigOptions(name = "messages.4") - public String message4 = ChatColor.GREEN + "Padlock finder enabled ! Your compasses will now point to its location. You can reset it back to the spawn by doing another right click with any padlock finder."; - @ConfigOptions(name = "messages.5") - public String message5 = ChatColor.RED + "Padlock finder has been disabled."; - @ConfigOptions(name = "messages.6") - public String message6 = ChatColor.RED + "You can't place this key in this chest."; - - /** - * Creates a new plugin messages instance. - * - * @param dataFolder The plugin data folder. - */ - - public PluginMessages(final File dataFolder) { - super(new File(dataFolder, "messages.yml"), Collections.singletonList("SerialKey messages")); - } - -} diff --git a/src/main/java/fr/skyost/serialkey/listener/BlocksListener.java b/src/main/java/fr/skyost/serialkey/listener/BlocksListener.java deleted file mode 100644 index 7b78858..0000000 --- a/src/main/java/fr/skyost/serialkey/listener/BlocksListener.java +++ /dev/null @@ -1,139 +0,0 @@ -package fr.skyost.serialkey.listener; - -import org.bukkit.GameMode; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.Sound; -import org.bukkit.block.Block; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.block.BlockPlaceEvent; -import org.bukkit.event.block.BlockRedstoneEvent; -import org.bukkit.event.entity.EntityBreakDoorEvent; -import org.bukkit.event.entity.EntityExplodeEvent; -import org.bukkit.inventory.ItemStack; - -import java.util.ArrayList; -import java.util.List; - -import fr.skyost.serialkey.SerialKey; - -/** - * A listener that allows to listen blocks related events. - */ - -public class BlocksListener extends SerialKeyListener { - - /** - * Creates a new blocks listener instance. - * - * @param plugin The plugin. - */ - - public BlocksListener(final SerialKey plugin) { - super(plugin); - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onBlockPlace(final BlockPlaceEvent event) { - if(event.isCancelled()) { - return; - } - - final ItemStack item = event.getItemInHand(); - if(api.isKey(item) || api.isMasterKey(item) || api.isBunchOfKeys(item)/* || api.isPadlockFinder(item)*/) { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onBlockBreak(final BlockBreakEvent event) { - if(event.isCancelled()) { - return; - } - - final Location location = event.getBlock().getLocation(); - if(!api.hasPadlock(location)) { - return; - } - - final Player player = event.getPlayer(); - final ItemStack inHand = player.getInventory().getItemInMainHand(); - if(player.getGameMode() == GameMode.CREATIVE && api.isBlankKey(inHand)) { - api.correctLocation(location); - api.formatItem(location, inHand); - } - else if(api.isValidKey(inHand, location)) { - if(!api.isMasterKey(inHand)) { - if(plugin.getPluginConfig().reusableKeys) { - int amount = 0; - if(api.isUsedKey(inHand)) { - amount = inHand.getAmount(); - player.getInventory().setItemInMainHand(new ItemStack(Material.AIR)); - } - else if(api.isBunchOfKeys(inHand)) { - amount = api.removeKey(inHand, api.getKey(location)); - } - if(amount == 0) { - return; - } - - final ItemStack key = api.getKeyItem(); - key.setAmount(amount); - player.getWorld().dropItemNaturally(player.getEyeLocation(), key); - } - else { - if(api.isBunchOfKeys(inHand)) { - api.removeKey(inHand, api.getKey(location)); - } - else { - player.getInventory().setItemInMainHand(new ItemStack(Material.AIR)); - } - player.playSound(player.getLocation(), Sound.ENTITY_ITEM_BREAK, 1f, 1f); - } - } - api.removePadlock(location); - plugin.sendMessage(player, plugin.getPluginMessages().message2); - } - else { - plugin.sendMessage(player, plugin.getPluginMessages().message3); - } - - event.setCancelled(true); - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onEntityBreakDoor(final EntityBreakDoorEvent event) { - if(event.isCancelled()) { - return; - } - - if(api.hasPadlock(event.getBlock().getLocation())) { - event.setCancelled(true); - } - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onEntityExplode(final EntityExplodeEvent event) { - if(event.isCancelled()) { - return; - } - - final List blocks = event.blockList(); - for(final Block block : new ArrayList<>(blocks)) { - if(api.hasPadlock(block.getLocation())) { - blocks.remove(block); - } - } - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onBlockRedstone(final BlockRedstoneEvent event) { - if(api.hasPadlock(event.getBlock().getLocation())) { - event.setNewCurrent(0); - } - } - -} diff --git a/src/main/java/fr/skyost/serialkey/listener/BunchOfKeysListener.java b/src/main/java/fr/skyost/serialkey/listener/BunchOfKeysListener.java deleted file mode 100644 index 24013ff..0000000 --- a/src/main/java/fr/skyost/serialkey/listener/BunchOfKeysListener.java +++ /dev/null @@ -1,93 +0,0 @@ -package fr.skyost.serialkey.listener; - -import org.bukkit.Material; -import org.bukkit.entity.HumanEntity; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryCloseEvent; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; - -import java.util.Collection; - -import fr.skyost.serialkey.SerialKey; - -/** - * A listener that allows to listen bunch of keys related events. - */ - -public class BunchOfKeysListener extends SerialKeyListener { - - /** - * Creates a new bunch of keys listener instance. - * - * @param plugin The plugin. - */ - - public BunchOfKeysListener(final SerialKey plugin) { - super(plugin); - } - - @EventHandler(priority = EventPriority.HIGHEST) - private void onPlayerInteract(final PlayerInteractEvent event) { - switch(event.getAction()) { - case RIGHT_CLICK_BLOCK: - if(api.hasPadlock(event.getClickedBlock().getLocation())) { - break; - } - case RIGHT_CLICK_AIR: - if(api.createInventory(event.getItem(), event.getPlayer()) != null) { - event.setCancelled(true); - } - default: - break; - } - } - - @EventHandler - private void onInventoryClick(final InventoryClickEvent event) { - if(!api.isBunchOfKeys(event.getInventory())) { - return; - } - - final ItemStack item = event.getCurrentItem(); - if(item.getType() != Material.AIR && !api.isUsedKey(item)) { - event.setCancelled(true); - } - } - - @EventHandler - private void onInventoryClose(final InventoryCloseEvent event) { - final Inventory inventory = event.getInventory(); - if(!api.isBunchOfKeys(inventory)) { - return; - } - - final HumanEntity player = event.getPlayer(); - - final ItemStack bunchOfKeys = player.getInventory().getItemInMainHand(); - if(bunchOfKeys.getAmount() > 1) { - final ItemStack clone = bunchOfKeys.clone(); - clone.setAmount(bunchOfKeys.getAmount() - 1); - player.getWorld().dropItemNaturally(player.getEyeLocation(), clone); - - bunchOfKeys.setAmount(1); - } - - api.clearKeys(bunchOfKeys); - - final Collection items = inventory.all(api.getKeyItem().getType()).values(); - for(final ItemStack item : items) { - api.addKey(bunchOfKeys, item); - - if(item.getAmount() > 1) { - final ItemStack clone = item.clone(); - clone.setAmount(item.getAmount() - 1); - player.getWorld().dropItemNaturally(player.getEyeLocation(), clone); - } - } - } - -} diff --git a/src/main/java/fr/skyost/serialkey/listener/GlobalListener.java b/src/main/java/fr/skyost/serialkey/listener/GlobalListener.java deleted file mode 100644 index e4fe74a..0000000 --- a/src/main/java/fr/skyost/serialkey/listener/GlobalListener.java +++ /dev/null @@ -1,148 +0,0 @@ -package fr.skyost.serialkey.listener; - -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.type.Chest; -import org.bukkit.block.data.type.TrapDoor; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.block.Action; -import org.bukkit.event.inventory.PrepareItemCraftEvent; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.inventory.CraftingInventory; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; - -import java.util.function.Function; - -import fr.skyost.serialkey.SerialKey; -import fr.skyost.serialkey.util.DoorUtil; - -/** - * A listener that allows to globally listen plugin events. - */ - -public class GlobalListener extends SerialKeyListener { - - /** - * Creates a new global listener instance. - * - * @param plugin The plugin. - */ - - public GlobalListener(final SerialKey plugin) { - super(plugin); - } - - @EventHandler(priority = EventPriority.HIGHEST) - private void onPrepareItemCraft(final PrepareItemCraftEvent event) { - final CraftingInventory craftingTable = event.getInventory(); - final ItemStack result = craftingTable.getResult(); - final Player player = (Player)event.getView().getPlayer(); - final boolean isKeyClone = api.getKeyCloneItem().equals(result); - final boolean isPadlockFinder = api.isPadlockFinder(result); - - if((api.isBlankKey(result) && !player.hasPermission("serialkey.craft.key")) || (api.isMasterKey(result) && !player.hasPermission("serialkey.craft.masterkey")) || (isKeyClone && !player.hasPermission("serialkey.craft.keyclone")) || (api.isBlankBunchOfKeys(result) && !player.hasPermission("serialkey.craft.bunchofkeys")) || (isPadlockFinder && !player.hasPermission("serialkey.craft.padlockfinder"))) { - plugin.sendMessage(player, plugin.getPluginMessages().messagePermission); - event.getInventory().setResult(null); - return; - } - - if(isKeyClone) { - cloneLore(craftingTable, item -> api.isBlankKey(item)); - return; - } - - if(isPadlockFinder) { - cloneLore(craftingTable, item -> item.getType() == Material.COMPASS); - } - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onPlayerInteract(final PlayerInteractEvent event) { - if(event.isCancelled()) { - return; - } - - final Block clicked = event.getClickedBlock(); - if(clicked == null) { - return; - } - final BlockData data = clicked.getBlockData(); - if(!(data instanceof Chest) && DoorUtil.getInstance(data) == null && !(data instanceof TrapDoor)) { - return; - } - - final Action action = event.getAction(); - final ItemStack item = event.getItem(); - if(action == Action.LEFT_CLICK_BLOCK) { - boolean isBlankKey = api.isBlankKey(item); - if(!isBlankKey && !api.isMasterKey(item)) { - return; - } - event.setCancelled(true); - final Player player = event.getPlayer(); - if(isBlankKey ? !player.hasPermission("serialkey.use.key") : !player.hasPermission("serialkey.use.masterkey")) { - plugin.sendMessage(player, plugin.getPluginMessages().messagePermission); - return; - } - final Location location = clicked.getLocation(); - if(api.hasPadlock(location)) { - plugin.sendMessage(player, plugin.getPluginMessages().message3); - return; - } - api.createPadlock(location, item); - plugin.sendMessage(player, plugin.getPluginMessages().message1); - return; - } - - if(action != Action.RIGHT_CLICK_BLOCK) { - return; - } - - final Location location = clicked.getLocation(); - if(!api.hasPadlock(location)) { - return; - } - - if(!api.isValidKey(item, location)) { - plugin.sendMessage(event.getPlayer(), plugin.getPluginMessages().message3); - event.setCancelled(true); - } - } - - private boolean cloneLore(final CraftingInventory craftingTable, final Function isIngredient) { - final ItemStack[] items = craftingTable.getContents(); - ItemStack key = null; - ItemStack ingredient = null; - for(final ItemStack item : items) { - if(item.getAmount() >= 2) { - continue; - } - - if(api.isUsedKey(item)) { - key = item; - continue; - } - - if(isIngredient.apply(item)) { - ingredient = item; - } - } - - if(key == null || ingredient == null) { - craftingTable.setResult(null); - return false; - } - - final ItemStack result = craftingTable.getResult(); - final ItemMeta meta = result.getItemMeta(); - meta.setLore(key.getItemMeta().getLore()); - result.setItemMeta(meta); - return true; - } - -} \ No newline at end of file diff --git a/src/main/java/fr/skyost/serialkey/listener/HopperListener.java b/src/main/java/fr/skyost/serialkey/listener/HopperListener.java deleted file mode 100644 index 8df929f..0000000 --- a/src/main/java/fr/skyost/serialkey/listener/HopperListener.java +++ /dev/null @@ -1,65 +0,0 @@ -package fr.skyost.serialkey.listener; - -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.Chest; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.block.BlockPlaceEvent; - -import fr.skyost.serialkey.SerialKey; - -/** - * A listener that allows to listen hoppers related events. - */ - -public class HopperListener extends SerialKeyListener { - - /** - * Faces to check for hoppers. - */ - - private static final BlockFace[] FACES = new BlockFace[]{ - BlockFace.UP, - BlockFace.DOWN, - BlockFace.NORTH, - BlockFace.EAST, - BlockFace.SOUTH, - BlockFace.WEST - }; - - /** - * Creates a new hoppers listener instance. - * - * @param plugin The plugin. - */ - - public HopperListener(final SerialKey plugin) { - super(plugin); - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onBlockPlace(final BlockPlaceEvent event) { - if(event.isCancelled()) { - return; - } - - final Block block = event.getBlockPlaced(); - if(block.getType() != Material.HOPPER) { - return; - } - - for(final BlockFace face : FACES) { - final Block relative = block.getRelative(face); - if(!(relative.getState() instanceof Chest) || !api.hasPadlock(relative.getLocation(), true)) { - continue; - } - - plugin.sendMessage(event.getPlayer(), plugin.getPluginMessages().message3); - event.setCancelled(true); - return; - } - } - -} diff --git a/src/main/java/fr/skyost/serialkey/listener/LostChestsListener.java b/src/main/java/fr/skyost/serialkey/listener/LostChestsListener.java deleted file mode 100644 index d90294e..0000000 --- a/src/main/java/fr/skyost/serialkey/listener/LostChestsListener.java +++ /dev/null @@ -1,46 +0,0 @@ -package fr.skyost.serialkey.listener; - -import org.bukkit.Location; -import org.bukkit.block.Chest; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.inventory.InventoryHolder; - -import fr.skyost.serialkey.SerialKey; - -/** - * A listener that allows to listen chests related events. - */ - -public class LostChestsListener extends SerialKeyListener { - - /** - * Creates a new lost chests listener instance. - * - * @param plugin The plugin. - */ - - public LostChestsListener(final SerialKey plugin) { - super(plugin); - } - - @EventHandler(priority = EventPriority.HIGHEST) - private void onInventoryClick(final InventoryClickEvent event) { - if(!api.isUsedKey(event.getCurrentItem())) { - return; - } - - final InventoryHolder holder = event.getInventory().getHolder(); - if(!(holder instanceof Chest)) { - return; - } - - final Location location = ((Chest)holder).getLocation(); - if(api.hasPadlock(location) && api.extractLocation(event.getCurrentItem()).equals(location)) { - event.setCancelled(true); - plugin.sendMessage(event.getWhoClicked(), plugin.getPluginMessages().message6); - } - } - -} \ No newline at end of file diff --git a/src/main/java/fr/skyost/serialkey/listener/PadlockFinderListener.java b/src/main/java/fr/skyost/serialkey/listener/PadlockFinderListener.java deleted file mode 100644 index 7f22a23..0000000 --- a/src/main/java/fr/skyost/serialkey/listener/PadlockFinderListener.java +++ /dev/null @@ -1,60 +0,0 @@ -package fr.skyost.serialkey.listener; - -import org.bukkit.ChatColor; -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.block.Action; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.inventory.ItemStack; - -import fr.skyost.serialkey.SerialKey; - -/** - * A listener that allows to listen padlock finder related events. - */ - -public class PadlockFinderListener extends SerialKeyListener { - - /** - * Creates a new padlock finder listener instance. - * - * @param plugin The plugin. - */ - - public PadlockFinderListener(final SerialKey plugin) { - super(plugin); - } - - @EventHandler(priority = EventPriority.LOWEST) - private void onPlayerInteract(final PlayerInteractEvent event) { - if(event.isCancelled() || event.getAction() != Action.RIGHT_CLICK_AIR && event.getAction() != Action.RIGHT_CLICK_BLOCK) { - return; - } - - final ItemStack item = event.getItem(); - if(!api.isUsedPadlockFinder(item)) { - return; - } - - final Player player = event.getPlayer(); - final Location spawn = player.getWorld().getSpawnLocation(); - if(player.getCompassTarget().equals(spawn)) { - try { - player.setCompassTarget(api.extractLocation(item)); - player.sendMessage(plugin.getPluginMessages().message4); - } - catch(final Exception ex) { - ex.printStackTrace(); - plugin.sendMessage(player, ChatColor.RED + ex.getClass().getName()); - } - } - else { - player.setCompassTarget(spawn); - player.sendMessage(plugin.getPluginMessages().message5); - } - event.setCancelled(true); - } - -} diff --git a/src/main/java/fr/skyost/serialkey/listener/SerialKeyListener.java b/src/main/java/fr/skyost/serialkey/listener/SerialKeyListener.java deleted file mode 100644 index b2d0db1..0000000 --- a/src/main/java/fr/skyost/serialkey/listener/SerialKeyListener.java +++ /dev/null @@ -1,57 +0,0 @@ -package fr.skyost.serialkey.listener; - -import org.bukkit.event.Listener; - -import fr.skyost.serialkey.SerialKey; -import fr.skyost.serialkey.SerialKeyAPI; - -/** - * A class that represents a SerialKey listener. - */ - -public abstract class SerialKeyListener implements Listener { - - /** - * The plugin instance. - */ - - SerialKey plugin; - - /** - * The plugin API. - */ - - SerialKeyAPI api; - - /** - * Creates a new SerialKey listener instance. - * - * @param plugin The plugin. - */ - - SerialKeyListener(final SerialKey plugin) { - setPlugin(plugin); - } - - /** - * Returns the plugin instance. - * - * @return The plugin instance. - */ - - public SerialKey getPlugin() { - return plugin; - } - - /** - * Sets the plugin instance. - * - * @param plugin The plugin instance. - */ - - public void setPlugin(final SerialKey plugin) { - this.plugin = plugin; - this.api = plugin.getAPI(); - } - -} \ No newline at end of file diff --git a/src/main/java/fr/skyost/serialkey/util/Util.java b/src/main/java/fr/skyost/serialkey/util/Util.java deleted file mode 100644 index ab03793..0000000 --- a/src/main/java/fr/skyost/serialkey/util/Util.java +++ /dev/null @@ -1,118 +0,0 @@ -package fr.skyost.serialkey.util; - -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; - -/** - * Contains some useful methods. - */ - -public class Util { - - /** - * Picks a random ChatColor. - * - * @param exclude If you want to exclude some colors. - * - * @return A random ChatColor. - */ - - public static ChatColor randomChatColor(final ChatColor... exclude) { - final List excludeList = Arrays.asList(exclude); - final ChatColor[] values = ChatColor.values(); - final Random random = new Random(); - - ChatColor result; - do { - result = values[random.nextInt(values.length)]; - } - while(excludeList.contains(result)); - return result; - } - - /** - * Returns the map which contains only the specified objects. - * - * @param map The map. - * @param objects The objects. - * - * @return A map which contains only the objects (as keys). - */ - - public static Map keepAll(final Map map, final Collection objects) { - final Map result = new HashMap<>(); - for(final String object : objects) { - final char[] chars = new char[object.length()]; - object.getChars(0, object.length() > 3 ? 3 : object.length(), chars, 0); - for(final char c : chars) { - if(c == ' ') { - continue; - } - final String character = String.valueOf(c); - if(map.containsKey(character)) { - result.put(character, map.get(character)); - } - } - } - return result; - } - - /** - * Creates a map. - * - * @param keys The keys. - * @param values The values. - * - * @return The map. - */ - - public static Map createMap(final K[] keys, final V[] values) { - if(keys.length != values.length) { - return null; - } - final Map map = new HashMap<>(); - for(int i = 0; i != keys.length; i++) { - map.put(keys[i], values[i]); - } - return map; - } - - /** - * Creates an item with a custom name. - * - * @param name The name. - * @param material The item's material. - * - * @return The item. - */ - - public static ItemStack createItem(final String name, final Material material) { - final ItemStack item = new ItemStack(material); - final ItemMeta meta = item.getItemMeta(); - meta.setDisplayName(name); - item.setItemMeta(meta); - return item; - } - - /** - * Checks if the specified item is valid. - * - * @param item The item. - * - * @return true : yes. - *
false : no. - */ - - public static boolean isValidItem(final ItemStack item) { - return (item != null && item.hasItemMeta()) && item.getItemMeta().hasDisplayName(); - } - -} \ No newline at end of file