Announcement

Collapse
No announcement yet.

[VIDEO][CODE] Replication Part 4 - Listen Servers: Replicating Special Effects

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    [VIDEO][CODE] Replication Part 4 - Listen Servers: Replicating Special Effects

    Dear Community,

    Replication Series
    Part 1: Change Pawn Materials Based On a Unique PlayerID
    Part 2: Efficiently Replicate Custom Special Effects & Projectile Bounces
    Part 3: Grapple Hook and Custom Pawn Movements
    Part 4: Listen Servers - Replicating Special Effects


    1. Below is my code structure for replicating special effects on clients AND the listen server.

    2. My code structure puts special emphasis on Network Efficiency.

    3. Please note that this code I am presenting will also work perfectly for Dedicated Servers.

    Special Thanks
    Special Thanks goes to Solid Snake for encouraging me to not give up on figuring out the complexity of using Listen Servers. My entire game format will be relying on listen servers rather than dedicated!

    Thanks Solid Snake!




    ~~~

    Over View of Listen vs Dedicated Servers

    Listen servers are different from dedicated servers because the server is also a player.

    The complication here is that listen server players do not receive replication notification events (because its the server, there's no need to tell itself what it's doing).

    In a dedicated server, every player is a client, so the code is always the same for all players.

    But in a listen server, you literally have to write different code for the listen server player and the client player.


    This usually just amounts to calling the function that you put in your replciation event directly from the server function.

    See below for details!

    ~~~

    Playing a Shield Effect

    In this example there is a shield effect (I did not actually implement it, it could be any special effect).

    I use a boolean to trigger the replication event, because I only need to tell the pawn to play the shield effect, and the actual effect itself is always centered on the pawn, so no need to pass any other data.

    If you did need to pass data, dont use a boolean but a vector or whatever info you need to tell the pawn.

    //pawn class
    Code:
    var repnotify bool r_playShield;
    
    replication{
      if(bnetdirty)
       r_playShield;
    }
    
    //=============== REPLICATION EVENT ============
    simulated event ReplicatedEvent( name VarName )
    {
    	//very important line
    	super.ReplicatedEvent( VarName );
    	
    	if (varname == 'r_playShield') {
    		doShieldEffect();
    	}
    }
    
    simulated function doShieldEffect(){
      //do the actual special effect code
    }
    ~~~

    Network Efficiency

    The advantage of only replicating a boolean is that it is extremely small amount of data to pass over a network

    and using the structure bool = !bool guarantees that the event will always fire off due to the variable's value being guaranteed to change (becomes dirty)

    //player controller class
    Code:
    reliable server function activateShield(yourpawnclass yourpawn){
    
      //the listen server needs to run it directly
      yourpawn.doShieldEffect();
    
      //ADVANCED: if your code is purely a special effect, 
      //you do not need a dedicated server to do the line above
      //but if any pawn vars are changed to indicate for example 
      //that no damage can be taken, you need to at least set those vars 
      //on the dedicated server side.
    
      //repnotify for clients to call on their side
      yourpawn.r_playShield = !yourpawn.r_playShield; 
      
    }
    ~~~

    Function Parameters ARE Replicated

    Code:
    reliable server function activateShield(yourpawnclass yourpawn){}
    The reason the player controller class needs to specify the pawn (even though since it is player controller you should be able to just use the pawn variable):

    In order to trigger the repnotify event you have to change the repnotified var on the SERVER, using reliable server functions.

    But since this is a server function, if a client player controller calls the code, the value of the pawn will be different when the server runs the function if you dont pass in which pawn you mean as a parameter.

    So always pass in the pawn to the player controller function, it is not reduntant info, because you are using a reliable server function

    so if a client calls the function, that code is NOT being run on the client, but on the other machine, the server machine, and any important data must be passed in as function parameters.




    Enjoooy!



    Rama

    This is the code format I use for my own multiplayer game

    Footage from several contiguous actual multiplayer game sessions,

    most relevant footage starts around 4:20


    Original Tutorial about this video:
    http://forums.epicgames.com/threads/...2#post31594372


    #2
    Hi,

    Code:
    //the listen server needs to run it directly
      yourpawn.doShieldEffect();
    You should test whether you are on a dedicated server or not. By this way you can be sure you are not playing the ShieldEffect on the dedicated server.

    I'm not following you on the explanation for the parameter.
    On a client you have the PC that possess the pawn. All are proxy actor and the "real" actors are on the server. From the client PC, if you call the server function, you can use PC.Pawn to get your actual pawn.

    What your function allows to do is to be able to spawn the shield effect on any pawn that is present on the Client Side. I'm not sure this is what you intended to do or not.

    In term of implementation, I would have done a function PlayEffect on the client side that play your effect and call the server function to replicate it over all clients.
    By using this approach, the client side (listen server or stand alone or networked) will always play the effect when required and by using the same code. The server will only handle the replication of variable so the other clients play the effect too.

    What do you think of this?

    Comment


      #3
      Originally posted by elvince View Post
      In term of implementation, I would have done a function PlayEffect on the client side that play your effect and call the server function to replicate it over all clients.
      By using this approach, the client side (listen server or stand alone or networked) will always play the effect when required and by using the same code. The server will only handle the replication of variable so the other clients play the effect too.

      What do you think of this?
      Nice to hear from you!

      I am having a bit of trouble sorting through your verbal explanation, can you provide a short code sample to show me what you mean?

      Rama

      Comment


        #4
        Here is some pseudo code as I'm not at home ^^
        I hope this will help

        on PC
        Code:
        exec function CreateShield()
        {
        //This will be called from the client by a KeyBinding for example
        
        Pawn.doShieldEffect(false);
        ServerCreateShield();
        
        }
        
        reliable server function ServerCreateShield()
        {
        //You are on ServerSide, do any logic that you need to implement on the server side
        
        //Here you are changing the value to trigger the effect on all the clients
        Pawn.PRI.r_playShield= !Pawn.PRI.r_playShield;
        
        //The function managed the ListenServer side so you can call it safely
        Pawn.doShieldEffect(false);
        }
        PRI
        Code:
        var repnotify bool r_playShield;
        
        replication{
          if(bnetdirty)
           r_playShield;
        }
        
        simulated event ReplicatedEvent( name VarName )
        {
        local YourPawn YPawn;
        	//very important line
        	super.ReplicatedEvent( VarName );
        	
        	if (varname == 'r_playShield') {
        //Get the Pawn that PRI belongs to
                foreach WorldInfo.AllPawns(class'YourPawn', YPawn)
        		{
        			if (YPawn.PlayerReplicationInfo == self || (YPawn.DrivenVehicle != None && YPawn.DrivenVehicle.PlayerReplicationInfo == self))
        			{
        				YPawn.doShieldEffect(true);
        			}
        		}
        
        		
        	}
        }
        yourPawn class
        Code:
        simulated function doShieldEffect(bool viaReplication){
          
        //Do it only if you are the owner the pawn and not called via replication
        //or Do it only if you not the owner (other network client)
        //AND Do not do it if we are on a dedicated server!!
          if (((bNetOwner && !ViaReplication) || !bNetOwner)
            && WorldInfo.NetMode != NM_DedicatedServer)
             {
                //do the actual special effect code
        
        
             }
        }

        Comment


          #5
          Thanks for providing this code sample!

          I am glad to have it to show readers an alternative approach that especially focuses on making sure dedicated servers do not attempt to play special effects.

          I made a note of this in my main post but did not go into much detail.

          ~~~

          Also regarding the issue of the pawn as a parameter, I suppose my surrounding game code logic would cause that to make more sense, I always call the server function first, via exec function or other game event/function.

          I will happily look into your approach further at a future time, thanks again for sharing!

          Rama

          Comment

          Working...
          X