├── complete ├── Sword │ ├── plugin.yml │ └── me │ │ └── piggypiglet │ │ └── sword │ │ ├── Sword.java │ │ └── SwordCommand.java ├── Greet │ ├── plugin.yml │ └── me │ │ └── piggypiglet │ │ └── greet │ │ ├── Greet.java │ │ └── GreetCommand.java ├── RandomRespawn │ ├── plugin.yml │ ├── config.yml │ └── me │ │ └── piggypiglet │ │ └── rr │ │ ├── RREvent.java │ │ ├── RRCommand.java │ │ └── RandomRespawn.java ├── Arena │ ├── plugin.yml │ └── me │ │ └── piggypiglet │ │ └── arena │ │ ├── Arena.java │ │ └── ArenaCommand.java ├── Warrior │ ├── plugin.yml │ └── me │ │ └── piggypiglet │ │ └── warrior │ │ ├── Warrior.java │ │ ├── MageEvent.java │ │ └── WarriorCommand.java └── pom.xml └── README.md /complete/Sword/plugin.yml: -------------------------------------------------------------------------------- 1 | name: Sword 2 | version: 1.0.0 3 | description: 4 | author: PiggyPiglet 5 | website: https://www.piggypiglet.me 6 | main: me.piggypiglet.sword.Sword 7 | commands: 8 | sword: 9 | description: Give yourself a kewlios sword -------------------------------------------------------------------------------- /complete/Greet/plugin.yml: -------------------------------------------------------------------------------- 1 | name: Greet 2 | version: 1.0.0 3 | description: Greet plugin 4 | author: PiggyPiglet 5 | website: https://www.piggypiglet.me 6 | main: me.piggypiglet.greet.Greet 7 | commands: 8 | greet: 9 | description: Greet yourself 10 | usage: /greet [optional args] -------------------------------------------------------------------------------- /complete/Sword/me/piggypiglet/sword/Sword.java: -------------------------------------------------------------------------------- 1 | package me.piggypiglet.sword; 2 | 3 | import org.bukkit.plugin.java.JavaPlugin; 4 | 5 | public class Sword extends JavaPlugin { 6 | @Override 7 | public void onEnable() { 8 | getCommand("sword").setExecutor(new SwordCommand()); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /complete/Greet/me/piggypiglet/greet/Greet.java: -------------------------------------------------------------------------------- 1 | package me.piggypiglet.greet; 2 | 3 | import org.bukkit.plugin.java.JavaPlugin; 4 | 5 | public class Greet extends JavaPlugin { 6 | 7 | @Override 8 | public void onEnable() { 9 | getCommand("greet").setExecutor(new GreetCommand()); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /complete/RandomRespawn/plugin.yml: -------------------------------------------------------------------------------- 1 | name: RandomRespawn 2 | version: 1.0.0 3 | description: A plugin that randomly respawns you at a set respawn point. 4 | author: PiggyPiglet 5 | website: https://www.piggypiglet.me 6 | main: me.piggypiglet.rr.RandomRespawn 7 | commands: 8 | rr: 9 | description: random respawn 10 | usage: /rr -------------------------------------------------------------------------------- /complete/Arena/plugin.yml: -------------------------------------------------------------------------------- 1 | name: Arena 2 | version: 1.0.0 3 | description: Arena plugin 4 | author: PiggyPiglet 5 | website: https://www.piggypiglet.me 6 | main: me.piggypiglet.arena.Arena 7 | commands: 8 | arena: 9 | description: Teleport to an arena 10 | usage: /arena 11 | setarena: 12 | description: Set an arena 13 | usage: /setarena -------------------------------------------------------------------------------- /complete/Warrior/plugin.yml: -------------------------------------------------------------------------------- 1 | name: Warrior 2 | version: 1.0.0 3 | description: 4 | author: PiggyPiglet 5 | website: https://www.piggypiglet.me 6 | main: me.piggypiglet.warrior.Warrior 7 | commands: 8 | warrior: 9 | description: Get the warrior kit 10 | usage: /warrior 11 | archer: 12 | description: Get the archer kit 13 | usage: /archer 14 | mage: 15 | description: Get the mage kit 16 | usage: /mage -------------------------------------------------------------------------------- /complete/RandomRespawn/config.yml: -------------------------------------------------------------------------------- 1 | # This is the default config for RandomRespawn. 2 | # You can add more locations in the location section. 3 | # locations: 4 | # Swamp: 5 | # x: 632 6 | # y: 10 7 | # z: 31 8 | # Bush: 9 | # x: 4321 10 | # y: 120 11 | # z: 951 12 | # Clay: 13 | # x: 66123 14 | # y: 109 15 | # z: 392 16 | 17 | locations: 18 | name: 19 | x: 10 20 | y: 100 21 | z: 10 -------------------------------------------------------------------------------- /complete/Warrior/me/piggypiglet/warrior/Warrior.java: -------------------------------------------------------------------------------- 1 | package me.piggypiglet.warrior; 2 | 3 | import org.bukkit.plugin.java.JavaPlugin; 4 | 5 | public class Warrior extends JavaPlugin { 6 | @Override 7 | public void onEnable() { 8 | getCommand("warrior").setExecutor(new WarriorCommand()); 9 | getCommand("archer").setExecutor(new WarriorCommand()); 10 | getCommand("mage").setExecutor(new WarriorCommand()); 11 | getServer().getPluginManager().registerEvents(new MageEvent(), this); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /complete/Arena/me/piggypiglet/arena/Arena.java: -------------------------------------------------------------------------------- 1 | package me.piggypiglet.arena; 2 | 3 | import org.bukkit.plugin.java.JavaPlugin; 4 | 5 | public class Arena extends JavaPlugin { 6 | 7 | @Override 8 | public void onEnable() { 9 | getCommand("arena").setExecutor(new ArenaCommand(this)); 10 | getCommand("setarena").setExecutor(new ArenaCommand(this)); 11 | getConfig().addDefault("x", 1); 12 | getConfig().addDefault("y", 1); 13 | getConfig().addDefault("z", 1); 14 | getConfig().options().copyDefaults(true); 15 | saveConfig(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /complete/Greet/me/piggypiglet/greet/GreetCommand.java: -------------------------------------------------------------------------------- 1 | package me.piggypiglet.greet; 2 | 3 | import org.bukkit.command.Command; 4 | import org.bukkit.command.CommandExecutor; 5 | import org.bukkit.command.CommandSender; 6 | import org.bukkit.entity.Player; 7 | 8 | public class GreetCommand implements CommandExecutor { 9 | @Override 10 | public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { 11 | if (sender instanceof Player) { 12 | Player player = (Player) sender; 13 | if (args.length == 0) { 14 | player.sendRawMessage("§7Greetings §c" + player.getDisplayName()); 15 | } 16 | else { 17 | player.sendRawMessage("§7Greetings §c" + args[0].replace("[", "").replace("]", "")); 18 | } 19 | } 20 | return true; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /complete/Warrior/me/piggypiglet/warrior/MageEvent.java: -------------------------------------------------------------------------------- 1 | package me.piggypiglet.warrior; 2 | 3 | import org.bukkit.entity.Player; 4 | import org.bukkit.event.EventHandler; 5 | import org.bukkit.event.Listener; 6 | import org.bukkit.event.block.Action; 7 | import org.bukkit.event.player.PlayerInteractEvent; 8 | 9 | import static org.bukkit.Material.STICK; 10 | import static org.bukkit.entity.EntityType.FIREBALL; 11 | import static org.bukkit.event.block.Action.*; 12 | 13 | public class MageEvent implements Listener { 14 | @EventHandler 15 | public void onPlayerInteractEvent(PlayerInteractEvent e) { 16 | Player p = e.getPlayer(); 17 | Action a = e.getAction(); 18 | if (a == RIGHT_CLICK_AIR || a == RIGHT_CLICK_BLOCK || a == LEFT_CLICK_AIR || a == LEFT_CLICK_BLOCK) { 19 | if (e.getItem().getType() == STICK) { 20 | if (e.getItem().getItemMeta().getDisplayName().equals("§cWand")) { 21 | p.getWorld().spawnEntity(p.getLocation(), FIREBALL); 22 | } 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /complete/Sword/me/piggypiglet/sword/SwordCommand.java: -------------------------------------------------------------------------------- 1 | package me.piggypiglet.sword; 2 | 3 | import org.bukkit.Material; 4 | import org.bukkit.command.Command; 5 | import org.bukkit.command.CommandExecutor; 6 | import org.bukkit.command.CommandSender; 7 | import org.bukkit.enchantments.Enchantment; 8 | import org.bukkit.entity.Player; 9 | import org.bukkit.inventory.ItemStack; 10 | import org.bukkit.inventory.meta.ItemMeta; 11 | 12 | public class SwordCommand implements CommandExecutor { 13 | 14 | @Override 15 | public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { 16 | if (sender instanceof Player) { 17 | Player player = (Player) sender; 18 | 19 | ItemStack sword = new ItemStack(Material.DIAMOND_SWORD); 20 | ItemMeta meta = sword.getItemMeta(); 21 | meta.setDisplayName("§cSuper Damage"); 22 | meta.addEnchant(Enchantment.DAMAGE_ALL, 10, true); 23 | sword.setItemMeta(meta); 24 | 25 | player.getInventory().addItem(sword); 26 | } 27 | return true; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /complete/RandomRespawn/me/piggypiglet/rr/RREvent.java: -------------------------------------------------------------------------------- 1 | package me.piggypiglet.rr; 2 | 3 | import org.bukkit.Bukkit; 4 | import org.bukkit.Location; 5 | import org.bukkit.entity.Player; 6 | import org.bukkit.event.EventHandler; 7 | import org.bukkit.event.Listener; 8 | import org.bukkit.event.entity.PlayerDeathEvent; 9 | 10 | public class RREvent implements Listener { 11 | RandomRespawn plugin; 12 | 13 | public RREvent(RandomRespawn instance) { 14 | plugin = instance; 15 | } 16 | @EventHandler 17 | public void onPlayerDeathEvent(PlayerDeathEvent e) { 18 | Player p = e.getEntity(); 19 | if (p.isDead()) { 20 | p.setHealth(20); 21 | String randomString = plugin.getRandomStringFromList(plugin.getLocationList()); 22 | 23 | p.teleport(new Location(Bukkit.getWorld("world"), 24 | plugin.getConfig().getInt("locations." + randomString + ".x"), 25 | plugin.getConfig().getInt("locations." + randomString + ".y"), 26 | plugin.getConfig().getInt("locations." + randomString + ".z"))); 27 | } 28 | } 29 | } 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /complete/RandomRespawn/me/piggypiglet/rr/RRCommand.java: -------------------------------------------------------------------------------- 1 | package me.piggypiglet.rr; 2 | 3 | import org.bukkit.Bukkit; 4 | import org.bukkit.Location; 5 | import org.bukkit.command.Command; 6 | import org.bukkit.command.CommandExecutor; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | 10 | public class RRCommand implements CommandExecutor { 11 | RandomRespawn plugin; 12 | 13 | public RRCommand(RandomRespawn instance) { 14 | plugin = instance; 15 | } 16 | @Override 17 | public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { 18 | if (sender instanceof Player) { 19 | Player player = (Player) sender; 20 | if (cmd.getName().equalsIgnoreCase("rr")) { 21 | player.setHealth(0); 22 | player.setHealth(20); 23 | String randomString = plugin.getRandomStringFromList(plugin.getLocationList()); 24 | 25 | player.teleport(new Location(Bukkit.getWorld("world"), 26 | plugin.getConfig().getInt("locations." + randomString + ".x"), 27 | plugin.getConfig().getInt("locations." + randomString + ".y"), 28 | plugin.getConfig().getInt("locations." + randomString + ".z"))); 29 | } 30 | } 31 | return true; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /complete/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.piggypiglet 8 | Challenges 9 | 1.0 10 | Challenges 11 | 12 | 13 | 14 | org.apache.maven.plugins 15 | maven-compiler-plugin 16 | 3.6.1 17 | 18 | 1.8 19 | 1.8 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | spigot-repo 28 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 29 | 30 | 31 | 32 | 33 | 34 | org.spigotmc 35 | spigot-api 36 | 1.12-R0.1-SNAPSHOT 37 | provided 38 | 39 | 40 | org.bukkit 41 | bukkit 42 | 1.12-R0.1-SNAPSHOT 43 | provided 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /complete/Arena/me/piggypiglet/arena/ArenaCommand.java: -------------------------------------------------------------------------------- 1 | package me.piggypiglet.arena; 2 | 3 | import org.bukkit.Bukkit; 4 | import org.bukkit.Location; 5 | import org.bukkit.command.Command; 6 | import org.bukkit.command.CommandExecutor; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | 10 | public class ArenaCommand implements CommandExecutor { 11 | 12 | private Arena plugin; 13 | 14 | public ArenaCommand(Arena instance) { 15 | plugin = instance; 16 | } 17 | @Override 18 | public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { 19 | if (sender instanceof Player) { 20 | Player player = (Player) sender; 21 | if (cmd.getName().equalsIgnoreCase("setarena")) { 22 | plugin.getConfig().set("x", player.getLocation().getBlockX()); 23 | plugin.getConfig().set("y", player.getLocation().getBlockY()); 24 | plugin.getConfig().set("z", player.getLocation().getBlockZ()); 25 | plugin.saveConfig(); 26 | player.getWorld().setSpawnLocation(player.getLocation().getBlockX(), player.getLocation().getBlockY(), player.getLocation().getBlockZ()); 27 | player.sendRawMessage("§7Arena and world spawn set to §c" + player.getLocation().getBlockX() + " §c" + player.getLocation().getBlockY() + " §c" + player.getLocation().getBlockZ()); 28 | } 29 | if (cmd.getName().equalsIgnoreCase("arena")) { 30 | player.teleport(new Location(Bukkit.getWorld("world"), plugin.getConfig().getInt("x"), plugin.getConfig().getInt("y"), plugin.getConfig().getInt("z"))); 31 | player.sendRawMessage("§7You have been teleported to the arena"); 32 | } 33 | } 34 | return true; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /complete/RandomRespawn/me/piggypiglet/rr/RandomRespawn.java: -------------------------------------------------------------------------------- 1 | package me.piggypiglet.rr; 2 | 3 | import org.bukkit.configuration.ConfigurationSection; 4 | import org.bukkit.configuration.file.FileConfiguration; 5 | import org.bukkit.plugin.java.JavaPlugin; 6 | 7 | import java.util.*; 8 | import java.util.concurrent.ThreadLocalRandom; 9 | 10 | public class RandomRespawn extends JavaPlugin { 11 | 12 | public String getRandomStringFromList(List list) { 13 | ThreadLocalRandom random = ThreadLocalRandom.current(); 14 | int randomNumber = random.nextInt(list.size()); 15 | String randomString = list.get(randomNumber); 16 | 17 | return randomString; 18 | } 19 | 20 | private List locationList; 21 | 22 | public List getLocationList() { 23 | if (this.locationList == null) 24 | this.locationList = new ArrayList<>(); 25 | return this.locationList; 26 | } 27 | @Override 28 | public void onEnable() { 29 | getCommand("rr").setExecutor(new RRCommand(this)); 30 | getServer().getPluginManager().registerEvents(new RREvent(this), this); 31 | getConfig().addDefault("locations.name.x", 10); 32 | getConfig().addDefault("locations.name.y", 100); 33 | getConfig().addDefault("locations.name.z", 10); 34 | getConfig().options().copyDefaults(true); 35 | saveConfig(); 36 | ConfigurationSection section = this.getConfig().getConfigurationSection("locations"); 37 | 38 | if (section != null) { 39 | Set locations = section.getKeys(false); 40 | 41 | if (locations != null && !locations.isEmpty()) { 42 | for (String location : locations) { 43 | getLocationList().add(location); 44 | } 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /complete/Warrior/me/piggypiglet/warrior/WarriorCommand.java: -------------------------------------------------------------------------------- 1 | package me.piggypiglet.warrior; 2 | 3 | import org.bukkit.Location; 4 | import org.bukkit.command.Command; 5 | import org.bukkit.command.CommandExecutor; 6 | import org.bukkit.command.CommandSender; 7 | import org.bukkit.entity.EntityType; 8 | import org.bukkit.entity.Player; 9 | import org.bukkit.event.EventHandler; 10 | import org.bukkit.event.block.Action; 11 | import org.bukkit.event.player.PlayerInteractEvent; 12 | import org.bukkit.inventory.ItemStack; 13 | import org.bukkit.inventory.meta.ItemMeta; 14 | 15 | import static org.bukkit.Material.*; 16 | import static org.bukkit.enchantments.Enchantment.*; 17 | import static org.bukkit.event.block.Action.*; 18 | 19 | public class WarriorCommand implements CommandExecutor { 20 | @Override 21 | public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { 22 | if (sender instanceof Player) { 23 | Player player = (Player) sender; 24 | if (cmd.getName().equalsIgnoreCase("warrior")) { 25 | 26 | ItemStack[] warrior; 27 | warrior = new ItemStack[]{new ItemStack(IRON_SWORD), new ItemStack(FISHING_ROD)}; 28 | ItemStack helmet = new ItemStack(IRON_HELMET); 29 | ItemStack chestplate = new ItemStack(IRON_CHESTPLATE); 30 | ItemStack leggings = new ItemStack(IRON_LEGGINGS); 31 | ItemStack boots = new ItemStack(IRON_BOOTS); 32 | 33 | player.getInventory().clear(); 34 | player.getInventory().addItem(warrior); 35 | player.getInventory().setHelmet(helmet); 36 | player.getInventory().setChestplate(chestplate); 37 | player.getInventory().setLeggings(leggings); 38 | player.getInventory().setBoots(boots); 39 | } 40 | if (cmd.getName().equalsIgnoreCase("archer")) { 41 | 42 | ItemStack[] archer; 43 | archer = new ItemStack[]{new ItemStack(WOOD_SWORD), new ItemStack(BOW), new ItemStack(ARROW, 64), new ItemStack(ARROW, 64)}; 44 | ItemStack helmet = new ItemStack(CHAINMAIL_HELMET); 45 | ItemStack chestplate = new ItemStack(CHAINMAIL_CHESTPLATE); 46 | ItemStack leggings = new ItemStack(CHAINMAIL_LEGGINGS); 47 | ItemStack boots = new ItemStack(CHAINMAIL_BOOTS); 48 | 49 | player.getInventory().clear(); 50 | player.getInventory().addItem(archer); 51 | player.getInventory().setHelmet(helmet); 52 | player.getInventory().setChestplate(chestplate); 53 | player.getInventory().setLeggings(leggings); 54 | player.getInventory().setBoots(boots); 55 | } 56 | if (cmd.getName().equalsIgnoreCase("mage")) { 57 | 58 | ItemStack mage = new ItemStack(STICK); 59 | ItemMeta meta = mage.getItemMeta(); 60 | meta.addEnchant(KNOCKBACK, 2, false); 61 | meta.setDisplayName("§cWand"); 62 | mage.setItemMeta(meta); 63 | 64 | ItemStack helmet = new ItemStack(GOLD_HELMET); 65 | ItemStack chestplate = new ItemStack(GOLD_CHESTPLATE); 66 | ItemStack leggings = new ItemStack(GOLD_LEGGINGS); 67 | ItemStack boots = new ItemStack(GOLD_BOOTS); 68 | 69 | player.getInventory().clear(); 70 | player.getInventory().addItem(mage); 71 | player.getInventory().setHelmet(helmet); 72 | player.getInventory().setChestplate(chestplate); 73 | player.getInventory().setLeggings(leggings); 74 | player.getInventory().setBoots(boots); 75 | } 76 | } 77 | return true; 78 | } 79 | } 80 | 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Challenges 2 | Wtf is this? Well, this is my challenges repository, which contains the projects that assisted me in learning the spigot api and java when I was first starting out. 3 | 4 | WARNING: The complete folder in here isn't a full collection of the completed challenges, they are merely my attempts, and should not under any circumstances be used as a guide. 5 | 6 | # Challenges list 7 | 8 | 1. Register a /greet command which sends "Hello!" to the player (Extra challenge: Add an optional extra argument which is the player's name that should be greeted) 9 | 10 | 2. Create a sword with a custom name and give it to a player. (Extra challenge: with modified attack damage) 11 | 12 | 3. Register a /warrior which clears your inventory and gives you iron armor, iron sword and a fishing rod. (Extra challenge: Also /archer with chain armor and a wooden sword, a bow and two stacks of arrows. Super-advanced extra challenge: /mage which has a Knockback II stick with customized attack damage which summons fireballs on a 5s cooldown and gold armor) 13 | 14 | 4. Register a command which teleports you to some kind of arena and sets your spawn there (Extra challenge: Teleport the player to a different world you've created earlier instead) 15 | 16 | 5. Design multiple respawn points for a player of which randomly one will be chosen on death (Extra challenge: The one with the largest distance to another player will be chosen) 17 | 18 | 6. Make the player unable to break blocks (Extra challenge: Only in a certain area/world and not for players in Creative) 19 | 20 | 7. Make players not drop their inventory on death. (Extra challenge: Also make them respawn instantly, without having to click the "Respawn" button) 21 | 22 | 8. Whenever a user mines a block with a diamond hoe, summon TNT at the block's position (Extra challenge: Your custom sword instead of the diamond hoe, but not any other sword. Super-advanced extra challenge: Use NBT data to do the extra challenge) 23 | 24 | 9. Create a scoreboard in the sidebar that increases by one every time a player kills another (Extra challenge: Increase by 2, decrease by 1 on death) 25 | 26 | 10. Register a command /cube which places a dirt cube with the side length size at x,y,z (Extra challenge: an empty box instead, centered at the player's position) 27 | 28 | 11. Create a timer that starts at 5min, sends a chat message every minute and when it's over (Extra challenge: Also send a chat message at 30s, 15s and every second after 10s) 29 | 30 | 12. Create a signs that grants you items on right-click (Extra challenge: trade instead, so you will only get the items if you have enough of another item) 31 | 32 | 13. Create a (static) utility function that adds an ATTACK_SPEED attribute to a weapon or tool, in a way that it cancels out the 1.9 changes (Extra challenge: Also cancel out the damage changes for each tool. [Here](http://minecraft.gamepedia.com/index.php?title=Damage&oldid=950696#Dealing_damage) are the old damage values) 33 | 34 | 14. Create a chat filter. (Extra challenge: Detect leet speech, eg. sh1t or f4gg0t) 35 | 36 | 15. Combine 3, 4, 5, 6, 7, 9, 11 and 13 to create a simple KitPvP plug-in. Congrats! 37 | 38 | Here are some more advanced challenges that'll need a lot more research: 39 | 40 | 1. Use Databases to create a currency system. Be sure to take a deep look into this topic, while it's easy to throw something working together it will often be buggy and slow if you aren't cautious! 41 | 42 | 2. Set a player's client-side (in-game) time to his real time, depending on his timezone (geotracked using his IP - there are some free services out there to do that). The server-side time is always day and does not change. 43 | 44 | 3. Develop an anti-cheat. For every hack, think about how the hacker's behavior differs from a normal's user (eg. for speed hacks, a normal user moves only X blocks per seconds while a hacker may move faster). This is a very hard task and needs extensive testing. 45 | 46 | Challenges list by [StillNoNumber](https://www.spigotmc.org/threads/challenges.235525/#post-2378547) 47 | 48 | # Discord 49 | Talk to me and get notified whenever I update this, [Discord](https://www.piggypiglet.me/discord) 50 | --------------------------------------------------------------------------------