Unofficial Python QUIC client library for Hytale game servers. Supports full OAuth2 authentication, player movement, inventory management, block operations, and more.
pip install -e .Or with venv:
python -m venv venv
source venv/bin/activate
pip install -e .importasynciofromhytaleimportHytaleClientasyncdefmain():
client=HytaleClient(
host="127.0.0.1",
port=5520,
username="Player",
)
awaitclient.connect()
awaitclient.wait_for_position()
print(f"Position: {client.position}")
awaitclient.disconnect()
asyncio.run(main())importasynciofromhytaleimportHytaleClientasyncdefmain():
client=HytaleClient(host="127.0.0.1", port=5520)
# OAuth flow - opens browser for loginawaitclient.connect(auth=True)
awaitclient.wait_for_position()
print(f"Logged in as: {client.username}")
print(f"Position: {client.position}")
awaitclient.disconnect()
asyncio.run(main())# First run: opens browser for OAuth# Subsequent runs: uses cached tokensclient=HytaleClient(host="127.0.0.1", port=5520, cache_auth=True)
awaitclient.connect(auth=True)
# Clear cache when neededHytaleClient.clear_auth_cache()# Wait for spawn positionawaitclient.wait_for_position()
# Get current positionx, y, z=client.positionyaw, pitch=client.yaw, client.pitch# Move to absolute coordinatesclient.move(x=100, y=65, z=200)
# Move relative to current positionclient.move_relative(dx=5, dy=0, dz=-3)
# Fly forward (in look direction)client.fly_forward(distance=10.0)
# Look at directionclient.look(yaw=90.0, pitch=0.0)
# Walk to target (sends movement packets)awaitclient.walk_to(x=100, y=65, z=200, speed=4.3)# Wait for inventory to loadawaitasyncio.sleep(2)
# Print inventory summaryclient.print_inventory()
# Get itemsitem=client.get_item_in_hand()
hotbar_item=client.get_hotbar_item(slot=0)
storage_item=client.get_storage_item(slot=5)
# Select hotbar slot (0-8)client.select_hotbar_slot(3)
# Move items between slotsclient.move_item(
from_section=InventorySectionId.HOTBAR, from_slot=0,
to_section=InventorySectionId.STORAGE, to_slot=10,
quantity=5# or omit for all
)
# Convenient methodsclient.move_to_storage(hotbar_slot=2, storage_slot=0)
client.move_from_storage(storage_slot=0, hotbar_slot=2)
client.swap_hotbar_slots(slot1=0, slot2=1)
# Sort inventoryclient.sort_inventory(InventorySectionId.STORAGE)# Place block at coordinates (uses item in hand)client.place_block(x=100, y=65, z=200)
# Place specific block by IDclient.place_block(x=100, y=65, z=200, block_id=42)
# Place relative to player's look directionclient.place_block_relative(forward=2, right=0, up=0)# Break block at coordinatesclient.break_block(x=100, y=65, z=200)
# Break with multiple hits (for harder blocks)client.break_block_hold(x=100, y=65, z=200, hits=10)# Wait for chunks to loadawaitasyncio.sleep(5)
# Get chunk count and rangeprint(f"Loaded chunks: {client.chunk_count}")
print(f"Range: {client.get_loaded_chunk_range()}")
# Get current chunkchunk=client.get_current_chunk()
ifchunk:
print(f"Chunk at ({chunk.x}, {chunk.y}, {chunk.z})")
# Get block at world coordinatesblock_id=client.get_block(x=100, y=65, z=200)
# Check if position is loadedifclient.is_block_loaded(100, 65, 200):
print("Block is in a loaded chunk")Subscribe to events for real-time updates:
fromhytaleimportConnectedEvent, WorldSettingsEvent, JoinWorldEvent@client.on(ConnectedEvent)defon_connected(event):
print("Connected to server!")
@client.on(WorldSettingsEvent)defon_world_settings(event):
print(f"World height: {event.world_height}")
print(f"Spawn: {event.spawn_point}")
@client.on(JoinWorldEvent)defon_join(event):
print(f"Joined world: {event.world_name}")| Event | Description |
|---|---|
ConnectedEvent | QUIC connection established |
DisconnectedEvent | Disconnected from server |
WorldSettingsEvent | World configuration received |
JoinWorldEvent | Joined a world instance |
ClientIdEvent | Client entity ID assigned |
TeleportEvent | Player teleported |
EntityUpdatesEvent | Entity state updates |
ChunkEvent | Chunk data received |
ChunkUnloadEvent | Chunk unloaded |
InventoryUpdateEvent | Inventory changed |
ActiveSlotEvent | Active slot changed |
PingEvent | Server ping |
PacketEvent | Raw packet (all packets) |
# Insecure server
hytale-client --host 127.0.0.1 --port 5520 --username Player
# Authenticated server
hytale-client --host 127.0.0.1 --port 5520 --auth
# With debug logging
hytale-client --host 127.0.0.1 --port 5520 --debugFor full control over authentication:
fromhytaleimportHytaleClientfromhytale.authimport (
authorize, fetch_game_profile, SessionServiceClient
)
asyncdefmain():
# Step 1: OAuthtokens=awaitauthorize()
# Step 2: Get profileprofile=awaitfetch_game_profile()
# Step 3: Create sessionsession_client=SessionServiceClient()
session=awaitsession_client.create_game_session(
tokens.access_token, profile.uuid
)
# Step 4: Connectclient=HytaleClient(
host="server.example.com",
port=5520,
username=profile.username,
identity_token=session.identity_token,
session_token=session.session_token,
uuid=profile.uuid,
)
awaitclient.connect()
awaitclient.wait()
asyncio.run(main())- Transport: QUIC over UDP
- TLS: 1.3 with mTLS (self-signed client certificates)
- ALPN:
hytale/1 - Compression: Zstandard for large packets
- Frame format:
[4B length LE][4B packet ID LE][payload] - Authentication: OAuth2 + JWT identity tokens