UE2 - UT2kX No replication

  • Two Factor Authentication is now available on BeyondUnreal Forums. To configure it, visit your Profile and look for the "Two Step Verification" option on the left side. We can send codes via email (may be slower) or you can set up any TOTP Authenticator app on your phone (Authy, Google Authenticator, etc) to deliver codes. It is highly recommended that you configure this to keep your account safe.

Ominae392

New Member
Feb 26, 2008
13
0
1
I posted about this problem a few weeks ago but I didn't have a firm grasp of exactly what was going on. This time around I've got a better understanding of the problem

What's happening is that replication isn't working. The client never receives an updated game state.

So I have a mutator, Foo. Foo has 2 classes, MyInteraction, and MySpectator.

The mutator doesn't do anything special:
Code:
function ModifyLogin(out string Portal, out string Options) {
  Level.Game.PlayerControllerClassName = "Foo.MySpectator";

  Level.Game.PlayerControllerClass = class'MySpectator';
   
  log("modifylogin");

  super.ModifyLogin(Portal, Options);
}

simulated function Tick(float deltatime)
{
    local PlayerController PC;

    // if the player has an interaction already, exit function.
    if (bHasInteraction) {
      super.Tick(DeltaTime);
      return;
    }

    PC = Level.GetLocalPlayerController();

    if(PC == None) {
        log(" Could not find local player controller");
        Return;
    }

    // Run a check to see whether this mutator should create an interaction for the player
    PC.Player.InteractionMaster.AddInteraction("Foo.MyInteraction", PC.Player); // Create the interaction
    bHasInteraction = True; // Set the variable so this lot isn't called again
    log("Interaction Added");


    // hide all of the HUD display, but leave the code intact since we play nice that way
    if(PC.myHUD.bCrosshairShow )
      PC.ToggleScreenShotMode(); // (also hides the weapon)
}

The Interaction Class Tick:
Code:
simulate function Tick(float dt){
    local Controller C;
    local MySpectator S;

    if (ViewportOwner.Actor == None)
        return;

    time+=DT;

    if( Level.GetLocalPlayerController().PlayerReplicationInfo != none )
        bSpectator = Level.GetLocalPlayerController().PlayerReplicationInfo.bIsSpectator;

if (ViewportOwner.Actor.Role == ROLE_Authority || !bSpectator)
    {
        if( Level.NetMode != NM_Standalone && (bSpectator || ViewportOwner.Actor.Role==ROLE_Authority)) {
            for (C = ViewportOwner.Actor.Level.ControllerList; C != None; C = C.NextController)
            {
                S = MySpectator(C);
                if (S != None && (S != ViewportOwner.Actor) )
                {
            S.UpdateCursor(X,Y);
                            
                        }
                        else
                        {
S.UpdateCursor(-1,-1);
                        }
                    } 
                } 
            }
        }
         else {
              S =MySpectator(ViewportOwner.Actor);
             
                        S.UpdateCursor(X,Y);
                    }
              }
                else {
                    S.UpdateCursor(-1,-1);
                }       
         }

}

The MySpectator class tick and replicated stuff:
Code:
replication {
reliable if (Role == ROLE_Authority)
UpdateCursor;
}
simulated function UpdateCursor(float x, float y){
myX = x;
myY = y;
}

event PlayerCalcView(out actor ViewActor, out vector CameraLocation, out rotator CameraRotation )
{
//... Do some stuff.
    PTarget = Pawn(ViewTarget);
    if ( PTarget != None && !bBehindView ) { // This should always be true
                log("Target found.");
    } else {
		log("MySpectator.PlayerCalcView: You shouldn't see me."$PTarget$"  "$bBehindView);
    }
}

Obviously there is a lot more. So here is the problem. When I load my small test map, consisting of a single room and some boxes, the server will load just fine but the client will launch the map and not receive any of the game state. It doesn't see ammo drops, weapons, or the main player. Oddly enough if I remove the calls to MySpectator from MyInteraction the world loads a little bit better. Problems seem to get worse the more replicated functions I add that are called from MyInteraction.Tick(). The other thing to note is that what I do get a failed client connection the Interaction class is never added because Tick in Foo is never called.

