#multiplayer

1 messages ยท Page 592 of 1

tawny mason
#

I might not have understood what a lot is, i understand fortnite runs at 5-10 KB/s

chrome bay
#

Listen server or dedicated server?

tawny mason
#

Listen

chrome bay
#

What sort of framerate is it running at?

tawny mason
#

I have tried on multiple machines, this specific one was capped to 60

chrome bay
#

You can try reducing the network tick rate to start with, as 60 is probably too high - and you don't want the listen server running at whatever framerate the client can support anyway:

[/Script/OnlineSubsystemUtils.IpNetDriver]
InitialConnectTimeout=120.0f
MaxNetTickRate=60
NetServerMaxTickRate=60
LanServerMaxTickRate=60
NetClientTicksPerSecond=60
bClampListenServerTickRates=true
MaxClientRate=10000
MaxInternetClientRate=10000```
#

Otherwise it'll just be hammering out packets as fast as it can, the default for many actors (stupidly) being 100hz

winged badger
#

one problem with listen server running slower

#

if you lower that tick rate, and you have a client with inadequate machine

#

animations for that player's pawn will lag on server

chrome bay
#

ah yeah that is true

winged badger
#

and its very noticeable when your game is otherwise running smooth

tawny mason
#

@winged badger do you mean you see them animating stopmotion like

#

?

#

I have noticed that with some testers

winged badger
#

like his animations will be capped to the net tick rate

#

while everything else runs fine

tawny mason
#

ok, i can find a compromise there i guess, my issue right now is that the game is basically unplayable, everything lags very hard if i get the ai in

winged badger
#

we have one tester that insists on using old laptop and wireless connection

#

its a disaster ๐Ÿ˜„

tawny mason
#

as long as i keep the AI count down, everything is fine, @chrome bay i will try your config values

winged badger
#

so he can't run the game at more then 15-20 FPS

#

and his pawn on server suffers for it as well

tawny mason
#

Understood, thanks for the help guys, i will come back with my findings.

chrome bay
#

4.6 KB/s for one actor does look odd too

#

At nearly 420Hz...

winged badger
#

420Hz is impossible for a single Actor instance

chrome bay
#

yeah.. doesn't make sense

tawny mason
#

no, they are more than 1

chrome bay
#

Ah kk, couldn't work out what you were looking at

tawny mason
#

8 probably

#

8 AI characters just standing there and walking around

chrome bay
#

Probably another issue with root motion animation as well

#

Since they're going to be always moving and therefore always need to send network updates

#

Along with all the additional animation info

tawny mason
#

I'm using the Gameplay Ability System, the rootmotion sources to dash and stuff. I'm not sure my locomotion animations are using rootmotion thoe

winged badger
#

probably not

#

and the GameplayTasks should be well optimized, they are used in Fortnite

#

(what it uses for the root motion)

tawny mason
#

yes i'm aware ๐Ÿ™‚

#

hehe

#

so anything i could do about the rootmotion ? AFAIK, i only use the root motion sources

winged badger
#

funny how UE dev requires you to stay up-to-date with fortnite features

#

any system thats in fortnite is stable and optimized

#

any system thats not is a coin toss

tawny mason
#

hahaha yeah, i checked out the optimizing fortnite videos online but i can't do all that stuff they are doing

#

it would take me ages

winged badger
#

i don't know if its root motion, 4,7kB is super high for 8 AI

#

then again, if there are only those 8 AI on the level, or very few other replicated Actors

tawny mason
#

what would you suggest for a stable bandwidth usage for, the whole game i guess.

winged badger
#

and you didn't touch the crazy default NetUpdateFrequency of 100Hz

tawny mason
#

so i can target something, i'm not sure what to expect

winged badger
#

its... plausible

chrome bay
#

It does seem insane ๐Ÿ˜„

winged badger
#

AI don't need more then 12 or so Hz imo

tawny mason
#

yeah, thought so too, wasn't sure thoe

winged badger
#

20 if you have slack

tawny mason
#

i reduced the net update rate on the AI from 100 to something lower, i think 20

chrome bay
#

You're not doing something like calling RPC's etc on tick or something are you?

winged badger
#

but if that's the case, adding few hundred replicated Actors to the level

chrome bay
#

the properties are only a small part of the picture ofc

tawny mason
#

no, i'm not

#

made sure i wasn't

winged badger
#

would reduce the usage of AI

#

as they wouldn't be able to replicate whenever they want, but would have to wait their turn instead

#

listen server has a CPU time cap on how long it can evaluate Actors for replication each Tick

tawny mason
#

i have other replicated actors, but as long as you don't interact with them, they should be dormant

winged badger
#

so if you have few enough Actors to replicate, it will manage to evaluate the AI each Tick

tawny mason
#

they only 100% replicating actors are the characters, AI and Players

winged badger
#

btw, funny story

#

our designer accidentally changed the NetPriority of one trash mob to 100 once

#

i never saw the network perform worse mid game

tawny mason
#

xD

winged badger
#

basically, when there was 50 of them active

#

actors with normal Net Priority

#

and lifetime of 2-3 sec

#

didn't even have enough time to panic to increase their NetPriority enough to replicate while they were still alive

#

so client's couldn't see stull like AoE projectiles, which we replicate

#

at all

tawny mason
#

yeah sounds rough xD

winged badger
#

after that we made an editor utility widget that goes through all the Actor classes

#

and shows us their NetPriority and NetUpdateFrequency

#

and other replication settings

tawny mason
#

do you think it's good practice to just set all actors to say 10 netupdatefrequency and only make it higher if necesary ?

#

i guess characters are fine at a higher rate (players)

winged badger
#

depends on the Actor

#

those that very rarely change can have it down to less then 1, and ForceNetUpdate

#

if Dormancy is not an option

#

and its not if they can change while out of someones relevancy

#

then go Dormant, then they wouldn't update when they became relevant again

tawny mason
#

hmm i see, what about the bandwidth, what would be a good target or budget ?

#

for the whole thing i mean, i can see the server getting to 20 KB/s i get that is a lot

winged badger
#

listen servers?

rose egret
#

how u guys test and debug a multiplayer game with 50+ players ? do u have 50 developer or ?

empty axle
#

just the whole company plays

tawny mason
#

@winged badger yes listen server, if you know for dedicated servers would be interesting to know as well.

#

@rose egret we just get the whole team to play or maybe most of the company.

winged badger
#

listen servers are kinda worry about CPU, bandwidth will be fine unless you do something crazy situation

chrome bay
#

Though if you can get access to enough machines, sometimes simple things like having "bots" that are really clients that just apply random input can be useful

winged badger
#

it helps when you have player community from previous game(s)

chrome bay
#

yeah totally

winged badger
#

with enough hardcode fans

#

although, i got the steam beta mail few weeks ago

#

so that could probably be used for it

chrome bay
#

yeah that actually looks quite useful

winged badger
#

community is still better, as you can count on them showing up

chrome bay
#

I also wonder if there are any marketing/PR implications from that if it all goes wrong too..

#

hardcore fans might be more understanding ๐Ÿ˜„

rose egret
#

I am thinking about simulating clients, a kind of AI that plays the game. but I wonder how many clients I can simulate per machine , it takes lots of ram and CPU

winged badger
#

as long as it has the right feel, no matter how unpolished

#

hardcore fans are hardcore

meager fable
#

Hi, I'm using a character movement component and movin via AddMovementInput nodes but my client gets terrible lags for both his and servers pawn. What could be the cause of this?

winged badger
#

couple reliable RPCs running on Tick?

meager fable
#

All of the ones I made are unreliable but Imma double check this

#

is there a way to look for all of the rpc calls?

kindred widget
#

Is it constant 'lag'. Like a high latency?

meager fable
#

yup

kindred widget
#

Persists more than twenty or thirty seconds?

meager fable
#

no

#

more like 3/4 corrections per second

kindred widget
#

But does it keep happening for up to a minute after you've started testing?

meager fable
#

there were times where he could walk around without any corrections and it happened after a while but our testing sessions aren't long usually

kindred widget
#

Your input is client side? Just ButtonAxisEvent->AddMovementInput?

meager fable
#

yes, plus a few calculations and if checks

kindred widget
#

I'd assume either your server is running incredibly slow. It actually is a latency issue, or you have some crazy replication overflow going on like Zlo said. Either way too much replicated at once, or something like a lot of reliable RPCstoo often.

nocturne iron
meager fable
#

Yeah thats what I thought and was scared of, guess I'll go optimize now

#

thanks, to both of you

meager spade
#

GetLocalRole() != ROLE_Authority

#

or GetNetMode() == NM_Client depending..

kindred widget
#

Does the client own the version of the object that you're calling the multicast on?

nocturne iron
#

@kindred widget I think so, it's created in the character class.

nocturne iron
meager spade
#

one is the role of the actor, the other is if the game instance is client, correct.

#

hence the "or" and the depending... bit.

nocturne iron
#

If "GetLocalRole() != ROLE_Authority" the actor is locally controlled?

meager spade
#

no it means that the actor is not authoritive (ie actor on remote machines not the server)

#

(if replicated)

languid whale
#

I have this BP that is attached to the player and I want to destroy it when the player dies. Now it works fine on the server (It is destroyed on the server side) but on the client side it is still vissible. I destroy it using multicast and I am out of ideas? Anyone got a clue how I could fix this?

meager spade
#

there is no IsLocallyControlled really for an actor.

#

only thing you can do is GetWorld()->GetFirstPlayerController()->IsLocalController() or w/e

#

but i have never needed to do that

nocturne iron
#
{
    UE_LOG(LogTemp, Log, TEXT("[Drain] ABaseWeapon::Attack()"));

    Internal_Attack();

    if (!GetWorld()->IsServer())
    {
        // CLIENT
        Server_Attack();
    }
    else
    {
        // SERVER
        Multicast_Attack();
    }
}

void ABaseWeapon::Internal_Attack()
{
    if (m_AttackSound != nullptr)
    {
        UGameplayStatics::PlaySoundAtLocation(GetWorld(), m_AttackSound, GetTransform().GetLocation());
    }
}

void ABaseWeapon::Server_Attack_Implementation()
{
    UE_LOG(LogTemp, Log, TEXT("[Drain] ABaseWeapon::Server_Attack_Implementation()"));

    Multicast_Attack();
}

void ABaseWeapon::Multicast_Attack_Implementation()
{
    UE_LOG(LogTemp, Log, TEXT("[Drain] ABaseWeapon::Multicast_Attack_Implementation()"));
    
    if (/* What does here */)
    {
        Internal_Attack();
    }
}```
meager spade
#

whoa

#

use three `

nocturne iron
#

I need to check if the actor receiving the multicast is the one causeing it?

meager spade
#

why you doing GetWorld()->IsServer?

nocturne iron
#

If it is server, multicast, if not, call the server?

meager spade
#

regardless, you need to decide how your input is working.

nocturne iron
#

It's getting passed down from InputComponent on the character controller. Then to pawn, then to this actor.

kindred widget
#

Basically, if you want to know if the actor is locally controlled, you have to walk up it's owner tree to find out if GetPlayerController(0) is the owner. Pawn class does pretty much the same with IsLocallyControlled.

nocturne iron
#

So i need a reference to the pawn and then just call IsLocallyControlled on that one?

kindred widget
#

It just does it directly with GetController()->IsLocalController()

#

No.

meager spade
#

thing is he doesn't need to check that here

kindred widget
#

You have to check the Owner pointer in the pawn. If it's null, it's owned by the server. If it's set, you have to check what owns it. If it's not the controller, you have to keep walking up the actor's Owner tree to find out if the owner list is eventually owned by the local controller.

#

That's the only way to emulate IsLocallyControlled on a normal non possessed actor.

#

What exactly is this actor, why does the owning client need to do something different in this multicast than other connections?

meager spade
#

i assume local client calls Attack

#

and doesn't want the MC to call again the sound

nocturne iron
#

Yeah, it's a weapon actor

#

yeah! ;D

kindred widget
#

I'd steal the int counter rep that FPS games use for guns and just up that per call instead of multicast and set the replication to skip owner.

meager spade
#
{
    if (!ActorToQuery)
    {
        return false;
    }

    AActor* TopOwner;

    for (TopOwner = ActorToQuery; TopOwner->GetOwner(); TopOwner = TopOwner->GetOwner())
    {
        
    }

    APlayerController* Controller = Cast<APlayerController>(TopOwner);
    return Controller && Controller->IsLocalPlayerController();
}``` i mean.. you could make a function like this in a static library
#

