Skip to content

Commit

Permalink
Add datapack registration lifecycle event (#11804)
Browse files Browse the repository at this point in the history
  • Loading branch information
Machine-Maker authored Dec 28, 2024
1 parent dac977a commit feb8756
Show file tree
Hide file tree
Showing 14 changed files with 691 additions and 114 deletions.
2 changes: 2 additions & 0 deletions build-data/paper.at
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ public net.minecraft.server.network.ServerLoginPacketListenerImpl connection
public net.minecraft.server.network.ServerLoginPacketListenerImpl state
public net.minecraft.server.network.ServerLoginPacketListenerImpl$State
public net.minecraft.server.packs.VanillaPackResourcesBuilder safeGetPath(Ljava/net/URI;)Ljava/nio/file/Path;
public net.minecraft.server.packs.repository.FolderRepositorySource$FolderPackDetector
public net.minecraft.server.packs.repository.FolderRepositorySource$FolderPackDetector <init>(Lnet/minecraft/world/level/validation/DirectoryValidator;)V
public net.minecraft.server.packs.repository.Pack resources
public net.minecraft.server.players.PlayerList playerIo
public net.minecraft.server.players.PlayerList players
Expand Down
64 changes: 10 additions & 54 deletions paper-api/src/main/java/io/papermc/paper/datapack/Datapack.java
Original file line number Diff line number Diff line change
@@ -1,61 +1,17 @@
package io.papermc.paper.datapack;

import java.util.Set;
import net.kyori.adventure.text.Component;
import org.bukkit.FeatureFlag;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.NullMarked;

/**
* This is a snapshot of a datapack on the server. It
* won't be updated as datapacks are updated.
*/
@NullMarked
public interface Datapack {

/**
* Gets the name/id of this datapack.
*
* @return the name of the pack
*/
@Contract(pure = true)
String getName();

/**
* Gets the title component of this datapack.
*
* @return the title
*/
Component getTitle();

/**
* Gets the description component of this datapack.
*
* @return the description
*/
Component getDescription();

/**
* Gets if this datapack is required to be enabled.
*
* @return true if the pack is required
*/
boolean isRequired();

/**
* Gets the compatibility status of this pack.
*
* @return the compatibility of the pack
*/
Compatibility getCompatibility();

/**
* Gets the set of required features for this datapack.
*
* @return the set of required features
*/
@Unmodifiable Set<FeatureFlag> getRequiredFeatures();
@ApiStatus.NonExtendable
public interface Datapack extends DiscoveredDatapack {

/**
* Gets the enabled state of this pack.
Expand All @@ -74,13 +30,6 @@ public interface Datapack {
*/
void setEnabled(boolean enabled);

/**
* Gets the source for this datapack.
*
* @return the pack source
*/
DatapackSource getSource();

/**
* Computes the component vanilla Minecraft uses
* to display this datapack. Includes the {@link #getSource()},
Expand All @@ -96,4 +45,11 @@ enum Compatibility {
TOO_NEW,
COMPATIBLE,
}

/**
* Position of the pack in the load order.
*/
enum Position {
TOP, BOTTOM
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
package io.papermc.paper.datapack;

import io.papermc.paper.plugin.configuration.PluginMeta;
import io.papermc.paper.plugin.lifecycle.event.registrar.Registrar;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Path;
import java.util.Map;
import java.util.function.Consumer;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

/**
* The registrar for datapacks. The event for this registrar
* is called anytime the game tries to discover datapacks at any of the
* configured locations. This means that if a datapack should stay available to the server,
* it must always be discovered whenever this event fires.
* <p>An example of a plugin loading a datapack from within it's own jar is below</p>
* <pre>{@code
* public class YourPluginBootstrap implements PluginBootstrap {
* @Override
* public void bootstrap(BoostrapContext context) {
* final LifecycleEventManager<BootstrapContext> manager = context.getLifecycleManager();
* manager.registerEventHandler(LifecycleEvents.DATAPACK_DISCOVERY, event -> {
* DatapackRegistrar registrar = event.registrar();
* try {
* final URI uri = Objects.requireNonNull(
* YourPluginBootstrap.class.getResource("/pack")
* ).toURI();
* registrar.discoverPack(uri, "packId");
* } catch (final URISyntaxException | IOException e) {
* throw new RuntimeException(e);
* }
* });
* }
* }
* }</pre>
* @see io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents#DATAPACK_DISCOVERY
*/
@ApiStatus.NonExtendable
@ApiStatus.Experimental
@NullMarked
public interface DatapackRegistrar extends Registrar {

/**
* Checks if a datapack with the specified name has been discovered.
*
* @param name the name of the pack
* @return true if the pack has been discovered
* @see Datapack#getName()
*/
@Contract(pure = true)
boolean hasPackDiscovered(String name);

/**
* Gets a discovered datapack by its name.
*
* @param name the name of the pack
* @return the datapack
* @throws java.util.NoSuchElementException if the pack is not discovered
* @see Datapack#getName()
*/
@Contract(pure = true)
DiscoveredDatapack getDiscoveredPack(String name);

/**
* Removes a discovered datapack by its name.
*
* @param name the name of the pack
* @return true if the pack was removed
* @see Datapack#getName()
*/
@Contract(mutates = "this")
boolean removeDiscoveredPack(String name);

/**
* Gets all discovered datapacks.
*
* @return an unmodifiable map of discovered packs
*/
@Contract(pure = true)
@Unmodifiable Map<String, DiscoveredDatapack> getDiscoveredPacks();

/**
* Discovers a datapack at the specified {@link URI} with the id.
* <p>Symlinks obey the {@code allowed_symlinks.txt} in the server root directory.</p>
*
* @param uri the location of the pack
* @param id a unique id (will be combined with plugin for the datapacks name)
* @return the discovered datapack (or null if it failed)
* @throws IOException if any IO error occurs
*/
default @Nullable DiscoveredDatapack discoverPack(final URI uri, final String id) throws IOException {
return this.discoverPack(uri, id, c -> {});
}

/**
* Discovers a datapack at the specified {@link URI} with the id.
* <p>Symlinks obey the {@code allowed_symlinks.txt} in the server root directory.</p>
*
* @param uri the location of the pack
* @param id a unique id (will be combined with plugin for the datapacks name)
* @param configurer a configurer for extra options
* @return the discovered datapack (or null if it failed)
* @throws IOException if any IO error occurs
*/
@Nullable DiscoveredDatapack discoverPack(URI uri, String id, Consumer<Configurer> configurer) throws IOException;

/**
* Discovers a datapack at the specified {@link Path} with the id.
* <p>Symlinks obey the {@code allowed_symlinks.txt} in the server root directory.</p>
*
* @param path the location of the pack
* @param id a unique id (will be combined with plugin for the datapacks name)
* @return the discovered datapack (or null if it failed)
* @throws IOException if any IO error occurs
*/
default @Nullable DiscoveredDatapack discoverPack(final Path path, final String id) throws IOException {
return this.discoverPack(path, id, c -> {});
}

/**
* Discovers a datapack at the specified {@link Path} with the id.
* <p>Symlinks obey the {@code allowed_symlinks.txt} in the server root directory.</p>
*
* @param path the location of the pack
* @param id a unique id (will be combined with plugin for the datapacks name)
* @param configurer a configurer for extra options
* @return the discovered datapack (or null if it failed)
* @throws IOException if any IO error occurs
*/
@Nullable DiscoveredDatapack discoverPack(Path path, String id, Consumer<Configurer> configurer) throws IOException;

/**
* Discovers a datapack at the specified {@link URI} with the id.
* <p>Symlinks obey the {@code allowed_symlinks.txt} in the server root directory.</p>
*
* @param pluginMeta the plugin which will be the "owner" of this datapack
* @param uri the location of the pack
* @param id a unique id (will be combined with plugin for the datapacks name)
* @param configurer a configurer for extra options
* @return the discovered datapack (or null if it failed)
* @throws IOException if any IO error occurs
*/
@Nullable DiscoveredDatapack discoverPack(PluginMeta pluginMeta, URI uri, String id, Consumer<Configurer> configurer) throws IOException;

/**
* Discovers a datapack at the specified {@link Path} with the id.
* <p>Symlinks obey the {@code allowed_symlinks.txt} in the server root directory.</p>
*
* @param pluginMeta the plugin which will be the "owner" of this datapack
* @param path the location of the pack
* @param id a unique id (will be combined with plugin for the datapacks name)
* @param configurer a configurer for extra options
* @return the discovered datapack (or null if it failed)
* @throws IOException if any IO error occurs
*/
@Nullable DiscoveredDatapack discoverPack(PluginMeta pluginMeta, Path path, String id, Consumer<Configurer> configurer) throws IOException;

/**
* Configures additional, optional, details about a datapack.
*/
@ApiStatus.NonExtendable
@ApiStatus.Experimental
interface Configurer {

/**
* Changes the title of the datapack from the default which
* is just the "id" in the {@code registerPack} methods.
*
* @param title the new title
* @return the configurer for chaining
*/
@Contract(value = "_ -> this", mutates = "this")
Configurer title(Component title);

/**
* Sets whether this pack is going to be automatically enabled on server starts even if previously disabled.
* Defaults to false.
*
* @param autoEnableOnServerStart true to ensure the pack is enabled on server starts.
* @return the configurer for chaining
*/
@Contract(value = "_ -> this", mutates = "this")
Configurer autoEnableOnServerStart(boolean autoEnableOnServerStart);

/**
* Configures the position in the
* load order of this datapack.
*
* @param fixed won't move around in the load order as packs are added/removed
* @param position try to insert at the top of the order or bottom
* @return the configurer for chaining
*/
@Contract(value = "_, _ -> this", mutates = "this")
Configurer position(boolean fixed, Datapack.Position position);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public sealed interface DatapackSource permits DatapackSourceImpl {
DatapackSource FEATURE = create("feature");
DatapackSource WORLD = create("world");
DatapackSource SERVER = create("server");
DatapackSource PLUGIN = create("plugin");

private static DatapackSource create(final String name) {
return new DatapackSourceImpl(name);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package io.papermc.paper.datapack;

import java.util.Set;
import net.kyori.adventure.text.Component;
import org.bukkit.FeatureFlag;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Unmodifiable;
import org.jspecify.annotations.NullMarked;

/**
* This is a snapshot of a discovered datapack on the server. It
* won't be updated as datapacks are updated.
*/
@NullMarked
@ApiStatus.NonExtendable
public interface DiscoveredDatapack {

/**
* Gets the name/id of this datapack.
*
* @return the name of the pack
*/
@Contract(pure = true)
String getName();

/**
* Gets the title component of this datapack.
*
* @return the title
*/
Component getTitle();

/**
* Gets the description component of this datapack.
*
* @return the description
*/
Component getDescription();

/**
* Gets if this datapack is required.
* <p>
* A "required" datapack will always be enabled on server startup, even if previously disabled.
*
* @return true if the pack is required
*/
boolean isRequired();

/**
* Gets the compatibility status of this pack.
*
* @return the compatibility of the pack
*/
Datapack.Compatibility getCompatibility();

/**
* Gets the set of required features for this datapack.
*
* @return the set of required features
*/
@Unmodifiable
Set<FeatureFlag> getRequiredFeatures();

/**
* Gets the source for this datapack.
*
* @return the pack source
*/
DatapackSource getSource();
}
Loading

0 comments on commit feb8756

Please sign in to comment.