Autogenerate Feature Flags (#2201)

* Autogenerate Feature Flags (Won't work until MinestomDataGen 1.21-rev2 is released with datagen changes.)

* Rewrite feature flag auto-generation, add example in demo.

* Update data gen

* Remove dead code and fix access modifier on FeatureFlagImpl
This commit is contained in:
Not Flamgop 2024-07-02 15:59:38 -07:00 committed by GitHub
parent 3661ecbc54
commit 90fb708739
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 86 additions and 8 deletions

View File

@ -37,6 +37,7 @@ public class Generators {
generator.generate(resource("sounds.json"), "net.minestom.server.sound", "SoundEvent", "BuiltinSoundEvent", "SoundEvents");
generator.generate(resource("custom_statistics.json"), "net.minestom.server.statistic", "StatisticType", "StatisticTypeImpl", "StatisticTypes");
generator.generate(resource("attributes.json"), "net.minestom.server.entity.attribute", "Attribute", "AttributeImpl", "Attributes");
generator.generate(resource("feature_flags.json"), "net.minestom.server", "FeatureFlag", "FeatureFlagImpl", "FeatureFlags");
// Dynamic registries
generator.generateKeys(resource("chat_types.json"), "net.minestom.server.message", "ChatType", "ChatTypes");

View File

@ -2,6 +2,7 @@ package net.minestom.demo;
import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.text.Component;
import net.minestom.server.FeatureFlag;
import net.minestom.server.MinecraftServer;
import net.minestom.server.advancements.FrameType;
import net.minestom.server.advancements.notifications.Notification;
@ -103,6 +104,10 @@ public class PlayerInit {
.addListener(AsyncPlayerConfigurationEvent.class, event -> {
final Player player = event.getPlayer();
// Show off adding and removing feature flags
event.addFeatureFlag(FeatureFlag.BUNDLE);
event.removeFeatureFlag(FeatureFlag.TRADE_REBALANCE); // not enabled by default, just removed for demonstration
var instances = MinecraftServer.getInstanceManager().getInstances();
Instance instance = instances.stream().skip(new Random().nextInt(instances.size())).findFirst().orElse(null);
event.setSpawningInstance(instance);

View File

@ -3,7 +3,7 @@ metadata.format.version = "1.1"
[versions]
# Important dependencies
data = "1.21"
data = "1.21-rv2"
adventure = "4.17.0"
jetbrainsAnnotations = "23.0.0"
slf4j = "2.0.7"

View File

@ -0,0 +1,13 @@
package net.minestom.server;
/**
* Code autogenerated, do not edit!
*/
@SuppressWarnings("unused")
interface FeatureFlags {
FeatureFlag BUNDLE = FeatureFlagImpl.get("minecraft:bundle");
FeatureFlag VANILLA = FeatureFlagImpl.get("minecraft:vanilla");
FeatureFlag TRADE_REBALANCE = FeatureFlagImpl.get("minecraft:trade_rebalance");
}

View File

@ -0,0 +1,6 @@
package net.minestom.server;
import net.minestom.server.registry.StaticProtocolObject;
public sealed interface FeatureFlag extends StaticProtocolObject, FeatureFlags permits FeatureFlagImpl {
}

View File

@ -0,0 +1,33 @@
package net.minestom.server;
import net.minestom.server.registry.Registry;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.NotNull;
record FeatureFlagImpl(@NotNull Registry.FeatureFlagEntry registry) implements FeatureFlag {
private static final Registry.Container<FeatureFlagImpl> CONTAINER = Registry.createStaticContainer(Registry.Resource.FEATURE_FLAGS,
(namespace, properties) -> new FeatureFlagImpl(Registry.featureFlag(namespace, properties)));
static FeatureFlag get(@NotNull String namespace) {
return CONTAINER.get(namespace);
}
static FeatureFlag getSafe(@NotNull String namespace) {
return CONTAINER.getSafe(namespace);
}
static FeatureFlag getId(int id) {
return CONTAINER.getId(id);
}
@Override
public @NotNull NamespaceID namespace() {
return registry.namespace();
}
@Override
public int id() {
return registry.id();
}
}

View File

@ -2,12 +2,12 @@ package net.minestom.server.event.player;
import it.unimi.dsi.fastutil.objects.ObjectArraySet;
import it.unimi.dsi.fastutil.objects.ObjectSets;
import net.minestom.server.FeatureFlag;
import net.minestom.server.entity.Player;
import net.minestom.server.event.trait.PlayerEvent;
import net.minestom.server.instance.Instance;
import net.minestom.server.network.packet.server.configuration.ResetChatPacket;
import net.minestom.server.network.packet.server.configuration.UpdateEnabledFeaturesPacket;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -26,7 +26,7 @@ public class AsyncPlayerConfigurationEvent implements PlayerEvent {
private final Player player;
private final boolean isFirstConfig;
private final ObjectArraySet<NamespaceID> featureFlags = new ObjectArraySet<>();
private final ObjectArraySet<FeatureFlag> featureFlags = new ObjectArraySet<>();
private boolean hardcore;
private boolean clearChat;
private boolean sendRegistryData;
@ -36,7 +36,7 @@ public class AsyncPlayerConfigurationEvent implements PlayerEvent {
this.player = player;
this.isFirstConfig = isFirstConfig;
this.featureFlags.add(NamespaceID.from("minecraft:vanilla")); // Vanilla feature-set, without this you get nothing at all. Kinda wacky!
this.featureFlags.add(FeatureFlag.VANILLA); // Vanilla feature-set, without this you get nothing at all. Kinda wacky!
this.hardcore = false;
this.clearChat = false;
@ -71,8 +71,9 @@ public class AsyncPlayerConfigurationEvent implements PlayerEvent {
* @param feature A minecraft feature flag
*
* @see UpdateEnabledFeaturesPacket
* @see net.minestom.server.FeatureFlag
*/
public void addFeatureFlag(@NotNull NamespaceID feature) {
public void addFeatureFlag(@NotNull FeatureFlag feature) {
this.featureFlags.add(feature);
}
@ -84,8 +85,9 @@ public class AsyncPlayerConfigurationEvent implements PlayerEvent {
* @return if the feature specified existed prior to being removed
*
* @see UpdateEnabledFeaturesPacket
* @see net.minestom.server.FeatureFlag
*/
public boolean removeFeatureFlag(@NotNull NamespaceID feature) {
public boolean removeFeatureFlag(@NotNull FeatureFlag feature) {
return this.featureFlags.remove(feature); // Should this have sanity checking to see if the feature was actually contained in the list?
}
@ -95,8 +97,9 @@ public class AsyncPlayerConfigurationEvent implements PlayerEvent {
* @return An unmodifiable set of feature flags
*
* @see UpdateEnabledFeaturesPacket
* @see net.minestom.server.FeatureFlag
*/
public @NotNull Set<NamespaceID> getFeatureFlags() {
public @NotNull Set<FeatureFlag> getFeatureFlags() {
return ObjectSets.unmodifiable(this.featureFlags);
}

View File

@ -24,6 +24,7 @@ import net.minestom.server.network.packet.server.play.StartConfigurationPacket;
import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.network.player.PlayerSocketConnection;
import net.minestom.server.network.plugin.LoginPluginMessageProcessor;
import net.minestom.server.registry.StaticProtocolObject;
import net.minestom.server.utils.StringUtils;
import net.minestom.server.utils.async.AsyncUtils;
import net.minestom.server.utils.debug.DebugUtils;
@ -37,6 +38,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Manages the connected clients.
@ -278,7 +280,7 @@ public final class ConnectionManager {
EventDispatcher.call(event);
if (!player.isOnline()) return; // Player was kicked during config.
player.sendPacket(new UpdateEnabledFeaturesPacket(event.getFeatureFlags())); // send player features that were enabled or disabled during async config event
player.sendPacket(new UpdateEnabledFeaturesPacket(event.getFeatureFlags().stream().map(StaticProtocolObject::namespace).collect(Collectors.toSet()))); // send player features that were enabled or disabled during async config event
final Instance spawningInstance = event.getSpawningInstance();
Check.notNull(spawningInstance, "You need to specify a spawning instance in the AsyncPlayerConfigurationEvent");

View File

@ -70,6 +70,11 @@ public final class Registry {
return new EntityEntry(namespace, main, null);
}
@ApiStatus.Internal
public static FeatureFlagEntry featureFlag(String namespace, @NotNull Properties main) {
return new FeatureFlagEntry(namespace, main, null);
}
@ApiStatus.Internal
public static PotionEffectEntry potionEffect(String namespace, @NotNull Properties main) {
return new PotionEffectEntry(namespace, main, null);
@ -207,6 +212,7 @@ public final class Registry {
BLOCKS("blocks.json"),
ITEMS("items.json"),
ENTITIES("entities.json"),
FEATURE_FLAGS("feature_flags.json"),
SOUNDS("sounds.json"),
COMMAND_ARGUMENTS("command_arguments.json"),
STATISTICS("custom_statistics.json"),
@ -708,6 +714,15 @@ public final class Registry {
}
}
public record FeatureFlagEntry(NamespaceID namespace, int id, Properties custom) implements Entry {
public FeatureFlagEntry(String namespace, Properties main, Properties custom) {
this(NamespaceID.from(namespace),
main.getInt("id"),
null
);
}
}
public record DamageTypeEntry(NamespaceID namespace, float exhaustion,
String messageId,
String scaling,