Open CamronW opened 5 years ago
If I remember correctly the server will send the player an Entity Velocity packet with a negative Z value if it floats. The negative Z essentially should tell the client to go down. The same packet is also being sent, when the player is being punched, getting pushed by water, or any other external force (not sure about pistons, but you get the point).
As far as i can tell the (new) requested position gets acknowledged by sending out a Player Position
packet (search for Updates the player's XYZ position on the server.
on the wiki). As far as I can see this client currently has the Player Position And Look (serverbound) implemented.
Now it's getting a bit more complicated, since the Player Position And Look Packet, would need your new position and your client entity id. The client entity id is part of the Join Game packet and your current position can be obtained by the Player Position And Look (clientbound).
Rough outline of the code:
def main():
player_entity_id = 0
player_x = 0
player_y = 0
player_z = 0
player_yaw = 0
player_pitch = 0
# listen to JoinGamePacket
def handle_join_game(join_game_packet):
player_entity_id = join_game_packet.entity_id
connection.register_packet_listener(
handle_join_game, clientbound.play.JoinGamePacket)
# listen to PlayerPositionAndLookPacket
def handle_player_position_and_look(playerPositionAndLookPacket):
player_x = playerPositionAndLookPacket.x
player_y = playerPositionAndLookPacket.y
player_z = playerPositionAndLookPacket.z
player_yaw = playerPositionAndLookPacket.yaw
player_pitch = playerPositionAndLookPacket.pitch
connection.register_packet_listener(
handle_player_position_and_look, clientbound.play.PlayerPositionAndLookPacket)
# listen to EntityVelocityPacket
def handle_entity_velocity(entity_velocity_packet):
new_x = player_x + entity_velocity_packet.velocity_x
new_y = player_y + entity_velocity_packet.velocity_y
new_z = player_z + entity_velocity_packet.velocity_z
# send an acknowledgement to the server
position_response = serverbound.play.PositionAndLookPacket()
position_response.x = new_x
position_response.feet_y = new_y
position_response.z = new_z
position_response.yaw = player_yaw
position_response.pitch = player_pitch
self.connection.write_packet(position_response)
# update local reference
player_x = new_x
player_y = new_y
player_z = new_z
connection.register_packet_listener(
handle_join_game, clientbound.play.EntityVelocityPacket)
Consider this as free and unencumbered software (piece) released into the public domain, without warranty of any kind
Hey,
I've done some testing and I believe the Entity Velocity Packet only accounts for things other than a player? For example a chicken falling displays the correct packet, however when the player falls no packets are sent.
Do you know of a way to get player velocity?
Thanks
I believe the way Minecraft Console Client does it is by checking if the player is on ground by checking the block below them. However this would require loading in the chunk packets and finding the block below the player which seems overly complicated
Ahh thanks for pointing that out. You seem to be correct and the Velocity is only being sent when the player gets pushed by an external force.
I actually looked into the Minecraft Console Client and did some debugging and it seems, that it simply sends out a negative Y position regardless if it stands on a block or not. Based on the code it seems that what you mention is indeed the intended, but at least my short debugging session tells me something different.
In general I would agree it would be smarter if the client would only send the 'I want to go down', if there is no block below, but unfortunately pycraft does not have the chunk data implemented and thus is not aware of it's surroundings (would be a really great addition, but those packets are a pain).
I currently see two ways to solve this with pycraft.
clientbound.play.PlayerPositionAndLookPacket
if you do a movement that would not be possible (e.g. a block in its way). You could technically implement a new command that sends a 'I want to go down' packet, until you receive a correcting clientbound.play.PlayerPositionAndLookPacket
. You could also combine it with receiving chatmessage or whatever you want.Rough sketch of 2:
class Player:
x = 0
y = 0
z = 0
yaw = 0
pitch = 0
last_y_pos = 0
def main():
# ....
# listen to PlayerPositionAndLookPacket
def handle_player_position_and_look(playerPositionAndLookPacket):
Player.x = playerPositionAndLookPacket.x
Player.y = playerPositionAndLookPacket.y
Player.z = playerPositionAndLookPacket.z
Player.yaw = playerPositionAndLookPacket.yaw
Player.pitch = playerPositionAndLookPacket.pitch
Player.last_y_pos = playerPositionAndLookPacket.y
connection.register_packet_listener(
handle_player_position_and_look, clientbound.play.PlayerPositionAndLookPacket)
# ....
while True:
try:
text = input()
if text == "/respawn":
print("respawning...")
packet = serverbound.play.ClientStatusPacket()
packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN
connection.write_packet(packet)
elif text == "/down":
import time
Player.last_y_pos = 0
while (int(Player.last_y_pos) - 1) != int(Player.y):
# send a packet every ~50ms
time.sleep(0.05)
Player.y -= 1
position_response = serverbound.play.PositionAndLookPacket()
position_response.on_ground = False
position_response.x = Player.x
position_response.feet_y = Player.y
position_response.z = Player.z
position_response.yaw = Player.yaw
position_response.pitch = Player.pitch
connection.write_packet(position_response)
else:
packet = serverbound.play.ChatPacket()
packet.message = text
connection.write_packet(packet)
except KeyboardInterrupt:
print("Bye!")
sys.exit()
That code might need some tweaking (looks ugly), but lets the player 'fall' when you enter the command /down
.
Yeah I did try to implement chunk packets, I managed to get the data through but couldn't work out how to parse it.
For the last day or so I've been working on trying to get gravity to work, however I can't get it to work properly so that it doesn't get kicked by anticheats on the server. Setting the feet_y 1 block lower gets it kicked so I've been trying to find a way to replicate "vanilla" falling velocity.
Still trying, hopefully will get there soon aha
Yeah I did try to implement chunk packets, I managed to get the data through but couldn't work out how to parse it.
Maybe that would already be worth creating a merge request and integrate the relevant packets into this repo? I could take a look and help troubleshoot....
I can remember I tried to implement the chunk data myself and then lost interest in minecraft xD
Yeah, I'll have to clean the code up a bit first - it's not looking that good at the moment aha. Once I've done that I'll create a merge request
Do you have any idea how to properly calculate the velocity of the player so it falls like normal?
I'm not a 100% certain what the exact values are, but I would recommend to look at other clients.
Here is the information I found while doing a very quick search:
And the actual implementations
That should give a good impression, how it works, if its 100% right, I guess you would only find out by looking at the actual java client code.
I've tried to get this to work, I got the numbers to match that of Minecraft Console Client, however it still doesn't fall smoothly.
Here are the numbers I got when comparing my fall script with Minecraft Console Clients: Minecraft Console Client: https://hastebin.com/ujimibiluh.sql My Client: https://hastebin.com/taqubawoxi.sql The numbers are the same apart from some slight rounding, I can't see what can be wrong.
Can you see anything wrong with this code? I'm just trying to get it to fall 1 block normally for now
print("Trying to move down 1 block")
velocity = 0.08
newY = pos_look.y
motionY = 0
for x in range(5): #5 Because that's enough to get the Y to be down greater than 1 block
print("Before position:", round(newY, 13))
motionY = motionY - 0.08
motionY = motionY * 0.9800000190734863
print("Motion Y:", motionY)
newY = newY + motionY
print("End position:", round(newY, 13))
connection.write_packet(PositionAndLookPacket(
x = pos_look.x,
feet_y = round(newY, 13),
z = pos_look.z,
yaw = pos_look.yaw,
pitch = pos_look.pitch,
on_ground = True))
time.sleep(0.05) #20 Ticks per second
Indeed the numbers seems about the same. Does your player fall smoothly with the Minecraft Console Client? If so, I guess the timing of sending out the new position could be off (e.g. the other client sends more packets in a shorter period). When do the packets get sent out with the Minecraft Console Client and your version?
For future reference (the Minecraft Console Client):
Before Position :89
Motion Y: -0.0784000015258789
End Position :88.9215999984741
Before Position :88.9215999984741
Motion Y: -0.155232004516602
End Position :88.7663679939575
Before Position :88.7663679939575
Motion Y: -0.230527368912964
End Position :88.5358406250446
Before Position :88.5358406250446
Motion Y: -0.304316827457544
End Position :88.231523797587
Before Position :88.231523797587
Motion Y: -0.376630498238655
End Position :87.8548932993484
Before Position :88
Motion Y: -0.447497896983418
and your client:
Before position: 89.0
Motion Y: -0.0784000015258789
End position: 88.9215999984741
Before position: 88.9215999984741
Motion Y: -0.1552320045166016
End position: 88.7663679939575
Before position: 88.7663679939575
Motion Y: -0.230527368912964
End position: 88.5358406250446
Before position: 88.5358406250446
Motion Y: -0.30431682745754424
End position: 88.231523797587
Before position: 88.231523797587
Motion Y: -0.37663049823865513
End position: 87.8548932993484
Before position: 87.8548932993484
Motion Y: -0.44749789698341763
Yeah with Minecraft Console Client the player falls almost perfect, as if it was a vanilla client.
I added some timings to the Minecraft Console Client, here's what they look like.
12/08/2018 05:24:44.639: Before Position :89
12/08/2018 05:24:44.639: Motion Y: -0.0784000015258789
12/08/2018 05:24:44.641: End Position :88.9215999984741
12/08/2018 05:24:44.743: Before Position :88.9215999984741
12/08/2018 05:24:44.743: Motion Y: -0.155232004516602
12/08/2018 05:24:44.744: End Position :88.7663679939575
12/08/2018 05:24:44.745: Before Position :88.7663679939575
12/08/2018 05:24:44.745: Motion Y: -0.230527368912964
12/08/2018 05:24:44.746: End Position :88.5358406250446
12/08/2018 05:24:44.856: Before Position :88.5358406250446
12/08/2018 05:24:44.857: Motion Y: -0.304316827457544
12/08/2018 05:24:44.858: End Position :88.231523797587
12/08/2018 05:24:44.858: Before Position :88.231523797587
12/08/2018 05:24:44.858: Motion Y: -0.376630498238655
12/08/2018 05:24:44.859: End Position :87.8548932993484
12/08/2018 05:24:44.961: Before Position :88
12/08/2018 05:24:44.962: Motion Y: -0.447497896983418
As you can see, it sends out 5 packets, spanning over 219 miliseconds before it updated the "Before Position". This means it's running at a little under 20 tps, however I believe it's most likely running at 20 and that there are some inaccuracies with the timings.
My client is also running at 20~tps due to the time.sleep(0.05)
As far as I can see, the numbers match between my client and Minecraft Console Client. Unless I've calculated them wrong, the only thing I can think of is that perhaps Minecraft Console Client is sending out another packet?
Mhh interesting, I would try to investigate what packets are actually send by the pycraft client. Try using --dump-packets
that should at least print the packets. Maybe add a timestamp there too. Have you changed your initial code? Maybe post your current code and the timing results?
Could you figure this out?
Yeah, I gave up now aha. I spent too much time trying to figure it out. If anybody else has a solution it would be greatly appriciated!
Could you share your current version of the chunk data handling?
Anyone else have any ideas on how to do this? I’ve tried to follow the Minecraft wiki’s transportation page on falling but I wasn’t able to get it to fall correctly. At this point I’d be willing to pay to get it solved
Could you perhaps share the current state of your pycraft-client (e.g. have you implemented some custom classes)? I'm willing to help....
Hey,
So I have a pretty modified version at the moment, it would take a lot of work to try and post it all here. Currently what I'm doing is calling a fall() function every 5 seconds in an attempt to make it fall at least 1 block properly without getting stopped by servers anti-cheat.
Here's the function:
def fall():
yVel = 0
#for every tick
#decrease y velocity by 0.08 blocks per tick
#then multiply by 0.98
for x in range(20):
yVel -= 0.08
yVel *= 0.98
print(yVel, pos_look.y, pos_look.y + yVel)
connection.write_packet(PositionAndLookPacket(
x = pos_look.x,
feet_y = pos_look.y + yVel,
z = pos_look.z,
yaw = pos_look.yaw,
pitch = pos_look.pitch,
on_ground = False))
time.sleep(0.05)
I'm using the numbers based off of other open source projects, as well as the Minecraft wiki's tranportation page (https://minecraft.gamepedia.com/Transportation#cite_ref-8) which states
Every tick (1⁄20 second), non-flying players and mobs have their vertical speed decremented (less upward motion, more downward motion) by 0.08 blocks per tick (1.6 m/s), then multiplied by 0.98.
An example of this being used is in Minecraft Console Client: https://github.com/ORelio/Minecraft-Console-Client/blob/ecf0114f626ac2ee6ae8ffbf2cceb69b7e0b1b09/MinecraftClient/Mapping/Movement.cs#L81
Another example, TwistedBot: https://github.com/lukleh/TwistedBot/blob/310509c037335845838e699f9f9d56af117e03c9/twistedbot/botentity.py#L340
I have no idea what I'm doing wrong anymore, in my head my solution should work.
Any ideas @TheSnoozer
Yeah I got it to work, just need to polish the code over the weekend and then will post it here.
Would you be able to post the unpolished version now should so I could take a look and try getting it working my end? I don't mind bad code
As requested - unpolished and a bit off the result I want. In general the idea is that I want to sent a 'move down' packet whenever the alt floats around in the air. This can happen when teleported (an indicator for being teleported is a PlayerPositionAndLookPacket
packet), or when the block the alt is standing on is broken. Unfortunately the pycraft-library does not have any chunk information otherwise you could get the 'falling' implemented way better (e.g. you would know just by looking at the chunks and the current player position).
Anyways here is the draft, again right now a bit flaky - the thread sometimes stops in the middle of 'falling' causing the alt to stop falling.
PS: I should note that is this build open the start.py.
I might be being stupid here, but on the untouched code you sent me it doesn't work?
https://i.gyazo.com/e2de400023ed52280933cf9017e01a59.mp4
Not sure if I'm doing anything wrong
Did you end up creating a polished version @TheSnoozer
I am interested in this as well. Did you make a polished version you could post here, @TheSnoozer ?
from what I can see, that 'unpolished version' only attempts to fall when connecting, and stops trying to once it gets told by the server it's trying to fall into a block.
one anticheat I know of accounts for this and sends a position to the client to reset your velocity before you hit the ground, on login.
edit: scratch that I misread, but am also interested in this.
another edit, sorry: from what it looks like that code doesn't properly reset velocity on the server telling the client where they are, so falling will hit max velocity, then continue there even if it stops falling.
Anyone else managed to get falling to work on pycraft?
Still nothing here?
Maybe the PositionAndLookPacket is broken?
Hey,
I've been struggling to work out how to properly implement falling. Right now the player floats in the air if the block is deleted below them. I can't find anything useful on the protocol wiki.
Thanks