Skip to main content

Overview

Sakuya AC uses a packet-based communication system to handle all network traffic between YSFlight clients and servers. The PacketManager class provides the core functionality for identifying and routing packets.

PacketManager

The PacketManager class is responsible for parsing incoming network data and identifying packet types.

Location

lib/PacketManager/PacketManager.py

Methods

get_packet_type
method
Returns the message type of a packet by reading its header.Parameters:
  • data (bytes): Raw packet data (minimum 4 bytes)
Returns:
  • str | None: Packet type name from MESSAGE_TYPES, or None if data is too short
Example:
from lib.PacketManager.PacketManager import PacketManager

pm = PacketManager()
packet_type = pm.get_packet_type(data)
# Returns: "FSNETCMD_AIRPLANESTATE", "FSNETCMD_LOGON", etc.

Packet Structure

All YSFlight packets follow a common structure:
  1. Size Header (4 bytes): Packet length excluding the size header itself
  2. Packet Type (4 bytes): Integer ID identifying the packet type
  3. Payload: Packet-specific data
# Reading a packet
header = await reader.readexactly(4)  # Size header
length = unpack("I", header)[0]
packet = await reader.read(length)    # Packet type + payload
data = header + packet                 # Full packet with size

Message Types

All packet types are defined in lib/PacketManager/packets/constants.py as the MESSAGE_TYPES list:
  • Connection Packets (0-3): NULL, LOGON, LOGOFF, ERROR
  • Flight Packets (11-13): AIRPLANESTATE, UNJOIN, REMOVEAIRPLANE
  • Join Packets (8-10): JOINREQUEST, JOINAPPROVAL, REJECTJOINREQ
  • Command Packets (30-36): AIRCMD, TEXTMESSAGE, WEAPONCONFIG
  • Combat Packets (18-22): LOCKON, MISSILELAUNCH, GETDAMAGE
  • Environment Packets (33, 48-50): ENVIRONMENT, FOGCOLOR, SKYCOLOR

Packet Decoding Pattern

All packet classes follow a consistent pattern:
from lib.PacketManager.packets import FSNETCMD_AIRPLANESTATE

# Decode received packet
packet = FSNETCMD_AIRPLANESTATE(data, should_decode=True)
print(packet.position)  # Access decoded fields

# Encode new packet
buffer = FSNETCMD_AIRPLANESTATE.encode(
    remote_time=0.0,
    player_id=1,
    packet_version=5,
    position=[0, 0, 0],
    # ... other fields
    with_size=True  # Include size header
)

Usage in Proxy

In proxy.py, packets are intercepted and processed:
# Identify packet type
packet_type = PacketManager().get_packet_type(packet)

# Decode based on type
if packet_type == "FSNETCMD_LOGON":
    decode = FSNETCMD_LOGON(packet)
    player.login(decode)
elif packet_type == "FSNETCMD_AIRPLANESTATE":
    decode = FSNETCMD_AIRPLANESTATE(packet)
    player.aircraft.add_state(decode)
elif packet_type == "FSNETCMD_TEXTMESSAGE":
    msg = FSNETCMD_TEXTMESSAGE(packet)
    # Process chat message

Packet Modification

Packets can be modified before forwarding:
# Stop aircraft from firing
if packet_type == "FSNETCMD_AIRPLANESTATE":
    modified_packet = FSNETCMD_AIRPLANESTATE(data).stop_firing()
    writer.write(modified_packet)

# Add smoke effect
if packet_type == "FSNETCMD_AIRPLANESTATE":
    smoke_packet = FSNETCMD_AIRPLANESTATE(data).smoke()
    writer.write(smoke_packet)

Creating New Packets

You can create packets to send to clients or servers:
from lib.PacketManager.packets import FSNETCMD_TEXTMESSAGE, FSNETCMD_AIRCMD

# Send chat message
message = FSNETCMD_TEXTMESSAGE.encode("Welcome to the server!", with_size=True)
message_to_client.append(message)

# Control aircraft
afterburer_on = FSNETCMD_AIRCMD.set_afterburner(aircraft_id, True, with_size=True)
message_to_client.append(afterburner_on)

Best Practices

  1. Always use with_size=True when sending packets over the network
  2. Check packet length before decoding to avoid errors
  3. Use should_decode=False if you only need to forward the packet
  4. Handle packet versions for AIRPLANESTATE (versions 0-5 have different structures)
  5. Validate packet data before processing to prevent crashes

Build docs developers (and LLMs) love