From 9115546ead8fcc7285414dc937b3655d5c93af0e Mon Sep 17 00:00:00 2001 From: fernando-cortez Date: Tue, 23 Sep 2025 15:06:05 -0400 Subject: [PATCH 1/3] syncing state for downed player on spawn, using onnetworkpostspawn to remove UI race conditions --- .../CharacterSetController.controller | 6 +- Assets/Prefabs/Character/Character.prefab | 69 ++++++++++++++----- Assets/Prefabs/Character/Enemy.prefab | 6 +- Assets/Prefabs/Character/Imp.prefab | 48 ++++++++++++- Assets/Prefabs/Character/ImpBoss.prefab | 55 ++++++++++++++- Assets/Prefabs/Character/PlayerAvatar.prefab | 41 +---------- Assets/Prefabs/Character/VandalImp.prefab | 36 +++++++++- Assets/Prefabs/State/BossRoomState.prefab | 28 +++++--- .../Gameplay/GameState/ServerBossRoomState.cs | 44 +++++++++--- .../Character/ClientCharacter.cs | 7 +- .../Character/ClientPlayerAvatar.cs | 13 ++++ .../ClientPlayerAvatarNetworkAnimator.cs | 59 +++++++++++++++- .../Character/NetworkAvatarGuidState.cs | 55 --------------- .../Character/ServerAnimationHandler.cs | 14 +++- .../Character/ServerCharacter.cs | 44 +++++------- .../GameplayObjects/NetworkLifeState.cs | 4 +- .../GameplayObjects/PersistentPlayer.cs | 13 +--- Assets/Scripts/Gameplay/UI/HeroActionBar.cs | 21 +++--- Assets/Scripts/Gameplay/UI/HeroEmoteBar.cs | 4 +- Assets/Scripts/Gameplay/UI/PartyHUD.cs | 4 +- .../Gameplay/UI/Session/SessionUIMediator.cs | 3 - .../Gameplay/UI/UIStateDisplayHandler.cs | 16 ++--- .../Gameplay/UserInput/ClientInputSender.cs | 4 +- 23 files changed, 378 insertions(+), 216 deletions(-) diff --git a/Assets/Models/Animation Controllers/CharacterSetController.controller b/Assets/Models/Animation Controllers/CharacterSetController.controller index d5061a60f0..c2e7f6a0b6 100644 --- a/Assets/Models/Animation Controllers/CharacterSetController.controller +++ b/Assets/Models/Animation Controllers/CharacterSetController.controller @@ -3481,11 +3481,11 @@ AnimatorStateTransition: m_Mute: 0 m_IsExit: 0 serializedVersion: 3 - m_TransitionDuration: 0.25 + m_TransitionDuration: 0 m_TransitionOffset: 0 - m_ExitTime: 0.75 + m_ExitTime: 0 m_HasExitTime: 0 - m_HasFixedDuration: 1 + m_HasFixedDuration: 0 m_InterruptionSource: 0 m_OrderedInterruption: 1 m_CanTransitionToSelf: 1 diff --git a/Assets/Prefabs/Character/Character.prefab b/Assets/Prefabs/Character/Character.prefab index 4a94e1a992..759353b0da 100644 --- a/Assets/Prefabs/Character/Character.prefab +++ b/Assets/Prefabs/Character/Character.prefab @@ -21,7 +21,6 @@ GameObject: - component: {fileID: 4309865414312164521} - component: {fileID: 753347877357705413} - component: {fileID: -2014321709067566591} - - component: {fileID: 3884788294438199994} m_Layer: 3 m_Name: Character m_TagString: Player @@ -36,13 +35,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4600110157238723781} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &4600110157238723790 MonoBehaviour: @@ -56,13 +55,19 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} m_Name: m_EditorClassIdentifier: - GlobalObjectIdHash: 951099334 + GlobalObjectIdHash: 3952140259 + InScenePlacedSourceGlobalObjectIdHash: 0 + DeferredDespawnTick: 0 + Ownership: 0 AlwaysReplicateAsRoot: 0 SynchronizeTransform: 1 ActiveSceneSynchronization: 0 SceneMigrationSynchronization: 1 + SpawnWithObservers: 1 DontDestroyWithOwner: 0 AutoObjectParentSync: 1 + SyncOwnerTransformWhenParented: 1 + AllowOwnerToParent: 0 --- !u!114 &514105321093282895 MonoBehaviour: m_ObjectHideFlags: 0 @@ -75,6 +80,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 6733907396f36c44891916e5c62f25a0, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 HitPoints: m_InternalValue: 0 --- !u!114 &-5107732197415868221 @@ -89,6 +95,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 818290ebb0ac23541921b4dfc6842778, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 m_LifeState: m_InternalValue: 0 --- !u!114 &7420593339233078707 @@ -103,6 +110,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 920a440eb254ba348915767fd046027a, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 m_ClientCharacter: {fileID: 0} m_CharacterClass: {fileID: 0} m_BrainEnabled: 1 @@ -124,6 +132,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: bc85fa0320dca1545a3e037ee46391cf, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 m_Transform: {fileID: 4600110157238723791} m_DamageCollider: {fileID: 7679526581544062779} --- !u!114 &350177175035117514 @@ -138,6 +147,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 162844b92fe5b9a469bf0cd049b43138, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 m_NetworkAnimator: {fileID: 0} m_VisualizationConfiguration: {fileID: 11400000, guid: 9504973cdecd65749889771972fa0117, type: 2} m_NetworkLifeState: {fileID: -5107732197415868221} @@ -148,10 +158,21 @@ Rigidbody: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4600110157238723781} - serializedVersion: 2 + serializedVersion: 4 m_Mass: 1 m_Drag: 0 m_AngularDrag: 0.05 + m_CenterOfMass: {x: 0, y: 0, z: 0} + m_InertiaTensor: {x: 1, y: 1, z: 1} + m_InertiaRotation: {x: 0, y: 0, z: 0, w: 1} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ImplicitCom: 1 + m_ImplicitTensor: 1 m_UseGravity: 0 m_IsKinematic: 1 m_Interpolate: 0 @@ -187,8 +208,17 @@ CapsuleCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4600110157238723781} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 + serializedVersion: 2 m_Radius: 0.5 m_Height: 2 m_Direction: 1 @@ -205,6 +235,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: fdad0914904cc1e47933ecd7a58514ac, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 m_NavMeshAgent: {fileID: 745390554272530562} m_Rigidbody: {fileID: 3375535791397456380} m_CharLogic: {fileID: 7420593339233078707} @@ -220,6 +251,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 1da3998b52020a949af0999ab7ea2d14, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 m_NetworkLifeState: {fileID: -5107732197415868221} --- !u!114 &-2014321709067566591 MonoBehaviour: @@ -233,6 +265,21 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: e96cb6065543e43c4a752faaa1468eb1, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 + NetworkTransformExpanded: 0 + AutoOwnerAuthorityTickOffset: 1 + PositionInterpolationType: 0 + RotationInterpolationType: 0 + ScaleInterpolationType: 0 + PositionLerpSmoothing: 1 + PositionMaxInterpolationTime: 0.1 + RotationLerpSmoothing: 1 + RotationMaxInterpolationTime: 0.1 + ScaleLerpSmoothing: 1 + ScaleMaxInterpolationTime: 0.1 + AuthorityMode: 0 + TickSyncChildren: 0 + UseUnreliableDeltas: 0 SyncPositionX: 1 SyncPositionY: 0 SyncPositionZ: 1 @@ -249,18 +296,6 @@ MonoBehaviour: UseQuaternionCompression: 0 UseHalfFloatPrecision: 1 InLocalSpace: 0 + SwitchTransformSpaceWhenParented: 0 Interpolate: 1 SlerpPosition: 0 ---- !u!114 &3884788294438199994 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 4600110157238723781} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: ebe9bfd21e47445488dfd84b4e0c9884, type: 3} - m_Name: - m_EditorClassIdentifier: - m_AvatarRegistry: {fileID: 11400000, guid: 48d17d764bff6c643a3dc035fb71c979, type: 2} diff --git a/Assets/Prefabs/Character/Enemy.prefab b/Assets/Prefabs/Character/Enemy.prefab index d8bb5e95e2..a6ee533632 100644 --- a/Assets/Prefabs/Character/Enemy.prefab +++ b/Assets/Prefabs/Character/Enemy.prefab @@ -5,6 +5,7 @@ PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 0} m_Modifications: - target: {fileID: 4600110157238723777, guid: 0d2d836e2e83b754fa1a1c4022d6d65d, type: 3} @@ -25,7 +26,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 4600110157238723790, guid: 0d2d836e2e83b754fa1a1c4022d6d65d, type: 3} propertyPath: GlobalObjectIdHash - value: 951099334 + value: 3885025524 objectReference: {fileID: 0} - target: {fileID: 4600110157238723791, guid: 0d2d836e2e83b754fa1a1c4022d6d65d, type: 3} propertyPath: m_RootOrder @@ -77,4 +78,7 @@ PrefabInstance: objectReference: {fileID: 0} m_RemovedComponents: - {fileID: 4600110157238723776, guid: 0d2d836e2e83b754fa1a1c4022d6d65d, type: 3} + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 0d2d836e2e83b754fa1a1c4022d6d65d, type: 3} diff --git a/Assets/Prefabs/Character/Imp.prefab b/Assets/Prefabs/Character/Imp.prefab index 0075669bfa..d86e1715c1 100644 --- a/Assets/Prefabs/Character/Imp.prefab +++ b/Assets/Prefabs/Character/Imp.prefab @@ -5,12 +5,17 @@ PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 3713729372785093434} m_Modifications: - target: {fileID: 234724737205816310, guid: 1ffde884792e9a44a9fbe049ebb79c9f, type: 3} propertyPath: m_Controller value: objectReference: {fileID: 22100000, guid: 0406729e6e1c12e4d99fd34970073cd0, type: 2} + - target: {fileID: 2663813019036984750, guid: 1ffde884792e9a44a9fbe049ebb79c9f, type: 3} + propertyPath: m_ServerCharacter + value: + objectReference: {fileID: 7684320411818213958} - target: {fileID: 3687104631385136546, guid: 1ffde884792e9a44a9fbe049ebb79c9f, type: 3} propertyPath: m_LocalEulerAnglesHint.x value: 90 @@ -63,11 +68,21 @@ PrefabInstance: propertyPath: m_Name value: ImpGraphics objectReference: {fileID: 0} + - target: {fileID: 8005647249789065143, guid: 1ffde884792e9a44a9fbe049ebb79c9f, type: 3} + propertyPath: m_ClientCharacter + value: + objectReference: {fileID: 3121817358174221070} - target: {fileID: 8005647249789065143, guid: 1ffde884792e9a44a9fbe049ebb79c9f, type: 3} propertyPath: m_ClientCharacterVisualization value: objectReference: {fileID: 3121817358174221070} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: + - targetCorrespondingSourceObject: {fileID: 6839301660383890230, guid: 1ffde884792e9a44a9fbe049ebb79c9f, type: 3} + insertIndex: -1 + addedObject: {fileID: 5401475423617019972} m_SourcePrefab: {fileID: 100100000, guid: 1ffde884792e9a44a9fbe049ebb79c9f, type: 3} --- !u!95 &929086692793228630 stripped Animator: @@ -102,6 +117,8 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: e8d0727d5ae3244e3b569694d3912374, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 + TransitionStateInfoList: [] m_Animator: {fileID: 929086692793228630} --- !u!4 &6486568539699693356 stripped Transform: @@ -113,6 +130,7 @@ PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 0} m_Modifications: - target: {fileID: 1632591904850424414, guid: 64cfd098f62285f42918875fef849e88, type: 3} @@ -145,7 +163,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 3288448142974003546, guid: 64cfd098f62285f42918875fef849e88, type: 3} propertyPath: GlobalObjectIdHash - value: 951099334 + value: 36723199 objectReference: {fileID: 0} - target: {fileID: 3288448142974003546, guid: 64cfd098f62285f42918875fef849e88, type: 3} propertyPath: PrefabHashGenerator @@ -251,6 +269,10 @@ PrefabInstance: propertyPath: m_CharacterClass value: objectReference: {fileID: 11400000, guid: fa81aca63f2fce545a3b9e046b6e42e7, type: 2} + - target: {fileID: 8398944656959553575, guid: 64cfd098f62285f42918875fef849e88, type: 3} + propertyPath: m_ClientCharacter + value: + objectReference: {fileID: 3121817358174221070} - target: {fileID: 8398944656959553575, guid: 64cfd098f62285f42918875fef849e88, type: 3} propertyPath: m_ClientVisualization value: @@ -272,6 +294,15 @@ PrefabInstance: value: 0.8 objectReference: {fileID: 0} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: + - targetCorrespondingSourceObject: {fileID: 3288448142974003547, guid: 64cfd098f62285f42918875fef849e88, type: 3} + insertIndex: -1 + addedObject: {fileID: 6486568539699693356} + m_AddedComponents: + - targetCorrespondingSourceObject: {fileID: 3288448142974003537, guid: 64cfd098f62285f42918875fef849e88, type: 3} + insertIndex: -1 + addedObject: {fileID: -4570750241117365215} m_SourcePrefab: {fileID: 100100000, guid: 64cfd098f62285f42918875fef849e88, type: 3} --- !u!114 &827948680237315002 stripped MonoBehaviour: @@ -301,12 +332,12 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: cead4253912fb1241be383143c5f3b59, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 m_DisplayHealth: 1 m_DisplayName: 0 m_UIStatePrefab: {fileID: -1943162842029199943, guid: 2b07482491a17964380023240087ce16, type: 3} - m_NetworkNameState: {fileID: 0} m_NetworkHealthState: {fileID: 827948680237315002} - m_ServerCharacter: {fileID: 0} + m_NetworkNameState: {fileID: 0} m_BaseHP: {fileID: 11400000, guid: 95633dc134a4e654f863831b22b5681a, type: 2} m_TransformToTrack: {fileID: 6486568539699693356} m_VerticalWorldOffset: 2.2 @@ -327,3 +358,14 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 818290ebb0ac23541921b4dfc6842778, type: 3} m_Name: m_EditorClassIdentifier: +--- !u!114 &7684320411818213958 stripped +MonoBehaviour: + m_CorrespondingSourceObject: {fileID: 8398944656959553575, guid: 64cfd098f62285f42918875fef849e88, type: 3} + m_PrefabInstance: {fileID: 2173896227866280545} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3713729372785093424} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 920a440eb254ba348915767fd046027a, type: 3} + m_Name: + m_EditorClassIdentifier: diff --git a/Assets/Prefabs/Character/ImpBoss.prefab b/Assets/Prefabs/Character/ImpBoss.prefab index 7dae503c9f..80d7d399a1 100644 --- a/Assets/Prefabs/Character/ImpBoss.prefab +++ b/Assets/Prefabs/Character/ImpBoss.prefab @@ -5,6 +5,7 @@ PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 0} m_Modifications: - target: {fileID: 1632591904850424414, guid: 64cfd098f62285f42918875fef849e88, type: 3} @@ -29,7 +30,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 3288448142974003546, guid: 64cfd098f62285f42918875fef849e88, type: 3} propertyPath: GlobalObjectIdHash - value: 951099334 + value: 591709222 objectReference: {fileID: 0} - target: {fileID: 3288448142974003546, guid: 64cfd098f62285f42918875fef849e88, type: 3} propertyPath: PrefabHashGenerator @@ -135,6 +136,10 @@ PrefabInstance: propertyPath: m_CharacterClass value: objectReference: {fileID: 11400000, guid: 1fdbcd02edb11484a91de118f5ef44b3, type: 2} + - target: {fileID: 8398944656959553575, guid: 64cfd098f62285f42918875fef849e88, type: 3} + propertyPath: m_ClientCharacter + value: + objectReference: {fileID: 5970403413429264330} - target: {fileID: 8398944656959553575, guid: 64cfd098f62285f42918875fef849e88, type: 3} propertyPath: m_ClientVisualization value: @@ -156,6 +161,21 @@ PrefabInstance: value: 4 objectReference: {fileID: 0} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: + - targetCorrespondingSourceObject: {fileID: 3288448142974003547, guid: 64cfd098f62285f42918875fef849e88, type: 3} + insertIndex: -1 + addedObject: {fileID: 2561745155600296936} + m_AddedComponents: + - targetCorrespondingSourceObject: {fileID: 3288448142974003537, guid: 64cfd098f62285f42918875fef849e88, type: 3} + insertIndex: -1 + addedObject: {fileID: 3035161527766636097} + - targetCorrespondingSourceObject: {fileID: 3288448142974003537, guid: 64cfd098f62285f42918875fef849e88, type: 3} + insertIndex: -1 + addedObject: {fileID: 7653970338354900554} + - targetCorrespondingSourceObject: {fileID: 3288448142974003537, guid: 64cfd098f62285f42918875fef849e88, type: 3} + insertIndex: -1 + addedObject: {fileID: 8073394427373638825} m_SourcePrefab: {fileID: 100100000, guid: 64cfd098f62285f42918875fef849e88, type: 3} --- !u!114 &848206395698055335 stripped MonoBehaviour: @@ -190,12 +210,12 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: cead4253912fb1241be383143c5f3b59, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 m_DisplayHealth: 1 m_DisplayName: 0 m_UIStatePrefab: {fileID: -1943162842029199943, guid: 2b07482491a17964380023240087ce16, type: 3} - m_NetworkNameState: {fileID: 0} m_NetworkHealthState: {fileID: 848206395698055335} - m_ServerCharacter: {fileID: 0} + m_NetworkNameState: {fileID: 0} m_BaseHP: {fileID: 11400000, guid: f393c51c2bce455478a0b995b8d4e3dd, type: 2} m_TransformToTrack: {fileID: 2561745155600296936} m_VerticalWorldOffset: 0 @@ -212,6 +232,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: b7ee3f8e8fb4491495b03f890258082e, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 m_CharacterName: The Boss --- !u!114 &8073394427373638825 MonoBehaviour: @@ -238,11 +259,23 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 818290ebb0ac23541921b4dfc6842778, type: 3} m_Name: m_EditorClassIdentifier: +--- !u!114 &7646031874161457499 stripped +MonoBehaviour: + m_CorrespondingSourceObject: {fileID: 8398944656959553575, guid: 64cfd098f62285f42918875fef849e88, type: 3} + m_PrefabInstance: {fileID: 2203142923062114684} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3688950541947916333} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 920a440eb254ba348915767fd046027a, type: 3} + m_Name: + m_EditorClassIdentifier: --- !u!1001 &8515429031237761636 PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 3688950541947916327} m_Modifications: - target: {fileID: 234724737205816310, guid: 1e8ae28d24c5683478548d7e96e5ba55, type: 3} @@ -253,6 +286,14 @@ PrefabInstance: propertyPath: m_LocalEulerAnglesHint.x value: 90 objectReference: {fileID: 0} + - target: {fileID: 2663813019036984750, guid: 1e8ae28d24c5683478548d7e96e5ba55, type: 3} + propertyPath: m_ServerCharacter + value: + objectReference: {fileID: 7646031874161457499} + - target: {fileID: 4538447335436592133, guid: 1e8ae28d24c5683478548d7e96e5ba55, type: 3} + propertyPath: m_ClientCharacter + value: + objectReference: {fileID: 5970403413429264330} - target: {fileID: 4538447335436592133, guid: 1e8ae28d24c5683478548d7e96e5ba55, type: 3} propertyPath: m_ClientCharacterVisualization value: @@ -306,6 +347,12 @@ PrefabInstance: value: BossGraphics objectReference: {fileID: 0} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: + - targetCorrespondingSourceObject: {fileID: 6839301660383890230, guid: 1e8ae28d24c5683478548d7e96e5ba55, type: 3} + insertIndex: -1 + addedObject: {fileID: 2816369992019265630} m_SourcePrefab: {fileID: 100100000, guid: 1e8ae28d24c5683478548d7e96e5ba55, type: 3} --- !u!4 &2561745155600296936 stripped Transform: @@ -329,6 +376,8 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: e8d0727d5ae3244e3b569694d3912374, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 + TransitionStateInfoList: [] m_Animator: {fileID: 8461429659892969874} --- !u!114 &5970403413429264330 stripped MonoBehaviour: diff --git a/Assets/Prefabs/Character/PlayerAvatar.prefab b/Assets/Prefabs/Character/PlayerAvatar.prefab index ac931f2f00..ee9b232234 100644 --- a/Assets/Prefabs/Character/PlayerAvatar.prefab +++ b/Assets/Prefabs/Character/PlayerAvatar.prefab @@ -70,6 +70,7 @@ MonoBehaviour: ShowTopMostFoldoutHeaderGroup: 1 m_ClientVisualsAnimator: {fileID: 1829276847453002016} m_VisualizationConfiguration: {fileID: 11400000, guid: 9504973cdecd65749889771972fa0117, type: 2} + m_ServerCharacter: {fileID: 741733315856861890} --- !u!1001 &7831782662127126385 PrefabInstance: m_ObjectHideFlags: 0 @@ -112,7 +113,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 4600110157238723790, guid: 0d2d836e2e83b754fa1a1c4022d6d65d, type: 3} propertyPath: GlobalObjectIdHash - value: 3177087589 + value: 3737985212 objectReference: {fileID: 0} - target: {fileID: 4600110157238723790, guid: 0d2d836e2e83b754fa1a1c4022d6d65d, type: 3} propertyPath: PrefabHashGenerator @@ -218,9 +219,6 @@ PrefabInstance: - targetCorrespondingSourceObject: {fileID: 4600110157238723781, guid: 0d2d836e2e83b754fa1a1c4022d6d65d, type: 3} insertIndex: -1 addedObject: {fileID: 2576537793715222015} - - targetCorrespondingSourceObject: {fileID: 4600110157238723781, guid: 0d2d836e2e83b754fa1a1c4022d6d65d, type: 3} - insertIndex: -1 - addedObject: {fileID: 4727018541459492655} m_SourcePrefab: {fileID: 100100000, guid: 0d2d836e2e83b754fa1a1c4022d6d65d, type: 3} --- !u!114 &741733315856861890 stripped MonoBehaviour: @@ -612,7 +610,7 @@ MonoBehaviour: TriggerNameHash: -1747783153 TransitionIndex: 1 m_Animator: {fileID: 1829276847453002016} - m_NetworkAvatarGuidState: {fileID: 6438351454149486027} + m_AvatarRegistry: {fileID: 11400000, guid: 48d17d764bff6c643a3dc035fb71c979, type: 2} --- !u!114 &7232939173259361562 MonoBehaviour: m_ObjectHideFlags: 0 @@ -649,28 +647,6 @@ MonoBehaviour: m_TransformToTrack: {fileID: 7902288483105483375} m_VerticalWorldOffset: 2.2 m_VerticalScreenOffset: 20 ---- !u!95 &4727018541459492655 -Animator: - serializedVersion: 7 - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6009713983291384756} - m_Enabled: 0 - m_Avatar: {fileID: 0} - m_Controller: {fileID: 0} - m_CullingMode: 2 - m_UpdateMode: 0 - m_ApplyRootMotion: 0 - m_LinearVelocityBlending: 0 - m_StabilizeFeet: 0 - m_AnimatePhysics: 0 - m_WarningMessage: - m_HasTransformHierarchy: 1 - m_AllowConstantClipSamplingOptimization: 1 - m_KeepAnimatorStateOnDisable: 0 - m_WriteDefaultValuesOnDisable: 0 --- !u!4 &6009713983291384766 stripped Transform: m_CorrespondingSourceObject: {fileID: 4600110157238723791, guid: 0d2d836e2e83b754fa1a1c4022d6d65d, type: 3} @@ -687,17 +663,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: bc85fa0320dca1545a3e037ee46391cf, type: 3} m_Name: m_EditorClassIdentifier: ---- !u!114 &6438351454149486027 stripped -MonoBehaviour: - m_CorrespondingSourceObject: {fileID: 3884788294438199994, guid: 0d2d836e2e83b754fa1a1c4022d6d65d, type: 3} - m_PrefabInstance: {fileID: 7831782662127126385} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 6009713983291384756} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 11500000, guid: ebe9bfd21e47445488dfd84b4e0c9884, type: 3} - m_Name: - m_EditorClassIdentifier: --- !u!114 &7751377510591478590 stripped MonoBehaviour: m_CorrespondingSourceObject: {fileID: 514105321093282895, guid: 0d2d836e2e83b754fa1a1c4022d6d65d, type: 3} diff --git a/Assets/Prefabs/Character/VandalImp.prefab b/Assets/Prefabs/Character/VandalImp.prefab index ef208dae0a..4b3ea452a0 100644 --- a/Assets/Prefabs/Character/VandalImp.prefab +++ b/Assets/Prefabs/Character/VandalImp.prefab @@ -5,6 +5,7 @@ PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 176558388678216186} m_Modifications: - target: {fileID: -1890528778572688093, guid: 7c82a6f62673a3b4fb51d5c6d76f7a96, type: 3} @@ -15,6 +16,10 @@ PrefabInstance: propertyPath: GlobalObjectIdHash value: 951099334 objectReference: {fileID: 0} + - target: {fileID: 2663813019036984750, guid: 7c82a6f62673a3b4fb51d5c6d76f7a96, type: 3} + propertyPath: m_ServerCharacter + value: + objectReference: {fileID: 6583879376515408006} - target: {fileID: 6030569013059244219, guid: 7c82a6f62673a3b4fb51d5c6d76f7a96, type: 3} propertyPath: m_Animator value: @@ -68,6 +73,9 @@ PrefabInstance: value: VandalImpGraphics objectReference: {fileID: 0} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: [] + m_AddedComponents: [] m_SourcePrefab: {fileID: 100100000, guid: 7c82a6f62673a3b4fb51d5c6d76f7a96, type: 3} --- !u!95 &1397358572536670155 stripped Animator: @@ -106,6 +114,7 @@ PrefabInstance: m_ObjectHideFlags: 0 serializedVersion: 2 m_Modification: + serializedVersion: 3 m_TransformParent: {fileID: 0} m_Modifications: - target: {fileID: 1632591904850424414, guid: 64cfd098f62285f42918875fef849e88, type: 3} @@ -118,7 +127,7 @@ PrefabInstance: objectReference: {fileID: 0} - target: {fileID: 3288448142974003546, guid: 64cfd098f62285f42918875fef849e88, type: 3} propertyPath: GlobalObjectIdHash - value: 951099334 + value: 3952065690 objectReference: {fileID: 0} - target: {fileID: 3288448142974003547, guid: 64cfd098f62285f42918875fef849e88, type: 3} propertyPath: m_RootOrder @@ -176,6 +185,10 @@ PrefabInstance: propertyPath: m_CharacterClass value: objectReference: {fileID: 11400000, guid: 686e97a1c3abaf243a72ce1c5a9d7a9f, type: 2} + - target: {fileID: 8398944656959553575, guid: 64cfd098f62285f42918875fef849e88, type: 3} + propertyPath: m_ClientCharacter + value: + objectReference: {fileID: 3806176119258990995} - target: {fileID: 8398944656959553575, guid: 64cfd098f62285f42918875fef849e88, type: 3} propertyPath: m_ClientVisualization value: @@ -185,6 +198,15 @@ PrefabInstance: value: 2 objectReference: {fileID: 0} m_RemovedComponents: [] + m_RemovedGameObjects: [] + m_AddedGameObjects: + - targetCorrespondingSourceObject: {fileID: 3288448142974003547, guid: 64cfd098f62285f42918875fef849e88, type: 3} + insertIndex: -1 + addedObject: {fileID: 5009206627901439409} + m_AddedComponents: + - targetCorrespondingSourceObject: {fileID: 3288448142974003537, guid: 64cfd098f62285f42918875fef849e88, type: 3} + insertIndex: -1 + addedObject: {fileID: 2273131261567159713} m_SourcePrefab: {fileID: 100100000, guid: 64cfd098f62285f42918875fef849e88, type: 3} --- !u!1 &176558388678216176 stripped GameObject: @@ -203,6 +225,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: cead4253912fb1241be383143c5f3b59, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 m_DisplayHealth: 1 m_DisplayName: 0 m_UIStatePrefab: {fileID: -1943162842029199943, guid: 2b07482491a17964380023240087ce16, type: 3} @@ -228,3 +251,14 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 6733907396f36c44891916e5c62f25a0, type: 3} m_Name: m_EditorClassIdentifier: +--- !u!114 &6583879376515408006 stripped +MonoBehaviour: + m_CorrespondingSourceObject: {fileID: 8398944656959553575, guid: 64cfd098f62285f42918875fef849e88, type: 3} + m_PrefabInstance: {fileID: 3445720743357571233} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 176558388678216176} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 920a440eb254ba348915767fd046027a, type: 3} + m_Name: + m_EditorClassIdentifier: diff --git a/Assets/Prefabs/State/BossRoomState.prefab b/Assets/Prefabs/State/BossRoomState.prefab index 3cf0d19f82..11c80aa004 100644 --- a/Assets/Prefabs/State/BossRoomState.prefab +++ b/Assets/Prefabs/State/BossRoomState.prefab @@ -26,6 +26,7 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 297185343939699586} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} @@ -40,7 +41,6 @@ Transform: - {fileID: 8725901042666772653} - {fileID: 7239491272522478247} m_Father: {fileID: 0} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &3608714310874968837 MonoBehaviour: @@ -54,13 +54,19 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} m_Name: m_EditorClassIdentifier: - GlobalObjectIdHash: 951099334 + GlobalObjectIdHash: 2824749288 + InScenePlacedSourceGlobalObjectIdHash: 0 + DeferredDespawnTick: 0 + Ownership: 1 AlwaysReplicateAsRoot: 0 SynchronizeTransform: 0 ActiveSceneSynchronization: 0 SceneMigrationSynchronization: 1 + SpawnWithObservers: 1 DontDestroyWithOwner: 0 AutoObjectParentSync: 1 + SyncOwnerTransformWhenParented: 1 + AllowOwnerToParent: 0 --- !u!114 &5762482089640033414 MonoBehaviour: m_ObjectHideFlags: 0 @@ -77,6 +83,7 @@ MonoBehaviour: TypeName: Unity.BossRoom.ApplicationLifecycle.ApplicationController autoRun: 1 autoInjectGameObjects: [] + m_AvatarRegistry: {fileID: 11400000, guid: 48d17d764bff6c643a3dc035fb71c979, type: 2} m_NetcodeHooks: {fileID: 1290852967062359934} m_PlayerPrefab: {fileID: 6009713983291384767, guid: 8237adf32a9b6de4892e6febe6b4bdef, type: 3} m_PlayerSpawnPoints: @@ -100,6 +107,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 6aedfcf74b3f4f248897af16490caa9d, type: 3} m_Name: m_EditorClassIdentifier: + ShowTopMostFoldoutHeaderGroup: 1 --- !u!1 &1088799320945899822 GameObject: m_ObjectHideFlags: 0 @@ -123,13 +131,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1088799320945899822} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 1, z: 0, w: 0} m_LocalPosition: {x: -1, y: 0, z: 2} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5655447013084909147} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0} --- !u!1 &1514641935926907818 GameObject: @@ -154,13 +162,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1514641935926907818} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 1, z: 0, w: 0} m_LocalPosition: {x: 8, y: 0, z: 2} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5655447013084909147} - m_RootOrder: 7 m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0} --- !u!1 &1963938774227499709 GameObject: @@ -185,13 +193,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1963938774227499709} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 1, z: 0, w: 0} m_LocalPosition: {x: 4, y: 0, z: 4} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5655447013084909147} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0} --- !u!1 &4557113922522348671 GameObject: @@ -216,13 +224,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 4557113922522348671} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 1, z: 0, w: 0} m_LocalPosition: {x: 7, y: 0, z: 4} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5655447013084909147} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0} --- !u!1 &6672739281842085047 GameObject: @@ -247,13 +255,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 6672739281842085047} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 1, z: 0, w: 0} m_LocalPosition: {x: -2, y: 0, z: 4} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5655447013084909147} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0} --- !u!1 &7050848846150978064 GameObject: @@ -278,13 +286,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 7050848846150978064} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 1, z: 0, w: 0} m_LocalPosition: {x: 5, y: 0, z: 2} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5655447013084909147} - m_RootOrder: 6 m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0} --- !u!1 &7682807672526693023 GameObject: @@ -309,13 +317,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 7682807672526693023} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 1, z: 0, w: 0} m_LocalPosition: {x: 1, y: 0, z: 4} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5655447013084909147} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0} --- !u!1 &9086465339882682673 GameObject: @@ -340,11 +348,11 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 9086465339882682673} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 1, z: 0, w: 0} m_LocalPosition: {x: 2, y: 0, z: 2} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 5655447013084909147} - m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0} diff --git a/Assets/Scripts/Gameplay/GameState/ServerBossRoomState.cs b/Assets/Scripts/Gameplay/GameState/ServerBossRoomState.cs index ca0610fd15..3293245f12 100644 --- a/Assets/Scripts/Gameplay/GameState/ServerBossRoomState.cs +++ b/Assets/Scripts/Gameplay/GameState/ServerBossRoomState.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using Unity.BossRoom.ConnectionManagement; +using Unity.BossRoom.Gameplay.Configuration; using Unity.BossRoom.Gameplay.GameplayObjects; using Unity.BossRoom.Gameplay.GameplayObjects.Character; using Unity.BossRoom.Gameplay.Messages; @@ -25,6 +26,9 @@ namespace Unity.BossRoom.Gameplay.GameState [RequireComponent(typeof(NetcodeHooks))] public class ServerBossRoomState : GameStateBehaviour { + [SerializeField] + AvatarRegistry m_AvatarRegistry; + [FormerlySerializedAs("m_NetworkWinState")] [SerializeField] PersistentGameState persistentGameState; @@ -192,23 +196,45 @@ void SpawnPlayer(ulong clientId, bool lateJoin) // pass character type from persistent player to avatar var networkAvatarGuidStateExists = - newPlayer.TryGetComponent(out NetworkAvatarGuidState networkAvatarGuidState); + playerNetworkObject.TryGetComponent(out NetworkAvatarGuidState networkAvatarGuidState); Assert.IsTrue(networkAvatarGuidStateExists, - $"NetworkCharacterGuidState not found on player avatar!"); - + $"NetworkCharacterGuidState not found on PersistentPlayer!"); + + var newPlayerNetworkAvatarExists = + newPlayer.TryGetComponent(out ClientPlayerAvatarNetworkAnimator clientPlayerAvatarNetworkAnimator); + + Assert.IsTrue(newPlayerNetworkAvatarExists, + $"ClientPlayerAvatarNetworkAnimator not found on PlayerAvatar!"); + // if reconnecting, set the player's position and rotation to its previous state + // instantiate new NetworkVariables above with a default value to ensure they're ready for use on OnNetworkSpawn if (lateJoin) { - SessionPlayerData? sessionPlayerData = SessionManager.Instance.GetPlayerData(clientId); - if (sessionPlayerData is { HasCharacterSpawned: true }) + var sessionPlayerData = SessionManager.Instance.GetPlayerData(clientId); + if (sessionPlayerData.HasValue) { - physicsTransform.SetPositionAndRotation(sessionPlayerData.Value.PlayerPosition, sessionPlayerData.Value.PlayerRotation); + if (sessionPlayerData.Value.HasCharacterSpawned) + { + physicsTransform.SetPositionAndRotation(sessionPlayerData.Value.PlayerPosition, sessionPlayerData.Value.PlayerRotation); + networkAvatarGuidState.AvatarGuid = clientPlayerAvatarNetworkAnimator.AvatarGuid = + new NetworkVariable(sessionPlayerData.Value.AvatarNetworkGuid); + } + else + { + var randomAvatar = m_AvatarRegistry.GetRandomAvatar().Guid.ToNetworkGuid(); + networkAvatarGuidState.AvatarGuid = clientPlayerAvatarNetworkAnimator.AvatarGuid = + new NetworkVariable(randomAvatar); + var playerData = sessionPlayerData.Value; + playerData.AvatarNetworkGuid = networkAvatarGuidState.AvatarGuid.Value; + SessionManager.Instance.SetPlayerData(clientId, playerData); + } } } - - // instantiate new NetworkVariables with a default value to ensure they're ready for use on OnNetworkSpawn - networkAvatarGuidState.AvatarGuid = new NetworkVariable(persistentPlayer.NetworkAvatarGuidState.AvatarGuid.Value); + else + { + clientPlayerAvatarNetworkAnimator.AvatarGuid = new NetworkVariable(persistentPlayer.NetworkAvatarGuidState.AvatarGuid.Value); + } // pass name from persistent player to avatar if (newPlayer.TryGetComponent(out NetworkNameState networkNameState)) diff --git a/Assets/Scripts/Gameplay/GameplayObjects/Character/ClientCharacter.cs b/Assets/Scripts/Gameplay/GameplayObjects/Character/ClientCharacter.cs index 314683246d..1b1ff54d39 100644 --- a/Assets/Scripts/Gameplay/GameplayObjects/Character/ClientCharacter.cs +++ b/Assets/Scripts/Gameplay/GameplayObjects/Character/ClientCharacter.cs @@ -46,6 +46,7 @@ public class ClientCharacter : NetworkBehaviour public bool CanPerformActions => m_ServerCharacter.CanPerformActions; + [SerializeField] ServerCharacter m_ServerCharacter; public ServerCharacter serverCharacter => m_ServerCharacter; @@ -109,8 +110,9 @@ void Awake() enabled = false; } - public override void OnNetworkSpawn() + protected override void OnNetworkPostSpawn() { + base.OnNetworkPostSpawn(); if (!IsClient || transform.parent == null) { return; @@ -120,8 +122,6 @@ public override void OnNetworkSpawn() m_ClientActionViz = new ClientActionPlayer(this); - m_ServerCharacter = GetComponentInParent(); - m_ServerCharacter.IsStealthy.OnValueChanged += OnStealthyChanged; m_ServerCharacter.MovementStatus.OnValueChanged += OnMovementStatusChanged; OnMovementStatusChanged(MovementStatus.Normal, m_ServerCharacter.MovementStatus.Value); @@ -171,6 +171,7 @@ public override void OnNetworkSpawn() public override void OnNetworkDespawn() { + base.OnNetworkDespawn(); if (m_ServerCharacter) { m_ServerCharacter.IsStealthy.OnValueChanged -= OnStealthyChanged; diff --git a/Assets/Scripts/Gameplay/GameplayObjects/Character/ClientPlayerAvatar.cs b/Assets/Scripts/Gameplay/GameplayObjects/Character/ClientPlayerAvatar.cs index ccf623ccab..c3a102f384 100644 --- a/Assets/Scripts/Gameplay/GameplayObjects/Character/ClientPlayerAvatar.cs +++ b/Assets/Scripts/Gameplay/GameplayObjects/Character/ClientPlayerAvatar.cs @@ -10,11 +10,14 @@ public class ClientPlayerAvatar : NetworkBehaviour ClientPlayerAvatarRuntimeCollection m_PlayerAvatars; public static event Action LocalClientSpawned; + + public static event Action LocalClientPostSpawned; public static event Action LocalClientDespawned; public override void OnNetworkSpawn() { + base.OnNetworkSpawn(); name = "PlayerAvatar" + OwnerClientId; if (IsClient && IsOwner) @@ -28,8 +31,18 @@ public override void OnNetworkSpawn() } } + protected override void OnNetworkPostSpawn() + { + base.OnNetworkPostSpawn(); + if (IsClient && IsOwner) + { + LocalClientPostSpawned?.Invoke(this); + } + } + public override void OnNetworkDespawn() { + base.OnNetworkDespawn(); if (IsClient && IsOwner) { LocalClientDespawned?.Invoke(); diff --git a/Assets/Scripts/Gameplay/GameplayObjects/Character/ClientPlayerAvatarNetworkAnimator.cs b/Assets/Scripts/Gameplay/GameplayObjects/Character/ClientPlayerAvatarNetworkAnimator.cs index 2d9fdf353d..273453ebd1 100644 --- a/Assets/Scripts/Gameplay/GameplayObjects/Character/ClientPlayerAvatarNetworkAnimator.cs +++ b/Assets/Scripts/Gameplay/GameplayObjects/Character/ClientPlayerAvatarNetworkAnimator.cs @@ -1,6 +1,10 @@ +using System; +using Unity.BossRoom.Gameplay.Configuration; +using Unity.BossRoom.Infrastructure; using Unity.Netcode; using Unity.Netcode.Components; using UnityEngine; +using Avatar = Unity.BossRoom.Gameplay.Configuration.Avatar; namespace Unity.BossRoom.Gameplay.GameplayObjects.Character { @@ -13,14 +17,63 @@ namespace Unity.BossRoom.Gameplay.GameplayObjects.Character /// public class ClientPlayerAvatarNetworkAnimator : NetworkAnimator { + [HideInInspector] + public NetworkVariable AvatarGuid = new NetworkVariable(); + + bool m_AvatarInstantiated; + [SerializeField] - NetworkAvatarGuidState m_NetworkAvatarGuidState; + AvatarRegistry m_AvatarRegistry; - bool m_AvatarInstantiated; + Avatar m_Avatar; + public Avatar RegisteredAvatar + { + get + { + if (m_Avatar == null) + { + RegisterAvatar(AvatarGuid.Value.ToGuid()); + } + + return m_Avatar; + } + } + public override void OnNetworkSpawn() { base.OnNetworkSpawn(); + + RegisterAvatar(AvatarGuid.Value.ToGuid()); + } + + void RegisterAvatar(Guid guid) + { + if (guid.Equals(Guid.Empty)) + { + // not a valid Guid + return; + } + + // based on the Guid received, Avatar is fetched from AvatarRegistry + if (!m_AvatarRegistry.TryGetAvatar(guid, out var avatar)) + { + Debug.LogError("Avatar not found!"); + return; + } + + if (m_Avatar != null) + { + // already set, this is an idempotent call, we don't want to Instantiate twice + return; + } + + m_Avatar = avatar; + } + + protected override void OnNetworkPostSpawn() + { + base.OnNetworkPostSpawn(); if (!IsClient || m_AvatarInstantiated) { return; @@ -60,7 +113,7 @@ void InstantiateAvatar() } // spawn avatar graphics GameObject - Instantiate(m_NetworkAvatarGuidState.RegisteredAvatar.Graphics, Animator.transform); + Instantiate(RegisteredAvatar.Graphics, Animator.transform); Animator.Rebind(); diff --git a/Assets/Scripts/Gameplay/GameplayObjects/Character/NetworkAvatarGuidState.cs b/Assets/Scripts/Gameplay/GameplayObjects/Character/NetworkAvatarGuidState.cs index 6548f89373..d0cb1c89b2 100644 --- a/Assets/Scripts/Gameplay/GameplayObjects/Character/NetworkAvatarGuidState.cs +++ b/Assets/Scripts/Gameplay/GameplayObjects/Character/NetworkAvatarGuidState.cs @@ -1,10 +1,7 @@ -using System; -using Unity.BossRoom.Gameplay.Configuration; using Unity.BossRoom.Infrastructure; using Unity.Netcode; using UnityEngine; using UnityEngine.Serialization; -using Avatar = Unity.BossRoom.Gameplay.Configuration.Avatar; namespace Unity.BossRoom.Gameplay.GameplayObjects.Character { @@ -16,57 +13,5 @@ public class NetworkAvatarGuidState : NetworkBehaviour [FormerlySerializedAs("AvatarGuidArray")] [HideInInspector] public NetworkVariable AvatarGuid = new NetworkVariable(); - - [SerializeField] - AvatarRegistry m_AvatarRegistry; - - Avatar m_Avatar; - - public Avatar RegisteredAvatar - { - get - { - if (m_Avatar == null) - { - RegisterAvatar(AvatarGuid.Value.ToGuid()); - } - - return m_Avatar; - } - } - - public void SetRandomAvatar() - { - AvatarGuid.Value = m_AvatarRegistry.GetRandomAvatar().Guid.ToNetworkGuid(); - } - - void RegisterAvatar(Guid guid) - { - if (guid.Equals(Guid.Empty)) - { - // not a valid Guid - return; - } - - // based on the Guid received, Avatar is fetched from AvatarRegistry - if (!m_AvatarRegistry.TryGetAvatar(guid, out var avatar)) - { - Debug.LogError("Avatar not found!"); - return; - } - - if (m_Avatar != null) - { - // already set, this is an idempotent call, we don't want to Instantiate twice - return; - } - - m_Avatar = avatar; - - if (TryGetComponent(out var serverCharacter)) - { - serverCharacter.CharacterClass = avatar.CharacterClass; - } - } } } diff --git a/Assets/Scripts/Gameplay/GameplayObjects/Character/ServerAnimationHandler.cs b/Assets/Scripts/Gameplay/GameplayObjects/Character/ServerAnimationHandler.cs index 85fa6ff692..2cc72cd0d6 100644 --- a/Assets/Scripts/Gameplay/GameplayObjects/Character/ServerAnimationHandler.cs +++ b/Assets/Scripts/Gameplay/GameplayObjects/Character/ServerAnimationHandler.cs @@ -19,10 +19,12 @@ public class ServerAnimationHandler : NetworkBehaviour public NetworkAnimator NetworkAnimator => m_NetworkAnimator; - public override void OnNetworkSpawn() + protected override void OnNetworkPostSpawn() { + base.OnNetworkPostSpawn(); if (IsServer) { + OnLifeStateChanged(LifeState.Unset, m_NetworkLifeState.LifeState.Value); m_NetworkLifeState.LifeState.OnValueChanged += OnLifeStateChanged; } } @@ -32,10 +34,15 @@ void OnLifeStateChanged(LifeState previousValue, LifeState newValue) switch (newValue) { case LifeState.Alive: - NetworkAnimator.SetTrigger(m_VisualizationConfiguration.AliveStateTriggerID); + if (previousValue == LifeState.Fainted) + { + NetworkAnimator.SetTrigger(m_VisualizationConfiguration.AliveStateTriggerID); + } break; case LifeState.Fainted: - NetworkAnimator.SetTrigger(m_VisualizationConfiguration.FaintedStateTriggerID); + NetworkAnimator.SetTrigger(previousValue == LifeState.Unset + ? m_VisualizationConfiguration.EntryFaintedTriggerID + : m_VisualizationConfiguration.FaintedStateTriggerID); break; case LifeState.Dead: NetworkAnimator.SetTrigger(m_VisualizationConfiguration.DeadStateTriggerID); @@ -47,6 +54,7 @@ void OnLifeStateChanged(LifeState previousValue, LifeState newValue) public override void OnNetworkDespawn() { + base.OnNetworkDespawn(); if (IsServer && m_NetworkLifeState != null) { m_NetworkLifeState.LifeState.OnValueChanged -= OnLifeStateChanged; diff --git a/Assets/Scripts/Gameplay/GameplayObjects/Character/ServerCharacter.cs b/Assets/Scripts/Gameplay/GameplayObjects/Character/ServerCharacter.cs index 623af424e8..923ebe7765 100644 --- a/Assets/Scripts/Gameplay/GameplayObjects/Character/ServerCharacter.cs +++ b/Assets/Scripts/Gameplay/GameplayObjects/Character/ServerCharacter.cs @@ -17,8 +17,7 @@ namespace Unity.BossRoom.Gameplay.GameplayObjects.Character /// This class was separated in two to keep client and server context self contained. This way you don't have to continuously ask yourself if code is running client or server side. /// [RequireComponent(typeof(NetworkHealthState), - typeof(NetworkLifeState), - typeof(NetworkAvatarGuidState))] + typeof(NetworkLifeState))] public class ServerCharacter : NetworkBehaviour, ITargetable { [FormerlySerializedAs("m_ClientVisualization")] @@ -30,20 +29,7 @@ public class ServerCharacter : NetworkBehaviour, ITargetable [SerializeField] CharacterClass m_CharacterClass; - public CharacterClass CharacterClass - { - get - { - if (m_CharacterClass == null) - { - m_CharacterClass = m_State.RegisteredAvatar.CharacterClass; - } - - return m_CharacterClass; - } - - set => m_CharacterClass = value; - } + public CharacterClass CharacterClass => m_CharacterClass; /// Indicates how the character's movement should be depicted. public NetworkVariable MovementStatus { get; } = new NetworkVariable(); @@ -99,7 +85,7 @@ public LifeState LifeState /// public CharacterTypeEnum CharacterType => CharacterClass.CharacterType; - private ServerActionPlayer m_ServerActionPlayer; + ServerActionPlayer m_ServerActionPlayer; /// /// The Character's ActionPlayer. This is mainly exposed for use by other Actions. In particular, users are discouraged from @@ -109,16 +95,15 @@ public LifeState LifeState [SerializeField] [Tooltip("If set to false, an NPC character will be denied its brain (won't attack or chase players)")] - private bool m_BrainEnabled = true; + bool m_BrainEnabled = true; [SerializeField] [Tooltip("Setting negative value disables destroying object after it is killed.")] - private float m_KilledDestroyDelaySeconds = 3.0f; + float m_KilledDestroyDelaySeconds = 3.0f; [SerializeField] [Tooltip("If set, the ServerCharacter will automatically play the StartingAction when it is created. ")] - private Action m_StartingAction; - + Action m_StartingAction; [SerializeField] DamageReceiver m_DamageReceiver; @@ -139,18 +124,23 @@ public LifeState LifeState public ServerAnimationHandler serverAnimationHandler => m_ServerAnimationHandler; private AIBrain m_AIBrain; - NetworkAvatarGuidState m_State; void Awake() { m_ServerActionPlayer = new ServerActionPlayer(this); NetLifeState = GetComponent(); NetHealthState = GetComponent(); - m_State = GetComponent(); } public override void OnNetworkSpawn() { + base.OnNetworkSpawn(); + + if (m_CharacterClass == null) + { + m_CharacterClass = GetComponent().RegisteredAvatar.CharacterClass; + } + if (!IsServer) { enabled = false; } else { @@ -175,6 +165,7 @@ public override void OnNetworkSpawn() public override void OnNetworkDespawn() { + base.OnNetworkDespawn(); NetLifeState.LifeState.OnValueChanged -= OnLifeStateChanged; if (m_DamageReceiver) @@ -185,7 +176,6 @@ public override void OnNetworkDespawn() } } - /// /// RPC to send inputs for this character from a client to a server. /// @@ -241,8 +231,6 @@ public void ServerStopChargingUpRpc() void InitializeHitPoints() { - HitPoints = CharacterClass.BaseHP.Value; - if (!IsNpc) { SessionPlayerData? sessionPlayerData = SessionManager.Instance.GetPlayerData(OwnerClientId); @@ -252,9 +240,13 @@ void InitializeHitPoints() if (HitPoints <= 0) { LifeState = LifeState.Fainted; + return; } } } + + HitPoints = CharacterClass.BaseHP.Value; + LifeState = LifeState.Alive; } /// diff --git a/Assets/Scripts/Gameplay/GameplayObjects/NetworkLifeState.cs b/Assets/Scripts/Gameplay/GameplayObjects/NetworkLifeState.cs index 4ca7af80b0..6647d91654 100644 --- a/Assets/Scripts/Gameplay/GameplayObjects/NetworkLifeState.cs +++ b/Assets/Scripts/Gameplay/GameplayObjects/NetworkLifeState.cs @@ -1,4 +1,3 @@ -using System; using Unity.Netcode; using UnityEngine; @@ -6,6 +5,7 @@ namespace Unity.BossRoom.Gameplay.GameplayObjects { public enum LifeState { + Unset, Alive, Fainted, Dead, @@ -17,7 +17,7 @@ public enum LifeState public class NetworkLifeState : NetworkBehaviour { [SerializeField] - NetworkVariable m_LifeState = new NetworkVariable(GameplayObjects.LifeState.Alive); + NetworkVariable m_LifeState = new NetworkVariable(GameplayObjects.LifeState.Unset); public NetworkVariable LifeState => m_LifeState; diff --git a/Assets/Scripts/Gameplay/GameplayObjects/PersistentPlayer.cs b/Assets/Scripts/Gameplay/GameplayObjects/PersistentPlayer.cs index 12a4e31f22..6694f9797a 100644 --- a/Assets/Scripts/Gameplay/GameplayObjects/PersistentPlayer.cs +++ b/Assets/Scripts/Gameplay/GameplayObjects/PersistentPlayer.cs @@ -1,4 +1,3 @@ -using System; using Unity.BossRoom.ConnectionManagement; using Unity.BossRoom.Gameplay.GameplayObjects.Character; using Unity.BossRoom.Utils; @@ -35,6 +34,7 @@ public class PersistentPlayer : NetworkBehaviour public override void OnNetworkSpawn() { + base.OnNetworkSpawn(); gameObject.name = "PersistentPlayer" + OwnerClientId; // Note that this is done here on OnNetworkSpawn in case this NetworkBehaviour's properties are accessed @@ -48,16 +48,6 @@ public override void OnNetworkSpawn() { var playerData = sessionPlayerData.Value; m_NetworkNameState.Name.Value = playerData.PlayerName; - if (playerData.HasCharacterSpawned) - { - m_NetworkAvatarGuidState.AvatarGuid.Value = playerData.AvatarNetworkGuid; - } - else - { - m_NetworkAvatarGuidState.SetRandomAvatar(); - playerData.AvatarNetworkGuid = m_NetworkAvatarGuidState.AvatarGuid.Value; - SessionManager.Instance.SetPlayerData(OwnerClientId, playerData); - } } } } @@ -70,6 +60,7 @@ public override void OnDestroy() public override void OnNetworkDespawn() { + base.OnNetworkDespawn(); RemovePersistentPlayer(); } diff --git a/Assets/Scripts/Gameplay/UI/HeroActionBar.cs b/Assets/Scripts/Gameplay/UI/HeroActionBar.cs index 850703ef6d..eeaa8be194 100644 --- a/Assets/Scripts/Gameplay/UI/HeroActionBar.cs +++ b/Assets/Scripts/Gameplay/UI/HeroActionBar.cs @@ -128,26 +128,31 @@ void RegisterInputSender(ClientPlayerAvatar clientPlayerAvatar) m_InputSender = inputSender; m_InputSender.action1ModifiedCallback += Action1ModifiedCallback; + if (!clientPlayerAvatar.TryGetComponent(out ServerCharacter serverCharacter)) + { + Debug.LogError("ServerCharacter not found on ClientPlayerAvatar!", clientPlayerAvatar); + } + Action action1 = null; - if (m_InputSender.actionState1 != null) + if (serverCharacter.CharacterClass.Skill1) { - GameDataSource.Instance.TryGetActionPrototypeByID(m_InputSender.actionState1.actionID, out action1); + GameDataSource.Instance.TryGetActionPrototypeByID(serverCharacter.CharacterClass.Skill1.ActionID, out action1); } UpdateActionButton(m_ButtonInfo[ActionButtonType.BasicAction], action1); Action action2 = null; - if (m_InputSender.actionState2 != null) + if (serverCharacter.CharacterClass.Skill2) { - GameDataSource.Instance.TryGetActionPrototypeByID(m_InputSender.actionState2.actionID, out action2); + GameDataSource.Instance.TryGetActionPrototypeByID(serverCharacter.CharacterClass.Skill2.ActionID, out action2); } UpdateActionButton(m_ButtonInfo[ActionButtonType.Special1], action2); Action action3 = null; - if (m_InputSender.actionState3 != null) + if (serverCharacter.CharacterClass.Skill3) { - GameDataSource.Instance.TryGetActionPrototypeByID(m_InputSender.actionState3.actionID, out action3); + GameDataSource.Instance.TryGetActionPrototypeByID(serverCharacter.CharacterClass.Skill3.ActionID, out action3); } UpdateActionButton(m_ButtonInfo[ActionButtonType.Special2], action3); @@ -184,7 +189,7 @@ void Awake() m_ToggleEmoteBarAction.action.performed += OnToggleEmoteBarPerformed; - ClientPlayerAvatar.LocalClientSpawned += RegisterInputSender; + ClientPlayerAvatar.LocalClientPostSpawned += RegisterInputSender; ClientPlayerAvatar.LocalClientDespawned += DeregisterInputSender; } @@ -210,7 +215,7 @@ void OnDestroy() m_ToggleEmoteBarAction.action.performed -= OnToggleEmoteBarPerformed; - ClientPlayerAvatar.LocalClientSpawned -= RegisterInputSender; + ClientPlayerAvatar.LocalClientPostSpawned -= RegisterInputSender; ClientPlayerAvatar.LocalClientDespawned -= DeregisterInputSender; } diff --git a/Assets/Scripts/Gameplay/UI/HeroEmoteBar.cs b/Assets/Scripts/Gameplay/UI/HeroEmoteBar.cs index 0ca4f62cbd..b3e5ca53a8 100644 --- a/Assets/Scripts/Gameplay/UI/HeroEmoteBar.cs +++ b/Assets/Scripts/Gameplay/UI/HeroEmoteBar.cs @@ -17,7 +17,7 @@ public class HeroEmoteBar : MonoBehaviour void Awake() { - ClientPlayerAvatar.LocalClientSpawned += RegisterInputSender; + ClientPlayerAvatar.LocalClientPostSpawned += RegisterInputSender; ClientPlayerAvatar.LocalClientDespawned += DeregisterInputSender; } @@ -45,7 +45,7 @@ void DeregisterInputSender() void OnDestroy() { - ClientPlayerAvatar.LocalClientSpawned -= RegisterInputSender; + ClientPlayerAvatar.LocalClientPostSpawned -= RegisterInputSender; ClientPlayerAvatar.LocalClientDespawned -= DeregisterInputSender; } diff --git a/Assets/Scripts/Gameplay/UI/PartyHUD.cs b/Assets/Scripts/Gameplay/UI/PartyHUD.cs index dfc3461981..85fe96e5a9 100644 --- a/Assets/Scripts/Gameplay/UI/PartyHUD.cs +++ b/Assets/Scripts/Gameplay/UI/PartyHUD.cs @@ -96,9 +96,9 @@ void SetHeroData(ClientPlayerAvatar clientPlayerAvatar) m_PartyIds[0] = m_OwnedServerCharacter.NetworkObject.NetworkObjectId; // set hero portrait - if (m_OwnedServerCharacter.TryGetComponent(out NetworkAvatarGuidState avatarGuidState)) + if (m_OwnedServerCharacter.TryGetComponent(out ClientPlayerAvatarNetworkAnimator clientPlayerAvatarNetworkAnimator)) { - m_HeroPortrait.sprite = avatarGuidState.RegisteredAvatar.Portrait; + m_HeroPortrait.sprite = clientPlayerAvatarNetworkAnimator.RegisteredAvatar.Portrait; } SetUIFromSlotData(0, m_OwnedServerCharacter); diff --git a/Assets/Scripts/Gameplay/UI/Session/SessionUIMediator.cs b/Assets/Scripts/Gameplay/UI/Session/SessionUIMediator.cs index d3d3331eb0..ffcb0a4dac 100644 --- a/Assets/Scripts/Gameplay/UI/Session/SessionUIMediator.cs +++ b/Assets/Scripts/Gameplay/UI/Session/SessionUIMediator.cs @@ -1,4 +1,3 @@ -using System; using Unity.BossRoom.Gameplay.Configuration; using TMPro; using Unity.BossRoom.ConnectionManagement; @@ -44,8 +43,6 @@ public class SessionUIMediator : MonoBehaviour const string k_DefaultSessionName = "no-name"; const int k_MaxPlayers = 8; - ISession m_Session; - [Inject] void InjectDependenciesAndInitialize( AuthenticationServiceFacade authenticationServiceFacade, diff --git a/Assets/Scripts/Gameplay/UI/UIStateDisplayHandler.cs b/Assets/Scripts/Gameplay/UI/UIStateDisplayHandler.cs index b9c3730364..8e7ac9dafd 100644 --- a/Assets/Scripts/Gameplay/UI/UIStateDisplayHandler.cs +++ b/Assets/Scripts/Gameplay/UI/UIStateDisplayHandler.cs @@ -41,12 +41,8 @@ public class UIStateDisplayHandler : NetworkBehaviour [SerializeField] NetworkNameState m_NetworkNameState; - ServerCharacter m_ServerCharacter; - ClientPlayerAvatarNetworkAnimator m_ClientPlayerAvatarNetworkAnimator; - NetworkAvatarGuidState m_NetworkAvatarGuidState; - [SerializeField] IntVariable m_BaseHP; @@ -74,13 +70,9 @@ public class UIStateDisplayHandler : NetworkBehaviour // used to compute world position based on target and offsets Vector3 m_WorldPos; - void Awake() - { - m_ServerCharacter = GetComponent(); - } - - public override void OnNetworkSpawn() + protected override void OnNetworkPostSpawn() { + base.OnNetworkPostSpawn(); if (!NetworkManager.Singleton.IsClient) { enabled = false; @@ -110,9 +102,9 @@ public override void OnNetworkSpawn() m_VerticalOffset = new Vector3(0f, m_VerticalScreenOffset, 0f); // if PC, find our graphics transform and update health through callbacks, if displayed - if (TryGetComponent(out m_ClientPlayerAvatarNetworkAnimator) && TryGetComponent(out m_NetworkAvatarGuidState)) + if (TryGetComponent(out m_ClientPlayerAvatarNetworkAnimator)) { - m_BaseHP = m_NetworkAvatarGuidState.RegisteredAvatar.CharacterClass.BaseHP; + m_BaseHP = m_ClientPlayerAvatarNetworkAnimator.RegisteredAvatar.CharacterClass.BaseHP; m_TransformToTrack = m_ClientPlayerAvatarNetworkAnimator.Animator.transform; diff --git a/Assets/Scripts/Gameplay/UserInput/ClientInputSender.cs b/Assets/Scripts/Gameplay/UserInput/ClientInputSender.cs index dcd6a1411e..15c74024a6 100644 --- a/Assets/Scripts/Gameplay/UserInput/ClientInputSender.cs +++ b/Assets/Scripts/Gameplay/UserInput/ClientInputSender.cs @@ -145,8 +145,9 @@ void Awake() m_MainCamera = Camera.main; } - public override void OnNetworkSpawn() + protected override void OnNetworkPostSpawn() { + base.OnNetworkPostSpawn(); if (!IsClient || !IsOwner) { enabled = false; @@ -195,6 +196,7 @@ public override void OnNetworkSpawn() public override void OnNetworkDespawn() { + base.OnNetworkDespawn(); if (m_ServerCharacter) { m_ServerCharacter.TargetId.OnValueChanged -= OnTargetChanged; From 71d9ddfc394b2452f1f69160739888c2fb3d5412 Mon Sep 17 00:00:00 2001 From: fernando-cortez Date: Tue, 23 Sep 2025 16:20:46 -0400 Subject: [PATCH 2/3] changelog addition --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 585e06561b..b295f06bcc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) Additional documentation and release notes are available at [Multiplayer Documentation](https://docs-multiplayer.unity3d.com). +## [unreleased] yyyy-mm-dd + +### Changed +* Moved behaviour of client-side visual Components to OnNetworkPostSpawn method to prevent race conditions on other NetworkBehaviours that initialize on OnNetworkSpawn (#925) +* Responsibility of the initialization of an Avatar's GUID is now deferred to ServerBossRoomState component (#925) This allows the Avatar's NetworkBehaviours to read the GUID value at spawn time. + +### Fixed +* Fixed fainted players not spawning with the correct animation (#925) + ## [3.0.0] 2025-08-06 ### Added From 9f803594a10b53f577b9d4bccfb8bae95feaface Mon Sep 17 00:00:00 2001 From: fernando-cortez Date: Wed, 24 Sep 2025 12:47:00 -0400 Subject: [PATCH 3/3] code standard pass --- .../Gameplay/GameState/ServerBossRoomState.cs | 27 ++++++++++--------- .../Character/ClientPlayerAvatar.cs | 2 +- .../ClientPlayerAvatarNetworkAnimator.cs | 9 +++---- .../Character/ServerCharacter.cs | 18 ++++++++----- Assets/Scripts/Gameplay/UI/HeroActionBar.cs | 23 ++++++++++------ 5 files changed, 45 insertions(+), 34 deletions(-) diff --git a/Assets/Scripts/Gameplay/GameState/ServerBossRoomState.cs b/Assets/Scripts/Gameplay/GameState/ServerBossRoomState.cs index 3293245f12..cb102af88d 100644 --- a/Assets/Scripts/Gameplay/GameState/ServerBossRoomState.cs +++ b/Assets/Scripts/Gameplay/GameState/ServerBossRoomState.cs @@ -14,7 +14,6 @@ using UnityEngine; using UnityEngine.Assertions; using UnityEngine.SceneManagement; -using UnityEngine.Serialization; using VContainer; using Random = UnityEngine.Random; @@ -28,10 +27,6 @@ public class ServerBossRoomState : GameStateBehaviour { [SerializeField] AvatarRegistry m_AvatarRegistry; - - [FormerlySerializedAs("m_NetworkWinState")] - [SerializeField] - PersistentGameState persistentGameState; [SerializeField] NetcodeHooks m_NetcodeHooks; @@ -46,7 +41,7 @@ public class ServerBossRoomState : GameStateBehaviour private List m_PlayerSpawnPointsList = null; - public override GameState ActiveState { get { return GameState.BossRoom; } } + public override GameState ActiveState => GameState.BossRoom; // Wait time constants for switching to post game after the game is won or lost private const float k_WinDelay = 7.0f; @@ -80,6 +75,7 @@ void OnNetworkSpawn() enabled = false; return; } + m_PersistentGameState.Reset(); m_LifeStateChangedEventMessageSubscriber.Subscribe(OnLifeStateChangedEventMessage); @@ -130,7 +126,8 @@ void OnSynchronizeComplete(ulong clientId) } } - void OnLoadEventCompleted(string sceneName, LoadSceneMode loadSceneMode, List clientsCompleted, List clientsTimedOut) + void OnLoadEventCompleted(string sceneName, LoadSceneMode loadSceneMode, List clientsCompleted, + List clientsTimedOut) { if (!InitialSpawnDone && loadSceneMode == LoadSceneMode.Single) { @@ -200,13 +197,13 @@ void SpawnPlayer(ulong clientId, bool lateJoin) Assert.IsTrue(networkAvatarGuidStateExists, $"NetworkCharacterGuidState not found on PersistentPlayer!"); - + var newPlayerNetworkAvatarExists = newPlayer.TryGetComponent(out ClientPlayerAvatarNetworkAnimator clientPlayerAvatarNetworkAnimator); Assert.IsTrue(newPlayerNetworkAvatarExists, $"ClientPlayerAvatarNetworkAnimator not found on PlayerAvatar!"); - + // if reconnecting, set the player's position and rotation to its previous state // instantiate new NetworkVariables above with a default value to ensure they're ready for use on OnNetworkSpawn if (lateJoin) @@ -216,14 +213,15 @@ void SpawnPlayer(ulong clientId, bool lateJoin) { if (sessionPlayerData.Value.HasCharacterSpawned) { - physicsTransform.SetPositionAndRotation(sessionPlayerData.Value.PlayerPosition, sessionPlayerData.Value.PlayerRotation); + physicsTransform.SetPositionAndRotation(sessionPlayerData.Value.PlayerPosition, + sessionPlayerData.Value.PlayerRotation); networkAvatarGuidState.AvatarGuid = clientPlayerAvatarNetworkAnimator.AvatarGuid = new NetworkVariable(sessionPlayerData.Value.AvatarNetworkGuid); } else { var randomAvatar = m_AvatarRegistry.GetRandomAvatar().Guid.ToNetworkGuid(); - networkAvatarGuidState.AvatarGuid = clientPlayerAvatarNetworkAnimator.AvatarGuid = + networkAvatarGuidState.AvatarGuid = clientPlayerAvatarNetworkAnimator.AvatarGuid = new NetworkVariable(randomAvatar); var playerData = sessionPlayerData.Value; playerData.AvatarNetworkGuid = networkAvatarGuidState.AvatarGuid.Value; @@ -233,13 +231,15 @@ void SpawnPlayer(ulong clientId, bool lateJoin) } else { - clientPlayerAvatarNetworkAnimator.AvatarGuid = new NetworkVariable(persistentPlayer.NetworkAvatarGuidState.AvatarGuid.Value); + clientPlayerAvatarNetworkAnimator.AvatarGuid = + new NetworkVariable(persistentPlayer.NetworkAvatarGuidState.AvatarGuid.Value); } // pass name from persistent player to avatar if (newPlayer.TryGetComponent(out NetworkNameState networkNameState)) { - networkNameState.Name = new NetworkVariable(persistentPlayer.NetworkNameState.Name.Value); + networkNameState.Name = + new NetworkVariable(persistentPlayer.NetworkNameState.Name.Value); } // spawn players characters with destroyWithScene = true @@ -266,6 +266,7 @@ void OnLifeStateChangedEventMessage(LifeStateChangedEventMessage message) { BossDefeated(); } + break; default: throw new ArgumentOutOfRangeException(); diff --git a/Assets/Scripts/Gameplay/GameplayObjects/Character/ClientPlayerAvatar.cs b/Assets/Scripts/Gameplay/GameplayObjects/Character/ClientPlayerAvatar.cs index c3a102f384..ed16a616f6 100644 --- a/Assets/Scripts/Gameplay/GameplayObjects/Character/ClientPlayerAvatar.cs +++ b/Assets/Scripts/Gameplay/GameplayObjects/Character/ClientPlayerAvatar.cs @@ -10,7 +10,7 @@ public class ClientPlayerAvatar : NetworkBehaviour ClientPlayerAvatarRuntimeCollection m_PlayerAvatars; public static event Action LocalClientSpawned; - + public static event Action LocalClientPostSpawned; public static event Action LocalClientDespawned; diff --git a/Assets/Scripts/Gameplay/GameplayObjects/Character/ClientPlayerAvatarNetworkAnimator.cs b/Assets/Scripts/Gameplay/GameplayObjects/Character/ClientPlayerAvatarNetworkAnimator.cs index 273453ebd1..eb13ecd60f 100644 --- a/Assets/Scripts/Gameplay/GameplayObjects/Character/ClientPlayerAvatarNetworkAnimator.cs +++ b/Assets/Scripts/Gameplay/GameplayObjects/Character/ClientPlayerAvatarNetworkAnimator.cs @@ -19,11 +19,10 @@ public class ClientPlayerAvatarNetworkAnimator : NetworkAnimator { [HideInInspector] public NetworkVariable AvatarGuid = new NetworkVariable(); - + bool m_AvatarInstantiated; - - [SerializeField] - AvatarRegistry m_AvatarRegistry; + + [SerializeField] AvatarRegistry m_AvatarRegistry; Avatar m_Avatar; @@ -39,7 +38,7 @@ public Avatar RegisteredAvatar return m_Avatar; } } - + public override void OnNetworkSpawn() { base.OnNetworkSpawn(); diff --git a/Assets/Scripts/Gameplay/GameplayObjects/Character/ServerCharacter.cs b/Assets/Scripts/Gameplay/GameplayObjects/Character/ServerCharacter.cs index 923ebe7765..f92de59ec5 100644 --- a/Assets/Scripts/Gameplay/GameplayObjects/Character/ServerCharacter.cs +++ b/Assets/Scripts/Gameplay/GameplayObjects/Character/ServerCharacter.cs @@ -135,13 +135,16 @@ void Awake() public override void OnNetworkSpawn() { base.OnNetworkSpawn(); - + if (m_CharacterClass == null) { m_CharacterClass = GetComponent().RegisteredAvatar.CharacterClass; } - - if (!IsServer) { enabled = false; } + + if (!IsServer) + { + enabled = false; + } else { NetLifeState.LifeState.OnValueChanged += OnLifeStateChanged; @@ -159,6 +162,7 @@ public override void OnNetworkSpawn() var startingAction = new ActionRequestData() { ActionID = m_StartingAction.ActionID }; PlayAction(ref startingAction); } + InitializeHitPoints(); } } @@ -233,7 +237,8 @@ void InitializeHitPoints() { if (!IsNpc) { - SessionPlayerData? sessionPlayerData = SessionManager.Instance.GetPlayerData(OwnerClientId); + SessionPlayerData? sessionPlayerData = + SessionManager.Instance.GetPlayerData(OwnerClientId); if (sessionPlayerData is { HasCharacterSpawned: true }) { HitPoints = sessionPlayerData.Value.CurrentHitPoints; @@ -244,7 +249,7 @@ void InitializeHitPoints() } } } - + HitPoints = CharacterClass.BaseHP.Value; LifeState = LifeState.Alive; } @@ -396,7 +401,6 @@ int GetTotalDamage() /// /// This character's AIBrain. Will be null if this is not an NPC. /// - public AIBrain AIBrain { get { return m_AIBrain; } } - + public AIBrain AIBrain => m_AIBrain; } } diff --git a/Assets/Scripts/Gameplay/UI/HeroActionBar.cs b/Assets/Scripts/Gameplay/UI/HeroActionBar.cs index eeaa8be194..ccad54062c 100644 --- a/Assets/Scripts/Gameplay/UI/HeroActionBar.cs +++ b/Assets/Scripts/Gameplay/UI/HeroActionBar.cs @@ -122,7 +122,8 @@ void RegisterInputSender(ClientPlayerAvatar clientPlayerAvatar) if (m_InputSender != null) { - Debug.LogWarning($"Multiple ClientInputSenders in scene? Discarding sender belonging to {m_InputSender.gameObject.name} and adding it for {inputSender.gameObject.name} "); + Debug.LogWarning( + $"Multiple ClientInputSenders in scene? Discarding sender belonging to {m_InputSender.gameObject.name} and adding it for {inputSender.gameObject.name} "); } m_InputSender = inputSender; @@ -132,11 +133,12 @@ void RegisterInputSender(ClientPlayerAvatar clientPlayerAvatar) { Debug.LogError("ServerCharacter not found on ClientPlayerAvatar!", clientPlayerAvatar); } - + Action action1 = null; if (serverCharacter.CharacterClass.Skill1) { - GameDataSource.Instance.TryGetActionPrototypeByID(serverCharacter.CharacterClass.Skill1.ActionID, out action1); + GameDataSource.Instance.TryGetActionPrototypeByID(serverCharacter.CharacterClass.Skill1.ActionID, + out action1); } UpdateActionButton(m_ButtonInfo[ActionButtonType.BasicAction], action1); @@ -144,7 +146,8 @@ void RegisterInputSender(ClientPlayerAvatar clientPlayerAvatar) Action action2 = null; if (serverCharacter.CharacterClass.Skill2) { - GameDataSource.Instance.TryGetActionPrototypeByID(serverCharacter.CharacterClass.Skill2.ActionID, out action2); + GameDataSource.Instance.TryGetActionPrototypeByID(serverCharacter.CharacterClass.Skill2.ActionID, + out action2); } UpdateActionButton(m_ButtonInfo[ActionButtonType.Special1], action2); @@ -152,7 +155,8 @@ void RegisterInputSender(ClientPlayerAvatar clientPlayerAvatar) Action action3 = null; if (serverCharacter.CharacterClass.Skill3) { - GameDataSource.Instance.TryGetActionPrototypeByID(serverCharacter.CharacterClass.Skill3.ActionID, out action3); + GameDataSource.Instance.TryGetActionPrototypeByID(serverCharacter.CharacterClass.Skill3.ActionID, + out action3); } UpdateActionButton(m_ButtonInfo[ActionButtonType.Special2], action3); @@ -181,9 +185,12 @@ void Awake() { m_ButtonInfo = new Dictionary() { - [ActionButtonType.BasicAction] = new ActionButtonInfo(ActionButtonType.BasicAction, m_BasicActionButton, this), - [ActionButtonType.Special1] = new ActionButtonInfo(ActionButtonType.Special1, m_SpecialAction1Button, this), - [ActionButtonType.Special2] = new ActionButtonInfo(ActionButtonType.Special2, m_SpecialAction2Button, this), + [ActionButtonType.BasicAction] = + new ActionButtonInfo(ActionButtonType.BasicAction, m_BasicActionButton, this), + [ActionButtonType.Special1] = + new ActionButtonInfo(ActionButtonType.Special1, m_SpecialAction1Button, this), + [ActionButtonType.Special2] = + new ActionButtonInfo(ActionButtonType.Special2, m_SpecialAction2Button, this), [ActionButtonType.EmoteBar] = new ActionButtonInfo(ActionButtonType.EmoteBar, m_EmoteBarButton, this), };