Difference between revisions of "How to Write a Client"
Thejoshwolfe (talk | contribs) (→Digging: probably want those notes non-wiki formatted) |
|||
Line 77: | Line 77: | ||
== Digging == | == Digging == | ||
− | 1. send a start digging packet and an abort digging packet | + | 1. send a start digging packet and an abort digging packet |
− | 2. server sends you an update block telling you of your success or failure, after amount of time has passed. explanation of how time is computed below (note that you the client don't have to know about it.): | + | 2. server sends you an update block telling you of your success or failure, after amount of time has passed. explanation of how time is computed below (note that you the client don't have to know about it.): |
− | + | ||
− | + | 20 ticks per second | |
− | + | ||
− | 20 ticks per second | + | sum = 0 |
− | + | every tick, sum += strengthVsBlock | |
− | sum = 0 | + | when sum >= 1.0, block is broken |
− | every tick, sum += strengthVsBlock | + | |
− | when sum >= 1.0, block is broken | + | function strengthVsBlock(tool, block, underwater, on_ground) { |
− | + | if block.hardness < 0: | |
− | function strengthVsBlock(tool, block, underwater, on_ground) { | + | return 0 |
− | + | if not canHarvestBlock(tool, block): | |
− | + | return 1.0 / block.hardness / 100 | |
− | + | ||
− | + | mult = 1 | |
− | + | ||
− | + | if tool effective against block: | |
− | + | mult *= tool.effectiveness | |
− | + | if underwater: | |
− | + | mult /= 5 | |
− | + | if not on_ground: | |
− | + | mult /= 5 | |
− | + | ||
− | + | return mult / block.hardness / 30 | |
− | + | } | |
− | + | ||
− | } | + | function canHarvestBlock(block) { |
− | + | ||
− | + | if block material is not in (rock, iron, snow, snow_block): | |
− | function canHarvestBlock(block) { | + | return true |
− | + | else | |
− | + | return whether equipped item can harvest block | |
− | + | ||
− | + | } | |
− | + | ||
− | + | effectiveness against proper material | |
− | } | + | wood: 2.0 |
− | + | stone: 4.0 | |
− | effectiveness against proper material | + | iron: 6.0 |
− | wood: 2.0 | + | diamond: 8.0 |
− | stone: 4.0 | + | gold: 12.0 |
− | iron: 6.0 | + | |
− | diamond: 8.0 | + | |
− | gold: 12.0 | + | Shovel effective against: |
− | + | Block.grass, Block.dirt, Block.sand, Block.gravel, Block.snow, Block.blockSnow, Block.blockClay | |
− | + | ||
− | + | ||
− | Shovel effective against: | + | axe affective against: |
− | Block.grass, Block.dirt, Block.sand, Block.gravel, Block.snow, Block.blockSnow, Block.blockClay | + | |
− | + | Block.planks, Block.bookShelf, Block.wood, Block.crate | |
− | + | ||
− | axe affective against: | + | |
− | + | pick effective against: | |
− | Block.planks, Block.bookShelf, Block.wood, Block.crate | + | Block.cobblestone, Block.stairDouble, Block.stairSingle, Block.stone, Block.sandStone, Block.cobblestoneMossy, Block.oreIron, Block.blockSteel, Block.oreCoal, Block.blockGold, |
− | + | ||
− | + | Block.oreGold, Block.oreDiamond, Block.blockDiamond, Block.ice, Block.bloodStone, Block.oreLapis, Block.blockLapis | |
− | pick effective against: | + | |
− | + | pick can harvest: | |
− | + | switch(harvest level of tool): | |
− | + | case 3: | |
− | + | obsidian | |
− | pick can harvest: | + | case 2: |
− | + | diamond | |
− | + | diamondore | |
− | + | gold | |
− | + | goldore | |
− | + | redstone | |
− | + | redstoneglowing | |
− | + | case 1: | |
− | + | iron | |
− | + | ironore | |
− | + | lapis | |
− | + | lapisore | |
− | + | ||
− | + | if block.material is rock | |
− | + | return true | |
− | + | if block.material is iron | |
− | + | return true | |
− | + | ||
− | + | ||
− | + | return false | |
− | + | ||
− | + | shovel can harvest: | |
− | + | both types of snow = true | |
− | + | else false | |
− | + | ||
− | shovel can harvest: | + | axe can harvest: |
− | + | false | |
− | + | ||
− | + | ||
− | axe can harvest: | + | harvest level: |
− | + | wood: 0 | |
− | + | stone: 1 | |
− | + | iron: 2 | |
− | harvest level: | + | diamond: 3 |
− | wood: 0 | + | gold: 0 |
− | stone: 1 | + | |
− | iron: 2 | + | for the following fields, see https://github.com/superjoe30/mineflayer/blob/dev/resources/items.txt |
− | diamond: 3 | + | Block.hardness |
− | gold: 0 | + | block.material |
− | |||
− | for the following fields, see https://github.com/superjoe30/mineflayer/blob/dev/resources/items.txt | ||
− | Block.hardness | ||
− | block.material | ||
=== Left to test === | === Left to test === | ||
* Can you dig behind blocks? | * Can you dig behind blocks? | ||
− | |||
− | |||
− | |||
− | |||
== Placing/using stuff == | == Placing/using stuff == |
Revision as of 23:26, 5 March 2011
This tutorial is being created to document what it takes to write a stand-alone client to interact with a notchian server (version Beta 1.2_01). The tutorial is incomplete but will be updated whenever more information is discovered.
Contents
Before You Get Started
- Make sure you don't want to join or fork an existing project instead.
- Ponder how much work it will be.
- Download the latest official minecraft server and run it with authentication turned off. Bind it to localhost if you want.
Parsing the messages
Main article: Protocol
Sorry, but you have to be able to parse all the messages. Write all the message parsing code. If you don't, your client will effectively crash if the server sends you any message you can't parse.
Login
Connect to the server at localhost, port 25565.
Main article: Protocol FAQ
Paraphrase (with no authentication): send a 0x02, get a 0x02, send a 0x01, get a 0x01. Then you'll eventually get a 0x0D, and that's when the game really begins.
To test if this worked, connect to your localhost server with the notchian client and see if you can see your custom client appear and float in the air.
Getting the map chunks
Once you get your first 0x0D, you have to convince the server that you deserve to know about all the map chunks that are around you. The vanilla client will actually start sending 0x0D packets BEFORE receiving it's first 0x0D packet (while receiving 0x32 prechunks)
- Every 50 milliseconds, send one of 0x0A - 0x0D.
You will get several fragments of chunks at first that are very far away from your location. Ignore these, since the chunks they are in will be loaded fully later. Full chunks load spiraling outward from your location in a circular (not square) pattern, eventually creating a 20x20 square for a total of 400 chunks.
To test if this worked, count the number of 0x33 map chunk messages you get that are full-sized chunks (16,128,16). If you don't get any, or you only get a couple, you probably aren't sending the 0x0A - 0x0D properly.
Moving around
Send the server updates on your position (0x0A - 0x0D) at every 50 milliseconds. This section was written experimenting with sending only 0x0D messages. You can send position updates slower, but it causes health updates to be slower.
The server will mostly keep quiet about your position, which means that your movements are acceptable. If the server sends you your position, it means you've done something wrong. In this situation, you must apologize by sending back identical data or else the server will begin ignoring any future position updates. The jerking effect that happens in this event can be seen in notchian clients when trying to walk across missing chunks.
The walking animation is automatically displayed in notchian clients when you move around on the ground. There's no walking animation when you move straight up and down though.
Allowed super-human abilities
- Flying - float and move about in the air. You must set on-ground to false to move vertically. Gravity only exists if you say it exists.
- Walking in the air - walk off a cliff with on-ground set to true. The avatar will continue walking as if there were ground bellow him. Maybe the server doesn't even check any correlation between on-ground and proximity to the ground.
- Moving arbitrarily fast - update your position to move as far as you like with each step to move arbitrarily fast (as long as you have a clear path). Tested up to 1000m in a single step.
Restrictions
- You cannot intersect with solid blocks. Your bounding box is an apothem of 0.3m in the horizontal axises, and 1.74m from your feet to the top of your head.
- You cannot move through walls. If there are solid blocks in your way, you have to move around them no matter how fast you're going.
If you break any of the above rules, the server will send you your corrected position. Corrected positions preserve your on-ground status, even if you were trying to fall through the top of a solid block. Note that sometimes increasing the rate of position update messages will help the server follow along and not correct your position. 50 milliseconds seems to be a good update interval.
Look and Stance
Look is defined by yaw and pitch. Providing the yaw component to the server rotates your avatar's head about the vertical-axis. The notion of an avatar's body orientation seems to exist only in clients and updates automatically in notchian clients when players rotate their yaw and move around. Providing the pitch component rotates your avatar's head up and down. There seems to be no limit to the pitch, so providing a pitch of 180 degrees result in your avatar's head appearing upsidedown. See Protocol for how the yaw and pitch values are calibrated.
Modifying your stance seems to have no visible effect. Its purpose is known.
Fall damage
Fall damage happens when on-ground changes from false to true and the fall height is greater than some threshold around 4 meters. Fall height seems to be calculated by the difference between the point where on-ground changed to true and the highest point of the jump/fall/flight while on-ground was false.
TODO: can you avoid fall damage while flying if you set on-ground to true in the air?
Testing/Debugging
To test if moving around is working, watch your avatar move around from a notchian client.
If your avatar remains still despite your position update messages, then maybe you didn't apologize in response to the server correcting your position. Reconnect to give yourself another chance.
If your avatar is invisible to notchian clients despite the server sending you reasonable spawn coordinates, then maybe you died. Watch for 0x08 health updates to verify this cause. Try respawning or deleting the server's player file (world/players/<username>.dat) to recover from this situation.
Digging
1. send a start digging packet and an abort digging packet 2. server sends you an update block telling you of your success or failure, after amount of time has passed. explanation of how time is computed below (note that you the client don't have to know about it.): 20 ticks per second sum = 0 every tick, sum += strengthVsBlock when sum >= 1.0, block is broken function strengthVsBlock(tool, block, underwater, on_ground) { if block.hardness < 0: return 0 if not canHarvestBlock(tool, block): return 1.0 / block.hardness / 100 mult = 1 if tool effective against block: mult *= tool.effectiveness if underwater: mult /= 5 if not on_ground: mult /= 5 return mult / block.hardness / 30 } function canHarvestBlock(block) { if block material is not in (rock, iron, snow, snow_block): return true else return whether equipped item can harvest block } effectiveness against proper material wood: 2.0 stone: 4.0 iron: 6.0 diamond: 8.0 gold: 12.0 Shovel effective against: Block.grass, Block.dirt, Block.sand, Block.gravel, Block.snow, Block.blockSnow, Block.blockClay axe affective against: Block.planks, Block.bookShelf, Block.wood, Block.crate pick effective against: Block.cobblestone, Block.stairDouble, Block.stairSingle, Block.stone, Block.sandStone, Block.cobblestoneMossy, Block.oreIron, Block.blockSteel, Block.oreCoal, Block.blockGold, Block.oreGold, Block.oreDiamond, Block.blockDiamond, Block.ice, Block.bloodStone, Block.oreLapis, Block.blockLapis pick can harvest: switch(harvest level of tool): case 3: obsidian case 2: diamond diamondore gold goldore redstone redstoneglowing case 1: iron ironore lapis lapisore if block.material is rock return true if block.material is iron return true return false shovel can harvest: both types of snow = true else false axe can harvest: false harvest level: wood: 0 stone: 1 iron: 2 diamond: 3 gold: 0 for the following fields, see https://github.com/superjoe30/mineflayer/blob/dev/resources/items.txt Block.hardness block.material
Left to test
- Can you dig behind blocks?
Placing/using stuff
TODO: can you place blocks from behind walls?
Inventory and Crafting
When you log in, before you get a 0x0D, you will get a 0x68 Window Items with your complete inventory including armor. You will then get a series of 0x67 Set Slot messages repeating the information for each non-empty slot. The slot indexes of these messages are for an inventory window with the crafting zone empty.
TODO: more info.
Attacking mobs/players
To attack another player or mob, send a 0x07 with the entity's id. The range seems to be unlimited as long as there is a clear line of sight. Damage is calculated on the server side.
Chat
Main article: Chat
Health and Respawning
The server sends 0x08 health updates whenever your health changes. If the server sends a health value less than 1, you are dead. Your avatar will disappear a second or two after dying. The "fall over" animation is not automatic.
You can safely send a 0x09 Respawn immediately after you get a death notice. You'll be given a 0x0D and possibly other messages after you do.
If your health updates come much slower than on the notchian client, make sure your sending position updates fast enough.