Skip to content

fix(core): prevent Paper startup config binding collision#59

Merged
robinbraemer merged 3 commits into
mainfrom
fm/connect-java-paper-token-startup-fix-q2
Jul 22, 2026
Merged

fix(core): prevent Paper startup config binding collision#59
robinbraemer merged 3 commits into
mainfrom
fm/connect-java-paper-token-startup-fix-q2

Conversation

@robinbraemer

Copy link
Copy Markdown
Member

Intent

Fix the connect-java 0.12.0 Paper startup regression: the plugin crashed in SpigotPlugin.onLoad() with Guice [JitBindingAlreadySet] on ConnectConfig, before token initialization, so plugins/Connect/token.json was never created. Root cause: PR #57's Bedrock identity components (BedrockIdentityEnforcer, BedrockIdentityKeyProvider) are resolved by the platform injector while SpigotPlatform is constructed, i.e. before ConnectPlatform.init() loads the config, yet they injected ConnectConfig directly. ConnectConfig is only bound in the child injector that init() creates via ConfigLoadedModule, so resolving the Bedrock graph from the parent injector forced Guice to create a just-in-time ConnectConfig binding on the parent that then collided with the child binding. Fix: the injected Bedrock components now read ConnectConfig lazily through the parent-bound ConfigHolder (populated by init() before the config is used), so no ConnectConfig binding is ever established on the parent injector. Deliberate decisions a reviewer should know: (1) all existing (ConnectConfig, ...) constructors are intentionally preserved so ~40 existing test call-sites and direct callers keep working; BedrockIdentityKeyProvider/BedrockIdentityEnforcer store a Supplier and the ConnectConfig constructors wrap it as a constant supplier. (2) BedrockIdentityKeyProvider was made @Inject @singleton and its CommonModule @provides removed so Guice binds it just-in-time; the readiness @provides now takes ConfigHolder. (3) Token precedence (CONNECT_TOKEN env, then existing token.json, else generate+persist) is deliberately untouched. (4) Scope was intentionally limited to startup/injector/token init; a separate PRE-EXISTING libp2p runtime reflection error ('Failed to initialize Connect libp2p endpoint runtime') that surfaces later in enable() is gracefully caught (WatchService fallback) and was intentionally left out of scope. Added regression test core/BedrockParentInjectorStartupTest and an AGENTS.md injector-scoping note. Verified end-to-end on Paper 26.2 build 60 + Java 25 with a single plugin jar: unfixed crashes in onLoad with no token.json (falsifying the duplicate-jar theory on a one-jar clean data dir), fixed reaches enabled state and creates token.json, restart reuses the token, and an explicit CONNECT_TOKEN does not overwrite token.json.

What Changed

  • Fixed the Paper startup injector collision by making Bedrock identity components resolve ConnectConfig lazily through ConfigHolder, while preserving existing constructors for compatibility.
  • Updated Guice bindings/readiness provisioning and added parent-injector startup regression coverage plus injector-scoping documentation.
  • Verified with targeted/full Gradle tests and clean Paper startup, restart/token reuse, and explicit CONNECT_TOKEN checks.

Risk Assessment

✅ Low: The requested compatibility overload is restored, and the remaining changes are narrowly scoped to lazy injector configuration without other material source risks found.

Testing

Focused and full tests, shaded-jar build, and real Paper 26.2 build 60/Java 25 runs verified token creation, restart reuse, environment-token non-overwrite, and absence of the startup crash; the known later libp2p reflection error was caught while the server reached ready state and was left out of scope, and no linters or static analysis were run.

Evidence: Paper startup evidence
Connect Java Paper startup/token end-to-end evidence
Paper: 26.2 build 60 (sha256 c2ece6c481c70b7f369234cff3628c35414df005c51043cc8b4d51a8bf853ac8)
Java: Eclipse Adoptium 25.0.3+9
Plugin artifact: connect-spigot.jar (sha256 2f0ff88bb0627d2f0bb68a6e5e740f57f1d9602758115e01549ea707222ee287)

[clean first start]
single_plugin_jar=connect-spigot.jar
token_file=present
token_json={"token":"T-pcsukkjezwrtxszpqhhv"}
[restart reuse]
before_token=T-pcsukkjezwrtxszpqhhv
before_mtime=1784720810
after_token=T-pcsukkjezwrtxszpqhhv
after_mtime=1784720810
token_reused=true
[13:47:29 INFO]: [bootstrap] Running Java 25 (OpenJDK 64-Bit Server VM 25.0.3+9-LTS; Eclipse Adoptium Temurin-25.0.3+9) on Mac OS X 26.5.2 (aarch64)
[13:47:29 INFO]: [bootstrap] Loading Paper 26.2-60-main@1cb58fb (2026-07-12T16:08:02Z) for Minecraft 26.2
>   [13:47:33 INFO]: [connect] Loading server plugin connect v0.0.0-SNAPSHOT+unknown
>   [13:47:34 INFO]: [connect] Took 562ms to boot Connect
>   [13:47:34 INFO]: [connect] Enabling connect v0.0.0-SNAPSHOT+unknown
>   [13:47:35 INFO]: Done (6.300s)! For help, type "help"
>   [13:47:35 INFO]: [connect] Started watching for new player sessions

