diff --git a/README.md b/README.md index b240f6b..1b9fe58 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ repositories { } dependencies { - implementation("com.github.YOUR_USERNAME:msmp-lib:VERSION") + implementation("com.github.MinecraftPlayground:msmp-lib-mod:VERSION") } ``` @@ -34,20 +34,20 @@ Payloads are simple records that describe the data sent and received over MSMP. Each payload needs a `Codec` for serialization and a `Schema` for MSMP discovery. ```java -public record PingPayload(String message) { +public record EchoPayload(String message) { - public static final Codec CODEC = RecordCodecBuilder.create(i -> i.group( - Codec.STRING.fieldOf("message").forGetter(PingPayload::message) - ).apply(i, PingPayload::new)); + public static final Codec CODEC = RecordCodecBuilder.create(i -> i.group( + Codec.STRING.fieldOf("message").forGetter(EchoPayload::message) + ).apply(i, EchoPayload::new)); - public static final Schema SCHEMA = Schema.record(CODEC) + public static final Schema SCHEMA = Schema.record(CODEC) .withField("message", Schema.STRING_SCHEMA); } ``` ### 2. Register methods and notifications -Create a `MsmpNamespace` and register methods and notifications in `onInitialize()` — +Create a `MSMPNamespace` and register methods and notifications in `onInitialize()` - before the server starts and the registry is frozen. Use `attach()` and `detach()` in the server lifecycle events to bind and release @@ -56,26 +56,34 @@ the server instance for use in method handlers. ```java public class MyMod implements ModInitializer { - private static final MsmpNamespace NS = new MsmpNamespace("my_mod"); + private static final MSMPNamespace NS = new MSMPNamespace("my_mod"); - // Registered immediately as static fields — before the server starts - private static final MsmpNotification PING = - NS.notification("ping", PingPayload.SCHEMA, "A ping notification"); + // Registered as a field - before the server starts + private static final MSMPNotification PING = + NS.notification("ping") + .responseSchema(EchoPayload.SCHEMA) + .description("A ping notification") + .register(); - private static MsmpServer msmp; + private static MSMPServer msmp; @Override public void onInitialize() { - // Methods are registered here — registry is still open - NS.method("echo", - EchoPayload.SCHEMA, - EchoPayload.SCHEMA, - "Echoes a message back to the client", - (server, params, client) -> { + // Register a method that receives a parameter + NS.method("echo") + .requestSchema(EchoPayload.SCHEMA) + .responseSchema(EchoPayload.SCHEMA) + .description("Echoes a message back to the client") + .register((server, client, params) -> { System.out.println("Called by connection: " + client.connectionId()); return params; - } - ); + }); + + // Register a method without parameters + NS.method("get_time") + .responseSchema(TimePayload.SCHEMA) + .description("Returns the current game time") + .register((server, client) -> new TimePayload(server.getGameTime())); ServerLifecycleEvents.SERVER_STARTED.register(server -> { NS.attach(server); // bind server for use in method handlers @@ -90,52 +98,84 @@ public class MyMod implements ModInitializer { // Broadcast a notification to all connected clients public static void sendPing() { - if (msmp != null) msmp.send(PING, new PingPayload("hello")); + if (msmp != null) msmp.send(PING, new EchoPayload("hello")); } } ``` +### Method types + +**Methods with request and response payload:** + +```java +NS.method("echo") + .requestSchema(EchoPayload.SCHEMA) + .responseSchema(EchoPayload.SCHEMA) + .description("Echoes a message back to the client") + .register((server, client, params) -> { + System.out.println("Received: " + params.message()); + return params; + }); +``` + +**Parameterless methods (no request payload):** + +```java +NS.method("get_time") + .responseSchema(TimePayload.SCHEMA) + .description("Returns the current game time") + .register((server, client) -> new TimePayload(server.getGameTime())); +``` + ### Handler parameters -The method handler receives three parameters: +The method handler signature depends on whether the method expects parameters: + +**With parameters:** `(server, client, params) -> result` -| Parameter | Type | Description | -|---|---|---| -| `server` | `MinecraftServer` | The running Minecraft server instance | -| `params` | `Param` | The payload received from the client | -| `client` | `ClientInfo` | Info about the calling client, including `connectionId()` | +| Parameter | Type | Description | +|-----------|-------------------|:--------------------------------------| +| `server` | `MinecraftServer` | The running Minecraft server instance | +| `client` | `ClientInfo` | Info about the calling client | +| `params` | `Param` | The payload received from the client | + +**Without parameters:** `(server, client) -> result` + +| Parameter | Type | Description | +|-----------|-------------------|:--------------------------------------| +| `server` | `MinecraftServer` | The running Minecraft server instance | +| `client` | `ClientInfo` | Info about the calling client | ### Sending notifications **Broadcast to all connected clients:** ```java -msmp.send(PING, new PingPayload("hello")); +msmp.send(PING, new EchoPayload("hello")); ``` **Send to a specific client** using the `connectionId` from a method handler: ```java -NS.method("echo", - EchoPayload.SCHEMA, - EchoPayload.SCHEMA, - "Echoes a message back to the calling client only", - (server, params, client) -> { - msmp.sendTo(client.connectionId(), PING, new PingPayload("only for you")); - return params; - } -); +NS.method("notify_client") + .requestSchema(RequestPayload.SCHEMA) + .responseSchema(ResponsePayload.SCHEMA) + .description("Sends a notification only to the calling client") + .register((server, client, params) -> { + msmp.sendTo(client.connectionId(), PING, new EchoPayload("only for you")); + return new ResponsePayload("sent"); + }); ``` ### JSON-RPC examples -**Calling a method** (`my_mod:method/echo`): +**Calling a method** (`my_mod:echo`): ```json { "jsonrpc": "2.0", "id": 1, - "method": "my_mod:method/echo", + "method": "my_mod:echo", "params": [{ "message": "hello" }] diff --git a/gradle.properties b/gradle.properties index 861c272..d1c654f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ loom_version=1.16-SNAPSHOT # check this on https://fabricmc.net/develop/ # Mod Properties -mod_version=1.4.0 +mod_version=2.0.0 maven_group=dev.loat archives_base_name=msmp-lib-mod diff --git a/src/main/java/dev/loat/msmp/MSMPMethod.java b/src/main/java/dev/loat/msmp/MSMPMethod.java index 65a2bef..01e4e5d 100644 --- a/src/main/java/dev/loat/msmp/MSMPMethod.java +++ b/src/main/java/dev/loat/msmp/MSMPMethod.java @@ -6,60 +6,83 @@ import net.minecraft.server.jsonrpc.IncomingRpcMethod; import net.minecraft.server.jsonrpc.api.Schema; - /** * Represents an incoming MSMP method that can be called by connected clients. * - *

Instances are created via - * {@link MSMPNamespace#method(String, Schema, Schema, java.util.function.BiFunction)} or - * {@link MSMPNamespace#method(String, Schema, Schema, String, java.util.function.BiFunction)} + *

Instances are created via {@link dev.loat.msmp.builder.MethodBuilderWithSchemas#register(MSMPMethodHandler)} + * or {@link dev.loat.msmp.builder.MethodBuilderWithoutParameters#register(MSMPMethodHandlerWithoutParameters)} * and are registered in the MSMP registry immediately upon creation.

* *
{@code
- * msmp.namespace("my_mod")
- *     .method("get_time",
- *         VoidPayload.SCHEMA,
- *         TimePayload.SCHEMA,
- *         "Returns the current game time",
- *         (server, params) -> new TimePayload(server.getGameTime())
- *     );
+ * // With request payload:
+ * NS.method("echo")
+ *     .requestSchema(EchoPayload.SCHEMA)
+ *     .responseSchema(EchoPayload.SCHEMA)
+ *     .description("Echoes a message back to the client")
+ *     .register((server, client, params) -> params);
+ *
+ * // Without request payload:
+ * NS.method("get_time")
+ *     .responseSchema(TimePayload.SCHEMA)
+ *     .description("Returns the current game time")
+ *     .register((server, client) -> new TimePayload(server.getGameTime()));
  * }
* - * @param The type of the payload received from the client + * @param The type of the payload received from the client, or {@link Void} for methods without parameters * @param The type of the payload returned to the client */ public final class MSMPMethod { /** - * Creates and registers a new incoming method under the given namespace. - * Package-private — use - * {@link MSMPNamespace#method(String, Schema, Schema, java.util.function.BiFunction)} or - * {@link MSMPNamespace#method(String, Schema, Schema, String, java.util.function.BiFunction)}. + * Creates and registers a new incoming method. * - * @param namespace The namespace to register this method under (e.g. {@code "my_mod"}) - * @param name The name of this method (e.g. {@code "get_time"}), - * resulting in the identifier {@code namespace:name} + * @param namespace The namespace to register this method under + * @param name The name of this method * @param paramSchema The schema describing the payload received from the client * @param resultSchema The schema describing the payload returned to the client * @param description A human-readable description of this method - * @param handler The function to invoke when this method is called by a client + * @param rpcFunction The raw RPC function to invoke when this method is called */ @SuppressWarnings("unchecked") - MSMPMethod( + public MSMPMethod( String namespace, String name, Schema paramSchema, Schema resultSchema, String description, - IncomingRpcMethod.RpcMethodFunction handler + IncomingRpcMethod.RpcMethodFunction rpcFunction + ) { + Identifier id = Identifier.fromNamespaceAndPath(namespace, name); + ((IncomingRpcMethodBuilderAccessor) + IncomingRpcMethod.method(rpcFunction) + .description(description) + .param(name, paramSchema) + .response(name, resultSchema) + ).invokeRegister(BuiltInRegistries.INCOMING_RPC_METHOD, id); + } + + /** + * Creates and registers a new incoming method without parameters. + * + * @param namespace The namespace to register this method under + * @param name The name of this method + * @param resultSchema The schema describing the payload returned to the client + * @param description A human-readable description of this method + * @param rpcFunction The raw RPC function without parameters to invoke when this method is called + */ + @SuppressWarnings("unchecked") + public MSMPMethod( + String namespace, + String name, + Schema resultSchema, + String description, + IncomingRpcMethod.ParameterlessRpcMethodFunction rpcFunction ) { Identifier id = Identifier.fromNamespaceAndPath(namespace, name); - - ((IncomingRpcMethodBuilderAccessor) IncomingRpcMethod - .method(handler) - .description(description) - .param(name, paramSchema) - .response(name, resultSchema) + ((IncomingRpcMethodBuilderAccessor) + IncomingRpcMethod.method(rpcFunction) + .description(description) + .response(name, resultSchema) ).invokeRegister(BuiltInRegistries.INCOMING_RPC_METHOD, id); } } diff --git a/src/main/java/dev/loat/msmp/MSMPMethodHandler.java b/src/main/java/dev/loat/msmp/MSMPMethodHandler.java index 6bb75c3..f879f2d 100644 --- a/src/main/java/dev/loat/msmp/MSMPMethodHandler.java +++ b/src/main/java/dev/loat/msmp/MSMPMethodHandler.java @@ -20,10 +20,10 @@ public interface MSMPMethodHandler { * Handles an incoming method call from a connected client. * * @param server The running {@link MinecraftServer} instance - * @param params The payload received from the client * @param client The {@link ClientInfo} of the calling client, * providing access to the client's {@link ClientInfo#connectionId()} + * @param params The payload received from the client * @return The payload to return to the client */ - Result apply(MinecraftServer server, Param params, ClientInfo client); + Result apply(MinecraftServer server, ClientInfo client, Param params); } diff --git a/src/main/java/dev/loat/msmp/MSMPMethodHandlerWithoutParameters.java b/src/main/java/dev/loat/msmp/MSMPMethodHandlerWithoutParameters.java new file mode 100644 index 0000000..ae140b5 --- /dev/null +++ b/src/main/java/dev/loat/msmp/MSMPMethodHandlerWithoutParameters.java @@ -0,0 +1,24 @@ +package dev.loat.msmp; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.jsonrpc.methods.ClientInfo; + +/** + * Functional interface for handling incoming MSMP method calls without parameters. + * + *

Use this when no request payload is expected from the client.

+ * + * @param The type of the payload returned to the client + */ +@FunctionalInterface +public interface MSMPMethodHandlerWithoutParameters { + + /** + * Handles an incoming method call without parameters from a connected client. + * + * @param server The running {@link MinecraftServer} instance + * @param client The {@link ClientInfo} of the calling client + * @return the payload to return to the client + */ + Result apply(MinecraftServer server, ClientInfo client); +} diff --git a/src/main/java/dev/loat/msmp/MSMPNamespace.java b/src/main/java/dev/loat/msmp/MSMPNamespace.java index d90c1e7..a0e1f7c 100644 --- a/src/main/java/dev/loat/msmp/MSMPNamespace.java +++ b/src/main/java/dev/loat/msmp/MSMPNamespace.java @@ -1,24 +1,31 @@ package dev.loat.msmp; +import dev.loat.msmp.builder.MethodBuilder; +import dev.loat.msmp.builder.NotificationBuilder; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.jsonrpc.api.Schema; - /** * Represents a custom MSMP namespace under which methods and notifications can be registered. * - *

Instances are created directly via {@code new MSMPNamespace("my_mod")} and should be + *

Instances are created directly via {@code new MsmpNamespace("my_mod")} and should be * registered in {@code onInitialize()} before the server starts. Use {@link #attach(MinecraftServer)} * in {@code SERVER_STARTED} to bind the server instance, and {@link #detach()} in * {@code SERVER_STOPPED} to release it.

* *
{@code
- * private static final MSMPNamespace NS = new MSMPNamespace("my_mod");
+ * private static final MsmpNamespace NS = new MsmpNamespace("my_mod");
  *
  * public void onInitialize() {
- *     NS.method("echo", EchoPayload.SCHEMA, EchoPayload.SCHEMA,
- *         (server, params, client) -> params
- *     );
+ *     NS.method("echo")
+ *         .requestSchema(EchoPayload.SCHEMA)
+ *         .responseSchema(EchoPayload.SCHEMA)
+ *         .description("Echoes a message back to the client")
+ *         .register((server, client, params) -> params);
+ *
+ *     NS.notification("ping")
+ *         .responseSchema(PingPayload.SCHEMA)
+ *         .description("A ping notification")
+ *         .register();
  *
  *     ServerLifecycleEvents.SERVER_STARTED.register(server -> NS.attach(server));
  *     ServerLifecycleEvents.SERVER_STOPPED.register(server -> NS.detach());
@@ -61,104 +68,34 @@ public void detach() {
     }
 
     /**
-     * Creates and registers a new outgoing notification without a description.
-     *
-     * @param  The type of the payload sent with this notification
-     * @param name The name of this notification (e.g. {@code "ping"}),
-     * resulting in the identifier {@code namespace:notification/name}
-     * @param schema The schema describing the payload structure
-     * 
-     * @return The registered {@link MSMPNotification}
-     */
-    public  MSMPNotification notification(
-        String name,
-        Schema schema
-    ) {
-        return new MSMPNotification<>(namespace, name, schema, "");
-    }
-
-    /**
-     * Creates and registers a new outgoing notification with a description.
+     * Returns the currently bound {@link MinecraftServer} instance.
+     * Package-private — used by builder classes to resolve the server lazily.
      *
-     * @param  The type of the payload sent with this notification
-     * @param name The name of this notification (e.g. {@code "ping"}),
-     * resulting in the identifier {@code namespace:notification/name}
-     * @param schema The schema describing the payload structure
-     * @param description A human-readable description of this notification
-     * 
-     * @return The registered {@link MSMPNotification}
+     * @return the bound server, or {@code null} if not attached
      */
-    public  MSMPNotification notification(
-        String name,
-        Schema schema,
-        String description
-    ) {
-        return new MSMPNotification<>(namespace, name, schema, description);
+    public MinecraftServer getServer() {
+        return server;
     }
 
     /**
-     * Creates and registers a new incoming method without a description.
-     *
-     * 

The handler is invoked when a client calls this method. The {@link MinecraftServer} - * is resolved lazily at call time — {@link #attach(MinecraftServer)} must have been - * called before any client can invoke this method.

+ * Creates a new method builder for the given name. * - * @param The type of the payload received from the client - * @param The type of the payload returned to the client * @param name The name of this method (e.g. {@code "echo"}), - * resulting in the identifier {@code namespace:name} - * @param paramSchema The schema describing the payload received from the client - * @param resultSchema The schema describing the payload returned to the client - * @param handler The handler to invoke when this method is called by a client - * - * @return The registered {@link MSMPMethod} + * resulting in the identifier {@code namespace:method/name} + * @return a {@link MethodBuilder} to configure and register the method */ - public MSMPMethod method( - String name, - Schema paramSchema, - Schema resultSchema, - MSMPMethodHandler handler - ) { - return new MSMPMethod<>(namespace, name, paramSchema, resultSchema, "", - (api, params, client) -> { - if (server == null) throw new IllegalStateException( - "MSMPNamespace '%s' has no server attached. Call attach(server) in SERVER_STARTED.".formatted(namespace) - ); - return handler.apply(server, params, client); - }); + public MethodBuilder method(String name) { + return new MethodBuilder(this, namespace, name); } /** - * Creates and registers a new incoming method with a description. + * Creates a new notification builder for the given name. * - *

The handler is invoked when a client calls this method. The {@link MinecraftServer} - * is resolved lazily at call time — {@link #attach(MinecraftServer)} must have been - * called before any client can invoke this method.

- * - * @param The type of the payload received from the client - * @param The type of the payload returned to the client - * @param name The name of this method (e.g. {@code "echo"}), - * resulting in the identifier {@code namespace:name} - * @param paramSchema The schema describing the payload received from the client - * @param resultSchema The schema describing the payload returned to the client - * @param description A human-readable description of this method - * @param handler The handler to invoke when this method is called by a client - * - * @return The registered {@link MSMPMethod} + * @param name The name of this notification (e.g. {@code "ping"}), + * resulting in the identifier {@code namespace:notification/name} + * @return a {@link NotificationBuilder} to configure and register the notification */ - public MSMPMethod method( - String name, - Schema paramSchema, - Schema resultSchema, - String description, - MSMPMethodHandler handler - ) { - return new MSMPMethod<>(namespace, name, paramSchema, resultSchema, description, - (api, params, client) -> { - if (server == null) throw new IllegalStateException( - "MSMPNamespace '%s' has no server attached. Call attach(server) in SERVER_STARTED.".formatted(namespace) - ); - return handler.apply(server, params, client); - }); + public NotificationBuilder notification(String name) { + return new NotificationBuilder(namespace, name); } } diff --git a/src/main/java/dev/loat/msmp/MSMPNotification.java b/src/main/java/dev/loat/msmp/MSMPNotification.java index ab67f52..8257378 100644 --- a/src/main/java/dev/loat/msmp/MSMPNotification.java +++ b/src/main/java/dev/loat/msmp/MSMPNotification.java @@ -9,15 +9,16 @@ /** * Represents an outgoing MSMP notification that can be broadcast to all connected clients. * - *

Instances are created via {@link MSMPNamespace#notification(String, Schema)} or - * {@link MSMPNamespace#notification(String, Schema, String)} and are registered + *

Instances are created via {@link MSMPNamespace#notification(String)} and are registered * in the MSMP registry immediately upon creation.

* *

Use {@link MSMPServer#send(MSMPNotification, Object)} to broadcast this notification.

* *
{@code
- * MSMPNotification ping = msmp.namespace("my_mod")
- *     .notification("ping", PingPayload.SCHEMA, "A ping notification");
+ * MSMPNotification ping = NS.notification("ping")
+ *     .responseSchema(PingPayload.SCHEMA)
+ *     .description("A ping notification")
+ *     .register();
  *
  * msmp.send(ping, new PingPayload("hello"));
  * }
@@ -30,17 +31,15 @@ public final class MSMPNotification { /** * Creates and registers a new outgoing notification under the given namespace. - * Package-private — use {@link MSMPNamespace#notification(String, Schema)} or - * {@link MSMPNamespace#notification(String, Schema, String)}. * * @param namespace The namespace to register this notification under (e.g. {@code "my_mod"}) * @param name The name of this notification (e.g. {@code "ping"}), * resulting in the identifier {@code namespace:notification/name} * @param schema The schema describing the payload structure - * @param description A human-readable description of this notification + * @param description a human-readable description of this notification */ @SuppressWarnings("unchecked") - MSMPNotification(String namespace, String name, Schema schema, String description) { + public MSMPNotification(String namespace, String name, Schema schema, String description) { this.holder = ((OutgoingRpcMethodBuilderAccessor) OutgoingRpcMethod .notificationWithParams() .description(description) @@ -54,7 +53,7 @@ public final class MSMPNotification { *

Used internally by {@link MSMPServer#send(MSMPNotification, Object)} * to broadcast this notification to all connected clients.

* - * @return The holder reference to the registered outgoing RPC method + * @return the holder reference to the registered outgoing RPC method */ Holder.Reference> holder() { return holder; diff --git a/src/main/java/dev/loat/msmp/MSMPServer.java b/src/main/java/dev/loat/msmp/MSMPServer.java index 3349775..53acf95 100644 --- a/src/main/java/dev/loat/msmp/MSMPServer.java +++ b/src/main/java/dev/loat/msmp/MSMPServer.java @@ -42,7 +42,7 @@ public final class MSMPServer { *

The {@link ManagementServer} is located via reflection since it is not * publicly accessible on {@link MinecraftServer}.

* - * @param server the running {@link MinecraftServer} instance + * @param server The running {@link MinecraftServer} instance */ public MSMPServer(MinecraftServer server) { this.managementServer = findManagementServer(server); @@ -96,7 +96,7 @@ public void sendTo( * Finds the {@link ManagementServer} instance held by the given {@link MinecraftServer} * by traversing its class hierarchy via reflection. * - * @param server the running {@link MinecraftServer} instance + * @param server The running {@link MinecraftServer} instance * * @return the {@link ManagementServer} instance, or {@code null} if not found */ diff --git a/src/main/java/dev/loat/msmp/builder/MethodBuilder.java b/src/main/java/dev/loat/msmp/builder/MethodBuilder.java new file mode 100644 index 0000000..ecda53a --- /dev/null +++ b/src/main/java/dev/loat/msmp/builder/MethodBuilder.java @@ -0,0 +1,25 @@ +package dev.loat.msmp.builder; + +import dev.loat.msmp.MSMPNamespace; +import net.minecraft.server.jsonrpc.api.Schema; + +public final class MethodBuilder { + + private final MSMPNamespace msmpNamespace; + private final String namespace; + private final String name; + + public MethodBuilder(MSMPNamespace msmpNamespace, String namespace, String name) { + this.msmpNamespace = msmpNamespace; + this.namespace = namespace; + this.name = name; + } + + public MethodBuilderWithParameters requestSchema(Schema schema) { + return new MethodBuilderWithParameters<>(msmpNamespace, namespace, name, schema); + } + + public MethodBuilderWithoutParameters responseSchema(Schema schema) { + return new MethodBuilderWithoutParameters<>(msmpNamespace, namespace, name, schema); + } +} diff --git a/src/main/java/dev/loat/msmp/builder/MethodBuilderWithParameters.java b/src/main/java/dev/loat/msmp/builder/MethodBuilderWithParameters.java new file mode 100644 index 0000000..fefaa08 --- /dev/null +++ b/src/main/java/dev/loat/msmp/builder/MethodBuilderWithParameters.java @@ -0,0 +1,23 @@ +package dev.loat.msmp.builder; + +import dev.loat.msmp.MSMPNamespace; +import net.minecraft.server.jsonrpc.api.Schema; + +public final class MethodBuilderWithParameters { + + private final MSMPNamespace msmpNamespace; + private final String namespace; + private final String name; + private final Schema paramSchema; + + MethodBuilderWithParameters(MSMPNamespace msmpNamespace, String namespace, String name, Schema paramSchema) { + this.msmpNamespace = msmpNamespace; + this.namespace = namespace; + this.name = name; + this.paramSchema = paramSchema; + } + + public MethodBuilderWithSchemas responseSchema(Schema schema) { + return new MethodBuilderWithSchemas<>(msmpNamespace, namespace, name, paramSchema, schema); + } +} diff --git a/src/main/java/dev/loat/msmp/builder/MethodBuilderWithSchemas.java b/src/main/java/dev/loat/msmp/builder/MethodBuilderWithSchemas.java new file mode 100644 index 0000000..3a34802 --- /dev/null +++ b/src/main/java/dev/loat/msmp/builder/MethodBuilderWithSchemas.java @@ -0,0 +1,63 @@ +package dev.loat.msmp.builder; + +import dev.loat.msmp.MSMPMethod; +import dev.loat.msmp.MSMPMethodHandler; +import dev.loat.msmp.MSMPNamespace; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.jsonrpc.IncomingRpcMethod; +import net.minecraft.server.jsonrpc.api.Schema; + +/** + * Final stage of the method builder — both schemas are known. + * + *

Optionally call {@link #description(String)} before calling + * {@link #register(MSMPMethodHandler)} to complete the registration.

+ * + * @param The type of the request payload + * @param The type of the response payload + */ +public final class MethodBuilderWithSchemas { + + private final MSMPNamespace msmpNamespace; + private final String namespace; + private final String name; + private final Schema paramSchema; + private final Schema resultSchema; + private String description = ""; + + MethodBuilderWithSchemas(MSMPNamespace msmpNamespace, String namespace, String name, Schema paramSchema, Schema resultSchema) { + this.msmpNamespace = msmpNamespace; + this.namespace = namespace; + this.name = name; + this.paramSchema = paramSchema; + this.resultSchema = resultSchema; + } + + /** + * Sets the description for this method. + * + * @param description a human-readable description of this method + * @return this builder + */ + public MethodBuilderWithSchemas description(String description) { + this.description = description; + return this; + } + + /** + * Registers the method with a handler that receives the request payload. + * + * @param handler The handler to invoke when this method is called by a client + * @return the registered {@link MSMPMethod} + */ + public MSMPMethod register(MSMPMethodHandler handler) { + IncomingRpcMethod.RpcMethodFunction rpcFunction = (api, params, client) -> { + MinecraftServer server = msmpNamespace.getServer(); + if (server == null) throw new IllegalStateException( + "MSMPNamespace '%s' has no server attached. Call attach(server) in SERVER_STARTED.".formatted(namespace) + ); + return handler.apply(server, client, params); + }; + return new MSMPMethod<>(namespace, name, paramSchema, resultSchema, description, rpcFunction); + } +} diff --git a/src/main/java/dev/loat/msmp/builder/MethodBuilderWithoutParameters.java b/src/main/java/dev/loat/msmp/builder/MethodBuilderWithoutParameters.java new file mode 100644 index 0000000..ad43342 --- /dev/null +++ b/src/main/java/dev/loat/msmp/builder/MethodBuilderWithoutParameters.java @@ -0,0 +1,60 @@ +package dev.loat.msmp.builder; + +import dev.loat.msmp.MSMPMethod; +import dev.loat.msmp.MSMPMethodHandlerWithoutParameters; +import dev.loat.msmp.MSMPNamespace; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.jsonrpc.IncomingRpcMethod; +import net.minecraft.server.jsonrpc.api.Schema; + +/** + * Final stage of the method builder for methods without parameters — the response schema is known. + * + *

Optionally call {@link #description(String)} before calling + * {@link #register(MSMPMethodHandlerWithoutParameters)} to complete the registration.

+ * + * @param The type of the response payload + */ +public final class MethodBuilderWithoutParameters { + + private final MSMPNamespace msmpNamespace; + private final String namespace; + private final String name; + private final Schema resultSchema; + private String description = ""; + + MethodBuilderWithoutParameters(MSMPNamespace msmpNamespace, String namespace, String name, Schema resultSchema) { + this.msmpNamespace = msmpNamespace; + this.namespace = namespace; + this.name = name; + this.resultSchema = resultSchema; + } + + /** + * Sets the description for this method. + * + * @param description a human-readable description of this method + * @return this builder + */ + public MethodBuilderWithoutParameters description(String description) { + this.description = description; + return this; + } + + /** + * Registers the method without parameters. + * + * @param handler The handler to invoke when this method is called by a client + * @return the registered {@link MSMPMethod} + */ + public MSMPMethod register(MSMPMethodHandlerWithoutParameters handler) { + IncomingRpcMethod.ParameterlessRpcMethodFunction rpcFunction = (api, client) -> { + MinecraftServer server = msmpNamespace.getServer(); + if (server == null) throw new IllegalStateException( + "MSMPNamespace '%s' has no server attached. Call attach(server) in SERVER_STARTED.".formatted(namespace) + ); + return handler.apply(server, client); + }; + return new MSMPMethod<>(namespace, name, resultSchema, description, rpcFunction); + } +} diff --git a/src/main/java/dev/loat/msmp/builder/NotificationBuilder.java b/src/main/java/dev/loat/msmp/builder/NotificationBuilder.java new file mode 100644 index 0000000..034e9bf --- /dev/null +++ b/src/main/java/dev/loat/msmp/builder/NotificationBuilder.java @@ -0,0 +1,18 @@ +package dev.loat.msmp.builder; + +import net.minecraft.server.jsonrpc.api.Schema; + +public final class NotificationBuilder { + + private final String namespace; + private final String name; + + public NotificationBuilder(String namespace, String name) { + this.namespace = namespace; + this.name = name; + } + + public NotificationBuilderWithSchema responseSchema(Schema schema) { + return new NotificationBuilderWithSchema<>(namespace, name, schema); + } +} diff --git a/src/main/java/dev/loat/msmp/builder/NotificationBuilderWithSchema.java b/src/main/java/dev/loat/msmp/builder/NotificationBuilderWithSchema.java new file mode 100644 index 0000000..6d6c732 --- /dev/null +++ b/src/main/java/dev/loat/msmp/builder/NotificationBuilderWithSchema.java @@ -0,0 +1,27 @@ +package dev.loat.msmp.builder; + +import dev.loat.msmp.MSMPNotification; +import net.minecraft.server.jsonrpc.api.Schema; + +public final class NotificationBuilderWithSchema { + + private final String namespace; + private final String name; + private final Schema schema; + private String description = ""; + + public NotificationBuilderWithSchema(String namespace, String name, Schema schema) { + this.namespace = namespace; + this.name = name; + this.schema = schema; + } + + public NotificationBuilderWithSchema description(String description) { + this.description = description; + return this; + } + + public MSMPNotification register() { + return new MSMPNotification<>(namespace, name, schema, description); + } +}