protocol updates, movement fixes

packet_map is now auto-generated
This commit is contained in:
extremeheat 2021-02-25 17:47:14 -05:00
commit b64a22a8fc
7 changed files with 531 additions and 42 deletions

View file

@ -1,17 +1,43 @@
/**
* This is a utility script that converts the YAML here into ProtoDef schema code and (soon) docs/typescript definitions.
* It also pre-compiles JS code from the schema for easier development.
*
* You can run this with `npm run build`
*
*/
const fs = require('fs')
const { ProtoDefCompiler } = require('protodef').Compiler
let compile
try {
compile = require('protodef-yaml/compiler').compile
} catch (e) {
require('child_process').execSync('npx protodef-yaml proto.yml protocol.json')
}
function genProtoSchema() {
const { parse, compile } = require('protodef-yaml/compiler')
// Create the packet_map.yml from proto.yml
const parsed = parse('./proto.yml')
const packets = []
for (const key in parsed) {
if (key.startsWith('%container')) {
const [, name] = key.split(',')
if (name.startsWith('packet_')) {
const children = parsed[key]
const packetName = name.replace('packet_', '')
const packetID = children['!id']
packets.push([packetID, packetName, name])
}
}
}
let l1 = l2 = ''
for (const [id,name,fname] of packets) {
l1 += ` 0x${id.toString(16).padStart(2, '0')}: ${name}\n`
l2 += ` if ${name}: ${fname}\n`
}
const t = `!import: types.yaml\nmcpe_packet:\n name: varint =>\n${l1}\n params: name ?\n${l2}`
fs.writeFileSync('./packet_map.yml', t)
if (compile) {
compile('./proto.yml', 'protocol.json')
}
genProtoSchema()
fs.writeFileSync('../newproto.json', JSON.stringify({ types: require('./protocol.json') }, null, 2))
fs.unlinkSync('./protocol.json') //remove temp file

View file

@ -1,4 +1,3 @@
!import: types.yaml
mcpe_packet:
name: varint =>
@ -64,6 +63,7 @@ mcpe_packet:
0x3e: set_player_game_type
0x3f: player_list
0x40: simple_event
0x41: event
0x42: spawn_experience_orb
0x43: clientbound_map_item_data
0x44: map_info_request
@ -141,6 +141,7 @@ mcpe_packet:
0x9c: packet_violation_warning
0xa2: item_component
0xa3: filter_text_packet
params: name ?
if login: packet_login
if play_status: packet_play_status
@ -204,6 +205,7 @@ mcpe_packet:
if set_player_game_type: packet_set_player_game_type
if player_list: packet_player_list
if simple_event: packet_simple_event
if event: packet_event
if spawn_experience_orb: packet_spawn_experience_orb
if clientbound_map_item_data: packet_clientbound_map_item_data
if map_info_request: packet_map_info_request
@ -280,4 +282,4 @@ mcpe_packet:
if update_player_game_type: packet_update_player_game_type
if packet_violation_warning: packet_packet_violation_warning
if item_component: packet_item_component
if filter_text_packet: packet_filter_text_packet
if filter_text_packet: packet_filter_text_packet

View file

@ -453,19 +453,52 @@ packet_move_entity:
position: vec3f
rotation: Rotation
# MovePlayer is sent by players to send their movement to the server, and by the server to update the
# movement of player entities to other players.
packet_move_player:
!id: 0x13
!bound: both
runtime_entity_id: varint
x: lf32
y: lf32
z: lf32
# EntityRuntimeID is the runtime ID of the player. The runtime ID is unique for each world session, and
# entities are generally identified in packets using this runtime ID.
runtime_id: varint
# Position is the position to spawn the player on. If the player is on a distance that the viewer cannot
# see it, the player will still show up if the viewer moves closer.
position: vec3f
# Pitch is the vertical rotation of the player. Facing straight forward yields a pitch of 0. Pitch is
# measured in degrees.
pitch: lf32
# Yaw is the horizontal rotation of the player. Yaw is also measured in degrees
yaw: lf32
# HeadYaw is the same as Yaw, except that it applies specifically to the head of the player. A different
# value for HeadYaw than Yaw means that the player will have its head turned
head_yaw: lf32
mode: u8
# Mode is the mode of the movement. It specifies the way the player's movement should be shown to other
# players. It is one of the constants below.
mode: u8 =>
0: normal
1: reset
2: teleport
3: rotation
# OnGround specifies if the player is considered on the ground. Note that proxies or hacked clients could
# fake this to always be true, so it should not be taken for granted.
on_ground: bool
other_runtime_entity_id: varint
# RiddenEntityRuntimeID is the runtime ID of the entity that the player might currently be riding. If not
# riding, this should be left 0.
ridden_runtime_id: varint
teleport: mode ?
if teleport:
# TeleportCause is written only if Mode is MoveModeTeleport. It specifies the cause of the teleportation,
# which is one of the constants above.
cause: li32 =>
0: unknown
1: projectile
2: chorus_fruit
3: command
4: behavior
# TeleportSourceEntityType is the entity type that caused the teleportation, for example an ender pearl.
# TODO: is this still a integer and not a string?
source_entity_type: LegacyEntityType
tick: varint64
packet_rider_jump:
!id: 0x14
@ -489,10 +522,20 @@ packet_add_painting:
direction: zigzag32
title: string
# TickSync is sent by the client and the server to maintain a synchronized, server-authoritative tick between
# the client and the server. The client sends this packet first, and the server should reply with another one
# of these packets, including the response time.
packet_tick_sync:
!id: 0x17
!bound: both
# ClientRequestTimestamp is the timestamp on which the client sent this packet to the server. The server
# should fill out that same value when replying.
# The ClientRequestTimestamp is always 0
request_time: li64
# ServerReceptionTimestamp is the timestamp on which the server received the packet sent by the client.
# When the packet is sent by the client, this value is 0.
# ServerReceptionTimestamp is generally the current tick of the server. It isn't an actual timestamp, as
# the field implies
response_time: li64
packet_level_sound_event_old:
@ -1037,6 +1080,33 @@ packet_simple_event:
!bound: client
event_type: lu16
# Event is sent by the server to send an event with additional data. It is typically sent to the client for
# telemetry reasons, much like the SimpleEvent packet.
packet_event:
!id: 0x41
!bound: client
runtime_id: varint64
event_type: varint =>
0: achievement_awarded
1: entity_interact
2: portal_built
3: portal_used
4: mob_killed
5: cauldron_used
6: player_death
7: boss_killed
8: agent_command
9: agent_created
10: banner_pattern_removed
11: commaned_executed
12: fish_bucketed
13: mob_born
14: pet_died
15: cauldron_block_used
16: composter_block_used
17: bell_block_used
use_player_id: u8
packet_spawn_experience_orb:
!id: 0x42
!bound: client
@ -1374,7 +1444,7 @@ packet_update_trade:
window_id: u8
# WindowType is an identifier specifying the type of the window opened. In vanilla, it appears this is
# always filled out with 15.
window_type: u8
window_type: WindowType
# Size is the amount of trading options that the villager has.
size: varint
# TradeTier is the tier of the villager that the player is trading with. The tier starts at 0 with a
@ -1412,7 +1482,7 @@ packet_update_equipment:
window_id: u8
# WindowType is the type of the window that was opened. Generally, this is the type of a horse inventory,
# as the packet is specifically made for that.
window_type: u8
window_type: WindowType
# Size is the size of the horse inventory that should be opened. A bigger size does, in fact, change the
# amount of slots displayed.
size: u8

View file

@ -673,3 +673,142 @@ CommandOrigin:
if dev_console or test:
player_entity_id: zigzag64
WindowType: u8 =>
0: container
1: workbench
2: furnace
3: enchantment
4: brewing_stand
5: anvil
6: dispenser
7: dropper
8: hopper
9: cauldron
10: minecart_chest
11: minecart_hopper
12: horse
13: beacon
14: structure_editor
15: trading
16: command_block
17: jukebox
18: armor
19: hand
20: compound_creator
21: element_constructor
22: material_reducer
23: lab_table
24: loom
25: lectern
26: grindstone
27: blast_furnace
28: smoker
29: stonecutter
30: cartography
31: hud
32: jigsaw_editor
33: smithing_table
# TODO: remove?
LegacyEntityType: li32 =>
10: chicken
11: cow
12: pig
13: sheep
14: wolf
15: villager
16: mooshroom
17: squid
18: rabbit
19: bat
20: iron_golem
21: snow_golem
22: ocelot
23: horse
24: donkey
25: mule
26: skeleton_horse
27: zombie_horse
28: polar_bear
29: llama
30: parrot
31: dolphin
32: zombie
33: creeper
34: skeleton
35: spider
36: zombie_pigman
37: slime
38: enderman
39: silverfish
40: cave_spider
41: ghast
42: magma_cube
43: blaze
44: zombie_villager
45: witch
46: stray
47: husk
48: wither_skeleton
49: guardian
50: elder_guardian
51: npc
52: wither
53: ender_dragon
54: shulker
55: endermite
56: agent # LEARN_TO_CODE_MASCOT
57: vindicator
58: phantom
61: armor_stand
62: tripod_camera
63: player
64: item
65: tnt
66: falling_block
67: moving_block
68: xp_bottle
69: xp_orb
70: eye_of_ender_signal
71: ender_crystal
72: fireworks_rocket
73: thrown_trident
74: turtle
75: cat
76: shulker_bullet
77: fishing_hook
78: chalkboard
79: dragon_fireball
80: arrow
81: snowball
82: egg
83: painting
84: minecart
85: fireball
86: splash_potion
87: ender_pearl
88: leash_knot
89: wither_skull
90: boat
91: wither_skull_dangerous
93: lightning_bolt
94: small_fireball
95: area_effect_cloud
96: hopper_minecart
97: tnt_minecart
98: chest_minecart
100: command_block_minecart
101: lingering_potion
102: llama_spit
103: evocation_fang
104: evocation_illager
105: vex
106: ice_bomb
107: balloon
108: pufferfish
109: salmon
110: drowned
111: tropicalfish
112: cod
113: panda

View file

@ -2371,6 +2371,156 @@
}
]
],
"WindowType": [
"mapper",
{
"type": "u8",
"mappings": {
"0": "container",
"1": "workbench",
"2": "furnace",
"3": "enchantment",
"4": "brewing_stand",
"5": "anvil",
"6": "dispenser",
"7": "dropper",
"8": "hopper",
"9": "cauldron",
"10": "minecart_chest",
"11": "minecart_hopper",
"12": "horse",
"13": "beacon",
"14": "structure_editor",
"15": "trading",
"16": "command_block",
"17": "jukebox",
"18": "armor",
"19": "hand",
"20": "compound_creator",
"21": "element_constructor",
"22": "material_reducer",
"23": "lab_table",
"24": "loom",
"25": "lectern",
"26": "grindstone",
"27": "blast_furnace",
"28": "smoker",
"29": "stonecutter",
"30": "cartography",
"31": "hud",
"32": "jigsaw_editor",
"33": "smithing_table"
}
}
],
"LegacyEntityType": [
"mapper",
{
"type": "li32",
"mappings": {
"10": "chicken",
"11": "cow",
"12": "pig",
"13": "sheep",
"14": "wolf",
"15": "villager",
"16": "mooshroom",
"17": "squid",
"18": "rabbit",
"19": "bat",
"20": "iron_golem",
"21": "snow_golem",
"22": "ocelot",
"23": "horse",
"24": "donkey",
"25": "mule",
"26": "skeleton_horse",
"27": "zombie_horse",
"28": "polar_bear",
"29": "llama",
"30": "parrot",
"31": "dolphin",
"32": "zombie",
"33": "creeper",
"34": "skeleton",
"35": "spider",
"36": "zombie_pigman",
"37": "slime",
"38": "enderman",
"39": "silverfish",
"40": "cave_spider",
"41": "ghast",
"42": "magma_cube",
"43": "blaze",
"44": "zombie_villager",
"45": "witch",
"46": "stray",
"47": "husk",
"48": "wither_skeleton",
"49": "guardian",
"50": "elder_guardian",
"51": "npc",
"52": "wither",
"53": "ender_dragon",
"54": "shulker",
"55": "endermite",
"56": "agent",
"57": "vindicator",
"58": "phantom",
"61": "armor_stand",
"62": "tripod_camera",
"63": "player",
"64": "item",
"65": "tnt",
"66": "falling_block",
"67": "moving_block",
"68": "xp_bottle",
"69": "xp_orb",
"70": "eye_of_ender_signal",
"71": "ender_crystal",
"72": "fireworks_rocket",
"73": "thrown_trident",
"74": "turtle",
"75": "cat",
"76": "shulker_bullet",
"77": "fishing_hook",
"78": "chalkboard",
"79": "dragon_fireball",
"80": "arrow",
"81": "snowball",
"82": "egg",
"83": "painting",
"84": "minecart",
"85": "fireball",
"86": "splash_potion",
"87": "ender_pearl",
"88": "leash_knot",
"89": "wither_skull",
"90": "boat",
"91": "wither_skull_dangerous",
"93": "lightning_bolt",
"94": "small_fireball",
"95": "area_effect_cloud",
"96": "hopper_minecart",
"97": "tnt_minecart",
"98": "chest_minecart",
"100": "command_block_minecart",
"101": "lingering_potion",
"102": "llama_spit",
"103": "evocation_fang",
"104": "evocation_illager",
"105": "vex",
"106": "ice_bomb",
"107": "balloon",
"108": "pufferfish",
"109": "salmon",
"110": "drowned",
"111": "tropicalfish",
"112": "cod",
"113": "panda"
}
}
],
"mcpe_packet": [
"container",
[
@ -2443,6 +2593,7 @@
"62": "set_player_game_type",
"63": "player_list",
"64": "simple_event",
"65": "event",
"66": "spawn_experience_orb",
"67": "clientbound_map_item_data",
"68": "map_info_request",
@ -2593,6 +2744,7 @@
"set_player_game_type": "packet_set_player_game_type",
"player_list": "packet_player_list",
"simple_event": "packet_simple_event",
"event": "packet_event",
"spawn_experience_orb": "packet_spawn_experience_orb",
"clientbound_map_item_data": "packet_clientbound_map_item_data",
"map_info_request": "packet_map_info_request",
@ -3534,20 +3686,12 @@
"container",
[
{
"name": "runtime_entity_id",
"name": "runtime_id",
"type": "varint"
},
{
"name": "x",
"type": "lf32"
},
{
"name": "y",
"type": "lf32"
},
{
"name": "z",
"type": "lf32"
"name": "position",
"type": "vec3f"
},
{
"name": "pitch",
@ -3563,15 +3707,67 @@
},
{
"name": "mode",
"type": "u8"
"type": [
"mapper",
{
"type": "u8",
"mappings": {
"0": "normal",
"1": "reset",
"2": "teleport",
"3": "rotation"
}
}
]
},
{
"name": "on_ground",
"type": "bool"
},
{
"name": "other_runtime_entity_id",
"name": "ridden_runtime_id",
"type": "varint"
},
{
"anon": true,
"type": [
"switch",
{
"compareTo": "mode",
"fields": {
"teleport": [
"container",
[
{
"name": "teleport_cause",
"type": [
"mapper",
{
"type": "li32",
"mappings": {
"0": "unknown",
"1": "projectile",
"2": "chorus_fruit",
"3": "command",
"4": "behavior"
}
}
]
},
{
"name": "source_entity_type",
"type": "LegacyEntityType"
}
]
]
},
"default": "void"
}
]
},
{
"name": "tick",
"type": "varint64"
}
]
],
@ -4572,6 +4768,48 @@
}
]
],
"packet_event": [
"container",
[
{
"name": "runtime_id",
"type": "varint64"
},
{
"name": "event_type",
"type": [
"mapper",
{
"type": "varint",
"mappings": {
"0": "achievement_awarded",
"1": "entity_interact",
"2": "portal_built",
"3": "portal_used",
"4": "mob_killed",
"5": "cauldron_used",
"6": "player_death",
"7": "boss_killed",
"8": "agent_command",
"9": "agent_created",
"10": "pattern_removed",
"11": "commaned_executed",
"12": "fish_bucketed",
"13": "mob_born",
"14": "pet_died",
"15": "cauldron_block_used",
"16": "composter_block_used",
"17": "bell_block_used"
}
}
]
},
{
"name": "use_player_id",
"type": "u8"
}
]
],
"packet_spawn_experience_orb": [
"container",
[
@ -5115,7 +5353,7 @@
},
{
"name": "window_type",
"type": "u8"
"type": "WindowType"
},
{
"name": "size",
@ -5160,7 +5398,7 @@
},
{
"name": "window_type",
"type": "u8"
"type": "WindowType"
},
{
"name": "size",

View file

@ -4,6 +4,7 @@
"description": "Parse and serialize Minecraft Bedrock Edition packets",
"main": "index.js",
"scripts": {
"build": "cd data/new && node compile.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [

View file

@ -54,6 +54,7 @@ server.on('connect', ({ client }) => {
client.queue('player_list', require('./packets/player_list.json'))
client.queue('start_game', require('./packets/start_game.json'))
client.queue('item_component', {"entries":[]})
client.queue('set_spawn_position', require('./packets/set_spawn_position.json'))
client.queue('set_time', { time: 5433771 })
client.queue('set_difficulty', { difficulty: 1 })
client.queue('set_commands_enabled', { enabled: true })
@ -64,24 +65,28 @@ server.on('connect', ({ client }) => {
client.queue('update_attributes', require('./packets/update_attributes.json'))
client.queue('creative_content', require('./packets/creative_content.json'))
client.queue('inventory_content', require('./packets/inventory_content.json'))
client.queue('player_hotbar', {"selected_slot":3,"window_id":0,"select_slot":true})
client.queue('crafting_data', require('./packets/crafting_data.json'))
client.queue('available_commands', require('./packets/available_commands.json'))
client.queue('chunk_radius_update', {"chunk_radius":5})
client.queue('set_entity_data', require('./packets/set_entity_data.json'))
client.queue('game_rules_changed', require('./packets/game_rules_changed.json'))
client.queue('respawn', {"x":646.9405517578125,"y":65.62001037597656,"z":77.86255645751953,"state":0,"runtime_entity_id":0})
// for (const file of fs.readdirSync('chunks')) {
// const buffer = Buffer.from(fs.readFileSync('./chunks/' + file, 'utf8'), 'hex')
// // console.log('Sending chunk', chunk)
// client.sendBuffer(buffer)
// }
for (const chunk of chunks) {
client.queue('level_chunk', chunk)
for (const file of fs.readdirSync('chunks')) {
const buffer = Buffer.from(fs.readFileSync('./chunks/' + file, 'utf8'), 'hex')
// console.log('Sending chunk', chunk)
client.sendBuffer(buffer)
}
// for (const chunk of chunks) {
// client.queue('level_chunk', chunk)
// }
setInterval(() => {
client.write('network_chunk_publisher_update', {"coordinates":{"x":646,"y":130,"z":77},"radius":64})
}, 9500)
@ -90,6 +95,14 @@ server.on('connect', ({ client }) => {
setTimeout(() => {
client.write('play_status', { status: 'player_spawn' })
}, 8000)
// Respond to tick synchronization packets
client.on('tick_sync', ({ request_time }) => {
client.queue('tick_sync', {
request_time,
response_time: BigInt(Date.now())
})
})
})
})
})
@ -101,7 +114,7 @@ async function sleep(ms) {
}
// CHUNKS
const { ChunkColumn, Version } = require('bedrock-provider')
// const { ChunkColumn, Version } = require('bedrock-provider')
const mcData = require('minecraft-data')('1.16')
var chunks = []
async function buildChunks() {
@ -133,4 +146,4 @@ async function buildChunks() {
// console.log('Chunks',chunks)
}
buildChunks()
// buildChunks()