Brief Overview of Packets
All communication between the player (client) and the server is done via “packets”. A packet is a collection of data, with the first entry defining what sort of packet it is. For example, when an entity is spawned in the world, the server sends a Spawn Entity packet to the client and the client will render that entity at the given location with the given fields. A server that operates according the specifications expected by the vanilla (unmodified) client is referred to as a “Notchian” server.
It may be modified in any way, so long as the packets are sent and received in the same way as an unmodified server. This means that a server with Spigot plugins might not be considered vanilla (unmodified) but would still technically be a Notchian server. There’s a public packet specification wiki updated by members of the community and by the Mojang team (unofficially) that contains an updated list of every type of packet and other useful information (inventory slot IDs, some NBT data documentation, etc), called Wiki.vg.
This means that everything a plugin can do is limited by what the client can understand. The server can send anything to the client that’s supported as listed by the packet wiki, however. A mod, however, modifies the client and in most cases the server (to add support for whatever is modified in the client). If a new block is created, the modified server will send world data with that block, but unless the client already contains data in the form of a modification for that block (textures, models, etc) it will either ignore it or simply crash.
Getting Movement Controls
When the client moves the player, the updated location and velocity is sent to the server. The controls are handled by the client, and the position updating is actually done clientside. Of course, the server can send a packet back to cancel the position change on the client and simply not store the new position, but by default this isn’t the case. That’s why “hacks” are possible – they simply modify the client to send things such as staying in the air instead of following gravity (once again, done clientside) to the server.
Anticheats will double check the data to make sure it’s following (loosely) the same mechanics as a vanilla client. One feature which most plugin developers/server modifiers wish for on the client is the ability to get the exact WASD key presses. You could check if the player moves on a block in a certain block, but cancelling that move on the client will look strange as their viewpoint is shifted back to the starting point. One example where you may want to get just the movement controls without actually moving the player or doing your own movement calculations is a custom vehicle.
In this case, we could say a helicopter. You can send entity packets to the client to create a sort of helicopter out of entity models by positioning them correctly, but controls will be tricky. You could send a flight mode enabled packet to the client and allow them to move in all six directions and simply move the entities along with the player, but unfortunately the delay between the client moving clientside and the entities’ position being updated to the client will create an ugly delay (lagspikes, for example, could make the entities seem a little “jumpy”).There is another method of getting a player’s movement controls.
In vanilla Minecraft, there are vehicle entities such as the horse or minecart. When a player is mounted on a vehicle, movement controls are sent to the server to process and move the vehicle accordingly. This is because vehicles usually have different movement needs. A minecart, for example, must follow the tracks set down.
It *can* be done clientside, but because of the many calculations needed it’s much more efficient to simply take user input (that’s important) and update the velocity of the vehicle from that. What’s interesting, however, is that whenever the client is mounted on *any entity*, it sends these movement control packets to the server whether or not it means anything in vanilla Minecraft. Movement packets will be sent whether you’re sitting on a horse or an armorstand (the client can be mounted on any entity, not just vehicles). The packet for this is listed on the technical wiki as “Steer Vehicle”, and is also known as the “input” packet.
It contains the following data:
The first two fields are the forward/backward and left/right fields. The last tells you if the action is the player jumping (pressing space, used by horses usually) or unmounting (player dismounting is handled serverside). From this packet, you can tell the WASD controls and get the space/shift keys aswell. To apply this to the helicopter example above, you can send spawn entity packet with an invisible armorstand, then another packet to mount the player on that entity.
Once done, the client will send a movement packet every tick. From there you can listen for those packets either via ProtocolLib’s API or injecting your own listener (I prefer injection, especially so that an external dependency isn’t required). Get the forward/backward and left/right fields from the packet, and update the position of the helicopter entities and the mount entity, resulting in a synchronized client & entity movement feel. I first found this little trick from the Spigot forums.
One user had used a custom map renderer to allow you to move a rendered screen on a wall using the WASD controls. I plan to use a combination of these features to move a 2D game screen in an upcoming game I’ve been working on.