Skip to content

[BUG] IL2CPP joining client throws in Customer.Awake and duplicates a custom dealer #357

Closed

Description

@ifBars

Summary

An IL2CPP multiplayer client joining a host with four automatic custom NPC roles throws NullReferenceException in native Customer.Awake and ends up with a duplicate custom dealer ID. The strict four-NPC validation gate fails. This was reproduced with PR #353 alone, without either optimization PR (#354 or #355).

Environment

  • Schedule I beta 0.4.7f11, Steam build 25770926, updated and validated with SteamCMD.
  • S1API PR fix: keep packaging-content patches working on game 0.4.7f11 #353 exact head: 8c22c42e2a3703320fc6af58424d0109957cbb84 (beta.8 lineage plus the packaging compatibility change).
  • IL2CPP, MelonLoader 0.7.3, fresh f11 interop wrappers.
  • Two isolated installations with S1API and the same disposable minimal probe registering plain, customer, dealer, and supplier NPC roles.
  • LAN test used GSE Steam emulation with distinct peer identities and the native lobby/FishNet path. Real Steam transport was not tested.
  • Copied save fixture originally created on 0.4.6f8 Alternate. An unrelated custom product descriptor with no installed provider was removed only from the disposable save copy before co-op testing. A newly created f11 save was not tested.

Steps to reproduce

  1. Register one automatic custom NPC for each of the four roles on both peers.
  2. Start the IL2CPP host and load the copied save. Confirm four custom NPCs are present.
  3. Start a second IL2CPP process with a distinct Steam identity and join the host through the native lobby path.
  4. Allow the joining client to follow the host's save load into Main.
  5. Check the client's exception output and the custom NPC registry for all four expected IDs and duplicate entries.

Expected behavior

The client reaches normal gameplay with one instance of each custom NPC and no Customer.Awake exception.

Observed behavior

The host completes its four-role validation. The joining client reaches Main but reports NullReferenceException in native Customer.Awake. Its custom NPC registry contains five entries instead of four, including a duplicate s1api_pr_dealernpc. The client's strict gate times out:

FAIL|Role=sender|Phase=Gameplay1|Reason=Timed out; Scene=Main|NPCs=5

The exception and duplicate entry were observed together; their causal relationship has not been established.

Controls and investigation limits

The #353-only control and the #353+#354+#355 integration reproduce the same exception, duplicate dealer ID, and terminal client failure. Mono two-peer validation passes with distinct identities, shared two-member lobby membership, correct host/client network state, and four expected NPCs on both peers.

This establishes reproduction without the optimization PRs. It does not identify the responsible game/S1API lifecycle path or the first affected version. The latest published prerelease was not separately exercised for this scenario. Complete raw logs and the disposable probe were removed after validation; the exception method, duplicate ID, and terminal marker above were retained.

Acceptance criteria

Isolate the client spawn/initialization failure, explain the duplicate dealer entry, and verify one instance of each role on an IL2CPP joining client without Customer.Awake exceptions. Include a fresh-save control and preserve passing Mono multiplayer behavior.

