Migrates the entire package nomenclature to be more direct and straightforward. (#17)

Signed-off-by: Paul Reilly <pawereus@gmail.com>
This commit is contained in:
Paldiu
2023-08-01 22:34:18 -05:00
committed by GitHub
parent e1a6b5e587
commit 21463c50fe
146 changed files with 595 additions and 608 deletions

View File

@ -0,0 +1,83 @@
package fns.datura;
import fns.datura.features.CommandSpy;
import fns.datura.features.Fuckoff;
import fns.datura.punishment.Cager;
import fns.datura.punishment.Halter;
import fns.datura.punishment.Locker;
import fns.datura.sql.MySQL;
import fns.patchwork.base.Patchwork;
import fns.patchwork.service.SubscriptionProvider;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
public class Datura extends JavaPlugin
{
private final MySQL sql = new MySQL("localhost", 3011, "master");
// Punishment
private final Halter halter = new Halter();
private final Locker locker = new Locker();
private final Cager cager = new Cager();
// Features
private final CommandSpy commandSpy = new CommandSpy();
private final Fuckoff fuckoff = new Fuckoff();
@Override
public void onEnable()
{
Patchwork.getInstance()
.getRegistrations()
.getModuleRegistry()
.addModule(this);
Patchwork.getInstance()
.getRegistrations()
.getServiceTaskRegistry()
.registerService(SubscriptionProvider.syncService(this, locker));
Patchwork.getInstance()
.getRegistrations()
.getServiceTaskRegistry()
.registerService(SubscriptionProvider.syncService(this, cager));
Patchwork.getInstance()
.getRegistrations()
.getServiceTaskRegistry()
.registerService(SubscriptionProvider.syncService(this, fuckoff));
Bukkit.getPluginManager()
.registerEvents(halter, this);
Bukkit.getPluginManager()
.registerEvents(commandSpy, this);
}
public MySQL getSQL()
{
return sql;
}
public Halter getHalter()
{
return halter;
}
public Locker getLocker()
{
return locker;
}
public Cager getCager()
{
return cager;
}
public CommandSpy getCommandSpy()
{
return commandSpy;
}
public Fuckoff getFuckoff()
{
return fuckoff;
}
}

View File

@ -0,0 +1,64 @@
package fns.datura.cmd;
import fns.patchwork.base.Patchwork;
import fns.patchwork.command.Commander;
import fns.patchwork.command.annotation.Base;
import fns.patchwork.command.annotation.Info;
import fns.patchwork.command.annotation.Permissive;
import fns.patchwork.command.annotation.Subcommand;
import net.kyori.adventure.text.Component;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
@Info(name = "")
@Permissive(perm = "")
public class AdminChatCommand extends Commander
{
/**
* Initializes this command object. The provided {@link JavaPlugin} should be the plugin which contains the
* command.
* <p>
* This constructor will automatically register all subcommands and completions for this command. It will also
* automatically infer all required information from the provided {@link Info} and {@link Permissive} annotations.
*
* @param plugin The plugin which contains this command.
*/
public AdminChatCommand(@NotNull final JavaPlugin plugin)
{
super(plugin);
}
@Base
public void onAdminChat(final CommandSender sender)
{
if (!(sender instanceof Player)) return;
final Player player = (Player) sender;
Patchwork.getInstance()
.getAdminChatDisplay()
.toggleChat(player);
final boolean toggled = Patchwork.getInstance()
.getAdminChatDisplay()
.isToggled(player);
String message = "Toggled adminchat ";
message += toggled ? "on" : "off";
player.sendPlainMessage(message + ".");
}
// String here will automatically have all additional args appended to it :)
@Subcommand(permission = "patchwork.adminchat", args = {String.class})
public void sendMessage(final CommandSender sender, final String message)
{
Patchwork.getInstance()
.getAdminChatDisplay()
.adminChatMessage(sender, Component.text(message));
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2023 TotalFreedom
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the “Software”), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package fns.datura.cmd;
import fns.datura.Datura;
import fns.patchwork.base.Shortcuts;
import fns.patchwork.command.Commander;
import fns.patchwork.command.annotation.Completion;
import fns.patchwork.command.annotation.Info;
import fns.patchwork.command.annotation.Permissive;
import fns.patchwork.command.annotation.Subcommand;
import org.bukkit.Material;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
@Info(name = "cage", description = "Cage a player.",
usage = "/cage <player> <on|off> [material]")
@Permissive(perm = "datura.cage")
@Completion(args = {"%player%"}, index = 0)
@Completion(args = {"on", "off"}, index = 1)
@Completion(args = {"[material]"}, index = 2)
public class CageCommand extends Commander
{
public CageCommand(final @NotNull JavaPlugin plugin)
{
super(plugin);
}
@Subcommand(permission = "datura.cage", args = {Player.class, String.class})
public void cagePlayer(final CommandSender sender, final Player player, final String string)
{
if (string.equalsIgnoreCase("on"))
{
((Datura) getPlugin()).getCager()
.cagePlayer(player.getUniqueId());
sender.sendPlainMessage("Caged " + player.getName() + ".");
} else if (string.equalsIgnoreCase("off"))
{
((Datura) getPlugin()).getCager()
.uncagePlayer(player.getUniqueId());
sender.sendPlainMessage("Liberated " + player.getName() + ".");
}
}
@Subcommand(permission = "datura.cage.custom", args = {Player.class, String.class, Material.class})
public void cagePlayer(final CommandSender sender, final Player player, final String string,
final Material material)
{
if (string.equalsIgnoreCase("on"))
{
Shortcuts.provideModule(Datura.class)
.getCager()
.cagePlayer(player.getUniqueId(), material);
sender.sendPlainMessage("Caged " + player.getName() + ".");
} else if (string.equalsIgnoreCase("off"))
{
Shortcuts.provideModule(Datura.class)
.getCager()
.uncagePlayer(player.getUniqueId());
sender.sendPlainMessage("Liberated " + player.getName() + ".");
}
}
}

View File

@ -0,0 +1,94 @@
package fns.datura.cmd;
import fns.datura.Datura;
import fns.patchwork.base.Patchwork;
import fns.patchwork.base.Shortcuts;
import fns.patchwork.command.Commander;
import fns.patchwork.command.annotation.Base;
import fns.patchwork.command.annotation.Completion;
import fns.patchwork.command.annotation.Info;
import fns.patchwork.command.annotation.Permissive;
import fns.patchwork.command.annotation.Subcommand;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
@Info(name = "cleardrops", description = "Clears all item drops in the world" + ".", usage = "/<command>", aliases =
{"cd", "clearitems", "ci", "wipeitems", "wi", "removedrops", "rd"})
@Permissive(perm = "datura.cleardrops")
@Completion(index = 0, args = {"%world%"})
public class ClearDropsCommand extends Commander
{
/**
* Initializes this command object. The provided {@link JavaPlugin}
* should be the plugin which contains the
* command.
* <p>
* This constructor will automatically register all subcommands and
* completions for this command. It will also
* automatically infer all required information from the provided
* {@link Info} and {@link Permissive} annotations.
*
* @param plugin The plugin which contains this command.
*/
public ClearDropsCommand(final @NotNull JavaPlugin plugin)
{
super(plugin);
}
@Base
public void clearDrops(final CommandSender sender)
{
if (!(sender instanceof Player))
{
sender.sendPlainMessage("You must define a world.");
return;
}
final Player player = (Player) sender;
Patchwork.getInstance()
.getExecutor()
.delayedExecutor(Shortcuts.provideModule(Datura.class), 20 * 10L)
.execute(() ->
{
int i = 0;
for (final Entity entity : player.getWorld().getEntities())
{
if (entity instanceof Item)
{
entity.remove();
i++;
}
}
player.sendPlainMessage("Successfully removed " + i + " items.");
});
}
@Subcommand(permission = "datura.cleardrops", args = {World.class})
public void clearDrops(final CommandSender sender, final World world)
{
Patchwork.getInstance()
.getExecutor()
.delayedExecutor(Shortcuts.provideModule(Datura.class), 20 * 10L)
.execute(() ->
{
int i = 0;
for (final Entity entity : world.getEntities())
{
if (entity instanceof Item)
{
entity.remove();
i++;
}
}
sender.sendPlainMessage("Successfully removed " + i + " items.");
});
}
}

View File

@ -0,0 +1,74 @@
package fns.datura.cmd;
import fns.patchwork.command.Commander;
import fns.patchwork.command.annotation.Base;
import fns.patchwork.command.annotation.Completion;
import fns.patchwork.command.annotation.Info;
import fns.patchwork.command.annotation.Permissive;
import fns.patchwork.command.annotation.Subcommand;
import fns.patchwork.utils.Tagged;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
@Info(name = "clearentities", description = "Clears all entities in the world.", usage = "/<command> [world]",
aliases = {"ew", "ce", "entitywipe", "entityclear", "ec"})
@Permissive(perm = "datura.clearentities")
@Completion(index = 0, args = {"%world%"})
public class ClearEntitiesCommand extends Commander
{
/**
* Initializes this command object. The provided {@link JavaPlugin} should be the plugin which contains the
* command.
* <p>
* This constructor will automatically register all subcommands and completions for this command. It will also
* automatically infer all required information from the provided {@link Info} and {@link Permissive} annotations.
*
* @param plugin The plugin which contains this command.
*/
public ClearEntitiesCommand(final @NotNull JavaPlugin plugin)
{
super(plugin);
}
@Base
public void clearEntities(final CommandSender sender)
{
if (!(sender instanceof Player))
{
sender.sendPlainMessage("You must specify a world to clear entities from.");
return;
}
final Player player = (Player) sender;
int i = 0;
for (final Entity entity : player.getWorld().getEntities())
{
if (!Tagged.NON_WIPEABLE.isTagged(entity.getType()))
{
entity.remove();
i++;
}
}
sender.sendPlainMessage("Cleared " + i + " entities.");
}
@Subcommand(permission = "datura.clearentities", args = {World.class})
public void clearEntities(final CommandSender sender, final World world)
{
int i = 0;
for (final Entity entity : world.getEntities())
{
if (!Tagged.NON_WIPEABLE.isTagged(entity.getType()))
{
entity.remove();
i++;
}
}
sender.sendPlainMessage("Cleared " + i + " entities.");
}
}

View File

@ -0,0 +1,51 @@
package fns.datura.cmd;
import fns.datura.Datura;
import fns.patchwork.command.Commander;
import fns.patchwork.command.annotation.Base;
import fns.patchwork.command.annotation.Info;
import fns.patchwork.command.annotation.Permissive;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
@Info(name = "commandspy", description = "Spy on commands executed by players.", usage = "/commandspy")
@Permissive(perm = "datura.commandspy", onlyPlayers = true)
public class CommandSpyCommand extends Commander
{
/**
* Initializes this command object. The provided {@link JavaPlugin} should be the plugin which contains the
* command.
* <p>
* This constructor will automatically register all subcommands and completions for this command. It will also
* automatically infer all required information from the provided {@link Info} and {@link Permissive} annotations.
*
* @param plugin The plugin which contains this command.
*/
public CommandSpyCommand(@NotNull final Datura plugin)
{
super(plugin);
}
@Base
public void commandSpy(final Player sender)
{
final var commandSpy = ((Datura) getPlugin()).
getCommandSpy();
final var uuid = sender.
getUniqueId();
if (commandSpy.isSpying(uuid))
{
commandSpy.stop(uuid);
sender.sendPlainMessage("CommandSpy disabled.");
}
else
{
commandSpy.spy(uuid);
sender.sendPlainMessage("CommandSpy enabled.");
}
}
}

View File

@ -0,0 +1,64 @@
package fns.datura.cmd;
import fns.datura.Datura;
import fns.patchwork.base.Shortcuts;
import fns.patchwork.command.Commander;
import fns.patchwork.command.annotation.Completion;
import fns.patchwork.command.annotation.Info;
import fns.patchwork.command.annotation.Permissive;
import fns.patchwork.command.annotation.Subcommand;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
@Info(name = "fuckoff", description = "You'll never even see it coming - repeatedly push players away from you until command is untoggled.", usage = "/fuckoff <on|off> [radius]")
@Permissive(perm = "datura.fuckoff", onlyPlayers = true)
@Completion(args = {"on", "off"}, index = 0)
@Completion(args = {"[radius]"}, index = 1)
public class FuckoffCommand extends Commander
{
private final Datura plugin = Shortcuts.provideModule(Datura.class);
/**
* Initializes this command object. The provided {@link JavaPlugin} should be the plugin which contains the
* command.
* <p>
* This constructor will automatically register all subcommands and completions for this command. It will also
* automatically infer all required information from the provided {@link Info} and {@link Permissive} annotations.
*
* @param plugin The plugin which contains this command.
*/
public FuckoffCommand(@NotNull final JavaPlugin plugin)
{
super(plugin);
}
@Subcommand(permission = "datura.fuckoff", args = {String.class})
public void fuckOff(final Player sender, final String toggle)
{
execute(sender, toggle, 15);
}
@Subcommand(permission = "datura.fuckoff", args = {String.class, Integer.class})
public void fuckOff(final Player sender, final String toggle, final Integer radius)
{
execute(sender, toggle, radius);
}
private void execute(final Player sender, final String toggle, final int radius)
{
if (toggle.equalsIgnoreCase("on"))
{
plugin.getFuckoff().
add(sender, radius);
sender.sendPlainMessage("FuckOff enabled.");
} else if (toggle.equalsIgnoreCase("off"))
{
plugin.getFuckoff().
remove(sender);
sender.sendPlainMessage("FuckOff disabled.");
}
}
}

View File

@ -0,0 +1,88 @@
package fns.datura.cmd;
import fns.datura.Datura;
import fns.patchwork.base.Shortcuts;
import fns.patchwork.command.Commander;
import fns.patchwork.command.annotation.Completion;
import fns.patchwork.command.annotation.Info;
import fns.patchwork.command.annotation.Permissive;
import fns.patchwork.command.annotation.Subcommand;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
@Info(name = "halt", description = "Halt a single player, or every player.", usage = "/<command> <player | all> <on, off>")
@Permissive(perm = "datura.halt")
@Completion(index = 0, args = {"%player%", "all"})
@Completion(index = 1, args = {"on", "off"})
public class HaltCommand extends Commander
{
private final Datura plugin = Shortcuts.provideModule(Datura.class);
/**
* Initializes this command object. The provided {@link JavaPlugin} should be the plugin which contains the
* command.
* <p>
* This constructor will automatically register all subcommands and completions for this command. It will also
* automatically infer all required information from the provided {@link Info} and {@link Permissive} annotations.
*
* @param plugin The plugin which contains this command.
*/
public HaltCommand(@NotNull final JavaPlugin plugin)
{
super(plugin);
}
@Subcommand(permission = "datura.halt", args = {Player.class, String.class})
public void haltPlayer(final CommandSender sender, final Player target, final String toggle)
{
if (toggle.equalsIgnoreCase("on"))
{
plugin.getHalter()
.halt(target.getUniqueId());
target.sendPlainMessage("You have been frozen!");
sender.sendPlainMessage("You have halted " + target.getName() + ".");
} else if (toggle.equalsIgnoreCase("off"))
{
plugin.getHalter()
.stop(target.getUniqueId());
target.sendPlainMessage("You have been unfrozen!");
sender.sendPlainMessage("You have unhalted " + target.getName() + ".");
}
}
@Subcommand(permission = "datura.halt.all", args = {String.class, String.class})
public void haltAll(final CommandSender sender, final String all, final String toggle)
{
if (all.equalsIgnoreCase("all"))
{
if (toggle.equalsIgnoreCase("on"))
{
Bukkit.getServer()
.getOnlinePlayers()
.forEach(player -> plugin.getHalter()
.halt(player.getUniqueId()));
final Component message = sender.name()
.append(Component.text(": Freezing all players"))
.color(NamedTextColor.AQUA);
Bukkit.broadcast(message);
sender.sendPlainMessage("All players have been halted.");
} else if (toggle.equalsIgnoreCase("off"))
{
plugin.getHalter()
.clear();
Bukkit.broadcast(Component.text("All players have been unfrozen!", NamedTextColor.AQUA));
sender.sendPlainMessage("All players have been unhalted.");
}
}
}
}

View File

@ -0,0 +1,41 @@
package fns.datura.cmd;
import fns.datura.Datura;
import fns.patchwork.command.Commander;
import fns.patchwork.command.annotation.Completion;
import fns.patchwork.command.annotation.Info;
import fns.patchwork.command.annotation.Permissive;
import fns.patchwork.command.annotation.Subcommand;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@Info(name = "locker", description = "Lock a player, preventing them from interacting with their game client.",
usage = "/locker <player> <on|off>", aliases = {"lock", "lockup"})
@Permissive(perm = "datura.locker")
@Completion(args = {"%player%"}, index = 0)
@Completion(args = {"on", "off"}, index = 1)
public final class LockerCommand extends Commander
{
public LockerCommand(final @NotNull Datura plugin)
{
super(plugin);
}
@Subcommand(permission = "datura.locker", args = {Player.class, String.class})
public void lockPlayer(final CommandSender sender, final Player player, final String string)
{
if (string.equalsIgnoreCase("on"))
{
((Datura) getPlugin()).getLocker()
.lock(player);
sender.sendPlainMessage("Locked " + player.getName() + ".");
} else if (string.equalsIgnoreCase("off"))
{
((Datura) getPlugin()).getLocker()
.unlock(player);
sender.sendPlainMessage("Unlocked " + player.getName() + ".");
}
}
}

View File

@ -0,0 +1,93 @@
package fns.datura.cmd;
import fns.datura.perms.PermissionNodeBuilder;
import fns.patchwork.base.Shortcuts;
import fns.patchwork.command.Commander;
import fns.patchwork.command.annotation.Completion;
import fns.patchwork.command.annotation.Info;
import fns.patchwork.command.annotation.Permissive;
import fns.patchwork.command.annotation.Subcommand;
import fns.patchwork.security.Node;
import fns.patchwork.security.NodeType;
import fns.patchwork.security.PermissionHolder;
import fns.patchwork.user.User;
import java.time.Duration;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
@Info(name = "manageuser", description = "Manage a user's permissions", usage = "/manageuser <username> <info | (add "
+ "| remove <permission>)>", aliases = {"mu", "userdata", "ud", "usermanager", "um"})
@Permissive(perm = "datura.manageuser")
@Completion(index = 0, args = {"%player%"})
@Completion(index = 1, args = {"info", "add", "remove"})
@Completion(index = 2, args = {"<permission>"})
public class ManageUserCommand extends Commander
{
/**
* Initializes this command object. The provided {@link JavaPlugin} should be the plugin which contains the
* command.
* <p>
* This constructor will automatically register all subcommands and completions for this command. It will also
* automatically infer all required information from the provided {@link Info} and {@link Permissive} annotations.
*
* @param plugin The plugin which contains this command.
*/
public ManageUserCommand(final @NotNull JavaPlugin plugin)
{
super(plugin);
}
@Subcommand(permission = "datura.manageuser", args = {Player.class, String.class, String.class, Long.class})
public void manageUser(final CommandSender sender, final Player player, final String addOrRemove,
final String permission, final long duration)
{
final PermissionHolder user = Shortcuts.getUser(player);
final Node node = new PermissionNodeBuilder().key(permission)
.type(NodeType.PERMISSION)
.expiry(Duration.ofMinutes(duration)
.getSeconds() + System.currentTimeMillis())
.build();
ifElse(addOrRemove, user, node);
}
@Subcommand(permission = "datura.manageuser", args = {Player.class, String.class, String.class})
public void manageUser(final CommandSender sender, final Player player, final String addOrRemove,
final String permission)
{
final PermissionHolder user = Shortcuts.getUser(player);
final Node node = new PermissionNodeBuilder().key(permission).type(NodeType.PERMISSION).build();
ifElse(addOrRemove, user, node);
}
@Subcommand(permission = "datura.manageuser", args = {Player.class, String.class})
public void userInfo(final CommandSender sender, final Player player, final String info)
{
final User user = Shortcuts.getUser(player);
if (info.equalsIgnoreCase("info"))
{
final StringBuilder permissions = new StringBuilder();
user.getEffectivePermissions().forEach(node -> permissions.append(node.getPermission()));
final String text = """
User: %s
Group: %s
Permissions: %s""".formatted(user.getName(), user.getUserData().getGroup(),
permissions.toString());
sender.sendPlainMessage(text);
}
}
private void ifElse(final String addOrRemove, final PermissionHolder user, final Node node)
{
if (addOrRemove.equalsIgnoreCase("add"))
{
user.addPermission(node);
}
else if (addOrRemove.equalsIgnoreCase("remove"))
{
user.removePermission(node);
}
}
}

View File

@ -0,0 +1,51 @@
package fns.datura.cmd;
import fns.patchwork.command.Commander;
import fns.patchwork.command.annotation.Completion;
import fns.patchwork.command.annotation.Info;
import fns.patchwork.command.annotation.Permissive;
import fns.patchwork.command.annotation.Subcommand;
import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
@Info(name = "smite", description = "Smite a player.", usage = "/smite <player>", aliases = {"sm"})
@Permissive(perm = "datura.smite")
@Completion(index = 0, args = {"%player%"})
public class SmiteCommand extends Commander
{
/**
* Initializes this command object. The provided {@link JavaPlugin} should be the plugin which contains the
* command.
* <p>
* This constructor will automatically register all subcommands and completions for this command. It will also
* automatically infer all required information from the provided {@link Info} and {@link Permissive} annotations.
*
* @param plugin The plugin which contains this command.
*/
public SmiteCommand(@NotNull final JavaPlugin plugin)
{
super(plugin);
}
@Subcommand(permission = "datura.smite", args = {Player.class})
public void smite(final CommandSender sender, final Player player)
{
final double size = 5D;
for (int i = 0; i < size * size * size; i++)
{
final double x = i % size;
final double y = (i / size) % size;
final double z = (i / size / size) % size;
final Location location = player.getLocation()
.clone()
.add(x - size / 2, y - size / 2, z - size / 2);
player.getWorld()
.strikeLightning(location);
}
}
}

View File

@ -0,0 +1,25 @@
package fns.datura.event;
import fns.patchwork.event.FEvent;
import fns.patchwork.user.UserData;
public class UserDataUpdateEvent extends FEvent
{
private final UserData data;
public UserDataUpdateEvent(final UserData data)
{
this.data = data;
}
public UserData getData()
{
return data;
}
@Override
public Class<? extends FEvent> getEventClass()
{
return UserDataUpdateEvent.class;
}
}

View File

@ -0,0 +1,54 @@
package fns.datura.features;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
public class CommandSpy implements Listener
{
private final Set<UUID> commandWatchers;
public CommandSpy()
{
this.commandWatchers = new HashSet<>();
}
public void spy(final UUID uuid)
{
this.commandWatchers.add(uuid);
}
public void stop(final UUID uuid)
{
this.commandWatchers.remove(uuid);
}
public void clear()
{
this.commandWatchers.clear();
}
public boolean isSpying(final UUID uuid)
{
return this.commandWatchers.contains(uuid);
}
@EventHandler
public void commandProcess(final PlayerCommandPreprocessEvent event)
{
Bukkit.getOnlinePlayers().stream()
.filter(player -> isSpying(player.getUniqueId()))
.forEach(player -> player.sendMessage(Component.text(event.getPlayer().getName(), NamedTextColor.GRAY)
.append(Component.text(": ", NamedTextColor.GRAY))
.append(Component.text(event.getMessage(), NamedTextColor.GRAY))
)
);
}
}

View File

@ -0,0 +1,61 @@
package fns.datura.features;
import fns.patchwork.service.Service;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public class Fuckoff extends Service
{
private final Map<UUID, Integer> players = new ConcurrentHashMap<>();
public Fuckoff()
{
super("fuckoff-service");
}
public void add(final Player player, final int radius)
{
players.put(player.getUniqueId(), radius);
}
public void remove(final Player player)
{
players.remove(player.getUniqueId());
}
@Override
public void tick()
{
for (final Map.Entry<UUID, Integer> entry : players.entrySet())
{
final var player = Bukkit.getPlayer(entry.getKey());
if (player == null)
{
players.remove(entry.getKey());
continue;
}
pushPlayers(player, entry.getValue());
}
}
private void pushPlayers(@NotNull final Player player, final int radius)
{
Bukkit.getOnlinePlayers()
.stream()
.filter(onlinePlayer -> onlinePlayer.getLocation()
.clone()
.distanceSquared(player.getLocation()) < Math.pow(radius, 2))
.forEach(onlinePlayer ->
onlinePlayer.setVelocity(player.getLocation()
.toVector()
.add(onlinePlayer.getLocation().toVector())
.normalize()
.multiply(radius)));
}
}

View File

@ -0,0 +1,41 @@
package fns.datura.perms;
import fns.patchwork.security.Node;
import fns.patchwork.security.NodeType;
public class DefaultNodes
{
public static final Node OP = new PermissionNodeBuilder()
.key("freedom.master_key")
.value(true)
.type(NodeType.PERMISSION)
.negated(false)
.wildcard(true)
.build();
public static final Node NON_OP = new PermissionNodeBuilder()
.key("freedom.default")
.value(true)
.type(NodeType.PERMISSION)
.negated(false)
.wildcard(false)
.build();
public static final Node ALL = new PermissionNodeBuilder()
.key("*")
.value(true)
.type(NodeType.PERMISSION)
.negated(false)
.wildcard(true)
.build();
public static final Node NONE = new PermissionNodeBuilder()
.key("freedom.none")
.value(true)
.type(NodeType.PERMISSION)
.negated(false)
.wildcard(false)
.build();
private DefaultNodes()
{
throw new AssertionError();
}
}

View File

@ -0,0 +1,243 @@
package fns.datura.perms;
import fns.patchwork.base.Patchwork;
import fns.patchwork.security.Group;
import fns.patchwork.security.Node;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import net.kyori.adventure.text.Component;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class FreedomGroup implements Group
{
private final Component name;
private final Component prefix;
private final Component abbreviation;
private final int weight;
private final boolean isDefault;
private final boolean isHidden;
private final Set<Node> permissions;
private final PermissionAttachment attachment;
public FreedomGroup(final Component name,
final Component prefix,
final Component abbreviation,
final int weight,
final boolean isDefault,
final boolean isHidden)
{
this.name = name;
this.prefix = prefix;
this.abbreviation = abbreviation;
this.weight = weight;
this.isDefault = isDefault;
this.isHidden = isHidden;
this.permissions = new HashSet<>();
this.attachment = new PermissionAttachment(Patchwork.getInstance(), this);
}
@Override
public UUID getUniqueId()
{
return UUID.nameUUIDFromBytes(getName().toString()
.getBytes());
}
@Override
public Component getName()
{
return name;
}
@Override
public Component getPrefix()
{
return prefix;
}
@Override
public Component getAbbreviation()
{
return abbreviation;
}
@Override
public int getWeight()
{
return weight;
}
@Override
public boolean isDefault()
{
return isDefault;
}
@Override
public boolean isHidden()
{
return isHidden;
}
@Override
public Set<Node> permissions()
{
return permissions;
}
@Override
public boolean addPermission(final Node node)
{
return permissions().add(node);
}
@Override
public boolean removePermission(final Node node)
{
return permissions().remove(node);
}
@Override
public boolean isPermissionSet(@NotNull final String name)
{
final Node node = permissions().stream()
.filter(n -> n.key()
.equalsIgnoreCase(name))
.findFirst()
.orElse(null);
return node != null;
}
@Override
public boolean isPermissionSet(@NotNull final Permission perm)
{
final Node node = permissions()
.stream()
.filter(n -> n.bukkit()
.equals(perm))
.findFirst()
.orElse(null);
return node != null;
}
@Override
public boolean hasPermission(@NotNull final String name)
{
final Node node = permissions().stream()
.filter(n -> n.key()
.equalsIgnoreCase(name))
.findFirst()
.orElse(null);
return node != null;
}
@Override
public boolean hasPermission(@NotNull final Permission perm)
{
final Node node = permissions()
.stream()
.filter(n -> n.bukkit()
.equals(perm))
.findFirst()
.orElse(null);
return node != null;
}
/**
* Adds a permission to the relative PermissionAttachment for this group. This method is not thread-safe and should
* not be called asynchronously.
* <p>
* This method is only here for compatibility with the Bukkit API.
*
* @param plugin The plugin responsible for this attachment. May not be null or disabled.
* @param name Name of the permission to attach
* @param value Value of the permission
* @return This group's PermissionAttachment.
*/
@Override
public @NotNull PermissionAttachment addAttachment(@NotNull final Plugin plugin, @NotNull final String name,
final boolean value)
{
attachment.setPermission(name, value);
return attachment;
}
@Override
public @NotNull PermissionAttachment addAttachment(@NotNull final Plugin plugin)
{
return new PermissionAttachment(plugin, this);
}
@Override
public @Nullable PermissionAttachment addAttachment(@NotNull final Plugin plugin, @NotNull final String name,
final boolean value, final int ticks)
{
attachment.setPermission(name, value);
return attachment;
}
@Override
public @Nullable PermissionAttachment addAttachment(@NotNull final Plugin plugin, final int ticks)
{
return new PermissionAttachment(plugin, this);
}
@Override
public void removeAttachment(@NotNull final PermissionAttachment attachment)
{
// This method shouldn't do anything, because we don't want to remove our attachment.
}
@Override
public void recalculatePermissions()
{
// Not sure what this method should do, so I'm leaving it empty.
}
@Override
public @NotNull Set<PermissionAttachmentInfo> getEffectivePermissions()
{
return permissions()
.stream()
.map(n -> new PermissionAttachmentInfo(
this,
n.key(),
attachment, true))
.collect(Collectors.toSet());
}
@Override
public boolean isOp()
{
final Node node = permissions()
.stream()
.filter(n -> n.equals(DefaultNodes.OP))
.findFirst()
.orElse(null);
return node != null;
}
@Override
public void setOp(final boolean value)
{
if (value)
{
permissions().add(DefaultNodes.OP);
} else
{
permissions().remove(DefaultNodes.OP);
}
}
}

View File

@ -0,0 +1,243 @@
package fns.datura.perms;
import fns.datura.Datura;
import fns.datura.user.SimpleUserData;
import fns.patchwork.base.Patchwork;
import fns.patchwork.security.Node;
import fns.patchwork.user.User;
import fns.patchwork.user.UserData;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* The superinterface User extends PermissionHolder, which is an extension of
* {@link org.bukkit.permissions.Permissible}. This means that our permission data can be interchanged with other
* permission plugins.
*/
public class FreedomUser implements User
{
private final UUID uuid;
private final Set<Node> permissions;
private final Map<Node, PermissionAttachment> bukkitAttachments = new HashMap<>();
private final Component displayName;
private static final String NOT_ONLINE = "Player is not online";
private final UserData userData;
public FreedomUser(final Player player)
{
this.uuid = player.getUniqueId();
this.permissions = new HashSet<>();
this.displayName = player.displayName();
final Datura datura = Patchwork.getInstance()
.getRegistrations()
.getModuleRegistry()
.getProvider(Datura.class)
.getModule();
UserData data = SimpleUserData.fromSQL(datura.getSQL(), uuid.toString());
if (data == null)
{
data = new SimpleUserData(player);
}
this.userData = data;
Patchwork.getInstance()
.getRegistrations()
.getUserRegistry()
.registerUserData(this, userData);
}
@Override
public UserData getUserData()
{
return userData;
}
@Override
public Component getDisplayName()
{
return displayName;
}
@Override
public boolean isOnline()
{
return Bukkit.getPlayer(uuid) != null;
}
@Override
public UUID getUniqueId()
{
return uuid;
}
@Override
public Set<Node> permissions()
{
return permissions;
}
@Override
public boolean addPermission(final Node node)
{
final boolean value = !node.isTemporary() || node.isExpired();
final PermissionAttachment attachment = addAttachment(Patchwork.getInstance(), node.key(), value);
bukkitAttachments.put(node, attachment);
return permissions().add(node);
}
@Override
public boolean removePermission(final Node node)
{
removeAttachment(bukkitAttachments.get(node));
bukkitAttachments.remove(node);
return permissions.remove(node);
}
@Override
public boolean isPermissionSet(@NotNull final String name)
{
final Player player = Bukkit.getPlayer(uuid);
return player != null && player.isPermissionSet(name);
}
@Override
public boolean isPermissionSet(@NotNull final Permission perm)
{
final Player player = Bukkit.getPlayer(uuid);
return player != null && player.isPermissionSet(perm);
}
@Override
public boolean hasPermission(@NotNull final String name)
{
final Player player = Bukkit.getPlayer(uuid);
return player != null && player.hasPermission(name);
}
@Override
public boolean hasPermission(@NotNull final Permission perm)
{
final Player player = Bukkit.getPlayer(uuid);
return player != null && player.hasPermission(perm);
}
@Override
public @NotNull PermissionAttachment addAttachment(@NotNull final Plugin plugin, @NotNull final String name,
final boolean value)
{
final Player player = Bukkit.getPlayer(uuid);
if (player != null)
{
return player.addAttachment(plugin, name, value);
}
throw new IllegalStateException(NOT_ONLINE);
}
@Override
public @NotNull PermissionAttachment addAttachment(@NotNull final Plugin plugin)
{
final Player player = Bukkit.getPlayer(uuid);
if (player != null)
{
return player.addAttachment(plugin);
}
throw new IllegalStateException(NOT_ONLINE);
}
@Override
public @Nullable PermissionAttachment addAttachment(@NotNull final Plugin plugin, @NotNull final String name,
final boolean value, final int ticks)
{
final Player player = Bukkit.getPlayer(uuid);
if (player != null)
{
return player.addAttachment(plugin, name, value, ticks);
}
throw new IllegalStateException(NOT_ONLINE);
}
@Override
public @Nullable PermissionAttachment addAttachment(@NotNull final Plugin plugin, final int ticks)
{
final Player player = Bukkit.getPlayer(uuid);
if (player != null)
{
return player.addAttachment(plugin, ticks);
}
throw new IllegalStateException(NOT_ONLINE);
}
@Override
public void removeAttachment(@NotNull final PermissionAttachment attachment)
{
final Player player = Bukkit.getPlayer(uuid);
if (player != null)
{
player.removeAttachment(attachment);
}
throw new IllegalStateException(NOT_ONLINE);
}
@Override
public void recalculatePermissions()
{
final Player player = Bukkit.getPlayer(uuid);
if (player != null)
{
player.recalculatePermissions();
}
throw new IllegalStateException(NOT_ONLINE);
}
@Override
public @NotNull Set<PermissionAttachmentInfo> getEffectivePermissions()
{
final Player player = Bukkit.getPlayer(uuid);
if (player != null)
{
return player.getEffectivePermissions();
}
throw new IllegalStateException(NOT_ONLINE);
}
@Override
public boolean isOp()
{
return permissions().contains(DefaultNodes.OP);
}
@Override
public void setOp(final boolean value)
{
if (value)
{
permissions().add(DefaultNodes.OP);
} else
{
permissions().remove(DefaultNodes.OP);
}
}
}

View File

@ -0,0 +1,39 @@
package fns.datura.perms;
import fns.patchwork.security.Node;
import fns.patchwork.security.NodeType;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
record PermissionNode(String key, long expiry, NodeType type, boolean wildcard) implements Node
{
@Override
public Permission bukkit()
{
return new Permission(key(), PermissionDefault.FALSE);
}
@Override
public boolean compare(final Node node)
{
return node.key().equalsIgnoreCase(key()) && node.type().equals(type()) && !node.isExpired();
}
@Override
public boolean isExpired()
{
if (!isTemporary())
{
return false;
}
return System.currentTimeMillis() > expiry();
}
@Override
public boolean isTemporary()
{
return expiry() > -1;
}
}

View File

@ -0,0 +1,47 @@
package fns.datura.perms;
import fns.patchwork.security.Node;
import fns.patchwork.security.NodeBuilder;
import fns.patchwork.security.NodeType;
public class PermissionNodeBuilder implements NodeBuilder
{
private String key = "freedom.default";
private long expiry = -1;
private NodeType type = NodeType.PERMISSION;
private boolean wildcard = false;
@Override
public NodeBuilder key(final String key)
{
this.key = key;
return this;
}
@Override
public NodeBuilder expiry(final long expiry)
{
this.expiry = expiry;
return this;
}
@Override
public NodeBuilder type(final NodeType type)
{
this.type = type;
return this;
}
@Override
public NodeBuilder wildcard(final boolean wildcard)
{
this.wildcard = wildcard;
return this;
}
@Override
public Node build()
{
return new PermissionNode(key, expiry, type, wildcard);
}
}

View File

@ -0,0 +1,178 @@
package fns.datura.punishment;
import fns.patchwork.base.Patchwork;
import fns.patchwork.service.Service;
import fns.patchwork.utils.ShapeUtils;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.DoubleUnaryOperator;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.player.PlayerQuitEvent;
public class Cager extends Service
{
private final Set<UUID> cagedPlayers;
private final Map<UUID, Location> cageLocations;
public Cager()
{
super("cager-service");
this.cagedPlayers = new HashSet<>();
this.cageLocations = new HashMap<>();
Bukkit.getPluginManager()
.registerEvents(new CageListener(), Patchwork.getInstance());
}
/**
* This method will cage the player using {@link #createCage(Location, Material)}.
* <p>This will also add the returned location to the {@link #cageLocations} map.
*
* @param uuid The UUID of the player to cage.
*/
public void cagePlayer(final UUID uuid)
{
final Player player = Bukkit.getPlayer(uuid);
if (player == null) return;
cagedPlayers.add(uuid);
cageLocations.put(uuid, createCage(player.getLocation(), Material.GLASS));
}
/**
* This method generates a cube centered around the passed location, made of the provided material. This method
* returns the passed location object. We use the {@link ShapeUtils} class to generate the cube, which allows us to
* define custom shapes using {@link DoubleUnaryOperator}s.
*
* @param location The location to center the cube around.
* @param material The material to use for the cube.
* @return The center location of the cube (the passed location).
* @see ShapeUtils
* @see DoubleUnaryOperator
*/
public Location createCage(final Location location, final Material material)
{
final ShapeUtils shapeUtils = new ShapeUtils(location.getWorld(), 0.0, 4.0);
final List<Location> cubed = new LinkedList<>();
cubed.addAll(shapeUtils.generate(5, t -> t, t -> 4.0, t -> t));
cubed.addAll(shapeUtils.generate(5, t -> t, t -> 0.0, t -> t));
cubed.addAll(shapeUtils.generate(5, t -> 0.0, t -> t, t -> t));
cubed.addAll(shapeUtils.generate(5, t -> 4.0, t -> t, t -> t));
cubed.addAll(shapeUtils.generate(5, t -> t, t -> t, t -> 0.0));
cubed.addAll(shapeUtils.generate(5, t -> t, t -> t, t -> 4.0));
for (final Location l : cubed)
{
location.getWorld()
.getBlockAt(l)
.setType(material);
}
return location.clone(); // Return the passed location as that is the center of the cube.
}
public void cagePlayer(final UUID uuid, final Material material)
{
final Player player = Bukkit.getPlayer(uuid);
if (player == null) return;
cagedPlayers.add(uuid);
cageLocations.put(uuid, createCage(player.getLocation(), material));
}
/**
* This method will uncage the player by removing them from the {@link #cagedPlayers} set.
*
* @param uuid The UUID of the player to uncage.
*/
public void uncagePlayer(final UUID uuid)
{
cagedPlayers.remove(uuid);
final Location location = cageLocations.get(uuid);
createCage(location, Material.AIR); // Remove the cage (set all blocks to air).
cageLocations.remove(uuid);
}
/**
* This method will check to make sure each caged player remains within their cage. We use
* <p>
* <code>{@link Location#distanceSquared(Location)} * {@link Math#pow(double, double)}</code>
* <p>
* to check if the player is outside the cage.
*/
@Override
public void tick()
{
for (final UUID uuid : cagedPlayers)
{
final Player player = Bukkit.getPlayer(uuid);
if (player == null) continue;
final Location cageLocation = getCageLocation(player);
final boolean inside;
if (!player.getWorld()
.equals(cageLocation.getWorld()))
{
inside = false;
} else
{
inside = player.getLocation()
.distanceSquared(cageLocation) > (Math.pow(2.5, 2.0));
}
if (!inside)
{
player.teleport(cageLocation);
}
}
}
/**
* This method returns whether the player is caged.
* <p>This method requires the player to be online to execute properly.</p>
*
* @param player The player to check.
* @return Whether the player is caged.
*/
public Location getCageLocation(final Player player)
{
return cageLocations.get(player.getUniqueId());
}
private final class CageListener implements Listener
{
@EventHandler
public void blockBreakEvent(final BlockBreakEvent event)
{
if (cagedPlayers.contains(event.getPlayer()
.getUniqueId()))
{
event.setCancelled(true);
}
}
@EventHandler
public void playerLeaveEvent(final PlayerQuitEvent event)
{
if (cagedPlayers.contains(event.getPlayer()
.getUniqueId()))
{
uncagePlayer(event.getPlayer()
.getUniqueId());
}
}
}
}

View File

@ -0,0 +1,43 @@
package fns.datura.punishment;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
public class Halter implements Listener
{
private final Set<UUID> haltedPlayers;
public Halter()
{
this.haltedPlayers = new HashSet<>();
}
public void halt(final UUID uuid)
{
this.haltedPlayers.add(uuid);
}
public void stop(final UUID uuid)
{
this.haltedPlayers.remove(uuid);
}
public void clear()
{
this.haltedPlayers.clear();
}
@EventHandler
public void playerMove(final PlayerMoveEvent event)
{
if (haltedPlayers.contains(event.getPlayer()
.getUniqueId()))
{
event.setCancelled(true);
}
}
}

View File

@ -0,0 +1,88 @@
package fns.datura.punishment;
import fns.patchwork.base.Patchwork;
import fns.patchwork.service.Service;
import java.util.HashSet;
import java.util.Set;
import java.util.SplittableRandom;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
public class Locker extends Service
{
private final Set<UUID> lockedPlayers = new HashSet<>();
public Locker()
{
super("locker-service");
}
public void lock(final Player player)
{
lockedPlayers.add(player.getUniqueId());
}
public void unlock(final Player player)
{
lockedPlayers.remove(player.getUniqueId());
}
@Override
public void tick()
{
lockedPlayers.removeIf(uuid -> !Patchwork.getInstance()
.getServer()
.getOfflinePlayer(uuid)
.isOnline());
for (final UUID uuid : lockedPlayers)
{
final Player player = Bukkit.getPlayer(uuid);
if (player == null) continue;
lockingMethod(player);
}
}
private void lockingMethod(@NotNull final Player player)
{
final double x = player.getLocation()
.getX();
final double z = player.getLocation()
.getZ();
if ((x / z % 0.001) < 1)
{
player.setVelocity(new Vector(x % 12, 0, z % 12));
}
player.setWalkSpeed(0.0f);
player.setFlySpeed(0.0f);
player.setAllowFlight(false);
player.setFlying(false);
player.setInvulnerable(true);
player.setCollidable(false);
player.setGliding(false);
player.setGlowing(true);
player.setSilent(true);
player.setCanPickupItems(false);
player.setInvisible(true);
player.openInventory(Bukkit.createInventory(null, 54));
player.closeInventory(InventoryCloseEvent.Reason.UNKNOWN);
player.teleport(player.getLocation()
.clone());
final SplittableRandom random = new SplittableRandom();
player.getEyeLocation()
.add(new Vector(
random.nextDouble(-1.0, 1.0),
random.nextDouble(-1.0, 1.0),
random.nextDouble(-1.0, 1.0)
));
}
}

View File

@ -0,0 +1,260 @@
package fns.datura.sql;
import fns.patchwork.base.Patchwork;
import fns.patchwork.sql.SQL;
import fns.patchwork.utils.container.Identity;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
public class MySQL implements SQL
{
/**
* Using StringBuilder for finality.
*/
private final StringBuilder url = new StringBuilder("jdbc:mysql://");
public MySQL(final String host, final int port, final String database)
{
url.append(host)
.append(':')
.append(port)
.append('/')
.append(database);
}
/**
* Adds credentials to the MySQL URL. If the URL already contains credentials, they will be overwritten.
*
* @param username The username to add
* @param password The password to add
*/
public void addCredentials(final String username, final String password)
{
if (url.toString()
.contains("?user="))
{
final String split = url.toString()
.split("\\x3f")[0];
url.setLength(0);
url.append(split);
}
url.append("?user=")
.append(username)
.append("&password=")
.append(password);
}
public CompletableFuture<ResultSet> getRow(final String table, final String column, final Identity identity)
{
return executeQuery("SELECT * FROM ? WHERE ? = ?", table, column, identity.getId());
}
@Override
public CompletableFuture<PreparedStatement> prepareStatement(final String query, final Object... args)
{
return getConnection()
.thenApplyAsync(connection ->
{
try
{
final PreparedStatement statement = connection.prepareStatement(query);
for (int i = 0; i < args.length; i++)
{
statement.setObject(i + 1, args[i]);
}
return statement;
} catch (SQLException ex)
{
throw new CompletionException("Failed to prepare statement: "
+ query + "\n", ex);
}
}, Patchwork.getInstance()
.getExecutor()
.getAsync());
}
private CompletableFuture<Connection> getConnection()
{
return CompletableFuture.supplyAsync(() ->
{
try
{
return DriverManager.getConnection(url.toString());
} catch (SQLException ex)
{
throw new CompletionException("Failed to connect to the database: "
+ url.toString() + "\n", ex);
}
}, Patchwork.getInstance()
.getExecutor()
.getAsync());
}
@Override
public CompletableFuture<ResultSet> executeQuery(final String query, final Object... args)
{
return prepareStatement(query, args)
.thenApplyAsync(statement ->
{
try
{
return statement.executeQuery();
} catch (SQLException ex)
{
throw new CompletionException(
"Failed to retrieve a result set from query: "
+ query + "\n", ex);
}
}, Patchwork.getInstance()
.getExecutor()
.getAsync());
}
@Override
public CompletableFuture<Integer> executeUpdate(final String query, final Object... args)
{
return prepareStatement(query, args)
.thenApplyAsync(statement ->
{
try
{
return statement.executeUpdate();
} catch (SQLException ex)
{
throw new CompletionException("Failed to execute update: "
+ query + "\n", ex);
}
}, Patchwork.getInstance()
.getExecutor()
.getAsync());
}
@Override
public CompletableFuture<Boolean> execute(final String query, final Object... args)
{
return prepareStatement(query, args)
.thenApplyAsync(statement ->
{
try
{
return statement.execute();
} catch (SQLException ex)
{
throw new CompletionException("Failed to execute statement: "
+ query + "\n", ex);
}
}, Patchwork.getInstance()
.getExecutor()
.getAsync());
}
@Override
public CompletableFuture<Boolean> createTable(final String table, final String... columns)
{
final StringBuilder query = new StringBuilder();
query.append("CREATE TABLE IF NOT EXISTS ? (");
for (int i = 0; i < columns.length; i++)
{
query.append("?");
if (i != columns.length - 1)
{
query.append(", ");
}
}
query.append(")");
return execute(query.toString(), table, columns);
}
public <T> CompletableFuture<T> getColumn(final String table, final String column, final String key,
final Identity identity, final Class<T> type)
{
return executeQuery("SELECT ? FROM ? WHERE ? = ?", column, table, key, identity.getId())
.thenApplyAsync(resultSet ->
{
try
{
if (resultSet.next())
{
return resultSet.getObject(column, type);
}
} catch (SQLException ex)
{
throw new CompletionException(
"Failed to retrieve column: " + column + " from table: " + table + " " +
"where primary key: " + key + " is equal to: " + identity.getId() + "\n",
ex);
}
return null;
}, Patchwork.getInstance()
.getExecutor()
.getAsync());
}
public CompletableFuture<Boolean> updateColumn(final String table, final String column, final Object value,
final String key, final Identity identity)
{
return executeUpdate("UPDATE ? SET ? = ? WHERE ? = ?", table, column, value, key, identity.getId())
.thenApplyAsync(result -> result > 0, Patchwork.getInstance()
.getExecutor()
.getAsync());
}
public CompletableFuture<Boolean> deleteRow(final String table, final String key, final Identity identity)
{
return executeUpdate("DELETE FROM ? WHERE ? = ?", table, key, identity.getId())
.thenApplyAsync(result -> result > 0, Patchwork.getInstance()
.getExecutor()
.getAsync());
}
public CompletableFuture<Boolean> insertRow(final String table, final Object... values)
{
final StringBuilder query = new StringBuilder();
query.append("INSERT INTO ? VALUES (");
for (int i = 0; i < values.length; i++)
{
query.append("?");
if (i != values.length - 1)
{
query.append(", ");
}
}
query.append(")");
return execute(query.toString(), table, values);
}
public CompletableFuture<Boolean> insertRow(final String table, final String[] columns, final Object... values)
{
final StringBuilder query = new StringBuilder();
query.append("INSERT INTO ? (");
for (int i = 0; i < columns.length; i++)
{
query.append("?");
if (i != columns.length - 1)
{
query.append(", ");
}
}
query.append(") VALUES (");
for (int i = 0; i < values.length; i++)
{
query.append("?");
if (i != values.length - 1)
{
query.append(", ");
}
}
query.append(")");
return execute(query.toString(), table, columns, values);
}
}

View File

@ -0,0 +1,14 @@
package fns.datura.sql;
import com.google.errorprone.annotations.Immutable;
/**
* Represents a single result from a result set.
*/
@Immutable
public record Result(String name, Object value)
{
}

View File

@ -0,0 +1,128 @@
package fns.datura.user;
import fns.patchwork.economy.EconomicEntity;
import fns.patchwork.economy.EconomicEntityData;
/**
* Represents the server's economy holder.
* <br>
* <br>
* This is effectively a Bank object which is meant to represent the server itself, which can store a balance and
* perform transactions with other EconomicEntity objects.
* <br>
* <br>
* The server is initially given a maximum balance of {@link Long#MAX_VALUE}, though this can be changed using the
* constructor {@link #ServerEconomyHolder(String, long)}. The value that this bank object holds is persistent, which
* means that the total economic resources available are of limited supply.
* <br>
* <br>
* Please be aware, if the server's economy falls below 0, it will have drastic consequences.
*/
public class ServerEconomyHolder implements EconomicEntity, EconomicEntityData
{
private final String name;
private long balance;
/**
* Constructs a new ServerEconomyHolder with the specified name and a balance of {@link Long#MAX_VALUE}.
*
* @param name The name of this server economy holder.
*/
public ServerEconomyHolder(final String name)
{
this.name = name;
this.balance = Long.MAX_VALUE;
}
/**
* Constructs a new ServerEconomyHolder with the specified name and balance.
*
* @param name The name of this server economy holder.
* @param balance The balance of this server economy holder.
*/
public ServerEconomyHolder(final String name, final long balance)
{
this.name = name;
this.balance = balance;
}
/**
* This method will return this object, as it is both the EconomicEntity and the EconomicEntityData. This is due to
* the fact that the server should only ever have one singular concrete representation of it's economic entity and
* the respective data.
*
* @return this object.
*/
@Override
public EconomicEntityData getEconomicData()
{
return this;
}
/**
* @return The name of this server economy holder.
*/
@Override
public String getName()
{
return name;
}
/**
* This method will always return false, as the server should not ever be prevented from performing transactions.
*
* @return false
*/
@Override
public boolean areTransactionsFrozen()
{
return false;
}
/**
* @return The server's current available balance.
*/
@Override
public long getBalance()
{
return balance;
}
/**
* Sets the server's balance to the specified value.
*
* @param newBalance The new balance to set.
*/
@Override
public void setBalance(final long newBalance)
{
balance = newBalance;
}
/**
* Adds the specified amount to the server's balance. This method mutates the balance and returns the new balance.
*
* @param amount The amount to add.
* @return The new balance.
*/
@Override
public long addToBalance(final long amount)
{
balance += amount;
return balance;
}
/**
* Removes the specified amount from the server's balance. This method mutates the balance and returns the new
* balance.
*
* @param amount The amount to remove.
* @return The new balance.
*/
@Override
public long removeFromBalance(final long amount)
{
balance -= amount;
return balance;
}
}

View File

@ -0,0 +1,244 @@
package fns.datura.user;
import fns.datura.event.UserDataUpdateEvent;
import fns.datura.perms.FreedomUser;
import fns.patchwork.base.Patchwork;
import fns.patchwork.display.adminchat.AdminChatFormat;
import fns.patchwork.security.Group;
import fns.patchwork.sql.SQL;
import fns.patchwork.user.User;
import fns.patchwork.user.UserData;
import fns.patchwork.utils.logging.FreedomLogger;
import java.sql.SQLException;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SimpleUserData implements UserData
{
private final UUID uuid;
private final String username;
private final User user;
private final UserDataUpdateEvent event = new UserDataUpdateEvent(this);
private Group group;
private long playtime;
private boolean canInteract;
private AtomicLong balance;
private boolean transactionsFrozen;
private boolean hasCustomACFormat = false;
private String customACFormat;
public SimpleUserData(final Player player)
{
this.uuid = player.getUniqueId();
this.username = player.getName();
this.user = new FreedomUser(player);
Patchwork.getInstance()
.getEventBus()
.addEvent(event);
}
private SimpleUserData(
final UUID uuid,
final String username,
final User user,
final Group group,
final long playtime,
final boolean canInteract,
final long balance,
final boolean transactionsFrozen)
{
this.uuid = uuid;
this.username = username;
this.user = user;
this.group = group;
this.playtime = playtime;
this.canInteract = canInteract;
this.balance = new AtomicLong(balance);
this.transactionsFrozen = transactionsFrozen;
this.customACFormat = AdminChatFormat.DEFAULT.serialize();
}
public static SimpleUserData fromSQL(final SQL sql, final String uuid)
{
return sql.executeQuery("SELECT * FROM users WHERE UUID = ?", uuid)
.thenApplyAsync(result ->
{
try
{
if (result.next())
{
final String g = result.getString("group");
final UUID u = UUID.fromString(uuid);
final String username = result.getString("username");
final Player player = Bukkit.getPlayer(u);
if (player == null)
throw new IllegalStateException("Player should be online but they are not!");
final User user = new FreedomUser(player);
final Group group = Patchwork.getInstance()
.getRegistrations()
.getGroupRegistry()
.getGroup(g);
final long playtime = result.getLong("playtime");
final boolean canInteract = result.getBoolean("canInteract");
final long balance = result.getLong("balance");
final boolean transactionsFrozen = result.getBoolean("transactionsFrozen");
return new SimpleUserData(u, username, user, group, playtime,
canInteract, balance, transactionsFrozen);
}
} catch (SQLException ex)
{
final String sb = "An error occurred while trying to retrieve user data for" +
" UUID " +
uuid +
" from the database." +
"\nCaused by: " +
ExceptionUtils.getRootCauseMessage(ex) +
"\nStack trace: " +
ExceptionUtils.getStackTrace(ex);
FreedomLogger.getLogger("Datura")
.error(sb);
}
final Player player = Bukkit.getPlayer(UUID.fromString(uuid));
if (player == null) throw new IllegalStateException("Player should be online but they are not!");
return new SimpleUserData(player);
}, Patchwork.getInstance()
.getExecutor()
.getAsync())
.join();
}
@Override
public @NotNull UUID getUniqueId()
{
return uuid;
}
@Override
public String getUsername()
{
return username;
}
@Override
public User getUser()
{
return user;
}
@Override
public @Nullable Group getGroup()
{
return group;
}
@Override
public void setGroup(@Nullable final Group group)
{
event.ping();
this.group = group;
}
@Override
public long getPlaytime()
{
return playtime;
}
@Override
public void setPlaytime(final long playtime)
{
event.ping();
this.playtime = playtime;
}
@Override
public void addPlaytime(final long playtime)
{
event.ping();
this.playtime += playtime;
}
@Override
public void resetPlaytime()
{
event.ping();
this.playtime = 0L;
}
@Override
public boolean canInteract()
{
return canInteract;
}
@Override
public void setInteractionState(final boolean canInteract)
{
event.ping();
this.canInteract = canInteract;
}
@Override
public boolean areTransactionsFrozen()
{
return transactionsFrozen;
}
@Override
public long getBalance()
{
return balance.get();
}
@Override
public void setBalance(final long newBalance)
{
balance.set(newBalance);
}
@Override
public long addToBalance(final long amount)
{
return balance.addAndGet(amount);
}
@Override
public long removeFromBalance(final long amount)
{
return balance.addAndGet(-amount);
}
@Override
public boolean hasCustomACFormat()
{
return hasCustomACFormat;
}
@Override
public AdminChatFormat getCustomACFormat()
{
return AdminChatFormat.deserialize(customACFormat);
}
@Override
public void setCustomACFormat(final String format)
{
this.hasCustomACFormat = format.equals(AdminChatFormat.DEFAULT.serialize());
this.customACFormat = format;
}
}