mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-13 00:37:52 +00:00
Add DeferredRegistryData to help with generating data driven registry entries.
Revive GameTest system and make all but one Forge Test mod pass.
This commit is contained in:
102 files changed
+1229
-469
No files matched your search
@@ -27,7 +27,6 @@ import net.minecraft.client.gui.components.ChatComponent;
|
||||
import net.minecraft.client.gui.components.LerpingBossEvent;
|
||||
import net.minecraft.client.gui.screens.ConfirmScreen;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.client.gui.screens.TitleScreen;
|
||||
import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent;
|
||||
import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipPositioner;
|
||||
import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen;
|
||||
@@ -50,7 +49,6 @@ import net.minecraft.client.renderer.entity.state.HumanoidRenderState;
|
||||
import net.minecraft.client.renderer.texture.SpriteContents;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlas;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.resources.language.I18n;
|
||||
import net.minecraft.client.resources.metadata.animation.FrameSize;
|
||||
import net.minecraft.client.resources.model.ModelBaker;
|
||||
import net.minecraft.client.resources.model.ModelBakery;
|
||||
@@ -132,7 +130,6 @@ import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.fml.IExtensionPoint;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.fml.ModLoader;
|
||||
import net.minecraftforge.fml.VersionChecker;
|
||||
import net.minecraftforge.network.NetworkContext;
|
||||
import net.minecraftforge.network.NetworkInitialization;
|
||||
import net.minecraftforge.network.NetworkRegistry;
|
||||
@@ -293,50 +290,6 @@ public class ForgeHooksClient {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization of Forge Renderers.
|
||||
*/
|
||||
static {
|
||||
//FluidRegistry.renderIdFluid = RenderingRegistry.getNextAvailableRenderId();
|
||||
//RenderingRegistry.registerBlockHandler(RenderBlockFluid.instance);
|
||||
}
|
||||
|
||||
private static VersionChecker.Status getForgeVersionStatus() {
|
||||
final class LazyInit {
|
||||
private static final VersionChecker.Status INSTANCE = ForgeVersion.getStatus();
|
||||
|
||||
static {
|
||||
forgeStatusLine = switch (INSTANCE) {
|
||||
// case FAILED -> " Version check failed";
|
||||
// case UP_TO_DATE -> "Forge up to date";
|
||||
// case AHEAD -> "Using non-recommended Forge build, issues may arise.";
|
||||
case OUTDATED, BETA_OUTDATED -> I18n.get("forge.update.newversion", ForgeVersion.getTarget());
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
private LazyInit() {}
|
||||
}
|
||||
|
||||
return LazyInit.INSTANCE;
|
||||
}
|
||||
|
||||
// NO-OP method, kept for bin-compat. Used in TitleScreen.java
|
||||
@Deprecated(forRemoval = true, since = "1.21.4")
|
||||
public static void renderMainMenu(TitleScreen gui, GuiGraphics graphics, Font font, int width, int height, int alpha) {
|
||||
/*
|
||||
VersionChecker.Status status = getForgeVersionStatus();
|
||||
|
||||
if (status == VersionChecker.Status.BETA || status == VersionChecker.Status.BETA_OUTDATED) {
|
||||
// Render a warning at the top of the screen
|
||||
Component line = Component.translatable("forge.update.beta.1", ChatFormatting.RED, ChatFormatting.RESET).withStyle(ChatFormatting.RED);
|
||||
graphics.drawCenteredString(font, line, width / 2, 4, 0xFFFFFF | alpha);
|
||||
line = Component.translatable("forge.update.beta.2");
|
||||
graphics.drawCenteredString(font, line, width / 2, 4 + (font.lineHeight + 1), 0xFFFFFF | alpha);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
public static String forgeStatusLine;
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -412,7 +412,8 @@ public enum ForgeRenderTypes {
|
||||
/**
|
||||
* Use {@link #TEXT_INTENSITY_SEE_THROUGH} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("unused")
|
||||
@Deprecated(forRemoval = true, since = "1.21.5")
|
||||
public static Function<ResourceLocation, RenderType> TEXT_INTENSITY_SEETHROUGH = TEXT_INTENSITY_SEE_THROUGH;
|
||||
private static RenderType getTextIntensitySeeThrough(ResourceLocation locationIn) {
|
||||
return create(
|
||||
|
||||
@@ -18,8 +18,6 @@ import java.util.stream.Collectors;
|
||||
import net.minecraftforge.client.gui.widget.ScrollPanel;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import com.mojang.blaze3d.vertex.Tesselator;
|
||||
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
@@ -22,6 +22,18 @@ public class DatapackBuiltinEntriesProvider extends RegistriesDatapackGenerator
|
||||
|
||||
private final CompletableFuture<HolderLookup.Provider> fullRegistries;
|
||||
|
||||
/**
|
||||
* Constructs a new datapack provider which generates all registry objects
|
||||
* from the provided mods using the holder.
|
||||
*
|
||||
* @param output the target directory of the data generator
|
||||
* @param registries a future of patched registries
|
||||
* @param modId The mod id to generate the dynamic registry objects of
|
||||
*/
|
||||
public DatapackBuiltinEntriesProvider(PackOutput output, CompletableFuture<RegistrySetBuilder.PatchedRegistries> registries, String modId) {
|
||||
this(output, registries, Set.of(modId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new datapack provider which generates all registry objects
|
||||
* from the provided mods using the holder.
|
||||
@@ -35,6 +47,20 @@ public class DatapackBuiltinEntriesProvider extends RegistriesDatapackGenerator
|
||||
this.fullRegistries = registries.thenApply(RegistrySetBuilder.PatchedRegistries::full);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new datapack provider which generates all registry objects
|
||||
* from the provided mods using the holder. All entries that need to be
|
||||
* bootstrapped are provided within the {@link RegistrySetBuilder}.
|
||||
*
|
||||
* @param output the target directory of the data generator
|
||||
* @param registries a future of a lookup for registries and their objects
|
||||
* @param registryBuilder a builder containing the dynamic registry objects added by this provider
|
||||
* @param modId The mod id to generate the dynamic registry objects of
|
||||
*/
|
||||
public DatapackBuiltinEntriesProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> registries, RegistrySetBuilder registryBuilder, String modId) {
|
||||
this(output, registries, registryBuilder, Set.of(modId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new datapack provider which generates all registry objects
|
||||
* from the provided mods using the holder. All entries that need to be
|
||||
|
||||
@@ -87,7 +87,7 @@ public interface IForgeBlockEntity extends ICapabilityProvider {
|
||||
bb = AABB.encapsulatingFullBlocks(pos.offset(-1, 0, -1), pos.offset(1, 0, 1));
|
||||
} else if (block == Blocks.STRUCTURE_BLOCK) {
|
||||
bb = INFINITE_EXTENT_AABB;
|
||||
} else if (block != null && block != Blocks.BEACON) {
|
||||
} else if (block != null && block != Blocks.BEACON && block != Blocks.TEST_INSTANCE_BLOCK) {
|
||||
AABB cbb = null;
|
||||
try {
|
||||
VoxelShape collisionShape = state.getCollisionShape(self().getLevel(), pos);
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.common.extensions;
|
||||
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
|
||||
import net.minecraft.core.RegistrySetBuilder;
|
||||
import net.minecraftforge.registries.DeferredRegisterData;
|
||||
|
||||
public interface IForgeRegistrySetBuilder {
|
||||
private RegistrySetBuilder self() {
|
||||
return (RegistrySetBuilder)this;
|
||||
}
|
||||
|
||||
default <T> RegistrySetBuilder add(DeferredRegisterData<T> dr) {
|
||||
return self().add(dr.getRegistryKey(), dr);
|
||||
}
|
||||
|
||||
default <T> RegistrySetBuilder add(DeferredRegisterData<T> dr, Lifecycle lifecycle) {
|
||||
return self().add(dr.getRegistryKey(), lifecycle, dr);
|
||||
}
|
||||
}
|
||||
@@ -6,46 +6,31 @@
|
||||
package net.minecraftforge.gametest;
|
||||
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.gametest.framework.GameTestEnvironments;
|
||||
import net.minecraft.gametest.framework.GameTestHelper;
|
||||
import net.minecraft.gametest.framework.GameTestInstance;
|
||||
import net.minecraft.gametest.framework.TestData;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.fml.ModLoader;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
|
||||
import net.minecraftforge.forgespi.language.ModFileScanData;
|
||||
import net.minecraftforge.forgespi.language.ModFileScanData.AnnotationData;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Internal class used to glue mods into the game test framework.
|
||||
* Modders should use the supplied annotations and DeferredRegister
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@ApiStatus.Internal
|
||||
public class ForgeGameTestHooks {
|
||||
private static boolean registeredGametests = false;
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static final Type GAME_TEST_HOLDER = Type.getType(GameTestHolder.class);
|
||||
private static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
|
||||
|
||||
public static boolean isGametestEnabled() {
|
||||
return !FMLLoader.isProduction() && (SharedConstants.IS_RUNNING_IN_IDE || isGametestServer() || Boolean.getBoolean("forge.enableGameTest"));
|
||||
@@ -55,78 +40,45 @@ public class ForgeGameTestHooks {
|
||||
return !FMLLoader.isProduction() && Boolean.getBoolean("forge.gameTestServer");
|
||||
}
|
||||
|
||||
/*
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void registerGametests() {
|
||||
if (!registeredGametests && isGametestEnabled() && ModLoader.isLoadingStateValid()) {
|
||||
Set<String> enabledNamespaces = getEnabledNamespaces();
|
||||
LOGGER.info("Enabled Gametest Namespaces: {}", enabledNamespaces);
|
||||
|
||||
Set<Method> gameTestMethods = new HashSet<>();
|
||||
RegisterGameTestsEvent event = new RegisterGameTestsEvent(gameTestMethods);
|
||||
|
||||
ModLoader.get().postEvent(event);
|
||||
|
||||
ModList.get().getAllScanData().stream()
|
||||
.map(ModFileScanData::getAnnotations)
|
||||
.flatMap(Collection::stream)
|
||||
.filter(a -> GAME_TEST_HOLDER.equals(a.annotationType()))
|
||||
.forEach(a -> addGameTestMethods(a, gameTestMethods));
|
||||
|
||||
for (Method gameTestMethod : gameTestMethods) {
|
||||
GameTestRegistry.register(gameTestMethod, enabledNamespaces);
|
||||
}
|
||||
|
||||
registeredGametests = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static Set<String> getEnabledNamespaces() {
|
||||
String enabledNamespacesStr = System.getProperty("forge.enabledGameTestNamespaces");
|
||||
if (enabledNamespacesStr == null)
|
||||
return Set.of();
|
||||
|
||||
return Arrays.stream(enabledNamespacesStr.split(",")).filter(s -> !s.isBlank()).collect(Collectors.toUnmodifiableSet());
|
||||
}
|
||||
|
||||
private static void addGameTestMethods(AnnotationData annotationData, Set<Method> gameTestMethods) {
|
||||
try {
|
||||
Class<?> clazz = Class.forName(annotationData.clazz().getClassName(), true, ForgeGameTestHooks.class.getClassLoader());
|
||||
|
||||
gameTestMethods.addAll(Arrays.asList(clazz.getDeclaredMethods()));
|
||||
} catch (ClassNotFoundException e) {
|
||||
// Should not be possible
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static String getTestName(Method method, GameTest meta) {
|
||||
var name = method.getName().toLowerCase(Locale.ENGLISH);
|
||||
return getPrefixed(method, name);
|
||||
}
|
||||
|
||||
private static String getPrefixed(Method method, String name) {
|
||||
var prefix = getPrefix(method);
|
||||
return prefix == null ? name : prefix + '.' + name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getPrefix(Method method) {
|
||||
private static boolean shouldPrefix(Method method) {
|
||||
var cls = method.getDeclaringClass();
|
||||
var shouldPrefix = !method.isAnnotationPresent(GameTestDontPrefix.class) &&
|
||||
!cls.isAnnotationPresent(GameTestDontPrefix.class);
|
||||
if (!shouldPrefix)
|
||||
return null;
|
||||
return !method.isAnnotationPresent(GameTestDontPrefix.class) &&
|
||||
!cls.isAnnotationPresent(GameTestDontPrefix.class);
|
||||
}
|
||||
|
||||
return getPrefix(cls);
|
||||
private static String snake(String input) {
|
||||
var result = new StringBuilder();
|
||||
for (char c : input.toCharArray()) {
|
||||
if (Character.isUpperCase(c)) {
|
||||
if (!result.isEmpty())
|
||||
result.append('_');
|
||||
result.append(Character.toLowerCase(c));
|
||||
} else
|
||||
result.append(c);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private static String getPrefix(Class<?> cls) {
|
||||
var prefix = cls.getAnnotation(GameTestPrefix.class);
|
||||
if (prefix != null)
|
||||
return prefix.value();
|
||||
return prefix.value() + '/';
|
||||
|
||||
var holder = cls.getAnnotation(GameTestHolder.class);
|
||||
var mod = cls.getAnnotation(Mod.class);
|
||||
if (mod != null) {
|
||||
var holder = cls.getAnnotation(GameTestNamespace.class);
|
||||
if (holder != null && !holder.value().isEmpty())
|
||||
return mod.value() + '/';
|
||||
}
|
||||
|
||||
|
||||
return cls.getSimpleName().toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
|
||||
private static String getNamespace(Class<?> cls) {
|
||||
var holder = cls.getAnnotation(GameTestNamespace.class);
|
||||
if (holder != null && !holder.value().isEmpty())
|
||||
return holder.value();
|
||||
|
||||
@@ -134,21 +86,32 @@ public class ForgeGameTestHooks {
|
||||
if (mod != null)
|
||||
return mod.value();
|
||||
|
||||
return cls.getSimpleName().toLowerCase(Locale.ENGLISH);
|
||||
throw new IllegalArgumentException("Could not find modid for " + cls.getName());
|
||||
}
|
||||
|
||||
private static ResourceLocation key(String namespace, String path) {
|
||||
return path.indexOf(':') == -1 ? ResourceLocation.parse(path) : ResourceLocation.fromNamespaceAndPath(namespace, path);
|
||||
private static ResourceLocation key(String namespace, String prefix, String path) {
|
||||
if (path.indexOf(':') != -1) {
|
||||
var rl = ResourceLocation.parse(path);
|
||||
if (!prefix.isEmpty())
|
||||
return rl.withPrefix(prefix);
|
||||
return rl;
|
||||
}
|
||||
|
||||
return ResourceLocation.fromNamespaceAndPath(namespace, prefix + path);
|
||||
}
|
||||
|
||||
public static Map<ResourceLocation, GameTestInstance> gatherTests(Class<?> root) {
|
||||
String prefix = getPrefix(root);
|
||||
var seen = new HashSet<ResourceLocation>();
|
||||
public record TestReference(Consumer<GameTestHelper> consumer, TestData<ResourceLocation> data) {}
|
||||
|
||||
public static Map<ResourceLocation, TestReference> gatherTests(Class<?> root, Object instance) {
|
||||
var ret = new HashMap<ResourceLocation, TestReference>();
|
||||
var seen = new HashSet<String>();
|
||||
String class_prefix = getPrefix(root);
|
||||
String namespace = getNamespace(root);
|
||||
Class<?> cls = root;
|
||||
|
||||
while (cls != Object.class) {
|
||||
for (var method : cls.getDeclaredMethods()) {
|
||||
if (cls != root && Modifier.isStatic(method.getModifiers()))
|
||||
if (!Modifier.isPublic(method.getModifiers()) || (cls != root && Modifier.isStatic(method.getModifiers())))
|
||||
continue;
|
||||
|
||||
var gametest = method.getAnnotation(GameTest.class);
|
||||
@@ -160,40 +123,73 @@ public class ForgeGameTestHooks {
|
||||
if (method.getParameterCount() != 1 || method.getParameterTypes()[0] != GameTestHelper.class)
|
||||
throw new IllegalStateException("Invalid @GameTest function " + owner + " incorrect arguments");
|
||||
|
||||
var testName = key(prefix, gametest.name().isEmpty() ? method.getName() : gametest.name());
|
||||
if (!seen.add(testName)) {
|
||||
if (!Modifier.isStatic(method.getModifiers()))
|
||||
continue;
|
||||
throw new IllegalStateException("Failed to register test, already seen " + testName + ", for " + owner);
|
||||
}
|
||||
// We don't want parent methods
|
||||
if (!seen.add(method.getName()))
|
||||
continue;
|
||||
|
||||
if (gametest.type() == GameTest.Type.FUNCTION) {
|
||||
var funcName = key(prefix, gametest.function());
|
||||
var func = BuiltInRegistries.TEST_FUNCTION.get(funcName).orElse(null);
|
||||
var prefix = shouldPrefix(method) ? class_prefix : "";
|
||||
var name = key(namespace, prefix, gametest.name().isEmpty() ? snake(method.getName()) : gametest.name());
|
||||
|
||||
if (func == null)
|
||||
throw new IllegalStateException("Could not find referenced function `" + funcName + "` for " + owner);
|
||||
var structure = GameTest.DEFAULT_STRUCTURE.equals(gametest.structure())
|
||||
? ResourceLocation.parse(gametest.structure())
|
||||
: key(namespace, "", gametest.structure());
|
||||
|
||||
var data = getTestData(prefix, owner, gametest.data());
|
||||
var env = GameTestEnvironments.DEFAULT.equals(gametest.environment())
|
||||
? ResourceLocation.withDefaultNamespace(gametest.environment())
|
||||
: key(namespace, "", gametest.environment());
|
||||
|
||||
var data = new TestData<ResourceLocation>(
|
||||
env,
|
||||
structure,
|
||||
gametest.maxTicks(),
|
||||
gametest.setupTicks(),
|
||||
gametest.required(),
|
||||
gametest.rotation(),
|
||||
gametest.manualOnly(),
|
||||
gametest.maxAttempts(),
|
||||
gametest.requiredSuccesses(),
|
||||
gametest.skyAccess()
|
||||
);
|
||||
|
||||
} else if (gametest.type() == GameTest.Type.BLOCK) {
|
||||
MethodHandle handle = unreflect(method, owner);
|
||||
|
||||
Consumer<GameTestHelper> func;
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
func = ctx -> {
|
||||
try {
|
||||
handle.invoke(ctx);
|
||||
} catch (Throwable e) {
|
||||
sneakyThrow(e);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
throw new IllegalStateException("Could not determine GameTest type for " + root.getName() + " -> " + gametest.type());
|
||||
func = ctx -> {
|
||||
try {
|
||||
handle.invoke(instance, ctx);
|
||||
} catch (Throwable e) {
|
||||
sneakyThrow(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ret.put(name, new TestReference(func, data));
|
||||
}
|
||||
|
||||
cls = cls.getSuperclass();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static MethodHandle unreflect(Method method, String owner) {
|
||||
try {
|
||||
return LOOKUP.unreflect(method);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalArgumentException("Failed to unreflect " + owner, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static TestData<?> getTestData(String prefix, String owner, GameTest.Data data) {
|
||||
var envName = key(prefix, data.environment());
|
||||
var env = BuiltInRegistries.TEST_ENVIRONMENT
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <E extends Throwable> void sneakyThrow(Throwable e) throws E {
|
||||
throw (E)e;
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -6,39 +6,23 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import net.minecraft.gametest.framework.GameTestEnvironments;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
|
||||
@Retention(RUNTIME)
|
||||
@Target(METHOD)
|
||||
public @interface GameTest {
|
||||
String name() default "";
|
||||
String function() default "";
|
||||
Type type() default Type.BLOCK;
|
||||
Data data() default @Data;
|
||||
|
||||
|
||||
|
||||
int rotationSteps() default 0;
|
||||
|
||||
|
||||
public enum Type {
|
||||
BLOCK,
|
||||
FUNCTION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotation definitions for {@link net.minecraft.gametest.framework.TestData}
|
||||
*/
|
||||
public @interface Data {
|
||||
String environment() default "";
|
||||
String structure() default "forge:empty3x3";
|
||||
int maxTicks() default Integer.MAX_VALUE;
|
||||
int setupTicks() default 0;
|
||||
boolean required() default false;
|
||||
Rotation rotation() default Rotation.NONE;
|
||||
boolean manualOnly() default false;
|
||||
int maxAttempts() default 1;
|
||||
int requiredSuccesses() default 1;
|
||||
boolean skyAccess() default false;
|
||||
}
|
||||
static final String DEFAULT_STRUCTURE = "forge:empty3x3x3";
|
||||
String environment() default GameTestEnvironments.DEFAULT;
|
||||
String structure() default DEFAULT_STRUCTURE;
|
||||
int maxTicks() default 100;
|
||||
int setupTicks() default 0;
|
||||
boolean required() default true;
|
||||
Rotation rotation() default Rotation.NONE;
|
||||
boolean manualOnly() default false;
|
||||
int maxAttempts() default 1;
|
||||
int requiredSuccesses() default 1;
|
||||
boolean skyAccess() default false;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.gametest;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks a class as containing game tests that should be registered automatically.
|
||||
* All methods annotated with {@link GameTest} or {@link GameTestGenerator} will be registered.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface GameTestHolder {
|
||||
/**
|
||||
* The default batch for all tests in this class that do not manually specify one.
|
||||
* It it recommended that this includes your mod id.
|
||||
* <br/>
|
||||
* This is also used when filtering out which batches to run during automation.
|
||||
* the filters will be tested against the prefix of the batch name. So it is
|
||||
* recommended to be structured in a logical fashion.
|
||||
* <pre>
|
||||
* Examples:
|
||||
* forge.tags.items
|
||||
* forge.tags.blocks
|
||||
* forge.global_loot_modifiers.block_drops
|
||||
* forge.global_loot_modifiers.tool_filter
|
||||
* </pre>
|
||||
*
|
||||
* The filter 'forge.global_loot_modifiers' would execute both the block_drops and tool_filter batches.
|
||||
* <p>
|
||||
* This will default to your modid if it is on the same class that is annotated with {@link net.minecraftforge.fml.common.Mod @Mod}
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* Used as the default namespace to use for {@link GameTest#template() templates} for any game tests in the class.
|
||||
* Defaults to the modid if in a class with @Mod or else defaults to "minecraft"
|
||||
*/
|
||||
String namespace() default "";
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.gametest;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Marks a class as containing game tests that should be registered automatically.
|
||||
* All methods annotated with {@link GameTest} or {@link GameTestGenerator} will be registered.
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface GameTestNamespace {
|
||||
/**
|
||||
* The default namespace for all tests in this class that do not manually specify one.
|
||||
* It it recommended that this is your modid, but if you have multiple test mods that
|
||||
* you want to group together you can use this to do so.
|
||||
* <p>
|
||||
* This will default to your modid if it is on the same class that is annotated with {@link net.minecraftforge.fml.common.Mod @Mod}
|
||||
*/
|
||||
String value() default "";
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.registries;
|
||||
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.RegistrySetBuilder.RegistryBootstrap;
|
||||
import net.minecraft.data.worldgen.BootstrapContext;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraftforge.common.extensions.IForgeRegistrySetBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Utility class to help with managing registry entries.
|
||||
* Maintains a list of all suppliers for entries and registers them during the proper Register event.
|
||||
* Suppliers should return NEW instances every time.
|
||||
*
|
||||
*Example Usage:
|
||||
*<pre>{@code
|
||||
* public static final DeferredRegisterData<ConfiguredFeature<?, ?>> CONFIGURED_FEATURES = DeferredRegisterData.create(Registries.CONFIGURED_FEATURE, MOD_ID);
|
||||
* private static final RegistryObject<ConfiguredFeature<?, ?>> CONFIGURED = CONFIGURED_FEATURES.register("configured", () ->
|
||||
* new ConfiguredFeature<>(Feature.NO_OP, NoneFeatureConfiguration.INSTANCE)
|
||||
* );
|
||||
*
|
||||
* public static final DeferredRegisterData<PlacedFeature> PLACED_FEATURES = DeferredRegisterData.create(Registries.PLACED_FEATURE, MOD_ID);
|
||||
* private static final RegistryObject<PlacedFeature> PLACED = PLACED_FEATURES.register("placed", () ->
|
||||
* new PlacedFeature(CONFIGURED.getHolder().orElseThrow(), List.of())
|
||||
* );
|
||||
*
|
||||
* public static final DeferredRegisterData<BiomeModifier> BIOME_MODIFIERS = DeferredRegisterData.create(ForgeRegistries.Keys.BIOME_MODIFIERS, MOD_ID);
|
||||
* @SuppressWarnings("unused")
|
||||
* private static final RegistryObject<BiomeModifier> MODIFIER = BIOME_MODIFIERS.register("modifier", ctx -> {
|
||||
* return new ForgeBiomeModifiers.AddFeaturesBiomeModifier(
|
||||
* ctx.lookup(Registries.BIOME).getOrThrow(BiomeTags.IS_OVERWORLD),
|
||||
* HolderSet.direct(PLACED.getHolder().orElseThrow()),
|
||||
* GenerationStep.Decoration.UNDERGROUND_ORES
|
||||
* );
|
||||
* });
|
||||
*
|
||||
* protected void generateDataRegistries(GatherDataEvent event) {
|
||||
* var registrySet = VanillaRegistries.builder()
|
||||
* .add(CONFIGURED_FEATURES)
|
||||
* .add(PLACED_FEATURES)
|
||||
* .add(BIOME_MODIFIERS);
|
||||
*
|
||||
* event.getGenerator().addProvider(event.includeServer(), new DatapackBuiltinEntriesProvider(
|
||||
* gen.getPackOutput(), event.getLookupProvider(), registrySet, modid)
|
||||
* );
|
||||
* }
|
||||
*
|
||||
*}</pre>
|
||||
*
|
||||
* @param <T> The base registry type
|
||||
*/
|
||||
public class DeferredRegisterData<T> implements RegistryBootstrap<T> {
|
||||
|
||||
/**
|
||||
* DeferredRegister factory for data driven registries. This will not create objects during the normal registry events.
|
||||
* This is meant to be used as helper for creating a {@link RegistrySetBuilder}, and thus should <string>NOT</strong>
|
||||
* be registered to and {@link EventBus}. But instead used with {@link IForgeRegistrySetBuilder#add(DeferredRegister)}.
|
||||
*
|
||||
* @param registryKey the key of the registry to reference.
|
||||
* @param modid the namespace for all objects registered to this DeferredRegister
|
||||
*/
|
||||
public static <B> DeferredRegisterData.Builder<B> builder(ResourceKey<? extends Registry<B>> registryKey, String modid) {
|
||||
requireNonNull("registryKey", registryKey);
|
||||
requireNonNull("modid", modid);
|
||||
return new Builder<>(registryKey, modid);
|
||||
}
|
||||
|
||||
/**
|
||||
* DeferredRegister factory for data driven registries. This will not create objects during the normal registry events.
|
||||
* This is meant to be used as helper for creating a {@link RegistrySetBuilder}, and thus should <string>NOT</strong>
|
||||
* be registered to and {@link EventBus}. But instead used with {@link IForgeRegistrySetBuilder#add(DeferredRegister)}.
|
||||
*
|
||||
* @param registryKey the key of the registry to reference.
|
||||
* @param modid the namespace for all objects registered to this DeferredRegister
|
||||
*
|
||||
* @return A built instance using the default values for the builder. Equivalent to {@code builder(registryKey, modid).build()}
|
||||
*/
|
||||
public static <B> DeferredRegisterData<B> create(ResourceKey<? extends Registry<B>> registryKey, String modid) {
|
||||
return new Builder<>(registryKey, modid).build();
|
||||
}
|
||||
|
||||
public static class Builder<T> {
|
||||
private final ResourceKey<? extends Registry<T>> registryKey;
|
||||
private final String modid;
|
||||
|
||||
private Lifecycle lifecycle = Lifecycle.stable();
|
||||
|
||||
private Builder(ResourceKey<? extends Registry<T>> registryKey, String modid) {
|
||||
this.registryKey = registryKey;
|
||||
this.modid = modid;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default will register objects as {@link Lifecycle#stable()}
|
||||
*/
|
||||
public Builder<T> lifecycle(Lifecycle value) {
|
||||
this.lifecycle = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DeferredRegisterData<T> build() {
|
||||
return new DeferredRegisterData<>(this);
|
||||
}
|
||||
}
|
||||
|
||||
private final ResourceKey<? extends Registry<T>> registryKey;
|
||||
private final String modid;
|
||||
private final Lifecycle lifecycle;
|
||||
private final Map<RegistryObject<T>, Function<BootstrapContext<T>, ? extends T>> entries = new LinkedHashMap<>();
|
||||
private final Set<RegistryObject<T>> entriesView = Collections.unmodifiableSet(entries.keySet());
|
||||
|
||||
private boolean beenBuilt = false;
|
||||
|
||||
private DeferredRegisterData(Builder<T> builder) {
|
||||
this.registryKey = builder.registryKey;
|
||||
this.modid = builder.modid;
|
||||
this.lifecycle = builder.lifecycle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a RegistryObject for the specified name, which will be filled when the {@link RegistrySetBuilder} is built.
|
||||
* This is useful is you need references to entries added not using the {@link DeferredRegisterData} system.
|
||||
*
|
||||
* @param name The entry's name, it will automatically have the modid prefixed.
|
||||
*/
|
||||
public <I extends T> RegistryObject<I> register(final String name) {
|
||||
return registerInternal(name, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new supplier to the list of entries to be registered, and returns a RegistryObject that will be populated with the created entry automatically.
|
||||
* If you need context from other data registries, use {@link DeferredRegisterData#register(String, Function)}
|
||||
*
|
||||
* @param name The new entry's name, it will automatically have the modid prefixed.
|
||||
* @param factory A factory for the new entry, it should return a new instance every time it is called.
|
||||
* @return A RegistryObject that will be updated with when the entries in the registry change.
|
||||
* @see #register(String, Supplier)
|
||||
*/
|
||||
public <I extends T> RegistryObject<I> register(final String name, final Supplier<? extends I> factory) {
|
||||
return register(name, ctx -> factory.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new entry to be registered, the Function will be invoked with the {@link BootstrapContext} which allows lookup of other registry entries.
|
||||
* Returns a RegistryObject that will be populated with the created entry automatically.
|
||||
*
|
||||
* @param name The new entry's name, it will automatically have the modid prefixed.
|
||||
* @param factory A factory for the new entry, it should return a new instance every time it is called.
|
||||
* @return A RegistryObject that will be updated with when the entries in the registry change.
|
||||
*/
|
||||
public <I extends T> RegistryObject<I> register(final String name, final Function<BootstrapContext<T>, ? extends I> factory) {
|
||||
requireNonNull("factory", factory);
|
||||
return registerInternal(name, factory);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <I extends T> RegistryObject<I> registerInternal(final String name, final Function<BootstrapContext<T>, ? extends I> factory) {
|
||||
if (beenBuilt)
|
||||
throw new IllegalStateException("Cannot register new entries to DeferredRegister after it has been used. You should use this in a static context");
|
||||
|
||||
requireNonNull("name", name);
|
||||
var key = ResourceLocation.fromNamespaceAndPath(modid, name);
|
||||
|
||||
RegistryObject<I> ret = new RegistryObject<>(key, this.registryKey, this.modid);
|
||||
|
||||
if (entries.containsKey((RegistryObject<T>)ret))
|
||||
throw new IllegalArgumentException("Duplicate registration " + name);
|
||||
|
||||
entries.put((RegistryObject<T>)ret, factory);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a ResourceKey based on the current modid and provided path as the location and the registry name linked to this DeferredRegister.
|
||||
* To control the namespace, use {@link #key(ResourceLocation)}.
|
||||
*
|
||||
* @see #key(ResourceLocation)
|
||||
*/
|
||||
@NotNull
|
||||
public ResourceKey<T> key(@NotNull String path) {
|
||||
requireNonNull("path", path);
|
||||
return key(ResourceLocation.fromNamespaceAndPath(this.modid, path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a tag key based on the provided resource location and the registry name linked to this DeferredRegister.
|
||||
* To use the current modid as the namespace, use {@link #createTagKey(String)}.
|
||||
*
|
||||
* @see #key(String)
|
||||
*/
|
||||
@NotNull
|
||||
public ResourceKey<T> key(@NotNull ResourceLocation location) {
|
||||
requireNonNull("location", location);
|
||||
return ResourceKey.create(getRegistryKey(), location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a tag key based on the current modid and provided path as the location and the registry name linked to this DeferredRegister.
|
||||
* To control the namespace, use {@link #createTagKey(ResourceLocation)}.
|
||||
*
|
||||
* @see #createTagKey(ResourceLocation)
|
||||
* @see #createOptionalTagKey(String, Set)
|
||||
*/
|
||||
@NotNull
|
||||
public TagKey<T> createTagKey(@NotNull String path) {
|
||||
requireNonNull("path", path);
|
||||
return createTagKey(ResourceLocation.fromNamespaceAndPath(this.modid, path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a tag key based on the provided resource location and the registry name linked to this DeferredRegister.
|
||||
* To use the current modid as the namespace, use {@link #createTagKey(String)}.
|
||||
*
|
||||
* @see #createTagKey(String)
|
||||
* @see #createOptionalTagKey(ResourceLocation, Set)
|
||||
*/
|
||||
@NotNull
|
||||
public TagKey<T> createTagKey(@NotNull ResourceLocation location) {
|
||||
requireNonNull("location", location);
|
||||
return TagKey.create(this.registryKey, location);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The {@link Lifecycle} that objects are registered as.
|
||||
*/
|
||||
public Lifecycle getLifecycle() {
|
||||
return this.lifecycle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The unmodifiable view of registered entries. Useful for bulk operations on all values.
|
||||
*/
|
||||
public Collection<RegistryObject<T>> getEntries() {
|
||||
return this.entriesView;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The registry key stored in this deferred register. Useful for creating new deferred registers based on an existing one.
|
||||
*/
|
||||
public ResourceKey<? extends Registry<T>> getRegistryKey() {
|
||||
return this.registryKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The registry name stored in this deferred register. Useful for creating new deferred registers based on an existing one.
|
||||
*/
|
||||
@NotNull
|
||||
public ResourceLocation getRegistryName() {
|
||||
return this.registryKey.location();
|
||||
}
|
||||
|
||||
private static <T> T requireNonNull(String name, T value) {
|
||||
if (value == null)
|
||||
throw new IllegalArgumentException(name + " can not be null");
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the key is registered to a valid object factory, useful for data generation knowing if this actually declares data.
|
||||
*/
|
||||
public boolean isDeclared(ResourceKey<T> key) {
|
||||
for (var entry : this.entries.entrySet()) {
|
||||
if (entry.getKey().getKey() == key)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build this DeferredRegister using the supplied {@link BoostrapContext}. This is meant to be called by {@link RegistrySetBuilder} by adding this
|
||||
* function to the builder using {@link RegistrySetBuilder#add(ResourceKey, RegistryBootstrap)} or {@link IForgeRegistrySetBuilder#add(DeferredRegisterData)}
|
||||
*/
|
||||
@Override
|
||||
public void run(BootstrapContext<T> context) {
|
||||
beenBuilt = true;
|
||||
|
||||
var lookup = context.lookup(this.registryKey);
|
||||
|
||||
for (var e : entries.entrySet()) {
|
||||
var ro = e.getKey();
|
||||
T value;
|
||||
if (e.getValue() != null) {
|
||||
value = e.getValue().apply(context);
|
||||
context.register(ro.getKey(), value, this.lifecycle);
|
||||
} else
|
||||
value = lookup.get(ro.getKey()).map(Holder::get).orElse(null);
|
||||
|
||||
ro.updateReference(value, () -> lookup.getOrThrow(ro.getKey()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,6 +175,15 @@ public final class RegistryObject<T> implements Supplier<T> {
|
||||
this.updateReference(registryName);
|
||||
}
|
||||
|
||||
/** Internal constructor used for {@link DeferredRegisterData} */
|
||||
RegistryObject(final ResourceLocation name, final ResourceKey<? extends Registry<? super T>> registry, final String modid) {
|
||||
this.name = name;
|
||||
@SuppressWarnings("unchecked")
|
||||
var typed = (ResourceKey<Registry<T>>)registry;
|
||||
this.key = ResourceKey.create(typed, name);
|
||||
this.optionalRegistry = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the wrapped object in the registry.
|
||||
* This value will automatically be updated when the backing registry is updated.
|
||||
@@ -265,6 +274,11 @@ public final class RegistryObject<T> implements Supplier<T> {
|
||||
this.value = null;
|
||||
}
|
||||
|
||||
void updateReference(T value, Supplier<Holder<T>> holder) {
|
||||
this.value = value;
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
private static boolean registryExists(ResourceLocation registryName) {
|
||||
return RegistryManager.ACTIVE.getRegistry(registryName) != null
|
||||
|| BuiltInRegistries.REGISTRY.containsKey(registryName);
|
||||
|
||||
Reference in new issue
Block a user