diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..22d1c4f
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/discord.xml b/.idea/discord.xml
new file mode 100644
index 0000000..d8e9561
--- /dev/null
+++ b/.idea/discord.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..aa00ffa
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 0000000..680f95b
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..6546f14
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..787efec
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CivilCore.iml b/CivilCore.iml
new file mode 100644
index 0000000..9cb719e
--- /dev/null
+++ b/CivilCore.iml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ BUKKIT
+
+ 1
+
+
+
+
\ No newline at end of file
diff --git a/db.ps1 b/db.ps1
new file mode 100644
index 0000000..01fddb1
--- /dev/null
+++ b/db.ps1
@@ -0,0 +1,2 @@
+docker-compose up
+docker ps
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..5765784
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,8 @@
+version: '3'
+services:
+ civil_test_db:
+ environment:
+ - POSTGRES_PASSWORD=TEST123
+ image: postgres:16.3
+ ports:
+ - 5432:5432
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 8fd1e50..976bac2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -13,7 +13,7 @@
rip.iwakura
CivilCore
- 0.1a
+ 0.1c
@@ -24,9 +24,25 @@
papermc
https://repo.papermc.io/repository/maven-public/
+
+ jitpack.io
+ https://jitpack.io
+
+
+ net.dv8tion
+ JDA
+ 5.0.1
+ compile
+
+
+ club.minnced
+ discord-webhooks
+ 0.1.6
+ compile
+
org.postgresql
postgresql
diff --git a/run.ps1 b/run.ps1
new file mode 100644
index 0000000..3260453
--- /dev/null
+++ b/run.ps1
@@ -0,0 +1,9 @@
+if (-not (Test-Path -Path test_server)) {
+ New-Item -ItemType Directory -Path test_server
+}
+
+# mvn clean package dependency:copy -DskipTests -DoutputDirectory=test_server/plugins
+
+Set-Location -Path test_server
+java -jar server.jar -nogui
+Set-Location -Path ..
\ No newline at end of file
diff --git a/src/main/java/rip/iwakura/civilcore/CivilCore.java b/src/main/java/rip/iwakura/civilcore/CivilCore.java
index efa312e..137e0e1 100644
--- a/src/main/java/rip/iwakura/civilcore/CivilCore.java
+++ b/src/main/java/rip/iwakura/civilcore/CivilCore.java
@@ -2,20 +2,34 @@ package rip.iwakura.civilcore;
import java.sql.SQLException;
import java.util.ArrayList;
+import java.util.HashMap;
import org.bukkit.Bukkit;
+import org.bukkit.WorldCreator;
+import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import net.md_5.bungee.api.ChatColor;
+import rip.iwakura.civilcore.commands.TeamChatCommand;
import rip.iwakura.civilcore.commands.TeamCommand;
+import rip.iwakura.civilcore.discord.Discord;
public class CivilCore extends JavaPlugin {
public Database db = new Database();
+ public Discord bot;
+ public ArrayList teamChatRegister = new ArrayList<>();
@Override
public void onEnable() {
this.saveDefaultConfig();
+ this.bot = new Discord(getConfig().getString("discord.token"), getConfig().getString("discord.channel"), this);
+ this.bot.initialize();
+
+ if (Bukkit.getWorld("court") == null) {
+ new WorldCreator("court").createWorld();
+ }
+
try {
Class.forName("org.postgresql.Driver");
} catch (ClassNotFoundException e) {
@@ -28,6 +42,9 @@ public class CivilCore extends JavaPlugin {
e.printStackTrace();
}
getServer().getPluginManager().registerEvents(new PlayerHandler(this), this);
+ getServer().getPluginManager().registerEvents(new Court(), this);
this.getCommand("team").setExecutor(new TeamCommand(this));
+ this.getCommand("court").setExecutor(new Court());
+ this.getCommand("tc").setExecutor(new TeamChatCommand(this));
}
}
diff --git a/src/main/java/rip/iwakura/civilcore/Court.java b/src/main/java/rip/iwakura/civilcore/Court.java
new file mode 100644
index 0000000..b0e723a
--- /dev/null
+++ b/src/main/java/rip/iwakura/civilcore/Court.java
@@ -0,0 +1,89 @@
+package rip.iwakura.civilcore;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.World;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.block.BlockBreakEvent;
+import org.bukkit.event.block.BlockBurnEvent;
+import org.bukkit.event.block.BlockPlaceEvent;
+import org.bukkit.event.block.BlockSpreadEvent;
+import org.bukkit.event.entity.EntityDamageEvent;
+import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
+import org.bukkit.event.player.PlayerBucketEmptyEvent;
+
+public class Court implements Listener,CommandExecutor{
+ private World court_world = Bukkit.getWorld("court");
+
+ private Location court_location = new Location(court_world, 4.5, 70.0, 0.5, -90f, 1.0f);
+
+ @EventHandler
+ public void PlayerPlaceWater(PlayerBucketEmptyEvent e) {
+ Player p = (Player) e.getPlayer();
+ if (!p.getLocation().getWorld().getName().equals("court")) return;
+ e.setCancelled(true);
+ }
+
+ @EventHandler
+ public void PlayerHurt(EntityDamageEvent e) {
+ if (!(e.getEntity() instanceof Player)) return;
+ Player p = (Player) e.getEntity();
+
+ if (!p.getLocation().getWorld().getName().equals("court")) return;
+
+ e.setCancelled(true);
+
+ if (e.getCause() == DamageCause.VOID) {
+ p.teleport(court_location);
+ return;
+ }
+ }
+
+ @EventHandler
+ public void PlayerBreak(BlockBreakEvent e) {
+ if (e.getPlayer().isOp()) return;
+ if (e.getBlock().getWorld().getName().equals(court_world.getName())) e.setCancelled(true);
+ }
+ @EventHandler
+ public void PlayerPlace(BlockPlaceEvent e) {
+ if (e.getPlayer().isOp()) return;
+ if (e.getBlock().getWorld().getName().equals(court_world.getName())) e.setCancelled(true);
+ }
+ @EventHandler
+ public void PlayerBurn(BlockBurnEvent e) {
+ if (e.getBlock().getWorld().getName().equals(court_world.getName())) e.setCancelled(true);
+ }
+ @EventHandler
+ public void FireSpread(BlockSpreadEvent e) {
+ if (e.getBlock().getWorld().getName().equals(court_world.getName())) e.setCancelled(true);
+ }
+ @Override
+ public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
+ if (!(sender instanceof Player)) {
+ sender.sendMessage("Non-player type.");
+ return true;
+ }
+
+ Player p = (Player) sender;
+
+ switch (args[0]) {
+ case "enter":
+ p.teleport(court_location);
+ break;
+ case "leave":
+ Location loc = p.getBedSpawnLocation();
+ if (loc == null) {
+ loc = Bukkit.getWorld("world").getSpawnLocation();
+ }
+ p.teleport(loc);
+ break;
+ }
+ return true;
+ }
+
+}
diff --git a/src/main/java/rip/iwakura/civilcore/Database.java b/src/main/java/rip/iwakura/civilcore/Database.java
index 4fe2e86..044fc05 100644
--- a/src/main/java/rip/iwakura/civilcore/Database.java
+++ b/src/main/java/rip/iwakura/civilcore/Database.java
@@ -82,6 +82,17 @@ public class Database {
rs.close();
}
+ public List getPlayersInTeam(String team_name) {
+ List filtered = players.stream().filter(
+ s -> {
+ if (s.team.name == null) return false;
+ return s.team.name.equals(team_name);
+ })
+ .collect(Collectors.toList());
+ if(filtered.isEmpty()) return null;
+ return filtered;
+ }
+
public void refreshPlayers() throws SQLException {
players.clear();
String sql = "SELECT players.NAME AS player_name, teams.NAME AS team_name, teams.TEAM_ID as t_team_id, teams.PREFIX AS team_prefix " +
diff --git a/src/main/java/rip/iwakura/civilcore/PlayerHandler.java b/src/main/java/rip/iwakura/civilcore/PlayerHandler.java
index 15cc9ac..e2eebb2 100644
--- a/src/main/java/rip/iwakura/civilcore/PlayerHandler.java
+++ b/src/main/java/rip/iwakura/civilcore/PlayerHandler.java
@@ -1,6 +1,7 @@
package rip.iwakura.civilcore;
import java.sql.SQLException;
+import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@@ -14,7 +15,7 @@ import rip.iwakura.civilcore.types.DbPlayer;
public class PlayerHandler implements Listener {
- private CivilCore civilCore = null;
+ private final CivilCore civilCore;
public PlayerHandler(CivilCore civilCore) {
this.civilCore = civilCore;
@@ -26,12 +27,34 @@ public class PlayerHandler implements Listener {
Player p = e.getPlayer();
String player_name = p.getName();
+ boolean inTeamChat = civilCore.teamChatRegister.contains(p);
DbPlayer dbPlayer = civilCore.db.getPlayerByName(player_name);
- String prefix = dbPlayer.team.prefix == null ? "" : ChatColor.translateAlternateColorCodes('&', dbPlayer.team.prefix) + " ";
+ String prefix = (inTeamChat ? ChatColor.DARK_GREEN + "TEAM >" + ChatColor.RESET : "")
+ + (dbPlayer.team.prefix == null ?
+ "" :
+ ChatColor.translateAlternateColorCodes('&', inTeamChat ? "" : "[" + dbPlayer.team.prefix + "]"));
+ String message = String.format("%s %s: %s", prefix, player_name, e.getMessage());
+
+ if (!inTeamChat) {
+ Bukkit.broadcastMessage(message);
+ civilCore.bot.sendMessage(e);
+ } else {
+ List players = civilCore.db.getPlayersInTeam(dbPlayer.team.name);
+
+ if (players == null) return;
+
+ for (DbPlayer team_dbp : players) {
+ Player team_p = Bukkit.getPlayer(team_dbp.name);
+
+ if (team_p == null) continue;
+
+ team_p.sendMessage(message);
+ }
+ }
- Bukkit.broadcastMessage(String.format("%s%s: %s", prefix, player_name, e.getMessage()));
}
+
@EventHandler
public void PlayerJoinEvent(PlayerJoinEvent e) {
Player p = e.getPlayer();
diff --git a/src/main/java/rip/iwakura/civilcore/commands/TeamChatCommand.java b/src/main/java/rip/iwakura/civilcore/commands/TeamChatCommand.java
new file mode 100644
index 0000000..33b66e1
--- /dev/null
+++ b/src/main/java/rip/iwakura/civilcore/commands/TeamChatCommand.java
@@ -0,0 +1,83 @@
+package rip.iwakura.civilcore.commands;
+
+import org.bukkit.Bukkit;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+import net.md_5.bungee.api.ChatColor;
+import org.jetbrains.annotations.NotNull;
+import rip.iwakura.civilcore.Utils;
+import rip.iwakura.civilcore.CivilCore;
+import rip.iwakura.civilcore.types.DbPlayer;
+
+import java.util.List;
+import java.util.Objects;
+
+public class TeamChatCommand implements CommandExecutor {
+ private final CivilCore civilCore;
+
+ public TeamChatCommand(CivilCore civilCore) {
+ this.civilCore = civilCore;
+ }
+
+ private String getPrefix(String name, boolean inTeamChat) {
+ DbPlayer dbPlayer = civilCore.db.getPlayerByName(name);
+ return (inTeamChat ? ChatColor.DARK_GREEN + "TEAM >" + ChatColor.RESET : "")
+ + (dbPlayer.team.prefix == null ?
+ "" :
+ ChatColor.translateAlternateColorCodes('&', inTeamChat ? "" : "[" + dbPlayer.team.prefix + "]"));
+ }
+
+ @Override
+ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
+ args = Objects.requireNonNull(Utils.parser(args)).toArray(new String[0]);
+
+ if (!(sender instanceof Player p)) return true;
+ if (args.length == 0) {
+ if (civilCore.teamChatRegister.contains(p)) {
+ civilCore.teamChatRegister.remove(p);
+ p.sendMessage(ChatColor.GREEN + "You are now in the " + ChatColor.GOLD + "GLOBAL" + ChatColor.GREEN + " channel");
+ } else {
+ civilCore.teamChatRegister.add(p);
+ p.sendMessage(ChatColor.GREEN + "You are now in the " + ChatColor.GOLD + "TEAM" + ChatColor.GREEN + " channel");
+ }
+ return true;
+ }
+
+ String message = String.join(" ", args);
+
+ String player_name = p.getName();
+ boolean inTeamChat = civilCore.teamChatRegister.contains(p);
+
+ DbPlayer dbPlayer = civilCore.db.getPlayerByName(player_name);
+
+ if (inTeamChat) {
+ // Send message to global chat
+ civilCore.teamChatRegister.remove(p);
+ String formatted = String.format("%s%s: %s", getPrefix(player_name, false) + " ", player_name, message);
+ Bukkit.broadcastMessage(formatted);
+ civilCore.bot.sendMessage(p, message);
+ civilCore.teamChatRegister.add(p);
+ } else {
+ // Send message to team chat
+ civilCore.teamChatRegister.add(p);
+ String formatted = String.format("%s%s: %s", getPrefix(player_name, true) + " ", player_name, message);
+ List players = civilCore.db.getPlayersInTeam(dbPlayer.team.name);
+
+ if (players == null) return true;
+
+ for (DbPlayer team_dbp : players) {
+ Player team_p = Bukkit.getPlayer(team_dbp.name);
+
+ if (team_p == null) continue;
+
+ team_p.sendMessage(formatted);
+ }
+ civilCore.teamChatRegister.remove(p);
+ }
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/rip/iwakura/civilcore/commands/TeamCommand.java b/src/main/java/rip/iwakura/civilcore/commands/TeamCommand.java
index 0e1a282..66040dd 100644
--- a/src/main/java/rip/iwakura/civilcore/commands/TeamCommand.java
+++ b/src/main/java/rip/iwakura/civilcore/commands/TeamCommand.java
@@ -22,7 +22,7 @@ public class TeamCommand implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
- args = Utils.parser(args).toArray(new String[args.length]);
+ args = Utils.parser(args).toArray(new String[0]);
if(args.length == 0) {
sender.sendMessage(ChatColor.RED + "Usage: /team [options]");
diff --git a/src/main/java/rip/iwakura/civilcore/discord/Discord.java b/src/main/java/rip/iwakura/civilcore/discord/Discord.java
new file mode 100644
index 0000000..be440ee
--- /dev/null
+++ b/src/main/java/rip/iwakura/civilcore/discord/Discord.java
@@ -0,0 +1,108 @@
+package rip.iwakura.civilcore.discord;
+
+import java.util.EnumSet;
+
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.event.player.AsyncPlayerChatEvent;
+import org.checkerframework.common.reflection.qual.GetConstructor;
+
+import club.minnced.discord.webhook.WebhookClient;
+import club.minnced.discord.webhook.WebhookClientBuilder;
+import club.minnced.discord.webhook.send.WebhookMessage;
+import club.minnced.discord.webhook.send.WebhookMessageBuilder;
+import net.dv8tion.jda.api.JDA;
+import net.dv8tion.jda.api.JDABuilder;
+import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
+import net.dv8tion.jda.api.hooks.ListenerAdapter;
+import net.dv8tion.jda.api.requests.GatewayIntent;
+import rip.iwakura.civilcore.CivilCore;
+import net.dv8tion.jda.api.events.session.ReadyEvent;
+import rip.iwakura.civilcore.types.DbPlayer;
+
+public class Discord extends ListenerAdapter {
+ private String token;
+ private CivilCore civilCore;
+
+ public String channelId;
+
+ private TextChannel channel;
+ private WebhookClient webhook;
+
+ public JDA bot;
+
+ private EnumSet intents = EnumSet.of(GatewayIntent.GUILD_MESSAGES, GatewayIntent.MESSAGE_CONTENT, GatewayIntent.GUILD_WEBHOOKS);
+
+ public Discord(String token, String channelId, CivilCore civilCore){
+ this.civilCore = civilCore;
+ this.channelId = channelId;
+ this.token = token;
+ }
+
+ public void initialize() {
+ this.bot = JDABuilder.createLight(token, intents)
+ .addEventListeners(new MessageReceive(civilCore))
+ .addEventListeners(this)
+ .build();
+ }
+
+ public void sendMessage(AsyncPlayerChatEvent e) {
+ DbPlayer dbPlayer = civilCore.db.getPlayerByName(e.getPlayer().getName());
+ String prefix = (dbPlayer.team.prefix == null ?
+ "" :
+ "[" + dbPlayer.team.prefix + "]");
+
+ String player_name = e.getPlayer().getName();
+ WebhookMessageBuilder builder = new WebhookMessageBuilder()
+ .setUsername(prefix + " " + player_name)
+ .setAvatarUrl(String.format("https://mc-heads.net/avatar/%s", player_name))
+ .setContent(e.getMessage());
+ webhook.send(builder.build());
+ //channel.sendMessage(String.format("%s: %s", e.getPlayer().getName(), e.getMessage())).complete();
+ }
+
+ public void sendMessage(Player p, String message) {
+ DbPlayer dbPlayer = civilCore.db.getPlayerByName(p.getName());
+ String prefix = (dbPlayer.team.prefix == null ?
+ "" :
+ "[" + dbPlayer.team.prefix + "]");
+
+ WebhookMessageBuilder builder = new WebhookMessageBuilder()
+ .setUsername(prefix + " " + p.getName())
+ .setAvatarUrl(String.format("https://mc-heads.net/avatar/%s", p.getName()))
+ .setContent(message);
+ webhook.send(builder.build());
+ //channel.sendMessage(String.format("%s: %s", p.getName(), message)).complete();
+ }
+
+ public void sendMessage(String message, String name) {
+ DbPlayer dbPlayer = civilCore.db.getPlayerByName(name);
+ String prefix = (dbPlayer.team.prefix == null ?
+ "" :
+ "[" + dbPlayer.team.prefix + "]");
+
+ WebhookMessageBuilder builder = new WebhookMessageBuilder()
+ .setUsername(prefix + " " + name)
+ .setAvatarUrl(String.format("https://mc-heads.net/avatar/%s", name))
+ .setContent(message);
+ webhook.send(builder.build());
+ //channel.sendMessage(String.format("%s: %s", name, message)).complete();
+ }
+
+ @Override
+ public void onReady(ReadyEvent e) {
+ this.channel = bot.getTextChannelById(channelId);
+ String webhook_token = civilCore.getConfig().getString("discord.webhook.token");
+ if (webhook_token != null) {
+ Long webhook_id = Long.parseLong(civilCore.getConfig().getString("discord.webhook.id"));
+ this.webhook = new WebhookClientBuilder(webhook_id, webhook_token).build();
+ } else {
+ channel.createWebhook("CivilBot").queue((hook) -> {
+ civilCore.getConfig().set("discord.webhook.id", hook.getId());
+ civilCore.getConfig().set("discord.webhook.token", hook.getToken());
+ civilCore.saveConfig();
+ this.webhook = new WebhookClientBuilder(Long.parseLong(hook.getId()), hook.getToken()).build();
+ });
+ }
+ }
+}
diff --git a/src/main/java/rip/iwakura/civilcore/discord/MessageReceive.java b/src/main/java/rip/iwakura/civilcore/discord/MessageReceive.java
new file mode 100644
index 0000000..a2a5617
--- /dev/null
+++ b/src/main/java/rip/iwakura/civilcore/discord/MessageReceive.java
@@ -0,0 +1,22 @@
+package rip.iwakura.civilcore.discord;
+
+import org.bukkit.Bukkit;
+
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
+import net.dv8tion.jda.api.hooks.ListenerAdapter;
+import net.md_5.bungee.api.ChatColor;
+import rip.iwakura.civilcore.CivilCore;
+
+public class MessageReceive extends ListenerAdapter {
+ private CivilCore civilCore;
+
+ public MessageReceive (CivilCore civilCore) {
+ this.civilCore = civilCore;
+ }
+
+ @Override
+ public void onMessageReceived(MessageReceivedEvent e) {
+ if (e.getAuthor().isBot()) return;
+ if(e.getChannel().getId().equals(civilCore.bot.channelId)) Bukkit.broadcastMessage(String.format("%sDiscord%s %s: %s", ChatColor.BLUE, ChatColor.RESET, e.getAuthor().getName(), e.getMessage()));
+ }
+}
diff --git a/src/main/java/rip/iwakura/civilcore/types/CoreCommand.java b/src/main/java/rip/iwakura/civilcore/types/CoreCommand.java
new file mode 100644
index 0000000..9f24454
--- /dev/null
+++ b/src/main/java/rip/iwakura/civilcore/types/CoreCommand.java
@@ -0,0 +1,7 @@
+package rip.iwakura.civilcore.types;
+
+import org.bukkit.command.CommandSender;
+
+public interface CoreCommand {
+ public boolean run(CommandSender sender);
+}
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 7152f6b..529c1fa 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -8,3 +8,10 @@ commands:
team:
description: Team Manager
usage: /
+ permission: "civil.teams"
+ court:
+ description: Court Manager
+ usage: /
+ tc:
+ description: Team Chat
+ usage: /