Skip to content

Commit 31e3457

Browse files
committed
start work on separate API JAR
1 parent a8ee30b commit 31e3457

20 files changed

+465
-3
lines changed

api/build.gradle.kts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
plugins {
2+
`java-library`
3+
}
4+
5+
repositories {
6+
mavenCentral()
7+
}
8+
9+
dependencies {
10+
implementation("org.slf4j:slf4j-api:2.0.17")
11+
implementation("org.jspecify:jspecify:1.0.0")
12+
}
13+
14+
java {
15+
toolchain {
16+
languageVersion.set(JavaLanguageVersion.of(21))
17+
}
18+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package dev.isxander.controlify.api;
2+
3+
public record CIdentifier(String namespace, String path) {
4+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package dev.isxander.controlify.api;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
/**
9+
* Annotated on {@link Object} to indicate that the object is a
10+
* <code>Component</code>/<code>Text</code> and can be safely cast.
11+
* If annotated on a parameter, it indicates that the method expects the parameter
12+
* to be a Minecraft Component/Text, and will result in a ClassCastException if it is not.
13+
* <p>
14+
* This is required because this API is designed without a direct dependency to Minecraft.
15+
*/
16+
@Retention(RetentionPolicy.SOURCE)
17+
@Target({
18+
ElementType.TYPE_USE,
19+
ElementType.PARAMETER
20+
})
21+
public @interface MinecraftComponent {
22+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package dev.isxander.controlify.api.v1;
2+
3+
import dev.isxander.controlify.api.v1.bindings.BuiltinBindings;
4+
import dev.isxander.controlify.api.v1.widgetguide.WidgetGuideApi;
5+
6+
import java.util.Optional;
7+
8+
public interface ControlifyApi {
9+
WidgetGuideApi widgetGuide();
10+
11+
BuiltinBindings builtinBindings();
12+
13+
/**
14+
* Checks if the current input mode is controller mode.
15+
* This is always true when the user uses "Mixed Input" mode, or true
16+
* when the controller was the last used input device.
17+
* @return true if the current input mode is controller mode.
18+
*/
19+
boolean isControllerMode();
20+
21+
static Optional<ControlifyApi> get() {
22+
return Optional.ofNullable(ControlifyApiLoader.get());
23+
}
24+
25+
static boolean isControlifyInstalled() {
26+
return get().isPresent();
27+
}
28+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package dev.isxander.controlify.api.v1;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
6+
import java.util.ServiceLoader;
7+
8+
final class ControlifyApiLoader {
9+
private static final Logger LOGGER = LoggerFactory.getLogger("Controlify API Loader");
10+
private static ControlifyApi INSTANCE = null;
11+
12+
static ControlifyApi get() {
13+
if (INSTANCE == null) {
14+
INSTANCE = ServiceLoader.load(ControlifyApi.class)
15+
.findFirst()
16+
.orElseGet(() -> {
17+
LOGGER.warn("Controlify API implementation not found. Controlify not installed?");
18+
return null;
19+
});
20+
}
21+
return INSTANCE;
22+
}
23+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package dev.isxander.controlify.api.v1;
2+
3+
/**
4+
* Represents a specific game controller.
5+
* This could be a gamepad, or a generic joystick device.
6+
* <p>
7+
* Each controller has different capabilities and button layouts.
8+
*/
9+
public interface ControlifyController {
10+
11+
/**
12+
* Gets the unique identifier for this controller.
13+
* This identifier is stable and will be the same across sessions for the same
14+
* physical controller. If there are multiple controllers of the same type connected,
15+
* they will have different UIDs, the first to connect will have the same UID across sessions.
16+
* @return the unique identifier for this controller.
17+
*/
18+
String uid();
19+
20+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package dev.isxander.controlify.api.v1.bindings;
2+
3+
public interface BuiltinBindings {
4+
InputBindingSupplier walkForward();
5+
InputBindingSupplier walkBackward();
6+
InputBindingSupplier walkLeft();
7+
InputBindingSupplier walkRight();
8+
9+
InputBindingSupplier lookUp();
10+
InputBindingSupplier lookDown();
11+
InputBindingSupplier lookLeft();
12+
InputBindingSupplier lookRight();
13+
14+
InputBindingSupplier jump();
15+
InputBindingSupplier sprint();
16+
InputBindingSupplier sneak();
17+
18+
InputBindingSupplier attack();
19+
InputBindingSupplier use();
20+
21+
InputBindingSupplier dropIngame();
22+
InputBindingSupplier dropStackIngame();
23+
24+
InputBindingSupplier pause();
25+
26+
InputBindingSupplier changePerspective();
27+
InputBindingSupplier swapHands();
28+
29+
InputBindingSupplier nextHotbarSlot();
30+
InputBindingSupplier previousHotbarSlot();
31+
32+
InputBindingSupplier openInventory();
33+
34+
InputBindingSupplier containerSelect();
35+
InputBindingSupplier containerQuickMove();
36+
InputBindingSupplier containerTakeHalf();
37+
InputBindingSupplier containerDrop();
38+
39+
InputBindingSupplier pickBlock();
40+
InputBindingSupplier pickBlockNbt();
41+
42+
InputBindingSupplier openChat();
43+
InputBindingSupplier toggleHudVisibility();
44+
InputBindingSupplier playerList();
45+
InputBindingSupplier takeScreenshot();
46+
47+
InputBindingSupplier guiPress();
48+
InputBindingSupplier guiBack();
49+
InputBindingSupplier guiNextTab();
50+
InputBindingSupplier guiPreviousTab();
51+
InputBindingSupplier guiAbstract1();
52+
InputBindingSupplier guiAbstract2();
53+
InputBindingSupplier guiAbstract3();
54+
InputBindingSupplier guiNavigateUp();
55+
InputBindingSupplier guiNavigateDown();
56+
InputBindingSupplier guiNavigateLeft();
57+
InputBindingSupplier guiNavigateRight();
58+
InputBindingSupplier guiSecondaryNavigateUp();
59+
InputBindingSupplier guiSecondaryNavigateDown();
60+
InputBindingSupplier guiSecondaryNavigateLeft();
61+
InputBindingSupplier guiSecondaryNavigateRight();
62+
63+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package dev.isxander.controlify.api.v1.bindings;
2+
3+
import dev.isxander.controlify.api.CIdentifier;
4+
import dev.isxander.controlify.api.MinecraftComponent;
5+
6+
public interface InputBinding {
7+
CIdentifier id();
8+
9+
@MinecraftComponent Object glyphIcon();
10+
11+
/**
12+
* Gets the current analogue value of this binding.
13+
* In the range [0.0, 1.0].
14+
* <p>
15+
* When the underlying binding is digital,
16+
* this returns 1.0 if the digital value is true, otherwise 0.
17+
*/
18+
float analogueNow();
19+
20+
/**
21+
* Gets the previous tick analogue value of this binding.
22+
* In the range [0.0, 1.0].
23+
* <p>
24+
* When the underlying binding is digital,
25+
* this returns 1.0 if the digital value is true, otherwise 0.
26+
*/
27+
float analoguePrev();
28+
29+
/**
30+
* Gets the current digital value of this binding.
31+
* <p>
32+
* When the underlying binding is analogue,
33+
* this returns true if the analogue value is greater than the configured threshold.
34+
*/
35+
boolean digitalNow();
36+
37+
/**
38+
* Gets the previous tick digital value of this binding.
39+
* <p>
40+
* When the underlying binding is analogue,
41+
* this returns true if the analogue value is greater than the configured threshold.
42+
*/
43+
boolean digitalPrev();
44+
45+
/**
46+
* Whether the binding was just pressed this tick.
47+
* <p>
48+
* Equivalent to calling
49+
* <pre><code>
50+
* binding.digitalNow() && !binding.digitalPrev()
51+
* </code></pre>
52+
*/
53+
boolean justPressed();
54+
55+
/**
56+
* Whether the binding was just released this tick.
57+
* <p>
58+
* Equivalent to calling
59+
* <pre><code>
60+
* !binding.digitalNow() && binding.digitalPrev()
61+
* </code></pre>
62+
*/
63+
boolean justReleased();
64+
65+
/**
66+
* Whether the binding was just tapped (pressed and released).
67+
*/
68+
boolean justTapped();
69+
70+
/**
71+
* Similar to {@link #justTapped()}, but with infinite max hold time,
72+
* and is canceled by GUI navigation.
73+
* <p>
74+
* In practice, this means if a user held down the binding when hovering a button,
75+
* then whilst still holding it, navigated to another button, and then released, this would not activate.
76+
*/
77+
boolean guiPressed();
78+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package dev.isxander.controlify.api.v1.bindings;
2+
3+
import dev.isxander.controlify.api.CIdentifier;
4+
import org.jspecify.annotations.NonNull;
5+
6+
public interface InputBindingBuilder {
7+
/**
8+
* Sets the unique identifier for the binding.
9+
* This is a required field. The namespace should be your mod's ID.
10+
* <p>
11+
* The id is used to automatically generate the translation keys for the name and description.
12+
* <ul>
13+
* <li>Name: <code>controlify.binding.(namespace).(path)</code></li>
14+
* <li>Desc: <code>controlify.binding.(namespace).(path).desc</code>, optional</li>
15+
* </ul>
16+
* This is the ID referred to in {@link InputBindingSupplier#bindId()}.
17+
*
18+
* @param rl the binding's id
19+
* @return this builder
20+
*/
21+
InputBindingBuilder id(@NonNull CIdentifier rl);
22+
23+
/**
24+
* Sets the unique identifier for the binding.
25+
* This is a required field. The namespace should be your mod's ID.
26+
* <p>
27+
* The id is used to automatically generate the translation keys for the name and description.
28+
* <ul>
29+
* <li>Name: <code>controlify.binding.(namespace).(path)</code></li>
30+
* <li>Desc: <code>controlify.binding.(namespace).(path).desc</code>, optional</li>
31+
* </ul>
32+
* This is the ID referred to in {@link InputBindingSupplier#bindId()}.
33+
*
34+
* @param namespace namespace of {@link CIdentifier}
35+
* @param path path of {@link CIdentifier}
36+
* @return this builder
37+
*/
38+
default InputBindingBuilder id(@NonNull String namespace, @NonNull String path) {
39+
return this.id(new CIdentifier(namespace, path));
40+
}
41+
42+
/**
43+
* Sets the category for the binding. <strong>Required field.</strong>
44+
* This is used to group the binding in the option menu.
45+
* It's recommended that you use a single category for all of your mod's bindings.
46+
*
47+
* @param category the category
48+
* @return this builder
49+
*/
50+
InputBindingBuilder category(@NonNull InputBindingCategory category);
51+
52+
/**
53+
* Registers this binding as a radial menu candidate with the given icon.
54+
* @param icon the radial icon
55+
* @return this builder
56+
*/
57+
InputBindingBuilder radialCandidate(@NonNull RadialIcon icon);
58+
59+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package dev.isxander.controlify.api.v1.bindings;
2+
3+
import dev.isxander.controlify.api.CIdentifier;
4+
5+
public record InputBindingCategory(CIdentifier id) {
6+
}

0 commit comments

Comments
 (0)