Fix logging, add config, fix UnsafeValues

This commit is contained in:
Bjorn Beishline 2026-02-17 10:31:56 -07:00
parent 56eff72525
commit 018ef2a8fa
24 changed files with 748 additions and 364 deletions

View File

@ -54,6 +54,7 @@ dependencies {
implementation("org.apache.maven.resolver:maven-resolver-transport-http:1.9.18")
implementation("org.apache.maven.resolver:maven-resolver-util:1.9.18")
implementation("com.google.protobuf:protobuf-java:$protobufVersion")
implementation("org.slf4j:slf4j-jdk14:2.0.16")
}
java {

View File

@ -0,0 +1,30 @@
package org.patchbukkit;
import org.bukkit.Material;
import org.bukkit.material.MaterialData;
@SuppressWarnings({ "deprecation", "removal" })
public final class PatchBukkitLegacy {
private static final org.slf4j.Logger LOGGER = org.slf4j.LoggerFactory.getLogger(PatchBukkitLegacy.class);
public static Material fromLegacy(Material material) {
if (material == null || !material.isLegacy()) {
return material;
}
return PatchBukkitLegacy.fromLegacy(new MaterialData(material));
}
public static Material fromLegacy(MaterialData materialData) {
return PatchBukkitLegacy.fromLegacy(materialData, false);
}
public static Material fromLegacy(MaterialData materialData, boolean itemPriority) {
Material material = materialData.getItemType();
if (material == null || !material.isLegacy()) {
return material;
}
throw new UnsupportedOperationException("Cannot convert from legacy to new Material");
}
}

View File

@ -90,6 +90,8 @@ import org.patchbukkit.command.PatchBukkitCommandMap;
import org.patchbukkit.command.PatchBukkitConsoleCommandSender;
import org.patchbukkit.events.PatchBukkitEventManager;
import org.patchbukkit.scheduler.PatchBukkitScheduler;
import org.patchbukkit.versioning.Versioning;
import patchbukkit.bridge.NativeBridgeFfi;
import patchbukkit.log.LogLevel;
import patchbukkit.log.SendLogRequest;
@ -114,7 +116,7 @@ public class PatchBukkitServer implements Server {
configureRootLogger();
}
private static final Map<Level, LogLevel> LEVEL_MAP = Map.of(
public static final Map<Level, LogLevel> LEVEL_MAP = Map.of(
Level.SEVERE, LogLevel.SEVERE,
Level.WARNING, LogLevel.WARNING,
Level.INFO, LogLevel.INFO,

View File

@ -12,11 +12,14 @@ import java.util.Map;
import java.util.function.BooleanSupplier;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.event.HoverEvent.ShowItem;
import net.kyori.adventure.text.flattener.ComponentFlattener;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import patchbukkit.bridge.NativeBridgeFfi;
import org.bukkit.*;
import org.bukkit.advancement.Advancement;
import org.bukkit.attribute.Attributable;
@ -25,6 +28,7 @@ import org.bukkit.attribute.AttributeModifier;
import org.bukkit.block.data.BlockData;
import org.bukkit.command.CommandSender;
import org.bukkit.damage.DamageSource;
import org.bukkit.damage.DamageSource.Builder;
import org.bukkit.damage.DamageType;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
@ -38,9 +42,14 @@ import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.potion.PotionType;
import org.bukkit.potion.PotionType.InternalPotionData;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NonNull;
import org.patchbukkit.events.PatchBukkitLifecycleEventManager;
import org.patchbukkit.versioning.ApiVersion;
import org.patchbukkit.versioning.Versioning;
import patchbukkit.common.EmptyRequest;
@SuppressWarnings("removal")
public class PatchBukkitUnsafeValues implements UnsafeValues {
@ -48,342 +57,334 @@ public class PatchBukkitUnsafeValues implements UnsafeValues {
public static final PatchBukkitUnsafeValues INSTANCE =
new PatchBukkitUnsafeValues();
// These are commonly used - implement first
@Override
public int getDataVersion() {
return 4189; // 1.21.4 data version - match your server
}
@Override
public ItemStack modifyItemStack(ItemStack stack, String arguments) {
return null;
}
@Override
public boolean isSupportedApiVersion(String apiVersion) {
// Accept common API versions
return (
apiVersion != null &&
(apiVersion.equals("1.21") || apiVersion.equals("1.20"))
);
}
if (apiVersion == null) return false;
final ApiVersion toCheck = ApiVersion.getOrCreateVersion(apiVersion);
var minimumApi = NativeBridgeFfi.getPatchBukkitConfig(EmptyRequest.newBuilder().build()).getMinimumSupportedPluginApi();
final ApiVersion minimumVersion = ApiVersion.getOrCreateVersion(minimumApi);
@Override
public byte[] serializeItem(ItemStack item) {
return new byte[0];
}
@Override
public ItemStack deserializeItem(byte[] data) {
return null;
}
@Override
public @NotNull JsonObject serializeItemAsJson(
@NotNull ItemStack itemStack
) {
return null;
}
@Override
public @NotNull ItemStack deserializeItemFromJson(@NotNull JsonObject data)
throws IllegalArgumentException {
return null;
}
@Override
public byte@NotNull [] serializeEntity(
@NotNull Entity entity,
@NonNull @NotNull EntitySerializationFlag... serializationFlags
) {
return new byte[0];
}
@Override
public @NotNull Entity deserializeEntity(
byte@NotNull [] data,
@NotNull World world,
boolean preserveUUID,
boolean preservePassengers
) {
return null;
}
@Override
public int nextEntityId() {
return 0;
}
@Override
public @NotNull String getMainLevelName() {
return "";
}
@Override
public int getProtocolVersion() {
return 0;
}
@Override
public boolean isValidRepairItemStack(
@NotNull ItemStack itemToBeRepaired,
@NotNull ItemStack repairMaterial
) {
return false;
}
@Override
public boolean hasDefaultEntityAttributes(
@NotNull NamespacedKey entityKey
) {
return false;
}
@Override
public @NotNull Attributable getDefaultEntityAttributes(
@NotNull NamespacedKey entityKey
) {
return null;
}
@Override
public @NotNull NamespacedKey getBiomeKey(
RegionAccessor accessor,
int x,
int y,
int z
) {
return null;
}
@Override
public void setBiomeKey(
RegionAccessor accessor,
int x,
int y,
int z,
NamespacedKey biomeKey
) {}
@Override
public String getStatisticCriteriaKey(@NotNull Statistic statistic) {
return "";
}
@Override
public @Nullable Color getSpawnEggLayerColor(
EntityType entityType,
int layer
) {
return null;
}
@Override
public LifecycleEventManager<Plugin> createPluginLifecycleEventManager(
JavaPlugin plugin,
BooleanSupplier registrationCheck
) {
return null;
}
@Override
public @NotNull List<Component> computeTooltipLines(
@NotNull ItemStack itemStack,
@NotNull TooltipContext tooltipContext,
@Nullable Player player
) {
return List.of();
}
@Override
public ItemStack createEmptyStack() {
return null;
}
@Override
public @NotNull Map<String, Object> serializeStack(ItemStack itemStack) {
return Map.of();
}
@Override
public @NotNull ItemStack deserializeStack(
@NotNull Map<String, Object> args
) {
return null;
}
@Override
public @NotNull ItemStack deserializeItemHover(
HoverEvent.@NotNull ShowItem itemHover
) {
return null;
return !toCheck.isNewerThan(ApiVersion.CURRENT) && !toCheck.isOlderThan(minimumVersion);
}
@Override
public void checkSupported(PluginDescriptionFile pdf)
throws InvalidPluginException {
// Be lenient during development - accept most plugins
String api = pdf.getAPIVersion();
if (api != null && !isSupportedApiVersion(api)) {
throw new InvalidPluginException("Unsupported API: " + api);
}
}
@Override
public byte[] processClass(
PluginDescriptionFile pdf,
String path,
byte[] clazz
) {
// Return unchanged - skip Commodore remapping for now
return clazz;
}
@Override
public ComponentFlattener componentFlattener() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'componentFlattener'");
}
@Override
public Advancement loadAdvancement(NamespacedKey key, String advancement) {
return null;
}
@Override
public PlainComponentSerializer plainComponentSerializer() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'plainComponentSerializer'");
}
@Override
public boolean removeAdvancement(NamespacedKey key) {
return false;
}
@Override
public PlainTextComponentSerializer plainTextSerializer() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'plainTextSerializer'");
}
@Override
public Multimap<Attribute, AttributeModifier> getDefaultAttributeModifiers(
Material material,
EquipmentSlot slot
) {
return null;
}
@Override
public GsonComponentSerializer gsonComponentSerializer() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'gsonComponentSerializer'");
}
@Override
public CreativeCategory getCreativeCategory(Material material) {
return null;
}
@Override
public GsonComponentSerializer colorDownsamplingGsonComponentSerializer() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'colorDownsamplingGsonComponentSerializer'");
}
@Override
public String getBlockTranslationKey(Material material) {
return "";
}
@Override
public LegacyComponentSerializer legacyComponentSerializer() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'legacyComponentSerializer'");
}
@Override
public String getItemTranslationKey(Material material) {
return "";
}
@Override
public Component resolveWithContext(Component component, CommandSender context, Entity scoreboardSubject,
boolean bypassPermissions) throws IOException {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'resolveWithContext'");
}
@Override
public String getTranslationKey(EntityType entityType) {
return "";
}
@Override
public Material toLegacy(Material material) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'toLegacy'");
}
@Override
public String getTranslationKey(ItemStack itemStack) {
return "";
}
@Override
public Material fromLegacy(Material material) {
return PatchBukkitLegacy.fromLegacy(material);
}
@Override
public String getTranslationKey(Attribute attribute) {
return "";
}
@Override
public Material fromLegacy(MaterialData material) {
return PatchBukkitLegacy.fromLegacy(material);
}
@Override
public PotionType.InternalPotionData getInternalPotionData(
NamespacedKey key
) {
return null;
}
@Override
public Material fromLegacy(MaterialData material, boolean itemPriority) {
return PatchBukkitLegacy.fromLegacy(material, itemPriority);
}
@Override
public DamageSource.@NotNull Builder createDamageSourceBuilder(
@NotNull DamageType damageType
) {
return null;
}
@Override
public BlockData fromLegacy(Material material, byte data) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'fromLegacy'");
}
@Override
public String get(Class<?> aClass, String value) {
return "";
}
@Override
public Material getMaterial(String material, int version) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getMaterial'");
}
@Override
public <B extends Keyed> B get(RegistryKey<B> registry, NamespacedKey key) {
return null;
}
@Override
public int getDataVersion() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getDataVersion'");
}
// Adventure components - commonly used
@Override
public ComponentFlattener componentFlattener() {
return ComponentFlattener.basic();
}
@Override
public ItemStack modifyItemStack(ItemStack stack, String arguments) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'modifyItemStack'");
}
@Override
public PlainComponentSerializer plainComponentSerializer() {
return null;
}
@Override
public byte[] processClass(PluginDescriptionFile pdf, String path, byte[] clazz) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'processClass'");
}
@Override
public PlainTextComponentSerializer plainTextSerializer() {
return null;
}
@Override
public Advancement loadAdvancement(NamespacedKey key, String advancement) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'loadAdvancement'");
}
@Override
public GsonComponentSerializer gsonComponentSerializer() {
return null;
}
@Override
public boolean removeAdvancement(NamespacedKey key) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'removeAdvancement'");
}
@Override
public GsonComponentSerializer colorDownsamplingGsonComponentSerializer() {
return null;
}
@Override
public Multimap<Attribute, AttributeModifier> getDefaultAttributeModifiers(Material material, EquipmentSlot slot) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getDefaultAttributeModifiers'");
}
@Override
public LegacyComponentSerializer legacyComponentSerializer() {
return null;
}
@Override
public CreativeCategory getCreativeCategory(Material material) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getCreativeCategory'");
}
@Override
public Component resolveWithContext(
Component component,
CommandSender context,
Entity scoreboardSubject,
boolean bypassPermissions
) throws IOException {
return null;
}
@Override
public String getBlockTranslationKey(Material material) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getBlockTranslationKey'");
}
// Stub everything else
@Override
public Material toLegacy(Material material) {
throw new UnsupportedOperationException(
"Pumpkin doesn't support legacy materials"
);
}
@Override
public String getItemTranslationKey(Material material) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getItemTranslationKey'");
}
@Override
public Material fromLegacy(Material material) {
return null;
}
@Override
public String getTranslationKey(EntityType entityType) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getTranslationKey'");
}
@Override
public Material fromLegacy(MaterialData material) {
return null;
}
@Override
public String getTranslationKey(ItemStack itemStack) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getTranslationKey'");
}
@Override
public Material fromLegacy(MaterialData material, boolean itemPriority) {
return null;
}
@Override
public String getTranslationKey(Attribute attribute) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getTranslationKey'");
}
@Override
public BlockData fromLegacy(Material material, byte data) {
return null;
}
@Override
public InternalPotionData getInternalPotionData(NamespacedKey key) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getInternalPotionData'");
}
@Override
public Material getMaterial(String material, int version) {
return null;
}
@Override
public @NotNull Builder createDamageSourceBuilder(@NotNull DamageType damageType) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'createDamageSourceBuilder'");
}
// ... etc
@Override
public String get(Class<?> aClass, String value) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'get'");
}
@Override
public <B extends Keyed> B get(RegistryKey<B> registry, NamespacedKey key) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'get'");
}
@Override
public byte[] serializeItem(ItemStack item) {
System.out.println("Serializing item: " + item);
System.out.println(item.getType().getKey().getKey());
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'serializeItem'");
}
@Override
public ItemStack deserializeItem(byte[] data) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'deserializeItem'");
}
@Override
public @NotNull JsonObject serializeItemAsJson(@NotNull ItemStack itemStack) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'serializeItemAsJson'");
}
@Override
public @NotNull ItemStack deserializeItemFromJson(@NotNull JsonObject data) throws IllegalArgumentException {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'deserializeItemFromJson'");
}
@Override
public byte @NotNull [] serializeEntity(@NotNull Entity entity,
@NotNull EntitySerializationFlag... serializationFlags) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'serializeEntity'");
}
@Override
public @NotNull Entity deserializeEntity(byte @NotNull [] data, @NotNull World world, boolean preserveUUID,
boolean preservePassengers) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'deserializeEntity'");
}
@Override
public int nextEntityId() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'nextEntityId'");
}
@Override
public @NotNull String getMainLevelName() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getMainLevelName'");
}
@Override
public int getProtocolVersion() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getProtocolVersion'");
}
@Override
public boolean isValidRepairItemStack(@NotNull ItemStack itemToBeRepaired, @NotNull ItemStack repairMaterial) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'isValidRepairItemStack'");
}
@Override
public boolean hasDefaultEntityAttributes(@NotNull NamespacedKey entityKey) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'hasDefaultEntityAttributes'");
}
@Override
public @NotNull Attributable getDefaultEntityAttributes(@NotNull NamespacedKey entityKey) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getDefaultEntityAttributes'");
}
@Override
public @NotNull NamespacedKey getBiomeKey(RegionAccessor accessor, int x, int y, int z) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getBiomeKey'");
}
@Override
public void setBiomeKey(RegionAccessor accessor, int x, int y, int z, NamespacedKey biomeKey) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'setBiomeKey'");
}
@Override
public String getStatisticCriteriaKey(@NotNull Statistic statistic) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getStatisticCriteriaKey'");
}
@Override
public @Nullable Color getSpawnEggLayerColor(EntityType entityType, int layer) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getSpawnEggLayerColor'");
}
@Override
public LifecycleEventManager<Plugin> createPluginLifecycleEventManager(JavaPlugin plugin,
BooleanSupplier registrationCheck) {
return new PatchBukkitLifecycleEventManager(plugin, registrationCheck);
}
@Override
public @NotNull List<Component> computeTooltipLines(@NotNull ItemStack itemStack,
@NotNull TooltipContext tooltipContext, @Nullable Player player) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'computeTooltipLines'");
}
@Override
public ItemStack createEmptyStack() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'createEmptyStack'");
}
@Override
public @NotNull Map<String, Object> serializeStack(ItemStack itemStack) {
System.out.println("Serializing itemstack: " + itemStack);
System.out.println(itemStack.getType().getKey().getKey());
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'serializeStack'");
}
@Override
public @NotNull ItemStack deserializeStack(@NotNull Map<String, Object> args) {
System.out.println("Deserializing itemstack: " + args);
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'deserializeStack'");
}
@Override
public @NotNull ItemStack deserializeItemHover(@NotNull ShowItem itemHover) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'deserializeItemHover'");
}
}

