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) {
|
public void tick(Minecraft client) {
|
||||||
var minecraft = Minecraft.getInstance();
|
|
||||||
if (minecraft.getOverlay() == null) {
|
if (minecraft.getOverlay() == null) {
|
||||||
if (!calibrationQueue.isEmpty()) {
|
if (!calibrationQueue.isEmpty()) {
|
||||||
Screen screen = minecraft.screen;
|
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.
|
* 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()}
|
* 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.
|
* Makes the button render the image of the binding specified.
|
||||||
* This does not invoke the button press on binding trigger, only renders the guide.
|
* 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 position where the guide should be rendered relative to the button
|
||||||
* @param renderPredicate whether the guide should be rendered
|
* @param renderPredicate whether the guide should be rendered
|
||||||
*/
|
*/
|
||||||
static <T extends AbstractButton> void addGuideToButton(
|
public static <T extends AbstractButton> void addGuideToButton(
|
||||||
T button,
|
T button,
|
||||||
Function<ControllerBindings<?>, ControllerBinding<?>> binding,
|
Function<ControllerBindings<?>, ControllerBinding<?>> binding,
|
||||||
ButtonRenderPosition position,
|
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.ScreenProcessorProvider;
|
||||||
import dev.isxander.controlify.screenop.ScreenProcessor;
|
import dev.isxander.controlify.screenop.ScreenProcessor;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.gui.screens.Screen;
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
@ -21,6 +23,13 @@ public class ScreenMixin implements ScreenProcessorProvider {
|
|||||||
|
|
||||||
@Inject(method = "rebuildWidgets", at = @At("RETURN"))
|
@Inject(method = "rebuildWidgets", at = @At("RETURN"))
|
||||||
private void onScreenInit(CallbackInfo ci) {
|
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) {
|
static ComponentProcessor provide(GuiEventListener component) {
|
||||||
if (component instanceof ComponentProcessorProvider provider)
|
if (component instanceof ComponentProcessorProvider provider)
|
||||||
return provider.componentProcessor();
|
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 net.minecraft.client.gui.screens.Screen;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface ScreenProcessorProvider {
|
public interface ScreenProcessorProvider {
|
||||||
ScreenProcessor<?> screenProcessor();
|
ScreenProcessor<?> screenProcessor();
|
||||||
|
|
||||||
static ScreenProcessor<?> provide(Screen screen) {
|
static ScreenProcessor<?> provide(Screen screen) {
|
||||||
|
Optional<ScreenProcessor<?>> optional = REGISTRY.get(screen);
|
||||||
|
if (optional.isPresent()) return optional.get();
|
||||||
|
|
||||||
return ((ScreenProcessorProvider) screen).screenProcessor();
|
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.AbstractButtonMixin",
|
||||||
"feature.guide.screen.AbstractWidgetMixin",
|
"feature.guide.screen.AbstractWidgetMixin",
|
||||||
"feature.guide.screen.TabNavigationBarMixin",
|
"feature.guide.screen.TabNavigationBarMixin",
|
||||||
|
"feature.screenop.ScreenMixin",
|
||||||
"feature.screenop.vanilla.AbstractButtonMixin",
|
"feature.screenop.vanilla.AbstractButtonMixin",
|
||||||
"feature.screenop.vanilla.AbstractContainerEventHandlerMixin",
|
"feature.screenop.vanilla.AbstractContainerEventHandlerMixin",
|
||||||
"feature.screenop.vanilla.AbstractContainerScreenMixin",
|
"feature.screenop.vanilla.AbstractContainerScreenMixin",
|
||||||
@ -44,7 +45,6 @@
|
|||||||
"feature.screenop.vanilla.LanguageSelectionListEntryMixin",
|
"feature.screenop.vanilla.LanguageSelectionListEntryMixin",
|
||||||
"feature.screenop.vanilla.OptionsSubScreenAccessor",
|
"feature.screenop.vanilla.OptionsSubScreenAccessor",
|
||||||
"feature.screenop.vanilla.ScreenAccessor",
|
"feature.screenop.vanilla.ScreenAccessor",
|
||||||
"feature.screenop.vanilla.ScreenMixin",
|
|
||||||
"feature.screenop.vanilla.SelectWorldScreenAccessor",
|
"feature.screenop.vanilla.SelectWorldScreenAccessor",
|
||||||
"feature.screenop.vanilla.SelectWorldScreenMixin",
|
"feature.screenop.vanilla.SelectWorldScreenMixin",
|
||||||
"feature.screenop.vanilla.ServerSelectionListEntryMixin",
|
"feature.screenop.vanilla.ServerSelectionListEntryMixin",
|
||||||
|
@ -1,17 +1,13 @@
|
|||||||
package dev.isxander.controlify.test;
|
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.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.Minecraft;
|
||||||
|
import net.minecraft.client.Screenshot;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
@ -52,86 +48,21 @@ public class ClientTestHelper {
|
|||||||
return submit(function).join();
|
return submit(function).join();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Controller<?, ?> createFakeController() {
|
public static void takeScreenshot(String name) {
|
||||||
return new Controller<>() {
|
AtomicBoolean returned = new AtomicBoolean(false);
|
||||||
private final ControllerBindings<ControllerState> bindings = new ControllerBindings<>(this);
|
submitAndWait(mc -> {
|
||||||
private final ControllerConfig config = new ControllerConfig() {
|
Screenshot.grab(mc.gameDirectory, name+".png", mc.getMainRenderTarget(), text -> returned.set(true));
|
||||||
@Override
|
return true;
|
||||||
public void setDeadzone(int axis, float deadzone) {
|
});
|
||||||
|
waitFor("Screenshot to be taken", mc -> returned.get(), Duration.ofSeconds(2));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static FakeController createAndUseDummyController() {
|
||||||
public float getDeadzone(int axis) {
|
var controller = new FakeController();
|
||||||
return 0;
|
Controller.CONTROLLERS.put(controller.uid(), controller);
|
||||||
}
|
controller.use();
|
||||||
};
|
return controller;
|
||||||
|
|
||||||
@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() {
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ import java.util.List;
|
|||||||
import static dev.isxander.controlify.test.ClientTestHelper.*;
|
import static dev.isxander.controlify.test.ClientTestHelper.*;
|
||||||
|
|
||||||
public class ControlifyAutoTestClient implements ClientModInitializer {
|
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
|
@Override
|
||||||
public void onInitializeClient() {
|
public void onInitializeClient() {
|
||||||
@ -36,8 +36,8 @@ public class ControlifyAutoTestClient implements ClientModInitializer {
|
|||||||
for (var method : methods) {
|
for (var method : methods) {
|
||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
|
|
||||||
Test.PreLoad preLoad = method.getAnnotation(Test.PreLoad.class);
|
Test.Entrypoint entrypoint = method.getAnnotation(Test.Entrypoint.class);
|
||||||
if (preLoad != null) {
|
if (entrypoint != null) {
|
||||||
if (method.getParameterCount() > 0)
|
if (method.getParameterCount() > 0)
|
||||||
throw new RuntimeException("PreLoad test method " + method.getName() + " has parameters!");
|
throw new RuntimeException("PreLoad test method " + method.getName() + " has parameters!");
|
||||||
|
|
||||||
@ -47,11 +47,11 @@ public class ControlifyAutoTestClient implements ClientModInitializer {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}, preLoad.value()));
|
}, entrypoint.value()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Test.PostLoad postLoad = method.getAnnotation(Test.PostLoad.class);
|
Test.TitleScreen titleScreen = method.getAnnotation(Test.TitleScreen.class);
|
||||||
if (postLoad != null) {
|
if (titleScreen != null) {
|
||||||
if (method.getParameterCount() > 0)
|
if (method.getParameterCount() > 0)
|
||||||
throw new RuntimeException("PostLoad test method " + method.getName() + " has parameters!");
|
throw new RuntimeException("PostLoad test method " + method.getName() + " has parameters!");
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ public class ControlifyAutoTestClient implements ClientModInitializer {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}, postLoad.value()));
|
}, titleScreen.value()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,11 +88,13 @@ public class ControlifyAutoTestClient implements ClientModInitializer {
|
|||||||
private boolean wrapTestExecution(Test test) {
|
private boolean wrapTestExecution(Test test) {
|
||||||
LOGGER.info("\u001b[36mRunning test " + test.name() + "...");
|
LOGGER.info("\u001b[36mRunning test " + test.name() + "...");
|
||||||
try {
|
try {
|
||||||
test.method().run();
|
test.runTest();
|
||||||
LOGGER.info("\u001b[32mPassed test " + test.name() + "!");
|
LOGGER.info("\u001b[32mPassed test " + test.name() + "!");
|
||||||
|
takeScreenshot(test.name());
|
||||||
return true;
|
return true;
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
LOGGER.error("\u001b[31mFailed test " + test.name() + "!", t);
|
LOGGER.error("\u001b[31mFailed test " + test.name() + "!", t);
|
||||||
|
takeScreenshot(test.name());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,10 @@ import dev.isxander.controlify.api.bind.ControlifyBindingsApi;
|
|||||||
import dev.isxander.controlify.api.event.ControlifyEvents;
|
import dev.isxander.controlify.api.event.ControlifyEvents;
|
||||||
import dev.isxander.controlify.bindings.BindingSupplier;
|
import dev.isxander.controlify.bindings.BindingSupplier;
|
||||||
import dev.isxander.controlify.bindings.GamepadBinds;
|
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 net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
@ -15,8 +19,9 @@ import static dev.isxander.controlify.test.ClientTestHelper.*;
|
|||||||
|
|
||||||
public class ControlifyTests {
|
public class ControlifyTests {
|
||||||
BindingSupplier binding = null;
|
BindingSupplier binding = null;
|
||||||
|
boolean titleScreenProcessorPresent = false;
|
||||||
|
|
||||||
@Test.PreLoad("Binding registry test")
|
@Test.Entrypoint("Binding registry test")
|
||||||
void bindingRegistryTest() {
|
void bindingRegistryTest() {
|
||||||
var registry = ControlifyBindingsApi.get();
|
var registry = ControlifyBindingsApi.get();
|
||||||
assertNotNull(registry, "Binding registry is null");
|
assertNotNull(registry, "Binding registry is null");
|
||||||
@ -24,13 +29,14 @@ public class ControlifyTests {
|
|||||||
assertNotNull(binding, "Bind registry failed - BindingSupplier is null");
|
assertNotNull(binding, "Bind registry failed - BindingSupplier is null");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test.PostLoad("BindingSupplier getter test")
|
@Test.TitleScreen("BindingSupplier getter test")
|
||||||
void bindingSupplierGetterTest() {
|
void bindingSupplierGetterTest() {
|
||||||
var controller = createFakeController();
|
var controller = createAndUseDummyController();
|
||||||
assertNotNull(binding.get(controller), "Bind registry failed - Bind for fake controller is null");
|
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() {
|
void checkInputModeChangedEvent() {
|
||||||
var api = ControlifyApi.get();
|
var api = ControlifyApi.get();
|
||||||
|
|
||||||
@ -42,4 +48,20 @@ public class ControlifyTests {
|
|||||||
|
|
||||||
assertTrue(called.get(), "Input mode changed event was not called");
|
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.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
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)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
public @interface PreLoad {
|
public @interface Entrypoint {
|
||||||
String value();
|
String value();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
public @interface PostLoad {
|
public @interface TitleScreen {
|
||||||
String value();
|
String value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user