(Feat) Customisation -- replication et persistance
Lot 3 : l'apparence survit desormais au respawn, au changement de map et au relancement du jeu, et les trois autres joueurs la voient. Elle vit sur ASurvivalPlayerState et pas sur le pawn : c'est le JOUEUR qu'elle decrit, pas le corps. Le pawn meurt et reapparait, le PlayerState non -- reapparaitre avec le visage de quelqu'un d'autre n'aurait aucun sens, alors que perdre son inventaire au sol est la regle du jeu. SubmitCharacterAppearance() sur AFpsPlayerController est le point d'entree UNIQUE : il persiste ET annonce au serveur, jamais l'un sans l'autre. Persister sans annoncer laisserait le joueur seul a se voir change, annoncer sans persister le ferait repartir au visage par defaut au lancement suivant. C'est par la que passent les commandes console, et par la que passera l'ecran du lot 4. La course pawn / PlayerState est resolue des DEUX cotes. Chez un client ils arrivent par deux chemins de replication independants et rien ne dit lequel gagne : OnRep_CharacterAppearance monte le personnage si le pawn est deja la, AFpsPlayer::OnRep_PlayerState va chercher l'apparence si c'est l'inverse. Les deux appellent la meme fonction idempotente. Chez l'hote aucun OnRep ne se declenche, d'ou l'appel local dans SetCharacterAppearance -- sans lui, l'hote serait le seul a ne pas voir son propre personnage. Rien n'est valide cote PlayerState, deliberement : borner un index demande le catalogue, que seul UCharacterAppearanceComponent connait, et celui-ci passe deja tout ce qu'il monte par Catalog->Sanitize() sur chaque machine. Piege de test note dans CLAUDE.md : en PIE a deux joueurs, les deux fenetres lisent le meme .ini, donc les deux personnages demarrent identiques. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -154,6 +154,16 @@ public:
|
||||
UFUNCTION(Exec, Category = "Apparence")
|
||||
void EmberAppearanceDump();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Persiste l'apparence du composant et l'annonce au serveur, via le
|
||||
* controller. Appelee apres chaque commande console, pour que le
|
||||
* changement se voie chez les autres et survive au relancement.
|
||||
*/
|
||||
void SubmitCurrentAppearance();
|
||||
|
||||
public:
|
||||
|
||||
/** Sert a l'UI pour afficher la vraie touche mappee, plutot qu'un "E" ecrit en dur. */
|
||||
UFUNCTION(BlueprintPure, Category = "Input")
|
||||
const UInputAction* GetInteractAction() const { return InteractAction; }
|
||||
@@ -230,10 +240,25 @@ public:
|
||||
*/
|
||||
virtual void CalcCamera(float DeltaTime, FMinimalViewInfo& OutResult) override;
|
||||
|
||||
/**
|
||||
* L'autre cote de la course avec le PlayerState.
|
||||
*
|
||||
* Chez un client, le pawn et le PlayerState arrivent par deux chemins de
|
||||
* replication independants : tantot l'apparence est la avant le corps --
|
||||
* ASurvivalPlayerState::OnRep_CharacterAppearance la pose alors lui-meme --
|
||||
* tantot c'est l'inverse, et c'est ici qu'on va la chercher. Ni l'un ni
|
||||
* l'autre ne peut supposer avoir gagne, donc les deux appellent la meme
|
||||
* fonction idempotente.
|
||||
*/
|
||||
virtual void OnRep_PlayerState() override;
|
||||
|
||||
protected:
|
||||
// Called when the game starts or when spawned
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
/** Lit l'apparence sur le PlayerState et la monte, s'il est deja arrive. */
|
||||
void RefreshAppearanceFromPlayerState();
|
||||
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
|
||||
// Appelés par le CharacterMovement quand la capsule change de taille (accroupi)
|
||||
|
||||
Reference in New Issue
Block a user