There are some checks in the MySpectator class to see if player bSpectator has been set and the PlayerReplicationInfo is not NONE, these both fail. Weirdly enough if I load a larger map the problems seem to go away. At first I thought it might be my map so I downloads a bunch off the net. The problems happens pretty randomly but generally the smaller maps have issues.

The other thing I tried was to put a wait condition in the Tick of MyInteraction. One thing I don't understand, the Tick() of MyInteraction is always running, even before it is added as an interaction in Foo. Is this supposed to be this way?

I'm at a loss as to whats wrong. It seems that the less I call MySpectator during map start up the more likely I will be to have a successful connection.
 
Last edited:

meowcat

take a chance
Jun 7, 2001
803
3
18
Well I suppose one obvious question is do you have all of the proper replication variables set in your mutator class to ensure that it is always relevant to the client & that it's always 'tick'ed? (RemoteRole=ROLE_SimulatedProxy, bAlwaysRelevant=True)

Also, just out of curiosity, what exactly are you trying to do by using the interaction serverside to adjust stuff clientside?
 

Ominae392

New Member
Feb 26, 2008
13
0
1
Do I need to do the remote role for all of the classes? The mutator is set to RemoteRole=Role_SimulateProxy but the Interaction and Spectator classes aren't. The same applies to bAlwaysRelevant.

As for what I'm trying to do: The clients aren't actually other users. I'm using the clients as spectators in to a scene and my interaction class allows me to have a cursor that can span all of the clients as if they were 1 monitor.
 

meowcat

take a chance
Jun 7, 2001
803
3
18
Ahh, ok that makes sense (the multi monitor thing). You should not need to adjust the remote roles on the spectator class or interaction (interactions are standalone/clientside anyways, they can't be linked from server to client since they're not actors), and bAlwaysRelevant should be true for the mutator class (not needed for the spectator class).

Do you have any other log messages indicating errors? (maybe in the client logs?)

Maybe another dumb question, but do you have bots running around in the maps when you test them, because if the problem is more prevalent in smaller maps, could the server MaxPlayers/MaxSpectators/ Map Recommended # of Players settings be messing your code up (with respect to spawning)?
 

Ominae392

New Member
Feb 26, 2008
13
0
1
"Do you have any other log messages indicating errors? (maybe in the client logs?)"
- Not that I can tell. There is a "can't find package" error but it appears regardless of the map and mutator I am using. Really the only thing I notice is the lack of logging statements from the main mutator (no "interaction added"). Otherwise it's pretty quiet.

