Is PostNetBeginPlay() called only when NetMode is LstnSrv?

Discussions about Coding and Scripting
Post Reply
User avatar
PrinceOfFunky
Godlike
Posts: 1200
Joined: Mon Aug 31, 2015 10:31 pm

Is PostNetBeginPlay() called only when NetMode is LstnSrv?

Post by PrinceOfFunky »

I read that the simulated function PostNetBeginPlay() is called after PostBeginPlay(), after variables first replication(or at least those who reached the client), but I don't want to handle any replicated variable in this function, I'm trying to get the local player and starting a simulated timer function(Disabled in a DedicatedServer) this way:

Code: Select all

simulated event PostNetBeginPlay() {
   local PlayerPawn pp;
   
	Log(Level.NetMode);
	foreach AllActors(class'PlayerPawn', pp)
		if (Viewport(pp.player) != None)
			localPlayer = pp;
	Log(localPlayer);
				
	setTimer(spawnInterval, true);
}
"localPlayer" is not replicated of course, fact is this event is not called unless when NetMode is NM_ListenServer. I thought it would have been called in all the ENetMode's except for NM_DedicatedServer.
I set PostBeginPlay() as simulated too, but I get the same result.
What am I doing wrong? :what:
"Your stuff is known to be buggy and unfinished/not properly tested"
nogardilaref
Masterful
Posts: 577
Joined: Tue Jun 20, 2017 1:00 pm
Personal rank: ⚋⚊⚌☰⚞⌖⚟☰⚌⚊⚋

Re: Is PostNetBeginPlay() called only when NetMode is LstnSr

Post by nogardilaref »

Testing anything on a listen server is always a bad idea.
Anything which works in dedicated, is certain to work in listen, however the other way around is not true, given that a listen server is simply the host being the server and client at the same time afaik, so the host player is always authoritative and no real replication occurs towards the host itself.

Therefore, it seems to be a simple case of your actor not getting replicated at all, hence why working in listen, but not on dedicated.
Hence the question: are you absolutely sure the actor you're testing is being replicated to the client at all?
User avatar
PrinceOfFunky
Godlike
Posts: 1200
Joined: Mon Aug 31, 2015 10:31 pm

Re: Is PostNetBeginPlay() called only when NetMode is LstnSr

Post by PrinceOfFunky »

nogardilaref wrote:Therefore, it seems to be a simple case of your actor not getting replicated at all, hence why working in listen, but not on dedicated.
Hence the question: are you absolutely sure the actor you're testing is being replicated to the client at all?
Well I'm not rly sure, I'm new into replication :ironic2:
defaultproperties look like it should be replicated:

Code: Select all

bAlwaysRelevant=True
   RemoteRole=ROLE_SimulatedProxy
   bNetTemporary=False
More replicated than this...
bAlwaysRelevant to true is already a replication security.
bNetTemporary is set to false so it won't stop replicating after the first replication occurs.
RemoteRole is set to ROLE_SimulatedProxy, so it should call PostNetBeginPlay() on clients, since it's simulated. PostBeginPlay() is actually called, all the other simulated functions are called, just not PostNetBeginPlay().
Well, I will find a way around this for this tme.
"Your stuff is known to be buggy and unfinished/not properly tested"
User avatar
sektor2111
Godlike
Posts: 6403
Joined: Sun May 09, 2010 6:15 pm
Location: On the roof.

Re: Is PostNetBeginPlay() called only when NetMode is LstnSr

Post by sektor2111 »

PostNetBeginPlay is called in clients joining in Net games - end of blabbering. I used that to setup a speed aligner for screwed Levels making a mess in classic setup of games. Cacher mutators uses that as well for setting up 2 states: one addressing client and other one addressing server - Higor was doing this first time and I've used that for other toys which I could develop later. Did I spoke about LOGS for figuring stuff? No ? My bad... :ironic2: .
The only problem of this function is "Packaging", if it's not located in a "serverpackages=", client will not even dream at it, yeah, a server-actor-only having that it's useless.
User avatar
PrinceOfFunky
Godlike
Posts: 1200
Joined: Mon Aug 31, 2015 10:31 pm

Re: Is PostNetBeginPlay() called only when NetMode is LstnSr

Post by PrinceOfFunky »

