Jump to content

[1.12][Solved] Variation in player movement speed


FredTargaryen

Recommended Posts

From what I have read the most reliable way to get the player's motion on a server is to have some prevPos variables, and when the player updates obtain the motion by subtracting its position from the previous position. The code I've used for this is:

protected Entity e;
protected double prevPosX;
protected double prevPosY;
protected double prevPosZ;

@SubscribeEvent(priority= EventPriority.HIGHEST)
public void speedCheck(TickEvent.WorldTickEvent event)
{
    if(event.phase == TickEvent.Phase.START)
    {
        double motionX = this.e.posX - this.prevPosX;
        double motionY = this.e.posY - this.prevPosY;
        double motionZ = this.e.posZ - this.prevPosZ;
        double distance = Math.sqrt(motionX * motionX + motionY * motionY + motionZ * motionZ);
        if (distance >= DataReference.MINIMUM_ENTITY_SPEED) 
        {
            System.out.println(distance);
        }
        if (this.e.isDead)
        {
            MinecraftForge.EVENT_BUS.unregister(this);
        }
        this.prevPosX = this.e.posX;
        this.prevPosY = this.e.posY;
        this.prevPosZ = this.e.posZ;
    }
}

But the distance values that are printed out vary, even though they are printed out while the player is walking straight forward. Here's a sample:

Spoiler

0.4317180760788172
0.4317180760788172
0.4317180760788172
0.4317180760788172
0.41552447515751856
0.42908221391122287
0.43128902593028906
0.6474245332133046
0.4317118609673257
0.6475748941811714
0.43171797770438
1.0792951684334544
0.43171806748241603
0.8634361349739166
1.2951542024638933
0.4317180674880145
0.4317180674880145
1.0792951687200363
0.4317180674880145
0.4317180674880145
0.6475771012320217
0.4317180674880145

 

So the mode value is 0.4317180760788172 but I've seen speeds as high as 1.5 even while walking. Speeds can go lower than the above values as well.

Even the value of 0.43 bothers me, because the wiki lists average walk speed as 4.3 metres per second. With 20 ticks to a second I assume the value I should be getting is 0.215.

 

So where have I gone wrong? If I had to guess, it's because of not taking skipped ticks into account; in which case how can I get the number of ticks skipped to divide by that number?

Thanks for reading.

Edited by FredTargaryen
Link to comment
Share on other sites

What do you mean by not taking skpped ticks into account? Are you talking about when there is lag? People argue about it but most end up agreeing that if the whole game is slowed down then you should scale accordingly -- if the ticks are only 10 per second then the player speed should be half as much.

 

If you really wanted to calibrate to real time that is fairly easy to do though. Minecraft is just a Java program and Java has various methods for checking real-time. So you can create a timer that checks for difference in system time on each server tick and if it varies from the expected 1/20th of a second you can set a scale factor you can use for calculations during that tick.

  • Like 1

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Link to comment
Share on other sites

Yeah, I only have a potato to develop on so I often get "Server is 23123s behind, skipping 1024 ticks" or words to that effect. I thought if ticks were being skipped then PlayerTickEvents would not fire so the prevPos values weren't being updated, so the speed would look faster when the next PlayerTickEvent fired. Maybe that doesn't quite make sense but I was just guessing.

 

OK, I'll set up a timer and see what happens. Thanks!

Link to comment
Share on other sites

  • 2 weeks later...

I made a timer and after checking with that, realised that I'm not actually skipping any ticks. I removed a minimum distance that I had put into the code and realised the mode distance travelled each tick is actually about 0.215, which is consistent with the wiki.

 

However during a normal forward walk, the distance in one tick can still sometimes be set to 0.43 or higher, which is faster than sprinting. This is bad for my code because ideally I should be able to check if the player is definitely moving at sprinting speed or faster in just one tick. My best guess is that the server occasionally moves the player ahead further than their movement suggests, in which case I'm at a loss as to what to do about it (I probably shouldn't be doing anything about it).

 

I could keep track of the mean speed over several ticks but these outliers are so high that the mean might still be faster than sprinting.

I'm thinking about tracking the mode of the last few tick distances, and assuming the mode distance if the latest distances are out of line with the mode. This probably gives me the best chance of getting the right speed but it does mean a change from walking to sprinting, for example, wouldn't register for a few ticks. If anyone has any better ideas I'm all ears.

Link to comment
Share on other sites

This is due to the fact that the client does not inform the server about it's position change every tick, but rather once every couple of ticks.

  • Like 1

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

3 hours ago, FredTargaryen said:

So does the server not move players around at all? The position only updates on the server when the client tells it to update? I assumed if the player was falling for example the server would process that without waiting for client info.

