Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ dependencies {

implementation(include("org.yaml:snakeyaml:2.2"))

implementation("com.github.MinecraftPlayground:msmp-lib-mod:v1.3.0")
implementation("com.github.MinecraftPlayground:msmp-lib-mod:v1.4.1")
}

processResources {
Expand Down
24 changes: 23 additions & 1 deletion src/main/java/dev/loat/msmp_entity_data/MSMPEntityData.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,42 @@
import dev.loat.msmp.MSMPServer;
import dev.loat.msmp_entity_data.logging.Logger;
import dev.loat.msmp_entity_data.msmp.methods.Methods;
import dev.loat.msmp_entity_data.msmp.notifications.Notifications;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;


/**
* Main entrypoint for the MSMP Entity Data mod.
*
* <p>Initializes the {@code entity_data} MSMP namespace, registers all methods
* and notifications, and manages the server lifecycle binding.</p>
*/
public class MSMPEntityData implements ModInitializer {

/**
* The shared {@code entity_data} namespace used for all MSMP registrations.
* Attached to the running server in {@code SERVER_STARTED} and detached in {@code SERVER_STOPPED}.
*/
private static final MSMPNamespace NS = new MSMPNamespace("entity_data");

/**
* Provides access to the {@link net.minecraft.server.jsonrpc.ManagementServer}
* for broadcasting notifications. {@code null} when no server is running.
*/
private static MSMPServer msmp;

/**
* Called by Fabric during mod initialization, before the server starts.
*
* <p>Registers all MSMP methods and notifications and sets up server
* lifecycle hooks for attaching and detaching the namespace.</p>
*/
@Override
public void onInitialize() {
Logger.setLoggerClass(MSMPEntityData.class);

Methods.register(NS);
Notifications.register(NS, () -> msmp);

ServerLifecycleEvents.SERVER_STARTED.register(server -> {
NS.attach(server);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package dev.loat.msmp_entity_data.logging;

public class RPCConnectionLogger {

public static void debug(Integer connectionId, String message) {
Logger.debug("RPC Connection #%s: %s".formatted(connectionId, message));
}

public static void info(Integer connectionId, String message) {
Logger.info("RPC Connection #%s: %s".formatted(connectionId, message));
}

public static void warning(Integer connectionId, String message) {
Logger.warning("RPC Connection #%s: %s".formatted(connectionId, message));
}

public static void error(Integer connectionId, String message) {
Logger.error("RPC Connection #%s: %s".formatted(connectionId, message));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package dev.loat.msmp_entity_data.msmp.exceptions;

/**
* Thrown when an entity cannot be found by the given UUID or player name.
*/
public class EntityNotFoundException extends MSMPException {

/**
* @param identifier The UUID or name that was used for the lookup
*/
public EntityNotFoundException(String identifier) {
super("Entity not found: " + identifier);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package dev.loat.msmp_entity_data.msmp.exceptions;

/**
* Thrown when a method requires a {@link net.minecraft.world.entity.LivingEntity}
* but the resolved entity is not one.
*/
public class EntityNotLivingException extends MSMPException {

/**
* @param uuid The UUID of the entity that was found but is not a LivingEntity
*/
public EntityNotLivingException(String uuid) {
super("Entity is not a LivingEntity: " + uuid);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package dev.loat.msmp_entity_data.msmp.exceptions;

/**
* Thrown when a method requires a {@link net.minecraft.world.entity.player.Player}
* but the resolved entity is not one.
*/
public class EntityNotPlayerException extends MSMPException {

/**
* @param uuid The UUID of the entity that was found but is not a Player
*/
public EntityNotPlayerException(String uuid) {
super("Entity is not a Player: " + uuid);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package dev.loat.msmp_entity_data.msmp.exceptions;

/**
* Thrown when a method receives invalid or incomplete parameters.
*
* <p>Used for method-specific validation errors, such as missing required
* fields or out-of-range values.</p>
*/
public class InvalidParamsException extends MSMPException {

/**
* @param message A description of which parameter is invalid and why
*/
public InvalidParamsException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package dev.loat.msmp_entity_data.msmp.exceptions;

/**
* Thrown when a provided UUID string cannot be parsed.
*/
public class InvalidUUIDException extends MSMPException {

/**
* @param raw The malformed UUID string
*/
public InvalidUUIDException(String raw) {
super("Invalid UUID: " + raw);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package dev.loat.msmp_entity_data.msmp.exceptions;

/**
* Base exception for all MSMP entity data errors.
*
* <p>All method-specific exceptions extend this class, allowing handlers
* to catch all MSMP errors with a single {@code catch (MSMPException e)} block.</p>
*/
public class MSMPException extends RuntimeException {

/**
* Creates a new {@link MSMPException} with the given message.
*
* @param message A human-readable description of the error
*/
public MSMPException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package dev.loat.msmp_entity_data.msmp.exceptions;

/**
* Thrown when an entity is missing an expected attribute,
* such as {@link net.minecraft.world.entity.ai.attributes.Attributes#MAX_HEALTH}.
*/
public class MissingAttributeException extends MSMPException {

/**
* @param uuid The UUID of the entity
* @param attribute The name of the missing attribute
*/
public MissingAttributeException(String uuid, String attribute) {
super("Entity %s has no %s attribute".formatted(uuid, attribute));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package dev.loat.msmp_entity_data.msmp.exceptions;

/**
* Thrown when a request provides neither {@code id} nor {@code name}
* for entity lookup.
*/
public class MissingIdentifierException extends MSMPException {

public MissingIdentifierException() {
super("Either 'id' or 'name' must be provided");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package dev.loat.msmp_entity_data.msmp.exceptions;

/**
* Thrown when a requested dimension identifier is not registered on the server.
*/
public class UnknownDimensionException extends MSMPException {

/**
* @param dimension The dimension identifier that could not be resolved
*/
public UnknownDimensionException(String dimension) {
super("Unknown dimension: " + dimension);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import dev.loat.msmp.MSMPNamespace;
import dev.loat.msmp_entity_data.msmp.methods.dimension.Dimension;
import dev.loat.msmp_entity_data.msmp.methods.dimension.DimensionSet;
import dev.loat.msmp_entity_data.msmp.methods.dimension.subscribe.DimensionSubscribe;
import dev.loat.msmp_entity_data.msmp.methods.dimension.subscribe.DimensionUnsubscribe;
import dev.loat.msmp_entity_data.msmp.methods.health.Health;
import dev.loat.msmp_entity_data.msmp.methods.health.HealthSet;
import dev.loat.msmp_entity_data.msmp.methods.inventory.Inventory;
Expand Down Expand Up @@ -35,6 +37,8 @@ private Methods() {}
public static void register(MSMPNamespace namespace) {
Dimension.register(namespace);
DimensionSet.register(namespace);
DimensionSubscribe.register(namespace);
DimensionUnsubscribe.register(namespace);
Health.register(namespace);
HealthSet.register(namespace);
Inventory.register(namespace);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package dev.loat.msmp_entity_data.msmp.methods.dimension;

import dev.loat.msmp.MSMPNamespace;
import dev.loat.msmp_entity_data.logging.Logger;
import dev.loat.msmp_entity_data.logging.RPCConnectionLogger;
import dev.loat.msmp_entity_data.msmp.components.EntityResolver;
import net.minecraft.resources.Identifier;
import net.minecraft.resources.ResourceKey;
Expand Down Expand Up @@ -64,12 +64,14 @@ public static void register(MSMPNamespace namespace) {
true
);

RPCConnectionLogger.info(client.connectionId(), "entity_data:dimension/set - teleported %s to dimension %s".formatted(entity.getUUID(), params.dimension()));

return new DimensionResponse(
EntityResolver.toEntityRef(entity),
entity.level().dimension().identifier().toString()
);
} catch (IllegalArgumentException e) {
Logger.warning("entity_data:dimension/set - " + e.getMessage());
RPCConnectionLogger.warning(client.connectionId(), "entity_data:dimension/set - " + e.getMessage());
throw e;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package dev.loat.msmp_entity_data.msmp.methods.dimension.subscribe;

import dev.loat.msmp.MSMPNamespace;
import dev.loat.msmp_entity_data.logging.RPCConnectionLogger;
import dev.loat.msmp_entity_data.msmp.components.EntityRef;
import dev.loat.msmp_entity_data.msmp.components.EntityRequest;
import dev.loat.msmp_entity_data.msmp.components.EntityResolver;
import dev.loat.msmp_entity_data.msmp.subscription.SubscribeRequest;
import dev.loat.msmp_entity_data.msmp.subscription.SubscribeResponse;
import dev.loat.msmp_entity_data.msmp.subscription.SubscriptionManager;
import net.minecraft.world.entity.Entity;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;

public class DimensionSubscribe {

public static void register(MSMPNamespace namespace) {
namespace.method("dimension/subscribe",
SubscribeRequest.SCHEMA,
SubscribeResponse.SCHEMA,
"Subscribe to dimension change notifications for the given entities",
(server, params, client) -> {
if (params.entities().isEmpty()) {
return new SubscribeResponse(List.of());
}

SubscriptionManager manager = SubscriptionManager.get("entity_data:dimension/subscribe");
Set<UUID> uuids = new HashSet<>();
List<EntityRef> resolved = new ArrayList<>();

for (EntityRequest entry : params.entities()) {
try {
Entity entity = EntityResolver.resolveEntity(server, entry);
uuids.add(entity.getUUID());
resolved.add(EntityResolver.toEntityRef(entity));
} catch (IllegalArgumentException e) {
RPCConnectionLogger.warning(client.connectionId(), "entity_data:dimension/subscribe - " + e.getMessage());
throw e;
}
}

manager.subscribe(uuids);
RPCConnectionLogger.info(client.connectionId(), "entity_data:dimension/subscribe - subscribed to %s".formatted(uuids));
return new SubscribeResponse(resolved);
}
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package dev.loat.msmp_entity_data.msmp.methods.dimension.subscribe;

import dev.loat.msmp.MSMPNamespace;
import dev.loat.msmp_entity_data.logging.RPCConnectionLogger;
import dev.loat.msmp_entity_data.msmp.components.EntityRef;
import dev.loat.msmp_entity_data.msmp.components.EntityRequest;
import dev.loat.msmp_entity_data.msmp.components.EntityResolver;
import dev.loat.msmp_entity_data.msmp.subscription.SubscribeRequest;
import dev.loat.msmp_entity_data.msmp.subscription.SubscribeResponse;
import dev.loat.msmp_entity_data.msmp.subscription.SubscriptionManager;
import net.minecraft.world.entity.Entity;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;

public class DimensionUnsubscribe {

public static void register(MSMPNamespace namespace) {
namespace.method("dimension/unsubscribe",
SubscribeRequest.SCHEMA,
SubscribeResponse.SCHEMA,
"Unsubscribe from dimension change notifications for the given entities",
(server, params, client) -> {
if (params.entities().isEmpty()) {
return new SubscribeResponse(List.of());
}

SubscriptionManager manager = SubscriptionManager.get("entity_data:dimension/subscribe");
Set<UUID> uuids = new HashSet<>();
List<EntityRef> resolved = new ArrayList<>();

for (EntityRequest entry : params.entities()) {
try {
Entity entity = EntityResolver.resolveEntity(server, entry);
uuids.add(entity.getUUID());
resolved.add(EntityResolver.toEntityRef(entity));
} catch (IllegalArgumentException e) {
RPCConnectionLogger.warning(client.connectionId(), "entity_data:dimension/unsubscribe - " + e.getMessage());
throw e;
}
}

manager.unsubscribe(uuids);
RPCConnectionLogger.info(client.connectionId(), "entity_data:dimension/unsubscribe - unsubscribed from %s".formatted(uuids));
return new SubscribeResponse(resolved);
}
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import dev.loat.msmp.MSMPNamespace;
import dev.loat.msmp_entity_data.logging.Logger;
import dev.loat.msmp_entity_data.msmp.components.EntityResolver;
import dev.loat.msmp_entity_data.msmp.methods.inventory.InventoryResponse;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;

Expand Down
Loading