mirror of
https://github.com/filoghost/ChestCommands.git
synced 2025-02-19 21:11:42 +01:00
Refactoring
This commit is contained in:
parent
5afcca3682
commit
123bedfbd8
@ -17,11 +17,7 @@ package me.filoghost.chestcommands;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bstats.bukkit.MetricsLite;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
@ -50,7 +46,6 @@ import me.filoghost.chestcommands.listener.SignListener;
|
||||
import me.filoghost.chestcommands.parser.MenuParser;
|
||||
import me.filoghost.chestcommands.task.RefreshMenusTask;
|
||||
import me.filoghost.chestcommands.util.BukkitUtils;
|
||||
import me.filoghost.chestcommands.util.CaseInsensitiveMap;
|
||||
import me.filoghost.chestcommands.util.ErrorCollector;
|
||||
import me.filoghost.chestcommands.util.Utils;
|
||||
import me.filoghost.updatechecker.UpdateChecker;
|
||||
@ -62,14 +57,10 @@ public class ChestCommands extends JavaPlugin {
|
||||
|
||||
|
||||
private static ChestCommands instance;
|
||||
private MenuManager menuManager;
|
||||
private static Settings settings;
|
||||
private static Lang lang;
|
||||
|
||||
private static Map<String, ExtendedIconMenu> fileNameToMenuMap;
|
||||
private static Map<String, ExtendedIconMenu> commandsToMenuMap;
|
||||
|
||||
private static Set<BoundItem> boundItems;
|
||||
|
||||
private static ErrorCollector lastLoadErrors;
|
||||
private static String newVersion;
|
||||
|
||||
@ -81,10 +72,7 @@ public class ChestCommands extends JavaPlugin {
|
||||
}
|
||||
|
||||
instance = this;
|
||||
fileNameToMenuMap = CaseInsensitiveMap.create();
|
||||
commandsToMenuMap = CaseInsensitiveMap.create();
|
||||
boundItems = new HashSet<>();
|
||||
|
||||
menuManager = new MenuManager();
|
||||
settings = new Settings(new PluginConfig(this, "config.yml"));
|
||||
lang = new Lang(new PluginConfig(this, "lang.yml"));
|
||||
|
||||
@ -129,12 +117,12 @@ public class ChestCommands extends JavaPlugin {
|
||||
int pluginID = 3658;
|
||||
new MetricsLite(this, pluginID);
|
||||
|
||||
Bukkit.getPluginManager().registerEvents(new CommandListener(), this);
|
||||
Bukkit.getPluginManager().registerEvents(new InventoryListener(), this);
|
||||
Bukkit.getPluginManager().registerEvents(new CommandListener(menuManager), this);
|
||||
Bukkit.getPluginManager().registerEvents(new InventoryListener(menuManager), this);
|
||||
Bukkit.getPluginManager().registerEvents(new JoinListener(), this);
|
||||
Bukkit.getPluginManager().registerEvents(new SignListener(), this);
|
||||
Bukkit.getPluginManager().registerEvents(new SignListener(menuManager), this);
|
||||
|
||||
CommandFramework.register(this, new CommandHandler("chestcommands"));
|
||||
CommandFramework.register(this, new CommandHandler(menuManager, "chestcommands"));
|
||||
|
||||
ErrorCollector errorCollector = new ErrorCollector();
|
||||
load(errorCollector);
|
||||
@ -157,9 +145,7 @@ public class ChestCommands extends JavaPlugin {
|
||||
|
||||
|
||||
public void load(ErrorCollector errorCollector) {
|
||||
fileNameToMenuMap.clear();
|
||||
commandsToMenuMap.clear();
|
||||
boundItems.clear();
|
||||
menuManager.clear();
|
||||
|
||||
try {
|
||||
settings.load();
|
||||
@ -223,21 +209,7 @@ public class ChestCommands extends JavaPlugin {
|
||||
MenuData data = MenuParser.loadMenuData(menuConfig, errorCollector);
|
||||
ExtendedIconMenu iconMenu = MenuParser.loadMenu(menuConfig, data.getTitle(), data.getRows(), errorCollector);
|
||||
|
||||
if (fileNameToMenuMap.containsKey(menuConfig.getFileName())) {
|
||||
errorCollector.addError("Two menus have the same file name \"" + menuConfig.getFileName() + "\" with different cases. There will be problems opening one of these two menus.");
|
||||
}
|
||||
fileNameToMenuMap.put(menuConfig.getFileName(), iconMenu);
|
||||
|
||||
if (data.hasCommands()) {
|
||||
for (String command : data.getCommands()) {
|
||||
if (!command.isEmpty()) {
|
||||
if (commandsToMenuMap.containsKey(command)) {
|
||||
errorCollector.addError("The menus \"" + commandsToMenuMap.get(command).getFileName() + "\" and \"" + menuConfig.getFileName() + "\" have the same command \"" + command + "\". Only one will be opened.");
|
||||
}
|
||||
commandsToMenuMap.put(command, iconMenu);
|
||||
}
|
||||
}
|
||||
}
|
||||
menuManager.registerMenu(menuConfig.getFileName(), data.getCommands(), iconMenu, errorCollector);
|
||||
|
||||
iconMenu.setRefreshTicks(data.getRefreshTenths());
|
||||
|
||||
@ -250,7 +222,7 @@ public class ChestCommands extends JavaPlugin {
|
||||
if (data.hasBoundDataValue()) {
|
||||
boundItem.setRestrictiveData(data.getBoundDataValue());
|
||||
}
|
||||
boundItems.add(boundItem);
|
||||
menuManager.registerTriggerItem(boundItem);
|
||||
}
|
||||
}
|
||||
|
||||
@ -295,6 +267,10 @@ public class ChestCommands extends JavaPlugin {
|
||||
public static ChestCommands getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public MenuManager getMenuManager() {
|
||||
return menuManager;
|
||||
}
|
||||
|
||||
public static Settings getSettings() {
|
||||
return settings;
|
||||
@ -312,18 +288,6 @@ public class ChestCommands extends JavaPlugin {
|
||||
return newVersion;
|
||||
}
|
||||
|
||||
public static Map<String, ExtendedIconMenu> getFileNameToMenuMap() {
|
||||
return fileNameToMenuMap;
|
||||
}
|
||||
|
||||
public static Map<String, ExtendedIconMenu> getCommandToMenuMap() {
|
||||
return commandsToMenuMap;
|
||||
}
|
||||
|
||||
public static Set<BoundItem> getBoundItems() {
|
||||
return boundItems;
|
||||
}
|
||||
|
||||
public static void setLastReloadErrors(ErrorCollector lastLoadErrors) {
|
||||
ChestCommands.lastLoadErrors = lastLoadErrors;
|
||||
}
|
||||
|
109
Plugin/src/main/java/me/filoghost/chestcommands/MenuManager.java
Normal file
109
Plugin/src/main/java/me/filoghost/chestcommands/MenuManager.java
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package me.filoghost.chestcommands;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import me.filoghost.chestcommands.api.IconMenu;
|
||||
import me.filoghost.chestcommands.internal.BoundItem;
|
||||
import me.filoghost.chestcommands.internal.ExtendedIconMenu;
|
||||
import me.filoghost.chestcommands.internal.MenuInventoryHolder;
|
||||
import me.filoghost.chestcommands.util.CaseInsensitiveMap;
|
||||
import me.filoghost.chestcommands.util.ErrorCollector;
|
||||
|
||||
public class MenuManager {
|
||||
|
||||
private static Map<String, ExtendedIconMenu> fileNameToMenuMap;
|
||||
private static Map<String, ExtendedIconMenu> commandsToMenuMap;
|
||||
|
||||
private static Set<BoundItem> boundItems;
|
||||
|
||||
public MenuManager() {
|
||||
fileNameToMenuMap = CaseInsensitiveMap.create();
|
||||
commandsToMenuMap = CaseInsensitiveMap.create();
|
||||
boundItems = new HashSet<>();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
fileNameToMenuMap.clear();
|
||||
commandsToMenuMap.clear();
|
||||
boundItems.clear();
|
||||
}
|
||||
|
||||
public ExtendedIconMenu getMenuByFileName(String fileName) {
|
||||
return fileNameToMenuMap.get(fileName);
|
||||
}
|
||||
|
||||
public void registerMenu(String fileName, String[] triggerCommands, ExtendedIconMenu menu, ErrorCollector errorCollector) {
|
||||
if (fileNameToMenuMap.containsKey(fileName)) {
|
||||
errorCollector.addError("Two menus have the same file name \"" + fileName + "\" with different cases. There will be problems opening one of these two menus.");
|
||||
}
|
||||
|
||||
fileNameToMenuMap.put(fileName, menu);
|
||||
|
||||
for (String triggerCommand : triggerCommands) {
|
||||
if (!triggerCommand.isEmpty()) {
|
||||
if (commandsToMenuMap.containsKey(triggerCommand)) {
|
||||
errorCollector.addError("The menus \"" + commandsToMenuMap.get(triggerCommand).getFileName() + "\" and \"" + fileName + "\" have the same command \"" + triggerCommand + "\". Only one will be opened.");
|
||||
}
|
||||
commandsToMenuMap.put(triggerCommand, menu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void registerTriggerItem(BoundItem boundItem) {
|
||||
boundItems.add(boundItem);
|
||||
}
|
||||
|
||||
public void openMenuByItem(Player player, ItemStack itemInHand, Action clickAction) {
|
||||
for (BoundItem boundItem : boundItems) {
|
||||
if (boundItem.isValidTrigger(itemInHand, clickAction)) {
|
||||
if (player.hasPermission(boundItem.getMenu().getPermission())) {
|
||||
boundItem.getMenu().open(player);
|
||||
} else {
|
||||
boundItem.getMenu().sendNoPermissionMessage(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IconMenu getIconMenu(Inventory inventory) {
|
||||
if (inventory.getHolder() instanceof MenuInventoryHolder) {
|
||||
return ((MenuInventoryHolder) inventory.getHolder()).getIconMenu();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public ExtendedIconMenu getMenuByCommand(String command) {
|
||||
return commandsToMenuMap.get(command);
|
||||
}
|
||||
|
||||
public Collection<String> getMenuFileNames() {
|
||||
return Collections.unmodifiableCollection(fileNameToMenuMap.keySet());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -30,7 +30,7 @@ public class OpenMenuAction extends Action {
|
||||
@Override
|
||||
public void execute(final Player player) {
|
||||
String target = hasVariables ? getParsedAction(player) : action;
|
||||
final ExtendedIconMenu menu = ChestCommands.getFileNameToMenuMap().get(target.toLowerCase());
|
||||
final ExtendedIconMenu menu = ChestCommands.getInstance().getMenuManager().getMenuByFileName(target.toLowerCase());
|
||||
if (menu != null) {
|
||||
|
||||
/*
|
||||
|
@ -26,7 +26,7 @@ public class ChestCommandsAPI {
|
||||
* @return true - if the menu was found.
|
||||
*/
|
||||
public static boolean isPluginMenu(String yamlFile) {
|
||||
return ChestCommands.getFileNameToMenuMap().containsKey(yamlFile);
|
||||
return ChestCommands.getInstance().getMenuManager().getMenuByFileName(yamlFile) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -38,7 +38,7 @@ public class ChestCommandsAPI {
|
||||
* @return true - if the menu was found and opened, false if not.
|
||||
*/
|
||||
public static boolean openPluginMenu(Player player, String yamlFile) {
|
||||
IconMenu menu = ChestCommands.getFileNameToMenuMap().get(yamlFile);
|
||||
IconMenu menu = ChestCommands.getInstance().getMenuManager().getMenuByFileName(yamlFile);
|
||||
|
||||
if (menu != null) {
|
||||
menu.open(player);
|
||||
|
@ -21,6 +21,7 @@ import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import me.filoghost.chestcommands.ChestCommands;
|
||||
import me.filoghost.chestcommands.MenuManager;
|
||||
import me.filoghost.chestcommands.Permissions;
|
||||
import me.filoghost.chestcommands.command.framework.CommandFramework;
|
||||
import me.filoghost.chestcommands.command.framework.CommandValidate;
|
||||
@ -28,9 +29,12 @@ import me.filoghost.chestcommands.internal.ExtendedIconMenu;
|
||||
import me.filoghost.chestcommands.util.ErrorCollector;
|
||||
|
||||
public class CommandHandler extends CommandFramework {
|
||||
|
||||
public CommandHandler(String label) {
|
||||
|
||||
private MenuManager menuManager;
|
||||
|
||||
public CommandHandler(MenuManager menuManager, String label) {
|
||||
super(label);
|
||||
this.menuManager = menuManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -100,7 +104,7 @@ public class CommandHandler extends CommandFramework {
|
||||
CommandValidate.notNull(target, "That player is not online.");
|
||||
|
||||
String menuName = args[1].toLowerCase().endsWith(".yml") ? args[1] : args[1] + ".yml";
|
||||
ExtendedIconMenu menu = ChestCommands.getFileNameToMenuMap().get(menuName);
|
||||
ExtendedIconMenu menu = menuManager.getMenuByFileName(menuName);
|
||||
CommandValidate.notNull(menu, "The menu \"" + menuName + "\" was not found.");
|
||||
|
||||
if (!sender.hasPermission(menu.getPermission())) {
|
||||
@ -126,7 +130,7 @@ public class CommandHandler extends CommandFramework {
|
||||
if (args[0].equalsIgnoreCase("list")) {
|
||||
CommandValidate.isTrue(sender.hasPermission(Permissions.COMMAND_BASE + "list"), "You don't have permission.");
|
||||
sender.sendMessage(ChestCommands.CHAT_PREFIX + " Loaded menus:");
|
||||
for (String file : ChestCommands.getFileNameToMenuMap().keySet()) {
|
||||
for (String file : menuManager.getMenuFileNames()) {
|
||||
sender.sendMessage(ChatColor.GRAY + "- " + ChatColor.WHITE + file);
|
||||
}
|
||||
|
||||
|
@ -19,11 +19,17 @@ import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
|
||||
import me.filoghost.chestcommands.ChestCommands;
|
||||
import me.filoghost.chestcommands.MenuManager;
|
||||
import me.filoghost.chestcommands.internal.ExtendedIconMenu;
|
||||
import me.filoghost.chestcommands.util.StringUtils;
|
||||
|
||||
public class CommandListener implements Listener {
|
||||
|
||||
private MenuManager menuManager;
|
||||
|
||||
public CommandListener(MenuManager menuManager) {
|
||||
this.menuManager = menuManager;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onCommand(PlayerCommandPreprocessEvent event) {
|
||||
@ -34,7 +40,7 @@ public class CommandListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
ExtendedIconMenu menu = ChestCommands.getCommandToMenuMap().get(command);
|
||||
ExtendedIconMenu menu = menuManager.getMenuByCommand(command);
|
||||
|
||||
if (menu != null) {
|
||||
event.setCancelled(true);
|
||||
|
@ -25,66 +25,66 @@ import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import me.filoghost.chestcommands.ChestCommands;
|
||||
import me.filoghost.chestcommands.MenuManager;
|
||||
import me.filoghost.chestcommands.api.Icon;
|
||||
import me.filoghost.chestcommands.api.IconMenu;
|
||||
import me.filoghost.chestcommands.internal.BoundItem;
|
||||
import me.filoghost.chestcommands.internal.MenuInventoryHolder;
|
||||
import me.filoghost.chestcommands.task.ExecuteActionsTask;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class InventoryListener implements Listener {
|
||||
|
||||
|
||||
private static Map<Player, Long> antiClickSpam = new HashMap<>();
|
||||
|
||||
private MenuManager menuManager;
|
||||
|
||||
public InventoryListener(MenuManager menuManager) {
|
||||
this.menuManager = menuManager;
|
||||
}
|
||||
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false)
|
||||
public void onInteract(PlayerInteractEvent event) {
|
||||
if (event.hasItem() && event.getAction() != Action.PHYSICAL) {
|
||||
for (BoundItem boundItem : ChestCommands.getBoundItems()) {
|
||||
if (boundItem.isValidTrigger(event.getItem(), event.getAction())) {
|
||||
if (event.getPlayer().hasPermission(boundItem.getMenu().getPermission())) {
|
||||
boundItem.getMenu().open(event.getPlayer());
|
||||
} else {
|
||||
boundItem.getMenu().sendNoPermissionMessage(event.getPlayer());
|
||||
}
|
||||
}
|
||||
}
|
||||
menuManager.openMenuByItem(event.getPlayer(), event.getItem(), event.getAction());
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false)
|
||||
public void onInventoryClick(InventoryClickEvent event) {
|
||||
if (event.getInventory().getHolder() instanceof MenuInventoryHolder) {
|
||||
IconMenu menu = menuManager.getIconMenu(event.getInventory());
|
||||
if (menu == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.setCancelled(true); // First thing to do, if an exception is thrown at least the player doesn't take the item
|
||||
|
||||
event.setCancelled(true); // First thing to do, if an exception is thrown at least the player doesn't take the item
|
||||
int slot = event.getRawSlot();
|
||||
if (slot < 0 || slot >= menu.getSize()) {
|
||||
return;
|
||||
}
|
||||
|
||||
IconMenu menu = ((MenuInventoryHolder) event.getInventory().getHolder()).getIconMenu();
|
||||
int slot = event.getRawSlot();
|
||||
Icon icon = menu.getIconRaw(slot);
|
||||
if (icon == null || event.getInventory().getItem(slot) == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (slot >= 0 && slot < menu.getSize()) {
|
||||
Player clicker = (Player) event.getWhoClicked();
|
||||
|
||||
Icon icon = menu.getIconRaw(slot);
|
||||
Long cooldownUntil = antiClickSpam.get(clicker);
|
||||
long now = System.currentTimeMillis();
|
||||
int minDelay = ChestCommands.getSettings().anti_click_spam_delay;
|
||||
|
||||
if (icon != null && event.getInventory().getItem(slot) != null) {
|
||||
Player clicker = (Player) event.getWhoClicked();
|
||||
|
||||
Long cooldownUntil = antiClickSpam.get(clicker);
|
||||
long now = System.currentTimeMillis();
|
||||
int minDelay = ChestCommands.getSettings().anti_click_spam_delay;
|
||||
|
||||
if (minDelay > 0) {
|
||||
if (cooldownUntil != null && cooldownUntil > now) {
|
||||
return;
|
||||
} else {
|
||||
antiClickSpam.put(clicker, now + minDelay);
|
||||
}
|
||||
}
|
||||
|
||||
// Closes the inventory and executes actions AFTER the event
|
||||
Bukkit.getScheduler().runTask(ChestCommands.getInstance(), new ExecuteActionsTask(clicker, icon));
|
||||
}
|
||||
if (minDelay > 0) {
|
||||
if (cooldownUntil != null && cooldownUntil > now) {
|
||||
return;
|
||||
} else {
|
||||
antiClickSpam.put(clicker, now + minDelay);
|
||||
}
|
||||
}
|
||||
|
||||
// Closes the inventory and executes actions AFTER the event
|
||||
Bukkit.getScheduler().runTask(ChestCommands.getInstance(), new ExecuteActionsTask(clicker, icon));
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
|
@ -24,6 +24,7 @@ import org.bukkit.event.block.SignChangeEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
|
||||
import me.filoghost.chestcommands.ChestCommands;
|
||||
import me.filoghost.chestcommands.MenuManager;
|
||||
import me.filoghost.chestcommands.Permissions;
|
||||
import me.filoghost.chestcommands.api.IconMenu;
|
||||
import me.filoghost.chestcommands.internal.ExtendedIconMenu;
|
||||
@ -31,6 +32,12 @@ import me.filoghost.chestcommands.util.BukkitUtils;
|
||||
import me.filoghost.chestcommands.util.MaterialsRegistry;
|
||||
|
||||
public class SignListener implements Listener {
|
||||
|
||||
private MenuManager menuManager;
|
||||
|
||||
public SignListener(MenuManager menuManager) {
|
||||
this.menuManager = menuManager;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
||||
public void onInteract(PlayerInteractEvent event) {
|
||||
@ -41,7 +48,7 @@ public class SignListener implements Listener {
|
||||
if (sign.getLine(0).equalsIgnoreCase(ChatColor.DARK_BLUE + "[menu]")) {
|
||||
|
||||
sign.getLine(1);
|
||||
ExtendedIconMenu iconMenu = ChestCommands.getFileNameToMenuMap().get(BukkitUtils.addYamlExtension(sign.getLine(1)));
|
||||
ExtendedIconMenu iconMenu = menuManager.getMenuByFileName(BukkitUtils.addYamlExtension(sign.getLine(1)));
|
||||
if (iconMenu != null) {
|
||||
|
||||
if (event.getPlayer().hasPermission(iconMenu.getPermission())) {
|
||||
@ -68,7 +75,7 @@ public class SignListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
IconMenu iconMenu = ChestCommands.getFileNameToMenuMap().get(BukkitUtils.addYamlExtension(event.getLine(1)));
|
||||
IconMenu iconMenu = menuManager.getMenuByFileName(BukkitUtils.addYamlExtension(event.getLine(1)));
|
||||
if (iconMenu == null) {
|
||||
event.setLine(0, ChatColor.RED + event.getLine(0));
|
||||
event.getPlayer().sendMessage(ChatColor.RED + "That menu was not found.");
|
||||
|
Loading…
Reference in New Issue
Block a user