Update wireshark dissector (#9621)
authorsfan5 <sfan5@live.de>
Tue, 14 Apr 2020 19:22:19 +0000 (21:22 +0200)
committerGitHub <noreply@github.com>
Tue, 14 Apr 2020 19:22:19 +0000 (21:22 +0200)
util/wireshark/minetest.lua

index 1346adbc3597c70c30e49a5926388582020a640e..13cd6d48264411dba093fb255888a87887f29c2e 100644 (file)
 --
 
 
+-- Wireshark documentation:
+-- https://web.archive.org/web/20170711121726/https://www.wireshark.org/docs/wsdg_html_chunked/lua_module_Proto.html
+-- https://web.archive.org/web/20170711121844/https://www.wireshark.org/docs/wsdg_html_chunked/lua_module_Tree.html
+-- https://web.archive.org/web/20170711121917/https://www.wireshark.org/docs/wsdg_html_chunked/lua_module_Tvb.html
+
+
 -- Table of Contents:
---   Part 1: Client command dissectors (TOSERVER_*)
---   Part 2: Server command dissectors (TOCLIENT_*)
---   Part 3: Wrapper protocol subdissectors
---   Part 4: Wrapper protocol main dissector
---   Part 5: Utility functions
+--   Part 1: Utility functions
+--   Part 2: Client command dissectors (TOSERVER_*)
+--   Part 3: Server command dissectors (TOCLIENT_*)
+--   Part 4: Wrapper protocol subdissectors
+--   Part 5: Wrapper protocol main dissector
+--   Part 6: Utility functions part 2
+
+
+-----------------------
+-- Part 1            --
+-- Utility functions --
+-----------------------
+
+-- Creates two ProtoFields to hold a length and variable-length text content
+-- lentype must be either "uint16" or "uint32"
+function minetest_field_helper(lentype, name, abbr)
+       local f_textlen = ProtoField[lentype](name .. "len", abbr .. " (length)", base.DEC)
+       local f_text = ProtoField.string(name, abbr)
+       return f_textlen, f_text
+end
 
 
 
 
 --------------------------------------------
--- Part 1                                 --
+-- Part 2                                 --
 -- Client command dissectors (TOSERVER_*) --
 --------------------------------------------
 
@@ -42,34 +63,71 @@ minetest_client_commands = {}
 minetest_client_obsolete = {}
 
 -- TOSERVER_INIT
-minetest_client_commands[0x02] = { "INIT", 2 }
 
 do
-       local f_ser_fmt = ProtoField.uint8("minetest.client.init_ser_version",
+       local abbr = "minetest.client.init_"
+
+       local f_ser_fmt = ProtoField.uint8(abbr.."ser_version",
                "Maximum serialization format version", base.DEC)
-       local f_player_name = ProtoField.stringz("minetest.client.init_player_name", "Player Name")
-       local f_password = ProtoField.stringz("minetest.client.init_password", "Password")
-       local f_version = ProtoField.uint16("minetest.client.init_version", "Version", base.DEC)
-
-       minetest_client_commands[0x10] = {
-               "INIT_LEGACY",                      -- Command name
-               53,                                 -- Minimum message length including code
-               { f_ser_fmt,                        -- List of fields [optional]
-                 f_player_name,
-                 f_password,
-                 f_version },
-               function(buffer, pinfo, tree, t)    -- Dissector function [optional]
+       local f_comp_modes = ProtoField.uint16(abbr.."compression",
+               "Supported compression modes", base.DEC, { [0] = "No compression" })
+       local f_proto_min = ProtoField.uint16(abbr.."proto_min", "Minimum protocol version", base.DEC)
+       local f_proto_max = ProtoField.uint16(abbr.."_proto_max", "Maximum protocol version", base.DEC)
+       local f_player_namelen, f_player_name =
+               minetest_field_helper("uint16", abbr.."player_name", "Player Name")
+
+       minetest_client_commands[0x02] = {
+               "INIT",                            -- Command name
+               11,                                -- Minimum message length including code
+               { f_ser_fmt,                       -- List of fields [optional]
+                 f_comp_modes,
+                 f_proto_min,
+                 f_proto_max,
+                 f_player_namelen,
+                 f_player_name },
+               function(buffer, pinfo, tree, t)   -- Dissector function [optional]
                        t:add(f_ser_fmt, buffer(2,1))
-                       t:add(f_player_name, buffer(3,20))
-                       t:add(f_password, buffer(23,28))
-                       t:add(f_version, buffer(51,2))
+                       t:add(f_comp_modes, buffer(3,2))
+                       t:add(f_proto_min, buffer(5,2))
+                       t:add(f_proto_max, buffer(7,2))
+                       minetest_decode_helper_ascii(buffer, t, "uint16", 9, f_player_namelen, f_player_name)
                end
        }
 end
 
+-- TOSERVER_INIT_LEGACY (obsolete)
+
+minetest_client_commands[0x10] = { "INIT_LEGACY", 53 }
+minetest_client_obsolete[0x10] = true
+
 -- TOSERVER_INIT2
 
-minetest_client_commands[0x11] = { "INIT2", 2 }
+do
+       local f_langlen, f_lang =
+               minetest_field_helper("uint16", "minetest.client.init2_language", "Language Code")
+
+       minetest_client_commands[0x11] = {
+               "INIT2",
+               2,
+               { f_langlen,
+                 f_lang },
+               function(buffer, pinfo, tree, t)
+                       minetest_decode_helper_ascii(buffer, t, "uint16", 2, f_langlen, f_lang)
+               end
+       }
+end
+
+-- TOSERVER_MODCHANNEL_JOIN
+
+minetest_client_commands[0x17] = { "MODCHANNEL_JOIN", 2 }
+
+-- TOSERVER_MODCHANNEL_LEAVE
+
+minetest_client_commands[0x18] = { "MODCHANNEL_LEAVE", 2 }
+
+-- TOSERVER_MODCHANNEL_MSG
+
+minetest_client_commands[0x19] = { "MODCHANNEL_MSG", 2 }
 
 -- TOSERVER_GETBLOCK (obsolete)
 
@@ -89,18 +147,24 @@ minetest_client_obsolete[0x22] = true
 -- TOSERVER_PLAYERPOS
 
 do
-       local f_x = ProtoField.int32("minetest.client.playerpos_x", "Position X", base.DEC)
-       local f_y = ProtoField.int32("minetest.client.playerpos_y", "Position Y", base.DEC)
-       local f_z = ProtoField.int32("minetest.client.playerpos_z", "Position Z", base.DEC)
-       local f_speed_x = ProtoField.int32("minetest.client.playerpos_speed_x", "Speed X", base.DEC)
-       local f_speed_y = ProtoField.int32("minetest.client.playerpos_speed_y", "Speed Y", base.DEC)
-       local f_speed_z = ProtoField.int32("minetest.client.playerpos_speed_z", "Speed Z", base.DEC)
-       local f_pitch = ProtoField.int32("minetest.client.playerpos_pitch", "Pitch", base.DEC)
-       local f_yaw = ProtoField.int32("minetest.client.playerpos_yaw", "Yaw", base.DEC)
+       local abbr = "minetest.client.playerpos_"
+
+       local f_x = ProtoField.int32(abbr.."x", "Position X", base.DEC)
+       local f_y = ProtoField.int32(abbr.."y", "Position Y", base.DEC)
+       local f_z = ProtoField.int32(abbr.."z", "Position Z", base.DEC)
+       local f_speed_x = ProtoField.int32(abbr.."speed_x", "Speed X", base.DEC)
+       local f_speed_y = ProtoField.int32(abbr.."speed_y", "Speed Y", base.DEC)
+       local f_speed_z = ProtoField.int32(abbr.."speed_z", "Speed Z", base.DEC)
+       local f_pitch = ProtoField.int32(abbr.."pitch", "Pitch", base.DEC)
+       local f_yaw = ProtoField.int32(abbr.."yaw", "Yaw", base.DEC)
+       local f_key_pressed = ProtoField.bytes(abbr.."key_pressed", "Pressed keys")
+       local f_fov = ProtoField.uint8(abbr.."fov", "FOV", base.DEC)
+       local f_wanted_range = ProtoField.uint8(abbr.."wanted_range", "Requested view range", base.DEC)
 
        minetest_client_commands[0x23] = {
                "PLAYERPOS", 34,
-               { f_x, f_y, f_z, f_speed_x, f_speed_y, f_speed_z, f_pitch, f_yaw },
+               { f_x, f_y, f_z, f_speed_x, f_speed_y, f_speed_z, f_pitch, f_yaw,
+                 f_key_pressed, f_fov, f_wanted_range },
                function(buffer, pinfo, tree, t)
                        t:add(f_x, buffer(2,4))
                        t:add(f_y, buffer(6,4))
@@ -110,6 +174,9 @@ do
                        t:add(f_speed_z, buffer(22,4))
                        t:add(f_pitch, buffer(26,4))
                        t:add(f_yaw, buffer(30,4))
+                       t:add(f_key_pressed, buffer(34,4))
+                       t:add(f_fov, buffer(38,1))
+                       t:add(f_wanted_range, buffer(39,1))
                end
        }
 end
@@ -148,7 +215,6 @@ do
 end
 
 -- TOSERVER_DELETEDBLOCKS
--- TODO: Test this
 
 do
        local f_count = ProtoField.uint8("minetest.client.deletedblocks_count", "Count", base.DEC)
@@ -186,122 +252,25 @@ end
 minetest_client_commands[0x26] = { "ADDNODE_FROM_INVENTORY", 2 }
 minetest_client_obsolete[0x26] = true
 
--- TOSERVER_CLICK_OBJECT
--- TODO: Test this
-
-do
-       local vs_button = {
-               [0] = "left",
-               [1] = "right"
-       }
-
-       local f_button = ProtoField.uint8("minetest.client.click_object_button", "Button", base.DEC, vs_button)
-       local f_blockpos_x = ProtoField.int16("minetest.client.click_object_blockpos_x", "Block position X", base.DEC)
-       local f_blockpos_y = ProtoField.int16("minetest.client.click_object_blockpos_y", "Block position Y", base.DEC)
-       local f_blockpos_z = ProtoField.int16("minetest.client.click_object_blockpos_z", "Block position Z", base.DEC)
-       local f_id = ProtoField.int16("minetest.client.click_object_id", "ID", base.DEC)
-       local f_item = ProtoField.uint16("minetest.client.click_object_item", "Item", base.DEC)
-
-       minetest_client_commands[0x27] = {
-               "CLICK_OBJECT", 13,
-               { f_button, f_blockpos_x, f_blockpos_y, f_blockpos_z, f_id, f_item },
-               function(buffer, pinfo, tree, t)
-                       t:add(f_button, buffer(2,1))
-                       t:add(f_blockpos_x, buffer(3,2))
-                       t:add(f_blockpos_y, buffer(5,2))
-                       t:add(f_blockpos_z, buffer(7,2))
-                       t:add(f_id, buffer(9,2))
-                       t:add(f_item, buffer(11,2))
-               end
-       }
-end
-
--- TOSERVER_GROUND_ACTION
+-- TOSERVER_CLICK_OBJECT (obsolete)
 
-do
-       local vs_action = {
-               [0] = "Start digging",
-               [1] = "Place block",
-               [2] = "Stop digging",
-               [3] = "Digging completed"
-       }
+minetest_client_commands[0x27] = { "CLICK_OBJECT", 2 }
+minetest_client_obsolete[0x27] = true
 
-       local f_action = ProtoField.uint8("minetest.client.ground_action", "Action", base.DEC, vs_action)
-       local f_nodepos_undersurface_x = ProtoField.int16(
-               "minetest.client.ground_action_nodepos_undersurface_x",
-               "Node position (under surface) X")
-       local f_nodepos_undersurface_y = ProtoField.int16(
-               "minetest.client.ground_action_nodepos_undersurface_y",
-               "Node position (under surface) Y")
-       local f_nodepos_undersurface_z = ProtoField.int16(
-               "minetest.client.ground_action_nodepos_undersurface_z",
-               "Node position (under surface) Z")
-       local f_nodepos_abovesurface_x = ProtoField.int16(
-               "minetest.client.ground_action_nodepos_abovesurface_x",
-               "Node position (above surface) X")
-       local f_nodepos_abovesurface_y = ProtoField.int16(
-               "minetest.client.ground_action_nodepos_abovesurface_y",
-               "Node position (above surface) Y")
-       local f_nodepos_abovesurface_z = ProtoField.int16(
-               "minetest.client.ground_action_nodepos_abovesurface_z",
-               "Node position (above surface) Z")
-       local f_item = ProtoField.uint16("minetest.client.ground_action_item", "Item")
+-- TOSERVER_GROUND_ACTION (obsolete)
 
-       minetest_client_commands[0x28] = {
-               "GROUND_ACTION", 17,
-               { f_action,
-                 f_nodepos_undersurface_x,
-                 f_nodepos_undersurface_y,
-                 f_nodepos_undersurface_z,
-                 f_nodepos_abovesurface_x,
-                 f_nodepos_abovesurface_y,
-                 f_nodepos_abovesurface_z,
-                 f_item },
-               function(buffer, pinfo, tree, t)
-                       t:add(f_action, buffer(2,1))
-                       t:add(f_nodepos_undersurface_x, buffer(3,2))
-                       t:add(f_nodepos_undersurface_y, buffer(5,2))
-                       t:add(f_nodepos_undersurface_z, buffer(7,2))
-                       t:add(f_nodepos_abovesurface_x, buffer(9,2))
-                       t:add(f_nodepos_abovesurface_y, buffer(11,2))
-                       t:add(f_nodepos_abovesurface_z, buffer(13,2))
-                       t:add(f_item, buffer(15,2))
-               end
-       }
-end
+minetest_client_commands[0x28] = { "GROUND_ACTION", 2 }
+minetest_client_obsolete[0x28] = true
 
 -- TOSERVER_RELEASE (obsolete)
 
 minetest_client_commands[0x29] = { "RELEASE", 2 }
 minetest_client_obsolete[0x29] = true
 
--- TOSERVER_SIGNTEXT (old signs)
--- TODO: Test this or mark obsolete
+-- TOSERVER_SIGNTEXT (obsolete)
 
-do
-       local f_blockpos_x = ProtoField.int16("minetest.client.signtext_blockpos_x", "Block position X", base.DEC)
-       local f_blockpos_y = ProtoField.int16("minetest.client.signtext_blockpos_y", "Block position Y", base.DEC)
-       local f_blockpos_z = ProtoField.int16("minetest.client.signtext_blockpos_z", "Block position Z", base.DEC)
-       local f_id = ProtoField.int16("minetest.client.signtext_id", "ID", base.DEC)
-       local f_textlen = ProtoField.uint16("minetest.client.signtext_textlen", "Text length", base.DEC)
-       local f_text = ProtoField.string("minetest.client.signtext_text", "Text")
-
-       minetest_client_commands[0x30] = {
-               "SIGNTEXT", 12,
-               { f_blockpos_x, f_blockpos_y, f_blockpos_z, f_id, f_textlen, f_text },
-               function(buffer, pinfo, tree, t)
-                       t:add(f_blockpos_x, buffer(2,2))
-                       t:add(f_blockpos_y, buffer(4,2))
-                       t:add(f_blockpos_z, buffer(6,2))
-                       t:add(f_id, buffer(8,2))
-                       t:add(f_textlen, buffer(10,2))
-                       local textlen = buffer(10,2):uint()
-                       if minetest_check_length(buffer, 12 + textlen, t) then
-                               t:add(f_text, buffer, buffer(12,textlen))
-                       end
-               end
-       }
-end
+minetest_client_commands[0x30] = { "SIGNTEXT", 2 }
+minetest_client_obsolete[0x30] = true
 
 -- TOSERVER_INVENTORY_ACTION
 
@@ -336,53 +305,16 @@ do
        }
 end
 
--- TOSERVER_SIGNNODETEXT
+-- TOSERVER_SIGNNODETEXT (obsolete)
 
-do
-       local f_pos_x = ProtoField.int16("minetest.client.signnodetext_pos_x", "Block position X", base.DEC)
-       local f_pos_y = ProtoField.int16("minetest.client.signnodetext_pos_y", "Block position Y", base.DEC)
-       local f_pos_z = ProtoField.int16("minetest.client.signnodetext_pos_z", "Block position Z", base.DEC)
-       local f_textlen = ProtoField.uint16("minetest.client.signnodetext_textlen", "Text length", base.DEC)
-       local f_text = ProtoField.string("minetest.client.signnodetext_text", "Text")
-
-       minetest_client_commands[0x33] = {
-               "SIGNNODETEXT", 10,
-               { f_pos_x, f_pos_y, f_pos_z, f_textlen, f_text },
-               function(buffer, pinfo, tree, t)
-                       t:add(f_pos_x, buffer(2,2))
-                       t:add(f_pos_y, buffer(4,2))
-                       t:add(f_pos_z, buffer(6,2))
-                       t:add(f_textlen, buffer(8,2))
-                       local textlen = buffer(8,2):uint()
-                       if minetest_check_length(buffer, 10 + textlen, t) then
-                               t:add(f_text, buffer(10, textlen))
-                       end
-               end
-       }
-end
+minetest_client_commands[0x33] = { "SIGNNODETEXT", 2 }
+minetest_client_obsolete[0x33] = true
 
--- TOSERVER_CLICK_ACTIVEOBJECT
 
-do
-       local vs_button = {
-               [0] = "left",
-               [1] = "right"
-       }
+-- TOSERVER_CLICK_ACTIVEOBJECT (obsolete)
 
-       local f_button = ProtoField.uint8("minetest.client.click_activeobject_button", "Button", base.DEC, vs_button)
-       local f_id = ProtoField.uint16("minetest.client.click_activeobject_id", "ID", base.DEC)
-       local f_item = ProtoField.uint16("minetest.client.click_activeobject_item", "Item", base.DEC)
-
-       minetest_client_commands[0x34] = {
-               "CLICK_ACTIVEOBJECT", 7,
-               { f_button, f_id, f_item },
-               function(buffer, pinfo, tree, t)
-                       t:add(f_button, buffer(2,1))
-                       t:add(f_id, buffer(3,2))
-                       t:add(f_item, buffer(5,2))
-               end
-       }
-end
+minetest_client_commands[0x34] = { "CLICK_ACTIVEOBJECT", 2 }
+minetest_client_obsolete[0x34] = true
 
 -- TOSERVER_DAMAGE
 
@@ -398,21 +330,10 @@ do
        }
 end
 
--- TOSERVER_PASSWORD
-
-do
-       local f_old_password = ProtoField.string("minetest.client.password_old", "Old password")
-       local f_new_password = ProtoField.string("minetest.client.password_new", "New password")
+-- TOSERVER_PASSWORD (obsolete)
 
-       minetest_client_commands[0x36] = {
-               "PASSWORD", 58,
-               { f_old_password, f_new_password },
-               function(buffer, pinfo, tree, t)
-                       t:add(f_old_password, buffer(2,28))
-                       t:add(f_new_password, buffer(30,28))
-               end
-       }
-end
+minetest_client_commands[0x36] = { "CLICK_ACTIVEOBJECT", 2 }
+minetest_client_obsolete[0x36] = true
 
 -- TOSERVER_PLAYERITEM
 
@@ -429,16 +350,135 @@ do
 end
 
 -- TOSERVER_RESPAWN
+
 minetest_client_commands[0x38] = { "RESPAWN", 2 }
 
-minetest_client_commands[0x39] = { "INTERACT", 2 }
+-- TOSERVER_INTERACT
+
+do
+       local abbr = "minetest.client.interact_"
+       local vs_action = {
+               [0] = "Start digging",
+               [1] = "Stop digging",
+               [2] = "Digging completed",
+               [3] = "Place block or item",
+               [4] = "Use item",
+               [5] = "Activate held item",
+       }
+       local vs_pointed_type = {
+               [0] = "Nothing",
+               [1] = "Node",
+               [2] = "Object",
+       }
+
+       local f_action = ProtoField.uint8(abbr.."action", "Action", base.DEC, vs_action)
+       local f_item = ProtoField.uint16(abbr.."item", "Item Index", base.DEC)
+       local f_plen = ProtoField.uint32(abbr.."plen", "Length of pointed thing", base.DEC)
+       local f_pointed_version = ProtoField.uint8(abbr.."pointed_version",
+               "Pointed Thing Version", base.DEC)
+       local f_pointed_type = ProtoField.uint8(abbr.."pointed_version",
+               "Pointed Thing Type", base.DEC, vs_pointed_type)
+       local f_pointed_under_x = ProtoField.int16(abbr.."pointed_under_x",
+               "Node position (under surface) X")
+       local f_pointed_under_y = ProtoField.int16(abbr.."pointed_under_y",
+               "Node position (under surface) Y")
+       local f_pointed_under_z = ProtoField.int16(abbr.."pointed_under_z",
+               "Node position (under surface) Z")
+       local f_pointed_above_x = ProtoField.int16(abbr.."pointed_above_x",
+               "Node position (above surface) X")
+       local f_pointed_above_y = ProtoField.int16(abbr.."pointed_above_y",
+               "Node position (above surface) Y")
+       local f_pointed_above_z = ProtoField.int16(abbr.."pointed_above_z",
+               "Node position (above surface) Z")
+       local f_pointed_object_id = ProtoField.int16(abbr.."pointed_object_id",
+               "Object ID")
+       -- mising: additional playerpos data just like in TOSERVER_PLAYERPOS
+
+       minetest_client_commands[0x39] = {
+               "INTERACT", 11,
+               { f_action,
+                 f_item,
+                 f_plen,
+                 f_pointed_version,
+                 f_pointed_type,
+                 f_pointed_under_x,
+                 f_pointed_under_y,
+                 f_pointed_under_z,
+                 f_pointed_above_x,
+                 f_pointed_above_y,
+                 f_pointed_above_z,
+                 f_pointed_object_id },
+               function(buffer, pinfo, tree, t)
+                       t:add(f_action, buffer(2,1))
+                       t:add(f_item, buffer(3,2))
+                       t:add(f_plen, buffer(5,4))
+                       local plen = buffer(5,4):uint()
+                       if minetest_check_length(buffer, 9 + plen, t) then
+                               t:add(f_pointed_version, buffer(9,1))
+                               t:add(f_pointed_type, buffer(10,1))
+                               local ptype = buffer(10,1):uint()
+                               if ptype == 1 then -- Node
+                                       t:add(f_pointed_under_x, buffer(11,2))
+                                       t:add(f_pointed_under_y, buffer(13,2))
+                                       t:add(f_pointed_under_z, buffer(15,2))
+                                       t:add(f_pointed_above_x, buffer(17,2))
+                                       t:add(f_pointed_above_x, buffer(19,2))
+                                       t:add(f_pointed_above_x, buffer(21,2))
+                               elseif ptype == 2 then -- Object
+                                       t:add(f_pointed_object_id, buffer(11,2))
+                               end
+                       end
+               end
+       }
+end
+
+-- ...
+
 minetest_client_commands[0x3a] = { "REMOVED_SOUNDS", 2 }
 minetest_client_commands[0x3b] = { "NODEMETA_FIELDS", 2 }
 minetest_client_commands[0x3c] = { "INVENTORY_FIELDS", 2 }
 minetest_client_commands[0x40] = { "REQUEST_MEDIA", 2 }
 minetest_client_commands[0x41] = { "RECEIVED_MEDIA", 2 }
+
+-- TOSERVER_BREATH (obsolete)
+
 minetest_client_commands[0x42] = { "BREATH", 2 }
-minetest_client_commands[0x43] = { "CLIENT_READY", 2 }
+minetest_client_obsolete[0x42] = true
+
+-- TOSERVER_CLIENT_READY
+
+do
+       local abbr = "minetest.client.client_ready_"
+       local f_major = ProtoField.uint8(abbr.."major","Version Major")
+       local f_minor = ProtoField.uint8(abbr.."minor","Version Minor")
+       local f_patch = ProtoField.uint8(abbr.."patch","Version Patch")
+       local f_reserved = ProtoField.uint8(abbr.."reserved","Reserved")
+       local f_versionlen, f_version =
+               minetest_field_helper("uint16", abbr.."version", "Full Version String")
+       local f_formspec_ver = ProtoField.uint16(abbr.."formspec_version",
+               "Formspec API version")
+
+       minetest_client_commands[0x43] = {
+               "CLIENT_READY",
+               8,
+               { f_major, f_minor, f_patch, f_reserved, f_versionlen,
+                 f_version, f_formspec_ver },
+               function(buffer, pinfo, tree, t)
+                       t:add(f_major, buffer(2,1))
+                       t:add(f_minor, buffer(3,1))
+                       t:add(f_patch, buffer(4,1))
+                       t:add(f_reserved, buffer(5,1))
+                       local off = minetest_decode_helper_ascii(buffer, t, "uint16", 6,
+                               f_versionlen, f_version)
+                       if off and minetest_check_length(buffer, off + 2, t) then
+                               t:add(f_formspec_ver, buffer(off,2))
+                       end
+               end
+       }
+end
+
+-- ...
+
 minetest_client_commands[0x50] = { "FIRST_SRP", 2 }
 minetest_client_commands[0x51] = { "SRP_BYTES_A", 2 }
 minetest_client_commands[0x52] = { "SRP_BYTES_M", 2 }
@@ -446,41 +486,85 @@ minetest_client_commands[0x52] = { "SRP_BYTES_M", 2 }
 
 
 --------------------------------------------
--- Part 2                                 --
+-- Part 3                                 --
 -- Server command dissectors (TOCLIENT_*) --
 --------------------------------------------
 
 minetest_server_commands = {}
 minetest_server_obsolete = {}
 
--- TOCLIENT_INIT
+-- TOCLIENT_HELLO
 
-minetest_server_commands[0x02] = {"HELLO", 2}
-minetest_server_commands[0x03] = {"AUTH_ACCEPT", 2}
-minetest_server_commands[0x04] = {"ACCEPT_SUDO_MODE", 2}
-minetest_server_commands[0x05] = {"DENY_SUDO_MODE", 2}
-minetest_server_commands[0x0A] = {"ACCESS_DENIED", 2}
+do
+       local abbr = "minetest.server.hello_"
+
+       local f_ser_fmt = ProtoField.uint8(abbr.."ser_version",
+               "Deployed serialization format version", base.DEC)
+       local f_comp_mode = ProtoField.uint16(abbr.."compression",
+               "Deployed compression mode", base.DEC, { [0] = "No compression" })
+       local f_proto = ProtoField.uint16(abbr.."proto",
+               "Deployed protocol version", base.DEC)
+       local f_auth_methods = ProtoField.bytes(abbr.."auth_modes",
+               "Supported authentication modes")
+       local f_legacy_namelen, f_legacy_name = minetest_field_helper("uint16",
+               abbr.."legacy_name", "Legacy player name for hashing")
+
+       minetest_server_commands[0x02] = {
+               "HELLO",
+               13,
+               { f_ser_fmt, f_comp_mode, f_proto, f_auth_methods,
+                 f_legacy_namelen, f_legacy_name },
+               function(buffer, pinfo, tree, t)
+                       t:add(f_ser_fmt, buffer(2,1))
+                       t:add(f_comp_mode, buffer(3,2))
+                       t:add(f_proto, buffer(5,2))
+                       t:add(f_auth_methods, buffer(7,4))
+                       minetest_decode_helper_ascii(buffer, t, "uint16", 11, f_legacy_namelen, f_legacy_name)
+               end
+       }
+end
+
+-- TOCLIENT_AUTH_ACCEPT
 
 do
-       local f_version = ProtoField.uint8("minetest.server.init_version", "Deployed version", base.DEC)
-       local f_pos_x = ProtoField.int16("minetest.server.init_pos_x", "Position X", base.DEC)
-       local f_pos_y = ProtoField.int16("minetest.server.init_pos_y", "Position Y", base.DEC)
-       local f_pos_z = ProtoField.int16("minetest.server.init_pos_x", "Position Z", base.DEC)
-       local f_map_seed = ProtoField.uint64("minetest.server.init_map_seed", "Map seed", base.DEC)
-
-       minetest_server_commands[0x10] = {
-               "INIT", 17,
-               { f_version, f_pos_x, f_pos_y, f_pos_z, f_map_seed },
+       local abbr = "minetest.server.auth_accept_"
+
+       local f_player_x = ProtoField.float(abbr.."player_x", "Player position X")
+       local f_player_y = ProtoField.float(abbr.."player_y", "Player position Y")
+       local f_player_z = ProtoField.float(abbr.."player_z", "Player position Z")
+       local f_map_seed = ProtoField.uint64(abbr.."map_seed", "Map seed")
+       local f_send_interval = ProtoField.float(abbr.."send_interval",
+               "Recommended send interval")
+       local f_sudo_auth_methods = ProtoField.bytes(abbr.."sudo_auth_methods",
+               "Supported auth methods for sudo mode")
+
+       minetest_server_commands[0x03] = {
+               "AUTH_ACCEPT",
+               30,
+               { f_player_x, f_player_y, f_player_z, f_map_seed,
+                 f_send_interval, f_sudo_auth_methods },
                function(buffer, pinfo, tree, t)
-                       t:add(f_version, buffer(2,1))
-                       t:add(f_pos_x, buffer(3,2))
-                       t:add(f_pos_y, buffer(5,2))
-                       t:add(f_pos_z, buffer(7,2))
-                       t:add(f_map_seed, buffer(9,8))
+                       t:add(f_player_x, buffer(2,4))
+                       t:add(f_player_y, buffer(6,4))
+                       t:add(f_player_z, buffer(10,4))
+                       t:add(f_map_seed, buffer(14,8))
+                       t:add(f_send_interval, buffer(22,4))
+                       t:add(f_sudo_auth_methods, buffer(26,4))
                end
        }
 end
 
+-- ...
+
+minetest_server_commands[0x04] = {"ACCEPT_SUDO_MODE", 2}
+minetest_server_commands[0x05] = {"DENY_SUDO_MODE", 2}
+minetest_server_commands[0x0A] = {"ACCESS_DENIED", 2}
+
+-- TOCLIENT_INIT (obsolete)
+
+minetest_server_commands[0x10] = { "INIT", 2 }
+minetest_server_obsolete[0x10] = true
+
 -- TOCLIENT_BLOCKDATA
 
 do
@@ -544,37 +628,10 @@ end
 minetest_server_commands[0x23] = { "PLAYERPOS", 2 }
 minetest_server_obsolete[0x23] = true
 
--- TOCLIENT_PLAYERINFO
+-- TOCLIENT_PLAYERINFO (obsolete)
 
-do
-       local f_count = ProtoField.uint16("minetest.server.playerinfo_count", "Count", base.DEC)
-       local f_player = ProtoField.bytes("minetest.server.playerinfo_player", "Player", base.NONE)
-       local f_peer_id = ProtoField.uint16("minetest.server.playerinfo_peer_id", "Peer ID", base.DEC)
-       local f_name = ProtoField.string("minetest.server.playerinfo_name", "Name")
-
-       minetest_server_commands[0x24] = {
-               "PLAYERINFO", 2,
-               { f_count, f_player, f_peer_id, f_name },
-               function(buffer, pinfo, tree, t)
-                       local count = 0
-                       local pos, index
-                       for pos = 2, buffer:len() - 22, 22 do  -- does lua have integer division?
-                               count = count + 1
-                       end
-                       t:add(f_count, count):set_generated()
-                       t:set_len(2 + 22 * count)
-                       pinfo.cols.info:append(" * " .. count)
-                       for index = 0, count - 1 do
-                               local pos = 2 + 22 * index
-                               local t2 = t:add(f_player, buffer(pos, 22))
-                               t2:set_text("Player, ID: " .. buffer(pos, 2):uint()
-                                       .. ", Name: " .. buffer(pos + 2, 20):string())
-                               t2:add(f_peer_id, buffer(pos, 2))
-                               t2:add(f_name, buffer(pos + 2, 20))
-                       end
-               end
-       }
-end
+minetest_server_commands[0x24] = { "PLAYERINFO", 2 }
+minetest_server_obsolete[0x24] = true
 
 -- TOCLIENT_OPT_BLOCK_NOT_FOUND (obsolete)
 
@@ -600,100 +657,74 @@ do
        }
 end
 
--- TOCLIENT_OBJECTDATA
-
-do
-       local f_player_count = ProtoField.uint16("minetest.server.objectdata_player_count",
-               "Count of player positions", base.DEC)
-       local f_player = ProtoField.bytes("minetest.server.objectdata_player", "Player position")
-       local f_peer_id = ProtoField.uint16("minetest.server.objectdata_player_peer_id", "Peer ID")
-       local f_x = ProtoField.int32("minetest.server.objectdata_player_x", "Position X", base.DEC)
-       local f_y = ProtoField.int32("minetest.server.objectdata_player_y", "Position Y", base.DEC)
-       local f_z = ProtoField.int32("minetest.server.objectdata_player_z", "Position Z", base.DEC)
-       local f_speed_x = ProtoField.int32("minetest.server.objectdata_player_speed_x", "Speed X", base.DEC)
-       local f_speed_y = ProtoField.int32("minetest.server.objectdata_player_speed_y", "Speed Y", base.DEC)
-       local f_speed_z = ProtoField.int32("minetest.server.objectdata_player_speed_z", "Speed Z", base.DEC)
-       local f_pitch = ProtoField.int32("minetest.server.objectdata_player_pitch", "Pitch", base.DEC)
-       local f_yaw = ProtoField.int32("minetest.server.objectdata_player_yaw", "Yaw", base.DEC)
-       local f_block_count = ProtoField.uint16("minetest.server.objectdata_block_count",
-               "Count of blocks", base.DEC)
-
-       minetest_server_commands[0x28] = {
-               "OBJECTDATA", 6,
-               { f_player_count, f_player, f_peer_id, f_x, f_y, f_z,
-                 f_speed_x, f_speed_y, f_speed_z,f_pitch, f_yaw,
-                 f_block_count },
-               function(buffer, pinfo, tree, t)
-                       local t2, index, pos
-
-                       local player_count_pos = 2
-                       local player_count = buffer(player_count_pos, 2):uint()
-                       t:add(f_player_count, buffer(player_count_pos, 2))
-
-                       local block_count_pos = player_count_pos + 2 + 34 * player_count
-                       if not minetest_check_length(buffer, block_count_pos + 2, t) then
-                               return
-                       end
-
-                       for index = 0, player_count - 1 do
-                               pos = player_count_pos + 2 + 34 * index
-                               t2 = t:add(f_player, buffer(pos, 34))
-                               t2:set_text("Player position, ID: " .. buffer(pos, 2):uint())
-                               t2:add(f_peer_id, buffer(pos, 2))
-                               t2:add(f_x, buffer(pos + 2, 4))
-                               t2:add(f_y, buffer(pos + 6, 4))
-                               t2:add(f_z, buffer(pos + 10, 4))
-                               t2:add(f_speed_x, buffer(pos + 14, 4))
-                               t2:add(f_speed_y, buffer(pos + 18, 4))
-                               t2:add(f_speed_z, buffer(pos + 22, 4))
-                               t2:add(f_pitch, buffer(pos + 26, 4))
-                               t2:add(f_yaw, buffer(pos + 30, 4))
-                       end
-
-                       local block_count = buffer(block_count_pos, 2):uint()
-                       t:add(f_block_count, buffer(block_count_pos, 2))
+-- TOCLIENT_OBJECTDATA (obsolete)
 
-                       -- TODO: dissect blocks.
-                       -- NOTE: block_count > 0 is obsolete. (?)
-
-                       pinfo.cols.info:append(" * " .. (player_count + block_count))
-               end
-       }
-end
+minetest_server_commands[0x28] = { "OBJECTDATA", 2 }
+minetest_server_obsolete[0x28] = true
 
 -- TOCLIENT_TIME_OF_DAY
 
 do
        local f_time = ProtoField.uint16("minetest.server.time_of_day", "Time", base.DEC)
+       local f_time_speed = ProtoField.float("minetest.server.time_speed", "Time Speed", base.DEC)
 
        minetest_server_commands[0x29] = {
                "TIME_OF_DAY", 4,
-               { f_time },
+               { f_time, f_time_speed },
                function(buffer, pinfo, tree, t)
                        t:add(f_time, buffer(2,2))
+                       t:add(f_time_speed, buffer(4,4))
                end
        }
 end
 
+-- TOCLIENT_CSM_RESTRICTION_FLAGS
+
+minetest_server_commands[0x2a] = { "CSM_RESTRICTION_FLAGS", 2 }
+
+-- TOCLIENT_PLAYER_SPEED
+
+minetest_server_commands[0x2b] = { "PLAYER_SPEED", 2 }
+
 -- TOCLIENT_CHAT_MESSAGE
 
 do
-       local f_length = ProtoField.uint16("minetest.server.chat_message_length", "Length", base.DEC)
-       local f_message = ProtoField.string("minetest.server.chat_message", "Message")
+       local abbr = "minetest.server.chat_message_"
+       local vs_type = {
+               [0] = "Raw",
+               [1] = "Normal",
+               [2] = "Announce",
+               [3] = "System",
+       }
 
-       minetest_server_commands[0x30] = {
-               "CHAT_MESSAGE", 4,
-               { f_length, f_message },
+       local f_version = ProtoField.uint8(abbr.."version", "Version")
+       local f_type = ProtoField.uint8(abbr.."type", "Message Type", base.DEC, vs_type)
+       local f_senderlen, f_sender = minetest_field_helper("uint16", abbr.."sender",
+               "Message sender")
+       local f_messagelen, f_message = minetest_field_helper("uint16", abbr:sub(1,-2),
+               "Message")
+
+       minetest_server_commands[0x2f] = {
+               "CHAT_MESSAGE", 8,
+               { f_version, f_type, f_senderlen, f_sender,
+                 f_messagelen, f_message },
                function(buffer, pinfo, tree, t)
-                       t:add(f_length, buffer(2,2))
-                       local textlen = buffer(2,2):uint()
-                       if minetest_check_length(buffer, 4 + textlen*2, t) then
-                               t:add(f_message, minetest_convert_utf16(buffer(4, textlen*2), "Converted chat message"))
+                       t:add(f_version, buffer(2,1))
+                       t:add(f_type, buffer(3,1))
+                       local off = 4
+                       off = minetest_decode_helper_utf16(buffer, t, "uint16", off, f_senderlen, f_sender)
+                       if off then
+                               off = minetest_decode_helper_utf16(buffer, t, "uint16", off, f_messagelen, f_message)
                        end
                end
        }
 end
 
+-- TOCLIENT_CHAT_MESSAGE_OLD (obsolete)
+
+minetest_server_commands[0x30] = { "CHAT_MESSAGE_OLD", 2 }
+minetest_server_obsolete[0x30] = true
+
 -- TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD
 
 do
@@ -842,13 +873,13 @@ end
 -- TOCLIENT_HP
 
 do
-       local f_hp = ProtoField.uint8("minetest.server.hp", "Hitpoints", base.DEC)
+       local f_hp = ProtoField.uint16("minetest.server.hp", "Hitpoints", base.DEC)
 
        minetest_server_commands[0x33] = {
-               "HP", 3,
+               "HP", 4,
                { f_hp },
                function(buffer, pinfo, tree, t)
-                       t:add(f_hp, buffer(2,1))
+                       t:add(f_hp, buffer(2,2))
                end
        }
 end
@@ -856,45 +887,35 @@ end
 -- TOCLIENT_MOVE_PLAYER
 
 do
-       local f_x = ProtoField.int32("minetest.server.move_player_x", "Position X", base.DEC)
-       local f_y = ProtoField.int32("minetest.server.move_player_y", "Position Y", base.DEC)
-       local f_z = ProtoField.int32("minetest.server.move_player_z", "Position Z", base.DEC)
-       local f_pitch = ProtoField.int32("minetest.server.move_player_pitch", "Pitch", base.DEC)
-       local f_yaw = ProtoField.int32("minetest.server.move_player_yaw", "Yaw", base.DEC)
-       local f_garbage = ProtoField.bytes("minetest.server.move_player_garbage", "Garbage")
+       local abbr = "minetest.server.move_player_"
+
+       local f_x = ProtoField.float(abbr.."x", "Position X")
+       local f_y = ProtoField.float(abbr.."y", "Position Y")
+       local f_z = ProtoField.float(abbr.."z", "Position Z")
+       local f_pitch = ProtoField.float(abbr.."_pitch", "Pitch")
+       local f_yaw = ProtoField.float(abbr.."yaw", "Yaw")
 
        minetest_server_commands[0x34] = {
-               "MOVE_PLAYER", 18,  -- actually 22, but see below
+               "MOVE_PLAYER", 22,
                { f_x, f_y, f_z, f_pitch, f_yaw, f_garbage },
                function(buffer, pinfo, tree, t)
                        t:add(f_x, buffer(2, 4))
                        t:add(f_y, buffer(6, 4))
                        t:add(f_z, buffer(10, 4))
-
-                       -- Compatibility note:
-                       -- Up to 2011-08-23, there was a bug in Minetest that
-                       -- caused the server to serialize the pitch and yaw
-                       -- with 2 bytes each instead of 4, creating a
-                       -- malformed message.
-                       if buffer:len() >= 22 then
-                               t:add(f_pitch, buffer(14, 4))
-                               t:add(f_yaw, buffer(18, 4))
-                       else
-                               t:add(f_garbage, buffer(14, 4))
-                               t:add_expert_info(PI_MALFORMED, PI_WARN, "Malformed pitch and yaw, possibly caused by a serialization bug in Minetest")
-                       end
+                       t:add(f_pitch, buffer(14, 4))
+                       t:add(f_yaw, buffer(18, 4))
                end
        }
 end
 
--- TOCLIENT_ACCESS_DENIED
+-- TOCLIENT_ACCESS_DENIED_LEGACY
 
 do
        local f_reason_length = ProtoField.uint16("minetest.server.access_denied_reason_length", "Reason length", base.DEC)
        local f_reason = ProtoField.string("minetest.server.access_denied_reason", "Reason")
 
        minetest_server_commands[0x35] = {
-               "ACCESS_DENIED", 4,
+               "ACCESS_DENIED_LEGACY", 4,
                { f_reason_length, f_reason },
                function(buffer, pinfo, tree, t)
                        t:add(f_reason_length, buffer(2,2))
@@ -906,68 +927,16 @@ do
        }
 end
 
--- TOCLIENT_PLAYERITEM
+-- TOCLIENT_FOV
 
-do
-       local f_count = ProtoField.uint16(
-               "minetest.server.playeritem_count",
-               "Count of players", base.DEC)
-       local f_player = ProtoField.bytes(
-               "minetest.server.playeritem_player",
-               "Player")
-       local f_peer_id = ProtoField.uint16(
-               "minetest.server.playeritem_peer_id",
-               "Peer ID", base.DEC)
-       local f_item_length = ProtoField.uint16(
-               "minetest.server.playeritem_item_length",
-               "Item information length", base.DEC)
-       local f_item = ProtoField.string(
-               "minetest.server.playeritem_item",
-               "Item information")
-
-       minetest_server_commands[0x36] = {
-               "PLAYERITEM", 4,
-               { f_count, f_player, f_peer_id, f_item_length, f_item },
-               function(buffer, pinfo, tree, t)
-                       local count, index, pos, item_length
-
-                       count = buffer(2,2):uint()
-                       pinfo.cols.info:append(" * " .. count)
-                       t:add(f_count, buffer(2,2))
-
-                       pos = 4
-                       for index = 0, count - 1 do
-                               if not minetest_check_length(buffer, pos + 4, t) then
-                                       return
-                               end
-                               item_length = buffer(pos + 2, 2):uint()
-                               if not minetest_check_length(buffer, pos + 4 + item_length, t) then
-                                       return
-                               end
-
-                               local t2 = t:add(f_player, buffer(pos, 4 + item_length))
-                               t2:set_text("Player, ID: " .. buffer(pos, 2):uint())
-                               t2:add(f_peer_id, buffer(pos, 2))
-                               t2:add(f_item_length, buffer(pos + 2, 2))
-                               t2:add(f_item, buffer(pos + 4, item_length))
-
-                               pos = pos + 4 + item_length
-                       end
-               end
-       }
-end
+minetest_server_commands[0x36] = { "FOV", 2 }
 
 -- TOCLIENT_DEATHSCREEN
 
 do
-       local vs_set_camera_point_target = {
-               [0] = "False",
-               [1] = "True"
-       }
-
-       local f_set_camera_point_target = ProtoField.uint8(
+       local f_set_camera_point_target = ProtoField.bool(
                "minetest.server.deathscreen_set_camera_point_target",
-               "Set camera point target", base.DEC, vs_set_camera_point_target)
+               "Set camera point target")
        local f_camera_point_target_x = ProtoField.int32(
                "minetest.server.deathscreen_camera_point_target_x",
                "Camera point target X", base.DEC)
@@ -991,10 +960,26 @@ do
        }
 end
 
+-- TOCLIENT_MEDIA
+
 minetest_server_commands[0x38] = {"MEDIA", 2}
+
+-- TOCLIENT_TOOLDEF (obsolete)
+
 minetest_server_commands[0x39] = {"TOOLDEF", 2}
+minetest_server_obsolete[0x39] = true
+
+-- TOCLIENT_NODEDEF
+
 minetest_server_commands[0x3a] = {"NODEDEF", 2}
+
+-- TOCLIENT_CRAFTITEMDEF (obsolete)
+
 minetest_server_commands[0x3b] = {"CRAFTITEMDEF", 2}
+minetest_server_obsolete[0x3b] = true
+
+-- ...
+
 minetest_server_commands[0x3c] = {"ANNOUNCE_MEDIA", 2}
 minetest_server_commands[0x3d] = {"ITEMDEF", 2}
 minetest_server_commands[0x3f] = {"PLAY_SOUND", 2}
@@ -1006,7 +991,14 @@ minetest_server_commands[0x44] = {"SHOW_FORMSPEC", 2}
 minetest_server_commands[0x45] = {"MOVEMENT", 2}
 minetest_server_commands[0x46] = {"SPAWN_PARTICLE", 2}
 minetest_server_commands[0x47] = {"ADD_PARTICLE_SPAWNER", 2}
+
+-- TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY (obsolete)
+
 minetest_server_commands[0x48] = {"DELETE_PARTICLESPAWNER_LEGACY", 2}
+minetest_server_obsolete[0x48] = true
+
+-- ...
+
 minetest_server_commands[0x49] = {"HUDADD", 2}
 minetest_server_commands[0x4a] = {"HUDRM", 2}
 minetest_server_commands[0x4b] = {"HUDCHANGE", 2}
@@ -1020,11 +1012,57 @@ minetest_server_commands[0x52] = {"EYE_OFFSET", 2}
 minetest_server_commands[0x53] = {"DELETE_PARTICLESPAWNER", 2}
 minetest_server_commands[0x54] = {"CLOUD_PARAMS", 2}
 minetest_server_commands[0x55] = {"FADE_SOUND", 2}
-minetest_server_commands[0x61] = {"SRP_BYTES_S_B", 2}
+
+-- TOCLIENT_UPDATE_PLAYER_LIST
+
+do
+       local abbr = "minetest.server.update_player_list_"
+       local vs_type = {
+               [0] = "Init",
+               [1] = "Add",
+               [2] = "Remove",
+       }
+
+       local f_type = ProtoField.uint8(abbr.."type", "Type", base.DEC, vs_type)
+       local f_count = ProtoField.uint16(abbr.."count", "Number of players", base.DEC)
+       local f_name = ProtoField.string(abbr.."name", "Name")
+
+       minetest_server_commands[0x56] = {
+               "UPDATE_PLAYER_LIST",
+               5,
+               { f_type, f_count, f_name },
+               function(buffer, pinfo, tree, t)
+                       t:add(f_type, buffer(2,1))
+                       t:add(f_count, buffer(3,2))
+                       local count = buffer(3,2):uint()
+                       local off = 5
+                       for i = 1, count do
+                               if not minetest_check_length(buffer, off + 2, t) then
+                                       return
+                               end
+                               off = minetest_decode_helper_ascii(buffer, t, "uint16", off, nil, f_name)
+                               if not off then
+                                       return
+                               end
+                       end
+               end
+       }
+end
+
+-- ...
+
+minetest_server_commands[0x57] = {"MODCHANNEL_MSG", 2}
+minetest_server_commands[0x58] = {"MODCHANNEL_SIGNAL", 2}
+minetest_server_commands[0x59] = {"NODEMETA_CHANGED", 2}
+minetest_server_commands[0x5a] = {"SET_SUN", 2}
+minetest_server_commands[0x5b] = {"SET_MOON", 2}
+minetest_server_commands[0x5c] = {"SET_STARS", 2}
+minetest_server_commands[0x60] = {"SRP_BYTES_S_B", 2}
+minetest_server_commands[0x61] = {"FORMSPEC_PREPEND", 2}
 
 
 ------------------------------------
--- Part 3                         --
+-- Part 4                         --
 -- Wrapper protocol subdissectors --
 ------------------------------------
 
@@ -1093,15 +1131,15 @@ function minetest_define_client_or_server_proto(is_client)
                this_peer = "Client"
                other_peer = "Server"
                empty_message_info = "Empty message / Connect"
-               commands = minetest_client_commands  -- defined in Part 1
-               obsolete = minetest_client_obsolete  -- defined in Part 1
+               commands = minetest_client_commands  -- defined in Part 2
+               obsolete = minetest_client_obsolete  -- defined in Part 2
        else
                proto_name = "minetest.server"
                this_peer = "Server"
                other_peer = "Client"
                empty_message_info = "Empty message"
-               commands = minetest_server_commands  -- defined in Part 2
-               obsolete = minetest_server_obsolete  -- defined in Part 2
+               commands = minetest_server_commands  -- defined in Part 3
+               obsolete = minetest_server_obsolete  -- defined in Part 3
        end
 
        -- Create the protocol object.
@@ -1124,8 +1162,8 @@ function minetest_define_client_or_server_proto(is_client)
        for code, command_info in pairs(commands) do
                local command_fields = command_info[3]
                if command_fields ~= nil then
-                       local index, field
                        for index, field in ipairs(command_fields) do
+                               assert(field ~= nil)
                                table.insert(proto.fields, field)
                        end
                end
@@ -1199,7 +1237,7 @@ end
 
 
 -------------------------------------
--- Part 4                          --
+-- Part 5                          --
 -- Wrapper protocol main dissector --
 -------------------------------------
 
@@ -1311,10 +1349,10 @@ end
 
 
 
------------------------
--- Part 5            --
--- Utility functions --
------------------------
+------------------------------
+-- Part 6                   --
+-- Utility functions part 2 --
+------------------------------
 
 -- Checks if a (sub-)Tvb is long enough to be further dissected.
 -- If it is long enough, sets the dissector tree item length to min_len
@@ -1329,14 +1367,11 @@ function minetest_check_length(tvb, min_len, t)
                t:set_len(min_len)
                return true
 
-       -- Tvb:reported_length_remaining() has been added in August 2011
-       -- and is not yet widely available, disable for the time being
-       -- TODO: uncomment at a later date
-       -- TODO: when uncommenting this, also re-check if other parts of
+       -- TODO: check if other parts of
        -- the dissector could benefit from reported_length_remaining
-       --elseif tvb:reported_length_remaining() >= min_len then
-       --      t:add_expert_info(PI_UNDECODED, PI_INFO, "Only part of this packet was captured, unable to decode.")
-       --      return false
+       elseif tvb:reported_length_remaining() >= min_len then
+               t:add_expert_info(PI_UNDECODED, PI_INFO, "Only part of this packet was captured, unable to decode.")
+               return false
 
        else
                t:add_expert_info(PI_MALFORMED, PI_ERROR, "Message is too short")
@@ -1358,7 +1393,7 @@ function minetest_convert_utf16(tvb, name)
        hex = ""
        for pos = 0, tvb:len() - 2, 2 do
                char = tvb(pos, 2):uint()
-               if (char >= 0x20) and (char <= 0x7e) then
+               if (char >= 0x20 and char <= 0x7e) or char == 0x0a then
                        hex = hex .. string.format(" %02x", char)
                else
                        hex = hex .. " 3F"
@@ -1373,3 +1408,37 @@ function minetest_convert_utf16(tvb, name)
        end
 end
 
+-- Decodes a variable-length string as ASCII text
+-- t_textlen, t_text should be the ProtoFields created by minetest_field_helper
+--   alternatively t_text can be a ProtoField.string and t_textlen can be nil
+-- lentype must be the type of the length field (as passed to minetest_field_helper)
+-- returns nil if length check failed
+function minetest_decode_helper_ascii(tvb, t, lentype, offset, f_textlen, f_text)
+       local n = ({uint16 = 2, uint32 = 4})[lentype]
+       assert(n)
+
+       if f_textlen then
+               t:add(f_textlen, tvb(offset, n))
+       end
+       local textlen = tvb(offset, n):uint()
+       if minetest_check_length(tvb, offset + n + textlen, t) then
+               t:add(f_text, tvb(offset + n, textlen))
+               return offset + n + textlen
+       end
+end
+
+-- Decodes a variable-length string as UTF-16 text
+-- (see minetest_decode_helper_ascii)
+function minetest_decode_helper_utf16(tvb, t, lentype, offset, f_textlen, f_text)
+       local n = ({uint16 = 2, uint32 = 4})[lentype]
+       assert(n)
+
+       if f_textlen then
+               t:add(f_textlen, tvb(offset, n))
+       end
+       local textlen = tvb(offset, n):uint() * 2
+       if minetest_check_length(tvb, offset + n + textlen, t) then
+               t:add(f_text, minetest_convert_utf16(tvb(offset + n, textlen), "UTF-16 text"))
+               return offset + n + textlen
+       end
+end