So i am following ue4 multiplayer guide, but i want to make first person game, rather than vehicle, so i tried to adapt it to my needs. But it does not work, logs are saying that client updates the position, but server does not, so it rolls client back and i dont why. Here is the part of code that should replicate movement, would appreciate any tips.
AIgoraCharacter::AIgoraCharacter()
{
PrimaryActorTick.bCanEverTick = true;
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(55.f, 96.0f);
bReplicates = true;
//Create a body
//Body = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("Body1P"));
//Body->SetOnlyOwnerSee(false);
//Body->SetupAttachment(GetCapsuleComponent());
// set our turn rates for input
BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;
// Create a CameraComponent
FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
FirstPersonCameraComponent->SetupAttachment(GetMesh());
FirstPersonCameraComponent->bUsePawnControlRotation = true;
BootsComp = CreateDefaultSubobject<UIgoraClothingComponent>(TEXT("BootsComp"));
BootsComp->SetupAttachment(GetMesh());
HatComp = CreateDefaultSubobject<UIgoraClothingComponent>(TEXT("HatComp"));
HatComp->SetupAttachment(GetMesh());
MaskComp = CreateDefaultSubobject<UIgoraClothingComponent>(TEXT("MaskComp"));
MaskComp->SetupAttachment(GetMesh());
ShirtComp = CreateDefaultSubobject<UIgoraClothingComponent>(TEXT("ShirtComp"));
ShirtComp->SetupAttachment(GetMesh());
PantsComp = CreateDefaultSubobject<UIgoraClothingComponent>(TEXT("PantsComp"));
PantsComp->SetupAttachment(GetMesh());
//Set a base value for the sphere radis
UsableDistance = 400.0f;
//Setting up raycasting for picking things up
CollisionParams.AddIgnoredActor(this);
GunComp = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("GunComp"));
GunComp->SetupAttachment(GetMesh());
}
void AIgoraCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AIgoraCharacter, ServerState);
DOREPLIFETIME(AIgoraCharacter, bIsAiming);
DOREPLIFETIME(AIgoraCharacter, UsableDistance);
DOREPLIFETIME(AIgoraCharacter, CameraStartLoc);
DOREPLIFETIME(AIgoraCharacter, EndLocation);
DOREPLIFETIME(AIgoraCharacter, Hit);
DOREPLIFETIME(AIgoraCharacter, ActorHit);
DOREPLIFETIME(AIgoraCharacter, CameraRot);
}
void AIgoraCharacter::BeginPlay()
{
// Call the base class
Super::BeginPlay();
if (HasAuthority())
{
NetUpdateFrequency = 100;
}
FirstPersonCameraComponent->AttachToComponent(GetMesh(), FAttachmentTransformRules(EAttachmentRule::SnapToTarget, true), FName("HeadSocket"));
GunComp->AttachToComponent(GetMesh(), FAttachmentTransformRules(EAttachmentRule::SnapToTarget, true), FName("GripPoint"));
}
//////////////////////////////////////////////////////////////////////////
// Input
void AIgoraCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
// set up gameplay key bindings
check(PlayerInputComponent);
// Bind jump events
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
// Bind fire event
PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &AIgoraCharacter::ServerFire);
// Bind movement events
PlayerInputComponent->BindAxis("MoveForward", this, &AIgoraCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AIgoraCharacter::MoveRight);
// We have 2 versions of the rotation bindings to handle different kinds of devices differently
// "turn" handles devices that provide an absolute delta, such as a mouse.
// "turnrate" is for devices that we choose to treat as a rate of change, such as an analog joystick
PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
//handle collecting pickups button
PlayerInputComponent->BindAction("Interact", IE_Pressed, this, &AIgoraCharacter::Interact);
//Handle the open inventory button
PlayerInputComponent->BindAction("ToggleInventory", IE_Pressed, this, &AIgoraCharacter::ToggleInventory);
//Handle the aiming
PlayerInputComponent->BindAction("Aim", IE_Pressed, this, &AIgoraCharacter::BeginAim);
PlayerInputComponent->BindAction("Aim", IE_Released, this, &AIgoraCharacter::EndAim);
}
void AIgoraCharacter::ServerFire_Implementation()
{
if (bIsAiming)
{
PlayGunParticles();
}
}
void AIgoraCharacter::MoveForward(float Value)
{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorForwardVector(), Value);
MovementValue = Value;
bMoveForward = true;
}
}
void AIgoraCharacter::MoveRight(float Value)
{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorRightVector(), Value);
MovementValue = Value;
bMoveRight = true;
}
}
void AIgoraCharacter::TurnAtRate(float Rate)
{
// calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
RotationRate = Rate;
bAddYaw = true;
}
void AIgoraCharacter::LookUpAtRate(float Rate)
{
// calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
RotationRate = Rate;
bAddPitch = true;
}
void AIgoraCharacter::Server_SendMove_Implementation(FCharacterMove Move)
{
SimulateMove(Move);
ServerState.LastMove = Move;
ServerState.Transform = Move.Character->GetTransform();
}
bool AIgoraCharacter::Server_SendMove_Validate(FCharacterMove Move)
{
if(Move.MovementValue > 1.01 || Move.MovementValue < -1.01)
return false;
if(Move.RotationRate > 1.01 || Move.RotationRate < -1.01)
return false;
if(Move.BaseTurnRate > 90.f || Move.BaseTurnRate < -90.f)
return false;
if(Move.BaseLookUpRate > 90.f || Move.BaseLookUpRate < -90.f)
return false;
return true;
}
void AIgoraCharacter::SimulateMove(FCharacterMove Move)
{
if(Move.bMoveForward)
{
AddMovementInput(Move.Character->GetActorForwardVector(), Move.MovementValue); // tried Move.Character->AddMovementInput, same result
UE_LOG(LogTemp, Warning, TEXT("Character Name: %s"), *Move.Character->GetName())
UE_LOG(LogTemp, Warning, TEXT("Server state: %f"), ServerState.Transform.GetLocation().X)
UE_LOG(LogTemp, Warning, TEXT("Character transform: %f"), Move.Character->GetTransform().GetLocation().X)
UE_LOG(LogTemp, Warning, TEXT("Move.MovementValue: %f"), Move.MovementValue)
UE_LOG(LogTemp, Warning, TEXT("Move.Character->GetActorForwardVector(): %f"),Move.Character->GetActorForwardVector().X)
UE_LOG(LogTemp, Warning, TEXT("moved forward"))
}
if(Move.bMoveRight)
Move.Character->AddMovementInput(Move.Character->GetActorRightVector(), Move.MovementValue);
if(Move.bAddYaw)
Move.Character->AddControllerYawInput(Move.RotationRate * Move.BaseTurnRate * Move.DeltaTime);
if(Move.bAddPitch)
Move.Character->AddControllerPitchInput(Move.RotationRate * Move.BaseLookUpRate * Move.DeltaTime);
}
void AIgoraCharacter::OnRep_ServerState()
{
SetActorTransform(ServerState.Transform);
}
bool AIgoraCharacter::ShouldTickIfViewportsOnly() const
{
return true;
}
void AIgoraCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
CheckForInteractable();
if(IsLocallyControlled())
{
FCharacterMove Move;
Move.Character = this;
Move.DeltaTime = DeltaTime;
Move.MovementValue = MovementValue;
Move.RotationRate = RotationRate;
Move.BaseLookUpRate = BaseLookUpRate;
Move.BaseTurnRate = BaseTurnRate;
Move.bMoveForward = bMoveForward;
Move.bMoveRight = bMoveRight;
Move.bAddPitch = bAddPitch;
Move.bAddYaw = bAddYaw;
Server_SendMove(Move);
SimulateMove(Move);
bMoveForward = false;
bMoveRight = false;
bAddYaw = false;
bAddPitch = false;
}
}