Skip to content

Conversation

@raphael-goetz
Copy link
Contributor

  • adjusted plugin impl to match Octopus v2

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the Paper plugin + API to work with Octopus v2 by switching to the new SDK, introducing token-based gRPC credentials + a shared channel, and replacing the old listener API with a handler/observer-based streaming model.

Changes:

  • Add YAML config for Octopus gRPC host/port/token and wire it into plugin startup.
  • Rework plugin/API around Octopus v2 SDK types and gentle.Result error handling, including new streaming observer + handler registration.
  • Update build tooling/dependencies (Remora, gRPC libs, run-server task) and remove legacy proto/utils + JSON Struct parsing utilities.

Reviewed changes

Copilot reviewed 25 out of 27 changed files in this pull request and generated 22 comments.

Show a summary per file
File Description
plugin/src/main/resources/config.yml Adds default Octopus host/port/token configuration.
plugin/src/main/java/studio/o7/octopus/plugin/utils/ProtoUtils.java Removes legacy Timestamp conversion utility (no longer needed with new API).
plugin/src/main/java/studio/o7/octopus/plugin/observer/OctopusObserver.java New gRPC StreamObserver that dispatches EventCalls to registered handlers.
plugin/src/main/java/studio/o7/octopus/plugin/observer/EmptyObserver.java Removes unused no-op observer.
plugin/src/main/java/studio/o7/octopus/plugin/channel/OctopusChannelFactory.java Introduces shared gRPC channel creation + shutdown.
plugin/src/main/java/studio/o7/octopus/plugin/authentication/OctopusCredentials.java Adds token-based CallCredentials for gRPC calls.
plugin/src/main/java/studio/o7/octopus/plugin/OctopusPlugin.java Loads config and constructs OctopusImpl(token, host, port); shuts down channel on disable.
plugin/src/main/java/studio/o7/octopus/plugin/OctopusImpl.java Re-implements Octopus operations against v2 stubs; adds streaming handler registration.
plugin/build.gradle.kts Adds gRPC dependencies + run-server task + ShadowJar service file handling.
build.gradle.kts Bumps Remora plugin version.
api/src/main/java/studio/o7/octopus/plugin/api/parser/StructParser.java Removes legacy Gson/Struct conversion support.
api/src/main/java/studio/o7/octopus/plugin/api/adapters/ResourcePackInfoAdapter.java Removes legacy JSON adapter.
api/src/main/java/studio/o7/octopus/plugin/api/adapters/PlayerAdapter.java Removes legacy JSON adapter.
api/src/main/java/studio/o7/octopus/plugin/api/adapters/OfflinePlayerAdapter.java Removes legacy JSON adapter.
api/src/main/java/studio/o7/octopus/plugin/api/adapters/LocationAdapter.java Removes legacy JSON adapter.
api/src/main/java/studio/o7/octopus/plugin/api/adapters/ItemStackAdapter.java Removes legacy JSON adapter.
api/src/main/java/studio/o7/octopus/plugin/api/adapters/InetSocketAddressAdapter.java Removes legacy JSON adapter.
api/src/main/java/studio/o7/octopus/plugin/api/adapters/ComponentAdapter.java Removes legacy JSON adapter.
api/src/main/java/studio/o7/octopus/plugin/api/QueryParameter.java Adds query parameter builder object for v2 query requests.
api/src/main/java/studio/o7/octopus/plugin/api/OctopusError.java Adds typed error enum for gentle.Result failures.
api/src/main/java/studio/o7/octopus/plugin/api/Octopus.java Updates public API to v2 signatures (Result, query, write, handler registration).
api/src/main/java/studio/o7/octopus/plugin/api/EventHandler.java Renames/moves old listener abstraction to EventHandler and updates SDK types.
api/build.gradle.kts Updates SDK dependency and adds gentle.
README.md Updates usage docs and documents new config layout.
.gitignore Ignores run-server output directory.
.devcontainer/devcontainer.json Removes devcontainer setup.
Comments suppressed due to low confidence (1)

plugin/src/main/java/studio/o7/octopus/plugin/channel/OctopusChannelFactory.java:57

  • firstNonEmpty and parseIntOrDefault are currently unused dead code. Either use them (e.g., to handle missing config/env values) or remove them to keep the factory minimal.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +46 to +49
} catch (RuntimeException e) {
log.error("Failed to send a get request: {}", e.getMessage());
return Result.err(OctopusError.GET_REQUEST_FAILED);
}
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These catch blocks log only e.getMessage() and lose the stack trace, making gRPC failures hard to debug. Log the exception itself (e.g., pass e to the logger) and consider mapping gRPC StatusRuntimeException to more specific OctopusErrors if available.

