9
0
mirror of https://github.com/WiIIiam278/HuskSync.git synced 2025-12-27 10:39:11 +00:00

feat: add support for Fabric targeting Minecraft 1.20.1 (#217)

* Upgrade the Fabric version and rewrite the code.

* Migrate the completed code of version 1.19.2.

* fabric: some events.

* Updated open source license to Apache 2.0.

* Add Plan analyzer support.

* Fix build.

* `UnsupportedOperationException`

* More fabric implementation work, update to v3's structure

* Suppress compiler warnings

* Add commands, adjust registration order

* Inventory and ender chest data/serializers

* Update license headers

* Fixup shaded library relocations

* Fix build

* Potion effects & location serializers

* Catch `Files.createDirectory(path);` in `#getDataFolder`

* Update fabric.mod.json metadata, correct icon

* Events for Fabric (#218)

* Added apache commons pool2 dependency

A NoClassDefFoundError would get thrown without this dependency. Relocation appears to not work very well either, so it has been excluded for now

* Added in Item Pickup and Drop events and mixins

* Update husksync.mixins.json

* Switch drop item event to using Network Handler mixin

* Implemented even more events

- Interact block (place too)
- Interact Entity
- Use Item
- Block Break
- Player damage
- Inventory Click (handles drops)
- Player Commands

* Re-implement the dropItem mixin

* Set dropItem mixin as cancellable

* deps: Include all bukkit runtime deps

* fix/fabric: Supply AudienceProvider to `ConsoleUser` constructor

* docs: credit Fabric porters :)

* fix: Item deserialization now working

* refactor: Remove inventory debug log

* docs: Update `fabric.mod.json`

* refactor: update with upstream changes

* fix: dangling JD comment

* fix: config file reference fixes

* refactor: optimize imports, fix relocation

* refactor: move tag references to common

* refactor: use lombok for data / serializer methods

* fix: bad annotating

* refactor: adjust callback formatting

* fabric: bump deps, refactor to match main branch

* fabric: more serializer type work

* feat: register more fabric data serializers

also fixes a compile issue on bukkit, and refactors the JSON serializer to be in the common module

* feat: implement remaining Fabric serializers

* feat: add on-the-fly DFU for Fabric

Now auto-upgrades item data to support version bumps. Also improved the schema a lil' bit.

* feat: add missing mixins

* feat: implement toKeep/toDrop option on Fabric

* feat: apply stats on sync

* build: append fabric MC version to file name

* feat: add HuskSync API support for Fabric

Also updates the docs

* refactor: fixup a deprecation in the wrong spot

* refactor: optimize fabric item serializing in-line with Bukkit

* feat: implement viewer GUIs on Fabric

* docs: Fabric is in Alpha for now

---------

Co-authored-by: hanbings <hanbings@hanbings.io>
Co-authored-by: Stampede <carterblowers01@gmail.com>
This commit is contained in:
William
2024-06-09 22:41:37 +01:00
committed by GitHub
parent e3fb1762a1
commit 89368778f3
60 changed files with 3876 additions and 142 deletions

View File

@@ -150,15 +150,15 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync, BukkitTask.S
registerSerializer(Identifier.INVENTORY, new BukkitSerializer.Inventory(this));
registerSerializer(Identifier.ENDER_CHEST, new BukkitSerializer.EnderChest(this));
registerSerializer(Identifier.ADVANCEMENTS, new BukkitSerializer.Advancements(this));
registerSerializer(Identifier.STATISTICS, new BukkitSerializer.Json<>(this, BukkitData.Statistics.class));
registerSerializer(Identifier.STATISTICS, new Serializer.Json<>(this, BukkitData.Statistics.class));
registerSerializer(Identifier.POTION_EFFECTS, new BukkitSerializer.PotionEffects(this));
registerSerializer(Identifier.GAME_MODE, new BukkitSerializer.Json<>(this, BukkitData.GameMode.class));
registerSerializer(Identifier.FLIGHT_STATUS, new BukkitSerializer.Json<>(this, BukkitData.FlightStatus.class));
registerSerializer(Identifier.ATTRIBUTES, new BukkitSerializer.Json<>(this, BukkitData.Attributes.class));
registerSerializer(Identifier.HEALTH, new BukkitSerializer.Json<>(this, BukkitData.Health.class));
registerSerializer(Identifier.HUNGER, new BukkitSerializer.Json<>(this, BukkitData.Hunger.class));
registerSerializer(Identifier.EXPERIENCE, new BukkitSerializer.Json<>(this, BukkitData.Experience.class));
registerSerializer(Identifier.LOCATION, new BukkitSerializer.Json<>(this, BukkitData.Location.class));
registerSerializer(Identifier.GAME_MODE, new Serializer.Json<>(this, BukkitData.GameMode.class));
registerSerializer(Identifier.FLIGHT_STATUS, new Serializer.Json<>(this, BukkitData.FlightStatus.class));
registerSerializer(Identifier.ATTRIBUTES, new Serializer.Json<>(this, BukkitData.Attributes.class));
registerSerializer(Identifier.HEALTH, new Serializer.Json<>(this, BukkitData.Health.class));
registerSerializer(Identifier.HUNGER, new Serializer.Json<>(this, BukkitData.Hunger.class));
registerSerializer(Identifier.EXPERIENCE, new Serializer.Json<>(this, BukkitData.Experience.class));
registerSerializer(Identifier.LOCATION, new Serializer.Json<>(this, BukkitData.Location.class));
validateDependencies();
});