and call it..

nocturne iron
meager spade
#

the server does the traces (if you want secure)

#

or client does the trace and tells the server (less secure, but can be secured with server checks before applying damage, etc)

#

weapons can be quite complex for a decent implementation

nocturne iron
#

That's what I'm doing - @kindred widget would just use rep notify, but don't get how to make that secure?

kindred widget
#

My point was just about the cosmetics. The Int counter just allows non owning clients to play the same cosmetics like sounds.

gritty pelican
#

How i can disable NetCullDistance for Actor?

#

I have a small map and I want it to be always loaded

nocturne iron
#

@kindred widget & @meager spade couldn't i just do this when the weapon is spawned (in the pawn) and then check that in the multicast?

CurrentWeapon->IsLocalWeapon = IsLocallyControlled(); The images is so freaking wrong xD

chrome bay
#

@gritty pelican bAlwaysRelevant

gritty pelican
#

@chrome bay thx

peak sentinel
#

Do I need to replicate "Play Sound 2D"?

kindred widget
#

@nocturne iron I mean, it really depends on how nuts you're going to go on the Owner chains for other actors. My initial suggestion would be to just make a simple library function like Const said and just check if the owner of the actor is either PlayerController(0) or PlayerPawn(0) and if not dedicated server.

peak sentinel
#

If yes, how can I test it working lol ๐Ÿ˜„

kindred widget
#

@peak sentinel Typically no. Multicast and have clients play it themselves, or replicate a property and have the repnotify play it.

peak sentinel
#

Okay thanks

chrome bay
#

Weapons are surely 'owned' by something, so just casting that owner to a pawn and checking if it's controlled locally should be enough

nocturne iron
#

How do i make an actor have authority (weapon on client) and is it secure?

kindred widget
#

@nocturne iron That's what the Owner variable in AActor is for. You set it on the server to a server version of their controller, or something their controller owns.

chrome bay
#

Make the clients player controller or pawn it's owner

#

Usually pawn IMO, since pawn is often the thing carrying the weapon

kindred widget
#

That owner chain is what allows clients to call RPCs on actors.

nocturne iron
#

Right - just wondering are there any great multiplayer demos?

kindred widget
#

ShooterGame is a pretty good example project from what I've heard. Haven't quite gotten around to looking through it myself.

chrome bay
#

yep. pretty comprehensive

twin juniper
#

Is it a bad idea to assume that world time is the same on the client and server? Or do they slowly desync from oneanother?

winged badger
#

its not the same

#

but delta doesn't change

kindred widget
#

GetServerWorldTimeSeconds is relatively synchronized I believe. But in general, it's probably a bad assumption to make, or to rely on the travel time of any form of RPCed or Replicated data.

winged badger
#

the one replicated in GameState by default is fairly inaccurate

#

so best to periodically send a Server_RequestWorldTime(float ClientTimeStamp); to which server immediately replies with Client_SendWorldTime(float ClientTimeStamp, float ServerTimeStamp);

#

when client receives the reply, it can assume that half the difference between its current worltime and ClientTimeStamp is time it took for reply to arrive from server

#

and from there calculate a ServerWorldTime accurately within few miliseconds

#

it then saves the Delta, and when you request ServerWorldTime on client you just return WorldTime + Delta

twin juniper
#

interesting

#

I've been using ExactPing so far for prediction purposes and it was worked well enough, but knowing this about World Time helps

#

it has*

winged badger
#

ExactPing is fine for travel time

#

but if you want server world time

#

late joining client can have half an hour delta

#

but will probably have at least few seconds, even if it seamless traveled in

twin juniper
#

Yes, I see that now, I am not sure I want to use world time for what I'm working on, but it might be interesting to test it. It may be I can do the same thing and use ping for my calculations but I will need to experiment.

winged badger
#

its good for syncing montages

#

and OnRep stuff like say EDoorStatus::Open

#

if its sent bunched up with a timestamp

#

you know if you should just set the door open or play the opening animation + sounds

#

and multicast here doesn't cut it, as anyone out of relevancy has the door in wrong state

twin juniper
#

Indeed, it does make for some overhead with all these floats bouncing about though, no?

winged badger
#

in my experience, few floats don't matter

#

CPU time evaluating actors for replication is the main chokepoint

twin juniper
#

on the client?

winged badger
#

server

#

client doesn't do that

twin juniper
#

hmm, what do you mean by evaluating actors?

winged badger
#

if 800 actors want to replicate in a single tick

#

doesn't mean they will

#

official docs page for that is a bit technical, but good

twin juniper
#

do you have the link? or a search term? I'd like to read it.

winged badger
twin juniper
#

Thanks

meager fable
#

on the server side the name displays correctly

#

but the clients get no name

winged badger
#

except their own?

meager fable
#

not even their own

#

the widget is created on begin play, and represented char is exposed on spawn

winged badger
#

doesn't matter

#

your PlayerState usually won't replicate as soon as your PlayerPawn does

#

if thats a widget component

#

i would recommend moving the widget component itself to PlayerState

#

and attaching the PlayerState to PlayerPawn

#

makes life so much simpler

tawny mason
#

@winged badger this is where net dormancy and relevancy come in right ?

winged badger
#

and net priority

tawny mason
#

true

#

btw the stuff we talked about earlier helped, not seeing as much lagging as i did before

#

๐Ÿ˜‰ ty

winged badger
#

yw

tawny mason
#

do you think being aggresive with the net dormancy is a good call ? using force net update with actors getting attached and such ? we have pickable items that only realy change network whise when attached or released

mighty zinc
#

Hey, how do i replicate character movement like dashing ?

tawny mason
#

@mighty zinc look into Gameplay Ability System, i use it for that

#

they have AbilityTasks that use RootMotionSources for it

mighty zinc
#

Kk

#

I'll look into it

#

Thakns

tawny mason
winged badger
#

problem with dormancy

#

is it won't figure out the new connection becoming relevant

#

we only use initial dormancy on pickups

#

until they are interacted with

tawny mason
#

so having it be on DormantAll and using FlushNetDormancy is not worth it ? i recall a VOD where some developers talked about it being a good option if you dont really need to send info more than on events or something

winged badger
#

if you can figure out when to flush it accurately

#

its fine

#

but if your actor changes, goes dormant, then i walk into relevancy range

#

that becomes a serious problem

tawny mason
#

hmm, in my games context, you can't really walk into relevancy range, rooms are small.

#

everything in it is basically relevant, if i understand correctly what relevant means

winged badger
#

yeah

#

then its fine

tawny mason
#

ok, i'll give it a try then

#

i'll come back with my findings once again ๐Ÿ˜„

chrome bay
#

Have to be careful with NetDormancy btw

#

everytime an actor wakes from dormancy it sends all it's properties again

#

As far as network is concerned, it's like a new actor

#

Can be a good CPU saving but not so useful for bandwidth

#

discovered this the hard way with pooled projectiles

twin juniper
#

How can i use My own sever for Multiplayer?

empty axle
winged badger
#

it changed, but i was not relevant when it did

#

so i don't know about the change

#

i walk in, its still dormant

#

so i see invalid state

empty axle
#

wow it's really like that? I thought the actor will replicate the changes and go dormant automatically

winged badger
#

and how would it know that it needs to snap out of dormancy

#

and for what connection

#

that is not an easy test to make

empty axle
#

@winged badger Let's go with example
so if one player for which tree is relevant will cut it down and we will change the tree to dormant the other players which were not relevant after coming to that tree wouldn't see felled tree?

#

I thought that server when changing actor to dormant would save non-relevant connections for that actor and replicate it just once for every connection when the actor is relevant again for them.

tawny mason
twin juniper
#

Thx

tawny mason
#

@winged badger @chrome bay I'm testing some more stuff and got it down quite a bit

#

89 hz & 1.21 KB/s for 8 AI characters and 2 players

#

so basically 1 client to send this to

#

does this seem ok ?

winged badger
#

i have about 4kB/Player total

#

but can spike to 8-10

#

on 8 players

tawny mason
#

yes, with stat net i see about 4KB/s per client

winged badger
#

so as long as host isn't using ADSL from 1995

#

its good

#

or dial up

tawny mason
#

haha yes i'm sure that would be an issue hehe

#

What about the RPC size ? is this normal ?

#

sorry for asking about everything it's my first time submerging myself in this

silent valley
#

Hi guys, can someone clarify how I can set my Dedicated server session 'name' up so that clients calling FindSessions() can get it?
I'm using the Steam online subsystem.

tawny mason
#

you can add a session parameter

#

with name session for example

nocturne iron
#

Is the GetFName of an actor the name on every client - will a, like the pawn, on the server and the client have the same name?

tawny mason
#

and when getting all the sessions reading it

silent valley
#

@tawny mason using this member of FOnlineSessionSettings?

/** Array of custom session settings */
FSessionSettings Settings;
tawny mason
#

@nocturne iron characters on client and server will have _C_0 at the end for local characters and have a count up _C_1..._C_N for every additional character, this is not supposed to be _C_1 on client and server for the same character.

#

@silent valley i'm not entirely sure, but there is a generic list of session settings where you can add some params in form of Key <-> Value

silent valley
#

yeah I was looking at that but it looks like the Steam OSS strips out anything it doesn't expect

tawny mason
#

mmm, let me find my blueprint

#

i will put up a snippet

silent valley
#

that would be great thanks, otherwise I'll just try and see what happens

#

but it's a bit of a pain to add the code, build, deploy to server etc

#

if I knew what to do in advance it goes a lot quicker!

tawny mason
#

@silent valley btw are you using the Advanced Sessions plugin by any chance ?

silent valley
#

I have a test project using it, yes

#

if your BP requires the plugin that's fine I can figure out what it's doing in the background

tawny mason
#

ok

silent valley
#

do you pass it in as ExtraSettings into CreateAdvancedSession ?

tawny mason
#

yes

#

that's it

silent valley
#

perfect, thanks!

tawny mason
#

then i read it and put it on the session umg widget

silent valley
#

and you use Steam?

#

thanks man, works great ๐Ÿ‘

tawny mason
#

nice yw!

nocturne iron
#

I have been dealing with this for 2 hours now... I think it's a simple problem, but I'm super new to UE networking (and yes, i have looked in the network compendium). I think the problem has something to do with ownership but I'm not sure how. I have two classes ABaseWeapon(actor) which is created on BeginPlay() from APlayerCharacter(pawn). What i want to happen is that the pawn can call a single function on the weapon (ABaseWeapon::Attack()) and then the weapon handles the replication. But i don't get how this doesn't work:..

Result (client):

