aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0673-Custom-Potion-Mixes.patch
blob: 8cd0b19f493d58a2487a663010de79b3649c28e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Thu, 7 Oct 2021 14:34:55 -0700
Subject: [PATCH] Custom Potion Mixes

== AT ==
public-f net.minecraft.server.MinecraftServer potionBrewing

diff --git a/src/main/java/io/papermc/paper/potion/PaperPotionBrewer.java b/src/main/java/io/papermc/paper/potion/PaperPotionBrewer.java
new file mode 100644
index 0000000000000000000000000000000000000000..d9390227a2bba4e03aa9ee592ca157127633c41b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/potion/PaperPotionBrewer.java
@@ -0,0 +1,56 @@
+package io.papermc.paper.potion;
+
+import com.google.common.base.Preconditions;
+import java.util.Collection;
+import net.minecraft.server.MinecraftServer;
+import org.bukkit.NamespacedKey;
+import org.bukkit.potion.PotionBrewer;
+import org.bukkit.potion.PotionEffect;
+import org.bukkit.potion.PotionType;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@DefaultQualifier(NonNull.class)
+public class PaperPotionBrewer implements PotionBrewer {
+
+    private final MinecraftServer minecraftServer;
+
+    public PaperPotionBrewer(final MinecraftServer minecraftServer) {
+        this.minecraftServer = minecraftServer;
+    }
+
+    @Override
+    @Deprecated(forRemoval = true)
+    public Collection<PotionEffect> getEffects(PotionType type, boolean upgraded, boolean extended) {
+        final org.bukkit.NamespacedKey key = type.getKey();
+
+        Preconditions.checkArgument(!key.getKey().startsWith("strong_"), "Strong potion type cannot be used directly, got %s", key);
+        Preconditions.checkArgument(!key.getKey().startsWith("long_"), "Extended potion type cannot be used directly, got %s", key);
+
+        org.bukkit.NamespacedKey effectiveKey = key;
+        if (upgraded) {
+            effectiveKey = new org.bukkit.NamespacedKey(key.namespace(), "strong_" + key.key());
+        } else if (extended) {
+            effectiveKey = new org.bukkit.NamespacedKey(key.namespace(), "long_" + key.key());
+        }
+
+        final org.bukkit.potion.PotionType effectivePotionType = org.bukkit.Registry.POTION.get(effectiveKey);
+        Preconditions.checkNotNull(type, "Unknown potion type from data " + effectiveKey.asMinimalString()); // Legacy error message in 1.20.4
+        return effectivePotionType.getPotionEffects();
+    }
+
+    @Override
+    public void addPotionMix(final PotionMix potionMix) {
+        this.minecraftServer.potionBrewing().addPotionMix(potionMix);
+    }
+
+    @Override
+    public void removePotionMix(final NamespacedKey key) {
+        this.minecraftServer.potionBrewing.removePotionMix(key);
+    }
+
+    @Override
+    public void resetPotionMixes() {
+        this.minecraftServer.potionBrewing = this.minecraftServer.potionBrewing().reload(this.minecraftServer.getWorldData().enabledFeatures());
+    }
+}
diff --git a/src/main/java/io/papermc/paper/potion/PaperPotionMix.java b/src/main/java/io/papermc/paper/potion/PaperPotionMix.java
new file mode 100644
index 0000000000000000000000000000000000000000..7ea357ac2f3a93db4ebdf24b5072be7d1cad3e33
--- /dev/null
+++ b/src/main/java/io/papermc/paper/potion/PaperPotionMix.java
@@ -0,0 +1,21 @@
+package io.papermc.paper.potion;
+
+import java.util.function.Predicate;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
+import org.bukkit.craftbukkit.inventory.CraftRecipe;
+import org.bukkit.inventory.RecipeChoice;
+
+public record PaperPotionMix(ItemStack result, Predicate<ItemStack> input, Predicate<ItemStack> ingredient) {
+
+    public PaperPotionMix(PotionMix potionMix) {
+        this(CraftItemStack.asNMSCopy(potionMix.getResult()), convert(potionMix.getInput()), convert(potionMix.getIngredient()));
+    }
+
+    static Predicate<ItemStack> convert(final RecipeChoice choice) {
+        if (choice instanceof PredicateRecipeChoice predicateRecipeChoice) {
+            return stack -> predicateRecipeChoice.test(CraftItemStack.asBukkitCopy(stack));
+        }
+        return CraftRecipe.toIngredient(choice, true);
+    }
+}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 04286e907ff14cc8c45dbfc6ab12f520afcca2ca..8142e4a238066404f3d1685f9cee1a2b91cdd371 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -2160,6 +2160,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
 
             this.worldData.setDataConfiguration(worlddataconfiguration);
             this.resources.managers.updateRegistryTags();
