-
Notifications
You must be signed in to change notification settings - Fork 1
Feat/octopusv2 #20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/octopusv2 #20
Conversation
raphael-goetz
commented
Jan 27, 2026
- adjusted plugin impl to match Octopus v2
There was a problem hiding this 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.Resulterror 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
firstNonEmptyandparseIntOrDefaultare 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.
| } catch (RuntimeException e) { | ||
| log.error("Failed to send a get request: {}", e.getMessage()); | ||
| return Result.err(OctopusError.GET_REQUEST_FAILED); | ||
| } |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
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.
| Metadata.Key<String> authKey = Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER); | ||
| headers.put(authKey, this.token); | ||
| metadataApplier.apply(headers); | ||
| } |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
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.
| } | |
| } | |
| @Override | |
| protected void thisUsesUnstableApi() { | |
| // Acknowledge usage of gRPC's unstable CallCredentials API. | |
| } |
|
|
||
| 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"), |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
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).
| 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"), |
| host: "127.0.0.1" | ||
| # Port of Octopus-gRPC Server | ||
| port: 50051 | ||
| # Replace to Octopus-API token |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
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.
| # Replace to Octopus-API token | |
| # Set this to your Octopus API token |
| @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); | ||
| } |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
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.
| var handler = optionalHandler.get(); | ||
| handler.onCall(object); | ||
| log.info("Successfully handled callID: {} with pattern: {}", currentCallID, object.getKey()); | ||
| this.currentCallID = ""; | ||
| } |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
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.
| public static void shutdown() { | ||
| var c = channel; | ||
| if (c == null) return; | ||
| c.shutdown(); | ||
| channel = null; | ||
| } |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
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.
| * 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! |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
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.
| * 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! |
|
|
||
| @Override | ||
| public void onError(Throwable throwable) { | ||
| log.error("Failed listener {}, callID: {}", throwable.getMessage(), currentCallID); |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
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.
| log.error("Failed listener {}, callID: {}", throwable.getMessage(), currentCallID); | |
| log.error("Failed listener {}, callID: {}", throwable.getMessage(), currentCallID, throwable); |
| @Override | ||
| public void unregisterListener(@NonNull Listener listener) { | ||
| unregisterListener(listener.getListenerUniqueId()); | ||
| public void write(Object obj) { |
Copilot
AI
Jan 27, 2026
There was a problem hiding this comment.
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.