LogTemp: [Drain] ABaseWeapon::Multicast_Attack_Implementation()```

Expected Result  (client):
```LogTemp: [Drain] ABaseWeapon::Server_Attack_Implementation()
LogTemp: [Drain] ABaseWeapon::Multicast_Attack_Implementation()
LogTemp: [Drain] ABaseWeapon::Multicast_Attack_Implementation()```

Result (server - which is what i want / correct result):
```LogTemp: [Drain] ABaseWeapon::Multicast_Attack_Implementation()
LogTemp: [Drain] ABaseWeapon::Multicast_Attack_Implementation()```

ABaseWeapon:
```c
void ABaseWeapon::Attack()
{
    if (!GetWorld()->IsServer())
    { // CLIENT
        Server_Attack(); 
    }
    else
    { // SERVER
        Multicast_Attack(); 
    }
}

void ABaseWeapon::Server_Attack_Implementation()
{
    UE_LOG(LogTemp, Log, TEXT("[Drain] ABaseWeapon::Server_Attack_Implementation()"));

    Multicast_Attack();
}

void ABaseWeapon::Multicast_Attack_Implementation()
{
    UE_LOG(LogTemp, Log, TEXT("[Drain] ABaseWeapon::Multicast_Attack_Implementation()"));
}```
#

.
.
APlayerCharacter (where the weapon is spawned):

void ABasePlayerCharacter::BeginPlay()
{
    Super::BeginPlay();

    // Spawn Weapon
    if (PrimaryWeaponClass == nullptr) PrimaryWeaponClass = ABaseWeapon::StaticClass();
    FActorSpawnParameters SpawnParams;
    SpawnParams.bNoFail = true;
    SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
    
    FTransform SpawnTransform;
    SpawnTransform.SetLocation(GetTransform().GetLocation());
    SpawnTransform.SetRotation(FQuat(FRotator::ZeroRotator));

    CurrentWeapon = GetWorld()->SpawnActor<ABaseWeapon>(PrimaryWeaponClass, SpawnTransform, SpawnParams);
    if (CurrentWeapon)
    {
        CurrentWeapon->AttachToComponent(FirstPersonMesh, FAttachmentTransformRules::SnapToTargetNotIncludingScale);
    }
}```
winged badger
#

CurrentWeapon->SetOwner(this)

#

and the entire spawn code needs to be server only

nocturne iron
#

@winged badger Ok, now i don't have weapon reference on the client, how do i get that?

#
void ABasePlayerCharacter::BeginPlay()
{
    Super::BeginPlay();

    if (!GetWorld()->IsServer())
    {
        Server_SpawnWeapon(); // CLIENT
    }
    else
    {
        Internal_SpawnWeapon(); // SERVER
    }
}

void ABasePlayerCharacter::Internal_SpawnWeapon()
{
    // Spawn Weapon
    if (PrimaryWeaponClass == nullptr) PrimaryWeaponClass = ABaseWeapon::StaticClass();
    FActorSpawnParameters SpawnParams;
    SpawnParams.bNoFail = true;
    SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;

    FTransform SpawnTransform;
    SpawnTransform.SetLocation(GetTransform().GetLocation());
    SpawnTransform.SetRotation(FQuat(FRotator::ZeroRotator));

    CurrentWeapon = GetWorld()->SpawnActor<ABaseWeapon>(PrimaryWeaponClass, SpawnTransform, SpawnParams);
    if (CurrentWeapon)
    {
        CurrentWeapon->AttachToComponent(FirstPersonMesh, FAttachmentTransformRules::SnapToTargetNotIncludingScale);
        CurrentWeapon->SetOwner(this);
    }
}

