Merged
Conversation
…dependency caching
… as context error.
…anguage to xsapi.
Make the reconnect-only resubscribe path explicit instead of hiding it behind a boolean flag on call/subscribe. This keeps the interrupted replacement-socket case readable and documents why pending subscriptions must survive across reconnect waves. Constraint: Reconnect clears the live subscription map before resubscribe completes Rejected: Keep waitReconnect boolean mode | obscured the reconnect-specific control flow Confidence: high Scope-risk: narrow Directive: Preserve the distinction between normal calls and reconnect-wave resubscribe calls
Social reconnect failures now clear the live subscription and leave registered handlers intact until callers subscribe again. MPSD reconnect failures stop automatic tracking instead of forcing old session handles back into service, and the remaining reattach logic only preserves the currently valid handle while marking displaced or invalidated sessions TrackingLost. The reconnect paths are also smaller and easier to audit now: dead unguarded helpers are removed, stale-subscription handling is flattened, shared tracking-loss teardown is centralized, and redundant tests were trimmed in favor of higher-signal behavior coverage. Constraint: Session still exposes an auto-updating handle while tracking is healthy Rejected: Retry every reconnect path to keep sessions alive | hides failure state and revives stale handles Rejected: Remove all tracking and reconcile logic | breaks shoulder-tap routing and current-session auto-sync Confidence: high Scope-risk: moderate Directive: Keep reconnect handling biased toward explicit TrackingLost or caller-driven resubscribe; do not reintroduce unguarded recovery paths Tested: go test ./...; go vet ./... Not-tested: Live Xbox/RTA reconnect behavior against the real service
# Conflicts: # xal/sisu/session_test.go
Cancel stale MPSD refresh waves when subscriptions go inactive, return clearer close errors from social subscribe paths, and keep reconnect-ready callbacks aligned with the current handler.
Gate MPSD reconcile writes on the current background generation, make reconnect success callbacks require a still-live replacement reader, and keep reconnect completion blocked until resubscribe failure handlers finish.
Preserve reconnect callback ordering, keep Wait blocked until tracked failure handlers finish, and avoid stale reconnect success notifications from dead replacement sockets.
… reconnect paths clearly distinguish the callback source handle from the client’s current live subscription.
Resilient reconnect with replacement-socket retry and session rebinding
Keep subscriber interfaces truly nil when New is called without an RTA connection so unavailable-subscription paths return clean errors instead of panicking on typed-nil interface calls.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
lactyy
added a commit
that referenced
this pull request
Apr 20, 2026
* xsapi: Rewrite * xsapi: Major rewrite * xsapi: Remove empty file * mpsd: Synchronize multiplayer session in RTA subscription event * xal/sisu: Implement Authorization Code Flow * xsapi: Address issues pointed out by Go vet * xal/sisu: Support refreshing token * xsapi/social: Work in progress * xal/sisu/session.go: Hold mutex for XSTS tokens while returning Snapshot * xsapi/social: Implement User Profiles * xsapi: Address issues pointed out by Staticcheck * xsapi/social/user.go: Correctly parse UTC times * xal/sisu: Revert changes on caching XSTS tokens from Authorization Token * xal/social: Implement adding/removing relationships * xal/social: Always use 'me' as self ID * client.go: Add CloseContext * example_test.go: Add example test for Client * mpsd/session.go: Add more docs in Session * README.md: Add example code * .github/workflows/go.yml: Check out the repository earlier to enable dependency caching * mpsd/session.go: Add Session.MemberByXUID * xal/xsts/request.go: Use context-deriven HTTP client * mpsd/client.go: Close a session if activity cannot be published. * mpsd/session.go: Return `context.Canceled` instead of `net.ErrClosed` as context error. * social/client.go: Do not reference a pointer-to-pointer respBody in do * client.go: Refactor `internal.RequestOption` to func and move AcceptLanguage to xsapi. * client.go: Move AcceptLanguage to internal * social/user.go: Fix AcceptLanguage * mpsd/activity.go: Remove `omitempty` field tag since it is response type * social/client.go: Refactor New to accept every dependency instead of interface * social/subscription.go: Call handler methods in a goroutine to avoid deadlock * mpsd/session.go: Reflect actual behavior to docs in context error * mpsd/publish.go: Include CustomConstants in session description * internal/option.go: Check for nil option in Apply since it can be passed by caller * mpsd/client.go: Remove unused types * social/user.go: Fix UserTitleHistory.LastTimePlayedText * mpsd/session.go: Use `sync.RWMutex` for cacheMu * mpsd/client.go: Correctly decode response body in SessionReference * example_test.go: Add missing MinecraftAndroid * mpsd/subscription.go: Reset broken subscription on service-side if it contains broken data * xal/internal/client.go: Try removing InsecureSkipVerify. * xal/nsal/title.go: Use Hostname for Host comparison * xal/sisu/session.go: Remove debugging fmt.Println * xal/xast/authenticate.go: Include public key * mpsd/subscription.go: Fix typo * .github/workflows/go.yml: Quote go-version * .github/workflows/go.yml: Quote go-version * client.go: Support signing requests without body * xal/xsts/token.go: Overwrite underlying slice on Privileges.UnmarshalJSON * mpsd/description.go: Remove meaningless `omitempty` field tag on uuid.UUID * social/relationship.go: Fix duplicate headers on methods that use deleteRelationships * xal/sisu/oauth2.go: Fix misuse of %s verb * mpsd/client.go: Check for `http.StatusNotModified` in response status code * mpsd/description.go: Add `omitempty` fie tag to SessionConstants.System * xal/xsts/token.go: Fix docs * xal/xsts/token.go: Fix docs for Privileges.UnmarshalJSON * xal/sisu/oauth2.go: Estimate missing `Token.Expiry` field in `refresh_token` grant * example_test.go: Remove `os.Kill` from targeting signals * xal: Use `http.DefaultClient` by default, minor improvements to use shared method * xal/sisu/session.go: Add docs * xal/nsal/title.go: Add docs and nsal.Title for retrieving title-specific TitleData * mpsd/activity.go: Add `Session.Invite` * xal/nsal: Use context client * xal/sisu: Remove debug tests * xal/sisu: Address staticcheck issues * .github/workflows/go.yml: Fix Go's dependency caching * social: Initial work for receiving notifications * social: Revert weird ConnectionID refactor * social/notification: Add Client.Inbox * client.go: Export Notifications from client-set * client.go: Refactor Client.Notifications to Client.Notification * social/notification: Fix missing prefixes in constants * xsapi: Address issues pointed out by go vet * README.md: Remove trailing space * xsapi: Use different code path for subscription-based methods * xsapi: Add docs * xal/sisu/session_test.go: Address issues pointed out by Go vet * social/user.go: Remove leftover Japanese docs * mpsd/session.go: Add Handler for receiving updates for individual session * mpsd/activity.go: Migrate to internal.RequestOption * mpsd: Add more docs * xal/sisu/session_test.go: Address issues pointed out by Go vet * xal: remove debugging code, fix typo * mpsd/client.go: fix wrong module path * example_test.go: Remove Output line * mpsd/session.go: Add closed check to write * social/subscription.go: Clone xuids when dispatching handler * xsapi: Hide actual network tests behind a build tag * xal/sisu/session_test.go: fix minor bug * mpsd/session.go: Specify If-Match header in CloseContext * social/client.go: Allow retrying CloseContext * xal/xast/authenticate.go: Use internal.ProofKey * xal/nsal/title.go: Use AuthPolicy if SignaturePolicyIndex is absent from Endpoint * xal/nsal/title_test.go: Fix build error * xal/nsal/title.go: Use context client for making requests * xal/nsal/title.go: Match docs to actual behavior * xal/nsal/title.go: Use effective port for matching Endpoint at the moment * xal/nsal/title.go: Expect IP address in matchCIDR * xsapi: Minor improvements * xsapi: Implement user presence * xal/sisu/session_test.go: Remove debugging tests * presence: Add docs * social/client.go: Reset subscription * xal/xsts/authorize.go: Check for nil underlying token * xal/xasd/token_source.go: Don't generate a proof key if token is already present * mpsd/session.go: Clone custom properties/constants * xal/xasd: Add docs to ReuseTokenSource * xal/config.go: Add docs to Config * presence/client.go: Add contractVersion in Batch * mpsd/subscription.go: Use `sync.RWMutex` for iterating sessions * mpsd/subscription.go: Capture session in goroutine * xal/sisu/session_test.go: Remove debugging code * social/relationship.go: Add a note about expected status code in each method * mpsd/session.go: Support absolute URL in parseSessionReference just in case * social/user.go: Fix reversed JSON tags in User.Following/Followed * mpsd/activity.go: Update docs * mpsd: Improve robustness of session creation * mpsd: Allocate fresh SessionDescription to avoid retaining stale members * mpsd/publish.go: Address issues pointed out by Go vet * mpsd/session.go: Do not decode empty response * mpsd/session.go: Use a shared method for updating cache * mpsd: Pass initial ETag in session creation * mpsd/subscription.go: Don't run goroutine in a goroutine * mpsd/publish.go: Wrap cleanup error in session creation * xal/xasd/token_source.go: fix nil token check * social/user.go: Specify `Content-Type` in POST requests * xal: Compensate for clock skew in signed requests * .github/workflows/go.yml: Simplify workflow * xal/sisu/session.go: Add AccountRequiredError * mpsd: Decode initial contents on Publish/Join * client.go: Add a note that RoundTrip may serve for non-Microsoft servers * conn.go: Remove subscription record on Unsubscribe * mpsd/session.go: Allow retrying CloseContext * xsapi: Clarify things * fix: correct session close retry semantics and signed request timing - keep MPSD sessions usable when the remote close request fails - only mark sessions closed after a successful remote leave/close - make repeated successful Session.CloseContext calls idempotent - sign xsapi requests using cached Microsoft server time instead of raw time.Now - expose ServerTime from xal as the public bridge to the internal timestamp cache * go mod tidy * feat: agents.md with link to docs * fix: preserve correct shutdown semantics across xsapi and MPSD - make top-level Client shutdown terminal once RTA close is attempted - persist the first RTA close error and return it on later CloseContext calls - keep subclient cleanup failures retryable instead of marking the client closed too early - distinguish MPSD 200 OK from 204 No Content on session updates - close the local Session handle after a successful leave without treating every successful PUT as a remote delete - only clear cached session state and ETag when MPSD explicitly reports deletion - factor shared Session finalization through closeLocked while keeping delete-specific cleanup separate Constraint: rta.Conn.Close closes its internal state before returning any websocket close error Constraint: MPSD uses 204 No Content to signal deletion as a result of PUT; 200 OK means the session body was updated Rejected: Keep sync.Once-based xsapi CloseContext | masks retry semantics after subclient failures and mishandles terminal RTA close errors Confidence: high Scope-risk: moderate Reversibility: clean Directive: Do not make xsapi Client shutdown retryable after RTA close has been attempted unless rta.Conn.Close semantics change Tested: go test ./... Not-tested: direct unit simulation of non-nil rta.Close error in xsapi without a dedicated test seam * test: cover shutdown retry paths and simplify session close tests - add top-level xsapi close test that exercises retry semantics through a real presence failure path - add mpsd and social tests proving unsubscribe failures preserve state for retry - add session tests for 204 delete reporting, delete-on-no-content cleanup, and 200 close-with-synced-state behavior - keep public snapshot isolation coverage at the Session API boundary - remove the reflection-heavy clone helper test file - replace scheduler- and sleep-based concurrent close timing with an explicit mutex gate - drop unsafe private-field mutation from the xsapi close test Constraint: tests should verify public behavior and package-local invariants without depending on private field layout Rejected: keep clone helper reflection harness | broad but tightly coupled to struct shape and redundant with public snapshot coverage Rejected: keep sleep-based concurrent close test | passes today but is scheduler-dependent Confidence: high Scope-risk: narrow Reversibility: clean Directive: Prefer boundary-level tests over private-field injection when retry behavior can be exercised through a real subclient path Tested: go test ./... Not-tested: none * fix: enforce closed-client behavior and nil-safe constructors - reject authenticated requests after xsapi client shutdown - preserve request-body cleanup on the closed RoundTrip fast path - keep CloseContext retryable when subclient cleanup fails - default nil loggers in mpsd.New and social.New - remove the unused internal.DecodeJSON helper - prevent post-shutdown request races by separating shutdown serialization from lock-free closed-state checks Constraint: CloseContext holds shutdown state while subclient cleanup may still traverse the shared transport Rejected: Protect closed state with closeMu only | RoundTrip/TokenAndSignature would have to take the same lock and could deadlock during shutdown Confidence: high Scope-risk: narrow Directive: Keep post-close request rejection at the shared xsapi transport boundary unless shutdown semantics are redesigned end-to-end Tested: go test ./... Tested: go test -race ./... Not-tested: network-gated SISU/Xbox integration flows * fix: preserve MPSD session correctness around delete and conflict handling - treat synchronized ETag `412` responses as retryable conflicts only for shared session writes - handle MPSD missing-session `GET` as `204 No Content` and tear down the local session state - treat conflict-followed-by-delete and bootstrap-delete paths as normal deleted-session outcomes - keep wildcard update callers like `CloseContext` and `SetMemberCustomProperties` failing on `412` - prevent stale session handles from unregistering replacement sessions by checking pointer identity - tighten `mpsd/session.go` docs around `commit`, `synchronizedUpdate`, precondition modes, and helper behavior - trim redundant tests and simplify remaining MPSD test boilerplate with small local helpers * rta: Implement reconnect logic * rta/conn.go: Cleanup reconnect logic * rta/conn.go: Reset sequence counter on reconnect * rta/conn.go: Add docs * mpsd/subscription.go: Handle subscription reconnect * xsapi: Handle RTA reconnect on subscriptions * Resilient reconnect with replacement-socket retry and session rebinding * Clarify reconnect-specific RTA subscription flow Make the reconnect-only resubscribe path explicit instead of hiding it behind a boolean flag on call/subscribe. This keeps the interrupted replacement-socket case readable and documents why pending subscriptions must survive across reconnect waves. Constraint: Reconnect clears the live subscription map before resubscribe completes Rejected: Keep waitReconnect boolean mode | obscured the reconnect-specific control flow Confidence: high Scope-risk: narrow Directive: Preserve the distinction between normal calls and reconnect-wave resubscribe calls * refactor: simplify subscription logic * slightly simplify subscription code * add check for nil subs * simplify * Prefer explicit reconnect failure over hidden session recovery Social reconnect failures now clear the live subscription and leave registered handlers intact until callers subscribe again. MPSD reconnect failures stop automatic tracking instead of forcing old session handles back into service, and the remaining reattach logic only preserves the currently valid handle while marking displaced or invalidated sessions TrackingLost. The reconnect paths are also smaller and easier to audit now: dead unguarded helpers are removed, stale-subscription handling is flattened, shared tracking-loss teardown is centralized, and redundant tests were trimmed in favor of higher-signal behavior coverage. Constraint: Session still exposes an auto-updating handle while tracking is healthy Rejected: Retry every reconnect path to keep sessions alive | hides failure state and revives stale handles Rejected: Remove all tracking and reconcile logic | breaks shoulder-tap routing and current-session auto-sync Confidence: high Scope-risk: moderate Directive: Keep reconnect handling biased toward explicit TrackingLost or caller-driven resubscribe; do not reintroduce unguarded recovery paths Tested: go test ./...; go vet ./... Not-tested: Live Xbox/RTA reconnect behavior against the real service * go-xsapi: Refactor module name to `github.com/df-mc/go-xsapi/v2` (#6) * Revert "go-xsapi: Refactor module name to `github.com/df-mc/go-xsapi/v2` (#6)" This reverts commit 471a159. * social: address issues pointed out by go vet * fix: use rlock instead of lock * simplify code * small code improvements * fix bugs * fix some bugs * fix docs * fix reconnect and subscription lifecycle edge cases Cancel stale MPSD refresh waves when subscriptions go inactive, return clearer close errors from social subscribe paths, and keep reconnect-ready callbacks aligned with the current handler. * fix bugs * fix: tighten reconnect readiness and reconcile gating Gate MPSD reconcile writes on the current background generation, make reconnect success callbacks require a still-live replacement reader, and keep reconnect completion blocked until resubscribe failure handlers finish. * fix: remove unused method * fix reconnect callback and wait edge cases Preserve reconnect callback ordering, keep Wait blocked until tracked failure handlers finish, and avoid stale reconnect success notifications from dead replacement sockets. * Rename handler-local subscription references to sourceSubscription so reconnect paths clearly distinguish the callback source handle from the client’s current live subscription. * fix nil rta conn handling in constructors Keep subscriber interfaces truly nil when New is called without an RTA connection so unavailable-subscription paths return clean errors instead of panicking on typed-nil interface calls. * session_test.go: resolve conflicts * Update internal/option.go Co-authored-by: lactyy <92302002+lactyy@users.noreply.github.com> --------- Co-authored-by: lactyy <92302002+lactyy@users.noreply.github.com>
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.
dont merge yet