9
0
mirror of https://github.com/WiIIiam278/HuskSync.git synced 2025-12-24 17:19:19 +00:00

Compare commits

...

8 Commits

Author SHA1 Message Date
William
8e5b794b6d Still fire synccomplete for dead player syncs 2022-07-04 10:26:10 +01:00
William
649b7c0857 Bump to 1.4.1 2022-07-04 09:58:16 +01:00
William
75a9050c39 Merge remote-tracking branch 'origin/master' 2022-07-04 09:57:16 +01:00
William
7d38b9941e Don't sync now-dead players 2022-07-04 09:57:12 +01:00
William
b3e4fbb3de Update a few jitpack links to use net.william278 2022-06-09 12:22:31 +01:00
William
633847a254 Fix inventory clearing on world save 2022-06-08 11:17:53 +01:00
William
3cd144088d Support deserializing old package stuff 2022-06-08 11:11:51 +01:00
William
e312e8dd01 Fix hikari shading 2022-06-08 01:47:13 +01:00
10 changed files with 115 additions and 86 deletions

View File

@@ -121,7 +121,7 @@ Most likely not - and I cannot support it - but feel free to test it, as dependi
### API
HuskSync has an API for Bukkit providing events that fire when synchronisation takes place as well as a method to access and deserialize player data on demand. There is no API for the proxy side currently.
HuskSync's API is available on [JitPack](https://jitpack.io/#WiIIiam278/HuskSync/Tag). You can view the [HuskSync JavaDocs here](https://javadoc.jitpack.io/com/github/WiIIiam278/HuskSync/latest/javadoc/index.html). You should only use stuff in the `husksync.bukkit.api` and `husksync.bukkit.data` packages (as well as the PlayerData class located in the `husksync` root package.
HuskSync's API is available on [JitPack](https://jitpack.io/#net.william278/HuskSync/Tag). You can view the [HuskSync JavaDocs here](https://javadoc.jitpack.io/net/william278/HuskSync/latest/javadoc/index.html). You should only use stuff in the `husksync.bukkit.api` and `husksync.bukkit.data` packages (as well as the PlayerData class located in the `husksync` root package.
#### Including the API in your project
With Maven, add the repository to your pom.xml:
@@ -133,7 +133,7 @@ With Maven, add the repository to your pom.xml:
</repository>
</repositories>
```
Then, add the dependency. Replace `version` with the latest version of HuskSync: [![](https://jitpack.io/v/WiIIiam278/HuskSync.svg)](https://jitpack.io/#WiIIiam278/HuskSync)
Then, add the dependency. Replace `version` with the latest version of HuskSync: [![](https://jitpack.io/v/WiIIiam278/HuskSync.svg)](https://jitpack.io/#net.william278/HuskSync)
```xml
<dependency>
<groupId>net.william278</groupId>

View File

@@ -0,0 +1,57 @@
package me.william278.husksync.bukkit.data;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.World;
import org.bukkit.entity.EntityType;
import java.io.Serializable;
import java.time.Instant;
import java.util.*;
/**
* Holds legacy data store methods for data storage
*/
@Deprecated
@SuppressWarnings("DeprecatedIsStillUsed")
public class DataSerializer {
/**
* A record used to store data for advancement synchronisation
*
* @deprecated Old format - Use {@link AdvancementRecordDate} instead
*/
@Deprecated
@SuppressWarnings("DeprecatedIsStillUsed")
// Suppress deprecation warnings here (still used for backwards compatibility)
public record AdvancementRecord(String advancementKey,
ArrayList<String> awardedAdvancementCriteria) implements Serializable {
}
/**
* A record used to store data for a player's statistics
*/
public record StatisticData(HashMap<Statistic, Integer> untypedStatisticValues,
HashMap<Statistic, HashMap<Material, Integer>> blockStatisticValues,
HashMap<Statistic, HashMap<Material, Integer>> itemStatisticValues,
HashMap<Statistic, HashMap<EntityType, Integer>> entityStatisticValues) implements Serializable {
}
/**
* A record used to store data for native advancement synchronisation, tracking advancement date progress
*/
public record AdvancementRecordDate(String key, Map<String, Date> criteriaMap) implements Serializable {
public AdvancementRecordDate(String key, List<String> criteriaList) {
this(key, new HashMap<>() {{
criteriaList.forEach(s -> put(s, Date.from(Instant.EPOCH)));
}});
}
}
/**
* A record used to store data for a player's location
*/
public record PlayerLocation(double x, double y, double z, float yaw, float pitch,
String worldName, World.Environment environment) implements Serializable {
}
}