void ABasePlayerCharacter::Server_SpawnWeapon_Implementation()
{
    Internal_SpawnWeapon();
}```
winged badger
#

UPROPETY(Replicated) ABaseWeapon* PrimaryWeapon;

#

and corresponding entry in GetlifetimeREplicatedProps

nocturne iron
#

You can replicate a pointer?

winged badger
#

and no

#

BeginPlay if (GetLocalRole() == ROLE_Authority) SpawnWeapon();

#

you know it needs to be spawned every time

#

so no point in making the client wait for it

#
  • this is simpler, and therefore, better
nocturne iron
#

I don't get this? How does it make sense that you replicate a pointer?

quasi tide
#

@chrome bay Sorry to ping you, but if you don't mind; how does hit detection work on HLL?

chrome bay
#

clients simulate the projectile locally, notifies the server if it's hit something

#

the server rewinds all players to their past positions based on ping and resimulates the bullet

#

and either validates/rejects the hit

quasi tide
#

And do you just check the capsule collider on the server or mesh based? If the latter, couldn't the animation position mess it up?

chrome bay
#

We don't use the mesh because we don't run animations on the server

quasi tide
#

I'm doing a PvE game, and my thought was to do what you just described, except on the server, just count a hit against the capsule collider as a registered hit.

chrome bay
#

and even if we did they wouldn't be in-sync anyway

#

But we don't do hits vs the capsule because we don't physically move the players when rewinding

#

We validate as a line-line intersection with a tolerance radius

#

A capsule can just be represented as a line and a radius from that line

#

So a line-line intersection test it easy

kindred widget
#

But server hit test on meshes works greeeeat. Just ask ARK. Dodo birds running around with arrows in their head.

quasi tide
#

Alright - highly appreciate the info. I've been splitting hairs over this for a few days now and Authaer pointed out last night the sync issues with mesh based collisions.

winged badger
#

@nocturne iron its not the pointer that gets sent, its the weapon actor's NetGUID

chrome bay
#

yeah mesh-based is a losing battle

winged badger
#

which client can resolve as a pointer to its instance of the weapon actor

chrome bay
#

If you want per-limb damage, my advise is to send an enum to the server, and either trust the client on that detail, or add some extra validation

nocturne iron
#

@winged badger Thx, that makes more sense!

chrome bay
#

we validate headshots but nothing else

quasi tide
winged badger
#

i'd actually name the bones to be compatible with GameplayTag hierarchy

chrome bay
#

yeah you could do that also

winged badger
#

then i would send the Tag, not the enum or Name for bone hits

#

Enums are terrible for systems that evolve

meager spade
#

well

chrome bay
#

we treat thigh-below as "leg" and shoulder-below as "arm", so the hit is always either head, torso or limb

meager spade
#

you wouldnt do that

winged badger
#

and Names don't replicate efficinetly

chrome bay
#

So just 2-bits

meager spade
#

you would have a map, BoneName to GT

quasi tide
#

But yeah, I definitely want per-limb damage, because I imagine, then, I would be able to do hit responses properly.

As it stands, I'm not sure how I would go about doing a hit reaction using the line-line intersection (I need to figure out how to do that as it is, lol)

chrome bay
#

yeah we don't go into per-bone detail, just distinguish from head, limb and torso in our case

#

well all the hit FX are played locally on each client

winged badger
#

so you can't get shot in the arse? ๐Ÿ˜„

chrome bay
#

haha

meager spade
#

meh in my pet project, all hits are client side

chrome bay
#

We replicate the health state and "last damage event" together in a struct for each character

meager spade
#

server does some validation, but no re-winding ๐Ÿ˜„

chrome bay
#

So they just play the hits/responses that side based on when they receive damage

#

yeah we had to do rewind once we moved away from hitscan weps

meager spade
#

all you weapons are projectile?

chrome bay
#

yeah all of em

#

not actor projectiles though

meager spade
#

my hitscan sniper had drop off, and timed hit based on distance

#

has*

#

still WIP though, not touched any of that in months ๐Ÿ˜ฆ

quasi tide
#

Yeah - I have that set up properly. It's just been the hit detection that I've been struggling with. Making sure it lines up properly. But once I get this wired up sometime this week (I wish I had more time to work on this project), the next challenge would be to get the right hit reaction, so if the hit is on the shoulder versus the arm for example.

Maybe I could send the bone hit and let the server figure it out with the line-line test? ๐Ÿค”

dark edge
chrome bay
#

yeah

dark edge
#

How do you do particle effects?

#

The trails etc

chrome bay
#

Just spawn the particles in the world, then the projectile manager updates their transforms after bullets have all been updated

meager spade
#

think TRS2 does a similar system

winged badger
#

it does

meager spade
#

iirc @winged badger ?

#

yeah

winged badger
#

also very fake "not-hitscan"

dark edge
#

1 particle system per projectile tho or 1 global particle sim with each projectile getting a trail?

winged badger
#

all weapons are hitscan

#

but

chrome bay
#

@dark edge one for each bullet

winged badger
#

they won't inflict damage immediately to remote targets

chrome bay
#

But many bullets don't have particles too tbh, we only show the local players' ones and big snipery / MG ones

#

Yeah we needed things like shot lead and ballistics etc, so delaying the hit wasn't suitable ๐Ÿ˜ฆ

#

Would have made life easier though ๐Ÿ˜„

winged badger
#

you're doing WWI, we're doing Mars, not the same feel there ๐Ÿ˜„

chrome bay
#

haha

dark edge
#

@chrome bay I got you. Yeah I implemented this type of setup with a subsystem and it performed really well. I take it you just do a line Trace per tick and adjust the line Trace based on position, velocity, acceleration?

chrome bay
#

pretty much

#

the good thing is you can spawn/despawn/simulate as much as you want without having to worry about actor lifetimes and stuff too

meager spade
#

only projectiles i use is for Rockets/Grenades ๐Ÿ˜„

chrome bay
#

Also was very helpful once we added bullet penetration too

#

yeah we're the same

meager spade
#

ah thats one thing i need to tackle, when i get free time (not any time soon..), penetrating weapons

chrome bay
#

Yeah that was... not enjoyable

dark edge
#

I guess effectively it's doing the same thing as the projectile movement component, but with line traces instead of shaped traces, and in parallel instead of each actors update

chrome bay
#

Had to duplicate a lot of the engines' code to trace backfaces and such ๐Ÿ˜ฆ

#

it's quite a simple system but trying to distinguish between "entry" and "exit" was the challenging bit

meager spade
#

my idea was to trace on a certain channel, and overlap to grab the stuff

chrome bay
#

Damn level designers and their overlapping geometry and modular stuff ๐Ÿ˜„

meager spade
#

and only allow penetrating on certain things

chrome bay
#

yeah we did it based on physical materials

winged badger
#

wouldn't the normal dot direction work, be positive for exit, negative for entry?

chrome bay
#

ah yeah it does, but I just wanted to do one trace for each segment, and not have to back-trace to find the exit locations etc (for particles/decals)

#

So I ended up with a "multi-block multi line trace" sort of thing

#

Which returns all the blocks on a given segment, rather than stopping at the first one

#

Then yeah use dot product to work out if it's forward/backward face

whole urchin
#

when i enable "use controller rotation yaw" for TPS rifle mode, in client side, the server character always faces client camera forward

#

is there a work around this problem, or a better way to do it ?

kindred widget
#

That's not default behavior. UseControllerRotationYaw is only supposed to apply to that pawn's Controller's rotation. So unless you're updating the other pawn's controller's rotation somewhere?

whole urchin
#

no

meager fable
#

if I have an actor that has the same velocity and physics settings on the server and the client then its trajectory should be the same on both and the server should never need to correct its position so there should never be any lag to its movement is that correct?

steel fractal
meager spade
#

yeah using GetPlayerChraracter i assume

#

common mistake for new people

steel fractal
#

lmao just saw it in my bp exactly

#

what can i use as an alternative

winged badger
#

always use a relative path over static one

#

if you're calling that from controller

#

GetControlledPawn is the same Pawn GetPlayerCharacter[0] on client

#

but its the same pawn you intended to shoot with on server, as well

#

and GetPlayerCharacter[0] on server is host's pawn

steel fractal
#

hmm

winged badger
#

paired with the fact that whatever is connected to ServerRPC executes on server

#

you begin to understand your problem

#

and if GetPlayerCharacter[0] is connected to a node executing on server, it gets evaluated on server, as well

steel fractal
#

i see

#

im shooting from host with getplayercharacter

#

so i need something thats not so specific

winged badger
#

from which blueprint are you shooting?

steel fractal
#

Im shooting from my weapon BP

winged badger
#

when you spawn the weapon

#

do you call SetOwner on it?

steel fractal
#

I did not call set owner

winged badger
#

is it spawned at runtime? show me how

steel fractal
#

ive been making it, now im implementing multiplayer after the fact

kindred widget
#

It comes out of the Listenserver's character's weapon.

winged badger
#

ok, easiest solve for your access character from a weapon

#

add a variable of your character's type to weapon

#

after you spawn a weapon

#

call SetOwner, giving it Self as a reference, and also set your OwningCharacter variable on the weapon

#

make that OwningCharacter variable replicated

#

result: weapon can access the character holding it from both client and server directly

#

and

#

it can also call RPCs

steel fractal
#

okay ill try that method thank you so much

#

Im a bit new to replicating

winged badger
#

i can tell ๐Ÿ™‚

steel fractal
#

haha

#

so i would cast from my third person character

#

but what variable would I make?

#

wait no get player character smh

winged badger
#

nonono

#

weapon gets a variable CharacterOwner

#

which is of same type as your character

#

your SpawnActor node for weapon has a return pin

#

thats a pointer to your weapon

#

you pull from it and you do SetOwner(function) and SetCharacterOwner(variable) (replicated)

#

and as you are spawning weapon from a character

#

you put Self as input for both of those nodes

steel fractal
#

so character owner is the non specific way of saying third person character bp?

#

oh wait i get it

#

I set a variable for my weapon

#

I connect the variable to the return pin in my Third Person BP under spawn actor

winged badger
#

you'll have to screen it, as you're really bad at describing it

steel fractal
#

lmao ill map it out and send it

#

blue prints are one thing

#

but replicated blueprints are a different breed

#

imo

#

lol

hidden thorn
#

Is the PlayerCharacter spawned before or after the PlayerState?? (I would personally say after but want to check)

winged badger
#

before on server, usually after on client ๐Ÿ˜„

hidden thorn
#

That's good enough for me as I only need to use it locally

winged badger
#

(to be spawned on client the actor has to replicate, and it has lower net priority by default then pawn)

#

so it replicates after pawns and controllers do

#

which makes it usually after in client

#

and sorry

#

edited above

#

PlayerState is spawned from APlayerController::PostInitializeComponents

#

which is by default, and almost always for custom spawning, before the Pawn is Spawned

hidden thorn
#

I will have to start referring to PlayerCharacter as Pawn ๐Ÿ˜„ and no worries, yeah I guess I can just override it and do what I need to do in there.

winged badger
#

HandleStartingNewPlayer

#

is a good hook for most spawning logic

#

you have a PC reference provided, PS is already valid by that point, and can be accessed via the PC

#

Parent/Super call calls RestartPlayer, which spawns the PlayerPawn

#

(GameMode BlueprintNativeEvent)

hidden thorn
#

I will most likely need to dig deeper as there are a ton of useful methods that I don't even know about, but thanks for the explanation so far.

#

Also just wondering is there some sort of method that can keep players from spawning on ServerTravel until all the clients have travelled to the new map (Since not everyone would have the same hardware)

steel fractal
#

im looking up a lot of tutorials but it looks liek thjeyre doing it from the character bp

#

is that whats making it harder for me?

hidden thorn
#

Are you just trying to set the owner of the Weapon once spawned?

steel fractal
#

yes

#

lol the way everyoen says it it sounds like its easy

hidden thorn
#

๐Ÿ˜„

steel fractal
#

and i just really dont have a clue ๐Ÿ˜‚

#

idk why there isnt any tutorials on set owner

#

im a very visual person thats why

hidden thorn
#

Everyone is to a certain extent ๐Ÿ˜„

#

Left side would be the weapon you spawn and that's all you have to do to set the owner (This is if you're spawning it off the PlayerCharacter)

steel fractal
#

Ah i see now that makes a lot of sense

hidden thorn
#

Now if you spawn it from a different blueprint you should be fine by just doing this

#

Now if you spawn it from a different blueprint you should be fine by just doing this, I've never done it via BP and I've done multiplayer about 3 years ago and just started again so forgot a loooooooooot of things. I might be wrong in saying this is one way of doing it as it might not be.

steel fractal
#

Ill take a look at both, but what you showed me was a step in the right direction because the lines end at my cross hair

#

But the origin is still coming from the other characters weapom

hidden thorn
#

Then you're problem is when you AttachActorToComponent as it's referencing the wrong Mesh

steel fractal
#

interesting

#

i set the parent to my third person character skeletal mesh

hidden thorn
#

So you're spawning it from the Character BP??

winged badger
#

well, you did connect Owner now

steel fractal
#

fuck i think i am

hidden thorn
#

Also try to print out the Parent see if its actually referencing the wrong Mesh

winged badger
#

you also want the SwitchAuthority before you spawn the weapon

#

not spawning extra one on clients

#

and you can then access your character with Cast<ThirdPerson_BP>(GetOwner())

#

from the weapon

steel fractal
#

im checking it out rn

carmine citrus
#

Is there any documentation on what is on the server and what is on the client? For example PlayerController vs Character. I'm still new to unreal, and I can't find any good info. Most of the sv -> cl stuff I done is winsock and just sending bits across

steel fractal
#

@winged badger do you mean switch has autority?

winged badger
#

that

#

yes

#

doesn't normally do any networking in BP

hidden thorn
#

Is there a difference between that and HasAuthority?

winged badger
#

nno

#

IsServer and HasAuthority can vary in output

#

in case you spawned the Actor locally on client

#

then client has authority over it

#

the netmode is still ENetMode::NM_Client

steel fractal
#

that what I tried for the line trace but it still comes out of the original players gun

#

wait im dense i dont think i replecated the blueprint itself

#

like the mesh

kindred widget
#

@carmine citrus Natively, I believe all the major actor classes are normally replicated except for PlayerController and GameMode. GameMode isn't actually replicated and only exists on the server. PlayerController only exists on it's owning client and the Server. So Client2 has no way of accessing Client3's PlayerController without going through the server. Which means that on a ListenServer, each client or the server's PlayerController0 is always the owning client's controller. Everything else I know of exists one on each machine.

carmine citrus
#

Yea that all makes sense. I am still reading the doc @winged badger gave me, thanks again. For a practical example, should things like the player's inventory and weapons live in the player controller as that is on the server and owning client, and not the character?

kindred widget
#

Inventory depends on your system. If you don't care about other players knowing about the inventory, probably place it on the Controller. Otherwise PlayerState is an okay place for it. Alternatively if you want it to remain on their corpse, you can place it on the Character as well so that it's where it needs to be.

#

Weapons, I'd personally place on the character class and make the weapon replicated so the client can RPC through it.

#

Well, attach a weapon to the character class I mean. It being it's own replicated actor.

carmine citrus
#

The more you know.gif. OK, thats where I was confused when looking at the shooter game example, and talking to an old classmate. Thanks for clearing it up.

#

And giving a real world exmaple

kindred widget
#

I place inventory stuff in Components myself. I have a main ActorComponent that I can attach to anything in the world. Character, Shelf, crafting station, etc. I use the main component for most everything except for the player. I didn't want players looting one another, so I made a child of the inventory component and made it only replicate to the player owning the character to avoid network traffic to the other clients.

winged badger
#

i keep inventory in playerstate

#

so it can survive disconnect and reconnect

#

on an actorcomponemt

hidden thorn
#

So does the PlayerState remain even after player disconnect? If so for how long?

#

Also worst case you can duplicate the inventory and have it on the Character as well if its a sort of survival game where players can pick other players/

knotty veldt
#

@hidden thorn Depends which game mode you're using. One of the implementations will destroy the playerstate after some amount of time disconnected. If reconnect happens in time it'll copy the properties from the inactive state to the new one... so you need to override that copy function to include your new stuff you've added. I don't recommend using AGameMode unless it maps well to what your game requirements are. Otherwise use AGameModeBase and make it do exactly what you want. Trying to shoehorn non-arena based multiplayer (ala Unreal Tournament) into the GameMode by overriding its virtual functions is a recipe for spaghetti code bug fest.

unkempt tiger
#

it feels like I asked this because this question has been burning for me for quite some time now but I don't recall if I did

#

Can wolrd subsystems do any form of networking / sending RPCs?

rotund topaz
#

Anyone here have any issues with networking functioning very poorly through steam vs editor? I put editor settings to 400 ping and 5 packet loss coming and going and it seems mostly fine. When I package the game and play online with a friend through steam it is very choppy and I get a lot of server corrections with no packet loss and ~70 ping.

hidden thorn
#

2 things I want to ask,
1: Is it a bad thing passing an Object Pointer e.g Character as a parameter to a Server RPC ??? Would it be better to pass an ID and let the server find the player and grab it.
2: Just trying to remember how RPCs work really, if I press a Key e.g. 'E' I send an Server RPC call and I then call some methods on the Object I passed such as setting it to be a ragdoll and then call a separate method on it that he is dead (The Boolean is Replicated so this should be fine) do I need a multicast to let the other players know that he is a Ragdoll now or if it happens on the server it happens on everyone's machine? Or should I send a Multicast RPC not a Server RPC

empty axle
#
  1. it is fine to pass pointers
unique wave
#
  1. you have to (from the server) send to other clients that he should ragdoll.
    You can do that with a multicast RPC, or a replicated property (OnRepNotify). Which one is best depends a bit on your project condition, as well as how long the Ragdoll character will stay alive (whether or not you are calling DestroyActor on it)

Multicast RPC only works if you call it from the server, you can't call it from client. If you need to communicate something from Client A to other clients, you have to go through server first. So Server RPC is the right beginning.

hidden thorn
#

Yeah that makes sense thanks, also thanks for pointing out the duration of the Ragdoll Character, as I will not be destroying the actor I guess I will have to simply freeze the character so its not a ragdoll anymore but still in that position (This is one for another time)

gritty pelican
#

how i can serialize movement location, rotation and velocity?

unkempt tiger
#

in a NetSerialize override of a struct

#

or just pass those values straight in as function parameters

#

if thats what you meant

gritty pelican
#

I have a pawn (player). I send location, rotation, linearvelocity, angularvelocity to the server from the client on event tick

#

What do I need to do to serialize these values

unkempt tiger
#

how are you sending them?

#

are they all in a struct?

gritty pelican
#

no

#

with RPC

#

Unreliable

unkempt tiger
#

then its just ServerReceive(FVector Location, FRotator Rotation, FVector LinVel, ... etc)

#

the engine automatically serializes it for you

#

if you want custom serialization, then place all of those in a ustruct and override NetSerialize()

gritty pelican
#
UFUNCTION(Server, UnReliable)
void Server_PassMovementInfo(FVector_NetQuantize NewPawnLocation, FRotator NewPawnRotation, FVector_NetQuantize NewPawnLinearVelocity, FVector_NetQuantize NewPawnAngularVelocity);
void Server_PassMovementInfo_Implementation(FVector_NetQuantize NewPawnLocation, FRotator NewPawnRotation, FVector_NetQuantize NewPawnLinearVelocity, FVector_NetQuantize NewPawnAngularVelocity);
unkempt tiger
#

that should work then

gritty pelican
#

yes, it's work

unkempt tiger
#

then what else do you wana do?

gritty pelican
#

But I think this is too much data.

unkempt tiger
#

these are already compressed by the engine

#

the only way to lower bandwidth is if you have some special assumptions based on your pawns movement

gritty pelican
#

Would it be better to do this in a structure and pass one structure?

unkempt tiger
#

for example if its in some state, then you dont need to pass linear velocity, in which case just flag it with a single bit etc

#

it would be cleaner that's for sure

#

and that way you can net serialize it yourself per your specifications

#

see for example how FHitResult is net serialized

plush wave
#

How does unreal package, say a vector, over the network?

#

Does it just serialize then send?

#

Or does it somehow cut the precision of the vector

gritty pelican
#

they use

bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess);
#

can i use FRepMovement?

#

or override ReplicateMovement?

#

Should I do the same? UPROPERTY(Transient)

USTRUCT()
struct ENGINE_API FRepMovement
{
    GENERATED_BODY()

    /** Velocity of component in world space */
    UPROPERTY(Transient)
    FVector LinearVelocity;
hidden thorn
#

So just had some issues with ServerRPC and MulticastRPC I also left the print logs just for easiness, they all get called expect the ones inside _Validation
KillPlayer gets called on key press.

#
void UCharacterIntruder::KillPlayer()
{
    if (IsValid(OwningPlayer) && IsValid(OwningPlayer->GetPlayerInRange()))
    {
        float _distance = FVector::Distance(OwningPlayer->GetActorLocation(), OwningPlayer->GetPlayerInRange()->GetActorLocation());
        if (_distance <= 200)
        {
            Server_KillPlayer_Implementation(OwningPlayer->GetPlayerInRange());
        }
    }
}

void UCharacterIntruder::Server_KillPlayer_Implementation(AIntruderCharacter* player)
{
    UE_LOG(LogTemp, Warning, TEXT("Server_KillPlayer_Implementation"));
    float _distance = FVector::Distance(OwningPlayer->GetActorLocation(), player->GetActorLocation());

    if (_distance <= 200)
    {
        UE_LOG(LogTemp, Warning, TEXT("Server_KillPlayer_Implementation - DistanceValid"));
        Multicast_KillPlayer_Implementation(player);
    }
}
bool UCharacterIntruder::Server_KillPlayer_Validate(AIntruderCharacter* player) 
{
    UE_LOG(LogTemp, Warning, TEXT("Server_KillPlayer_Validate"));

    if (!IsValid(player)) return false;

    UE_LOG(LogTemp, Warning, TEXT("Server_KillPlayer_Validate - ValidPlayer"));

    float _distance = FVector::Distance(OwningPlayer->GetActorLocation(), player->GetActorLocation());
    if (_distance > 200) return false;

    UE_LOG(LogTemp, Warning, TEXT("Server_KillPlayer_Validate - ValidPlayer - ValidDistance"));

    return true;
}
#
void UCharacterIntruder::Multicast_KillPlayer_Implementation(AIntruderCharacter* player)
{
    UE_LOG(LogTemp, Warning, TEXT("Multicast_KillPlayer_Implementation"));

    if (IsValid(player))
    {
        UE_LOG(LogTemp, Warning, TEXT("Multicast_KillPlayer_Implementation - ValidPlayer"));
        
        // This doesn't work
        ACharacterState* playerState = Cast<ACharacterState>(player->GetPlayerState());
        playerState->PlayerDied();

        // This works, the player can't move anymore
        player->GetCharacterMovement()->DisableMovement();
        
        // This doesn't work
        player->GetMesh()->SetAllBodiesSimulatePhysics(true);
        player->GetMesh()->SetAllBodiesPhysicsBlendWeight(1);
        player->GetMesh()->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
    }
}

PlayerDied declaration & implementation


UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated)
bool bIsAlive;
    
DOREPLIFETIME(ACharacterState, bIsAlive);
    
void ACharacterState::PlayerDied()
{
    bIsAlive = false;
    OnIsPlayerAlive.Broadcast(false);
}
meager spade
#

you do know Validate will disconnect the player

#

from the game if it returns false?

hidden thorn
#

Yes

meager spade
#

show the header for it

#

the rpcs

hidden thorn
#
public:
UFUNCTION(BlueprintCallable, Category = Player)
void KillPlayer();

UFUNCTION(Server, Reliable, WithValidation)
void Server_KillPlayer(AIntruderCharacter* player);

UFUNCTION(NetMulticast, Reliable)
void Multicast_KillPlayer(AIntruderCharacter* player);
#

Even the print log inside the Multicast gets called and the DisableMovement seems to work as well the player can't move anymore after that.

gritty pelican
#

@hidden thorn u need to add this


UFUNCTION(BlueprintCallable, Category = Player)
void KillPlayer();

UFUNCTION(Server, Reliable, WithValidation)
void Server_KillPlayer(AIntruderCharacter* player);
void Server_KillPlayer_Implementation(AIntruderCharacter* player);

UFUNCTION(NetMulticast, Reliable)
void Multicast_KillPlayer(AIntruderCharacter* player);
void Multicast_KillPlayer_Implementation(AIntruderCharacter* player);
hidden thorn
#

Hmmm, from what I know after one of the version can't remember which one declaring _Implementation wasn't necessary anymore. I will give it a try but i doubt as it compiles just fine.

gritty pelican
#

try to add

bool Server_KillPlayer_Validate(AIntruderCharacter* player);
bool Multicast_KillPlayer_Validate(AIntruderCharacter* player);
#

under Implementation

hidden thorn
#

Another thing I tried is set the Mesh to Component Replicates, when called from the Server the client would go down but the bones wouldn't blend, it was still STIFF as a stick but on the ground. When called from the Client it seems the Server isn't affected.

#

I will try those now

gritty pelican
#

@plush wave can i disable bool ReplicateMovement and send FRepMovement from Client to Server? and for other client call GetReplicatedMovement() for get Location, Rotation etc?

hidden thorn
#

@gritty pelican That didn't do it

gritty pelican
#

Maybe you should put the "validate" function higher than the "implementation"

#
bool SomeRPCFunction_Validate( int32 AddHealth )
{
    if ( AddHealth > MAX_ADD_HEALTH )
    {
        return false;                       // This will disconnect the caller
    }
    return true;                              // This will allow the RPC to be called
}

void SomeRPCFunction_Implementation( int32 AddHealth )
{
    Health += AddHealth;
}
#

Although I don't know if this makes sense

hidden thorn
#

That's prob not going to make a difference, all the other calls I have Validate is after Implementation, thanks for the help anyway, I will go have some sleep and try again tomorrow ๐Ÿ˜„

gritty pelican
#

can I use this structure to replicate movement? or i need to add something?

USTRUCT()
struct EXTREMEARENA_API FRepExtremePawnMovement
{
    GENERATED_BODY()

        /** Velocity of component in world space */
    UPROPERTY()
        FVector_NetQuantize LinearVelocity;

    /** Velocity of rotation for component */
    UPROPERTY()
        FVector_NetQuantize AngularVelocity;

    /** Location in world space */
    UPROPERTY()
        FVector_NetQuantize Location;

    /** Current rotation */
    UPROPERTY()
        FRotator Rotation;

    bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess);
};

template<>
struct TStructOpsTypeTraits<FRepExtremePawnMovement> : public TStructOpsTypeTraitsBase2<FRepExtremePawnMovement>
{
    enum
    {
        WithNetSerializer = true,
    };
};
winged badger
still path
#

What would cause my clients not being able to cast to a game mode? (Server has no problem doing it)

fossil spoke
#

The fact that Clients dont get a GameMode.

#

Only the Server has the GameMode

shut gyro
#

Does anyone know how the ClientTravel command works? I am trying to implement a travel using EOS and am getting the following error:

#
[2020.11.26-00.04.11:942][738]LogNet: Browse: EPIC://000260a3badb42808d1f8a910f7cd066:0/
[2020.11.26-00.04.11:942][738]LogNet: Warning: Travel Failure: [ClientTravelFailure]: 
[2020.11.26-00.04.11:942][738]LogNet: TravelFailure: ClientTravelFailure, Reason for Failure: ''
#

Steam's implementation of the OSS has a port set, where mine is 0 which may be a problem? Also, Steam also has a map of travel and mine does not

fossil spoke
shut gyro
#

thought this would be more relevant, since it is engine-specific but can do

still path
#

@fossil spoke would running the code via a custom event set to server-reliable all clients to communicate with the game mode? (trying to set up a menu for respawns that are requested from the game-mode)

fossil spoke
#

No, the GameMode does not exist on Clients period.

#

Use the GameState

still path
#

got it, thank you.

steel vault
#

Ok. Time to ask a complete noob question. I have put this off for so long because I haven't needed to address this yet: From what I've read in here I know that I'm eventually going to need some custom logic in my PlayerController, GameState, and PlayerState, so I've just created C++ subclasses of each and then created a BP version of each. When I assign these BP's inside the custom GameMode BP, all of my movement, animation, and pretty much everything else just breaks. I believe this might have something to do with me now needing to define specific actions for spawning and possessing? What specifically do I need to override to get things back to normal?

winged badger
#

you paired GameMode with GameStateBase or GameModeBase with GameState

#

and your output log is also complaining about it

steel vault
#

You really have seen it all....

#

So do you recommend I use base for gamestate?

#

Or should I be forcing both to be non base

winged badger
#

don't use base ones

steel vault
#

Damn tutorials I swear to god

winged badger
#

gives you better control about match start

steel vault
#

Thanks man.

gritty pelican
#

@shut gyro Example

FString Port = FString::FromInt(Result.pfServerDetails->Ports[0].Num);
FString URL = Result.pfServerDetails->IPV4Address + ":" + Port + "?PlayFabId=" + PlayFabEntityKeyID;

UE_LOG(LogTemp, Warning, TEXT("OnPlayFabGetMatchSuccess | Connect to URL %s"), *URL);

GetWorld()->GetFirstPlayerController()->ClientTravel(URL, ETravelType::TRAVEL_Absolute, false);
steel vault
#

@winged badger got rid of the error but I still don't appear to posses my character and no inputs work still. Anything else you can think of?

gritty pelican
#

Inside your character, there is an AI controller class

winged badger
#

no super call on beginplay - gamemode, gamestate

gritty pelican
#

and possessing method

winged badger
#

no super call on getlifetimereplicatedprops in gamestate

#

if its clients only

steel vault
#

Those are all required overrides? I thought not overriding would essentially use the super classes.

winged badger
#

not required

#

but if you added any and didn't call super

#

kaboom

steel vault
#

Yea I just have the subclasses completely empty right now and it's not working.

#

Unless the BP version does something bad like that

winged badger
#

the fact that you rebased them

#

doesn't mean your world settings on that level are set correctly now

steel vault
#

Gotcha. Will start overriding

winged badger
#

level can still ahve the base - non base pair

#

set under its classes

gritty pelican
#

@steel vault One of the options:
in gamemode create "event handle start new player", then call spawn actor from a class, select your class, then you can possess NewController(from event) to spawned character

#

Also, to get the coordinates for the spawn, you can call "FindPlayerStart" from the NewController(from event)

winged badger
#

@gritty pelican this is not a my pawn doesn't work, this is nothing works situation

#

where World didn't call BeginPlay at all

steel vault
#

What I find very strange is that when I switched them out, I switched them back to regular again and nothing worked still.

winged badger
#

did you set the correct GameState class in GameMode?

#

also, PC, Pawn...erc

steel vault
#

To my custom one that I subclassed from GameState yea.

#

If you mean when I tried to reset, then I believe so, I can try it again to see if it fixes everything or not.

gritty pelican
#

Did u select GameMode in WorldSettings?

steel vault
#

WorldSettings? I have it selected in maps and modes for project settings.

winged badger
#

levels can override that

#

individually

#

in their world settings

gritty pelican
#

click here

shut gyro
steel vault
#

I just tried to override it and that didn't do the trick. What I can tell you is that now it appears I am spawning separate to the other character. It appears to be a problem where I am not possessing the character that I see

shut gyro
#

I am using a custom eos plugin based solution for listen servers and am wondering if I need to put the port in at all since your approach seems dedi-based, hm

steel vault
#

So I have no body or controls, but I can see a character fully built near me who does

gritty pelican
#

@shut gyro You might be better off using "ServerTravel

#

for server

#

If you did everything correctly, your character will spawn where the player start placed

shut gyro
#

@gritty pelican problem is I am using beacons so I think I have to destroy the beacons after I get the RPC so that I don't crash in the next level - at least that's how it was in steam - but I will give it a try

gritty pelican
#

@shut gyro try to enable seamless travel option in gamemode

shut gyro
#

yea, I'm aware. AFAIK though wouldn't that persist the actors across levels though?

#

or can I turn that off

#

I had problems in the past with it because I think I tried to use it with persistent levels and my pawns would fall through the floor

gritty pelican
#

I do not know this. I had no problem with this, I used this about two years ago

#

@winged badger can i pass structure values like this?

USTRUCT()
struct EXTREMEARENA_API FRepExtremePawnMovement
{
    GENERATED_BODY()

        /** Velocity of component in world space */
    UPROPERTY()
        FVector_NetQuantize LinearVelocity;

    /** Velocity of rotation for component */
    UPROPERTY()
        FVector_NetQuantize AngularVelocity;

    /** Location in world space */
    UPROPERTY()
        FVector_NetQuantize Location;

    /** Current rotation */
    UPROPERTY()
        FRotator Rotation;

    bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
    {
        Rotation.SerializeCompressed(Ar);
        return true;
    }
};
UFUNCTION(Server, UnReliable)
void Server_PassMovementInfo(const FRepExtremePawnMovement & NewRepExtremePawnMovement);
void Server_PassMovementInfo_Implementation(const FRepExtremePawnMovement & NewRepExtremePawnMovement);
Server_PassMovementInfo(FRepExtremePawnMovement{RootCapsuleComponent->GetPhysicsLinearVelocity(), RootCapsuleComponent->GetPhysicsAngularVelocityInDegrees(), GetActorLocation(), GetActorRotation() });
#

Will it be automatically set in order like in a structure?

FRepExtremePawnMovement{RootCapsuleComponent->GetPhysicsLinearVelocity(), 
RootCapsuleComponent->GetPhysicsAngularVelocityInDegrees(), 
GetActorLocation(), 
GetActorRotation() }
steel vault
#

@gritty pelican your possession code works to possess a new player, it just doesn't fix my issue. Thanks for the effort though. Gonna try a couple more things that Zlo suggested to see if it helps.

shut gyro
#

@gritty pelican Crash when I try to server travel.

#
 Getting objects with a null outer is no longer supported. If you want to get all packages you might consider using GetObjectsOfClass instead.
#

have you ever gotten that before?

gritty pelican
#

@shut gyro Yes, it seems to test "ServerTravel" you need to package the project

shut gyro
#

ah I got it

#

I needed to specify a transition map

gritty pelican
#

Yeah that too

shut gyro
#

yea getting that "falling through floor" problem

#

I guess I would have to set the collisions up in the transition level hm

gritty pelican
#

make custom spawn

#

on game mode

shut gyro
#

I do

gritty pelican
#

or, in game mode call event handle start new player

#

and

#

in your controller

#

on begin play, make delay

#

and after you can spawn character and possess

slim mist
#

Hi all! I am using SpawnEmitterAtLocation, however I am aware that it doesn't replicate. How should I go about replicating emitters over the network? Cheers!

steel vault
#

Ok. Managed to get things working. I just ended up doing a source control reset of everything I changed besides my new custom classes. It appears that the error using GameState with GameModeBase completely biffed the blueprint or something so when I started fresh with that knowledge and set everything properly it all works @winged badger @gritty pelican

gritty pelican
#

@slim mist Hi, replication of sounds, emitters are not supported

#

you need to call SpawnEmitter or SpawnSound in MulticastEvent

slim mist
#

Thanks @gritty pelican, TIL about MulticastEvent! I'll find out how to use them

gritty pelican
#

example

slim mist
#

woohoo!

#

thank you so much!

#

you probably saved me hours

#

I imagine C++ equiv isnt too different

#

thank god for blueprints

gritty pelican
cinder lily
#

So I'm running a simple preliminary multiplayer test, just 2 players both controlled by me on a listen server (I think that's what it's called?)
Wanted to test to make sure that when Player 1 is looking at an object and has it highlighted, that Player 2 doesn't see the highlight. This works. However, Player 2 doesn't seem to be able to highlight objects at all. Anyone got a guess as to why?
It's just a basic continuous line trace that puts a post-process outline on any physics object the player character is looking at.

cosmic badge
#

anyone got any idea why when I attach an actor to another actor in multiplayer, it shows fine on the local client, and the listen host, but the attached actor is at the origin of the scene on other client

gritty pelican
#

@cinder lily Check that the function runs on the client

cinder lily
#

@gritty pelican The line trace works, its just the post-process that doesn't seem to be working

gritty pelican
#

You need to attach an object using the multicast event @cosmic badge

#

or you can make Variable OwnerActor (RepNotify) inside attached actor

#

and in OnRep_OwnerActor function you can make attach to actor

cosmic badge
#

yea, I am @gritty pelican I think the issue is something unrelated now after some more experimenting, but thanks for the reply ๐Ÿ™‚

gritty pelican
#

After that, to make the object attach, just set OwnerActor value on server side

cosmic badge
#

yea, thats exactly what I'm doing actually, using a replicated actor ref, setting it on the server, I think I figured the problem out, it was related to some physics junk I was doing to smooth things out over the wire

#

it was fighting with it, I wouldnt have thought it would have done THAT, but sure, haha.

#

all fixed now ๐Ÿ˜„

steel vault
#

@cinder lily that sounds to me like something that needs to be done locally on each client itself and not replicated at all. Line tracing just to highlight is a non multiplayer feature if you are doing it correctly.

obtuse moon
#

TMaps cannot be replicated right?

#

I should do struct?

gritty pelican
#

@obtuse moon yes

#

Perhaps this will work in struct

cinder lily
#

@steel vault I haven't even touched replication yet. I was doing everything single-player so far and just wanted to test multiplayer. Thought it was weird that the highlight works for player 1 but not player 2. I should mention this is blueprint, not code.

gritty pelican
#

example from google @obtuse moon

USTRUCT(BlueprintType)
 struct FStructWithMap
 {
     GENERATED_BODY()
     
     UPROPERTY(EditAnywhere, BlueprintReadWrite)
     TMap<FString, FString> StringMap;
  
     TArray<FString> StringKeys;
     TArray<FString> StringValues;
 
 
     bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
     {
         if (Ar.IsLoading())
         {
             // Move data to Map
             Ar << StringKeys;
             Ar << StringValues;
             for (auto It = StringKeys.CreateConstIterator(); It; ++It)
             {
                 StringMap.Add(StringKeys[It.GetIndex()], StringValues[It.GetIndex()]);
             }
         } else {
             // Move data to Arrays
             StringMap.GenerateKeyArray(StringKeys);
             StringMap.GenerateValueArray(StringValues);
             Ar << StringKeys;
             Ar << StringValues;
         }
         StringKeys.Empty();
         StringValues.Empty();
 
         bOutSuccess = true;
         return true;
     }
 };
  
 template<>
 struct TStructOpsTypeTraits<FStructWithMap> : public TStructOpsTypeTraitsBase2<FStructWithMap>
 {
     enum
     {
         WithNetSerializer = true
     };
 };
obtuse moon
#

I read about that one, but also read that it's for simple types?

#

Or it's just been tested with simple types

#

I swear I lose so much time on some shit sometimes... I'll have to refactor things because of not knowing tmaps didn't replicate

gritty pelican
#

I think you can replicate anything in a struct if you can serialize it

steel vault
obtuse moon
cinder lily
gritty pelican
#

@cinder lily if RunDedicatedServer disabled - it's listen server

#

and first player will be server and second will be client

cinder lily
#

The basic jist of the blueprint is that the player character is constantly doing a line trace and if that trace hits a physics object, a post-process effect is turned on to highlight it. Stop looking at it, the highlight goes away. pretty simple. But when I try the multiplayer, the 2nd player (client) only does the linetrace part - it's not doing the highlight

open jacinth
#

@cinder lily when do you use the outline start function? can you capture it so i can find the problem

cinder lily
#

@open jacinth Outline Start is called as part of the continuous LineTrace, if certain criteria is met

open jacinth
#

try printing string in fail cast to gamemode, and see what it comes first

cinder lily
#

I set it to show the linetrace, the little red laser, so I know the linetrace is working fine

open jacinth
#

no, i mean in the outline start function, in fail node, try create a print string

cinder lily
#

Fail node?

#

Oh I see it

open jacinth
#

cast to treasurehuntbase

cinder lily
#

Yes, its printing on failed to cast

open jacinth
#

does it fail on client or server, i guess it on client

cinder lily
#

Client

open jacinth
#

then the client can't use that function, you need to work around that

#

my opinion is creating a rpc to server rather than a function, when criteria is met you call rpc to server, then server call a rpc to owning client, but i still dont know what your gamemode does, maybe you dont need to work with it

#

since client can't cast to game mode that why you dont have outline in client view

steel vault
#

@cinder lily pretty sure your code isn't working because you are calling things from the game mode and only the server owns the game mode so that is why you aren't seeing it on the client. I don't know much about the post process stenciling but you should find a way to do it elsewhere.

cinder lily
#

I stored the variables in the GameMode because it seemed like the easiest and best place to store variables that would need to be called by multiple different things. Is there a better alternative, one that could be accessed by the client?

kindred widget
#

@cinder lily GameMode only exists on the server and can never be reached from any client machine without going through the server version of their owned actors. PlayerController only exists on the owning client and the server, thus client 3 cannot get ahold of client 2's PlayerController. Every other actor like GameState or PlayerStates, or possibly PlayerCharacters, exist one copy per machine.

#

If you need a game wide variable for all clients, consider GameState. If it directly relates to a specific player, consider their PlayerState or PlayerCharacter. If you need something private for a client that other clients should never see, consider PlayerController. If it's something that needs secured on the server only away from any client's reach, GameMode.

fiery badge
cinder lily
#

Sounds like I've got some reconfiguring to do. Thanks for all the help everyone!

silent valley
#

Don't suppose anyone knows much about terraform do they? ๐Ÿ˜„

fiery badge
#

Hey!

mighty zinc
#

Hi

fiery badge
#

can anyone explain what is session in multiplayer

#

i mean i am trying to find about it and no one is talking about what is it exactly.

#

there is no mansion about it in basic multiplayer, so what exactly it is?

hidden thorn
#

From Characacter A can I check if a variable on Character B's player state is true or false (the variable is marked as replicated) ?

thin stratus
#

If you have a reference to Character B, yeah

hidden thorn
#

So I am calling a ServerRPC from A passing B as parameter and Grabbing the PlayerState and changing a variable (Marked as Replicated), when I am doing it that from Client or Server it seems to only work on the Local Player and not on the server for some reason.

#

Hmmm another weird thing is I added a AddOnScreenDebugMessage on the server and it seems that the LocalPlayer has it displayed on screen not the server

twilit oak
#

is it ok to do ~1000 reliable rpc,s in a frame as a one-time thing? with each rpc having FName + 32 bits of data or should i do one really big rpc instead? idk wich is better

silent valley
#

@fiery badge a Session is a collection of meta-data stored about a multiplayer game. The Online subsystem can use this Session data to search for games using Matchmaking system.
So if you don't use Session then when you create a game no-one knows about it and you have to give them the IP to join.
If you register a Session when you start your server then people can discover it without knowing the IP address.

It does some other things too but that's the key part.

hidden thorn
#

I am really struggling with making a Server RPC call from the Character, what I am doing is call Server Method -> Set Replicated Variable From Player State -> OnRep_Notify should be getting called but it isn't. It works when I am making the call from the Server but not from the clients.

kindred widget
#

Show your RPC from the client?

hidden thorn
#

1 sec just putting it together now ๐Ÿ˜„, OwningPlayer is set in BeginPlay from GetOwner()

#
void UCharacterIntruder::KillPlayer()
{
    if (IsValid(OwningPlayer) && IsValid(OwningPlayer->GetPlayerInRange()))
    {
        if (GetOwner()->HasAuthority())
        {
            float _distance = FVector::Distance(OwningPlayer->GetActorLocation(), OwningPlayer->GetPlayerInRange()->GetActorLocation());

            if (_distance <= 200)
            {
                ACharacterState* playerState = Cast<ACharacterState>(OwningPlayer->GetPlayerInRange()->GetPlayerState());
                playerState->PlayerDied();
            }
        }
        else
        {
            float _distance = FVector::Distance(OwningPlayer->GetActorLocation(), OwningPlayer->GetPlayerInRange()->GetActorLocation());
            if (_distance <= 200)
            {
                Server_KillPlayer_Implementation(OwningPlayer->GetPlayerInRange());
            }
        }
    }
}

void UCharacterIntruder::Server_KillPlayer_Implementation(AIntruderCharacter* player)
{
    float _distance = FVector::Distance(OwningPlayer->GetActorLocation(), player->GetActorLocation());

    if (_distance <= 200)
    {
        ACharacterState* playerState = Cast<ACharacterState>(player->GetPlayerState());
        playerState->PlayerDied();
    }
}
bool UCharacterIntruder::Server_KillPlayer_Validate(AIntruderCharacter* player)
{ return true; }
#
void ACharacterState::OnRep_IsPlayerAlive()
{
    AIntruderCharacter* owningCharacter = Cast<AIntruderCharacter>(GetPawn());

    if (IsValid(owningCharacter))
    {
        owningCharacter->GetCharacterMovement()->DisableMovement();
    }
}

void ACharacterState::PlayerDied()
{
    // When calling from the client it comes into this method but doesn't get past the if statment
    // When calling from the server it passes the if statement
    if (HasAuthority())
    {
        bIsAlive = false;    
        
        AIntruderCharacter* owningCharacter = Cast<AIntruderCharacter>(GetPawn());
        if (IsValid(owningCharacter))
        {
            owningCharacter->GetCharacterMovement()->DisableMovement();
        }
    }
}
// UCharacterIntruder .h
UFUNCTION(BlueprintCallable, Category = Player)
void KillPlayer();

UFUNCTION(Server, Reliable, WithValidation)
void Server_KillPlayer(AIntruderCharacter* player);


// ACharacterState .h
UPROPERTY(EditAnywhere, ReplicatedUsing = OnRep_IsPlayerAlive, BlueprintReadWrite, Replicated)
bool bIsAlive;

UFUNCTION()
void OnRep_IsPlayerAlive();
#

What I don't get is why when called from the Client inside PlayerDied I had 2 prints 1 outside the if and 1 inside the if the one outside was getting displayed but not the one inside. Am I not calling PlayerDied from the server properly? From what I get I shouldn't have got either of those prints since its called on the server.

kindred widget
#

I'm fairly certain you only have to call the Server_KillPlayer(OwningPlayer->GetPlayerInRange()); without the Implementation part that you declared in the .h file. At least I never had to. That is just for the generated file to handle stuff as far as I was aware. I'd also place a print right before that and check that the print is working from the client when it's supposed to in the same way it works on the server.

hidden thorn
#

So I had prints before every call (I removed them for simplicity) and everything is getting called from both the Client & Server except what I mentioned above inside PlayerDied

#

I'm fairly certain you only have to call the Server_KillPlayer(OwningPlayer->GetPlayerInRange()); without the Implementation
How is this even possible ๐Ÿ˜ฆ ๐Ÿ˜ 

#

That worked

#

I literally spent yesterday afternoon and today (last 4 hrs) trying to figure this shit out.

kindred widget
#

Haha. Yeah. I had that issue myself with BlueprintNativeEvents and the _implementation a while ago. Assumed it was the same for RPC calling.

#

It's probably something that Unreal does before or after calling the Implementation event when you call the normal one. It'll run the normal one, but the generated file adds code around your functions. So it's likely that reason that it wasn't networking, but was working on the server.

hidden thorn
#

Oh well, regardless its working now thanks a HUMONGOUS BUNCH

tawny mason
#

hey, is there a way to show a splash screen or some widget while joining a listen server ?

#

or does everything just get destroyed no mater what ?

#

I was wondering if there was a way to show some sort of loading screen or something while joining a friend on a server or something, like seamless travel.

winged badger
#

your viewport doesn't get destroyed

#

nor do your game instance or local player ^^

hidden thorn
gritty pelican
#

Who can help with serialization? For some reason, I get zero values on the server and on other clients
MyStruct

USTRUCT()
struct EXTREMEARENA_API FRepExtremePawnMovement
{
    GENERATED_BODY()

    UPROPERTY()
        FVector_NetQuantize LinearVelocity;

    UPROPERTY()
        FVector_NetQuantize AngularVelocity;

    UPROPERTY()
        FVector_NetQuantize Location;

    UPROPERTY()
        FRotator Rotation;

    FRepExtremePawnMovement()
    {
        LinearVelocity = FVector::ZeroVector;
        AngularVelocity = FVector::ZeroVector;
        Location = FVector::ZeroVector;
        Rotation = FRotator::ZeroRotator;
    }

    bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
    {
        Rotation.SerializeCompressed(Ar);
        bOutSuccess = true;
        return true;
    }
    
public:
    FRepExtremePawnMovement(
        const FVector& InLinearVelocity,
        const FVector& InAngularVelocity,
        const FVector& InLocation,
        const FRotator& InRotation)
        : LinearVelocity(InLinearVelocity)
        , AngularVelocity(InAngularVelocity)
        , Location(InLocation)
        , Rotation(InRotation)
    {}
};

template<>
struct TStructOpsTypeTraits<FRepExtremePawnMovement> : public TStructOpsTypeTraitsBase2<FRepExtremePawnMovement>
{
    enum
    {
        WithNetSerializer = true,
    };
};
chrome bay
#

Well only the rotation will ever be serialized

#

Since that's the only property you're writing to the archive

gritty pelican
#

@chrome bay Wouldn't the FVector_NetQuantize be serialized automatically?

chrome bay
#

no

#

When you create a NetSerialize function only what you put in there will be sent

#

You lose UPROPERTY() serialization and per-property comparisons too - so the full archive is sent everytime

gritty pelican
#

Could you tell me how to do it right?

chrome bay
#

Well it depends. Do you want to send the full struct every time?

gritty pelican
#

I just started learning about serialization yesterday, so this is a new topic for me

#

yes

chrome bay
#

If so, you just need to add

AngularVelocity.NetSerialize(...);``` etc
#

