(Feat) Add mulitplayer

This commit is contained in:
2026-08-03 10:30:47 +02:00
parent 41d6e6a7f3
commit a4cc59a411
41 changed files with 3418 additions and 125 deletions
@@ -12,12 +12,26 @@
UInteractionComponent::UInteractionComponent()
{
PrimaryComponentTick.bCanEverTick = true;
// Indispensable pour que Server_Interact soit route : un composant non
// replique voit ses RPC serveur silencieusement jetes par le moteur. Aucune
// propriete n'est repliquee pour autant, seul le routage nous interesse.
SetIsReplicatedByDefault(true);
}
void UInteractionComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// Le trace ne sert qu'a afficher un prompt, donc uniquement a celui qui
// regarde. Sans ce filtre, l'hote lancerait un trace physique pour CHACUN
// des quatre pawns a 20 Hz, dont trois dont il n'affichera jamais l'UI.
const APawn* OwnerPawn = Cast<APawn>(GetOwner());
if (!OwnerPawn || !OwnerPawn->IsLocallyControlled())
{
return;
}
// On espace les traces : c'est une requete physique, pas gratuite, et
// l'affichage du prompt n'a pas besoin d'etre rafraichi chaque frame.
TimeSinceLastTrace += DeltaTime;
@@ -158,14 +172,55 @@ void UInteractionComponent::TryInteract()
return;
}
IInteractable::Execute_Interact(Target, GetOwner());
// L'hote agit directement, le client demande. Le test porte sur l'autorite
// et non sur "suis-je un client" : sur un serveur d'ecoute, le joueur qui
// heberge passe par la premiere branche et ne paie aucune latence.
if (GetOwner()->HasAuthority())
{
PerformInteract(Target);
}
else
{
Server_Interact(Target);
}
// L'interaction vient peut-etre de detruire la cible, ou de la rendre
// non-interactive (porte maintenant ouverte). On reevalue tout de suite
// plutot que d'attendre le prochain trace periodique.
//
// Cote client la cible est encore la a cet instant -- le serveur n'a pas
// encore repondu. Ce n'est pas grave : la destruction repliquee videra
// FocusedActor toute seule, et bHasFocusedActor fera diffuser la transition.
UpdateFocus();
}
void UInteractionComponent::Server_Interact_Implementation(AActor* Target)
{
PerformInteract(Target);
}
void UInteractionComponent::PerformInteract(AActor* Target)
{
AActor* Owner = GetOwner();
if (!Owner || !Owner->HasAuthority() || !IsValidInteractable(Target))
{
return;
}
// Le client a vise il y a un aller-retour : entre-temps un autre joueur a pu
// ramasser l'objet, ou s'en eloigner. On revalide donc la distance ici, avec
// la meme portee que le trace mais elargie par ServerRangeTolerance.
const float MaxDistance = InteractionRange * ServerRangeTolerance;
if (FVector::DistSquared(Owner->GetActorLocation(), Target->GetActorLocation()) > FMath::Square(MaxDistance))
{
UE_LOG(LogTemp, Verbose, TEXT("UInteractionComponent : %s a demande %s, hors de portee cote serveur."),
*GetNameSafe(Owner), *GetNameSafe(Target));
return;
}
IInteractable::Execute_Interact(Target, Owner);
}
void UInteractionComponent::RefreshPrompt()
{
AActor* Target = FocusedActor.Get();