[CONNECT_TOKEN precedence]
before_hash=d752f283c8cf93864d2228b8f19a6faecec321b12a691b60185a3e35481104e2
before_content={"token":"file-token"}
after_hash=d752f283c8cf93864d2228b8f19a6faecec321b12a691b60185a3e35481104e2
after_content={"token":"file-token"}
file_unchanged=true
[13:48:18 INFO]: [bootstrap] Running Java 25 (OpenJDK 64-Bit Server VM 25.0.3+9-LTS; Eclipse Adoptium Temurin-25.0.3+9) on Mac OS X 26.5.2 (aarch64)
[13:48:18 INFO]: [bootstrap] Loading Paper 26.2-60-main@1cb58fb (2026-07-12T16:08:02Z) for Minecraft 26.2
>   [13:48:22 INFO]: [connect] Loading server plugin connect v0.0.0-SNAPSHOT+unknown
>   [13:48:23 INFO]: [connect] Took 667ms to boot Connect
>   [13:48:25 INFO]: [connect] Enabling connect v0.0.0-SNAPSHOT+unknown
>   [13:48:26 INFO]: Done (8.020s)! For help, type "help"

[scope note]
The known libp2p runtime reflection error was logged during later enable() and the server continued to Done/Started watching; it was not part of this startup/token fix.
Evidence: Startup symptom check
Paper startup regression symptom check
log=/var/folders/1y/cjgf53nj31n_dxsspqnjfjvc0000gn/T/no-mistakes-evidence/01KY4S9C5HZS832P0QCYB637AJ/clean/restart-console.log JitBindingAlreadySet_occurrences=0
log=/var/folders/1y/cjgf53nj31n_dxsspqnjfjvc0000gn/T/no-mistakes-evidence/01KY4S9C5HZS832P0QCYB637AJ/env/console.log JitBindingAlreadySet_occurrences=0
Both real Paper runs logged Connect boot/enabling and reached server Done; no duplicate-config crash was observed.
Evidence: Paper restart console
Starting org.bukkit.craftbukkit.Main
[13:47:29 INFO]: [bootstrap] Running Java 25 (OpenJDK 64-Bit Server VM 25.0.3+9-LTS; Eclipse Adoptium Temurin-25.0.3+9) on Mac OS X 26.5.2 (aarch64)
[13:47:29 INFO]: [bootstrap] Loading Paper 26.2-60-main@1cb58fb (2026-07-12T16:08:02Z) for Minecraft 26.2
[13:47:29 INFO]: [PluginInitializerManager] Initializing plugins...
[13:47:30 INFO]: [PluginInitializerManager] Initialized 1 plugin
[13:47:30 INFO]: [PluginInitializerManager] Bukkit plugins (1):
 - connect (0.0.0-SNAPSHOT+unknown)
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by org.joml.MemUtil$MemUtilUnsafe (file:/private/var/folders/1y/cjgf53nj31n_dxsspqnjfjvc0000gn/T/no-mistakes-evidence/01KY4S9C5HZS832P0QCYB637AJ/clean/libraries/org/joml/joml/1.10.8/joml-1.10.8.jar)
WARNING: Please consider reporting this to the maintainers of class org.joml.MemUtil$MemUtilUnsafe
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release
[13:47:31 INFO]: Environment: Environment[sessionHost=https://sessionserver.mojang.com, servicesHost=https://api.minecraftservices.com, profilesHost=https://api.mojang.com, name=PROD]
[13:47:32 INFO]: Loaded 1585 recipes
[13:47:32 INFO]: Loaded 1688 advancements
[13:47:32 INFO]: [ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry] Initialising converters for DataConverter...
[13:47:32 INFO]: [ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry] Finished initialising converters for DataConverter in 145.9ms
[13:47:32 INFO]: Starting minecraft server version 26.2
[13:47:32 INFO]: Loading properties
[13:47:32 INFO]: This server is running Paper version 26.2-60-main@1cb58fb (2026-07-12T16:08:02Z) (Implementing API version 26.2.build.60-beta)
[13:47:32 INFO]: [spark] This server bundles the spark profiler. For more information please visit https://docs.papermc.io/paper/profiling
[13:47:32 INFO]: Server Ping Player Sample Count: 12
[13:47:32 INFO]: Using 4 threads for Netty based IO
[13:47:33 INFO]: [MoonriseCommon] Paper is using 3 worker threads, 1 I/O threads
[13:47:33 INFO]: Default game type: SURVIVAL
[13:47:33 INFO]: Generating keypair
[13:47:33 INFO]: Starting Minecraft server on *:25575
> 
  
[13:47:33 INFO]: Paper: Using libdeflate (macOS ARM64 / Apple Silicon) compression from Velocity.
> 
  
[13:47:33 INFO]: Paper: Using native (macOS ARM64 / Apple Silicon) cipher from Velocity.
> 
  
[13:47:33 INFO]: [connect] Loading server plugin connect v0.0.0-SNAPSHOT+unknown
> 
  
[13:47:34 INFO]: [connect] Took 562ms to boot Connect
> 
  
[13:47:34 INFO]: Server permissions file permissions.yml is empty, ignoring it
> 
  
�[33;1m[13:47:34 WARN]: **** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!
�[m> 
  
�[33;1m[13:47:34 WARN]: The server will make no attempt to authenticate usernames. Beware.
�[m> 
  
�[33;1m[13:47:34 WARN]: While this makes the game possible to play without internet access, it also opens up the ability for hackers to connect with any username they choose.
�[m> 
  
�[33;1m[13:47:34 WARN]: To change this, set "online-mode" to "true" in the server.properties file.
�[m> 
  
[13:47:34 INFO]: Preparing level "world"
> 
  
[13:47:34 INFO]: Loading 0 persistent chunks for level 'minecraft:overworld'...
> 
  
[13:47:34 INFO]: Prepared spawn area in 0 ms
> 
  
[13:47:34 INFO]: Loading 0 persistent chunks for level 'minecraft:the_nether'...
> 
  
[13:47:34 INFO]: Preparing spawn area: 100%
> 
  
[13:47:34 INFO]: Prepared spawn area in 1 ms
> 
  
[13:47:34 INFO]: Loading 0 persistent chunks for level 'minecraft:the_end'...
> 
  
[13:47:34 INFO]: Prepared spawn area in 0 ms
> 
  
[13:47:34 INFO]: Done preparing level "world" (0.185s)
> 
  
[13:47:34 INFO]: [connect] Enabling connect v0.0.0-SNAPSHOT+unknown
> 
  
�[31;1m[13:47:34 ERROR]: [connect] Failed to initialize Connect libp2p endpoint runtime
java.lang.NoSuchMethodException: com.minekube.connect.tunnel.p2p.Libp2pEndpointRuntime.<init>(java.nio.file.Path,com.minekube.connect.config.ConnectConfig,java.lang.String,com.minekube.connect.platform.util.PlatformUtils,com.minekube.connect.api.logger.ConnectLogger,com.minekube.connect.api.inject.PlatformInjector,com.minekube.connect.api.SimpleConnectApi)
	at java.base/java.lang.Class.getConstructor0(Class.java:3187) ~[?:?]
	at java.base/java.lang.Class.getDeclaredConstructor(Class.java:2491) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.tunnel.p2p.Libp2pEndpoint.<init>(Libp2pEndpoint.java:64) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.tunnel.p2p.Libp2pEndpoint$$FastClassByGuice$$1705705.GUICE$TRAMPOLINE(<generated>) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.tunnel.p2p.Libp2pEndpoint$$FastClassByGuice$$1705705.apply(<generated>) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.DefaultConstructionProxyFactory$FastClassProxy.newInstance(DefaultConstructionProxyFactory.java:82) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.ConstructorInjector.provision(ConstructorInjector.java:114) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:91) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:300) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:40) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.SingletonScope$1.get(SingletonScope.java:169) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:45) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.InternalInjectorCreator.loadEagerSingletons(InternalInjectorCreator.java:213) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.InternalInjectorCreator.injectDynamically(InternalInjectorCreator.java:186) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:113) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.InjectorImpl.createChildInjector(InjectorImpl.java:240) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.InjectorImpl.createChildInjector(InjectorImpl.java:245) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.ConnectPlatform.enable(ConnectPlatform.java:141) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.SpigotPlatform.enable(SpigotPlatform.java:55) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.SpigotPlugin.onEnable(SpigotPlugin.java:68) ~[?:?]
	at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:279) ~[paper-api-26.2.build.60-beta.jar:?]
	at io.papermc.paper.plugin.manager.PaperPluginInstanceManager.enablePlugin(PaperPluginInstanceManager.java:201) ~[paper-26.2.jar:26.2-60-1cb58fb]
	at io.papermc.paper.plugin.manager.PaperPluginManagerImpl.enablePlugin(PaperPluginManagerImpl.java:109) ~[paper-26.2.jar:26.2-60-1cb58fb]
	at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:519) ~[paper-api-26.2.build.60-beta.jar:?]
	at org.bukkit.craftbukkit.CraftServer.enablePlugin(CraftServer.java:633) ~[paper-26.2.jar:26.2-60-1cb58fb]
	at org.bukkit.craftbukkit.CraftServer.enablePlugins(CraftServer.java:590) ~[paper-26.2.jar:26.2-60-1cb58fb]
	at net.minecraft.server.MinecraftServer.initPostWorld(MinecraftServer.java:648) ~[paper-26.2.jar:26.2-60-1cb58fb]
	at net.minecraft.server.dedicated.DedicatedServer.initServer(DedicatedServer.java:343) ~[paper-26.2.jar:26.2-60-1cb58fb]
	at net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:1267) ~[paper-26.2.jar:26.2-60-1cb58fb]
	at net.minecraft.server.MinecraftServer.lambda$spin$0(MinecraftServer.java:303) ~[paper-26.2.jar:26.2-60-1cb58fb]
	at java.base/java.lang.Thread.run(Thread.java:1474) ~[?:?]
�[m> 
  
[13:47:34 INFO]: [connect] Endpoint name: costly-moose
> 
  
[13:47:34 INFO]: [connect] Your public address: costly-moose.play.minekube.net
> 
  
[13:47:34 INFO]: [spark] Starting background profiler...
> 
  
[13:47:35 INFO]: Saving chunks for level 'ServerLevel[world]'/minecraft:overworld
> 
  
[13:47:35 INFO]: Saving chunks for level 'ServerLevel[world]'/minecraft:the_nether
> 
  
[13:47:35 INFO]: Saving chunks for level 'ServerLevel[world]'/minecraft:the_end
> 
  
[13:47:35 INFO]: ThreadedAnvilChunkStorage (world): All chunks are saved
> 
  
[13:47:35 INFO]: ThreadedAnvilChunkStorage (DIM-1): All chunks are saved
> 
  
[13:47:35 INFO]: ThreadedAnvilChunkStorage (DIM1): All chunks are saved
> 
  
[13:47:35 INFO]: ThreadedAnvilChunkStorage: All dimensions are saved
> 
  
[13:47:35 INFO]: Running delayed init tasks
> 
  
[13:47:35 INFO]: Done (6.300s)! For help, type "help"
> 
  
[13:47:35 INFO]: *** Currently you are 5 builds behind ***
> 
  
[13:47:35 INFO]: *** It is highly recommended to download the latest build from https://papermc.io/downloads/paper ***
> 
  
[13:47:35 INFO]: [connect] Started watching for new player sessions
> stop
> 
  
[13:47:45 INFO]: Stopping the server
> 
  
[13:47:45 INFO]: Stopping server
> 
  
[13:47:45 INFO]: [connect] Disabling connect v0.0.0-SNAPSHOT+unknown
> 
  
[13:47:45 INFO]: [connect] Stopped watching for sessions
> 
  
[13:47:45 INFO]: Saving players
> 
  
[13:47:45 INFO]: Saving worlds
> 
  
[13:47:45 INFO]: Saving chunks for level 'ServerLevel[world]'/minecraft:overworld
> 
  
[13:47:45 INFO]: [ChunkHolderManager] Waiting 60s for chunk system to halt for world 'minecraft:overworld'
> 
  
[13:47:45 INFO]: [ChunkHolderManager] Halted chunk system for world 'minecraft:overworld'
> 
  
[13:47:45 INFO]: [ChunkHolderManager] Saving all chunkholders for world 'minecraft:overworld'
> 
  
[13:47:45 INFO]: [ChunkHolderManager] Saved 0 block chunks, 0 entity chunks, 0 poi chunks in world 'minecraft:overworld' in 0.00s
> 
  
[13:47:45 INFO]: [ChunkHolderManager] Waiting 60s for chunk I/O to halt for world 'minecraft:overworld'
> 
  
[13:47:45 INFO]: [ChunkHolderManager] Halted I/O scheduler for world 'minecraft:overworld'
> 
  
[13:47:45 INFO]: Saving chunks for level 'ServerLevel[world]'/minecraft:the_nether
> 
  
[13:47:45 INFO]: [ChunkHolderManager] Waiting 60s for chunk system to halt for world 'minecraft:the_nether'
> 
  
[13:47:45 INFO]: [ChunkHolderManager] Halted chunk system for world 'minecraft:the_nether'
> 
  
[13:47:45 INFO]: [ChunkHolderManager] Saving all chunkholders for world 'minecraft:the_nether'
> 
  
[13:47:45 INFO]: [ChunkHolderManager] Saved 0 block chunks, 0 entity chunks, 0 poi chunks in world 'minecraft:the_nether' in 0.00s
> 
  
[13:47:45 INFO]: [ChunkHolderManager] Waiting 60s for chunk I/O to halt for world 'minecraft:the_nether'
> 
  
[13:47:45 INFO]: [ChunkHolderManager] Halted I/O scheduler for world 'minecraft:the_nether'
> 
  
[13:47:45 INFO]: Saving chunks for level 'ServerLevel[world]'/minecraft:the_end
> 
  
[13:47:45 INFO]: [ChunkHolderManager] Waiting 60s for chunk system to halt for world 'minecraft:the_end'
> 
  
[13:47:45 INFO]: [ChunkHolderManager] Halted chunk system for world 'minecraft:the_end'
> 
  
[13:47:45 INFO]: [ChunkHolderManager] Saving all chunkholders for world 'minecraft:the_end'
> 
  
[13:47:45 INFO]: [ChunkHolderManager] Saved 0 block chunks, 0 entity chunks, 0 poi chunks in world 'minecraft:the_end' in 0.00s
> 
  
[13:47:45 INFO]: [ChunkHolderManager] Waiting 60s for chunk I/O to halt for world 'minecraft:the_end'
> 
  
[13:47:45 INFO]: [ChunkHolderManager] Halted I/O scheduler for world 'minecraft:the_end'
> 
  
[13:47:45 INFO]: ThreadedAnvilChunkStorage (world): All chunks are saved
> 
  
[13:47:45 INFO]: ThreadedAnvilChunkStorage (DIM-1): All chunks are saved
> 
  
[13:47:45 INFO]: ThreadedAnvilChunkStorage (DIM1): All chunks are saved
> 
  
[13:47:45 INFO]: ThreadedAnvilChunkStorage: All dimensions are saved
> 
  
[13:47:45 INFO]: Waiting for all RegionFile I/O tasks to complete...
> 
  
[13:47:45 INFO]: All RegionFile I/O tasks to complete
> 
  
[13:47:45 INFO]: [MoonriseCommon] Awaiting termination of worker pool for up to 60s...
> 
  
[13:47:45 INFO]: [MoonriseCommon] Awaiting termination of I/O pool for up to 60s...
> 
Evidence: CONNECT_TOKEN console
Downloading mojang_26.2.jar
Applying patches
Starting org.bukkit.craftbukkit.Main
[13:48:18 INFO]: [bootstrap] Running Java 25 (OpenJDK 64-Bit Server VM 25.0.3+9-LTS; Eclipse Adoptium Temurin-25.0.3+9) on Mac OS X 26.5.2 (aarch64)
[13:48:18 INFO]: [bootstrap] Loading Paper 26.2-60-main@1cb58fb (2026-07-12T16:08:02Z) for Minecraft 26.2
[13:48:19 INFO]: [PluginInitializerManager] Initializing plugins...
[13:48:19 INFO]: [PluginInitializerManager] Initialized 1 plugin
[13:48:19 INFO]: [PluginInitializerManager] Bukkit plugins (1):
 - connect (0.0.0-SNAPSHOT+unknown)
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by org.joml.MemUtil$MemUtilUnsafe (file:/private/var/folders/1y/cjgf53nj31n_dxsspqnjfjvc0000gn/T/no-mistakes-evidence/01KY4S9C5HZS832P0QCYB637AJ/env/libraries/org/joml/joml/1.10.8/joml-1.10.8.jar)
WARNING: Please consider reporting this to the maintainers of class org.joml.MemUtil$MemUtilUnsafe
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release
[13:48:21 INFO]: Environment: Environment[sessionHost=https://sessionserver.mojang.com, servicesHost=https://api.minecraftservices.com, profilesHost=https://api.mojang.com, name=PROD]
[13:48:21 INFO]: Found new data pack file/bukkit, loading it automatically
[13:48:21 INFO]: Found new data pack paper, loading it automatically
[13:48:21 INFO]: No existing world data, creating new world
[13:48:21 INFO]: Loaded 1585 recipes
[13:48:21 INFO]: Loaded 1688 advancements
[13:48:21 INFO]: [ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry] Initialising converters for DataConverter...
[13:48:21 INFO]: [ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry] Finished initialising converters for DataConverter in 163.7ms
[13:48:21 INFO]: Starting minecraft server version 26.2
[13:48:21 INFO]: Loading properties
[13:48:21 INFO]: This server is running Paper version 26.2-60-main@1cb58fb (2026-07-12T16:08:02Z) (Implementing API version 26.2.build.60-beta)
[13:48:21 INFO]: [spark] This server bundles the spark profiler. For more information please visit https://docs.papermc.io/paper/profiling
[13:48:22 INFO]: Server Ping Player Sample Count: 12
[13:48:22 INFO]: Using 4 threads for Netty based IO
[13:48:22 INFO]: [MoonriseCommon] Paper is using 3 worker threads, 1 I/O threads
[13:48:22 INFO]: Default game type: SURVIVAL
[13:48:22 INFO]: Generating keypair
> 
  
[13:48:22 INFO]: Starting Minecraft server on *:25576
> 
  
[13:48:22 INFO]: Paper: Using libdeflate (macOS ARM64 / Apple Silicon) compression from Velocity.
> 
  
[13:48:22 INFO]: Paper: Using native (macOS ARM64 / Apple Silicon) cipher from Velocity.
> 
  
[13:48:22 INFO]: [connect] Loading server plugin connect v0.0.0-SNAPSHOT+unknown
> 
  
[13:48:23 INFO]: [connect] Took 667ms to boot Connect
> 
  
�[33;1m[13:48:23 WARN]: **** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!
�[m> 
  
�[33;1m[13:48:23 WARN]: The server will make no attempt to authenticate usernames. Beware.
�[m> 
  
�[33;1m[13:48:23 WARN]: While this makes the game possible to play without internet access, it also opens up the ability for hackers to connect with any username they choose.
�[m> 
  
�[33;1m[13:48:23 WARN]: To change this, set "online-mode" to "true" in the server.properties file.
�[m> 
  
[13:48:23 INFO]: Preparing level "world"
> 
  
[13:48:23 INFO]: Selecting spawn point for level 'minecraft:overworld'...
> 
  
[13:48:24 INFO]: Selecting spawn point for level 'minecraft:the_nether'...
> 
  
[13:48:25 INFO]: Selecting spawn point for level 'minecraft:the_end'...
> 
  
[13:48:25 INFO]: Loading 0 persistent chunks for level 'minecraft:overworld'...
> 
  
[13:48:25 INFO]: Preparing spawn area: 100%
> 
  
[13:48:25 INFO]: Prepared spawn area in 1750 ms
> 
  
[13:48:25 INFO]: Loading 0 persistent chunks for level 'minecraft:the_nether'...
> 
  
[13:48:25 INFO]: Preparing spawn area: 100%
> 
  
[13:48:25 INFO]: Prepared spawn area in 377 ms
> 
  
[13:48:25 INFO]: Loading 0 persistent chunks for level 'minecraft:the_end'...
> 
  
[13:48:25 INFO]: Preparing spawn area: 100%
> 
  
[13:48:25 INFO]: Prepared spawn area in 122 ms
> 
  
[13:48:25 INFO]: Done preparing level "world" (1.891s)
> 
  
[13:48:25 INFO]: [connect] Enabling connect v0.0.0-SNAPSHOT+unknown
> 
  
�[31;1m[13:48:25 ERROR]: [connect] Failed to initialize Connect libp2p endpoint runtime
java.lang.NoSuchMethodException: com.minekube.connect.tunnel.p2p.Libp2pEndpointRuntime.<init>(java.nio.file.Path,com.minekube.connect.config.ConnectConfig,java.lang.String,com.minekube.connect.platform.util.PlatformUtils,com.minekube.connect.api.logger.ConnectLogger,com.minekube.connect.api.inject.PlatformInjector,com.minekube.connect.api.SimpleConnectApi)
	at java.base/java.lang.Class.getConstructor0(Class.java:3187) ~[?:?]
	at java.base/java.lang.Class.getDeclaredConstructor(Class.java:2491) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.tunnel.p2p.Libp2pEndpoint.<init>(Libp2pEndpoint.java:64) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.tunnel.p2p.Libp2pEndpoint$$FastClassByGuice$$17fe973.GUICE$TRAMPOLINE(<generated>) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.tunnel.p2p.Libp2pEndpoint$$FastClassByGuice$$17fe973.apply(<generated>) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.DefaultConstructionProxyFactory$FastClassProxy.newInstance(DefaultConstructionProxyFactory.java:82) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.ConstructorInjector.provision(ConstructorInjector.java:114) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:91) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:300) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:40) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.SingletonScope$1.get(SingletonScope.java:169) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:45) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.InternalInjectorCreator.loadEagerSingletons(InternalInjectorCreator.java:213) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.InternalInjectorCreator.injectDynamically(InternalInjectorCreator.java:186) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:113) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.InjectorImpl.createChildInjector(InjectorImpl.java:240) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.shadow.com.google.inject.internal.InjectorImpl.createChildInjector(InjectorImpl.java:245) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.ConnectPlatform.enable(ConnectPlatform.java:141) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.SpigotPlatform.enable(SpigotPlatform.java:55) ~[?:?]
	at connect-spigot.jar//com.minekube.connect.SpigotPlugin.onEnable(SpigotPlugin.java:68) ~[?:?]
	at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:279) ~[paper-api-26.2.build.60-beta.jar:?]
	at io.papermc.paper.plugin.manager.PaperPluginInstanceManager.enablePlugin(PaperPluginInstanceManager.java:201) ~[paper-26.2.jar:26.2-60-1cb58fb]
	at io.papermc.paper.plugin.manager.PaperPluginManagerImpl.enablePlugin(PaperPluginManagerImpl.java:109) ~[paper-26.2.jar:26.2-60-1cb58fb]
	at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:519) ~[paper-api-26.2.build.60-beta.jar:?]
	at org.bukkit.craftbukkit.CraftServer.enablePlugin(CraftServer.java:633) ~[paper-26.2.jar:26.2-60-1cb58fb]
	at org.bukkit.craftbukkit.CraftServer.enablePlugins(CraftServer.java:590) ~[paper-26.2.jar:26.2-60-1cb58fb]
	at net.minecraft.server.MinecraftServer.initPostWorld(MinecraftServer.java:648) ~[paper-26.2.jar:26.2-60-1cb58fb]
	at net.minecraft.server.dedicated.DedicatedServer.initServer(DedicatedServer.java:343) ~[paper-26.2.jar:26.2-60-1cb58fb]
	at net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:1267) ~[paper-26.2.jar:26.2-60-1cb58fb]
	at net.minecraft.server.MinecraftServer.lambda$spin$0(MinecraftServer.java:303) ~[paper-26.2.jar:26.2-60-1cb58fb]
	at java.base/java.lang.Thread.run(Thread.java:1474) ~[?:?]
�[m> 
  
[13:48:25 INFO]: [connect] Endpoint name: naked-helen
> 
  
[13:48:25 INFO]: [connect] Your public address: naked-helen.play.minekube.net
> 
  
[13:48:25 INFO]: [spark] Starting background profiler...
> 
  
[13:48:26 INFO]: Saving chunks for level 'ServerLevel[world]'/minecraft:overworld
> 
  
[13:48:26 INFO]: Saving chunks for level 'ServerLevel[world]'/minecraft:the_nether
> 
  
[13:48:26 INFO]: Saving chunks for level 'ServerLevel[world]'/minecraft:the_end
> 
  
[13:48:26 INFO]: ThreadedAnvilChunkStorage (world): All chunks are saved
> 
  
[13:48:26 INFO]: ThreadedAnvilChunkStorage (DIM-1): All chunks are saved
> 
  
[13:48:26 INFO]: ThreadedAnvilChunkStorage (DIM1): All chunks are saved
> 
  
[13:48:26 INFO]: ThreadedAnvilChunkStorage: All dimensions are saved
> 
  
[13:48:26 INFO]: Running delayed init tasks
> 
  
[13:48:26 INFO]: Done (8.020s)! For help, type "help"
> 
  
[13:48:26 INFO]: *************************************************************************************
> 
  
[13:48:26 INFO]: This is the first time you're starting this server.
> 
  
[13:48:26 INFO]: It's recommended you read our 'Getting Started' documentation for guidance.
> 
  
[13:48:26 INFO]: View this and more helpful information here: https://docs.papermc.io/paper/next-steps
> 
  
[13:48:26 INFO]: *************************************************************************************
> 
  
[13:48:26 INFO]: *** Currently you are 5 builds behind ***
> 
  
[13:48:26 INFO]: *** It is highly recommended to download the latest build from https://papermc.io/downloads/paper ***
> stop
> 
  
[13:48:31 INFO]: Stopping the server
> 
  
[13:48:31 INFO]: Stopping server
> 
  
[13:48:31 INFO]: [connect] Disabling connect v0.0.0-SNAPSHOT+unknown
> 
  
[13:48:31 INFO]: [connect] Stopped watching for sessions
> 
  
[13:48:31 INFO]: Saving players
> 
  
[13:48:32 INFO]: Saving worlds
> 
  
[13:48:32 INFO]: Saving chunks for level 'ServerLevel[world]'/minecraft:overworld
> 
  
[13:48:32 INFO]: [ChunkHolderManager] Waiting 60s for chunk system to halt for world 'minecraft:overworld'
> 
  
[13:48:32 INFO]: [ChunkHolderManager] Halted chunk system for world 'minecraft:overworld'
> 
  
[13:48:32 INFO]: [ChunkHolderManager] Saving all chunkholders for world 'minecraft:overworld'
> 
  
[13:48:32 INFO]: [ChunkHolderManager] Saved 0 block chunks, 0 entity chunks, 0 poi chunks in world 'minecraft:overworld' in 0.02s
> 
  
[13:48:32 INFO]: [ChunkHolderManager] Waiting 60s for chunk I/O to halt for world 'minecraft:overworld'
> 
  
[13:48:32 INFO]: [ChunkHolderManager] Halted I/O scheduler for world 'minecraft:overworld'
> 
  
[13:48:32 INFO]: Saving chunks for level 'ServerLevel[world]'/minecraft:the_nether
> 
  
[13:48:32 INFO]: [ChunkHolderManager] Waiting 60s for chunk system to halt for world 'minecraft:the_nether'
> 
  
[13:48:32 INFO]: [ChunkHolderManager] Halted chunk system for world 'minecraft:the_nether'
> 
  
[13:48:32 INFO]: [ChunkHolderManager] Saving all chunkholders for world 'minecraft:the_nether'
> 
  
[13:48:32 INFO]: [ChunkHolderManager] Saved 0 block chunks, 0 entity chunks, 0 poi chunks in world 'minecraft:the_nether' in 0.02s
> 
  
[13:48:32 INFO]: [ChunkHolderManager] Waiting 60s for chunk I/O to halt for world 'minecraft:the_nether'
> 
  
[13:48:32 INFO]: [ChunkHolderManager] Halted I/O scheduler for world 'minecraft:the_nether'
> 
  
[13:48:32 INFO]: Saving chunks for level 'ServerLevel[world]'/minecraft:the_end
> 
  
[13:48:32 INFO]: [ChunkHolderManager] Waiting 60s for chunk system to halt for world 'minecraft:the_end'
> 
  
[13:48:32 INFO]: [ChunkHolderManager] Halted chunk system for world 'minecraft:the_end'
> 
  
[13:48:32 INFO]: [ChunkHolderManager] Saving all chunkholders for world 'minecraft:the_end'
> 
  
[13:48:32 INFO]: [ChunkHolderManager] Saved 0 block chunks, 0 entity chunks, 0 poi chunks in world 'minecraft:the_end' in 0.02s
> 
  
[13:48:32 INFO]: [ChunkHolderManager] Waiting 60s for chunk I/O to halt for world 'minecraft:the_end'
> 
  
[13:48:32 INFO]: [ChunkHolderManager] Halted I/O scheduler for world 'minecraft:the_end'
> 
  
[13:48:32 INFO]: ThreadedAnvilChunkStorage (world): All chunks are saved
> 
  
[13:48:32 INFO]: ThreadedAnvilChunkStorage (DIM-1): All chunks are saved
> 
  
[13:48:32 INFO]: ThreadedAnvilChunkStorage (DIM1): All chunks are saved
> 
  
[13:48:32 INFO]: ThreadedAnvilChunkStorage: All dimensions are saved
> 
  
[13:48:32 INFO]: Waiting for all RegionFile I/O tasks to complete...
> 
  
[13:48:32 INFO]: All RegionFile I/O tasks to complete
> 
  
[13:48:32 INFO]: [MoonriseCommon] Awaiting termination of worker pool for up to 60s...
> 
  
[13:48:32 INFO]: [MoonriseCommon] Awaiting termination of I/O pool for up to 60s...
> 

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 1 issue found → auto-fixed ✅
  • 🚨 core/src/main/java/com/minekube/connect/bedrock/BedrockIdentityEnforcer.java:45 - The intent requires that “all existing (ConnectConfig, ...) constructors are intentionally preserved.” The public constructor BedrockIdentityEnforcer(ConnectConfig, ConnectLogger, BedrockIdentityKeyProvider, BedrockAdmissionCoordinator) was replaced by a ConfigHolder overload at lines 44–51, breaking existing callers. Restore a delegating compatibility overload or confirm this intentional exception.

🔧 Fix: Restore legacy Bedrock enforcer constructor compatibility
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • ./gradlew :core:test --tests com.minekube.connect.module.BedrockParentInjectorStartupTest --tests com.minekube.connect.bedrock.BedrockIdentityEnforcerTest :spigot:test
  • ./gradlew test
  • ./gradlew :spigot:shadowJar
  • Paper 26.2 build 60 launched with one shaded plugin jar on Java 25 in a clean data directory
  • Restarted Paper with the same data directory and compared token contents and mtime
  • Launched Paper with CONNECT_TOKEN=explicit-token and compared the existing token file SHA-256 before/after
  • Checked Paper logs for JitBindingAlreadySet and verified no occurrences
  • ./gradlew clean and ./gradlew -p build-logic clean
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

…tup crash

The 0.12.0 Bedrock identity work (#57) wired BedrockIdentityEnforcer and
BedrockIdentityKeyProvider with a direct dependency on ConnectConfig. Those
components are resolved by the platform injector while the platform is
constructed, i.e. before ConnectPlatform.init() loads the config. ConnectConfig
is only bound in the child injector that init() creates via ConfigLoadedModule,
so resolving the Bedrock graph from the parent injector forced Guice to create a
just-in-time ConnectConfig binding on the parent, which then collided with the
child binding:

  [Guice/JitBindingAlreadySet]: A just-in-time binding to ConnectConfig was
  already configured on a parent injector at ConfigLoadedModule.config(...)

The plugin crashed in SpigotPlugin.onLoad() before token initialization, so
plugins/Connect/token.json was never created.

Fix: the injected Bedrock components read ConnectConfig lazily through the
parent-bound ConfigHolder (populated by init() before the config is used), so no
ConnectConfig binding is ever established on the parent injector. The existing
(ConnectConfig, ...) constructors are preserved for tests and direct use, and
token precedence (CONNECT_TOKEN, existing token.json, else generate + persist)
is unchanged.

Verified end-to-end on Paper 26.2 (build 60) + Java 25 with a single plugin jar:
- unfixed: JitBindingAlreadySet in onLoad, no token.json (reproduces the report
  with exactly one jar on a clean data dir, so the duplicate-jar theory is false)
- fixed: reaches enabled state and creates token.json; restart reuses the token;
  an explicit CONNECT_TOKEN does not overwrite token.json

Regression guard: core BedrockParentInjectorStartupTest.
@robinbraemer
robinbraemer merged commit 3cab908 into main Jul 22, 2026
1 check passed
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.

1 participant