Recent changes and additions (general functionality)

This commit is contained in:
Roni Lehto 2021-10-21 21:31:56 +03:00
parent 459dbf80ec
commit 97af7663cc
13 changed files with 544 additions and 50 deletions

View File

@ -1,18 +1,37 @@
# Language file for XAquamarine # Language file for XAquamarine
generic-no-permission: "Sinulla ei ole oikeutta tuohon." generic-no-permission: "Sinulla ei ole oikeutta tuohon."
command-ticket-usage: "Käytä: &b/%label% [viesti]" generic-not-player: "Tämä komento toimii vain pelaajilla."
generic-invalid-number: "Virheellinen numero: &b$n$"
generic-ticket-not-found: "Apupyyntöä ei löytynyt."
generic-invalid-ticket: "Apupyyntö on virheellinen tai sitä koskevaa maailmaa ei ole ladattu."
generic-invalid-player: "Pelaajaa '$player$' ei löytynyt. Oletko varma, että hän on pelannut täällä tuolla nimellä?"
generic-no-tickets: "Apupyyntöjä ei löytynyt."
generic-previous: "Edelliset"
generic-next: "Seuraavat"
generic-page: "Sivu "
gui-click-teleport: "Klikkaa vasemmalla siirtyäksesi"
gui-click-solve: "Klikkaa oikealla sulkeaksesi"
command-ticket-usage: "Käytä: &b/$label$ [viesti]"
command-ticket-goto-usage: "Käytä: &b/$label$ goto [id]"
command-ticket-goto-teleport: "Woosh! Täällä ollaan."
command-ticket-solve-usage: "Käytä: &b/$label$ solve [id] [viesti]"
command-ticket-solved: "&b$solver$ &7sulki apupyynnön &b#$n$&7: &f&o$comment$"
command-ticket-view-usage: "Käytä: &b/$label$ view [id]"
command-ticket-player-usage: "Käytä: &b/$label$ player [pelaaja] [sivu]"
ticket-deny-nearby: "Et voi luoda uutta apupyyntöä tässä, koska lähellä on tehty jo muita apupyyntöjä." ticket-deny-nearby: "Et voi luoda uutta apupyyntöä tässä, koska lähellä on tehty jo muita apupyyntöjä."
ticket-deny-player: "Et voi luoda uutta apupyyntöä, koska sinulla on jo useita odottavia apupyyntöjä." ticket-deny-player: "Et voi luoda uutta apupyyntöä, koska sinulla on jo useita odottavia apupyyntöjä."
ticket-created: "Avasit uuden apupyynnön. Numero: &b%ticketId%" ticket-created: "Avasit uuden apupyynnön. Numero: &b$ticketId$"
ticket-created-announcement: "&b%player% &7avasi apupyynnön &b#%ticketId%&7:" ticket-created-announcement: "&b$player$ &7avasi apupyynnön &b#$ticketId$&7:"
ticket-join-announcement: "Palvelimella odottaa &b%ticketCount% apupyyntöä" ticket-join-announcement: "Palvelimella odottaa &b$ticketCount$ apupyyntöä"
ticket-preview-teleport: "Teleporttaa" ticket-preview-teleport: "Teleporttaa"
ticket-preview-solve: "Muuta ratkaistuksi" ticket-preview-solve: "Muuta ratkaistuksi"
ticket-hover-title: "&bApupyyntö #%ticketId%" ticket-hover-title: "&bApupyyntö #$ticketId$"
ticket-hover-sender: "&7Lähettäjä: &f%s" ticket-hover-sender: "&7Lähettäjä: &f%s"
ticket-hover-timestamp: "&7Aikaleima: &f%s" ticket-hover-timestamp: "&7Aikaleima: &f%s"
ticket-hover-location: "&7Sijainti: &f%s" ticket-hover-location: "&7Sijainti: &f%s"

View File

@ -13,9 +13,11 @@ commands:
permission: aquamarine.ticket permission: aquamarine.ticket
xt: xt:
description: Staff features description: Staff features
aliases: [aq]
permission: aquamarine.staff permission: aquamarine.staff
xti: xti:
description: Staff features description: Staff features
aliases: [aqi]
permission: aquamarine.staff permission: aquamarine.staff
permissions: permissions:
aquamarine.ticket: aquamarine.ticket:

View File

