Created
July 24, 2023 08:56
-
-
Save fizzrepo/6b1a2fc091eaa089392c8d2be2b13c2d to your computer and use it in GitHub Desktop.
Minecraft classic protocol implementation in Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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