Announcement

Collapse
No announcement yet.

Custom movement type

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

    Custom movement type

    I would like to implement gliding in the air, but can't make it work properly in the net games because the default location / velocity / physics replication keeps overriding whatever properties I set on my pawn with ROLE_SimulatedProxy.

    I reduced the whole thing to simply extending the PlayerFlying state in the controller, but still nothing happens on the client. I can see it starts doing the right motion for a split second, but then gets overriden back from the server, probably because the server detects too large deviation from its own version of pawn properties.

    My code looks like this:

    Code:
    exec function Glide(Bool pressed)
    {
      if (pressed) GotoState('PlayerGliding');
      else GotoState('PlayerWalking');
    }
    
    state PlayerGliding extends PlayerFlying
    {
      function BeginState()
      {
        Log("*** BEGIN state gliding");
        pawn.SetPhysics(PHYS_Flying);
      }
    
      function EndState()
      {
        Log("*** END state gliding");
        pawn.setPhysics(PHYS_Falling);
        pawn.Velocity = Vect(0,0,0);
        pawn.Acceleration = Vect(0,0,0);
      }
    
      function PlayerMove(float DeltaTime)
      {
        local vector X,Y,Z;
        local vector glideDir;
        local vector glideVel;
        local rotator oldRotation;
    
        oldRotation = rotation;
        UpdateRotation(deltaTime, 1);
    
        GetAxes(rotation,X,Y,Z);
        glideDir = Normal(Y cross Vect(0,0,1)) + Vect(0,0,-0.2);
        glideVel = PawnBub(pawn).glideSpeed * Normal(glideDir);
    
        if ( Role < ROLE_Authority )
            ReplicateMove(deltaTime, glideVel, DCLICK_None, Rot(0,0,0));
        else
            ProcessMove(deltaTime, glideVel, DCLICK_None, Rot(0,0,0));
      }
    
      function ProcessMove(float deltaTime, vector newAccel, eDoubleClickDir doubleClickMove, rotator deltaRot)
      {
        pawn.acceleration = newAccel;
      }
    }
    It works perfectly fine on standalone and listen server pawn and even replicates properly to other clients, however, when I try to glide on a simulated client, it just keeps falling down. What am I doing wrong?

    #2
    Try playing a net game and using the 'ShowDebug' console command on the simulated controlling client, then check to see that the controlling client's playercontroller is actually going into your gliding state. The debug information tells you what state the controllers (player and AI) are in.

    Comment


      #3
      My log reports have already confirmed that the controller enters the right state. Moreover, I said that a short attempt of the correct movement is noticable, just that it seems it gets overriden the very next millisecond, and the only place in code that can happen is in the gliding state, so.....

      Anyway, what I'm trying to figure out is whyexactly, or what part of the original code overrides my custom behavior. The problem is my gliding state does pretty much the same as every other original state does (like 'PlayerWalking' or 'PlayerFlying'), just that the acceleration that I calculate in the PlayerMove() function is different. Therefore, I see no reason why it shouldn't work or why it doesn't replicate to the server the right way (which eventually results in the server overriding and forcing its own behavior back to the client, assuming that the client went too much off in the simulation).

      After carefully inspecting and searching through the original script code, many things appear that really puzzle me:

      1) I couldn't find any code that sets the PlayerController state to 'PlayerWalking' or any other states, apart from some extreme conditions like pawn's death etc.

      2) I don't see how the physics ever replicate from PlayerController on the client side to the server side, since the ServerMove function (which is replicated from client to server) doesn't take that as an argument. However, pretty much every PlayerController state sets another type of physics to the pawn in their BeginState() function.

      3) The same goes for the state of the controller itself. I can't see where it gets replicated from client to the sever, which means that when the server calls ProcessMove() from inside the ServerMove() function, it might happen that the wrong version of ProcessMove() actually gets called.

      4) Despite 2) and 3), the server does enforce its own version of physics and state onto client, if they don't match. How can then client controller ever successfully change the type of movement (or its state and pawns physics) when it always gets overriden back by the server and never replicated to it?

      Combining the 1-4 observations, I can only conclude, that the state and physics on the server change automatically from some hidden native code, according to the pawns movement through space (touching ground, loosing ground under feet, falling into water volumes, going back out). This would explain why it doesn't have to be replicated from the client, since a ROLE_AutonomousProxy client just dictates the orientation / velocity / acceleration of the pawn, and the state and physics change according to the pawns interaction with the world, which is processed both on the server and on the client separately.

      But this finally leads to the fact, that the client is never able to change the type of movement (physics / state) as a direct response to a player input (e.g. an exec function mapped to a certain key). The problem is this event only happens on the client and not on the server, so the server can never know about it without it being replicatedfrom the client to the server.

      Now, every possible replication tutorial (and I've read tons of them) says that "its obviously stupid to reliably replicate every player's mouse click or key press to the server" (which is almost a direct quote by the way). Now, not having to replicate every key press is surely easy to achieve when speaking about original types of movement, because they are handled according to the pawns interaction with world in the native code, so the only information needed to be replicated is the location / direction / speed of the pawn in the world. But what about custom movement? Now, if I wan't the player to be able to start or stop gliding upon a key press or release, does that mean I have to literally replicate (reliably) every one of those key presses? Well, it sure seems so to me.

      All this is fairly inconvenient when it comes to modding and I must say that in certain ways the UT engine just keeps disappointing me again and again in terms of being user-unfriendly.

      If there is a nicer and better way to solve the problem though, please correct me and explain to me what I'm missing.

      Comment


        #4
        Originally posted by ileben View Post
        My log reports have already confirmed that the controller enters the right state.
        Double check that on server.

        Comment


          #5
          Originally posted by Switch` View Post
          Double check that on server.
          Thanks, but i KNOW that it doesn't get into the right state on the SERVER, and so the server enforces its own state back to the client via "client adjustment". That was exactly the whole point of my previous post. Gosh, do you guys even read!?

          Comment


            #6
            Sorry, I didn't read it too closely, there was too much stuff and the whining put me off.

            That exec function you have doesn't look like it's executed both on client and server. Make that function replicated so the code runs on server, verify on server that such state change would be valid. If so, change the state and call server->client replicated function to change state on client.

            Comment


              #7
              OF COURSE the exec function doesn't happen on the server - if you press a key on your keyboard there is no one to go to the sever machine and press that same key for you there. And the conclusion I came to in my previous long post was that i actually have to replicate that even over to the server every single time the key is pressed/released and that I surely DONT WANT to do that, because its a bad replication / networking practice. All the advice you keep giving me is exactly what Ive already come to myself and written in that post.

              Is there any OTHER way than that, to achieve what I want?

              Comment


                #8
                ileben: Your conclusion is correct, you will have to replicate the function (this is not replicated every tick though, only when the initial key press is detected). So unless the player will be pressing it every 5th or 6th tick it *should be* okay. Also, yes native code does handle a lot of the Physics changes, and as I'm sure you've seen from the playercontroller code the PHYS_Walking and PHYS_Swimming can also be set by physics volume change notifications (passed to the controller). The pawn class itself can actually force the playerController to a new state (see climbladder). And yes these are set by 'static' environmental changes (volumes changes, pawn has physics walking then has no ground beneath it so physics is set to falling via native code). The server should always be the ultimate authority of states for pawns. It would appear that the server->Client replicated function LongClientAdjustPosition is what is forcing your clients back to its version of state (see last couple of lines of function).

                I'm guessing you're probably pretty familiar with the type of code shown below (for example only, untested), I only include it to help make this thread more useful to those who search it later, if corrections need to be made please point them out.

                Preferred method since the server propagates the state change (only an issue since 'state' is already updated by the server->client through the LongClientAdjustPosition function).
                Code:
                // Placed in the PlayerController (or SubClass)
                // asking the server's permission method 
                replication
                {
                	// Functions the server calls on the client side.
                	reliable if( RemoteRole==ROLE_AutonomousProxy )
                		ClientICommandYou;
                	// Functions the client calls on the server.
                	reliable if ( Role < ROLE_Authority )
                		ServerPleaseChangeState;
                }
                
                exec function IwantChangeState(name newState){
                    if(Role<Role_Authority) ServerPleaseChangeState(name newState);
                   else if(GetStatename() != newState) GotoState(newState); // so that it still works on local machine in standalone games
                } 
                
                //executed on server
                function ServerPleaseChangeState(name newState){
                       if(GetStatename() != newState){ // some basic conditional logic
                           GotoState(newState);
                           ClientICommandYou(newState);
                       }
                }
                
                // executed on client
                function ClientICommandYou(name newState){
                    GotoState(newState); //server's already doen the checks so go ahead and change
                }
                - or -

                The below version may have issues since the server version may replicate the old state to the client for an instant before being updated by the client's call to ServerITellYouChangeState, after which the client would then be set to the correct newState.
                Code:
                // Placed in the PlayerController (or SubClass)
                // only informing the server of the client's change
                replication
                {
                	// Functions the client calls on the server.
                	reliable if ( Role < ROLE_Authority )
                		ServerItellYouChangeState;
                }
                
                //executed on client or standalone
                exec function PleaseChangeState(name newState){
                       if(GetStatename() != newState){
                           GotoState(newState);
                           ServerItellYouChangeState(newState);
                       }
                }
                
                //executed on server
                function ServerItellYouChangeState(name newState){
                           GotoState(newState);
                }
                Please note that exec functions can also be simulated, and IIRC you can directly replicate a non-simulated exec function from client to server in the playercontroller class (with code execution on server only, or other classes based on Role and RemoteRole settings and net ownership) but its usually cleaner to have separate client/server functions (and is much easier to debug/read). You can also have simulated states on clients, which are useful for running animation latent functions, but these are probably not really useful for pawn/player code (though very useful for certain weapon animation code).

                Also, I can understand your frustration, but you might want to be careful with the tone in your posts, it is a bit offputting...

                Comment


                  #9
                  In some game or other's source code, I saw a really simple replicated set of functions right in Actor:

                  ServerGotoState()
                  ClientGotoState()

                  I thought that had the potential of being a rather brilliant addition.

                  So, anyway, yes, you need to replicate your desire to change state to the server, which would then do the state changes.

                  Comment

                  Working...
                  X