Okay, found the solution, got helped by daekesh in the IRC channel. Kiwi IRC
Once you define a function to be UFUNCTION(Server, Reliable, WithValidation), you have to remove the said function from the cpp and put its code into the implementation instead so that uht can create the function using those.
Updated cpp file:
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "MyProject.h"
#include "MyProjectCharacter.h"
#include "MyProjectProjectile.h"
#include "Animation/AnimInstance.h"
#include "GameFramework/InputSettings.h"
#include "UnrealNetwork.h"
DEFINE_LOG_CATEGORY_STATIC(LogFPChar, Warning, All);
//////////////////////////////////////////////////////////////////////////
// AMyProjectCharacter
AMyProjectCharacter::AMyProjectCharacter()
{
// Set size for collision capsule
GetCapsuleComponent()->InitCapsuleSize(55.f, 96.0f);
// set our turn rates for input
BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;
// Create a CameraComponent
FirstPersonCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("FirstPersonCamera"));
FirstPersonCameraComponent->AttachParent = GetCapsuleComponent();
FirstPersonCameraComponent->RelativeLocation = FVector(-39.56f, 1.75f, 64.f); // Position the camera
FirstPersonCameraComponent->bUsePawnControlRotation = true;
// Create a mesh component that will be used when being viewed from a '1st person' view (when controlling this pawn)
Mesh1P = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("CharacterMesh1P"));
Mesh1P->SetOnlyOwnerSee(true);
Mesh1P->AttachParent = FirstPersonCameraComponent;
Mesh1P->bCastDynamicShadow = false;
Mesh1P->CastShadow = false;
Mesh1P->RelativeRotation = FRotator(1.9f, -19.19f, 5.2f);
Mesh1P->RelativeLocation = FVector(-0.5f, -4.4f, -155.7f);
// Create a gun mesh component
FP_Gun = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("FP_Gun"));
FP_Gun->SetOnlyOwnerSee(true); // only the owning player will see this mesh
FP_Gun->bCastDynamicShadow = false;
FP_Gun->CastShadow = false;
//FP_Gun->AttachTo(Mesh1P, TEXT("GripPoint"), EAttachLocation::SnapToTargetIncludingScale, true);
FP_MuzzleLocation = CreateDefaultSubobject<USceneComponent>(TEXT("MuzzleLocation"));
FP_MuzzleLocation->AttachTo(FP_Gun);
FP_MuzzleLocation->SetRelativeLocation(FVector(0.2f, 48.4f, -10.6f));
FP_MuzzleLocation = nullptr; //Remove Muzzle
// Default offset from the character location for projectiles to spawn
GunOffset = FVector(100.0f, 30.0f, 10.0f);
bReplicates = true;
// Note: The ProjectileClass and the skeletal mesh/anim blueprints for Mesh1P are set in the
// derived blueprint asset named MyCharacter (to avoid direct content references in C++)
}
void AMyProjectCharacter::BeginPlay()
{
// Call the base class
Super::BeginPlay();
FP_Gun->AttachTo(Mesh1P, TEXT("GripPoint"), EAttachLocation::SnapToTargetIncludingScale, true); //Attach gun mesh component to Skeleton, doing it here because the skelton is not yet created in the constructor
}
//////////////////////////////////////////////////////////////////////////
// Input
void AMyProjectCharacter::SetupPlayerInputComponent(class UInputComponent* InputComponent)
{
// set up gameplay key bindings
check(InputComponent);
InputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
InputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
//InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AMyProjectCharacter::TouchStarted);
if (EnableTouchscreenMovement(InputComponent) == false)
{
InputComponent->BindAction("Fire", IE_Pressed, this, &AMyProjectCharacter::OnFire);
}
InputComponent->BindAxis("MoveForward", this, &AMyProjectCharacter::MoveForward);
InputComponent->BindAxis("MoveRight", this, &AMyProjectCharacter::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
InputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
InputComponent->BindAxis("TurnRate", this, &AMyProjectCharacter::TurnAtRate);
InputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
InputComponent->BindAxis("LookUpRate", this, &AMyProjectCharacter::LookUpAtRate);
}
/*void AMyProjectCharacter::SetProjectile()
{
if (FactionColor != NULL)
{
//p->ProjectileStaticMesh->SetMaterial(0, FactionColor);
}
if (FactionDecal != NULL)
{
//p->FactionDecal = FactionDecal;
}
}*/
void AMyProjectCharacter::OnFire_Implementation()
{
// try and fire a projectile
if (ProjectileClass != NULL)
{
const FRotator SpawnRotation = GetControlRotation();
// MuzzleOffset is in camera space, so transform it to world space before offsetting from the character location to find the final muzzle position
const FVector SpawnLocation = ((FP_MuzzleLocation != nullptr) ? FP_MuzzleLocation->GetComponentLocation() : GetActorLocation()) + SpawnRotation.RotateVector(GunOffset);
UWorld* const World = GetWorld();
if (World != NULL)
{
// spawn the projectile at the muzzle
AMyProjectProjectile *projectile = World->SpawnActor<AMyProjectProjectile>(ProjectileClass, SpawnLocation, SpawnRotation);
if (FactionColor != NULL)
{
projectile->ProjectileStaticMesh->SetMaterial(0, FactionColor);
}
if (FactionDecal != NULL)
{
projectile->FactionDecal = FactionDecal;
}
}
}
// try and play the sound if specified
if (FireSound != NULL)
{
UGameplayStatics::PlaySoundAtLocation(this, FireSound, GetActorLocation());
}
// try and play a firing animation if specified
if (FireAnimation != NULL)
{
// Get the animation object for the arms mesh
UAnimInstance* AnimInstance = Mesh1P->GetAnimInstance();
if (AnimInstance != NULL)
{
AnimInstance->Montage_Play(FireAnimation, 1.f);
}
}
}
bool AMyProjectCharacter::OnFire_Validate()
{
return true;
}
void AMyProjectCharacter::BeginTouch(const ETouchIndex::Type FingerIndex, const FVector Location)
{
if (TouchItem.bIsPressed == true)
{
return;
}
TouchItem.bIsPressed = true;
TouchItem.FingerIndex = FingerIndex;
TouchItem.Location = Location;
TouchItem.bMoved = false;
}
void AMyProjectCharacter::EndTouch(const ETouchIndex::Type FingerIndex, const FVector Location)
{
if (TouchItem.bIsPressed == false)
{
return;
}
if ((FingerIndex == TouchItem.FingerIndex) && (TouchItem.bMoved == false))
{
OnFire();
}
TouchItem.bIsPressed = false;
}
void AMyProjectCharacter::TouchUpdate(const ETouchIndex::Type FingerIndex, const FVector Location)
{
if ((TouchItem.bIsPressed == true) && (TouchItem.FingerIndex == FingerIndex))
{
if (TouchItem.bIsPressed)
{
if (GetWorld() != nullptr)
{
UGameViewportClient* ViewportClient = GetWorld()->GetGameViewport();
if (ViewportClient != nullptr)
{
FVector MoveDelta = Location - TouchItem.Location;
FVector2D ScreenSize;
ViewportClient->GetViewportSize(ScreenSize);
FVector2D ScaledDelta = FVector2D(MoveDelta.X, MoveDelta.Y) / ScreenSize;
if (FMath::Abs(ScaledDelta.X) >= 4.0 / ScreenSize.X)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.X * BaseTurnRate;
AddControllerYawInput(Value);
}
if (FMath::Abs(ScaledDelta.Y) >= 4.0 / ScreenSize.Y)
{
TouchItem.bMoved = true;
float Value = ScaledDelta.Y * BaseTurnRate;
AddControllerPitchInput(Value);
}
TouchItem.Location = Location;
}
TouchItem.Location = Location;
}
}
}
}
void AMyProjectCharacter::MoveForward(float Value)
{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorForwardVector(), Value);
}
}
void AMyProjectCharacter::MoveRight(float Value)
{
if (Value != 0.0f)
{
// add movement in that direction
AddMovementInput(GetActorRightVector(), Value);
}
}
void AMyProjectCharacter::TurnAtRate(float Rate)
{
// calculate delta for this frame from the rate information
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}
void AMyProjectCharacter::LookUpAtRate(float Rate)
{
// calculate delta for this frame from the rate information
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}
bool AMyProjectCharacter::EnableTouchscreenMovement(class UInputComponent* InputComponent)
{
bool bResult = false;
if (FPlatformMisc::GetUseVirtualJoysticks() || GetDefault<UInputSettings>()->bUseMouseForTouch)
{
bResult = true;
InputComponent->BindTouch(EInputEvent::IE_Pressed, this, &AMyProjectCharacter::BeginTouch);
InputComponent->BindTouch(EInputEvent::IE_Released, this, &AMyProjectCharacter::EndTouch);
InputComponent->BindTouch(EInputEvent::IE_Repeat, this, &AMyProjectCharacter::TouchUpdate);
}
return bResult;
}
void AMyProjectCharacter::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AMyProjectCharacter, FactionColor);
DOREPLIFETIME(AMyProjectCharacter, FactionDecal);
}