The client takes input, converts that to motion, and sends position updates to the server.

 

Yes.

  • Like 1

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

Are you trying to make some sort of anti-cheat? What are you looking to do with the player's speed? I'm just wondering, because if it's not for something like an anti-cheat or a live speed meter I'd imagine there might be more elegant sollutions than sending a packet to the server every tick for every online player.

 

Have you considered measuring the average speed client side by calculating the position every X ticks and then sending that average speed to the server? as said above the client has more accurate 'live' information on position. If you don't need something to happen the exact tick your movement speed drops below 4.3m/s that could very well work.

  • Like 1
Link to comment
Share on other sites

It's not an anti-cheat but I need to check speed every tick, or as near as possible, because I want blocks to respond to the player speed before the player can collide with them. I didn't want the heavy traffic of movement packets, or to have to deal with the latency of packets being sent, so I tried to make it all server-side, but as you can see that didn't work.

 

Yeah, sending the average speed every few ticks sounds much saner than every tick. I will try it.

Link to comment
Share on other sites

Have you thought about possibly making this part of the block rather than the player sending updates to the server? If you want a specific block to move out of the way when a player runs into it you could make the tile Entity search for players within an X radius of it. Then when the player gets near it could look at average speed and react to it. Depending on what block behaviour you want this wouldn't even have to be incredibly precise.  

 

that way blocks could react to entities being within X meters of the block rather than players sending a continuous signal to the server. Ofcourse both of those ideas have their merit

  • Like 1
Link to comment
Share on other sites

I did used to have this as a block as you described but for various reasons, a lot of which I can't remember, I decided it would be best to make this a player thing. The main reason is I'm making fragile glass blocks, so I would expect players to use them like glass blocks, i.e. TileEntities all over the place if they make a big building or window. Those TileEntities would be constantly checking for players even if none were close enough, and if they're next to each other they'd be unnecessarily checking the same player twice or more.

Luckily I have my version control so I can branch if I don't get anywhere with this, but I am getting a much better effect this way for every entity... except players.

 

I'm thinking of only sending a message when the player changes speed significantly... but I need to see some client motion values first, and if they're as unpredictable as the server that won't really be possible.

Link to comment
Share on other sites

So in light of the fact that the client is the only good source of speed info, I have found a compromise to this problem which works pretty well. When an AttachCapabilitiesEvent is fired for the player, if the player's world is remote I construct a listener object and register it with the Forge Event Bus (if not remote I attach the capability). Each tick the listener calculates the speed of the player and compares it with the previous speed. If the current speed differs from the previous speed by more than 0.01, only then do I send a packet (there is still variation on the client but it is extremely subtle compared to the server) and update the previous speed. The packet then updates the speed on the Capability on the server. So on the server, instead of trying to calculate the speed from player variables, the last speed sent from the client is used. To account for the latency of sending the update packet I run the break code by simulating collisions ahead of where the player will be next tick, so the blocks break as if the player has been moving for three ticks instead of one. Most of the time this produces the effect I was looking for. Thank you to everyone for your help and advice!

 

Code:

@SubscribeEvent
    public void onBreakerConstruct(AttachCapabilitiesEvent<Entity> evt)
    {
        final Entity e = evt.getObject();
        if(e.world.isRemote)
        {
            if(e instanceof EntityPlayer)
            {
                MinecraftForge.EVENT_BUS.register(new Object() {
                    private EntityPlayer ep = (EntityPlayer) e;
                    private double lastSpeed;
                    @SubscribeEvent(priority=EventPriority.HIGHEST)
                    public void speedUpdate(TickEvent.ClientTickEvent event)
                    {
                        if(event.phase == TickEvent.Phase.END)
                        {
                            double speed = Math.sqrt(ep.motionX * ep.motionX + ep.motionY * ep.motionY + ep.motionZ * ep.motionZ);
                            if(Math.abs(speed - this.lastSpeed) > 0.01)
                            {
                                MessageBreakerMovement mbm = new MessageBreakerMovement();
                                mbm.motionx = ep.motionX;
                                mbm.motiony = ep.motionY;
                                mbm.motionz = ep.motionZ;
                                mbm.speed = speed;
                                PacketHandler.INSTANCE.sendToServer(mbm);
                                this.lastSpeed = speed;
                            }
                            if(ep.isDead)
                            {
                                MinecraftForge.EVENT_BUS.unregister(this);
                            }
                        }
                    }
                    @SubscribeEvent
                    public void killObject(FMLNetworkEvent.ClientDisconnectionFromServerEvent event)
                    {
                        MinecraftForge.EVENT_BUS.unregister(this);
                    }
            });
            }
          ...
}

 

For a ridiculous block comment thesis about my block break system you can see here.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.