Compare commits

...

10 commits

Author SHA1 Message Date
grng
01aeefaaa3 add /tc <message>, fix scripts, increment version 2024-07-25 13:32:04 +01:00
grng
696e2b09aa /tc <message> doesnt work 2024-07-25 11:21:28 +01:00
grng
6dec52a317 /tc <message> doesnt work 2024-07-25 11:16:52 +01:00
grng
160677ff82 restyled team chat (buggy do not use) 2024-07-25 10:32:25 +01:00
grng
ec97defd6a a 2024-07-25 00:48:39 +01:00
grng
d10e195b35 done (untested code) 2024-07-25 00:41:36 +01:00
grng
08cfd82d09 code that i havnt tested, gn chat 2024-07-25 00:35:52 +01:00
hexlocation pc
fc27e415f4 feat: add team chat func + handle water bucket in court 2024-07-24 20:32:05 +02:00
hexlocation pc
55fc458c02 fix: court bug which kills yall & disable pvp 2024-07-24 19:32:22 +02:00
hexlocation pc
80cf17d14a Add discord bot support 2024-07-24 12:19:32 +02:00
23 changed files with 511 additions and 5 deletions

3
.idea/.gitignore generated vendored Normal file
View file

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

13
.idea/compiler.xml generated Normal file
View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="CivilCore" />
</profile>
</annotationProcessing>
</component>
</project>

7
.idea/discord.xml generated Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DiscordProjectSettings">
<option name="show" value="PROJECT_FILES" />
<option name="description" value="" />
</component>
</project>

7
.idea/encodings.xml generated Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

35
.idea/jarRepositories.xml generated Normal file
View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jitpack.io" />
<option name="name" value="jitpack.io" />
<option name="url" value="https://jitpack.io" />
</remote-repository>
<remote-repository>
<option name="id" value="spigot-repo" />
<option name="name" value="spigot-repo" />
<option name="url" value="https://hub.spigotmc.org/nexus/content/repositories/snapshots/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="papermc" />
<option name="name" value="papermc" />
<option name="url" value="https://repo.papermc.io/repository/maven-public/" />
</remote-repository>
</component>
</project>

12
.idea/misc.xml generated Normal file
View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="corretto-21" project-jdk-type="JavaSDK" />
</project>

8
.idea/modules.xml generated Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/CivilCore.iml" filepath="$PROJECT_DIR$/CivilCore.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

13
CivilCore.iml Normal file
View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="FacetManager">
<facet type="minecraft" name="Minecraft">
<configuration>
<autoDetectTypes>
<platformType>BUKKIT</platformType>
</autoDetectTypes>
<projectReimportVersion>1</projectReimportVersion>
</configuration>
</facet>
</component>
</module>

2
db.ps1 Normal file
View file

@ -0,0 +1,2 @@
docker-compose up
docker ps

8
docker-compose.yml Normal file
View file

@ -0,0 +1,8 @@
version: '3'
services:
civil_test_db:
environment:
- POSTGRES_PASSWORD=TEST123
image: postgres:16.3
ports:
- 5432:5432

18
pom.xml
View file

@ -13,7 +13,7 @@
<groupId>rip.iwakura</groupId> <groupId>rip.iwakura</groupId>
<artifactId>CivilCore</artifactId> <artifactId>CivilCore</artifactId>
<version>0.1a</version> <version>0.1c</version>
<repositories> <repositories>
<repository> <repository>
@ -24,9 +24,25 @@
<id>papermc</id> <id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url> <url>https://repo.papermc.io/repository/maven-public/</url>
</repository> </repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories> </repositories>
<dependencies> <dependencies>
<dependency>
<groupId>net.dv8tion</groupId>
<artifactId>JDA</artifactId>
<version>5.0.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>club.minnced</groupId>
<artifactId>discord-webhooks</artifactId>
<version>0.1.6</version>
<scope>compile</scope>
</dependency>
<dependency> <dependency>
<groupId>org.postgresql</groupId> <groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId> <artifactId>postgresql</artifactId>

9
run.ps1 Normal file
View file

@ -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 ..

View file

@ -2,20 +2,34 @@ package rip.iwakura.civilcore;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.WorldCreator;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import rip.iwakura.civilcore.commands.TeamChatCommand;
import rip.iwakura.civilcore.commands.TeamCommand; import rip.iwakura.civilcore.commands.TeamCommand;
import rip.iwakura.civilcore.discord.Discord;
public class CivilCore extends JavaPlugin { public class CivilCore extends JavaPlugin {
public Database db = new Database(); public Database db = new Database();
public Discord bot;
public ArrayList<Player> teamChatRegister = new ArrayList<>();
@Override @Override
public void onEnable() { public void onEnable() {
this.saveDefaultConfig(); 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 { try {
Class.forName("org.postgresql.Driver"); Class.forName("org.postgresql.Driver");
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
@ -28,6 +42,9 @@ public class CivilCore extends JavaPlugin {
e.printStackTrace(); e.printStackTrace();
} }
getServer().getPluginManager().registerEvents(new PlayerHandler(this), this); getServer().getPluginManager().registerEvents(new PlayerHandler(this), this);
getServer().getPluginManager().registerEvents(new Court(), this);
this.getCommand("team").setExecutor(new TeamCommand(this)); this.getCommand("team").setExecutor(new TeamCommand(this));
this.getCommand("court").setExecutor(new Court());
this.getCommand("tc").setExecutor(new TeamChatCommand(this));
} }
} }

View file

@ -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;
}
}

View file

@ -82,6 +82,17 @@ public class Database {
rs.close(); rs.close();
} }
public List<DbPlayer> getPlayersInTeam(String team_name) {
List<DbPlayer> 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 { public void refreshPlayers() throws SQLException {
players.clear(); 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 " + 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 " +

View file

@ -1,6 +1,7 @@
package rip.iwakura.civilcore; package rip.iwakura.civilcore;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -14,7 +15,7 @@ import rip.iwakura.civilcore.types.DbPlayer;
public class PlayerHandler implements Listener { public class PlayerHandler implements Listener {
private CivilCore civilCore = null; private final CivilCore civilCore;
public PlayerHandler(CivilCore civilCore) { public PlayerHandler(CivilCore civilCore) {
this.civilCore = civilCore; this.civilCore = civilCore;
@ -26,12 +27,34 @@ public class PlayerHandler implements Listener {
Player p = e.getPlayer(); Player p = e.getPlayer();
String player_name = p.getName(); String player_name = p.getName();
boolean inTeamChat = civilCore.teamChatRegister.contains(p);
DbPlayer dbPlayer = civilCore.db.getPlayerByName(player_name); 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());
Bukkit.broadcastMessage(String.format("%s%s: %s", prefix, player_name, e.getMessage())); if (!inTeamChat) {
Bukkit.broadcastMessage(message);
civilCore.bot.sendMessage(e);
} else {
List<DbPlayer> 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);
} }
}
}
@EventHandler @EventHandler
public void PlayerJoinEvent(PlayerJoinEvent e) { public void PlayerJoinEvent(PlayerJoinEvent e) {
Player p = e.getPlayer(); Player p = e.getPlayer();

View file

@ -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<DbPlayer> 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;
}
}

View file

@ -22,7 +22,7 @@ public class TeamCommand implements CommandExecutor {
@Override @Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { 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) { if(args.length == 0) {
sender.sendMessage(ChatColor.RED + "Usage: /team <create | destroy | add | remove | list> [options]"); sender.sendMessage(ChatColor.RED + "Usage: /team <create | destroy | add | remove | list> [options]");

View file

@ -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<GatewayIntent> 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();
});
}
}
}

View file

@ -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()));
}
}

View file

@ -0,0 +1,7 @@
package rip.iwakura.civilcore.types;
import org.bukkit.command.CommandSender;
public interface CoreCommand {
public boolean run(CommandSender sender);
}

View file

@ -8,3 +8,10 @@ commands:
team: team:
description: Team Manager description: Team Manager
usage: /<command> <create|destroy|add|remove> usage: /<command> <create|destroy|add|remove>
permission: "civil.teams"
court:
description: Court Manager
usage: /<command> <enter|leave>
tc:
description: Team Chat
usage: /<command>