Copilot uses AI. Check for mistakes.
Metadata.Key<String> authKey = Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER);
headers.put(authKey, this.token);
metadataApplier.apply(headers);
}
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CallCredentials in gRPC Java requires overriding thisUsesUnstableApi(). With the declared gRPC dependency versions, this class will not compile without that override.

Suggested change
}
}
@Override
protected void thisUsesUnstableApi() {
// Acknowledge usage of gRPC's unstable CallCredentials API.
}

Copilot uses AI. Check for mistakes.

GET_REQUEST_FAILED(0, "While trying to get an entry, a gRPC-Error occurred"),
QUERY_REQUEST_FAILED(1, "While trying to query an entry, a gRPC-Error occurred"),
CALL_REQUEST_FAILED(1, "While trying to call an object, a gRPC-Error occurred"),
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CALL_REQUEST_FAILED and QUERY_REQUEST_FAILED both use error code 1. If codes are intended to be unique/stable for consumers, give each enum constant a distinct code (and keep them stable across releases).

Suggested change
CALL_REQUEST_FAILED(1, "While trying to call an object, a gRPC-Error occurred"),
CALL_REQUEST_FAILED(2, "While trying to call an object, a gRPC-Error occurred"),

Copilot uses AI. Check for mistakes.
host: "127.0.0.1"
# Port of Octopus-gRPC Server
port: 50051
# Replace to Octopus-API token
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment wording is confusing: “Replace to Octopus-API token”. Consider changing it to something clearer like “Set this to your Octopus API token” so users know what value is expected.

Suggested change
# Replace to Octopus-API token
# Set this to your Octopus API token

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +22
@Override
public void applyRequestMetadata(RequestInfo requestInfo, Executor executor, MetadataApplier metadataApplier) {
Metadata headers = new Metadata();
Metadata.Key<String> authKey = Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER);
headers.put(authKey, this.token);
metadataApplier.apply(headers);
}
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

applyRequestMetadata ignores the provided Executor and calls metadataApplier.apply(...) directly. gRPC expects credentials to apply metadata via the given executor and to call metadataApplier.fail(...) on error; otherwise this can violate threading expectations and hide failures.

Copilot uses AI. Check for mistakes.
Comment on lines +90 to +94
var handler = optionalHandler.get();
handler.onCall(object);
log.info("Successfully handled callID: {} with pattern: {}", currentCallID, object.getKey());
this.currentCallID = "";
}
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

handler.onCall(object) is invoked without any exception handling. If a handler throws, it can abort the gRPC stream and currentCallID will not be cleared. Wrap the handler invocation in try/catch (and use finally to clear state) so one bad handler doesn’t take down the listener.

Copilot uses AI. Check for mistakes.
Comment on lines +40 to +45
public static void shutdown() {
var c = channel;
if (c == null) return;
c.shutdown();
channel = null;
}
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shutdown() calls channel.shutdown() but does not await termination (or fall back to shutdownNow()), which can leave gRPC threads running during plugin disable/reload. Consider awaiting termination for a short timeout and then forcing shutdown if needed.

Copilot uses AI. Check for mistakes.
Comment on lines +81 to +83
* EventCalls, using symbols such as `*` and `<` will subscribe on multiple keys
* There's no safeguard to prevent subscribing to the same topic. So please make
* shure you're not handling a topic twice!
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs say key patterns use * and <, but the matching implementation and other docs use > as the multi-token wildcard. Update this to > (or the correct symbol) to avoid misleading API consumers.

Suggested change
* EventCalls, using symbols such as `*` and `<` will subscribe on multiple keys
* There's no safeguard to prevent subscribing to the same topic. So please make
* shure you're not handling a topic twice!
* EventCalls, using symbols such as `*` and `>` will subscribe on multiple keys
* There's no safeguard to prevent subscribing to the same topic. So please make
* sure you're not handling a topic twice!

Copilot uses AI. Check for mistakes.

@Override
public void onError(Throwable throwable) {
log.error("Failed listener {}, callID: {}", throwable.getMessage(), currentCallID);
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The log.error(...) call drops the exception stack trace by only logging throwable.getMessage(). Pass the Throwable as the last argument (or use the dedicated overload) so failures are diagnosable in production logs.

Suggested change
log.error("Failed listener {}, callID: {}", throwable.getMessage(), currentCallID);
log.error("Failed listener {}, callID: {}", throwable.getMessage(), currentCallID, throwable);

Copilot uses AI. Check for mistakes.
@Override
public void unregisterListener(@NonNull Listener listener) {
unregisterListener(listener.getListenerUniqueId());
public void write(Object obj) {
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method OctopusImpl.write(..) could be confused with overloaded method Octopus.write, since dispatch depends on static types.

Copilot uses AI. Check for mistakes.
@raphael-goetz raphael-goetz merged commit 03d3e61 into main Jan 27, 2026
9 checks passed
@raphael-goetz raphael-goetz deleted the feat/octopusv2 branch January 27, 2026 10:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants