├── 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 |
--------------------------------------------------------------------------------