From f3604fa9b543d554ccffef2ecfaca298b6e9a8ee Mon Sep 17 00:00:00 2001 From: extremeheat Date: Sun, 21 Feb 2021 15:22:04 -0500 Subject: [PATCH] protocol updates Add some updates --- data/new/proto.yml | 142 +++++++++++++++- data/new/types.yaml | 65 +++++++- data/newproto.json | 248 ++++++++++++++++++++++++++-- src/datatypes/compiler-minecraft.js | 58 ++++++- 4 files changed, 479 insertions(+), 34 deletions(-) diff --git a/data/new/proto.yml b/data/new/proto.yml index 6bbb0d5..b209d4f 100644 --- a/data/new/proto.yml +++ b/data/new/proto.yml @@ -200,7 +200,18 @@ packet_start_game: # 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_entity_id: varint64 - player_gamemode: zigzag32 + # PlayerGameMode is the game mode the player currently has. It is a value from 0-4, with 0 being + # survival mode, 1 being creative mode, 2 being adventure mode, 3 being survival spectator and 4 being + # creative spectator. + # This field may be set to 5 to make the client fall back to the game mode set in the WorldGameMode + # field. + player_gamemode: zigzag32 => + 0: survival + 1: creative + 2: adventure + 3: survival_spectator + 4: creative_spectator + 5: fallback # The spawn position of the player in the world. In servers this is often the same as the # world's spawn position found below. spawn: vec3f @@ -680,11 +691,25 @@ packet_mob_armor_equipment: leggings: Item boots: Item +# Interact is sent by the client when it interacts with another entity in some way. It used to be used for +# normal entity and block interaction, but this is no longer the case now. packet_interact: !id: 0x21 !bound: both - action_id: u8 + # Action type is the ID of the action that was executed by the player. It is one of the constants that + # may be found above. + action_id: u8 => + 3: leave_vehicle + 4: mouse_over_entity + 6: open_inventory + # TargetEntityRuntimeID is the runtime ID of the entity that the player interacted with. This is empty + # for the InteractActionOpenInventory action type. target_runtime_entity_id: varint + # Position associated with the ActionType above. For the InteractActionMouseOverEntity, this is the + # position relative to the entity moused over over which the player hovered with its mouse/touch. For the + # InteractActionLeaveVehicle, this is the position that the player spawns at after leaving the vehicle. + position: action_id ? + if mouse_over_entity or leave_vehicle: vec3f packet_block_pick_request: !id: 0x22 @@ -865,16 +890,75 @@ packet_gui_data_pick_item: !id: 0x36 !bound: client + +# AdventureSettings is sent by the server to update game-play related features, in particular permissions to +# access these features for the client. It includes allowing the player to fly, build and mine, and attack +# entities. Most of these flags should be checked server-side instead of using this packet only. +# The client may also send this packet to the server when it updates one of these settings through the +# in-game settings interface. The server should verify if the player actually has permission to update those +# settings. packet_adventure_settings: !id: 0x37 !bound: both - flags: varint - command_permission: varint - action_permissions: varint - permission_level: varint + # Flags is a set of flags that specify certain properties of the player, such as whether or not it can + # fly and/or move through blocks. It is one of the AdventureFlag constants above. + flags: AdventureFlags + # CommandPermissionLevel is a permission level that specifies the kind of commands that the player is + # allowed to use. + command_permission: varint32 => + 0: normal + 1: operator + 2: host + 3: automation + 4: admin + # ActionPermissions is, much like Flags, a set of flags that specify actions that the player is allowed + # to undertake, such as whether it is allowed to edit blocks, open doors etc. It is a combination of the + # ActionPermission constants above. + action_permissions: ActionPermissions + # PermissionLevel is the permission level of the player as it shows up in the player list built up using + # the PlayerList packet. It is one of the PermissionLevel constants above. + permission_level: varint => + 0: visitor + 1: member + 2: operator + 3: custom + # Custom permissions custom_stored_permissions: varint + # PlayerUniqueID is a unique identifier of the player. It appears it is not required to fill this field + # out with a correct value. Simply writing 0 seems to work. user_id: li64 +AdventureFlags: [ "bitflags", + { + "type": "varint", + "flags": { + "world_immutable": 1, + "no_pvp": 2, + "auto_jump": 0x20, + "allow_flight": 0x40, + "no_clip": 0x80, + "world_builder": 0x100, + "flying": 0x200, + "muted": 0x400 + } + } +] + +ActionPermissions: [ "bitflags", + { + "type": "varint", + "flags": { + "build_and_mine": 0x10001, + "doors_and_switches": 0x10002, + "open_containers": 0x10004, + "attack_players": 0x10008, + "attack_mobs": 0x10010, + "operator": 0x10020, + "teleport": 0x10080, + } + } +] + packet_block_entity_data: !id: 0x38 !bound: both @@ -1547,21 +1631,61 @@ packet_update_block_synced: unknown0: varint unknown1: varint +# MoveActorDelta is sent by the server to move an entity. The packet is specifically optimised to save as +# much space as possible, by only writing non-zero fields. +# As of 1.16.100, this packet no longer actually contains any deltas. packet_move_entity_delta: !id: 0x6f !bound: client - runtime_entity_id: varint - flags: lu16 + # EntityRuntimeID is the runtime ID of the entity that is being moved. The packet works provided a + # non-player entity with this runtime ID is present. + runtime_entity_id: varint64 + # Flags is a list of flags that specify what data is in the packet. + flags: DeltaMoveFlags + x: flags.has_x? + if true: lf32 + y: flags.has_y? + if true: lf32 + z: flags.has_z? + if true: lf32 + rot_x: flags.has_rot_x? + if true: u8 # TODO: * implement ByteFloat + rot_y: flags.has_rot_y? + if true: u8 + rot_z: flags.has_rot_z? + if true: u8 + +DeltaMoveFlags: [ "bitflags", + { + "type": "lu16", + "flags": { + "has_x": 0x01, + "has_y": 0x02, + "has_z": 0x04, + "has_rot_x": 0x08, + "has_rot_y": 0x10, + "has_rot_z": 0x20, + "on_ground": 0x40, + "teleport": 0x80, + "force_move": 0x100 + } + } +] packet_set_scoreboard_identity: !id: 0x70 !bound: client entries: ScoreboardIdentityEntries +# SetLocalPlayerAsInitialised is sent by the client in response to a PlayStatus packet with the status set +# to spawn. The packet marks the moment at which the client is fully initialised and can receive any packet +# without discarding it. packet_set_local_player_as_initialized: !id: 0x71 !bound: server - runtime_entity_id: varint + # EntityRuntimeID is the entity runtime ID the player was assigned earlier in the login sequence in the + # StartGame packet. + runtime_entity_id: varint64 packet_update_soft_enum: !id: 0x72 diff --git a/data/new/types.yaml b/data/new/types.yaml index a0835b1..8f6b109 100644 --- a/data/new/types.yaml +++ b/data/new/types.yaml @@ -328,25 +328,76 @@ Transaction: default: void transaction_data: transaction_type? if normal or inventory_mismatch: void + # UseItemTransactionData represents an inventory transaction data object sent when the client uses an item on + # a block. if item_use: - action_type: varint + # ActionType is the type of the UseItem inventory transaction. It is one of the action types found above, + # and specifies the way the player interacted with the block. + action_type: varint => + 0: click_block + 1: click_air + 2: break_block + # BlockPosition is the position of the block that was interacted with. This is only really a correct + # block position if ActionType is not UseItemActionClickAir. + block_position: BlockCoordinates + # BlockFace is the face of the block that was interacted with. When clicking the block, it is the face + # clicked. When breaking the block, it is the face that was last being hit until the block broke. face: varint + # HotBarSlot is the hot bar slot that the player was holding while clicking the block. It should be used + # to ensure that the hot bar slot and held item are correctly synchronised with the server. hotbar_slot: varint - item_in_hand: Item + # HeldItem is the item that was held to interact with the block. The server should check if this item + # is actually present in the HotBarSlot. + held_item: Item + # Position is the position of the player at the time of interaction. For clicking a block, this is the + # position at that time, whereas for breaking the block it is the position at the time of breaking. player_pos: vec3f + # ClickedPosition is the position that was clicked relative to the block's base coordinate. It can be + # used to find out exactly where a player clicked the block. click_pos: vec3f + # BlockRuntimeID is the runtime ID of the block that was clicked. It may be used by the server to verify + # that the player's world client-side is synchronised with the server's. block_runtime_id: varint + # UseItemOnEntityTransactionData represents an inventory transaction data object sent when the client uses + # an item on an entity. if item_use_on_entity: + # TargetEntityRuntimeID is the entity runtime ID of the target that was clicked. It is the runtime ID + # that was assigned to it in the AddEntity packet. entity_runtime_id: varint64 - action_type: varint + # ActionType is the type of the UseItemOnEntity inventory transaction. It is one of the action types + # found in the constants above, and specifies the way the player interacted with the entity. + action_type: varint => + 0: interact + 1: attack + # HotBarSlot is the hot bar slot that the player was holding while clicking the entity. It should be used + # to ensure that the hot bar slot and held item are correctly synchronised with the server. hotbar_slot: zigzag32 - item_in_hand: Item + # HeldItem is the item that was held to interact with the entity. The server should check if this item + # is actually present in the HotBarSlot. + held_item: Item + # Position is the position of the player at the time of clicking the entity. player_pos: vec3f - click_pos: vec3f + # ClickedPosition is the position that was clicked relative to the entity's base coordinate. It can be + # used to find out exactly where a player clicked the entity. + click_pos: vec3f + # ReleaseItemTransactionData represents an inventory transaction data object sent when the client releases + # the item it was using, for example when stopping while eating or stopping the charging of a bow. if item_release: - action_type: varint + # ActionType is the type of the ReleaseItem inventory transaction. It is one of the action types found + # in the constants above, and specifies the way the item was released. + # As of 1.13, the ActionType is always 0. This field can be ignored, because releasing food (by consuming + # it) or releasing a bow (to shoot an arrow) is essentially the same. + action_type: varint => + 0: release + 1: consume + # HotBarSlot is the hot bar slot that the player was holding while releasing the item. It should be used + # to ensure that the hot bar slot and held item are correctly synchronised with the server. hotbar_slot: zigzag32 - item_in_hand: Item + # HeldItem is the item that was released. The server should check if this item is actually present in the + # HotBarSlot. + held_item: Item + # HeadPosition is the position of the player's head at the time of releasing the item. This is used + # mainly for purposes such as spawning eating particles at that position. head_pos: vec3f ItemStacks: []varint diff --git a/data/newproto.json b/data/newproto.json index 671b939..689ff79 100644 --- a/data/newproto.json +++ b/data/newproto.json @@ -918,7 +918,21 @@ [ { "name": "action_type", - "type": "varint" + "type": [ + "mapper", + { + "type": "varint", + "mappings": { + "0": "click_block", + "1": "click_air", + "2": "break_block" + } + } + ] + }, + { + "name": "block_position", + "type": "BlockCoordinates" }, { "name": "face", @@ -929,7 +943,7 @@ "type": "varint" }, { - "name": "item_in_hand", + "name": "held_item", "type": "Item" }, { @@ -955,14 +969,23 @@ }, { "name": "action_type", - "type": "varint" + "type": [ + "mapper", + { + "type": "varint", + "mappings": { + "0": "interact", + "1": "attack" + } + } + ] }, { "name": "hotbar_slot", "type": "zigzag32" }, { - "name": "item_in_hand", + "name": "held_item", "type": "Item" }, { @@ -980,14 +1003,23 @@ [ { "name": "action_type", - "type": "varint" + "type": [ + "mapper", + { + "type": "varint", + "mappings": { + "0": "release", + "1": "consume" + } + } + ] }, { "name": "hotbar_slot", "type": "zigzag32" }, { - "name": "item_in_hand", + "name": "held_item", "type": "Item" }, { @@ -2993,7 +3025,20 @@ }, { "name": "player_gamemode", - "type": "zigzag32" + "type": [ + "mapper", + { + "type": "zigzag32", + "mappings": { + "0": "survival", + "1": "creative", + "2": "adventure", + "3": "survival_spectator", + "4": "creative_spectator", + "5": "fallback" + } + } + ] }, { "name": "spawn", @@ -3926,11 +3971,35 @@ [ { "name": "action_id", - "type": "u8" + "type": [ + "mapper", + { + "type": "u8", + "mappings": { + "3": "leave_vehicle", + "4": "mouse_over_entity", + "6": "open_inventory" + } + } + ] }, { "name": "target_runtime_entity_id", "type": "varint" + }, + { + "name": "position", + "type": [ + "switch", + { + "compareTo": "action_id", + "fields": { + "mouse_over_entity": "vec3f", + "leave_vehicle": "vec3f" + }, + "default": "void" + } + ] } ] ], @@ -4308,19 +4377,42 @@ [ { "name": "flags", - "type": "varint" + "type": "AdventureFlags" }, { "name": "command_permission", - "type": "varint" + "type": [ + "mapper", + { + "type": "varint32", + "mappings": { + "0": "normal", + "1": "operator", + "2": "host", + "3": "automation", + "4": "admin" + } + } + ] }, { "name": "action_permissions", - "type": "varint" + "type": "ActionPermissions" }, { "name": "permission_level", - "type": "varint" + "type": [ + "mapper", + { + "type": "varint", + "mappings": { + "0": "visitor", + "1": "member", + "2": "operator", + "3": "custom" + } + } + ] }, { "name": "custom_stored_permissions", @@ -5611,11 +5703,89 @@ [ { "name": "runtime_entity_id", - "type": "varint" + "type": "varint64" }, { "name": "flags", - "type": "lu16" + "type": "DeltaMoveFlags" + }, + { + "name": "x", + "type": [ + "switch", + { + "compareTo": "flags.has_x", + "fields": { + "true": "lf32" + }, + "default": "void" + } + ] + }, + { + "name": "y", + "type": [ + "switch", + { + "compareTo": "flags.has_y", + "fields": { + "true": "lf32" + }, + "default": "void" + } + ] + }, + { + "name": "z", + "type": [ + "switch", + { + "compareTo": "flags.has_z", + "fields": { + "true": "lf32" + }, + "default": "void" + } + ] + }, + { + "name": "rot_x", + "type": [ + "switch", + { + "compareTo": "flags.has_rot_x", + "fields": { + "true": "u8" + }, + "default": "void" + } + ] + }, + { + "name": "rot_y", + "type": [ + "switch", + { + "compareTo": "flags.has_rot_y", + "fields": { + "true": "u8" + }, + "default": "void" + } + ] + }, + { + "name": "rot_z", + "type": [ + "switch", + { + "compareTo": "flags.has_rot_z", + "fields": { + "true": "u8" + }, + "default": "void" + } + ] } ] ], @@ -5633,7 +5803,7 @@ [ { "name": "runtime_entity_id", - "type": "varint" + "type": "varint64" } ] ], @@ -5994,6 +6164,37 @@ "countType": "li32" } ], + "AdventureFlags": [ + "bitflags", + { + "type": "varint", + "flags": { + "world_immutable": 1, + "no_pvp": 2, + "auto_jump": 32, + "allow_flight": 64, + "no_clip": 128, + "world_builder": 256, + "flying": 512, + "muted": 1024 + } + } + ], + "ActionPermissions": [ + "bitflags", + { + "type": "varint", + "flags": { + "build_and_mine": 65537, + "doors_and_switches": 65538, + "open_containers": 65540, + "attack_players": 65544, + "attack_mobs": 65552, + "operator": 65568, + "teleport": 65664 + } + } + ], "CommandFlags": [ "bitfield", [ @@ -6013,6 +6214,23 @@ "signed": false } ] + ], + "DeltaMoveFlags": [ + "bitflags", + { + "type": "lu16", + "flags": { + "has_x": 1, + "has_y": 2, + "has_z": 4, + "has_rot_x": 8, + "has_rot_y": 16, + "has_rot_z": 32, + "on_ground": 64, + "teleport": 128, + "force_move": 256 + } + } ] } } \ No newline at end of file diff --git a/src/datatypes/compiler-minecraft.js b/src/datatypes/compiler-minecraft.js index 7130749..8d1e10f 100644 --- a/src/datatypes/compiler-minecraft.js +++ b/src/datatypes/compiler-minecraft.js @@ -45,18 +45,70 @@ Read.nbt = ['native', minecraft.nbt[0]] Write.nbt = ['native', minecraft.nbt[1]] SizeOf.nbt = ['native', minecraft.nbt[2]] +/** + * Bits + */ +// nvm, +// Read.bitflags = ['parametrizable', (compiler, { type, flags }) => { + // return compiler.wrapCode(` + // const { value, size } = ${compiler.callType('buffer, offset', type)} + // const val = {} + // for (let i = 0; i < size; i++) { + // const hi = (value >> i) & 1 + // if () + // const v = value & + // if (flags[i]) + // } + // ` +// }] + +Read.bitflags = ['parametrizable', (compiler, { type, flags }) => { + return compiler.wrapCode(` + const { value: _value, size } = ${compiler.callType(type, 'offset')} + const value = { _value } + const flags = ${JSON.stringify(flags)} + for (const key in flags) { + value[key] = (_value & flags[key]) == flags[key] + } + return { value, size } + `.trim()) +}] + + +Write.bitflags = ['parametrizable', (compiler, { type, flags }) => { + return compiler.wrapCode(` + const flags = ${JSON.stringify(flags)} + let val = value._value + for (const key in flags) { + if (value[key]) val |= flags[key] + } + return (ctx.${type})(val, buffer, offset) + `.trim()) +}] + +SizeOf.bitflags = ['parametrizable', (compiler, { type, flags }) => { + return compiler.wrapCode(` + const flags = ${JSON.stringify(flags)} + let val = value._value + for (const key in flags) { + if (value[key]) val |= flags[key] + } + return (ctx.${type})(val) + `.trim()) +}] + /** * Command Packet * - used for determining the size of the following enum */ -Read.enum_size_based_on_values_len = ['parametrizable', (compiler, array) => { +Read.enum_size_based_on_values_len = ['parametrizable', (compiler) => { return compiler.wrapCode(js(() => { if (values_len <= 0xff) return { value: 'byte', size: 0 } if (values_len <= 0xffff) return { value: 'short', size: 0 } if (values_len <= 0xffffff) return { value: 'int', size: 0 } })) }] -Write.enum_size_based_on_values_len = ['parametrizable', (compiler, array) => { +Write.enum_size_based_on_values_len = ['parametrizable', (compiler) => { return str(() => { if (value.values_len <= 0xff) _enum_type = 'byte' else if (value.values_len <= 0xffff) _enum_type = 'short' @@ -64,7 +116,7 @@ Write.enum_size_based_on_values_len = ['parametrizable', (compiler, array) => { return offset }) }] -SizeOf.enum_size_based_on_values_len = ['parametrizable', (compiler, array) => { +SizeOf.enum_size_based_on_values_len = ['parametrizable', (compiler) => { return str(() => { if (value.values_len <= 0xff) _enum_type = 'byte' else if (value.values_len <= 0xffff) _enum_type = 'short'