aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/api/0485-Add-FeatureFlag-API.patch
blob: ae5e00b2830069225775f08bd62fe82f05dc23fc (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
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Thu, 9 Mar 2023 11:24:43 -0800
Subject: [PATCH] Add FeatureFlag API


diff --git a/src/main/java/io/papermc/paper/world/flag/FeatureDependant.java b/src/main/java/io/papermc/paper/world/flag/FeatureDependant.java
new file mode 100644
index 0000000000000000000000000000000000000000..f5633319c64bf19cf07e2b9a1cac2dd1d11ab70b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/world/flag/FeatureDependant.java
@@ -0,0 +1,28 @@
+package io.papermc.paper.world.flag;
+
+import java.util.Set;
+import org.bukkit.FeatureFlag;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Unmodifiable;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Implemented by types in built-in registries that are controlled by {@link FeatureFlag FeatureFlags}.
+ * Types in data-driven registries that are controlled by feature flags just will not exist in that registry.
+ * @apiNote When a type that currently implements this interface transitions to being data-drive, this
+ * interface will be removed from that type in the following major version.
+ */
+@NullMarked
+@ApiStatus.NonExtendable
+public interface FeatureDependant {
+
+    /**
+     * Gets the set of required feature flags for this
+     * to be enabled.
+     *
+     * @return the immutable set of feature flags
+     */
+    default @Unmodifiable Set<FeatureFlag> requiredFeatures() {
+        return FeatureFlagProvider.provider().requiredFeatures(this);
+    }
+}
diff --git a/src/main/java/io/papermc/paper/world/flag/FeatureFlagProvider.java b/src/main/java/io/papermc/paper/world/flag/FeatureFlagProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..388e70628f88d8ee0749a43f82b228453e25d5d0
--- /dev/null
+++ b/src/main/java/io/papermc/paper/world/flag/FeatureFlagProvider.java
@@ -0,0 +1,21 @@
+package io.papermc.paper.world.flag;
+
+import java.util.Optional;
+import java.util.ServiceLoader;
+import java.util.Set;
+import org.bukkit.FeatureFlag;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+@NullMarked
+@ApiStatus.Internal
+interface FeatureFlagProvider {
+
+    Optional<FeatureFlagProvider> PROVIDER = ServiceLoader.load(FeatureFlagProvider.class).findFirst();
+
+    static FeatureFlagProvider provider() {
+        return PROVIDER.orElseThrow();
+    }
+
+    Set<FeatureFlag> requiredFeatures(FeatureDependant dependant);
+}
diff --git a/src/main/java/io/papermc/paper/world/flag/FeatureFlagSetHolder.java b/src/main/java/io/papermc/paper/world/flag/FeatureFlagSetHolder.java
new file mode 100644
index 0000000000000000000000000000000000000000..0955699df65ccbb8711cfa48f0b34d5a8805f1af
--- /dev/null
+++ b/src/main/java/io/papermc/paper/world/flag/FeatureFlagSetHolder.java
@@ -0,0 +1,32 @@
+package io.papermc.paper.world.flag;
+
+import java.util.Set;
+import org.bukkit.FeatureFlag;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.Unmodifiable;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * Implemented by types that hold {@link FeatureFlag FeatureFlags} like
+ * {@link org.bukkit.generator.WorldInfo} and {@link org.bukkit.RegionAccessor}.
+ */
+@NullMarked
+@ApiStatus.NonExtendable
+public interface FeatureFlagSetHolder {
+
+    /**
+     * Checks if this is enabled based on the loaded feature flags.
+     *
+     * @return true if enabled
+     */
+    default boolean isEnabled(final FeatureDependant featureDependant) {
+        return this.getFeatureFlags().containsAll(featureDependant.requiredFeatures());
+    }
+
+    /**
+     * Get all {@link FeatureFlag FeatureFlags} enabled in this world.
+     *
+     * @return all enabled {@link FeatureFlag FeatureFlags}
+     */
+    @Unmodifiable Set<FeatureFlag> getFeatureFlags();
+}
diff --git a/src/main/java/org/bukkit/FeatureFlag.java b/src/main/java/org/bukkit/FeatureFlag.java
index 026b1832bcd163ab89668c991bf002e608e36aef..b5e87480b6a1d064547e4e608f3d402825931a00 100644
--- a/src/main/java/org/bukkit/FeatureFlag.java
+++ b/src/main/java/org/bukkit/FeatureFlag.java
@@ -1,37 +1,56 @@
 package org.bukkit;
 
+// Paper start - overhaul FeatureFlag API
+import com.google.common.base.Preconditions;
+import java.util.List;
+import net.kyori.adventure.key.Key;
+import net.kyori.adventure.util.Index;
+import org.intellij.lang.annotations.Subst;
+// Paper end - overhaul FeatureFlag API
 import org.jetbrains.annotations.ApiStatus;
 
 /**
- * This represents a Feature Flag for a World.
- * <br>
- * Flags which are unavailable in the current version will be null and/or
- * removed.
+ * This represents a Feature Flag for a {@link io.papermc.paper.world.flag.FeatureFlagSetHolder}.
  */
-@ApiStatus.Experimental
 public interface FeatureFlag extends Keyed {
 
-    public static final FeatureFlag VANILLA = Bukkit.getUnsafe().getFeatureFlag(NamespacedKey.minecraft("vanilla"));
+    // Paper start - overhaul FeatureFlag API
+    /**
+     * The {@code vanilla} feature flag.
+     */
+    FeatureFlag VANILLA = create("vanilla");
 
+    /**
+     * The {@code bundle} feature flag.
+     */
     @ApiStatus.Experimental // Paper - add missing annotation
-    public static final FeatureFlag BUNDLE = Bukkit.getUnsafe().getFeatureFlag(NamespacedKey.minecraft("bundle"));
+    FeatureFlag BUNDLE = create("bundle");
 
     /**
-     * <strong>AVAILABLE BETWEEN VERSIONS:</strong> 1.19 - 1.19.4
-     *
-     * @deprecated not available since 1.20
+     * The {@code trade_rebalance} feature flag.
      */
-    @Deprecated
-    public static final FeatureFlag UPDATE_1_20 = Bukkit.getUnsafe().getFeatureFlag(NamespacedKey.minecraft("update_1_20"));
-
     @ApiStatus.Experimental // Paper - add missing annotation
-    public static final FeatureFlag TRADE_REBALANCE = Bukkit.getUnsafe().getFeatureFlag(NamespacedKey.minecraft("trade_rebalance"));
+    FeatureFlag TRADE_REBALANCE = create("trade_rebalance");
+
+    @Deprecated(since = "1.20")
+    FeatureFlag UPDATE_1_20 = deprecated("update_1_20");
+
+    @Deprecated(since = "1.21")
+    FeatureFlag UPDATE_121 = deprecated("update_1_21");
 
     /**
-     * <strong>AVAILABLE BETWEEN VERSIONS:</strong> 1.20.5 - 1.20.6
-     *
-     * @deprecated not available since 1.21
+     * An index of all feature flags.
      */
-    @Deprecated
-    public static final FeatureFlag UPDATE_121 = Bukkit.getUnsafe().getFeatureFlag(NamespacedKey.minecraft("update_1_21"));
+    Index<Key, FeatureFlag> ALL_FLAGS = Index.create(FeatureFlag::key, List.copyOf(FeatureFlagImpl.ALL_FLAGS));
+
+    private static FeatureFlag create(@Subst("vanilla") final String name) {
+        final FeatureFlag flag = new FeatureFlagImpl(NamespacedKey.minecraft(name));
+        Preconditions.checkState(FeatureFlagImpl.ALL_FLAGS.add(flag), "Tried to add duplicate feature flag: " + name);
+        return flag;
+    }
+
+    private static FeatureFlag deprecated(@Subst("vanilla") final String name) {
+        return new FeatureFlagImpl.Deprecated(NamespacedKey.minecraft(name));
+    }
+    // Paper end - overhaul FeatureFlag API
 }
diff --git a/src/main/java/org/bukkit/FeatureFlagImpl.java b/src/main/java/org/bukkit/FeatureFlagImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..5a43f65f633dc9a1a0ba7dba0c15488375427444
--- /dev/null
+++ b/src/main/java/org/bukkit/FeatureFlagImpl.java
@@ -0,0 +1,27 @@
+package org.bukkit;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+@ApiStatus.Internal
+@NullMarked
+record FeatureFlagImpl(NamespacedKey key) implements FeatureFlag {
+
+    static final Set<FeatureFlag> ALL_FLAGS = new HashSet<>();
+
+    @Override
+    public NamespacedKey getKey() {
+        return this.key;
+    }
+
+    @ApiStatus.Internal
+    record Deprecated(NamespacedKey key) implements FeatureFlag {
+
+        @Override
+        public NamespacedKey getKey() {
+            return this.key;
+        }
+    }
+}
diff --git a/src/main/java/org/bukkit/RegionAccessor.java b/src/main/java/org/bukkit/RegionAccessor.java
index eb33e8e671972aa308ad75a7ce9aa9ac526f470f..05ecf3cb38ff42c8b52405d900197e6bf47dd1e6 100644
--- a/src/main/java/org/bukkit/RegionAccessor.java
+++ b/src/main/java/org/bukkit/RegionAccessor.java
@@ -18,7 +18,7 @@ import org.jetbrains.annotations.Nullable;
  * A RegionAccessor gives access to getting, modifying and spawning {@link Biome}, {@link BlockState} and {@link Entity},
  * as well as generating some basic structures.
  */
-public interface RegionAccessor extends Keyed { // Paper
+public interface RegionAccessor extends Keyed, io.papermc.paper.world.flag.FeatureFlagSetHolder { // Paper - feature flag API
 
     /**
      * Gets the {@link Biome} at the given {@link Location}.
diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java
index 330e3013eda204aa9b33d5e1c3104e0b595abdbc..c80e0ef587a001ee6de3f5c182cc9696d58bafeb 100644
--- a/src/main/java/org/bukkit/UnsafeValues.java
+++ b/src/main/java/org/bukkit/UnsafeValues.java
@@ -114,8 +114,7 @@ public interface UnsafeValues {
 
     String getTranslationKey(Attribute attribute);
 
-    @Nullable
-    FeatureFlag getFeatureFlag(@NotNull NamespacedKey key);
+    // Paper - replace with better system
 
     /**
      * Do not use, method will get removed, and the plugin won't run
diff --git a/src/main/java/org/bukkit/block/BlockType.java b/src/main/java/org/bukkit/block/BlockType.java
index a58ef2238208fbb55341f4532eaa288577ed8c0e..fb2b373c1822e7248a30e610d11e2c2bd438c19a 100644
--- a/src/main/java/org/bukkit/block/BlockType.java
+++ b/src/main/java/org/bukkit/block/BlockType.java
@@ -125,7 +125,7 @@ import org.jetbrains.annotations.Nullable;
  * changes may occur. Do not use this API in plugins.
  */
 @ApiStatus.Internal
-public interface BlockType extends Keyed, Translatable, net.kyori.adventure.translation.Translatable { // Paper - add translatable
+public interface BlockType extends Keyed, Translatable, net.kyori.adventure.translation.Translatable, io.papermc.paper.world.flag.FeatureDependant { // Paper - add translatable & feature flag API
 
     /**
      * Typed represents a subtype of {@link BlockType}s that have a known block
@@ -3490,7 +3490,9 @@ public interface BlockType extends Keyed, Translatable, net.kyori.adventure.tran
      *
      * @param world the world to check
      * @return true if this BlockType can be used in this World.
+     * @deprecated Use {@link io.papermc.paper.world.flag.FeatureFlagSetHolder#isEnabled(io.papermc.paper.world.flag.FeatureDependant)}
      */
+    @Deprecated(forRemoval = true, since = "1.21.1") // Paper
     boolean isEnabledByFeature(@NotNull World world);
 
     /**
diff --git a/src/main/java/org/bukkit/entity/EntityType.java b/src/main/java/org/bukkit/entity/EntityType.java
index ba605ad75d4ed920c0dc4527529998041a58676b..a78e1c431a6ea46ba7c44880e25a871f473bef41 100644
--- a/src/main/java/org/bukkit/entity/EntityType.java
+++ b/src/main/java/org/bukkit/entity/EntityType.java
@@ -23,7 +23,7 @@ import org.jetbrains.annotations.Contract;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
-public enum EntityType implements Keyed, Translatable, net.kyori.adventure.translation.Translatable { // Paper - translatable
+public enum EntityType implements Keyed, Translatable, net.kyori.adventure.translation.Translatable, io.papermc.paper.world.flag.FeatureDependant { // Paper - translatable
 
     // These strings MUST match the strings in nms.EntityTypes and are case sensitive.
     /**
diff --git a/src/main/java/org/bukkit/generator/WorldInfo.java b/src/main/java/org/bukkit/generator/WorldInfo.java
index 5067f1371433cccd3287af7f03e152f2c3c1ece3..e0cb282541548ac3bd24cce86b3413f5748ea8bc 100644
--- a/src/main/java/org/bukkit/generator/WorldInfo.java
+++ b/src/main/java/org/bukkit/generator/WorldInfo.java
@@ -7,7 +7,7 @@ import org.jetbrains.annotations.NotNull;
 /**
  * Holds various information of a World
  */
-public interface WorldInfo {
+public interface WorldInfo extends io.papermc.paper.world.flag.FeatureFlagSetHolder { // Paper - feature flag API
 
     /**
      * Gets the unique name of this world
diff --git a/src/main/java/org/bukkit/inventory/ItemType.java b/src/main/java/org/bukkit/inventory/ItemType.java
index 15d68c4997f739c39675ef8ffa5ab7967dac59f2..f96ced6ae8a969319728efb4fc4fe545923e32be 100644
--- a/src/main/java/org/bukkit/inventory/ItemType.java
+++ b/src/main/java/org/bukkit/inventory/ItemType.java
@@ -47,7 +47,7 @@ import org.jetbrains.annotations.Nullable;
  * changes may occur. Do not use this API in plugins.
  */
 @ApiStatus.Experimental // Paper - already required for registry builders
-public interface ItemType extends Keyed, Translatable, net.kyori.adventure.translation.Translatable { // Paper - add Translatable
+public interface ItemType extends Keyed, Translatable, net.kyori.adventure.translation.Translatable, io.papermc.paper.world.flag.FeatureDependant { // Paper - add Translatable & feature flag API
 
     /**
      * Typed represents a subtype of {@link ItemType}s that have a known item meta type
@@ -2300,7 +2300,9 @@ public interface ItemType extends Keyed, Translatable, net.kyori.adventure.trans
      *
      * @param world the world to check
      * @return true if this ItemType can be used in this World.
+     * @deprecated use {@link io.papermc.paper.world.flag.FeatureFlagSetHolder#isEnabled(io.papermc.paper.world.flag.FeatureDependant)}
      */
+    @Deprecated(forRemoval = true, since = "1.21.1") // Paper
     boolean isEnabledByFeature(@NotNull World world);
 
     /**
diff --git a/src/main/java/org/bukkit/inventory/MenuType.java b/src/main/java/org/bukkit/inventory/MenuType.java
index 34d45a105da72481a7c4f2e3831f5d2a676c91c6..529143c9007d6da4f671576f9934933f2e5d6f23 100644
--- a/src/main/java/org/bukkit/inventory/MenuType.java
+++ b/src/main/java/org/bukkit/inventory/MenuType.java
@@ -22,7 +22,7 @@ import org.jetbrains.annotations.NotNull;
  * created and viewed by the player.
  */
 @ApiStatus.Experimental
-public interface MenuType extends Keyed {
+public interface MenuType extends Keyed, io.papermc.paper.world.flag.FeatureDependant { // Paper - make FeatureDependant
 
     /**
      * A MenuType which represents a chest with 1 row.
diff --git a/src/main/java/org/bukkit/potion/PotionEffectType.java b/src/main/java/org/bukkit/potion/PotionEffectType.java
index d02bb37768404422969d8b93e009960d0a693a93..294ba70f1e446ec8d502e5c14f82ae52547aeb21 100644
--- a/src/main/java/org/bukkit/potion/PotionEffectType.java
+++ b/src/main/java/org/bukkit/potion/PotionEffectType.java
@@ -17,7 +17,7 @@ import org.jetbrains.annotations.Nullable;
 /**
  * Represents a type of potion and its effect on an entity.
  */
-public abstract class PotionEffectType implements Keyed, Translatable, net.kyori.adventure.translation.Translatable { // Paper - implement Translatable
+public abstract class PotionEffectType implements Keyed, Translatable, net.kyori.adventure.translation.Translatable, io.papermc.paper.world.flag.FeatureDependant { // Paper - implement Translatable & feature flag API
     private static final BiMap<Integer, PotionEffectType> ID_MAP = HashBiMap.create();
 
     /**
diff --git a/src/main/java/org/bukkit/potion/PotionType.java b/src/main/java/org/bukkit/potion/PotionType.java
index 453c626092caa734a0af5800699f299fc6fd0e43..c82cc9d9db5cb0e0e3c02f6a9564fc935ee594aa 100644
--- a/src/main/java/org/bukkit/potion/PotionType.java
+++ b/src/main/java/org/bukkit/potion/PotionType.java
@@ -14,7 +14,7 @@ import org.jetbrains.annotations.Nullable;
  * This enum reflects and matches each potion state that can be obtained from
  * the Creative mode inventory
  */
-public enum PotionType implements Keyed {
+public enum PotionType implements Keyed, io.papermc.paper.world.flag.FeatureDependant { // Paper - feature flag API
     WATER("water"),
     MUNDANE("mundane"),
     THICK("thick"),