View File

@ -4,6 +4,7 @@ import java.util.Set;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Server;
import org.bukkit.command.CommandSender;
import org.bukkit.permissions.PermissibleBase;
@ -18,6 +19,11 @@ import org.jetbrains.annotations.Nullable;
import net.kyori.adventure.text.Component;
import net.md_5.bungee.api.chat.BaseComponent;
import patchbukkit.bridge.NativeBridgeFfi;
import patchbukkit.log.LogLevel;
import patchbukkit.log.SendLogRequest;
import org.patchbukkit.PatchBukkitServer;
public class PatchBukkitCommandSender implements CommandSender, ServerOperator {
private final Server server;
@ -133,7 +139,14 @@ public class PatchBukkitCommandSender implements CommandSender, ServerOperator {
if (this.server != null && this.server.getLogger() != null) {
this.server.getLogger().info(message);
} else {
System.out.println(message);
LogLevel logLevel = LogLevel.INFO;
NativeBridgeFfi.sendLog(
SendLogRequest.newBuilder()
.setLevel(logLevel)
.setMessage(ChatColor.stripColor(message))
.setLoggerName("console")
.build()
);
}
}

View File

@ -0,0 +1,44 @@
package org.patchbukkit.events;
import io.papermc.paper.plugin.lifecycle.event.LifecycleEvent;
import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager;
import io.papermc.paper.plugin.lifecycle.event.handler.LifecycleEventHandler;
import io.papermc.paper.plugin.lifecycle.event.handler.configuration.LifecycleEventHandlerConfiguration;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEventType;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BooleanSupplier;
public class PatchBukkitLifecycleEventManager implements LifecycleEventManager<Plugin> {
private final Plugin plugin;
private final BooleanSupplier registrationCheck;
private final List<RegisteredHandler<?>> handlers = new ArrayList<>();
public PatchBukkitLifecycleEventManager(Plugin plugin, BooleanSupplier registrationCheck) {
this.plugin = plugin;
this.registrationCheck = registrationCheck;
}
@Override
public void registerEventHandler(
@NotNull LifecycleEventHandlerConfiguration<? super Plugin> handlerConfiguration) {
if (!registrationCheck.getAsBoolean()) {
throw new IllegalStateException(
"Cannot register lifecycle event handler for " + plugin.getName()
+ " — registration is not allowed at this time");
}
}
public Plugin getPlugin() {
return plugin;
}
private record RegisteredHandler<E extends LifecycleEvent>(
LifecycleEventType<? super Plugin, E, ?> type,
LifecycleEventHandler<? super E> handler
) {}
}

View File

@ -0,0 +1,130 @@
package org.patchbukkit.versioning;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.jetbrains.annotations.NotNull;
public final class ApiVersion implements Comparable<ApiVersion>, Serializable {
public static final ApiVersion CURRENT;
public static final ApiVersion FLATTENING;
public static final ApiVersion FIELD_NAME_PARITY;
public static final ApiVersion ABSTRACT_COW;
public static final ApiVersion NONE;
private static final Map<String, ApiVersion> versions;
static {
versions = new HashMap<>();
CURRENT = getOrCreateVersion(Versioning.getCurrentApiVersion());
FLATTENING = getOrCreateVersion("1.13");
FIELD_NAME_PARITY = getOrCreateVersion("1.20.5");
ABSTRACT_COW = getOrCreateVersion("1.21.5");
NONE = getOrCreateVersion("none");
}
private final boolean none;
private final int major;
private final int minor;
private final int patch;
private ApiVersion() {
this.none = true;
this.major = Integer.MIN_VALUE;
this.minor = Integer.MIN_VALUE;
this.patch = Integer.MIN_VALUE;
}
private ApiVersion(int major, int minor, int patch) {
this.none = false;
this.major = major;
this.minor = minor;
this.patch = patch;
}
public static ApiVersion getOrCreateVersion(String versionString) {
if (versionString == null || versionString.trim().isEmpty() || versionString.equalsIgnoreCase("none")) {
return ApiVersion.versions.computeIfAbsent("none", s -> new ApiVersion());
}
ApiVersion version = ApiVersion.versions.get(versionString);
if (version != null) {
return version;
}
String[] versionParts = versionString.split("\\.");
if (versionParts.length != 2 && versionParts.length != 3) {
throw new IllegalArgumentException(String.format("API version string should be of format \"major.minor.patch\" or \"major.minor\", where \"major\", \"minor\" and \"patch\" are numbers. For example \"1.18.2\" or \"1.13\", but got '%s' instead.", versionString));
}
int major = ApiVersion.parseNumber(versionParts[0]);
int minor = ApiVersion.parseNumber(versionParts[1]);
int patch;
if (versionParts.length == 3) {
patch = ApiVersion.parseNumber(versionParts[2]);
} else {
patch = 0;
}
versionString = ApiVersion.toVersionString(major, minor, patch);
return ApiVersion.versions.computeIfAbsent(versionString, s -> new ApiVersion(major, minor, patch));
}
private static int parseNumber(String number) {
return Integer.parseInt(number);
}
private static String toVersionString(int major, int minor, int patch) {
return major + "." + minor + "." + patch;
}
@Override
public int compareTo(@NotNull ApiVersion other) {
int result = Integer.compare(this.major, other.major);
if (result == 0) {
result = Integer.compare(this.minor, other.minor);
}
if (result == 0) {
result = Integer.compare(this.patch, other.patch);
}
return result;
}
public String getVersionString() {
if (this.none) {
return "none";
}
return ApiVersion.toVersionString(this.major, this.minor, this.patch);
}
public boolean isNewerThan(ApiVersion apiVersion) {
return this.compareTo(apiVersion) > 0;
}
public boolean isOlderThan(ApiVersion apiVersion) {
return this.compareTo(apiVersion) < 0;
}
public boolean isNewerThanOrSameAs(ApiVersion apiVersion) {
return this.compareTo(apiVersion) >= 0;
}
public boolean isOlderThanOrSameAs(ApiVersion apiVersion) {
return this.compareTo(apiVersion) <= 0;
}
@Override
public String toString() {
return this.getVersionString();
}
private static final long serialVersionUID = 0L;
}

View File

@ -1,4 +1,4 @@
package org.patchbukkit;
package org.patchbukkit.versioning;
import java.io.BufferedReader;
import java.io.IOException;

View File

@ -5,6 +5,7 @@ import "google/protobuf/empty.proto";
import "google/protobuf/wrappers.proto";
import "patchbukkit/abilities.proto";
import "patchbukkit/common/types.proto";
import "patchbukkit/config.proto";
import "patchbukkit/events/event.proto";
import "patchbukkit/log.proto";
import "patchbukkit/message.proto";
@ -28,4 +29,6 @@ service NativeBridge {
rpc PlayerPlaySound(patchbukkit.sound.PlayerPlaySoundRequest) returns (google.protobuf.Empty);
rpc SendLog(patchbukkit.log.SendLogRequest) returns (google.protobuf.Empty);
rpc GetPatchBukkitConfig(patchbukkit.common.EmptyRequest) returns (patchbukkit.config.GetPatchBukkitConfigResponse);
}

View File

@ -25,3 +25,5 @@ message Location {
float yaw = 3;
float pitch = 4;
}
message EmptyRequest {}

View File

@ -0,0 +1,12 @@
syntax = "proto3";
package patchbukkit.config;
import "patchbukkit/common/types.proto";
option java_multiple_files = true;
option java_package = "patchbukkit.config";
message GetPatchBukkitConfigResponse {
string minimum_supported_plugin_api = 1;
}

View File

@ -0,0 +1,17 @@
syntax = "proto3";
package patchbukkit.itemstack;
import "patchbukkit/common/types.proto";
option java_multiple_files = true;
option java_package = "patchbukkit.itemstack";
// message GetPatchBukkitConfigResponse {
// string minimum_supported_plugin_api = 1;
// }
message ItemStack {
string type = 1;
uint32 amount = 2;
}

141
rust/Cargo.lock generated
View File

@ -400,9 +400,9 @@ checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
[[package]]
name = "constant_time_eq"
version = "0.4.2"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
[[package]]
name = "cookie"
@ -1132,18 +1132,16 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.4.1"
version = "0.4.0-rc.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec"
checksum = "74f70a332ddf75e5e5e43284304179ba02f391f82f692f030b08a8378adf3c99"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"r-efi",
"rand_core 0.10.0",
"wasip2",
"wasip3",
"wasm-bindgen",
]
[[package]]
@ -1905,12 +1903,14 @@ dependencies = [
"pumpkin-data",
"pumpkin-protocol",
"pumpkin-util",
"pumpkin-world",
"rand",
"rust-embed",
"serde",
"serde-saphyr",
"serde_json",
"tokio",
"toml 1.0.2+spec-1.1.0",
"tracing",
"uuid",
"walkdir",
@ -2193,7 +2193,7 @@ dependencies = [
[[package]]
name = "pumpkin"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#e438e09991619d105c9835570b09c1b4624eedc5"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#6e1bea72b7ec7b1d193149a124225e08930764f3"
dependencies = [
"arc-swap",
"base64",
@ -2203,7 +2203,6 @@ dependencies = [
"futures",
"hmac 0.13.0-rc.5",
"libloading 0.9.0",
"log",
"num-bigint",
"pkcs8 0.11.0-rc.10",
"pumpkin-config",
@ -2235,7 +2234,7 @@ dependencies = [
[[package]]
name = "pumpkin-api-macros"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#e438e09991619d105c9835570b09c1b4624eedc5"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#6e1bea72b7ec7b1d193149a124225e08930764f3"
dependencies = [
"proc-macro-error2",
"proc-macro2",
@ -2246,19 +2245,19 @@ dependencies = [
[[package]]
name = "pumpkin-config"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#e438e09991619d105c9835570b09c1b4624eedc5"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#6e1bea72b7ec7b1d193149a124225e08930764f3"
dependencies = [
"log",
"pumpkin-util",
"serde",
"toml",
"toml 0.9.11+spec-1.1.0",
"tracing",
"uuid",
]
[[package]]
name = "pumpkin-data"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#e438e09991619d105c9835570b09c1b4624eedc5"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#6e1bea72b7ec7b1d193149a124225e08930764f3"
dependencies = [
"crc-fast",
"phf",
@ -2270,22 +2269,22 @@ dependencies = [
[[package]]
name = "pumpkin-inventory"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#e438e09991619d105c9835570b09c1b4624eedc5"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#6e1bea72b7ec7b1d193149a124225e08930764f3"
dependencies = [
"crossbeam-utils",
"log",
"pumpkin-data",
"pumpkin-protocol",
"pumpkin-util",
"pumpkin-world",
"thiserror",
"tokio",
"tracing",
]
[[package]]
name = "pumpkin-macros"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#e438e09991619d105c9835570b09c1b4624eedc5"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#6e1bea72b7ec7b1d193149a124225e08930764f3"
dependencies = [
"proc-macro-error2",
"pumpkin-data",
@ -2296,7 +2295,7 @@ dependencies = [
[[package]]
name = "pumpkin-nbt"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#e438e09991619d105c9835570b09c1b4624eedc5"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#6e1bea72b7ec7b1d193149a124225e08930764f3"
dependencies = [
"bytes",
"cesu8",
@ -2308,13 +2307,14 @@ dependencies = [
[[package]]
name = "pumpkin-protocol"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#e438e09991619d105c9835570b09c1b4624eedc5"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#6e1bea72b7ec7b1d193149a124225e08930764f3"
dependencies = [
"aes",
"async-compression",
"bitflags",
"bytes",
"cfb8",
"flate2",
"pumpkin-data",
"pumpkin-macros",
"pumpkin-nbt",
@ -2330,7 +2330,7 @@ dependencies = [
[[package]]
name = "pumpkin-util"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#e438e09991619d105c9835570b09c1b4624eedc5"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#6e1bea72b7ec7b1d193149a124225e08930764f3"
dependencies = [
"base64",
"bytes",
@ -2355,7 +2355,7 @@ dependencies = [
[[package]]
name = "pumpkin-world"
version = "0.1.0-dev+1.21.11"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#e438e09991619d105c9835570b09c1b4624eedc5"
source = "git+https://github.com/Pumpkin-MC/Pumpkin.git?branch=master#6e1bea72b7ec7b1d193149a124225e08930764f3"
dependencies = [
"bitflags",
"bytes",
@ -2366,7 +2366,6 @@ dependencies = [
"flate2",
"futures",
"itertools",
"log",
"lz4-java-wrc",
"num_cpus",
"pumpkin-config",
@ -2383,6 +2382,7 @@ dependencies = [
"thiserror",
"tokio",
"tokio-util",
"tracing",
"uuid",
]
@ -2417,7 +2417,7 @@ version = "0.10.0-rc.9"
source = "git+https://github.com/rust-random/rand#72afe1e973fcd83d840cf597888223072bbdb04c"
dependencies = [
"chacha20",
"getrandom 0.4.1",
"getrandom 0.4.0-rc.1",
"rand_core 0.10.0",
]
@ -3124,9 +3124,9 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.47"
version = "0.3.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
checksum = "9da98b7d9b7dad93488a84b8248efc35352b0b2657397d4167e7ad67e5d535e5"
dependencies = [
"deranged",
"itoa",
@ -3146,9 +3146,9 @@ checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
[[package]]
name = "time-macros"
version = "0.2.27"
version = "0.2.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
checksum = "78cc610bac2dcee56805c99642447d4c5dbde4d01f752ffea0199aee1f601dc4"
dependencies = [
"num-conv",
"time-core",
@ -3214,7 +3214,22 @@ dependencies = [
"indexmap",
"serde_core",
"serde_spanned",
"toml_datetime",
"toml_datetime 0.7.5+spec-1.1.0",
"toml_parser",
"toml_writer",
"winnow",
]
[[package]]
name = "toml"
version = "1.0.2+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1dfefef6a142e93f346b64c160934eb13b5594b84ab378133ac6815cb2bd57f"
dependencies = [
"indexmap",
"serde_core",
"serde_spanned",
"toml_datetime 1.0.0+spec-1.1.0",
"toml_parser",
"toml_writer",
"winnow",
@ -3230,10 +3245,19 @@ dependencies = [
]
[[package]]
name = "toml_parser"
version = "1.0.6+spec-1.1.0"
name = "toml_datetime"
version = "1.0.0+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44"
checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_parser"
version = "1.0.9+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4"
dependencies = [
"winnow",
]
@ -3524,16 +3548,16 @@ version = "1.0.2+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
dependencies = [
"wit-bindgen",
"wit-bindgen 0.51.0",
]
[[package]]
name = "wasip3"
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
version = "0.3.1+wasi-0.3.0-rc-2025-09-16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
checksum = "87ba4be47b1d11244670d11857eee0758a8f2c39aea64d80b78c1ce29b4642cd"
dependencies = [
"wit-bindgen",
"wit-bindgen 0.48.1",
]
[[package]]
@ -3583,9 +3607,9 @@ dependencies = [
[[package]]
name = "wasm-encoder"
version = "0.244.0"
version = "0.241.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
checksum = "e01164c9dda68301e34fdae536c23ed6fe90ce6d97213ccc171eebbd3d02d6b8"
dependencies = [
"leb128fmt",
"wasmparser",
@ -3593,9 +3617,9 @@ dependencies = [
[[package]]
name = "wasm-metadata"
version = "0.244.0"
version = "0.241.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
checksum = "876fe286f2fa416386deedebe8407e6f19e0b5aeaef3d03161e77a15fa80f167"
dependencies = [
"anyhow",
"indexmap",
@ -3605,9 +3629,9 @@ dependencies = [
[[package]]
name = "wasmparser"
version = "0.244.0"
version = "0.241.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
checksum = "46d90019b1afd4b808c263e428de644f3003691f243387d30d673211ee0cb8e8"
dependencies = [
"bitflags",
"hashbrown 0.15.5",
@ -3834,18 +3858,24 @@ checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
[[package]]
name = "wit-bindgen"
version = "0.51.0"
version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
checksum = "7f8c2adb5f74ac9395bc3121c99a1254bf9310482c27b13f97167aedb5887138"
dependencies = [
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen-core"
name = "wit-bindgen"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
[[package]]
name = "wit-bindgen-core"
version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b881a098cae03686d7a0587f8f306f8a58102ad8da8b5599100fbe0e7f5800b"
dependencies = [
"anyhow",
"heck",
@ -3854,9 +3884,9 @@ dependencies = [
[[package]]
name = "wit-bindgen-rust"
version = "0.51.0"
version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
checksum = "69667efa439a453e1d50dac939c6cab6d2c3ac724a9d232b6631dad2472a5b70"
dependencies = [
"anyhow",
"heck",
@ -3870,9 +3900,9 @@ dependencies = [
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.51.0"
version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
checksum = "eae2e22cceb5d105d52326c07e3e67603a861cc7add70fc467f7cc7ec5265017"
dependencies = [
"anyhow",
"prettyplease",
@ -3885,9 +3915,9 @@ dependencies = [
[[package]]
name = "wit-component"
version = "0.244.0"
version = "0.241.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
checksum = "1fd0c57df25e7ee612d946d3b7646c1ddb2310f8280aa2c17e543b66e0812241"
dependencies = [
"anyhow",
"bitflags",
@ -3904,9 +3934,9 @@ dependencies = [
[[package]]
name = "wit-parser"
version = "0.244.0"
version = "0.241.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
checksum = "09ef1c6ad67f35c831abd4039c02894de97034100899614d1c44e2268ad01c91"
dependencies = [
"anyhow",
"id-arena",
@ -4045,9 +4075,9 @@ dependencies = [
[[package]]
name = "zip"
version = "7.4.0"
version = "7.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc12baa6db2b15a140161ce53d72209dacea594230798c24774139b54ecaa980"
checksum = "c42e33efc22a0650c311c2ef19115ce232583abbe80850bc8b66509ebef02de0"
dependencies = [
"aes",
"bzip2",
@ -4055,7 +4085,8 @@ dependencies = [
"crc32fast",
"deflate64",
"flate2",
"getrandom 0.4.1",
"generic-array",
"getrandom 0.3.4",
"hmac 0.12.1",
"indexmap",
"lzma-rust2",

View File

@ -14,6 +14,7 @@ pumpkin-data = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "ma
pumpkin-protocol = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin-protocol" }
pumpkin-util = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin-util" }
pumpkin-api-macros = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin-api-macros" }
pumpkin-world = { git = "https://github.com/Pumpkin-MC/Pumpkin.git", branch = "master", package = "pumpkin-world" }
tokio = "1"
tracing = "0.1"
@ -33,6 +34,8 @@ serde-saphyr = "0.0.17"
rand = { git = "https://github.com/rust-random/rand"}
prost = "0.14"
toml = "1.0"
[build-dependencies]
j4rs = { version = "0.24", artifact = "cdylib", lib = true }
env_logger = "0.11.8"

View File

@ -1,2 +1,3 @@
pub mod paper;
pub mod patchbukkit;
pub mod spigot;

View File

@ -0,0 +1,37 @@
use std::{fs, path::Path};
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize, Debug)]
pub struct PatchBukkitConfig {
pub settings: SettingsConfig,
}
#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "kebab-case")]
pub struct SettingsConfig {
pub minimum_supported_plugin_api: Option<String>,
}
impl PatchBukkitConfig {
pub const DEFAULT: PatchBukkitConfig = PatchBukkitConfig {
settings: SettingsConfig {
minimum_supported_plugin_api: None,
},
};
pub fn parse<S: AsRef<str>>(config: S) -> Result<Self, toml::de::Error> {
toml::from_str(config.as_ref())
}
pub fn get_or_create<P: AsRef<Path>>(config_path: P) -> anyhow::Result<Self> {
let config_path = config_path.as_ref();
if !config_path.exists() {
let default = PatchBukkitConfig::DEFAULT;
std::fs::write(config_path, &toml::to_string_pretty(&default)?)?;
return Ok(default);
}
let config = PatchBukkitConfig::parse(fs::read_to_string(config_path)?)?;
Ok(config)
}
}

View File

@ -7,8 +7,8 @@ use tokio::sync::{mpsc, oneshot};
use uuid::Uuid;
use crate::{
commands::SimpleCommandSender, events::handler::JvmEventPayload,
proto::patchbukkit::events::FireEventResponse,
commands::SimpleCommandSender, config::patchbukkit::PatchBukkitConfig,
events::handler::JvmEventPayload, proto::patchbukkit::events::FireEventResponse,
};
pub enum LoadPluginResult {
@ -26,6 +26,7 @@ pub enum JvmCommand {
respond_to: oneshot::Sender<Result<()>>,
context: Arc<Context>,
command_tx: mpsc::Sender<Self>,
config: PatchBukkitConfig,
},
LoadPlugin {
plugin_path: PathBuf,

View File

@ -48,11 +48,13 @@ impl JvmWorker {
respond_to,
context,
command_tx,
config,
} => {
init_callback_context(
context.clone(),
tokio::runtime::Handle::current(),
command_tx.clone(),
config,
)
.unwrap();
self.context = Some(context);

View File

@ -0,0 +1,21 @@
use crate::{
java::native_callbacks::CALLBACK_CONTEXT,
proto::patchbukkit::{common::EmptyRequest, config::GetPatchBukkitConfigResponse},
};
pub fn ffi_native_bridge_get_patch_bukkit_config_impl(
_request: EmptyRequest,
) -> Option<GetPatchBukkitConfigResponse> {
if let Some(context) = CALLBACK_CONTEXT.get() {
Some(GetPatchBukkitConfigResponse {
minimum_supported_plugin_api: context
.config
.settings
.minimum_supported_plugin_api
.clone()
.unwrap_or("0.0.0".to_string()),
})
} else {
None
}
}

View File

@ -0,0 +1,5 @@
// pub fn ffi_native_bridge_serialize_item_stack_impl(
// request: SerializeItemStackRequest,
// ) -> Option<SerializeItemStackResponse> {
// Some(())
// }

View File

@ -5,7 +5,10 @@ use j4rs::Jvm;
use pumpkin::plugin::Context;
use tokio::sync::mpsc;
use crate::{java::jvm::commands::JvmCommand, proto::initialize_ffi_callbacks};
use crate::{
config::patchbukkit::PatchBukkitConfig, java::jvm::commands::JvmCommand,
proto::initialize_ffi_callbacks,
};
mod abilities;
pub use abilities::*;
@ -30,23 +33,32 @@ pub mod utils;
pub mod log;
pub use log::*;
pub mod config;
pub use config::*;
pub mod itemstack;
pub use itemstack::*;
static CALLBACK_CONTEXT: OnceLock<CallbackContext> = OnceLock::new();
struct CallbackContext {
pub plugin_context: Arc<Context>,
pub runtime: tokio::runtime::Handle,
pub command_tx: mpsc::Sender<JvmCommand>,
pub config: PatchBukkitConfig,
}
pub fn init_callback_context(
plugin_context: Arc<Context>,
runtime: tokio::runtime::Handle,
command_tx: mpsc::Sender<JvmCommand>,
config: PatchBukkitConfig,
) -> Result<()> {
let context = CallbackContext {
plugin_context,
runtime,
command_tx,
config,
};
CALLBACK_CONTEXT

View File

@ -86,6 +86,7 @@ pub const DEPENDENCIES: &[(&str, &str, &str)] = &[
// These are deps of patchbukkit itself
("io.papermc.paper", "paper-api", "1.21.11-R0.1-SNAPSHOT"),
("com.google.protobuf", "protobuf-java", "4.33.5"),
("org.slf4j", "slf4j-jdk14", "2.0.16"),
];
pub fn setup_j4rs(j4rs_folder: &Path) -> Result<()> {

View File

@ -19,12 +19,15 @@ use tokio::sync::{
oneshot,
};
use crate::java::{
jvm::{
commands::{JvmCommand, LoadPluginResult},
worker::JvmWorker,
use crate::{
config::patchbukkit::PatchBukkitConfig,
java::{
jvm::{
commands::{JvmCommand, LoadPluginResult},
worker::JvmWorker,
},
resources::setup_j4rs,
},
resources::setup_j4rs,
};
async fn on_load_inner(plugin: &PatchBukkitPlugin, server: Arc<Context>) -> Result<(), String> {
@ -34,7 +37,10 @@ async fn on_load_inner(plugin: &PatchBukkitPlugin, server: Arc<Context>) -> Resu
// Setup directories
let dirs = setup_directories(&server)?;
proto::patchbukkit::common::Uuid::default();
let mut config_path = dirs.base.clone();
config_path.push("patchbukkit.config.toml");
let config = PatchBukkitConfig::get_or_create(config_path)
.map_err(|e| format!("Failed to setup PatchBukkit config: {e}"))?;
// Discover and prepare JAR files
let jar_paths = discover_jar_files(&dirs.plugins);
@ -112,6 +118,7 @@ async fn on_load_inner(plugin: &PatchBukkitPlugin, server: Arc<Context>) -> Resu
respond_to: tx,
context: server.clone(),
command_tx: plugin.command_tx.clone(),
config,
})
.await
.map_err(|e| format!("Failed to send command to initialize J4RS: {e}"))?;

View File

@ -30,6 +30,14 @@ pub mod patchbukkit {
pub mod log {
include!(concat!(env!("OUT_DIR"), "/patchbukkit.log.rs"));
}
pub mod config {
include!(concat!(env!("OUT_DIR"), "/patchbukkit.config.rs"));
}
pub mod itemstack {
include!(concat!(env!("OUT_DIR"), "/patchbukkit.itemstack.rs"));
}
}
include!(concat!(env!("OUT_DIR"), "/ffi_init.rs"));