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-transport-http:1.9.18")
implementation("org.apache.maven.resolver:maven-resolver-util:1.9.18") implementation("org.apache.maven.resolver:maven-resolver-util:1.9.18")
implementation("com.google.protobuf:protobuf-java:$protobufVersion") implementation("com.google.protobuf:protobuf-java:$protobufVersion")
implementation("org.slf4j:slf4j-jdk14:2.0.16")
} }
java { 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.command.PatchBukkitConsoleCommandSender;
import org.patchbukkit.events.PatchBukkitEventManager; import org.patchbukkit.events.PatchBukkitEventManager;
import org.patchbukkit.scheduler.PatchBukkitScheduler; import org.patchbukkit.scheduler.PatchBukkitScheduler;
import org.patchbukkit.versioning.Versioning;
import patchbukkit.bridge.NativeBridgeFfi; import patchbukkit.bridge.NativeBridgeFfi;
import patchbukkit.log.LogLevel; import patchbukkit.log.LogLevel;
import patchbukkit.log.SendLogRequest; import patchbukkit.log.SendLogRequest;
@ -114,7 +116,7 @@ public class PatchBukkitServer implements Server {
configureRootLogger(); 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.SEVERE, LogLevel.SEVERE,
Level.WARNING, LogLevel.WARNING, Level.WARNING, LogLevel.WARNING,
Level.INFO, LogLevel.INFO, Level.INFO, LogLevel.INFO,

View File

@ -12,11 +12,14 @@ import java.util.Map;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.HoverEvent; 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.flattener.ComponentFlattener;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer; import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import patchbukkit.bridge.NativeBridgeFfi;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.advancement.Advancement; import org.bukkit.advancement.Advancement;
import org.bukkit.attribute.Attributable; import org.bukkit.attribute.Attributable;
@ -25,6 +28,7 @@ import org.bukkit.attribute.AttributeModifier;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.damage.DamageSource; import org.bukkit.damage.DamageSource;
import org.bukkit.damage.DamageSource.Builder;
import org.bukkit.damage.DamageType; import org.bukkit.damage.DamageType;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
@ -38,9 +42,14 @@ import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.potion.PotionType; import org.bukkit.potion.PotionType;
import org.bukkit.potion.PotionType.InternalPotionData;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jspecify.annotations.NonNull; 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") @SuppressWarnings("removal")
public class PatchBukkitUnsafeValues implements UnsafeValues { public class PatchBukkitUnsafeValues implements UnsafeValues {
@ -48,342 +57,334 @@ public class PatchBukkitUnsafeValues implements UnsafeValues {
public static final PatchBukkitUnsafeValues INSTANCE = public static final PatchBukkitUnsafeValues INSTANCE =
new PatchBukkitUnsafeValues(); 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 @Override
public boolean isSupportedApiVersion(String apiVersion) { public boolean isSupportedApiVersion(String apiVersion) {
// Accept common API versions if (apiVersion == null) return false;
return ( final ApiVersion toCheck = ApiVersion.getOrCreateVersion(apiVersion);
apiVersion != null && var minimumApi = NativeBridgeFfi.getPatchBukkitConfig(EmptyRequest.newBuilder().build()).getMinimumSupportedPluginApi();
(apiVersion.equals("1.21") || apiVersion.equals("1.20")) final ApiVersion minimumVersion = ApiVersion.getOrCreateVersion(minimumApi);
);
}
@Override return !toCheck.isNewerThan(ApiVersion.CURRENT) && !toCheck.isOlderThan(minimumVersion);
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;
} }
@Override @Override
public void checkSupported(PluginDescriptionFile pdf) public void checkSupported(PluginDescriptionFile pdf)
throws InvalidPluginException { throws InvalidPluginException {
// Be lenient during development - accept most plugins
String api = pdf.getAPIVersion(); String api = pdf.getAPIVersion();
if (api != null && !isSupportedApiVersion(api)) { if (api != null && !isSupportedApiVersion(api)) {
throw new InvalidPluginException("Unsupported API: " + api); throw new InvalidPluginException("Unsupported API: " + api);
} }
} }
@Override @Override
public byte[] processClass( public ComponentFlattener componentFlattener() {
PluginDescriptionFile pdf, // TODO Auto-generated method stub
String path, throw new UnsupportedOperationException("Unimplemented method 'componentFlattener'");
byte[] clazz }
) {
// Return unchanged - skip Commodore remapping for now
return clazz;
}
@Override @Override
public Advancement loadAdvancement(NamespacedKey key, String advancement) { public PlainComponentSerializer plainComponentSerializer() {
return null; // TODO Auto-generated method stub
} throw new UnsupportedOperationException("Unimplemented method 'plainComponentSerializer'");
}
@Override @Override
public boolean removeAdvancement(NamespacedKey key) { public PlainTextComponentSerializer plainTextSerializer() {
return false; // TODO Auto-generated method stub
} throw new UnsupportedOperationException("Unimplemented method 'plainTextSerializer'");
}
@Override @Override
public Multimap<Attribute, AttributeModifier> getDefaultAttributeModifiers( public GsonComponentSerializer gsonComponentSerializer() {
Material material, // TODO Auto-generated method stub
EquipmentSlot slot throw new UnsupportedOperationException("Unimplemented method 'gsonComponentSerializer'");
) { }
return null;
}
@Override @Override
public CreativeCategory getCreativeCategory(Material material) { public GsonComponentSerializer colorDownsamplingGsonComponentSerializer() {
return null; // TODO Auto-generated method stub
} throw new UnsupportedOperationException("Unimplemented method 'colorDownsamplingGsonComponentSerializer'");
}
@Override @Override
public String getBlockTranslationKey(Material material) { public LegacyComponentSerializer legacyComponentSerializer() {
return ""; // TODO Auto-generated method stub
} throw new UnsupportedOperationException("Unimplemented method 'legacyComponentSerializer'");
}
@Override @Override
public String getItemTranslationKey(Material material) { public Component resolveWithContext(Component component, CommandSender context, Entity scoreboardSubject,
return ""; boolean bypassPermissions) throws IOException {
} // TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'resolveWithContext'");
}
@Override @Override
public String getTranslationKey(EntityType entityType) { public Material toLegacy(Material material) {
return ""; // TODO Auto-generated method stub
} throw new UnsupportedOperationException("Unimplemented method 'toLegacy'");
}
@Override @Override
public String getTranslationKey(ItemStack itemStack) { public Material fromLegacy(Material material) {
return ""; return PatchBukkitLegacy.fromLegacy(material);
} }
@Override @Override
public String getTranslationKey(Attribute attribute) { public Material fromLegacy(MaterialData material) {
return ""; return PatchBukkitLegacy.fromLegacy(material);
} }
@Override @Override
public PotionType.InternalPotionData getInternalPotionData( public Material fromLegacy(MaterialData material, boolean itemPriority) {
NamespacedKey key return PatchBukkitLegacy.fromLegacy(material, itemPriority);
) { }
return null;
}
@Override @Override
public DamageSource.@NotNull Builder createDamageSourceBuilder( public BlockData fromLegacy(Material material, byte data) {
@NotNull DamageType damageType // TODO Auto-generated method stub
) { throw new UnsupportedOperationException("Unimplemented method 'fromLegacy'");
return null; }
}
@Override @Override
public String get(Class<?> aClass, String value) { public Material getMaterial(String material, int version) {
return ""; // TODO Auto-generated method stub
} throw new UnsupportedOperationException("Unimplemented method 'getMaterial'");
}
@Override @Override
public <B extends Keyed> B get(RegistryKey<B> registry, NamespacedKey key) { public int getDataVersion() {
return null; // TODO Auto-generated method stub
} throw new UnsupportedOperationException("Unimplemented method 'getDataVersion'");
}
// Adventure components - commonly used @Override
@Override public ItemStack modifyItemStack(ItemStack stack, String arguments) {
public ComponentFlattener componentFlattener() { // TODO Auto-generated method stub
return ComponentFlattener.basic(); throw new UnsupportedOperationException("Unimplemented method 'modifyItemStack'");
} }
@Override @Override
public PlainComponentSerializer plainComponentSerializer() { public byte[] processClass(PluginDescriptionFile pdf, String path, byte[] clazz) {
return null; // TODO Auto-generated method stub
} throw new UnsupportedOperationException("Unimplemented method 'processClass'");
}
@Override @Override
public PlainTextComponentSerializer plainTextSerializer() { public Advancement loadAdvancement(NamespacedKey key, String advancement) {
return null; // TODO Auto-generated method stub
} throw new UnsupportedOperationException("Unimplemented method 'loadAdvancement'");
}
@Override @Override
public GsonComponentSerializer gsonComponentSerializer() { public boolean removeAdvancement(NamespacedKey key) {
return null; // TODO Auto-generated method stub
} throw new UnsupportedOperationException("Unimplemented method 'removeAdvancement'");
}
@Override @Override
public GsonComponentSerializer colorDownsamplingGsonComponentSerializer() { public Multimap<Attribute, AttributeModifier> getDefaultAttributeModifiers(Material material, EquipmentSlot slot) {
return null; // TODO Auto-generated method stub
} throw new UnsupportedOperationException("Unimplemented method 'getDefaultAttributeModifiers'");
}
@Override @Override
public LegacyComponentSerializer legacyComponentSerializer() { public CreativeCategory getCreativeCategory(Material material) {
return null; // TODO Auto-generated method stub
} throw new UnsupportedOperationException("Unimplemented method 'getCreativeCategory'");
}
@Override @Override
public Component resolveWithContext( public String getBlockTranslationKey(Material material) {
Component component, // TODO Auto-generated method stub
CommandSender context, throw new UnsupportedOperationException("Unimplemented method 'getBlockTranslationKey'");
Entity scoreboardSubject, }
boolean bypassPermissions
) throws IOException {
return null;
}
// Stub everything else @Override
@Override public String getItemTranslationKey(Material material) {
public Material toLegacy(Material material) { // TODO Auto-generated method stub
throw new UnsupportedOperationException( throw new UnsupportedOperationException("Unimplemented method 'getItemTranslationKey'");
"Pumpkin doesn't support legacy materials" }
);
}
@Override @Override
public Material fromLegacy(Material material) { public String getTranslationKey(EntityType entityType) {
return null; // TODO Auto-generated method stub
} throw new UnsupportedOperationException("Unimplemented method 'getTranslationKey'");
}
@Override @Override
public Material fromLegacy(MaterialData material) { public String getTranslationKey(ItemStack itemStack) {
return null; // TODO Auto-generated method stub
} throw new UnsupportedOperationException("Unimplemented method 'getTranslationKey'");
}
@Override @Override
public Material fromLegacy(MaterialData material, boolean itemPriority) { public String getTranslationKey(Attribute attribute) {
return null; // TODO Auto-generated method stub
} throw new UnsupportedOperationException("Unimplemented method 'getTranslationKey'");
}
@Override @Override
public BlockData fromLegacy(Material material, byte data) { public InternalPotionData getInternalPotionData(NamespacedKey key) {
return null; // TODO Auto-generated method stub
} throw new UnsupportedOperationException("Unimplemented method 'getInternalPotionData'");
}
@Override @Override
public Material getMaterial(String material, int version) { public @NotNull Builder createDamageSourceBuilder(@NotNull DamageType damageType) {
return null; // 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 java.util.UUID;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Server; import org.bukkit.Server;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.permissions.PermissibleBase; import org.bukkit.permissions.PermissibleBase;
@ -18,6 +19,11 @@ import org.jetbrains.annotations.Nullable;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.md_5.bungee.api.chat.BaseComponent; 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 { public class PatchBukkitCommandSender implements CommandSender, ServerOperator {
private final Server server; private final Server server;
@ -133,7 +139,14 @@ public class PatchBukkitCommandSender implements CommandSender, ServerOperator {
if (this.server != null && this.server.getLogger() != null) { if (this.server != null && this.server.getLogger() != null) {
this.server.getLogger().info(message); this.server.getLogger().info(message);
} else { } 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.BufferedReader;
import java.io.IOException; import java.io.IOException;

View File

@ -5,6 +5,7 @@ import "google/protobuf/empty.proto";
import "google/protobuf/wrappers.proto"; import "google/protobuf/wrappers.proto";
import "patchbukkit/abilities.proto"; import "patchbukkit/abilities.proto";
import "patchbukkit/common/types.proto"; import "patchbukkit/common/types.proto";
import "patchbukkit/config.proto";
import "patchbukkit/events/event.proto"; import "patchbukkit/events/event.proto";
import "patchbukkit/log.proto"; import "patchbukkit/log.proto";
import "patchbukkit/message.proto"; import "patchbukkit/message.proto";
@ -28,4 +29,6 @@ service NativeBridge {
rpc PlayerPlaySound(patchbukkit.sound.PlayerPlaySoundRequest) returns (google.protobuf.Empty); rpc PlayerPlaySound(patchbukkit.sound.PlayerPlaySoundRequest) returns (google.protobuf.Empty);
rpc SendLog(patchbukkit.log.SendLogRequest) 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 yaw = 3;
float pitch = 4; 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]] [[package]]
name = "constant_time_eq" name = "constant_time_eq"
version = "0.4.2" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
[[package]] [[package]]
name = "cookie" name = "cookie"
@ -1132,18 +1132,16 @@ dependencies = [
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.4.1" version = "0.4.0-rc.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" checksum = "74f70a332ddf75e5e5e43284304179ba02f391f82f692f030b08a8378adf3c99"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys",
"libc", "libc",
"r-efi", "r-efi",
"rand_core 0.10.0", "rand_core 0.10.0",
"wasip2", "wasip2",
"wasip3", "wasip3",
"wasm-bindgen",
] ]
[[package]] [[package]]
@ -1905,12 +1903,14 @@ dependencies = [
"pumpkin-data", "pumpkin-data",
"pumpkin-protocol", "pumpkin-protocol",
"pumpkin-util", "pumpkin-util",
"pumpkin-world",
"rand", "rand",
"rust-embed", "rust-embed",
"serde", "serde",
"serde-saphyr", "serde-saphyr",
"serde_json", "serde_json",
"tokio", "tokio",
"toml 1.0.2+spec-1.1.0",
"tracing", "tracing",
"uuid", "uuid",
"walkdir", "walkdir",
@ -2193,7 +2193,7 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin" name = "pumpkin"
version = "0.1.0-dev+1.21.11" 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 = [ dependencies = [
"arc-swap", "arc-swap",
"base64", "base64",
@ -2203,7 +2203,6 @@ dependencies = [
"futures", "futures",
"hmac 0.13.0-rc.5", "hmac 0.13.0-rc.5",
"libloading 0.9.0", "libloading 0.9.0",
"log",
"num-bigint", "num-bigint",
"pkcs8 0.11.0-rc.10", "pkcs8 0.11.0-rc.10",
"pumpkin-config", "pumpkin-config",
@ -2235,7 +2234,7 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin-api-macros" name = "pumpkin-api-macros"
version = "0.1.0-dev+1.21.11" 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 = [ dependencies = [
"proc-macro-error2", "proc-macro-error2",
"proc-macro2", "proc-macro2",
@ -2246,19 +2245,19 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin-config" name = "pumpkin-config"
version = "0.1.0-dev+1.21.11" 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 = [ dependencies = [
"log",
"pumpkin-util", "pumpkin-util",
"serde", "serde",
"toml", "toml 0.9.11+spec-1.1.0",
"tracing",
"uuid", "uuid",
] ]
[[package]] [[package]]
name = "pumpkin-data" name = "pumpkin-data"
version = "0.1.0-dev+1.21.11" 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 = [ dependencies = [
"crc-fast", "crc-fast",
"phf", "phf",
@ -2270,22 +2269,22 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin-inventory" name = "pumpkin-inventory"
version = "0.1.0-dev+1.21.11" 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 = [ dependencies = [
"crossbeam-utils", "crossbeam-utils",
"log",
"pumpkin-data", "pumpkin-data",
"pumpkin-protocol", "pumpkin-protocol",
"pumpkin-util", "pumpkin-util",
"pumpkin-world", "pumpkin-world",
"thiserror", "thiserror",
"tokio", "tokio",
"tracing",
] ]
[[package]] [[package]]
name = "pumpkin-macros" name = "pumpkin-macros"
version = "0.1.0-dev+1.21.11" 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 = [ dependencies = [
"proc-macro-error2", "proc-macro-error2",
"pumpkin-data", "pumpkin-data",
@ -2296,7 +2295,7 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin-nbt" name = "pumpkin-nbt"
version = "0.1.0-dev+1.21.11" 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 = [ dependencies = [
"bytes", "bytes",
"cesu8", "cesu8",
@ -2308,13 +2307,14 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin-protocol" name = "pumpkin-protocol"
version = "0.1.0-dev+1.21.11" 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 = [ dependencies = [
"aes", "aes",
"async-compression", "async-compression",
"bitflags", "bitflags",
"bytes", "bytes",
"cfb8", "cfb8",
"flate2",
"pumpkin-data", "pumpkin-data",
"pumpkin-macros", "pumpkin-macros",
"pumpkin-nbt", "pumpkin-nbt",
@ -2330,7 +2330,7 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin-util" name = "pumpkin-util"
version = "0.1.0-dev+1.21.11" 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 = [ dependencies = [
"base64", "base64",
"bytes", "bytes",
@ -2355,7 +2355,7 @@ dependencies = [
[[package]] [[package]]
name = "pumpkin-world" name = "pumpkin-world"
version = "0.1.0-dev+1.21.11" 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 = [ dependencies = [
"bitflags", "bitflags",
"bytes", "bytes",
@ -2366,7 +2366,6 @@ dependencies = [
"flate2", "flate2",
"futures", "futures",
"itertools", "itertools",
"log",
"lz4-java-wrc", "lz4-java-wrc",
"num_cpus", "num_cpus",
"pumpkin-config", "pumpkin-config",
@ -2383,6 +2382,7 @@ dependencies = [
"thiserror", "thiserror",
"tokio", "tokio",
"tokio-util", "tokio-util",
"tracing",
"uuid", "uuid",
] ]
@ -2417,7 +2417,7 @@ version = "0.10.0-rc.9"
source = "git+https://github.com/rust-random/rand#72afe1e973fcd83d840cf597888223072bbdb04c" source = "git+https://github.com/rust-random/rand#72afe1e973fcd83d840cf597888223072bbdb04c"
dependencies = [ dependencies = [
"chacha20", "chacha20",
"getrandom 0.4.1", "getrandom 0.4.0-rc.1",
"rand_core 0.10.0", "rand_core 0.10.0",
] ]
@ -3124,9 +3124,9 @@ dependencies = [
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.47" version = "0.3.46"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" checksum = "9da98b7d9b7dad93488a84b8248efc35352b0b2657397d4167e7ad67e5d535e5"
dependencies = [ dependencies = [
"deranged", "deranged",
"itoa", "itoa",
@ -3146,9 +3146,9 @@ checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
[[package]] [[package]]
name = "time-macros" name = "time-macros"
version = "0.2.27" version = "0.2.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" checksum = "78cc610bac2dcee56805c99642447d4c5dbde4d01f752ffea0199aee1f601dc4"
dependencies = [ dependencies = [
"num-conv", "num-conv",
"time-core", "time-core",
@ -3214,7 +3214,22 @@ dependencies = [
"indexmap", "indexmap",
"serde_core", "serde_core",
"serde_spanned", "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_parser",
"toml_writer", "toml_writer",
"winnow", "winnow",
@ -3230,10 +3245,19 @@ dependencies = [
] ]
[[package]] [[package]]
name = "toml_parser" name = "toml_datetime"
version = "1.0.6+spec-1.1.0" version = "1.0.0+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" 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 = [ dependencies = [
"winnow", "winnow",
] ]
@ -3524,16 +3548,16 @@ version = "1.0.2+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
dependencies = [ dependencies = [
"wit-bindgen", "wit-bindgen 0.51.0",
] ]
[[package]] [[package]]
name = "wasip3" 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" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" checksum = "87ba4be47b1d11244670d11857eee0758a8f2c39aea64d80b78c1ce29b4642cd"
dependencies = [ dependencies = [
"wit-bindgen", "wit-bindgen 0.48.1",
] ]
[[package]] [[package]]
@ -3583,9 +3607,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-encoder" name = "wasm-encoder"
version = "0.244.0" version = "0.241.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" checksum = "e01164c9dda68301e34fdae536c23ed6fe90ce6d97213ccc171eebbd3d02d6b8"
dependencies = [ dependencies = [
"leb128fmt", "leb128fmt",
"wasmparser", "wasmparser",
@ -3593,9 +3617,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-metadata" name = "wasm-metadata"
version = "0.244.0" version = "0.241.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" checksum = "876fe286f2fa416386deedebe8407e6f19e0b5aeaef3d03161e77a15fa80f167"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"indexmap", "indexmap",
@ -3605,9 +3629,9 @@ dependencies = [
[[package]] [[package]]
name = "wasmparser" name = "wasmparser"
version = "0.244.0" version = "0.241.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" checksum = "46d90019b1afd4b808c263e428de644f3003691f243387d30d673211ee0cb8e8"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"hashbrown 0.15.5", "hashbrown 0.15.5",
@ -3834,18 +3858,24 @@ checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
[[package]] [[package]]
name = "wit-bindgen" name = "wit-bindgen"
version = "0.51.0" version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" checksum = "7f8c2adb5f74ac9395bc3121c99a1254bf9310482c27b13f97167aedb5887138"
dependencies = [ dependencies = [
"wit-bindgen-rust-macro", "wit-bindgen-rust-macro",
] ]
[[package]] [[package]]
name = "wit-bindgen-core" name = "wit-bindgen"
version = "0.51.0" version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index" 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 = [ dependencies = [
"anyhow", "anyhow",
"heck", "heck",
@ -3854,9 +3884,9 @@ dependencies = [
[[package]] [[package]]
name = "wit-bindgen-rust" name = "wit-bindgen-rust"
version = "0.51.0" version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" checksum = "69667efa439a453e1d50dac939c6cab6d2c3ac724a9d232b6631dad2472a5b70"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"heck", "heck",
@ -3870,9 +3900,9 @@ dependencies = [
[[package]] [[package]]
name = "wit-bindgen-rust-macro" name = "wit-bindgen-rust-macro"
version = "0.51.0" version = "0.48.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" checksum = "eae2e22cceb5d105d52326c07e3e67603a861cc7add70fc467f7cc7ec5265017"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"prettyplease", "prettyplease",
@ -3885,9 +3915,9 @@ dependencies = [
[[package]] [[package]]
name = "wit-component" name = "wit-component"
version = "0.244.0" version = "0.241.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" checksum = "1fd0c57df25e7ee612d946d3b7646c1ddb2310f8280aa2c17e543b66e0812241"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bitflags", "bitflags",
@ -3904,9 +3934,9 @@ dependencies = [
[[package]] [[package]]
name = "wit-parser" name = "wit-parser"
version = "0.244.0" version = "0.241.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" checksum = "09ef1c6ad67f35c831abd4039c02894de97034100899614d1c44e2268ad01c91"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"id-arena", "id-arena",
@ -4045,9 +4075,9 @@ dependencies = [
[[package]] [[package]]
name = "zip" name = "zip"
version = "7.4.0" version = "7.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc12baa6db2b15a140161ce53d72209dacea594230798c24774139b54ecaa980" checksum = "c42e33efc22a0650c311c2ef19115ce232583abbe80850bc8b66509ebef02de0"
dependencies = [ dependencies = [
"aes", "aes",
"bzip2", "bzip2",
@ -4055,7 +4085,8 @@ dependencies = [
"crc32fast", "crc32fast",
"deflate64", "deflate64",
"flate2", "flate2",
"getrandom 0.4.1", "generic-array",
"getrandom 0.3.4",
"hmac 0.12.1", "hmac 0.12.1",
"indexmap", "indexmap",
"lzma-rust2", "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-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-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-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" tokio = "1"
tracing = "0.1" tracing = "0.1"
@ -33,6 +34,8 @@ serde-saphyr = "0.0.17"
rand = { git = "https://github.com/rust-random/rand"} rand = { git = "https://github.com/rust-random/rand"}
prost = "0.14" prost = "0.14"
toml = "1.0"
[build-dependencies] [build-dependencies]
j4rs = { version = "0.24", artifact = "cdylib", lib = true } j4rs = { version = "0.24", artifact = "cdylib", lib = true }
env_logger = "0.11.8" env_logger = "0.11.8"

View File

@ -1,2 +1,3 @@
pub mod paper; pub mod paper;
pub mod patchbukkit;
pub mod spigot; 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 uuid::Uuid;
use crate::{ use crate::{
commands::SimpleCommandSender, events::handler::JvmEventPayload, commands::SimpleCommandSender, config::patchbukkit::PatchBukkitConfig,
proto::patchbukkit::events::FireEventResponse, events::handler::JvmEventPayload, proto::patchbukkit::events::FireEventResponse,
}; };
pub enum LoadPluginResult { pub enum LoadPluginResult {
@ -26,6 +26,7 @@ pub enum JvmCommand {
respond_to: oneshot::Sender<Result<()>>, respond_to: oneshot::Sender<Result<()>>,
context: Arc<Context>, context: Arc<Context>,
command_tx: mpsc::Sender<Self>, command_tx: mpsc::Sender<Self>,
config: PatchBukkitConfig,
}, },
LoadPlugin { LoadPlugin {
plugin_path: PathBuf, plugin_path: PathBuf,

View File

@ -48,11 +48,13 @@ impl JvmWorker {
respond_to, respond_to,
context, context,
command_tx, command_tx,
config,
} => { } => {
init_callback_context( init_callback_context(
context.clone(), context.clone(),
tokio::runtime::Handle::current(), tokio::runtime::Handle::current(),
command_tx.clone(), command_tx.clone(),
config,
) )
.unwrap(); .unwrap();
self.context = Some(context); 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 pumpkin::plugin::Context;
use tokio::sync::mpsc; 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; mod abilities;
pub use abilities::*; pub use abilities::*;
@ -30,23 +33,32 @@ pub mod utils;
pub mod log; pub mod log;
pub use log::*; pub use log::*;
pub mod config;
pub use config::*;
pub mod itemstack;
pub use itemstack::*;
static CALLBACK_CONTEXT: OnceLock<CallbackContext> = OnceLock::new(); static CALLBACK_CONTEXT: OnceLock<CallbackContext> = OnceLock::new();
struct CallbackContext { struct CallbackContext {
pub plugin_context: Arc<Context>, pub plugin_context: Arc<Context>,
pub runtime: tokio::runtime::Handle, pub runtime: tokio::runtime::Handle,
pub command_tx: mpsc::Sender<JvmCommand>, pub command_tx: mpsc::Sender<JvmCommand>,
pub config: PatchBukkitConfig,
} }
pub fn init_callback_context( pub fn init_callback_context(
plugin_context: Arc<Context>, plugin_context: Arc<Context>,
runtime: tokio::runtime::Handle, runtime: tokio::runtime::Handle,
command_tx: mpsc::Sender<JvmCommand>, command_tx: mpsc::Sender<JvmCommand>,
config: PatchBukkitConfig,
) -> Result<()> { ) -> Result<()> {
let context = CallbackContext { let context = CallbackContext {
plugin_context, plugin_context,
runtime, runtime,
command_tx, command_tx,
config,
}; };
CALLBACK_CONTEXT CALLBACK_CONTEXT

View File

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

View File

@ -19,12 +19,15 @@ use tokio::sync::{
oneshot, oneshot,
}; };
use crate::java::{ use crate::{
jvm::{ config::patchbukkit::PatchBukkitConfig,
commands::{JvmCommand, LoadPluginResult}, java::{
worker::JvmWorker, jvm::{
commands::{JvmCommand, LoadPluginResult},
worker::JvmWorker,
},
resources::setup_j4rs,
}, },
resources::setup_j4rs,
}; };
async fn on_load_inner(plugin: &PatchBukkitPlugin, server: Arc<Context>) -> Result<(), String> { 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 // Setup directories
let dirs = setup_directories(&server)?; 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 // Discover and prepare JAR files
let jar_paths = discover_jar_files(&dirs.plugins); 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, respond_to: tx,
context: server.clone(), context: server.clone(),
command_tx: plugin.command_tx.clone(), command_tx: plugin.command_tx.clone(),
config,
}) })
.await .await
.map_err(|e| format!("Failed to send command to initialize J4RS: {e}"))?; .map_err(|e| format!("Failed to send command to initialize J4RS: {e}"))?;

View File

@ -30,6 +30,14 @@ pub mod patchbukkit {
pub mod log { pub mod log {
include!(concat!(env!("OUT_DIR"), "/patchbukkit.log.rs")); 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")); include!(concat!(env!("OUT_DIR"), "/ffi_init.rs"));