5970db0512
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>
74 lines
2.7 KiB
C++
74 lines
2.7 KiB
C++
// Fill out your copyright notice in the Description page of Project Settings.
|
|
|
|
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "GameFramework/PlayerState.h"
|
|
#include "CharacterAppearanceTypes.h"
|
|
#include "SurvivalPlayerState.generated.h"
|
|
|
|
/**
|
|
* Ce que les AUTRES joueurs savent de toi : le pseudo, porte par APlayerState
|
|
* lui-meme, et l'apparence du personnage.
|
|
*
|
|
* La regle de partage avec le pawn est simple : le PlayerState survit a la mort
|
|
* du pawn, le pawn non. L'inventaire et les jauges restent donc sur le pawn,
|
|
* puisque la regle du jeu veut qu'on perde tout au sol en mourant -- alors que
|
|
* reapparaitre avec le visage de quelqu'un d'autre n'aurait aucun sens.
|
|
*
|
|
* C'est exactement pour cette raison que l'apparence est ici et non sur
|
|
* AFpsPlayer : elle appartient au JOUEUR, pas au corps.
|
|
*/
|
|
UCLASS()
|
|
class SURVIVAL_PROJET_API ASurvivalPlayerState : public APlayerState
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
public:
|
|
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
|
|
|
UFUNCTION(BlueprintPure, Category = "Apparence")
|
|
const FCharacterAppearance& GetCharacterAppearance() const { return CharacterAppearance; }
|
|
|
|
/**
|
|
* A n'appeler que sur le SERVEUR : c'est lui qui fait autorite sur ce que
|
|
* les autres voient.
|
|
*
|
|
* Applique aussi en local dans la foulee, parce qu'un OnRep ne se declenche
|
|
* jamais sur la machine qui ecrit. Sans ce second appel, l'hote serait le
|
|
* seul a ne pas voir sa propre apparence -- le genre de bug qui ne se
|
|
* remarque qu'en testant a deux, et du mauvais cote.
|
|
*/
|
|
void SetCharacterAppearance(const FCharacterAppearance& InAppearance);
|
|
|
|
/**
|
|
* Pousse l'apparence sur le pawn possede, s'il existe deja.
|
|
*
|
|
* Idempotente et appelee des DEUX cotes de la course : par l'OnRep quand
|
|
* l'apparence arrive avant le pawn, et par le pawn lui-meme a son BeginPlay
|
|
* quand c'est l'inverse. On ne sait pas lequel des deux gagne -- chez un
|
|
* client le pawn et le PlayerState arrivent par deux chemins de replication
|
|
* independants -- donc les deux doivent savoir se debrouiller seuls.
|
|
*/
|
|
void ApplyAppearanceToPawn();
|
|
|
|
protected:
|
|
/**
|
|
* Repliquee a TOUT LE MONDE et pas au seul proprietaire : c'est justement
|
|
* ce que les autres doivent voir. Treize octets par joueur, envoyes une
|
|
* fois -- l'apparence ne change pas en cours de partie.
|
|
*/
|
|
UPROPERTY(ReplicatedUsing = OnRep_CharacterAppearance)
|
|
FCharacterAppearance CharacterAppearance;
|
|
|
|
UFUNCTION()
|
|
void OnRep_CharacterAppearance();
|
|
|
|
/**
|
|
* Le moteur engendre un NOUVEAU PlayerState a chaque voyage de map, et
|
|
* recopie l'ancien par ici. Sans cette surcharge, changer de carte remettrait
|
|
* tout le monde au visage par defaut.
|
|
*/
|
|
virtual void CopyProperties(APlayerState* PlayerState) override;
|
|
};
|