"
Maybe another dumb question, but do you have bots running around in the maps when you test them, because if the problem is more prevalent in smaller maps, could the server MaxPlayers/MaxSpectators/ Map Recommended # of Players settings be messing your code up (with respect to spawning)?"
- No bots for me, I wanted to keep the tests simple for now. The test map is a single room 2048x2048x2048(I think, it's been a few months since I did the map.) It has 9 static meshes of simple blocks with a metallic texture. Just as an aside, I did a comparison of all the level variables between my map and a working map, no difference. I also remade the map using textures from a known working map. Lastly I recompiled the map on a different PC with a fresh copy of UT but without my class present. I don't know if that last part means anything but UED sometimes does things I don't understand.

Small addendum: Loading the map without my mutator works just fine. The clients can load in as spectators, see my player, and they receive an up to date game state.
 
Last edited:

meowcat

take a chance
Jun 7, 2001
803
3
18
Based on what you said about weaponpickups/other non-static objects not showing up, its really does sound like a net relevancy issue. Unfortunately I'm not quite sure how thats is handled (most of it is native), but what I do know is that objects have to pass a visibility check for the client camera view and I believe be within the FOV, this could mean that if the camera location (spectator controller) was outside the level, it might start 'dropping' replicated objects.

It would seem to imply that you may be doing some more interesting stuff in your MySpectator class that is not shown, or possibly in your mutator class (the Check replacement, IsRelevant functions can cause all sorts of issues really quickly). Could you maybe post some default properties that were changed from their parent classes?
 
Last edited:

Ominae392

New Member
Feb 26, 2008
13
0
1
Sorry for the uber-delayed response. It's been a rough couple of weeks. Below you'll find my MySpectator class code. I've removed all the variable declarations from the top and removed some of the unnecessary functions like conversions and math crap. The remained is anything having to do with actual Unreal stuff.

Code:
class MySpectator extends xPlayer;


replication
{
	reliable if (Role == ROLE_Authority)
		 LeftClick, RightClick, LeftDblClick, RightDblClick;

	reliable if (Role == ROLE_Authority)
		ClientViewPosition, ClientUpdateCursor, IncrLeftClick;
}


//Playertick to get input from Gamepad
event Playertick(float deltatime)
event Playertick(float deltatime)
{

    local int i;
    local float X,Y;
	local string tmp;

    //local bool bSpectator;

    Time+= deltatime;


    if( PlayerReplicationInfo != none ) {
        bSpectator = PlayerReplicationInfo.bIsSpectator;
	}
	
	if(Role == ROLE_Authority || !bSpectator )
    {
        for (i = 0; i < Player.LocalInteractions.Length; i++)
        {
            if(Player.LocalInteractions[i].isA('MyInteraction'))
            {
                if( JoystickInput )
                {
                    X = MyInteraction(Player.LocalInteractions[i]).VScreenPosX;
                    Y = MyInteraction(Player.LocalInteractions[i]).VScreenPosY;
                    MyInteraction(Player.LocalInteractions[i]).VScreenPosX = FClamp(X+aJoyX*CursorSpeedX, 0.0, 1.0);
                    MyInteraction(Player.LocalInteractions[i]).VScreenPosY = FClamp(Y+aJoyY*CursorSpeedY, 0.0, 1.0);
                }
                else
                {
                    aBaseY += aJoyY*MovementSpeed;
                    aBaseX += aJoyX*TurnSpeed;
                }
          		HandleMouseClick();

            }
        }
    }
    else if(PlayerReplicationInfo != none)
    {
       if(PlayerReplicationInfo.bOnlySpectator)
       {
			if(Pawn(ViewTarget) == none || Pawn(ViewTarget).PlayerReplicationInfo != none && Pawn(ViewTarget).PlayerReplicationInfo.GetHumanReadableName() != WatchedPlayer)
			{
				SetView();
			}
       }
    }
	
	super.PlayerTick(deltatime);
}

function SetView()
{

    local Controller C, Pick;
    local bool bFound, bRealSpec, bWasSpec;
	local TeamInfo RealTeam;

	bRealSpec = PlayerReplicationInfo.bOnlySpectator;
    bWasSpec = !bBehindView && (ViewTarget != Pawn) && (ViewTarget != self);
    PlayerReplicationInfo.bOnlySpectator = true;
    RealTeam = PlayerReplicationInfo.Team;

    // view next player
    for ( C=Level.ControllerList; C!=None; C=C.NextController )
    {
      if(C.PlayerReplicationInfo.GetHumanReadableName() != WatchedPlayer)
         continue;

		if ( bRealSpec && (C.PlayerReplicationInfo != None) ) // hack fix for invasion spectating
			PlayerReplicationInfo.Team = C.PlayerReplicationInfo.Team;

        if ( Level.Game.CanSpectate(self,bRealSpec,C) )
        {
            if ( Pick == None )
                Pick = C;
            if ( bFound )
            {
                Pick = C;
                break;
            }
            else
                bFound = ( (RealViewTarget == C) || (ViewTarget == C) );
        }
    }
    PlayerReplicationInfo.Team = RealTeam;
    SetViewTarget(Pick);
    ClientSetViewTarget(Pick);
    if ( (ViewTarget == self) || bWasSpec )
        bBehindView = false;
    else
        bBehindView = true; //bChaseCam;
    ClientSetBehindView(bBehindView);
    PlayerReplicationInfo.bOnlySpectator = bRealSpec;
	bAllowUpdate = true;
}


exec function ToggleMouseLook()
{

   local int i;
   local bool MouseLook;

   for (i = 0; i < Player.LocalInteractions.Length; i++)
   {
      if(Player.LocalInteractions[i].isA('MyInteraction'))
      {
         MyInteraction(Player.LocalInteractions[i]).MouseLook = !MyInteraction(Player.LocalInteractions[i]).MouseLook;
         MouseLook = MyInteraction(Player.LocalInteractions[i]).MouseLook;
         MyInteraction(Player.LocalInteractions[i]).VScreenPosX = 0.5;
         MyInteraction(Player.LocalInteractions[i]).VScreenPosY = 0.5;
      }
   }
}

function PostBeginPlay() {
    Super.PostBeginPlay();
    UpdateSettings();
}

function UpdateSettings() {
    local int i;

    SaveConfig();

    if( Role == ROLE_AUTHORITY && !bSpectator )
    {
        for (i = 0; i < Player.LocalInteractions.Length; i++)
        {
             if(Player.LocalInteractions[i].isA('MyInteraction')) {
                MyInteraction(Player.LocalInteractions[i]).UpdateSettings();
                break;
             }
        }
    }

	ScreenSegmentXStart = 0;
	ScreenSegmentXEnd = 1.0;
	ScreenSegmentYStart = 0;
	ScreenSegmentYEnd = 1.0;
}

simulated function ClientUpdateCursor(float x, float y){
	VScreenPosX = x;
	VScreenPosY = y;
}

// Replicate the screen properties back to the server
simulated function PostNetBeginPlay()
{
  local vector tempVec,tempVec2;
  Super.PostNetBeginPlay();
  
  UpdateSettings();
}

function ServerPassPosition( int PackedRotation, float X, float Y, float Z )
{
    local Controller C;
	
    if( Role==ROLE_Authority )
    {
       for(C = Level.ControllerList; C != None; C = C.NextController)
       {
          //...update the UncompressedViewRotation for all clients viewing the one I'm controlling
          if(Spectator(C) != None && Spectator(C).ViewTarget == Self.ViewTarget)
          {
             Spectator(C).ClientViewPosition(PackedRotation, X, Y, Z);
          }
       }
    }
}

event PlayerCalcView(out actor ViewActor, out vector CameraLocation, out rotator CameraRotation )
{
    local Pawn PTarget;
    local Controller C;
    local rotator NewCameraRotation, CameraDiff;
  
    bBehindView = false; // Never allow behind view

    super.PlayerCalcView(ViewActor, CameraLocation, CameraRotation);

    if ( PlayerReplicationInfo != None )
        bSpectator = PlayerReplicationInfo.bIsSpectator;

    if( Role==ROLE_Authority )
    {
       //.... Update client position
    }
    else
    {
      if( bSpectator )
      {
		//.... Accept player input to change view if we are a spectator
      }
   }


    // Get the PTarget, and then adjust all the camera location and rotation values
    // Do these camera adjustments in pretty much all cases, even when we aren't a spectator
    PTarget = Pawn(ViewTarget);
    if ( PTarget != None && !bBehindView ) { // This should always be true
		log("PTarget found");
    } else {
		log("You shouldn't see me."$PTarget$"  "$bBehindView);
	}
}

simulated function PostNetReceive()
{
    super.PostNetReceive();
    HandleMouseClick();
}
simulated function IncrLeftClick(){
	LeftClick++;
}
function HandleMouseClick()
{
	
}

exec function Fire(optional float F)
{
    super.Fire(F);

    LeftClick++;
}
function WeaponsFire(){
	PlaySound( GunShot );
}

simulated function SetInitialState(){
	super.SetInitialState();
	bInitd = true;
}
defaultproperties
{
	RecentServers(0)="192.168.50.103:7777"
	RecentServers(1)="192.168.1.108:7777"
	RecentServers(2)="192.168.1.20:7777"
	RecentServers(3)="66.219.41.218:7777"
	RecentServers(4)="192.168.1.112:7777"
	RecentServers(5)="192.168.1.100:7777"
	RecentServers(6)="192.168.1.101:7777"
	RecentServers(7)="192.168.1.106:7777"
	RecentServers(8)="127.0.0.1:7777"
	Handedness=2.000000
	bAllowUpdate = false
	GunShot = Sound'sf2k4NewSounds.m4Single'
}