Front End Update v1.0

This commit is contained in:
Paldiu 2023-01-26 19:30:35 -06:00
parent 4205be4aae
commit 940765176f
17 changed files with 358 additions and 48 deletions

View File

@ -1,7 +1,17 @@
package mc.unraveled.reforged.api;
public interface Baker {
/**
* This method should force immutability on all lists, maps, and sets.
* This method should also compile raw data into a compound data format,
* and mark that data as immutable.
*/
void bake();
/**
* This method should force mutability on all lists, maps, and sets.
* This method should also decompile compound data formats into raw data,
* and mark that data as mutable.
*/
void unbake();
}

View File

@ -1,9 +1,39 @@
package mc.unraveled.reforged.api;
import net.kyori.adventure.text.Component;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import java.util.ArrayList;
import java.util.List;
public interface ICommandBase extends TabExecutor {
Component run(CommandSender sender, String[] args);
/**
* This represents the actual command execution.
* It returns a component, which is sent back to the sender.
* You can send the sender nothing by returning a {@link Component#empty()}.
*
* @param sender The sender of the command.
* @param args The arguments of the command, for subcommand parsing.
* @return A component to be sent back to the sender.
* This allows for us to do things like return a direct error message,
* or signify the command completed successfully.
*/
Component cmd(CommandSender sender, String[] args);
/**
* This represents the tab completion of the command.
* It returns a list of strings, which are sent back to the sender.
* This by default returns an empty list, which is used by {@link #onTabComplete(CommandSender, Command, String, String[])}
* to signify that there are no tab completions. If you want to implement your own completions,
* you must override this method.
*
* @param sender The sender of the command.
* @param args The arguments of the command, for subcommand parsing.
* @return A list of strings to be sent back to the sender.
*/
default List<String> tab(CommandSender sender, String[] args) {
return new ArrayList<>();
}
}

View File

