Fix Android client game events failing to deserialize in cross-platform network play#10304
Merged
tool4ever merged 2 commits intoCard-Forge:masterfrom Apr 6, 2026
Merged
Conversation
JVM hardcodes serialVersionUID=0 for records, but Android's D8 desugaring computes a different UID, causing InvalidClassException for all GameEvent types in GameEventProxy's inner deserialization. Override readClassDescriptor() to use the local class descriptor when UIDs mismatch, matching the existing pattern in SaveFileData and CObjectInputStream. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
tool4ever
reviewed
Apr 6, 2026
tool4ever
approved these changes
Apr 6, 2026
MostCromulent
added a commit
to MostCromulent/forge
that referenced
this pull request
Apr 6, 2026
Resolve conflict on GameEventProxy.java (deleted on this branch, modified on NetworkPlay/main by Card-Forge#10304) by porting Card-Forge#10304's serialVersionUID fix into TrackableSerializer.ResolvingInputStream — the event deserialization path that replaced GameEventProxy.
Contributor
|
i don't have a problem with hardcoding |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a bug where Android clients connecting to a desktop host cannot see or interact with their hand (and other zones). The game loads and the match screen appears, but all zone-change events (cards entering hand, battlefield, etc.) are silently dropped, leaving the mobile player with an empty/non-interactive board. The desktop host sees everything correctly.
This appears to only affect cross-platform play between desktop (JVM) and Android — both sides built from the same source.
Root Cause
The
GameEventProxyinner serialization (introduced in #10018) serializes game events into byte arrays usingObjectOutputStream, then deserializes on the client withObjectInputStream. This triggers aserialVersionUIDmismatch between desktop and Android:serialVersionUID = 0Lwhen none is explicitly declared.serialVersionUIDbased on class structure (e.g.-841910661983749810forGameEventZone).The result is
InvalidClassExceptionfor every event type —GameEventZone,GameEventShuffle,GameEventFlipCoin,GameEventGameStarted,GameEventCardStatsChanged,GameEventCardChangeZone. The proxy'sunwrapAll()catches these and drops the events with a warning log. SincesetGameView/copyChangedPropsstill works (it doesn't go through the proxy), the underlying data model is correct, but the UI never receives the events that trigger visual updates.Client log showing the failure:
Fix
Override
readClassDescriptor()inGameEventProxy.IdResolvingInputStreamto detectserialVersionUIDmismatches and substitute the local class descriptor. This bypasses the false-positive UID check while preserving normal deserialization — the classes have identical fields, only the UID computation differs.This is safe because the proxy is used for same-version communication — server and client are built from the same source. The UID mismatch is a false positive caused by JVM vs Android computing different UIDs for structurally identical classes. In the worst case (a genuinely incompatible class change between versions), deserialization would fail at the field level with an
IOExceptionrather than at the UID check — the event would still be caught and dropped by the existing error handling inunwrapAll(), same as today.This is an established pattern in the Forge codebase, already used in:
SaveFileData.java— Adventure mode save file compatibilityCObjectInputStream.java— Netty network layer (desktop side)Verification
Built Android APK from this branch and tested on an emulator connecting to a desktop host:
Failed to unwrap GameEventProxywarnings in client log🤖 Generated with Claude Code