Add native sync advancements without toast, announces, rewards;

feat/data-edit-commands
HarvelsX 3 years ago
parent 50af023d41
commit 3639c3c488

@ -1,16 +1,15 @@
package me.william278.husksync;
import me.william278.husksync.bukkit.util.BukkitUpdateChecker;
import me.william278.husksync.bukkit.util.PlayerSetter;
import me.william278.husksync.bukkit.config.ConfigLoader;
import me.william278.husksync.bukkit.data.BukkitDataCache;
import me.william278.husksync.bukkit.listener.BukkitRedisListener;
import me.william278.husksync.bukkit.listener.BukkitEventListener;
import me.william278.husksync.bukkit.listener.BukkitRedisListener;
import me.william278.husksync.bukkit.util.BukkitUpdateChecker;
import me.william278.husksync.bukkit.util.PlayerSetter;
import me.william278.husksync.redis.RedisMessage;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitTask;

@ -24,6 +24,9 @@ public class ConfigLoader {
Settings.syncAdvancements = config.getBoolean("synchronisation_settings.advancements", true);
Settings.syncLocation = config.getBoolean("synchronisation_settings.location", false);
Settings.syncFlight = config.getBoolean("synchronisation_settings.flight", false);
// Future
Settings.useNativeImplementation = config.getBoolean("useNativeImplementation", false);
}
}