@ -90,7 +90,7 @@ public class XText {
for (String s:words) { for (String s:words) {
out.append(s); out.append(s).append(' ');
line += s.length(); line += s.length();
if (line >= charsPerLine) { if (line >= charsPerLine) {

View File

@ -61,13 +61,14 @@ public class XTicketManager implements Listener {
throw new RuntimeException("Unable to create new ticket. Check your storage method."); throw new RuntimeException("Unable to create new ticket. Check your storage method.");
String staffAnnounce = plugin.lang("ticket-created-announcement") String staffAnnounce = plugin.lang("ticket-created-announcement")
.replace("player", player.getName()) .replaceAll("\\$player\\$", player.getName())
.replace("ticketId", ""+ticket.getId()); .replaceAll("\\$ticketId\\$", ""+ticket.getId());
Bukkit.getOnlinePlayers() Bukkit.getOnlinePlayers()
.stream() .stream()
.filter(p -> p.hasPermission(AquamarinePermission.STAFF)) .filter(p -> p.hasPermission(AquamarinePermission.STAFF))
.forEach(p -> plugin.sendPrefixed(staffAnnounce, p)); .peek(p -> plugin.sendPrefixed(staffAnnounce, p))
.forEach(p -> p.sendMessage("§f§o" + ticket.getMessage()));
return ticket; return ticket;
@ -83,10 +84,10 @@ public class XTicketManager implements Listener {
Player player = e.getPlayer(); Player player = e.getPlayer();
if (player.hasPermission(AquamarinePermission.STAFF)) { if (player.hasPermission(AquamarinePermission.STAFF) || player.isOp()) {
storage.getWaitingTicketsAsync((List<XTicket> tickets) -> { storage.getWaitingTicketsAsync((List<XTicket> tickets) -> {
if (tickets.size() > 0) { if (tickets.size() > 0) {
plugin.sendPrefixed(plugin.lang("ticket-join-announcement").replace("%ticketCount%", ""+tickets.size())); plugin.sendPrefixed(plugin.lang("ticket-join-announcement").replaceAll("\\$ticketCount\\$", ""+tickets.size()), player);
player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1f, 1.5f); player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1f, 1.5f);
} }
}); });

View File

@ -1,6 +1,8 @@
package fi.xeno.aquamarine; package fi.xeno.aquamarine;
import fi.xeno.aquamarine.command.CommandTicket; import fi.xeno.aquamarine.command.CommandTicket;
import fi.xeno.aquamarine.command.CommandXt;
import fi.xeno.aquamarine.command.CommandXti;
import fi.xeno.aquamarine.sql.XHikariDatabase; import fi.xeno.aquamarine.sql.XHikariDatabase;
import fi.xeno.aquamarine.storage.XFlatFileTicketDataStorage; import fi.xeno.aquamarine.storage.XFlatFileTicketDataStorage;
import fi.xeno.aquamarine.storage.XMemoryTicketDataStorage; import fi.xeno.aquamarine.storage.XMemoryTicketDataStorage;
@ -8,6 +10,8 @@ import fi.xeno.aquamarine.storage.XSQLTicketDataStorage;
import fi.xeno.aquamarine.storage.XTicketDataStorage; import fi.xeno.aquamarine.storage.XTicketDataStorage;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.NamespacedKey;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor; import org.bukkit.command.TabExecutor;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@ -19,6 +23,8 @@ import java.text.SimpleDateFormat;
import java.time.Instant; import java.time.Instant;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger; import java.util.logging.Logger;
public class XTicketsPlugin extends JavaPlugin { public class XTicketsPlugin extends JavaPlugin {
@ -87,8 +93,14 @@ public class XTicketsPlugin extends JavaPlugin {
} }
ticketManager = new XTicketManager(this, dataStorage); ticketManager = new XTicketManager(this, dataStorage);
Bukkit.getPluginManager().registerEvents(ticketManager, this);
registerCommand("ticket", new CommandTicket(this, ticketManager)); registerCommand("ticket", new CommandTicket(this, ticketManager));
registerCommand("xt", new CommandXt(this, ticketManager));
CommandXti ticketGuiCommand = new CommandXti(this, ticketManager);
Bukkit.getPluginManager().registerEvents(ticketGuiCommand, this);
registerCommand("xti", ticketGuiCommand);
logger.info("Aquamarine has been enabled!"); logger.info("Aquamarine has been enabled!");
@ -106,21 +118,31 @@ public class XTicketsPlugin extends JavaPlugin {
this.getCommand(label).setTabCompleter(command); this.getCommand(label).setTabCompleter(command);
} }
public void sendPrefixed(String message, Player... recipients) { public void sendPrefixed(String message, CommandSender... recipients) {
sendPrefixed(message, Arrays.asList(recipients)); sendPrefixed(message, Arrays.asList(recipients));
} }
public void sendPrefixed(String message, Iterable<Player> recipients) { public void sendPrefixed(String message, Iterable<CommandSender> recipients) {
String out = INFO_PREFIX + message; String out = INFO_PREFIX + message;
recipients.forEach(p -> p.sendMessage(out)); recipients.forEach(p -> p.sendMessage(out));
} }
public String lang(String key){ public String lang(String key){
return lang.getString(key, "lang["+key+"]"); return ChatColor.translateAlternateColorCodes('&', lang.getString(key, "lang["+key+"]"));
} }
public String formatTimestamp(long timestamp) { public String formatTimestamp(long timestamp) {
return dateFormat.format(Date.from(Instant.ofEpochMilli(timestamp))); return dateFormat.format(Date.from(Instant.ofEpochMilli(timestamp)));
} }
private static Map<String, NamespacedKey> keyCache = new ConcurrentHashMap<>();
public static NamespacedKey key(String n) {
if (!keyCache.containsKey(n)) keyCache.put(n, new NamespacedKey(getInstance(), n));
return keyCache.get(n);
}
public XTicketManager getTicketManager() {
return ticketManager;
}
} }

View File

@ -37,7 +37,7 @@ public class CommandTicket implements TabExecutor {
} }
if (args.length == 0) { if (args.length == 0) {
sender.sendMessage(plugin.lang("command-ticket-usage").replace("%label%", label)); sender.sendMessage(plugin.lang("command-ticket-usage").replaceAll("\\$label\\$", label));
return true; return true;
} }
@ -62,7 +62,7 @@ public class CommandTicket implements TabExecutor {
XTicket ticket = ticketManager.createTicket(player, msgJoiner.toString().trim()); XTicket ticket = ticketManager.createTicket(player, msgJoiner.toString().trim());
plugin.sendPrefixed(plugin.lang("ticket-created").replace("%ticketId%", ""+ticket.getId())); plugin.sendPrefixed(plugin.lang("ticket-created").replaceAll("\\$ticketId\\$", ""+ticket.getId()));
}); });

View File

@ -1,11 +1,16 @@
package fi.xeno.aquamarine.command; package fi.xeno.aquamarine.command;
import fi.xeno.aquamarine.AquamarinePermission; import fi.xeno.aquamarine.AquamarinePermission;
import fi.xeno.aquamarine.XText;
import fi.xeno.aquamarine.XTicketManager; import fi.xeno.aquamarine.XTicketManager;
import fi.xeno.aquamarine.XTicketsPlugin; import fi.xeno.aquamarine.XTicketsPlugin;
import fi.xeno.aquamarine.util.TimestampedValue; import fi.xeno.aquamarine.util.TimestampedValue;
import fi.xeno.aquamarine.util.XTicket; import fi.xeno.aquamarine.util.XTicket;
import net.md_5.bungee.api.chat.ComponentBuilder;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.OfflinePlayer;
import org.bukkit.Sound;
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;
@ -46,22 +51,245 @@ public class CommandXt implements TabExecutor {
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (!sender.hasPermission(AquamarinePermission.STAFF)) { if (!sender.hasPermission(AquamarinePermission.STAFF)) {
sender.sendMessage(plugin.lang("generic-no-permission")); plugin.sendPrefixed(plugin.lang("generic-no-permission"), sender);
return true; return true;
} }
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
if (args.length == 0) { if (args.length == 0) {
List<XTicket> tickets = ticketManager.getStorage().getWaitingTickets(); List<XTicket> tickets = ticketManager.getStorage().getWaitingTickets();
if (tickets.size() == 0) {
plugin.sendPrefixed(plugin.lang("generic-no-tickets"), sender);
return;
}
tickets.forEach(xt -> sender.spigot().sendMessage(xt.renderChatPreview(true)));
return; return;
} }
switch (args[0].toLowerCase(Locale.ROOT)) {
case "help": {
plugin.sendPrefixed("§b/" + label + " §7help, view, goto, solve, player, all");
break;
}
case "all": {
int page = 0;
if (args.length > 1) {
try {
page = Integer.parseInt(args[1])-1;
} catch (NumberFormatException e) {
plugin.sendPrefixed(plugin.lang("generic-invalid-number").replaceAll("\\$n\\$", args[1]), sender);
return;
}
}
List<XTicket> tickets = ticketManager.getStorage().getTickets();
if (tickets.size() == 0) {
plugin.sendPrefixed(plugin.lang("generic-no-tickets"), sender);
return;
}
int perPage = 8;
int totalPages = (int)Math.ceil((double)tickets.size() / (double)perPage);
tickets.stream()
.skip((long)page*perPage)
.limit(perPage)
.forEach(xt -> sender.spigot().sendMessage(xt.renderChatPreview(true)));
ComponentBuilder paginationMenu = new ComponentBuilder();
if (page > 0)
paginationMenu.append(XText.commandButton("§8[§b§8] ", "§7" + plugin.lang("generic-previous"), "/" + label + " all " + (page-1)));
if (page+1 < totalPages)
paginationMenu.append(XText.commandButton("§8[§b§8] ", "§7" + plugin.lang("generic-next"), "/" + label + " all " + (page+1)));
paginationMenu.append(XText.hoverText(plugin.lang("generic-page") + "§b" + (page+1) + "/" + totalPages, "§r"));
sender.spigot().sendMessage(paginationMenu.create());
break;
}
case "player":
case "from": {
if (args.length < 2) {
plugin.sendPrefixed(plugin.lang("command-ticket-player-usage").replaceAll("\\$label\\$", label), sender);
return;
}
String playerName = args[1];
OfflinePlayer ofp = Bukkit.getOfflinePlayer(playerName);
if (!ofp.hasPlayedBefore() && !ofp.isOnline()) {
plugin.sendPrefixed(plugin.lang("generic-invalid-player").replaceAll("\\$player\\$", playerName), sender);
return;
}
int page = 0;
if (args.length > 2) {
try {
page = Integer.parseInt(args[2]);
} catch (NumberFormatException e) {
plugin.sendPrefixed(plugin.lang("generic-invalid-number").replaceAll("\\$n\\$", args[1]), sender);
return;
}
}
List<XTicket> tickets = ticketManager.getStorage().getTicketsBySender(ofp.getUniqueId());
if (tickets.size() == 0) {
plugin.sendPrefixed(plugin.lang("generic-no-tickets"), sender);
return;
}
int perPage = 8;
int totalPages = (int)Math.ceil((double)tickets.size() / (double)perPage);
tickets.stream()
.skip((long)page*perPage)
.limit(perPage)
.forEach(xt -> sender.spigot().sendMessage(xt.renderChatPreview(true)));
ComponentBuilder paginationMenu = new ComponentBuilder();
if (page > 0)
paginationMenu.append(XText.commandButton("§8[§b§8] ", "§7" + plugin.lang("generic-previous"), "/" + label + " player " + ofp.getName() + " " + (page-1)));
if (page+1 < totalPages)
paginationMenu.append(XText.commandButton("§8[§b§8] ", "§7" + plugin.lang("generic-next"), "/" + label + " player " + ofp.getName() + " " + (page+1)));
paginationMenu.append(XText.hoverText(plugin.lang("generic-page") + "§b" + (page+1) + "/" + totalPages, "§r"));
sender.spigot().sendMessage(paginationMenu.create());
break;
}
case "view":
case "info":
case "show": {
if (args.length < 2) {
plugin.sendPrefixed(plugin.lang("command-ticket-view-usage").replaceAll("\\$label\\$", label), sender);
return;
}
int ticketId;
try {
ticketId = Integer.parseInt(args[1]);
} catch (NumberFormatException e) {
plugin.sendPrefixed(plugin.lang("generic-invalid-number").replaceAll("\\$n\\$", args[1]), sender);
return;
}
Optional<XTicket> ticketOptional = ticketManager.getStorage().getTicketByNumber(ticketId);
if (!ticketOptional.isPresent()) {
plugin.sendPrefixed(plugin.lang("generic-ticket-not-found"), sender);
return;
}
XTicket ticket = ticketOptional.get();
sender.spigot().sendMessage(ticket.renderChatPreview(true, true));
break;
}
case "goto": {
if (!(sender instanceof Player)) {
plugin.sendPrefixed(plugin.lang("generic-not-player"), sender);
return;
}
if (args.length < 2) {
plugin.sendPrefixed(plugin.lang("command-ticket-goto-usage").replaceAll("\\$label\\$", label), sender);
return;
}
int ticketId;
try {
ticketId = Integer.parseInt(args[1]);
} catch (NumberFormatException e) {
plugin.sendPrefixed(plugin.lang("generic-invalid-number").replaceAll("\\$n\\$", args[1]), sender);
return;
}
Optional<XTicket> ticketOptional = ticketManager.getStorage().getTicketByNumber(ticketId);
if (!ticketOptional.isPresent()) {
plugin.sendPrefixed(plugin.lang("generic-ticket-not-found"), sender);
return;
}
XTicket ticket = ticketOptional.get();
Player player = (Player)sender;
Location location = ticket.getLocation().toLocation();
if (location == null || !location.isWorldLoaded()) {
plugin.sendPrefixed(plugin.lang("generic-invalid-ticket"), sender);
return;
}
if (!location.getChunk().isLoaded())
location.getChunk().load();
Bukkit.getScheduler().runTaskLater(plugin, () -> {
player.teleport(location);
plugin.sendPrefixed(plugin.lang("command-ticket-goto-teleport"), sender);
player.playSound(player.getLocation(), "entity.enderman.teleport", 1f, 1f);
player.playSound(player.getLocation(), "entity.endermen.teleport", 1f, 1f);
}, 2L);
break;
}
case "solve": {
if (args.length < 2) {
plugin.sendPrefixed(plugin.lang("command-ticket-solve-usage").replaceAll("\\$label\\$", label), sender);
return;
}
StringJoiner commentJoiner = new StringJoiner(" ");
Arrays.stream(args).skip(2).forEach(commentJoiner::add);
String comment = commentJoiner.toString();
int ticketId;
try {
ticketId = Integer.parseInt(args[1]);
} catch (NumberFormatException e) {
plugin.sendPrefixed(plugin.lang("generic-invalid-number").replaceAll("\\$n\\$", args[1]), sender);
return;
}
Optional<XTicket> ticketOptional = ticketManager.getStorage().getTicketByNumber(ticketId);
if (!ticketOptional.isPresent()) {
plugin.sendPrefixed(plugin.lang("generic-ticket-not-found"), sender);
return;
}
XTicket ticket = ticketOptional.get();
ticketManager.getStorage().solveTicket(ticket, sender, comment);
break;
}
}
}); });
return true; return true;

View File

@ -0,0 +1,149 @@
package fi.xeno.aquamarine.command;
import fi.xeno.aquamarine.AquamarinePermission;
import fi.xeno.aquamarine.XText;
import fi.xeno.aquamarine.XTicketManager;
import fi.xeno.aquamarine.XTicketsPlugin;
import fi.xeno.aquamarine.util.TimestampedValue;
import fi.xeno.aquamarine.util.XTicket;
import net.md_5.bungee.api.chat.ComponentBuilder;
import org.bukkit.*;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class CommandXti implements TabExecutor, Listener {
private final XTicketsPlugin plugin;
private final XTicketManager ticketManager;
private final XTicketGui ticketGui;
public CommandXti(XTicketsPlugin plugin, XTicketManager ticketManager) {
this.plugin = plugin;
this.ticketManager = ticketManager;
this.ticketGui = new XTicketGui();
}
@Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (!sender.hasPermission(AquamarinePermission.STAFF)) {
plugin.sendPrefixed(plugin.lang("generic-no-permission"), sender);
return true;
}
if (!(sender instanceof Player)) {
plugin.sendPrefixed(plugin.lang("generic-not-player"), sender);
return true;
}
Player player = (Player)sender;
ticketManager.getStorage().getWaitingTicketsAsync(tickets -> {
if (tickets.size() == 0) {
plugin.sendPrefixed(plugin.lang("generic-no-tickets"), player);
return;
}
sync(() -> {
Inventory inv = ticketGui.getInventory();
ticketGui.refresh(tickets);
player.playSound(player.getLocation(), Sound.BLOCK_CHEST_OPEN, 1f, 1f);
player.openInventory(inv);
});
});
return true;
}
@EventHandler
private void onInventoryClick(InventoryClickEvent e) {
if (e.getInventory().getHolder() == null || !(e.getInventory().getHolder() instanceof XTicketGui)) {
return;
}
if (e.getCurrentItem() == null) {
return;
}
Player player = (Player)e.getWhoClicked();
ItemStack item = e.getCurrentItem();
PersistentDataContainer pd = item.getItemMeta().getPersistentDataContainer();
String ticketId = pd.getOrDefault(XTicketsPlugin.key("xTicketId"), PersistentDataType.STRING, "?");
if (e.getClick().equals(ClickType.LEFT)) {
player.performCommand("xt goto " + ticketId);
} else if (e.getClick().equals(ClickType.RIGHT)) {
player.performCommand("xt solve " + ticketId + " -");
}
player.closeInventory();
e.setCancelled(true);
}
@EventHandler
private void onInventoryClick(InventoryDragEvent e) {
if (e.getInventory().getHolder() != null && e.getInventory().getHolder() instanceof XTicketGui) {
e.setCancelled(true);
}
}
@Override
public List<String> onTabComplete(CommandSender sender, Command cmd, String label, String[] args) {
return null;
}
private void sync(Runnable r) {
Bukkit.getScheduler().runTask(plugin, r);
}
private static class XTicketGui implements InventoryHolder {
private final Inventory inv;
public XTicketGui() {
this.inv = Bukkit.createInventory(this, 9*6, "* ✎ *");
}
public synchronized void refresh(List<XTicket> tickets) {
inv.clear();
tickets.forEach(t -> inv.addItem(t.renderMenuItem()));
}
@Override
public Inventory getInventory() {
return inv;
}
}
}

