Skip to content

Fix SSC (Server Side Characters) for Terraria 1.4.5#3164

Merged
hakusaro merged 10 commits intoPryaxis:general-develfrom
Reisenbug:fix-ssc-only
Feb 18, 2026
Merged

Fix SSC (Server Side Characters) for Terraria 1.4.5#3164
hakusaro merged 10 commits intoPryaxis:general-develfrom
Reisenbug:fix-ssc-only

Conversation

@Reisenbug
Copy link
Contributor

@Reisenbug Reisenbug commented Jan 30, 2026

ssc mode broken in 1.4.5
/uploadssc throwing IndexOutOfRangeException in log
ssc failed. Players can join an ssc-enabled server without being reset, but when they click on items in their inventory, those items turn into whatever ssc already has in that slot. Also, the items can't actually be picked up.
etc.

Root Cause:
1.4.5 source code:
image
1.4.4.9 source code:
image
They allocate 200 slots per bank instead of 40.

Changes:
Use PlayerItemSlotID constants instead of sequential slot counter in RestoreCharacter
Add try-finally to ensure IgnoreSSCPackets is reset even if exception occurs
Update HandlePlayerSlot slot validation to use PlayerItemSlotID.Count (990) instead of NetItem.MaxInventory (350)

…adssc failed)

Terraria 1.4.5 changed PlayerItemSlotID to reserve 200 slots per bank instead of 40.
Use PlayerItemSlotID constants (Bank1_0, Bank2_0, etc.) for correct network slot indices.
…doutDyeSlots - 1 (989) instead of NetItem.MaxInventory (350) for the last slot check

Slot validation: use PlayerItemSlotID.Count (990) instead of NetItem.MaxInventory (350)
@Reisenbug
Copy link
Contributor Author

conflicts solved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes successfully solved at least the issue that players lose all of their items in Void Vault and Defender's Forge

// Players send a slot update packet for each inventory slot right after they've joined.
bool bypassTrashCanCheck = false;
if (plr == args.Player.Index && !args.Player.HasSentInventory && slot == NetItem.MaxInventory)
if (plr == args.Player.Index && !args.Player.HasSentInventory && slot == PlayerItemSlotID.Count - 1) // equals to 989, avoid hardcoding
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we derive this somehow in a way that makes it clear what -1 is?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PlayerItemSlotID.Count (990) is the total number of slot IDs, slots range from 0 to 989, so Count - 1 is the last valid slot index. Would a descriptive comment or a local variable like const int lastSyncSlot = PlayerItemSlotID.Count - 1 be clearer

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Either, yes

@Reisenbug Reisenbug requested a review from hakusaro February 16, 2026 18:04
@bartico6
Copy link
Collaborator

random thoughts screamed into the ether on the ssc changes in 1.4.5.0+ (not related to this PR per se, just something to note for future reference if anyone wants to make changes)

  • packet 5 no longer requires an Item instance to send, so all senditem methods (or any SendData(5, ...) calls for that matter) could be collapsed into array-less overloads like so:

    • SendItem(int player, int index);
    • SendItems(int player, int index, int count); => count * SendItem(player, index + i);
  • be careful around personal banks. you want to use the offsets from PlayerItemSlotID (because they're correct) but use array lengths for the bounds of each bank (because PlayerItemSlotID is incorrect) - once the 40-200 discrepancy is resolved, PlayerItemSlotID is Terraria's internal version of what you currently have in NetItem, and the one in NetItem should be deprecated/removed.

  • it could be worthwhile to import these values from PlayerItemSlotID at runtime instead of hardcoding them, as otherwise you'll be returning to this code and reasoning about it every update - though this is guaranteed to temporarily happen anyway due to the bad indexes issue with personal banks

  • the massive loop sending everything from the server in RestorePlayer could be simplified: instead of manually deciding which slots to broadcast and which slots to send only to the player being restored, you can use PlayerItemSlotID.CanRelay[int] with the global index of that item slot. if it's false, send to owner only, if it's true, send to everyone including owner. You could also make it into one loop going 0-990 since you're using NetMessage.SendData() which will automatically convert the index back to the correct Item array anyway

overall fix looks okay from experience making similar changes elsewhere but I cannot personally test tshock's version anytime soon. 👍

Copy link
Member

@hakusaro hakusaro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most likely this can be merged if you wanna make a small comment or change on the magic number. Otherwise I’m reasonably satisfied!

@Reisenbug Reisenbug requested a review from hakusaro February 17, 2026 18:00
Copy link
Member

@hakusaro hakusaro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems legit on paper

@hakusaro hakusaro requested a review from ACaiCat February 18, 2026 17:24
@hakusaro
Copy link
Member

@ACaiCat can you re-review and approve if you're good?

Copy link
Member

@ACaiCat ACaiCat left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me

@ACaiCat
Copy link
Member

ACaiCat commented Feb 18, 2026

image all slots are tracked normally

@hakusaro hakusaro merged commit 65928f1 into Pryaxis:general-devel Feb 18, 2026
8 checks passed
@hakusaro
Copy link
Member

@Nitori-Lab if you can review #3164 I would really appreciate it, but if you can't, nbd!

@Reisenbug Reisenbug deleted the fix-ssc-only branch February 21, 2026 15:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants