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

build: Use multi-version and preprocessor to build all target versions from one branch (#463)

* feat: convert Fabric to use essential-multi-version

* fix: populate compatibility.yml with mc version

* feat: start WIP work on doing the same for bukkit

* refactor: use preprocessor plugin to multi-version bukkit

* docs: update README to mention multi-version stuff

* build: also include javadocs for Bukkit

* build: update CI workflows

also slightly simplifies buildscript
This commit is contained in:
William
2025-03-05 16:52:21 +00:00
committed by GitHub
parent 1ff4cab88d
commit be6bebe361
46 changed files with 594 additions and 398 deletions

View File

@@ -0,0 +1,60 @@
/*
* This file is part of HuskSync, licensed under the Apache License 2.0.
*
* Copyright (c) William278 <will27528@gmail.com>
* Copyright (c) contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.william278.husksync;
import net.kyori.adventure.audience.Audience;
import net.william278.desertwell.util.Version;
import net.william278.husksync.listener.BukkitEventListener;
import net.william278.husksync.listener.PaperEventListener;
import net.william278.uniform.Uniform;
import net.william278.uniform.paper.PaperUniform;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
@SuppressWarnings({"unused"})
public class PaperHuskSync extends BukkitHuskSync {
@NotNull
@Override
protected BukkitEventListener createEventListener() {
return new PaperEventListener(this);
}
@NotNull
@Override
public Audience getAudience(@NotNull UUID user) {
final Player player = getServer().getPlayer(user);
return player == null || !player.isOnline() ? Audience.empty() : player;
}
@NotNull
@Override
public Version getMinecraftVersion() {
return Version.fromString(getServer().getMinecraftVersion());
}
@Override
@NotNull
public Uniform getUniform() {
return PaperUniform.getInstance(this);
}
}

View File

@@ -0,0 +1,82 @@
/*
* This file is part of HuskSync, licensed under the Apache License 2.0.
*
* Copyright (c) William278 <will27528@gmail.com>
* Copyright (c) contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.william278.husksync;
import de.exlll.configlib.Configuration;
import de.exlll.configlib.YamlConfigurations;
import io.papermc.paper.plugin.loader.PluginClasspathBuilder;
import io.papermc.paper.plugin.loader.PluginLoader;
import io.papermc.paper.plugin.loader.library.impl.MavenLibraryResolver;
import lombok.NoArgsConstructor;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.repository.RemoteRepository;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.InputStream;
import java.util.List;
import java.util.Objects;
@NoArgsConstructor
@SuppressWarnings("UnstableApiUsage")
public class PaperHuskSyncLoader implements PluginLoader {
@Override
public void classloader(@NotNull PluginClasspathBuilder classpathBuilder) {
final MavenLibraryResolver resolver = new MavenLibraryResolver();
resolveLibraries(classpathBuilder).stream()
.map(DefaultArtifact::new)
.forEach(artifact -> resolver.addDependency(new Dependency(artifact, null)));
resolver.addRepository(new RemoteRepository.Builder(
"maven", "default", "https://repo.maven.apache.org/maven2/"
).build());
classpathBuilder.addLibrary(resolver);
}
@NotNull
private static List<String> resolveLibraries(@NotNull PluginClasspathBuilder classpathBuilder) {
try (InputStream input = getLibraryListFile()) {
return YamlConfigurations.read(
Objects.requireNonNull(input, "Failed to read libraries file"),
PaperLibraries.class
).libraries;
} catch (Throwable e) {
classpathBuilder.getContext().getLogger().error("Failed to resolve libraries", e);
}
return List.of();
}
@Nullable
private static InputStream getLibraryListFile() {
return PaperHuskSyncLoader.class.getClassLoader().getResourceAsStream("paper-libraries.yml");
}
@Configuration
@NoArgsConstructor
public static class PaperLibraries {
private List<String> libraries;
}
}

View File

@@ -38,7 +38,11 @@ import org.bukkit.attribute.AttributeModifier;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
//#if MC==12001
//$$ import org.bukkit.inventory.EquipmentSlot;
//#else
import org.bukkit.inventory.EquipmentSlotGroup;
//#endif
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.potion.PotionEffect;
@@ -590,19 +594,33 @@ public abstract class BukkitData implements Data {
instance.getBaseValue(),
instance.getModifiers().stream()
.filter(modifier -> !settings.isIgnoredModifier(modifier.getName()))
//#if MC==12001
//$$ .filter(modifier -> modifier.getSlot() == null)
//#else
.filter(modifier -> modifier.getSlotGroup() != EquipmentSlotGroup.ANY)
//#endif
.map(BukkitData.Attributes::adapt).collect(Collectors.toSet())
);
}
@NotNull
private static Modifier adapt(@NotNull AttributeModifier modifier) {
//#if MC==12001
//$$ return new Modifier(
//$$ modifier.getUniqueId(),
//$$ modifier.getName(),
//$$ modifier.getAmount(),
//$$ modifier.getOperation().ordinal(),
//$$ modifier.getSlot() != null ? modifier.getSlot().ordinal() : -1
//$$ );
//#else
return new Modifier(
modifier.getKey().toString(),
modifier.getAmount(),
modifier.getOperation().ordinal(),
modifier.getSlotGroup().toString()
);
//#endif
}
private static void applyAttribute(@Nullable AttributeInstance instance, @Nullable Attribute attribute) {
@@ -622,12 +640,22 @@ public abstract class BukkitData implements Data {
@NotNull
private static AttributeModifier adapt(@NotNull Modifier modifier) {
//#if MC==12001
//$$ return new AttributeModifier(
//$$ modifier.uuid(),
//$$ modifier.name(),
//$$ modifier.amount(),
//$$ AttributeModifier.Operation.values()[modifier.operation()],
//$$ modifier.equipmentSlot() != -1 ? EquipmentSlot.values()[modifier.equipmentSlot()] : null
//$$ );
//#else
return new AttributeModifier(
Objects.requireNonNull(NamespacedKey.fromString(modifier.name())),
modifier.amount(),
AttributeModifier.Operation.values()[modifier.operation()],
Optional.ofNullable(EquipmentSlotGroup.getByName(modifier.slotGroup())).orElse(EquipmentSlotGroup.ANY)
);
//#endif
}
@Override

View File

@@ -0,0 +1,105 @@
/*
* This file is part of HuskSync, licensed under the Apache License 2.0.
*
* Copyright (c) William278 <will27528@gmail.com>
* Copyright (c) contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.william278.husksync.listener;
import com.google.common.collect.Lists;
import net.william278.husksync.BukkitHuskSync;
import net.william278.husksync.data.BukkitData;
import net.william278.husksync.user.BukkitUser;
import net.william278.husksync.user.OnlineUser;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerAdvancementDoneEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
import java.util.List;
import static net.william278.husksync.config.Settings.SynchronizationSettings.SaveOnDeathSettings;
public class PaperEventListener extends BukkitEventListener {
public PaperEventListener(@NotNull BukkitHuskSync plugin) {
super(plugin);
}
@Override
public void onEnable() {
getPlugin().getServer().getPluginManager().registerEvents(this, getPlugin());
lockedHandler.onEnable();
}
@Override
public void handlePlayerDeath(@NotNull PlayerDeathEvent event) {
// If the player is locked or the plugin disabling, clear their drops
final OnlineUser user = BukkitUser.adapt(event.getEntity(), plugin);
if (lockedHandler.cancelPlayerEvent(user.getUuid())) {
event.getDrops().clear();
event.getItemsToKeep().clear();
return;
}
// Handle saving player data snapshots on death
final SaveOnDeathSettings settings = plugin.getSettings().getSynchronization().getSaveOnDeath();
if (!settings.isEnabled()) {
return;
}
// Paper - support saving the player's items to keep if enabled
final int maxInventorySize = BukkitData.Items.Inventory.INVENTORY_SLOT_COUNT;
final List<ItemStack> itemsToSave = switch (settings.getItemsToSave()) {
case DROPS -> event.getDrops();
case ITEMS_TO_KEEP -> preserveOrder(event.getEntity().getInventory(), event.getItemsToKeep());
};
if (itemsToSave.size() > maxInventorySize) {
itemsToSave.subList(maxInventorySize, itemsToSave.size()).clear();
}
super.saveOnPlayerDeath(user, BukkitData.Items.ItemArray.adapt(itemsToSave));
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPlayerAdvancementDone(@NotNull PlayerAdvancementDoneEvent event) {
if (lockedHandler.cancelPlayerEvent(event.getPlayer().getUniqueId())) {
event.message(null);
}
}
@NotNull
private List<ItemStack> preserveOrder(@NotNull PlayerInventory inventory, @NotNull List<ItemStack> toKeep) {
final List<ItemStack> preserved = Lists.newArrayList();
final List<ItemStack> items = Lists.newArrayList(inventory.getContents());
for (ItemStack item : toKeep) {
final Iterator<ItemStack> iterator = items.iterator();
while (iterator.hasNext()) {
final ItemStack originalItem = iterator.next();
if (originalItem != null && originalItem.equals(item)) {
preserved.add(originalItem);
iterator.remove();
break;
}
}
}
return preserved;
}
}

View File

@@ -51,7 +51,11 @@ public final class BukkitKeyedAdapter {
@Nullable
public static PotionEffectType matchEffectType(@NotNull String key) {
//#if MC==12001
//$$ return PotionEffectType.getByName(key);
//#else
return getRegistryValue(Registry.EFFECT, key);
//#endif
}
private static <T extends Keyed> T getRegistryValue(@NotNull Registry<T> registry, @NotNull String keyString) {

View File

@@ -279,7 +279,7 @@ public interface BukkitMapPersister {
@NotNull
private static World getDefaultMapWorld() {
final World world = Bukkit.getWorlds().getFirst();
final World world = Bukkit.getWorlds().get(0);
if (world == null) {
throw new IllegalStateException("No worlds are loaded on the server!");
}
@@ -359,7 +359,7 @@ public interface BukkitMapPersister {
/**
* A {@link MapCanvas} implementation used for pre-rendering maps to be converted into {@link MapData}
*/
@SuppressWarnings("deprecation")
@SuppressWarnings({"deprecation", "removal"})
class PersistentMapCanvas implements MapCanvas {
private final int mapDataVersion;
@@ -454,7 +454,11 @@ public interface BukkitMapPersister {
final String BANNER_PREFIX = "banner_";
for (int i = 0; i < getCursors().size(); i++) {
final MapCursor cursor = getCursors().getCursor(i);
//#if MC==12001
//$$ final String type = cursor.getType().name().toLowerCase(Locale.ENGLISH);
//#else
final String type = cursor.getType().getKey().getKey();
//#endif
if (type.startsWith(BANNER_PREFIX)) {
banners.add(new MapBanner(
type.replaceAll(BANNER_PREFIX, ""),