These handle all game-specific data.

The two primary functions of interest are

bool sendNetMessage(NS_FRPG::FrpgSessionManagerImp *sessionman,ConnectedPlayerData *playerdata, uint64_t type, byte *data, int size) @1405098A0

ulonglong getNetMessage(NS_FRPG::FrpgSessionManagerImp *session_man,ConnectedPlayerData *player, uint type, byte *store_data_here, uint max_message_size) @140509560

Packet Types

This list is a Work-In-Progress. Underlined types are fully understood.
The functions which handle each type are labeled sendTypeXNetMessage and readparseTypeXNetMessage

0. TestMessage.

  • Unused in release, functions still exist but are not called.

1. PlayerFrameUpdate

  • Player Position/Rotation/Hp/maxHp/ezstate related stuff.
  • This packet is used for all attacking (weapons or spells) and general animations.
  • Continually sent on a timer (every 10 frames), not on/per events.
    • The timer/frames till next sent is tracked in (padman->chrman_parent).ticks_remaining_before_packetsent
  • Listens for either a 32 byte struct or a 40 byte struct. Contains all const size variables.
  • Contains position, ezstate animation info, hp, rotation, and other info.
  • The ezstate info fields contain
  • Parent function send_PlayerGeneralInfo.

3. WorldChrSyncLocation

  • Syncs the position of enemies over the network
  • Works based on distance moved, rather then absolute position
  • Continually sent from client to other players

4. WorldChrSyncHealth

  • Syncs the health of enemies over the network
  • Returns current health
  • Continually sent from client to other players

5. PlayerNumber

  • Host sends player number to guest (Host is 1, Phantoms are 2+).
  • Sent on session start or new player connection.
  • This is the first P2P packet sent by the host upon having another player join.

6. PlayerData

  • Guest sends PlayerGameData: names, stats, skin colour, body proportions, etc.
  • This also contains the phantom type (red, white, etc). This doesn't affect kick-out on boss challenge, though
  • Only sent once on connection to host. Not resent when other guests connect.

7. RequestPlayerNumber

  • Request Playerno from host. Responds with type 5
  • This is optional/unused. The host may just send type5 without prompting, on summon.

8. RequestWorldChrSyncInitInfo

  • Sent to the host by an incoming guest on initial connection
  • Triggers the host to send back current state about the world. (WorldChrSync packets, PlayerEquipment, etc)

9. TeamChrType

  • Send/Resends Character Type upon update

10. PlayerEquipment

  • Transfers PlayerGameData regularly between connected players. Send ChrAsm / Appearance
  • This specifies the player sex, all their equipment, and covenant

11. WeaponChange

  • Transfer flags in PlayerGameData. Update Weapon Sheathed

12. MsgMapList

  • Handles EMEVD events
  • Player Event (4000 range).
  • 100% reliable trigger: host weapon bounces off wall.
  • Specifies sending playerNum and receiving playerNum

13. CompleteEvent

  • Unused in release.

14. EventInit

  • event init data, which sends event data from the host for that particular area
  • Includes game progression flags, NG+ flags, etc
  • Only send on initial connection

15. WorldInit

  • world init data (from host only)
  • What enemies are dead
  • Blood decals

16. Grab

  • Put in Throw animation
  • Backstab, Parry, etc

17. GoodsSfxUpdate

  • Passes equipped magic id and item being used between phantoms and host

18. PvPDamage

  • Attack which has hit the other player
  • Is sent regardless of iframes or not (iframes are checked on client side).
  • This is sent for each instance of damage. I.e if a hornet backstab hits 3 times, 3 packets sent.
  • This also contains the speffect that is to be applied. There are separate fields for weapon speffect and spell speffect

19. PvEDamage

20. WorldChrSyncRemoteToHostEnemyAI

  • Syncs the AI state of enemies over the network
  • One way from guest to host

21. WorldChrSyncHostToRemoteEnemyAI

  • Syncs the AI state of enemies over the network
  • One way communication from host to guest
  • Sent from @140352448 a little after guest connection
  • Read at @14035252e

23. One of the packets sent early on when summoning. Precedes type 51.

  • Appears to be all 0s?

32. Unknown

33. EnemyAction

  • Array of elements. Key/value pairs. WorldChrManImp_field0x528_struct->field_0x10[key]->field_0x28[key] is set to the value
  • Animations and behaviours for enemies. Ranging from everything from attacks and idle animations to death animations

34. NPCSpEffectSync

Synchronises NPC SpEffects between players. It is used to tell all other players what SpEffect an NPC has.
Packet size is 8, it contains:

- (2 bytes) The effect ID itself
- (2 bytes) Unknown
- (4 bytes) Timestamp of when the effect was applied

35. ApplyEffectToPlayer

  • SpEffect on hit. Only used for resins (e.g. poison resin) when you hit another player
  • Now 0x10 bytes long after patch

38. Unknown

39. EnemyInit

  • Enemy initial locations, actions, and health states for when you join
  • One packet per enemy

40. ObjDestroy

  • Break environment object. Sent once per object broken

41. ObjUnknown

42. ItemSync

43. ActionObjSync

44. ActionObjFlagUnknown

45. ActionObjFlag

46. Unknown

47. EmvdSync

48. NPCSummon

49. NPCLeave

50. WorldChrSyncFlags

  • Syncs the state of various entities on the map over multiplayer
  • NPC and enemy actions

51. NetRankingDb

52. ApplyEnemyEffect

  • WorldChrSync for enemy/NPC effects

53. WorldChrSyncEffect

  • Syncs the state of various effects?? over multiplayer
  • Read/sent upon joining/creating session. Receives either 0, 1, or 2 (after a mod 4).

