From 43c567ee025a5d212a0f6643413023a39a12bdb6 Mon Sep 17 00:00:00 2001 From: DroppingAnvil Date: Sat, 29 Feb 2020 20:23:57 -0600 Subject: [PATCH] Made break listener safer, Duels started, Task scheduling reduced and streamlined, many other improvements that I cant remember --- .../com/massivecraft/factions/FPlayer.java | 26 +++ .../com/massivecraft/factions/Faction.java | 2 + .../massivecraft/factions/FactionsPlugin.java | 3 + .../massivecraft/factions/cmd/Aliases.java | 6 +- .../massivecraft/factions/cmd/CmdDiscord.java | 3 + .../massivecraft/factions/cmd/CmdReload.java | 4 + .../cmd/configsf/ConvertConfigHandler.java | 3 + .../factions/cmd/wild/CmdWild.java | 39 ++--- .../factions/cmd/wild/WildGUI.java | 8 +- .../factions/discord/Discord.java | 3 + .../factions/discord/DiscordSetupAttempt.java | 3 + .../com/massivecraft/factions/duels/Duel.java | 164 ++++++++++++++++++ .../massivecraft/factions/duels/Duels.java | 73 ++++++++ .../factions/duels/RequestGUI.java | 121 +++++++++++++ .../factions/duels/RequestResponse.java | 10 ++ .../factions/duels/SpawnPoint.java | 27 +++ .../listeners/FactionsBlockListener.java | 60 ++++--- .../listeners/FactionsPlayerListener.java | 12 +- .../factions/util/PermissionList.java | 3 + .../massivecraft/factions/util/StaticGUI.java | 15 ++ .../factions/util/Sync/SyncExecutor.java | 35 ++++ .../factions/util/Sync/SyncTask.java | 63 +++++++ .../factions/util/Wait/WaitExecutor.java | 41 +++++ .../factions/util/Wait/WaitTask.java | 47 +++++ .../factions/util/Wait/WaitedTask.java | 11 ++ .../factions/zcore/persist/MemoryFPlayer.java | 12 ++ .../factions/zcore/persist/MemoryFaction.java | 6 + .../massivecraft/factions/zcore/util/TL.java | 6 + src/main/resources/config.yml | 55 ++++++ 29 files changed, 800 insertions(+), 61 deletions(-) create mode 100644 src/main/java/com/massivecraft/factions/duels/Duel.java create mode 100644 src/main/java/com/massivecraft/factions/duels/Duels.java create mode 100644 src/main/java/com/massivecraft/factions/duels/RequestGUI.java create mode 100644 src/main/java/com/massivecraft/factions/duels/RequestResponse.java create mode 100644 src/main/java/com/massivecraft/factions/duels/SpawnPoint.java create mode 100644 src/main/java/com/massivecraft/factions/util/StaticGUI.java create mode 100644 src/main/java/com/massivecraft/factions/util/Sync/SyncExecutor.java create mode 100644 src/main/java/com/massivecraft/factions/util/Sync/SyncTask.java create mode 100644 src/main/java/com/massivecraft/factions/util/Wait/WaitExecutor.java create mode 100644 src/main/java/com/massivecraft/factions/util/Wait/WaitTask.java create mode 100644 src/main/java/com/massivecraft/factions/util/Wait/WaitedTask.java diff --git a/src/main/java/com/massivecraft/factions/FPlayer.java b/src/main/java/com/massivecraft/factions/FPlayer.java index 75c32f49..2fbdbdd2 100644 --- a/src/main/java/com/massivecraft/factions/FPlayer.java +++ b/src/main/java/com/massivecraft/factions/FPlayer.java @@ -12,8 +12,10 @@ import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; import java.util.List; +import java.util.Map; /** @@ -44,6 +46,30 @@ public interface FPlayer extends EconomyParticipator { */ void setEnemiesNearby(Boolean b); + /** + * Get this players inventory prior to entering the duel (Will be set to null after duel!) + * @return Map of old inventory contents + */ + Map getOldInv(); + + /** + * Set this players stored inventory, this inventory will be retrieved after a duel is complete + * @param inv Map of inventory contents + */ + void setOldInv(Map inv); + + /** + * Used to check if player has entered a duel and has not proceeded + * @return boolean determining if the player is in a duel + */ + boolean isInDuel(); + + /** + * Used before and after duels to set the inDuel status of the FPlayer + * @param b Desired status + */ + void setInDuel(Boolean b); + /** * Get if a player has setup their Discord before * @return if the player setup Discord as a boolean diff --git a/src/main/java/com/massivecraft/factions/Faction.java b/src/main/java/com/massivecraft/factions/Faction.java index d9f7cb08..e5a3c5d3 100644 --- a/src/main/java/com/massivecraft/factions/Faction.java +++ b/src/main/java/com/massivecraft/factions/Faction.java @@ -23,6 +23,8 @@ import java.util.concurrent.ConcurrentHashMap; public interface Faction extends EconomyParticipator { + void broadcast(String s); + String getMemberRoleId(); void setMemberRoleId(String roleId); diff --git a/src/main/java/com/massivecraft/factions/FactionsPlugin.java b/src/main/java/com/massivecraft/factions/FactionsPlugin.java index fd5b0d00..d5138bc6 100755 --- a/src/main/java/com/massivecraft/factions/FactionsPlugin.java +++ b/src/main/java/com/massivecraft/factions/FactionsPlugin.java @@ -26,6 +26,7 @@ import com.massivecraft.factions.struct.Relation; import com.massivecraft.factions.struct.Role; import com.massivecraft.factions.util.*; import com.massivecraft.factions.util.Particles.ReflectionUtils; +import com.massivecraft.factions.util.Sync.SyncExecutor; import com.massivecraft.factions.zcore.CommandVisibility; import com.massivecraft.factions.zcore.MPlugin; import com.massivecraft.factions.zcore.fperms.Access; @@ -181,6 +182,8 @@ public class FactionsPlugin extends MPlugin { this.saveResource("config.yml", false); this.reloadConfig(); } + //Start synctask + SyncExecutor.startTask(); //Attempt to generate a permission list PermissionList.generateFile(); // Load Conf from disk diff --git a/src/main/java/com/massivecraft/factions/cmd/Aliases.java b/src/main/java/com/massivecraft/factions/cmd/Aliases.java index 35e351ac..ad5e7e54 100644 --- a/src/main/java/com/massivecraft/factions/cmd/Aliases.java +++ b/src/main/java/com/massivecraft/factions/cmd/Aliases.java @@ -6,10 +6,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +/** + * @author DroppingAnvil + */ public class Aliases { - /** - * @author DroppingAnvil - */ public static ArrayList alts_alts = new ArrayList<>(Arrays.asList("alts", "alt")); public static ArrayList alts_list = new ArrayList<>(Arrays.asList("list", "l")); public static ArrayList alts_invite = new ArrayList<>(Collections.singletonList("invite")); diff --git a/src/main/java/com/massivecraft/factions/cmd/CmdDiscord.java b/src/main/java/com/massivecraft/factions/cmd/CmdDiscord.java index 33fd4a58..157aefba 100644 --- a/src/main/java/com/massivecraft/factions/cmd/CmdDiscord.java +++ b/src/main/java/com/massivecraft/factions/cmd/CmdDiscord.java @@ -6,6 +6,9 @@ import com.massivecraft.factions.zcore.util.TL; import java.util.Random; +/** + * @author droppinganvil + */ public class CmdDiscord extends FCommand { public CmdDiscord() { super(); diff --git a/src/main/java/com/massivecraft/factions/cmd/CmdReload.java b/src/main/java/com/massivecraft/factions/cmd/CmdReload.java index c74611b5..9ee4d1a1 100644 --- a/src/main/java/com/massivecraft/factions/cmd/CmdReload.java +++ b/src/main/java/com/massivecraft/factions/cmd/CmdReload.java @@ -3,6 +3,7 @@ package com.massivecraft.factions.cmd; import com.massivecraft.factions.Conf; import com.massivecraft.factions.FactionsPlugin; import com.massivecraft.factions.discord.Discord; +import com.massivecraft.factions.duels.RequestGUI; import com.massivecraft.factions.listeners.FactionsPlayerListener; import com.massivecraft.factions.shop.ShopConfig; import com.massivecraft.factions.struct.Permission; @@ -44,6 +45,9 @@ public class CmdReload extends FCommand { FCmdRoot.instance.addVariableCommands(); FCmdRoot.instance.rebuild(); long timeReload = (System.currentTimeMillis() - timeInitStart); + //Duels + RequestGUI.inv = null; + RequestGUI.responseMap.clear(); context.msg(TL.COMMAND_RELOAD_TIME, timeReload); } diff --git a/src/main/java/com/massivecraft/factions/cmd/configsf/ConvertConfigHandler.java b/src/main/java/com/massivecraft/factions/cmd/configsf/ConvertConfigHandler.java index f7fdc93a..d1ed08b7 100644 --- a/src/main/java/com/massivecraft/factions/cmd/configsf/ConvertConfigHandler.java +++ b/src/main/java/com/massivecraft/factions/cmd/configsf/ConvertConfigHandler.java @@ -17,6 +17,9 @@ import java.util.Arrays; import java.util.List; import java.util.logging.Level; +/** + * @author droppinganvil + */ public class ConvertConfigHandler { static File savageConfigFile = new File("plugins/Factions/SavageFactions/config.yml"); diff --git a/src/main/java/com/massivecraft/factions/cmd/wild/CmdWild.java b/src/main/java/com/massivecraft/factions/cmd/wild/CmdWild.java index b3d7bf1f..ee0498cb 100644 --- a/src/main/java/com/massivecraft/factions/cmd/wild/CmdWild.java +++ b/src/main/java/com/massivecraft/factions/cmd/wild/CmdWild.java @@ -10,6 +10,7 @@ import com.massivecraft.factions.cmd.CommandContext; import com.massivecraft.factions.cmd.CommandRequirements; import com.massivecraft.factions.cmd.FCommand; import com.massivecraft.factions.struct.Permission; +import com.massivecraft.factions.util.Wait.WaitedTask; import com.massivecraft.factions.zcore.util.TL; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -23,18 +24,21 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Random; -public class CmdWild extends FCommand { - public static HashMap waitingTeleport; +/** + * @author droppinganvil + */ +public class CmdWild extends FCommand implements WaitedTask { public static HashMap teleportRange; public static HashSet teleporting; + public static CmdWild instance; public CmdWild() { super(); + if (this.instance == null) instance = this; this.aliases.addAll(Aliases.wild); this.requirements = new CommandRequirements.Builder(Permission.WILD) .playerOnly() .build(); - waitingTeleport = new HashMap<>(); teleporting = new HashSet<>(); teleportRange = new HashMap<>(); startWild(); @@ -42,7 +46,7 @@ public class CmdWild extends FCommand { @Override public void perform(CommandContext context) { - if (!waitingTeleport.containsKey(context.player)) { + if (!teleportRange.containsKey(context.player)) { context.player.openInventory(new WildGUI(context.player, context.fPlayer).getInventory()); } else { context.fPlayer.msg(TL.COMMAND_WILD_WAIT); @@ -50,23 +54,6 @@ public class CmdWild extends FCommand { } public void startWild() { - Bukkit.getScheduler().scheduleSyncRepeatingTask(FactionsPlugin.instance, () -> { - for (Player p : waitingTeleport.keySet()) { - int i = waitingTeleport.get(p) - 1; - if (i > 0) { - if (i != 1) { - p.sendMessage(TL.COMMAND_WILD_WAIT.format((i + " Seconds"))); - } else { - p.sendMessage(TL.COMMAND_WILD_WAIT.format((i + " Second"))); - } - waitingTeleport.replace(p, i); - } else { - p.sendMessage(TL.COMMAND_WILD_SUCCESS.toString()); - waitingTeleport.remove(p); - attemptTeleport(p); - } - } - }, 0L, 20L); } public void attemptTeleport(Player p) { @@ -121,4 +108,14 @@ public class CmdWild extends FCommand { public TL getUsageTranslation() { return TL.COMMAND_WILD_DESCRIPTION; } + + @Override + public void handleSuccess(Player player) { + attemptTeleport(player); + } + + @Override + public void handleFailure(Player player) { + player.sendMessage(TL.COMMAND_WILD_INTERUPTED.toString()); + } } diff --git a/src/main/java/com/massivecraft/factions/cmd/wild/WildGUI.java b/src/main/java/com/massivecraft/factions/cmd/wild/WildGUI.java index ca36ddef..05e446f9 100644 --- a/src/main/java/com/massivecraft/factions/cmd/wild/WildGUI.java +++ b/src/main/java/com/massivecraft/factions/cmd/wild/WildGUI.java @@ -3,6 +3,8 @@ package com.massivecraft.factions.cmd.wild; import com.massivecraft.factions.FPlayer; import com.massivecraft.factions.FactionsPlugin; import com.massivecraft.factions.util.FactionGUI; +import com.massivecraft.factions.util.Wait.WaitExecutor; +import com.massivecraft.factions.util.Wait.WaitTask; import com.massivecraft.factions.util.XMaterial; import com.massivecraft.factions.zcore.util.TL; import org.bukkit.Bukkit; @@ -18,6 +20,10 @@ import java.util.HashMap; import java.util.List; import java.util.Objects; +/** + * @author droppinganvil + */ +//TODO StaticGUI public class WildGUI implements FactionGUI { Player player; FPlayer fplayer; @@ -35,7 +41,7 @@ public class WildGUI implements FactionGUI { if (map.containsKey(slot)) { String zone = map.get(slot); if (fplayer.hasMoney(FactionsPlugin.getInstance().getConfig().getInt("Wild.Zones." + zone + ".Cost"))) { - CmdWild.waitingTeleport.put(player, FactionsPlugin.getInstance().getConfig().getInt("Wild.Wait")); + WaitExecutor.taskMap.put(player, new WaitTask(FactionsPlugin.getInstance().getConfig().getInt("Wild.Wait"), TL.COMMAND_WILD_INPROGRESS, player, CmdWild.instance)); CmdWild.teleportRange.put(player, zone); fplayer.msg(TL.COMMAND_WILD_WAIT, FactionsPlugin.getInstance().getConfig().getInt("Wild.Wait") + " Seconds"); player.closeInventory(); diff --git a/src/main/java/com/massivecraft/factions/discord/Discord.java b/src/main/java/com/massivecraft/factions/discord/Discord.java index 38cc0f5e..ce9ba8dc 100644 --- a/src/main/java/com/massivecraft/factions/discord/Discord.java +++ b/src/main/java/com/massivecraft/factions/discord/Discord.java @@ -19,6 +19,9 @@ import java.util.HashSet; import java.util.Objects; import java.util.logging.Level; +/** + * @author droppinganvil + */ public class Discord { //We dont want waitingLink to reset during reload so we are going to set it here public static HashMap waitingLink; diff --git a/src/main/java/com/massivecraft/factions/discord/DiscordSetupAttempt.java b/src/main/java/com/massivecraft/factions/discord/DiscordSetupAttempt.java index 9426f6e5..0be3ed26 100644 --- a/src/main/java/com/massivecraft/factions/discord/DiscordSetupAttempt.java +++ b/src/main/java/com/massivecraft/factions/discord/DiscordSetupAttempt.java @@ -1,5 +1,8 @@ package com.massivecraft.factions.discord; +/** + * @author droppinganvil + */ public class DiscordSetupAttempt { private Boolean success; private String reason; diff --git a/src/main/java/com/massivecraft/factions/duels/Duel.java b/src/main/java/com/massivecraft/factions/duels/Duel.java new file mode 100644 index 00000000..f35acfda --- /dev/null +++ b/src/main/java/com/massivecraft/factions/duels/Duel.java @@ -0,0 +1,164 @@ +package com.massivecraft.factions.duels; + +import com.massivecraft.factions.FPlayer; +import com.massivecraft.factions.Faction; +import com.massivecraft.factions.FactionsPlugin; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * @author droppinganvil + */ +public class Duel { + private Faction faction1; + private Faction faction2; + private HashMap eliminationMap; + private HashMap oldLocMap; + + public Duel(Faction faction1, Faction faction2) { + this.faction1 = faction1; + this.faction2 = faction2; + this.eliminationMap = new HashMap<>(); + this.oldLocMap = new HashMap<>(); + } + + public Faction getFaction1() { + return faction1; + } + + public Faction getFaction2() { + return faction2; + } + + public Set getEliminated() { + Set list = new HashSet<>(); + for (Map.Entry entry : eliminationMap.entrySet()) { + if (entry.getValue()) list.add(entry.getKey()); + } + return list; + } + + public Set getEliminated(Faction faction) { + Set list = new HashSet<>(); + for (Map.Entry entry : eliminationMap.entrySet()) { + if (entry.getValue() && entry.getKey().getFaction().equals(faction)) list.add(entry.getKey()); + } + return list; + } + + public Set getRemaining() { + Set list = new HashSet<>(); + for (Map.Entry entry : eliminationMap.entrySet()) { + if (!entry.getValue()) list.add(entry.getKey()); + } + return list; + } + + public Set getRemaining(Faction faction) { + Set list = new HashSet<>(); + for (Map.Entry entry : eliminationMap.entrySet()) { + if (!entry.getValue() && entry.getKey().getFaction().equals(faction)) list.add(entry.getKey()); + } + return list; + } + + public void handleExit(FPlayer fplayer, Boolean death) { + if (death) { + eliminationMap.replace(fplayer, false, true); + fplayer.getPlayer().spigot().respawn(); + } + fplayer.getPlayer().teleport(oldLocMap.get(fplayer), PlayerTeleportEvent.TeleportCause.PLUGIN); + for (Map.Entry entry : fplayer.getOldInv().entrySet()) { + fplayer.getPlayer().getInventory().setItem(entry.getKey(), entry.getValue()); + } + fplayer.setOldInv(null); + fplayer.setInDuel(false); + if (getClearWinner() != null) { + handleEnd(); + } + } + + public void handleEnd() { + if (FactionsPlugin.getInstance().getConfig().getBoolean("Duels.Broadcast.BroadcastResult.Enabled")) { + Bukkit.broadcastMessage(getEndMessage()); + } + for (FPlayer fp : getRemaining()) { + handleExit(fp, false); + } + Map.Entry fvf = (Map.Entry) Duels.duelQueue.entrySet().toArray()[0]; + if (fvf != null) { + Duels.duel = new Duel(fvf.getKey(), fvf.getValue()); + Duels.preparingDuel = true; + Duels.acceptedDuel.clear(); + } + } + + public Faction getClearWinner() { + Set team1r = getRemaining(faction1); + Set team2r = getRemaining(faction2); + if (team1r.isEmpty() && !team2r.isEmpty()) return faction2; + if (team2r.isEmpty() && !team1r.isEmpty()) return faction1; + return null; + } + + private Faction getWinning(Set s1, Set s2) { + if (s1.size() > s2.size()) return faction1; + return faction2; + } + + public String getEndMessage() { + String temp; + Set team1r = getRemaining(faction1); + Set team2r = getRemaining(faction2); + if (team1r.size() == team2r.size()) { + //Tied + temp = FactionsPlugin.getInstance().getConfig().getString("Duels.Broadcast.BroadcastResult.FormatTie"); + } else { + temp = FactionsPlugin.getInstance().getConfig().getString("Duels.Broadcast.BroadcastResult.Format"); + } + Faction winner = getWinning(team1r, team2r); + return ChatColor.translateAlternateColorCodes('&', temp + .replace("{team1_tag}", faction1.getTag()) + .replace("{team2_tag}", faction2.getTag()) + .replace("{remaining}", String.valueOf(getRemaining(winner).size())) + .replace("{tag}", winner.getTag()) + .replace("{losers_tag}", winner == faction1 ? faction2.getTag() : faction1.getTag())); + } + + public String getDeathMessage(Player player) { + return ChatColor.translateAlternateColorCodes('&', + FactionsPlugin.getInstance().getConfig().getString("Duels.Broadcast.BroadcastKill.Format") + .replace("{team1_remaining}", String.valueOf(getRemaining(getFaction1()).size())) + .replace("{team2_remaining}", String.valueOf(getRemaining(getFaction2()).size())) + .replace("{team1_eliminated}", String.valueOf(getEliminated(getFaction1()).size())) + .replace("{team2_eliminated}", String.valueOf(getEliminated(getFaction2()).size())) + .replace("{team1_tag}", faction1.getTag()) + .replace("{team2_tag}", faction2.getTag()) + .replace("{eliminated_name}", player.getName())); + } + + public HashMap getLocMap() { + return oldLocMap; + } + + public void addFPlayer(FPlayer fp) { + oldLocMap.put(fp, fp.getPlayer().getLocation()); + eliminationMap.put(fp, false); + } + + public void start() { + Duels.preparingDuel = false; + for (FPlayer fplayer : Duels.acceptedDuel) { + + } + } +} diff --git a/src/main/java/com/massivecraft/factions/duels/Duels.java b/src/main/java/com/massivecraft/factions/duels/Duels.java new file mode 100644 index 00000000..7b7eb345 --- /dev/null +++ b/src/main/java/com/massivecraft/factions/duels/Duels.java @@ -0,0 +1,73 @@ +package com.massivecraft.factions.duels; + +import com.massivecraft.factions.FPlayer; +import com.massivecraft.factions.Faction; +import com.massivecraft.factions.FactionsPlugin; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.configuration.file.FileConfiguration; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author droppinganvil + */ +public class Duels { + public static boolean enabled = true; + public static long targetTimeout = Integer.toUnsignedLong(FactionsPlugin.getInstance().getConfig().getInt("Duels.WaitTime")) * 1000; + public static ConcurrentHashMap guiMap = new ConcurrentHashMap<>(); + public static HashSet acceptedDuel = new HashSet<>(); + public static HashMap duelQueue = new HashMap<>(); + public static Boolean preparingDuel = false; + public static Duel duel; + public static HashSet spawnPoints = new HashSet<>(); + + private static void startGUITask() { + Bukkit.getScheduler().scheduleAsyncRepeatingTask(FactionsPlugin.instance, () -> { + for (Map.Entry entry : guiMap.entrySet()) { + if (System.currentTimeMillis() - entry.getValue() <= targetTimeout) RequestGUI.closeSync(true, entry.getKey()); + } + }, 0L, 20L); + } + + public void setup() { + enabled = FactionsPlugin.getInstance().getConfig().getBoolean("Duels.Enabled", true); + if (enabled) startGUITask(); + loadSpawnPoints(); + + } + + public void loadSpawnPoints() { + spawnPoints.clear(); + loadTeamSpawnPoints(1); + loadTeamSpawnPoints(2); + } + + public void loadTeamSpawnPoints(Integer i) { + FileConfiguration config = FactionsPlugin.getInstance().getConfig(); + String team = "Team" + i; + for (String key : config.getConfigurationSection("Duels.SpawnPoints." + team).getKeys(false)) { + spawnPoints.add(new SpawnPoint (i, getSpawnPointLocation(team, key, config), Integer.parseInt(key))); + } + + } + + public Location getSpawnPointLocation(String team, String key, FileConfiguration config) { + String[] locKeyArray = config.getString("Duels.SpawnPoints." + team + "." + key).split(","); + return new Location(Bukkit.getWorld(locKeyArray[0]), Double.parseDouble(locKeyArray[1]), Double.parseDouble(locKeyArray[2]), Double.parseDouble(locKeyArray[3])); + } + + public void sendRequests() { + for (FPlayer fplayer : duel.getFaction1().getFPlayers()) { + if (fplayer.isOnline()) { + fplayer.getPlayer().openInventory(RequestGUI.inv); + guiMap.put(fplayer, System.currentTimeMillis()); + } + } + } + + +} diff --git a/src/main/java/com/massivecraft/factions/duels/RequestGUI.java b/src/main/java/com/massivecraft/factions/duels/RequestGUI.java new file mode 100644 index 00000000..8fb212ad --- /dev/null +++ b/src/main/java/com/massivecraft/factions/duels/RequestGUI.java @@ -0,0 +1,121 @@ +package com.massivecraft.factions.duels; + +import com.massivecraft.factions.FPlayer; +import com.massivecraft.factions.FPlayers; +import com.massivecraft.factions.FactionsPlugin; +import com.massivecraft.factions.util.StaticGUI; +import com.massivecraft.factions.util.Sync.SyncExecutor; +import com.massivecraft.factions.util.Sync.SyncTask; +import com.massivecraft.factions.util.XMaterial; +import com.massivecraft.factions.zcore.util.TL; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.logging.Level; + +/** + * @author droppinganvil + */ +public class RequestGUI implements StaticGUI { + private static ItemStack fillItem; + private static RequestResponse fallback; + FileConfiguration config = FactionsPlugin.getInstance().getConfig(); + public static HashMap responseMap = new HashMap<>(); + public static Inventory inv; + private static RequestGUI instance; + public static RequestGUI getInstance() { + return instance; + } + + public RequestGUI() { + if (instance == null) { + instance = this; + } + if (fillItem == null) { + fillItem = XMaterial.matchXMaterial(config.getString("Duels.GUI.Fill-Item.Material")).get().parseItem(); + ItemMeta fillMeta = fillItem.getItemMeta(); + fillMeta.setDisplayName(" "); + fillItem.setItemMeta(fillMeta); + } + if (fallback == null) { + try { + fallback = RequestResponse.valueOf(config.getString("Duels.GUI.Fill-Item.Response")); + } catch (EnumConstantNotPresentException e) { + FactionsPlugin.getInstance().getLogger().log(Level.WARNING, "FallbackResponse is invalid! Using Ignored"); + fallback = RequestResponse.Ignored; + } + } + if (inv == null) { + inv = Bukkit.createInventory(this, 27, "Join Faction Duel"); + int fi = 0; + while (fi != 26) { + inv.setItem(fi, fillItem); + fi++; + } + for (String key : config.getConfigurationSection("Duels.GUI.Items").getKeys(false)) { + ItemStack item = XMaterial.matchXMaterial(config.getString("Duels.GUI.Items." + key + ".Material")).get().parseItem(); + ItemMeta itemMeta = item.getItemMeta(); + itemMeta.setDisplayName(ChatColor.translateAlternateColorCodes('&', config.getString("Duels.GUI.Items." + key + ".Name"))); + List lore = new ArrayList<>(); + for (String loreEntry : config.getStringList("Duels.GUI.Items." + key + ".Lore")) { + lore.add(ChatColor.translateAlternateColorCodes('&', loreEntry)); + } + itemMeta.setLore(lore); + item.setItemMeta(itemMeta); + int slot = Integer.parseInt(key); + responseMap.put(slot, RequestResponse.valueOf(config.getString("Duels.GUI.Items." + key + ".Response"))); + inv.setItem(slot, item); + } + } + } + + @Override + public void click(int slot, ClickType action, Player player) { + FPlayer fPlayer = FPlayers.getInstance().getByPlayer(player); + if (action != ClickType.LEFT) { + processResponse(fallback, fPlayer); + } else { + processResponse(responseMap.get(slot), fPlayer); + } + } + + public static void processResponse(RequestResponse response, FPlayer fPlayer) { + switch (response) { + case Accepted: + fPlayer.getFaction().broadcast(TL.DUEL_REQUEST_ACCEPTED_PLAYER.format(fPlayer.getNameAndTitle())); + Duels.acceptedDuel.add(fPlayer); + close(false, fPlayer); + break; + case Rejected: + fPlayer.getFaction().broadcast(TL.DUEL_REQUEST_REJECTED_PLAYER.format(fPlayer.getNameAndTitle())); + close(false, fPlayer); + break; + } + } + + public static void close(Boolean reject, FPlayer fPlayer) { + if (reject) { + processResponse(RequestResponse.Rejected, fPlayer); + } + fPlayer.getPlayer().closeInventory(); + Duels.guiMap.remove(fPlayer); + } + + public static void closeSync(Boolean reject, FPlayer fPlayer) { + SyncExecutor.taskQueue.add(new SyncTask(RequestGUI.getInstance(), "close", reject, fPlayer)); + } + + @Override + public Inventory getInventory() { + return inv; + } +} diff --git a/src/main/java/com/massivecraft/factions/duels/RequestResponse.java b/src/main/java/com/massivecraft/factions/duels/RequestResponse.java new file mode 100644 index 00000000..5510dd22 --- /dev/null +++ b/src/main/java/com/massivecraft/factions/duels/RequestResponse.java @@ -0,0 +1,10 @@ +package com.massivecraft.factions.duels; + +/** + * @author droppinganvil + */ +public enum RequestResponse { + Accepted, + Rejected, + Ignored, +} diff --git a/src/main/java/com/massivecraft/factions/duels/SpawnPoint.java b/src/main/java/com/massivecraft/factions/duels/SpawnPoint.java new file mode 100644 index 00000000..0cba4345 --- /dev/null +++ b/src/main/java/com/massivecraft/factions/duels/SpawnPoint.java @@ -0,0 +1,27 @@ +package com.massivecraft.factions.duels; + +import org.bukkit.Location; + +public class SpawnPoint { + private Integer team; + private Integer id; + private Location location; + + public SpawnPoint(Integer team, Location location, Integer id) { + this.team = team; + this.location = location; + this.id = id; + } + + public Integer getTeam() { + return team; + } + + public Location getLocation() { + return location; + } + + public Integer getID() { + return id; + } +} diff --git a/src/main/java/com/massivecraft/factions/listeners/FactionsBlockListener.java b/src/main/java/com/massivecraft/factions/listeners/FactionsBlockListener.java index ccf0d509..4c375e71 100644 --- a/src/main/java/com/massivecraft/factions/listeners/FactionsBlockListener.java +++ b/src/main/java/com/massivecraft/factions/listeners/FactionsBlockListener.java @@ -493,39 +493,45 @@ public class FactionsBlockListener implements Listener { @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onBlockBreak(BlockBreakEvent event) { - Block block = event.getBlock(); + //If there is an error its much safer to not allow the block to be broken + try { + Block block = event.getBlock(); - Faction at = Board.getInstance().getFactionAt(new FLocation(block)); - boolean isSpawner = event.getBlock().getType().equals(XMaterial.SPAWNER.parseMaterial()); + Faction at = Board.getInstance().getFactionAt(new FLocation(block)); + boolean isSpawner = event.getBlock().getType().equals(XMaterial.SPAWNER.parseMaterial()); - if (!playerCanBuildDestroyBlock(event.getPlayer(), event.getBlock().getLocation(), "destroy", false)) { - event.setCancelled(true); - return; - } - - FPlayer fme = FPlayers.getInstance().getByPlayer(event.getPlayer()); - if (fme == null || !fme.hasFaction()) return; - - if (isSpawner) { - Access access = fme.getFaction().getAccess(fme, PermissableAction.SPAWNER); - if (access != Access.ALLOW && fme.getRole() != Role.LEADER) { - fme.msg(TL.GENERIC_FPERM_NOPERMISSION, "mine spawners"); + if (!playerCanBuildDestroyBlock(event.getPlayer(), event.getBlock().getLocation(), "destroy", false)) { + event.setCancelled(true); + return; } - } - if (isSpawner && !fme.isAdminBypassing()) { - ItemStack item = new ItemStack(block.getType(), 1, block.getData()); - if (at != null && at.isNormal()) { - FPlayer fplayer = FPlayers.getInstance().getByPlayer(event.getPlayer()); - if (fplayer != null) { - BlockState state = block.getState(); - if (state instanceof CreatureSpawner) { - CreatureSpawner spawner = (CreatureSpawner) state; - item.setDurability(spawner.getSpawnedType().getTypeId()); - } - handleSpawnerUpdate(at, event.getPlayer(), item, LogTimer.TimerSubType.SPAWNER_BREAK); + FPlayer fme = FPlayers.getInstance().getByPlayer(event.getPlayer()); + if (fme == null || !fme.hasFaction()) return; + + if (isSpawner) { + Access access = fme.getFaction().getAccess(fme, PermissableAction.SPAWNER); + if (access != Access.ALLOW && fme.getRole() != Role.LEADER) { + fme.msg(TL.GENERIC_FPERM_NOPERMISSION, "mine spawners"); } } + + if (isSpawner && !fme.isAdminBypassing()) { + ItemStack item = new ItemStack(block.getType(), 1, block.getData()); + if (at != null && at.isNormal()) { + FPlayer fplayer = FPlayers.getInstance().getByPlayer(event.getPlayer()); + if (fplayer != null) { + BlockState state = block.getState(); + if (state instanceof CreatureSpawner) { + CreatureSpawner spawner = (CreatureSpawner) state; + item.setDurability(spawner.getSpawnedType().getTypeId()); + } + handleSpawnerUpdate(at, event.getPlayer(), item, LogTimer.TimerSubType.SPAWNER_BREAK); + } + } + } + } catch (Exception e) { + event.setCancelled(true); + e.printStackTrace(); } } diff --git a/src/main/java/com/massivecraft/factions/listeners/FactionsPlayerListener.java b/src/main/java/com/massivecraft/factions/listeners/FactionsPlayerListener.java index ae77aaa6..275bc1d4 100644 --- a/src/main/java/com/massivecraft/factions/listeners/FactionsPlayerListener.java +++ b/src/main/java/com/massivecraft/factions/listeners/FactionsPlayerListener.java @@ -21,6 +21,7 @@ import com.massivecraft.factions.struct.Role; import com.massivecraft.factions.util.CC; import com.massivecraft.factions.util.FactionGUI; import com.massivecraft.factions.util.VisualizeUtil; +import com.massivecraft.factions.util.Wait.WaitExecutor; import com.massivecraft.factions.util.XMaterial; import com.massivecraft.factions.zcore.fperms.Access; import com.massivecraft.factions.zcore.fperms.PermissableAction; @@ -935,11 +936,7 @@ public class FactionsPlayerListener implements Listener { handler.cancelLogout(e.getPlayer()); e.getPlayer().sendMessage(String.valueOf(TL.COMMAND_LOGOUT_MOVED)); } - - if (CmdWild.waitingTeleport.containsKey(e.getPlayer())) { - CmdWild.waitingTeleport.remove(e.getPlayer()); - FPlayers.getInstance().getByPlayer(e.getPlayer()).msg(TL.COMMAND_WILD_INTERUPTED); - } + WaitExecutor.handleAction(e.getPlayer()); } @EventHandler @@ -951,10 +948,7 @@ public class FactionsPlayerListener implements Listener { handler.cancelLogout(player); player.sendMessage(String.valueOf(TL.COMMAND_LOGOUT_DAMAGE_TAKEN)); } - if (CmdWild.waitingTeleport.containsKey(player)) { - CmdWild.waitingTeleport.remove(player); - FPlayers.getInstance().getByPlayer(player).msg(TL.COMMAND_WILD_INTERUPTED); - } + WaitExecutor.handleAction(player); if (CmdWild.teleporting.contains(player)) { if (!FactionsPlugin.getInstance().getConfig().getBoolean("Wild.FallDamage") && e.getCause() == EntityDamageEvent.DamageCause.FALL) { e.setCancelled(true); diff --git a/src/main/java/com/massivecraft/factions/util/PermissionList.java b/src/main/java/com/massivecraft/factions/util/PermissionList.java index 3ffc1342..2d4208a5 100644 --- a/src/main/java/com/massivecraft/factions/util/PermissionList.java +++ b/src/main/java/com/massivecraft/factions/util/PermissionList.java @@ -8,6 +8,9 @@ import org.bukkit.configuration.file.YamlConfiguration; import java.io.File; import java.io.IOException; +/** + * @author droppinganvil + */ public class PermissionList { public static void generateFile() { File file = new File(FactionsPlugin.getInstance().getDataFolder().toString() + "/" + "permissions.yml"); diff --git a/src/main/java/com/massivecraft/factions/util/StaticGUI.java b/src/main/java/com/massivecraft/factions/util/StaticGUI.java new file mode 100644 index 00000000..d55b4f44 --- /dev/null +++ b/src/main/java/com/massivecraft/factions/util/StaticGUI.java @@ -0,0 +1,15 @@ +package com.massivecraft.factions.util; + +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.inventory.InventoryHolder; + +/** + * @author droppinganvil + * Inspired by FactionGUI with the difference being that this should be used for GUIs that will not differ between players + * Using StaticGUI and only generating one will not require generating one object per player unlike FactionGUI this will save many resources from being used. + * @see FactionGUI + */ +public interface StaticGUI extends InventoryHolder { + void click(int slot, ClickType action, Player player); +} diff --git a/src/main/java/com/massivecraft/factions/util/Sync/SyncExecutor.java b/src/main/java/com/massivecraft/factions/util/Sync/SyncExecutor.java new file mode 100644 index 00000000..03400bfe --- /dev/null +++ b/src/main/java/com/massivecraft/factions/util/Sync/SyncExecutor.java @@ -0,0 +1,35 @@ +package com.massivecraft.factions.util.Sync; + +import com.massivecraft.factions.FactionsPlugin; +import org.bukkit.Bukkit; + +import java.lang.reflect.InvocationTargetException; +import java.util.LinkedList; +import java.util.Queue; +import java.util.logging.Level; + +/** + * @author droppinganvil + */ +public class SyncExecutor { + private static boolean started = false; + /** + * This queue is used to collect task that need to happen async rather than scheduling for every action. + */ + public static Queue taskQueue = new LinkedList<>(); + + public static void startTask() { + if (started) return; + Bukkit.getScheduler().scheduleSyncRepeatingTask(FactionsPlugin.instance, () -> { + SyncTask syncTask = taskQueue.poll(); + if (syncTask != null) { + try { + syncTask.call(); + } catch (InvocationTargetException | IllegalAccessException e) { + e.printStackTrace(); + FactionsPlugin.getInstance().getLogger().log(Level.SEVERE, "A task was not able to execute successfully! Please provide this stacktrace to the Saber team at Discord.Saber.pw"); + } + } + }, 0L, 2L); + } +} diff --git a/src/main/java/com/massivecraft/factions/util/Sync/SyncTask.java b/src/main/java/com/massivecraft/factions/util/Sync/SyncTask.java new file mode 100644 index 00000000..3d664e01 --- /dev/null +++ b/src/main/java/com/massivecraft/factions/util/Sync/SyncTask.java @@ -0,0 +1,63 @@ +package com.massivecraft.factions.util.Sync; + +import com.massivecraft.factions.FactionsPlugin; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.logging.Level; + +/** + * @author droppinganvil + */ +public class SyncTask { + private Object o; + private Method m; + private Object[] arguments; + private Collection> params; + + public SyncTask(Object target, Method method, Object... arguments) { + this.o = target; + this.m = method; + this.arguments = arguments; + } + + /** + * Generate sync task without the ability to specify the Method object, made for easy typing + * @param target Object the method is on + * @param method Method name + * @param arguments Objects needed to call method + */ + public SyncTask(Object target, String method, Object... arguments) { + this.o = target; + this.arguments = arguments; + this.params = new ArrayList<>(); + for (Object o : this.arguments) { + this.params.add(o.getClass()); + } + try { + this.m = o.getClass().getMethod(method, (Class[]) params.toArray()); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + FactionsPlugin.getInstance().getLogger().log(Level.SEVERE, "A Method could not be located! This means a task that was supposed to happen did not, this may be a very serious issue"); + } + + } + /** + * Generate sync task without the ability to specify the Method object or arguments, made for easy typing + */ + public SyncTask(Object target, String method) { + this.o = target; + try { + this.m = o.getClass().getMethod(method); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + FactionsPlugin.getInstance().getLogger().log(Level.SEVERE, "A Method could not be located! This means a task that was supposed to happen did not, this may be a very serious issue"); + } + } + + public void call() throws InvocationTargetException, IllegalAccessException { + m.invoke(o, arguments); + } +} diff --git a/src/main/java/com/massivecraft/factions/util/Wait/WaitExecutor.java b/src/main/java/com/massivecraft/factions/util/Wait/WaitExecutor.java new file mode 100644 index 00000000..1ebc4191 --- /dev/null +++ b/src/main/java/com/massivecraft/factions/util/Wait/WaitExecutor.java @@ -0,0 +1,41 @@ +package com.massivecraft.factions.util.Wait; + +import com.massivecraft.factions.FactionsPlugin; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author droppinganvil + */ +public class WaitExecutor { + private static boolean enabled = false; + public static ConcurrentHashMap taskMap = new ConcurrentHashMap<>(); + + public static void startTask() { + if (enabled) return; + Bukkit.getScheduler().scheduleSyncRepeatingTask(FactionsPlugin.instance, () -> + { + for (WaitTask task : taskMap.values()) { + int i = task.getWait() - 1; + if (i > 0) { + if (i != 1) { + task.getPlayer().sendMessage(task.getMessage().format((i + " Seconds"))); + } else { + task.getPlayer().sendMessage(task.getMessage().format((i + " Second"))); + } + task.setWait(i); + } else { + task.success(); + taskMap.remove(task.getPlayer()); + } + } + },0L,20L); + enabled = true; + } + public static void handleAction(Player player) { + if (!taskMap.containsKey(player)) return; + taskMap.get(player).fail(); + taskMap.remove(player); + } +} diff --git a/src/main/java/com/massivecraft/factions/util/Wait/WaitTask.java b/src/main/java/com/massivecraft/factions/util/Wait/WaitTask.java new file mode 100644 index 00000000..5d528fdc --- /dev/null +++ b/src/main/java/com/massivecraft/factions/util/Wait/WaitTask.java @@ -0,0 +1,47 @@ +package com.massivecraft.factions.util.Wait; + +import com.massivecraft.factions.FPlayer; +import com.massivecraft.factions.zcore.util.TL; +import org.bukkit.entity.Player; + +/** + * @author droppinganvil + */ +public class WaitTask { + private Integer wait; + private TL msg; + //Using player as to not have to convert every event + private Player player; + private WaitedTask origin; + + public WaitTask(Integer wait, TL message, Player player, WaitedTask waitedTask) { + this.wait = wait; + this.msg = message; + this.player = player; + this.origin = waitedTask; + } + + public Integer getWait() { + return wait; + } + + public TL getMessage() { + return msg; + } + + public Player getPlayer() { + return player; + } + + public void setWait(Integer i) { + wait = i; + } + + public void success() { + origin.handleSuccess(player); + } + + public void fail() { + origin.handleFailure(player); + } +} diff --git a/src/main/java/com/massivecraft/factions/util/Wait/WaitedTask.java b/src/main/java/com/massivecraft/factions/util/Wait/WaitedTask.java new file mode 100644 index 00000000..aad23306 --- /dev/null +++ b/src/main/java/com/massivecraft/factions/util/Wait/WaitedTask.java @@ -0,0 +1,11 @@ +package com.massivecraft.factions.util.Wait; + +import org.bukkit.entity.Player; + +/** + * @author droppinganvil + */ +public interface WaitedTask { + void handleSuccess(Player player); + void handleFailure(Player player); +} diff --git a/src/main/java/com/massivecraft/factions/zcore/persist/MemoryFPlayer.java b/src/main/java/com/massivecraft/factions/zcore/persist/MemoryFPlayer.java index f45f2dc0..4208dbc1 100644 --- a/src/main/java/com/massivecraft/factions/zcore/persist/MemoryFPlayer.java +++ b/src/main/java/com/massivecraft/factions/zcore/persist/MemoryFPlayer.java @@ -32,6 +32,8 @@ import org.bukkit.*; import org.bukkit.command.CommandSender; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; import java.text.DecimalFormat; import java.util.*; @@ -47,6 +49,8 @@ import java.util.*; */ public abstract class MemoryFPlayer implements FPlayer { + public boolean inDuel = false; + public Map oldInv = null; public boolean enemiesNearby = false; public boolean inChest = false; public boolean discordSetup = false; @@ -226,6 +230,14 @@ public abstract class MemoryFPlayer implements FPlayer { public void setEnemiesNearby(Boolean b) {this.enemiesNearby = b;} + public Map getOldInv() {return this.oldInv;} + + public void setOldInv(Map inv) {this.oldInv = inv;} + + public void setInDuel(Boolean b) {this.inDuel = b;} + + public boolean isInDuel() {return this.inDuel;} + public boolean discordSetup() {return this.discordSetup;} public String discordUserID() {return this.discordUserID;} diff --git a/src/main/java/com/massivecraft/factions/zcore/persist/MemoryFaction.java b/src/main/java/com/massivecraft/factions/zcore/persist/MemoryFaction.java index dc6e0b0f..69efd61b 100644 --- a/src/main/java/com/massivecraft/factions/zcore/persist/MemoryFaction.java +++ b/src/main/java/com/massivecraft/factions/zcore/persist/MemoryFaction.java @@ -1429,4 +1429,10 @@ public abstract class MemoryFaction implements Faction, EconomyParticipator { public Set getAllClaims() { return Board.getInstance().getAllClaims(this); } + + public void broadcast(String s) { + for (FPlayer fPlayer : getFPlayersWhereOnline(true)) { + fPlayer.sendMessage(s); + } + } } diff --git a/src/main/java/com/massivecraft/factions/zcore/util/TL.java b/src/main/java/com/massivecraft/factions/zcore/util/TL.java index 343cde2e..a15cad9c 100644 --- a/src/main/java/com/massivecraft/factions/zcore/util/TL.java +++ b/src/main/java/com/massivecraft/factions/zcore/util/TL.java @@ -355,6 +355,12 @@ public enum TL { DISCORD_CODE_INVALID_KEY("That code is invalid, verify the code is correct."), DISCORD_CODE_INVALID_FORMAT("If you are submitting a code please only type the code. Example message: 0000"), + //Duels + DUEL_REQUEST_REJECTED_FACTION("&c&l[!]&7 %1$s has rejected your duel request"), + DUEL_REQUEST_REJECTED_PLAYER("&c&l[!]&7 %1$s has exited the duel queue"), + DUEL_REQUEST_ACCEPTED_FACTION("&c&l[!]&7 %1$s has accepted your duel request"), + DUEL_REQUEST_ACCEPTED_PLAYER("&c&l[!]&7 %1$s has entered the duel queue"), + COMMAND_DEINVITE_CANDEINVITE("&c&l[!]&7 Players you can &cdeinvite: "), COMMAND_DEINVITE_CLICKTODEINVITE("&c&l[!]&7 Click to &crevoke&7 invite for &c%1$s"), diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 7f873ee7..b4fe3c2f 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1439,6 +1439,61 @@ Wild: SpawnAboveBlocks: 10 Effects: - 'NIGHT_VISION' +Duels: + Enabled: true + # Cooldown before a faction can request another faction to duel after dueling + Cooldown: 0 + # In minutes + Time: 3 + # After individual players receive the request for the duel how long should it take to start in seconds + WaitTime: 60 + Broadcast: + BroadcastKill: + Enabled: true + # {team1_remaining} {team2_remaining} {team1_eliminated} {team2_eliminated} {team1_tag} {team2_tag} {eliminated_name} + Format: '&c&lFaction Duels&r &7{eliminated} has been eliminated. {team1_tag} &d{team1_remaining} &7| {team2_tag} &d{team2_remaining}' + BroadcastResult: + Enabled: true + # These placeholders are all for the winning team unless specified otherwise + # {remaining} {tag} {losers_tag} + Format: '&c&lFaction Duels&r &d{tag}&7 has defeated &d{losers_tag}&7 with {remaining} players remaining' + FormatTie: '&c&lFaction Duels&r &d{team1_tag} &7has tied with &d{team2_tag}' + + SpawnPoints: + # Please change these to your arena + Team1: + # Make sure each player has somewhere to spawn if max faction size is 10 you need 10 spawn points for each team! + # SpawnPoint format: World,X,Y,Z + 1: Duel,0,60,0 + 2: Duel,5,60,5 + 3: Duel,10,60,10 + Team2: + 1: Duel,50,50,50 + 2: Duel,55,50,55 + 3: Duel,60,50,60 + GUI: + # Responses # + # Accepted: This player is queued for the duel, GUI closes + # Rejected: This player is not included in the duel, GUI closes + # Ignored: This player remains in GUI and no action is taken on their entry in the duel + # This happens when the click type is not left click + FallbackResponse: Ignored + Fill-Item: + Material: GRAY_STAINED_GLASS_PANE + Response: Ignored + Items: + 12: + Material: GREEN_STAINED_GLASS_PANE + Name: '&2Accept' + Lore: + - '&2Join your faction''s duel' + Response: Accepted + 13: + Material: RED_STAINED_GLASS_PANE + Name: '&cReject' + Lore: + - '&cLeave your faction''s duels' + Response: Rejected ############################################################ # +------------------------------------------------------+ #