View File

@@ -69,7 +69,6 @@ public abstract class BukkitData implements Data {
private final @Nullable ItemStack @NotNull [] contents;
private Items(@Nullable ItemStack @NotNull [] contents) {
this.contents = Arrays.stream(contents.clone())
.map(i -> i == null || i.getType() == Material.AIR ? null : i)
.toArray(ItemStack[]::new);
@@ -127,8 +126,6 @@ public abstract class BukkitData implements Data {
@Getter
public static class Inventory extends BukkitData.Items implements Data.Items.Inventory {
public static final int INVENTORY_SLOT_COUNT = 41;
@Range(from = 0, to = 8)
private int heldItemSlot;
@@ -175,15 +172,18 @@ public abstract class BukkitData implements Data {
public static class EnderChest extends BukkitData.Items implements Data.Items.EnderChest {
public static final int ENDER_CHEST_SLOT_COUNT = 27;
private EnderChest(@NotNull ItemStack[] contents) {
private EnderChest(@Nullable ItemStack @NotNull [] contents) {
super(contents);
}
@NotNull
public static BukkitData.Items.EnderChest adapt(@NotNull ItemStack[] items) {
return new BukkitData.Items.EnderChest(items);
public static BukkitData.Items.EnderChest adapt(@Nullable ItemStack @NotNull [] contents) {
return new BukkitData.Items.EnderChest(contents);
}
@NotNull
public static BukkitData.Items.EnderChest adapt(@NotNull Collection<ItemStack> items) {
return adapt(items.toArray(ItemStack[]::new));
}
@NotNull
@@ -200,7 +200,7 @@ public abstract class BukkitData implements Data {
public static class ItemArray extends BukkitData.Items implements Data.Items {
private ItemArray(@NotNull ItemStack[] contents) {
private ItemArray(@Nullable ItemStack @NotNull [] contents) {
super(contents);
}
@@ -210,7 +210,7 @@ public abstract class BukkitData implements Data {
}
@NotNull
public static ItemArray adapt(@NotNull ItemStack[] drops) {
public static ItemArray adapt(@Nullable ItemStack @NotNull [] drops) {
return new ItemArray(drops);
}
@@ -341,9 +341,12 @@ public abstract class BukkitData implements Data {
}));
}
private void setAdvancement(@NotNull HuskSync plugin, @NotNull org.bukkit.advancement.Advancement advancement,
@NotNull Player player, @NotNull BukkitUser user,
@NotNull Collection<String> toAward, @NotNull Collection<String> toRevoke) {
private void setAdvancement(@NotNull HuskSync plugin,
@NotNull org.bukkit.advancement.Advancement advancement,
@NotNull Player player,
@NotNull BukkitUser user,
@NotNull Collection<String> toAward,
@NotNull Collection<String> toRevoke) {
plugin.runSync(() -> {
// Track player exp level & progress
final int expLevel = player.getLevel();
@@ -355,7 +358,8 @@ public abstract class BukkitData implements Data {
toRevoke.forEach(progress::revokeCriteria);
// Set player experience and level (prevent advancement awards applying twice), reset game rule
if (!toAward.isEmpty() && player.getLevel() != expLevel || player.getExp() != expProgress) {
if (!toAward.isEmpty()
&& (player.getLevel() != expLevel || player.getExp() != expProgress)) {
player.setLevel(expLevel);
player.setExp(expProgress);
}

View File

@@ -29,6 +29,7 @@ import de.tr7zw.changeme.nbtapi.utils.DataFixerUtil;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import net.william278.desertwell.util.Version;
import net.william278.husksync.BukkitHuskSync;
import net.william278.husksync.HuskSync;
import net.william278.husksync.adapter.Adaptable;
import net.william278.husksync.api.HuskSyncAPI;
@@ -41,6 +42,8 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
import static net.william278.husksync.data.BukkitData.Items.Inventory.INVENTORY_SLOT_COUNT;
import static net.william278.husksync.data.Data.Items.Inventory.HELD_ITEM_SLOT_TAG;
import static net.william278.husksync.data.Data.Items.Inventory.ITEMS_TAG;
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public class BukkitSerializer {
@@ -60,8 +63,6 @@ public class BukkitSerializer {
public static class Inventory extends BukkitSerializer implements Serializer<BukkitData.Items.Inventory>,
ItemDeserializer {
private static final String ITEMS_TAG = "items";
private static final String HELD_ITEM_SLOT_TAG = "held_item_slot";
public Inventory(@NotNull HuskSync plugin) {
super(plugin);
@@ -74,7 +75,7 @@ public class BukkitSerializer {
final ReadWriteNBT items = root.hasTag(ITEMS_TAG) ? root.getCompound(ITEMS_TAG) : null;
return BukkitData.Items.Inventory.from(
items != null ? getItems(items, dataMcVersion) : new ItemStack[INVENTORY_SLOT_COUNT],
root.getInteger(HELD_ITEM_SLOT_TAG)
root.hasTag(HELD_ITEM_SLOT_TAG) ? root.getInteger(HELD_ITEM_SLOT_TAG) : 0
);
}
@@ -126,15 +127,15 @@ public class BukkitSerializer {
@Nullable
default ItemStack[] getItems(@NotNull ReadWriteNBT tag, @NotNull Version mcVersion) {
if (mcVersion.compareTo(getPlugin().getMinecraftVersion()) < 0) {
return upgradeItemStack((NBTCompound) tag, mcVersion);
return upgradeItemStacks((NBTCompound) tag, mcVersion);
}
return NBT.itemStackArrayFromNBT(tag);
}
@NotNull
private ItemStack @NotNull [] upgradeItemStack(@NotNull NBTCompound compound, @NotNull Version mcVersion) {
final ReadWriteNBTCompoundList items = compound.getCompoundList("items");
final ItemStack[] itemStacks = new ItemStack[compound.getInteger("size")];
private ItemStack @NotNull [] upgradeItemStacks(@NotNull NBTCompound itemsNbt, @NotNull Version mcVersion) {
final ReadWriteNBTCompoundList items = itemsNbt.getCompoundList("items");
final ItemStack[] itemStacks = new ItemStack[itemsNbt.getInteger("size")];
for (int i = 0; i < items.size(); i++) {
if (items.get(i) == null) {
itemStacks[i] = new ItemStack(Material.AIR);
@@ -163,6 +164,7 @@ public class BukkitSerializer {
case "1.19", "1.19.1", "1.19.2" -> DataFixerUtil.VERSION1_19_2;
case "1.20", "1.20.1", "1.20.2" -> DataFixerUtil.VERSION1_20_2;
case "1.20.3", "1.20.4" -> DataFixerUtil.VERSION1_20_4;
case "1.20.5", "1.20.6" -> DataFixerUtil.VERSION1_20_5;
default -> DataFixerUtil.getCurrentVersion();
};
}
@@ -237,24 +239,19 @@ public class BukkitSerializer {
}
public static class Json<T extends Data & Adaptable> extends BukkitSerializer implements Serializer<T> {
/**
* @deprecated Use {@link Serializer.Json} in the common module instead
*/
@Deprecated(since = "2.6")
public class Json<T extends Data & Adaptable> extends Serializer.Json<T> {
private final Class<T> type;
public Json(@NotNull HuskSync plugin, Class<T> type) {
super(plugin);
this.type = type;
}
@Override
public T deserialize(@NotNull String serialized) throws DeserializationException {
return plugin.getDataAdapter().fromJson(serialized, type);
public Json(@NotNull HuskSync plugin, @NotNull Class<T> type) {
super(plugin, type);
}
@NotNull
@Override
public String serialize(@NotNull T element) throws SerializationException {
return plugin.getDataAdapter().toJson(element);
public BukkitHuskSync getPlugin() {
return (BukkitHuskSync) plugin;
}
}

View File

@@ -67,9 +67,9 @@ public interface BukkitUserDataHolder extends UserDataHolder {
.isSyncDeadPlayersChangingServer())) {
return Optional.of(BukkitData.Items.Inventory.empty());
}
final PlayerInventory inventory = getBukkitPlayer().getInventory();
final PlayerInventory inventory = getPlayer().getInventory();
return Optional.of(BukkitData.Items.Inventory.from(
getMapPersister().persistLockedMaps(inventory.getContents(), getBukkitPlayer()),
getMapPersister().persistLockedMaps(inventory.getContents(), getPlayer()),
inventory.getHeldItemSlot()
));
}
@@ -78,80 +78,89 @@ public interface BukkitUserDataHolder extends UserDataHolder {
@Override
default Optional<Data.Items.EnderChest> getEnderChest() {
return Optional.of(BukkitData.Items.EnderChest.adapt(
getMapPersister().persistLockedMaps(getBukkitPlayer().getEnderChest().getContents(), getBukkitPlayer())
getMapPersister().persistLockedMaps(getPlayer().getEnderChest().getContents(), getPlayer())
));
}
@NotNull
@Override
default Optional<Data.PotionEffects> getPotionEffects() {
return Optional.of(BukkitData.PotionEffects.from(getBukkitPlayer().getActivePotionEffects()));
return Optional.of(BukkitData.PotionEffects.from(getPlayer().getActivePotionEffects()));
}
@NotNull
@Override
default Optional<Data.Advancements> getAdvancements() {
return Optional.of(BukkitData.Advancements.adapt(getBukkitPlayer()));
return Optional.of(BukkitData.Advancements.adapt(getPlayer()));
}
@NotNull
@Override
default Optional<Data.Location> getLocation() {
return Optional.of(BukkitData.Location.adapt(getBukkitPlayer().getLocation()));
return Optional.of(BukkitData.Location.adapt(getPlayer().getLocation()));
}
@NotNull
@Override
default Optional<Data.Statistics> getStatistics() {
return Optional.of(BukkitData.Statistics.adapt(getBukkitPlayer()));
return Optional.of(BukkitData.Statistics.adapt(getPlayer()));
}
@NotNull
@Override
default Optional<Data.Health> getHealth() {
return Optional.of(BukkitData.Health.adapt(getBukkitPlayer()));
return Optional.of(BukkitData.Health.adapt(getPlayer()));
}
@NotNull
@Override
default Optional<Data.Hunger> getHunger() {
return Optional.of(BukkitData.Hunger.adapt(getBukkitPlayer()));
return Optional.of(BukkitData.Hunger.adapt(getPlayer()));
}
@NotNull
@Override
default Optional<Data.Attributes> getAttributes() {
return Optional.of(BukkitData.Attributes.adapt(getBukkitPlayer(), getPlugin()));
return Optional.of(BukkitData.Attributes.adapt(getPlayer(), getPlugin()));
}
@NotNull
@Override
default Optional<Data.Experience> getExperience() {
return Optional.of(BukkitData.Experience.adapt(getBukkitPlayer()));
return Optional.of(BukkitData.Experience.adapt(getPlayer()));
}
@NotNull
@Override
default Optional<Data.GameMode> getGameMode() {
return Optional.of(BukkitData.GameMode.adapt(getBukkitPlayer()));
return Optional.of(BukkitData.GameMode.adapt(getPlayer()));
}
@NotNull
@Override
default Optional<Data.FlightStatus> getFlightStatus() {
return Optional.of(BukkitData.FlightStatus.adapt(getBukkitPlayer()));
return Optional.of(BukkitData.FlightStatus.adapt(getPlayer()));
}
@NotNull
@Override
default Optional<Data.PersistentData> getPersistentData() {
return Optional.of(BukkitData.PersistentData.adapt(getBukkitPlayer().getPersistentDataContainer()));
return Optional.of(BukkitData.PersistentData.adapt(getPlayer().getPersistentDataContainer()));
}
boolean isDead();
@NotNull
Player getBukkitPlayer();
Player getPlayer();
/**
* @deprecated Use {@link #getPlayer()} instead
*/
@Deprecated(since = "3.6")
@NotNull
default Player getBukkitPlayer() {
return getPlayer();
}
@NotNull
default BukkitMapPersister getMapPersister() {

View File

@@ -62,17 +62,6 @@ public class BukkitUser extends OnlineUser implements BukkitUserDataHolder {
return new BukkitUser(player, plugin);
}
/**
* Get the Bukkit {@link Player} instance of this user
*
* @return the {@link Player} instance
* @since 3.0
*/
@NotNull
public Player getPlayer() {
return player;
}
@Override
public boolean isOffline() {
return player == null || !player.isOnline();
@@ -132,9 +121,14 @@ public class BukkitUser extends OnlineUser implements BukkitUserDataHolder {
return player.hasMetadata("NPC");
}
/**
* Get the Bukkit {@link Player} instance of this user
*
* @return the {@link Player} instance
* @since 3.6
*/
@NotNull
@Override
public Player getBukkitPlayer() {
public Player getPlayer() {
return player;
}