2022-05-26 15:32:23 +00:00
|
|
|
// Fill out your copyright notice in the Description page of Project Settings.
|
|
|
|
|
|
|
|
|
|
|
|
#include "LagCompensationComponent.h"
|
|
|
|
|
2022-05-27 22:03:06 +00:00
|
|
|
#include "Blaster/Blaster.h"
|
2022-05-27 16:36:56 +00:00
|
|
|
#include "Blaster/Weapon/Weapon.h"
|
2022-05-27 09:29:32 +00:00
|
|
|
#include "Components/BoxComponent.h"
|
2022-05-27 16:36:56 +00:00
|
|
|
#include "Kismet/GameplayStatics.h"
|
2022-05-27 09:29:32 +00:00
|
|
|
|
2022-05-26 15:32:23 +00:00
|
|
|
ULagCompensationComponent::ULagCompensationComponent()
|
|
|
|
{
|
|
|
|
PrimaryComponentTick.bCanEverTick = true;
|
|
|
|
}
|
|
|
|
|
2022-05-27 15:05:07 +00:00
|
|
|
FFramePackage ULagCompensationComponent::InterpBetweenFrames(const FFramePackage& OlderFrame, const FFramePackage& YoungerFrame, float HitTime)
|
|
|
|
{
|
|
|
|
const float Distance = YoungerFrame.Time - OlderFrame.Time;
|
|
|
|
const float InterpFraction = FMath::Clamp((HitTime - OlderFrame.Time) / Distance, 0.f, 1.f);
|
|
|
|
|
|
|
|
FFramePackage InterpFramePackage;
|
|
|
|
InterpFramePackage.Time = HitTime;
|
|
|
|
|
|
|
|
for (auto& YoungerPair : YoungerFrame.HitBoxInfo)
|
|
|
|
{
|
|
|
|
const FName& BoxInfoName = YoungerPair.Key;
|
|
|
|
const FBoxInformation& OlderBox = OlderFrame.HitBoxInfo[BoxInfoName];
|
|
|
|
const FBoxInformation& YoungerBox = YoungerFrame.HitBoxInfo[BoxInfoName];
|
|
|
|
|
|
|
|
FBoxInformation InterpBoxInfo;
|
|
|
|
InterpBoxInfo.Location = FMath::VInterpTo(OlderBox.Location, YoungerBox.Location, 1.f, InterpFraction);
|
|
|
|
InterpBoxInfo.Rotation = FMath::RInterpTo(OlderBox.Rotation, YoungerBox.Rotation, 1.f, InterpFraction);
|
2022-05-29 09:59:15 +00:00
|
|
|
InterpBoxInfo.BoxExtent = YoungerBox.BoxExtent;
|
2022-05-27 15:05:07 +00:00
|
|
|
|
|
|
|
InterpFramePackage.HitBoxInfo.Add(BoxInfoName, InterpBoxInfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
return InterpFramePackage;
|
|
|
|
}
|
|
|
|
|
2022-05-27 15:42:34 +00:00
|
|
|
FServerSideRewindResult ULagCompensationComponent::ConfirmHit(const FFramePackage& Package, ABlasterCharacter* HitCharacter,
|
|
|
|
const FVector_NetQuantize& TraceStart, const FVector_NetQuantize HitLocation)
|
|
|
|
{
|
|
|
|
if (HitCharacter == nullptr) return FServerSideRewindResult();
|
|
|
|
|
|
|
|
FFramePackage CurrentFrame;
|
|
|
|
CacheBoxPositions(HitCharacter, CurrentFrame);
|
|
|
|
MoveBoxes(HitCharacter, Package);
|
|
|
|
EnableCharacterMeshCollision(HitCharacter, ECollisionEnabled::NoCollision);
|
|
|
|
|
|
|
|
// Enable collision for the head first
|
|
|
|
UBoxComponent* HeadBox = HitCharacter->HitCollisionBoxes[FName("head")];
|
|
|
|
HeadBox->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
|
2022-05-27 22:03:06 +00:00
|
|
|
HeadBox->SetCollisionResponseToChannel(ECC_HitBox, ECR_Block);
|
2022-05-27 15:42:34 +00:00
|
|
|
|
|
|
|
const FVector TraceEnd = TraceStart + (HitLocation - TraceStart) * 1.25f;
|
2022-05-28 20:21:03 +00:00
|
|
|
if (UWorld* World = GetWorld())
|
2022-05-27 15:42:34 +00:00
|
|
|
{
|
2022-05-27 19:12:11 +00:00
|
|
|
FHitResult ConfirmHitResult;
|
2022-05-27 15:42:34 +00:00
|
|
|
World->LineTraceSingleByChannel(
|
|
|
|
ConfirmHitResult,
|
|
|
|
TraceStart,
|
|
|
|
TraceEnd,
|
2022-05-27 22:03:06 +00:00
|
|
|
ECC_HitBox
|
2022-05-27 15:42:34 +00:00
|
|
|
);
|
|
|
|
if (ConfirmHitResult.bBlockingHit) // we hit the head, return early
|
|
|
|
{
|
|
|
|
ResetHitBoxes(HitCharacter, CurrentFrame);
|
|
|
|
EnableCharacterMeshCollision(HitCharacter, ECollisionEnabled::QueryAndPhysics);
|
|
|
|
return FServerSideRewindResult{ true, true };
|
|
|
|
}
|
|
|
|
|
|
|
|
// Didn't hit the head, check the rest of the boxes
|
|
|
|
for (auto& HitBoxPair : HitCharacter->HitCollisionBoxes)
|
|
|
|
{
|
|
|
|
if (HitBoxPair.Value != nullptr)
|
|
|
|
{
|
|
|
|
HitBoxPair.Value->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
|
2022-05-27 22:03:06 +00:00
|
|
|
HitBoxPair.Value->SetCollisionResponseToChannel(ECC_HitBox, ECR_Block);
|
2022-05-27 15:42:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
World->LineTraceSingleByChannel(
|
|
|
|
ConfirmHitResult,
|
|
|
|
TraceStart,
|
|
|
|
TraceEnd,
|
2022-05-27 22:03:06 +00:00
|
|
|
ECC_HitBox
|
2022-05-27 15:42:34 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if (ConfirmHitResult.bBlockingHit)
|
|
|
|
{
|
|
|
|
ResetHitBoxes(HitCharacter, CurrentFrame);
|
|
|
|
EnableCharacterMeshCollision(HitCharacter, ECollisionEnabled::QueryAndPhysics);
|
|
|
|
return FServerSideRewindResult{ true, false };
|
|
|
|
}
|
|
|
|
}
|
2022-05-27 16:36:56 +00:00
|
|
|
|
|
|
|
ResetHitBoxes(HitCharacter, CurrentFrame);
|
|
|
|
EnableCharacterMeshCollision(HitCharacter, ECollisionEnabled::QueryAndPhysics);
|
|
|
|
return FServerSideRewindResult{ false, false };
|
2022-05-27 15:42:34 +00:00
|
|
|
}
|
|
|
|
|
2022-05-27 22:28:02 +00:00
|
|
|
FServerSideRewindResult ULagCompensationComponent::ProjectileConfirmHit(const FFramePackage& Package, ABlasterCharacter* HitCharacter, const FVector_NetQuantize& TraceStart,
|
|
|
|
const FVector_NetQuantize100& InitialVelocity, float HitTime)
|
|
|
|
{
|
|
|
|
FFramePackage CurrentFrame;
|
|
|
|
CacheBoxPositions(HitCharacter, CurrentFrame);
|
|
|
|
MoveBoxes(HitCharacter, Package);
|
|
|
|
EnableCharacterMeshCollision(HitCharacter, ECollisionEnabled::NoCollision);
|
|
|
|
|
|
|
|
// Enable collision for the head first
|
|
|
|
UBoxComponent* HeadBox = HitCharacter->HitCollisionBoxes[FName("head")];
|
|
|
|
HeadBox->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
|
|
|
|
HeadBox->SetCollisionResponseToChannel(ECC_HitBox, ECR_Block);
|
|
|
|
|
|
|
|
FPredictProjectilePathParams PathParams;
|
|
|
|
PathParams.bTraceWithCollision = true;
|
|
|
|
PathParams.MaxSimTime = MaxRecordTime;
|
|
|
|
PathParams.LaunchVelocity = InitialVelocity;
|
|
|
|
PathParams.StartLocation = TraceStart;
|
|
|
|
PathParams.SimFrequency = 15.f;
|
|
|
|
PathParams.ProjectileRadius = 5.f;
|
|
|
|
PathParams.TraceChannel = ECC_HitBox;
|
|
|
|
PathParams.ActorsToIgnore.Add(GetOwner());
|
|
|
|
|
|
|
|
FPredictProjectilePathResult PathResult;
|
|
|
|
UGameplayStatics::PredictProjectilePath(this, PathParams, PathResult);
|
|
|
|
|
|
|
|
if (PathResult.HitResult.bBlockingHit) // we hit the head, return early
|
|
|
|
{
|
|
|
|
ResetHitBoxes(HitCharacter, CurrentFrame);
|
|
|
|
EnableCharacterMeshCollision(HitCharacter, ECollisionEnabled::QueryAndPhysics);
|
|
|
|
return FServerSideRewindResult{ true, true };
|
|
|
|
}
|
|
|
|
|
|
|
|
// we didn't hit the head; check the rest of the boxes
|
|
|
|
for (auto& HitBoxPair : HitCharacter->HitCollisionBoxes)
|
|
|
|
{
|
|
|
|
if (HitBoxPair.Value != nullptr)
|
|
|
|
{
|
|
|
|
HitBoxPair.Value->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
|
|
|
|
HitBoxPair.Value->SetCollisionResponseToChannel(ECC_HitBox, ECR_Block);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UGameplayStatics::PredictProjectilePath(this, PathParams, PathResult);
|
|
|
|
if (PathResult.HitResult.bBlockingHit)
|
|
|
|
{
|
|
|
|
ResetHitBoxes(HitCharacter, CurrentFrame);
|
|
|
|
EnableCharacterMeshCollision(HitCharacter, ECollisionEnabled::QueryAndPhysics);
|
|
|
|
return FServerSideRewindResult{ true, false };
|
|
|
|
}
|
|
|
|
|
|
|
|
ResetHitBoxes(HitCharacter, CurrentFrame);
|
|
|
|
EnableCharacterMeshCollision(HitCharacter, ECollisionEnabled::QueryAndPhysics);
|
|
|
|
return FServerSideRewindResult{ false, false };
|
|
|
|
}
|
2022-05-27 19:12:11 +00:00
|
|
|
|
|
|
|
FShotgunServerSideRewindResult ULagCompensationComponent::ShotgunConfirmHit(const TArray<FFramePackage>& FramePackages,
|
2022-05-27 22:28:02 +00:00
|
|
|
const FVector_NetQuantize& TraceStart, const TArray<FVector_NetQuantize>& HitLocations)
|
2022-05-27 19:12:11 +00:00
|
|
|
{
|
|
|
|
for (auto& Frame : FramePackages)
|
|
|
|
{
|
|
|
|
if (Frame.Character == nullptr) return FShotgunServerSideRewindResult();
|
|
|
|
}
|
|
|
|
|
|
|
|
FShotgunServerSideRewindResult ShotgunResult;
|
|
|
|
|
|
|
|
TArray<FFramePackage> CurrentFrames;
|
|
|
|
for (auto& Frame : FramePackages)
|
|
|
|
{
|
|
|
|
FFramePackage CurrentFrame;
|
|
|
|
CurrentFrame.Character = Frame.Character;
|
|
|
|
CacheBoxPositions(Frame.Character, CurrentFrame);
|
|
|
|
MoveBoxes(Frame.Character, Frame);
|
|
|
|
EnableCharacterMeshCollision(Frame.Character, ECollisionEnabled::NoCollision);
|
|
|
|
CurrentFrames.Add(CurrentFrame);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& Frame : FramePackages)
|
|
|
|
{
|
|
|
|
// Enable collision for the head first
|
|
|
|
UBoxComponent* HeadBox = Frame.Character->HitCollisionBoxes[FName("head")];
|
|
|
|
HeadBox->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
|
2022-05-27 22:03:06 +00:00
|
|
|
HeadBox->SetCollisionResponseToChannel(ECC_HitBox, ECR_Block);
|
2022-05-27 19:12:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
UWorld* World = GetWorld();
|
|
|
|
|
|
|
|
// Check for headshots
|
|
|
|
for (auto& HitLocation : HitLocations)
|
|
|
|
{
|
|
|
|
const FVector TraceEnd = TraceStart + (HitLocation - TraceStart) * 1.25f;
|
|
|
|
if (World)
|
|
|
|
{
|
|
|
|
FHitResult ConfirmHitResult;
|
|
|
|
World->LineTraceSingleByChannel(
|
|
|
|
ConfirmHitResult,
|
|
|
|
TraceStart,
|
|
|
|
TraceEnd,
|
2022-05-27 22:03:06 +00:00
|
|
|
ECC_HitBox
|
2022-05-27 19:12:11 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
ABlasterCharacter* BlasterCharacter = Cast<ABlasterCharacter>(ConfirmHitResult.GetActor());
|
|
|
|
if (BlasterCharacter)
|
|
|
|
{
|
|
|
|
if (ShotgunResult.Headshots.Contains(BlasterCharacter))
|
|
|
|
{
|
|
|
|
ShotgunResult.Headshots[BlasterCharacter]++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ShotgunResult.Headshots.Emplace(BlasterCharacter, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enable collision for all boxes, then disable for head box
|
2022-05-29 09:59:15 +00:00
|
|
|
for (auto& Frame : FramePackages)
|
2022-05-27 19:12:11 +00:00
|
|
|
{
|
|
|
|
for (auto& HitBoxPair : Frame.Character->HitCollisionBoxes)
|
|
|
|
{
|
|
|
|
if (HitBoxPair.Value != nullptr)
|
|
|
|
{
|
|
|
|
HitBoxPair.Value->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
|
2022-05-27 22:03:06 +00:00
|
|
|
HitBoxPair.Value->SetCollisionResponseToChannel(ECC_HitBox, ECR_Block);
|
2022-05-27 19:12:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
UBoxComponent* HeadBox = Frame.Character->HitCollisionBoxes[FName("head")];
|
|
|
|
HeadBox->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for body shots
|
|
|
|
for (auto& HitLocation : HitLocations)
|
|
|
|
{
|
|
|
|
const FVector TraceEnd = TraceStart + (HitLocation - TraceStart) * 1.25f;
|
|
|
|
if (World)
|
|
|
|
{
|
|
|
|
FHitResult ConfirmHitResult;
|
|
|
|
World->LineTraceSingleByChannel(
|
|
|
|
ConfirmHitResult,
|
|
|
|
TraceStart,
|
|
|
|
TraceEnd,
|
2022-05-27 22:03:06 +00:00
|
|
|
ECC_HitBox
|
2022-05-27 19:12:11 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
ABlasterCharacter* BlasterCharacter = Cast<ABlasterCharacter>(ConfirmHitResult.GetActor());
|
|
|
|
if (BlasterCharacter)
|
|
|
|
{
|
|
|
|
if (ShotgunResult.BodyShots.Contains(BlasterCharacter))
|
|
|
|
{
|
|
|
|
ShotgunResult.BodyShots[BlasterCharacter]++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ShotgunResult.BodyShots.Emplace(BlasterCharacter, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& CurrentFrame : CurrentFrames)
|
|
|
|
{
|
|
|
|
ResetHitBoxes(CurrentFrame.Character, CurrentFrame);
|
|
|
|
EnableCharacterMeshCollision(CurrentFrame.Character, ECollisionEnabled::QueryAndPhysics);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ShotgunResult;
|
|
|
|
}
|
|
|
|
|
2022-05-27 15:42:34 +00:00
|
|
|
void ULagCompensationComponent::CacheBoxPositions(ABlasterCharacter* HitCharacter, FFramePackage& OutFramePackage)
|
|
|
|
{
|
|
|
|
if (HitCharacter == nullptr) return;
|
|
|
|
for (auto& HitBoxPair : HitCharacter->HitCollisionBoxes)
|
|
|
|
{
|
|
|
|
if (HitBoxPair.Value != nullptr)
|
|
|
|
{
|
|
|
|
FBoxInformation BoxInfo;
|
|
|
|
BoxInfo.Location = HitBoxPair.Value->GetComponentLocation();
|
|
|
|
BoxInfo.Rotation = HitBoxPair.Value->GetComponentRotation();
|
2022-05-29 09:59:15 +00:00
|
|
|
BoxInfo.BoxExtent = HitBoxPair.Value->GetScaledBoxExtent();
|
2022-05-27 15:42:34 +00:00
|
|
|
OutFramePackage.HitBoxInfo.Add(HitBoxPair.Key, BoxInfo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ULagCompensationComponent::MoveBoxes(ABlasterCharacter* HitCharacter, const FFramePackage& Package)
|
|
|
|
{
|
|
|
|
if (HitCharacter == nullptr) return;
|
|
|
|
for (auto& HitBoxPair : HitCharacter->HitCollisionBoxes)
|
|
|
|
{
|
|
|
|
if (HitBoxPair.Value != nullptr)
|
|
|
|
{
|
|
|
|
HitBoxPair.Value->SetWorldLocation(Package.HitBoxInfo[HitBoxPair.Key].Location);
|
|
|
|
HitBoxPair.Value->SetWorldRotation(Package.HitBoxInfo[HitBoxPair.Key].Rotation);
|
2022-05-29 09:59:15 +00:00
|
|
|
HitBoxPair.Value->SetBoxExtent(Package.HitBoxInfo[HitBoxPair.Key].BoxExtent);
|
2022-05-27 15:42:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ULagCompensationComponent::ResetHitBoxes(ABlasterCharacter* HitCharacter, const FFramePackage& Package)
|
|
|
|
{
|
|
|
|
if (HitCharacter == nullptr) return;
|
|
|
|
for (auto& HitBoxPair : HitCharacter->HitCollisionBoxes)
|
|
|
|
{
|
|
|
|
if (HitBoxPair.Value != nullptr)
|
|
|
|
{
|
|
|
|
HitBoxPair.Value->SetWorldLocation(Package.HitBoxInfo[HitBoxPair.Key].Location);
|
|
|
|
HitBoxPair.Value->SetWorldRotation(Package.HitBoxInfo[HitBoxPair.Key].Rotation);
|
2022-05-29 09:59:15 +00:00
|
|
|
HitBoxPair.Value->SetBoxExtent(Package.HitBoxInfo[HitBoxPair.Key].BoxExtent);
|
2022-05-27 15:42:34 +00:00
|
|
|
HitBoxPair.Value->SetCollisionEnabled(ECollisionEnabled::NoCollision);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-27 09:29:32 +00:00
|
|
|
void ULagCompensationComponent::ShowFramePackage(const FFramePackage& Package, const FColor Color)
|
|
|
|
{
|
|
|
|
for (auto& BoxInfo : Package.HitBoxInfo)
|
|
|
|
{
|
|
|
|
DrawDebugBox(
|
|
|
|
GetWorld(),
|
|
|
|
BoxInfo.Value.Location,
|
2022-05-29 09:59:15 +00:00
|
|
|
BoxInfo.Value.BoxExtent,
|
2022-05-27 09:29:32 +00:00
|
|
|
FQuat(BoxInfo.Value.Rotation),
|
|
|
|
Color,
|
2022-05-27 13:36:32 +00:00
|
|
|
false,
|
|
|
|
4.f
|
2022-05-27 09:29:32 +00:00
|
|
|
);
|
|
|
|
}
|
2022-05-26 15:32:23 +00:00
|
|
|
}
|
|
|
|
|
2022-05-27 15:42:34 +00:00
|
|
|
FServerSideRewindResult ULagCompensationComponent::ServerSideRewind(ABlasterCharacter* HitCharacter, const FVector_NetQuantize& TraceStart, const FVector_NetQuantize& HitLocation,
|
2022-05-27 14:06:23 +00:00
|
|
|
float HitTime)
|
2022-05-27 17:13:59 +00:00
|
|
|
{
|
|
|
|
const FFramePackage FrameToCheck = GetFrameToCheck(HitCharacter, HitTime);
|
|
|
|
return ConfirmHit(FrameToCheck, HitCharacter, TraceStart, HitLocation);
|
|
|
|
}
|
|
|
|
|
2022-05-29 09:59:15 +00:00
|
|
|
FServerSideRewindResult ULagCompensationComponent::ProjectileServerSideRewind(ABlasterCharacter* HitCharacter, const FVector_NetQuantize& TraceStart,
|
2022-05-27 22:28:02 +00:00
|
|
|
const FVector_NetQuantize100& InitialVelocity, float HitTime)
|
|
|
|
{
|
|
|
|
const FFramePackage FrameToCheck = GetFrameToCheck(HitCharacter, HitTime);
|
|
|
|
return ProjectileConfirmHit(FrameToCheck, HitCharacter, TraceStart, InitialVelocity, HitTime);
|
|
|
|
}
|
|
|
|
|
2022-05-27 17:13:59 +00:00
|
|
|
FShotgunServerSideRewindResult ULagCompensationComponent::ShotgunServerSideRewind(const TArray<ABlasterCharacter*>& HitCharacters,
|
2022-05-27 19:29:25 +00:00
|
|
|
const FVector_NetQuantize& TraceStart, const TArray<FVector_NetQuantize>& HitLocations, float HitTime)
|
2022-05-27 17:13:59 +00:00
|
|
|
{
|
|
|
|
TArray<FFramePackage> FramesToCheck;
|
|
|
|
for (ABlasterCharacter* HitCharacter : HitCharacters)
|
|
|
|
{
|
|
|
|
FramesToCheck.Add(GetFrameToCheck(HitCharacter, HitTime));
|
|
|
|
}
|
|
|
|
|
2022-05-27 19:12:11 +00:00
|
|
|
return ShotgunConfirmHit(FramesToCheck, TraceStart, HitLocations);
|
2022-05-27 17:13:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FFramePackage ULagCompensationComponent::GetFrameToCheck(ABlasterCharacter* HitCharacter, float HitTime)
|
2022-05-27 14:06:23 +00:00
|
|
|
{
|
|
|
|
bool bReturn = HitCharacter == nullptr ||
|
|
|
|
HitCharacter->GetLagCompensation() == nullptr ||
|
|
|
|
HitCharacter->GetLagCompensation()->FrameHistory.GetHead() == nullptr ||
|
|
|
|
HitCharacter->GetLagCompensation()->FrameHistory.GetTail() == nullptr;
|
2022-05-27 17:13:59 +00:00
|
|
|
if (bReturn) FFramePackage();
|
2022-05-27 14:06:23 +00:00
|
|
|
// Frame package that we check to verify a hit
|
2022-05-27 15:42:34 +00:00
|
|
|
FFramePackage FrameToCheck;
|
2022-05-27 14:06:23 +00:00
|
|
|
bool bShouldInterpolate = true;
|
|
|
|
|
|
|
|
const TDoubleLinkedList<FFramePackage>& History = HitCharacter->GetLagCompensation()->FrameHistory;
|
|
|
|
const float OldestHistoryTime = History.GetTail()->GetValue().Time;
|
|
|
|
const float NewestHistoryTime = History.GetHead()->GetValue().Time;
|
|
|
|
if (OldestHistoryTime > HitTime)
|
|
|
|
{
|
|
|
|
// Too far back, too laggy to do SSR
|
2022-05-27 17:13:59 +00:00
|
|
|
FFramePackage();
|
2022-05-27 14:06:23 +00:00
|
|
|
}
|
|
|
|
if (OldestHistoryTime == HitTime)
|
|
|
|
{
|
2022-05-27 15:42:34 +00:00
|
|
|
FrameToCheck = History.GetTail()->GetValue();
|
2022-05-27 14:06:23 +00:00
|
|
|
bShouldInterpolate = false;
|
|
|
|
}
|
|
|
|
if (NewestHistoryTime <= HitTime)
|
|
|
|
{
|
2022-05-27 15:42:34 +00:00
|
|
|
FrameToCheck = History.GetHead()->GetValue();
|
2022-05-27 14:06:23 +00:00
|
|
|
bShouldInterpolate = false;
|
|
|
|
}
|
|
|
|
auto Younger = History.GetHead();
|
|
|
|
auto Older = History.GetHead();
|
|
|
|
while (Older->GetValue().Time > HitTime)
|
|
|
|
{
|
|
|
|
if (Older->GetNextNode() == nullptr) break;
|
|
|
|
Older = Older->GetNextNode();
|
|
|
|
if (Older->GetValue().Time > HitTime)
|
|
|
|
{
|
|
|
|
Younger = Older;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (Older->GetValue().Time == HitTime)
|
|
|
|
{
|
2022-05-27 15:42:34 +00:00
|
|
|
FrameToCheck = Older->GetValue();
|
2022-05-27 14:06:23 +00:00
|
|
|
bShouldInterpolate = false;
|
|
|
|
}
|
|
|
|
if (bShouldInterpolate) // Why not just check if FrameToCheck == nullptr?
|
|
|
|
{
|
2022-05-29 09:59:15 +00:00
|
|
|
// Interpolate between Younger and Older
|
|
|
|
FrameToCheck = InterpBetweenFrames(Older->GetValue(), Younger->GetValue(), HitTime);
|
2022-05-27 15:42:34 +00:00
|
|
|
}
|
2022-05-27 19:57:52 +00:00
|
|
|
FrameToCheck.Character = HitCharacter;
|
|
|
|
|
2022-05-27 17:13:59 +00:00
|
|
|
return FrameToCheck;
|
2022-05-27 15:42:34 +00:00
|
|
|
}
|
2022-05-29 09:59:15 +00:00
|
|
|
void ULagCompensationComponent::ServerScoreRequest_Implementation(ABlasterCharacter* HitCharacter, const FVector_NetQuantize& TraceStart,
|
|
|
|
const FVector_NetQuantize& HitLocation, float HitTime, AWeapon* DamageCauser)
|
|
|
|
{
|
|
|
|
const FServerSideRewindResult Confirm = ServerSideRewind(HitCharacter, TraceStart, HitLocation, HitTime);
|
|
|
|
|
|
|
|
if (Character && HitCharacter && DamageCauser && Confirm.bHitConfirmed)
|
|
|
|
{
|
|
|
|
UGameplayStatics::ApplyDamage(
|
|
|
|
HitCharacter,
|
|
|
|
DamageCauser->GetDamage(),
|
|
|
|
Character->Controller,
|
|
|
|
DamageCauser,
|
|
|
|
UDamageType::StaticClass()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ULagCompensationComponent::ProjectileServerScoreRequest_Implementation(ABlasterCharacter* HitCharacter, const FVector_NetQuantize& TraceStart,
|
|
|
|
const FVector_NetQuantize100& InitialVelocity, float HitTime)
|
|
|
|
{
|
|
|
|
const FServerSideRewindResult Confirm = ProjectileServerSideRewind(HitCharacter, TraceStart, InitialVelocity, HitTime);
|
|
|
|
|
|
|
|
if (Character && HitCharacter && Confirm.bHitConfirmed)
|
|
|
|
{
|
|
|
|
UGameplayStatics::ApplyDamage(
|
|
|
|
HitCharacter,
|
|
|
|
Character->GetPrimaryWeapon()->GetDamage(),
|
|
|
|
Character->Controller,
|
|
|
|
Character->GetPrimaryWeapon(),
|
|
|
|
UDamageType::StaticClass()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ULagCompensationComponent::ShotgunServerScoreRequest_Implementation(const TArray<ABlasterCharacter*>& HitCharacters, const FVector_NetQuantize& TraceStart,
|
|
|
|
const TArray<FVector_NetQuantize>& HitLocations, float HitTime)
|
|
|
|
{
|
|
|
|
FShotgunServerSideRewindResult Confirm = ShotgunServerSideRewind(HitCharacters, TraceStart, HitLocations, HitTime);
|
|
|
|
|
|
|
|
for (auto& HitCharacter : HitCharacters)
|
|
|
|
{
|
|
|
|
if (HitCharacter == nullptr || Character == nullptr || HitCharacter->GetPrimaryWeapon() == nullptr) continue;
|
|
|
|
float TotalDamage = 0.f;
|
|
|
|
if (Confirm.Headshots.Contains(HitCharacter))
|
|
|
|
{
|
|
|
|
TotalDamage += Confirm.Headshots[HitCharacter] * Character->GetPrimaryWeapon()->GetDamage();
|
|
|
|
}
|
|
|
|
if (Confirm.BodyShots.Contains(HitCharacter))
|
|
|
|
{
|
|
|
|
TotalDamage += Confirm.BodyShots[HitCharacter] * Character->GetPrimaryWeapon()->GetDamage();
|
|
|
|
}
|
|
|
|
|
|
|
|
UGameplayStatics::ApplyDamage(
|
|
|
|
HitCharacter,
|
|
|
|
TotalDamage,
|
|
|
|
Character->Controller,
|
|
|
|
Character->GetPrimaryWeapon(),
|
|
|
|
UDamageType::StaticClass()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2022-05-27 15:42:34 +00:00
|
|
|
|
|
|
|
void ULagCompensationComponent::EnableCharacterMeshCollision(ABlasterCharacter* HitCharacter, ECollisionEnabled::Type CollisionEnabled)
|
|
|
|
{
|
|
|
|
if (HitCharacter && HitCharacter->GetMesh())
|
|
|
|
{
|
|
|
|
HitCharacter->GetMesh()->SetCollisionEnabled(CollisionEnabled);
|
2022-05-27 14:06:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-26 15:32:23 +00:00
|
|
|
void ULagCompensationComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
|
|
|
{
|
|
|
|
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
2022-05-27 13:36:32 +00:00
|
|
|
|
2022-05-27 16:36:56 +00:00
|
|
|
SaveFramePackage();
|
|
|
|
}
|
2022-05-29 09:59:15 +00:00
|
|
|
|
|
|
|
void ULagCompensationComponent::SaveFramePackage()
|
|
|
|
{
|
|
|
|
if (Character == nullptr || !Character->HasAuthority()) return;
|
|
|
|
if (FrameHistory.Num() <= 1)
|
|
|
|
{
|
|
|
|
FFramePackage ThisFrame;
|
|
|
|
SaveFramePackage(ThisFrame);
|
|
|
|
FrameHistory.AddHead(ThisFrame);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
float HistoryLength = FrameHistory.GetHead()->GetValue().Time - FrameHistory.GetTail()->GetValue().Time;
|
|
|
|
while (HistoryLength > MaxRecordTime)
|
|
|
|
{
|
|
|
|
FrameHistory.RemoveNode(FrameHistory.GetTail());
|
|
|
|
HistoryLength = FrameHistory.GetHead()->GetValue().Time - FrameHistory.GetTail()->GetValue().Time;
|
|
|
|
}
|
|
|
|
FFramePackage ThisFrame;
|
|
|
|
SaveFramePackage(ThisFrame);
|
|
|
|
FrameHistory.AddHead(ThisFrame);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ULagCompensationComponent::SaveFramePackage(FFramePackage& Package)
|
|
|
|
{
|
|
|
|
Character = Character == nullptr ? Cast<ABlasterCharacter>(GetOwner()) : Character;
|
|
|
|
if (Character)
|
|
|
|
{
|
|
|
|
Package.Time = GetWorld()->GetTimeSeconds();
|
|
|
|
Package.Character = Character;
|
|
|
|
for (auto& BoxPair : Character->HitCollisionBoxes)
|
|
|
|
{
|
|
|
|
FBoxInformation BoxInformation;
|
|
|
|
BoxInformation.Location = BoxPair.Value->GetComponentLocation();
|
|
|
|
BoxInformation.Rotation = BoxPair.Value->GetComponentRotation();
|
|
|
|
BoxInformation.BoxExtent = BoxPair.Value->GetScaledBoxExtent();
|
|
|
|
Package.HitBoxInfo.Add(BoxPair.Key, BoxInformation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|