#multiplayer
1 messages · Page 47 of 1
i dont know how to do this praticcaly
Limit BeginPlay with SwitchHasAuthority to only us the Authority pin
So if a variable is replicated from a player state, it's stored on the player state entry in the player array. If it's replicated from a pawn, where is that stored on the server? Rather, where can it be retrieved from?
From... the Pawn?
i have no idea what does that mean
how to do that
What if I need to change that variable? I send a message to the server with what identififer for the pawn?
*identifier
Yeah something like that
let me check
so i have to clear the work i made with timeline and make it with actor location?
I know it was mentioned earlier about a uniqueNetID, but I'm unsure how I would get that for an actor
You are using SetRelativeLocation
Just swap that
I think you are overcomplicating something here
No one touches NetIds for this
You get the reference to that Actor in question and change the Variable
And if it has to be on the server, then the server needs to get that
I was under the impression that actor references were unique to the client, though - is that not hte case?
And if the local client wants to tell the server to change a variable in its owned actor, it uses a server rpc
Idk what that means
Yeah there is no need to connect that as you are inside the actor already
"self" is the actor
You seem to have a knowledge gap surrounding what references are and how they work
Getting a Pointer (In BPs reference) to a Actor or any Object is either done by making sure you save it to a variable somewhere already accessible
Or if it's an actor that has collision and it makes sense context wise, you get it via an overlap, a linetrace, a hit etc.
E.g. a Projectile hitting a Character
If you have that Reference, then you can change your variable
Multiplayer just adds more to that concept
If you have a reference to an Actor on the Server side and that actor is replicated, you can use Multicasts to trigger events on the replicated instances of that actor, or replicate some variable
If you have a reference on the Client and the Actor is owned by that client, you can perform a Server RPC
As well as target that client from server side with a Server RPC
If you have an Actor that is not owned by that client, you have to go through another actor that is owned by the client, otherwise you have no way to tell the server to change the variabl
E.g. a Door in the middle of the map can't just execute Server RPCs
Youre right I think I did have a gap
Another example is, if you have a variable on your Character, and you want that to replicate to everyone, and you want to increment that every time you press the E key, you use a Server RPC (cause a Possessed Character is owned by that client) to tell the Server to change the variable
In a lot of cases, you only ever use a Server RPC to tell the Server about an input that happened
Maybe it be a controller, keyboard, or a UI button press
After the ServerRPC, you are on the server and you can then do whatever you want there
e.g. Left mouse Button to fire a Gun, ServerRPC_FireWeapon
Then on the Server you do your LineTrace or spawned a Projectile
And when it hits you damage whatever you hit
it works fine thanks but now the problem is when i put the cube where i want in the editor it goes different location like this
Since you are still on the server after the RPC (if no other RPCs get involved) you can modify a health variable that is replicated in some other character that your line trace hits
But that's specific the context of firing a line trace weapon
There is no one solution for getting references etc. It's a matter of understanding the basics, the tools, and using them
it's because your Timeline uses a location that is relative
Before start your timeline, get the ActorLocation and save it to a variable
And then instead of directly using the timeline value on the SetActorLocation node, add it to the variable you created
So you have "SpawnLocation + Relative Offset" basically
Otherwise you will always have that cube at Relative Offset to World 0,0,0
I must honestly ask why you are trying to do a Multiplayer project if you can't set a variable
Like, no offense, but this seems really the wrong approach
I think that's a pretty good example - my only difference is I'm trying to do an RPG style attack where I press a button and my character damages a selected character, so I guess I just need to know what character I have selected and pass that information instead.
Yeah, just keep in mind if you send the select Actor via the ServerRPC along, it allows for cheating
Cause then a player can select someone out of range
So you need to double check
It's better to send a blank RPC to perform the Attack
And then do the targeting on the server
Whatever that might be for your attack
Yeah but then please learn the basics a bit more
Getting and Setting Variables is really the most fundamental thing in the Editor
I gladly help you with network issues, but this is a bit much
There are tons of tutorials you can watch and read for basics
Hmm, I think I see what you mean. I was storing the targetted character as an RPC on the player state, but I can see how that could be exploited.
PlayerState is also not really the place for this
So I'm learning
Your Character is the one attacking after all
I had a lot on the player state, but started moving a lot of it off.
How would you check for targetting in a game where you explicitly have to 'select' someone, though?
Sorry, more of a rhetorical question
When using UE you have to first understand that it's heavily inspired by the original Unreal Tournament Game, where a PlayerState held Name, Kills, Deaths, etc.
Data that has to be known to every connect player to show it in some scoreboard.
Doens't mean that's the only way to use it, but sometimes some classes just aren't useful to something that isn't Unreal Tournament.
Again, PlayerState is super useful, but it's meant for data that other players have to see regardless of where the player is and if they have a character or not
The idea would be to send the minimal amount of information to the Server that it needs to select
Yeah I had some data on there that other players are meant to 'see'
If it's targeting via mouse selection, then you could send the select actor and the mouse location via the RPC
Server could then double check if there is really EnemyABC under the mouse
Which I guess I could do similar to a line trace?
Where would you go for the basics? I've been doing a bit of BRY watching on Youtube but his series stopped at 4(multicasting) and hasn't released the part 5 for his series.
I'm a bit out of touch with tutorials now a days as I rarely use them. Not sure I can recommend anything
Hey.
Question: I am dealing with a very simple multiplayer mechanic: shooting. I raytrace from the middle of the client's screen and send it to the server so the server can do its thing. Working great.
However - is there any way to get "the middle of the screen of the user" without relying on the client transmitting that info through the network?
Why are my players not replicating their transform?
The server/client will replicate to all connected clients, but the clients won't replicate on the server/client.
Replication is enabled, so I don't know what's going on. I'm not getting any errors.
I’ve been following this chat for the past week. I’m going to need sprint+GAS myself soon, and I’m very confused how/why this is not a simple “solved” thing. I never would have thought of half the issues faced - Seems so fundamental?
Would love if you get time to blog a summary of the final outcome on how best to handle this? I’m sure others would benefit too.
No, because how else would the server know? You could maybe use the server to guess based upon the player location and rotation, but for a shooter it will be slightly laggy by a few frames.
Why would you not want the client to tell the server the exact spot it was looking at with the fire command?
I'm using a CPP pawn and the moment code is cpp. I implemented FloatingPawnMovement component from the blueprint not in C++
If you want a good c++ MP course, Stephen Ulibiri has a good one on Udemy. It’s great quality (unlike many others). Has regular discounts coupons on his Discord, totally worth it.
Tom Looman has another one, more expensive, but also high quality.
So I THINK I have this figured out, but mind double-checking m work? So I have an input action that does a Get Hit Result Under Cursor for Objects. I then call an RPC and pass in the local controller as well as the hit result. On the RPC, I do the same thing passing in the controller and then compare that actor result to the passed actor result. If it's true then I have the target.
Target is then stored on the server until it's cleared (so any attacks/etc are just directed to it)
Oh wait, it's failing... nevermind
@thin stratus 😎
I'm still lost lol, wish I could write a blog on it. I have sprinting with stamina on the CMC which drains while sprinting and has a 50 cost with jumping and dashing. And then it regenerates too. I've just connected it to a UI, but it only syncs to the server with a net correction (which hardly happens).
So I'm currently stuck on not being able to predict Stamina, so the server and client are way off. I have the stamina drain with veloicty * a modifier but regenerating it needs to be done while standing still so have been using deltatime. But it seems the server and client regenerate at different amounts because of that so their gap is so far.
If anyone has any idea how to predict stamina on the Character Movement Component, or has any ideas I might be missing, I need the help 😄
Unless most people don't use CSP for things like this and just get the server to send down the final result and only update UI once it's recieved? I may be looking too much into it lol
Server and client being way off seems odd - is this under pretty bad network conditions?
With minimal network simulation, and even if doing pretty simple prediction they shouldn't be far off if it's something like Stamina += DeltaTime * SomeConstant
Or is the issue that the server is constantly correcting the client? What you want to do there is make it so the client value isn't directly updated by the server but instead only corrects itself over time or if the correction is too large.
Hey all, is there any reason for custom events in a GameMode blueprint to be marked RunOnServer? It seems redundant because the GameMode only exists on the server? I followed a tutorial series that does this many times and (while everything works) I want to check my understanding and simplify my BPs if possible
There is none, it's a bad practice
GameMode is inherently server-only and it doesn't replicate in the first place, so yeah
RPCs are out of the picture
OK great, thanks for helping with my sanity check lol
Also fwiw Epic multiplayer tutorials does worse than that, as they in fact do stuff that basically don't work™️
this was an official epic tutorial series ha
Is it possible to call a get objects under cursor on the server? Or do I need to do a line trace?
Great, you proved my point
It's possible wherever there is a local-controller
SO pass in the local controller as a reference and put it on that?
No, because the other end won't get that local-controller but instead its Net GUID
And in return it's corresponding version, which is 100% non local
You do it locally
You pass the results
How does the server ensure you're not cheating, then?
That's another question, and the answer lies in doing the line-trace on the server. The server could keep track of the location of the cursor for example
So in fact you would end up doing the line-trace on both client and then on server
No need to RPC if the client is not cheating
You only RPC if things are "getting out of order"
So why not just use a line trace in both instances?
Because that would be gross for bandwith
Gotcha
The less you RPC or replicate data while getting your desired results the better
So get results, pass that to the server in an RPC - Server needs to do... I guess a line trace from camera to cursor's V2 position?
I have no idea how that function works
You will have to look what they do
But yeah camera must be involved and actually you could take advantage of that
So for example the server checks if the thing that was hit could exist by the FOV on server
So no cursor-location tracking whatsoever
sigh ok so my multiplayer lobby has everything being replicated through player controllers and it seems like PlayerState would make a lot more sense?
This definately seems like one of the more complicated RPCs to execute.
Yeah and it smells bad
Well it depends on what properties you're storing. There is no such a class that should store all replicated properties
Yeah fair point. PlayerState is not utilized at all however and I think it would make sense for a lot of properties. Need to do more research
Honestly all you need is to understand what every GameFramework actor is responsible for
Multiplayer Compendium for example does a good job at explaining it
cool, thanks for the link
even on 0 ping, the client was stamina 100 and server 50 catching up. Not sure why, it was like the server ticked so much slower than the client, even just in PIE, play as client. Any ideas what could cause that?
That seems like a bug with how stamina is being calculated - single process PIE with no network simulation should not be that far off.
And it wouldn't be down to deltatime differences - I don't think there'd be any in single process PIE anyway.
Sounds more like the server is doing something the client isn't, or vice versa.
Random possibilities to look out for:
Something else affecting stamina on the server (or client) that shouldn't be or isn't predicted.
Server stopping some operation too soon/too late compared to the client.
Some other multiplier on the server being different than the client.
I was doing in UpdateCharacterStateBeforeMovement for stamina regen
if (!IsSprinting() && Stamina < 100.f)
{
const float StaminaRegen = 10.f * DeltaSeconds;
Stamina += StaminaRegen
}```
Not a huge amount to go on here unfortunately. Just spitballing at the moment.
That at least does look fine
Something I'd do to debug is have the client store its stamina somewhere else every frame, and when it receives an OnRep for new stamina from the server print the difference. Just to confirm what kind of corrections are coming in.
Are you doing this in PlayerState, which has a slow replication by default?
That wouldn't matter as long as both the client and server are ticking this function.
good idea I'll try that
No CMC
But I agree with Silex, it doesn’t make sense it’s “slow” on 0 ping. Sounds like you have a seperate issue…
Also, you will run into some corrections happening just due to the server and client being slightly off at all times (even without network sim, they'd still be at least a frame off from each other). Which may result in a correction every network tick but it shouldn't be by much.
There's a general solution for that but figure out why it's such a big difference first.
It’s almost like maybe the client or server is doing the calc twice? I.e. missing an Auth check somewhere?
I didn't have an auth check, just run on client and server as is
This should be fine
Maybe follow the paths and double check it’s only running where you expect? Just spit balling as well.
I'll do some tests ^^.
Again, sanity checking: marking properties as Replicated in a GameMode is meaningless, right?
holy moly the official epic tutorials introduce so much unnecessary confusion
Which tutorial is talking about replicating properties in a gamemode?
In this video we take a look at the finished project and step through each of the features that will be covered in this series. We show our functional Main Menu and its options, a lobby where players can chat with one another and select their characters for the game, some server options such as changing the map or match time as well as the abili...
Which video specifically?
What time stamp?
That's the entire playlist
Almost everything in LobbyGameMode he marks replicated, properties and events
...yeah all the videos lol
oh man you weren't kidding
they're using a switch has authority in the GM
yeah uh... wat
Can you timestamp link that?
Mother of jeebus
Man - can we ping someone at Epic; that should be removed. There is so much good content out there, having this is actually a negative.
This stuff is also pretty old
I hope they put a bit more effort into keeping things on the new(ish) dev site up to date.
Oh yeah, super old.
There is one comment on youtube from 2 years ago that mentions that
Did game mode ever replicate?
Nope
@hollow eagle @woven basin Okay so well... it magically started working. Even with 500 ping, it's not that far off each other. I perhaps had some logic broken that with jump/dash cost that made the split so different lol. I'm glad you guys encouraged me to test it again, now I don't need to replicate stamina. I just correct it whenever there's a net correction now
GM has always been server-only
it's the whole point of having a GM separate from game state
Yay!!! I look forward to the final blog now 😂
i am looking to find out how to make player lobbies that fill up to about 15 players the enter a game level instance. part of what im trying to find out is how many instances of the lobby and levels would run simultaneously. does anyone know a good place for me to find some of this out?
What do you mean by not replicating and only updating it where there is a "Net correction" ?
Only way to know how many it can run is spin up multiple and test yourself. Each game is different.
So the client and the server both run the CMC. They both drain/regen a Stamina float on the CMC. And as @thin stratus taught me, I simply sync the client and server Stamina if there's a net correction.
What calls the net correction? Is that a native engine thing? @dry pebble ?
They're talking about replicating something for their character. Clients generally need to predict what the server is going to do for characters they control, but the server has ultimate say on what's actually going to happen. When the server and client disagree and the server tells the client to change a result that's a "net correction" (not a technical term for something in the engine, just generally what you might call it)
Client says some value is 10, server says some value is 20. Client corrects its value to 20.
So stamina is getting replicated, but if its ever off, the server will correct it?
Right.
ok
thanks
was confused for a second by the it not being replicated part of the statement
I think they just mean that the stamina value itself isn't being replicated directly anymore - instead they're sending a "set stamina to x value" rpc manually or bundling it with other data.
There are a few reasons to do that - primarily when you want direct control over how a client resolves incoming new data from the server.
Or if you only want to send a value at specific points in time rather than whenever it changes.
Yeah I was hitting that point myself. Do I use an RPC or do I use a OnRepNotify and have the variable be replicated, not sure when to do what exactly yet
I think the end result will be the same either way
Sort of
This is how I handle updating a custom stamina float and syncing it with the server when there's a correction. Whenever a correction happens, velocity/position etc gets synced, and we add our own custom float to sync as well like this. I could maybe check if the client and server stamina are far apart and sync another time too, but this is all I have for now.
void FMMOCharacterMoveResponseDataContainer::ServerFillResponseData(
const UCharacterMovementComponent& CharacterMovement, const FClientAdjustment& PendingAdjustment)
{
Super::ServerFillResponseData(CharacterMovement, PendingAdjustment);
const UMMOCharacterMovementComponent* MMOMoveComp = Cast<UMMOCharacterMovementComponent>(&CharacterMovement);
const AMMOCharacter* MMOCharacter = Cast<AMMOCharacter>(MMOMoveComp->GetCharacterOwner());
Stamina = MMOMoveComp->GetStamina();
}
bool FMMOCharacterMoveResponseDataContainer::Serialize(UCharacterMovementComponent& CharacterMovement, FArchive& Ar,
UPackageMap* PackageMap)
{
//return Super::Serialize(CharacterMovement, Ar, PackageMap);
if (!Super::Serialize(CharacterMovement, Ar, PackageMap))
{
return false;
}
if (IsCorrection())
{
Ar << Stamina;
}
return !Ar.IsError();
}
void UMMOCharacterMovementComponent::ClientHandleMoveResponse(const FCharacterMoveResponseDataContainer& MoveResponse)
{
Super::ClientHandleMoveResponse(MoveResponse);
if (!MoveResponse.IsGoodMove())
{
const FMMOCharacterMoveResponseDataContainer& MMOMoveResponse = static_cast<const FMMOCharacterMoveResponseDataContainer&>(MoveResponse);
/*trust server and update stamina*/
Stamina = MMOMoveResponse.Stamina;
}
}```
If you're just getting started with replication then go for whatever seems simplest to use for your situation.
And while it may seem like the result is the same either way, that's not quite true. Replicated variables only replicate at a specific rate. RPCs can get sent immediately.
And if you don't replicate a property then new clients joining won't have their value immediately.
Are there any docs on CMC in general? Or just have to go through source and figure it out? Like what is ClientHandleMoveReponse for, is it just to handle Client Side Prediction ?
Unfortunately CMC is its own documentation
as in no, there aren't really any good docs. Except one article that I can try to find...
this is a very general overview and doesn't go into specifics about how CMC actually works or what various methods are for.
No, we're not replicating it. But will correct it during a correction (could be caused by them being too far out of sync for e.g or something else)
I see, that saves on bandwidth?
I've just recently done a MultiCast RPC that also sets a Replicated bool, that is probably a bit redundant eh? I should just remove the replication from the bool in that case.
Yes, exe was saying it's not necessary to replicate it. They say "There is no need for it
The idea of the CMC is that it does the same on the server as on the client
In the same timestamp at least
If you consume the value during the sprint, it should consume on both ends the same
If you end up having a different value, it will cause a correction, at which point you send the server's value to the client to sync again"
makes sense
Replication on CMC specifically is a bad idea due to CMC handling conflicting results on client/server itself. It doesn't just stomp what the client sees with the server's result, it tries to smooth things over so you don't rubber-band a character.
Hey guys just wondering how I might do this.
Player A creates a session and can go mess around picking up item.
Player B joins the session after the player A picks up the item.
How can I sync everything up when player B joins?
Well that's good for movement type stuff right?
Replication outright stomps the client's value with the server's so you don't get an opportunity to resolve things "nicely" without some extra work.
In CMC's case, that extra work is very complicated.
What CMC does, yes.
Something like Stamina is better off on PlayreState or Player Pawn?
Given that it affects movement, not necessarily
Seeing siliex take a more active role in #multiplayer is cool.
I pop in here from time to time
You do - but it has been a bit more frequent the past few weeks.
Yus! it seems to work too. So this is how we handle stamina on the CMC instead of using a GAS attribute. Because those two don't like talking to eachother much lol
Although I did turn a "NotEnoughStamina" tag on whenever our CMC stamina is too low for a GAS dash ability and that works well
Ideally anything movement related works through CMC because CMC has a whole framework for how clients can predict actions and how to apply corrections from the server.
Mirroring data to GAS isn't uncommon.
That sounds like you missed the class on stateful replication: https://vorixo.github.io/devtricks/stateful-events-multiplayer/
Ill take a look at that
Also is there any better way of doing this or am I just dumb.
Yes, there is, it's explained in the article
oh cool 'cause that worked well. perhaps it's more GAS -> CMC that isn't friendly lol
Is the https://cedric-neukirchen.net/Downloads/Compendium/UE4_Network_Compendium_by_Cedric_eXi_Neukirchen.pdf still not pinned here?
it is
I think the general solution to that is to just use GAS to drive CMC actions on the client
GAS meaning?
but yeah, if you need something that depends on both GAS and CMC... that's a bit difficult
Depends what that is, but a multicast for displaying effects is good (for spawning muzzle flash for example)
But if there's a net correction because of GAS, then the CMC correction would be all out of whack wouldn't it?
Right
holy crap aint no way in the 2ms of me being in this chat you just gave really great info. ill read into it and if i cant figure it out ill post another thing. thanks
The article I linked goes over stateful changes, for example a player changing his skin (which is what you asked for in the first place)
Hello, I have a problem, im trying to open a widget depending on if I interacted with a bp character, problem is the widget never creates, I used some code for another actor that uses an interface, and iot still didnt work, any clue whats going on?
@woven basin I wrote some explanations and showed some code, lemme know if you need more 😄
From a while ago right?
I mean the changing skin thing. I dont think I posted anything in multiplayer about that in a while
You can't call Run On Server RPC on actors that are not owned by the client. This bit here wouldn't work assuming that the QuestGiver_Water isn't a player's character.
This is what I meant with "in the first place"
That's a stateful change you're seeking to sync
OK, Ill fix that part up and tes it
I mean the thing I was asking about was if a player picks up an item for instance a gun (which is spawned with the map) and then someone else joins later, I want to show the gun being held by the player and not still be on the ground
I know, the article goes over how you achieve that
With another example
You should learn the technique and apply it in your own world
Ok it still didnt seem to work, so I added a print string and just found out, that for some reason, the Event is never called
Gameplay Ability System
Has its own channel too #gameplay-ability-system
I understand that but I'm not sure how I can make my whole system synced using booleans.
This is how my system works.
Gamemode Generates map based off a seed given in my online game instance
Roads are generated with that seed
Buildings which are childrens of a parent are generated around eachother
In each building child I have a empty with the location of the gun spawner empty
In the building child, when it is created it then spawns a random gun actor that I have.
Everything is the same but when the player joins, the map gets generated based of the seed in the players eyes including the guns. So not including any changes that happened before.
OnRep the seed, and inside the OnRep_Seed you do what you want late-joiners (as well current clients) to see
Literally the same concept in that article
But I ruined the reading experience for you 😔
It's not though
In the article the example is if a player has press E and set the skeletal mesh while this has different steps, like if the weapon location, if it's attached to someone, who it's attached to, and just a few other things
I just had a big brain idea, one moment I am gonna just look up smth rq
Why do you feel like you must generate the level client-side when it's already being constructed server side?
Like why not just generate the level with all replicated actors
It's, it's just that you're not able to connect the dots tbh. Replicate the effing variable the way you see best and just let the OnRep do its thing
@fathom aspen did you mean to link this article instead of the one you did earlier https://vorixo.github.io/devtricks/procgen/
because that seems more relevant
and it does go over this specific stuff
Oh yeah that could be tbh, I just skimmed through it last time, so could be
Like the client would just join the session and the level would already be there all made already
this is the one about local generation + net addressability
so yeah, @tranquil swallow ^
Also if OnRep is called before the client joins they won't ever get it
replicated values will be sent on initial join regardless of when they changed, provided they don't have a condition against it
are you thinking of an RPC?
Thanks, Ill take more of a look into it
Sorry my pea brain just cant comprehend multiplayer lol
So a replicated variable that changes 10x on the server before a client joins... the client will join and automatically have OnRep fired off with the most recent value??
Yeah the seed now make more sense for them to look at that article, though they will find some advanced bits of info if they are BP-only beeps 🥲
Incorrect
yeah sadly that article isn't relevant for BP only because epic pls
the client will have the latest value set, yes
once the actor replicates at least - which may take some time.
OnRep is per client, so the client gets it when they log in and they get synced by the server @bronze mauve
It would be kind of silly for the client to not get the latest value when OnRep is meant for stateful changes
You're referring to the single trip time?
When a client joins it takes time for the initial replication of actors. Which happens in an effectively random order over multiple frames based on network conditions.
I wasn't insinuating that it would get a less-recent value, rather mistakenly assuming that it wouldn't get any replicated value at all that came in before the client joined the session
but BeginPlay won't be called on replicated actors until their own replication has completed, which includes the latest replicated properties from the server.
BeginPlay will however be called on replicated actors as soon as they are ready, even if other replicated actors aren't. Which goes back to how the order of actor replication is random.
By basically PostNetInit calling BeginPlay
Yeah it seems their BeginPlay gets called sometimes before said actor's player state/player controller etc. Are assigned/replicated sometimes
Exactly
You can't depend on replication order, sadly. "Important" actors like game state might not be ready when other actors are.
So unfortunately a common pattern is to delay your initialization until everything you care about is ready.
Kind of a pain tbh.
AFAIK it must if it's being accessed from BeginPlay of actor (yes it's mind-blowing)
How do you get around this problem? Like say you want to do something important like initialize a HUD. But you can't do it in BeginPlay because the info you need exists on some other class that might not have replicated yet
Ah you might be right about game state specifically since it routes beginplay
but other actors, nope
player state and such
Exactly, it's what "initiates" BeginPlay on clients worlds
I handle HUD stuff in OnRep mostly.
Fought Zlo a couple of times over that but yeah it is what it is
So a lot of player-related actors won't even get created until you have a valid player controller and such so it isn't an issue.
Like delay node: 2 seconds or something? Idk it seems hacky and prone to errors due to the unreliability of speeds
The issue is really around depending on other "global" things that might not be replicated yet.
lol - a losing battle I bet
Exactly that's why the guarantee becomes useless
99% of the times yes
Nope, because that 2 seconds might not be long enough on a bad connection.
I only won one time, when I said Push Model can be used with no cpp lol
A victory is a victory
If there are a set of common actors you absolutely need to be ready before running your initialization logic, you might write a "global" system that exists clientside that checks every frame if everything you care about is ready, and then fire off initialization from there.
This is a bit easier to handle in C++ than Blueprint
Even better, don't check every frame and just have those important actors tell the system when things are ready.
Ahh checking each frame, the "polling" method then
Ah okay so like a single manager class that knows when things are ready and relays that to each relevant class
Yep, that'd be a decent way to do it.
Like it has handles on the PlayerState, PlayerController, HUD etc. Also though it kinda needs to know about client and server side too doesn't it
On the C++ side I've very often written methods like this:
void CallOrRegister_OnInitialization(FSomeDelegate Callback);
Which calls the given callback immediately if everything is already ready for initialization, or stores the callback for later if things aren't ready.
I like this
I think Lyra uses that pattern in a few places
It's like fire off now if ready, otherwise just fire me off when you're ready
Yep
Yeah Lyra has a bunch of "Register this, register that" all over the place
There is such a subsystem James and vori made but I'm not able to find it
Speaking of James - he must be on vacation 😅
ah nice
Yeah hasn't showed in a while
Um, should I know what paliate means?
tbh I haven't had a need for that kind of thing on clients. I have on the server though - waiting for players to finish joining before initializing a match.
Reduce the pain of the situation pretty much
Actually I take this back - I needed it for one specific thing... waiting for a bunch of subobjects to replicate ;_;
why does subobject replication suck so much
iris save us pls
Epic fixed it for you in 5.1, next
Does anyone know anything about iris yet? Is it in UE5 yet?
It's in UE5, yeah.
Our lord and savior
Some of the smart people say that it does look nice. But that's about the gist of it.
In toto?
actually the biggest bug I hit with subobject replication was that a random object I had was marked as RF_WasLoaded (which prevents full replication since it's loaded from disk). But DuplicateObject doesn't strip that flag by default for some reason ???
Are there any such smart people who have created any guides for us yet lol
So I passed a duplicated version of it without knowing it wouldn't replicate
Is there a way to get the previous value from a RepNotify in BP? or only CPP?
Only in C++
Question. When a gamemode has a event that is ran on the server on beginplay, would that run everytime someone joins or just once
I like those low level bugs/issues
As in there isn't much code to look at? (I obviously haven't looked at it)
As in, it's not really usable.
As in no one knows enough about it yet.
sad but thanks
So it's pointless to even look at this point lol
Pretty much
Just once. GameMode is an actor. BeginPlay only fires once for it.
So if someone joins later after that gamemode as been ran, it will not rerun
Just once, as there is only one server instance
Definitely not. GameMode already exists when a new player joins.
Gotcha. Thanks, just tryna work through which stuff is replicated and how I can make this not as shitty
Well - nothing in GameMode is replicated. So that solves that problem for you.
I spawn actors that are replicated, would that be a issue
like performance wise
fair point lol
one sec let me word it out
I have a gamemode that spawns quite a bit of building actors which have replication enabled, this allows me to see all the buildings.
Would it be better to have an actor that the gamemode spawns which spawns the map (and only have that replicated)
not sure if that would work but yeah
thats what i'm kinda trying to say
GameMode can spawn things that are replicated themselves, yes.
Spawning an actor that is a replicated actor is perfectly fine.
Ok so it wouldnt be bad to do it the way I'm currently doing it
I don't know if I'd use the GM to spawn building actors personally
How would you do it just generally
You can use whatever you want to spawn replicated actors
What spawns it doesn't really affect anything about replication
It becomes a design matter
If an actor is replicated and exists when a client joins, it'll be replicated.
Wouldnt having a map generation in a gamemode be better because gamemodes are ran when the session becomes a thing
#just-adore-replication
I think I used the word wrong
the gamemode is created upon loading into a level as part of initialization of the world
but so are all other actors in the level
I meant the world
if you want the gamemode to create your actors, then you can do that just fine
If you looked at the article siliex linked you would see that's more or less what is done there
Well they have a dedicated actor it seems ProcGenSpawner
But it's managed by GameMode
Keep in mind the GameMode itself is null on clients
Not that they can't spawn replicated actors. Just saying
But a proc Gen level spawned seems like it should be handled by its own class dontcha think?
That's a design question, but if you're asking about my opinion nah, I find the GameMode pretty handy for spawning it
Like it spawns/manages other replicated actors already
GameState, Pawn, etc.
The benefit of the gamemode not being what handles it is the process can be initiated on the client separately (and then link generated actors up to the networked ones). Which can save on bandwidth and initial replication time, but for a beginner this is super unnecessary (and not possible in pure BP).
Yeah I figured a bit later that they static load the PrcGenActor
@fathom aspen 😅
Yes that's the new travel, it happens so fast, that why it's seemless
Travel go brrrr
No - the documentation for UWorld::ServerTravel()
Noted, thanks!
I already have plenty of them heh
@fathom aspen You might get a kick out of this: #cpp message
Trying to keep things free. It should be possible to learn all this information for free
I'd use the compendiums pinned in this channel, they're excellent
Ahh duplicate object is its own can of worms XD
@sinful tree Got it 🙂 Thanks for your help!
Ok, Im stumped. Can anyone offer a concrete solution on how to verify a clicked actor on the server? I've been trying to do the math of a line trace for a few hours, but I'm stuck and I think I might be doing it incorrectly.
This seems like deprecated since release... jeeez
What is the context? If the click is for "UI" purposes (for example, selecting the actor) I'd do that entirely client-side
Gameplay. Think of old school MMOs where you select someone or a mob and then just auto-attack them.
To clarify further, I'm using Get Hit Result Under Cursor For Objects right now
Which is great local, of course but not so great for server replication and verification
Mmm but I don't get the purpose of making it server-side. For example, in an FPS it makes sense to check server-side if the client did aim correctly a shot, because it could happen that the enemy is not there.
However, in this context, is the 'aiming' part that important? It sounds like it is akin to selecting a unit in an RTS game, just a client-side way to interface with a entity
Have you tried to do it client-side first to see if the problem is due to the server or it's something else?
The main reason is I'm concerned about cheating. Since you have to select a player or a mob to fight them, I'm worried the client could say it's selected a mob that's nowhere near them and then fight them.
I did plan some additional behaviour, like upon starting an attack you have to move into range to actually hit it... maybe that would mitigate any concerns I have and I'm just inventing problems that aren't present.
It works client side (I store it to Player State as I want other players to see what players are targetting), but again - concerned about how secure it might be.
The server should check the conditions that are relevant to gameplay. For example, if the target is in range, if the player has enough mana, damage and health calculations...
The fact that the player cheats to select an enemy does not sound like important at all, it's not like an aimbot or smth
Yeah I think you might be right. I guess if I had an ability that auto-hit something with no range it would be an issue if I cared for LOS, etc.
But I don't plan to have anything like that. any sort of attack will need to be within X meters and have los, so those I can always easily check on the server
yeah, exactly. Basically, anything that is really dependant on aim or exact location might be worth checking on server.
just make sure to check everything important (mana, range, that kind of conditions) so that players cannot cheat there
Yeah, luckily those are much easier to check. Thanks for being a sounding board!
np, glad to help and be useful
The most maintainable way to do this is to have a function for the condition check
that you call on both client and server
instead of separately maintaining all checks in two places
@obsidian basin
Hello guys I have weird problem I propably made some dumb mistake I have healthbar on my plane and when I am shooting I am applying damage on his health but as you can see on the screens it is replicating damage for both of players on the left window but on the right both have full health what can be problem?, this is how I am doing it I am using repnotify
last screen is from widget health bar
GetPlayerPawn(0) is your problem
Most likely without seeing more
Or GetPlayerController(0) - the screenshot is limited, there's not enough context, but you want to avoid those in general
There's more issues I have with the screenshots, you're updating UI on EventTick, rather than only updating it when the value changes
I'm using a pawn as my player character because I'm doing spaceships demo. Clients are spawning as Authority not Simulated Proxy. How do I change this?
Also if one of those windows is the ListenServer, you don't get a repnotify there, so your 2nd value is never set
Which is another problem, don't have multiple places that represent the value that you then update arbitrarily
Use delegates, keep the value in one place, don't copy it everywhere, the other places that do read from it, should change their display or usage when the value changes in the place that has ownership over it
how then update value when it changes like health if no on event tick
now I changed it to this , first photo is after linetrace when I hit pawn I will send whom I hit, second photo is when i get hit as pawn I will take a damage and third photo is bind action made for my progress bar
but it seems it has same result
you haven't changed any of the core logic, just moved things around
all of my previous messages still apply, it's most likely one of the two outlined problems
i am not using repnotify now but I need use get player pawn when I need cast to pawn to get health
Im not gonna be available until in about an hour. are you using two clients, paly as client, or one listenerver and one client?
2clients
Server should spawn them
No clue what's going on. I just tried making them based on character, not pawn and it works, but I get the same authority message 🤷♂️
Game mode spawns the pawn?
It does spawn the spaceship pawn.
The local role is authority? or the remote role?
And the pawn is set to replicate and only spawned by the server?
I did some digging in C++ and Character has code for serializing movement and movement prediction - CharacterMovementReplication.h.
Pawn doesn't have any serialization code or movement prediction....
That's probably related to my problems...
So as always with MP logic you need to do debug. It's possible that you are calling your Server_ApplyDamage function from a non-owned actor
And then it only executes locally
So for Client1 for example
I already solved it
and you are still getting player at index(0) for your health bars
instead the healthbars should know what actor they are a part of
and get that actor's health
I got reference for my widget and then press set percent
set percent
then did update hud
and multicast it
and its working
yes this helped me thanks
So that's the problem with only showing small snippets, you have cutoff functions
so it didnt show that you're not multicasting it from the AnyDamage event
Always provide as much context as possible, then it's easiest to get help
you've taken small cropped screenshots, those can be larger to include everything in the function
the person helping will click on the image to get the full resolution on discord
The smaller resolution version in text is just downsized, but the full-sized version is available
yea 🙂
I do have function update position in my plane which is doing exactly moving in the air for my plane but when I try replicate it after Event tick (because I need move him every second) doing it like this , I am replicating it but with very stranges differences I made screen with numbers of planes plane 1 and plane 2 they are in different positions on both windows Idk what can cause it
also getting this error related to calling fly server from event tick
Trying to make a homing projectile work for the first time. It replicates its movement but not rotation (client on the right side). Any idea what did I forget?
It's been years since I last looked at this, the rotations aren't all replicated
You gotta use the right rotation and not the one that is local only 😄
Ofc depends on how you implemented the projectile and what rotation it is using
It is a homing projectile, so it sets it rotation to the target automatically it seems
At least on the server
Think of it this way, always look for things to reduce being replicated
this looks like it can simply be simulated on the client
Indeed, that's a good idea. Thanks!
It's rotation has no impact on gameplay, it's purely visual
Thought the projectile movement component would do that for me 😅
All epic classes are designed for some standard usage, if you want to do something special you need to take care of it yourself
the PMC's designed for easy to use projectiles. It'll take care of rotation based on movement if you let it, if you want your own arching rotation and whatnot, you gotta implement them things yourself
is there a node to check if the client is well connected to the server ?
So I figured out my stamina regen issue, but not sure why it happens. When sprinting and then letting go of input to stand still, server and client regenerate stamina aa similar speed. But when I press "shift" to cancel sprint and then idle, the client regenerates 2x faster than the server. The only difference being DeltaSeconds. This is the code
else if (!IsSprinting() && Stamina < MaxStamina)
{
StaminaRegen = 3.f * DeltaSeconds;
SetStamina(StaminaRegen);
}```
The StaminaRegen difference
Pressing shift:
StaminaRegen = 3.f * DeltaSeconds;
0.06 0.03 client 2x faster
0.18 0.08 client 2.25x faster
0.26 0.13 2x faster
0.38 0.18 2.1x faster
0.45 0.29 1.5x faster
0.5 0.29 1.7x faster
Letting go on input
0.023 0.024 - same for 3 frames
0.033 0.024 1.3x faster
0.024 0.24 - same for 5 frames
0.024 0.03 0.8x faster
0.025 0.024 - same for many frames
0.024 0.024
Any idea why pressing input to cancel a sprint (compressed flag) would somehow change the deltaseconds?
Are you checking Delta Seconds to see that the difference in Delta Seconds is enough to cause a 2x difference?
Maybe SetStaminaRegen is getting called twice on Client
I could be wrong, but I think what is actually happening is that it's double stacking the input of shift somehow. It seems very unlikely that it's changing delta timings
This
Oh interesting... how do you think I could prove/breakpoint that?
Do once node I believe would be the solution to prevent it from running more than once on a single input, and print string just to see how many times it actually fires the event
Pretty sure this is the issue though, as I had the same issue with a sprint system at one point and it sounds very familiar lol
If the var were replicated then the client would have it be corrected to servers
Perfect I'll try that
yeah it's not replicated, only syncs up with a net correction.
If not replicated you would want to be sure there is some error checking then right?
it's still server auth
Let me know if that works or not, at the Dr's office ATM bored 
If it were server auth then Client wouldn't get double the regen speed on server?
it's server auth with what it does with the stamina. E.g client at 90 stam, server at 40 and if client tried to jump (need 50 stamina), client will have a net correction, server won't allow it.
will do 😄
oh so just a visual issue won't actually effect gameplay
they're mostly always in sync + or minus a bit even with 500 ping, so this other issue is some weird thing
level blueprint depend of the server right ? client has no authority on it?
Correct
so i can consider that as a gamemode ?
Not sure I understand?
is the starting new player is call on every client in addition of the server ?
you're right, client is calling it twice!
Nice! Well now you know
Why the hell would it call it twice by pressing input again if anyone knows? It's a compressed flag in the CMC to toggle sprint on and off
Do you know how to call DoOnce in C++?
Ahhhh, I'm pretty new to c++ still, not sure. Let me do some googling
Use a bool
bool DoOnce = true;
Oh? It's actually that simple? I found some examples that weren't that simple 😅
ahh yes, it seems that's it lol. Lemme try it
Here's one I found. I'd certainly use the simple method over this
You mean Player Login? That only happens on the server in the Game Mode
in header
bool DoOnce = true;
and in .cpp
if (DoOnce == true)
{
DoOnce = false;
// Do something
}
so why if I print something is it printed on everygame ??
IDK what you are printing, where you are calling it to print and how you are testing.
@dry pebble I don't think Do Once is the right solution, it is likely a Band-Aid to the real problem.
Having more "regen" type variables to manage and having a bool just to do them once doesn't seem scalable
Can you show me the prints?
Yeah defs a bandaid. Couldn't get it to work anyway lol, as by resetting the bool back to false runs it again anyway.
I'll scour through the discord and see if anyone else had this weird input double method issue
What are you replicating? Maybe you need to do a check so that the call only happens server side? Are you doing Server only RPC, a MultiCast?
But if its a sprint type thing you are wanting the client to do it locally so it looks proper ?
So client does it once locally but is server telling client to do it again?
I'm not replicating the stamina right now (that's a last resort). So client and server are both running stamina drain and regen, but server is still authoritative. The stamina for the client is for UI
Ah sorry about that. I'll take a look at my code base later when I'm home and see how I fixed this issue. Can't remember the actual fix off the top of my head
UpdateCharacterStateBeforeMovement
if (IsSprinting())
{
const float SprintStaminaDrain = Velocity.Size() * StaminaDrainFactor;
SetStamina(-SprintStaminaDrain);
}
else if (!IsSprinting() && Stamina < MaxStamina)
{
StaminaRegen = 3.f * DeltaSeconds;
SetStamina(StaminaRegen);
}
void UMMOCharacterMovementComponent::SetStamina(float NewStamina)
{
Stamina += NewStamina;
//For UI on client
if (OnUpdateStamina.IsBound())
{
OnUpdateStamina.Broadcast(Stamina);
}
}
```
But its only a client side issue? Server doesn't have the issue right?
that would super helpful, thank you!
No issue on server, server calls the method once
@thin stratus Hey Cedric - a little bit ago you were going to try out the seamless travel in PIE thing added in 5.1, if you did, did it work? 😅
Seamless travel in PIE?
No I haven't tried it. I only noticed the console variable.
If I let go of WASD keys, client and server regenerate at the same rate. It's only when toggling sprint manually and letting go of WASD (so idling) that client calls it twice.
If none of that is replicated I'm not sure why there would be a difference between client and server like that
Yeah the only replicated part is the sprint flag (left shift)
Are you checking the value of the sprint flag client side ?
Actually nvm IsSprinting() just checks if our speed is sprinting speed
bool UMMOCharacterMovementComponent::IsSprinting()
{
return Velocity.SizeSquared() > pow(MaxWalkSpeed , 2) && (Acceleration.Size() >= (MaxSprintSpeed - 100.f));
}```
@dry pebble What are you struggling with
This is not going to happen. There are various tech debt items inside of the engine source that prevent this.
So fixes to the engine would be in order and Epic would need to accept them
@thin stratus Her client side sprint drains twice as fast as servers
Also Epic keeps adding new tech debt to this with major releases, because it's not supported in PIE
They introduced a new console variable lately, so they are working on it
I haven't seen that yet, I just noticed the new tech debt in 5.0 , that was the last time I checked
😂
When I'm sprinting, the stamins is drained. When I let go of WASD input to idle, client and server regenerate stamina at the same rate. But if I toggle "left shift" to stop sprinting, client regenerates double the speed as the server, it calls the stamina regeneration twice.
You aren't somehow making stamina drain framerate dependent, are you?
I'm also currently working 100h weeks atm, no luxury time for checking much right now. I haven't caught up on the 5.1 changes
this is the code, it regens with deltaseconds #multiplayer message
Where are you draining the stamina?
UpdateCharacterStateBeforeMovement in the CMC
Very cool. That would be a great improvement. I'd love for PIE to be a first class citizen
also 3D UI being fixed in multi-session PIE would be great
How much FPS does your Client have
Regen is with delta seconds, sure, but I responded to a message about drain. Also SetStamina is a bad name for a method that adds stamina.
Doing some MP prototype work in 5.1 and Net Mode listen server with 2 clients has a 30 or so second lag and I have yet to figure out why. No server lag emulation on.
I'll check
ModifyStamina(float ModifyAmount); - fite me Daekesh ✊
-500 👀
30 seconds ? 😂
or did you mean milliseconds? lol
capped to 120 it looks like, flickers between 119 to 120
Also, just to make a point, the stamina drain does not appear to use DeltaSeconds at all. Unless you are hiding part of the method.
Can you cap it to 60
And tell me if it's still broken
kk
Deltaseconds is passed in from the main method sorry
void UMMOCharacterMovementComponent::UpdateCharacterStateBeforeMovement(float DeltaSeconds)```
So? It's not actually used in half of your code
omg you genius!!!! that fixed it!
So the FPS were different between client and server?
Didn't I tell you that you need to implement the SavedMove for your Stamina
No
She's running into Combined Moves
If the Client has over 60 fps, it sends the Move Combined
Ohhh
But it performs the move first, then combines it and then performs the combined one
Performing the original move twice
Try setting it to 30 fps first, just to check.
I do with float Saved_Stamina; and in SetMoveFor and PrepMoveFor and Clear(). Is there somewhere else to add it?
Thank you for having dreams about the CMC Cedric.
Yes, CombineMove
You need to use the initial position or so function
Save your Stamina there
And in the CombineMove function
You gotta set the CMCs(!!) Stamina back to the Old Move
Well that is illuminating
You know what - screw it, unlimited stamina. Easy.
MP management what fun it is
ohhh oops. Lemme have a look and see if I can do that.
void FSavedMove_SomeCharacter::SetInitialPosition(ACharacter* Character)
{
Super::SetInitialPosition(Character);
const USomeCharacterMovementComponent* CharacterMovementComponent = Cast<USomeCharacterMovementComponent>(Character->GetCharacterMovement());
if (IsValid(CharacterMovementComponent))
{
bSavedWantsToSprint = CharacterMovementComponent->WantsToSprint();
Stamina = CharacterMovementComponent->GetStamina ();
}
}
void FSavedMove_SomeCharacter::CombineWith(const FSavedMove_Character* OldMove, ACharacter* InCharacter, APlayerController* PC, const FVector& OldStartLocation)
{
Super::CombineWith(OldMove, InCharacter, PC, OldStartLocation);
const FSavedMove_SomeCharacter* OldVRTMove = static_cast<const FSavedMove_SomeCharacter*>(OldMove);
USomeCharacterMovementComponent* CharacterMovementComponent = Cast<USomeCharacterMovementComponent>(InCharacter->GetCharacterMovement());
if (IsValid(CharacterMovementComponent))
{
CharacterMovementComponent->Stamina = OldVRTMove->Stamina;
}
}
omg thank you so much, I was getting confused again on how to get the old move. Gonna set it up now. Exe you're an absolute legend!
Cedric, when are you writing your own CMC as a plugin and publishing it? I can't imagine that being more difficult than constantly dealing with stuff like this 😛
Surely this will be in the network bible 2.0, right?
I haven't messed with CMC for networking since UE 4.20 and remember absolutely nothing from the work I did there. Got something working and was happy lol
Oooh you're using CombineWith, thought it was CanCombineWith, no wonder I was confused lol
What the CMC does is:
// Frame n
- Check if there is a PendingMove (let's assume there is not)
- Perform the NewMove (let's call it MoveOne)
- Perform MoveOne
- Check if it should postpone sending the move (>60 fps by default will cause this)
- Set PendingMove to MoveOne and don't send move
// Frame n+1
- Check if there is a PendingMove (yes there is)
- Check if we can combine PendingMove with NewMove (MoveTwo)
- Let's assume we can combine it so combine them
- Perform CombineMove
- Send CombineMove
The point here is that Frame n and Frame n+1 both will Perform MoveOne. Just that Frame n+1 will do it due to it being Combined with MoveTwo
That's why Epic reverts everything requires back to BEFORE MoveOne inside of CombineMove
where sever takes viewlocation of players from ?
So you gotta do that for your Stamina
I want to check if there is any player near my actor on ded server
CanCombineWith is just the check before it combines
By default this will fail if the Move can't be combined
E.g. cause we are sprinting in this frame but not in the new one
Or other way round
You only want to combine moves that don't cause problems if combined and can act as one bigger move
If you hold forward the hold time, you can just move twice as far in one move
But if MoveTwo for example means that you start sprinting, then you want to ensure that doesn't get combined
That is checked via the Flags already though
I only check this stuff here which shouldn't be important for you
bool FSavedMove_VRTCharacter::CanCombineWith(const FSavedMovePtr& NewMove, ACharacter* InCharacter, float MaxDelta) const
{
const bool bSuperCanCombineWith = Super::CanCombineWith(NewMove, InCharacter, MaxDelta);
const FSavedMove_VRTCharacter* ThisVRTMove = static_cast<const FSavedMove_VRTCharacter*>(this);
const FSavedMove_VRTCharacter* NewVRTMove = static_cast<const FSavedMove_VRTCharacter*>(NewMove.Get());
if (ThisVRTMove->bBoostOnCooldown != NewVRTMove->bBoostOnCooldown)
{
return false;
}
if (ThisVRTMove->bCanBoostXY != NewVRTMove->bCanBoostXY)
{
return false;
}
if (ThisVRTMove->BoostCooldownTimestamp != NewVRTMove->BoostCooldownTimestamp)
{
return false;
}
return bSuperCanCombineWith;
}
Taking a moment to read your message to let it sync in, appreciate you writing it up!
I'm not using PrepMoveFor
Cause I don't need to prep anything when boosting/sprinting
@thin stratus Would there be enough example code for Inputs like that if you look at source for "Jump/Jumping" and model that?
Jumping has no extra value
You will only find the Location being reset
in the parent implementation of CombineWith
Someone posted some info about the CMC ages ago
You can find that via google
But it's lacking
And not 100% correct
So I won't link that
There was one blog I read back in 4.20 days that guided me and I can't find it, thought I bookmarked it
So upside: I figured out how to spawn non-player controlled mobs. Downside my selection logic breaks clicking on them during the server call.
Are you spawning them on the client?
Gamemode is casting a Multicast event, so I don't think I am.
Ouch
GameMode doesn't exist on Clients. Multicast is not a thing that works in GameModes
You can just leave that a normal Event
Well, it'll run on the server 😛
:pain:
eer not game mode, game state
That on the other hand would be bad
Sorry I just woke up
Cause you want to only spawn them on the Server
So you should still not set that to be a Multicast
But Game Mode is a better place, or is there somewhere that's more desirable for this?
Doesn't necessarily matter at this point
As long as you don't spawn them via a Multicast
Just pick a place to spawn something and do it only on server.
I have a spawner actor that handles this for example.
Not necessarily GM or GS.
Ah, smart
It is completely design dependent
Just make sure you only spawn most things on the server.
You have an actor with functions? DawnMech will be mad! 😄
It even uses inheritance 🤫
I heard there was a way to mark a placed object be server owned as well, is that true?
So if I wanted to move the logic for each spawner into the spawner actor itself.
Things like respawn time, type of mob to spawn, etc.
Or is the spawner, which calls the spawn function ont he server enough?
My spawners are on the server only.
Ah, so placing a mob = no go. Placing a spawner which then spawns the mob on server = ok
Just don't spawn things on the client
So just to clarify I understand, Frame n+1 is setting Stamina twice because it is MoveOne + MoveTwo combined? And we don't actually want Frame n+1 to call MoveOne again on the client right? So to combat that, for Frame n+1, we reset our stamina back to before MoveOne in CombineMove. But MoveTwo still runs as usual?
And we don't actually want Frame n+1 to call MoveOne again on the client right?
That's not something you control
That's how the CMC works
Oh okay, so it's going to call it anyway, and we handle it with CombineWith?
So to combat that, for Frame n+1, we reset our stamina back to before MoveOne in CombineMove.
Correct
But MoveTwo still runs as usual?
No, MoveTo is now part of the Combined Move
MoveThree will be a single move again
That is later combined with MoveFour
In theory at least
What is the purpose of reverting it back to before MoveOne in combine move? Is this for the client's bookkeeping or for sending to the server?
The purpose I can't tell you, I didn't code it
The reason the have to revert is so that it doesn't perform MoveOne twice
The Combining itself is bandwidth saving
ahh perfect that's all I need to know then, how to use it. The why shall remain mysterious. 😄 Thanks Exi, you are so freaking helpful and knowledgeable.
I was just about to ask why they combine, but you answered, makes sense. if you're ticking too quickly, you'd be spamming the server so much more with FSavedMoves.
So without CombineWith and SetInitialPosition custom code, is stamina being changed three times in two frames? Frame n: stamina changed on client. Frame n + 1: Stamina changed from MoveOne and MoveTwo?
Yes
MoveOne and MoveCombined
It's still only two "PerformMovement" calls that end in your UpdateFromCharacterState code
But the second one ,due to being combined, has a "twice as high" (depends on the two moves) deltatime
@dry pebble
can we start subinstance of a game server ? like I have a GameLevel, and each game is redirected to a dedicated server of an instance of GameLevel or should i start it multiple time outside of the game ? (not so clear but if anyone understand)
If you're asking for multiple worlds per instance, then no. Unreal does not support this. If you're asking for Unreal to act as a proxy for multiple other Unreal instances then also no. That's up to you or your team to make sure of to make work.
thank you so much! It all works perfectly now 😄 :D!
eXi, i ALSO have a cmc related question 
theoretically, why does sending the old move exist?
well i mean i guess there's a REASON, right now i'm wrapping up my own implementation of network prediction interface and i think i fixed what was wrong before
i finished it up last night with autonomous <-> server sending single moves only
and the performance was good, after my fixes
now i implemented servermoveold (using the OLD style, not the new packed style)
it basically just sends an old, unack'd move, but doesn't ack it back
is the... ack not important for an older move?
(also what was wrong before was that uh... i was sending the FINAL rotation, not the starting rotation, and thus anytime rotation wasn't the same as the previous frame, i would always be off by about like 2-3 degrees of turning and that REALLY fucked up the prediction all around lol)
The server acknowledges movement packets because A) it may be a wrong move according to the server and needs to be corrected on the client B) the client can free up that move from memory afterwards.
but, why not for old moves? i'm not sure if that was changed for the packed/single RPC functions of CMC, and when i was doing single move transfers that made sense
but when a server receives old move via ServerMoveOld, it doesn't ack it back, it just takes it
hm... i guess i should look at the new packed versions of the functions to make sure they didn't change it, but in the non-packed version, old move doesn't seem to have an ack at the end D: unless...
It would be very odd if they didn't have any ack whatsoever as that is a fundamental part of how server authoritative movement works right now 😛
it verifies client time stamp, goes straight into moveauto, then doesn't ack at the end
yeah, it looks like the new single rpc function acks at the end, but OldMoves are always flagged as such and i guess it never gets ack'd?
yeah i am able to do that i just wanted to know if it is not native on unreal before dev anything lol i have doing work for nothing
Maybe that EOS can do dedicated server matchmaking, but not sure.
Also not sure if that's really considered a 'proxy' lol.
Although EOS still sounds too good to be true honestly. I'm still very suspicious of it xD
is there any way to pass things between levels? i.e i have a main menu level and a play level main menu is local i guess and play level is server side (dedicated) and i want to take a player name from the main menu to the play level. do i do that by passing in login info that returns a true berried inside the server with sockets/http using guid as the "its this player i should assign name to.
nvm another case of ask and find an answer that may suit. use open level and pass cmd line arg of username=xyz uuid=somenumberfromloginserver.
then serverside on beginplay load that cmd line to the player i think
and final bug down: i was handling rotation as part of my PerformMovement implementation and using AddYawInput, but the changes of that are only committed to the ControlRotation during PlayerTick
i had to change my rotation handling to write directly and pull directly from the control rotation like the rest of my networking, but i think every setcontrolrotation should be clamped... other than that, everything should be nice and neat! i'm done with networking and can move on to other things in my project!
Hi, has anyone here had issues getting dedicated servers to work on UE 4.27? I keep getting this error and the only solution I've heard is to go from 4.27 to 4.26. Has anyone here found a fix for 4.27 or know why it is happening?
Are you trying to open up a level as a listen server on a dedicated server? I'm not sure if that will work but haven't tried to spin up a dedicated server before.
I'm launching a dedicated server. It was working if I set it to a listen server, but launching a dedicated one started giving me issues.
I managed to get it to work just now by disabling all of the stuff related to OnlineSubsystemSteam in my DefaultEngine.ini.
The tutorial I followed told me to add all these commands, but I'm not trying to implement Steam integration yet.
My guess is that I haven't set up everything related to steam correctly so it can't list itself properly. Hopefully it will work fine when I get around to doing the Steam integration.
I am able to connect to my server via IP through the "Open Level" node, but I still cannot find it through the "Find Session" node.
I'm able to find the session if I host it as a listen server instead. Dedicated servers on LAN seems like they should be able to be found by the "Find Sessions" node, no? I have set DefaultPlatformService=Null in my DefaultEngine.ini and I can find it through listen servers but not through dedicated. Do I need to have an OnlineSubsystem like Steam/Epic to just find local dedicated server sessions on LAN?
Dedicated Servers don't magically create a Session though
Only because you start it doesn't mean it's Creating a Session
That's usually done in RegisterServer in AGameSession
At least when using C++
Without that, you won't find even LAN OSS NULL Sessions, cause there is none :P
@glossy nimbus
Ah, makes sense. Is an AGameSession created upon starting the dedicated server, or does it have to be called through a "Create Session" function?
Hey guys, I'm having an issue and I'm wondering if this thing is the issue.
Whenever a player dies it activates this spawn method, when the player dies, it will spawn a new actor and make the player possess it. The issue I'm currentlly having is this
Player A -- Server
Player B -- Client
Player C -- Client
Player B kills Player C, Player A then respawns.
Is it because I use Get player controller and it's index is 0 and how would I work around this and get it to possibly work
That's a class that exists already
Same as GameMode and such
You just need to make a child of it and override the get GameSessionClass function in the GM to return your own
And then override register Server
@thin stratus
I was able to get it to work. I just had to create a session in the level blueprint using the "Create Session" node using the IsDedicatedServer boolean
Alternatively you could just create your session in the GameMode BeginPlay locked behind is dedicated server
However it shows up twice with different pings
Yeah, that's what I tried to do now:)
Any idea why it shows up twice?
Yeah don't use GerPlayerCharacter
I clear the children when hitting the search button. It looks like this:
You know who died cause you usually are in the Character, code-wise, when they die
After all you reduce their health variable
If you have a default pawn in your gamemode you can also just call restart player on the PlayerController
Well when they die it calls the spawn method
I can't read that. Not sure if the image is bad or discord on phone sucks
So when their health is detected to 0, them being dead is set to true and then a few things set off
Let me send a better one
The GetPlayerController will always give you the servers controller at ID 0 if it is a listen server
Why do you server RPC again at the end
You could just place that delay where you call the Multicast
Or limit that part to authority
The multicast already executes on the server
Because this spawn method wont only be called by the death method
Oh I know what you mean lol, brain fart
@thin stratus
Any idea why the server appears twice? I clear the children of the scroll box every time I click the search button. I'm not finding anyone with the same problem on google:S
Just didnt realise I had 2 delays
Does the search result really Return two elements?
Lemme check, 2 sec
Do you maybe still have a server running in the background?
Check task manager I guess
This is what's happening, its a bit weird
It does return two servers and the servers disappear if I close the one dedicated server window. Let me check if the IPs are the same
Task manager also shows that no extra instance is running
Try the native find session and create session nodes
Just in case the plugin is funky
I'm heading to bed now so you are on your own now :P good luck though
True, I'll try that quickly. I was able to find another workaround also since both servers return the same SessionID. I could filter it out by adding it to an array and checking if it is already in the array. The "Contains" node call might get more and more expensive if there is a lot of servers online.
Alright, thanks for your help!😀
Hey, I want to iterate over all the connected PlayerStates after a player logs out but during the OnLogout event, the exiting player is still included in GameState->PlayerList. Is there a nice way to only get players that are not exiting or is there a different event that I should be hooking into?
I can pass along the Exiting Controller but it'll take some refactoring. Just wanted to check I'm not missing something.
Solution: I'm calling my iterator function from PlayerController->OnEndPlay instead (only when it has authroity)
Guess there's no OnRep for the playerstate array
It's not replicated, playerstates add themselves to it IIRC
My bad, I ment the playercontroller array^*, in the gamestate
Huh kinda surprised its not replicated 🤔🫨
I suppose now the local player always comes first, atleast
How do you check if the host has disconnected, when playing as a client in a listen-server setup?
So you can quit session and go back to the main menu for eg
I think the only way for a listen server is to ping it
Client call: ServerPing();
And then the server call ClientPong();
For example: use a timer and call the ServerPing(); every second
If the host disconnects all clients would be sent to the "Game Default Map" you've defined in your project settings.
There is a "Network Error" event that is present in the GameInstance that can be used to detect when unexpected client disconnects happen on the client side.
Does this work even if the server crash?
Yep
Hmmmm... I take that back.
There is a connection timeout setting that after 60 seconds it will boot them back.
[/Script/OnlineSubsystemUtils.IpNetDriver]
InitialConnectTimeout=60.0
ConnectionTimeout=60.0
So if the host leaves (non-crash scenario) it'll boot everyone immediately. If it's a crash, the timeout will come into play but all clients will be booted back to the default map.
Yes because during my tests I never received this error message that I implemented
The Network Error fires first, then the default level opens - you may not have enough time for your widget to be displayed before the engine automatically loads the new level which removes the widgets from the viewport.
How do I check that a client has disconnected or left the session, as a host in a listen-server? ie, I want to clean up and remove player's pawn in the map after they have disconnected
there's a "logout" event in AGameModeBase that you can override
Player Pawns should already be destroyed though, unless we aren't talking about possessed ones
that's strange, mine will freeze in place when a client disconnects
void APlayerController::PawnLeavingGame()
{
if (GetPawn() != NULL)
{
GetPawn()->Destroy();
SetPawn(NULL);
}
}
void APlayerController::Destroyed()
{
if (GetPawn() != NULL)
{
// Handle players leaving the game
if (Player == NULL && GetLocalRole() == ROLE_Authority)
{
PawnLeavingGame();
}
else
{
UnPossess();
}
}
Unless you override any of that, it should work automatically
Cause I usually know the opposite side where people are struggling to have the Pawn stay behind
hey is anyone know if i can trough the editor start a server with x standalone client that run a map A whereas server run map B ? if i use the server map name override setting it also start my client on the map B instead of A
Maybe if you uncheck UseSingleProcess?
Hm then not sure
so i guess i have to start everytime using a command meh
i can bypass it like that lol
Why would the Dedicated Server be on the MainMenu though
ahem, just do not care about name ahahah is it not really like it is supposed to be. Is it much more like a waiting room atm
Shooting this into the void, just in case. I'm working on a more complex project that I can't share many details about. But I'm working on tooling that is present both internally during editor usage as well as on the target device at runtime. So I have a singleton that references the current gameinstance, and a gameinstance subsystem that registers and unregisters itself with the singleton.
This is because some of the tooling needs to be available from within the editor whilst there is no session in play, and other tooling needs a game session to update within the editor (PIE) or on the target device.
The obvious caveat here is that it won't work on dedicated servers. I doubt anyone else has run across this, but if someone has do holla.
Yeah it's together with the sole purpose subsystem that sets and clears it, because order
Hello, tell me what is better to use for the open world, world composition or world partition?
World partition since world composition is deprecated and I don’t think it’s not even in UE5
just to be sure if i do it right. To change level on a server (so on the game mode) i have to do a console command with a "serverTravel" right ??
Yes
but this do not wait for all players right so if i want to do specific behaviour like spawning on the level i should wait for all players travel ?
What do you mean with Wait for all Players?
i do that and just next to for each i have my spawn logic
it do not work as inted everytime but if i had a delay it seems to work
You should not spawn anything after calling ServerTravel though
Preferably ServerTravel is the last thing that should be called
You can spawn again once the Travel is over
so i have to do something like :
open level -> spawn -> server travel instead
travel -> spawn ?
i am not so familiar with the online logic
No
What do you want to spawn?
ServerTravel moves from current to the new level
That means also a new UWorld
Everything you spawn before ServerTraveling is usually lost (unless specifically preserved via C++)
atm i am in scene A like waiting room
i need to switch everyone on scene B (that is why i do a travel)
scene B is where everyone is so i need to spawn every player on the world
Yeah the you will need to handle spawning after the travel
E.g. in your new GameMode on BeginPlay or even just through the already existing spawn code of the DefaultPawn
SceneA and SceneB should preferably have different GameMode classes assigned
fwiw with the same parent if needed
GameMode of SceneB can then spawn the Pawns for the Players
to change gamemode on command i have to do ServerTravel Map?game=Gamemode ?
Yeah but only if you have one map that can have multiple gamemodes
Otherwise that's not needed
Hi! Quick question regarding multiplayer and "Set viewport mouse capture mode". I've been trying to fix an issue I have been experiencing when the server hosts the game, then afterwards client(s) joins the game and suddenly mouse clicks stop working. Mouse axis/movement still works, but mouse1 and mouse2 stops working. This does not happen to client1 when client2 joins, only server. On beginplay on the controller I set "capture permanently" for capture mode, if server/clients join the game at the same time this problem doesn't occur. What solved the issue was changing the capture mode to "Capture Permanently including initial mouse down". I just don't understand why this change would fix the problem, and capture mode never changes on the server controller when clients join.. Any ideas?
Hi! Do I need a session if I want to join a multiplayer game? In other words, is it mandatory to have a session in a multiplayer game?
No
Sessions are just a kind of meta thing. You could literally just join a server directly without them.
OK. Thanks.
If your end goal is dedicated servers, you essentially need to develop for them from the start right? It's not a case of building a listen server model and converting it later, the architectures are significantly different enough that I need to be working with a dedicated mindset from the get go?
The more I typed that, the more I answered my own question I think..
No 😅 when in these big channels, messages tend to overlap, unless you are directly pinged you can assume usually people aren't talking to directly to you
Unless the context makes sense
id go dedicated from the outset.
i had an isse when going from a simple made project to decide to dedi server it just converting it to c++ broke it. so i started again from a base dedi client build. now it seems to be going alright. except iv no clue on how to get the "users details" from the mian menu to the dedicated servers map.
Listen servers vs. dedicated servers you basically just need to consider that on a listen server the host is effectively also a client, so your logic needs to take that into account, and if you don't then you'd end up having to go through all of your logic to fix it up later.
An example of an issue you could face would be using an Authority check on something where you may only want "clients" to execute a function, but in reality, the host is also a client and should probably execute the function too, so the authority check isn't sufficient for what you're attempting to do.
i had tried using cmd line inputs but seems they can only bee booleans and ints you can grab back out of it. so my next thought is sockets or something where id login req a pawn id load pawn ready and connect client with pawnid as the argument but thats a bit ridiculous as u could spam some random numbers as a hack and log into a random pawn....
Yeah i suppose my question was: is that process of fixing up later worth it given the overheads with dedicated models (building from source, having to add extra builds and ultimately host the thing) but i feel like the answer is no, I may aswell just put in that bit of work upfront and develop the game for the intended "final form"
You don't really have to
You can run a launcher build as if it were only a server just for testing purposes. It won't be using the actual dedicated server config, but it's fine for testing in the editor.
The important thing is to be building for multiplayer from the outset
Sticking with good practices and regularly testing in both listen server and separate server modes (if you need both) will get you most of the way until you get to a point where you really need to start testing things.
The editor has lots of options around testing these different scenarios and while they won't catch everything they're fine to start with.
Fixing it later rather than developing it to work for both as you go could take significantly more time as you would have to track down all the spots where such networking could happen.
Even if you eventually decided to scrap the idea of allowing dedicated servers, the logic will still work if you design your code for both but only use listen servers. It's not a big difference between the two in terms of coding, but rather the issue is that there is a difference, and that difference can be in hundreds of places depending on what your code needs to do.
They're asking about the actual need to start using dedicated server builds from the start.
Which should be an emphatic no since you can run normal game configs in server mode for testing purposes.
Most devs won't be working with real dedicated server builds except when specifically debugging issues with that build config anyway.
You're fine to start developing without it as long as you're working with it in mind - don't make assumptions about every instance of the game having a local player is the most important piece. And then test with separate server instances both in PIE and standalone when necessary.
The issues that will crop up in dedicated server builds later are primarily around actual compilation issues, and optimization (removing unnecessary logic and assets from server builds). Neither of which should be big concerns near the start of a project.
can't you run a "dedicated" server instance that doesn't have a player inside the editor when testing locally?
You'd want a real server build from source eventually but it's not something you have to plan for early on.
how can you stop players from connecting to your session when the host is in game and not in lobby map ?
override PreLogin on the gamemode
this says it is a hardware idso i guess if i changed the right something about the hardware it would change
Anyone know how to convert jump velocity to something I can calculate distance for a skill?
For example, say I have a character Jump skill of 100. I want to convert to a 10-1 so they would jump 10 feet (High I know, but just as an example)
Not sure how this is relevant, but just divide by 10 ?
Oh wrong room, my bad
Thought I was in #blueprint 😄
Question: When calling PlayerState->GetPlayerController the tool tip says it will return null for remote clients. Is this a reliable way to branch on "is player locally controlled?" Specifically when the function is called from a listen server, is it expected to return null for remote players even though a corresponding PlayerController exists on the server?
if the player is locally controlled, then yes, it should not be null
if you're skeptical, just put a null check and a warning
and the inverse - if it's not locally controlled will it definitely be null?
I would think so? Though I'm not sure, UE can be vague with the terms
I'd say the best way would be to test in editor and see
Yeah - I'm getting the behavior I want but the terms were confusing me. Originally I was calling GetPlayerController->IsLocalPlayerController on the server and I was surprised to get null exceptions. It seems like GetPlayerController itself is enough to verify locality. Thanks for your thoughts
yeah because local player controller is null for servers, all playercontrollers on the server are netconnection based
playercontrollers on a client, for the local player, are localplayer based
Sorry I should have specified, it's a listen server not dedicated. So it has one local controller.
ah yeah! sorry, i've been in dedi server brain mode because of my project
all good! :)
In single-process mode the exact same place that the rest of PIE lives. It's not doing anything special when running a PIE server, it's just starting a hidden PIE instance in server mode.
When running in multi-process mode it's just running a normal standalone instance with -server and whatever other arguments are necessary.
UNetDriver and UNetConnection are the start of the higher level framework (above udp).
They're not specific to the server.
Also "what is responsible for being the authority" is the wrong way of looking at things. There is no singular authority, every actor has the potential to be the authority on the current game instance or not.
Clients can be the authority for an actor. With the built in net driver that'll only happen when an actor only exists on that client (or if it's torn off), but the same concept would work if you managed to do a bunch of custom work for a true p2p setup.
I don't believe there'd ever be an instance where the server is not the authority for an actor that exists on it, but again the authority system isn't just for server-authoritative networking and would work with p2p if you provided a driver for it.
I'm pretty sure it creates the same game model and just doesn't create a player
For the most part, yes.
how do I check if I'm already in a multiplayer game? ie, I have a widget that I want to display differently to Host, Client, Standalone.
GetNetMode
Sorry, I don't understand you. I read it in this order, so I answered Darkest.
could someone help me fix a problem where my damage is different on server and client like if I hit client on server it dose less but if I hit server on client it dose more thank you!! 🙂
iv done this in last project but decided to restart it. and im not sure why i keep getting blank for options string this time round. any thoughts...
serverlog
client call
server game mode.
It will return null where a PlayerController for this Player doesn't exist
Server and owning Client will have a valid Pointer. If you want to know if it's locally controlled you will still need to check that on the Server, cause with x players, the Server will have access to all x PlayerController. Only its own (ListenServer) will return true for IsLocalPlayerController
Also I noticed my chat didn't scroll apparently, so if this was already answered then sorry for the ping :P
The Options String is not for Player Connections
The Options String that players provide is not available in BPs
You'll need to post your damage logic. Otherwise probably no one will engage your question
Any clue as to why I would sometimes (but not always) get an access violation on the indicated line? FocusTarget is an AActor*
{
// Look at focus target.
const FVector EyeLocation = GetCharacter()->GetActorLocation() + DefaultCameraOffset;
---> const FVector ToFocusTarget = FocusTarget->GetActorLocation() - EyeLocation;
...
}```
No worries! The explanation helped solidify my understanding so thanks
Testing UObjects via if (Object) isn't enough
The Pointer can be nullptr, and you are guarding against that, but they can also be pendingkill (at least actors can)
And PendingKill is a valid pointer in a "soon to be destroyed" state that will also cause this
You should get used to using IsValid(Object)
That checks on PendingKill
Is there a way for UObjects to be invalid at the start of the life cycle? It seems to crash at the start of a multiplayer session or not at all
right, yeah
IsValid is a good habit anyway, thanks
this code is in the player controller btw and crash only seems to happen on client (not positive though since it's been sporadic)
There isn't much I can say about it. You'll need to catch it with your IDE connected and check the callstack etc.
when i do a spawn Actor on my gamemode and then possess it it is supposed to be spawn on every clients ?
Hello 🙂 i'm making this prototype-game about 2 mounts. And want to show you my wip-video =))
Developing in solo. Unreal 5.1 (very good version!) , using AdvancedSteamSessions plugin. My english is very poor, sry!
https://www.youtube.com/watch?v=y8jtRRzgBEQ
Игра про панд на шарах. Катаются и стреляют.
Сингл-плеер/сетевая на Unreal Engine 5.1
Донаты: https://new.donatepay.ru/@1067825
Блог разработки игры - https://vk.com/event217255452
Моя группа в контакте - https://vk.com/wuz_here
Дискорд - https://discord.com/invite/tTf8t5eQ
#UnrealEngine #UE5 #BluePrints #GameMechanics #CodeSnippet #Platformer...
is that is good ? like is this suppose to spawn a top down character on every clients and server ?
You should check HasAuthority and only spawn it on the server.
If the character is replicated then it will automatically spawn in on clients too but the server will own it.
is it on gamemode is it always on server no ?
Oh, yes if this is in the game mode, it's always on the server, no need to check HasAuthoeiry then 🙂
Hi all, Need some advice, im making a character creation system in my project and i have coded this for changing the mesh for the hair, on the local character it updates the skeletal mesh of the hair but it doesnt replicate the change for everyone in the server. What am i missing?
Is there a reason you're not just using the default pawn class option in the gamemode? This seems like a very backwards way to spawn a/the player character
because i saw someone doing that so i tried like that ahaha is there a better way to that ? atm
i have a lobby then when all player has join i do a server travel to the main game where i init all actor and then throw an event that spawn the "playersactors" on game mode
maybe my logic is not good but as a beginer i work as i understand ahahaha
Yes, there's definitely a better way to spawn a character for each player when they connect, give me a few mins and I'll find my BP version of my spawn system for you
Yeah Kamikaze has a point, you can just set the default pawn on the game mode and it will automatically spawn all characters for you. You just place PlayerStarts in the world and the default pawns spawn at the PlayerStarts
I imagine it's also possible that doing that on BeginPlay might not even work (the game mode is responsible for spawning the controllers and BeginPlay will almost always fire before the first controller is even set)
Not to mention that GetPlayerController with 0 will always return the first controller
I.E only the first player will get their character
In multiplayer things are weird, like the characters spawn in and may not even have a valid player state/controller for the first few frames... but at least the game mode will get around to connecting them eventually, and player controllers will end up possessing the pawns automagically
If you're doing this in GameMode, there is "Handling Starting New Player" event in the game mode which if I recall right is normally where the character is spawned. It gives a controller reference you can use from the event as well.
You want to avoid using "Get Player Controller" unless you know for sure you're running something locally as you won't have the appropriate reference to which player controller on the server.