GUI Shenanigans.
This commit is contained in:
parent
22a30c07f9
commit
ad81617b73
10
pom.xml
10
pom.xml
|
@ -55,6 +55,10 @@
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>codemc-releases</id>
|
||||||
|
<url>https://repo.codemc.io/repository/maven-releases/</url>
|
||||||
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>papermc-repo</id>
|
<id>papermc-repo</id>
|
||||||
<url>https://papermc.io/repo/repository/maven-public/</url>
|
<url>https://papermc.io/repo/repository/maven-public/</url>
|
||||||
|
@ -83,6 +87,12 @@
|
||||||
<artifactId>mysql-connector-java</artifactId>
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
<version>8.0.28</version>
|
<version>8.0.28</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>xyz.janboerman</groupId>
|
||||||
|
<artifactId>GuiLib-Plugin</artifactId>
|
||||||
|
<version>1.11.9</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>webukkit</groupId>
|
<groupId>webukkit</groupId>
|
||||||
<artifactId>dynmap</artifactId>
|
<artifactId>dynmap</artifactId>
|
||||||
|
|
|
@ -9,6 +9,8 @@ import net.luckperms.api.LuckPerms;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
import static moe.oko.opennaw.util.CommandHelper.info;
|
import static moe.oko.opennaw.util.CommandHelper.info;
|
||||||
|
|
||||||
public final class OpenNAW extends JavaPlugin {
|
public final class OpenNAW extends JavaPlugin {
|
||||||
|
@ -45,6 +47,11 @@ public final class OpenNAW extends JavaPlugin {
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
// Initialize internals
|
// Initialize internals
|
||||||
instance = this;
|
instance = this;
|
||||||
|
if(!new File(getDataFolder().getAbsolutePath() + "/config.yml").exists()) {
|
||||||
|
saveDefaultConfig(); //Default if non existent.
|
||||||
|
} else {
|
||||||
|
saveConfig(); //Save cfg on start
|
||||||
|
}
|
||||||
configHelper = new ConfigHelper(this.getConfig());
|
configHelper = new ConfigHelper(this.getConfig());
|
||||||
this.luckPerms = getServer().getServicesManager().load(LuckPerms.class);
|
this.luckPerms = getServer().getServicesManager().load(LuckPerms.class);
|
||||||
actionLogger = new ActionLogger();
|
actionLogger = new ActionLogger();
|
||||||
|
|
|
@ -2,6 +2,7 @@ package moe.oko.opennaw.command;
|
||||||
|
|
||||||
import moe.oko.opennaw.OpenNAW;
|
import moe.oko.opennaw.OpenNAW;
|
||||||
import moe.oko.opennaw.util.CommandHelper;
|
import moe.oko.opennaw.util.CommandHelper;
|
||||||
|
import moe.oko.opennaw.util.GuiHelper;
|
||||||
import org.bukkit.command.Command;
|
import org.bukkit.command.Command;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.command.TabExecutor;
|
import org.bukkit.command.TabExecutor;
|
||||||
|
@ -50,20 +51,12 @@ public class NationCommand implements TabExecutor {
|
||||||
sender.sendMessage("Removed nation " + args[1]);
|
sender.sendMessage("Removed nation " + args[1]);
|
||||||
}
|
}
|
||||||
case "join" -> {
|
case "join" -> {
|
||||||
var player = args.length < 3
|
if(!(sender instanceof Player)) {
|
||||||
? ((Player) sender).getPlayer()
|
sender.sendMessage("This is an in-game command only.");
|
||||||
: OpenNAW.getInstance().getServer().getOfflinePlayer(args[2]);
|
|
||||||
|
|
||||||
if (!player.hasPlayedBefore()) {
|
|
||||||
sender.sendMessage(args[2] + " has never joined the server");
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
var player = (Player) sender;
|
||||||
var group = OpenNAW.getInstance().getNationHandler().getNationGroup(args[1]);
|
player.openInventory(GuiHelper.getJoinMenu().getInventory());
|
||||||
OpenNAW.getInstance().getNationHandler().getNationByName(args[1]).getPlayerMap().put(player.getUniqueId(), player);
|
|
||||||
OpenNAW.getInstance().getGroupHandler().addPlayerToGroup(player, group);
|
|
||||||
|
|
||||||
sender.sendMessage("Added " + player.getName() + " to nation " + args[1]);
|
|
||||||
}
|
}
|
||||||
case "leave" -> {
|
case "leave" -> {
|
||||||
var player = args.length < 3
|
var player = args.length < 3
|
||||||
|
|
|
@ -18,20 +18,24 @@ public class Nation {
|
||||||
|
|
||||||
//The icon used for cities owned by this nation (default is a tower)
|
//The icon used for cities owned by this nation (default is a tower)
|
||||||
private String dynmapIcon;
|
private String dynmapIcon;
|
||||||
|
//Should be a color of wool, used for join GUI and flag generation (TODO)
|
||||||
|
private String colour;
|
||||||
|
|
||||||
public Nation(String name, Group group) {
|
public Nation(String name, Group group) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.group = group;
|
this.group = group;
|
||||||
this.players = new HashMap<>();
|
this.players = new HashMap<>();
|
||||||
this.dynmapIcon = "tower";
|
this.dynmapIcon = "tower";
|
||||||
|
this.colour = "WHITE";
|
||||||
this.spawn = null;
|
this.spawn = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Db Constructor
|
//Db Constructor
|
||||||
public Nation(String name, Group group, String dynmapIcon, Location spawn) {
|
public Nation(String name, Group group, String dynmapIcon, String colour, Location spawn) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.group = group;
|
this.group = group;
|
||||||
this.players = new HashMap<>();
|
this.players = new HashMap<>();
|
||||||
|
this.colour = colour;
|
||||||
this.dynmapIcon = dynmapIcon;
|
this.dynmapIcon = dynmapIcon;
|
||||||
this.spawn = spawn;
|
this.spawn = spawn;
|
||||||
}
|
}
|
||||||
|
@ -39,6 +43,8 @@ public class Nation {
|
||||||
public String getName() { return name; }
|
public String getName() { return name; }
|
||||||
public Group getGroup() { return group; }
|
public Group getGroup() { return group; }
|
||||||
public Location getSpawn() { return spawn; }
|
public Location getSpawn() { return spawn; }
|
||||||
|
|
||||||
|
public String getColour() { return colour; }
|
||||||
public void setSpawn(Location l) {
|
public void setSpawn(Location l) {
|
||||||
this.spawn = l;
|
this.spawn = l;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ public class NAWDatabase {
|
||||||
"`nation_name` VARCHAR(50) NOT NULL COMMENT 'Nation name', PRIMARY KEY (`nation_name`)," +
|
"`nation_name` VARCHAR(50) NOT NULL COMMENT 'Nation name', PRIMARY KEY (`nation_name`)," +
|
||||||
"`groupName` VARCHAR(50) NOT NULL COMMENT 'Name for the luckperms group.', " +
|
"`groupName` VARCHAR(50) NOT NULL COMMENT 'Name for the luckperms group.', " +
|
||||||
"`dynmapIcon` VARCHAR(50) NOT NULL COMMENT 'National Icon for Dynmap'," +
|
"`dynmapIcon` VARCHAR(50) NOT NULL COMMENT 'National Icon for Dynmap'," +
|
||||||
|
"`colour` VARCHAR(50) NOT NULL COMMENT 'Colour for the nation'," +
|
||||||
"`spawnworld` VARCHAR(50) NOT NULL," +
|
"`spawnworld` VARCHAR(50) NOT NULL," +
|
||||||
"`spawnx` INT NOT NULL DEFAULT 0," +
|
"`spawnx` INT NOT NULL DEFAULT 0," +
|
||||||
"`spawny` INT NOT NULL DEFAULT 0," +
|
"`spawny` INT NOT NULL DEFAULT 0," +
|
||||||
|
@ -40,11 +41,11 @@ public class NAWDatabase {
|
||||||
private final String SELECT_NATION = "SELECT * FROM `nations`;";
|
private final String SELECT_NATION = "SELECT * FROM `nations`;";
|
||||||
private final String SELECT_PLAYERS = "SELECT * FROM `nation_players`;";
|
private final String SELECT_PLAYERS = "SELECT * FROM `nation_players`;";
|
||||||
|
|
||||||
//name 1,8; group 2,9; icon 3,10; world 4,11; x 5,12; y 6,13; z 7,14;
|
//name 1,8; group 2,9; icon 3,10; colour 4, 11; world 5,12; x 6,13; y 7,14; z 8,15;
|
||||||
//14
|
//14
|
||||||
private final String SAVE_NATION = "INSERT INTO `nations`(nation_name,groupName,dynmapIcon, spawnworld,spawnx,spawny,spawnz) VALUES " +
|
private final String SAVE_NATION = "INSERT INTO `nations`(nation_name,groupName,dynmapIcon, colour, spawnworld,spawnx,spawny,spawnz) VALUES " +
|
||||||
"(?,?,?,?,?,?,?)" +
|
"(?,?,?,?,?,?,?)" +
|
||||||
"ON DUPLICATE KEY UPDATE nation_name=?, groupName=?, dynmapIcon=?, spawnworld=?, spawnx=?, spawny=?, spawnz=?;";
|
"ON DUPLICATE KEY UPDATE nation_name=?, groupName=?, dynmapIcon=?, colour=?, spawnworld=?, spawnx=?, spawny=?, spawnz=?;";
|
||||||
//MAKE SURE YOU RUN AS BATCH (nation, 1,3) (playeruuid, 2,4);
|
//MAKE SURE YOU RUN AS BATCH (nation, 1,3) (playeruuid, 2,4);
|
||||||
private final String SAVE_PLAYER = "INSERT INTO `nation_players`(nation_name, player_uuid) VALUES (?,?) " +
|
private final String SAVE_PLAYER = "INSERT INTO `nation_players`(nation_name, player_uuid) VALUES (?,?) " +
|
||||||
"ON DUPLICATE KEY UPDATE nation_name=?, player_uuid=?;";
|
"ON DUPLICATE KEY UPDATE nation_name=?, player_uuid=?;";
|
||||||
|
@ -114,8 +115,9 @@ public class NAWDatabase {
|
||||||
var name = rs.getString(1);
|
var name = rs.getString(1);
|
||||||
var group = OpenNAW.getInstance().getGroupHandler().getGroupFromString(rs.getString(2));
|
var group = OpenNAW.getInstance().getGroupHandler().getGroupFromString(rs.getString(2));
|
||||||
var icon = rs.getString(3);
|
var icon = rs.getString(3);
|
||||||
var location = new Location(Bukkit.getWorld(rs.getString(4)), rs.getInt(5), rs.getInt(6), rs.getInt(7));
|
var colour = rs.getString(4);
|
||||||
nationLoad.add(new Nation(name, group, icon, location));
|
var location = new Location(Bukkit.getWorld(rs.getString(5)), rs.getInt(6), rs.getInt(7), rs.getInt(8));
|
||||||
|
nationLoad.add(new Nation(name, group, icon, colour, location));
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
OpenNAW.getInstance().getLogger().info(i + " nations loaded.");
|
OpenNAW.getInstance().getLogger().info(i + " nations loaded.");
|
||||||
|
@ -147,7 +149,7 @@ public class NAWDatabase {
|
||||||
public void saveNations(Collection<Nation> nations) {
|
public void saveNations(Collection<Nation> nations) {
|
||||||
try {
|
try {
|
||||||
OpenNAW.getInstance().getLogger().info("Starting nation save...");
|
OpenNAW.getInstance().getLogger().info("Starting nation save...");
|
||||||
//name 1,8; group 2,9; icon 3,10; world 4,11; x 5,12; y 6,13; z 7,14;
|
//name 1,8; group 2,9; icon 3,10; colour 4, 11; world 5,12; x 6,13; y 7,14; z 8,15;
|
||||||
//14
|
//14
|
||||||
PreparedStatement nationSave = connection.prepareStatement(SAVE_NATION);
|
PreparedStatement nationSave = connection.prepareStatement(SAVE_NATION);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
@ -158,15 +160,17 @@ public class NAWDatabase {
|
||||||
nationSave.setString(9, nation.getGroup().getName());
|
nationSave.setString(9, nation.getGroup().getName());
|
||||||
nationSave.setString(3, nation.getDynmapIcon());
|
nationSave.setString(3, nation.getDynmapIcon());
|
||||||
nationSave.setString(10, nation.getDynmapIcon());
|
nationSave.setString(10, nation.getDynmapIcon());
|
||||||
|
nationSave.setString(4, nation.getColour());
|
||||||
|
nationSave.setString(11, nation.getColour());
|
||||||
var loc = nation.getSpawn();
|
var loc = nation.getSpawn();
|
||||||
nationSave.setString(4, loc.getWorld().getName());
|
nationSave.setString(5, loc.getWorld().getName());
|
||||||
nationSave.setString(11, loc.getWorld().getName());
|
nationSave.setString(12, loc.getWorld().getName());
|
||||||
nationSave.setInt(5, loc.getBlockX());
|
nationSave.setInt(6, loc.getBlockX());
|
||||||
nationSave.setInt(12, loc.getBlockX());
|
nationSave.setInt(13, loc.getBlockX());
|
||||||
nationSave.setInt(6, loc.getBlockY());
|
nationSave.setInt(7, loc.getBlockY());
|
||||||
nationSave.setInt(13, loc.getBlockY());
|
nationSave.setInt(14, loc.getBlockY());
|
||||||
nationSave.setInt(7, loc.getBlockZ());
|
nationSave.setInt(8, loc.getBlockZ());
|
||||||
nationSave.setInt(14, loc.getBlockZ());
|
nationSave.setInt(15, loc.getBlockZ());
|
||||||
|
|
||||||
//Values are set, this is autistic. Run.
|
//Values are set, this is autistic. Run.
|
||||||
nationSave.addBatch();
|
nationSave.addBatch();
|
||||||
|
|
|
@ -18,34 +18,34 @@ public class ActionLogger {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean tryAction(Action action) {
|
public boolean tryAction(Action action) {
|
||||||
if(action instanceof CityAttackAction) {
|
if (action instanceof CityAttackAction) {
|
||||||
//City attack logic
|
//City attack logic
|
||||||
CityAttackAction CAA = (CityAttackAction) action;
|
CityAttackAction CAA = (CityAttackAction) action;
|
||||||
if (actionByUuid.containsKey(CAA.getPlayer().getUniqueId())) {
|
if (actionByUuid.containsKey(CAA.getPlayer().getUniqueId())) {
|
||||||
Action storedAction = actionByUuid.get(CAA.getPlayer().getUniqueId());
|
Action storedAction = actionByUuid.get(CAA.getPlayer().getUniqueId());
|
||||||
if(actionByUuid.get(CAA.getPlayer().getUniqueId()) instanceof CityAttackAction) {
|
if (actionByUuid.get(CAA.getPlayer().getUniqueId()) instanceof CityAttackAction) {
|
||||||
//Action is already running.
|
//Action is already running.
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
actionByUuid.get(CAA.getPlayer().getUniqueId()).setCancelled(true); //Cancel prior action.
|
actionByUuid.get(CAA.getPlayer().getUniqueId()).setCancelled(true); //Cancel prior action.
|
||||||
actionByUuid.remove(CAA.getPlayer().getUniqueId());
|
|
||||||
CAA.getPlayer().getPlayer().sendMessage(ChatColor.YELLOW + "Action switched.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
actionByUuid.put(CAA.getPlayer().getUniqueId(), action);
|
|
||||||
CAA.initiation(); //Code to run when action started.
|
|
||||||
Bukkit.getScheduler().runTaskLater(OpenNAW.getInstance(), () -> {
|
|
||||||
OpenNAW.getInstance().getLogger().info("Starting Callback...");
|
|
||||||
var actionCallback = actionByUuid.get(CAA.getPlayer().getUniqueId());
|
|
||||||
if (!actionCallback.isCancelled()) {
|
|
||||||
OpenNAW.getInstance().getLogger().info("Callback not cancelled!");
|
|
||||||
CAA.completion();
|
|
||||||
}
|
|
||||||
actionByUuid.remove(CAA.getPlayer().getUniqueId());
|
actionByUuid.remove(CAA.getPlayer().getUniqueId());
|
||||||
}, CityAttackAction.DELAY);
|
CAA.getPlayer().getPlayer().sendMessage(ChatColor.YELLOW + "Action switched.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
actionByUuid.put(CAA.getPlayer().getUniqueId(), action);
|
||||||
|
CAA.initiation(); //Code to run when action started.
|
||||||
|
Bukkit.getScheduler().runTaskLater(OpenNAW.getInstance(), () -> {
|
||||||
|
OpenNAW.getInstance().getLogger().info("Starting Callback...");
|
||||||
|
var actionCallback = actionByUuid.get(CAA.getPlayer().getUniqueId());
|
||||||
|
if (!actionCallback.isCancelled()) {
|
||||||
|
OpenNAW.getInstance().getLogger().info("Callback not cancelled!");
|
||||||
|
CAA.completion();
|
||||||
|
}
|
||||||
|
actionByUuid.remove(CAA.getPlayer().getUniqueId());
|
||||||
|
}, CityAttackAction.DELAY);
|
||||||
CAA.getPlayer().getPlayer().sendMessage(ChatColor.GREEN + "" + ChatColor.ITALIC + "You raise your sword...");
|
CAA.getPlayer().getPlayer().sendMessage(ChatColor.GREEN + "" + ChatColor.ITALIC + "You raise your sword...");
|
||||||
return true;
|
return true;
|
||||||
} else if(action instanceof CityDefenseAction) {
|
} else if (action instanceof CityDefenseAction) {
|
||||||
CityDefenseAction CDA = (CityDefenseAction) action;
|
CityDefenseAction CDA = (CityDefenseAction) action;
|
||||||
if (actionByUuid.containsKey(CDA.getPlayer().getUniqueId())) {
|
if (actionByUuid.containsKey(CDA.getPlayer().getUniqueId())) {
|
||||||
Action storedAction = actionByUuid.get(CDA.getPlayer().getUniqueId());
|
Action storedAction = actionByUuid.get(CDA.getPlayer().getUniqueId());
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
package moe.oko.opennaw.util;
|
||||||
|
|
||||||
|
import moe.oko.opennaw.OpenNAW;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.inventory.ClickType;
|
||||||
|
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import xyz.janboerman.guilib.api.menu.ItemButton;
|
||||||
|
import xyz.janboerman.guilib.api.menu.MenuHolder;
|
||||||
|
|
||||||
|
public class GuiHelper {
|
||||||
|
public static MenuHolder<OpenNAW> getJoinMenu() {
|
||||||
|
var menu = new MenuHolder<>(OpenNAW.getInstance(), 9, "Join a Nation!");
|
||||||
|
|
||||||
|
int i = 9;
|
||||||
|
int s = 0;
|
||||||
|
for(var nation : OpenNAW.getInstance().getNationHandler().getNationList()) {
|
||||||
|
var is = new ItemStack(Material.getMaterial(nation.getColour() + "_WOOL"));
|
||||||
|
is.getItemMeta().displayName(Component.text(ChatColor.valueOf(nation.getColour()) + nation.getName()));
|
||||||
|
menu.setButton(s, new ItemButton<>(is) {
|
||||||
|
@Override
|
||||||
|
public void onClick(MenuHolder<?> menu, InventoryClickEvent event) {
|
||||||
|
if(event.getClick().equals(ClickType.LEFT)) {
|
||||||
|
//Left click, run join logic.
|
||||||
|
var viewer = event.getViewers().get(1);
|
||||||
|
if(!(viewer instanceof Player)) { return; } //fugged
|
||||||
|
var player = (Player) viewer;
|
||||||
|
|
||||||
|
nation.getPlayerMap().put(player.getUniqueId(), player);
|
||||||
|
OpenNAW.getInstance().getGroupHandler().addPlayerToGroup(player, nation.getGroup());
|
||||||
|
|
||||||
|
player.sendMessage(ChatColor.GREEN + "Joined " + nation.getName() + " successfully.");
|
||||||
|
event.getView().close(); //Close inv.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,10 +7,7 @@ import net.luckperms.api.model.group.Group;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class NationHandler {
|
public class NationHandler {
|
||||||
|
|
||||||
|
@ -68,7 +65,7 @@ public class NationHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getNationList() {
|
public List<String> getNationStringList() {
|
||||||
List<String> nationList = new ArrayList<String>();
|
List<String> nationList = new ArrayList<String>();
|
||||||
for (Nation nation : nations.values()) {
|
for (Nation nation : nations.values()) {
|
||||||
nationList.add(nation.getName());
|
nationList.add(nation.getName());
|
||||||
|
@ -76,6 +73,10 @@ public class NationHandler {
|
||||||
return nationList;
|
return nationList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Collection<Nation> getNationList() {
|
||||||
|
return nations.values();
|
||||||
|
}
|
||||||
|
|
||||||
public void saveNations() {
|
public void saveNations() {
|
||||||
OpenNAW.getInstance().getDatabase().saveNations(nations.values());
|
OpenNAW.getInstance().getDatabase().saveNations(nations.values());
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ main: moe.oko.opennaw.OpenNAW
|
||||||
api-version: 1.18
|
api-version: 1.18
|
||||||
prefix: OpenNAW
|
prefix: OpenNAW
|
||||||
softdepend: [dynmap]
|
softdepend: [dynmap]
|
||||||
|
depend: [GuiLib]
|
||||||
authors: [ oko ]
|
authors: [ oko ]
|
||||||
description: Open source recreation of Nations at War
|
description: Open source recreation of Nations at War
|
||||||
website: https://oko.moe
|
website: https://oko.moe
|
||||||
|
@ -14,3 +15,4 @@ commands:
|
||||||
city:
|
city:
|
||||||
description: Manages cities
|
description: Manages cities
|
||||||
usage: /<command>
|
usage: /<command>
|
||||||
|
permission: naw.admin
|
Loading…
Reference in New Issue