feat(client): cliente autoritativo da pose + proxy visivel (ADR 0040/0041)
ZMMOPlayerCharacter: SendInputAxis agora envia viewYawDeg (GetController()->GetControlRotation().Yaw, fallback para GetActorRotation().Yaw) + posicao autoritativa (GetActorLocation) + velocidade XY (GetVelocity) — bate com o C_INPUT_AXIS de 41 bytes do servidor (ADR 0041). ZMMOPlayerProxy: - MOVE_Walking + RotationRate=0 + bRunPhysicsWithNoController=false para o AnimBP do Quinn transitar entre Idle/Run sem o CMC integrar fisica. - Defaults visuais SKM_Quinn_Simple + ABP_Unarmed no construtor para o proxy ser visivel em PIE sem precisar de BP filha. - Line trace local de fallback (ECC_Visibility, 5m down) ajusta Z quando o snapshot Z vier estranho. Armadilha conhecida: Landscape do UE5 esta em ECC_WorldStatic — quando o trace falhar, AdjustedPos.Z fica igual ao snapshot. Fix do canal fica no backlog (bug do proxy sumir ao encostar parede). ZMMO.uproject: indentacao tabs + plugin "meshy" registrado disabled. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -344,13 +344,28 @@ void AZMMOPlayerCharacter::FlushInputAxisToServer(const float DeltaSeconds)
|
|||||||
|
|
||||||
++InputSequence;
|
++InputSequence;
|
||||||
const int32 ClientTimeMs = static_cast<int32>(FPlatformTime::Seconds() * 1000.0);
|
const int32 ClientTimeMs = static_cast<int32>(FPlatformTime::Seconds() * 1000.0);
|
||||||
|
// ADR 0040: yaw da camara/controller para que o servidor consiga rotar o
|
||||||
|
// input (referencial-camara) para velocidade mundial. Fallback para o yaw
|
||||||
|
// do actor se nao houver controller (e.g. tela de loading).
|
||||||
|
const float ViewYawDeg = (GetController()
|
||||||
|
? static_cast<float>(GetController()->GetControlRotation().Yaw)
|
||||||
|
: static_cast<float>(GetActorRotation().Yaw));
|
||||||
|
// ADR 0041: cliente autoritativo da posicao XYZ + velocidade XY. O CMC ja
|
||||||
|
// integrou o input local antes deste flush, portanto `GetActorLocation` e
|
||||||
|
// `GetVelocity` reflectem o estado real que o servidor deve replicar para
|
||||||
|
// os outros clientes (sujeito ao clamp anti-cheat).
|
||||||
|
const FVector PosCm = GetActorLocation();
|
||||||
|
const FVector Vel = GetVelocity();
|
||||||
ZeusNetwork->SendInputAxis(
|
ZeusNetwork->SendInputAxis(
|
||||||
PendingMoveForward,
|
PendingMoveForward,
|
||||||
PendingMoveRight,
|
PendingMoveRight,
|
||||||
bPendingJumpPressed,
|
bPendingJumpPressed,
|
||||||
bPendingJumpReleased,
|
bPendingJumpReleased,
|
||||||
InputSequence,
|
InputSequence,
|
||||||
ClientTimeMs);
|
ClientTimeMs,
|
||||||
|
ViewYawDeg,
|
||||||
|
PosCm,
|
||||||
|
FVector2D(Vel.X, Vel.Y));
|
||||||
|
|
||||||
SendAccumulatorSec = 0.0f;
|
SendAccumulatorSec = 0.0f;
|
||||||
TimeSinceLastSendSec = 0.0f;
|
TimeSinceLastSendSec = 0.0f;
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
#include "ZMMOPlayerProxy.h"
|
#include "ZMMOPlayerProxy.h"
|
||||||
|
|
||||||
|
#include "Animation/AnimInstance.h"
|
||||||
#include "Components/CapsuleComponent.h"
|
#include "Components/CapsuleComponent.h"
|
||||||
|
#include "Components/SkeletalMeshComponent.h"
|
||||||
|
#include "Engine/SkeletalMesh.h"
|
||||||
|
#include "Engine/World.h"
|
||||||
#include "GameFramework/CharacterMovementComponent.h"
|
#include "GameFramework/CharacterMovementComponent.h"
|
||||||
|
#include "UObject/ConstructorHelpers.h"
|
||||||
|
#include "ZMMO.h"
|
||||||
|
|
||||||
AZMMOPlayerProxy::AZMMOPlayerProxy(const FObjectInitializer& ObjectInitializer)
|
AZMMOPlayerProxy::AZMMOPlayerProxy(const FObjectInitializer& ObjectInitializer)
|
||||||
: Super(ObjectInitializer)
|
: Super(ObjectInitializer)
|
||||||
@@ -19,8 +25,54 @@ AZMMOPlayerProxy::AZMMOPlayerProxy(const FObjectInitializer& ObjectInitializer)
|
|||||||
UCharacterMovementComponent* CMC = GetCharacterMovement();
|
UCharacterMovementComponent* CMC = GetCharacterMovement();
|
||||||
if (CMC)
|
if (CMC)
|
||||||
{
|
{
|
||||||
|
// MOVE_Walking (em vez de MOVE_None) e necessario para o AnimBP do Quinn
|
||||||
|
// transitar entre Idle/Run — a state machine consulta `IsFalling()` /
|
||||||
|
// `MovementMode` no CMC mesmo quando este nao integra fisica. Mantemos
|
||||||
|
// `bOrientRotationToMovement=false` e `RotationRate=0` porque a
|
||||||
|
// orientacao e ditada pelo snapshot do servidor; `bRunPhysicsWithNoController`
|
||||||
|
// evita que o CMC tente integrar movimento quando nao ha PlayerController.
|
||||||
|
// Espelha [F:/ZeusV1/ZeusClient ZeusRemoteCharacter:34-38].
|
||||||
CMC->bOrientRotationToMovement = false;
|
CMC->bOrientRotationToMovement = false;
|
||||||
CMC->SetMovementMode(MOVE_None);
|
CMC->RotationRate = FRotator::ZeroRotator;
|
||||||
|
CMC->bRunPhysicsWithNoController = false;
|
||||||
|
CMC->MaxWalkSpeed = 600.0f;
|
||||||
|
CMC->SetMovementMode(MOVE_Walking);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defaults visuais (mesh + AnimBP) em paridade com `AZMMOPlayerCharacter`.
|
||||||
|
// O `UZMMOWorldSubsystem` instancia `AZMMOPlayerProxy::StaticClass()`
|
||||||
|
// directamente — sem este bloco o `MeshComponent` ficaria sem
|
||||||
|
// `SkeletalMesh`/`AnimInstance` e o proxy seria invisivel em PIE. Uma BP
|
||||||
|
// filha continua opcional (basta atribuir em `RemoteEntityClasses[Player]`).
|
||||||
|
if (USkeletalMeshComponent* MeshComponent = GetMesh())
|
||||||
|
{
|
||||||
|
static ConstructorHelpers::FObjectFinder<USkeletalMesh> QuinnMesh(
|
||||||
|
TEXT("/Game/Characters/Mannequins/Meshes/SKM_Quinn_Simple.SKM_Quinn_Simple"));
|
||||||
|
static ConstructorHelpers::FClassFinder<UAnimInstance> QuinnAnimBp(
|
||||||
|
TEXT("/Game/Characters/Mannequins/Anims/Unarmed/ABP_Unarmed"));
|
||||||
|
|
||||||
|
if (QuinnMesh.Succeeded())
|
||||||
|
{
|
||||||
|
MeshComponent->SetSkeletalMesh(QuinnMesh.Object);
|
||||||
|
MeshComponent->SetRelativeLocation(FVector(0.0f, 0.0f, -90.0f));
|
||||||
|
MeshComponent->SetRelativeRotation(FRotator(0.0f, -90.0f, 0.0f));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogZMMO, Warning,
|
||||||
|
TEXT("AZMMOPlayerProxy: SKM_Quinn_Simple nao encontrado em /Game/Characters/Mannequins/Meshes."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (QuinnAnimBp.Succeeded())
|
||||||
|
{
|
||||||
|
MeshComponent->SetAnimationMode(EAnimationMode::AnimationBlueprint);
|
||||||
|
MeshComponent->SetAnimInstanceClass(QuinnAnimBp.Class);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UE_LOG(LogZMMO, Warning,
|
||||||
|
TEXT("AZMMOPlayerProxy: ABP_Unarmed nao encontrado em /Game/Characters/Mannequins/Anims/Unarmed."));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bUseControllerRotationPitch = false;
|
bUseControllerRotationPitch = false;
|
||||||
@@ -43,8 +95,31 @@ void AZMMOPlayerProxy::SetZMMOIdentity(const int64 InEntityId, const EZMMOEntity
|
|||||||
void AZMMOPlayerProxy::ApplyEntitySnapshot(const FZMMOEntitySnapshot& Snapshot)
|
void AZMMOPlayerProxy::ApplyEntitySnapshot(const FZMMOEntitySnapshot& Snapshot)
|
||||||
{
|
{
|
||||||
const FRotator NewRotation(0.0f, Snapshot.YawDeg, 0.0f);
|
const FRotator NewRotation(0.0f, Snapshot.YawDeg, 0.0f);
|
||||||
SetActorLocationAndRotation(Snapshot.PositionCm, NewRotation);
|
|
||||||
LastSnapshotPosition = Snapshot.PositionCm;
|
// V0: o servidor envia Z=spawnZ fixo (ADR 0039). ADR 0041 passou a Z a
|
||||||
|
// vir do cliente do dono (`GetActorLocation().Z`), pelo que o snapshot
|
||||||
|
// ja contem a Z autoritativa. Mantemos o line trace local como fallback
|
||||||
|
// gracioso para os casos em que o snapshot Z fica estranho (terreno
|
||||||
|
// receiver com buracos, drift entre clientes, packet loss). Sem hit, o
|
||||||
|
// AdjustedPos.Z fica igual a Snapshot.PositionCm.Z.
|
||||||
|
FVector AdjustedPos = Snapshot.PositionCm;
|
||||||
|
if (UWorld* World = GetWorld())
|
||||||
|
{
|
||||||
|
const FVector TraceStart = AdjustedPos + FVector(0.0f, 0.0f, 1000.0f);
|
||||||
|
const FVector TraceEnd = AdjustedPos - FVector(0.0f, 0.0f, 4000.0f);
|
||||||
|
FHitResult Hit;
|
||||||
|
FCollisionQueryParams Params(TEXT("ZMMOProxyGround"), false, this);
|
||||||
|
if (World->LineTraceSingleByChannel(Hit, TraceStart, TraceEnd, ECC_Visibility, Params))
|
||||||
|
{
|
||||||
|
const float HalfHeight = GetCapsuleComponent()
|
||||||
|
? GetCapsuleComponent()->GetScaledCapsuleHalfHeight()
|
||||||
|
: 96.0f;
|
||||||
|
AdjustedPos.Z = Hit.Location.Z + HalfHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SetActorLocationAndRotation(AdjustedPos, NewRotation);
|
||||||
|
LastSnapshotPosition = AdjustedPos;
|
||||||
VisualVelocity = Snapshot.VelocityCmS;
|
VisualVelocity = Snapshot.VelocityCmS;
|
||||||
bHasFirstSnapshot = true;
|
bHasFirstSnapshot = true;
|
||||||
RefreshAnimationDrivers();
|
RefreshAnimationDrivers();
|
||||||
|
|||||||
103
ZMMO.uproject
103
ZMMO.uproject
@@ -1,51 +1,56 @@
|
|||||||
{
|
{
|
||||||
"FileVersion": 3,
|
"FileVersion": 3,
|
||||||
"EngineAssociation": "5.7",
|
"EngineAssociation": "5.7",
|
||||||
"Category": "",
|
"Category": "",
|
||||||
"Description": "",
|
"Description": "",
|
||||||
"Modules": [
|
"Modules": [
|
||||||
{
|
{
|
||||||
"Name": "ZMMO",
|
"Name": "ZMMO",
|
||||||
"Type": "Runtime",
|
"Type": "Runtime",
|
||||||
"LoadingPhase": "Default"
|
"LoadingPhase": "Default"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"Plugins": [
|
"Plugins": [
|
||||||
{
|
{
|
||||||
"Name": "ModelingToolsEditorMode",
|
"Name": "ModelingToolsEditorMode",
|
||||||
"Enabled": true
|
"Enabled": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Name": "StateTree",
|
"Name": "StateTree",
|
||||||
"Enabled": true
|
"Enabled": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Name": "GameplayStateTree",
|
"Name": "GameplayStateTree",
|
||||||
"Enabled": true
|
"Enabled": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Name": "VisualStudioTools",
|
"Name": "VisualStudioTools",
|
||||||
"Enabled": true,
|
"Enabled": true,
|
||||||
"SupportedTargetPlatforms": [
|
"SupportedTargetPlatforms": [
|
||||||
"Win64"
|
"Win64"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Name": "ZeusNetwork",
|
"Name": "ZeusNetwork",
|
||||||
"Enabled": true
|
"Enabled": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Name": "ZeusMapTools",
|
"Name": "ZeusMapTools",
|
||||||
"Enabled": true,
|
"Enabled": true,
|
||||||
"TargetAllowList": [
|
"TargetAllowList": [
|
||||||
"Editor"
|
"Editor"
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
],
|
{
|
||||||
"TargetPlatforms": [],
|
"Name": "meshy",
|
||||||
"AdditionalRootDirectories": [],
|
"Enabled": false,
|
||||||
"AdditionalPluginDirectories": [
|
"SupportedTargetPlatforms": [
|
||||||
"../../Server/Plugins/Unreal"
|
"Win64",
|
||||||
],
|
"Mac"
|
||||||
"EpicSampleNameHash": ""
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"AdditionalPluginDirectories": [
|
||||||
|
"../../Server/Plugins/Unreal"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user