Skip to content

Follow-up fixes 2#5

Merged
lactyy merged 178 commits intodf-mc:mainfrom
lactyy:rewrite
Apr 20, 2026
Merged

Follow-up fixes 2#5
lactyy merged 178 commits intodf-mc:mainfrom
lactyy:rewrite

Conversation

@HashimTheArab
Copy link
Copy Markdown
Contributor

dont merge yet

lactyy added 30 commits January 23, 2026 02:20
HashimTheArab and others added 28 commits April 5, 2026 13:26
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 lactyy merged commit ce1ab49 into df-mc:main Apr 20, 2026
1 check passed
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>
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