... replacing the params ofc

winged badger
#

also, as long as you don't need roll, Rotator goes through the network just fine as FVector_NetQuantizeNormal

gritty pelican
#

right?

bool NetSerialize(FArchive& Ar, class UPackageMap* Map, bool& bOutSuccess)
    {
        LinearVelocity.NetSerialize(Ar, Map, bOutSuccess);
        AngularVelocity.NetSerialize(Ar, Map, bOutSuccess);
        Location.NetSerialize(Ar, Map, bOutSuccess);
        Rotation.SerializeCompressed(Ar);
        bOutSuccess = true;
        return true;
    }
chrome bay
#

yeah pretty much

gritty pelican
#

in my pawn i add this

protected:

UPROPERTY(Replicated)
FRepExtremePawnMovement RepExtremePawnMovement;
    
UFUNCTION(Server, UnReliable)
void Server_PassMovementInfo(const FRepExtremePawnMovement & NewRepExtremePawnMovement);
void Server_PassMovementInfo_Implementation(const FRepExtremePawnMovement & NewRepExtremePawnMovement);
#

in cpp

DOREPLIFETIME_CONDITION(ACPP_ExtremePawn, RepExtremePawnMovement, COND_SkipOwner);
chrome bay
#

Seems okay, just bear in mind the full struct will be replicated everytime too

