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
22 changes: 22 additions & 0 deletions api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
plugins {
`java-library`
`checkstyle`
}

java {
toolchain { languageVersion.set(JavaLanguageVersion.of(21)) }
withSourcesJar()
withJavadocJar()
}

repositories {
mavenCentral()
}

dependencies {
testImplementation(libs.junit.jupiter)
}

tasks.test {
useJUnitPlatform()
}
74 changes: 74 additions & 0 deletions api/src/main/java/dev/objz/commandbridge/api/CommandBridgeAPI.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package dev.objz.commandbridge.api;

import dev.objz.commandbridge.api.channel.ChannelPayload;
import dev.objz.commandbridge.api.channel.ChannelType;
import dev.objz.commandbridge.api.channel.MessageChannel;
import dev.objz.commandbridge.api.message.ServerEventListener;
import dev.objz.commandbridge.api.message.Subscription;
import dev.objz.commandbridge.api.platform.ConnectionState;
import dev.objz.commandbridge.api.platform.Platform;
import dev.objz.commandbridge.api.platform.PlayerLocator;

import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;

/** Main entry point for interacting with the CommandBridge network. */
public interface CommandBridgeAPI {

/**
* Obtains a {@link MessageChannel} for the given {@link ChannelType}.
*
* @param type the channel type identity
* @param <T> the payload type
* @param <C> the channel interface type
* @return the message channel
*/
<T extends ChannelPayload, C extends MessageChannel<T>> C channel(ChannelType<T, C> type);

/**
* Broadcasts a payload to all connected servers on a specific channel.
*
* @param channel the channel to broadcast on
* @param payload the data to send
* @return a future that completes when the broadcast is dispatched
*/
<P extends ChannelPayload> CompletableFuture<Void> broadcast(MessageChannel<P> channel, P payload);

/** @return the identity of the current server */
Platform.ServerTarget server();

/** @return the current connection state to the bridge network */
ConnectionState connectionState();

/** @return the IDs of all currently connected servers, if available */
Optional<Set<String>> connectedServers();

/** @return the player location lookup service, if available */
Optional<PlayerLocator> playerLocator();

/**
* Subscribes to server connection events.
*
* @param listener the listener to call when a server connects
* @return a subscription handle to cancel the listener
*/
Subscription onServerConnected(ServerEventListener listener);

/**
* Subscribes to server disconnection events.
*
* @param listener the listener to call when a server disconnects
* @return a subscription handle to cancel the listener
*/
Subscription onServerDisconnected(ServerEventListener listener);

/**
* Subscribes to connection state changes.
*
* @param listener the listener to call when the state changes
* @return a subscription handle to cancel the listener
*/
Subscription onConnectionStateChanged(Consumer<ConnectionState> listener);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package dev.objz.commandbridge.api;

import java.util.Objects;

/** Static provider for accessing the {@link CommandBridgeAPI} instance. */
public final class CommandBridgeProvider {

private static volatile CommandBridgeAPI instance;

private CommandBridgeProvider() {
throw new UnsupportedOperationException("Provider can't be instanced");
}

/**
* @return the registered API instance
* @throws IllegalStateException if the API is not registered
*/
public static CommandBridgeAPI get() {
if (instance == null) {
throw new IllegalStateException("CommandBridge is not available. Is it installed and running?");
}
return instance;
}

/**
* Obtains the API instance cast to a specific type.
*
* @param type the API class type
* @param <T> the API type
* @return the cast API instance
* @throws IllegalStateException if the instance is not available or compatible
*/
public static <T extends CommandBridgeAPI> T get(Class<T> type) {
CommandBridgeAPI api = get();
if (!type.isInstance(api)) {
throw new IllegalStateException(type.getSimpleName() + " is not available on this platform");
}
return type.cast(api);
}

/**
* Registers the API implementation.
*
* @param impl the implementation to register
* @throws IllegalStateException if an implementation is already registered
*/
public static void register(CommandBridgeAPI impl) {
Objects.requireNonNull(impl);
if (instance != null) {
throw new IllegalStateException("CommandBridge already registered");
}
instance = impl;
}

/** Unregisters the current API implementation. */
public static void unregister() {
instance = null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package dev.objz.commandbridge.api.channel;

/** Marker interface for data sent over a {@link MessageChannel}. */
public interface ChannelPayload {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package dev.objz.commandbridge.api.channel;

import java.util.Objects;

/**
* Identity and type information for a {@link MessageChannel}.
*
* @param <T> the payload type
* @param <C> the channel interface type
*/
public abstract class ChannelType<T extends ChannelPayload, C extends MessageChannel<T>> {

private final Class<T> type;

protected ChannelType(Class<T> type) {
this.type = Objects.requireNonNull(type);
}

/** @return the class of the payload handled by this channel */
public Class<T> type() {
return type;
}
}
14 changes: 14 additions & 0 deletions api/src/main/java/dev/objz/commandbridge/api/channel/Channels.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package dev.objz.commandbridge.api.channel;

import dev.objz.commandbridge.api.channel.command.CommandChannelType;

/** Registry of built-in {@link ChannelType}s. */
public final class Channels {

/** The default channel for executing commands. */
public static final CommandChannelType COMMAND = new CommandChannelType();

private Channels() {
throw new UnsupportedOperationException("Channels can't be instanced");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package dev.objz.commandbridge.api.channel;

import dev.objz.commandbridge.api.message.MessageListener;
import dev.objz.commandbridge.api.message.Subscription;
import dev.objz.commandbridge.api.platform.Platform;

import java.time.Duration;
import java.util.concurrent.CompletableFuture;

/**
* Communication pipe for sending and receiving {@link ChannelPayload}s.
*
* @param <P> the type of payload handled by this channel
*/
public interface MessageChannel<P extends ChannelPayload> {

/**
* Sends a payload to a target server without expecting a response.
*
* @param target the destination server
* @param payload the data to send
* @return a future that completes when the message is sent
*/
CompletableFuture<Void> send(Platform.ServerTarget target, P payload);

/**
* Sends a request to a target server and waits for a response.
*
* @param target the destination server
* @param payload the request data
* @return a future containing the response payload
*/
CompletableFuture<P> request(Platform.ServerTarget target, P payload);

/**
* Sends a request to a target server with a custom timeout.
*
* @param target the destination server
* @param payload the request data
* @param timeout the maximum time to wait for a response
* @return a future containing the response payload
*/
CompletableFuture<P> request(Platform.ServerTarget target, P payload, Duration timeout);

/**
* Subscribes a listener to messages received on this channel.
*
* @param listener the listener to call for incoming messages
* @return a subscription handle to cancel the listener
*/
Subscription listen(MessageListener<P> listener);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package dev.objz.commandbridge.api.channel.command;

import dev.objz.commandbridge.api.channel.MessageChannel;
import dev.objz.commandbridge.api.platform.Platform;

import java.util.UUID;
import java.util.concurrent.CompletableFuture;

/** Specialized channel for dispatching commands to servers. */
public interface CommandChannel extends MessageChannel<CommandPayload> {

/**
* Executes a command as the console.
*
* @param target the destination server
* @param command the command string to run
* @return a future that completes when the command is sent
*/
default CompletableFuture<Void> console(Platform.ServerTarget target, String command) {
return send(target, CommandPayload.console(command));
}

/**
* Executes a command as a specific player.
*
* @param target the destination server
* @param command the command string to run
* @param player the UUID of the player to run as
* @return a future that completes when the command is sent
*/
default CompletableFuture<Void> player(Platform.ServerTarget target, String command, UUID player) {
return send(target, CommandPayload.player(command, player));
}

/**
* Executes a command with operator permissions, bypassing standard checks.
*
* @param target the destination server
* @param command the command string to run
* @param player the UUID of the player to run as
* @return a future that completes when the command is sent
*/
default CompletableFuture<Void> operator(Platform.ServerTarget target, String command, UUID player) {
return send(target, CommandPayload.operator(command, player));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package dev.objz.commandbridge.api.channel.command;

import dev.objz.commandbridge.api.channel.ChannelType;

/** Identity for the {@link CommandChannel}. */
public final class CommandChannelType extends ChannelType<CommandPayload, CommandChannel> {

/** Creates a new command channel type. */
public CommandChannelType() {
super(CommandPayload.class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package dev.objz.commandbridge.api.channel.command;

import dev.objz.commandbridge.api.channel.ChannelPayload;

import java.util.UUID;

/**
* Payload for command execution.
*
* @param command the command string to run
* @param runAs the execution mode
* @param player the optional player context
*/
public record CommandPayload(String command, RunAs runAs, UUID player) implements ChannelPayload {

/** Creates a payload for console execution. */
public static CommandPayload console(String command) {
return new CommandPayload(command, RunAs.CONSOLE, null);
}

/** Creates a payload for player execution. */
public static CommandPayload player(String command, UUID player) {
return new CommandPayload(command, RunAs.PLAYER, player);
}

/** Creates a payload for operator execution. */
public static CommandPayload operator(String command, UUID player) {
return new CommandPayload(command, RunAs.OPERATOR, player);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package dev.objz.commandbridge.api.channel.command;

/** Defines the execution context for a command. */
public enum RunAs {
/** Run as the server console. */
CONSOLE,
/** Run as a specific player. */
PLAYER,
/** Run as a player with temporary operator permissions. */
OPERATOR
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package dev.objz.commandbridge.api.message;

import dev.objz.commandbridge.api.channel.ChannelPayload;
import dev.objz.commandbridge.api.channel.ChannelType;
import dev.objz.commandbridge.api.channel.MessageChannel;
import dev.objz.commandbridge.api.platform.Platform;

/**
* Metadata for a received message.
*
* @param channel the channel the message was received on
* @param from the server that sent the message
* @param timestamp the time the message was sent
* @param <T> the payload type
*/
public record MessageContext<T extends ChannelPayload>(
ChannelType<T, ? extends MessageChannel<T>> channel,
Platform.ServerTarget from,
long timestamp
) {
}
Loading
Loading