@ -6,6 +6,7 @@ import me.william278.husksync.Settings;
import me.william278.husksync.api.events.SyncCompleteEvent;
import me.william278.husksync.api.events.SyncEvent;
import me.william278.husksync.bukkit.data.DataSerializer;
import me.william278.husksync.bukkit.util.nms.AdvancementUtils;
import me.william278.husksync.redis.RedisMessage;
import org.bukkit.*;
import org.bukkit.advancement.Advancement;
@ -19,10 +20,9 @@ import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Objects;
import java.util.UUID;
import java.time.Instant;
import java.time.Period;
import java.util.*;
import java.util.logging.Level;
public class PlayerSetter {
@ -160,7 +160,25 @@ public class PlayerSetter {
// Set the player's data from the PlayerData
try {
if (Settings.syncAdvancements) {
setPlayerAdvancements(player, DataSerializer.deserializeAdvancementData(data.getSerializedAdvancements()), data);
ArrayList<DataSerializer.AdvancementRecord> advancementRecords
= DataSerializer.deserializeAdvancementData(data.getSerializedAdvancements());
if (Settings.useNativeImplementation) {
try {
nativeSyncPlayerAdvancements(player, advancementRecords);
} catch (Exception e) {
plugin.getLogger().log(Level.WARNING,
"Your server does not support a native implementation of achievements synchronization");
plugin.getLogger().log(Level.WARNING,
"Your server version {0}. Please disable using native implementation!", Bukkit.getVersion());
Settings.useNativeImplementation = false;
setPlayerAdvancements(player, advancementRecords, data);
plugin.getLogger().fine(e.toString());
e.printStackTrace();
}
}
else setPlayerAdvancements(player, advancementRecords, data);
}
if (Settings.syncInventories) {
setPlayerInventory(player, DataSerializer.deserializeInventory(data.getSerializedInventory()));
@ -260,6 +278,47 @@ public class PlayerSetter {
}
}
private static void nativeSyncPlayerAdvancements(final Player player, final List<DataSerializer.AdvancementRecord> advancementRecords) {
final Object playerAdvancements = AdvancementUtils.getPlayerAdvancements(player);
// Clear
AdvancementUtils.clearPlayerAdvancementsMap(playerAdvancements);
final Map<Object, List<String>> syncAdvancementMap = new HashMap<>();
advancementRecords.forEach(advancementRecord -> {
NamespacedKey namespacedKey = Objects.requireNonNull(
NamespacedKey.fromString(advancementRecord.advancementKey()),
"Invalid Namespaced key of " + advancementRecord.advancementKey()
);
Advancement bukkitAdvancement = Bukkit.getAdvancement(namespacedKey);
if (bukkitAdvancement == null) {
// todo: write logging
return;
}
syncAdvancementMap.put(
AdvancementUtils.getHandle(bukkitAdvancement),
advancementRecord.awardedAdvancementCriteria()
);
});
// todo: sync date of get advancement
Date date = Date.from(Instant.now().minus(Period.ofWeeks(1)));
syncAdvancementMap.forEach((advancement, criteriaList) -> {
Map<String, Object> nativeCriteriaMap = new HashMap<>();
criteriaList.forEach(criteria ->
nativeCriteriaMap.put(criteria, AdvancementUtils.newCriterionProgress(date))
);
Object nativeAdvancementProgress = AdvancementUtils.newAdvancementProgress(nativeCriteriaMap);
AdvancementUtils.startProgress(playerAdvancements, advancement, nativeAdvancementProgress);
});
AdvancementUtils.ensureAllVisible(playerAdvancements);
}
/**
* Update a player's advancements and progress to match the advancementData
*

@ -0,0 +1,121 @@
package me.william278.husksync.bukkit.util.nms;
import me.william278.husksync.util.ThrowSupplier;
import org.bukkit.advancement.Advancement;
import org.bukkit.entity.Player;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;
public class AdvancementUtils {
private final static Field PLAYER_ADVANCEMENTS_MAP;
private final static Field PLAYER_ADVANCEMENTS;
private final static Field CRITERIA_MAP;
private final static Field CRITERIA_DATE;
private final static Method GET_HANDLE;
private final static Method START_PROGRESS;
private final static Method ENSURE_ALL_VISIBLE;
private final static Class<?> ADVANCEMENT_PROGRESS;
private final static Class<?> CRITERION_PROGRESS;
static {
Class<?> SERVER_PLAYER = MinecraftVersionUtils.getMinecraftClass("level.EntityPlayer");
PLAYER_ADVANCEMENTS = ThrowSupplier.get(() -> SERVER_PLAYER.getDeclaredField("cs"));
PLAYER_ADVANCEMENTS.setAccessible(true);
Class<?> CRAFT_ADVANCEMENT = MinecraftVersionUtils.getBukkitClass("advancement.CraftAdvancement");
GET_HANDLE = ThrowSupplier.get(() -> CRAFT_ADVANCEMENT.getDeclaredMethod("getHandle"));
ADVANCEMENT_PROGRESS = ThrowSupplier.get(() -> Class.forName("net.minecraft.advancements.AdvancementProgress"));
CRITERIA_MAP = ThrowSupplier.get(() -> ADVANCEMENT_PROGRESS.getDeclaredField("a"));
CRITERIA_MAP.setAccessible(true);
CRITERION_PROGRESS = ThrowSupplier.get(() -> Class.forName("net.minecraft.advancements.CriterionProgress"));
CRITERIA_DATE = ThrowSupplier.get(() -> CRITERION_PROGRESS.getDeclaredField("b"));
CRITERIA_DATE.setAccessible(true);
Class<?> ADVANCEMENT = ThrowSupplier.get(() -> Class.forName("net.minecraft.advancements.Advancement"));
Class<?> PLAYER_ADVANCEMENTS = MinecraftVersionUtils.getMinecraftClass("AdvancementDataPlayer");
PLAYER_ADVANCEMENTS_MAP = ThrowSupplier.get(() -> PLAYER_ADVANCEMENTS.getDeclaredField("h"));
PLAYER_ADVANCEMENTS_MAP.setAccessible(true);
START_PROGRESS = ThrowSupplier.get(() -> PLAYER_ADVANCEMENTS.getDeclaredMethod("a", ADVANCEMENT, ADVANCEMENT_PROGRESS));
START_PROGRESS.setAccessible(true);
ENSURE_ALL_VISIBLE = ThrowSupplier.get(() -> PLAYER_ADVANCEMENTS.getDeclaredMethod("c"));
ENSURE_ALL_VISIBLE.setAccessible(true);
}
public static Object getPlayerAdvancements (Player player) {
Object nativePlayer = EntityUtils.getHandle(player);
try {
return PLAYER_ADVANCEMENTS.get(nativePlayer);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public static void clearPlayerAdvancementsMap (final Object playerAdvancement) {
try {
((Map<?,?>) PLAYER_ADVANCEMENTS_MAP.get(playerAdvancement))
.clear();
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public static Object getHandle (Advancement advancement) {
try {
return GET_HANDLE.invoke(advancement);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public static Object newCriterionProgress (final Date date) {
try {
Object nativeCriterionProgress = CRITERION_PROGRESS.getDeclaredConstructor().newInstance();
CRITERIA_DATE.set(nativeCriterionProgress, date);
return nativeCriterionProgress;
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public static Object newAdvancementProgress (final Map<String, Object> criteria) {
try {
Object nativeAdvancementProgress = ADVANCEMENT_PROGRESS.getDeclaredConstructor().newInstance();
final Map<String, Object> criteriaMap = (Map<String, Object>) CRITERIA_MAP.get(nativeAdvancementProgress);
criteriaMap.putAll(criteria);
return nativeAdvancementProgress;
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public static void startProgress (final Object playerAdvancements, final Object advancement, final Object advancementProgress) {
try {
START_PROGRESS.invoke(playerAdvancements, advancement, advancementProgress);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public static void ensureAllVisible (final Object playerAdvancements) {
try {
ENSURE_ALL_VISIBLE.invoke(playerAdvancements);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}

@ -0,0 +1,26 @@
package me.william278.husksync.bukkit.util.nms;
import me.william278.husksync.util.ThrowSupplier;
import org.bukkit.entity.LivingEntity;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class EntityUtils {
private final static Method GET_HANDLE;
static {
final Class<?> CRAFT_ENTITY = MinecraftVersionUtils.getBukkitClass("entity.CraftEntity");
GET_HANDLE = ThrowSupplier.get(() -> CRAFT_ENTITY.getDeclaredMethod("getHandle"));
}
public static Object getHandle (LivingEntity livingEntity) throws RuntimeException {
try {
return GET_HANDLE.invoke(livingEntity);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}

@ -0,0 +1,42 @@
package me.william278.husksync.bukkit.util.nms;
import me.william278.husksync.util.ThrowSupplier;
import org.bukkit.Bukkit;
public class MinecraftVersionUtils {
public final static String CRAFTBUKKIT_PACKAGE_PATH = Bukkit.getServer().getClass().getPackage().getName();
public final static String PACKAGE_VERSION = CRAFTBUKKIT_PACKAGE_PATH.split("\\.")[3];
public final static String SERVER_VERSION = Bukkit.getBukkitVersion().split("-")[0];
public final static String MINECRAFT_PACKAGE = compare("1.17") < 0 ?
"net.minecraft.server.".concat(PACKAGE_VERSION) : "net.minecraft.server";
public static int compare(String version) {
if(version == null) return 1;
String[] as = SERVER_VERSION.split("\\.");
String[] bs = version.split("\\.");
int length = Math.max(as.length, bs.length);
for(int i = 0; i < length; i++) {
int a = i < as.length ? Integer.parseInt(as[i]) : 0;
int b = i < bs.length ? Integer.parseInt(bs[i]) : 0;
if(a < b) return -1;
if(a > b) return 1;
}
return 0;
}
public static Class<?> getBukkitClass(String path) {
return ThrowSupplier.get(() -> Class.forName(CRAFTBUKKIT_PACKAGE_PATH.concat(".").concat(path)));
}
public static Class<?> getMinecraftClass(String path) {
return ThrowSupplier.get(() -> Class.forName(MINECRAFT_PACKAGE.concat(".").concat(path)));
}
}

@ -67,6 +67,9 @@ public class Settings {
public static boolean syncLocation;
public static boolean syncFlight;
// Future
public static boolean useNativeImplementation;
// This Cluster ID
public static String cluster;

@ -0,0 +1,13 @@
package me.william278.husksync.util;
public interface ThrowSupplier<T> {
T get() throws Exception;
static <A> A get(ThrowSupplier<A> supplier) {
try {
return supplier.get();
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
Loading…
Cancel
Save