1.21.4 (hard fork) - api only, server wip

This commit is contained in:
Blast-MC
2025-01-13 13:42:32 -05:00
parent 987ba064aa
commit 4f5a0f462f
97 changed files with 3009 additions and 18 deletions

6
.gitignore vendored
View File

@@ -6,10 +6,10 @@ build
/run /run
/fork-server/build.gradle.kts /parchment-server/build.gradle.kts
/fork-server/src/minecraft /parchment-server/src/minecraft
/paper-server /paper-server
/fork-api/build.gradle.kts /parchment-api/build.gradle.kts
/paper-api /paper-api
/paper-api-generator /paper-api-generator

10
README.md Normal file
View File

@@ -0,0 +1,10 @@
Things I've found while just trying to get this to work:
- If you've made an update to either of the build.gradle.kts files in api or server, run `./gradlew rebuildPaperSingleFilePatches` after adding to git (no commit)
- To create a file patch:
- Make the changes you need and have no other staged changes
- cd paper-api or paper-server
- git add .
- git commit --ammend
- This ammends it to the specific 'File Patch Commit'
- ./gradlew rebuildPaper<Api/Server>FilePatches

View File

@@ -12,23 +12,23 @@ paperweight {
patchFile { patchFile {
path = "paper-server/build.gradle.kts" path = "paper-server/build.gradle.kts"
outputFile = file("fork-server/build.gradle.kts") outputFile = file("parchment-server/build.gradle.kts")
patchFile = file("fork-server/build.gradle.kts.patch") patchFile = file("parchment-server/build.gradle.kts.patch")
} }
patchFile { patchFile {
path = "paper-api/build.gradle.kts" path = "paper-api/build.gradle.kts"
outputFile = file("fork-api/build.gradle.kts") outputFile = file("parchment-api/build.gradle.kts")
patchFile = file("fork-api/build.gradle.kts.patch") patchFile = file("parchment-api/build.gradle.kts.patch")
} }
patchDir("paperApi") { patchDir("paperApi") {
upstreamPath = "paper-api" upstreamPath = "paper-api"
excludes = setOf("build.gradle.kts") excludes = setOf("build.gradle.kts")
patchesDir = file("fork-api/paper-patches") patchesDir = file("parchment-api/paper-patches")
outputDir = file("paper-api") outputDir = file("paper-api")
} }
patchDir("paperApiGenerator") { patchDir("paperApiGenerator") {
upstreamPath = "paper-api-generator" upstreamPath = "paper-api-generator"
patchesDir = file("fork-api-generator/paper-patches") patchesDir = file("parchment-api-generator/paper-patches")
outputDir = file("paper-api-generator") outputDir = file("paper-api-generator")
} }
} }
@@ -49,6 +49,7 @@ subprojects {
repositories { repositories {
mavenCentral() mavenCentral()
maven(paperMavenPublicUrl) maven(paperMavenPublicUrl)
maven("https://sonatype.projecteden.gg/repository/maven-public/")
} }
dependencies { dependencies {
@@ -80,12 +81,11 @@ subprojects {
extensions.configure<PublishingExtension> { extensions.configure<PublishingExtension> {
repositories { repositories {
/* maven {
maven("https://repo.papermc.io/repository/maven-snapshots/") { name = "edenSnapshots"
name = "paperSnapshots" url = uri("https://sonatype.projecteden.gg/repository/maven-snapshots/")
credentials(PasswordCredentials::class) credentials(PasswordCredentials::class)
} }
*/
} }
} }
} }

View File

@@ -1,4 +1,4 @@
group=fork.test group=gg.projecteden.parchment
version=1.21.4-R0.1-SNAPSHOT version=1.21.4-R0.1-SNAPSHOT
mcVersion=1.21.4 mcVersion=1.21.4
paperRef=b03d39b5ce6b5046ce6854ddba74e8ae3641c230 paperRef=b03d39b5ce6b5046ce6854ddba74e8ae3641c230
@@ -7,3 +7,5 @@ org.gradle.configuration-cache=true
org.gradle.caching=true org.gradle.caching=true
org.gradle.parallel=true org.gradle.parallel=true
org.gradle.vfs.watch=false org.gradle.vfs.watch=false
edenVersion = 2.3.0-SNAPSHOT

View File

@@ -1,5 +1,22 @@
--- a/paper-api/build.gradle.kts --- a/paper-api/build.gradle.kts
+++ b/paper-api/build.gradle.kts +++ b/paper-api/build.gradle.kts
@@ -15,6 +_,8 @@
val slf4jVersion = "2.0.9"
val log4jVersion = "2.17.1"
+val edenVersion: String by project // Parchment
+
val apiAndDocs: Configuration by configurations.creating {
attributes {
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION))
@@ -39,6 +_,7 @@
}
dependencies {
+ api("gg.projecteden:eden-interfaces:$edenVersion") // Parchment
// api dependencies are listed transitively to API consumers
api("com.google.guava:guava:33.3.1-jre")
@@ -103,6 +_,18 @@ @@ -103,6 +_,18 @@
main { main {
java { java {

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/com/destroystokyo/paper/event/block/BeaconEffectEvent.java
+++ b/src/main/java/com/destroystokyo/paper/event/block/BeaconEffectEvent.java
@@ -13,7 +_,7 @@
* Called when a beacon effect is being applied to a player.
*/
@NullMarked
-public class BeaconEffectEvent extends BlockEvent implements Cancellable {
+public class BeaconEffectEvent extends BlockEvent implements Cancellable, gg.projecteden.parchment.HasPlayer { // Parchment
private static final HandlerList HANDLER_LIST = new HandlerList();

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/com/destroystokyo/paper/event/entity/EndermanAttackPlayerEvent.java
+++ b/src/main/java/com/destroystokyo/paper/event/entity/EndermanAttackPlayerEvent.java
@@ -38,7 +_,7 @@
* at the Enderman, according to Vanilla rules.
*/
@NullMarked
-public class EndermanAttackPlayerEvent extends EntityEvent implements Cancellable {
+public class EndermanAttackPlayerEvent extends EntityEvent implements Cancellable, gg.projecteden.parchment.HasPlayer { // Parchment
private static final HandlerList HANDLER_LIST = new HandlerList();

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/com/destroystokyo/paper/event/entity/TurtleLayEggEvent.java
+++ b/src/main/java/com/destroystokyo/paper/event/entity/TurtleLayEggEvent.java
@@ -12,7 +_,7 @@
* Fired when a Turtle lays eggs
*/
@NullMarked
-public class TurtleLayEggEvent extends EntityEvent implements Cancellable {
+public class TurtleLayEggEvent extends EntityEvent implements Cancellable, gg.projecteden.parchment.HasLocation { // Parchment
private static final HandlerList HANDLER_LIST = new HandlerList();

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/com/destroystokyo/paper/event/entity/TurtleStartDiggingEvent.java
+++ b/src/main/java/com/destroystokyo/paper/event/entity/TurtleStartDiggingEvent.java
@@ -12,7 +_,7 @@
* Fired when a Turtle starts digging to lay eggs
*/
@NullMarked
-public class TurtleStartDiggingEvent extends EntityEvent implements Cancellable {
+public class TurtleStartDiggingEvent extends EntityEvent implements Cancellable, gg.projecteden.parchment.HasLocation { // Parchment
private static final HandlerList HANDLER_LIST = new HandlerList();

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/com/destroystokyo/paper/event/player/PlayerHandshakeEvent.java
+++ b/src/main/java/com/destroystokyo/paper/event/player/PlayerHandshakeEvent.java
@@ -21,7 +_,7 @@
* <p>WARNING: TAMPERING WITH THIS EVENT CAN BE DANGEROUS</p>
*/
@NullMarked
-public class PlayerHandshakeEvent extends Event implements Cancellable {
+public class PlayerHandshakeEvent extends Event implements Cancellable, gg.projecteden.api.interfaces.OptionalUniqueId { // Parchment
private static final HandlerList HANDLER_LIST = new HandlerList();

View File

@@ -0,0 +1,22 @@
--- a/src/main/java/com/destroystokyo/paper/event/profile/PreLookupProfileEvent.java
+++ b/src/main/java/com/destroystokyo/paper/event/profile/PreLookupProfileEvent.java
@@ -22,7 +_,7 @@
* {@link Event#isAsynchronous()}
*/
@NullMarked
-public class PreLookupProfileEvent extends Event {
+public class PreLookupProfileEvent extends Event implements gg.projecteden.api.interfaces.OptionalUniqueId { // Parchment
private static final HandlerList HANDLER_LIST = new HandlerList();
@@ -52,6 +_,10 @@
* @return The UUID of the profile if it has already been provided by a plugin
*/
public @Nullable UUID getUUID() {
+ return this.uuid;
+ }
+
+ public UUID getUniqueId() {
return this.uuid;
}

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/com/destroystokyo/paper/event/server/AsyncTabCompleteEvent.java
+++ b/src/main/java/com/destroystokyo/paper/event/server/AsyncTabCompleteEvent.java
@@ -53,7 +_,7 @@
* Only 1 process will be allowed to provide completions, the Async Event, or the standard process.
*/
@NullMarked
-public class AsyncTabCompleteEvent extends Event implements Cancellable {
+public class AsyncTabCompleteEvent extends Event implements Cancellable, gg.projecteden.parchment.OptionalLocation { // Parchment
private static final HandlerList HANDLER_LIST = new HandlerList();

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/com/destroystokyo/paper/exception/ServerPluginMessageException.java
+++ b/src/main/java/com/destroystokyo/paper/exception/ServerPluginMessageException.java
@@ -8,7 +_,7 @@
/**
* Thrown when an incoming plugin message channel throws an exception
*/
-public class ServerPluginMessageException extends ServerPluginException {
+public class ServerPluginMessageException extends ServerPluginException implements gg.projecteden.parchment.HasPlayer { // Parchment
private final Player player;
private final String channel;

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/io/papermc/paper/event/packet/PlayerChunkLoadEvent.java
+++ b/src/main/java/io/papermc/paper/event/packet/PlayerChunkLoadEvent.java
@@ -16,7 +_,7 @@
* Not intended for modifying server side state.
*/
@NullMarked
-public class PlayerChunkLoadEvent extends ChunkEvent {
+public class PlayerChunkLoadEvent extends ChunkEvent implements gg.projecteden.parchment.HasPlayer { // Parchment
private static final HandlerList HANDLER_LIST = new HandlerList();

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/io/papermc/paper/event/packet/PlayerChunkUnloadEvent.java
+++ b/src/main/java/io/papermc/paper/event/packet/PlayerChunkUnloadEvent.java
@@ -14,7 +_,7 @@
* Not intended for modifying server side.
*/
@NullMarked
-public class PlayerChunkUnloadEvent extends ChunkEvent {
+public class PlayerChunkUnloadEvent extends ChunkEvent implements gg.projecteden.parchment.HasPlayer { // Parchment
private static final HandlerList HANDLER_LIST = new HandlerList();

View File

@@ -0,0 +1,73 @@
--- a/src/main/java/org/bukkit/ChatColor.java
+++ b/src/main/java/org/bukkit/ChatColor.java
@@ -13,7 +_,7 @@
* @deprecated ChatColor has been deprecated in favor of <a href="https://docs.advntr.dev/text.html">Adventure</a> API. See {@link net.kyori.adventure.text.format.NamedTextColor} for the adventure equivalent of pre-defined text colors
*/
@Deprecated // Paper
-public enum ChatColor {
+public enum ChatColor implements net.kyori.adventure.text.format.StyleBuilderApplicable, net.kyori.adventure.text.format.TextFormat { // Parchment
/**
* Represents black
*/
@@ -183,6 +_,13 @@
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.MAGIC;
}
+
+ // Parchment start
+ @Override
+ public void styleApply(net.kyori.adventure.text.format.Style.@NotNull Builder style) {
+ style.apply(net.kyori.adventure.text.format.TextDecoration.OBFUSCATED);
+ }
+ // Parchment end
},
/**
* Makes the text bold.
@@ -213,6 +_,13 @@
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.UNDERLINE;
}
+
+ // Parchment start
+ @Override
+ public void styleApply(net.kyori.adventure.text.format.Style.@NotNull Builder style) {
+ style.apply(net.kyori.adventure.text.format.TextDecoration.UNDERLINED);
+ }
+ // Parchment end
},
/**
* Makes the text italic.
@@ -233,6 +_,16 @@
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.RESET;
}
+
+ // Parchment start
+ @Override
+ public void styleApply(net.kyori.adventure.text.format.Style.@NotNull Builder style) {
+ style.color(null);
+ for (net.kyori.adventure.text.format.TextDecoration decoration : net.kyori.adventure.text.format.TextDecoration.values()) {
+ style.decoration(decoration, net.kyori.adventure.text.format.TextDecoration.State.NOT_SET);
+ }
+ }
+ // Parchment end
};
/**
@@ -264,6 +_,16 @@
public net.md_5.bungee.api.ChatColor asBungee() {
return net.md_5.bungee.api.ChatColor.RESET;
};
+
+ // Parchment start
+ @Override
+ public void styleApply(net.kyori.adventure.text.format.Style.@NotNull Builder style) {
+ if (isColor())
+ style.color(net.kyori.adventure.text.format.TextColor.color(asBungee().getColor().getRGB()));
+ else
+ style.decorate(net.kyori.adventure.text.format.TextDecoration.valueOf(name()));
+ }
+ // Parchment end
/**
* Gets the char value associated with this color

View File

@@ -0,0 +1,25 @@
--- a/src/main/java/org/bukkit/Color.java
+++ b/src/main/java/org/bukkit/Color.java
@@ -17,7 +_,7 @@
* but subject to change.
*/
@SerializableAs("Color")
-public final class Color implements ConfigurationSerializable {
+public final class Color implements ConfigurationSerializable, net.kyori.adventure.text.format.TextColor { // Parchment
private static final int BIT_MASK = 0xff;
private static final int DEFAULT_ALPHA = 255;
@@ -309,6 +_,13 @@
public int asARGB() {
return getAlpha() << 24 | getRed() << 16 | getGreen() << 8 | getBlue();
}
+
+ // Parchment start
+ @Override
+ public int value() {
+ return asRGB();
+ }
+ // Parchment end
/**
* Gets the color as an BGR integer.

View File

@@ -0,0 +1,40 @@
--- a/src/main/java/org/bukkit/DyeColor.java
+++ b/src/main/java/org/bukkit/DyeColor.java
@@ -8,7 +_,7 @@
/**
* All supported color values for dyes and cloth
*/
-public enum DyeColor {
+public enum DyeColor implements net.kyori.adventure.util.RGBLike, net.kyori.adventure.text.format.StyleBuilderApplicable { // Parchment
/**
* Represents white dye.
@@ -134,6 +_,28 @@
public Color getFireworkColor() {
return firework;
}
+
+ // Parchment start
+ @Override
+ public @org.jetbrains.annotations.Range(from = 0L, to = 255L) int red() {
+ return color.getRed();
+ }
+
+ @Override
+ public @org.jetbrains.annotations.Range(from = 0L, to = 255L) int green() {
+ return color.getGreen();
+ }
+
+ @Override
+ public @org.jetbrains.annotations.Range(from = 0L, to = 255L) int blue() {
+ return color.getBlue();
+ }
+
+ @Override
+ public void styleApply(net.kyori.adventure.text.format.Style.@org.jetbrains.annotations.NotNull Builder style) {
+ style.color(net.kyori.adventure.text.format.TextColor.color(color));
+ }
+ // Parchment end
/**
* Gets the DyeColor with the given wool data value.

View File

@@ -0,0 +1,24 @@
--- a/src/main/java/org/bukkit/Location.java
+++ b/src/main/java/org/bukkit/Location.java
@@ -27,13 +_,20 @@
* magnitude than 360 are valid, but may be normalized to any other equivalent
* representation by the implementation.
*/
-public class Location implements Cloneable, ConfigurationSerializable, io.papermc.paper.math.FinePosition {
+public class Location implements Cloneable, ConfigurationSerializable, io.papermc.paper.math.FinePosition, gg.projecteden.parchment.HasLocation { // Paper // Parchment
private Reference<World> world;
private double x;
private double y;
private double z;
private float pitch;
private float yaw;
+
+ // Parchment start
+ @Override
+ public @NotNull Location getLocation() {
+ return this;
+ }
+ // Parchment end
/**
* Constructs a new Location with the given coordinates

View File

@@ -0,0 +1,18 @@
--- a/src/main/java/org/bukkit/OfflinePlayer.java
+++ b/src/main/java/org/bukkit/OfflinePlayer.java
@@ -19,7 +_,14 @@
* player that is stored on the disk and can, thus, be retrieved without the
* player needing to be online.
*/
-public interface OfflinePlayer extends ServerOperator, AnimalTamer, ConfigurationSerializable, io.papermc.paper.persistence.PersistentDataViewHolder { // Paper - Add Offline PDC API
+public interface OfflinePlayer extends ServerOperator, AnimalTamer, ConfigurationSerializable, io.papermc.paper.persistence.PersistentDataViewHolder, gg.projecteden.parchment.HasOfflinePlayer, gg.projecteden.parchment.OptionalPlayer { // Parchment
+
+ // Parchment start
+ @Override
+ default @org.jetbrains.annotations.NotNull OfflinePlayer getOfflinePlayer() {
+ return this;
+ }
+ // Parchment end
/**
* Checks if this player is currently online

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/Raid.java
+++ b/src/main/java/org/bukkit/Raid.java
@@ -9,7 +_,7 @@
/**
* Represents a raid event.
*/
-public interface Raid extends org.bukkit.persistence.PersistentDataHolder { // Paper
+public interface Raid extends org.bukkit.persistence.PersistentDataHolder, gg.projecteden.parchment.HasLocation { // Paper // Parchment
/**
* Get whether this raid started.

View File

@@ -0,0 +1,12 @@
--- a/src/main/java/org/bukkit/Vibration.java
+++ b/src/main/java/org/bukkit/Vibration.java
@@ -74,7 +_,8 @@
}
}
- public static class BlockDestination implements Destination {
+ public static class BlockDestination implements Destination, gg.projecteden.parchment.HasLocation { // Parchment
+
private final Location block;

View File

@@ -0,0 +1,39 @@
--- a/src/main/java/org/bukkit/World.java
+++ b/src/main/java/org/bukkit/World.java
@@ -52,6 +_,36 @@
*/
public interface World extends RegionAccessor, WorldInfo, PluginMessageRecipient, Metadatable, PersistentDataHolder, Keyed, net.kyori.adventure.audience.ForwardingAudience { // Paper
+ // Parchment start
+ /**
+ * Returns the item that will result from smelting the input item, if applicable.
+ *
+ * @param toSmelt the item to simulate smelting
+ * @return the resulting item, or null
+ */
+ @Nullable
+ default ItemStack smeltItem(@NotNull ItemStack toSmelt) {
+ return smeltItem(toSmelt, gg.projecteden.parchment.inventory.RecipeType.SMELTING);
+ }
+
+ /**
+ * Returns the item that will result from smelting the input item, if applicable.
+ * <p>
+ * Applicable values for {@code recipeType} are
+ * {@link gg.projecteden.parchment.inventory.RecipeType#SMELTING SMELTING},
+ * {@link gg.projecteden.parchment.inventory.RecipeType#BLASTING BLASTING},
+ * {@link gg.projecteden.parchment.inventory.RecipeType#SMOKING SMOKING},
+ * and {@link gg.projecteden.parchment.inventory.RecipeType#CAMPFIRE_COOKING CAMPFIRE_COOKING}.
+ * An {@link IllegalArgumentException} will be thrown if another value is supplied.
+ *
+ * @param toSmelt the item to simulate smelting
+ * @param recipeType type of furnace to simulate smelting
+ * @return the resulting item, or null
+ */
+ @Nullable
+ ItemStack smeltItem(@NotNull ItemStack toSmelt, gg.projecteden.parchment.inventory.@NotNull RecipeType recipeType);
+ // Parchment end
+
// Paper start - void damage configuration
/**
* Checks if void damage is enabled on this world.

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/attribute/AttributeModifier.java
+++ b/src/main/java/org/bukkit/attribute/AttributeModifier.java
@@ -20,7 +_,7 @@
/**
* Concrete implementation of an attribute modifier.
*/
-public class AttributeModifier implements ConfigurationSerializable, Keyed {
+public class AttributeModifier implements ConfigurationSerializable, Keyed, gg.projecteden.api.interfaces.HasUniqueId { // Parchment
private static final Pattern UUID_PATTERN = Pattern.compile("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$");
private final NamespacedKey key;

View File

@@ -0,0 +1,32 @@
--- a/src/main/java/org/bukkit/block/Block.java
+++ b/src/main/java/org/bukkit/block/Block.java
@@ -32,7 +_,7 @@
* (i.e. lighting and power) may not be able to be safely accessed during world
* generation when used in cases like BlockPhysicsEvent!!!!
*/
-public interface Block extends Metadatable, Translatable, net.kyori.adventure.translation.Translatable { // Paper - translatable
+public interface Block extends Metadatable, Translatable, net.kyori.adventure.translation.Translatable, gg.projecteden.parchment.HasLocation { // Paper - translatable // Parchment
/**
* Gets the metadata for this block
@@ -593,6 +_,20 @@
* @return true if the block was destroyed
*/
boolean breakNaturally(@NotNull ItemStack tool, boolean triggerEffect, boolean dropExperience);
+
+ // Parchment Start
+ /**
+ * Breaks the block and spawns item drops as if a player had broken it
+ * with a specific tool
+ *
+ * @param player The player to break the block as
+ * @param tool The tool or item in hand used for digging
+ * @param triggerEffect Play the block break particle effect and sound
+ * @param dropExperience drop exp if the block normally does so
+ * @return true if the block was destroyed
+ */
+ boolean breakNaturally(Player player, @NotNull ItemStack tool, boolean triggerEffect, boolean dropExperience);
+ // Parchment end
/**
* Causes the block to be ticked, this is different from {@link Block#randomTick()},

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/block/BlockState.java
+++ b/src/main/java/org/bukkit/block/BlockState.java
@@ -21,7 +_,7 @@
* change the state of the block and you will not know, or they may change the
* block to another type entirely, causing your BlockState to become invalid.
*/
-public interface BlockState extends Metadatable {
+public interface BlockState extends Metadatable, gg.projecteden.parchment.HasLocation { // Parchment
/**
* Gets the block represented by this block state.

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/block/DoubleChest.java
+++ b/src/main/java/org/bukkit/block/DoubleChest.java
@@ -11,7 +_,7 @@
/**
* Represents a double chest.
*/
-public class DoubleChest implements InventoryHolder {
+public class DoubleChest implements InventoryHolder, gg.projecteden.parchment.HasLocation { // Parchment
private DoubleChestInventory inventory;
public DoubleChest(@NotNull DoubleChestInventory chest) {

View File

@@ -0,0 +1,12 @@
--- a/src/main/java/org/bukkit/entity/AnimalTamer.java
+++ b/src/main/java/org/bukkit/entity/AnimalTamer.java
@@ -4,7 +_,8 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-public interface AnimalTamer {
+public interface AnimalTamer extends gg.projecteden.api.interfaces.HasUniqueId { // Parchment
+
/**
* This is the name of the specified AnimalTamer.

View File

@@ -0,0 +1,19 @@
--- a/src/main/java/org/bukkit/entity/Entity.java
+++ b/src/main/java/org/bukkit/entity/Entity.java
@@ -33,7 +_,7 @@
* Not all methods are guaranteed to work/may have side effects when
* {@link #isInWorld()} is false.
*/
-public interface Entity extends Metadatable, CommandSender, Nameable, PersistentDataHolder, net.kyori.adventure.text.event.HoverEventSource<net.kyori.adventure.text.event.HoverEvent.ShowEntity>, net.kyori.adventure.sound.Sound.Emitter { // Paper
+public interface Entity extends Metadatable, CommandSender, Nameable, PersistentDataHolder, net.kyori.adventure.text.event.HoverEventSource<net.kyori.adventure.text.event.HoverEvent.ShowEntity>, net.kyori.adventure.sound.Sound.Emitter, gg.projecteden.api.interfaces.HasUniqueId, gg.projecteden.parchment.HasLocation { // Paper // Parchment
/**
* Gets the entity's current position
@@ -1196,4 +_,7 @@
*/
void broadcastHurtAnimation(@NotNull java.util.Collection<Player> players);
// Paper end - broadcast hurt animation
+
+ gg.projecteden.parchment.entity.EntityData getStoredEntityData();
+
}

View File

@@ -0,0 +1,10 @@
--- a/src/main/java/org/bukkit/entity/EntityType.java
+++ b/src/main/java/org/bukkit/entity/EntityType.java
@@ -341,6 +_,7 @@
/**
* An unknown entity without an Entity Class
*/
+ NPC("npc", NPC.class, -1, true),
UNKNOWN(null, null, -1, false);
private final String name;

View File

@@ -0,0 +1,25 @@
--- a/src/main/java/org/bukkit/entity/Hanging.java
+++ b/src/main/java/org/bukkit/entity/Hanging.java
@@ -20,4 +_,22 @@
* attach to in order to face the given direction.
*/
public boolean setFacingDirection(@NotNull BlockFace face, boolean force);
+
+ // Parchment start
+ /**
+ * Determines whether the hanging entity is allowed to tick.
+ * Ticking is used to validate the hanging entity's placement.
+ *
+ * @return True if the entity is allowed to tick.
+ */
+ default boolean canTick() { return true; }
+
+ /**
+ * Sets whether the hanging entity is allowed to tick.
+ * Ticking is used to validate the hanging entity's placement.
+ *
+ * @param tick True if the entity is allowed to tick.
+ */
+ default void setCanTick(boolean tick) { throw new UnsupportedOperationException(); }
+ // Parchment end
}

View File

@@ -0,0 +1,18 @@
--- a/src/main/java/org/bukkit/entity/HumanEntity.java
+++ b/src/main/java/org/bukkit/entity/HumanEntity.java
@@ -23,7 +_,14 @@
/**
* Represents a human entity, such as an NPC or a player
*/
-public interface HumanEntity extends LivingEntity, AnimalTamer, InventoryHolder {
+public interface HumanEntity extends LivingEntity, AnimalTamer, InventoryHolder, gg.projecteden.parchment.HasHumanEntity { // Parchment
+
+ // Parchment start
+ @Override
+ default @NotNull HumanEntity getPlayer() {
+ return this;
+ }
+ // Parchment end
// Paper start
@Override

View File

@@ -0,0 +1,85 @@
--- a/src/main/java/org/bukkit/entity/Player.java
+++ b/src/main/java/org/bukkit/entity/Player.java
@@ -59,7 +_,17 @@
/**
* Represents a player, connected or not
*/
-public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginMessageRecipient, net.kyori.adventure.identity.Identified, net.kyori.adventure.bossbar.BossBarViewer, com.destroystokyo.paper.network.NetworkClient { // Paper
+public interface Player extends HumanEntity, Conversable, OfflinePlayer, PluginMessageRecipient, net.kyori.adventure.identity.Identified, net.kyori.adventure.bossbar.BossBarViewer, com.destroystokyo.paper.network.NetworkClient , gg.projecteden.parchment.HasPlayer { // Paper // Parchment
+
+ // Parchment start
+ /**
+ * Returns this player object.
+ *
+ * @return this player
+ */
+ @Override
+ @NotNull Player getPlayer();
+ // Parchment end
// Paper start
@Override
@@ -2128,6 +_,17 @@
*/
public boolean canSee(@NotNull Entity entity);
+ // Parchment start
+ /**
+ * Gets a view of the hidden entity UUIDs.
+ *
+ * @param plugin Plugin that has hidden entities
+ * @return a view of hidden entity UUIDs
+ */
+ public java.util.@NotNull Set<java.util.UUID> getHiddenEntities(@NotNull Plugin plugin);
+ // Parchment end
+
+
// Paper start
/**
* Returns whether the {@code other} player is listed for {@code this}.
@@ -3892,4 +_,45 @@
*/
void sendEntityEffect(org.bukkit.@NotNull EntityEffect effect, @NotNull Entity target);
// Paper end - entity effect API
+
+ /**
+ * Checks if the player will spawn phantoms at night
+ * Uses time since last rest statistic
+ *
+ * {@link #getTimeSinceLastRest()}
+ *
+ * @return if the player will spawn phantoms at night
+ */
+ boolean isInsomniac();
+
+ /**
+ * Sets if the player bypasses phantom spawning if insomniac
+ *
+ * @param val
+ */
+ void setBypassInsomnia(boolean val);
+
+ /**
+ * Does the player bypass phantom spawning if insomniac
+ *
+ * @return if the player bypasses phantom spawning
+ */
+ boolean doesBypassInsomnia();
+
+ /**
+ * Set the time since last rest stat for the player
+ * This modifies the phantom spawning timer
+ *
+ * @param ticks how long since last rest, greater than or equal to 1
+ */
+ void setTimeSinceLastRest(int ticks);
+
+ /**
+ * Gets the time since the player last slept
+ *
+ * @return ticks since the player last slept
+ */
+ int getTimeSinceLastRest();
+// Parchment end
+
}

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/block/BlockBreakEvent.java
+++ b/src/main/java/org/bukkit/event/block/BlockBreakEvent.java
@@ -26,7 +_,7 @@
* If a Block Break event is cancelled, the block will not break and
* experience will not drop.
*/
-public class BlockBreakEvent extends BlockExpEvent implements Cancellable {
+public class BlockBreakEvent extends BlockExpEvent implements Cancellable, gg.projecteden.parchment.HasPlayer { // Parchment
private final Player player;
private boolean dropItems;
private boolean cancel;

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/block/BlockCanBuildEvent.java
+++ b/src/main/java/org/bukkit/event/block/BlockCanBuildEvent.java
@@ -19,7 +_,7 @@
* #getMaterial()} instead.
* </ul>
*/
-public class BlockCanBuildEvent extends BlockEvent {
+public class BlockCanBuildEvent extends BlockEvent implements gg.projecteden.parchment.OptionalPlayer { // Parchment
private static final HandlerList handlers = new HandlerList();
protected boolean buildable;

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/block/BlockDamageEvent.java
+++ b/src/main/java/org/bukkit/event/block/BlockDamageEvent.java
@@ -13,7 +_,7 @@
* If a Block Damage event is cancelled, the block will not be damaged.
* @see BlockDamageAbortEvent
*/
-public class BlockDamageEvent extends BlockEvent implements Cancellable {
+public class BlockDamageEvent extends BlockEvent implements Cancellable, gg.projecteden.parchment.HasPlayer { // Parchment
private static final HandlerList handlers = new HandlerList();
private final Player player;
private boolean instaBreak;

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/block/BlockDropItemEvent.java
+++ b/src/main/java/org/bukkit/event/block/BlockDropItemEvent.java
@@ -28,7 +_,7 @@
* AIR in most cases. Use #getBlockState() for more Information about the broken
* block.
*/
-public class BlockDropItemEvent extends BlockEvent implements Cancellable {
+public class BlockDropItemEvent extends BlockEvent implements Cancellable, gg.projecteden.parchment.HasPlayer { // Parchment
private static final HandlerList handlers = new HandlerList();
private final Player player;

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/block/BlockFertilizeEvent.java
+++ b/src/main/java/org/bukkit/event/block/BlockFertilizeEvent.java
@@ -15,7 +_,7 @@
* block with bonemeal. Will be called after the applicable
* {@link StructureGrowEvent}.
*/
-public class BlockFertilizeEvent extends BlockEvent implements Cancellable {
+public class BlockFertilizeEvent extends BlockEvent implements Cancellable, gg.projecteden.parchment.HasPlayer { // Parchment
private static final HandlerList handlers = new HandlerList();
private boolean cancelled;

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/block/BlockIgniteEvent.java
+++ b/src/main/java/org/bukkit/event/block/BlockIgniteEvent.java
@@ -14,7 +_,7 @@
* <p>
* If a Block Ignite event is cancelled, the block will not be ignited.
*/
-public class BlockIgniteEvent extends BlockEvent implements Cancellable {
+public class BlockIgniteEvent extends BlockEvent implements Cancellable, gg.projecteden.parchment.OptionalPlayer { // Parchment
private static final HandlerList handlers = new HandlerList();
private final IgniteCause cause;
private final Entity ignitingEntity;

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/block/BlockPlaceEvent.java
+++ b/src/main/java/org/bukkit/event/block/BlockPlaceEvent.java
@@ -14,7 +_,7 @@
* <p>
* If a Block Place event is cancelled, the block will not be placed.
*/
-public class BlockPlaceEvent extends BlockEvent implements Cancellable {
+public class BlockPlaceEvent extends BlockEvent implements Cancellable, gg.projecteden.parchment.HasPlayer { // Parchment
private static final HandlerList handlers = new HandlerList();
protected boolean cancel;
protected boolean canBuild;

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/block/SignChangeEvent.java
+++ b/src/main/java/org/bukkit/event/block/SignChangeEvent.java
@@ -13,7 +_,7 @@
* <p>
* If a Sign Change event is cancelled, the sign will not be changed.
*/
-public class SignChangeEvent extends BlockEvent implements Cancellable {
+public class SignChangeEvent extends BlockEvent implements Cancellable, gg.projecteden.parchment.HasPlayer { // Parchment
private static final HandlerList handlers = new HandlerList();
private boolean cancel = false;
private final Player player;

View File

@@ -0,0 +1,53 @@
--- a/src/main/java/org/bukkit/event/entity/EntityDamageByBlockEvent.java
+++ b/src/main/java/org/bukkit/event/entity/EntityDamageByBlockEvent.java
@@ -20,6 +_,7 @@
public class EntityDamageByBlockEvent extends EntityDamageEvent {
private final Block damager;
private final BlockState damagerState;
+ private final org.bukkit.Location location; // Parchment
@Deprecated(since = "1.20.4", forRemoval = true)
public EntityDamageByBlockEvent(@Nullable final Block damager, @NotNull final Entity damagee, @NotNull final DamageCause cause, final double damage) {
@@ -30,18 +_,39 @@
super(damagee, cause, damageSource, damage);
this.damager = damager;
this.damagerState = damagerState;
+ this.location = damager != null ? damager.getLocation() : null; // Parchment
}
@Deprecated(since = "1.20.4", forRemoval = true)
public EntityDamageByBlockEvent(@Nullable final Block damager, @NotNull final Entity damagee, @NotNull final DamageCause cause, @NotNull final Map<DamageModifier, Double> modifiers, @NotNull final Map<DamageModifier, ? extends Function<? super Double, Double>> modifierFunctions) {
- this(damager, (damager != null) ? damager.getState() : null, damagee, cause, (damager != null) ? DamageSource.builder(DamageType.GENERIC).withDamageLocation(damager.getLocation()).build() : DamageSource.builder(DamageType.GENERIC).build(), modifiers, modifierFunctions);
+ this(damager, (damager != null) ? damager.getState() : null, damagee, cause, (damager != null) ? DamageSource.builder(DamageType.GENERIC).withDamageLocation(damager.getLocation()).build() : DamageSource.builder(DamageType.GENERIC).build(), modifiers, modifierFunctions, null);
}
- public EntityDamageByBlockEvent(@Nullable final Block damager, @Nullable final BlockState damagerState, @NotNull final Entity damagee, @NotNull final DamageCause cause, @NotNull final DamageSource damageSource, @NotNull final Map<DamageModifier, Double> modifiers, @NotNull final Map<DamageModifier, ? extends Function<? super Double, Double>> modifierFunctions) {
+ public EntityDamageByBlockEvent(@Nullable final Block damager, @Nullable final BlockState damagerState, @NotNull final Entity damagee, @NotNull final DamageCause cause, @NotNull final DamageSource damageSource, @NotNull final Map<DamageModifier, Double> modifiers, @NotNull final Map<DamageModifier, ? extends Function<? super Double, Double>> modifierFunctions, @Nullable final org.bukkit.Location damageLocation) { // Parchment
super(damagee, cause, damageSource, modifiers, modifierFunctions);
this.damager = damager;
this.damagerState = damagerState;
- }
+ // Parchment start
+ if (damageLocation != null)
+ this.location = damageLocation;
+ else if (damager != null)
+ this.location = damager.getLocation();
+ else
+ this.location = null;
+ // Parchment end
+ }
+
+ // Parchment - add Location
+ /**
+ * Gets the location of the damage source.
+ *
+ * @return Originating location of the damage source
+ */
+ @Nullable
+ public org.bukkit.Location getLocation() {
+ return location;
+ }
+ // Parchment end
/**
* Returns the block that damaged the player.

View File

@@ -0,0 +1,37 @@
--- a/src/main/java/org/bukkit/event/entity/EntityEnterLoveModeEvent.java
+++ b/src/main/java/org/bukkit/event/entity/EntityEnterLoveModeEvent.java
@@ -13,7 +_,7 @@
* This can be cancelled but the item will still be consumed that was used to
* make the entity enter into love mode.
*/
-public class EntityEnterLoveModeEvent extends EntityEvent implements Cancellable {
+public class EntityEnterLoveModeEvent extends EntityEvent implements Cancellable, gg.projecteden.parchment.OptionalHumanEntity { // Parchment
private static final HandlerList handlers = new HandlerList();
private boolean cancel;
@@ -44,9 +_,24 @@
* null if there wasn't one.
*/
@Nullable
- public HumanEntity getHumanEntity() {
+ // Parchment start
+ @Deprecated
+ public final HumanEntity getHumanEntity() {
+ return getPlayer();
+ }
+
+ /**
+ * Gets the Human Entity that caused the animal to enter love mode.
+ *
+ * @return The Human entity that caused the animal to enter love mode, or
+ * null if there wasn't one.
+ */
+ @Override
+ @Nullable
+ public HumanEntity getPlayer() {
return humanEntity;
}
+ // Parchment end
/**
* Gets the amount of ticks that the animal will fall in love for.

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/entity/EntityExplodeEvent.java
+++ b/src/main/java/org/bukkit/event/entity/EntityExplodeEvent.java
@@ -14,7 +_,7 @@
* event isn't called if the {@link org.bukkit.GameRule#MOB_GRIEFING}
* is disabled as no block interaction will occur.
*/
-public class EntityExplodeEvent extends EntityEvent implements Cancellable {
+public class EntityExplodeEvent extends EntityEvent implements Cancellable, gg.projecteden.parchment.HasLocation { // Parchment
private static final HandlerList handlers = new HandlerList();
private boolean cancel;
private final Location location;

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/entity/EntityPlaceEvent.java
+++ b/src/main/java/org/bukkit/event/entity/EntityPlaceEvent.java
@@ -17,7 +_,7 @@
* Note that this event is currently only fired for four specific placements:
* armor stands, boats, minecarts, and end crystals.
*/
-public class EntityPlaceEvent extends EntityEvent implements Cancellable {
+public class EntityPlaceEvent extends EntityEvent implements Cancellable, gg.projecteden.parchment.OptionalPlayer { // Parchment
private static final HandlerList handlers = new HandlerList();
private boolean cancelled;

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/entity/EntityPortalEnterEvent.java
+++ b/src/main/java/org/bukkit/event/entity/EntityPortalEnterEvent.java
@@ -11,7 +_,7 @@
* Cancelling this event prevents any further processing of the portal for that tick.
* @see io.papermc.paper.event.entity.EntityInsideBlockEvent
*/
-public class EntityPortalEnterEvent extends EntityEvent implements org.bukkit.event.Cancellable { // Paper
+public class EntityPortalEnterEvent extends EntityEvent implements org.bukkit.event.Cancellable, gg.projecteden.parchment.HasLocation { // Parchment
private static final HandlerList handlers = new HandlerList();
private final Location location;

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/entity/EntitySpawnEvent.java
+++ b/src/main/java/org/bukkit/event/entity/EntitySpawnEvent.java
@@ -11,7 +_,7 @@
* <p>
* If an Entity Spawn event is cancelled, the entity will not spawn.
*/
-public class EntitySpawnEvent extends EntityEvent implements Cancellable {
+public class EntitySpawnEvent extends EntityEvent implements Cancellable, gg.projecteden.parchment.HasLocation { // Parchment
private static final HandlerList handlers = new HandlerList();
private boolean canceled;

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/entity/ItemDespawnEvent.java
+++ b/src/main/java/org/bukkit/event/entity/ItemDespawnEvent.java
@@ -13,7 +_,7 @@
* Cancelling the event results in the item being allowed to exist for 5 more
* minutes. This behavior is not guaranteed and may change in future versions.
*/
-public class ItemDespawnEvent extends EntityEvent implements Cancellable {
+public class ItemDespawnEvent extends EntityEvent implements Cancellable, gg.projecteden.parchment.HasLocation { // Parchment
private static final HandlerList handlers = new HandlerList();
private boolean canceled;
private final Location location;

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/entity/PlayerLeashEntityEvent.java
+++ b/src/main/java/org/bukkit/event/entity/PlayerLeashEntityEvent.java
@@ -11,7 +_,7 @@
/**
* Called immediately prior to a creature being leashed by a player.
*/
-public class PlayerLeashEntityEvent extends Event implements Cancellable {
+public class PlayerLeashEntityEvent extends Event implements Cancellable, gg.projecteden.parchment.HasPlayer { // Parchment
private static final HandlerList handlers = new HandlerList();
private final Entity leashHolder;
private final Entity entity;

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/hanging/HangingPlaceEvent.java
+++ b/src/main/java/org/bukkit/event/hanging/HangingPlaceEvent.java
@@ -14,7 +_,7 @@
/**
* Triggered when a hanging entity is created in the world
*/
-public class HangingPlaceEvent extends HangingEvent implements Cancellable {
+public class HangingPlaceEvent extends HangingEvent implements Cancellable, gg.projecteden.parchment.OptionalPlayer { // Parchment
private static final HandlerList handlers = new HandlerList();
private boolean cancelled;
private final Player player;

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/inventory/FurnaceExtractEvent.java
+++ b/src/main/java/org/bukkit/event/inventory/FurnaceExtractEvent.java
@@ -13,7 +_,7 @@
* {@link org.bukkit.block.Furnace}, {@link org.bukkit.block.Smoker}, or
* {@link org.bukkit.block.BlastFurnace}.
*/
-public class FurnaceExtractEvent extends BlockExpEvent {
+public class FurnaceExtractEvent extends BlockExpEvent implements gg.projecteden.parchment.HasPlayer { // Parchment
private final Player player;
private final Material itemType;
private final int itemAmount;

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/inventory/InventoryCloseEvent.java
+++ b/src/main/java/org/bukkit/event/inventory/InventoryCloseEvent.java
@@ -28,7 +_,7 @@
* on the next tick. Also be aware that this is not an exhaustive list, and
* other methods could potentially create issues as well.
*/
-public class InventoryCloseEvent extends InventoryEvent {
+public class InventoryCloseEvent extends InventoryEvent implements gg.projecteden.parchment.HasHumanEntity { // Parchment
private static final HandlerList handlers = new HandlerList();
// Paper start
private final Reason reason;

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/inventory/InventoryOpenEvent.java
+++ b/src/main/java/org/bukkit/event/inventory/InventoryOpenEvent.java
@@ -9,7 +_,7 @@
/**
* Called when a player opens an inventory
*/
-public class InventoryOpenEvent extends InventoryEvent implements Cancellable {
+public class InventoryOpenEvent extends InventoryEvent implements Cancellable, gg.projecteden.parchment.HasHumanEntity { // Parchment
private static final HandlerList handlers = new HandlerList();
private boolean cancelled;
private net.kyori.adventure.text.Component titleOverride; // Paper

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/player/AsyncPlayerPreLoginEvent.java
+++ b/src/main/java/org/bukkit/event/player/AsyncPlayerPreLoginEvent.java
@@ -18,7 +_,7 @@
* Consider rendering any translatable yourself with {@link net.kyori.adventure.translation.GlobalTranslator#render}
* if the client's language is known.
*/
-public class AsyncPlayerPreLoginEvent extends Event {
+public class AsyncPlayerPreLoginEvent extends Event implements gg.projecteden.api.interfaces.HasUniqueId { // Parchment
private static final HandlerList handlers = new HandlerList();
private Result result;
private net.kyori.adventure.text.Component message; // Paper

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/player/PlayerEvent.java
+++ b/src/main/java/org/bukkit/event/player/PlayerEvent.java
@@ -7,7 +_,7 @@
/**
* Represents a player related event
*/
-public abstract class PlayerEvent extends Event {
+public abstract class PlayerEvent extends Event implements gg.projecteden.parchment.HasPlayer { // Parchment
protected Player player;
public PlayerEvent(@NotNull final Player who) {

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/player/PlayerPreLoginEvent.java
+++ b/src/main/java/org/bukkit/event/player/PlayerPreLoginEvent.java
@@ -23,7 +_,7 @@
*/
@Deprecated(since = "1.3.2")
@Warning(reason = "This event causes a login thread to synchronize with the main thread")
-public class PlayerPreLoginEvent extends Event {
+public class PlayerPreLoginEvent extends Event implements gg.projecteden.api.interfaces.HasUniqueId { // Parchment
private static final HandlerList handlers = new HandlerList();
private Result result;
private net.kyori.adventure.text.Component message; // Paper

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/player/PlayerUnleashEntityEvent.java
+++ b/src/main/java/org/bukkit/event/player/PlayerUnleashEntityEvent.java
@@ -10,7 +_,7 @@
/**
* Called prior to an entity being unleashed due to a player's action.
*/
-public class PlayerUnleashEntityEvent extends EntityUnleashEvent implements Cancellable {
+public class PlayerUnleashEntityEvent extends EntityUnleashEvent implements Cancellable, gg.projecteden.parchment.HasPlayer { // Parchment
private boolean cancelled = false;

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/raid/RaidTriggerEvent.java
+++ b/src/main/java/org/bukkit/event/raid/RaidTriggerEvent.java
@@ -11,7 +_,7 @@
* Called when a {@link Raid} is triggered (e.g: a player with Bad Omen effect
* enters a village).
*/
-public class RaidTriggerEvent extends RaidEvent implements Cancellable {
+public class RaidTriggerEvent extends RaidEvent implements Cancellable, gg.projecteden.parchment.HasPlayer { // Parchment
private static final HandlerList handlers = new HandlerList();
//

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/server/TabCompleteEvent.java
+++ b/src/main/java/org/bukkit/event/server/TabCompleteEvent.java
@@ -21,7 +_,7 @@
* @apiNote Only called for bukkit API commands {@link org.bukkit.command.Command} and
* {@link org.bukkit.command.CommandExecutor} and not for brigadier commands ({@link io.papermc.paper.command.brigadier.Commands}).
*/
-public class TabCompleteEvent extends Event implements Cancellable {
+public class TabCompleteEvent extends Event implements Cancellable, gg.projecteden.parchment.OptionalLocation { // Parchment
private static final HandlerList handlers = new HandlerList();
//

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/world/GenericGameEvent.java
+++ b/src/main/java/org/bukkit/event/world/GenericGameEvent.java
@@ -15,7 +_,7 @@
* Specific Bukkit events should be used where possible, this event is mainly
* used internally by Sculk sensors.
*/
-public class GenericGameEvent extends WorldEvent implements Cancellable {
+public class GenericGameEvent extends WorldEvent implements Cancellable, gg.projecteden.parchment.HasLocation { // Parchment
private static final HandlerList handlers = new HandlerList();
private final GameEvent event;

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/event/world/StructureGrowEvent.java
+++ b/src/main/java/org/bukkit/event/world/StructureGrowEvent.java
@@ -14,7 +_,7 @@
* Event that is called when an organic structure attempts to grow (Sapling {@literal ->}
* Tree), (Mushroom {@literal ->} Huge Mushroom), naturally or using bonemeal.
*/
-public class StructureGrowEvent extends WorldEvent implements Cancellable {
+public class StructureGrowEvent extends WorldEvent implements Cancellable, gg.projecteden.parchment.OptionalPlayer { // Parchment
private static final HandlerList handlers = new HandlerList();
private boolean cancelled = false;
private final Location location;

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/inventory/Inventory.java
+++ b/src/main/java/org/bukkit/inventory/Inventory.java
@@ -25,7 +_,7 @@
* @see #getContents()
* @see #getStorageContents()
*/
-public interface Inventory extends Iterable<ItemStack> {
+public interface Inventory extends Iterable<ItemStack>, gg.projecteden.parchment.OptionalLocation { // Parchment
/**
* Returns the size of the inventory

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/inventory/InventoryView.java
+++ b/src/main/java/org/bukkit/inventory/InventoryView.java
@@ -9,7 +_,7 @@
* Represents a view linking two inventories and a single player (whose
* inventory may or may not be one of the two).
*/
-public interface InventoryView {
+public interface InventoryView extends gg.projecteden.parchment.HasHumanEntity { // Parchment
public static final int OUTSIDE = -999;
/**
* Represents various extra properties of certain inventory windows.

View File

@@ -0,0 +1,11 @@
--- a/src/main/java/org/bukkit/loot/LootContext.java
+++ b/src/main/java/org/bukkit/loot/LootContext.java
@@ -11,7 +_,7 @@
* Represents additional information a {@link LootTable} can use to modify it's
* generated loot.
*/
-public final class LootContext {
+public final class LootContext implements gg.projecteden.parchment.HasLocation { // Parchment
public static final int DEFAULT_LOOT_MODIFIER = -1;

View File

@@ -0,0 +1,19 @@
package gg.projecteden.parchment;
import org.bukkit.entity.HumanEntity;
import org.jetbrains.annotations.NotNull;
/**
* Represents an object that has a {@link HumanEntity}
* @see gg.projecteden.parchment.OptionalHumanEntity
*/
@FunctionalInterface
public interface HasHumanEntity extends OptionalHumanEntity {
/**
* Gets a {@link HumanEntity} object that this represents
*
* @return human entity
*/
@Override
@NotNull HumanEntity getPlayer();
}

View File

@@ -0,0 +1,18 @@
package gg.projecteden.parchment;
import org.bukkit.Location;
import org.jetbrains.annotations.NotNull;
/**
* Represents an object that has a {@link Location}
* @see OptionalLocation
*/
public interface HasLocation extends OptionalLocation {
/**
* Gets a {@link Location} attached to this object
*
* @return attached location
*/
@Override
@NotNull Location getLocation();
}

View File

@@ -0,0 +1,17 @@
package gg.projecteden.parchment;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
/**
* Represents an object that has a {@link OfflinePlayer}
*/
@FunctionalInterface
public interface HasOfflinePlayer {
/**
* Gets an {@link OfflinePlayer} object that this represents
*
* @return offline player
*/
@NotNull OfflinePlayer getOfflinePlayer();
}

View File

@@ -0,0 +1,19 @@
package gg.projecteden.parchment;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Represents an object that has a {@link Player}
* @see gg.projecteden.parchment.OptionalPlayer
*/
@FunctionalInterface
public interface HasPlayer extends OptionalPlayer, HasHumanEntity {
/**
* Gets a {@link Player} object that this represents
*
* @return player
*/
@Override
@NotNull Player getPlayer();
}

View File

@@ -0,0 +1,18 @@
package gg.projecteden.parchment;
import org.bukkit.entity.HumanEntity;
import org.jetbrains.annotations.Nullable;
/**
* Represents an object that may have a {@link HumanEntity}
* @see HasHumanEntity
*/
@FunctionalInterface
public interface OptionalHumanEntity {
/**
* Gets a {@link HumanEntity} object that this represents, if there is one
*
* @return human entity or null
*/
@Nullable HumanEntity getPlayer();
}

View File

@@ -0,0 +1,17 @@
package gg.projecteden.parchment;
import org.bukkit.Location;
import org.jetbrains.annotations.Nullable;
/**
* Represents an object that may have a {@link Location}
* @see HasLocation
*/
@FunctionalInterface
public interface OptionalLocation {
/**
* Gets a {@link Location} attached to this object if present *
* @return attached location
*/
@Nullable Location getLocation();
}

View File

@@ -0,0 +1,19 @@
package gg.projecteden.parchment;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
/**
* Represents an object that may have a {@link Player}
* @see HasPlayer
*/
@FunctionalInterface
public interface OptionalPlayer extends OptionalHumanEntity {
/**
* Gets a {@link Player} object that this represents, if there is one. This may be null if the
* player is online or the player object being optional.
*
* @return player or null
*/
@Nullable Player getPlayer();
}

View File

@@ -0,0 +1,48 @@
package gg.projecteden.parchment;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.audience.ForwardingAudience;
import net.kyori.adventure.identity.Identified;
import net.kyori.adventure.identity.Identity;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
/**
* Class that may be like a {@link org.bukkit.entity.Player} in that it has a {@link java.util.UUID}, {@link org.bukkit.OfflinePlayer}, {@link Identity}, and a nullable Player.
* @see gg.projecteden.parchment.PlayerLike
*/
public interface OptionalPlayerLike extends OptionalPlayer, gg.projecteden.api.interfaces.HasUniqueId, HasOfflinePlayer, OptionalLocation, Identified, ForwardingAudience.Single {
/**
* Gets the identity associated with this object
*
* @return associated identity
*/
@Override
default @NotNull Identity identity() {
return Identity.identity(getUniqueId());
}
/**
* Returns if the {@link Player} associated with this object is online.
*
* @return if the player is online
*/
default boolean isOnline() {
return getPlayer() != null;
}
@Override
default @NotNull Audience audience() {
return Objects.requireNonNullElse(getPlayer(), Audience.empty());
}
@Override
default @Nullable Location getLocation() {
final Player player = getPlayer();
return player == null ? null : player.getLocation();
}
}

View File

@@ -0,0 +1,30 @@
package gg.projecteden.parchment;
import net.kyori.adventure.audience.Audience;
import org.bukkit.Location;
import org.jetbrains.annotations.NotNull;
/**
* Class that is like a {@link org.bukkit.entity.Player} in that it has a Player, {@link java.util.UUID}, {@link org.bukkit.OfflinePlayer}, and an {@link net.kyori.adventure.identity.Identity}.
* @see gg.projecteden.parchment.OptionalPlayerLike
*/
public interface PlayerLike extends HasPlayer, HasLocation, OptionalPlayerLike {
// reduce nullability checks by re-implementing the methods from OptionalPlayerLike
// (but without the null checks)
@Override
default @NotNull Audience audience() {
return getPlayer();
}
@Override
default boolean isOnline() {
return true;
}
@Override
default @NotNull Location getLocation() {
return getPlayer().getLocation();
}
}

View File

@@ -0,0 +1,140 @@
package gg.projecteden.parchment.entity;
import org.bukkit.entity.Entity;
import java.util.*;
import java.util.function.Supplier;
public final class EntityData {
private static final Map<UUID, List<DataSlot>> PARKED = new HashMap<>();
private static int DATA_IDX;
private final List<EntityDataFragment<?>> data = new ArrayList<>();
private final Class<?> ownerType;
private Entity owner;
EntityData(Class<?> ownerType) {
this.ownerType = ownerType;
}
public static <T extends EntityDataFragment<E>, E extends Entity> EntityDataKey<T, E> createKey(
Supplier<T> generator,
Class<E> ownerType
) {
return new EntityDataKey<>(ownerType, generator, EntityData.DATA_IDX);
}
public static EntityData create(Entity entity) {
EntityData data = new EntityData(entity.getClass());
List<DataSlot> slots = EntityData.PARKED.get(entity.getUniqueId());
if (slots != null) {
for (DataSlot slot : slots) {
data.set(slot.idx, slot.data);
}
}
data.setOwner(entity);
return data;
}
public <T extends EntityDataFragment<E>, E extends Entity> T get(EntityDataKey<T, E> key) {
while (this.data.size() <= key.getIdx()) {
this.data.add(null);
}
T out = cast(this.data.get(key.getIdx()));
if (out == null) {
this.checkEntityType(key.ownerType);
out = key.getGenerator().get();
out.setOwner(cast(this.owner));
this.data.set(key.getIdx(), out);
}
return out;
}
public <T extends EntityDataFragment<E>, E extends Entity> boolean clear(EntityDataKey<T, E> key) {
if (this.data.size() <= key.getIdx()) {
return false;
}
this.checkEntityType(key.ownerType);
return this.data.set(key.getIdx(), null) != null;
}
public void orphan() {
for (EntityDataFragment<?> frag : this.data) {
if (frag != null) {
frag.onOrphan();
}
}
List<DataSlot> persist = new ArrayList<>();
for (int i = this.data.size() - 1; i >= 0; i--) {
EntityDataFragment<?> frag = this.data.get(i);
if (frag != null && frag.isPersistent()) {
persist.add(new DataSlot(frag, i));
}
}
if (!persist.isEmpty()) {
EntityData.PARKED.put(this.owner.getUniqueId(), persist);
}
}
void set(int slot, EntityDataFragment<?> data) {
while (this.data.size() <= slot) {
this.data.add(null);
}
this.data.set(slot, data);
}
void setOwner(Entity entity) {
Class<?> ownerType = entity.getClass();
if (!ownerType.equals(this.ownerType)) {
throw new IllegalArgumentException(String.format(
"Wrong entity type. (entity=%s@%s, expect=%s@%s)",
ownerType, ownerType.getClassLoader(),
this.ownerType, this.ownerType.getClassLoader()
));
}
this.owner = entity;
for (EntityDataFragment<?> frag : this.data) {
if (frag != null) {
frag.setOwner(cast(entity));
}
}
}
private void checkEntityType(Class<?> ownerType) {
if (!ownerType.isAssignableFrom(this.ownerType)) {
throw new IllegalArgumentException(String.format(
"Incompatible entity types. (key=%s@%s, expect=%s@%s)",
ownerType, ownerType.getClassLoader(),
this.ownerType, this.ownerType.getClassLoader()
));
}
}
private <S, T> T cast(S src) {
return (T) src;
}
private static final class DataSlot {
private final EntityDataFragment<?> data;
private final int idx;
private DataSlot(EntityDataFragment<?> data, int idx) {
this.data = data;
this.idx = idx;
}
}
}

View File

@@ -0,0 +1,34 @@
package gg.projecteden.parchment.entity;
import org.bukkit.entity.Entity;
public abstract class EntityDataFragment<E extends Entity> {
private E owner;
private boolean persistent = true;
protected EntityDataFragment() {
}
protected void onOwnerChange() {
}
protected void onOrphan() {
}
protected final E getOwner() {
return this.owner;
}
protected final void setPersistent(boolean persistent) {
this.persistent = persistent;
}
final boolean isPersistent() {
return this.persistent;
}
final void setOwner(E entity) {
this.owner = entity;
this.onOwnerChange();
}
}

View File

@@ -0,0 +1,27 @@
package gg.projecteden.parchment.entity;
import org.bukkit.entity.Entity;
import java.util.function.Supplier;
public final class EntityDataKey<T extends EntityDataFragment<E>, E extends Entity> {
private final Supplier<T> generator;
private final int idx;
final Class<E> ownerType;
EntityDataKey(Class<E> ownerType, Supplier<T> generator, int idx) {
this.generator = generator;
this.idx = idx;
this.ownerType = ownerType;
}
Supplier<T> getGenerator() {
return this.generator;
}
int getIdx() {
return this.idx;
}
}

View File

@@ -0,0 +1,30 @@
package gg.projecteden.parchment.entity;
public final class EntityDataServiceKey<S> {
private final Class<S> serviceType;
private S service;
public EntityDataServiceKey(Class<S> serviceType) {
this.serviceType = serviceType;
}
public S get() {
if (this.service == null) {
throw new IllegalStateException("Service is not initialized.");
}
return this.service;
}
public void set(S service) {
if (this.service != null) {
throw new IllegalStateException("Service is already initialized.");
}
if (!this.serviceType.isInstance(service)) {
throw new IllegalArgumentException("Value does not implement service contract.");
}
this.service = service;
}
}

View File

@@ -0,0 +1,45 @@
package gg.projecteden.parchment.event.block;
import org.bukkit.block.Block;
import org.bukkit.event.HandlerList;
import org.bukkit.event.block.BlockEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Called when a block drops resources in the world. The block will exist in the world at the time.
* <p>
* This event fires in between {@link org.bukkit.event.block.BlockBreakEvent BlockBreakEvent}
* and {@link org.bukkit.event.block.BlockDropItemEvent BlockDropItemEvent}.
*/
public class BlockDropResourcesEvent extends BlockEvent {
private static final HandlerList handlers = new HandlerList();
private final @NotNull List<ItemStack> resources;
public BlockDropResourcesEvent(@NotNull Block block, @NotNull List<ItemStack> resources) {
super(block);
this.resources = resources;
}
/**
* Gets the resources being dropped by the block. This list is guaranteed to be mutable
* and may be safely altered.
* @return mutable list of items
*/
public @NotNull List<ItemStack> getResources() {
return resources;
}
@NotNull
@Override
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
}

View File

@@ -0,0 +1,70 @@
package gg.projecteden.parchment.event.block;
import org.bukkit.block.data.BlockData;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.Location;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
public class CustomBlockUpdateEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private boolean cancelled;
private BlockData block;
private UpdateType updateType;
private Location location;
public CustomBlockUpdateEvent(BlockData block, UpdateType updateType, Location location) {
this.block = block;
this.updateType = updateType;
this.location = location;
}
public CustomBlockUpdateEvent(BlockData block, UpdateType updateType) {
this.block = block;
this.updateType = updateType;
}
@Override
public boolean isCancelled() {
return this.cancelled;
}
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
public BlockData getBlock() {
return block;
}
public UpdateType getUpdateType() {
return updateType;
}
public Location getLocation() {
return location;
}
@NotNull
@Override
public HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
public enum UpdateType {
POWERED,
SHAPE,
INSTRUMENT,
PITCH
}
}

View File

@@ -0,0 +1,60 @@
package gg.projecteden.parchment.event.entity;
import org.bukkit.entity.Entity;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.entity.EntityEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
public class PreEntityShootBowEvent extends EntityEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private final @NotNull ItemStack bow;
private final @NotNull ItemStack arrow;
boolean relative = true;
boolean cancelled = false;
public PreEntityShootBowEvent(@NotNull Entity entity, @NotNull ItemStack bow, @NotNull ItemStack arrow) {
super(entity);
this.bow = bow;
this.arrow = arrow;
}
public @NotNull ItemStack getBow() {
return this.bow;
}
public @NotNull ItemStack getArrow() {
return this.arrow;
}
public boolean isRelative() {
return this.relative;
}
public void setRelative(boolean relative) {
this.relative = relative;
}
@Override
public boolean isCancelled() {
return this.cancelled;
}
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
@Override
public @NotNull HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
}

View File

@@ -0,0 +1,117 @@
package gg.projecteden.parchment.event.player;
import com.google.common.base.Preconditions;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull;
/**
* This event is fired after determining what action should result from a player interacting with
* a respawn anchor.
*/
public class PlayerUseRespawnAnchorEvent extends PlayerEvent implements Cancellable {
/**
* Represents the default possible outcomes of this event.
*/
public enum RespawnAnchorResult {
/**
* The player's spawn point will be set
*/
SET_SPAWN,
/**
* The respawn anchor will explode due to being used outside the nether
*/
EXPLODE,
/**
* The player will charge the respawn anchor
*/
CHARGE,
/**
* The respawn anchor will do nothing
*/
NOTHING
}
private static final HandlerList handlers = new HandlerList();
private final Block respawnAnchor;
private RespawnAnchorResult respawnAnchorResult;
private boolean cancelled = false;
public PlayerUseRespawnAnchorEvent(@NotNull Player who, @NotNull Block respawnAnchor, @NotNull RespawnAnchorResult respawnAnchorResult) {
super(who);
this.respawnAnchor = respawnAnchor;
this.respawnAnchorResult = respawnAnchorResult;
}
/**
* Returns the respawn anchor block involved in this event.
*
* @return the respawn anchor block involved in this event
*/
@NotNull
public Block getRespawnAnchor() {
return this.respawnAnchor;
}
/**
* Describes the outcome of the event.
*
* @return the respawn anchor result for the outcome of the event
*/
@NotNull
public RespawnAnchorResult getResult() {
return this.respawnAnchorResult;
}
/**
* Sets the outcome of the event.
*
* @param result event to set
*/
public void setResult(@NotNull RespawnAnchorResult result) {
this.respawnAnchorResult = Preconditions.checkNotNull(result, "result");
}
/**
* Gets the cancellation state of this event. A cancelled event will not
* be executed in the server, but will still pass to other plugins.
* <p>
* A positive value means the respawn anchor will not take any action, as
* if it had not been clicked at all.
*
* @return true if this event is cancelled
*/
@Override
public boolean isCancelled() {
return cancelled;
}
/**
* Sets the cancellation state of this event. A canceled event will not be
* executed in the server, but will still pass to other plugins.
* <p>
* Canceling this event will prevent use of the respawn anchor, leaving it
* as thought it hadn't been clicked at all.
*
* @param cancel true if you wish to cancel this event
*/
@Override
public void setCancelled(boolean cancel) {
this.cancelled = cancel;
}
@Override
public @NotNull HandlerList getHandlers() {
return handlers;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
}

View File

@@ -0,0 +1,359 @@
package gg.projecteden.parchment.event.sound;
import gg.projecteden.parchment.HasLocation;
import gg.projecteden.parchment.OptionalHumanEntity;
import net.kyori.adventure.sound.Sound;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* Called when a sound is sent to a player.
* Cancelling this event will prevent the packet from sending.
*/
public final class SoundEvent extends Event implements Cancellable {
private static final Logger LOGGER = org.slf4j.LoggerFactory.getLogger(SoundEvent.class);
private static final org.bukkit.event.HandlerList handlers = new org.bukkit.event.HandlerList();
public static final @NotNull Function<Sound, Double> DEFAULT_DISTANCE_FUNCTION = event -> event.volume() > 1.0F ? (double) (16.0F * event.volume()) : 16.0D;
public static final @NotNull Function<SoundEvent, List<Player>> DEFAULT_RECIPIENTS_FUNCTION = new WrappedRecipientsFunction(event -> {
final double distance = event.calculateDistance();
final Location loc = event.getEmitter().location();
return loc.getWorld().getPlayers().stream()
.filter(player -> {
Location pl = player.getLocation();
double x = loc.getX() - pl.getX();
double y = loc.getY() - pl.getY();
double z = loc.getZ() - pl.getZ();
return x * x + y * y + z * z < distance * distance;
})
.toList();
});
private @Nullable HumanEntity except;
private @NotNull Function<@NotNull Sound, @NotNull Double> distanceFunction;
private @NotNull Function<@NotNull SoundEvent, @NotNull List<@NotNull Player>> recipientsFunction;
private @NotNull Sound sound;
private @NotNull Emitter emitter;
private boolean cancelled;
private @Nullable BiFunction<@NotNull SoundEvent, @NotNull Player, @Nullable Sound> soundOverrideFunction;
private @Nullable BiFunction<@NotNull SoundEvent, @NotNull Player, @Nullable Emitter> emitterOverrideFunction;
public SoundEvent(@Nullable HumanEntity except, @NotNull Sound sound, @NotNull Emitter emitter, @Nullable Function<Sound, Double> distanceFunction, @Nullable Function<SoundEvent, List<Player>> recipientsFunction) {
super(true);
this.except = except;
this.sound = Objects.requireNonNull(sound, "sound cannot be null");
this.emitter = Objects.requireNonNull(emitter, "emitter cannot be null");
this.distanceFunction = Objects.requireNonNullElse(distanceFunction, DEFAULT_DISTANCE_FUNCTION);
this.recipientsFunction = wrapRecipientsFunction(Objects.requireNonNullElse(recipientsFunction, DEFAULT_RECIPIENTS_FUNCTION));
}
/**
* Gets the player that <b>won't</b> be receiving this sound.
*
* @return player excluded from receiving this sound
*/
public @Nullable HumanEntity getException() {
return except;
}
/**
* Sets the player that <b>won't</b> be receiving this sound.
*
* @param except player excluded from receiving this sound
*/
public void setException(@Nullable HumanEntity except) {
this.except = except;
}
/**
* Gets the sound that will be sent.
*
* @return sound that will be sent
*/
public @NotNull Sound getSound() {
return sound;
}
/**
* Sets the sound that will be sent.
*
* @param sound sound that will be sent
*/
public void setSound(@NotNull Sound sound) {
this.sound = Objects.requireNonNull(sound, "sound cannot be null");
}
/**
* Gets the emitter which determines how and where the sound will be played from.
*
* @return emitter which determines how and where the sound will be played from
*/
public @NotNull Emitter getEmitter() {
return emitter;
}
/**
* Sets the emitter which determines how and where the sound will be played from.
*
* @param emitter emitter which determines how and where the sound will be played from
*/
public void setEmitter(@NotNull Emitter emitter) {
this.emitter = Objects.requireNonNull(emitter, "emitter cannot be null");
}
/**
* Calculates the distance of the sound.
* <p>
* The distance value is dynamically calculated using a
* {@link Function Function&lt;SoundEvent, Double&gt;}.
* In vanilla Minecraft, the default function is {@link #DEFAULT_DISTANCE_FUNCTION}
* ({@code event -> event.getVolume() > 1.0F ? (double) (16.0F * event.getVolume()) : 16.0D}).
* </p>
* This is used by the vanilla implementation of {@link #calculateRecipients()}, though custom
* implementations won't always use this method.
*
* @return calculated distance
* @see #getDistanceFunction()
* @see #setDistanceFunction(Function)
*/
public double calculateDistance() {
return distanceFunction.apply(sound);
}
/**
* Gets the function that calculates the distance of the sound.
*
* @return distance function
* @see #calculateDistance()
* @see #setDistanceFunction(Function)
*/
public @NotNull Function<@NotNull Sound, @NotNull Double> getDistanceFunction() {
return distanceFunction;
}
/**
* Sets the function that calculates the distance of the sound.
*
* @param distanceFunction distance function
* @see #calculateDistance()
* @see #getDistanceFunction()
*/
public void setDistanceFunction(@NotNull Function<@NotNull Sound, @NotNull Double> distanceFunction) {
this.distanceFunction = Objects.requireNonNull(distanceFunction, "distanceFunction cannot be null");
}
/**
* Determines which players will receive this sound packet.
*
* @return immutable list of players
* @see #getRecipientsFunction()
* @see #setRecipientsFunction(Function)
*/
public @NotNull List<Player> calculateRecipients() {
return recipientsFunction.apply(this);
}
/**
* Gets the function that determines which players will receive the sound packet.
*
* @return recipients function
* @see #calculateRecipients()
* @see #setRecipientsFunction(Function)
*/
public @NotNull Function<@NotNull SoundEvent, @NotNull List<@NotNull Player>> getRecipientsFunction() {
return recipientsFunction;
}
/**
* Sets the function that determines which players will receive the sound packet.
* <p>
* This function does not need to query {@link #getException()} as this is done automatically.
*
* @param recipientsFunction recipients function
* @see #calculateRecipients()
* @see #getRecipientsFunction()
*/
public void setRecipientsFunction(@NotNull Function<@NotNull SoundEvent, @NotNull List<@NotNull Player>> recipientsFunction) {
this.recipientsFunction = wrapRecipientsFunction(Objects.requireNonNull(recipientsFunction, "recipientsFunction cannot be null"));
}
/**
* Gets the function that overrides what {@link Sound} is sent to a {@link Player}.
*
* @return sound override function (or {@code null} if not overridden)
*/
public @Nullable BiFunction<@NotNull SoundEvent, @NotNull Player, @Nullable Sound> getSoundOverrideFunction() {
return soundOverrideFunction;
}
/**
* Sets the function that overrides what {@link Sound} is sent to a {@link Player}.
*
* @param soundOverrideFunction function which accepts a sound event and a player and returns
* a sound (or {@code null} if the default sound should be used)
*/
public void setSoundOverrideFunction(@Nullable BiFunction<@NotNull SoundEvent, @NotNull Player, @Nullable Sound> soundOverrideFunction) {
this.soundOverrideFunction = soundOverrideFunction;
}
/**
* Calculates the sound that will be sent to a {@link Player}.
*
* @param player player to calculate the sound for
* @return sound that will be sent to the player
*/
public @NotNull Sound calculateSound(@NotNull Player player) {
if (soundOverrideFunction != null) {
try {
Sound override = soundOverrideFunction.apply(this, player);
if (override != null) {
return override;
}
} catch (Throwable e) {
LOGGER.error("Error while overriding sound for player " + player.getName(), e);
}
}
return sound;
}
/**
* Gets the function that overrides what {@link Emitter} is used when playing this sound to a
* {@link Player}.
*
* @return emitter override function (or {@code null} if not overridden)
*/
public @Nullable BiFunction<@NotNull SoundEvent, @NotNull Player, @Nullable Emitter> getEmitterOverrideFunction() {
return emitterOverrideFunction;
}
/**
* Sets the function that overrides what {@link Emitter} is used when playing this sound to a
* {@link Player}.
*
* @param emitterOverrideFunction function which accepts a sound event and a player and returns
* an emitter
* (or {@code null} if the default emitter should be used)
*/
public void setEmitterOverrideFunction(@Nullable BiFunction<@NotNull SoundEvent, @NotNull Player, @Nullable Emitter> emitterOverrideFunction) {
this.emitterOverrideFunction = emitterOverrideFunction;
}
/**
* Calculates the emitter that will be used when playing this sound to a {@link Player}.
*
* @param player player to calculate the emitter for
* @return emitter that will be used when playing the sound to the player
*/
public @NotNull Emitter calculateEmitter(@NotNull Player player) {
if (emitterOverrideFunction != null) {
try {
Emitter override = emitterOverrideFunction.apply(this, player);
if (override != null) {
return override;
}
} catch (Throwable e) {
LOGGER.error("Error while overriding emitter for player " + player.getName(), e);
}
}
return emitter;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
@NotNull
public static org.bukkit.event.HandlerList getHandlerList() {
return handlers;
}
@Override
public @NotNull org.bukkit.event.HandlerList getHandlers() {
return handlers;
}
private record WrappedRecipientsFunction(@NotNull Function<SoundEvent, List<Player>> wrapped) implements Function<SoundEvent, List<Player>> {
@Override
public @NotNull List<Player> apply(@NotNull SoundEvent event) {
List<Player> recipients = wrapped.apply(event);
HumanEntity except = event.getException();
if (except != null) {
List<Player> filteredRecipients = new ArrayList<>(recipients.size());
for (Player player : recipients) {
if (!player.getUniqueId().equals(except.getUniqueId()))
filteredRecipients.add(player);
}
return filteredRecipients;
}
return recipients;
}
}
@NotNull
private static Function<SoundEvent, List<Player>> wrapRecipientsFunction(@NotNull Function<SoundEvent, List<Player>> recipientsFunction) {
if (recipientsFunction instanceof WrappedRecipientsFunction)
return recipientsFunction;
else
return new WrappedRecipientsFunction(recipientsFunction);
}
/**
* The class which determines where a sound will emit from.
*/
public sealed interface Emitter extends HasLocation permits EntityEmitter, LocationEmitter {
/**
* Gets the location at which the sound will be played.
*
* @return sound's location
* @deprecated use {@link #getLocation()} instead
*/
@NotNull
@Deprecated
default Location location() {
return getLocation();
}
}
/**
* An emitter which plays a sound from an entity.
*
* @param entity the entity from which the sound will be played
*/
public record EntityEmitter(@NotNull Entity entity) implements Emitter {
@Override
public @NotNull Location getLocation() {
return entity.getLocation();
}
}
/**
* An emitter which plays a sound from a location.
*
* @param location the location from which the sound will be played
*/
public record LocationEmitter(@NotNull Location location) implements Emitter {
@Override
public @NotNull Location getLocation() {
return location;
}
}
}

View File

@@ -0,0 +1,50 @@
package gg.projecteden.parchment.inventory;
/**
* A type of crafting recipe.
*/
public enum RecipeType {
/**
* Recipes crafted in the standard crafting table.
*/
CRAFTING(false),
/**
* Recipes for smelting an item inside a furnace.
*/
SMELTING(true),
/**
* Recipes for smelting an item inside a blasting furnace.
*/
BLASTING(true),
/**
* Recipes for smelting an item inside a smoker.
*/
SMOKING(true),
/**
* Recipes for cooking an item on a campfire.
*/
CAMPFIRE_COOKING(true),
/**
* Recipes for carving stones in a stonecutter.
*/
STONECUTTING(true),
/**
* Recipes for smithing an item in a smithing table.
*/
SMITHING(false),
;
private final boolean singleInput;
RecipeType(boolean singleInput) {
this.singleInput = singleInput;
}
/**
* Determines if the recipe only accepts a single item for input.
* @return true if the recipe only accepts a single item for input
*/
public boolean isSingleInput() {
return singleInput;
}
}

View File

@@ -0,0 +1,123 @@
package gg.projecteden.parchment.sidebar;
import gg.projecteden.parchment.entity.EntityData;
import gg.projecteden.parchment.entity.EntityDataFragment;
import gg.projecteden.parchment.entity.EntityDataKey;
import org.bukkit.entity.Player;
import java.util.Objects;
public final class Sidebar extends EntityDataFragment<Player> {
private static final EntityDataKey<Sidebar, Player> DATA_KEY = EntityData.createKey(Sidebar::new, Player.class);
private final SidebarBuffer[] buffer = new SidebarBuffer[2];
private final StageImpl stage = new StageImpl();
private final Runnable layoutListener;
private SidebarLayout layout;
private boolean visible;
private int back;
public static Sidebar get(Player player) {
Objects.requireNonNull(player);
return player.getStoredEntityData().get(Sidebar.DATA_KEY);
}
private Sidebar() {
this.buffer[0] = SidebarBufferUtilSpec.IMPL_KEY.get().create("_sidebar_l");
this.buffer[1] = SidebarBufferUtilSpec.IMPL_KEY.get().create("_sidebar_r");
this.layoutListener = () -> {
this.layout.update(this.stage);
this.flush();
};
this.setPersistent(false);
}
public void applyLayout(SidebarLayout layout) {
if (this.layout != null) {
this.layout.unsubscribe(this.layoutListener);
this.buffer[this.back].clear();
}
this.layout = layout;
if (layout == null) {
this.hide();
} else {
layout.setup(this.stage);
this.flush();
layout.subscribe(this.layoutListener);
}
}
private void setTitle(String title) {
this.buffer[this.back].setTitle(title);
}
private void setLine(int idx, String value, String display) {
if (value == null) {
this.buffer[this.back].clearLine(idx);
} else {
this.buffer[this.back].setLine(idx, value, display);
}
}
private void flush() {
SidebarBuffer front = this.buffer[this.back];
SidebarBuffer back = this.buffer[this.back ^ 1];
boolean shouldShow = !this.visible;
if (front.hasDiverged(back)) {
front.pushChanges();
back.sync(front);
this.back ^= 1;
shouldShow = true;
}
if (shouldShow) {
front.setActive();
this.visible = true;
}
}
private void hide() {
SidebarBufferUtilSpec.IMPL_KEY.get().hideSidebar(this.getOwner());
this.visible = false;
}
@Override
protected void onOwnerChange() {
this.buffer[0].setOwner(this.getOwner());
this.buffer[1].setOwner(this.getOwner());
if (this.visible) {
this.buffer[this.back ^ 1].setActive();
}
}
private final class StageImpl implements SidebarStage {
@Override
public void setTitle(String title) {
Sidebar.this.setTitle(title);
}
@Override
public void setLine(int line, String value) {
this.setLine(line, value, null);
}
@Override
public void setLine(int line, String value, String display) {
Sidebar.this.setLine(line, value, display);
}
}
}

View File

@@ -0,0 +1,100 @@
package gg.projecteden.parchment.sidebar;
import org.bukkit.entity.Player;
import java.util.Arrays;
import java.util.Objects;
public abstract class SidebarBuffer {
@SuppressWarnings("StringOperationCanBeSimplified")
private static final String AUTO_SPACE = new String();
protected final String name;
protected final int size;
protected final String[] liveLines;
protected final String[] stagedLines;
protected final String[] liveDisplays;
protected final String[] stagedDisplays;
protected String liveTitle = "";
protected String stagedTitle = "";
protected SidebarBuffer(String name, int size) {
this.name = name;
this.size = size;
this.liveLines = new String[size];
this.stagedLines = new String[size];
this.liveDisplays = new String[size];
this.stagedDisplays = new String[size];
}
protected abstract void setActive();
protected abstract void pushChanges();
protected abstract void setOwner(Player player);
protected abstract boolean checkTitle(String title);
protected abstract boolean checkLine(String line);
void setTitle(String title) {
this.stagedTitle = Objects.requireNonNullElse(title, "");
}
void setLine(int line, String value, String display) {
Objects.requireNonNull(value);
if (line < 0 || line > this.size - 1) {
throw new IndexOutOfBoundsException();
}
this.stagedLines[line] = value;
this.stagedDisplays[line] = display;
for (int i = line - 1; i >= 0; i--) {
if (this.stagedLines[i] == null) {
this.stagedLines[i] = SidebarBuffer.AUTO_SPACE;
} else {
break;
}
}
}
void clearLine(int line) {
if (line < 0 || line > this.size - 1) {
throw new IndexOutOfBoundsException();
}
this.stagedLines[line] = SidebarBuffer.AUTO_SPACE;
if (line + 1 == this.size || this.stagedLines[line + 1] == null) {
for (int i = line; i >= 0; i--) {
if (this.stagedLines[i] == SidebarBuffer.AUTO_SPACE) {
this.stagedLines[i] = null;
} else {
break;
}
}
}
}
void sync(SidebarBuffer live) {
this.stagedTitle = live.liveTitle;
System.arraycopy(live.liveLines, 0, this.stagedLines, 0, this.size);
System.arraycopy(live.liveDisplays, 0, this.stagedDisplays, 0, this.size);
}
void clear() {
this.stagedTitle = "";
Arrays.fill(this.stagedLines, null);
Arrays.fill(this.stagedDisplays, null);
}
boolean hasDiverged(SidebarBuffer live) {
boolean out = !Objects.equals(this.stagedTitle, live.liveTitle);
out = out || !Arrays.equals(this.stagedLines, live.liveLines);
out = out || !Arrays.equals(this.stagedDisplays, live.liveDisplays);
return out;
}
}

View File

@@ -0,0 +1,12 @@
package gg.projecteden.parchment.sidebar;
import gg.projecteden.parchment.entity.EntityDataServiceKey;
import org.bukkit.entity.Player;
public interface SidebarBufferUtilSpec {
EntityDataServiceKey<SidebarBufferUtilSpec> IMPL_KEY = new EntityDataServiceKey<>(SidebarBufferUtilSpec.class);
SidebarBuffer create(String bufferName);
void hideSidebar(Player player);
}

View File

@@ -0,0 +1,58 @@
package gg.projecteden.parchment.sidebar;
import java.util.HashSet;
import java.util.Set;
/**
* A sidebar layout. Subclasses can describe custom layouts by using the
* {@link #setup(SidebarStage)} and {@link #update(SidebarStage)} hooks.
*/
public abstract class SidebarLayout {
private final Set<Runnable> subscribers = new HashSet<>();
/**
* Pushes an update to all subscribers.
*/
public final void refresh() {
synchronized (this.subscribers) {
for (Runnable s : this.subscribers) {
s.run();
}
}
}
/**
* Runs when the layout is first applied.
*
* <p> The provided sidebar stage may be used to create the first
* frame of sidebar data, which will be pushed to the client upon
* exit of this method.
*
* @param stage The sidebar stage. Using it outside this method will
* result in undefined behavior.
*/
protected void setup(SidebarStage stage) {
}
/**
* Runs when a refresh is requested.
*
* <p> The provided sidebar stage may be used to update the existing
* sidebar data, which will be pushed to the client upon exit of
* this method. Sidebar data from previous hook calls will stay the
* same unless explicitly changed here.
*
* @param stage The sidebar stage. Using it outside this method will
* result in undefined behavior.
*/
protected void update(SidebarStage stage) {
}
final void subscribe(Runnable listener) {
this.subscribers.add(listener);
}
final void unsubscribe(Runnable listener) {
this.subscribers.remove(listener);
}
}

View File

@@ -0,0 +1,32 @@
package gg.projecteden.parchment.sidebar;
/**
* An abstracted sidebar stage.
*/
public interface SidebarStage {
/**
* Stages a new title.
*
* @param title The new title.
*/
void setTitle(String title);
/**
* Stages a new line at the provided index.
*
* @param line The line index.
* @param value The new line.
*/
void setLine(int line, String value);
/**
* Stages a new line at the provided index
* Uses the display as the right aligned text
*
* @param line The line index
* @param value The new line
* @param display The right aligned text
*/
void setLine(int line, String value, String display);
}

View File

@@ -4,7 +4,7 @@
// macheOldPath = file("F:\\Projects\\PaperTooling\\mache\\versions\\1.21.4\\src\\main\\java") // macheOldPath = file("F:\\Projects\\PaperTooling\\mache\\versions\\1.21.4\\src\\main\\java")
// gitFilePatches = true // gitFilePatches = true
+ val fork = forks.register("fork") { + val fork = forks.register("parchment") {
+ upstream.patchDir("paperServer") { + upstream.patchDir("paperServer") {
+ upstreamPath = "paper-server" + upstreamPath = "paper-server"
+ excludes = setOf("src/minecraft", "patches", "build.gradle.kts") + excludes = setOf("src/minecraft", "patches", "build.gradle.kts")
@@ -52,3 +52,23 @@
implementation("ca.spottedleaf:concurrentutil:0.0.2") implementation("ca.spottedleaf:concurrentutil:0.0.2")
implementation("org.jline:jline-terminal-ffm:3.27.1") // use ffm on java 22+ implementation("org.jline:jline-terminal-ffm:3.27.1") // use ffm on java 22+
implementation("org.jline:jline-terminal-jni:3.27.1") // fall back to jni on java 21 implementation("org.jline:jline-terminal-jni:3.27.1") // fall back to jni on java 21
@@ -192,14 +_,14 @@
val gitBranch = git.exec(providers, "rev-parse", "--abbrev-ref", "HEAD").get().trim()
attributes(
"Main-Class" to "org.bukkit.craftbukkit.Main",
- "Implementation-Title" to "Paper",
+ "Implementation-Title" to "Parchment",
"Implementation-Version" to implementationVersion,
"Implementation-Vendor" to date,
- "Specification-Title" to "Paper",
+ "Specification-Title" to "Parchment",
"Specification-Version" to project.version,
- "Specification-Vendor" to "Paper Team",
- "Brand-Id" to "papermc:paper",
- "Brand-Name" to "Paper",
+ "Specification-Vendor" to "Project Eden",
+ "Brand-Id" to "projectedengg:parchment",
+ "Brand-Name" to "Parchment",
"Build-Number" to (build ?: ""),
"Build-Time" to buildTime.toString(),
"Git-Branch" to gitBranch,

View File

@@ -0,0 +1,20 @@
package gg.projecteden.parchment.entity;
import gg.projecteden.parchment.sidebar.SidebarBufferUtil;
import gg.projecteden.parchment.sidebar.SidebarBufferUtilSpec;
public class EntityDataServices {
private static boolean initialized;
public static void init() {
if (initialized) {
throw new RuntimeException("EntityData Services already initialized");
}
initialized = true;
// Initialize Services Here
SidebarBufferUtilSpec.IMPL_KEY.set(new SidebarBufferUtil());
}
}

View File

@@ -0,0 +1,28 @@
package gg.projecteden.parchment.event.sound;
import io.papermc.paper.adventure.PaperAdventure;
import net.kyori.adventure.sound.Sound;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import org.bukkit.Location;
import java.util.Optional;
import java.util.function.Function;
public class ParchmentSoundEvent {
public static final Function<Sound, Double> DISTANCE_FUNCTION = sound -> {
Optional<SoundEvent> soundEvent = PaperAdventure.asVanillaSound(sound.name());
if (soundEvent.isPresent())
return Double.valueOf(soundEvent.get().getRange(sound.volume()));
return gg.projecteden.parchment.event.sound.SoundEvent.DEFAULT_DISTANCE_FUNCTION.apply(sound);
};
public static gg.projecteden.parchment.event.sound.SoundEvent.Emitter createEmitter(Level level, double x, double y, double z) {
return new gg.projecteden.parchment.event.sound.SoundEvent.LocationEmitter(new Location(level.getWorld(), x, y, z));
}
public static gg.projecteden.parchment.event.sound.SoundEvent.Emitter createEmitter(Entity entity) {
return new gg.projecteden.parchment.event.sound.SoundEvent.EntityEmitter(entity.getBukkitEntity());
}
}

View File

@@ -0,0 +1,44 @@
package gg.projecteden.parchment.inventory;
import net.minecraft.world.item.crafting.AbstractCookingRecipe;
public class CraftRecipeType {
public static net.minecraft.world.item.crafting.RecipeType asNMS(RecipeType recipeType) {
return switch (recipeType) {
case CRAFTING -> net.minecraft.world.item.crafting.RecipeType.CRAFTING;
case SMELTING -> net.minecraft.world.item.crafting.RecipeType.SMELTING;
case BLASTING -> net.minecraft.world.item.crafting.RecipeType.BLASTING;
case SMOKING -> net.minecraft.world.item.crafting.RecipeType.SMOKING;
case CAMPFIRE_COOKING -> net.minecraft.world.item.crafting.RecipeType.CAMPFIRE_COOKING;
case STONECUTTING -> net.minecraft.world.item.crafting.RecipeType.STONECUTTING;
case SMITHING -> net.minecraft.world.item.crafting.RecipeType.SMITHING;
};
}
public static RecipeType asBukkit(net.minecraft.world.item.crafting.RecipeType recipeType) {
if (recipeType == net.minecraft.world.item.crafting.RecipeType.CRAFTING) {
return RecipeType.CRAFTING;
} else if (recipeType == net.minecraft.world.item.crafting.RecipeType.SMELTING) {
return RecipeType.SMELTING;
} else if (recipeType == net.minecraft.world.item.crafting.RecipeType.BLASTING) {
return RecipeType.BLASTING;
} else if (recipeType == net.minecraft.world.item.crafting.RecipeType.SMOKING) {
return RecipeType.SMOKING;
} else if (recipeType == net.minecraft.world.item.crafting.RecipeType.CAMPFIRE_COOKING) {
return RecipeType.CAMPFIRE_COOKING;
} else if (recipeType == net.minecraft.world.item.crafting.RecipeType.STONECUTTING) {
return RecipeType.STONECUTTING;
} else if (recipeType == net.minecraft.world.item.crafting.RecipeType.SMITHING) {
return RecipeType.SMITHING;
}
throw new IllegalArgumentException("Unknown recipe type");
}
public static net.minecraft.world.item.crafting.RecipeType<AbstractCookingRecipe> asCookingRecipe(RecipeType recipeType) {
try {
return (net.minecraft.world.item.crafting.RecipeType<AbstractCookingRecipe>) asNMS(recipeType);
} catch (ClassCastException exc) {
throw new IllegalArgumentException("Recipe type must be a cooking recipe");
}
}
}

View File

@@ -0,0 +1,151 @@
package gg.projecteden.parchment.inventory;
import com.google.common.base.Preconditions;
import net.minecraft.world.Container;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.craftbukkit.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
/**
* A container which holds only one item and returns similar values to that of
* {@link net.minecraft.world.SimpleContainer SimpleContainer}, meaning it will raise
* {@link IndexOutOfBoundsException IndexOutOfBoundsExceptions} and return empty item stacks
* where applicable to mirror that class.
*/
public class SingletonContainer implements net.minecraft.world.Container {
private @NotNull ItemStack item;
private int maxStackSize = Container.MAX_STACK;
public SingletonContainer() {
this.item = ItemStack.EMPTY;
}
public SingletonContainer(@NotNull ItemStack item) {
this.item = Preconditions.checkNotNull(item, "item");
}
public SingletonContainer(org.bukkit.inventory.@NotNull ItemStack item) {
this.item = CraftItemStack.asNMSCopy(Preconditions.checkNotNull(item, "item"));
}
public SingletonContainer(@NotNull Material material) {
this(new org.bukkit.inventory.ItemStack(Preconditions.checkNotNull(material, "material")));
}
private static void throwIndexException(int index) {
throw new IndexOutOfBoundsException("Received slot " + index + ", expected 0");
}
@Override
public int getContainerSize() {
return 1;
}
@Override
public boolean isEmpty() {
return item.isEmpty();
}
@Override
public ItemStack getItem(int slot) {
if (slot < 0) {
throwIndexException(slot);
}
return slot == 0 ? item : ItemStack.EMPTY;
}
@Override
public ItemStack removeItem(int slot, int amount) {
if (slot < 0) {
throwIndexException(slot);
}
ItemStack itemStack = slot == 0 && !item.isEmpty() ? item.split(amount) : ItemStack.EMPTY;
if (!itemStack.isEmpty()) {
setChanged();
}
return itemStack;
}
@Override
public ItemStack removeItemNoUpdate(int slot) {
if (slot != 0) {
throwIndexException(slot);
}
ItemStack oldItem = item;
item = ItemStack.EMPTY;
return oldItem.isEmpty() ? ItemStack.EMPTY : oldItem;
}
@Override
public void setItem(int slot, ItemStack stack) {
if (slot != 0) {
throwIndexException(slot);
}
item = stack;
}
@Override
public int getMaxStackSize() {
return maxStackSize;
}
@Override
public void setMaxStackSize(int size) {
maxStackSize = size;
}
@Override
public void setChanged() {
}
@Override
public boolean stillValid(Player player) {
return true;
}
@Override
public List<ItemStack> getContents() {
return Collections.singletonList(item);
}
@Override
public void onOpen(CraftHumanEntity who) {
}
@Override
public void onClose(CraftHumanEntity who) {
}
@Override
public List<HumanEntity> getViewers() {
return Collections.emptyList();
}
@Override
public InventoryHolder getOwner() {
return null;
}
@Override
public Location getLocation() {
return null;
}
@Override
public void clearContent() {
item = ItemStack.EMPTY;
}
}

View File

@@ -0,0 +1,182 @@
package gg.projecteden.parchment.sidebar;
import gg.projecteden.parchment.util.StringUtils;
import net.minecraft.network.Connection;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.numbers.BlankFormat;
import net.minecraft.network.chat.numbers.FixedFormat;
import net.minecraft.network.chat.numbers.NumberFormat;
import net.minecraft.network.protocol.game.*;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.world.scores.DisplaySlot;
import net.minecraft.world.scores.Objective;
import net.minecraft.world.scores.PlayerTeam;
import net.minecraft.world.scores.Team;
import net.minecraft.world.scores.criteria.ObjectiveCriteria;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
import java.util.Objects;
import java.util.Optional;
public class SidebarBufferImpl extends SidebarBuffer {
private static final int NAME_LIMIT = 38;
private static final int AFFIX_LIMIT = 16;
private static final int SIZE = 15;
static final int SIDEBAR_SLOT = 1;
private final Objective objective = new Objective(DedicatedServer.getServer().getScoreboard(), this.name,
ObjectiveCriteria.DUMMY, Component.literal(this.stagedTitle), ObjectiveCriteria.RenderType.INTEGER,
false, null);
private Connection connection;
SidebarBufferImpl(String name) {
super(name, SidebarBufferImpl.SIZE);
}
protected void setActive() {
objective.setDisplayName(Component.literal(StringUtils.colorize(this.stagedTitle)));
ClientboundSetObjectivePacket packet = new ClientboundSetObjectivePacket(this.objective, ClientboundSetObjectivePacket.METHOD_CHANGE);
ClientboundSetDisplayObjectivePacket packet2 = new ClientboundSetDisplayObjectivePacket(DisplaySlot.SIDEBAR, this.objective);
this.connection.send(packet);
this.connection.send(packet2);
}
protected void pushChanges() {
if (!Objects.equals(this.liveTitle, this.stagedTitle)) {
this.liveTitle = this.stagedTitle;
ClientboundSetObjectivePacket packet = new ClientboundSetObjectivePacket(this.objective, ClientboundSetObjectivePacket.METHOD_CHANGE);
this.connection.send(packet);
}
int liveEnd = this.size;
for (int i = 0; i < this.size; i++) {
if (this.liveLines[i] == null) {
liveEnd = i;
break;
}
}
int stagedEnd = this.size;
for (int i = 0; i < this.size; i++) {
if (this.stagedLines[i] == null) {
stagedEnd = i;
break;
}
}
int maxEnd = Math.max(liveEnd, stagedEnd);
int liveIdx = liveEnd - maxEnd;
int stagedIdx = stagedEnd - maxEnd;
for (int i = 0; i < this.size; i++) {
String live = liveIdx >= 0 ? this.liveLines[liveIdx] : null;
String staged = stagedIdx >= 0 ? this.stagedLines[stagedIdx] : null;
String liveDisplay = liveIdx >= 0 ? this.liveDisplays[liveIdx] : null;
String stagedDisplay = stagedIdx >= 0 ? this.stagedDisplays[stagedIdx] : null;
if (!Objects.equals(live, staged) || !Objects.equals(liveDisplay, stagedDisplay)) {
if (live != null) {
this.sendDelete(live, liveEnd - liveIdx);
}
if (staged != null) {
this.sendCreate(staged, stagedEnd - stagedIdx, this.stagedDisplays[stagedIdx]);
}
}
liveIdx++;
stagedIdx++;
}
System.arraycopy(this.stagedLines, 0, this.liveLines, 0, this.size);
System.arraycopy(this.stagedDisplays, 0, this.liveDisplays, 0, this.size);
}
@Override
protected void setOwner(Player player) {
this.connection = ((CraftPlayer) player).getHandle().connection.connection;
ClientboundSetObjectivePacket packet = new ClientboundSetObjectivePacket(this.objective, ClientboundSetObjectivePacket.METHOD_ADD);
this.connection.send(packet);
for (int i = 0; i < this.size; i++) {
String live = this.liveLines[i];
if (live != null) {
this.sendCreate(live, i, stagedDisplays[i]);
}
}
}
@Override
protected boolean checkTitle(String line) {
return line.length() <= 32;
}
@Override
protected boolean checkLine(String line) {
return line.length() <= SidebarBufferImpl.NAME_LIMIT + SidebarBufferImpl.AFFIX_LIMIT * 2;
}
private void sendCreate(String value, int line, String display) {
String id = "\u00a7" + (char) ('\u0080' + line);
value = StringUtils.colorize(value);
if (value.length() > SidebarBufferImpl.NAME_LIMIT) {
String prefix = value.substring(0, SidebarBufferImpl.AFFIX_LIMIT);
String suffix = "";
value = value.substring(SidebarBufferImpl.AFFIX_LIMIT);
if (value.length() > SidebarBufferImpl.NAME_LIMIT) {
suffix = value.substring(SidebarBufferImpl.NAME_LIMIT);
value = value.substring(0, SidebarBufferImpl.NAME_LIMIT);
}
PlayerTeam team = new PlayerTeam(DedicatedServer.getServer().getScoreboard(), this.getTeamName(line));
team.setPlayerPrefix(Component.literal(prefix));
team.setPlayerSuffix(Component.literal(suffix));
team.setNameTagVisibility(Team.Visibility.NEVER);
team.setCollisionRule(Team.CollisionRule.NEVER);
team.getPlayers().add(id + value);
ClientboundSetPlayerTeamPacket packet = ClientboundSetPlayerTeamPacket.createAddOrModifyPacket(team, true);
this.connection.send(packet);
}
java.util.Optional<NumberFormat> numberFormat = java.util.Optional.of((display == null || display.isEmpty() || display.isBlank()) ? BlankFormat.INSTANCE : new FixedFormat(Component.literal(StringUtils.colorize(display))));
ClientboundSetScorePacket packet = new ClientboundSetScorePacket(id + value, this.name, line, Optional.empty(), numberFormat);
this.connection.send(packet);
}
private void sendDelete(String value, int line) {
String id = "\u00a7" + (char) ('\u0080' + line);
value = StringUtils.colorize(value);
if (value.length() > SidebarBufferImpl.NAME_LIMIT) {
value = value.substring(
SidebarBufferImpl.AFFIX_LIMIT,
Math.min(value.length(), SidebarBufferImpl.AFFIX_LIMIT + SidebarBufferImpl.NAME_LIMIT)
);
ClientboundSetPlayerTeamPacket packet = ClientboundSetPlayerTeamPacket.createRemovePacket(DedicatedServer.getServer().getScoreboard().getPlayerTeam(this.getTeamName(line)));
this.connection.send(packet);
}
ClientboundResetScorePacket packet = new ClientboundResetScorePacket(id + value, this.name);
this.connection.send(packet);
}
private String getTeamName(int line) {
return this.name + "/" + Integer.toUnsignedString(line, 32);
}
}

View File

@@ -0,0 +1,19 @@
package gg.projecteden.parchment.sidebar;
import net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket;
import net.minecraft.world.scores.DisplaySlot;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
public class SidebarBufferUtil implements SidebarBufferUtilSpec {
@Override
public SidebarBuffer create(String name) {
return new SidebarBufferImpl(name);
}
@Override
public void hideSidebar(Player player) {
ClientboundSetDisplayObjectivePacket packet = new ClientboundSetDisplayObjectivePacket(DisplaySlot.SIDEBAR, null);
((CraftPlayer) player).getHandle().connection.send(packet);
}
}

View File

@@ -0,0 +1,30 @@
package gg.projecteden.parchment.util;
import net.md_5.bungee.api.ChatColor;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class StringUtils {
private static final String colorChar = "§";
private static final String altColorChar = "&";
private static final String colorCharsRegex = "[" + colorChar + altColorChar + "]";
private static final Pattern hexPattern = Pattern.compile(colorCharsRegex + "#[a-fA-F\\d]{6}");
public static String colorize(String input) {
if (input == null)
return null;
while (true) {
Matcher matcher = hexPattern.matcher(input);
if (!matcher.find()) break;
String color = matcher.group();
input = input.replace(color, ChatColor.of(color.replaceFirst(colorCharsRegex, "")).toString());
}
return ChatColor.translateAlternateColorCodes(altColorChar.charAt(0), input);
}
}

View File

@@ -10,7 +10,7 @@ plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0" id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0"
} }
rootProject.name = "fork" rootProject.name = "Parchment"
include("fork-api") include("parchment-api")
include("fork-server") include("parchment-server")