sektor2111 wrote:PostNetBeginPlay is called in clients joining in Net games - end of blabbering. I used that to setup a speed aligner for screwed Levels making a mess in classic setup of games. Cacher mutators uses that as well for setting up 2 states: one addressing client and other one addressing server - Higor was doing this first time and I've used that for other toys which I could develop later. Did I spoke about LOGS for figuring stuff? No ? My bad... :ironic2: .
The only problem of this function is "Packaging", if it's not located in a "serverpackages=", client will not even dream at it, yeah, a server-actor-only having that it's useless.
So there's this wonder now, is PostNegBeginPlay() called right after PostBeginPlay()/SetInitialState() or is it called in every Client when they log in the server? Cause if it's the first one, then it's normal that localPlayer is None when I connect to the server.
Also, I'm wondering if I'm actually part of the server and not a client if I log as a Client into a DedicatedServer using the same machine.
"Your stuff is known to be buggy and unfinished/not properly tested"
Higor
Godlike
Posts: 1866
Joined: Sun Mar 04, 2012 6:47 pm

Re: Is PostNetBeginPlay() called only when NetMode is LstnSr

Post by Higor »

That is bad code.
It's not guaranteed the actor will arrive to your client before your own player pawn.
Assuming it will is assuming the server is running unaltered v451 UT.
User avatar
PrinceOfFunky
Godlike
Posts: 1200
Joined: Mon Aug 31, 2015 10:31 pm

Re: Is PostNetBeginPlay() called only when NetMode is LstnSr

Post by PrinceOfFunky »

Higor wrote:That is bad code.
It's not guaranteed the actor will arrive to your client before your own player pawn.
Assuming it will is assuming the server is running unaltered v451 UT.
Woah true, replication is a total different perspective. I was thinking more like a player than a programmer I think, cause I was thinking the player was the client itself but of course it isn't, and of course knowing some native code(which I don't know) would have been of help to lay it right in the head.
I guess that to fix it I should make it look for the local player everytime I need it if the local player is still None, this check sounds like a waste of memory tho :what:
"Your stuff is known to be buggy and unfinished/not properly tested"
Higor
Godlike
Posts: 1866
Joined: Sun Mar 04, 2012 6:47 pm

Re: Is PostNetBeginPlay() called only when NetMode is LstnSr

Post by Higor »

There are 2 ways to handle this:

Code: Select all

    var PlayerPawn LocalPlayer;
A classic accessor, call it everytime:

Code: Select all

simulated function PlayerPawn GetLocalPlayer()
{
    local PlayerPawn P;
    if ( (Level.NetMode != NM_DedicatedServer) && (LocalPlayer == none) )
    {
        foreach AllActors ( class'PlayerPawn', P)
            if ( ViewPort(P.Player) != none )
            {
                LocalPlayer = P;
                break;
            }
    }
    return LocalPlayer;
}
Delayed caching, call the accessor once:

Code: Select all

simulated event SetInitialState()
{
	if ( Level.NetMode == NM_Client )
		InitialState = 'FindClient';
	Super.SetInitialState();
}

Code: Select all

simulated state FindClient
{
	ignores Tick;
Begin:
	Sleep(0); //Game never ticks on the client until the player is rendering the game
	GetLocalPlayer();
	GotoState(''); //Override this with another state if you need to chain them
}
User avatar
PrinceOfFunky
Godlike
Posts: 1200
Joined: Mon Aug 31, 2015 10:31 pm

Re: Is PostNetBeginPlay() called only when NetMode is LstnSr

Post by PrinceOfFunky »

Higor wrote:There are 2 ways to handle this:

Code: Select all

    var PlayerPawn LocalPlayer;
A classic accessor, call it everytime:

Code: Select all

simulated function PlayerPawn GetLocalPlayer()
{
    local PlayerPawn P;
    if ( (Level.NetMode != NM_DedicatedServer) && (LocalPlayer == none) )
    {
        foreach AllActors ( class'PlayerPawn', P)
            if ( ViewPort(P.Player) != none )
            {
                LocalPlayer = P;
                break;
            }
    }
    return LocalPlayer;
}
Delayed caching, call the accessor once:

Code: Select all

simulated event SetInitialState()
{
	if ( Level.NetMode == NM_Client )
		InitialState = 'FindClient';
	Super.SetInitialState();
}

Code: Select all

simulated state FindClient
{
	ignores Tick;
Begin:
	Sleep(0); //Game never ticks on the client until the player is rendering the game
	GetLocalPlayer();
	GotoState(''); //Override this with another state if you need to chain them
}
Well the second way is way better since the same check is not repeated everytime, didn't think about it, I usually never used states much.
"Your stuff is known to be buggy and unfinished/not properly tested"
Higor
Godlike
Posts: 1866
Joined: Sun Mar 04, 2012 6:47 pm