View File

@@ -145,6 +145,10 @@ public final class HuskSyncBukkit extends JavaPlugin {
getLogger().info("Saving data for remaining online players...");
for (Player player : Bukkit.getOnlinePlayers()) {
PlayerSetter.updatePlayerData(player, false);
// Clear player inventory and ender chest
player.getInventory().clear();
player.getEnderChest().clear();
}
getLogger().info("Data save complete!");
}

View File

@@ -15,8 +15,6 @@ import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.time.Instant;
import java.util.*;
/**
@@ -194,12 +192,12 @@ public class DataSerializer {
return serializedPotionEffect != null ? new PotionEffect((Map<String, Object>) serializedPotionEffect) : null;
}
public static DataSerializer.PlayerLocation deserializePlayerLocationData(String serializedLocationData) throws IOException {
public static me.william278.husksync.bukkit.data.DataSerializer.PlayerLocation deserializePlayerLocationData(String serializedLocationData) throws IOException {
if (serializedLocationData.isEmpty()) {
return null;
}
try {
return (DataSerializer.PlayerLocation) RedisMessage.deserialize(serializedLocationData);
return (me.william278.husksync.bukkit.data.DataSerializer.PlayerLocation) RedisMessage.deserialize(serializedLocationData);
} catch (ClassNotFoundException e) {
throw new IOException("Unable to decode class type.", e);
}
@@ -207,19 +205,19 @@ public class DataSerializer {
public static String getSerializedLocation(Player player) throws IOException {
final Location playerLocation = player.getLocation();
return RedisMessage.serialize(new DataSerializer.PlayerLocation(playerLocation.getX(), playerLocation.getY(), playerLocation.getZ(),
return RedisMessage.serialize(new me.william278.husksync.bukkit.data.DataSerializer.PlayerLocation(playerLocation.getX(), playerLocation.getY(), playerLocation.getZ(),
playerLocation.getYaw(), playerLocation.getPitch(), player.getWorld().getName(), player.getWorld().getEnvironment()));
}
/**
* Deserializes a player's advancement data as serialized with {@link #getSerializedAdvancements(Player)} into {@link AdvancementRecordDate} data.
* Deserializes a player's advancement data as serialized with {@link #getSerializedAdvancements(Player)} into {@link me.william278.husksync.bukkit.data.DataSerializer.AdvancementRecordDate} data.
*
* @param serializedAdvancementData The serialized advancement data {@link String}
* @return The deserialized {@link AdvancementRecordDate} for the player
* @return The deserialized {@link me.william278.husksync.bukkit.data.DataSerializer.AdvancementRecordDate} for the player
* @throws IOException If the deserialization fails
*/
@SuppressWarnings("unchecked") // Ignore the unchecked cast here
public static List<DataSerializer.AdvancementRecordDate> deserializeAdvancementData(String serializedAdvancementData) throws IOException {
public static List<me.william278.husksync.bukkit.data.DataSerializer.AdvancementRecordDate> deserializeAdvancementData(String serializedAdvancementData) throws IOException {
if (serializedAdvancementData.isEmpty()) {
return new ArrayList<>();
}
@@ -227,15 +225,15 @@ public class DataSerializer {
List<?> deserialize = (List<?>) RedisMessage.deserialize(serializedAdvancementData);
// Migrate old AdvancementRecord into date format
if (!deserialize.isEmpty() && deserialize.get(0) instanceof AdvancementRecord) {
deserialize = ((List<AdvancementRecord>) deserialize).stream()
.map(o -> new AdvancementRecordDate(
o.advancementKey,
o.awardedAdvancementCriteria
if (!deserialize.isEmpty() && deserialize.get(0) instanceof me.william278.husksync.bukkit.data.DataSerializer.AdvancementRecord) {
deserialize = ((List<me.william278.husksync.bukkit.data.DataSerializer.AdvancementRecord>) deserialize).stream()
.map(o -> new me.william278.husksync.bukkit.data.DataSerializer.AdvancementRecordDate(
o.advancementKey(),
o.awardedAdvancementCriteria()
)).toList();
}
return (List<AdvancementRecordDate>) deserialize;
return (List<me.william278.husksync.bukkit.data.DataSerializer.AdvancementRecordDate>) deserialize;
} catch (ClassNotFoundException e) {
throw new IOException("Unable to decode class type.", e);
}
@@ -250,7 +248,7 @@ public class DataSerializer {
*/
public static String getSerializedAdvancements(Player player) throws IOException {
Iterator<Advancement> serverAdvancements = Bukkit.getServer().advancementIterator();
ArrayList<DataSerializer.AdvancementRecordDate> advancementData = new ArrayList<>();
ArrayList<me.william278.husksync.bukkit.data.DataSerializer.AdvancementRecordDate> advancementData = new ArrayList<>();
while (serverAdvancements.hasNext()) {
final AdvancementProgress progress = player.getAdvancementProgress(serverAdvancements.next());
@@ -259,25 +257,25 @@ public class DataSerializer {
final Map<String, Date> awardedCriteria = new HashMap<>();
progress.getAwardedCriteria().forEach(s -> awardedCriteria.put(s, progress.getDateAwarded(s)));
advancementData.add(new DataSerializer.AdvancementRecordDate(advancementKey.getNamespace() + ":" + advancementKey.getKey(), awardedCriteria));
advancementData.add(new me.william278.husksync.bukkit.data.DataSerializer.AdvancementRecordDate(advancementKey.getNamespace() + ":" + advancementKey.getKey(), awardedCriteria));
}
return RedisMessage.serialize(advancementData);
}
/**
* Deserializes a player's statistic data as serialized with {@link #getSerializedStatisticData(Player)} into {@link StatisticData}.
* Deserializes a player's statistic data as serialized with {@link #getSerializedStatisticData(Player)} into {@link me.william278.husksync.bukkit.data.DataSerializer.StatisticData}.
*
* @param serializedStatisticData The serialized statistic data {@link String}
* @return The deserialized {@link StatisticData} for the player
* @return The deserialized {@link me.william278.husksync.bukkit.data.DataSerializer.StatisticData} for the player
* @throws IOException If the deserialization fails
*/
public static DataSerializer.StatisticData deserializeStatisticData(String serializedStatisticData) throws IOException {
public static me.william278.husksync.bukkit.data.DataSerializer.StatisticData deserializeStatisticData(String serializedStatisticData) throws IOException {
if (serializedStatisticData.isEmpty()) {
return new DataSerializer.StatisticData(new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>());
return new me.william278.husksync.bukkit.data.DataSerializer.StatisticData(new HashMap<>(), new HashMap<>(), new HashMap<>(), new HashMap<>());
}
try {
return (DataSerializer.StatisticData) RedisMessage.deserialize(serializedStatisticData);
return (me.william278.husksync.bukkit.data.DataSerializer.StatisticData) RedisMessage.deserialize(serializedStatisticData);
} catch (ClassNotFoundException e) {
throw new IOException("Unable to decode class type.", e);
}
@@ -322,46 +320,8 @@ public class DataSerializer {
}
}
DataSerializer.StatisticData statisticData = new DataSerializer.StatisticData(untypedStatisticValues, blockStatisticValues, itemStatisticValues, entityStatisticValues);
me.william278.husksync.bukkit.data.DataSerializer.StatisticData statisticData = new me.william278.husksync.bukkit.data.DataSerializer.StatisticData(untypedStatisticValues, blockStatisticValues, itemStatisticValues, entityStatisticValues);
return RedisMessage.serialize(statisticData);
}
/**
* A record used to store data for a player's location
*/
public record PlayerLocation(double x, double y, double z, float yaw, float pitch,
String worldName, World.Environment environment) implements Serializable {
}
/**
* A record used to store data for advancement synchronisation
*
* @deprecated Old format - Use {@link AdvancementRecordDate} instead
*/
@Deprecated
@SuppressWarnings("DeprecatedIsStillUsed") // Suppress deprecation warnings here (still used for backwards compatibility)
public record AdvancementRecord(String advancementKey,
ArrayList<String> awardedAdvancementCriteria) implements Serializable {
}
/**
* A record used to store data for native advancement synchronisation, tracking advancement date progress
*/
public record AdvancementRecordDate(String key, Map<String, Date> criteriaMap) implements Serializable {
AdvancementRecordDate(String key, List<String> criteriaList) {
this(key, new HashMap<>() {{
criteriaList.forEach(s -> put(s, Date.from(Instant.EPOCH)));
}});
}
}
/**
* A record used to store data for a player's statistics
*/
public record StatisticData(HashMap<Statistic, Integer> untypedStatisticValues,
HashMap<Statistic, HashMap<Material, Integer>> blockStatisticValues,
HashMap<Statistic, HashMap<Material, Integer>> itemStatisticValues,
HashMap<Statistic, HashMap<EntityType, Integer>> entityStatisticValues) implements Serializable {
}
}

View File

@@ -39,7 +39,14 @@ public class BukkitEventListener implements Listener {
return; // If the plugin has not been initialized correctly
// Update the player's data
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> PlayerSetter.updatePlayerData(player, true));
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
// Update data to proxy
PlayerSetter.updatePlayerData(player, true);
// Clear player inventory and ender chest
player.getInventory().clear();
player.getEnderChest().clear();
});
}
@EventHandler(priority = EventPriority.LOWEST)

View File

@@ -108,10 +108,6 @@ public class PlayerSetter {
} catch (IOException e) {
plugin.getLogger().log(Level.SEVERE, "Failed to send a PlayerData update to the proxy", e);
}
// Clear player inventory and ender chest
player.getInventory().clear();
player.getEnderChest().clear();
}
/**
@@ -158,8 +154,12 @@ public class PlayerSetter {
// Set the player's data from the PlayerData
try {
// Don't sync the player if they are dead
if (player.isDead() || player.getHealth() <= 0) {
return;
}
if (Settings.syncAdvancements) {
List<DataSerializer.AdvancementRecordDate> advancementRecords
List<me.william278.husksync.bukkit.data.DataSerializer.AdvancementRecordDate> advancementRecords
= DataSerializer.deserializeAdvancementData(data.getSerializedAdvancements());
if (Settings.useNativeImplementation) {
@@ -179,6 +179,11 @@ public class PlayerSetter {
setPlayerAdvancements(player, advancementRecords, data);
}
}
// Don't sync the player if they are dead
if (player.isDead() || player.getHealth() <= 0) {
Bukkit.getPluginManager().callEvent(new SyncCompleteEvent(player, data));
return;
}
if (Settings.syncInventories) {
setPlayerInventory(player, DataSerializer.deserializeInventory(data.getSerializedInventory()));
player.getInventory().setHeldItemSlot(data.getSelectedSlot());
@@ -277,7 +282,7 @@ public class PlayerSetter {
}
}
private static void nativeSyncPlayerAdvancements(final Player player, final List<DataSerializer.AdvancementRecordDate> advancementRecords) {
private static void nativeSyncPlayerAdvancements(final Player player, final List<me.william278.husksync.bukkit.data.DataSerializer.AdvancementRecordDate> advancementRecords) {
final Object playerAdvancements = AdvancementUtils.getPlayerAdvancements(player);
// Clear
@@ -316,9 +321,9 @@ public class PlayerSetter {
* Update a player's advancements and progress to match the advancementData
*
* @param player The player to set the advancements of
* @param advancementData The ArrayList of {@link DataSerializer.AdvancementRecordDate}s to set
* @param advancementData The ArrayList of {@link me.william278.husksync.bukkit.data.DataSerializer.AdvancementRecordDate}s to set
*/
private static void setPlayerAdvancements(Player player, List<DataSerializer.AdvancementRecordDate> advancementData, PlayerData data) {
private static void setPlayerAdvancements(Player player, List<me.william278.husksync.bukkit.data.DataSerializer.AdvancementRecordDate> advancementData, PlayerData data) {
// Temporarily disable advancement announcing if needed
boolean announceAdvancementUpdate = false;
if (Boolean.TRUE.equals(player.getWorld().getGameRuleValue(GameRule.ANNOUNCE_ADVANCEMENTS))) {
@@ -336,7 +341,7 @@ public class PlayerSetter {
boolean correctExperienceCheck = false; // Determines whether the experience might have changed warranting an update
Advancement advancement = serverAdvancements.next();
AdvancementProgress playerProgress = player.getAdvancementProgress(advancement);
for (DataSerializer.AdvancementRecordDate record : advancementData) {
for (me.william278.husksync.bukkit.data.DataSerializer.AdvancementRecordDate record : advancementData) {
// If the advancement is one on the data
if (record.key().equals(advancement.getKey().getNamespace() + ":" + advancement.getKey().getKey())) {
@@ -379,9 +384,9 @@ public class PlayerSetter {
* Set a player's statistics (in the Statistic menu)
*
* @param player The player to set the statistics of
* @param statisticData The {@link DataSerializer.StatisticData} to set
* @param statisticData The {@link me.william278.husksync.bukkit.data.DataSerializer.StatisticData} to set
*/
private static void setPlayerStatistics(Player player, DataSerializer.StatisticData statisticData) {
private static void setPlayerStatistics(Player player, me.william278.husksync.bukkit.data.DataSerializer.StatisticData statisticData) {
// Set untyped statistics
for (Statistic statistic : statisticData.untypedStatisticValues().keySet()) {
player.setStatistic(statistic, statisticData.untypedStatisticValues().get(statistic));
@@ -422,12 +427,12 @@ public class PlayerSetter {
}
/**
* Set a player's location from {@link DataSerializer.PlayerLocation} data
* Set a player's location from {@link me.william278.husksync.bukkit.data.DataSerializer.PlayerLocation} data
*
* @param player The {@link Player} to teleport
* @param location The {@link DataSerializer.PlayerLocation}
* @param location The {@link me.william278.husksync.bukkit.data.DataSerializer.PlayerLocation}
*/
private static void setPlayerLocation(Player player, DataSerializer.PlayerLocation location) {
private static void setPlayerLocation(Player player, me.william278.husksync.bukkit.data.DataSerializer.PlayerLocation location) {
// Don't teleport if the location is invalid
if (location == null) {
return;

View File

@@ -15,7 +15,6 @@ shadowJar {
relocate 'org.bstats', 'net.william278.husksync.libraries.bstats'
relocate 'redis.clients', 'net.william278.husksync.libraries'
relocate 'org.apache', 'net.william278.husksync.libraries'
relocate 'com.zaxxer', 'net.william278.husksync.libraries'
dependencies {
//noinspection GroovyAssignabilityCheck

View File

@@ -1,7 +1,9 @@
package net.william278.husksync;
import net.william278.husksync.Server;
import net.william278.husksync.Settings;
import net.byteflux.libby.BungeeLibraryManager;
import net.byteflux.libby.Library;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.plugin.Plugin;
import net.william278.husksync.bungeecord.command.BungeeCommand;
import net.william278.husksync.bungeecord.config.ConfigLoader;
import net.william278.husksync.bungeecord.config.ConfigManager;
@@ -13,10 +15,6 @@ import net.william278.husksync.migrator.MPDBMigrator;
import net.william278.husksync.proxy.data.DataManager;
import net.william278.husksync.redis.RedisMessage;
import net.william278.husksync.util.Logger;
import net.byteflux.libby.BungeeLibraryManager;
import net.byteflux.libby.Library;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.plugin.Plugin;
import org.bstats.bungeecord.Metrics;
import java.io.IOException;

View File

@@ -1,3 +1,3 @@
javaVersion=16
plugin_version=1.4
plugin_version=1.4.1
plugin_archive=husksync

View File

@@ -15,7 +15,6 @@ shadowJar {
relocate 'org.bstats', 'net.william278.husksync.libraries.bstats'
relocate 'redis.clients', 'net.william278.husksync.libraries'
relocate 'org.apache', 'net.william278.husksync.libraries'
relocate 'com.zaxxer', 'net.william278.husksync.libraries'
dependencies {
//noinspection GroovyAssignabilityCheck