forked from Clones/Controlify
registry for screenop
This commit is contained in:
@ -124,7 +124,6 @@ public class Controlify implements ControlifyApi {
|
||||
}
|
||||
|
||||
public void tick(Minecraft client) {
|
||||
var minecraft = Minecraft.getInstance();
|
||||
if (minecraft.getOverlay() == null) {
|
||||
if (!calibrationQueue.isEmpty()) {
|
||||
Screen screen = minecraft.screen;
|
||||
|
@ -12,7 +12,7 @@ import java.util.function.Function;
|
||||
* Adds a guide to a button. This does not invoke the button press on binding trigger, only renders the guide.
|
||||
* This should be called every time a button is initialised, like in {@link Screen#init()}
|
||||
*/
|
||||
public interface ButtonGuideApi {
|
||||
public final class ButtonGuideApi {
|
||||
/**
|
||||
* Makes the button render the image of the binding specified.
|
||||
* This does not invoke the button press on binding trigger, only renders the guide.
|
||||
@ -23,7 +23,7 @@ public interface ButtonGuideApi {
|
||||
* @param position where the guide should be rendered relative to the button
|
||||
* @param renderPredicate whether the guide should be rendered
|
||||
*/
|
||||
static <T extends AbstractButton> void addGuideToButton(
|
||||
public static <T extends AbstractButton> void addGuideToButton(
|
||||
T button,
|
||||
Function<ControllerBindings<?>, ControllerBinding<?>> binding,
|
||||
ButtonRenderPosition position,
|
||||
|
@ -1,7 +1,9 @@
|
||||
package dev.isxander.controlify.mixins.feature.screenop.vanilla;
|
||||
package dev.isxander.controlify.mixins.feature.screenop;
|
||||
|
||||
import dev.isxander.controlify.screenop.ComponentProcessorProvider;
|
||||
import dev.isxander.controlify.screenop.ScreenProcessorProvider;
|
||||
import dev.isxander.controlify.screenop.ScreenProcessor;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
@ -21,6 +23,13 @@ public class ScreenMixin implements ScreenProcessorProvider {
|
||||
|
||||
@Inject(method = "rebuildWidgets", at = @At("RETURN"))
|
||||
private void onScreenInit(CallbackInfo ci) {
|
||||
screenProcessor().onWidgetRebuild();
|
||||
// cannot use screenProcessor() because it may be overriden by registry
|
||||
ScreenProcessorProvider.provide((Screen) (Object) this).onWidgetRebuild();
|
||||
}
|
||||
|
||||
@Inject(method = "init(Lnet/minecraft/client/Minecraft;II)V", at = @At("HEAD"))
|
||||
private void clearRegistryCaches(Minecraft client, int width, int height, CallbackInfo ci) {
|
||||
ScreenProcessorProvider.REGISTRY.clearCache();
|
||||
ComponentProcessorProvider.REGISTRY.clearCache();
|
||||
}
|
||||
}
|
@ -8,6 +8,10 @@ public interface ComponentProcessorProvider {
|
||||
static ComponentProcessor provide(GuiEventListener component) {
|
||||
if (component instanceof ComponentProcessorProvider provider)
|
||||
return provider.componentProcessor();
|
||||
return ComponentProcessor.EMPTY;
|
||||
|
||||
return REGISTRY.get(component).orElse(ComponentProcessor.EMPTY);
|
||||
|
||||
}
|
||||
|
||||
Registry<GuiEventListener, ComponentProcessor> REGISTRY = new Registry<>();
|
||||
}
|
||||
|
50
src/main/java/dev/isxander/controlify/screenop/Registry.java
Normal file
50
src/main/java/dev/isxander/controlify/screenop/Registry.java
Normal file
@ -0,0 +1,50 @@
|
||||
package dev.isxander.controlify.screenop;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class Registry<T, U> {
|
||||
private final Map<Class<? extends T>, Function<T, U>> registry;
|
||||
private final Map<T, U> cache;
|
||||
|
||||
public Registry() {
|
||||
this.registry = new HashMap<>();
|
||||
this.cache = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a constructor for a class
|
||||
*
|
||||
* @param clazz the class to bind the constructor to
|
||||
* @param constructor function to build the object from the class
|
||||
* @param <V> type of class
|
||||
*/
|
||||
public <V extends T> void register(Class<V> clazz, Function<V, U> constructor) {
|
||||
registry.put(clazz, (Function<T, U>) constructor);
|
||||
}
|
||||
|
||||
Optional<U> get(T object) {
|
||||
U cached = this.cache.get(object);
|
||||
if (cached != null)
|
||||
return Optional.of(cached);
|
||||
|
||||
Class<? extends T> clazz = (Class<? extends T>) object.getClass();
|
||||
Function<T, U> constructor = registry.get(clazz);
|
||||
if (constructor == null)
|
||||
return Optional.empty();
|
||||
|
||||
U constructed = constructor.apply(object);
|
||||
this.cache.put(object, constructed);
|
||||
return Optional.of(constructed);
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public void clearCache() {
|
||||
this.cache.clear();
|
||||
}
|
||||
}
|
@ -2,10 +2,20 @@ package dev.isxander.controlify.screenop;
|
||||
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface ScreenProcessorProvider {
|
||||
ScreenProcessor<?> screenProcessor();
|
||||
|
||||
static ScreenProcessor<?> provide(Screen screen) {
|
||||
Optional<ScreenProcessor<?>> optional = REGISTRY.get(screen);
|
||||
if (optional.isPresent()) return optional.get();
|
||||
|
||||
return ((ScreenProcessorProvider) screen).screenProcessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a screen processor for a screen from an entrypoint
|
||||
*/
|
||||
Registry<Screen, ScreenProcessor<?>> REGISTRY = new Registry<>();
|
||||
}
|
||||
|
@ -30,6 +30,7 @@
|
||||
"feature.guide.screen.AbstractButtonMixin",
|
||||
"feature.guide.screen.AbstractWidgetMixin",
|
||||
"feature.guide.screen.TabNavigationBarMixin",
|
||||
"feature.screenop.ScreenMixin",
|
||||
"feature.screenop.vanilla.AbstractButtonMixin",
|
||||
"feature.screenop.vanilla.AbstractContainerEventHandlerMixin",
|
||||
"feature.screenop.vanilla.AbstractContainerScreenMixin",
|
||||
@ -44,7 +45,6 @@
|
||||
"feature.screenop.vanilla.LanguageSelectionListEntryMixin",
|
||||
"feature.screenop.vanilla.OptionsSubScreenAccessor",
|
||||
"feature.screenop.vanilla.ScreenAccessor",
|
||||
"feature.screenop.vanilla.ScreenMixin",
|
||||
"feature.screenop.vanilla.SelectWorldScreenAccessor",
|
||||
"feature.screenop.vanilla.SelectWorldScreenMixin",
|
||||
"feature.screenop.vanilla.ServerSelectionListEntryMixin",
|
||||
|
@ -1,17 +1,13 @@
|
||||
package dev.isxander.controlify.test;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import dev.isxander.controlify.bindings.ControllerBindings;
|
||||
import dev.isxander.controlify.controller.Controller;
|
||||
import dev.isxander.controlify.controller.ControllerConfig;
|
||||
import dev.isxander.controlify.controller.ControllerState;
|
||||
import dev.isxander.controlify.controller.ControllerType;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.Screenshot;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@ -52,86 +48,21 @@ public class ClientTestHelper {
|
||||
return submit(function).join();
|
||||
}
|
||||
|
||||
public static Controller<?, ?> createFakeController() {
|
||||
return new Controller<>() {
|
||||
private final ControllerBindings<ControllerState> bindings = new ControllerBindings<>(this);
|
||||
private final ControllerConfig config = new ControllerConfig() {
|
||||
@Override
|
||||
public void setDeadzone(int axis, float deadzone) {
|
||||
public static void takeScreenshot(String name) {
|
||||
AtomicBoolean returned = new AtomicBoolean(false);
|
||||
submitAndWait(mc -> {
|
||||
Screenshot.grab(mc.gameDirectory, name+".png", mc.getMainRenderTarget(), text -> returned.set(true));
|
||||
return true;
|
||||
});
|
||||
waitFor("Screenshot to be taken", mc -> returned.get(), Duration.ofSeconds(2));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDeadzone(int axis) {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public String uid() {
|
||||
return "FAKE";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int joystickId() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControllerBindings<ControllerState> bindings() {
|
||||
return bindings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControllerConfig config() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControllerConfig defaultConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetConfig() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfig(Gson gson, JsonElement json) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControllerType type() {
|
||||
return ControllerType.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "FAKE CONTROLLER";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControllerState state() {
|
||||
return ControllerState.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControllerState prevState() {
|
||||
return ControllerState.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearState() {
|
||||
|
||||
}
|
||||
};
|
||||
public static FakeController createAndUseDummyController() {
|
||||
var controller = new FakeController();
|
||||
Controller.CONTROLLERS.put(controller.uid(), controller);
|
||||
controller.use();
|
||||
return controller;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import java.util.List;
|
||||
import static dev.isxander.controlify.test.ClientTestHelper.*;
|
||||
|
||||
public class ControlifyAutoTestClient implements ClientModInitializer {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger("Controlify Auto Test");
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger("Controlify Auto Test");
|
||||
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
@ -36,8 +36,8 @@ public class ControlifyAutoTestClient implements ClientModInitializer {
|
||||
for (var method : methods) {
|
||||
method.setAccessible(true);
|
||||
|
||||
Test.PreLoad preLoad = method.getAnnotation(Test.PreLoad.class);
|
||||
if (preLoad != null) {
|
||||
Test.Entrypoint entrypoint = method.getAnnotation(Test.Entrypoint.class);
|
||||
if (entrypoint != null) {
|
||||
if (method.getParameterCount() > 0)
|
||||
throw new RuntimeException("PreLoad test method " + method.getName() + " has parameters!");
|
||||
|
||||
@ -47,11 +47,11 @@ public class ControlifyAutoTestClient implements ClientModInitializer {
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}, preLoad.value()));
|
||||
}, entrypoint.value()));
|
||||
}
|
||||
|
||||
Test.PostLoad postLoad = method.getAnnotation(Test.PostLoad.class);
|
||||
if (postLoad != null) {
|
||||
Test.TitleScreen titleScreen = method.getAnnotation(Test.TitleScreen.class);
|
||||
if (titleScreen != null) {
|
||||
if (method.getParameterCount() > 0)
|
||||
throw new RuntimeException("PostLoad test method " + method.getName() + " has parameters!");
|
||||
|
||||
@ -61,7 +61,7 @@ public class ControlifyAutoTestClient implements ClientModInitializer {
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}, postLoad.value()));
|
||||
}, titleScreen.value()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,11 +88,13 @@ public class ControlifyAutoTestClient implements ClientModInitializer {
|
||||
private boolean wrapTestExecution(Test test) {
|
||||
LOGGER.info("\u001b[36mRunning test " + test.name() + "...");
|
||||
try {
|
||||
test.method().run();
|
||||
test.runTest();
|
||||
LOGGER.info("\u001b[32mPassed test " + test.name() + "!");
|
||||
takeScreenshot(test.name());
|
||||
return true;
|
||||
} catch (Throwable t) {
|
||||
LOGGER.error("\u001b[31mFailed test " + test.name() + "!", t);
|
||||
takeScreenshot(test.name());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,10 @@ import dev.isxander.controlify.api.bind.ControlifyBindingsApi;
|
||||
import dev.isxander.controlify.api.event.ControlifyEvents;
|
||||
import dev.isxander.controlify.bindings.BindingSupplier;
|
||||
import dev.isxander.controlify.bindings.GamepadBinds;
|
||||
import dev.isxander.controlify.screenop.ScreenProcessor;
|
||||
import dev.isxander.controlify.screenop.ScreenProcessorProvider;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screens.TitleScreen;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@ -15,8 +19,9 @@ import static dev.isxander.controlify.test.ClientTestHelper.*;
|
||||
|
||||
public class ControlifyTests {
|
||||
BindingSupplier binding = null;
|
||||
boolean titleScreenProcessorPresent = false;
|
||||
|
||||
@Test.PreLoad("Binding registry test")
|
||||
@Test.Entrypoint("Binding registry test")
|
||||
void bindingRegistryTest() {
|
||||
var registry = ControlifyBindingsApi.get();
|
||||
assertNotNull(registry, "Binding registry is null");
|
||||
@ -24,13 +29,14 @@ public class ControlifyTests {
|
||||
assertNotNull(binding, "Bind registry failed - BindingSupplier is null");
|
||||
}
|
||||
|
||||
@Test.PostLoad("BindingSupplier getter test")
|
||||
@Test.TitleScreen("BindingSupplier getter test")
|
||||
void bindingSupplierGetterTest() {
|
||||
var controller = createFakeController();
|
||||
var controller = createAndUseDummyController();
|
||||
assertNotNull(binding.get(controller), "Bind registry failed - Bind for fake controller is null");
|
||||
controller.finish();
|
||||
}
|
||||
|
||||
@Test.PostLoad("Input mode changed event test")
|
||||
@Test.TitleScreen("Input mode changed event test")
|
||||
void checkInputModeChangedEvent() {
|
||||
var api = ControlifyApi.get();
|
||||
|
||||
@ -42,4 +48,20 @@ public class ControlifyTests {
|
||||
|
||||
assertTrue(called.get(), "Input mode changed event was not called");
|
||||
}
|
||||
|
||||
@Test.Entrypoint("Screen component registry setup test")
|
||||
void setupScreenComponentRegistry() {
|
||||
ScreenProcessorProvider.REGISTRY.register(TitleScreen.class, ts -> new ScreenProcessor<>(ts){
|
||||
@Override
|
||||
public void onWidgetRebuild() {
|
||||
super.onWidgetRebuild();
|
||||
titleScreenProcessorPresent = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test.TitleScreen("Screen component registry test")
|
||||
void checkScreenComponentRegistry() {
|
||||
assertTrue(titleScreenProcessorPresent, "Screen processor was not called");
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
package dev.isxander.controlify.test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public record DiscoveredTests(List<Test> entrypointTests, List<Test> titleScreenTests) {
|
||||
public boolean hasRanAllTests() {
|
||||
return entrypointTests.stream().allMatch(Test::hasRan)
|
||||
&& titleScreenTests.stream().allMatch(Test::hasRan);
|
||||
}
|
||||
}
|
@ -0,0 +1,178 @@
|
||||
package dev.isxander.controlify.test;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import dev.isxander.controlify.Controlify;
|
||||
import dev.isxander.controlify.bindings.ControllerBindings;
|
||||
import dev.isxander.controlify.controller.Controller;
|
||||
import dev.isxander.controlify.controller.ControllerType;
|
||||
import dev.isxander.controlify.controller.joystick.JoystickConfig;
|
||||
import dev.isxander.controlify.controller.joystick.JoystickController;
|
||||
import dev.isxander.controlify.controller.joystick.JoystickState;
|
||||
import dev.isxander.controlify.controller.joystick.mapping.JoystickMapping;
|
||||
import dev.isxander.controlify.controller.joystick.mapping.UnmappedJoystickMapping;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class FakeController implements JoystickController<JoystickConfig> {
|
||||
public static int JOYSTICK_COUNT = 0;
|
||||
|
||||
private final String uid;
|
||||
private final int id;
|
||||
private final ControllerBindings<JoystickState> bindings;
|
||||
private final JoystickConfig config;
|
||||
private JoystickState state = JoystickState.EMPTY, prevState = JoystickState.EMPTY;
|
||||
|
||||
private float axisState;
|
||||
private boolean shouldClearAxisNextTick;
|
||||
private boolean buttonState, shouldButtonPressNextTick;
|
||||
private JoystickState.HatState hatState = JoystickState.HatState.CENTERED;
|
||||
private boolean shouldCenterHatNextTick;
|
||||
|
||||
public FakeController() {
|
||||
this.uid = "FAKE-" + JOYSTICK_COUNT++;
|
||||
this.id = -JOYSTICK_COUNT;
|
||||
this.bindings = new ControllerBindings<>(this);
|
||||
this.config = new JoystickConfig(this);
|
||||
this.config.calibrated = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String uid() {
|
||||
return uid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int joystickId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControllerBindings<JoystickState> bindings() {
|
||||
return bindings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JoystickState state() {
|
||||
return state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JoystickState prevState() {
|
||||
return prevState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JoystickConfig config() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JoystickConfig defaultConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetConfig() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfig(Gson gson, JsonElement json) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ControllerType type() {
|
||||
return ControllerType.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "Fake Controller";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateState() {
|
||||
buttonState = shouldButtonPressNextTick;
|
||||
shouldButtonPressNextTick = false;
|
||||
|
||||
state = new FakeControllerState(mapping(), axisState, buttonState, hatState);
|
||||
|
||||
if (shouldClearAxisNextTick) {
|
||||
shouldClearAxisNextTick = false;
|
||||
axisState = 0f;
|
||||
}
|
||||
if (shouldCenterHatNextTick) {
|
||||
shouldCenterHatNextTick = false;
|
||||
hatState = JoystickState.HatState.CENTERED;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearState() {
|
||||
state = JoystickState.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JoystickMapping mapping() {
|
||||
return UnmappedJoystickMapping.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int axisCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int buttonCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hatCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public void setAxis(float axis, boolean clearNextTick) {
|
||||
this.axisState = axis;
|
||||
this.shouldClearAxisNextTick = clearNextTick;
|
||||
}
|
||||
|
||||
public void clearAxisNextTick() {
|
||||
this.shouldClearAxisNextTick = true;
|
||||
}
|
||||
|
||||
public void pressButtonNextTick() {
|
||||
this.shouldButtonPressNextTick = true;
|
||||
}
|
||||
|
||||
public void setHat(JoystickState.HatState hatState, boolean clearNextTick) {
|
||||
this.hatState = hatState;
|
||||
this.shouldCenterHatNextTick = clearNextTick;
|
||||
}
|
||||
|
||||
public void clearHatNextTick() {
|
||||
this.shouldCenterHatNextTick = true;
|
||||
}
|
||||
|
||||
public void use() {
|
||||
Controlify.instance().setCurrentController(this);
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
Controlify.instance().setCurrentController(null);
|
||||
Controller.CONTROLLERS.remove(uid, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeUsed() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static class FakeControllerState extends JoystickState {
|
||||
protected FakeControllerState(JoystickMapping mapping, float axis, boolean button, HatState hat) {
|
||||
super(mapping, List.of(axis), List.of(axis), List.of(button), List.of(hat));
|
||||
}
|
||||
}
|
||||
}
|
@ -4,17 +4,66 @@ import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class Test {
|
||||
private final Runnable method;
|
||||
private final String name;
|
||||
private boolean hasRan;
|
||||
|
||||
public Test(Runnable method, String name) {
|
||||
this.method = method;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void runTest() {
|
||||
if (hasRan)
|
||||
throw new IllegalStateException("Test `" + name + "` cannot run twice.");
|
||||
|
||||
method.run();
|
||||
hasRan = true;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean hasRan() {
|
||||
return hasRan;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
if (obj == null || obj.getClass() != this.getClass()) return false;
|
||||
var that = (Test) obj;
|
||||
return Objects.equals(this.method, that.method) &&
|
||||
Objects.equals(this.name, that.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(method, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Test[" +
|
||||
"method=" + method + ", " +
|
||||
"name=" + name + ']';
|
||||
}
|
||||
|
||||
public record Test(Runnable method, String name) {
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface PreLoad {
|
||||
public @interface Entrypoint {
|
||||
String value();
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface PostLoad {
|
||||
public @interface TitleScreen {
|
||||
String value();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user