Mirror von
https://github.com/PaperMC/Paper.git
synchronisiert 2024-11-14 20:10:05 +01:00
Improve LootContext API
Dieser Commit ist enthalten in:
Ursprung
61fe23cd08
Commit
ff6babece2
381
patches/api/0490-Improve-LootContext-API.patch
Normale Datei
381
patches/api/0490-Improve-LootContext-API.patch
Normale Datei
@ -0,0 +1,381 @@
|
|||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||||
|
Date: Tue, 22 Mar 2022 22:14:10 -0700
|
||||||
|
Subject: [PATCH] Improve LootContext API
|
||||||
|
|
||||||
|
|
||||||
|
diff --git a/src/main/java/io/papermc/paper/loot/LootContextKey.java b/src/main/java/io/papermc/paper/loot/LootContextKey.java
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000000000000000000000000000000..67f8d027d68c0af5c26ee23d625da6917f6f1642
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/src/main/java/io/papermc/paper/loot/LootContextKey.java
|
||||||
|
@@ -0,0 +1,34 @@
|
||||||
|
+package io.papermc.paper.loot;
|
||||||
|
+
|
||||||
|
+import io.papermc.paper.math.Position;
|
||||||
|
+import net.kyori.adventure.key.Keyed;
|
||||||
|
+import org.bukkit.block.TileState;
|
||||||
|
+import org.bukkit.block.data.BlockData;
|
||||||
|
+import org.bukkit.damage.DamageSource;
|
||||||
|
+import org.bukkit.entity.Entity;
|
||||||
|
+import org.bukkit.entity.Player;
|
||||||
|
+import org.bukkit.inventory.ItemStack;
|
||||||
|
+
|
||||||
|
+import static io.papermc.paper.loot.LootContextKeyImpl.create;
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
+ * A key to a possible value in a {@link org.bukkit.loot.LootContext}.
|
||||||
|
+ *
|
||||||
|
+ * @param <T> the value type
|
||||||
|
+ */
|
||||||
|
+@SuppressWarnings("unused")
|
||||||
|
+public interface LootContextKey<T> extends Keyed {
|
||||||
|
+
|
||||||
|
+ LootContextKey<Entity> THIS_ENTITY = create("this_entity");
|
||||||
|
+ LootContextKey<Player> LAST_DAMAGE_PLAYER = create("last_damage_player");
|
||||||
|
+ LootContextKey<DamageSource> DAMAGE_SOURCE = create("damage_source");
|
||||||
|
+ LootContextKey<Entity> ATTACKING_ENTITY = create("attacking_entity");
|
||||||
|
+ LootContextKey<Entity> DIRECT_ATTACKING_ENTITY = create("direct_attacking_entity");
|
||||||
|
+ LootContextKey<Position> ORIGIN = create("origin");
|
||||||
|
+ LootContextKey<BlockData> BLOCK_DATA = create("block_state");
|
||||||
|
+ LootContextKey<TileState> TILE_STATE = create("block_entity");
|
||||||
|
+ LootContextKey<ItemStack> TOOL = create("tool");
|
||||||
|
+ LootContextKey<Float> EXPLOSION_RADIUS = create("explosion_radius");
|
||||||
|
+ LootContextKey<Integer> ENCHANTMENT_LEVEL = create("enchantment_level");
|
||||||
|
+ LootContextKey<Boolean> ENCHANTMENT_ACTIVE = create("enchantment_active");
|
||||||
|
+}
|
||||||
|
diff --git a/src/main/java/io/papermc/paper/loot/LootContextKeyImpl.java b/src/main/java/io/papermc/paper/loot/LootContextKeyImpl.java
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000000000000000000000000000000..941a490e1b8f865a6ec9fdfb57215f1674ce40ff
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/src/main/java/io/papermc/paper/loot/LootContextKeyImpl.java
|
||||||
|
@@ -0,0 +1,24 @@
|
||||||
|
+package io.papermc.paper.loot;
|
||||||
|
+
|
||||||
|
+import java.util.HashSet;
|
||||||
|
+import java.util.Set;
|
||||||
|
+import net.kyori.adventure.key.Key;
|
||||||
|
+import net.kyori.adventure.key.KeyPattern;
|
||||||
|
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
|
+import org.jetbrains.annotations.ApiStatus;
|
||||||
|
+
|
||||||
|
+@ApiStatus.Internal
|
||||||
|
+@DefaultQualifier(NonNull.class)
|
||||||
|
+record LootContextKeyImpl<T>(Key key) implements LootContextKey<T> {
|
||||||
|
+
|
||||||
|
+ static final Set<LootContextKey<?>> KEYS = new HashSet<>();
|
||||||
|
+
|
||||||
|
+ static <T> LootContextKey<T> create(@KeyPattern final String name) {
|
||||||
|
+ final LootContextKeyImpl<T> key = new LootContextKeyImpl<>(Key.key(name));
|
||||||
|
+ if (!KEYS.add(key)) {
|
||||||
|
+ throw new IllegalStateException("Already registered " + name);
|
||||||
|
+ }
|
||||||
|
+ return key;
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
diff --git a/src/main/java/org/bukkit/loot/LootContext.java b/src/main/java/org/bukkit/loot/LootContext.java
|
||||||
|
index 9c1ccaed727ec5e5dad93146bbfda798e3f536e7..43e7839157c37745a3623512b35b89fd6839ab3c 100644
|
||||||
|
--- a/src/main/java/org/bukkit/loot/LootContext.java
|
||||||
|
+++ b/src/main/java/org/bukkit/loot/LootContext.java
|
||||||
|
@@ -15,30 +15,124 @@ public final class LootContext {
|
||||||
|
|
||||||
|
public static final int DEFAULT_LOOT_MODIFIER = -1;
|
||||||
|
|
||||||
|
- private final Location location;
|
||||||
|
+ // Paper start - loot context overhaul
|
||||||
|
+ private final org.bukkit.World world;
|
||||||
|
private final float luck;
|
||||||
|
- private final int lootingModifier;
|
||||||
|
- private final Entity lootedEntity;
|
||||||
|
- private final HumanEntity killer;
|
||||||
|
-
|
||||||
|
- private LootContext(@NotNull Location location, float luck, int lootingModifier, @Nullable Entity lootedEntity, @Nullable HumanEntity killer) {
|
||||||
|
- Preconditions.checkArgument(location != null, "LootContext location cannot be null");
|
||||||
|
- Preconditions.checkArgument(location.getWorld() != null, "LootContext World cannot be null");
|
||||||
|
- this.location = location;
|
||||||
|
+ private final java.util.Random random;
|
||||||
|
+ private final java.util.Map<io.papermc.paper.loot.LootContextKey<?>, Object> contextMap;
|
||||||
|
+ // TODO dynamic drops API
|
||||||
|
+ @Deprecated
|
||||||
|
+ private @org.checkerframework.checker.nullness.qual.MonotonicNonNull Location legacyLocation;
|
||||||
|
+ @Deprecated // has no functionality
|
||||||
|
+ private int lootingModifier;
|
||||||
|
+ private final boolean isLegacy;
|
||||||
|
+
|
||||||
|
+ private LootContext(@NotNull org.bukkit.World world, float luck, @NotNull java.util.Random random, @NotNull java.util.Map<io.papermc.paper.loot.LootContextKey<?>, Object> contextMap, boolean isLegacy, @Deprecated int lootingModifier) {
|
||||||
|
+ this.world = world;
|
||||||
|
this.luck = luck;
|
||||||
|
+ this.random = random;
|
||||||
|
+ this.contextMap = java.util.Map.copyOf(contextMap);
|
||||||
|
+ this.isLegacy = isLegacy;
|
||||||
|
this.lootingModifier = lootingModifier;
|
||||||
|
- this.lootedEntity = lootedEntity;
|
||||||
|
- this.killer = killer;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ @org.jetbrains.annotations.ApiStatus.Internal
|
||||||
|
+ public boolean isLegacy() {
|
||||||
|
+ return this.isLegacy;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /**
|
||||||
|
+ * Checks if this context contains a value for the key
|
||||||
|
+ *
|
||||||
|
+ * @param contextKey the key to check
|
||||||
|
+ * @return true if this context has a value for that key
|
||||||
|
+ */
|
||||||
|
+ public boolean hasKey(final io.papermc.paper.loot.@NotNull LootContextKey<?> contextKey) {
|
||||||
|
+ return this.contextMap.containsKey(contextKey);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /**
|
||||||
|
+ * Gets the value for a context key
|
||||||
|
+ *
|
||||||
|
+ * @param contextKey the key for the value
|
||||||
|
+ * @return the value or null if this context doesn't have a value for the key
|
||||||
|
+ * @param <T> value type
|
||||||
|
+ * @see #hasKey(io.papermc.paper.loot.LootContextKey)
|
||||||
|
+ * @see #getOrThrow(io.papermc.paper.loot.LootContextKey)
|
||||||
|
+ */
|
||||||
|
+ @SuppressWarnings("unchecked")
|
||||||
|
+ public <T> @Nullable T get(final io.papermc.paper.loot.@NotNull LootContextKey<T> contextKey) {
|
||||||
|
+ return (T) this.contextMap.get(contextKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /**
|
||||||
|
+ * Gets the value of a context key, throwing an exception
|
||||||
|
+ * if one isn't found
|
||||||
|
+ *
|
||||||
|
+ * @param contextKey the key for the value
|
||||||
|
+ * @return the value
|
||||||
|
+ * @param <T> value type
|
||||||
|
+ * @throws java.util.NoSuchElementException if no value is found for that key
|
||||||
|
+ */
|
||||||
|
+ public <T> @NotNull T getOrThrow(final io.papermc.paper.loot.@NotNull LootContextKey<T> contextKey) {
|
||||||
|
+ final T value = this.get(contextKey);
|
||||||
|
+ if (value == null) {
|
||||||
|
+ throw new java.util.NoSuchElementException("No value found for " + contextKey);
|
||||||
|
+ }
|
||||||
|
+ return value;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /**
|
||||||
|
+ * Gets the random instance used for this context.
|
||||||
|
+ *
|
||||||
|
+ * @return the random
|
||||||
|
+ */
|
||||||
|
+ public java.util.@NotNull Random getRandom() {
|
||||||
|
+ return this.random;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /**
|
||||||
|
+ * Gets the world for this context.
|
||||||
|
+ *
|
||||||
|
+ * @return the world
|
||||||
|
+ */
|
||||||
|
+ public org.bukkit.@NotNull World getWorld() {
|
||||||
|
+ return this.world;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /**
|
||||||
|
+ * Gets the context map for this loot context.
|
||||||
|
+ *
|
||||||
|
+ * @return an unmodifiable map
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+ public java.util.@NotNull @org.jetbrains.annotations.Unmodifiable Map<io.papermc.paper.loot.LootContextKey<?>, Object> getContextMap() {
|
||||||
|
+ return this.contextMap;
|
||||||
|
+ }
|
||||||
|
+ // Paper end
|
||||||
|
+
|
||||||
|
/**
|
||||||
|
* The {@link Location} to store where the loot will be generated.
|
||||||
|
*
|
||||||
|
* @return the Location of where the loot will be generated
|
||||||
|
+ * @deprecated use {@link #get(io.papermc.paper.loot.LootContextKey)} methods
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
+ @Deprecated // Paper
|
||||||
|
public Location getLocation() {
|
||||||
|
- return location;
|
||||||
|
+ // Paper start - fallback to legacy location
|
||||||
|
+ if (this.legacyLocation == null) {
|
||||||
|
+ if (contextMap.containsKey(io.papermc.paper.loot.LootContextKey.ORIGIN)) {
|
||||||
|
+ io.papermc.paper.math.Position pos = this.getOrThrow(io.papermc.paper.loot.LootContextKey.ORIGIN);
|
||||||
|
+ this.legacyLocation = new Location(this.world, pos.x(), pos.y(), pos.z());
|
||||||
|
+ } else if (contextMap.containsKey(io.papermc.paper.loot.LootContextKey.THIS_ENTITY)) {
|
||||||
|
+ this.legacyLocation = this.getOrThrow(io.papermc.paper.loot.LootContextKey.THIS_ENTITY).getLocation();
|
||||||
|
+ } else {
|
||||||
|
+ throw new IllegalStateException("All known context key sets require \"origin\" or \"this_entity\" and this one doesn't have either");
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ return this.legacyLocation;
|
||||||
|
+ // Paper end
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@@ -73,10 +167,12 @@ public final class LootContext {
|
||||||
|
* Get the {@link Entity} that was killed. Can be null.
|
||||||
|
*
|
||||||
|
* @return the looted entity or null
|
||||||
|
+ * @deprecated use {@link #get(io.papermc.paper.loot.LootContextKey)} methods
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
+ @Deprecated // Paper
|
||||||
|
public Entity getLootedEntity() {
|
||||||
|
- return lootedEntity;
|
||||||
|
+ return this.get(io.papermc.paper.loot.LootContextKey.THIS_ENTITY); // Paper
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@@ -84,41 +180,89 @@ public final class LootContext {
|
||||||
|
* Can be null.
|
||||||
|
*
|
||||||
|
* @return the killer entity, or null.
|
||||||
|
+ * @deprecated use {@link #get(io.papermc.paper.loot.LootContextKey)} methods
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
+ @Deprecated // Paper
|
||||||
|
public HumanEntity getKiller() {
|
||||||
|
- return killer;
|
||||||
|
+ return this.get(io.papermc.paper.loot.LootContextKey.ATTACKING_ENTITY) instanceof HumanEntity humanEntity ? humanEntity : null; // Paper
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class to make building {@link LootContext} easier. The only
|
||||||
|
- * required argument is {@link Location} with a valid (non-null)
|
||||||
|
- * {@link org.bukkit.World}.
|
||||||
|
+ * required argument is {@link org.bukkit.World}.
|
||||||
|
*/
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
- private final Location location;
|
||||||
|
+ private final org.bukkit.World world; // Paper
|
||||||
|
private float luck;
|
||||||
|
+ @Deprecated // Paper - not functional
|
||||||
|
private int lootingModifier = LootContext.DEFAULT_LOOT_MODIFIER;
|
||||||
|
- private Entity lootedEntity;
|
||||||
|
- private HumanEntity killer;
|
||||||
|
+ private java.util.Random random = java.util.concurrent.ThreadLocalRandom.current(); // Paper
|
||||||
|
+ private final java.util.Map<io.papermc.paper.loot.LootContextKey<?>, Object> contextMap = new java.util.IdentityHashMap<>(); // Paper
|
||||||
|
+ private boolean isLegacy = false; // Paper
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new LootContext.Builder instance to facilitate easy
|
||||||
|
* creation of {@link LootContext}s.
|
||||||
|
*
|
||||||
|
* @param location the location the LootContext should use
|
||||||
|
+ * @deprecated not all loot contexts have locations
|
||||||
|
*/
|
||||||
|
+ @Deprecated // Paper
|
||||||
|
public Builder(@NotNull Location location) {
|
||||||
|
- this.location = location;
|
||||||
|
+ // Paper start
|
||||||
|
+ com.google.common.base.Preconditions.checkArgument(location.getWorld() != null, "location missing world");
|
||||||
|
+ this.world = location.getWorld();
|
||||||
|
+ this.contextMap.put(io.papermc.paper.loot.LootContextKey.ORIGIN, io.papermc.paper.math.Position.fine(location));
|
||||||
|
+ this.isLegacy = true;
|
||||||
|
+ // Paper end
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ // Paper start
|
||||||
|
+ public Builder(@NotNull org.bukkit.World world) {
|
||||||
|
+ this.world = world;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /**
|
||||||
|
+ * Sets the random instance to use for this context.
|
||||||
|
+ * Defaults to {@link java.util.concurrent.ThreadLocalRandom#current()}.
|
||||||
|
+ *
|
||||||
|
+ * @param random the random to use
|
||||||
|
+ * @return the builder
|
||||||
|
+ */
|
||||||
|
+ @org.jetbrains.annotations.Contract(value = "_ -> this", mutates = "this")
|
||||||
|
+ public @NotNull Builder withRandom(@NotNull java.util.Random random) {
|
||||||
|
+ this.random = random;
|
||||||
|
+ return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /**
|
||||||
|
+ * Sets or clears context values.
|
||||||
|
+ *
|
||||||
|
+ * @param contextKey the key to set or clear for
|
||||||
|
+ * @param context the value to set, or null to clear
|
||||||
|
+ * @param <T> the value type
|
||||||
|
+ * @return the builder
|
||||||
|
+ */
|
||||||
|
+ @org.jetbrains.annotations.Contract(value = "_, _ -> this", mutates = "this")
|
||||||
|
+ public <T> @NotNull Builder with(@NotNull io.papermc.paper.loot.LootContextKey<T> contextKey, @Nullable T context) {
|
||||||
|
+ if (context == null) {
|
||||||
|
+ this.contextMap.remove(contextKey);
|
||||||
|
+ } else {
|
||||||
|
+ this.contextMap.put(contextKey, context);
|
||||||
|
+ }
|
||||||
|
+ return this;
|
||||||
|
+ }
|
||||||
|
+ // Paper end
|
||||||
|
+
|
||||||
|
/**
|
||||||
|
* Set how much luck to have when generating loot.
|
||||||
|
*
|
||||||
|
* @param luck the luck level
|
||||||
|
* @return the Builder
|
||||||
|
*/
|
||||||
|
+ @org.jetbrains.annotations.Contract(value = "_ -> this", mutates = "this") // Paper
|
||||||
|
@NotNull
|
||||||
|
public Builder luck(float luck) {
|
||||||
|
this.luck = luck;
|
||||||
|
@@ -138,6 +282,7 @@ public final class LootContext {
|
||||||
|
@NotNull
|
||||||
|
@Deprecated
|
||||||
|
public Builder lootingModifier(int modifier) {
|
||||||
|
+ this.isLegacy = true; // Paper
|
||||||
|
this.lootingModifier = modifier;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
@@ -147,11 +292,14 @@ public final class LootContext {
|
||||||
|
*
|
||||||
|
* @param lootedEntity the looted entity
|
||||||
|
* @return the Builder
|
||||||
|
+ * @deprecated use {@link #with(io.papermc.paper.loot.LootContextKey, Object)}
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
+ @org.jetbrains.annotations.Contract(value = "_ -> this", mutates = "this") // Paper
|
||||||
|
+ @Deprecated // Paper
|
||||||
|
public Builder lootedEntity(@Nullable Entity lootedEntity) {
|
||||||
|
- this.lootedEntity = lootedEntity;
|
||||||
|
- return this;
|
||||||
|
+ this.isLegacy = true; // Paper
|
||||||
|
+ return this.with(io.papermc.paper.loot.LootContextKey.THIS_ENTITY, lootedEntity); // Paper
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@@ -161,11 +309,14 @@ public final class LootContext {
|
||||||
|
*
|
||||||
|
* @param killer the killer entity
|
||||||
|
* @return the Builder
|
||||||
|
+ * @deprecated use {@link #with(io.papermc.paper.loot.LootContextKey, Object)}
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
+ @org.jetbrains.annotations.Contract(value = "_ -> this", mutates = "this") // Paper
|
||||||
|
+ @Deprecated // Paper
|
||||||
|
public Builder killer(@Nullable HumanEntity killer) {
|
||||||
|
- this.killer = killer;
|
||||||
|
- return this;
|
||||||
|
+ this.isLegacy = true; // Paper
|
||||||
|
+ return this.with(io.papermc.paper.loot.LootContextKey.ATTACKING_ENTITY, killer); // Paper
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
@@ -175,8 +326,9 @@ public final class LootContext {
|
||||||
|
* @return a new {@link LootContext} instance
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
+ @org.jetbrains.annotations.Contract("-> new") // Paper
|
||||||
|
public LootContext build() {
|
||||||
|
- return new LootContext(location, luck, lootingModifier, lootedEntity, killer);
|
||||||
|
+ return new LootContext(this.world, luck, this.random, this.contextMap, this.isLegacy, this.lootingModifier); // Paper
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
248
patches/server/1060-Improve-LootContext-API.patch
Normale Datei
248
patches/server/1060-Improve-LootContext-API.patch
Normale Datei
@ -0,0 +1,248 @@
|
|||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||||
|
Date: Tue, 22 Mar 2022 22:17:13 -0700
|
||||||
|
Subject: [PATCH] Improve LootContext API
|
||||||
|
|
||||||
|
== AT ==
|
||||||
|
public net.minecraft.world.level.storage.loot.LootContext params
|
||||||
|
|
||||||
|
diff --git a/src/main/java/io/papermc/paper/loot/PaperLootContextKey.java b/src/main/java/io/papermc/paper/loot/PaperLootContextKey.java
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000000000000000000000000000000..9f0729c56608696d10e5b3cba5b01b85a1fb0b6a
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/src/main/java/io/papermc/paper/loot/PaperLootContextKey.java
|
||||||
|
@@ -0,0 +1,115 @@
|
||||||
|
+package io.papermc.paper.loot;
|
||||||
|
+
|
||||||
|
+import com.google.common.collect.BiMap;
|
||||||
|
+import com.google.common.collect.HashBiMap;
|
||||||
|
+import io.papermc.paper.util.MCUtil;
|
||||||
|
+import java.util.HashSet;
|
||||||
|
+import java.util.IdentityHashMap;
|
||||||
|
+import java.util.Map;
|
||||||
|
+import java.util.Set;
|
||||||
|
+import java.util.function.Function;
|
||||||
|
+import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
+import net.minecraft.world.level.storage.loot.LootParams;
|
||||||
|
+import net.minecraft.world.level.storage.loot.parameters.LootContextParam;
|
||||||
|
+import net.minecraft.world.level.storage.loot.parameters.LootContextParamSet;
|
||||||
|
+import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
|
||||||
|
+import org.bukkit.craftbukkit.block.CraftBlockEntityState;
|
||||||
|
+import org.bukkit.craftbukkit.block.CraftBlockStates;
|
||||||
|
+import org.bukkit.craftbukkit.block.data.CraftBlockData;
|
||||||
|
+import org.bukkit.craftbukkit.damage.CraftDamageSource;
|
||||||
|
+import org.bukkit.craftbukkit.entity.CraftEntity;
|
||||||
|
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||||
|
+import org.bukkit.entity.Entity;
|
||||||
|
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
|
+
|
||||||
|
+@DefaultQualifier(NonNull.class)
|
||||||
|
+public final class PaperLootContextKey {
|
||||||
|
+
|
||||||
|
+ public static final BiMap<LootContextParam<?>, LootContextKey<?>> KEY_BI_MAP = HashBiMap.create();
|
||||||
|
+ private static final Set<Converter<?, ?>> CONVERTERS = new HashSet<>();
|
||||||
|
+ private static final Map<LootContextParam<?>, Converter<?, ?>> NMS_KEY_MAP = new IdentityHashMap<>();
|
||||||
|
+ private static final Map<LootContextKey<?>, Converter<?, ?>> API_KEY_MAP = new IdentityHashMap<>();
|
||||||
|
+
|
||||||
|
+ static {
|
||||||
|
+ CONVERTERS.add(entity(LootContextParams.THIS_ENTITY, LootContextKey.THIS_ENTITY));
|
||||||
|
+ CONVERTERS.add(entity(LootContextParams.LAST_DAMAGE_PLAYER, LootContextKey.LAST_DAMAGE_PLAYER));
|
||||||
|
+ CONVERTERS.add(new LambdaConverter<>(LootContextParams.DAMAGE_SOURCE, LootContextKey.DAMAGE_SOURCE, ds -> ((CraftDamageSource) ds).getHandle(), CraftDamageSource::new));
|
||||||
|
+ CONVERTERS.add(entity(LootContextParams.ATTACKING_ENTITY, LootContextKey.ATTACKING_ENTITY));
|
||||||
|
+ CONVERTERS.add(entity(LootContextParams.DIRECT_ATTACKING_ENTITY, LootContextKey.DIRECT_ATTACKING_ENTITY));
|
||||||
|
+ CONVERTERS.add(new LambdaConverter<>(LootContextParams.ORIGIN, LootContextKey.ORIGIN, MCUtil::toVec3, MCUtil::toPosition));
|
||||||
|
+ CONVERTERS.add(new LambdaConverter<>(LootContextParams.BLOCK_STATE, LootContextKey.BLOCK_DATA, bd -> ((CraftBlockData) bd).getState(), BlockState::createCraftBlockData));
|
||||||
|
+ CONVERTERS.add(new LambdaConverter<>(LootContextParams.BLOCK_ENTITY, LootContextKey.TILE_STATE, ts -> ((CraftBlockEntityState<?>) ts).getTileEntity(), CraftBlockStates::getTileState));
|
||||||
|
+ CONVERTERS.add(new LambdaConverter<>(LootContextParams.TOOL, LootContextKey.TOOL, CraftItemStack::asNMSCopy, net.minecraft.world.item.ItemStack::asBukkitCopy));
|
||||||
|
+ CONVERTERS.add(identity(LootContextParams.EXPLOSION_RADIUS, LootContextKey.EXPLOSION_RADIUS));
|
||||||
|
+ CONVERTERS.add(identity(LootContextParams.ENCHANTMENT_LEVEL, LootContextKey.ENCHANTMENT_LEVEL));
|
||||||
|
+ CONVERTERS.add(identity(LootContextParams.ENCHANTMENT_ACTIVE, LootContextKey.ENCHANTMENT_ACTIVE));
|
||||||
|
+ for (final Converter<?, ?> converter : CONVERTERS) {
|
||||||
|
+ KEY_BI_MAP.put(converter.nmsKey, converter.apiKey);
|
||||||
|
+ NMS_KEY_MAP.put(converter.nmsKey, converter);
|
||||||
|
+ API_KEY_MAP.put(converter.apiKey, converter);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ private PaperLootContextKey() {
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ @SuppressWarnings("unchecked")
|
||||||
|
+ public static <API, MINECRAFT> void applyToNmsBuilder(final LootContextParamSet paramSet, final LootParams.Builder builder, final LootContextKey<API> apiKey, final Object object) {
|
||||||
|
+ final LootContextParam<MINECRAFT> nmsParam = (LootContextParam<MINECRAFT>) KEY_BI_MAP.inverse().get(apiKey);
|
||||||
|
+ if (paramSet.getAllowed().contains(nmsParam) || paramSet.getRequired().contains(nmsParam)) {
|
||||||
|
+ builder.withOptionalParameter(nmsParam, ((Converter<MINECRAFT, API>) API_KEY_MAP.get(apiKey)).toMinecraft((API) object));
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ @SuppressWarnings("unchecked")
|
||||||
|
+ public static <API, MINECRAFT> void applyToApiBuilder(final org.bukkit.loot.LootContext.Builder builder, final LootContextParam<MINECRAFT> nmsKey, final Object object) {
|
||||||
|
+ builder.with(((LootContextKey<API>) KEY_BI_MAP.get(nmsKey)), ((Converter<MINECRAFT, API>) NMS_KEY_MAP.get(nmsKey)).toApi((MINECRAFT) object));
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ abstract static class Converter<MINECRAFT, API> {
|
||||||
|
+
|
||||||
|
+ final LootContextParam<MINECRAFT> nmsKey;
|
||||||
|
+ final LootContextKey<API> apiKey;
|
||||||
|
+
|
||||||
|
+ private Converter(final LootContextParam<MINECRAFT> nmsKey, final LootContextKey<API> apiKey) {
|
||||||
|
+ this.nmsKey = nmsKey;
|
||||||
|
+ this.apiKey = apiKey;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ protected abstract MINECRAFT toMinecraft(API api);
|
||||||
|
+
|
||||||
|
+ protected abstract API toApi(MINECRAFT minecraft);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ static class LambdaConverter<MINECRAFT, API> extends Converter<MINECRAFT, API> {
|
||||||
|
+
|
||||||
|
+ private final Function<API, MINECRAFT> toMinecraft;
|
||||||
|
+ private final Function<MINECRAFT, API> toApi;
|
||||||
|
+
|
||||||
|
+ private LambdaConverter(final LootContextParam<MINECRAFT> nmsKey, final LootContextKey<API> apiKey, final Function<API, MINECRAFT> toMinecraft, final Function<MINECRAFT, API> toApi) {
|
||||||
|
+ super(nmsKey, apiKey);
|
||||||
|
+ this.toMinecraft = toMinecraft;
|
||||||
|
+ this.toApi = toApi;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ @Override
|
||||||
|
+ protected MINECRAFT toMinecraft(final API api) {
|
||||||
|
+ return this.toMinecraft.apply(api);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ @Override
|
||||||
|
+ protected API toApi(final MINECRAFT minecraft) {
|
||||||
|
+ return this.toApi.apply(minecraft);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ private static <T> LambdaConverter<T, T> identity(final LootContextParam<T> nmsKey, final LootContextKey<T> apiKey) {
|
||||||
|
+ return new LambdaConverter<>(nmsKey, apiKey, Function.identity(), Function.identity());
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ @SuppressWarnings("unchecked")
|
||||||
|
+ private static <MINECRAFT extends net.minecraft.world.entity.Entity, API extends Entity> LambdaConverter<MINECRAFT, API> entity(final LootContextParam<MINECRAFT> nmsKey, final LootContextKey<API> apiKey) {
|
||||||
|
+ return new LambdaConverter<>(nmsKey, apiKey, e -> (MINECRAFT) ((CraftEntity) e).getHandle(), e -> (API) e.getBukkitEntity());
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftLootTable.java b/src/main/java/org/bukkit/craftbukkit/CraftLootTable.java
|
||||||
|
index f028daa4f23a1f1868c9922991259739cadc5da2..3d37937186eb8c2bd950816ccecd52912d3956e4 100644
|
||||||
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftLootTable.java
|
||||||
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftLootTable.java
|
||||||
|
@@ -101,6 +101,22 @@ public class CraftLootTable implements org.bukkit.loot.LootTable {
|
||||||
|
|
||||||
|
private LootParams convertContext(LootContext context, Random random) {
|
||||||
|
Preconditions.checkArgument(context != null, "LootContext cannot be null");
|
||||||
|
+ // Paper start
|
||||||
|
+ if (!context.isLegacy()) {
|
||||||
|
+ final LootParams.Builder paramsBuilder = new LootParams.Builder(((CraftWorld) context.getWorld()).getHandle()).withLuck(context.getLuck());
|
||||||
|
+ context.getContextMap().forEach((lootContextKey, o) -> io.papermc.paper.loot.PaperLootContextKey.applyToNmsBuilder(this.handle.getParamSet(), paramsBuilder, lootContextKey, o));
|
||||||
|
+
|
||||||
|
+ return paramsBuilder.create(this.handle.getParamSet());
|
||||||
|
+ // final net.minecraft.world.level.storage.loot.LootContext.Builder contextBuilder = new net.minecraft.world.level.storage.loot.LootContext.Builder(paramsBuilder.create(this.handle.getParamSet()));
|
||||||
|
+ // // .withRandom(new RandomSourceWrapper(random != null ? random : context.getRandom()))
|
||||||
|
+ // return contextBuilder.create(java.util.Optional.empty());
|
||||||
|
+ } else {
|
||||||
|
+ return this.convertLegacyContext(context, random);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ @Deprecated
|
||||||
|
+ private LootParams convertLegacyContext(final LootContext context, final Random random) {
|
||||||
|
+ // Paper end
|
||||||
|
Location loc = context.getLocation();
|
||||||
|
Preconditions.checkArgument(loc.getWorld() != null, "LootContext.getLocation#getWorld cannot be null");
|
||||||
|
ServerLevel handle = ((CraftWorld) loc.getWorld()).getHandle();
|
||||||
|
@@ -151,6 +167,20 @@ public class CraftLootTable implements org.bukkit.loot.LootTable {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LootContext convertContext(net.minecraft.world.level.storage.loot.LootContext info) {
|
||||||
|
+ // Paper start
|
||||||
|
+ final LootContext.Builder builder = new LootContext.Builder(info.getLevel().getWorld())
|
||||||
|
+ .withRandom(new org.bukkit.craftbukkit.util.RandomSourceWrapper.RandomWrapper(info.getRandom()))
|
||||||
|
+ .luck(info.getLuck());
|
||||||
|
+ for (final LootContextParam<?> nmsParam : io.papermc.paper.loot.PaperLootContextKey.KEY_BI_MAP.keySet()) {
|
||||||
|
+ if (info.hasParam(nmsParam)) {
|
||||||
|
+ io.papermc.paper.loot.PaperLootContextKey.applyToApiBuilder(builder, nmsParam, info.getParam(nmsParam));
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ return builder.build();
|
||||||
|
+ }
|
||||||
|
+ @Deprecated @io.papermc.paper.annotation.DoNotUse
|
||||||
|
+ public static LootContext convertLegacyContext(net.minecraft.world.level.storage.loot.LootContext info) {
|
||||||
|
+ // Paper end
|
||||||
|
Vec3 position = info.getParamOrNull(LootContextParams.ORIGIN);
|
||||||
|
if (position == null) {
|
||||||
|
position = info.getParamOrNull(LootContextParams.THIS_ENTITY).position(); // Every vanilla context has origin or this_entity, see LootContextParameterSets
|
||||||
|
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
|
||||||
|
index b7ff7af2513204b151340538d50a65c850bdb75f..63f1f55bea16aece9d50e0eaed4deca050f05279 100644
|
||||||
|
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
|
||||||
|
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockStates.java
|
||||||
|
@@ -292,6 +292,13 @@ public final class CraftBlockStates {
|
||||||
|
return CraftBlockStates.getBlockState(null, blockPosition, blockData, tileEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ // Paper start
|
||||||
|
+ public static org.bukkit.block.TileState getTileState(final BlockEntity blockEntity) {
|
||||||
|
+ Preconditions.checkArgument(blockEntity.getLevel() != null, "blockEntity has no level");
|
||||||
|
+ return (org.bukkit.block.TileState) getBlockState(blockEntity.getLevel().getWorld(), blockEntity.getBlockPos(), blockEntity.getBlockState(), blockEntity);
|
||||||
|
+ }
|
||||||
|
+ // Paper end
|
||||||
|
+
|
||||||
|
// See BlockStateFactory#createBlockState(World, BlockPosition, IBlockData, TileEntity)
|
||||||
|
public static CraftBlockState getBlockState(World world, BlockPos blockPosition, net.minecraft.world.level.block.state.BlockState blockData, BlockEntity tileEntity) {
|
||||||
|
Material material = CraftBlockType.minecraftToBukkit(blockData.getBlock());
|
||||||
|
diff --git a/src/test/java/io/papermc/paper/loot/LootContextKeyTest.java b/src/test/java/io/papermc/paper/loot/LootContextKeyTest.java
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000000000000000000000000000000..4aa515b62245d0c20dcc5352aff1984036922932
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/src/test/java/io/papermc/paper/loot/LootContextKeyTest.java
|
||||||
|
@@ -0,0 +1,47 @@
|
||||||
|
+package io.papermc.paper.loot;
|
||||||
|
+
|
||||||
|
+import java.lang.reflect.Field;
|
||||||
|
+import java.lang.reflect.Modifier;
|
||||||
|
+import java.util.HashMap;
|
||||||
|
+import java.util.List;
|
||||||
|
+import java.util.Map;
|
||||||
|
+import net.minecraft.world.level.storage.loot.parameters.LootContextParam;
|
||||||
|
+import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
|
||||||
|
+import org.bukkit.support.AbstractTestingBase;
|
||||||
|
+import org.junit.jupiter.api.BeforeAll;
|
||||||
|
+import org.junit.jupiter.api.Test;
|
||||||
|
+
|
||||||
|
+import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||||
|
+
|
||||||
|
+class LootContextKeyTest {
|
||||||
|
+
|
||||||
|
+ static Map<String, LootContextParam<?>> vanillaParams = new HashMap<>();
|
||||||
|
+
|
||||||
|
+ @BeforeAll
|
||||||
|
+ static void collectVanillaContextParams() throws ReflectiveOperationException {
|
||||||
|
+ Class.forName(LootContextKey.class.getName()); // force-load class
|
||||||
|
+ for (final Field field : LootContextParams.class.getDeclaredFields()) {
|
||||||
|
+ if (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()) && field.getType().equals(LootContextParam.class)) {
|
||||||
|
+ vanillaParams.put(field.getName(), (LootContextParam<?>) field.get(null));
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ @Test
|
||||||
|
+ void testMinecraftToApi() {
|
||||||
|
+ vanillaParams.forEach((fieldName, lootContextParam) -> {
|
||||||
|
+ final List<LootContextKey<?>> matching = LootContextKeyImpl.KEYS.stream().filter(k -> k.key().asString().equals(lootContextParam.getName().toString())).toList();
|
||||||
|
+ assertEquals(1, matching.size(), "Did not find 1 matching context key for " + lootContextParam.getName());
|
||||||
|
+ });
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ @Test
|
||||||
|
+ void testApiToMinecraft() {
|
||||||
|
+ assertNotEquals(0, LootContextKeyImpl.KEYS.size());
|
||||||
|
+ LootContextKeyImpl.KEYS.forEach(lootContextKey -> {
|
||||||
|
+ final List<LootContextParam<?>> matching = vanillaParams.values().stream().filter(p -> p.getName().toString().equals(lootContextKey.key().asString())).toList();
|
||||||
|
+ assertEquals(1, matching.size(), "Did not find 1 matching loot param for " + lootContextKey.key());
|
||||||
|
+ });
|
||||||
|
+ }
|
||||||
|
+}
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren