mirror of
https://github.com/WiIIiam278/HuskSync.git
synced 2025-12-23 16:49:19 +00:00
v3.0: New modular, more compatible data format, new API, better UX (#160)
* Start work on v3 * More work on task scheduling * Add comment to notification display slot * Synchronise branches * Use new HuskHomes-style task system * Bump to 2.3 * Remove HuskSyncInitializationException.java * Optimise database for MariaDB * Update libraries, move some around * Tweak command registration * Remove dummyhusksync * Fixup core synchronisation logic to use new task system * Implement new event dispatch subsystem * Remove last remaining future calls * Remove `Event#fire()` * Refactor startup process * New command subsystem, more initialization improvements, locale fixes * Update docs, tweak command perms * Reduce task number during data setting * add todo * Start work on data format / serialization refactor * More work on Bukkit impl * More serialization work * Fixes to serialization, data preview system * Start legacy conversion skeleton * Handle setting empty inventories * Start on-the-fly legacy conversion work * Add advancement conversion * Rewrite advancement get / apply logic * Start work on locked map persistence * More map persistence work * More work on map serialization * Move around persistence logic * Add testing suite * Fix item synchronisation * Finalize more reliable locked map persistence * Remove deprecated method call * remove sync feature enum * Fix held item slot syncing * Make data types modular and API-extensible * Remove some excessive debugging, minor refactor * Fixup date formatting, improve menu UIs * Finish up legacy data converting * Null safety in item stack serializaiton * Fix relocation of nbtapi, update dumping docs * Add v1/MPDB Migrators back in * Fix pinning/unpinning data not working * Consumer instead of Function for editing data * Show file size in DataSnapshotOverview * Fix getIdentifier always returning empty * Re-add items and inventory GUI commands * Improve config file, fixup data restoration * Add min time between backups (more useful backups!) * More work on backups * Fixup backup rotation frequency * Remove stdout debug print in `#getEventPriority` * Improve sync complete locale logic, fix synchronization spelling * Remove `static` on exception * Use dedicated thread for Redis, properly unsubscribe * Refactor `player` package -> `user` * `PlayerDataHolder` -> `UserDataHolder` * Make `StatisticsMap` public, but `@ApiStatus.Internal` * Suppress unused warnings on `Data` * Add option to disable Plan hook * Decompress legacy data before converting * Decompress bytes in fromBytes * Check permission node before serving TAB suggestions * Actually convert legacy item stack data * Fix syntax errors * Minor method refactor in items command * Fixup case-sensitive parsing in HuskSync command * Start API work * More work on API, fix potion effects * Fix cross-server, config formatting for auto-pinned issue * Fix confusion with UserData command, update docs images * Update commands docs * More docs updating * Fix sync feature enabled/disabled checking logic * Fix `#isCustom()` * Enable persistent_data syncing by default * docs: update Sync-Features config snippet * docs: correct typo in Sync Features * More API work * bukkit: slightly optimized schedulers * More API work, various refactorings * docs: Start new API docs * bump dependencies * Add some basic unit tests * docs: Correct typos * More docs work, annotate DB methods as `@Blocking` * Encapsulate `RedisMessage`, minor optimisations * api: Simplify `#getCurrentData` * api: Simplify `editCurrentData`, using `ThrowingConsumers` for better error handling * docs: More Data Snapshot API documenting * docs: add TOC to Data Snapshot API page * bukkit: Make data types extend BukkitData * Move where custom data is stored, finish up Custom Data API docs * Optimise imports * Fix `data_manager_advancements_preview_remaining` locale * Fix advancement and playtime previews * Fix potion effect deserialization * Make snapshot_backup_frequency default to 4, more error handling/logging * docs: Add ToC to Custom Data API * docs: Minor legacy API tweaks * Remove some unneeded catch logic * Suppress a few warnings * Fix Effect constructor being supplied in wrong order
This commit is contained in:
@@ -19,31 +19,40 @@
|
||||
|
||||
package net.william278.husksync;
|
||||
|
||||
import com.fatboyindustrial.gsonjavatime.Converters;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import net.william278.annotaml.Annotaml;
|
||||
import net.william278.desertwell.util.ThrowingConsumer;
|
||||
import net.william278.desertwell.util.UpdateChecker;
|
||||
import net.william278.desertwell.util.Version;
|
||||
import net.william278.husksync.adapter.DataAdapter;
|
||||
import net.william278.husksync.config.Locales;
|
||||
import net.william278.husksync.config.Settings;
|
||||
import net.william278.husksync.data.DataAdapter;
|
||||
import net.william278.husksync.data.Data;
|
||||
import net.william278.husksync.data.Identifier;
|
||||
import net.william278.husksync.data.Serializer;
|
||||
import net.william278.husksync.database.Database;
|
||||
import net.william278.husksync.event.EventCannon;
|
||||
import net.william278.husksync.event.EventDispatcher;
|
||||
import net.william278.husksync.migrator.Migrator;
|
||||
import net.william278.husksync.player.OnlineUser;
|
||||
import net.william278.husksync.redis.RedisManager;
|
||||
import net.william278.husksync.user.ConsoleUser;
|
||||
import net.william278.husksync.user.OnlineUser;
|
||||
import net.william278.husksync.util.LegacyConverter;
|
||||
import net.william278.husksync.util.Task;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* Abstract implementation of the HuskSync plugin.
|
||||
*/
|
||||
public interface HuskSync {
|
||||
public interface HuskSync extends Task.Supplier, EventDispatcher {
|
||||
|
||||
int SPIGOT_RESOURCE_ID = 97144;
|
||||
|
||||
@@ -81,21 +90,45 @@ public interface HuskSync {
|
||||
@NotNull
|
||||
RedisManager getRedisManager();
|
||||
|
||||
/**
|
||||
* Returns the data adapter implementation
|
||||
*
|
||||
* @return the {@link DataAdapter} implementation
|
||||
*/
|
||||
@NotNull
|
||||
DataAdapter getDataAdapter();
|
||||
|
||||
/**
|
||||
* Returns the event firing cannon
|
||||
*
|
||||
* @return the {@link EventCannon} implementation
|
||||
* Returns the data serializer for the given {@link Identifier}
|
||||
*/
|
||||
@NotNull
|
||||
EventCannon getEventCannon();
|
||||
<T extends Data> Map<Identifier, Serializer<T>> getSerializers();
|
||||
|
||||
/**
|
||||
* Register a data serializer for the given {@link Identifier}
|
||||
*
|
||||
* @param identifier the {@link Identifier}
|
||||
* @param serializer the {@link Serializer}
|
||||
*/
|
||||
default void registerSerializer(@NotNull Identifier identifier,
|
||||
@NotNull Serializer<? extends Data> serializer) {
|
||||
if (identifier.isCustom()) {
|
||||
log(Level.INFO, String.format("Registered custom data type: %s", identifier));
|
||||
}
|
||||
getSerializers().put(identifier, (Serializer<Data>) serializer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link Identifier} for the given key
|
||||
*/
|
||||
default Optional<Identifier> getIdentifier(@NotNull String key) {
|
||||
return getSerializers().keySet().stream().filter(identifier -> identifier.toString().equals(key)).findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the set of registered data types
|
||||
*
|
||||
* @return the set of registered data types
|
||||
*/
|
||||
@NotNull
|
||||
default Set<Identifier> getRegisteredDataTypes() {
|
||||
return getSerializers().keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of available data {@link Migrator}s
|
||||
@@ -105,6 +138,25 @@ public interface HuskSync {
|
||||
@NotNull
|
||||
List<Migrator> getAvailableMigrators();
|
||||
|
||||
@NotNull
|
||||
Map<Identifier, Data> getPlayerCustomDataStore(@NotNull OnlineUser user);
|
||||
|
||||
/**
|
||||
* Initialize a faucet of the plugin.
|
||||
*
|
||||
* @param name the name of the faucet
|
||||
* @param runner a runnable for initializing the faucet
|
||||
*/
|
||||
default void initialize(@NotNull String name, @NotNull ThrowingConsumer<HuskSync> runner) {
|
||||
log(Level.INFO, "Initializing " + name + "...");
|
||||
try {
|
||||
runner.accept(this);
|
||||
} catch (Throwable e) {
|
||||
throw new FailedToLoadException("Failed to initialize " + name, e);
|
||||
}
|
||||
log(Level.INFO, "Successfully initialized " + name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the plugin {@link Settings}
|
||||
*
|
||||
@@ -113,6 +165,8 @@ public interface HuskSync {
|
||||
@NotNull
|
||||
Settings getSettings();
|
||||
|
||||
void setSettings(@NotNull Settings settings);
|
||||
|
||||
/**
|
||||
* Returns the plugin {@link Locales}
|
||||
*
|
||||
@@ -121,6 +175,16 @@ public interface HuskSync {
|
||||
@NotNull
|
||||
Locales getLocales();
|
||||
|
||||
void setLocales(@NotNull Locales locales);
|
||||
|
||||
/**
|
||||
* Returns if a dependency is loaded
|
||||
*
|
||||
* @param name the name of the dependency
|
||||
* @return {@code true} if the dependency is loaded, {@code false} otherwise
|
||||
*/
|
||||
boolean isDependencyLoaded(@NotNull String name);
|
||||
|
||||
/**
|
||||
* Get a resource as an {@link InputStream} from the plugin jar
|
||||
*
|
||||
@@ -129,6 +193,14 @@ public interface HuskSync {
|
||||
*/
|
||||
InputStream getResource(@NotNull String name);
|
||||
|
||||
/**
|
||||
* Returns the plugin data folder
|
||||
*
|
||||
* @return the plugin data folder as a {@link File}
|
||||
*/
|
||||
@NotNull
|
||||
File getDataFolder();
|
||||
|
||||
/**
|
||||
* Log a message to the console
|
||||
*
|
||||
@@ -146,10 +218,18 @@ public interface HuskSync {
|
||||
*/
|
||||
default void debug(@NotNull String message, @NotNull Throwable... throwable) {
|
||||
if (getSettings().doDebugLogging()) {
|
||||
log(Level.INFO, "[DEBUG] " + message, throwable);
|
||||
log(Level.INFO, String.format("[DEBUG] %s", message), throwable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the console user
|
||||
*
|
||||
* @return the {@link ConsoleUser}
|
||||
*/
|
||||
@NotNull
|
||||
ConsoleUser getConsole();
|
||||
|
||||
/**
|
||||
* Returns the plugin version
|
||||
*
|
||||
@@ -158,30 +238,6 @@ public interface HuskSync {
|
||||
@NotNull
|
||||
Version getPluginVersion();
|
||||
|
||||
/**
|
||||
* Returns the plugin data folder
|
||||
*
|
||||
* @return the plugin data folder as a {@link File}
|
||||
*/
|
||||
@NotNull
|
||||
File getDataFolder();
|
||||
|
||||
/**
|
||||
* Returns a future returning the latest plugin {@link Version} if the plugin is out-of-date
|
||||
*
|
||||
* @return a {@link CompletableFuture} returning the latest {@link Version} if the current one is out-of-date
|
||||
*/
|
||||
default CompletableFuture<Optional<Version>> getLatestVersionIfOutdated() {
|
||||
return UpdateChecker.builder()
|
||||
.currentVersion(getPluginVersion())
|
||||
.endpoint(UpdateChecker.Endpoint.SPIGOT)
|
||||
.resource(Integer.toString(SPIGOT_RESOURCE_ID)).build()
|
||||
.check()
|
||||
.thenApply(checked -> checked.isUpToDate()
|
||||
? Optional.empty()
|
||||
: Optional.of(checked.getLatestVersion()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Minecraft version implementation
|
||||
*
|
||||
@@ -191,12 +247,95 @@ public interface HuskSync {
|
||||
Version getMinecraftVersion();
|
||||
|
||||
/**
|
||||
* Reloads the {@link Settings} and {@link Locales} from their respective config files
|
||||
* Returns the platform type
|
||||
*
|
||||
* @return a {@link CompletableFuture} that will be completed when the plugin reload is complete and if it was successful
|
||||
* @return the platform type
|
||||
*/
|
||||
CompletableFuture<Boolean> reload();
|
||||
@NotNull
|
||||
String getPlatformType();
|
||||
|
||||
/**
|
||||
* Returns the legacy data converter, if it exists
|
||||
*
|
||||
* @return the {@link LegacyConverter}
|
||||
*/
|
||||
Optional<LegacyConverter> getLegacyConverter();
|
||||
|
||||
/**
|
||||
* Reloads the {@link Settings} and {@link Locales} from their respective config files.
|
||||
*/
|
||||
default void loadConfigs() {
|
||||
try {
|
||||
// Load settings
|
||||
setSettings(Annotaml.create(new File(getDataFolder(), "config.yml"), Settings.class).get());
|
||||
|
||||
// Load locales from language preset default
|
||||
final Locales languagePresets = Annotaml.create(
|
||||
Locales.class,
|
||||
Objects.requireNonNull(getResource(String.format("locales/%s.yml", getSettings().getLanguage())))
|
||||
).get();
|
||||
setLocales(Annotaml.create(new File(
|
||||
getDataFolder(),
|
||||
String.format("messages_%s.yml", getSettings().getLanguage())
|
||||
), languagePresets).get());
|
||||
} catch (IOException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
|
||||
throw new FailedToLoadException("Failed to load config or message files", e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
default UpdateChecker getUpdateChecker() {
|
||||
return UpdateChecker.builder()
|
||||
.currentVersion(getPluginVersion())
|
||||
.endpoint(UpdateChecker.Endpoint.SPIGOT)
|
||||
.resource(Integer.toString(SPIGOT_RESOURCE_ID))
|
||||
.build();
|
||||
}
|
||||
|
||||
default void checkForUpdates() {
|
||||
if (getSettings().doCheckForUpdates()) {
|
||||
getUpdateChecker().check().thenAccept(checked -> {
|
||||
if (!checked.isUpToDate()) {
|
||||
log(Level.WARNING, String.format(
|
||||
"A new version of HuskSync is available: v%s (running v%s)",
|
||||
checked.getLatestVersion(), getPluginVersion())
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
Set<UUID> getLockedPlayers();
|
||||
|
||||
@NotNull
|
||||
Gson getGson();
|
||||
|
||||
@NotNull
|
||||
default Gson createGson() {
|
||||
return Converters.registerOffsetDateTime(new GsonBuilder()).create();
|
||||
}
|
||||
|
||||
/**
|
||||
* An exception indicating the plugin has been accessed before it has been registered.
|
||||
*/
|
||||
final class FailedToLoadException extends IllegalStateException {
|
||||
|
||||
private static final String FORMAT = """
|
||||
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):
|
||||
|
||||
1) Make sure you've entered your MySQL or MariaDB database details correctly 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-files)
|
||||
4) Check the error below for more details
|
||||
|
||||
Caused by: %s""";
|
||||
|
||||
FailedToLoadException(@NotNull String message, @NotNull Throwable cause) {
|
||||
super(String.format(FORMAT, message), cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user