Re: Is PostNetBeginPlay() called only when NetMode is LstnSr

Post by Higor »

Also, another thing:

SpawnActor(...) as a func is the one and only method used to create an actor when not loading them from a level, and takes Location, Rotation, Owner as parameters.
It can optionally take another existing actor as template, but this is almost never used.
SpawnActor calls all spawn events during this function.
When PostNetBeginPlay() is called, the actor already has had it's initially replicated variables set.

LOGIC==>>>

#1 This means that whatever replication does to set an actor's initial variables, it can only set Location, Rotation, Owner during spawn.
#2 All other variables have to be set AFTER SpawnActor(...) finishes.
#3 This means PostNetBeginPlay() is called AFTER all other common spawn events.
#4 This also means that PostBeginPlay() is called BEFORE the additional variables are set.

STUFF NOT DEDUCTIBLE THAT NEEDS TESTING==>>>

Are location, rotation, owner set during SpawnActor by replication code? Test using PostBeginPlay()
What happens to Owner (or any other Actor reference) when said actor reference isn't present on the client?
(In short: you receive a Flak Cannon, but it's monster owner isn't on your client... yet)

Answer 1:
The 'initial replication' contains a fixed packet which is an encoded Location+Rotation, nothing related to owner.
This is used for spawn (i checked on newer UE's, should be the same on v436)

Answer 2:
Theoretically the server knows when said reference isn't present on your client and doesn't mark it as 'replicated', it will delay it's replication until the reference does exist.
There's a problem here, if the reference times out and becomes irrelevant, there's nothing in the server that marks the reference as 'not replicated', so this process only happens once.
(Player becomes irreleveant, his PlayerReplicationInfo loses owner, never regains it)
That's why when you're dealing with replicated actor references you have 2 cases:

You have control of the code of both actors:
Use PostNetBeginPlay() on both to cross-link them, see PlayerReplicationInfo.PostNetBeginPlay() and Pawn.PostNetBeginPlay().

You only have control of the code of one actor:
Store a unique identifier of the target actor, and run a periodic check to re-acquire it, replicate the Identifier and not the reference.
(see sgPlayerData actor in SiegeIV)



===================================
EDIT:

An example of advanced crosslinking.
Siege has a 'sgCategoryInfo' actor that contains up to 128 constructibles.
Each constructible (sgBuilding) can have a special rule that limits its availability.
Each special rule is an actor that knows on which contructibles is applied on.

This means the special rule would need to replicate somewhere between 0 and 128 integers to a client right?
Nope, only up to 4, because of bitwise operations compressing the data into 4 integers.
https://github.com/CacoFFF/SiegeIV-UT99 ... oryInfo.uc
https://github.com/CacoFFF/SiegeIV-UT99 ... ildRule.uc
See: MasterSet() in sgBaseBuildRule.

sgCategoryInfo and sgBaseBuildRule have special cross-referencing, idea is to find each other.
If sgCategoryInfo arrives first, it'll fail to find all sgBaseBuildRule not yet received.
If sgBaseBuildRule arrives first, it'll fail to find the sgCategoryInfo.
But since both are coded to find each other, the result will be perfect cross-referencing.

Back to the array, how do we encode the sgBaseBuildRule actor's position(s) in sgCategoryInfo.NetRules[128]?
See sgBaseBuildRule.AppliedOn[4], each INT has 32 BITS, each BIT is a position, you have 128 positions that can be 0 or 1.
This is simple packed boolean array compression.

=====
Another case of linking:

Rules come in various types, there's additional 'NOT' and 'COMBO' rules that either reverse the result, or demand that all child rules are valid before itself being valid.
The sgBaseBuildRule has a function IsEnabled() that indicates that this rule has been properly initialized and is ready to be used (by client or server).

https://github.com/CacoFFF/SiegeIV-UT99 ... RuleNot.uc
https://github.com/CacoFFF/SiegeIV-UT99 ... leCombo.uc

As you can see, the base sgBaseBuildRule contains all overridable methods needed to make the rule system ignore subclass casting, and the subrules tell the client when replication has been properly finished.
These methods also have a portion of linking themselves, which works by validating a variable the server sends and the actual linking done.

If a combo rules states that there's 3 sub-rules, then the client must find at least 3 rules that identify the combo as it's 'master' rule.
The cross-referencing done here is all based on PostNetBeginPlay() events, you'll find absolutely no periodic checks.
The code will stop working if a rule is deleted, and start working again when replicated by the server a second time.
So crosslinking is secure, doesn't break the client, allows the server to manipulate rules.
=====
Post Reply