#multiplayer
1 messages · Page 261 of 1
If I want to have multiple walking speeds, for example lets just say 10, is using a replicated variable to indicate which walking state is selected the best way? Or is this something that you would want in the saved move? The concern is having lots of corrections when switching walking speeds.
first thing you should start from making custom character movement class a PredictedMovementSpeed variable that will be updated by character
void UArmaMovementComponent::PhysWalking(float deltaTime, int32 Iterations)
{
Super::PhysWalking(deltaTime, Iterations);
if (MaxWalkSpeed != PredictedMovementSpeed)
{
MaxWalkSpeed = PredictedMovementSpeed;
}
}
using this method, you will get a very smooth transition between different speeds
depends how drastic these movement speeds are
but ideally you would want to pack them into a saved move
Is PredictedMovementSpeed just a replicated float? I was going to override GetMaxSpeed()
private:
float PredictedMovementSpeed;
not even a uproperty
Ok
and you just update it using multicast simply by character
void AArmaCharacterBase::MulticastManageMovementSpeed_Implementation(const float NewPredictedMovementSpeed)
{
if (IsValid(CustomMovementComponent))
{
CustomMovementComponent->SetPredictedMovementSpeed(NewPredictedMovementSpeed);
}
}
phys walking will handle everything else
Thanks I will keep this in mind. I am probably going to the saved move route as it fits better for my project.
yeah saved move route is perfect, I also have plans to update someday but now i'm coding the inventory component and don't want to open another task before completing the inventory component
That's a good way to be most of the time, sometimes it can be hard to get back up to speed on complex tasks if you swap them out for something else - in the context of programming at least.
@candid star you know about the bits?
AllowedObjects.ClearAllBits();
if (bIsConnectionRelevant)
{
AllowedObjects.SetAllBits();
}``` not sure i fully understand how these bits work or what im supposed to be setting
don't do the multicast
Speed is a state
also multicast makes no sense
just look at ForAllSetBits or whatever
hold on
You might really do well reading the docs page once. That already offers a lot of info, surprisingly so.
Also actively accessing objects in the filters etc is not gonna be a thing I would recommend
It's written like this to specifically avoid the cost of accessing random memory.
I assume your original function was in the IsNetRelevantFor function. It might be better if you just disallow the specific actor to the connection by hand.
Docs page should show you how that works
what you can do is make your own storage in the filter for your game data
instead of pointer chasing 4 times to the controller
Doesn't that need an engine mod then though?
I assume Kaos is sending an RPC to change the world generation state that he's checking. Might as well just set the actor allowed for a given connection in there.
Or if it's multiple actors , add them all to a group filter "Disallowed While Generating" and remove the connection from that group filter once they are done loading.
I have an actor for each player that spawns projectiles. Then projectile checks collision then tells the server to do damage (co-op). How can I handle that? I want projectiles client side because theres going to be hundreds so don't want them replicated. But once it's hit I only want to tell the server to deal damage once
your filter can have whatever the hell you want in it
the fact iris just gives you IDs is intentional to make it fast
but if you only have a few things in it there's nothing wrong with just going the slow way to bounce around to the uobject owner
UNetObjectGridFilter::Filter for example
It's been a while since I looked at this, but aren't those filters restricted to one per actor?
Can I use an is server? Because the host is technically the server so his version only will be the one to tell it to do damage
I would still suggest the other solutions given 90% of the time Kaos will be polling the state of the generation for no reason. The RPC he most likely has already offers a good entry point to stop the filtering manually.
You could, yeah
Checking NetMode is probably a good way too.
But in Blueprints that's probably hidden behind IsServer anyway
Sweet ty. Just trying to keep performance in mind as I go along. Could be 500+ projectiles at a time
Yeah i only use blueprints
they might be bound to each filter only handling each root object the same way
but that's not really a big deal imo as you can just do whatever you want with a function
not sure
there's really nothing limiting in my mind about the network ref handle unless it's difficult to access the filter from outside (or when the object is created)
I only ever saw epic use the additionally cached transforms within the filters. No other data. If at all you'd cache your own data similarly (which is probably what you want to do?).
But since this whole thing here is super event driven, I don't even see the need for a complex filter as opposed to a simple exclusion group
yeah exactly... just store the information in the most simple form and change it when you need to
and if that's annoying you can just resolve the network ref to the pointer and oh well
I have seen prioritizers more than filters
personally
I think the built in ones will probably cover a lot already though
I'm pretty sure we only have one of each per Actor. And the rest are group and connection filters.
The grid filter one and the first person prioritizer or whatever it is called.
that said I will not blame people for being confused by Iris stuff
because it's pretty much all new containers
Rest are exclusion groups for teams and what not
Yeah
The biggest confusion for me was the concept of the exclusion filters.
I say this once a week but oh my god, Iris serializers are just too much
let people make the scuffed overload for simple stuff imo
I like the idea of the serializer living in a distinct file from the type in some ways
but it's way too tryhard for most projects even though I enjoy that they are focused on the speediest setup possible
Yeah, 100 player project somewhere benefits from it but even then Iris is still too slow
But they are actively improving it. So we shall see.
when i play as host, why cant i see BP_ThirdpersonCharacter in the outliner at runtime? but if i play as client i can
that's because the outliner can change which world it shows
you can click the gear in to top to change the world it's showing
sweet thankyou
I'm not really sure which one it uses by default in all cases
It was on auto and that was making it editor instead of server
just trying to work out how to test if my projectiles are working properly only client side now xD confusing tf out of me
was easy having them all on server side
i think its working properly, just dunno how to confirm it
Does that look kinda right? or is that making a double up?
i want each player to be spawning all the projectiles themself for visuals and less network bandwidth, but then the server is the only one that will actually deal damage when it hits
honestly just printing logs for things is all you can do sometimes with this kind of thing
and by that I mean the output log not on screen
and visual logger
even the print strings can confuse me with client / server at times xD
because the host is server too yeah?
magician xD
Listen Server would be a playing+rendering instance with authority
okay i think i have it right then how its setup
if the server spawns a projectile that isnt replicated, no one else will see it yeah?
yep, unless something else caused the client to do something similar (would still not be a replicated object but it could replicate just "play vfx here")
okay sweet, i think im gucci then, thanks mate
the server sends data about replicated objects relevant to the client (new objects sometimes) and rpcs to already replicated objects
ive never tried to make anything only client side before, so it was melting my brain a bit
just be aware the client can't replicate properties to others, only rpcs
so anything the client tells the server has to be in the form of an rpc
Yep thats all good, its purly for visuals, i just didnt want 1000 projectiles being replicated bogging down the network
What's the smartest way to have an RPC enabled actor per connection, which doesn't need any of the existing game framework classes to spawn it? So basically no GameMode, PlayerController etc allowed.
no playercontroller component?
you might underestimate how early the player controller appears
That counts as involving game framework classes.
is the reason to avoid a dependency or just because
I could spawn it runtime and add it to the PC but then I would need a callback for when the PC is there, which I then could use to just spawn the actor to begin with
Dependencies
as for that I think I guess I would say try mimicing how the player controller is replicated
It's a plugin and I need the actor to transfer debug data that only exists on server
I personally do not see the big value in dodging the player controller component here though
you could just have some interface in another module
it doesn't need to gaf about what a playercontroller is, it could be a replicated subobject of it though
I want to avoid any modifications to other modules because this is just debug related.
Cause I need any form of callback for when to spawn that actor
There is no global PlayerController spawned callback afaik
Maybe the NetDriver has some for connection created or so
global viewtarget delegate for the first viewtarget change could do it
That could work yeah
I assume they spawn the GameplayDebugger Actor through the PC or so.
But maybe not. I might check how that thing is spawned
it just ticks lol
when it sees a new playercontroller in the iterator it makes a new one
LOL
this is actually probably easier
because view target delegates can be treacherous
view target delegates can trigger ANY time in the frame and even on other threads
Loops over a 100 PC sadly but well
why? because CheckViewTarget changes view targets
Hm yeah it would only be a hack
The view target one
I will def check the NetDriver
Just out of curiosity
Generic UObject or AActor created callback could also work. But also feels nasty
a tick would cost less than a universal cost to each new object
hell, you can even tick the new controller checking every half second
unless you have like 2000 controllers to iterate
Na. 100 max
Maybe I can use a bit array
To mark connections based on Iris or so.
To keep track of new ones.
Something something
I'm not sure if there's a new connection callback
I can look
that said iterating the iris connections will probably beat random pointers?
tbh I would not be too concerned with the cost of a debug replicating thing
unless it physically makes it harder to even test
I think you don't need to tryhard this for a first attempt
Yeah was thinking I could check for a bit array of connections from Iris and then use a second local bit array and all bits that are not on the local one but on the iris one are new and other way rounf
friend UE::Net::Private::FReplicationSystemImpl;
FConnectionAddedDelegate ConnectionAddedDelegate;
🤯
But all that improves is memory access, at which point you are right and it's a bit much for debug stuff
Shall try
also no idea if this works as I assume
you might still need to gather existing ones if your registration is late
Yeah, it's a dedicated server that is usually fully spun up before connections come in, but it's still wise to have an initial loop. Even just for editor
:D They aren't exported.
Ah, nvm, they work anyway. Was using the wrong module in the build.cs
@thin stratus you mention group filters per connection, but where do you even set this or call this? at what point in the flow? cause the doc's don't even say where you should call such function just shows code
I am running a dedicated server and running into an error where players who are late joining end up triggering the other connected players to disconnect/reconnect
I am getting this "actor channel failed" kind of error, Lion from multiplayscape suggested it might be because of something we are doing on begin play of the player controller or post login of the player controller
[2025.07.03-18.43.58:514][323]LogNet: Actor channel failed: [UActorChannel] Actor: GameplayDebuggerCategoryReplicator /Game/Clonk/Maps/Hubs/LVL_ClonkHub_Island.LVL_ClonkHub_Island:PersistentLevel.GameplayDebuggerCategoryReplicator_2147482212, Role: 3, RemoteRole: 1 [UChannel] ChIndex: 14, Closing: 0 [UNetConnection] RemoteAddr: 31.94.4.55:54179, Name: IpConnection_2147482250, Driver: Name:GameNetDriver Def:GameNetDriver IpNetDriver_2147482459, IsServer: YES, PC: ClonkBR_Player_Controller_ARnBox_C_2147482233, Owner: ClonkBR_Player_Controller_ARnBox_C_2147482233, UniqueId: Null:localhost-7A7450F4113E48BABDCD52F45FB2502F
Different targets?
On our postlogin event currently, we have been passing in a player session ID and attempting to do AcceptPlayerSession and also do a REST API call with VARest
I am wondering if these are not valid things to put in the post login flow and that ends up blocking the server or causing an issue, I'm building a development client build to try to see logs there of what happens for the disconnecting/reconnecting clients but if anyone has any other tips on how to diagnose this please let me know
you blocking the gamethread for extended times?
also strange it says GameplayDebuggerCategoryReplicator
I tried taking out all the delays I had but the blueprint nodes I am using on the postlogin are ones that would be blocking calls as they have call backs. I am using the multiplayscape plugin to do a gamelift call to accept PlayerSession
when I searched I found one forum post that suggested I might want to fast spawn someone into a spectator pawn, then do the accept player session and then transfer them into the real game pawn if that works, so I don't block the post login flow.
But it's only an issue for the already connected clients that are getting disconnected/reconnected, not the new person coming in.
that is odd
are they both the same builds
client and server?
and all clients
ie all using shipping?
or development?
we had a bug back in 4.27 where it would try to spawn it but some clients didnt have that actor
so they would get kicked out
which looks like what is happening here
Is it one Actor or multiple that you want to stop from replicating until the Client tells you it's ready?
server would spawn the replicator, other clients would not have the replicator
Hmmm FastArrayyReplication order isn't guaranteed between server and client. So how are we suppose to use this for lets say inventory items?
i think the issue is a bit more deep rooted for us atm, so i need to check some things, it seems like for some reason data is not replicating properly that should
for example we have a struct of initializer stuff for map generation but client seems to have no data even though server 100% set
(lives on a component on the game state)
we were running a shipping build for clients and development build for server, I am running a dev build for clients now to see if I can assess why they are getting disconnected
Well, once you know what exactly you want to do, lemme know.
i mean the current system the client and server should create a set of regioin points name stable (thats how it worked on the old system), on the old system the replicated property is working fine, client has the data and generates the point, on iris, client never has this data
so what would cause that struct to not send the data?
GeneratedMapData.SetPoints(NewPoints, MapSize);
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, GeneratedMapData, this);
GetOwner()->ForceNetUpdate();```
just odd
the OnRep doesnt even trigger
@thin stratus ok i solved those issues, but basically we have a bunch of actors called region point, the client and server both create locally, and they should be mapped (theres 100s of these). i need to not send any replciation to these if client has not generated its local side yet
i couldnt work out how to default them to a group, cause i can tell the server hey im ready for them, but how do i initially block them?
Hello, does it make a huge difference if I send the damage as a float or as an enum?
Because in my game, I handle damage by 5 stages
Not by numbers
Doesn't float have 4 bytes?
uint8 is 8 bits, 1 byte.
Perfect, do I save a noticeable amount of bandwidth by using uint8 instead of float?
You save 4 - 1 bytes.
Is that noticeable?
Assuming every second there are like 700 shots fired
You would save 3 * 700 bytes
2.1 kb
our reconnection issue ended up being something simpler we were able to find on our client logs
not sure why your worrying about it, i would worry if your sending a huge struct with tons of stuff thats not relevent
we are using a web socket system to initiate matchmaking and the web socket was left open, so when the second client went to join the server, the original client was getting web socket messages coming in telling it to also join the server
oof
2.1 kB is 2.1 kB I guess
Thank you
It's all contextual is my point, optimize when you can and just be aware in context, no point having a custom network serialized struct to optimize something that gets sent once upon the start of the game and that's it vs something actually worth optimizing that is sent every few ms (like firing a weapon or movement etc)
Thanks
The thing is, if you create a Group and add it as an exclusion group, it will disallowed all connectiosn already.
Usually the best approach is to have a CMC child class and to have a list of overlapping black holes (or something more generic that the black holes use that you can reuse for something else in the future). And in the CMC child you can then calculate the velocity of the player based on whatever the super call gives you from the input of the player and then on top of that a velocity calculated from the overlapping black holes.
Doesn't even need to be further networked as running into and out of the black holes will already happen in sync for the predicting client and the server. It would be enough if server and client handle the list of overlaps each on their own.
Fwiw, if you can't use C++ (which would be quite bad if you are working on a multiplayer project), you can try to set the Velocity variable of the CMC on tick in your character. That would replace having a child CMC class and overriding the calc velocity function. Keeping track of what you overlap would remain the same
Would want to limit that tick stuff to auto proxy and authority though I believe
My implementation is just add movement delta to CMC.
Undefined property BlueprintAuthorityOnly ?
UPROPERTY (BlueprintAuthorityOnly, Event, Private, BlueprintEvent)
Doesn't exist for UPROPERTY.
Glad I could help :P
Nice. Don't look at how expensive it is in the traces :D
Ah yeah, guess depends on the game.
On the server
100 players with a shit ton of stuff going on vs 6 player game.
Yeah, but their fixes and improvements only slowly get to us.
I wonder if they ever multi thread part of Iris.
Oh we fixed this in fortnite by doing x and y
There are some good candidates for work thread stuff
Well fix the underlying system ?
Yeah
I think like you said performance gains is based on what you do
hmm, so it only available in the detail panel... hmm
If you don't use dormancy and stuff it will just still eat into your costs
No, it's a function thing, not a property one.
The cost I'm talking about is the Quantization and Replication itself.
On top of some silly things they did for tagging what needs to be considered.
Hmm
E.g. you know what PushBased does in Iris?
It just marks the whole Actor to be checked
I assume it sets a dirty flag
Or a bit
Eek
Not just that single property б
But the entire actor which I assume makes sense
And when you mark a SubObject dirty, guess what it will do?
Yeah nark the owning actor dirty
Mark the Owner Dirty and the Owner will cause all Subobjects to be checked.
Oof
Yeah. I think that part they improved by now.
But there are some really lovely things in there. If you check this channel for Nick Darnell's and mine conversation, you should see what I mean.
I keep switching to Cyrillic keyboard lol. Joys of living in bulgaria haha. Still learning bulgarian
I don't actually know, but I think so.
Yeah it does seem to be better for us but I will need to do more profiles
Ah we are lucky we don't spawn many replicated actors during gameplay
Probably in entire game about 200 in an hour
There were also some fun bugs in the prioritizer, which they fixed nowadays, but a 2 versions or so ago, they had a bug in the prioritizer where it calculates the distance between 2 points but only used the Direction * Distance for one of them forgetting to add the Origin to it :D
Which means everything was prioritized around ~0,0,0
Oof
I'm looking at the player state prioritizer
Thinking I could make something for us to prioritise certain actors in our world
Yeah we only noticed cause my wife added some debugging to list all the priorities and they made 0 sense. So we combed through the code (which is quite annoying to read, cause they handle 4 objects/locations/etc. at once) and suddenly found that math error.
Basically we have a bunch of worldmapactors that are active but don't always need to update unless they are dirty
Can't use dormancy cause that kinda breaks a few things atm
Keep mind, afaik, each Actor Class can only have one Prioritizer at a time.
But we don't want to them to all go the same net frame if not needed
Or whatever
I see playerstate one has a round Robin thing
Howdy, what does the SessionName FName that you need to provide to a lot of the OnlineSubsystem functions really mean? Does it need to be unique on the online backend, or can it be a hardcoded name that's the same on every client?
I figured that you use it to lookup and modify the session instance locally, of course. But I'm wondering if there will be a problem if two clients are hosting their game via Steam, and use the same FName SessionName internally
There are only two options in theory.
REGISTER_NAME(287,GameSession)
REGISTER_NAME(288,PartySession)
Either you pass in NAME_GameSession, which you'll 99% of a time, or you pass NAME_PartySession, which is very specific to some of the Subsystems.
If you use Beacons with Steam to do a lobby in the mainmenu (to queue up for games or so) you'd for example use the Party one.
But that's a lot more advanced in theory.
Ah huh, interesting. I've been just passing my own name all the time. But that's easy, I can switch to NAME_GameSession
Na, this isn't meant to be used for custom names.
Why isn't it an enum then? 🤔
Cause it can potentially be something totally different based on your Subsystem.
You can also code your own, which might need more than 2 options.
It's used by the backend to know what kind of Session you want to host.
If you want to give your Session a name and display that in a list of Sessions, you'd usually use the additional Server Settings you can pass along.
How much do FastArrays dislike being altered very often?
if it's somthing that changes every frame you aren't saving many bits from them sending the changed indices
but if only some of a large set change it will still beat the entire thing sending?
So I heard fast arrays are great for things that don't often and ofc you get the benefit of easier callbacks for changes om the client side. If it's something updating super often like once a frame or it can be more overhead over a normal array. But again this is also dependent on how much data actually changes and how big the array is. You won't see benefits till your array is a big enough size. If your talking the replication size then if the data the array not sure that matter cause I'm pretty sure the normal array serializing doesn't send everything? Might be wrong. Ut I thought it can reduce the size by know what was changed but probably not cause you might want to just change the order in the array.
iirc when you edit a normal array, EVERYTHING past the modified index gets sent
so if you have 500 entries and edited at 110, everything from 110 to 500 will be sent
and oc on the other side you have to iterate the 500 entries to check if anything changed
so i guess it depends on how MANY entries you think you will have, as well as how big EACH entry will be
(for the update to much thing i dont remember)
Iris can delta array properties by default at least (still doesn't have per-element callbacks though so fast arrays are still useful)
How can I decrease the jitter on clients when using the CMC's Add Force?
either turn off corrections or make the force done in a way that is part of the cmc moves instead of externally
How can I make my mouse work with my player controller on both my server and client while testing in-engine with a listening server? When I run the "Get mouse position" node on the server (using controller 0) it returns true, meaning it detects the mouse. But when I run the "Get mouse position" node on the client (still using controller 0 since locally it refers to its own controller) it returns false.
Here are the nodes (Also for added context in case it's useful, the player is in Game & UI mode when this is fired):
@lean oak the mouse must be on focus on the window.
if you are focusing on one machine, the other one will just return false.
also I don't see a point using the index here at all
where is the blueprint of the cropped picture at?
When I was running the code, I had been clicking on the client's window and controlling the character's movement and all that, so I had just assumed it was focused. If that's not the case how should I go about focusing the mouse onto the client window?
The blueprint is in a basic custom actor, not the player character, controller, or any of those. The mouse is being used to drag around a static mesh that's attached to the actor.
I thought of adding a vote kick feature in my p2p game and then realised it's pointless unless I have a rehosting mechanism, which is too complicated for me to introduce at this stage
The boolean return true when there is a valid mouse device.
When you dont focus on the window then there seems to be no valid mouse device.
You can probably replicate mouse x and y as variable instead getting it directly from the controller function.
If theres valid device, continue to set the replicated x and y
Otherwise dont set it.
How r you gonna make p2p game with unreal?
That requires you to build from the ground up.
There's no p2p if you use what unreal provide
Epic use server client model not p2p
It is architecturally very different.
oh idk, i heard different people call stuff different things
There is no server with p2p
like 'listen server' is not understood in some circles
Everyone connects with each other.
orite well its listen server then that was irrelevant to the point but ty
Listen server is just a server that also renders the world
well its both a client and a server
Listen server and dedicated = server client model.
P2p will be like old diablo game
Where if someone disconnect, game may continue
Because theres no server
my game happens to be setup in such a way that if a client disconnects other people can still carry on playing
but if host disconnects everyone is booted
Because it is server client model
yea sure
Client doesn't talk to other client directlt ever
but anyway it is funny trying to think about anti cheat when 1 player is just the server anyway lol
Thats only in p2p
its a casual game not hyper competitive
If you are using server client model and you are the server
Then you can freely do w.e you want and have that change reflected on others.
Afterall you are the authority
well yea if ur a nerd enough to want to hack it, i think there is some protections u can put in place
So preventing cheat when you give the ability for others to host is pointless imo
maybe i shoulda made it coop but i aint undoing 6 years work lol
but i knew from day one i wouldnt have leaderboard anything like that u need secure game for that to be meaningful
Why do you need rehosting mechanic for a vote kcik to happend?
You can have player rejoin the game
And kick anyone
Except the host
what
I was thinking along the lines of one player initiates vote kick -> all other players except vote kicked player see yes/no
and if everyone says yes then the voted kicked gets kicked
but its 2-4 players so i wouldnt have it in 2 player mode in case everyone just vote kick when they losing because butthurt
I dont allow rejoin once session is formed
Your game your rule
Well that node that saves the mouse position is old code, at some point earlier in the project it resolved an issue somehow and I kept it there, it doesn't actually mean anything. The problem is that I have an item that moves in the world under the mouse, I click this item in order to begin moving, at which point it should snap to my cursor, but instead it goes off to world origin because it can't find the screen position of the mouse, because it can't detect the mouse (despite detecting the mouse's click). So it sounds like I just need to figure out how to focus on one window or another, which I'm not entirely sure how to do beyond just clicking on it, but I'll look into it, thanks for your help
how to make a platform (interp movement) with fully client authoritive movement?
like it is spawned at server and replicated, and then the movement is completely smooth on client side (no need to be synced)
move it relative to a shared accurate server time locally instead of replicating its position
shared accurate server time?
@pallid mesa hey! Just wanted to let you know your article ^ has a broken link to Jambax's network event subsystem
The file is still there on this discord but Discord's new temporary links to attachments suck
^
thanks...
except I don't understand C++ so I guess I'll find another way
edit: I did that with exposed on spawn replicated variables
could be done with bp I think
isn't there something you can use from the player state
GetServerTime()
it's "good enough" for general purpose
yeah that would be a decent start
nah it's doable with custom net driver
Are quat4f serializers reduced quality or are they just packed really well?
hello! Im running into a weird issue where if a player disconnects (and the players pawn gets unpossessed), on the simulated proxies the player pawn is like moving very slightly
does anyone know of an issue like that?
the pawn looks like it's sliding
I think you can always expect less quality if you remove bits.
The only exception being that the data originally didn't need those bits.
not always
given a known range you can reduce a lot of waste
Yeah that's what I meant with the second sentence.
your average normal value is only using a small subset of possible numbers
If your Quat needed the whole 32 bits of each component, (for whatever reason), then you probably lost some precesion.
But quats are also not using very high floats numbers iirc.
the frustrating thing here is that since it's not just some floats (it must be a perfect normalized amount) things get weird
and lower float numbers actually use more bits given how much more precision there is at the end of the exponent etc
Hm. I can't even find the code for that thing
Only same macro driven implementations
Ah nvm
I'm not awake enough yet
Right, Iris serializer code.
Who doesn't love reading that
I made my own 1000th decimal serializer for floats as meters
They are quite straight forward after you've written one
Just a lot of boilerplate code
(some partial llm sludge to do the boilerplate stuff) but oh my god the static config is frustrating
if you mix up the order of boilerplate stuff it actually can never see the property
So. One thing they seem to do is to expect a Unit Quat
So they can ditch one component
yes the W is derived
which is interesting
I'm kind of doing something similar to Mover where I manually bodge all values to a simple representation
which is nothing new I guess but to me it is
if constexpr (sizeof(FloatType) == 4U)
{
if (Value.Flags & EQuantizedFlags::XIsNotZero)
{
Writer->WriteBits(Value.X, SignificandBitCount);
}
if (Value.Flags & EQuantizedFlags::YIsNotZero)
{
Writer->WriteBits(Value.Y, SignificandBitCount);
}
if (Value.Flags & EQuantizedFlags::ZIsNotZero)
{
Writer->WriteBits(Value.Z, SignificandBitCount);
}
}
static constexpr uint32 SignificandBitCount = (sizeof(FloatType) == 4U ? 23U : 52U);
that's to make it change if it's a float or double
Yeah I know, just sharing what it writes out
I wonder how bad it would be to just make these Euler angles
Hm. Well most peeps serialize Rotators to shorts or similar
Which def cuts precision
a short for rotation is plenty for lossy trasmition
for something that doesn't have a long arm
else
{
// All transmitted quaternions must be unit quaternions, in which case we can deduce the value of W.
if (!TempSource.IsNormalized())
{
TempSource.Normalize();
}
TempSource.X = FMath::Clamp(TempSource.X, -FloatType(1), FloatType(1));
TempSource.Y = FMath::Clamp(TempSource.Y, -FloatType(1), FloatType(1));
TempSource.Z = FMath::Clamp(TempSource.Z, -FloatType(1), FloatType(1));
}
TempValue.Flags |= EQuantizedFlags::XIsNegative*(FloatAsUint(TempSource.X) >> SignBitShiftAmount);
TempValue.Flags |= EQuantizedFlags::YIsNegative*(FloatAsUint(TempSource.Y) >> SignBitShiftAmount);
TempValue.Flags |= EQuantizedFlags::ZIsNegative*(FloatAsUint(TempSource.Z) >> SignBitShiftAmount);
TempValue.Flags |= EQuantizedFlags::WIsNegative*(FloatAsUint(TempSource.W) >> SignBitShiftAmount);
// Rebase the X, Y and Z components to end up in the range [1.0, 2.0], which allows us to not replicate the exponent
// except for a bit to differentiate between 1.0 and 2.0.
TempSource.X = FGenericPlatformMath::Abs(TempSource.X) + FloatType(1);
TempSource.Y = FGenericPlatformMath::Abs(TempSource.Y) + FloatType(1);
TempSource.Z = FGenericPlatformMath::Abs(TempSource.Z) + FloatType(1);
// If the value is 1.0 after rebasing then we denote that to avoid replicating the significand.
TempValue.Flags |= EQuantizedFlags::XIsNotZero*(FloatAsUint(TempSource.X) != FloatOneAsUint);
TempValue.Flags |= EQuantizedFlags::YIsNotZero*(FloatAsUint(TempSource.Y) != FloatOneAsUint);
TempValue.Flags |= EQuantizedFlags::ZIsNotZero*(FloatAsUint(TempSource.Z) != FloatOneAsUint);
TempValue.X = FloatAsUint(TempSource.X);
TempValue.Y = FloatAsUint(TempSource.Y);
TempValue.Z = FloatAsUint(TempSource.Z);
I think the middle part is what saves a lot of bits.
Exponent is like 8 bits per float or so?
1 is the sign and 23 for the mantisse or however you write that
I'm struggling with something else currently.
I have data that is being collected 30 times per second, but only on the Server.
And it has to be only on the Server, because it needs to be Transforms and Shapes of Actors on the Server.
Now I would love to be able to visualize those Transforms and Shapes more or less live on the Client, BUT, not only in the Editor.
Which means I can't just use things like VLogger or Rewind Debugger, as those either won't work at all outside the Editor or they can't be viewed outside the Editor.
I tried replicating the data outside of shipping, but the Actor the data is replicated through is throttled I guess.
I would love to have a standalone VLogger Window >.>
You want me to commit the same crimes NPP does?
network prediction uses this to cheese sending more things by gaslighting the net driver
tbh the default bandwidth limits are kinda low anyways
The data itself is probably not even too much, but the update rate is super low.
I assume it's potentially just throttled by Iris naturally.
Like, it's currently an Array of 30 entries. And it doesn't grow or shrink. All I do is write the next index and loop back to 0 when I reach 30.
enable delta serialation for the class
I had this wrapped into a FastArray with DeltaSerialization. Didn't change a thing.
That's why I think it's the update rate that is throttled
I'm honestly not even sure how to measure when throttling is happening
just raise the defaults
it's kind of crazy how there's basically no inidication throttling is happening
SetNetUpdateFrequency(60.f); That's how often per second it should update or not?
That's what the tooltip says at least.
I went through so many iterations (theoretical ones) by now. I even have a Replicator setup per Client by now (based on what we looked at yesterday), but that doesn't help with data that changes so damn often. I can use that for data that changes once in a while and then request it via RPCs.
I would love to have a Standalone Rewind Debugger + VLogger, but well.
Yeah
Or well, will be considered for replication
Yeah, will check if the PollFrequency of Iris is the problem.
God I need to get started on Iris for work and everything I'm reading makes me anxious
I'll probably get the hang of it but it seems like a steep climb to get there
Yeah, still not updating often enough. I guess I drop the idea of replicating this for now..
Have you tried increasing the MinNetUpdateFrequency
Yop
odd then
Takes 2 to 3 seconds for each update to arrive.
Or rather
Not each update. It also only represents the current set of 30 entries 2-3 seconds later.
are they being sent as full sized doubles?
honestly that seems insanely low given it's a single element
tbh, we replicate more data at once through Mover than this.
I would be curious if it's just not marked dirty
I removed pushbased from it for now
Cause that made no difference either
I will replicate a float now and see if that updates more often (and stop replicating the array)
Sanity check..
How much data are you trying to send
Well if we assume the DeltaSerialization and FastArray setup that I originally had worked, which had the same update rate problem, then a set of 20 FKAggregateGeom atm.
Which each a single Elem in it fwiw.
Some have a BoxElem, others a Capsule (Sphyl...) Elem
That's kinda it.
Might be big yeah
It's like, at max 20 vectors, 10 rotators and 60 floats. That's a lot, I get it, but the cycle at which I get the updates is just too "in rhythm" to be related to that.
What's holding the fast array
Just an Actor
Yeah no, it's not the array. I'm setting an int now during the same call where I update the array and I removed the DOREPLIFETIME of amd the array dispaly the int and it updates similarly slow.
server tickrate?
30 Hz, since I tied it to NPP TickRate.
which vectors is it using
try with netquantize vectors
I can't without writing my own type atm
And a simple int has the same issue
Yeah it must be replication rate related. I will have to solve this differently I guess and forget about allowing visualization of the looping buffer for now.
Replicating a set of data that changes 30 times per second is just not viable. I will fall back to the limit of recording things via VLogger and users will have to open it in the Editor if they want to analyze it.
GameplayDebugger Replicator does a lot of extra work to manually delta serialize the data and also keep track of Acknowledge packages etc.
I could try and replicate it through NPP itself, by turning this stuff into a NPP Component, but I would rather not.
- that also doesn't replicate every frame for SimProxy stuff anyway.
you could probably help split the work up by having multiple net objects for broad things
but separate properties should have changemasks fine
did you enable delta serialization?
Yeah, already tried that within the FastArray
no
that is not delta serialization in iris (even though it does send deltas for a fast array)
Again, the Integer alone is already not replicating often enough
It updates more often, but I can never be sure to get every frame.
also like I said raise the bandwidth limits
default engine
[/Script/IrisCore.ObjectReplicationBridgeConfig]
; Classes for which deltacompression is enabled
+DeltaCompressionConfigs=(ClassName=/Script/MyModule.MyClass))
[/Script/Engine.Player]
; These numbers should match TotalNetBandwidth
ConfiguredInternetSpeed=200000000
ConfiguredLanSpeed=200000000
default game
[/Script/Engine.GameNetworkManager]
; Increase from the base bandwidth, numbers need to match ConfiguredInternetSpeed in DefaultEngine
TotalNetBandwidth=200000000
MaxDynamicBandwidth=200000000
MinDynamicBandwidth=200000
Anyone know how I can save my layout for PIE windows? I am over every... single... time... I have a few pie windows needing to drag them all into the optimal spot, make them larger etc
Can't you just use the Save Layout stuff under Window?
Or does that only apply to the Editor itself?
That has never worked for me like it should, almost always resets or re-organizes tabs, I dont think that works for pie stuff
Hm ok
We do have an internal change that allows PIE to play in the additional Viewports you can open.
As opposed to opening a new window for Client 2, 3 and 4.
But might not be what you want and needs an engine change.
ooh that sounds nice, so it just splits the main window?
A small one fwiw
I don't mind that at all
Well you can enable more Viewports under "Window" -> "Viewports". Up to 4 in total.
UE will use only Viewport 1 by default and open new PIE Windows for Clients 2 to 4 anyway.
But you can have it use the other Viewports too.
I just can't share the exact code. I can only give you tips where to change things.
Cause client code base.
Tips would be appreciated, whatever you can share within reason (if youre not too busy)
That sounds like a massive QOL
Inside of UEditorEngine::CreateInnerProcessPIEGameInstance there is a point where it checks if (InParams.DestinationSlateViewport.Get(nullptr).IsValid()). That is valid if you select "Play In Viewport" (as opposed to New Window).
In there, it usually does this:
SlatePlayInEditorSession.DestinationSlateViewport = InParams.DestinationSlateViewport.GetValue();
// Only one PIE Instance can live in a given viewport, so we'll null it out so that we create
// windows instead for the remaining clients.
InParams.DestinationSlateViewport = nullptr;
Which causes Clients 2 to 4 to get new Windows.
What you would need to do is to get the LevelEditor Module, and the ForegroundTabViewports of that.
And then you can map the InPIEInstanceIndex that you get from the CreateInnerProcessPIEGameInstance function to that array and use the given viewport for the SlatePlayInEditorSession.DestinationSlateViewport.
Appreciated greatly
Just need to make sure the index is valid, cause it will only use viewports you also have visibly added.
If you only have 1 viewport, it still needs to create windows for the others.
It's like 5 lines of code if we don't count {}
Maybe a few more if you want to wrap that behind a boolean you may add to UEditorExperimentalSettings to be able to turn it on/off
You will need to expose the ForegroundTabViewports from within the LevelEditor.h
That's potentially the bigger part of the change, but I'm sure you can figure out how to return an array of TSharedPtr<IAssetViewport> from there, by grabbing the LevelEditorInstancePtr and collecting all Viewports that are ForegroundTabs.
And that's kinda it. Overall something Epic could easily support.
Yeah it sounds simple enough, I'll make some food an get started on it, appreciate the tips
@modest crater fwiw, there is code that should make the Windows not reposition constantly.
UEditorEngine::GetWindowSizeAndPositionForInstanceIndex
// Now we can position the window. If it is the first window, we can respect the center window flag.
if (InWorldContext.bIsPrimaryPIEInstance)
{
// Center window if CenterNewWindow checked or if NewWindowPosition is FIntPoint::NoneValue (-1,-1)
if (InEditorPlaySettings.CenterNewWindow || InEditorPlaySettings.NewWindowPosition == FIntPoint::NoneValue)
{
// We don't store the last window position in this case, because we want additional windows
// to open starting at the top left of the monitor.
OutPosition.X = FMath::RoundToInt((DisplaySize.X / 2.f) - (OutSize.X / 2));
OutPosition.Y = FMath::RoundToInt((DisplaySize.Y / 2.f) - (OutSize.Y / 2));
}
else
{
OutPosition = InEditorPlaySettings.NewWindowPosition;
}
}
else
else
{
if (InViewportIndex < PlayInEditorSessionInfo->CachedWindowInfo.Num())
{
OutPosition = PlayInEditorSessionInfo->CachedWindowInfo[InViewportIndex].Position;
FitWindowPositionToWorkArea(OutPosition, OutSize, WindowBorderSize);
}
// Add a new entry.
else
{
// We bump the position to go to the right, and the clamp will auto-wrap it for us if it falls off screen.
OutPosition = PlayInEditorSessionInfo->LastOpenedWindowInfo.Position + FIntPoint(PlayInEditorSessionInfo->LastOpenedWindowInfo.Size.X, 0);
// We're opening multiple windows. We're going to calculate a new position (opening them
FitWindowPositionToWorkArea(OutPosition, OutSize, WindowBorderSize);
// Store this position as the 'last opened' position. This means additional windows will start to
// the right of this, unless they run out of room at which point they'll start over on the next row
PlayInEditorSessionInfo->LastOpenedWindowInfo.Size = OutSize;
PlayInEditorSessionInfo->LastOpenedWindowInfo.Position = OutPosition;
}
}
There is supposedly CachedWindowInfo.
That code fails a lot though. I even have Windows opening on top of each other..
They should honestly just expose the NewWindowPosition stuff for the other windows too.
What is a "creep"? Like the MOBA term for those things you last hit to gain currency?
What class are they?
Yeah, cause you throw away all the smoothing the CMC gives you.
Are you able to use C++?
Honestly, I would suggest you write your own, simplified, UCreepMovementComponent, inherting from UPawnMovementComponent and handle Interpolation/Smoothing there by hand.
Just enabling Replicate Movement will do nothing good for you, as that just replicates the Location and Rotation and applies it, without any smoothing applied.
UE only has a handful of things that have smoothing built in.
The CMC, The PMC (Projectile Movement Component) and, if you want to count it, NPP/Mover.
You could opt into using NPP and Mover for just the Creeps fwiw.
But, I usually suggest to stay away from NPP and Mover for a few more months, as it's still being developed and Epic is pushing harded for Chaos Mover than NPP Mover.
At least for Multiplayer.
I would suggest using the CMC for now then.
If you, in the future, notice that the CMC costs you dearly, you can still refactor it to make it cheaper.
I wouldn't get held up on that for now.
When you are probably the only one working on the project and Horde gives you 15 agents with over 500 cores to compile your 3.5k modules in a few seconds.
Yeah I modified PlayLevel.cpp and LevelEditorPlaySettings.h
Added that my new PIE windows just spawn in a grid.
Useful
There is more to horde than just that fwiw
But most of horde is tied to P4.
The UBA stuff is, however, funtional without P4, so everyone can set that up if they have Agents to help compiling.
No, the automated build stuff where you can kick off preflights etc. wouldn't work.
This is a customer's setup. Internally we just use UBA and for the rest I use the CICD stuff that comes with GitLab.
I see
For my own stuff I just rent a server and slapped Gitea on it
So far so good
I don't like having to pay for licenses on my own projects heh
Yeah GitLab is free so same here
Isn't it up to a certain amount of users?
Did that change or was I crazy a few years ago
I had a self hosted Gitlab CE instance and I'm pretty sure the limit applied? Could be wrong
Not to my knowledge.
rip
Is it a good idea to limit input triggering on the client until the current action is complete? Right now, the player can spam the mouse button.
If I implement this, something would need to be sent back to the client once the action is finished. However, I'm concerned that desynchronization could block the response entirely.
You usually wouldn't block that. Your Input RPCs should be Reliable and that's about it.
You can throttle the RPC itself locally if you want, by just not sending it if it has been sent recently.
will fix ty
should now be fixed thanks a lot @verbal ice
Is it normal to have huge amount of movement correction on client when the server window is on background in standalone game while using CMC's Add Force?
need help with steam sessions for UE 4.27
my game isn't always able to find steam sessions, only works about 10% of the time.
It works randomly when I relaunch the game, but works only once - if I rehost the lobby, can't find sessions again :(
incredibly frustrating, it would be awesome if I could get some help on this!
it sounds like you never destroy previously created session.
for a bandaid, you may just want to call destroy session before you host, before you search, and when you quit a game.
@golden merlin
I already do that actually
It works randomly sometimes when I relaunch the game, that's the issue
It's not like it always fails
relaunching the game will destroy the session. are you sure you are destroying it cleanly?
are you logging what is happening when finding sessions
as in do you find a session and if so at which point does the join fail?
you have to destroy session on both client and server btw
From what I understand if you put the window in the background it changes the framerate down to incredibly low fps, and since Add Force is done on tick, I'm assuming that the tick rate for the client is sending a bunch of movement requests but the server is ticking incredbly slow so the time differences are causing them to collide
Yes. Will that affect gameplay too much when I package the project?
I have no idea. On my dedicated server games I don't have a way to put the server in the background so I've never run into this issue
Thank you for your answer :)
Hi, I'm making a 3D third-person online (LAN) dueling game, and I'm having an issue with Xbox controller handling. If I launch the game with the controller already connected, it bugs out: the camera moves on its own, some buttons don't work or trigger the wrong actions, and the character constantly moves downward. This only happens when the controller is already connected when I join or host the session. If I wait to connect the controller after I'm already in the game, everything works perfectly. Have you ever seen something like this before?
Make sure your InputMappingContext has some dead zones set up. The sticks are probably not 100% zerod.
Also what is Online LAN? LAN is by definition Local 😅
Yeah is on network is the same 🙄😆 juste a little smaller network...
Yeah he have a dead zone i have try to increase their min value but that change nothing
Hello! I can't figure this one out for some reason so going to ask here. I’ve got this replicated actor BP_PizzaStation, and I’m trying to do a client side camera/input setup when the player interacts with it. I don’t want to move all that logic into the PlayerController, because that just turns into a dumping ground for stuff from random actors.
I know it doesn't have to be replicated, but these are called by the server as all interactions are processed on the server. Maybe a multicast? Or maybe I could make the client the owner?
Any help would be great 
You could add a component with your new stuff to the player controller if that helps organize more
It can still have rpcs and replication
Yeah guess so, it works. Thanks
Would just setting the client as the new owner of the actor not be suitable?
You shouldnt do that unless you have to and personally I never have to do that to anything yet.
If input is processed on server, it can take a reference to the client that press the input.
If you want the client to get an answer from the server you can just do client rpc.
But it must be routed to actor that the client pressing input own.
So i would add component to the PC like megafunk suggest.
Setting ownership may work but there can only be one owner.
If multiple people required to interact with your pizza station then the game breaks.
hi folks, I need a bit of help. SetActorLocation isn't really working for the client. here's the setup:
client doesn't have authority to move the actor on the server. you need to RPC that you want to do the move to the server
is the position replicated?
yep, a replicated trace
im doing the RPC and calling it on the client for client-prediction. still doesn't work
you need to pass the location in the server function and set it in the setactorlocation after server call
I just found a huge issue with Mover 2.0 & Anim BP's. I'm wondering if anyone has already ran into this.
TL:DR Essentially, any logic that uses the last frame's data and compares it with the current frame's data will have really unexpected results.
For example Lyra's animation state machine uses "Displacement Speed" which is calculated by getting the distance between the last updated position and the current one.
Problem is when you have your fixed tick set too low (I've been testing on 15hz, 20hz, 30hz, 60hz) those updates don't happen as fast as the animation blueprint is checking for it. Meaning you will get displacement speed up somewhere in the high 2000's for a frame then every other frame that value will be 0 causing the animation to output to look really bad.
I've done some things to try and solve it. The only thing that even seems like it is working is only updating the values(like displacement speed etc) when the values they use in calculations change in the sync state but that opens up a whole new can of worms.
Any alternative solutions are extremely welcomed!
Sounds like you are using the SimulationState instead of the Interpolated PresentationState.
I am!
SimulationenState only updates every SimTick. For visual stuff that's not often enough. PresentationState is updated every GameTick and gets set to the Interpolated values between last and current SimulationState, based on how much time has gone by.
does the PresentationState always use a pair of sim tick states?
or does it extrapolate
Would need to check again. It's also slightly different between local client and SimProxy
I’ll spend some time in a bit swapping things over. Thanks
USTRUCT()
struct FPlaneRoute
{
GENERATED_BODY()
FVector StartPoint;
FVector EndPoint;
};
UPROPERTY(Replicated, BlueprintReadOnly, Category = "Plane")
TArray<FPlaneRoute> Routes;
error : Type 'TArray<FPlaneRoute>' is not supported by blueprint. Class: AArmaBoardingPlane Property: Routes 🤔
Missing BlueprintType on the struct
Super thanks, seems like i need to take some rest 😄
I use World->SeamlessTravel() while testing in UE5 PIE, and I get a crash on flags,
ServerTravel doesn't crash though
I tried following the execution and all I found was it is some sort of debug rendering doing it,
but IDK why rider, is not showing what the flags, or what is actually causing the exception
only happens for clients connecting to server
Means the object it's called on is invalid
i can't figure out what object it is trying to call it on, the stacktrace is not helpful, I can't tell where it is causing this issue
I am using GAS if that means anything
Probably had the GAS GameplayDebugger window open?
Okay I switched everything over and it seems to work fine. Values are more stable. I've tried it a couple ways. First I was just using the Presentation state as is and that gave me a similar issue to earlier where the values are coming out as 0 most of the time.
My current code looks like this
FMoverSyncState PreviousPresentationState;
GetCharacterMovementComponent()->GetBackendLiaisonComp()->ReadPresentationSyncState(CurrentPresentationState);
GetCharacterMovementComponent()->GetBackendLiaisonComp()->ReadPrevPresentationSyncState(PreviousPresentationState);
const FMoverDefaultSyncState* CurrentSyncState = CurrentPresentationState.SyncStateCollection.FindDataByType<FMoverDefaultSyncState>();
const FMoverDefaultSyncState* PreviousSyncState = PreviousPresentationState.SyncStateCollection.FindDataByType<FMoverDefaultSyncState>();
if (CurrentSyncState && PreviousSyncState)
{
DisplacementSinceLastUpdate = (CurrentSyncState->GetLocation_WorldSpace() - PreviousSyncState->GetLocation_WorldSpace()).Size2D();
WorldLocation = CurrentSyncState->GetLocation_WorldSpace();
DisplacementSpeed = UKismetMathLibrary::SafeDivide(DisplacementSinceLastUpdate, UNetworkPredictionWorldManager::ActiveInstance->GetDeltaTime());
}```
I was using delta time from the anim instance before I decided to use the delta time from the prediction world manager and the values were coming out extremely high similar to earliier so thats why I switched.
Values are really similar to what I was getting with my manual lerp but the animation looks odd. I probably messed something else up when trying to fix the problem earlier.
nop I double checked, its something like replicating or something
cachedReplicated is null
You need to send bigger screenshots. It's not helpful to only see the one line.
Above that part it should be checking if the CachedReplicator is nullptr and early return.
{
// this change is required for multi-client PIE, since even though every client has its own UWorld this OnDebugDraw
// gets called by a multicast-delegate - the same for all the clients.
CA_SUPPRESS(6011);
const FSceneInterface* Scene = (Canvas && Canvas->Canvas) ? Canvas->Canvas->GetScene() : nullptr;
if (Scene && Scene->GetWorld() != CachedReplicator->GetWorld())
{
return;
}
check(Canvas);
if (CachedReplicator && CachedReplicator->IsEnabled() && bDebugDrawEnabled)
{
FGameplayDebuggerCanvasContext CanvasContext(Canvas, HUDFont);```
5.4
Welp, that's an Engine bug then.
They placed code that uses CachedReplicator above the part where they ensure it's valid.
This is not how that looks like in 5.5+ anymore.
That was the fix commit later on. https://github.com/EpicGames/UnrealEngine/commit/5185820c7840c77e9689c1f1667b124e872c26d9
@dawn cargo I would suggest not using SeamlessTravel in the Editor then. It's still somewhat experimental anyway.
Start the game standalone or package it to test SeamlessTravel flow.
Or update your project to a new Engine version, if that's an option for you.
You shouldn't need to do that manually.
Lemme check how native Mover looks like again.
You can't just read the states via the Backend Component. That is something you should generally avoid.
Right, man native Mover is ugly.
that’s the only access to it as far as I could see.
But in UMoverComponent::FinalizeFrame it caches the Sync and Aux States.
FinalizeFrame is what SimProxies call from within the Interpolation/Smoothing services.
ReadPresentationSyncState and ReadPrevPresentationSyncState return to you the two states that it interpolates between.
It calls FinalizeFrame with the result of the interpolation.
So in theory you only have access to the properly interpolated value in there.
But they cache the state.
So just grab the Cached State and you are good.
Ah so the cached sync state should already be interpolated for the sim proxy.
Yes
I don't know how their AutoProxy smoothing works again
We have have a FinalizeSmoothingFrame method for that one.
We also cache the Simulation and the Presentation States, not just one of them.
It's also an absolute mindf*ck to understand when a given state in the View is valid.
Like, if you call those Read methods, when does it point to the current and when the last state?
The stuff it gets that data from is the "View", which just contains pointers to the States in the Buffer.
When a SimTick happens, it updates those pointers to point to the OUT States, so the stuff that will be produced.
So if you grab those states during SimTick or after, it will have the new data, but if you grab it before the SimTick, it still has old data.
So the best is to avoid touching those backend functions.
Actually, during SimTick it will have just "sh*t" in it, cause nothing is produced yet.
But you get the idea.
Ah they do have UMoverComponent::FinalizeSmoothingFrame.
You will want to also cache the State there, so your AutoProxy (if you use FixedTick) can have smoothed values too.
Yea that makes sense. That’s why my initial approach was just to use the simulation state since it’s cached. I didn’t know that finalize frame ran at regular frame rate for sim proxies
FinalizeFrame actually calls for everyone fwiw.
But SimProxies call it additionally when smoothing.
It's relatively important to understand NPP and its Services for this stuff.
So you know when what is being called and valid.
True. I’m not a traditional engineer so that code is still cryptic to me. Especially with all of the templated functions. I’ll have to take more time with it.
Understandable. It took me a lot of time to get behind all of that.
It's relatively save to start in the UNetworkPredictionWorldManager, in its StartNewSimulationFrame function and then just going down the code to see what is called when.
The different Simulation Components, like UMoverNetworkPredictionLiaisonComponent, are basically plugged into arrays of Simulations. Mover is one Simulation, so when it calls stuff like this:
for (const TUniquePtr<ILocalTickService>& Ptr : Services.FixedTick.Array)
{
Ptr->Tick(Step, ServiceStep);
}
All that actually does is call the Tick function on a ILocalTickService instance that was created for Mover.
So if you have another simulation, like some custom Ability System or what not, then that Array would have two entries.
But if you only have Mover, than that's just one. And if you then click on that ILocalTickService to follow it you usually end up in files called NetworkPredictionService_XYZ.inl (here XYZ would be Ticking). And while you will just end up at the interface class, you can scroll down to find the implementations, such as TLocalTickServiceBase.
That thing then uuuuusually has something called Instances (here InstancesToTick) which are the actual Components (so UMoverNetworkPredictionLiaisonComponent).
If you then scroll it a bit further in the Ticking one, you'll find Tick_Internal, where it loops over those InstancesToTick. It then accesses the overall DataStorage that holds basically everything regarding that Simulation (Mover). In this case it will grab the matching TInstanceData<ModelDef> and TInstanceFrameState<ModelDef> and call TTickUtil<ModelDef>::DoTick.
If you follow that, you'll find where they call View->UpdateView (which is what shifts the Pointers around that you get via those Read methods), and then ultimately will grab the Instance.Info.Simulation and call SimulationTick on that.
Instance.Info.Simulation in this case is the UMoverNetworkPredictionLiaisonComponent which got originally passed along when registering the UNetworkPredictionComponent.
Ultimately, the rest of NPP works the same.
- Some Array of Service Interfaces, where each entry is a Service instance that takes care of one Simulation.
- Each Service instance usually has an Array of Instances, so the
UNetworkPredictionComponentsof a given Simulation. - Each Service will then call some templated function on either the
Simulationor theDriver.- For Mover that's both the same
UMoverNetworkPredictionLiaisonComponent.
- For Mover that's both the same
With that you can get through most of NPP when reading through the code without getting lost.
E.g. the code that ultimately calls the ProduceInput stuff.
Inside UNetworkPredictionWorldManager::BeginNewSimulationFrame_Internal:
// - Some Array of Service Interfaces.
for (const TUniquePtr<IInputService>& Ptr : Services.FixedInputLocal.Array)
{
Ptr->ProduceInput(FixedTickState.FixedStepMS);
}
Actual implementation in the .inl file class TLocalInputService : public IInputService.
class TLocalInputService : public IInputService
{
// [...]
// - Array (in this case Map, I guess) of Instances of a given Simulation.
TSortedMap<int32, FInstance, TInlineAllocator<1>> InstanceMap;
// [...]
}
void ProduceInput(int32 DeltaTimeMS) final override
{
for (auto& MapIt : InstanceMap)
{
FInstance& Instance = MapIt.Value;
// [...]
// Service calls templated function on Driver.
FNetworkPredictionDriver<ModelDef>::ProduceInput(Instance.Driver, DeltaTimeMS, (InputType*)Instance.View->PendingInputCmd);
// [...]
}
}
@boreal wadi Hope that helps a bit getting started on learning this crap :D
Ya, it's a project of one developer that was never really battle tested or finished.
That's why we will most likely see Chaos Mover in the future as opposed to anything NPP related.
Why does server handles lerp different from client and how do I fix this? I am doing lerp (rotator) to rotate my character mesh to the grappling point. It's supposed to look like more rotated like in the first screenshot. But others see like second screenshot.
When I don't use lerp it's completely same as what client sees
traveling from lobby to game level is retriggering listen option which leads to instant disconnect?
[2025.07.06-11.37.28:650][599]LogSteamSocketsAPI: Error: SteamSockets API: Error Cannot create listen socket. Already have a listen socket on P2P vport 7777
[2025.07.06-11.37.28:650][599]LogNet: Warning: SteamSockets: InitListen failed to start listening on the Steam Network.
[2025.07.06-11.37.28:650][599]LogNet: Error: UEngine::BroadcastNetworkFailure: FailureType = NetDriverListenFailure, ErrorString = , Driver = Name:GameNetDriver Def:GameNetDriver SteamSocketsNetDriver_2147481771
[2025.07.06-11.37.28:650][599]LogNet: Warning: Network Failure: GameNetDriver[NetDriverListenFailure]:
any idea?
maybe you can try changing the Test_Level_WP with Test_Level_WP?listen
Did you enabled seamless travel?
listen shouldn't be there because it's already listen server in lobby
let me check seamless
pretty sure we had to always pass listen just checking our code now
The log is complaining that I'm trying to reinitialize the same socket because the lobby is created as a listen server.
oh yeah seamless travel was off in game mode
gonna rebuild it and let see
ok map is loaded, but it doesn't assing player controller and pawn to player 😄 😄 😄 what the hell is that
camera stuck 0 0 0
seamless travel preserves PC's right? so I guess I need to manually reassign
maybe it's because of you have one player start. I don't know if you have more but just saying
If you don't have, you can try adding another
i dont think it preserves the controller, cause different maps can have different controllers
like LobbyCOntroller or GameController
iirc it recreates all of it
thats why playerstat has CopyProperties, etc
playerstate*
@meager spade it preserves them by default
Persisting Actors across Seamless Travel
When using seamless travel, it's possible to carry over (persist) actors from the current level to the new one. This is useful for certain actors, like inventory items, players, etc.
By default, these actors will persist automatically:
The GameMode actor (server only)
Any actors further added via AGameModeBase::GetSeamlessTravelActorList
All Controllers that have a valid PlayerState (server only)
All PlayerControllers (server only)
All local PlayerControllers (server and client)
Any actors further added via APlayerController::GetSeamlessTravelActorList called on local PlayerControllers
PlayerControllers are only recreated on SeamlessTravel if the class doesn't match.
Which is why a lot of peeps run into the issue that their BeginPlay on the PlayerController doesn't call anymore when they travel to the next gameplay map.
Also you don't need to pass ?listen along with the ServerTravel.
@torpid crest GameMode has OnSwapPlayerControllers, which allows you to move data from old to new PlayerController when it actually changes.
it's the equivelant of CopyProperties for PlayerStates.
I use that usually to move lobby data over, such as what character a player selected.
hmm it works fine in editor but it doesn't work in build
Lobby and game level are two different PC classes
PostLogin doesn't call for SeamlessTravel
[2025.07.06-14.22.42:410][475]LogGameMode: >> GameMode::HandleSeamlessTravelPlayer: BP_Lobby_PC_C_2147482166
[2025.07.06-14.22.42:410][475]LogGameMode: << GameMode::HandleSeamlessTravelPlayer: BP_Game_PC_C_2147481974
this is controller swap right?
oh yeah
PostLogin only calls on the initial Login.
Well yes and no
Not for spawning.
Only for moving data over.
If you don't need any custom logic for the Pawn Class, just set it as the Default one in your Gameplay GameMode.
If you need a class per player, then use the overrides that the GameMode offers you.
There is one that is called something with Get Pawn Class for Player or so.
That gives you the PlayerController and lets you return a Pawn Class. So you can take whatever identifies your Pawn from the PlayerController (assuming you store it there and move it over with OnSwapPlayerControllers).
If you really need to spawn the Pawn by hand and possess it by hand, then PostLogin would be wrong, as that would only call if a Player joins while already being on the Gameplay level.
There is a function/event you can override that calls for both cases. Something with Generic Initialize Player or so.
ok let me try to build it like that 🙂
You also don't need to package to test this btw.
Depending on the Engine version, you can try to enable the experimental Seamless Travel support in the Editor (console variable), or you just start the game as standalone (e.g. via the uproject file -> Right click -> Launch Game), as that supports Seamless Travel too.
Packaging to test this would otherwise take ages in terms of iteration time.
any ideas?
Show code
It's runnning on event tick
I did replication stuff before but it didn't work as I expected
Hey all, to achieve a very high max concurrent players (100 to 200), should i try to use Iris or stick with Replication Graph?
And if yes is iris plug and play or do i need to write custom code?
Iris replaces rep graph
use regpraph if you don't use iris
I know, i'm asking if Iris is worth using over rep graph
well thats hard to say
iris is new and experimental
repgrah is old and more battle tested
what you choose depends
Iris has some issues still
I'm not a multiplayer expert, does Iris have documentation? And do you need to write a lot of custom code with it?
only whats on the site
https://dev.epicgames.com/documentation/en-us/unreal-engine/iris-replication-system-in-unreal-engine
How much faster is it than rep graph?
Like how much more players can i handle on the same server compared to rep graph?
I'd say build something smaller than 100-200 player count first.
With Iris? With the normal multiplayer thing i got a little combat game thing working
Did you release it?
Do you have real networking conditions?
Do you have a playerbase?
Yeah im aware having a playerbase is the main issue
If your game needs to have 100 people in a game to be viable, you should also have plenty of budget for marketing
Even with iris - there are still going to be standard challenges with CMC
Yup i just want to support up to 100-200 players, it can work with 8 online
I would say, regardless, use Iris as that is where all the dev focus is going. So future proofing and all that.
Hmm smart
Is there an iris sample game the same way theres a mass sample game?
If I add a variable like uint8 MyVar : 4; to the saved moves, will it send a full byte or just 4 bits?
it will be uint8
Ok thanks. I will just use a full byte and split it then
I want the ability to have different crouching heights, which affect movement speed. And also be able to change the walking speed. So I can split a byte in half. Top 4 bits can be used for walking speed and last 4 bits can be crouch height. That gives me 16 walking speeds and 16 crouch heights if I needed.
I have no clue about your game, but 200 players might either way not be possible.
Most UE games struggle to get 100 players going.
Performance of the Server will just go down the drain eventually.
Not even Iris helps there.
200 players is also not even replication-wise a problem. The GameThread will need to do too much for the Server to keep a stable 30+fps.
RepGraph and Iris help with that, but all the other stuff that can happen in a game is still gonna cost ms.
UE is just not really made for 200 players.
Would i have more luck with Unity or is 200 a wet dream?
Idk. I never really used Unity. I don't like their licensing model.
It can totally be that Unity's networking and overall Engine is cheaper and allows for more stuff to happen on the same server.
You can also look into replacements for UEs networking and use something that is meant to work for a lot more players.
How expensive your game is in the end is really depending on what you are even trying to do.
But going custom requires a lot of work and C++.
For most projects, 100 players is already a wet dream fwiw
I think it's possible but it would require being quite brutal with many networking things and the server would be struggling to maintain a decent framerate just due to the pure simulation
Iris would help polling maybe but it would not change game code
Unity has like 5 different netcode solutions so... I have no idea lol
I think there are games that have gotten 100~ or so unreal players going, it's not unheard of
Yeah. Just depends a lot on what they need. There is a good chance that one can't code carelessly when trying 100 player games. Things have to be done with performance in mind, almost always. And Blueprints are probably very much a no-go in a lot of cases. At least for code that runs on the hot path
A lot of native systems also might need performance specific changes. Or have stuff that could make them more performant hidden behind cvars and experimental flags.
We've just been through optimizing a 100 player game, and it's really not that easy if the game uses a lot of stuff from UE and has a lot going on.
That's why I can hardly imagine 200 players.
Where can I get help for unreal engine?
Pick a channel that matches the topic you need help with. If there is no matching one, ask in the general channels.
okay thanks
Is it better to replicate variables on the character versus making the CMC replicated?
Depends on their purpose. There is no real performance benefit to doing it on a specific one.
Ok. It is a sprint variable for simulated proxies. The video I am watching the guy does it on the CMC but I have also seen the variable be stored on the character.
Thats what I was going to do
keeps everything similar
i am really suprised theres no built in sprint
in cmc
i mean 90% of people put sprint in lol
if i want to make an inventory array with three slots where should i put all the data when a player is picking up an item so others see it and its deleted for them as well?
could you explain further? i don't get neither of these
When replicating components an additional uint8 is sent to identify the component that is replicating
CMC routes all replication and RPCs through the character to avoid this cost
You can grab sprint etc. from my repo (if you want) https://github.com/Vaei/PredictedMovement/
I love how they use WALK speed instead of MOVE speed
So now we have MaxWalkSpeedSprinting instead of MaxMoveSpeedSprinting or MaxSpeedSprinting
Walk is in every potential gait 😄
Its like they didn't think a character would ever do more than walk
That is actually the real, bonafide, factual reason Epic never added sprinting
Because someone used the term walking they couldn't be bothered trying to resolve this or figure it out
||This is a complete lie||
I don't blame them
MaxWalkSpeedRunning 
Y'know...
I could have just done MaxRunSpeed and MaxSprintSpeed..
Naming IS hard
Meh, we did MaxWalkSpeedSprinting
Yep
Looks like either someone had a stroke during naming or the naming is going to give someone a stroke, not sure which one
Probably both
Sounds about right
Yeah I was looking at that. All of your plugins are good resources to learn from
I fixed this problem by disabling network smoothing mode in character movement component
Are you using listen server
yes
There is no extrapolation for mesh smoothing on listen server
Epic never really completed listen server support for CMC
Read here
Reason: Fortnite doesn't use it
thank you!
Isn't there a cvar for it?
Dunno, I don't really use listen server myself, I just looked up the UDN response for someone in the past
Yes there is
Less distinct objects to poll for replication is slightly better
Having an extra uint8 appended to the bunch isnt going to break the bank was what I was getting at. But you are right, there is a benefit.
A novice isnt going to notice the difference.
it's not just a byte if it's a separate subobject for the bunch
but it is probably not going to be that much more data
the bandwidth is kinda not the issue here I guess
my extra hot take for character sim proxy data is that it's generally better to keep it in 1 atomic replicated property
multiple properties can arrive at random times
so sometimes it's worthwhile to pack into one struct for atomicity
which is more of a thing for very fast paced games where missing it could move something
for example replicating the fact your are crouched but the location of where you end up when crouched is later
instant jitter on sim proxies
I totally agree with you, I was giving my answer in context to the question the OP asked specifically. Deciding to have a single replicated property on the Actor or the Component is not going to have a material impact on performance.
if it was already replicated yeah... I guess I mean making something that wasn't replicated before replicating
why RPC functions must be usually as const ref?
UFUNCTION(Client, Reliable, BlueprintCallable, Category = "UI")
void NotifyOwner_Client(const FText& message);
why can't be const? 🤔
why would you want to copy the entire FText?
I don't, but I think I had some errors with copy of other types, actor ref maybe ? 🤔
its not that important.
Can I send text messages?
Like properly with const &, will it work on client ?
like, will it work properly and display translated messages on client? :d
UFUNCTION(Client, Reliable, BlueprintCallable, Category = "UI")
void NotifyOwner_Client(const FText& message);
virtual void NotifyOwner_Client_Implementation(const FText& message);
I am pretty sure translation keys are deterministic
if they are then this seems like it should be okay
fine, ill code the rest and check it, no ui yet or anything ;d
I would say you could just make your own dumb mapping of gameplay tags to messages
or fnames
if this doesn't work out
i feel like it would be better for that text to be already known by the client instead of sent by the server
there's no requirement for them to be const or a ref afaik
it's just faster to pass by ref
and if you don't plan on changing the value, make it const so you don't do something weird
I can't find an iris serializer for text
it appears to use a "last resort" byte buffer serializer
text over network sounds ripe for weird errors
like what do you do if the text isn't on the client
yeah I'm questioning doing this vs just gameplay tags
for me it's sending 700 bits for like "asdasd" lol
I just question sending text at all
it should probably already be known by the client
or if it's a runtime thing it needs to be a string anyway
Is there even a legit reason to send Text in an RPC, or even across the network to begin with. I cant really think of a reason?
I mean if they want to show X locallized message
I would say some kind of key the the text kinda makes sense
I just don't see how these work with exports internally
But as you both have pointed out, the logical approach would assume the Text is already on the Client.
I can't think of a reason the server should tell the client specifically what text to see
What dynamic element would require Text be sent across the network.
yeah, text internally is a pointer to the text
I dont think there is one
yeah I can't find anything obvious that says this is exported as a key
across the network
it would be better to send some id/name/tag
message saying "x event happened"
doesn't seem that weird but of course it makes more sense as a mapped id
The Event itself would surely be enough to know where to find the X locally though.
yeah
Or at least, via some more efficient context
The only thing that might come close is Chat Messages, but even then, they should just be strings.
is that even possible?
I mean, you can construct texts on the fly and put them into widgets that are not set up for localization.
But then you can just use an FString.
Why this Actor can't move smoothly? it does lots of corrections!
AArmaBoardingPlane::AArmaBoardingPlane()
{
bReplicates = true;
SetReplicatingMovement(true);
AirplaneMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("AirplaneMesh"));
SetRootComponent(AirplaneMesh);
PlaneSpeed = 1500.0f;
ProjectileMovement = CreateDefaultSubobject<UProjectileProjectileMovement>(TEXT("FlightMovementComp"));
ProjectileMovement->ProjectileGravityScale = 0.0f;
ProjectileMovement->bSweepCollision = false;
ProjectileMovement->InitialSpeed = PlaneSpeed;
ProjectileMovement->MaxSpeed = PlaneSpeed;
}
void AArmaBoardingPlane::BeginPlay()
{
Super::BeginPlay();
if (HasAuthority())
{
InitializeRoutes();
SelectRandomRoute();
}
}
void AArmaBoardingPlane::InitializeRoutes()
{
Routes.Add({ FVector(-346832.280026f, -300673.276454f, 200000.0f), FVector(963401.868591f, 1004940.795252f, 200000.0f) });
Routes.Add({ FVector(963401.868591f, 1004940.795252f, 200000.0f), FVector(-346832.280026f, -300673.276454f, 200000.0f) });
}
void AArmaBoardingPlane::SelectRandomRoute()
{
if (Routes.Num() == 0) return;
if (ProjectileMovement)
{
const uint8 RandomIndex = FMath::RandRange(0, Routes.Num() - 1);
CurrentStartPoint = Routes[RandomIndex].StartPoint;
CurrentEndPoint = Routes[RandomIndex].EndPoint;
SetActorLocation(CurrentStartPoint);
SetActorRotation(FindLookAtRotation(CurrentStartPoint, CurrentEndPoint));
FVector Direction = (CurrentEndPoint - CurrentStartPoint).GetSafeNormal();
ProjectileMovement->Velocity = Direction * PlaneSpeed;
}
}
FRotator AArmaBoardingPlane::FindLookAtRotation(FVector Start, FVector Target)
{
FVector Direction = (Target - Start).GetSafeNormal();
return FRotationMatrix::MakeFromX(Direction).Rotator();
}
but on server it moves fine smoothly
DDo you enable ReplicateMovement ?
yes its in the second line in top
That is why, turn it off, for projectile movement actor, you should let projectile component move locally in client.
Has anybody had problems calling ServerTravel?
Doesnt seem to work. It works in editor, but on a shippiing Build no joy
i think i tried it and then the plane was moving only on server
let me try again
no difference
seems like i just have to use other approach maybe a timeline
Show me your setup.
i commented the line to enable movement replication
when i play as server everything is smooth
I can dig into source of unreal to find what is making this issue
but not worth it, so writed here maybe someone know the quick fix
Yeah, It won't work.
From your code to make it work, you need to run this code on client also:
SetActorLocation(CurrentStartPoint);
SetActorRotation(FindLookAtRotation(CurrentStartPoint, CurrentEndPoint));
FVector Direction = (CurrentEndPoint - CurrentStartPoint).GetSafeNormal();
ProjectileMovement->Velocity = Direction * PlaneSpeed;
Just replicate RandomIndex and setup based on it.
i can give it a quick test on multicast
void AArmaBoardingPlane::MultiStartFlightSimulation_Implementation(const FVector NewStartPoint, const FVector NewEndPoint)
{
SetActorLocation(NewStartPoint);
/** Set rotation towards the x Vector of the plane mesh*/
FVector Direction = (NewStartPoint - NewEndPoint).GetSafeNormal();
SetActorRotation(FRotationMatrix::MakeFromX(Direction).Rotator());
MovementComponent->Velocity = Direction * PlaneSpeed;
}
no luck
What exactly is 'consider actors time' and how can I improve it? This is costing me almost 4ms
From its name I wouldn't think it had anything to do with the actors' individual complexity/number of replicated properties and more to do with how many actors you have and whether the engine needs to calculate things like relevancy based on cull distance
But the bulk of replicated actors in my game are 'always relevant'
And that overrides cull distance right?
also cull distance is very high anyway
@nocturne quail You need to set up a bunch of stuff to make that smooth. The PMC has an Interpolated Component pointer for example. You are supposed to have a collision root component and a second non collision visual component, like a mesh. You then set the interpolated component on the PMC in post init components to the mesh.
There are also things like setting velocity of the PMC (iirc) via the virtual velocity replication function of your actor.
You might want to read through the PMC header file to get an idea.
There might also be the need to call something interpolation related on the PMC on tick
That's bad though?
Why is so much stuff always relevant?
its my rts units
Consider Actor Time is the time spent on checking if an Actor needs to replicate.
some are contained in hives and stored as data until hive is entered
Make sure you use push model whenever possible in c++.
Why are units that are off screen relevant?
If you need their data, store a simple version of it in a fast array on a single manager actor.
I guess they all get simulated on server anyway
You can't just make everything always relevant and then be surprised the consider time is high
But what's to consider? It is always relevant 🙃
Yeah thats what I thought
Super thanks, i will read it now
and I now tested with the timeline and it works very smoothly
in terms of optimization sticking with the timeline is a best choice right? since the plane actor will be spawning in the start of the match for few minutes and then will get destroyed
So, everybody else can do a server transfer?
somehow my lobby that spawns players broke and i havent changed anything 😢
If you can assure it deterministically flies on server and client the same way, then you can just run it on both without movement replication or even the PMC.
@quasi tide always relevant to the clients. Meaning it will always be considered for replication. Considering means it checks if it must replicate any changes.
The two concepts aren't really the same
Hm?
anyone have a spare second to help me try trouble shoot something? 3 players works fine, 4 players i get an error and it only spawns 3
Doubt.
4 platforms are there and 4 player controllers are there, but im getting these errors
Blueprint Runtime Error: "Accessed None trying to read (real) property CallFunc_Array_Get_Item_1 in not an UClass". Node: Branch Graph: IsValid Function: Update Players on Platforms Blueprint: GM_Lobby```
Just throw your info here, maybe into a 🧵