Activity

  1. added
    bugSomething isn't working
    npcsNative game NPC system
    betaA game update on the beta & alternate-beta steam branches
    HighAPI features not working, or minor gameplay hinderance
    on Oct 8, 2026
  2. ifBars commented on Oct 8, 2026

    @ifBars
    OwnerAuthor

    @Diffuin Investigate please

  3. diffuin commented on Oct 8, 2026

    @diffuin

    Issue investigation

    Confidence: Medium

    The leading hypothesis is incomplete Customer preparation before client Awake. Dealer conversion can also retain two NPC components, explaining an extra client wrapper while the host wraps only one. An unguarded consolidated loader provides a competing explanation if the duplicates are separate objects. The smallest likely fix is to prepare missing Customer data on the inactive prefab and remove a replaced dealer donor after rewiring. The exact native dereference and duplicate object identities still require runtime evidence.

    Recommended next steps

    1. Keep any fix internal. Preserve public signatures, existing omitted-input defaults, explicit configuration, durable IDs, save data, and host authority.
    2. Correct the demonstrated creation path before considering registry changes. Deduplicating NPC.All by ID could conceal an extra component or object.

    Open questions

    • Did the removed probe configure WithCustomerDefaults, and which Customer instance threw?
    • Was consolidated NPCsLoader.Load invoked on the joining client, and did the dealer prefab require conversion from a plain donor?
    Evidence and validation

    Evidence inspected

    Validation performed

    • Six read-only source assertions passed at the reported commit, covering Customer preparation, dealer/supplier donor handling, wrapper registration, and consolidated-loader authority.
    • git diff --check passed and the worktree remains clean. No repository files or game-derived artifacts were modified or copied.
    • dotnet --info failed because dotnet is unavailable. No builds or unit tests ran.

    Runtime validation remaining

    • Capture one failing IL2CPP join with full exception output and Customer.Awake entry state: customerData, DefaultAffinityData, owning NPC/NPCData, dialogue references, and initialization order. Valid data at entry would falsify the missing-data hypothesis.
    • Record both dealer entries' GameObject, component instance, NetworkObject.ObjectId, and spawn state. One object with two components supports donor retention; separate objects require tracing local construction versus repeated network spawning. Matching component identities would require investigating equality or registry insertion.
    • After a candidate fix, validate all four roles on IL2CPP and Mono host/joining-client pairs using both the copied save and a fresh f11 save. Preserve full logs and verify reload/rejoin behavior. Real Steam transport remains untested.
    • Inspect fresh f11 IL2CPP wrappers and actual Harmony patch registration, then run matching MonoMelon and Il2CppMelon builds and contract suites with deployment disabled.
    • Create a pull request to implement this plan and close this issue when merged.
    Diffuin run details
    • Provider: codex
    • Model: gpt-6.1-sol
    • Reasoning: xhigh (Luna advisor: complexity (high); baseline coupled source-backed request)
    • Elapsed: 430s
    • Codex thread: 01a119c0-90da-7573-a53c-28c2ebfcfcb4

    AI notice: Generated with AI assistance and not guaranteed accurate. Verify findings and plans against the current source and runtime.

  4. ifBars commented on Oct 8, 2026

    @ifBars
    OwnerAuthor

    The baseline f11 client reproduction now identifies both paths. Customer.Awake enters with customerData and DefaultAffinityData missing on S1API_CustomerNpc, then throws in Awake_UserLogic. The dealer appears twice in NPC.All on the client, with the same GameObject (-413470) and two different native component IDs (-413474 and -413498). Host count is four; client count is five. Both peers report a two-member lobby and the expected host/client network state.

    I will seed missing customer data on the inactive custom prefab using the existing runtime fallback defaults, preserving donor data and explicit WithCustomerDefaults configuration. Dealer conversion will remove the replaced donor component after rewiring, matching the existing supplier conversion. No registry deduplication or client-authority changes are needed for this reproduction. Validation will cover both runtimes and then combine this fix with the portrait fix and the two optimization PRs.

  5. ifBars commented on Oct 9, 2026

    @ifBars
    OwnerAuthor

    Fixed by #359 and shipped in 3.2.1-beta.9.

    Inactive customer prefabs now receive missing fallback CustomerData before the joining client's native Awake. Existing data and explicit configuration are preserved. Dealer conversion removes the replaced donor NPC component after references are rewired.

    Final beta.9 validation passes on Mono and IL2CPP game beta 0.4.7f11 with MelonLoader 0.7.3: two local peers with separate identities, first load and same-process reload/rejoin, normal gameplay on both peers, four custom NPC roles, and one native NPC component per object. Customer data and affinity exist before Awake, with omitted spending defaults still 100/400; the original customer Awake exception and duplicate dealer registration are absent. Closing as completed. Real Steam transport remains untested, and existing native Contacts warnings remain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    HighAPI features not working, or minor gameplay hinderancebetaA game update on the beta & alternate-beta steam branchesbugSomething isn't workingnpcsNative game NPC system

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions