mirror of
https://github.com/WiIIiam278/HuskSync.git
synced 2025-12-24 00:59:18 +00:00
Start work on Plan integration
This commit is contained in:
@@ -19,6 +19,7 @@ import net.william278.husksync.database.MySqlDatabase;
|
||||
import net.william278.husksync.editor.DataEditor;
|
||||
import net.william278.husksync.event.BukkitEventCannon;
|
||||
import net.william278.husksync.event.EventCannon;
|
||||
import net.william278.husksync.hook.PlanHook;
|
||||
import net.william278.husksync.listener.BukkitEventListener;
|
||||
import net.william278.husksync.listener.EventListener;
|
||||
import net.william278.husksync.migrator.LegacyMigrator;
|
||||
@@ -186,6 +187,13 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync {
|
||||
getLoggingAdapter().log(Level.INFO, "Successfully registered permissions & commands");
|
||||
}
|
||||
return succeeded;
|
||||
}).thenApply(succeeded -> {
|
||||
if (succeeded && Bukkit.getPluginManager().getPlugin("Plan") != null) {
|
||||
getLoggingAdapter().log(Level.INFO, "Enabling Plan integration...");
|
||||
new PlanHook(database, logger).hookIntoPlan();
|
||||
getLoggingAdapter().log(Level.INFO, "Plan integration enabled!");
|
||||
}
|
||||
return succeeded;
|
||||
}).thenApply(succeeded -> {
|
||||
// Check for updates
|
||||
if (succeeded && settings.getBooleanValue(Settings.ConfigOption.CHECK_FOR_UPDATES)) {
|
||||
|
||||
@@ -5,7 +5,9 @@ api-version: 1.16
|
||||
author: William278
|
||||
description: 'A modern, cross-server player data synchronization system'
|
||||
website: 'https://william278.net'
|
||||
softdepend: [ MysqlPlayerDataBridge ]
|
||||
softdepend:
|
||||
- MysqlPlayerDataBridge
|
||||
- Plan
|
||||
libraries:
|
||||
- 'mysql:mysql-connector-java:8.0.29'
|
||||
- 'org.xerial.snappy:snappy-java:1.1.8.4'
|
||||
|
||||
@@ -12,6 +12,11 @@ dependencies {
|
||||
compileOnly 'dev.dejvokep:boosted-yaml:1.2'
|
||||
compileOnly 'org.xerial.snappy:snappy-java:1.1.8.4'
|
||||
compileOnly 'org.jetbrains:annotations:23.0.0'
|
||||
compileOnly 'com.github.plan-player-analytics:Plan:5.4.1690'
|
||||
|
||||
testImplementation 'org.xerial.snappy:snappy-java:1.1.8.4'
|
||||
testImplementation 'com.github.plan-player-analytics:Plan:5.4.1690'
|
||||
testCompileOnly 'org.jetbrains:annotations:23.0.0'
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package net.william278.husksync.hook;
|
||||
|
||||
import com.djrapitops.plan.extension.CallEvents;
|
||||
import com.djrapitops.plan.extension.DataExtension;
|
||||
import com.djrapitops.plan.extension.FormatType;
|
||||
import com.djrapitops.plan.extension.annotation.NumberProvider;
|
||||
import com.djrapitops.plan.extension.annotation.PluginInfo;
|
||||
import com.djrapitops.plan.extension.annotation.StringProvider;
|
||||
import com.djrapitops.plan.extension.icon.Color;
|
||||
import com.djrapitops.plan.extension.icon.Family;
|
||||
import net.william278.husksync.data.VersionedUserData;
|
||||
import net.william278.husksync.database.Database;
|
||||
import net.william278.husksync.player.User;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@PluginInfo(
|
||||
name = "HuskSync",
|
||||
iconName = "arrow-right-arrow-left",
|
||||
iconFamily = Family.SOLID,
|
||||
color = Color.LIGHT_BLUE
|
||||
)
|
||||
public class PlanDataExtension implements DataExtension {
|
||||
|
||||
private Database database;
|
||||
|
||||
//todo add more providers
|
||||
protected PlanDataExtension(@NotNull Database database) {
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
protected PlanDataExtension() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallEvents[] callExtensionMethodsOn() {
|
||||
return new CallEvents[]{
|
||||
CallEvents.PLAYER_JOIN,
|
||||
CallEvents.PLAYER_LEAVE
|
||||
};
|
||||
}
|
||||
|
||||
private CompletableFuture<Optional<VersionedUserData>> getCurrentUserData(@NotNull UUID uuid) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
final Optional<User> optionalUser = database.getUser(uuid).join();
|
||||
if (optionalUser.isPresent()) {
|
||||
return database.getCurrentUserData(optionalUser.get()).join();
|
||||
}
|
||||
return Optional.empty();
|
||||
});
|
||||
}
|
||||
|
||||
@NumberProvider(
|
||||
text = "Data Sync Time",
|
||||
description = "The last time the user had their data synced with the server.",
|
||||
iconName = "clock",
|
||||
iconFamily = Family.SOLID,
|
||||
format = FormatType.TIME_MILLISECONDS,
|
||||
priority = 1
|
||||
)
|
||||
public long getCurrentDataTimestamp(@NotNull UUID uuid) {
|
||||
return getCurrentUserData(uuid).join().map(
|
||||
versionedUserData -> versionedUserData.versionTimestamp().getTime())
|
||||
.orElse(new Date().getTime());
|
||||
}
|
||||
|
||||
@StringProvider(
|
||||
text = "Data Version ID",
|
||||
description = "ID of the data version that the user is currently using.",
|
||||
iconName = "bolt",
|
||||
iconFamily = Family.SOLID,
|
||||
priority = 2
|
||||
)
|
||||
public String getCurrentDataId(@NotNull UUID uuid) {
|
||||
return getCurrentUserData(uuid).join().map(
|
||||
versionedUserData -> versionedUserData.versionUUID().toString()
|
||||
.split(Pattern.quote("-"))[0])
|
||||
.orElse("unknown");
|
||||
}
|
||||
|
||||
@NumberProvider(
|
||||
text = "Advancements",
|
||||
description = "The number of advancements & recipes the player has progressed in",
|
||||
iconName = "award",
|
||||
iconFamily = Family.SOLID,
|
||||
priority = 3
|
||||
)
|
||||
public long getAdvancementsCompleted(@NotNull UUID playerUUID) {
|
||||
return getCurrentUserData(playerUUID).join().map(
|
||||
versionedUserData -> (long) versionedUserData.userData().getAdvancementData().size())
|
||||
.orElse(0L);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package net.william278.husksync.hook;
|
||||
|
||||
import com.djrapitops.plan.capability.CapabilityService;
|
||||
import com.djrapitops.plan.extension.ExtensionService;
|
||||
import net.william278.husksync.database.Database;
|
||||
import net.william278.husksync.util.Logger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class PlanHook {
|
||||
|
||||
private final Database database;
|
||||
private final Logger logger;
|
||||
|
||||
public PlanHook(@NotNull Database database, @NotNull Logger logger) {
|
||||
this.database = database;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public void hookIntoPlan() {
|
||||
if (!areAllCapabilitiesAvailable()) {
|
||||
return;
|
||||
}
|
||||
registerDataExtension();
|
||||
handlePlanReload();
|
||||
}
|
||||
|
||||
private boolean areAllCapabilitiesAvailable() {
|
||||
CapabilityService capabilities = CapabilityService.getInstance();
|
||||
return capabilities.hasCapability("DATA_EXTENSION_VALUES");
|
||||
}
|
||||
|
||||
private void registerDataExtension() {
|
||||
try {
|
||||
ExtensionService.getInstance().register(new PlanDataExtension(database));
|
||||
} catch (IllegalStateException planIsNotEnabled) {
|
||||
logger.log(Level.SEVERE, "Plan extension hook failed to register. Plan is not enabled.", planIsNotEnabled);
|
||||
// Plan is not enabled, handle exception
|
||||
} catch (IllegalArgumentException dataExtensionImplementationIsInvalid) {
|
||||
logger.log(Level.SEVERE, "Plan extension hook failed to register. Data hook implementation is invalid.", dataExtensionImplementationIsInvalid);
|
||||
// The DataExtension implementation has an implementation error, handle exception
|
||||
}
|
||||
}
|
||||
|
||||
// Re-register the extension when plan enables
|
||||
private void handlePlanReload() {
|
||||
CapabilityService.getInstance().registerEnableListener(isPlanEnabled -> {
|
||||
if (isPlanEnabled) {
|
||||
registerDataExtension();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,6 +7,9 @@ import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Tests for the data system {@link DataAdapter}
|
||||
*/
|
||||
public class DataAdaptionTests {
|
||||
|
||||
@Test
|
||||
@@ -42,7 +45,7 @@ public class DataAdaptionTests {
|
||||
final DataAdapter dataAdapter = new JsonDataAdapter();
|
||||
final byte[] data = dataAdapter.toBytes(dummyUserData);
|
||||
final String json = new String(data, StandardCharsets.UTF_8);
|
||||
final String expectedJson = "{\"status\":{\"health\":20.0,\"max_health\":20.0,\"health_scale\":0.0,\"hunger\":20,\"saturation\":5.0,\"saturation_exhaustion\":5.0,\"selected_item_slot\":1,\"total_experience\":100,\"experience_level\":1,\"experience_progress\":1.0,\"game_mode\":\"SURVIVAL\",\"is_flying\":false},\"inventory\":{\"serialized_inventory\":\"\"},\"ender_chest\":{\"serialized_inventory\":\"\"},\"potion_effects\":{\"serialized_potion_effects\":\"\"},\"advancements\":[],\"statistics\":{\"untyped_statistics\":{},\"block_statistics\":{},\"item_statistics\":{},\"entity_statistics\":{}},\"location\":{\"world_name\":\"dummy_world\",\"world_uuid\":\"00000000-0000-0000-0000-000000000000\",\"world_environment\":\"NORMAL\",\"x\":0.0,\"y\":64.0,\"z\":0.0,\"yaw\":90.0,\"pitch\":180.0},\"persistent_data_container\":{\"persistent_data_map\":{}},\"format_version\":1}";
|
||||
final String expectedJson = "{\"status\":{\"health\":20.0,\"max_health\":20.0,\"health_scale\":0.0,\"hunger\":20,\"saturation\":5.0,\"saturation_exhaustion\":5.0,\"selected_item_slot\":1,\"total_experience\":100,\"experience_level\":1,\"experience_progress\":1.0,\"game_mode\":\"SURVIVAL\",\"is_flying\":false},\"inventory\":{\"serialized_items\":\"\"},\"ender_chest\":{\"serialized_items\":\"\"},\"potion_effects\":{\"serialized_potion_effects\":\"\"},\"advancements\":[],\"statistics\":{\"untyped_statistics\":{},\"block_statistics\":{},\"item_statistics\":{},\"entity_statistics\":{}},\"location\":{\"world_name\":\"dummy_world\",\"world_uuid\":\"00000000-0000-0000-0000-000000000000\",\"world_environment\":\"NORMAL\",\"x\":0.0,\"y\":64.0,\"z\":0.0,\"yaw\":90.0,\"pitch\":180.0},\"persistent_data_container\":{\"persistent_data_map\":{}},\"format_version\":1}";
|
||||
Assertions.assertEquals(expectedJson, json);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package net.william278.husksync.hook;
|
||||
|
||||
import com.djrapitops.plan.extension.extractor.ExtensionExtractor;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Tests for the {@link PlanHook} and {@link PlanDataExtension} implementation
|
||||
*/
|
||||
public class PlanHookTests {
|
||||
|
||||
/**
|
||||
* Throws IllegalArgumentException if there is an implementation error or warning.
|
||||
*/
|
||||
@Test
|
||||
public void testExtensionImplementationErrors() {
|
||||
new ExtensionExtractor(new PlanDataExtension()).validateAnnotations();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user