gritty pelican
#

and on tick from client to server

const FRepExtremePawnMovement RepExtremePawnMovementLocal(RootCapsuleComponent->GetPhysicsLinearVelocity(),
            RootCapsuleComponent->GetPhysicsAngularVelocityInDegrees(),
            GetActorLocation(),
            GetActorRotation()
        );
Server_PassMovementInfo(RepExtremePawnMovementLocal);
chrome bay
#

but tbh, since the default engine replicated movement works that way too, probably not worth worrying about

#

Lots of room for optimisation there

gritty pelican
#

I heard that replicating with one structure is better than each value separately

chrome bay
#

well it's not "better" as such

gritty pelican
#

i don't know if it's true

chrome bay
#

But more convenient a lot of the time

#

You probably do want to avoid sending an RPC on tick though, even an unreliable one

#

even CMC does what it can to avoid that

gritty pelican
#

Yes, I use linear interpolation to smooth movement on the server and other clients.

#

my sync movement function

void ACPP_ExtremePawn::SyncMovement(float DeltaTime)
{
    FVector NewPawnLocation = FMath::VInterpTo(GetActorLocation(), RepExtremePawnMovement.Location, DeltaTime, ClientSyncSpeed);
    FRotator NewPawnRotation = FMath::RInterpTo(GetActorRotation(), RepExtremePawnMovement.Rotation, DeltaTime, ClientSyncSpeed);
    FQuat NewPawnQuat = NewPawnRotation.Quaternion();
    SetActorLocationAndRotation(NewPawnLocation, NewPawnRotation, true);

    FVector NewPawnLinearVelocity = FMath::VInterpTo(RootCapsuleComponent->GetPhysicsLinearVelocity(), RepExtremePawnMovement.LinearVelocity, DeltaTime, ClientSyncSpeed);
    FVector NewPawnAngularVelocity = FMath::VInterpTo(RootCapsuleComponent->GetPhysicsAngularVelocityInDegrees(), RepExtremePawnMovement.AngularVelocity, DeltaTime, ClientSyncSpeed);
    RootCapsuleComponent->SetPhysicsLinearVelocity(NewPawnLinearVelocity);
    RootCapsuleComponent->SetPhysicsAngularVelocityInDegrees(NewPawnAngularVelocity);
}
chrome bay
#