54. Unknown

55. SkipCutscene

56. HumanityUpdate

Only received by remote players, the host does not listen for this packet. The size is 2 bytes, and will affect whoever receives the packet.

- (1 byte) Add/Subtract
- (1 byte) Amount of humanity to add or subtract

57. NetVagrantSync

58. SpEffectInitAlloc

  • SpEffectInit in general is applying pre-existing effects to player characters when your game loads them in. For example if TWOP lasts 30 seconds and I have TWOP applied to me 5 seconds before you join, then you join I'll send data to you saying you need to apply TWOP on my character for the remaining 25 seconds
  • This sends the amount of memory you need to allocate to hold the upcoming effects, and also the timestamp for the effect synchronisation

59. SpEffectInitApply

  • See SpEffectInitAlloc
  • This sends the effects IDs, and remaining time that get copied into that allocated buffer and applies them to the player character who sent them

60. BOSStartMatch

61. BOSRegisterPlayer

62. BOSBattleInformation

63. Unknown

64. BOSUpdateScore

66. Unknown

  • Specific to Battle Of Stoicism

67. Unknown

  • Specific to Battle Of Stoicism

68. BOSTimestamp

69. Unknown

  • Specific to Battle Of Stoicism

70. KarmicJustice

  • 1 int (player number) that causes a karmic justice bullet to emit from whichever player has the matching player number
  • Sent by the player with karmic justice, to the other players.

74. ResponseSteamId

  • The steamid of the player, sent in response to packet 75

75. RequestSteamId

  • Request that the player this is sent to send back their steamId

76. PCSpEffectSync

Synchronises SpEffects between players. It is used to tell all other players what SpEffect you have on yourself.
Packet size is 8, it contains:

- (2 bytes) The effect ID itself
- (2 bytes) Unknown
- (4 bytes) Timestamp of when the effect was applied

Invisible Backstab bug in DSR

DSR has a glitch where your first action after a backstab cancel is invisible. The explanation and fix for this is as follows.

The thread which sends the attack a player is currently doing (the ezstate info field in type 1 packets) is separate from the thread which sets what attack the player is doing. So, in rare situations the attacker could go into the backstab animation, then have it canceled, and only the packet which says "attacker in no animation" is sent, when instead both the "attacker in backstab animation" and then "attacker in no animation" packets should be sent.

But why, from the defender's view, do they see the attacker in the backstab animation?
Well, it's because there is a 2nd packet type sent! When the attacker triggers the backstab, the game IMMEDIATELY sends a packet saying "i'm backstabbing this player now" (the throw id in type 16 packets). The defender receives it, and sets both the defender and attacker animations on their client side to reflect this. This occurs even if the defender doesn't also receive the "attacker in backstab animation" packet, because that should be redundant in this case, right? ;)

Ok, but why doesn’t the visual go away when the defender does later receive the "attacker in no animation" packet?
The "attacker in backstab animation" packet must be setting up some special flags.
In the case of a normal backstab cancel, where the “attacker in no animation” packet is just sent sooner than expected, it does cancel the visual. So “attacker in backstab animation” packet must be allowing the visual to be cancelable somehow.

Why, if we remove the code in the "i'm backstabbing this player now" throw type receiving packet function, which sets the attacker animation, does the attacker animation not get set later by the "attacker in backstab animation" packet when it’s received (in a good backstab case)?
Because backstab doesn’t use ezstate.
The ezstate state id for backstabs doesn’t set any animation id like other states do (via ChangeGeneralAnimCategorized, etc).

The way to fix this is to always send the "attacker in backstab animation" ezstate packet right after the "i'm backstabbing this player now" throw packet.
Need to ensure the ezstate packet is actually sending type 225 (backstab).
With dropping packets, this bug can still happen. If the forced sent backstab ezstate animation packet gets dropped repeatedly and doesn't arrive before the 0 ezstate animation packet.
To be safe we should not have to even send the backstab ezstate packet, and just have that performed when the backstab throw packet is received.

Spell desyncing in DS1

In some circumstances, spell visuals can be desynced from the spells themselves in PvP, causing invisible bullets. This only occurs during any homing bullets. The explanation and fix for this is as follows.

This problem is due to the fact that, while spell casting is synced across clients by the type 1 packet, homing bullets don't actually fire off when the spell itself is cast. So while the bullet appearance is synced, when they fire is computed client side based on distance and other factors. And if the two clients don't have the exact same calculation for when the homing bullet is triggered, one side may see the visual for bullet firing when the other doesn't. The side that sees the visual sends a type 18 packet to be sent when the bullet on one client hits the other, and thus damage is taken by invisible bullet.

The code at 0x14042816a, in the HomingBullet_HandleAI function, is hit when a homing bullet is actually fired. Not when the spell itself is cast, but when the bullet actually shots out with a hitbox. We inject code there that gets the attacker and defender player's entity handles, along with the bullet number which is used to identify which of the bullets being rendered is actually fired off. This data is packed into a custom type 1 packet struct along with a magic header to indicate it's special.

The injected code then calls the sendType1NetMessage function, and the custom packet is then passed as an argument to it.

We also inject into the function which receives the type 1 packet, so we can check to see if the received packet is our special spell struct. If so, extract the handles and data, and store it off in an array.

Finally, inject into a bit earlier in the HomingBullet_HandleAI function. This function is called on every active bullet, every frame. We inject at the part which checks if the passed in bullet should be fired, and check if the bullet id and bullet owner are present in our list of received bullet packets. If it is, we set the bullet's target to the one provided by the packet, and it's fired off. We also don't allow any bullet we haven't gotten a packet for to be fired. This effectively syncs the bullet with the netcode.