View File

@ -6,6 +6,7 @@ import fi.xeno.aquamarine.XTicketsPlugin;
import fi.xeno.aquamarine.util.XStoredLocation; import fi.xeno.aquamarine.util.XStoredLocation;
import fi.xeno.aquamarine.util.XTicket; import fi.xeno.aquamarine.util.XTicket;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.io.*; import java.io.*;
@ -38,7 +39,7 @@ public class XFlatFileTicketDataStorage extends XTicketDataStorage {
} }
private void load() throws IOException { private synchronized void load() throws IOException {
tickets.clear(); tickets.clear();
@ -71,7 +72,7 @@ public class XFlatFileTicketDataStorage extends XTicketDataStorage {
} }
private void save() { private synchronized void save() {
JsonArray out = new JsonArray(); JsonArray out = new JsonArray();
tickets.values() tickets.values()
@ -82,6 +83,7 @@ public class XFlatFileTicketDataStorage extends XTicketDataStorage {
try { try {
Writer writer = new FileWriter(file); Writer writer = new FileWriter(file);
gson.toJson(out, writer); gson.toJson(out, writer);
writer.close();
} catch (IOException e) { } catch (IOException e) {
plugin.getLogger().severe("Unable to save ticket JSON data:"); plugin.getLogger().severe("Unable to save ticket JSON data:");
e.printStackTrace(); e.printStackTrace();
@ -95,7 +97,7 @@ public class XFlatFileTicketDataStorage extends XTicketDataStorage {
@Override @Override
public XTicket createTicket(Player player, String message) { public synchronized XTicket createTicket(Player player, String message) {
XTicket ticket = XTicket.asPlayer(getNextTicketId(), player, message); XTicket ticket = XTicket.asPlayer(getNextTicketId(), player, message);
tickets.put(ticket.getId(), ticket); tickets.put(ticket.getId(), ticket);
@ -107,11 +109,11 @@ public class XFlatFileTicketDataStorage extends XTicketDataStorage {
} }
@Override @Override
public void solveTicket(XTicket ticket, Player solver, String comment) { public synchronized void solveTicket(XTicket ticket, CommandSender solver, String comment) {
ticket.setSolved(true); ticket.setSolved(true);
ticket.setSolvedByUuid(solver.getUniqueId()); ticket.setSolvedByUuid(solver instanceof Player ? ((Player)solver).getUniqueId() : new UUID(0,0));
ticket.setSolvedByName(solver.getName()); ticket.setSolvedByName(solver.getName());
ticket.setSolveComment(comment); ticket.setSolveComment(comment);
@ -120,17 +122,18 @@ public class XFlatFileTicketDataStorage extends XTicketDataStorage {
// this SHOULD be unnecessary, but just to be sure... // this SHOULD be unnecessary, but just to be sure...
tickets.put(ticket.getId(), ticket); tickets.put(ticket.getId(), ticket);
announceSolveTicket(ticket, solver, comment);
saveAsync(); saveAsync();
} }
@Override @Override
public Optional<XTicket> getTicketByNumber(int id) { public synchronized Optional<XTicket> getTicketByNumber(int id) {
return Optional.ofNullable(tickets.getOrDefault(id, null)); return Optional.ofNullable(tickets.getOrDefault(id, null));
} }
@Override @Override
public List<XTicket> getTickets() { public synchronized List<XTicket> getTickets() {
return tickets.values() return tickets.values()
.stream() .stream()
.sorted(Comparator.comparingInt(XTicket::getId)) .sorted(Comparator.comparingInt(XTicket::getId))
@ -138,7 +141,7 @@ public class XFlatFileTicketDataStorage extends XTicketDataStorage {
} }
@Override @Override
public List<XTicket> getWaitingTickets() { public synchronized List<XTicket> getWaitingTickets() {
return tickets.values() return tickets.values()
.stream() .stream()
.filter(t -> !t.isSolved()) .filter(t -> !t.isSolved())
@ -147,7 +150,7 @@ public class XFlatFileTicketDataStorage extends XTicketDataStorage {
} }
@Override @Override
public List<XTicket> getSolvedTickets() { public synchronized List<XTicket> getSolvedTickets() {
return tickets.values() return tickets.values()
.stream() .stream()
.filter(XTicket::isSolved) .filter(XTicket::isSolved)
@ -156,7 +159,7 @@ public class XFlatFileTicketDataStorage extends XTicketDataStorage {
} }
@Override @Override
public List<XTicket> getWaitingNearbyTickets(XStoredLocation location, double radius) { public synchronized List<XTicket> getWaitingNearbyTickets(XStoredLocation location, double radius) {
return tickets.values() return tickets.values()
.stream() .stream()
.filter(t -> !t.isSolved() && t.getLocation().isInRadius(location, radius)) .filter(t -> !t.isSolved() && t.getLocation().isInRadius(location, radius))
@ -165,7 +168,7 @@ public class XFlatFileTicketDataStorage extends XTicketDataStorage {
} }
@Override @Override
public List<XTicket> getWaitingTicketsBySender(UUID uuid) { public synchronized List<XTicket> getWaitingTicketsBySender(UUID uuid) {
return tickets.values() return tickets.values()
.stream() .stream()
.filter(t -> !t.isSolved() && t.getSentByUuid().equals(uuid)) .filter(t -> !t.isSolved() && t.getSentByUuid().equals(uuid))
@ -174,7 +177,7 @@ public class XFlatFileTicketDataStorage extends XTicketDataStorage {
} }
@Override @Override
public List<XTicket> getTicketsBySender(UUID uuid) { public synchronized List<XTicket> getTicketsBySender(UUID uuid) {
return tickets.values() return tickets.values()
.stream() .stream()
.filter(t -> t.getSentByUuid().equals(uuid)) .filter(t -> t.getSentByUuid().equals(uuid))
@ -183,7 +186,7 @@ public class XFlatFileTicketDataStorage extends XTicketDataStorage {
} }
@Override @Override
public List<XTicket> getTicketsBySolver(UUID uuid) { public synchronized List<XTicket> getTicketsBySolver(UUID uuid) {
return tickets.values() return tickets.values()
.stream() .stream()
.filter(t -> t.isSolved() && t.getSolvedByUuid().equals(uuid)) .filter(t -> t.isSolved() && t.getSolvedByUuid().equals(uuid))
@ -192,12 +195,12 @@ public class XFlatFileTicketDataStorage extends XTicketDataStorage {
} }
@Override @Override
public int getNextTicketId() { public synchronized int getNextTicketId() {
return tickets.keySet().stream().max(Comparator.comparingInt(Integer::intValue)).orElse(0) + 1; return tickets.keySet().stream().max(Comparator.comparingInt(Integer::intValue)).orElse(0) + 1;
} }
@Override @Override
public void close() { public synchronized void close() {
plugin.getLogger().info("Saving ticket storage..."); plugin.getLogger().info("Saving ticket storage...");
save(); save();
} }

View File

@ -6,6 +6,7 @@ import fi.xeno.aquamarine.XTicketsPlugin;
import fi.xeno.aquamarine.util.XStoredLocation; import fi.xeno.aquamarine.util.XStoredLocation;
import fi.xeno.aquamarine.util.XTicket; import fi.xeno.aquamarine.util.XTicket;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.io.*; import java.io.*;
@ -33,11 +34,11 @@ public class XMemoryTicketDataStorage extends XTicketDataStorage {
} }
@Override @Override
public void solveTicket(XTicket ticket, Player solver, String comment) { public void solveTicket(XTicket ticket, CommandSender solver, String comment) {
ticket.setSolved(true); ticket.setSolved(true);
ticket.setSolvedByUuid(solver.getUniqueId()); ticket.setSolvedByUuid(solver instanceof Player ? ((Player)solver).getUniqueId() : new UUID(0,0));
ticket.setSolvedByName(solver.getName()); ticket.setSolvedByName(solver.getName());
ticket.setSolveComment(comment); ticket.setSolveComment(comment);
@ -46,6 +47,8 @@ public class XMemoryTicketDataStorage extends XTicketDataStorage {
// this SHOULD be unnecessary, but just to be sure... // this SHOULD be unnecessary, but just to be sure...
tickets.put(ticket.getId(), ticket); tickets.put(ticket.getId(), ticket);
announceSolveTicket(ticket, solver, comment);
} }
@Override @Override

View File

@ -4,6 +4,7 @@ import fi.xeno.aquamarine.XTicketsPlugin;
import fi.xeno.aquamarine.sql.XHikariDatabase; import fi.xeno.aquamarine.sql.XHikariDatabase;
import fi.xeno.aquamarine.util.XStoredLocation; import fi.xeno.aquamarine.util.XStoredLocation;
import fi.xeno.aquamarine.util.XTicket; import fi.xeno.aquamarine.util.XTicket;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.sql.Connection; import java.sql.Connection;
@ -118,11 +119,11 @@ public class XSQLTicketDataStorage extends XTicketDataStorage {
} }
@Override @Override
public void solveTicket(XTicket ticket, Player solver, String comment) { public void solveTicket(XTicket ticket, CommandSender solver, String comment) {
// these SHOULD be unneeded, but just in case... // these SHOULD be unneeded, but just in case...
ticket.setSolved(true); ticket.setSolved(true);
ticket.setSolvedByUuid(solver.getUniqueId()); ticket.setSolvedByUuid(solver instanceof Player ? ((Player)solver).getUniqueId() : new UUID(0,0));
ticket.setSolvedByName(solver.getName()); ticket.setSolvedByName(solver.getName());
ticket.setSolveComment(comment); ticket.setSolveComment(comment);
ticket.setTimeSolved(System.currentTimeMillis()); ticket.setTimeSolved(System.currentTimeMillis());
@ -137,7 +138,7 @@ public class XSQLTicketDataStorage extends XTicketDataStorage {
"SET isSolved = 1, solvedByUuid = ?, solvedByName = ?, solveComment = ?, timeSolved = ? " + "SET isSolved = 1, solvedByUuid = ?, solvedByName = ?, solveComment = ?, timeSolved = ? " +
"WHERE ticketId = ?"); "WHERE ticketId = ?");
st.setString(1, solver.getUniqueId().toString()); st.setString(1, (solver instanceof Player ? ((Player)solver).getUniqueId() : new UUID(0,0)).toString());
st.setString(2, solver.getName()); st.setString(2, solver.getName());
st.setString(3, comment); st.setString(3, comment);
@ -146,6 +147,7 @@ public class XSQLTicketDataStorage extends XTicketDataStorage {
st.setInt(5, ticket.getId()); st.setInt(5, ticket.getId());
st.executeUpdate(); st.executeUpdate();
announceSolveTicket(ticket, solver, comment);
} catch (SQLException throwables) { } catch (SQLException throwables) {
throwables.printStackTrace(); throwables.printStackTrace();

View File

@ -1,9 +1,11 @@
package fi.xeno.aquamarine.storage; package fi.xeno.aquamarine.storage;
import fi.xeno.aquamarine.AquamarinePermission;
import fi.xeno.aquamarine.XTicketsPlugin; import fi.xeno.aquamarine.XTicketsPlugin;
import fi.xeno.aquamarine.util.XStoredLocation; import fi.xeno.aquamarine.util.XStoredLocation;
import fi.xeno.aquamarine.util.XTicket; import fi.xeno.aquamarine.util.XTicket;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.List; import java.util.List;
@ -14,7 +16,7 @@ import java.util.function.Consumer;
public abstract class XTicketDataStorage { public abstract class XTicketDataStorage {
public abstract XTicket createTicket(Player player, String message); public abstract XTicket createTicket(Player player, String message);
public abstract void solveTicket(XTicket ticket, Player solver, String comment); public abstract void solveTicket(XTicket ticket, CommandSender solver, String comment);
public abstract Optional<XTicket> getTicketByNumber(int id); public abstract Optional<XTicket> getTicketByNumber(int id);
@ -42,7 +44,7 @@ public abstract class XTicketDataStorage {
}); });
} }
public Optional<XTicket> solveTicket(int ticketId, Player solver, String comment) { public Optional<XTicket> solveTicket(int ticketId, CommandSender solver, String comment) {
XTicket ticket = getTicketByNumber(ticketId).orElse(null); XTicket ticket = getTicketByNumber(ticketId).orElse(null);
@ -55,6 +57,21 @@ public abstract class XTicketDataStorage {
} }
public void announceSolveTicket(XTicket ticket, CommandSender sender, String comment) {
String announceText = XTicketsPlugin.getInstance().lang("command-ticket-solved")
.replaceAll("\\$solver\\$", sender.getName())
.replaceAll("\\$n\\$", ""+ticket.getId())
.replaceAll("\\$comment\\$", comment);
Bukkit.getOnlinePlayers()
.stream()
.filter(p -> p.hasPermission(AquamarinePermission.STAFF)
|| p.getUniqueId().equals(ticket.getSentByUuid()))
.forEach(p -> XTicketsPlugin.getInstance().sendPrefixed(announceText, p));
}
public void solveTicketAsync(int ticketId, Player solver, String comment, Consumer<Optional<XTicket>> callback) { public void solveTicketAsync(int ticketId, Player solver, String comment, Consumer<Optional<XTicket>> callback) {
Bukkit.getScheduler().runTaskAsynchronously(XTicketsPlugin.getInstance(), () -> callback.accept(solveTicket(ticketId, solver, comment))); Bukkit.getScheduler().runTaskAsynchronously(XTicketsPlugin.getInstance(), () -> callback.accept(solveTicket(ticketId, solver, comment)));
} }

View File

@ -6,11 +6,20 @@ import fi.xeno.aquamarine.XTicketsPlugin;
import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ComponentBuilder; import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.TextComponent;
import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors;
public class XTicket { public class XTicket {
@ -125,19 +134,26 @@ public class XTicket {
this.timeSolved = timeSolved; this.timeSolved = timeSolved;
} }
public BaseComponent[] renderChatPreview(boolean withButtons) { public BaseComponent[] renderChatPreview(boolean withButtons) {
return renderChatPreview(withButtons, false);
}
public BaseComponent[] renderChatPreview(boolean withButtons, boolean withFullText) {
ComponentBuilder out = new ComponentBuilder(); ComponentBuilder out = new ComponentBuilder();
XTicketsPlugin plugin = XTicketsPlugin.getInstance(); XTicketsPlugin plugin = XTicketsPlugin.getInstance();
if (withButtons) { if (withButtons) {
out.append(XText.commandButton("§7[§e»§7] ", "§e" + XTicketsPlugin.getInstance().lang("ticket-preview-teleport"), "/xt goto " + this.getId())); out.append(XText.commandButton("§8[§e»§8] ", "§e" + XTicketsPlugin.getInstance().lang("ticket-preview-teleport"), "/xt goto " + this.getId()));
out.append(XText.commandButton("§7[§a✔§7] ", "§a" + XTicketsPlugin.getInstance().lang("ticket-preview-solve"), "/xt solve " + this.getId())); out.append(XText.commandSuggestButton("§8[§a✔§8] ", "§a" + XTicketsPlugin.getInstance().lang("ticket-preview-solve"), "/xt solve " + this.getId()));
} }
out.append(TextComponent.fromLegacyText("§b#" + this.getId() + " §f" + this.getSentByName() + " §7")); out.append(TextComponent.fromLegacyText((this.isSolved ? "§a" : "§b") + "#" + this.getId() + " §f" + this.getSentByName() + " §7"));
String previewString = this.message.length() > 40 ? this.message.substring(0, 40).trim() + "..." : this.message; String previewString = this.message.length() > 40 && !withFullText
? this.message.substring(0, 40).trim() + "..."
: this.message;
String solvedString = ""; String solvedString = "";
if (this.isSolved) { if (this.isSolved) {
@ -148,12 +164,12 @@ public class XTicket {
} }
String hoverString = plugin.lang("ticket-hover-title").replace("%ticketId%", ""+this.id) + "\n" + String hoverString = plugin.lang("ticket-hover-title").replaceAll("\\$ticketId\\$", ""+this.id) + "\n" +
String.format(plugin.lang("ticket-hover-sender"), this.sentByName) + "\n" + String.format(plugin.lang("ticket-hover-sender"), this.sentByName) + "\n" +
String.format(plugin.lang("ticket-hover-timestamp"), plugin.formatTimestamp(this.timestamp)) + "\n" + String.format(plugin.lang("ticket-hover-timestamp"), plugin.formatTimestamp(this.timestamp)) + "\n" +
String.format(plugin.lang("ticket-hover-location"), this.location.toReadable() + "\n" + String.format(plugin.lang("ticket-hover-location"), this.location.toReadable()) + "\n" +
solvedString + "\n\n" + solvedString + "\n\n" +
"§f§o" + XText.wordWrap(this.message, 40)); "§f§o" + XText.wordWrap(this.message, 40);
out.append(XText.hoverText("§7" + previewString, hoverString)); out.append(XText.hoverText("§7" + previewString, hoverString));
@ -161,6 +177,38 @@ public class XTicket {
} }
public ItemStack renderMenuItem() {
XTicketsPlugin plugin = XTicketsPlugin.getInstance();
ItemStack item = new ItemStack(Material.PAPER);
ItemMeta meta = item.getItemMeta();
meta.setDisplayName(plugin.lang("ticket-hover-title").replaceAll("\\$ticketId\\$", ""+this.getId()));
List<String> lore = new ArrayList<>();
lore.add(String.format(plugin.lang("ticket-hover-sender"), this.sentByName));
lore.add(String.format(plugin.lang("ticket-hover-timestamp"), plugin.formatTimestamp(this.timestamp)));
lore.add(String.format(plugin.lang("ticket-hover-location"), this.location.toReadable()));
lore.add("§r");
lore.addAll(Arrays.stream(XText.wordWrap(this.message, 50).split("\\n"))
.map(l -> "§7" + l)
.collect(Collectors.toList()));
lore.add("§r");
lore.add("§e■□ " + plugin.lang("gui-click-teleport"));
lore.add("§a□■ " + plugin.lang("gui-click-solve"));
meta.setLore(lore);
PersistentDataContainer pd = meta.getPersistentDataContainer();
pd.set(XTicketsPlugin.key("xTicketId"), PersistentDataType.STRING, ""+this.getId());
item.setItemMeta(meta);
return item;
}
public JsonObject toJson() { public JsonObject toJson() {
JsonObject out = new JsonObject(); JsonObject out = new JsonObject();