@ -3,8 +3,6 @@ package mc.unraveled.reforged.api.annotations;
public @interface CommandInfo {
String name();
String permission() default "traverse.op";
String usage() default "/<command>";
String description() default "No description provided.";

View File

@ -1,5 +1,6 @@
package mc.unraveled.reforged.banning;
import lombok.Getter;
import lombok.SneakyThrows;
import mc.unraveled.reforged.api.Baker;
import mc.unraveled.reforged.api.Locker;
@ -11,9 +12,11 @@ import java.util.Set;
import java.util.stream.Collectors;
public final class BanManager implements Locker, Baker {
@Getter
private final Traverse plugin;
private Set<AbstractBan> storedBans; // This should only be reassigned by the baker.
@Getter
private boolean baked = false; // This should only be reassigned by the baker.
@SneakyThrows
@ -35,36 +38,44 @@ public final class BanManager implements Locker, Baker {
public void insert(AbstractBan ban) {
if (baked) throw new IllegalStateException("Cannot insert into a baked list.");
storedBans.add(ban);
lock().notify();
storedBans.add(ban);
}
public void eject(AbstractBan ban) {
if (baked) throw new IllegalStateException("Cannot eject from a baked list.");
storedBans.remove(ban);
lock().notify();
storedBans.remove(ban);
}
public void save() {
if (!baked) throw new IllegalStateException("Cannot save an unbaked list.");
lock().notify();
DBBan banHandler = new DBBan(plugin.getSQLManager().establish());
storedBans.forEach(banHandler::insert);
banHandler.close();
}
@Override
public void bake() {
if (baked) return;
lock().notify();
storedBans = storedBans.stream().collect(Collectors.toUnmodifiableSet());
baked = true;
lock().notify();
}
@Override
public void unbake() {
if (!baked) return;
lock().notify();
storedBans = new HashSet<>(storedBans);
baked = false;
lock().notify();
}
}

View File

@ -1,17 +1,29 @@
package mc.unraveled.reforged.banning;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.time.Instant;
import java.util.Date;
public class SimpleBan extends AbstractBan {
public SimpleBan(Player player, CommandSender source, String reason, Date propogated, Date expiry, boolean active) {
public SimpleBan(Player player, CommandSender source, String reason, Date expiry, boolean active) {
super(player.getUniqueId().toString(),
player.getAddress().getAddress().getHostAddress(),
source.getName(),
reason,
propogated.getTime(),
Date.from(Instant.now()).getTime(),
expiry.getTime(),
active);
}
public SimpleBan(OfflinePlayer player, CommandSender source, String reason, Date expiry, boolean active) {
super(player.getUniqueId().toString(),
"",
source.getName(),
reason,
Date.from(Instant.now()).getTime(),
expiry.getTime(),
active);
}

View File

@ -0,0 +1,57 @@
package mc.unraveled.reforged.command;
import mc.unraveled.reforged.api.annotations.CommandInfo;
import mc.unraveled.reforged.banning.BanManager;
import mc.unraveled.reforged.banning.SimpleBan;
import mc.unraveled.reforged.command.base.AbstractCommandBase;
import mc.unraveled.reforged.plugin.Traverse;
import mc.unraveled.reforged.util.Utilities;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Date;
@CommandInfo(name = "ban",
description = "Ban a player",
usage = "/ban <player> <reason> [duration]")
public class BanCMD extends AbstractCommandBase {
public BanCMD(@NotNull Traverse plugin) {
super(plugin, "ban");
}
@Override
public Component cmd(CommandSender sender, String[] args) {
if (args.length < 2) {
return Component.text("Usage: /ban <player> <reason> [duration]");
}
BanManager manager = getPlugin().getBanManager();
OfflinePlayer target = (getPlugin().getServer().getPlayer(args[0]) != null) ? getPlugin().getServer().getPlayer(args[0]) : getPlugin().getServer().getOfflinePlayer(args[0]);
String reason = args[1];
String duration = args.length > 2 ? args[2] : String.valueOf(60 * 24L);
Date expiry = Utilities.parseDate(duration);
String expiryString = Utilities.parseDateToString(expiry);
if (target == null) {
return MessageDefaults.MSG_NOT_FOUND;
}
manager.unbake();
SimpleBan ban = new SimpleBan(target, sender, reason, Utilities.parseDate(duration), true);
if (target.isOnline()) {
((Player) target).kick(Component.text("You have been banned for " + reason + " until " + expiryString));
}
manager.insert(ban);
manager.bake();
manager.save();
return Component.text("Successfully banned user " + target.getName() + " for " + reason + " until " + expiryString).color(NamedTextColor.YELLOW);
}
}

View File

@ -0,0 +1,97 @@
package mc.unraveled.reforged.command;
import mc.unraveled.reforged.api.annotations.CommandInfo;
import mc.unraveled.reforged.command.base.AbstractCommandBase;
import mc.unraveled.reforged.permission.Rank;
import mc.unraveled.reforged.permission.RankManager;
import mc.unraveled.reforged.plugin.Traverse;
import mc.unraveled.reforged.util.Context;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicBoolean;
@CommandInfo(name = "group",
description = "Manages groups for all players.",
usage = "/group <add | del> <player> <group>")
public class GroupCMD extends AbstractCommandBase {
private final Rank[] ranks = Rank.values();
public GroupCMD(@NotNull Traverse plugin) {
super(plugin, "group");
}
@Override
public Component cmd(CommandSender sender, String[] args) {
if (args.length < 4) {
return Component.text("Usage: /group <group> <add | del> <player <player> | permission <permission>>");
}
String group = args[0];
String action = args[1];
String target = args[2];
String value = args[3];
AtomicBoolean invalid = new AtomicBoolean(false);
Context<Rank> rankContext = new Context<>(Rank.NON_OP);
Arrays.stream(ranks)
.filter(rank -> rank.getAttachment().getName().equalsIgnoreCase(group))
.findFirst()
.ifPresentOrElse(rankContext::setContext, () -> invalid.set(true));
if (invalid.get()) return Component.text("Invalid group!").color(NamedTextColor.RED);
boolean actionType; // false is delete, true is add
switch (action) {
case "add":
actionType = true;
break;
case "del":
actionType = false;
break;
default:
return Component.text("Invalid action!").color(NamedTextColor.RED);
}
RankManager manager = getPlugin().getRankManager();
OfflinePlayer player;
switch (target) {
case "player":
if (getPlugin().getServer().getPlayer(value) != null) {
player = getPlugin().getServer().getPlayer(value);
} else {
return Component.text("Invalid player!").color(NamedTextColor.RED);
}
if (player == null) {
throw new IllegalStateException("Player cannot be null!");
}
if (actionType) {
manager.insertPlayer(rankContext.getContext(), player);
return Component.text("Added player " + player.getName() + " to group " + group + "!").color(NamedTextColor.GREEN);
} else {
manager.ejectPlayer(rankContext.getContext(), player);
return Component.text("Removed player " + player.getName() + " from group " + group + "!").color(NamedTextColor.GREEN);
}
case "permission":
if (actionType) {
manager.insertPermission(rankContext.getContext(), value);
return Component.text("Added permission " + value + " to group " + group + "!").color(NamedTextColor.GREEN);
} else {
manager.ejectPermission(rankContext.getContext(), value);
return Component.text("Removed permission " + value + " from group " + group + "!").color(NamedTextColor.GREEN);
}
default:
return Component.text("Invalid target!").color(NamedTextColor.RED);
}
}
}

View File

@ -1,16 +1,21 @@
package mc.unraveled.reforged.command;
import mc.unraveled.reforged.api.annotations.CommandInfo;
import mc.unraveled.reforged.command.base.AbstractCommandBase;
import mc.unraveled.reforged.plugin.Traverse;
import net.kyori.adventure.text.Component;
import org.bukkit.command.CommandSender;
@CommandInfo(name = "traverse",
usage = "/traverse",
description = "Traverse command")
public class TraverseCMD extends AbstractCommandBase {
public TraverseCMD() {
super("traverse.cmd", "You do not have permission to use this command!", true);
public TraverseCMD(Traverse plugin) {
super(plugin, "traverse", "You do not have permission to use this command!", true);
}
@Override
public Component run(CommandSender sender, String[] args) {
public Component cmd(CommandSender sender, String[] args) {
return Component.text("Hello!");
}
}

View File

@ -1,10 +1,13 @@
package mc.unraveled.reforged.command.base;
import lombok.Getter;
import mc.unraveled.reforged.api.ICommandBase;
import mc.unraveled.reforged.permission.TPermission;
import mc.unraveled.reforged.plugin.Traverse;
import mc.unraveled.reforged.util.BasicColors;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
@ -13,62 +16,75 @@ import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public abstract class AbstractCommandBase extends TPermission implements ICommandBase {
@Getter
private final Traverse plugin;
/**
* @param plugin The plugin that owns this command
* @param permission The permission the user should have to run the command
* @param permissionMessage The message to send when the user does not have the permission to run the command.
* @param allowConsole Whether to allow the command to be run anywhere, or only in game.
*/
public AbstractCommandBase(@NotNull String permission, String permissionMessage, boolean allowConsole) {
public AbstractCommandBase(@NotNull Traverse plugin, @NotNull String permission, String permissionMessage, boolean allowConsole) {
super(permission, permissionMessage, allowConsole);
this.plugin = plugin;
}
/**
* @param plugin The plugin that owns this command
* @param permission The permission the user should have to run the command
* @param permissionMessage The message to send when the user does not have the permission to run the command.
*/
public AbstractCommandBase(@NotNull String permission, String permissionMessage) {
this(permission, permissionMessage, true);
public AbstractCommandBase(@NotNull Traverse plugin, @NotNull String permission, String permissionMessage) {
this(plugin, permission, permissionMessage, true);
}
/**
* @param plugin The plugin that owns this command
* @param permission The permission the user should have to run the command
* @param allowConsole Whether to allow the command to be run anywhere, or only in game.
*/
public AbstractCommandBase(@NotNull String permission, boolean allowConsole) {
this(permission, "You do not have permission to use this command!", allowConsole);
public AbstractCommandBase(@NotNull Traverse plugin, @NotNull String permission, boolean allowConsole) {
this(plugin, permission, "You do not have permission to use this command!", allowConsole);
}
/**
* @param plugin The plugin that owns this command
* @param permission The permission the user should have to run the command
*/
public AbstractCommandBase(@NotNull String permission) {
this(permission, "You do not have permission to use this command!", true);
public AbstractCommandBase(@NotNull Traverse plugin, @NotNull String permission) {
this(plugin, permission, "You do not have permission to use this command!", true);
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String lbl, String[] args) {
if (!hasPermission(sender)) {
sender.sendMessage(msg(getPermissionMessage(), BasicColors.RED));
sender.sendMessage(MessageDefaults.MSG_NO_PERMS);
return true;
}
if (!(sender instanceof Player) && !allowConsole()) {
sender.sendMessage(msg("This command can only be run in game."));
sender.sendMessage(MessageDefaults.MSG_NOT_CONSOLE);
return true;
}
Component result = cmd(sender, args);
if (!result.equals(Component.empty())) {
sender.sendMessage(result);
}
sender.sendMessage(run(sender, args));
return true;
}
@Override
public @Nullable
List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) {
return new ArrayList<>();
return tab(sender, args);
}
/**
@ -123,4 +139,12 @@ public abstract class AbstractCommandBase extends TPermission implements IComman
public void disablePlugin(Plugin plugin) {
Bukkit.getServer().getPluginManager().disablePlugin(plugin);
}
protected static class MessageDefaults {
public static Component MSG_NO_PERMS = Component.text("You do not have permission to use this command!").color(NamedTextColor.RED);
public static Component MSG_NOT_PLAYER = Component.text("This command can only be run through the console.").color(NamedTextColor.RED);
public static Component MSG_NOT_FOUND = Component.text("Player not found.").color(NamedTextColor.RED);
public static Component MSG_NOT_CONSOLE = Component.text("This command can only be run by a player.").color(NamedTextColor.RED);
public static Component MSG_NOT_ENOUGH_ARGS = Component.text("Not enough arguments.").color(NamedTextColor.RED);
}
}

View File

@ -2,6 +2,7 @@ package mc.unraveled.reforged.command.base;
import mc.unraveled.reforged.api.annotations.CommandInfo;
import mc.unraveled.reforged.plugin.Traverse;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import java.util.ArrayList;
@ -39,6 +40,8 @@ public class CommandLoader {
info.description(),
info.usage(),
Arrays.asList(info.aliases()));
dummy.setPermission(command.getPermission());
dummy.permissionMessage(Component.text(command.getPermissionMessage()));
commandList.add(dummy);
} else {
throw new RuntimeException("Missing a required annotation! Unable to load the command.");

View File

@ -65,6 +65,18 @@ public class RankManager implements Baker, Locker {
.forEach(tuple -> tuple.getThird().remove(permission));
}
public void insertPlayer(Rank rank, OfflinePlayer player) {
rankQueue.stream()
.filter(tuple -> tuple.getFirst().equals(rank))
.forEach(tuple -> tuple.getSecond().add(player));
}
public void ejectPlayer(Rank rank, OfflinePlayer player) {
rankQueue.stream()
.filter(tuple -> tuple.getFirst().equals(rank))
.forEach(tuple -> tuple.getSecond().remove(player));
}
@Override
public void bake() {
if (baked) return;

View File

@ -13,7 +13,7 @@ public abstract class TPermission {
* @param allowConsole Whether to allow the command to be run anywhere, or only in game.
*/
public TPermission(String permission, String permissionMessage, boolean allowConsole) {
this.permission = permission;
this.permission = "traverse." + permission;
this.permissionMessage = permissionMessage;
this.allowConsole = allowConsole;
}

View File

@ -2,7 +2,11 @@ package mc.unraveled.reforged.plugin;
import lombok.Getter;
import lombok.SneakyThrows;
import mc.unraveled.reforged.api.Locker;
import mc.unraveled.reforged.banning.BanManager;
import mc.unraveled.reforged.command.BanCMD;
import mc.unraveled.reforged.command.GroupCMD;
import mc.unraveled.reforged.command.TraverseCMD;
import mc.unraveled.reforged.command.base.CommandLoader;
import mc.unraveled.reforged.data.DataManager;
import mc.unraveled.reforged.permission.RankManager;
@ -10,7 +14,7 @@ import mc.unraveled.reforged.storage.DBConnectionHandler;
import mc.unraveled.reforged.storage.DBProperties;
import org.bukkit.plugin.java.JavaPlugin;
public final class Traverse extends JavaPlugin {
public final class Traverse extends JavaPlugin implements Locker {
// Primary variable declaration.
@Getter
@ -38,4 +42,17 @@ public final class Traverse extends JavaPlugin {
public void onDisable() {
// Plugin shutdown logic
}
@SneakyThrows
public void registerCommands() {
synchronized (lock()) {
getCommandLoader().register(new TraverseCMD(this),
new BanCMD(this),
new GroupCMD(this));
lock().wait(1000);
}
lock().notify();
getCommandLoader().load();
}
}

View File

@ -31,14 +31,26 @@ public class DBBan {
@SneakyThrows
public void insert(@NotNull AbstractBan ban) {
PreparedStatement statement = getConnection().prepareStatement("INSERT INTO bans (uuid, ip, reason, banned_by, banned_at, expires_at, active) VALUES (?, ?, ?, ?, ?, ?, ?);");
PreparedStatement statement = getConnection().prepareStatement("IF NOT EXISTS (SELECT 1 FROM bans WHERE uuid = ?) " +
"BEGIN INSERT INTO bans (uuid, ip, reason, banned_by, banned_at, expires_at, active) VALUES (?, ?, ?, ?, ?, ?, ?) END " +
"ELSE BEGIN UPDATE bans SET ip = ?, reason = ?, banned_by = ?, banned_at = ?, expires_at = ?, active = ? WHERE uuid = ? END;");
statement.setString(1, ban.getUuid());
statement.setString(2, ban.getIp());
statement.setString(3, ban.getReason());
statement.setString(4, ban.getSource());
statement.setLong(5, ban.getPropogated());
statement.setLong(6, ban.getExpiry());
statement.setBoolean(7, ban.isActive());
statement.setString(2, ban.getUuid());
statement.setString(3, ban.getIp());
statement.setString(4, ban.getReason());
statement.setString(5, ban.getSource());
statement.setLong(6, ban.getPropogated());
statement.setLong(7, ban.getExpiry());
statement.setBoolean(8, ban.isActive());
statement.setString(9, ban.getIp());
statement.setString(10, ban.getReason());
statement.setString(11, ban.getSource());
statement.setLong(12, ban.getPropogated());
statement.setLong(13, ban.getExpiry());
statement.setBoolean(14, ban.isActive());
statement.setString(15, ban.getUuid());
statement.executeUpdate();
}
@ -77,18 +89,6 @@ public class DBBan {
return bans;
}
@SneakyThrows
public void update(@NotNull AbstractBan ban) {
PreparedStatement statement = getConnection().prepareStatement("UPDATE bans SET reason = ?, banned_by = ?, banned_at = ?, expires_at = ?, active = ? WHERE uuid = ?;");
statement.setString(1, ban.getReason());
statement.setString(2, ban.getSource());
statement.setLong(3, ban.getPropogated());
statement.setLong(4, ban.getExpiry());
statement.setBoolean(5, ban.isActive());
statement.setString(6, ban.getUuid());
statement.executeUpdate();
}
@SneakyThrows
public void delete(@NotNull AbstractBan ban) {
PreparedStatement statement = getConnection().prepareStatement("DELETE FROM bans WHERE uuid = ?;");

View File

@ -32,7 +32,7 @@ public class DBUser {
public void insert(@NotNull PlayerData playerData) {
PreparedStatement statement = getConnection().prepareStatement("IF NOT EXISTS (SELECT 1 FROM users WHERE uuid = ?) " +
"BEGIN INSERT INTO users (uuid, username, rank, play_time, coins, last_login) VALUES (?, ?, ?, ?, ?, ?) END " +
"ELSE BEGIN (UPDATE users SET username = ?, rank = ?, play_time = ?, coins = ?, last_login = ? WHERE uuid = ?) END;");
"ELSE BEGIN UPDATE users SET username = ?, rank = ?, play_time = ?, coins = ?, last_login = ? WHERE uuid = ? END;");
statement.setString(1, playerData.getUuid().toString());
statement.setString(2, playerData.getUuid().toString());

View File

@ -0,0 +1,10 @@
package mc.unraveled.reforged.util;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Context<S> {
private S context;
}

View File

@ -4,7 +4,10 @@ import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Array;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
public final class Utilities {
public static <T> @NotNull String serialize(@NotNull List<Pair<String, T>> objectPairs) {
@ -19,4 +22,25 @@ public final class Utilities {
}
return builder.toString();
}
public static Date parseDate(String duration) {
TimeUnit unit;
long amount;
unit = switch (duration.charAt(duration.length() - 1)) {
case 's' -> TimeUnit.SECONDS;
case 'h' -> TimeUnit.HOURS;
case 'd' -> TimeUnit.DAYS;
default -> TimeUnit.MINUTES;
};
amount = Long.parseLong(duration.substring(0, duration.length() - 1));
return new Date(System.currentTimeMillis() + unit.toMillis(amount));
}
public static String parseDateToString(Date date) {
SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
return format.format(date);
}
}