FYI RInterpTo / VInterpTo are not linear

gritty pelican
#

Oh yes

#

i saw the plugin SmoothSync and there used Lerp

chrome bay
#

And interpolation is all well and good, but it means you can't accurate get an position of an object

#

meh smoothsync

#

If you must smooth, it's probably better to smooth visuals only

#

I also suspect that too much smoothing, especially linear/angular velocity, will result in overall worse sync

gritty pelican
#

This works well at ping 150

#

how i can avoid sending an RPC?

chrome bay
#

Just send less, don't send on tick

#

Otherwise you'll be hammering the server with packets at whatever framerate the client can run at

#

this kills the server

#

Sending at a fixed rate, and lowering that rate when the value hasn't changed is a good start m

gritty pelican
#

Oh, exactly. Thanks, I didn't even think about it

#

fps 200 = 200 RPC

#

for example i can make this on tick

MyPrivateFloat -= DeltaTime;
if(MyPrivateFloat <= 0.f)
{
MyPrivateFloat = 0.1f;
Server_PassMovementInfo...
}
chrome bay
#

I tend to use world time seconds / real time seconds

#

and cache a "last sent time" or something

ember needle
#

hello, what is the current state-of-the-art way to wait for a PS to be ready from a PC so that I can initialize a VOIPTalker appropriately? I'd rather not do a delay loop with an isvalid switch...