+            this.potionBrewing = this.potionBrewing.reload(this.worldData.enabledFeatures()); // Paper - Custom Potion Mixes
             this.getPlayerList().saveAll();
             this.getPlayerList().reloadResources();
             this.functionManager.replaceLibrary(this.resources.managers.getFunctionLibrary());
diff --git a/src/main/java/net/minecraft/world/inventory/BrewingStandMenu.java b/src/main/java/net/minecraft/world/inventory/BrewingStandMenu.java
index a7c33675c3777cfbeeecbb3a123a0b03195e679c..93c4657f345b6d3442b1f2ab17b2703ded86b525 100644
--- a/src/main/java/net/minecraft/world/inventory/BrewingStandMenu.java
+++ b/src/main/java/net/minecraft/world/inventory/BrewingStandMenu.java
@@ -53,9 +53,11 @@ public class BrewingStandMenu extends AbstractContainerMenu {
         this.brewingStandData = propertyDelegate;
         PotionBrewing potionbrewer = playerInventory.player.level().potionBrewing();
 
-        this.addSlot(new BrewingStandMenu.PotionSlot(inventory, 0, 56, 51));
-        this.addSlot(new BrewingStandMenu.PotionSlot(inventory, 1, 79, 58));
-        this.addSlot(new BrewingStandMenu.PotionSlot(inventory, 2, 102, 51));
+        // Paper start - custom potion mixes
+        this.addSlot(new BrewingStandMenu.PotionSlot(inventory, 0, 56, 51, potionbrewer));
+        this.addSlot(new BrewingStandMenu.PotionSlot(inventory, 1, 79, 58, potionbrewer));
+        this.addSlot(new BrewingStandMenu.PotionSlot(inventory, 2, 102, 51, potionbrewer));
+        // Paper end - custom potion mixes
         this.ingredientSlot = this.addSlot(new BrewingStandMenu.IngredientsSlot(potionbrewer, inventory, 3, 79, 17));
         this.addSlot(new BrewingStandMenu.FuelSlot(inventory, 4, 17, 17));
         this.addDataSlots(propertyDelegate);
@@ -98,7 +100,7 @@ public class BrewingStandMenu extends AbstractContainerMenu {
                     if (!this.moveItemStackTo(itemstack1, 3, 4, false)) {
                         return ItemStack.EMPTY;
                     }
-                } else if (BrewingStandMenu.PotionSlot.mayPlaceItem(itemstack)) {
+                } else if (BrewingStandMenu.PotionSlot.mayPlaceItem(itemstack, this.player.player.level().potionBrewing())) { // Paper - custom potion mixes
                     if (!this.moveItemStackTo(itemstack1, 0, 3, false)) {
                         return ItemStack.EMPTY;
                     }
@@ -147,13 +149,15 @@ public class BrewingStandMenu extends AbstractContainerMenu {
 
     private static class PotionSlot extends Slot {
 
-        public PotionSlot(Container inventory, int index, int x, int y) {
+        private final PotionBrewing potionBrewing; // Paper - custom potion mixes
+        public PotionSlot(Container inventory, int index, int x, int y, PotionBrewing potionBrewing) { // Paper - custom potion mixes
             super(inventory, index, x, y);
+            this.potionBrewing = potionBrewing; // Paper - custom potion mixes
         }
 
         @Override
         public boolean mayPlace(ItemStack stack) {
-            return PotionSlot.mayPlaceItem(stack);
+            return PotionSlot.mayPlaceItem(stack, this.potionBrewing); // Paper - custom potion mixes
         }
 
         @Override
@@ -172,8 +176,8 @@ public class BrewingStandMenu extends AbstractContainerMenu {
             super.onTake(player, stack);
         }
 
-        public static boolean mayPlaceItem(ItemStack stack) {
-            return stack.is(Items.POTION) || stack.is(Items.SPLASH_POTION) || stack.is(Items.LINGERING_POTION) || stack.is(Items.GLASS_BOTTLE);
+        public static boolean mayPlaceItem(ItemStack stack, PotionBrewing potionBrewing) { // Paper - custom potion mixes
+            return stack.is(Items.POTION) || stack.is(Items.SPLASH_POTION) || stack.is(Items.LINGERING_POTION) || stack.is(Items.GLASS_BOTTLE) || potionBrewing.isCustomInput(stack); // Paper - Custom Potion Mixes
         }
     }
 
diff --git a/src/main/java/net/minecraft/world/item/alchemy/PotionBrewing.java b/src/main/java/net/minecraft/world/item/alchemy/PotionBrewing.java
index 50e81e3babd331077eda8daa769eb2b3f99e8ca2..ca01f3344005b295c7ae98f6d5b03f79513b12a4 100644
--- a/src/main/java/net/minecraft/world/item/alchemy/PotionBrewing.java
+++ b/src/main/java/net/minecraft/world/item/alchemy/PotionBrewing.java
@@ -19,6 +19,7 @@ public class PotionBrewing {
     private final List<Ingredient> containers;
     private final List<PotionBrewing.Mix<Potion>> potionMixes;
     private final List<PotionBrewing.Mix<Item>> containerMixes;
+    private final it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap<org.bukkit.NamespacedKey, io.papermc.paper.potion.PaperPotionMix> customMixes = new it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap<>(); // Paper - Custom Potion Mixes
 
     PotionBrewing(List<Ingredient> potionTypes, List<PotionBrewing.Mix<Potion>> potionRecipes, List<PotionBrewing.Mix<Item>> itemRecipes) {
         this.containers = potionTypes;
@@ -27,7 +28,7 @@ public class PotionBrewing {
     }
 
     public boolean isIngredient(ItemStack stack) {
-        return this.isContainerIngredient(stack) || this.isPotionIngredient(stack);
+        return this.isContainerIngredient(stack) || this.isPotionIngredient(stack) || this.isCustomIngredient(stack); // Paper - Custom Potion Mixes
     }
 
     private boolean isContainer(ItemStack stack) {
@@ -71,6 +72,11 @@ public class PotionBrewing {
     }
 
     public boolean hasMix(ItemStack input, ItemStack ingredient) {
+        // Paper start - Custom Potion Mixes
+        if (this.hasCustomMix(input, ingredient)) {
+            return true;
+        }
+        // Paper end - Custom Potion Mixes
         return this.isContainer(input) && (this.hasContainerMix(input, ingredient) || this.hasPotionMix(input, ingredient));
     }
 
@@ -103,6 +109,13 @@ public class PotionBrewing {
         if (input.isEmpty()) {
             return input;
         } else {
+            // Paper start - Custom Potion Mixes
+            for (io.papermc.paper.potion.PaperPotionMix mix : this.customMixes.values()) {
+                if (mix.input().test(input) && mix.ingredient().test(ingredient)) {
+                    return mix.result().copy();
+                }
+            }
+            // Paper end - Custom Potion Mixes
             Optional<Holder<Potion>> optional = input.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY).potion();
             if (optional.isEmpty()) {
                 return input;
@@ -190,6 +203,50 @@ public class PotionBrewing {
         builder.addMix(Potions.SLOW_FALLING, Items.REDSTONE, Potions.LONG_SLOW_FALLING);
     }
 
+    // Paper start - Custom Potion Mixes
+    public boolean isCustomIngredient(ItemStack stack) {
+        for (io.papermc.paper.potion.PaperPotionMix mix : this.customMixes.values()) {
+            if (mix.ingredient().test(stack)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean isCustomInput(ItemStack stack) {
+        for (io.papermc.paper.potion.PaperPotionMix mix : this.customMixes.values()) {
+            if (mix.input().test(stack)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean hasCustomMix(ItemStack input, ItemStack ingredient) {
+        for (io.papermc.paper.potion.PaperPotionMix mix : this.customMixes.values()) {
+            if (mix.input().test(input) && mix.ingredient().test(ingredient)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public void addPotionMix(io.papermc.paper.potion.PotionMix mix) {
+        if (this.customMixes.containsKey(mix.getKey())) {
+            throw new IllegalArgumentException("Duplicate recipe ignored with ID " + mix.getKey());
+        }
+        this.customMixes.putAndMoveToFirst(mix.getKey(), new io.papermc.paper.potion.PaperPotionMix(mix));
+    }
+
+    public boolean removePotionMix(org.bukkit.NamespacedKey key) {
+        return this.customMixes.remove(key) != null;
+    }
+
+    public PotionBrewing reload(FeatureFlagSet flags) {
+        return bootstrap(flags);
+    }
+    // Paper end - Custom Potion Mixes
+
     public static class Builder {
         private final List<Ingredient> containers = new ArrayList<>();
         private final List<PotionBrewing.Mix<Potion>> potionMixes = new ArrayList<>();
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java
index c8f9972ad1c2330908cc840d426f29c20b242ca8..a2fafef89d5354e2cb02f5672810909950a57777 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java
@@ -315,12 +315,12 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements
 
     @Override
     public boolean canPlaceItem(int slot, ItemStack stack) {
+        PotionBrewing potionbrewer = this.level != null ? this.level.potionBrewing() : PotionBrewing.EMPTY; // Paper - move up
         if (slot == 3) {
-            PotionBrewing potionbrewer = this.level != null ? this.level.potionBrewing() : PotionBrewing.EMPTY;
 
             return potionbrewer.isIngredient(stack);
         } else {
-            return slot == 4 ? stack.is(Items.BLAZE_POWDER) : (stack.is(Items.POTION) || stack.is(Items.SPLASH_POTION) || stack.is(Items.LINGERING_POTION) || stack.is(Items.GLASS_BOTTLE)) && this.getItem(slot).isEmpty();
+            return slot == 4 ? stack.is(Items.BLAZE_POWDER) : (stack.is(Items.POTION) || stack.is(Items.SPLASH_POTION) || stack.is(Items.LINGERING_POTION) || stack.is(Items.GLASS_BOTTLE) || potionbrewer.isCustomInput(stack)) && this.getItem(slot).isEmpty(); // Paper - Custom Potion Mixes
         }
     }
 
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 6f6d4a82c350864a60f084eb341d59cf025cde77..ee6bdc985e8396e873bfd5c1f1d0046e2e830784 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -308,6 +308,7 @@ public final class CraftServer implements Server {
     private final io.papermc.paper.datapack.PaperDatapackManager datapackManager; // Paper
     public static Exception excessiveVelEx; // Paper - Velocity warnings
     private final io.papermc.paper.logging.SysoutCatcher sysoutCatcher = new io.papermc.paper.logging.SysoutCatcher(); // Paper
+    private final io.papermc.paper.potion.PaperPotionBrewer potionBrewer; // Paper - Custom Potion Mixes
 
     static {
         ConfigurationSerialization.registerClass(CraftOfflinePlayer.class);
@@ -391,6 +392,7 @@ public final class CraftServer implements Server {
         if (this.configuration.getBoolean("settings.use-map-color-cache")) {
             MapPalette.setMapColorCache(new CraftMapColorCache(this.logger));
         }
+        this.potionBrewer = new io.papermc.paper.potion.PaperPotionBrewer(console); // Paper - custom potion mixes
         datapackManager = new io.papermc.paper.datapack.PaperDatapackManager(console.getPackRepository()); // Paper
     }
 
@@ -3049,5 +3051,9 @@ public final class CraftServer implements Server {
         return datapackManager;
     }
 
+    @Override
+    public io.papermc.paper.potion.PaperPotionBrewer getPotionBrewer() {
+        return this.potionBrewer;
+    }
     // Paper end
 }
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java
index 139dff90561ac6c51954c6289918a07aeea13a1b..6ba29875d78ede4aa7978ff689e588f7fed11528 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftRecipe.java
@@ -15,6 +15,11 @@ public interface CraftRecipe extends Recipe {
     void addToCraftingManager();
 
     default Ingredient toNMS(RecipeChoice bukkit, boolean requireNotEmpty) {
+        // Paper start
+        return toIngredient(bukkit, requireNotEmpty);
+    }
+    static Ingredient toIngredient(RecipeChoice bukkit, boolean requireNotEmpty) {
+        // Paper end
         Ingredient stack;
 
         if (bukkit == null) {