Skip to content

Instantly share code, notes, and snippets.

@fizzrepo
Created July 24, 2023 08:56
Show Gist options
  • Save fizzrepo/6b1a2fc091eaa089392c8d2be2b13c2d to your computer and use it in GitHub Desktop.
Save fizzrepo/6b1a2fc091eaa089392c8d2be2b13c2d to your computer and use it in GitHub Desktop.
Minecraft classic protocol implementation in Python
# protocol.py
# Copyright (c) 2023 fizzdev
# MIT License. See LICENSE for more information.
import struct
# Protocol Data Types
def read_byte(data):
return struct.unpack('B', data)[0]
def read_sbyte(data):
return struct.unpack('b', data)[0]
def read_fbyte(data):
return struct.unpack('>b', data)[0] / 32.0
def read_short(data):
return struct.unpack('>h', data)[0]
def read_fshort(data):
return struct.unpack('>h', data)[0] / 32.0
def read_string(data):
return data.decode('ascii').rstrip()
def read_byte_array(data):
return data.rstrip(b'\x00')
# Client → Server packets
class PlayerIdentificationPacket:
def __init__(self, packet_id, protocol_version, username, verification_key):
self.packet_id = packet_id
self.protocol_version = protocol_version
self.username = username
self.verification_key = verification_key
@classmethod
def from_bytes(cls, data):
packet_id = read_byte(data[0:1])
protocol_version = read_byte(data[1:2])
username = read_string(data[2:66])
verification_key = read_string(data[66:130])
return cls(packet_id, protocol_version, username, verification_key)
class SetBlockPacket:
def __init__(self, packet_id, x, y, z, mode, block_type):
self.packet_id = packet_id
self.x = x
self.y = y
self.z = z
self.mode = mode
self.block_type = block_type
@classmethod
def from_bytes(cls, data):
packet_id = read_byte(data[0:1])
x = read_short(data[5:7])
y = read_short(data[7:9])
z = read_short(data[9:11])
mode = read_byte(data[11:12])
block_type = read_byte(data[12:13])
return cls(packet_id, x, y, z, mode, block_type)
class PositionAndOrientationPacket:
def __init__(self, packet_id, player_id, x, y, z, yaw, pitch):
self.packet_id = packet_id
self.player_id = player_id
self.x = x
self.y = y
self.z = z
self.yaw = yaw
self.pitch = pitch
@classmethod
def from_bytes(cls, data):
packet_id = read_byte(data[0:1])
player_id = read_sbyte(data[1:2])
x = read_fshort(data[2:4])
y = read_fshort(data[4:6])
z = read_fshort(data[6:8])
yaw = read_byte(data[8:9])
pitch = read_byte(data[9:10])
return cls(packet_id, player_id, x, y, z, yaw, pitch)
class MessagePacket:
def __init__(self, packet_id, player_id, message):
self.packet_id = packet_id
self.player_id = player_id
self.message = message
@classmethod
def from_bytes(cls, data):
packet_id = read_byte(data[0:1])
player_id = read_sbyte(data[1:2])
message = read_string(data[2:])
return cls(packet_id, player_id, message)
# Server → Client packets
class ServerIdentificationPacket:
def __init__(self, packet_id, protocol_version, server_name, server_motd, user_type):
self.packet_id = packet_id
self.protocol_version = protocol_version
self.server_name = server_name
self.server_motd = server_motd
self.user_type = user_type
@classmethod
def from_bytes(cls, data):
packet_id = read_byte(data[0:1])
protocol_version = read_byte(data[1:2])
server_name = read_string(data[2:66])
server_motd = read_string(data[66:130])
user_type = read_byte(data[130:131])
return cls(packet_id, protocol_version, server_name, server_motd, user_type)
class ServerIdentificationPacket:
def __init__(self, packet_id, protocol_version, server_name, server_motd, user_type):
self.packet_id = packet_id
self.protocol_version = protocol_version
self.server_name = server_name
self.server_motd = server_motd
self.user_type = user_type
@classmethod
def from_bytes(cls, data):
packet_id = read_byte(data[0:1])
protocol_version = read_byte(data[1:2])
server_name = read_string(data[2:66])
server_motd = read_string(data[66:130])
user_type = read_byte(data[130:131])
return cls(packet_id, protocol_version, server_name, server_motd, user_type)
class PingPacket:
def __init__(self, packet_id):
self.packet_id = packet_id
@classmethod
def from_bytes(cls, data):
packet_id = read_byte(data[0:1])
return cls(packet_id)
class LevelInitializePacket:
def __init__(self, packet_id):
self.packet_id = packet_id
@classmethod
def from_bytes(cls, data):
packet_id = read_byte(data[0:1])
return cls(packet_id)
class LevelDataChunkPacket:
def __init__(self, packet_id, chunk_length, chunk_data, percent_complete):
self.packet_id = packet_id
self.chunk_length = chunk_length
self.chunk_data = chunk_data
self.percent_complete = percent_complete
@classmethod
def from_bytes(cls, data):
packet_id = read_byte(data[0:1])
chunk_length = read_short(data[1:3])
chunk_data = read_byte_array(data[3:3 + chunk_length])
percent_complete = read_byte(data[3 + chunk_length:4 + chunk_length])
return cls(packet_id, chunk_length, chunk_data, percent_complete)
class LevelFinalizePacket:
def __init__(self, packet_id, x_size, y_size, z_size):
self.packet_id = packet_id
self.x_size = x_size
self.y_size = y_size
self.z_size = z_size
@classmethod
def from_bytes(cls, data):
packet_id = read_byte(data[0:1])
x_size = read_short(data[1:3])
y_size = read_short(data[3:5])
z_size = read_short(data[5:7])
return cls(packet_id, x_size, y_size, z_size)
class SpawnPlayerPacket:
def __init__(self, packet_id, player_id, player_name, x, y, z, yaw, pitch):
self.packet_id = packet_id
self.player_id = player_id
self.player_name = player_name
self.x = x
self.y = y
self.z = z
self.yaw = yaw
self.pitch = pitch
@classmethod
def from_bytes(cls, data):
packet_id = read_byte(data[0:1])
player_id = read_sbyte(data[1:2])
player_name = read_string(data[2:66])
x = read_fshort(data[66:68])
y = read_fshort(data[68:70])
z = read_fshort(data[70:72])
yaw = read_byte(data[72:73])
pitch = read_byte(data[73:74])
return cls(packet_id, player_id, player_name, x, y, z, yaw, pitch)
class DisconnectPlayerPacket:
def __init__(self, packet_id, disconnect_reason):
self.packet_id = packet_id
self.disconnect_reason = disconnect_reason
@classmethod
def from_bytes(cls, data):
packet_id = read_byte(data[0:1])
disconnect_reason = read_string(data[1:])
return cls(packet_id, disconnect_reason)
class UpdateUserTypePacket:
def __init__(self, packet_id, user_type):
self.packet_id = packet_id
self.user_type = user_type
@classmethod
def from_bytes(cls, data):
packet_id = read_byte(data[0:1])
user_type = read_byte(data[1:2])
return cls(packet_id, user_type)
# Example usage
if __name__ == "__main__":
# Simulating receiving a packet
received_data = b'\x00\x07PlayerName\x00\x00'
packet = PlayerIdentificationPacket.from_bytes(received_data)
print(f"Packet ID: {packet.packet_id}")
print(f"Protocol Version: {packet.protocol_version}")
print(f"Username: {packet.username}")
print(f"Verification Key: {packet.verification_key}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment