mirror of
https://github.com/WiIIiam278/HuskSync.git
synced 2025-12-19 14:59:21 +00:00
refactor: use Uniform for native command support (#323)
* refactor: use Uniform for commands * refactor: remove commodore * fix: update Uniform, fix commands * fix: bump uniform, fix commands on fabric * feat: use new Uniform command permission system * test: target 1.21
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation project(path: ':common')
|
implementation project(path: ':common')
|
||||||
|
|
||||||
implementation 'org.bstats:bstats-bukkit:3.0.2'
|
implementation 'net.william278.uniform:uniform-bukkit:1.1'
|
||||||
implementation 'net.william278:mpdbdataconverter:1.0.1'
|
implementation 'net.william278:mpdbdataconverter:1.0.1'
|
||||||
implementation 'net.william278:hsldataconverter:1.0'
|
implementation 'net.william278:hsldataconverter:1.0'
|
||||||
implementation 'net.william278:mapdataapi:1.0.3'
|
implementation 'net.william278:mapdataapi:1.0.3'
|
||||||
implementation 'net.william278:andjam:1.0.2'
|
implementation 'net.william278:andjam:1.0.2'
|
||||||
implementation 'me.lucko:commodore:2.2'
|
implementation 'org.bstats:bstats-bukkit:3.0.2'
|
||||||
implementation 'net.kyori:adventure-platform-bukkit:4.3.3'
|
implementation 'net.kyori:adventure-platform-bukkit:4.3.3'
|
||||||
implementation 'dev.triumphteam:triumph-gui:3.1.10'
|
implementation 'dev.triumphteam:triumph-gui:3.1.10'
|
||||||
implementation 'space.arim.morepaperlib:morepaperlib:0.4.4'
|
implementation 'space.arim.morepaperlib:morepaperlib:0.4.4'
|
||||||
@@ -42,6 +42,7 @@ shadowJar {
|
|||||||
relocate 'org.intellij', 'net.william278.husksync.libraries'
|
relocate 'org.intellij', 'net.william278.husksync.libraries'
|
||||||
relocate 'com.zaxxer', 'net.william278.husksync.libraries'
|
relocate 'com.zaxxer', 'net.william278.husksync.libraries'
|
||||||
relocate 'de.exlll', 'net.william278.husksync.libraries'
|
relocate 'de.exlll', 'net.william278.husksync.libraries'
|
||||||
|
relocate 'net.william278.uniform', 'net.william278.husksync.libraries.uniform'
|
||||||
relocate 'net.william278.desertwell', 'net.william278.husksync.libraries.desertwell'
|
relocate 'net.william278.desertwell', 'net.william278.husksync.libraries.desertwell'
|
||||||
relocate 'net.william278.paginedown', 'net.william278.husksync.libraries.paginedown'
|
relocate 'net.william278.paginedown', 'net.william278.husksync.libraries.paginedown'
|
||||||
relocate 'net.william278.mapdataapi', 'net.william278.husksync.libraries.mapdataapi'
|
relocate 'net.william278.mapdataapi', 'net.william278.husksync.libraries.mapdataapi'
|
||||||
@@ -51,7 +52,6 @@ shadowJar {
|
|||||||
relocate 'org.json', 'net.william278.husksync.libraries.json'
|
relocate 'org.json', 'net.william278.husksync.libraries.json'
|
||||||
relocate 'net.querz', 'net.william278.husksync.libraries.nbtparser'
|
relocate 'net.querz', 'net.william278.husksync.libraries.nbtparser'
|
||||||
relocate 'net.roxeez', 'net.william278.husksync.libraries'
|
relocate 'net.roxeez', 'net.william278.husksync.libraries'
|
||||||
relocate 'me.lucko.commodore', 'net.william278.husksync.libraries.commodore'
|
|
||||||
relocate 'org.bstats', 'net.william278.husksync.libraries.bstats'
|
relocate 'org.bstats', 'net.william278.husksync.libraries.bstats'
|
||||||
relocate 'dev.triumphteam.gui', 'net.william278.husksync.libraries.triumphgui'
|
relocate 'dev.triumphteam.gui', 'net.william278.husksync.libraries.triumphgui'
|
||||||
relocate 'space.arim.morepaperlib', 'net.william278.husksync.libraries.paperlib'
|
relocate 'space.arim.morepaperlib', 'net.william278.husksync.libraries.paperlib'
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import net.william278.husksync.adapter.DataAdapter;
|
|||||||
import net.william278.husksync.adapter.GsonAdapter;
|
import net.william278.husksync.adapter.GsonAdapter;
|
||||||
import net.william278.husksync.adapter.SnappyGsonAdapter;
|
import net.william278.husksync.adapter.SnappyGsonAdapter;
|
||||||
import net.william278.husksync.api.BukkitHuskSyncAPI;
|
import net.william278.husksync.api.BukkitHuskSyncAPI;
|
||||||
import net.william278.husksync.command.BukkitCommand;
|
import net.william278.husksync.command.PluginCommand;
|
||||||
import net.william278.husksync.config.Locales;
|
import net.william278.husksync.config.Locales;
|
||||||
import net.william278.husksync.config.Server;
|
import net.william278.husksync.config.Server;
|
||||||
import net.william278.husksync.config.Settings;
|
import net.william278.husksync.config.Settings;
|
||||||
@@ -57,6 +57,8 @@ import net.william278.husksync.util.BukkitLegacyConverter;
|
|||||||
import net.william278.husksync.util.BukkitMapPersister;
|
import net.william278.husksync.util.BukkitMapPersister;
|
||||||
import net.william278.husksync.util.BukkitTask;
|
import net.william278.husksync.util.BukkitTask;
|
||||||
import net.william278.husksync.util.LegacyConverter;
|
import net.william278.husksync.util.LegacyConverter;
|
||||||
|
import net.william278.uniform.Uniform;
|
||||||
|
import net.william278.uniform.bukkit.BukkitUniform;
|
||||||
import org.bstats.bukkit.Metrics;
|
import org.bstats.bukkit.Metrics;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.map.MapView;
|
import org.bukkit.map.MapView;
|
||||||
@@ -64,7 +66,6 @@ import org.bukkit.plugin.Plugin;
|
|||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import space.arim.morepaperlib.MorePaperLib;
|
import space.arim.morepaperlib.MorePaperLib;
|
||||||
import space.arim.morepaperlib.commands.CommandRegistration;
|
|
||||||
import space.arim.morepaperlib.scheduling.AsynchronousScheduler;
|
import space.arim.morepaperlib.scheduling.AsynchronousScheduler;
|
||||||
import space.arim.morepaperlib.scheduling.AttachedScheduler;
|
import space.arim.morepaperlib.scheduling.AttachedScheduler;
|
||||||
import space.arim.morepaperlib.scheduling.GracefulScheduling;
|
import space.arim.morepaperlib.scheduling.GracefulScheduling;
|
||||||
@@ -135,6 +136,10 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
|
|||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
this.audiences = BukkitAudiences.create(this);
|
this.audiences = BukkitAudiences.create(this);
|
||||||
|
|
||||||
|
// Register commands
|
||||||
|
initialize("commands", (plugin) -> getUniform().register(PluginCommand.Type.create(this)));
|
||||||
|
|
||||||
// Prepare data adapter
|
// Prepare data adapter
|
||||||
initialize("data adapter", (plugin) -> {
|
initialize("data adapter", (plugin) -> {
|
||||||
if (settings.getSynchronization().isCompressData()) {
|
if (settings.getSynchronization().isCompressData()) {
|
||||||
@@ -196,9 +201,6 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
|
|||||||
// Register events
|
// Register events
|
||||||
initialize("events", (plugin) -> eventListener.onEnable());
|
initialize("events", (plugin) -> eventListener.onEnable());
|
||||||
|
|
||||||
// Register commands
|
|
||||||
initialize("commands", (plugin) -> BukkitCommand.Type.registerCommands(this));
|
|
||||||
|
|
||||||
// Register plugin hooks
|
// Register plugin hooks
|
||||||
initialize("hooks", (plugin) -> {
|
initialize("hooks", (plugin) -> {
|
||||||
if (isDependencyLoaded("Plan") && getSettings().isEnablePlanHook()) {
|
if (isDependencyLoaded("Plan") && getSettings().isEnablePlanHook()) {
|
||||||
@@ -264,6 +266,12 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
|
|||||||
this.dataSyncer = dataSyncer;
|
this.dataSyncer = dataSyncer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NotNull
|
||||||
|
public Uniform getUniform() {
|
||||||
|
return BukkitUniform.getInstance(this);
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public Map<Identifier, Data> getPlayerCustomDataStore(@NotNull OnlineUser user) {
|
public Map<Identifier, Data> getPlayerCustomDataStore(@NotNull OnlineUser user) {
|
||||||
@@ -352,11 +360,6 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
|
|||||||
return getScheduler().entitySpecificScheduler(((BukkitUser) user).getPlayer());
|
return getScheduler().entitySpecificScheduler(((BukkitUser) user).getPlayer());
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public CommandRegistration getCommandRegistrar() {
|
|
||||||
return paperLib.commandRegistration();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NotNull
|
@NotNull
|
||||||
public Path getConfigDirectory() {
|
public Path getConfigDirectory() {
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of HuskSync, licensed under the Apache License 2.0.
|
|
||||||
*
|
|
||||||
* Copyright (c) William278 <will27528@gmail.com>
|
|
||||||
* Copyright (c) contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.william278.husksync.command;
|
|
||||||
|
|
||||||
import me.lucko.commodore.CommodoreProvider;
|
|
||||||
import me.lucko.commodore.file.CommodoreFileReader;
|
|
||||||
import net.william278.husksync.BukkitHuskSync;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
|
|
||||||
public class BrigadierUtil {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses commodore to register command completions.
|
|
||||||
*
|
|
||||||
* @param plugin instance of the registering Bukkit plugin
|
|
||||||
* @param bukkitCommand the Bukkit PluginCommand to register completions for
|
|
||||||
* @param command the {@link Command} to register completions for
|
|
||||||
*/
|
|
||||||
protected static void registerCommodore(@NotNull BukkitHuskSync plugin,
|
|
||||||
@NotNull org.bukkit.command.Command bukkitCommand,
|
|
||||||
@NotNull Command command) {
|
|
||||||
final InputStream commodoreFile = plugin.getResource(
|
|
||||||
"commodore/" + bukkitCommand.getName() + ".commodore"
|
|
||||||
);
|
|
||||||
if (commodoreFile == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
CommodoreProvider.getCommodore(plugin).register(bukkitCommand,
|
|
||||||
CommodoreFileReader.INSTANCE.parse(commodoreFile),
|
|
||||||
player -> player.hasPermission(command.getPermission()));
|
|
||||||
} catch (IOException e) {
|
|
||||||
plugin.log(Level.SEVERE, String.format(
|
|
||||||
"Failed to read command commodore completions for %s", bukkitCommand.getName()), e
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,164 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of HuskSync, licensed under the Apache License 2.0.
|
|
||||||
*
|
|
||||||
* Copyright (c) William278 <will27528@gmail.com>
|
|
||||||
* Copyright (c) contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.william278.husksync.command;
|
|
||||||
|
|
||||||
|
|
||||||
import me.lucko.commodore.CommodoreProvider;
|
|
||||||
import net.william278.husksync.BukkitHuskSync;
|
|
||||||
import net.william278.husksync.user.BukkitUser;
|
|
||||||
import net.william278.husksync.user.CommandUser;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.permissions.Permission;
|
|
||||||
import org.bukkit.permissions.PermissionDefault;
|
|
||||||
import org.bukkit.plugin.PluginManager;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public class BukkitCommand extends org.bukkit.command.Command {
|
|
||||||
|
|
||||||
private final BukkitHuskSync plugin;
|
|
||||||
private final Command command;
|
|
||||||
|
|
||||||
public BukkitCommand(@NotNull Command command, @NotNull BukkitHuskSync plugin) {
|
|
||||||
super(command.getName(), command.getDescription(), command.getUsage(), command.getAliases());
|
|
||||||
this.command = command;
|
|
||||||
this.plugin = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
|
|
||||||
this.command.onExecuted(sender instanceof Player p ? BukkitUser.adapt(p, plugin) : plugin.getConsole(), args);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias,
|
|
||||||
@NotNull String[] args) throws IllegalArgumentException {
|
|
||||||
if (!(this.command instanceof TabProvider provider)) {
|
|
||||||
return List.of();
|
|
||||||
}
|
|
||||||
final CommandUser user = sender instanceof Player p ? BukkitUser.adapt(p, plugin) : plugin.getConsole();
|
|
||||||
if (getPermission() == null || user.hasPermission(getPermission())) {
|
|
||||||
return provider.getSuggestions(user, args);
|
|
||||||
}
|
|
||||||
return List.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void register() {
|
|
||||||
// Register with bukkit
|
|
||||||
plugin.getCommandRegistrar().getServerCommandMap().register("husksync", this);
|
|
||||||
|
|
||||||
// Register permissions
|
|
||||||
BukkitCommand.addPermission(
|
|
||||||
plugin,
|
|
||||||
command.getPermission(),
|
|
||||||
command.getUsage(),
|
|
||||||
BukkitCommand.getPermissionDefault(command.isOperatorCommand())
|
|
||||||
);
|
|
||||||
final List<Permission> childNodes = command.getAdditionalPermissions()
|
|
||||||
.entrySet().stream()
|
|
||||||
.map((entry) -> BukkitCommand.addPermission(
|
|
||||||
plugin,
|
|
||||||
entry.getKey(),
|
|
||||||
"",
|
|
||||||
BukkitCommand.getPermissionDefault(entry.getValue()))
|
|
||||||
)
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.toList();
|
|
||||||
if (!childNodes.isEmpty()) {
|
|
||||||
BukkitCommand.addPermission(
|
|
||||||
plugin,
|
|
||||||
command.getPermission("*"),
|
|
||||||
command.getUsage(),
|
|
||||||
PermissionDefault.FALSE,
|
|
||||||
childNodes.toArray(new Permission[0])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register commodore TAB completion
|
|
||||||
if (CommodoreProvider.isSupported() && plugin.getSettings().isBrigadierTabCompletion()) {
|
|
||||||
BrigadierUtil.registerCommodore(plugin, this, command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
protected static Permission addPermission(@NotNull BukkitHuskSync plugin, @NotNull String node,
|
|
||||||
@NotNull String description, @NotNull PermissionDefault permissionDefault,
|
|
||||||
@NotNull Permission... children) {
|
|
||||||
final Map<String, Boolean> childNodes = Arrays.stream(children)
|
|
||||||
.map(Permission::getName)
|
|
||||||
.collect(HashMap::new, (map, child) -> map.put(child, true), HashMap::putAll);
|
|
||||||
|
|
||||||
final PluginManager manager = plugin.getServer().getPluginManager();
|
|
||||||
if (manager.getPermission(node) != null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Permission permission;
|
|
||||||
if (description.isEmpty()) {
|
|
||||||
permission = new Permission(node, permissionDefault, childNodes);
|
|
||||||
} else {
|
|
||||||
permission = new Permission(node, description, permissionDefault, childNodes);
|
|
||||||
}
|
|
||||||
manager.addPermission(permission);
|
|
||||||
|
|
||||||
return permission;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
protected static PermissionDefault getPermissionDefault(boolean isOperatorCommand) {
|
|
||||||
return isOperatorCommand ? PermissionDefault.OP : PermissionDefault.TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Commands available on the Bukkit HuskSync implementation
|
|
||||||
*/
|
|
||||||
public enum Type {
|
|
||||||
|
|
||||||
HUSKSYNC_COMMAND(HuskSyncCommand::new),
|
|
||||||
USERDATA_COMMAND(UserDataCommand::new),
|
|
||||||
INVENTORY_COMMAND(InventoryCommand::new),
|
|
||||||
ENDER_CHEST_COMMAND(EnderChestCommand::new);
|
|
||||||
|
|
||||||
public final Function<BukkitHuskSync, Command> commandSupplier;
|
|
||||||
|
|
||||||
Type(@NotNull Function<BukkitHuskSync, Command> supplier) {
|
|
||||||
this.commandSupplier = supplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public Command createCommand(@NotNull BukkitHuskSync plugin) {
|
|
||||||
return commandSupplier.apply(plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void registerCommands(@NotNull BukkitHuskSync plugin) {
|
|
||||||
Arrays.stream(values())
|
|
||||||
.map((type) -> type.createCommand(plugin))
|
|
||||||
.forEach((command) -> new BukkitCommand(command, plugin).register());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
inventory {
|
|
||||||
name brigadier:string single_word;
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
husksync {
|
|
||||||
update;
|
|
||||||
about;
|
|
||||||
status;
|
|
||||||
reload;
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
enderchest {
|
|
||||||
name brigadier:string single_word;
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
userdata {
|
|
||||||
view {
|
|
||||||
name brigadier:string single_word {
|
|
||||||
version brigadier:string single_word;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
list {
|
|
||||||
name brigadier:string single_word {
|
|
||||||
page brigadier:integer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
delete {
|
|
||||||
name brigadier:string single_word {
|
|
||||||
version brigadier:string single_word;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
restore {
|
|
||||||
name brigadier:string single_word {
|
|
||||||
version brigadier:string single_word;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pin {
|
|
||||||
name brigadier:string single_word {
|
|
||||||
version brigadier:string single_word;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dump {
|
|
||||||
name brigadier:string single_word {
|
|
||||||
version brigadier:string single_word {
|
|
||||||
web;
|
|
||||||
file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -16,6 +16,8 @@ dependencies {
|
|||||||
exclude module: 'slf4j-api'
|
exclude module: 'slf4j-api'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compileOnly 'net.william278.uniform:uniform-common:1.1'
|
||||||
|
compileOnly 'com.mojang:brigadier:1.1.8'
|
||||||
compileOnly 'org.projectlombok:lombok:1.18.32'
|
compileOnly 'org.projectlombok:lombok:1.18.32'
|
||||||
compileOnly 'org.jetbrains:annotations:24.1.0'
|
compileOnly 'org.jetbrains:annotations:24.1.0'
|
||||||
compileOnly 'net.kyori:adventure-api:4.17.0'
|
compileOnly 'net.kyori:adventure-api:4.17.0'
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import net.william278.husksync.user.ConsoleUser;
|
|||||||
import net.william278.husksync.user.OnlineUser;
|
import net.william278.husksync.user.OnlineUser;
|
||||||
import net.william278.husksync.util.LegacyConverter;
|
import net.william278.husksync.util.LegacyConverter;
|
||||||
import net.william278.husksync.util.Task;
|
import net.william278.husksync.util.Task;
|
||||||
|
import net.william278.uniform.Uniform;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -111,6 +112,14 @@ public interface HuskSync extends Task.Supplier, EventDispatcher, ConfigProvider
|
|||||||
*/
|
*/
|
||||||
void setDataSyncer(@NotNull DataSyncer dataSyncer);
|
void setDataSyncer(@NotNull DataSyncer dataSyncer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the uniform command provider
|
||||||
|
*
|
||||||
|
* @return the command provider
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
Uniform getUniform();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of available data {@link Migrator}s
|
* Returns a list of available data {@link Migrator}s
|
||||||
*
|
*
|
||||||
@@ -256,10 +265,10 @@ public interface HuskSync extends Task.Supplier, EventDispatcher, ConfigProvider
|
|||||||
@NotNull
|
@NotNull
|
||||||
default UpdateChecker getUpdateChecker() {
|
default UpdateChecker getUpdateChecker() {
|
||||||
return UpdateChecker.builder()
|
return UpdateChecker.builder()
|
||||||
.currentVersion(getPluginVersion())
|
.currentVersion(getPluginVersion())
|
||||||
.endpoint(UpdateChecker.Endpoint.SPIGOT)
|
.endpoint(UpdateChecker.Endpoint.SPIGOT)
|
||||||
.resource(Integer.toString(SPIGOT_RESOURCE_ID))
|
.resource(Integer.toString(SPIGOT_RESOURCE_ID))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
default void checkForUpdates() {
|
default void checkForUpdates() {
|
||||||
@@ -267,8 +276,8 @@ public interface HuskSync extends Task.Supplier, EventDispatcher, ConfigProvider
|
|||||||
getUpdateChecker().check().thenAccept(checked -> {
|
getUpdateChecker().check().thenAccept(checked -> {
|
||||||
if (!checked.isUpToDate()) {
|
if (!checked.isUpToDate()) {
|
||||||
log(Level.WARNING, String.format(
|
log(Level.WARNING, String.format(
|
||||||
"A new version of HuskSync is available: v%s (running v%s)",
|
"A new version of HuskSync is available: v%s (running v%s)",
|
||||||
checked.getLatestVersion(), getPluginVersion())
|
checked.getLatestVersion(), getPluginVersion())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -311,15 +320,15 @@ public interface HuskSync extends Task.Supplier, EventDispatcher, ConfigProvider
|
|||||||
final class FailedToLoadException extends IllegalStateException {
|
final class FailedToLoadException extends IllegalStateException {
|
||||||
|
|
||||||
private static final String FORMAT = """
|
private static final String FORMAT = """
|
||||||
HuskSync has failed to load! The plugin will not be enabled and no data will be synchronized.
|
HuskSync has failed to load! The plugin will not be enabled and no data will be synchronized.
|
||||||
Please make sure the plugin has been setup correctly (https://william278.net/docs/husksync/setup):
|
Please make sure the plugin has been setup correctly (https://william278.net/docs/husksync/setup):
|
||||||
|
|
||||||
1) Make sure you've entered your MySQL, MariaDB or MongoDB database details correctly in config.yml
|
1) Make sure you've entered your MySQL, MariaDB or MongoDB database details correctly in config.yml
|
||||||
2) Make sure your Redis server details are also correct in config.yml
|
2) Make sure your Redis server details are also correct in config.yml
|
||||||
3) Make sure your config is up-to-date (https://william278.net/docs/husksync/config-file)
|
3) Make sure your config is up-to-date (https://william278.net/docs/husksync/config-file)
|
||||||
4) Check the error below for more details
|
4) Check the error below for more details
|
||||||
|
|
||||||
Caused by: %s""";
|
Caused by: %s""";
|
||||||
|
|
||||||
FailedToLoadException(@NotNull String message, @NotNull Throwable cause) {
|
FailedToLoadException(@NotNull String message, @NotNull Throwable cause) {
|
||||||
super(String.format(FORMAT, message), cause);
|
super(String.format(FORMAT, message), cause);
|
||||||
|
|||||||
@@ -1,94 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of HuskSync, licensed under the Apache License 2.0.
|
|
||||||
*
|
|
||||||
* Copyright (c) William278 <will27528@gmail.com>
|
|
||||||
* Copyright (c) contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.william278.husksync.command;
|
|
||||||
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import net.william278.husksync.HuskSync;
|
|
||||||
import net.william278.husksync.user.CommandUser;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public abstract class Command extends Node {
|
|
||||||
|
|
||||||
private final String usage;
|
|
||||||
private final Map<String, Boolean> additionalPermissions;
|
|
||||||
|
|
||||||
protected Command(@NotNull String name, @NotNull List<String> aliases, @NotNull String usage,
|
|
||||||
@NotNull HuskSync plugin) {
|
|
||||||
super(name, aliases, plugin);
|
|
||||||
this.usage = usage;
|
|
||||||
this.additionalPermissions = Maps.newHashMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void onExecuted(@NotNull CommandUser executor, @NotNull String[] args) {
|
|
||||||
if (!executor.hasPermission(getPermission())) {
|
|
||||||
plugin.getLocales().getLocale("error_no_permission")
|
|
||||||
.ifPresent(executor::sendMessage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
plugin.runAsync(() -> this.execute(executor, args));
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void execute(@NotNull CommandUser executor, @NotNull String[] args);
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
protected String[] removeFirstArg(@NotNull String[] args) {
|
|
||||||
if (args.length <= 1) {
|
|
||||||
return new String[0];
|
|
||||||
}
|
|
||||||
String[] newArgs = new String[args.length - 1];
|
|
||||||
System.arraycopy(args, 1, newArgs, 0, args.length - 1);
|
|
||||||
return newArgs;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public final String getRawUsage() {
|
|
||||||
return usage;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public final String getUsage() {
|
|
||||||
return "/" + getName() + " " + getRawUsage();
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void addAdditionalPermissions(@NotNull Map<String, Boolean> permissions) {
|
|
||||||
permissions.forEach((permission, value) -> this.additionalPermissions.put(getPermission(permission), value));
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public final Map<String, Boolean> getAdditionalPermissions() {
|
|
||||||
return additionalPermissions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public String getDescription() {
|
|
||||||
return plugin.getLocales().getRawLocale(getName() + "_command_description")
|
|
||||||
.orElse(getUsage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public final HuskSync getPlugin() {
|
|
||||||
return plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -37,7 +37,7 @@ import java.util.Optional;
|
|||||||
public class EnderChestCommand extends ItemsCommand {
|
public class EnderChestCommand extends ItemsCommand {
|
||||||
|
|
||||||
public EnderChestCommand(@NotNull HuskSync plugin) {
|
public EnderChestCommand(@NotNull HuskSync plugin) {
|
||||||
super(plugin, List.of("enderchest", "echest", "openechest"));
|
super("enderchest", List.of("echest", "openechest"), plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -46,29 +46,29 @@ public class EnderChestCommand extends ItemsCommand {
|
|||||||
final Optional<Data.Items.EnderChest> optionalEnderChest = snapshot.getEnderChest();
|
final Optional<Data.Items.EnderChest> optionalEnderChest = snapshot.getEnderChest();
|
||||||
if (optionalEnderChest.isEmpty()) {
|
if (optionalEnderChest.isEmpty()) {
|
||||||
plugin.getLocales().getLocale("error_no_data_to_display")
|
plugin.getLocales().getLocale("error_no_data_to_display")
|
||||||
.ifPresent(viewer::sendMessage);
|
.ifPresent(viewer::sendMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display opening message
|
// Display opening message
|
||||||
plugin.getLocales().getLocale("ender_chest_viewer_opened", user.getUsername(),
|
plugin.getLocales().getLocale("ender_chest_viewer_opened", user.getUsername(),
|
||||||
snapshot.getTimestamp().format(DateTimeFormatter
|
snapshot.getTimestamp().format(DateTimeFormatter
|
||||||
.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT)))
|
.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT)))
|
||||||
.ifPresent(viewer::sendMessage);
|
.ifPresent(viewer::sendMessage);
|
||||||
|
|
||||||
// Show GUI
|
// Show GUI
|
||||||
final Data.Items.EnderChest enderChest = optionalEnderChest.get();
|
final Data.Items.EnderChest enderChest = optionalEnderChest.get();
|
||||||
viewer.showGui(
|
viewer.showGui(
|
||||||
enderChest,
|
enderChest,
|
||||||
plugin.getLocales().getLocale("ender_chest_viewer_menu_title", user.getUsername())
|
plugin.getLocales().getLocale("ender_chest_viewer_menu_title", user.getUsername())
|
||||||
.orElse(new MineDown(String.format("%s's Ender Chest", user.getUsername()))),
|
.orElse(new MineDown(String.format("%s's Ender Chest", user.getUsername()))),
|
||||||
allowEdit,
|
allowEdit,
|
||||||
enderChest.getSlotCount(),
|
enderChest.getSlotCount(),
|
||||||
(itemsOnClose) -> {
|
(itemsOnClose) -> {
|
||||||
if (allowEdit && !enderChest.equals(itemsOnClose)) {
|
if (allowEdit && !enderChest.equals(itemsOnClose)) {
|
||||||
plugin.runAsync(() -> this.updateItems(viewer, itemsOnClose, user));
|
plugin.runAsync(() -> this.updateItems(viewer, itemsOnClose, user));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ public class EnderChestCommand extends ItemsCommand {
|
|||||||
final Optional<DataSnapshot.Packed> latestData = plugin.getDatabase().getLatestSnapshot(holder);
|
final Optional<DataSnapshot.Packed> latestData = plugin.getDatabase().getLatestSnapshot(holder);
|
||||||
if (latestData.isEmpty()) {
|
if (latestData.isEmpty()) {
|
||||||
plugin.getLocales().getLocale("error_no_data_to_display")
|
plugin.getLocales().getLocale("error_no_data_to_display")
|
||||||
.ifPresent(viewer::sendMessage);
|
.ifPresent(viewer::sendMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ public class EnderChestCommand extends ItemsCommand {
|
|||||||
data.getEnderChest().ifPresent(enderChest -> enderChest.setContents(items));
|
data.getEnderChest().ifPresent(enderChest -> enderChest.setContents(items));
|
||||||
data.setSaveCause(DataSnapshot.SaveCause.ENDERCHEST_COMMAND);
|
data.setSaveCause(DataSnapshot.SaveCause.ENDERCHEST_COMMAND);
|
||||||
data.setPinned(
|
data.setPinned(
|
||||||
plugin.getSettings().getSynchronization().doAutoPin(DataSnapshot.SaveCause.ENDERCHEST_COMMAND)
|
plugin.getSettings().getSynchronization().doAutoPin(DataSnapshot.SaveCause.ENDERCHEST_COMMAND)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of HuskSync, licensed under the Apache License 2.0.
|
|
||||||
*
|
|
||||||
* Copyright (c) William278 <will27528@gmail.com>
|
|
||||||
* Copyright (c) contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.william278.husksync.command;
|
|
||||||
|
|
||||||
import net.william278.husksync.user.CommandUser;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
public interface Executable {
|
|
||||||
|
|
||||||
void onExecuted(@NotNull CommandUser executor, @NotNull String[] args);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
package net.william278.husksync.command;
|
package net.william278.husksync.command;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import de.themoep.minedown.adventure.MineDown;
|
import de.themoep.minedown.adventure.MineDown;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.JoinConfiguration;
|
import net.kyori.adventure.text.JoinConfiguration;
|
||||||
@@ -31,229 +33,219 @@ import net.william278.husksync.HuskSync;
|
|||||||
import net.william278.husksync.database.Database;
|
import net.william278.husksync.database.Database;
|
||||||
import net.william278.husksync.migrator.Migrator;
|
import net.william278.husksync.migrator.Migrator;
|
||||||
import net.william278.husksync.user.CommandUser;
|
import net.william278.husksync.user.CommandUser;
|
||||||
import net.william278.husksync.user.OnlineUser;
|
import net.william278.uniform.BaseCommand;
|
||||||
|
import net.william278.uniform.CommandProvider;
|
||||||
|
import net.william278.uniform.Permission;
|
||||||
|
import net.william278.uniform.element.ArgumentElement;
|
||||||
import org.apache.commons.text.WordUtils;
|
import org.apache.commons.text.WordUtils;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class HuskSyncCommand extends Command implements TabProvider {
|
public class HuskSyncCommand extends PluginCommand {
|
||||||
|
|
||||||
private static final Map<String, Boolean> SUB_COMMANDS = Map.of(
|
|
||||||
"about", false,
|
|
||||||
"status", true,
|
|
||||||
"reload", true,
|
|
||||||
"migrate", true,
|
|
||||||
"update", true
|
|
||||||
);
|
|
||||||
|
|
||||||
private final UpdateChecker updateChecker;
|
private final UpdateChecker updateChecker;
|
||||||
private final AboutMenu aboutMenu;
|
private final AboutMenu aboutMenu;
|
||||||
|
|
||||||
public HuskSyncCommand(@NotNull HuskSync plugin) {
|
public HuskSyncCommand(@NotNull HuskSync plugin) {
|
||||||
super("husksync", List.of(), "[" + String.join("|", SUB_COMMANDS.keySet()) + "]", plugin);
|
super("husksync", List.of(), Permission.Default.TRUE, plugin);
|
||||||
addAdditionalPermissions(SUB_COMMANDS);
|
|
||||||
|
|
||||||
this.updateChecker = plugin.getUpdateChecker();
|
this.updateChecker = plugin.getUpdateChecker();
|
||||||
this.aboutMenu = AboutMenu.builder()
|
this.aboutMenu = AboutMenu.builder()
|
||||||
.title(Component.text("HuskSync"))
|
.title(Component.text("HuskSync"))
|
||||||
.description(Component.text("A modern, cross-server player data synchronization system"))
|
.description(Component.text("A modern, cross-server player data synchronization system"))
|
||||||
.version(plugin.getPluginVersion())
|
.version(plugin.getPluginVersion())
|
||||||
.credits("Author",
|
.credits("Author",
|
||||||
AboutMenu.Credit.of("William278").description("Click to visit website").url("https://william278.net"))
|
AboutMenu.Credit.of("William278").description("Click to visit website").url("https://william278.net"))
|
||||||
.credits("Contributors",
|
.credits("Contributors",
|
||||||
AboutMenu.Credit.of("HarvelsX").description("Code"),
|
AboutMenu.Credit.of("HarvelsX").description("Code"),
|
||||||
AboutMenu.Credit.of("HookWoods").description("Code"),
|
AboutMenu.Credit.of("HookWoods").description("Code"),
|
||||||
AboutMenu.Credit.of("Preva1l").description("Code"),
|
AboutMenu.Credit.of("Preva1l").description("Code"),
|
||||||
AboutMenu.Credit.of("hanbings").description("Code (Fabric porting)"),
|
AboutMenu.Credit.of("hanbings").description("Code (Fabric porting)"),
|
||||||
AboutMenu.Credit.of("Stampede2011").description("Code (Fabric mixins)"))
|
AboutMenu.Credit.of("Stampede2011").description("Code (Fabric mixins)"))
|
||||||
.credits("Translators",
|
.credits("Translators",
|
||||||
AboutMenu.Credit.of("Namiu").description("Japanese (ja-jp)"),
|
AboutMenu.Credit.of("Namiu").description("Japanese (ja-jp)"),
|
||||||
AboutMenu.Credit.of("anchelthe").description("Spanish (es-es)"),
|
AboutMenu.Credit.of("anchelthe").description("Spanish (es-es)"),
|
||||||
AboutMenu.Credit.of("Melonzio").description("Spanish (es-es)"),
|
AboutMenu.Credit.of("Melonzio").description("Spanish (es-es)"),
|
||||||
AboutMenu.Credit.of("Ceddix").description("German (de-de)"),
|
AboutMenu.Credit.of("Ceddix").description("German (de-de)"),
|
||||||
AboutMenu.Credit.of("Pukejoy_1").description("Bulgarian (bg-bg)"),
|
AboutMenu.Credit.of("Pukejoy_1").description("Bulgarian (bg-bg)"),
|
||||||
AboutMenu.Credit.of("mateusneresrb").description("Brazilian Portuguese (pt-br)"),
|
AboutMenu.Credit.of("mateusneresrb").description("Brazilian Portuguese (pt-br)"),
|
||||||
AboutMenu.Credit.of("小蔡").description("Traditional Chinese (zh-tw)"),
|
AboutMenu.Credit.of("小蔡").description("Traditional Chinese (zh-tw)"),
|
||||||
AboutMenu.Credit.of("Ghost-chu").description("Simplified Chinese (zh-cn)"),
|
AboutMenu.Credit.of("Ghost-chu").description("Simplified Chinese (zh-cn)"),
|
||||||
AboutMenu.Credit.of("DJelly4K").description("Simplified Chinese (zh-cn)"),
|
AboutMenu.Credit.of("DJelly4K").description("Simplified Chinese (zh-cn)"),
|
||||||
AboutMenu.Credit.of("Thourgard").description("Ukrainian (uk-ua)"),
|
AboutMenu.Credit.of("Thourgard").description("Ukrainian (uk-ua)"),
|
||||||
AboutMenu.Credit.of("xF3d3").description("Italian (it-it)"),
|
AboutMenu.Credit.of("xF3d3").description("Italian (it-it)"),
|
||||||
AboutMenu.Credit.of("cada3141").description("Korean (ko-kr)"),
|
AboutMenu.Credit.of("cada3141").description("Korean (ko-kr)"),
|
||||||
AboutMenu.Credit.of("Wirayuda5620").description("Indonesian (id-id)"),
|
AboutMenu.Credit.of("Wirayuda5620").description("Indonesian (id-id)"),
|
||||||
AboutMenu.Credit.of("WinTone01").description("Turkish (tr-tr)"),
|
AboutMenu.Credit.of("WinTone01").description("Turkish (tr-tr)"),
|
||||||
AboutMenu.Credit.of("IbanEtchep").description("French (fr-fr)"))
|
AboutMenu.Credit.of("IbanEtchep").description("French (fr-fr)"))
|
||||||
.buttons(
|
.buttons(
|
||||||
AboutMenu.Link.of("https://william278.net/docs/husksync").text("Documentation").icon("⛏"),
|
AboutMenu.Link.of("https://william278.net/docs/husksync").text("Documentation").icon("⛏"),
|
||||||
AboutMenu.Link.of("https://github.com/WiIIiam278/HuskSync/issues").text("Issues").icon("❌").color(TextColor.color(0xff9f0f)),
|
AboutMenu.Link.of("https://github.com/WiIIiam278/HuskSync/issues").text("Issues").icon("❌").color(TextColor.color(0xff9f0f)),
|
||||||
AboutMenu.Link.of("https://discord.gg/tVYhJfyDWG").text("Discord").icon("⭐").color(TextColor.color(0x6773f5)))
|
AboutMenu.Link.of("https://discord.gg/tVYhJfyDWG").text("Discord").icon("⭐").color(TextColor.color(0x6773f5)))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(@NotNull CommandUser executor, @NotNull String[] args) {
|
public void provide(@NotNull BaseCommand<?> command) {
|
||||||
final String subCommand = parseStringArg(args, 0).orElse("about").toLowerCase(Locale.ENGLISH);
|
command.setDefaultExecutor((ctx) -> about(command, ctx));
|
||||||
if (SUB_COMMANDS.containsKey(subCommand) && !executor.hasPermission(getPermission(subCommand))) {
|
command.addSubCommand("about", (sub) -> sub.setDefaultExecutor((ctx) -> about(command, ctx)));
|
||||||
plugin.getLocales().getLocale("error_no_permission")
|
command.addSubCommand("status", needsOp("status"), status());
|
||||||
.ifPresent(executor::sendMessage);
|
command.addSubCommand("reload", needsOp("reload"), reload());
|
||||||
return;
|
command.addSubCommand("update", needsOp("update"), update());
|
||||||
}
|
command.addSubCommand("migrate", migrate());
|
||||||
|
|
||||||
switch (subCommand) {
|
|
||||||
case "about" -> executor.sendMessage(aboutMenu.toComponent());
|
|
||||||
case "status" -> {
|
|
||||||
getPlugin().getLocales().getLocale("system_status_header").ifPresent(executor::sendMessage);
|
|
||||||
executor.sendMessage(Component.join(
|
|
||||||
JoinConfiguration.newlines(),
|
|
||||||
Arrays.stream(StatusLine.values()).map(s -> s.get(plugin)).toList()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
case "reload" -> {
|
|
||||||
try {
|
|
||||||
plugin.loadSettings();
|
|
||||||
plugin.loadLocales();
|
|
||||||
plugin.loadServer();
|
|
||||||
plugin.getLocales().getLocale("reload_complete").ifPresent(executor::sendMessage);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
executor.sendMessage(new MineDown(
|
|
||||||
"[Error:](#ff3300) [Failed to reload the plugin. Check console for errors.](#ff7e5e)"
|
|
||||||
));
|
|
||||||
plugin.log(Level.SEVERE, "Failed to reload the plugin", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "migrate" -> {
|
|
||||||
if (executor instanceof OnlineUser) {
|
|
||||||
plugin.getLocales().getLocale("error_console_command_only")
|
|
||||||
.ifPresent(executor::sendMessage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.handleMigrationCommand(args);
|
|
||||||
}
|
|
||||||
case "update" -> updateChecker.check().thenAccept(checked -> {
|
|
||||||
if (checked.isUpToDate()) {
|
|
||||||
plugin.getLocales().getLocale("up_to_date", plugin.getPluginVersion().toString())
|
|
||||||
.ifPresent(executor::sendMessage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
plugin.getLocales().getLocale("update_available", checked.getLatestVersion().toString(),
|
|
||||||
plugin.getPluginVersion().toString()).ifPresent(executor::sendMessage);
|
|
||||||
});
|
|
||||||
default -> plugin.getLocales().getLocale("error_invalid_syntax", getUsage())
|
|
||||||
.ifPresent(executor::sendMessage);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle a migration console command input
|
private void about(@NotNull BaseCommand<?> c, @NotNull CommandContext<?> ctx) {
|
||||||
private void handleMigrationCommand(@NotNull String[] args) {
|
user(c, ctx).getAudience().sendMessage(aboutMenu.toComponent());
|
||||||
if (args.length < 2) {
|
}
|
||||||
plugin.log(Level.INFO,
|
|
||||||
"Please choose a migrator, then run \"husksync migrate <migrator>\"");
|
|
||||||
this.logMigratorList();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Optional<Migrator> selectedMigrator = plugin.getAvailableMigrators().stream()
|
@NotNull
|
||||||
.filter(available -> available.getIdentifier().equalsIgnoreCase(args[1]))
|
private CommandProvider status() {
|
||||||
.findFirst();
|
return (sub) -> sub.setDefaultExecutor((ctx) -> {
|
||||||
selectedMigrator.ifPresentOrElse(migrator -> {
|
final CommandUser user = user(sub, ctx);
|
||||||
if (args.length < 3) {
|
plugin.getLocales().getLocale("system_status_header").ifPresent(user::sendMessage);
|
||||||
plugin.log(Level.INFO, migrator.getHelpMenu());
|
user.sendMessage(Component.join(
|
||||||
|
JoinConfiguration.newlines(),
|
||||||
|
Arrays.stream(StatusLine.values()).map(s -> s.get(plugin)).toList()
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private CommandProvider reload() {
|
||||||
|
return (sub) -> sub.setDefaultExecutor((ctx) -> {
|
||||||
|
final CommandUser user = user(sub, ctx);
|
||||||
|
try {
|
||||||
|
plugin.loadSettings();
|
||||||
|
plugin.loadLocales();
|
||||||
|
plugin.loadServer();
|
||||||
|
plugin.getLocales().getLocale("reload_complete").ifPresent(user::sendMessage);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
user.sendMessage(new MineDown(
|
||||||
|
"[Error:](#ff3300) [Failed to reload the plugin. Check console for errors.](#ff7e5e)"
|
||||||
|
));
|
||||||
|
plugin.log(Level.SEVERE, "Failed to reload the plugin", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private CommandProvider update() {
|
||||||
|
return (sub) -> sub.setDefaultExecutor((ctx) -> updateChecker.check().thenAccept(checked -> {
|
||||||
|
final CommandUser user = user(sub, ctx);
|
||||||
|
if (checked.isUpToDate()) {
|
||||||
|
plugin.getLocales().getLocale("up_to_date", plugin.getPluginVersion().toString())
|
||||||
|
.ifPresent(user::sendMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (args[2]) {
|
plugin.getLocales().getLocale("update_available", checked.getLatestVersion().toString(),
|
||||||
case "start" -> migrator.start().thenAccept(succeeded -> {
|
plugin.getPluginVersion().toString()).ifPresent(user::sendMessage);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private CommandProvider migrate() {
|
||||||
|
return (sub) -> {
|
||||||
|
sub.setCondition((ctx) -> sub.getUser(ctx).isConsole());
|
||||||
|
sub.setDefaultExecutor((ctx) -> {
|
||||||
|
plugin.log(Level.INFO, "Please choose a migrator, then run \"husksync migrate <migrator>\"");
|
||||||
|
plugin.log(Level.INFO, String.format(
|
||||||
|
"List of available migrators:\nMigrator ID / Migrator Name:\n%s",
|
||||||
|
plugin.getAvailableMigrators().stream()
|
||||||
|
.map(migrator -> String.format("%s - %s", migrator.getIdentifier(), migrator.getName()))
|
||||||
|
.collect(Collectors.joining("\n"))
|
||||||
|
));
|
||||||
|
});
|
||||||
|
sub.addSubCommand("start", (start) -> start.addSyntax((cmd) -> {
|
||||||
|
final Migrator migrator = cmd.getArgument("migrator", Migrator.class);
|
||||||
|
migrator.start().thenAccept(succeeded -> {
|
||||||
if (succeeded) {
|
if (succeeded) {
|
||||||
plugin.log(Level.INFO, "Migration completed successfully!");
|
plugin.log(Level.INFO, "Migration completed successfully!");
|
||||||
} else {
|
} else {
|
||||||
plugin.log(Level.WARNING, "Migration failed!");
|
plugin.log(Level.WARNING, "Migration failed!");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
case "set" -> migrator.handleConfigurationCommand(Arrays.copyOfRange(args, 3, args.length));
|
}, migrator()));
|
||||||
default -> plugin.log(Level.INFO, String.format(
|
sub.addSubCommand("set", (set) -> set.addSyntax((cmd) -> {
|
||||||
"Invalid syntax. Console usage: \"husksync migrate %s <start/set>", args[1]
|
final Migrator migrator = cmd.getArgument("migrator", Migrator.class);
|
||||||
));
|
final String[] args = cmd.getArgument("args", String.class).split(" ");
|
||||||
}
|
migrator.handleConfigurationCommand(Arrays.copyOfRange(args, 3, args.length));
|
||||||
}, () -> {
|
}, migrator(), BaseCommand.greedyString("args")));
|
||||||
plugin.log(Level.INFO,
|
|
||||||
"Please specify a valid migrator.\n" +
|
|
||||||
"If a migrator is not available, please verify that you meet the prerequisites to use it.");
|
|
||||||
this.logMigratorList();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log the list of available migrators
|
|
||||||
private void logMigratorList() {
|
|
||||||
plugin.log(Level.INFO, String.format(
|
|
||||||
"List of available migrators:\nMigrator ID / Migrator Name:\n%s",
|
|
||||||
plugin.getAvailableMigrators().stream()
|
|
||||||
.map(migrator -> String.format("%s - %s", migrator.getIdentifier(), migrator.getName()))
|
|
||||||
.collect(Collectors.joining("\n"))
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public List<String> suggest(@NotNull CommandUser user, @NotNull String[] args) {
|
|
||||||
return switch (args.length) {
|
|
||||||
case 0, 1 -> SUB_COMMANDS.keySet().stream().sorted().toList();
|
|
||||||
default -> null;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private <S> ArgumentElement<S, Migrator> migrator() {
|
||||||
|
return new ArgumentElement<>("migrator", reader -> {
|
||||||
|
final String id = reader.readString();
|
||||||
|
final Migrator migrator = plugin.getAvailableMigrators().stream()
|
||||||
|
.filter(m -> m.getIdentifier().equalsIgnoreCase(id)).findFirst().orElse(null);
|
||||||
|
if (migrator == null) {
|
||||||
|
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(reader);
|
||||||
|
}
|
||||||
|
return migrator;
|
||||||
|
}, (context, builder) -> {
|
||||||
|
for (Migrator material : plugin.getAvailableMigrators()) {
|
||||||
|
builder.suggest(material.getIdentifier());
|
||||||
|
}
|
||||||
|
return builder.buildFuture();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private enum StatusLine {
|
private enum StatusLine {
|
||||||
PLUGIN_VERSION(plugin -> Component.text("v" + plugin.getPluginVersion().toStringWithoutMetadata())
|
PLUGIN_VERSION(plugin -> Component.text("v" + plugin.getPluginVersion().toStringWithoutMetadata())
|
||||||
.appendSpace().append(plugin.getPluginVersion().getMetadata().isBlank() ? Component.empty()
|
.appendSpace().append(plugin.getPluginVersion().getMetadata().isBlank() ? Component.empty()
|
||||||
: Component.text("(build " + plugin.getPluginVersion().getMetadata() + ")"))),
|
: Component.text("(build " + plugin.getPluginVersion().getMetadata() + ")"))),
|
||||||
PLATFORM_TYPE(plugin -> Component.text(WordUtils.capitalizeFully(plugin.getPlatformType()))),
|
PLATFORM_TYPE(plugin -> Component.text(WordUtils.capitalizeFully(plugin.getPlatformType()))),
|
||||||
LANGUAGE(plugin -> Component.text(plugin.getSettings().getLanguage())),
|
LANGUAGE(plugin -> Component.text(plugin.getSettings().getLanguage())),
|
||||||
MINECRAFT_VERSION(plugin -> Component.text(plugin.getMinecraftVersion().toString())),
|
MINECRAFT_VERSION(plugin -> Component.text(plugin.getMinecraftVersion().toString())),
|
||||||
JAVA_VERSION(plugin -> Component.text(System.getProperty("java.version"))),
|
JAVA_VERSION(plugin -> Component.text(System.getProperty("java.version"))),
|
||||||
JAVA_VENDOR(plugin -> Component.text(System.getProperty("java.vendor"))),
|
JAVA_VENDOR(plugin -> Component.text(System.getProperty("java.vendor"))),
|
||||||
SYNC_MODE(plugin -> Component.text(WordUtils.capitalizeFully(
|
SYNC_MODE(plugin -> Component.text(WordUtils.capitalizeFully(
|
||||||
plugin.getSettings().getSynchronization().getMode().toString()
|
plugin.getSettings().getSynchronization().getMode().toString()
|
||||||
))),
|
))),
|
||||||
DELAY_LATENCY(plugin -> Component.text(
|
DELAY_LATENCY(plugin -> Component.text(
|
||||||
plugin.getSettings().getSynchronization().getNetworkLatencyMilliseconds() + "ms"
|
plugin.getSettings().getSynchronization().getNetworkLatencyMilliseconds() + "ms"
|
||||||
)),
|
)),
|
||||||
SERVER_NAME(plugin -> Component.text(plugin.getServerName())),
|
SERVER_NAME(plugin -> Component.text(plugin.getServerName())),
|
||||||
CLUSTER_ID(plugin -> Component.text(plugin.getSettings().getClusterId().isBlank() ? "None" : plugin.getSettings().getClusterId())),
|
CLUSTER_ID(plugin -> Component.text(plugin.getSettings().getClusterId().isBlank() ? "None" : plugin.getSettings().getClusterId())),
|
||||||
DATABASE_TYPE(plugin ->
|
DATABASE_TYPE(plugin ->
|
||||||
Component.text(plugin.getSettings().getDatabase().getType().getDisplayName() +
|
Component.text(plugin.getSettings().getDatabase().getType().getDisplayName() +
|
||||||
(plugin.getSettings().getDatabase().getType() == Database.Type.MONGO ?
|
(plugin.getSettings().getDatabase().getType() == Database.Type.MONGO ?
|
||||||
(plugin.getSettings().getDatabase().getMongoSettings().isUsingAtlas() ? " Atlas" : "") : ""))
|
(plugin.getSettings().getDatabase().getMongoSettings().isUsingAtlas() ? " Atlas" : "") : ""))
|
||||||
),
|
),
|
||||||
IS_DATABASE_LOCAL(plugin -> getLocalhostBoolean(plugin.getSettings().getDatabase().getCredentials().getHost())),
|
IS_DATABASE_LOCAL(plugin -> getLocalhostBoolean(plugin.getSettings().getDatabase().getCredentials().getHost())),
|
||||||
USING_REDIS_SENTINEL(plugin -> getBoolean(
|
USING_REDIS_SENTINEL(plugin -> getBoolean(
|
||||||
!plugin.getSettings().getRedis().getSentinel().getMaster().isBlank()
|
!plugin.getSettings().getRedis().getSentinel().getMaster().isBlank()
|
||||||
)),
|
)),
|
||||||
USING_REDIS_PASSWORD(plugin -> getBoolean(
|
USING_REDIS_PASSWORD(plugin -> getBoolean(
|
||||||
!plugin.getSettings().getRedis().getCredentials().getPassword().isBlank()
|
!plugin.getSettings().getRedis().getCredentials().getPassword().isBlank()
|
||||||
)),
|
)),
|
||||||
REDIS_USING_SSL(plugin -> getBoolean(
|
REDIS_USING_SSL(plugin -> getBoolean(
|
||||||
plugin.getSettings().getRedis().getCredentials().isUseSsl()
|
plugin.getSettings().getRedis().getCredentials().isUseSsl()
|
||||||
)),
|
)),
|
||||||
IS_REDIS_LOCAL(plugin -> getLocalhostBoolean(
|
IS_REDIS_LOCAL(plugin -> getLocalhostBoolean(
|
||||||
plugin.getSettings().getRedis().getCredentials().getHost()
|
plugin.getSettings().getRedis().getCredentials().getHost()
|
||||||
)),
|
)),
|
||||||
DATA_TYPES(plugin -> Component.join(
|
DATA_TYPES(plugin -> Component.join(
|
||||||
JoinConfiguration.commas(true),
|
JoinConfiguration.commas(true),
|
||||||
plugin.getRegisteredDataTypes().stream().map(i -> Component.textOfChildren(Component.text(i.toString())
|
plugin.getRegisteredDataTypes().stream().map(i -> Component.textOfChildren(Component.text(i.toString())
|
||||||
.appendSpace().append(Component.text(i.isEnabled() ? '✔' : '❌')))
|
.appendSpace().append(Component.text(i.isEnabled() ? '✔' : '❌')))
|
||||||
.color(i.isEnabled() ? NamedTextColor.GREEN : NamedTextColor.RED)
|
.color(i.isEnabled() ? NamedTextColor.GREEN : NamedTextColor.RED)
|
||||||
.hoverEvent(HoverEvent.showText(
|
.hoverEvent(HoverEvent.showText(
|
||||||
Component.text(i.isEnabled() ? "Enabled" : "Disabled")
|
Component.text(i.isEnabled() ? "Enabled" : "Disabled")
|
||||||
.append(Component.newline())
|
.append(Component.newline())
|
||||||
.append(Component.text("Dependencies: %s".formatted(i.getDependencies()
|
.append(Component.text("Dependencies: %s".formatted(i.getDependencies()
|
||||||
.isEmpty() ? "(None)" : i.getDependencies().stream()
|
.isEmpty() ? "(None)" : i.getDependencies().stream()
|
||||||
.map(d -> "%s (%s)".formatted(
|
.map(d -> "%s (%s)".formatted(
|
||||||
d.getKey().value(), d.isRequired() ? "Required" : "Optional"
|
d.getKey().value(), d.isRequired() ? "Required" : "Optional"
|
||||||
)).collect(Collectors.joining(", ")))
|
)).collect(Collectors.joining(", ")))
|
||||||
).color(NamedTextColor.GRAY))
|
).color(NamedTextColor.GRAY))
|
||||||
))).toList()
|
))).toList()
|
||||||
));
|
));
|
||||||
|
|
||||||
private final Function<HuskSync, Component> supplier;
|
private final Function<HuskSync, Component> supplier;
|
||||||
@@ -265,13 +257,13 @@ public class HuskSyncCommand extends Command implements TabProvider {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private Component get(@NotNull HuskSync plugin) {
|
private Component get(@NotNull HuskSync plugin) {
|
||||||
return Component
|
return Component
|
||||||
.text("•").appendSpace()
|
.text("•").appendSpace()
|
||||||
.append(Component.text(
|
.append(Component.text(
|
||||||
WordUtils.capitalizeFully(name().replaceAll("_", " ")),
|
WordUtils.capitalizeFully(name().replaceAll("_", " ")),
|
||||||
TextColor.color(0x848484)
|
TextColor.color(0x848484)
|
||||||
))
|
))
|
||||||
.append(Component.text(':')).append(Component.space().color(NamedTextColor.WHITE))
|
.append(Component.text(':')).append(Component.space().color(NamedTextColor.WHITE))
|
||||||
.append(supplier.apply(plugin));
|
.append(supplier.apply(plugin));
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -282,7 +274,7 @@ public class HuskSyncCommand extends Command implements TabProvider {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private static Component getLocalhostBoolean(@NotNull String value) {
|
private static Component getLocalhostBoolean(@NotNull String value) {
|
||||||
return getBoolean(value.equals("127.0.0.1") || value.equals("0.0.0.0")
|
return getBoolean(value.equals("127.0.0.1") || value.equals("0.0.0.0")
|
||||||
|| value.equals("localhost") || value.equals("::1"));
|
|| value.equals("localhost") || value.equals("::1"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import java.util.Optional;
|
|||||||
public class InventoryCommand extends ItemsCommand {
|
public class InventoryCommand extends ItemsCommand {
|
||||||
|
|
||||||
public InventoryCommand(@NotNull HuskSync plugin) {
|
public InventoryCommand(@NotNull HuskSync plugin) {
|
||||||
super(plugin, List.of("inventory", "invsee", "openinv"));
|
super("inventory", List.of("invsee", "openinv"), plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -47,29 +47,29 @@ public class InventoryCommand extends ItemsCommand {
|
|||||||
if (optionalInventory.isEmpty()) {
|
if (optionalInventory.isEmpty()) {
|
||||||
viewer.sendMessage(new MineDown("what the FUCK is happening"));
|
viewer.sendMessage(new MineDown("what the FUCK is happening"));
|
||||||
plugin.getLocales().getLocale("error_no_data_to_display")
|
plugin.getLocales().getLocale("error_no_data_to_display")
|
||||||
.ifPresent(viewer::sendMessage);
|
.ifPresent(viewer::sendMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display opening message
|
// Display opening message
|
||||||
plugin.getLocales().getLocale("inventory_viewer_opened", user.getUsername(),
|
plugin.getLocales().getLocale("inventory_viewer_opened", user.getUsername(),
|
||||||
snapshot.getTimestamp().format(DateTimeFormatter
|
snapshot.getTimestamp().format(DateTimeFormatter
|
||||||
.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT)))
|
.ofLocalizedDateTime(FormatStyle.MEDIUM, FormatStyle.SHORT)))
|
||||||
.ifPresent(viewer::sendMessage);
|
.ifPresent(viewer::sendMessage);
|
||||||
|
|
||||||
// Show GUI
|
// Show GUI
|
||||||
final Data.Items.Inventory inventory = optionalInventory.get();
|
final Data.Items.Inventory inventory = optionalInventory.get();
|
||||||
viewer.showGui(
|
viewer.showGui(
|
||||||
inventory,
|
inventory,
|
||||||
plugin.getLocales().getLocale("inventory_viewer_menu_title", user.getUsername())
|
plugin.getLocales().getLocale("inventory_viewer_menu_title", user.getUsername())
|
||||||
.orElse(new MineDown(String.format("%s's Inventory", user.getUsername()))),
|
.orElse(new MineDown(String.format("%s's Inventory", user.getUsername()))),
|
||||||
allowEdit,
|
allowEdit,
|
||||||
inventory.getSlotCount(),
|
inventory.getSlotCount(),
|
||||||
(itemsOnClose) -> {
|
(itemsOnClose) -> {
|
||||||
if (allowEdit && !inventory.equals(itemsOnClose)) {
|
if (allowEdit && !inventory.equals(itemsOnClose)) {
|
||||||
plugin.runAsync(() -> this.updateItems(viewer, itemsOnClose, user));
|
plugin.runAsync(() -> this.updateItems(viewer, itemsOnClose, user));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ public class InventoryCommand extends ItemsCommand {
|
|||||||
final Optional<DataSnapshot.Packed> latestData = plugin.getDatabase().getLatestSnapshot(holder);
|
final Optional<DataSnapshot.Packed> latestData = plugin.getDatabase().getLatestSnapshot(holder);
|
||||||
if (latestData.isEmpty()) {
|
if (latestData.isEmpty()) {
|
||||||
plugin.getLocales().getLocale("error_no_data_to_display")
|
plugin.getLocales().getLocale("error_no_data_to_display")
|
||||||
.ifPresent(viewer::sendMessage);
|
.ifPresent(viewer::sendMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ public class InventoryCommand extends ItemsCommand {
|
|||||||
data.getInventory().ifPresent(inventory -> inventory.setContents(items));
|
data.getInventory().ifPresent(inventory -> inventory.setContents(items));
|
||||||
data.setSaveCause(DataSnapshot.SaveCause.INVENTORY_COMMAND);
|
data.setSaveCause(DataSnapshot.SaveCause.INVENTORY_COMMAND);
|
||||||
data.setPinned(
|
data.setPinned(
|
||||||
plugin.getSettings().getSynchronization().doAutoPin(DataSnapshot.SaveCause.INVENTORY_COMMAND)
|
plugin.getSettings().getSynchronization().doAutoPin(DataSnapshot.SaveCause.INVENTORY_COMMAND)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -24,102 +24,90 @@ import net.william278.husksync.data.DataSnapshot;
|
|||||||
import net.william278.husksync.user.CommandUser;
|
import net.william278.husksync.user.CommandUser;
|
||||||
import net.william278.husksync.user.OnlineUser;
|
import net.william278.husksync.user.OnlineUser;
|
||||||
import net.william278.husksync.user.User;
|
import net.william278.husksync.user.User;
|
||||||
|
import net.william278.uniform.BaseCommand;
|
||||||
|
import net.william278.uniform.Permission;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public abstract class ItemsCommand extends Command implements TabProvider {
|
public abstract class ItemsCommand extends PluginCommand {
|
||||||
|
|
||||||
protected ItemsCommand(@NotNull HuskSync plugin, @NotNull List<String> aliases) {
|
protected ItemsCommand(@NotNull String name, @NotNull List<String> aliases, @NotNull HuskSync plugin) {
|
||||||
super(aliases.get(0), aliases.subList(1, aliases.size()), "<player> [version_uuid]", plugin);
|
super(name, aliases, Permission.Default.IF_OP, plugin);
|
||||||
setOperatorCommand(true);
|
|
||||||
addAdditionalPermissions(Map.of("edit", true));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(@NotNull CommandUser executor, @NotNull String[] args) {
|
public void provide(@NotNull BaseCommand<?> command) {
|
||||||
if (!(executor instanceof OnlineUser player)) {
|
command.addSyntax((ctx) -> {
|
||||||
plugin.getLocales().getLocale("error_in_game_command_only")
|
final User user = ctx.getArgument("username", User.class);
|
||||||
|
final UUID version = ctx.getArgument("version", UUID.class);
|
||||||
|
final CommandUser executor = user(command, ctx);
|
||||||
|
if (!(executor instanceof OnlineUser online)) {
|
||||||
|
plugin.getLocales().getLocale("error_in_game_command_only")
|
||||||
.ifPresent(executor::sendMessage);
|
.ifPresent(executor::sendMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.showSnapshotItems(online, user, version);
|
||||||
// Find the user to view the items for
|
}, user("username"), uuid("version"));
|
||||||
final Optional<User> optionalUser = parseStringArg(args, 0)
|
command.addSyntax((ctx) -> {
|
||||||
.flatMap(name -> plugin.getDatabase().getUserByName(name));
|
final User user = ctx.getArgument("username", User.class);
|
||||||
if (optionalUser.isEmpty()) {
|
final CommandUser executor = user(command, ctx);
|
||||||
plugin.getLocales().getLocale(
|
if (!(executor instanceof OnlineUser online)) {
|
||||||
args.length >= 1 ? "error_invalid_player" : "error_invalid_syntax", getUsage()
|
plugin.getLocales().getLocale("error_in_game_command_only")
|
||||||
).ifPresent(player::sendMessage);
|
.ifPresent(executor::sendMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.showLatestItems(online, user);
|
||||||
// Show the user data
|
}, user("username"));
|
||||||
final User user = optionalUser.get();
|
|
||||||
parseUUIDArg(args, 1).ifPresentOrElse(
|
|
||||||
version -> this.showSnapshotItems(player, user, version),
|
|
||||||
() -> this.showLatestItems(player, user)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// View (and edit) the latest user data
|
// View (and edit) the latest user data
|
||||||
private void showLatestItems(@NotNull OnlineUser viewer, @NotNull User user) {
|
private void showLatestItems(@NotNull OnlineUser viewer, @NotNull User user) {
|
||||||
plugin.getRedisManager().getUserData(user.getUuid(), user).thenAccept(data -> data
|
plugin.getRedisManager().getUserData(user.getUuid(), user).thenAccept(data -> data
|
||||||
.or(() -> plugin.getDatabase().getLatestSnapshot(user))
|
.or(() -> plugin.getDatabase().getLatestSnapshot(user))
|
||||||
.or(() -> {
|
.or(() -> {
|
||||||
plugin.getLocales().getLocale("error_no_data_to_display")
|
plugin.getLocales().getLocale("error_no_data_to_display")
|
||||||
.ifPresent(viewer::sendMessage);
|
.ifPresent(viewer::sendMessage);
|
||||||
|
return Optional.empty();
|
||||||
|
})
|
||||||
|
.flatMap(packed -> {
|
||||||
|
if (packed.isInvalid()) {
|
||||||
|
plugin.getLocales().getLocale("error_invalid_data", packed.getInvalidReason(plugin))
|
||||||
|
.ifPresent(viewer::sendMessage);
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
})
|
}
|
||||||
.flatMap(packed -> {
|
return Optional.of(packed.unpack(plugin));
|
||||||
if (packed.isInvalid()) {
|
})
|
||||||
plugin.getLocales().getLocale("error_invalid_data", packed.getInvalidReason(plugin))
|
.ifPresent(snapshot -> this.showItems(
|
||||||
.ifPresent(viewer::sendMessage);
|
viewer, snapshot, user, viewer.hasPermission(getPermission("edit"))
|
||||||
return Optional.empty();
|
)));
|
||||||
}
|
|
||||||
return Optional.of(packed.unpack(plugin));
|
|
||||||
})
|
|
||||||
.ifPresent(snapshot -> this.showItems(
|
|
||||||
viewer, snapshot, user, viewer.hasPermission(getPermission("edit"))
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// View a specific version of the user data
|
// View a specific version of the user data
|
||||||
private void showSnapshotItems(@NotNull OnlineUser viewer, @NotNull User user, @NotNull UUID version) {
|
private void showSnapshotItems(@NotNull OnlineUser viewer, @NotNull User user, @NotNull UUID version) {
|
||||||
plugin.getDatabase().getSnapshot(user, version)
|
plugin.getDatabase().getSnapshot(user, version)
|
||||||
.or(() -> {
|
.or(() -> {
|
||||||
plugin.getLocales().getLocale("error_invalid_version_uuid")
|
plugin.getLocales().getLocale("error_invalid_version_uuid")
|
||||||
.ifPresent(viewer::sendMessage);
|
.ifPresent(viewer::sendMessage);
|
||||||
|
return Optional.empty();
|
||||||
|
})
|
||||||
|
.flatMap(packed -> {
|
||||||
|
if (packed.isInvalid()) {
|
||||||
|
plugin.getLocales().getLocale("error_invalid_data", packed.getInvalidReason(plugin))
|
||||||
|
.ifPresent(viewer::sendMessage);
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
})
|
}
|
||||||
.flatMap(packed -> {
|
return Optional.of(packed.unpack(plugin));
|
||||||
if (packed.isInvalid()) {
|
})
|
||||||
plugin.getLocales().getLocale("error_invalid_data", packed.getInvalidReason(plugin))
|
.ifPresent(snapshot -> this.showItems(
|
||||||
.ifPresent(viewer::sendMessage);
|
viewer, snapshot, user, false
|
||||||
return Optional.empty();
|
));
|
||||||
}
|
|
||||||
return Optional.of(packed.unpack(plugin));
|
|
||||||
})
|
|
||||||
.ifPresent(snapshot -> this.showItems(
|
|
||||||
viewer, snapshot, user, false
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show a GUI menu with the correct item data from the snapshot
|
// Show a GUI menu with the correct item data from the snapshot
|
||||||
protected abstract void showItems(@NotNull OnlineUser viewer, @NotNull DataSnapshot.Unpacked snapshot,
|
protected abstract void showItems(@NotNull OnlineUser viewer, @NotNull DataSnapshot.Unpacked snapshot,
|
||||||
@NotNull User user, boolean allowEdit);
|
@NotNull User user, boolean allowEdit);
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public List<String> suggest(@NotNull CommandUser executor, @NotNull String[] args) {
|
|
||||||
return switch (args.length) {
|
|
||||||
case 0, 1 -> plugin.getOnlineUsers().stream().map(User::getUsername).toList();
|
|
||||||
default -> null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,105 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of HuskSync, licensed under the Apache License 2.0.
|
|
||||||
*
|
|
||||||
* Copyright (c) William278 <will27528@gmail.com>
|
|
||||||
* Copyright (c) contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.william278.husksync.command;
|
|
||||||
|
|
||||||
import net.william278.husksync.HuskSync;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.StringJoiner;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public abstract class Node implements Executable {
|
|
||||||
|
|
||||||
protected static final String PERMISSION_PREFIX = "husksync.command";
|
|
||||||
|
|
||||||
protected final HuskSync plugin;
|
|
||||||
private final String name;
|
|
||||||
private final List<String> aliases;
|
|
||||||
private boolean operatorCommand = false;
|
|
||||||
|
|
||||||
protected Node(@NotNull String name, @NotNull List<String> aliases, @NotNull HuskSync plugin) {
|
|
||||||
if (name.isBlank()) {
|
|
||||||
throw new IllegalArgumentException("Command name cannot be blank");
|
|
||||||
}
|
|
||||||
this.name = name;
|
|
||||||
this.aliases = aliases;
|
|
||||||
this.plugin = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public List<String> getAliases() {
|
|
||||||
return aliases;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public String getPermission(@NotNull String... child) {
|
|
||||||
final StringJoiner joiner = new StringJoiner(".")
|
|
||||||
.add(PERMISSION_PREFIX)
|
|
||||||
.add(getName());
|
|
||||||
for (final String node : child) {
|
|
||||||
joiner.add(node);
|
|
||||||
}
|
|
||||||
return joiner.toString().trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isOperatorCommand() {
|
|
||||||
return operatorCommand;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOperatorCommand(boolean operatorCommand) {
|
|
||||||
this.operatorCommand = operatorCommand;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Optional<String> parseStringArg(@NotNull String[] args, int index) {
|
|
||||||
if (args.length > index) {
|
|
||||||
return Optional.of(args[index]);
|
|
||||||
}
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Optional<Integer> parseIntArg(@NotNull String[] args, int index) {
|
|
||||||
return parseStringArg(args, index).flatMap(arg -> {
|
|
||||||
try {
|
|
||||||
return Optional.of(Integer.parseInt(arg));
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Optional<UUID> parseUUIDArg(@NotNull String[] args, int index) {
|
|
||||||
return parseStringArg(args, index).flatMap(arg -> {
|
|
||||||
try {
|
|
||||||
return Optional.of(UUID.fromString(arg));
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of HuskSync, licensed under the Apache License 2.0.
|
||||||
|
*
|
||||||
|
* Copyright (c) William278 <will27528@gmail.com>
|
||||||
|
* Copyright (c) contributors
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.william278.husksync.command;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
|
import net.william278.husksync.HuskSync;
|
||||||
|
import net.william278.husksync.user.CommandUser;
|
||||||
|
import net.william278.husksync.user.User;
|
||||||
|
import net.william278.uniform.BaseCommand;
|
||||||
|
import net.william278.uniform.Command;
|
||||||
|
import net.william278.uniform.Permission;
|
||||||
|
import net.william278.uniform.element.ArgumentElement;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public abstract class PluginCommand extends Command {
|
||||||
|
|
||||||
|
protected final HuskSync plugin;
|
||||||
|
|
||||||
|
protected PluginCommand(@NotNull String name, @NotNull List<String> aliases,
|
||||||
|
@NotNull Permission.Default permissionDefault, @NotNull HuskSync plugin) {
|
||||||
|
super(name, aliases, getDescription(plugin, name), new Permission(createPermission(name), permissionDefault));
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getDescription(@NotNull HuskSync plugin, @NotNull String name) {
|
||||||
|
return plugin.getLocales().getRawLocale("%s_command_description".formatted(name)).orElse("");
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static String createPermission(@NotNull String name, @NotNull String... sub) {
|
||||||
|
return "husksync.command." + name + (sub.length > 0 ? "." + String.join(".", sub) : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
protected String getPermission(@NotNull String... sub) {
|
||||||
|
return createPermission(this.getName(), sub);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
protected CommandUser user(@NotNull BaseCommand base, @NotNull CommandContext context) {
|
||||||
|
return adapt(base.getUser(context.getSource()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
protected Permission needsOp(@NotNull String... nodes) {
|
||||||
|
return new Permission(getPermission(nodes), Permission.Default.IF_OP);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
protected CommandUser adapt(net.william278.uniform.CommandUser user) {
|
||||||
|
return user.getUuid() == null ? plugin.getConsole() : plugin.getOnlineUser(user.getUuid()).orElseThrow();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
protected <S> ArgumentElement<S, User> user(@NotNull String name) {
|
||||||
|
return new ArgumentElement<>(name, reader -> {
|
||||||
|
final String username = reader.readString();
|
||||||
|
return plugin.getDatabase().getUserByName(username).orElseThrow(
|
||||||
|
() -> CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(reader)
|
||||||
|
);
|
||||||
|
}, (context, builder) -> {
|
||||||
|
plugin.getOnlineUsers().forEach(u -> builder.suggest(u.getUsername()));
|
||||||
|
return builder.buildFuture();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
protected <S> ArgumentElement<S, UUID> uuid(@NotNull String name) {
|
||||||
|
return new ArgumentElement<>(name, reader -> {
|
||||||
|
try {
|
||||||
|
return UUID.fromString(reader.readString());
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(reader);
|
||||||
|
}
|
||||||
|
}, (context, builder) -> builder.buildFuture());
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Type {
|
||||||
|
|
||||||
|
HUSKSYNC_COMMAND(HuskSyncCommand::new),
|
||||||
|
USERDATA_COMMAND(UserDataCommand::new),
|
||||||
|
INVENTORY_COMMAND(InventoryCommand::new),
|
||||||
|
ENDER_CHEST_COMMAND(EnderChestCommand::new);
|
||||||
|
|
||||||
|
public final Function<HuskSync, PluginCommand> commandSupplier;
|
||||||
|
|
||||||
|
Type(@NotNull Function<HuskSync, PluginCommand> supplier) {
|
||||||
|
this.commandSupplier = supplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public PluginCommand supply(@NotNull HuskSync plugin) {
|
||||||
|
return commandSupplier.apply(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static PluginCommand[] create(@NotNull HuskSync plugin) {
|
||||||
|
return Arrays.stream(values()).map(type -> type.supply(plugin)).toArray(PluginCommand[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of HuskSync, licensed under the Apache License 2.0.
|
|
||||||
*
|
|
||||||
* Copyright (c) William278 <will27528@gmail.com>
|
|
||||||
* Copyright (c) contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.william278.husksync.command;
|
|
||||||
|
|
||||||
import net.william278.husksync.user.CommandUser;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface TabProvider {
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
List<String> suggest(@NotNull CommandUser user, @NotNull String[] args);
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
default List<String> getSuggestions(@NotNull CommandUser user, @NotNull String[] args) {
|
|
||||||
List<String> suggestions = suggest(user, args);
|
|
||||||
if (suggestions == null) {
|
|
||||||
suggestions = List.of();
|
|
||||||
}
|
|
||||||
return filter(suggestions, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
default List<String> filter(@NotNull List<String> suggestions, @NotNull String[] args) {
|
|
||||||
return suggestions.stream()
|
|
||||||
.filter(suggestion -> args.length == 0 || suggestion.toLowerCase()
|
|
||||||
.startsWith(args[args.length - 1].toLowerCase().trim()))
|
|
||||||
.toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
package net.william278.husksync.command;
|
package net.william278.husksync.command;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import net.william278.husksync.HuskSync;
|
import net.william278.husksync.HuskSync;
|
||||||
import net.william278.husksync.data.DataSnapshot;
|
import net.william278.husksync.data.DataSnapshot;
|
||||||
import net.william278.husksync.redis.RedisKeyType;
|
import net.william278.husksync.redis.RedisKeyType;
|
||||||
@@ -28,116 +29,65 @@ import net.william278.husksync.user.User;
|
|||||||
import net.william278.husksync.util.DataDumper;
|
import net.william278.husksync.util.DataDumper;
|
||||||
import net.william278.husksync.util.DataSnapshotList;
|
import net.william278.husksync.util.DataSnapshotList;
|
||||||
import net.william278.husksync.util.DataSnapshotOverview;
|
import net.william278.husksync.util.DataSnapshotOverview;
|
||||||
|
import net.william278.uniform.BaseCommand;
|
||||||
|
import net.william278.uniform.CommandProvider;
|
||||||
|
import net.william278.uniform.Permission;
|
||||||
|
import net.william278.uniform.element.ArgumentElement;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
public class UserDataCommand extends Command implements TabProvider {
|
public class UserDataCommand extends PluginCommand {
|
||||||
|
|
||||||
private static final Map<String, Boolean> SUB_COMMANDS = Map.of(
|
|
||||||
"view", false,
|
|
||||||
"list", false,
|
|
||||||
"delete", true,
|
|
||||||
"restore", true,
|
|
||||||
"pin", true,
|
|
||||||
"dump", true
|
|
||||||
);
|
|
||||||
|
|
||||||
public UserDataCommand(@NotNull HuskSync plugin) {
|
public UserDataCommand(@NotNull HuskSync plugin) {
|
||||||
super("userdata", List.of("playerdata"), String.format(
|
super("userdata", List.of("playerdata"), Permission.Default.IF_OP, plugin);
|
||||||
"<%s> [username] [version_uuid]", String.join("/", SUB_COMMANDS.keySet())
|
|
||||||
), plugin);
|
|
||||||
setOperatorCommand(true);
|
|
||||||
addAdditionalPermissions(SUB_COMMANDS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(@NotNull CommandUser executor, @NotNull String[] args) {
|
public void provide(@NotNull BaseCommand<?> command) {
|
||||||
final String subCommand = parseStringArg(args, 0).orElse("view").toLowerCase(Locale.ENGLISH);
|
command.addSubCommand("view", needsOp("view"), view());
|
||||||
final Optional<User> optionalUser = parseStringArg(args, 1)
|
command.addSubCommand("list", needsOp("list"), list());
|
||||||
.flatMap(name -> plugin.getDatabase().getUserByName(name))
|
command.addSubCommand("delete", needsOp("delete"), delete());
|
||||||
.or(() -> parseStringArg(args, 0).flatMap(name -> plugin.getDatabase().getUserByName(name)))
|
command.addSubCommand("restore", needsOp("restore"), restore());
|
||||||
.or(() -> args.length < 2 && executor instanceof User userExecutor
|
command.addSubCommand("pin", needsOp("pin"), pin());
|
||||||
? Optional.of(userExecutor) : Optional.empty());
|
command.addSubCommand("dump", needsOp("dump"), dump());
|
||||||
final Optional<UUID> uuid = parseUUIDArg(args, 2).or(() -> parseUUIDArg(args, 1));
|
|
||||||
if (optionalUser.isEmpty()) {
|
|
||||||
plugin.getLocales().getLocale("error_invalid_player")
|
|
||||||
.ifPresent(executor::sendMessage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final User user = optionalUser.get();
|
|
||||||
switch (subCommand) {
|
|
||||||
case "view" -> uuid.ifPresentOrElse(
|
|
||||||
version -> viewSnapshot(executor, user, version),
|
|
||||||
() -> viewLatestSnapshot(executor, user)
|
|
||||||
);
|
|
||||||
case "list" -> listSnapshots(
|
|
||||||
executor, user, parseIntArg(args, 2).or(() -> parseIntArg(args, 1)).orElse(1)
|
|
||||||
);
|
|
||||||
case "delete" -> uuid.ifPresentOrElse(
|
|
||||||
version -> deleteSnapshot(executor, user, version),
|
|
||||||
() -> plugin.getLocales().getLocale("error_invalid_syntax",
|
|
||||||
"/userdata delete <username> <version_uuid>")
|
|
||||||
.ifPresent(executor::sendMessage)
|
|
||||||
);
|
|
||||||
case "restore" -> uuid.ifPresentOrElse(
|
|
||||||
version -> restoreSnapshot(executor, user, version),
|
|
||||||
() -> plugin.getLocales().getLocale("error_invalid_syntax",
|
|
||||||
"/userdata restore <username> <version_uuid>")
|
|
||||||
.ifPresent(executor::sendMessage)
|
|
||||||
);
|
|
||||||
case "pin" -> uuid.ifPresentOrElse(
|
|
||||||
version -> pinSnapshot(executor, user, version),
|
|
||||||
() -> plugin.getLocales().getLocale("error_invalid_syntax",
|
|
||||||
"/userdata pin <username> <version_uuid>")
|
|
||||||
.ifPresent(executor::sendMessage)
|
|
||||||
);
|
|
||||||
case "dump" -> uuid.ifPresentOrElse(
|
|
||||||
version -> dumpSnapshot(executor, user, version, parseStringArg(args, 3)
|
|
||||||
.map(arg -> arg.equalsIgnoreCase("web")).orElse(false)),
|
|
||||||
() -> plugin.getLocales().getLocale("error_invalid_syntax",
|
|
||||||
"/userdata dump <username> <version_uuid> <web/file>")
|
|
||||||
.ifPresent(executor::sendMessage)
|
|
||||||
);
|
|
||||||
default -> plugin.getLocales().getLocale("error_invalid_syntax", getUsage())
|
|
||||||
.ifPresent(executor::sendMessage);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show the latest snapshot
|
// Show the latest snapshot
|
||||||
private void viewLatestSnapshot(@NotNull CommandUser executor, @NotNull User user) {
|
private void viewLatestSnapshot(@NotNull CommandUser executor, @NotNull User user) {
|
||||||
plugin.getDatabase().getLatestSnapshot(user).ifPresentOrElse(
|
plugin.getDatabase().getLatestSnapshot(user).ifPresentOrElse(
|
||||||
data -> {
|
data -> {
|
||||||
if (data.isInvalid()) {
|
if (data.isInvalid()) {
|
||||||
plugin.getLocales().getLocale("error_invalid_data", data.getInvalidReason(plugin))
|
plugin.getLocales().getLocale("error_invalid_data", data.getInvalidReason(plugin))
|
||||||
.ifPresent(executor::sendMessage);
|
.ifPresent(executor::sendMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DataSnapshotOverview.of(data.unpack(plugin), data.getFileSize(plugin), user, plugin)
|
DataSnapshotOverview.of(data.unpack(plugin), data.getFileSize(plugin), user, plugin)
|
||||||
.show(executor);
|
.show(executor);
|
||||||
},
|
},
|
||||||
() -> plugin.getLocales().getLocale("error_no_data_to_display")
|
() -> plugin.getLocales().getLocale("error_no_data_to_display")
|
||||||
.ifPresent(executor::sendMessage)
|
.ifPresent(executor::sendMessage)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show the specified snapshot
|
// Show the specified snapshot
|
||||||
private void viewSnapshot(@NotNull CommandUser executor, @NotNull User user, @NotNull UUID version) {
|
private void viewSnapshot(@NotNull CommandUser executor, @NotNull User user, @NotNull UUID version) {
|
||||||
plugin.getDatabase().getSnapshot(user, version).ifPresentOrElse(
|
plugin.getDatabase().getSnapshot(user, version).ifPresentOrElse(
|
||||||
data -> {
|
data -> {
|
||||||
if (data.isInvalid()) {
|
if (data.isInvalid()) {
|
||||||
plugin.getLocales().getLocale("error_invalid_data", data.getInvalidReason(plugin))
|
plugin.getLocales().getLocale("error_invalid_data", data.getInvalidReason(plugin))
|
||||||
.ifPresent(executor::sendMessage);
|
.ifPresent(executor::sendMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DataSnapshotOverview.of(data.unpack(plugin), data.getFileSize(plugin), user, plugin)
|
DataSnapshotOverview.of(data.unpack(plugin), data.getFileSize(plugin), user, plugin)
|
||||||
.show(executor);
|
.show(executor);
|
||||||
},
|
},
|
||||||
() -> plugin.getLocales().getLocale("error_invalid_version_uuid")
|
() -> plugin.getLocales().getLocale("error_invalid_version_uuid")
|
||||||
.ifPresent(executor::sendMessage)
|
.ifPresent(executor::sendMessage)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,7 +96,7 @@ public class UserDataCommand extends Command implements TabProvider {
|
|||||||
final List<DataSnapshot.Packed> dataList = plugin.getDatabase().getAllSnapshots(user);
|
final List<DataSnapshot.Packed> dataList = plugin.getDatabase().getAllSnapshots(user);
|
||||||
if (dataList.isEmpty()) {
|
if (dataList.isEmpty()) {
|
||||||
plugin.getLocales().getLocale("error_no_data_to_display")
|
plugin.getLocales().getLocale("error_no_data_to_display")
|
||||||
.ifPresent(executor::sendMessage);
|
.ifPresent(executor::sendMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DataSnapshotList.create(dataList, user, plugin).displayPage(executor, page);
|
DataSnapshotList.create(dataList, user, plugin).displayPage(executor, page);
|
||||||
@@ -156,16 +106,16 @@ public class UserDataCommand extends Command implements TabProvider {
|
|||||||
private void deleteSnapshot(@NotNull CommandUser executor, @NotNull User user, @NotNull UUID version) {
|
private void deleteSnapshot(@NotNull CommandUser executor, @NotNull User user, @NotNull UUID version) {
|
||||||
if (!plugin.getDatabase().deleteSnapshot(user, version)) {
|
if (!plugin.getDatabase().deleteSnapshot(user, version)) {
|
||||||
plugin.getLocales().getLocale("error_invalid_version_uuid")
|
plugin.getLocales().getLocale("error_invalid_version_uuid")
|
||||||
.ifPresent(executor::sendMessage);
|
.ifPresent(executor::sendMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
plugin.getRedisManager().clearUserData(user);
|
plugin.getRedisManager().clearUserData(user);
|
||||||
plugin.getLocales().getLocale("data_deleted",
|
plugin.getLocales().getLocale("data_deleted",
|
||||||
version.toString().split("-")[0],
|
version.toString().split("-")[0],
|
||||||
version.toString(),
|
version.toString(),
|
||||||
user.getUsername(),
|
user.getUsername(),
|
||||||
user.getUuid().toString())
|
user.getUuid().toString())
|
||||||
.ifPresent(executor::sendMessage);
|
.ifPresent(executor::sendMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore a snapshot
|
// Restore a snapshot
|
||||||
@@ -173,7 +123,7 @@ public class UserDataCommand extends Command implements TabProvider {
|
|||||||
final Optional<DataSnapshot.Packed> optionalData = plugin.getDatabase().getSnapshot(user, version);
|
final Optional<DataSnapshot.Packed> optionalData = plugin.getDatabase().getSnapshot(user, version);
|
||||||
if (optionalData.isEmpty()) {
|
if (optionalData.isEmpty()) {
|
||||||
plugin.getLocales().getLocale("error_invalid_version_uuid")
|
plugin.getLocales().getLocale("error_invalid_version_uuid")
|
||||||
.ifPresent(executor::sendMessage);
|
.ifPresent(executor::sendMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,14 +131,14 @@ public class UserDataCommand extends Command implements TabProvider {
|
|||||||
final DataSnapshot.Packed data = optionalData.get().copy();
|
final DataSnapshot.Packed data = optionalData.get().copy();
|
||||||
if (data.isInvalid()) {
|
if (data.isInvalid()) {
|
||||||
plugin.getLocales().getLocale("error_invalid_data", data.getInvalidReason(plugin))
|
plugin.getLocales().getLocale("error_invalid_data", data.getInvalidReason(plugin))
|
||||||
.ifPresent(executor::sendMessage);
|
.ifPresent(executor::sendMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
data.edit(plugin, (unpacked -> {
|
data.edit(plugin, (unpacked -> {
|
||||||
unpacked.getHealth().ifPresent(status -> status.setHealth(Math.max(1, status.getHealth())));
|
unpacked.getHealth().ifPresent(status -> status.setHealth(Math.max(1, status.getHealth())));
|
||||||
unpacked.setSaveCause(DataSnapshot.SaveCause.BACKUP_RESTORE);
|
unpacked.setSaveCause(DataSnapshot.SaveCause.BACKUP_RESTORE);
|
||||||
unpacked.setPinned(
|
unpacked.setPinned(
|
||||||
plugin.getSettings().getSynchronization().doAutoPin(DataSnapshot.SaveCause.BACKUP_RESTORE)
|
plugin.getSettings().getSynchronization().doAutoPin(DataSnapshot.SaveCause.BACKUP_RESTORE)
|
||||||
);
|
);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -198,7 +148,7 @@ public class UserDataCommand extends Command implements TabProvider {
|
|||||||
redis.getUserData(u).ifPresent(d -> redis.setUserData(u, s, RedisKeyType.TTL_1_YEAR));
|
redis.getUserData(u).ifPresent(d -> redis.setUserData(u, s, RedisKeyType.TTL_1_YEAR));
|
||||||
redis.sendUserDataUpdate(u, s);
|
redis.sendUserDataUpdate(u, s);
|
||||||
plugin.getLocales().getLocale("data_restored", u.getUsername(), u.getUuid().toString(),
|
plugin.getLocales().getLocale("data_restored", u.getUsername(), u.getUuid().toString(),
|
||||||
s.getShortId(), s.getId().toString()).ifPresent(executor::sendMessage);
|
s.getShortId(), s.getId().toString()).ifPresent(executor::sendMessage);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,7 +157,7 @@ public class UserDataCommand extends Command implements TabProvider {
|
|||||||
final Optional<DataSnapshot.Packed> optionalData = plugin.getDatabase().getSnapshot(user, version);
|
final Optional<DataSnapshot.Packed> optionalData = plugin.getDatabase().getSnapshot(user, version);
|
||||||
if (optionalData.isEmpty()) {
|
if (optionalData.isEmpty()) {
|
||||||
plugin.getLocales().getLocale("error_invalid_version_uuid")
|
plugin.getLocales().getLocale("error_invalid_version_uuid")
|
||||||
.ifPresent(executor::sendMessage);
|
.ifPresent(executor::sendMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,8 +169,8 @@ public class UserDataCommand extends Command implements TabProvider {
|
|||||||
plugin.getDatabase().pinSnapshot(user, data.getId());
|
plugin.getDatabase().pinSnapshot(user, data.getId());
|
||||||
}
|
}
|
||||||
plugin.getLocales().getLocale(data.isPinned() ? "data_unpinned" : "data_pinned", data.getShortId(),
|
plugin.getLocales().getLocale(data.isPinned() ? "data_unpinned" : "data_pinned", data.getShortId(),
|
||||||
data.getId().toString(), user.getUsername(), user.getUuid().toString())
|
data.getId().toString(), user.getUsername(), user.getUuid().toString())
|
||||||
.ifPresent(executor::sendMessage);
|
.ifPresent(executor::sendMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dump a snapshot
|
// Dump a snapshot
|
||||||
@@ -228,7 +178,7 @@ public class UserDataCommand extends Command implements TabProvider {
|
|||||||
final Optional<DataSnapshot.Packed> data = plugin.getDatabase().getSnapshot(user, version);
|
final Optional<DataSnapshot.Packed> data = plugin.getDatabase().getSnapshot(user, version);
|
||||||
if (data.isEmpty()) {
|
if (data.isEmpty()) {
|
||||||
plugin.getLocales().getLocale("error_invalid_version_uuid")
|
plugin.getLocales().getLocale("error_invalid_version_uuid")
|
||||||
.ifPresent(executor::sendMessage);
|
.ifPresent(executor::sendMessage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,22 +187,98 @@ public class UserDataCommand extends Command implements TabProvider {
|
|||||||
final DataDumper dumper = DataDumper.create(userData, user, plugin);
|
final DataDumper dumper = DataDumper.create(userData, user, plugin);
|
||||||
try {
|
try {
|
||||||
plugin.getLocales().getLocale("data_dumped", userData.getShortId(), user.getUsername(),
|
plugin.getLocales().getLocale("data_dumped", userData.getShortId(), user.getUsername(),
|
||||||
(webDump ? dumper.toWeb() : dumper.toFile())).ifPresent(executor::sendMessage);
|
(webDump ? dumper.toWeb() : dumper.toFile())).ifPresent(executor::sendMessage);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
plugin.log(Level.SEVERE, "Failed to dump user data", e);
|
plugin.log(Level.SEVERE, "Failed to dump user data", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@NotNull
|
||||||
@Override
|
private CommandProvider view() {
|
||||||
public List<String> suggest(@NotNull CommandUser executor, @NotNull String[] args) {
|
return (sub) -> {
|
||||||
return switch (args.length) {
|
sub.addSyntax((ctx) -> {
|
||||||
case 0, 1 -> SUB_COMMANDS.keySet().stream().sorted().toList();
|
final User user = ctx.getArgument("username", User.class);
|
||||||
case 2 -> plugin.getOnlineUsers().stream().map(User::getUsername).toList();
|
viewLatestSnapshot(user(sub, ctx), user);
|
||||||
case 4 -> parseStringArg(args, 0)
|
}, user("username"));
|
||||||
.map(arg -> arg.equalsIgnoreCase("dump") ? List.of("web", "file") : null)
|
sub.addSyntax((ctx) -> {
|
||||||
.orElse(null);
|
final User user = ctx.getArgument("username", User.class);
|
||||||
default -> null;
|
final UUID version = ctx.getArgument("version", UUID.class);
|
||||||
|
viewSnapshot(user(sub, ctx), user, version);
|
||||||
|
}, user("username"), uuid("version"));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private CommandProvider list() {
|
||||||
|
return (sub) -> {
|
||||||
|
sub.addSyntax((ctx) -> {
|
||||||
|
final User user = ctx.getArgument("username", User.class);
|
||||||
|
listSnapshots(user(sub, ctx), user, 1);
|
||||||
|
}, user("username"));
|
||||||
|
sub.addSyntax((ctx) -> {
|
||||||
|
final User user = ctx.getArgument("username", User.class);
|
||||||
|
final int page = ctx.getArgument("page", Integer.class);
|
||||||
|
listSnapshots(user(sub, ctx), user, page);
|
||||||
|
}, user("username"), BaseCommand.intNum("page", 1));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private CommandProvider delete() {
|
||||||
|
return (sub) -> sub.addSyntax((ctx) -> {
|
||||||
|
final User user = ctx.getArgument("username", User.class);
|
||||||
|
final UUID version = ctx.getArgument("version", UUID.class);
|
||||||
|
deleteSnapshot(user(sub, ctx), user, version);
|
||||||
|
}, user("username"), uuid("version"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private CommandProvider restore() {
|
||||||
|
return (sub) -> sub.addSyntax((ctx) -> {
|
||||||
|
final User user = ctx.getArgument("username", User.class);
|
||||||
|
final UUID version = ctx.getArgument("version", UUID.class);
|
||||||
|
restoreSnapshot(user(sub, ctx), user, version);
|
||||||
|
}, user("username"), uuid("version"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private CommandProvider pin() {
|
||||||
|
return (sub) -> sub.addSyntax((ctx) -> {
|
||||||
|
final User user = ctx.getArgument("username", User.class);
|
||||||
|
final UUID version = ctx.getArgument("version", UUID.class);
|
||||||
|
pinSnapshot(user(sub, ctx), user, version);
|
||||||
|
}, user("username"), uuid("version"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private CommandProvider dump() {
|
||||||
|
return (sub) -> sub.addSyntax((ctx) -> {
|
||||||
|
final User user = ctx.getArgument("username", User.class);
|
||||||
|
final UUID version = ctx.getArgument("version", UUID.class);
|
||||||
|
final DumpType type = ctx.getArgument("type", DumpType.class);
|
||||||
|
dumpSnapshot(user(sub, ctx), user, version, type == DumpType.WEB);
|
||||||
|
}, user("username"), uuid("version"), dumpType());
|
||||||
|
}
|
||||||
|
|
||||||
|
private <S> ArgumentElement<S, DumpType> dumpType() {
|
||||||
|
return new ArgumentElement<>("type", reader -> {
|
||||||
|
final String type = reader.readString();
|
||||||
|
return switch (type.toLowerCase(Locale.ENGLISH)) {
|
||||||
|
case "web" -> DumpType.WEB;
|
||||||
|
case "file" -> DumpType.FILE;
|
||||||
|
default -> throw CommandSyntaxException.BUILT_IN_EXCEPTIONS
|
||||||
|
.dispatcherUnknownArgument().createWithContext(reader);
|
||||||
|
};
|
||||||
|
}, (context, builder) -> {
|
||||||
|
builder.suggest("web");
|
||||||
|
builder.suggest("file");
|
||||||
|
return builder.buildFuture();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DumpType {
|
||||||
|
WEB,
|
||||||
|
FILE
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,9 +69,6 @@ public class Settings {
|
|||||||
@Comment("Enable development debug logging")
|
@Comment("Enable development debug logging")
|
||||||
private boolean debugLogging = false;
|
private boolean debugLogging = false;
|
||||||
|
|
||||||
@Comment("Whether to provide modern, rich TAB suggestions for commands (if available)")
|
|
||||||
private boolean brigadierTabCompletion = false;
|
|
||||||
|
|
||||||
@Comment({"Whether to enable the Player Analytics hook.", "Docs: https://william278.net/docs/husksync/plan-hook"})
|
@Comment({"Whether to enable the Player Analytics hook.", "Docs: https://william278.net/docs/husksync/plan-hook"})
|
||||||
private boolean enablePlanHook = true;
|
private boolean enablePlanHook = true;
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import net.kyori.adventure.audience.Audience;
|
|||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public interface CommandUser {
|
public interface CommandUser {
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
Audience getAudience();
|
Audience getAudience();
|
||||||
|
|||||||
@@ -42,4 +42,6 @@ public final class ConsoleUser implements CommandUser {
|
|||||||
public boolean hasPermission(@NotNull String permission) {
|
public boolean hasPermission(@NotNull String permission) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ dependencies {
|
|||||||
modImplementation include("eu.pb4:sgui:${sgui_version}")
|
modImplementation include("eu.pb4:sgui:${sgui_version}")
|
||||||
modCompileOnly "net.fabricmc.fabric-api:fabric-api:${fabric_api_version}"
|
modCompileOnly "net.fabricmc.fabric-api:fabric-api:${fabric_api_version}"
|
||||||
|
|
||||||
// Runtime dependencies on Bukkit; "include" them on Fabric. (todo: minify JAR?)
|
implementation include('net.william278.uniform:uniform-fabric:1.1+1.20.1')
|
||||||
implementation include('org.apache.commons:commons-pool2:2.12.0')
|
implementation include('org.apache.commons:commons-pool2:2.12.0')
|
||||||
implementation include("redis.clients:jedis:$jedis_version")
|
implementation include("redis.clients:jedis:$jedis_version")
|
||||||
implementation include("com.mysql:mysql-connector-j:$mysql_driver_version")
|
implementation include("com.mysql:mysql-connector-j:$mysql_driver_version")
|
||||||
@@ -33,7 +33,7 @@ dependencies {
|
|||||||
|
|
||||||
annotationProcessor 'org.projectlombok:lombok:1.18.32'
|
annotationProcessor 'org.projectlombok:lombok:1.18.32'
|
||||||
|
|
||||||
shadow project(path: ":common")
|
implementation project(path: ":common")
|
||||||
}
|
}
|
||||||
|
|
||||||
shadowJar {
|
shadowJar {
|
||||||
@@ -54,6 +54,7 @@ shadowJar {
|
|||||||
relocate 'org.intellij', 'net.william278.husksync.libraries'
|
relocate 'org.intellij', 'net.william278.husksync.libraries'
|
||||||
relocate 'com.zaxxer', 'net.william278.husksync.libraries'
|
relocate 'com.zaxxer', 'net.william278.husksync.libraries'
|
||||||
relocate 'de.exlll', 'net.william278.husksync.libraries'
|
relocate 'de.exlll', 'net.william278.husksync.libraries'
|
||||||
|
relocate 'net.william278.uniform', 'net.william278.husksync.libraries.uniform'
|
||||||
relocate 'net.william278.desertwell', 'net.william278.husksync.libraries.desertwell'
|
relocate 'net.william278.desertwell', 'net.william278.husksync.libraries.desertwell'
|
||||||
relocate 'net.william278.paginedown', 'net.william278.husksync.libraries.paginedown'
|
relocate 'net.william278.paginedown', 'net.william278.husksync.libraries.paginedown'
|
||||||
relocate 'org.json', 'net.william278.husksync.libraries.json'
|
relocate 'org.json', 'net.william278.husksync.libraries.json'
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import lombok.Getter;
|
|||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import net.fabricmc.api.DedicatedServerModInitializer;
|
import net.fabricmc.api.DedicatedServerModInitializer;
|
||||||
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
|
||||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
import net.fabricmc.loader.api.ModContainer;
|
import net.fabricmc.loader.api.ModContainer;
|
||||||
@@ -40,8 +39,7 @@ import net.william278.husksync.adapter.DataAdapter;
|
|||||||
import net.william278.husksync.adapter.GsonAdapter;
|
import net.william278.husksync.adapter.GsonAdapter;
|
||||||
import net.william278.husksync.adapter.SnappyGsonAdapter;
|
import net.william278.husksync.adapter.SnappyGsonAdapter;
|
||||||
import net.william278.husksync.api.FabricHuskSyncAPI;
|
import net.william278.husksync.api.FabricHuskSyncAPI;
|
||||||
import net.william278.husksync.command.Command;
|
import net.william278.husksync.command.PluginCommand;
|
||||||
import net.william278.husksync.command.FabricCommand;
|
|
||||||
import net.william278.husksync.config.Locales;
|
import net.william278.husksync.config.Locales;
|
||||||
import net.william278.husksync.config.Server;
|
import net.william278.husksync.config.Server;
|
||||||
import net.william278.husksync.config.Settings;
|
import net.william278.husksync.config.Settings;
|
||||||
@@ -62,6 +60,8 @@ import net.william278.husksync.user.FabricUser;
|
|||||||
import net.william278.husksync.user.OnlineUser;
|
import net.william278.husksync.user.OnlineUser;
|
||||||
import net.william278.husksync.util.FabricTask;
|
import net.william278.husksync.util.FabricTask;
|
||||||
import net.william278.husksync.util.LegacyConverter;
|
import net.william278.husksync.util.LegacyConverter;
|
||||||
|
import net.william278.uniform.Uniform;
|
||||||
|
import net.william278.uniform.fabric.FabricUniform;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -74,22 +74,22 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class FabricHuskSync implements DedicatedServerModInitializer, HuskSync, FabricTask.Supplier,
|
public class FabricHuskSync implements DedicatedServerModInitializer, HuskSync, FabricTask.Supplier,
|
||||||
FabricEventDispatcher {
|
FabricEventDispatcher {
|
||||||
|
|
||||||
private static final String PLATFORM_TYPE_ID = "fabric";
|
private static final String PLATFORM_TYPE_ID = "fabric";
|
||||||
|
|
||||||
private final TreeMap<Identifier, Serializer<? extends Data>> serializers = Maps.newTreeMap(
|
private final TreeMap<Identifier, Serializer<? extends Data>> serializers = Maps.newTreeMap(
|
||||||
SerializerRegistry.DEPENDENCY_ORDER_COMPARATOR
|
SerializerRegistry.DEPENDENCY_ORDER_COMPARATOR
|
||||||
);
|
);
|
||||||
private final Map<UUID, Map<Identifier, Data>> playerCustomDataStore = Maps.newConcurrentMap();
|
private final Map<UUID, Map<Identifier, Data>> playerCustomDataStore = Maps.newConcurrentMap();
|
||||||
private final Map<String, Boolean> permissions = Maps.newHashMap();
|
private final Map<String, Boolean> permissions = Maps.newHashMap();
|
||||||
private final List<Migrator> availableMigrators = Lists.newArrayList();
|
private final List<Migrator> availableMigrators = Lists.newArrayList();
|
||||||
private final Set<UUID> lockedPlayers = Sets.newConcurrentHashSet();
|
private final Set<UUID> lockedPlayers = Sets.newConcurrentHashSet();
|
||||||
|
private final Map<UUID, FabricUser> playerMap = Maps.newConcurrentMap();
|
||||||
|
|
||||||
private Logger logger;
|
private Logger logger;
|
||||||
private ModContainer mod;
|
private ModContainer mod;
|
||||||
@@ -127,7 +127,7 @@ public class FabricHuskSync implements DedicatedServerModInitializer, HuskSync,
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Register commands
|
// Register commands
|
||||||
initialize("commands", (plugin) -> this.registerCommands());
|
initialize("commands", (plugin) -> getUniform().register(PluginCommand.Type.create(this)));
|
||||||
|
|
||||||
// Load HuskSync after server startup
|
// Load HuskSync after server startup
|
||||||
ServerLifecycleEvents.SERVER_STARTED.register(server -> {
|
ServerLifecycleEvents.SERVER_STARTED.register(server -> {
|
||||||
@@ -232,12 +232,6 @@ public class FabricHuskSync implements DedicatedServerModInitializer, HuskSync,
|
|||||||
log(Level.INFO, "Successfully disabled HuskSync v" + getPluginVersion());
|
log(Level.INFO, "Successfully disabled HuskSync v" + getPluginVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerCommands() {
|
|
||||||
final List<Command> commands = FabricCommand.Type.getCommands(this);
|
|
||||||
CommandRegistrationCallback.EVENT.register((dispatcher, registry, environment) ->
|
|
||||||
commands.forEach(command -> new FabricCommand(command, this).register(dispatcher))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
@@ -253,31 +247,34 @@ public class FabricHuskSync implements DedicatedServerModInitializer, HuskSync,
|
|||||||
@Override
|
@Override
|
||||||
@NotNull
|
@NotNull
|
||||||
public Set<OnlineUser> getOnlineUsers() {
|
public Set<OnlineUser> getOnlineUsers() {
|
||||||
return minecraftServer.getPlayerManager().getPlayerList()
|
return Sets.newHashSet(playerMap.values());
|
||||||
.stream().map(user -> (OnlineUser) FabricUser.adapt(user, this))
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NotNull
|
@NotNull
|
||||||
public Optional<OnlineUser> getOnlineUser(@NotNull UUID uuid) {
|
public Optional<OnlineUser> getOnlineUser(@NotNull UUID uuid) {
|
||||||
return Optional.ofNullable(minecraftServer.getPlayerManager().getPlayer(uuid))
|
return Optional.ofNullable(playerMap.get(uuid));
|
||||||
.map(user -> FabricUser.adapt(user, this));
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NotNull
|
||||||
|
public Uniform getUniform() {
|
||||||
|
return FabricUniform.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public InputStream getResource(@NotNull String name) {
|
public InputStream getResource(@NotNull String name) {
|
||||||
return this.mod.findPath(name)
|
return this.mod.findPath(name)
|
||||||
.map(path -> {
|
.map(path -> {
|
||||||
try {
|
try {
|
||||||
return Files.newInputStream(path);
|
return Files.newInputStream(path);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log(Level.WARNING, "Failed to load resource: " + name, e);
|
log(Level.WARNING, "Failed to load resource: " + name, e);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
.orElse(this.getClass().getClassLoader().getResourceAsStream(name));
|
.orElse(this.getClass().getClassLoader().getResourceAsStream(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -297,11 +294,11 @@ public class FabricHuskSync implements DedicatedServerModInitializer, HuskSync,
|
|||||||
@Override
|
@Override
|
||||||
public void log(@NotNull Level level, @NotNull String message, @NotNull Throwable... throwable) {
|
public void log(@NotNull Level level, @NotNull String message, @NotNull Throwable... throwable) {
|
||||||
LoggingEventBuilder logEvent = logger.makeLoggingEventBuilder(
|
LoggingEventBuilder logEvent = logger.makeLoggingEventBuilder(
|
||||||
switch (level.getName()) {
|
switch (level.getName()) {
|
||||||
case "WARNING" -> org.slf4j.event.Level.WARN;
|
case "WARNING" -> org.slf4j.event.Level.WARN;
|
||||||
case "SEVERE" -> org.slf4j.event.Level.ERROR;
|
case "SEVERE" -> org.slf4j.event.Level.ERROR;
|
||||||
default -> org.slf4j.event.Level.INFO;
|
default -> org.slf4j.event.Level.INFO;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if (throwable.length >= 1) {
|
if (throwable.length >= 1) {
|
||||||
logEvent = logEvent.setCause(throwable[0]);
|
logEvent = logEvent.setCause(throwable[0]);
|
||||||
|
|||||||
@@ -1,153 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of HuskSync, licensed under the Apache License 2.0.
|
|
||||||
*
|
|
||||||
* Copyright (c) William278 <will27528@gmail.com>
|
|
||||||
* Copyright (c) contributors
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package net.william278.husksync.command;
|
|
||||||
|
|
||||||
import com.mojang.brigadier.CommandDispatcher;
|
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
|
||||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
|
||||||
import me.lucko.fabric.api.permissions.v0.PermissionCheckEvent;
|
|
||||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
|
||||||
import net.fabricmc.fabric.api.util.TriState;
|
|
||||||
import net.minecraft.server.command.ServerCommandSource;
|
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
|
||||||
import net.william278.husksync.FabricHuskSync;
|
|
||||||
import net.william278.husksync.HuskSync;
|
|
||||||
import net.william278.husksync.user.CommandUser;
|
|
||||||
import net.william278.husksync.user.FabricUser;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
import static com.mojang.brigadier.arguments.StringArgumentType.greedyString;
|
|
||||||
import static net.minecraft.server.command.CommandManager.argument;
|
|
||||||
import static net.minecraft.server.command.CommandManager.literal;
|
|
||||||
|
|
||||||
public class FabricCommand {
|
|
||||||
|
|
||||||
private final FabricHuskSync plugin;
|
|
||||||
private final Command command;
|
|
||||||
|
|
||||||
public FabricCommand(@NotNull Command command, @NotNull FabricHuskSync plugin) {
|
|
||||||
this.command = command;
|
|
||||||
this.plugin = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void register(@NotNull CommandDispatcher<ServerCommandSource> dispatcher) {
|
|
||||||
// Register brigadier command
|
|
||||||
final Predicate<ServerCommandSource> predicate = Permissions
|
|
||||||
.require(command.getPermission(), command.isOperatorCommand() ? 3 : 0);
|
|
||||||
final LiteralArgumentBuilder<ServerCommandSource> builder = literal(command.getName())
|
|
||||||
.requires(predicate).executes(getBrigadierExecutor());
|
|
||||||
plugin.getPermissions().put(command.getPermission(), command.isOperatorCommand());
|
|
||||||
if (!command.getRawUsage().isBlank()) {
|
|
||||||
builder.then(argument(command.getRawUsage().replaceAll("[<>\\[\\]]", ""), greedyString())
|
|
||||||
.executes(getBrigadierExecutor())
|
|
||||||
.suggests(getBrigadierSuggester()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register additional permissions
|
|
||||||
final Map<String, Boolean> permissions = command.getAdditionalPermissions();
|
|
||||||
permissions.forEach((permission, isOp) -> plugin.getPermissions().put(permission, isOp));
|
|
||||||
PermissionCheckEvent.EVENT.register((player, node) -> {
|
|
||||||
if (permissions.containsKey(node) && permissions.get(node) && player.hasPermissionLevel(3)) {
|
|
||||||
return TriState.TRUE;
|
|
||||||
}
|
|
||||||
return TriState.DEFAULT;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Register aliases
|
|
||||||
final LiteralCommandNode<ServerCommandSource> node = dispatcher.register(builder);
|
|
||||||
dispatcher.register(literal("husksync:" + command.getName())
|
|
||||||
.requires(predicate).executes(getBrigadierExecutor()).redirect(node));
|
|
||||||
command.getAliases().forEach(alias -> dispatcher.register(literal(alias)
|
|
||||||
.requires(predicate).executes(getBrigadierExecutor()).redirect(node)));
|
|
||||||
}
|
|
||||||
|
|
||||||
private com.mojang.brigadier.Command<ServerCommandSource> getBrigadierExecutor() {
|
|
||||||
return (context) -> {
|
|
||||||
command.onExecuted(
|
|
||||||
resolveExecutor(context.getSource()),
|
|
||||||
command.removeFirstArg(context.getInput().split(" "))
|
|
||||||
);
|
|
||||||
return 1;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private com.mojang.brigadier.suggestion.SuggestionProvider<ServerCommandSource> getBrigadierSuggester() {
|
|
||||||
if (!(command instanceof TabProvider provider)) {
|
|
||||||
return (context, builder) -> com.mojang.brigadier.suggestion.Suggestions.empty();
|
|
||||||
}
|
|
||||||
return (context, builder) -> {
|
|
||||||
final String[] args = command.removeFirstArg(context.getInput().split(" ", -1));
|
|
||||||
provider.getSuggestions(resolveExecutor(context.getSource()), args).stream()
|
|
||||||
.map(suggestion -> {
|
|
||||||
final String completedArgs = String.join(" ", args);
|
|
||||||
int lastIndex = completedArgs.lastIndexOf(" ");
|
|
||||||
if (lastIndex == -1) {
|
|
||||||
return suggestion;
|
|
||||||
}
|
|
||||||
return completedArgs.substring(0, lastIndex + 1) + suggestion;
|
|
||||||
})
|
|
||||||
.forEach(builder::suggest);
|
|
||||||
return builder.buildFuture();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private CommandUser resolveExecutor(@NotNull ServerCommandSource source) {
|
|
||||||
if (source.getEntity() instanceof ServerPlayerEntity player) {
|
|
||||||
return FabricUser.adapt(player, plugin);
|
|
||||||
}
|
|
||||||
return plugin.getConsole();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Commands available on the Fabric HuskSync implementation.
|
|
||||||
*/
|
|
||||||
public enum Type {
|
|
||||||
|
|
||||||
HUSKSYNC_COMMAND(HuskSyncCommand::new),
|
|
||||||
USERDATA_COMMAND(UserDataCommand::new),
|
|
||||||
INVENTORY_COMMAND(InventoryCommand::new),
|
|
||||||
ENDER_CHEST_COMMAND(EnderChestCommand::new);
|
|
||||||
|
|
||||||
private final Function<HuskSync, Command> supplier;
|
|
||||||
|
|
||||||
Type(@NotNull Function<HuskSync, Command> supplier) {
|
|
||||||
this.supplier = supplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public Command createCommand(@NotNull HuskSync plugin) {
|
|
||||||
return supplier.apply(plugin);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static List<Command> getCommands(@NotNull FabricHuskSync plugin) {
|
|
||||||
return Arrays.stream(values()).map(type -> type.createCommand(plugin)).toList();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -315,7 +315,7 @@ public abstract class FabricData implements Data {
|
|||||||
|
|
||||||
// Only save the advancement if criteria has been completed
|
// Only save the advancement if criteria has been completed
|
||||||
if (!awardedCriteria.isEmpty()) {
|
if (!awardedCriteria.isEmpty()) {
|
||||||
advancements.add(Advancement.adapt(advancement.getId().asString(), awardedCriteria));
|
advancements.add(Advancement.adapt(advancement.getId().toString(), awardedCriteria));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return new FabricData.Advancements(advancements);
|
return new FabricData.Advancements(advancements);
|
||||||
@@ -479,7 +479,7 @@ public abstract class FabricData implements Data {
|
|||||||
Registries.STAT_TYPE.getEntrySet().forEach(stat -> {
|
Registries.STAT_TYPE.getEntrySet().forEach(stat -> {
|
||||||
final Registry<?> registry = stat.getValue().getRegistry();
|
final Registry<?> registry = stat.getValue().getRegistry();
|
||||||
|
|
||||||
final String registryId = registry.getKey().getValue().value();
|
final String registryId = registry.getKey().getValue().getPath();
|
||||||
if (registryId.equals("custom_stat")) {
|
if (registryId.equals("custom_stat")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -488,13 +488,13 @@ public abstract class FabricData implements Data {
|
|||||||
case ITEM_STAT_TYPE -> items;
|
case ITEM_STAT_TYPE -> items;
|
||||||
case ENTITY_STAT_TYPE -> entities;
|
case ENTITY_STAT_TYPE -> entities;
|
||||||
default -> throw new IllegalStateException("Unexpected value: %s".formatted(registryId));
|
default -> throw new IllegalStateException("Unexpected value: %s".formatted(registryId));
|
||||||
}).compute(stat.getKey().getValue().asString(), (k, v) -> v == null ? Maps.newHashMap() : v);
|
}).compute(stat.getKey().getValue().toString(), (k, v) -> v == null ? Maps.newHashMap() : v);
|
||||||
|
|
||||||
registry.getEntrySet().forEach(entry -> {
|
registry.getEntrySet().forEach(entry -> {
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"}) final int value = player.getStatHandler()
|
@SuppressWarnings({"unchecked", "rawtypes"}) final int value = player.getStatHandler()
|
||||||
.getStat((StatType) stat.getValue(), entry.getValue());
|
.getStat((StatType) stat.getValue(), entry.getValue());
|
||||||
if (value != 0) {
|
if (value != 0) {
|
||||||
map.put(entry.getKey().getValue().asString(), value);
|
map.put(entry.getKey().getValue().toString(), value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -504,7 +504,7 @@ public abstract class FabricData implements Data {
|
|||||||
Registries.CUSTOM_STAT.getEntrySet().forEach(stat -> {
|
Registries.CUSTOM_STAT.getEntrySet().forEach(stat -> {
|
||||||
final int value = player.getStatHandler().getStat(Stats.CUSTOM.getOrCreateStat(stat.getValue()));
|
final int value = player.getStatHandler().getStat(Stats.CUSTOM.getOrCreateStat(stat.getValue()));
|
||||||
if (value != 0) {
|
if (value != 0) {
|
||||||
generic.put(stat.getKey().getValue().asString(), value);
|
generic.put(stat.getKey().getValue().toString(), value);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -587,7 +587,7 @@ public abstract class FabricData implements Data {
|
|||||||
-1
|
-1
|
||||||
)));
|
)));
|
||||||
attributes.add(new Attribute(
|
attributes.add(new Attribute(
|
||||||
key.asString(),
|
key.toString(),
|
||||||
instance.getBaseValue(),
|
instance.getBaseValue(),
|
||||||
modifiers
|
modifiers
|
||||||
));
|
));
|
||||||
@@ -596,7 +596,7 @@ public abstract class FabricData implements Data {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Attribute> getAttribute(@NotNull EntityAttribute id) {
|
public Optional<Attribute> getAttribute(@NotNull EntityAttribute id) {
|
||||||
return Optional.ofNullable(Registries.ATTRIBUTE.getId(id)).map(Identifier::asString)
|
return Optional.ofNullable(Registries.ATTRIBUTE.getId(id)).map(Identifier::toString)
|
||||||
.flatMap(key -> attributes.stream().filter(attribute -> attribute.name().equals(key)).findFirst());
|
.flatMap(key -> attributes.stream().filter(attribute -> attribute.name().equals(key)).findFirst());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import net.minecraft.util.hit.BlockHitResult;
|
|||||||
import net.minecraft.util.hit.EntityHitResult;
|
import net.minecraft.util.hit.EntityHitResult;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
import net.william278.husksync.FabricHuskSync;
|
||||||
import net.william278.husksync.HuskSync;
|
import net.william278.husksync.HuskSync;
|
||||||
import net.william278.husksync.config.Settings.SynchronizationSettings.SaveOnDeathSettings;
|
import net.william278.husksync.config.Settings.SynchronizationSettings.SaveOnDeathSettings;
|
||||||
import net.william278.husksync.data.FabricData;
|
import net.william278.husksync.data.FabricData;
|
||||||
@@ -82,10 +83,13 @@ public class FabricEventListener extends EventListener implements LockedHandler
|
|||||||
|
|
||||||
private void handlePlayerJoin(@NotNull ServerPlayNetworkHandler handler, @NotNull PacketSender sender,
|
private void handlePlayerJoin(@NotNull ServerPlayNetworkHandler handler, @NotNull PacketSender sender,
|
||||||
@NotNull MinecraftServer server) {
|
@NotNull MinecraftServer server) {
|
||||||
handlePlayerJoin(FabricUser.adapt(handler.player, plugin));
|
final FabricUser user = FabricUser.adapt(handler.player, plugin);
|
||||||
|
((FabricHuskSync) plugin).getPlayerMap().put(handler.player.getUuid(), user);
|
||||||
|
handlePlayerJoin(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handlePlayerQuit(@NotNull ServerPlayNetworkHandler handler, @NotNull MinecraftServer server) {
|
private void handlePlayerQuit(@NotNull ServerPlayNetworkHandler handler, @NotNull MinecraftServer server) {
|
||||||
|
((FabricHuskSync) plugin).getPlayerMap().remove(handler.player.getUuid());
|
||||||
handlePlayerQuit(FabricUser.adapt(handler.player, plugin));
|
handlePlayerQuit(FabricUser.adapt(handler.player, plugin));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public class FabricUser extends OnlineUser implements FabricUserDataHolder {
|
|||||||
@Override
|
@Override
|
||||||
public void sendToast(@NotNull MineDown title, @NotNull MineDown description, @NotNull String iconMaterial,
|
public void sendToast(@NotNull MineDown title, @NotNull MineDown description, @NotNull String iconMaterial,
|
||||||
@NotNull String backgroundType) {
|
@NotNull String backgroundType) {
|
||||||
player.sendActionBar(title.toComponent()); // Toasts unimplemented for now
|
getAudience().sendActionBar(title.toComponent()); // Toasts unimplemented for now
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ dependencies {
|
|||||||
implementation project(':bukkit')
|
implementation project(':bukkit')
|
||||||
compileOnly project(':common')
|
compileOnly project(':common')
|
||||||
|
|
||||||
|
implementation 'net.william278.uniform:uniform-paper:1.1'
|
||||||
|
|
||||||
compileOnly 'io.papermc.paper:paper-api:1.19.4-R0.1-SNAPSHOT'
|
compileOnly 'io.papermc.paper:paper-api:1.19.4-R0.1-SNAPSHOT'
|
||||||
compileOnly 'org.jetbrains:annotations:24.1.0'
|
compileOnly 'org.jetbrains:annotations:24.1.0'
|
||||||
compileOnly 'org.projectlombok:lombok:1.18.32'
|
compileOnly 'org.projectlombok:lombok:1.18.32'
|
||||||
@@ -24,6 +26,7 @@ shadowJar {
|
|||||||
relocate 'org.intellij', 'net.william278.husksync.libraries'
|
relocate 'org.intellij', 'net.william278.husksync.libraries'
|
||||||
relocate 'com.zaxxer', 'net.william278.husksync.libraries'
|
relocate 'com.zaxxer', 'net.william278.husksync.libraries'
|
||||||
relocate 'de.exlll', 'net.william278.husksync.libraries'
|
relocate 'de.exlll', 'net.william278.husksync.libraries'
|
||||||
|
relocate 'net.william278.uniform', 'net.william278.husksync.libraries.uniform'
|
||||||
relocate 'net.william278.desertwell', 'net.william278.husksync.libraries.desertwell'
|
relocate 'net.william278.desertwell', 'net.william278.husksync.libraries.desertwell'
|
||||||
relocate 'net.william278.paginedown', 'net.william278.husksync.libraries.paginedown'
|
relocate 'net.william278.paginedown', 'net.william278.husksync.libraries.paginedown'
|
||||||
relocate 'net.william278.mapdataapi', 'net.william278.husksync.libraries.mapdataapi'
|
relocate 'net.william278.mapdataapi', 'net.william278.husksync.libraries.mapdataapi'
|
||||||
@@ -33,7 +36,6 @@ shadowJar {
|
|||||||
relocate 'org.json', 'net.william278.husksync.libraries.json'
|
relocate 'org.json', 'net.william278.husksync.libraries.json'
|
||||||
relocate 'net.querz', 'net.william278.husksync.libraries.nbtparser'
|
relocate 'net.querz', 'net.william278.husksync.libraries.nbtparser'
|
||||||
relocate 'net.roxeez', 'net.william278.husksync.libraries'
|
relocate 'net.roxeez', 'net.william278.husksync.libraries'
|
||||||
relocate 'me.lucko.commodore', 'net.william278.husksync.libraries.commodore'
|
|
||||||
relocate 'org.bstats', 'net.william278.husksync.libraries.bstats'
|
relocate 'org.bstats', 'net.william278.husksync.libraries.bstats'
|
||||||
relocate 'dev.triumphteam.gui', 'net.william278.husksync.libraries.triumphgui'
|
relocate 'dev.triumphteam.gui', 'net.william278.husksync.libraries.triumphgui'
|
||||||
relocate 'space.arim.morepaperlib', 'net.william278.husksync.libraries.paperlib'
|
relocate 'space.arim.morepaperlib', 'net.william278.husksync.libraries.paperlib'
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ package net.william278.husksync;
|
|||||||
import net.kyori.adventure.audience.Audience;
|
import net.kyori.adventure.audience.Audience;
|
||||||
import net.william278.husksync.listener.BukkitEventListener;
|
import net.william278.husksync.listener.BukkitEventListener;
|
||||||
import net.william278.husksync.listener.PaperEventListener;
|
import net.william278.husksync.listener.PaperEventListener;
|
||||||
|
import net.william278.uniform.Uniform;
|
||||||
|
import net.william278.uniform.paper.PaperUniform;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@@ -43,4 +45,9 @@ public class PaperHuskSync extends BukkitHuskSync {
|
|||||||
return player == null || !player.isOnline() ? Audience.empty() : player;
|
return player == null || !player.isOnline() ? Audience.empty() : player;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NotNull
|
||||||
|
public Uniform getUniform() {
|
||||||
|
return PaperUniform.getInstance(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class Parameters:
|
|||||||
proxy_plugins = []
|
proxy_plugins = []
|
||||||
proxy_plugin_folders = []
|
proxy_plugin_folders = []
|
||||||
|
|
||||||
just_update_plugins = True
|
just_update_plugins = False
|
||||||
|
|
||||||
|
|
||||||
def main(update=False):
|
def main(update=False):
|
||||||
|
|||||||
Reference in New Issue
Block a user