chrome bay
#

override OnRep_PlayerState?

ember needle
#

Right. unfortunately BP only. Maybe I'll set up a custom event for this one.

#

(I mean expose to BP an event called from OnRep_PlayerState)

#

thanks

chrome bay
#

Oh well if it's BP, RIP

#

BeginPlay in a playerstate maybe

#

And grab it's owner

hidden thorn
#

If I update a Replicated variable and I then want to broadcast a delegate what is the best way to do it? I tried to broadcast it from OnRep.... but wasn't successful.

rocky totem
#

@winged badger just wanted to say thanks, i've been using rider for all of 24hr and i'm hooked

chrome bay
#

@hidden thorn OnRep would be the way, assuming you want the delegate to be broadcast on the client

#

If on the Server, then broadcast as soon as you change it

hidden thorn
#

It would be both the clients & server (ListenServer), I will try it again

#

Does the delegate need to be marked as Replicated?

chrome bay
#

You can't replicate delegates, they're local only

#

If you want both, then you'll have to broadcast from both places

#

When changed, and in the OnRep

hidden thorn
#

Oh ok, I am able to get it on the Server but not Client, I will try it again though

#
//.h file
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnIsPlayerAlive, bool, bIsAlive);

UCLASS()
...

UPROPERTY(BlueprintAssignable)
FOnIsPlayerAlive OnIsPlayerAlive;

//.cpp file
// bIsAlive ReplciatedUsing = OnRep_IsPlayerAlive
void ACharacterState::OnRep_IsPlayerAlive()
{
    GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, FString::Printf(TEXT("OnRep_IsPlayerAlive")));

    if (AIntruderCharacter* owningCharacter = Cast<AIntruderCharacter>(GetPawn()))
    {
        GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, FString::Printf(TEXT("Broadcast")));
        OnIsPlayerAlive.Broadcast(false);
    }
}

void ACharacterState::PlayerDied()
{
    GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, FString::Printf(TEXT("PlayerDied")));

    if (HasAuthority())
    {
        GEngine->AddOnScreenDebugMessage(-1, 2.0f, FColor::Green, FString::Printf(TEXT("PlayerDied - HasAuthority")));

        bIsAlive = false;
        OnIsPlayerAlive.Broadcast(false);
    }
}
#

So it works on the server but not on the client

#

Broadcast does get printed on the client when called.

chrome bay
#

Do you ever set bAlive to true?

hidden thorn
#

In the constructor

chrome bay
#

And the actor itself is replicated I assume

#

Hang on, you said 'Broadcast' does get called and printed - so it is being broadcast

hidden thorn
#

I didn't set it personally but since everything works, I have 2 methods in there (which I removed for simplicity) which makes the character turn into a ragdoll which works on clients

hidden thorn
chrome bay
#

Yeah, so it must be working then

#

I would guess you don't have anything bound to OnIsPlayerAlive on the client

hidden thorn
#

I did bind it to a Widget and when its true or false it should display something on the screen which it doesn't do on the client.

chrome bay
#

If it's printing 'Broadcast' and the code above is accurate then the delegate is being called

#

So the only option really is that the binding isn't setup

#

I would put some breakpoints in and step through

hidden thorn
#

But if it works on the Server doesn't that mean that its setup properly?

chrome bay
#

not at all

#

For example, the widget might be valid before ACharacterState is

#

So it can't bind to it

#

etc.

hidden thorn
#

I see

#

Good shout

hidden thorn
chrome bay
#

oof ๐Ÿ˜„

#

You have to get used to making the codebase resilient to things not being available straight away

#

Delays are the fast, but extremely unreliable approach

hidden thorn
hidden thorn
chrome bay
#

Yeah it takes some time to figure out. There's some helpful design patterns you get used to doing after a while

#

adds to blogging backlog

hidden thorn
#

To be fair every time I come back and start using UE4 I learn something new that I haven't used before ๐Ÿ˜„

steel vault
#

There really arenโ€™t enough good materials out there on this subject especially for UE4. You guys are the real heroes helping out with such things. Would love to see more content on design patterns and the like.

hidden thorn
#

Are there any design patterns specific for UE4 ??

chrome bay
#

I'm not sure if there's anything specific

#

Or rather what the official names are if there are any

hidden thorn
#

Fair

kindred widget
#

I think a lot of it just comes down to the individual or company. It's just one of those topics where there's a lot of right ways, and a whole lot more wrong ways.

ember needle
#

I'm not making this OnRep_PlayerState event work, it doesn't get called in BP.

I have:

    // Event PlayerState replicated
    virtual void OnRep_PlayerState() override;

    UFUNCTION(BlueprintNativeEvent, Category = Character)
    void OnPlayerStateReplicated();
void AFECCharacter::OnRep_PlayerState()
{
    Super::OnRep_PlayerState();
    OnPlayerStateReplicated();
}

void AFECCharacter::OnPlayerStateReplicated_Implementation()
{
}
#

this doesn't get called

#

any hints welcome, I'm a real, real noob in C++.

kindred widget
#

Have you logged or printed in the OnRep to make sure it's running before the BlueprintEvent is called?

ember needle
#

apologies.

#

this is actually working. it's not being called on the server obviously since it's in C++

#

where can I call it though, to ensure that the server also has this event?

#

i'm basically trying to get on a character when the playerstate has been replicated, in BP.

kindred widget
#

You're trying to get to the server version of the character that belongs to the same player as the playerstate?

ember needle
#

Longer story: I'm using a VOIPTalker on a character, but that needs the player state to be ready so that it can be registered.

#

so instead of checking in a loop that the player state is valid, i'm trying to make proper things by having an event when the PS has been replicated

#

so I'd rather have the second part here called on an event

#

do i have to also override void APawn::SetPlayerState(APlayerState* NewPlayerState) and call OnPlayerStateReplicated() from there?

#

(which is not overrideable unfortunately)

hidden thorn
kindred widget
#

I have never personally worked in a professional environment myself. I'm only here because I have a serious love of game design. I find I love putting stuff together. I tried Unity a while ago, and many years ago dabbled with UDK but nothing more than plopping static meshes into a level. Before November of last year, my whole software skillset was HTML, and LUA UI design. Not sure how I'd handle a professional environment though. Interesting thought. I love problem solving and working around things, but I also love working on my own ideas too.

#

@ember needle I'm not finding much to help with that really. My only thought is that you could use something like the controller as a "register". Make each class call a function in the controller to let it know they're ready for use. It'd allow you to refresh the character if you kill it off and destroy it too. Controller would probably have to query when it first spawns if either of those two are active and set them up too just one time.

#

Either that, or you could mimic what the Pawn and Playerstate are already doing with your own functions to get the replication the way you need it.

ember needle
#

you don't know if your controller is there before your player state.

#

AFAIK

potent cradle
#

Hey guys, I'm looking for a bit of wisdom on how large a multiplayer map could responsibly be on a single server. I was hoping for at least 32km2, is that a pipedream? If need be, I'll scale down, just trying to understand the constraints and where the painpoints are.

In terms of actor count and characters I have a fairly decent understanding of what's expensive, but I'm surprised there's theoretical limits with world composition. That is to say, I'm a bit oblivious as to why a large world would be an issue for a server to handle (not withstanding any extra actors).

kindred widget
#

Nah, that's why you'd have to have the controller check as well when it first starts. Controller checks once when it's created. If nothing exists, they'll register when they are. If they are created, it can set them and when they query to the controller it would be invalid and not set anything, but the controller would handle it when it's spawned. After that it'd probably just be the character registering itself on spawn.

ember needle
#

i'm probably not following, but both your character and your controller don't know when the player state is replicated and ready

#

and the player state, doesn't know when the character is ready as well.

#

moreover the PC could be created before / after the PS

#

so there are 3 assets here and the only sure thing is that the controller gets created before the character (AFAIK)

kindred widget
#

If you only set this stuff on the server. Ignore clients. They don't need to do anything but get replicated to for the setting part. Controller can have two RepNotify variables. One for Pawn, one for PlayerState. One scenario is that controller spawns first. It calls a quick function to see if PlayerState or Pawn exsits yet. Nothing. No variables set. PlayerState spawns in. Calls it's own function on the controller and registers itself. Character spawns, registers itself. All things are now set and both variables should not be replicated to clients for them to do with what they like. Alternative start. Playerstate spawns in. Calls it's function on Contoller, controller doesn't exist so it doesn't call it. PlayerController spawns, checks playerstate, it exists, it sets Playerstate on itself since it's valid. Character spawns in and registers itself in the controller. Mind you this is all happening on server.

#

Now that the setting is taken care of, all that's left is client's work. Both variables having a repnotify means it can call the same function on the client when they change. If one thing is invalid, ignore. If both are valid, set up your stuff on the client.

ember needle
#

"PlayerState spawns in. Calls it's own function on the controller and registers itself" player controller might not exist yet.

#

you may actually have a race condition

#

where both PC and PS get created almost at the same time but they are not set yet so PC->PS is none and PS->PC is none

#

but they both exist

#

AFAIK obviously...

kindred widget
#

It's possible, but with Pawn also calling this function to update stuff, it's highly unlikely to fail.

ember needle
#

beginplay is called on PC and on PS but their PS on one and PC on the other are not yet set.

#

I've actually seen it quite often unfortunately

#

in logs

#

ah sorry not with the pawn part.

#

what do you think about this:

#
void APawn::PossessedBy(AController* NewController)
{
    SetOwner(NewController);
    
    AController* const OldController = Controller;

    Controller = NewController;
    ForceNetUpdate();

    if (Controller->PlayerState != nullptr)
    {
        SetPlayerState(Controller->PlayerState);
    }

    if (APlayerController* PlayerController = Cast<APlayerController>(Controller))
    {
        if (GetNetMode() != NM_Standalone)
        {
            SetReplicates(true);
            SetAutonomousProxy(true);
        }
    }
    else
    {
        CopyRemoteRoleFrom(GetDefault<APawn>());
    }

    // dispatch Blueprint event if necessary
    if (OldController != NewController)
    {
        ReceivePossessed(Controller);
    }
}
quick flint
#

Is there anyone who is good at the character movement component who would be interested in doing some consulting work for a few hours at a good rate until I am able to get my custom CMC working? It needs to work in multiplayer, I posted on #freelance-jobs. DM me if interested

kindred widget
#

Could very well work. I didn't think about possession events.

ember needle
#

thank you @kindred widget for listening and helping

#

FYI this works

#

the possessed event gets called only on server

kindred widget
#

Careful with that then. It'd only work for the Listenserver character.

ember needle
#

indeed, which is what I want because I'm registering a VOIPTalker

#

you are correct

steel vault
#

IIRC I put logic in the reset function override because itโ€™s basically ensures that everything else had been set up first. Would have to go back and research again to explain why though.

kindred widget
#

Wouldn't you want it to work for the owning client of the character though too?

ember needle
#

@steel vault are you talking about the PS situation here?

steel vault
#

I was referring to making sure the possession had occurred at least. I donโ€™t know if playerstate is or is not setup by then but I would assume it is because reset I believe is for the owning clientโ€™s player

ember needle